From 6a5fe6326694f9c3918acc380e85095e6c014521 Mon Sep 17 00:00:00 2001 From: "Chrome Release Bot (LUCI)" Date: Thu, 21 Mar 2024 17:19:53 +0000 Subject: [PATCH 1/3] Publish DEPS for 114.0.5735.358 git-subtree-dir: third_party/libwebm git-subtree-split: 1759c6ae9316996b9f150c0ce9d0ca78a3d15c02 --- BUILD.gn | 18 ++++++++++++++++++ DIR_METADATA | 3 +++ OWNERS | 9 +++++++++ README.chromium | 14 ++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 BUILD.gn create mode 100644 DIR_METADATA create mode 100644 OWNERS create mode 100644 README.chromium diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 000000000000..c6a30c901631 --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2015 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("libwebm_config") { + include_dirs = [ "./source" ] +} + +static_library("libwebm") { + configs += [ ":libwebm_config" ] + public_configs = [ ":libwebm_config" ] + + sources = [ + "source/mkvmuxer/mkvmuxer.cc", + "source/mkvmuxer/mkvmuxerutil.cc", + "source/mkvmuxer/mkvwriter.cc", + ] +} diff --git a/DIR_METADATA b/DIR_METADATA new file mode 100644 index 000000000000..698e49353d57 --- /dev/null +++ b/DIR_METADATA @@ -0,0 +1,3 @@ +monorail: { + component: "Blink>MediaRecording" +} diff --git a/OWNERS b/OWNERS new file mode 100644 index 000000000000..e4e65437691b --- /dev/null +++ b/OWNERS @@ -0,0 +1,9 @@ +# The following OWNERS refer to libwebm Chromium integration. +niklase@chromium.org + +# The following OWNER refer to libwebm content. +tomfinegan@chromium.org + +# Original (legacy) owner. +emircan@chromium.org +mcasas@chromium.org diff --git a/README.chromium b/README.chromium new file mode 100644 index 000000000000..965f3fc7db40 --- /dev/null +++ b/README.chromium @@ -0,0 +1,14 @@ +Name: WebM container parser and writer. +Short Name: libwebm +URL: https://chromium.googlesource.com/webm/libwebm +Version: 1.0.0.28 +Revision: e4fbea0c9751ae8aa86629b197a28d8276a2b0da +CPEPrefix: cpe:/a:webmproject:libwebm:1.0.0.28 +License: BSD +License File: source/LICENSE.TXT +Security Critical: yes + +Description: +libwebm is a library to read and write WebM files. It's used by MediaStream +Recording API to multiplex encoded audio and video into a WebM container. Only +the writing parts are used for this purpose, the rest is not compiled. From dd93ff94571ec507c04ce51c88029e969097290f Mon Sep 17 00:00:00 2001 From: Tom Finegan Date: Mon, 7 Jun 2021 10:17:38 -0700 Subject: [PATCH 2/3] mkvmuxer: Fix unit'd value in Segment. Init ebml_header_size_ to 0. Avoids MSan issues when tests that never mux samples call Finalize() on Segment. git-subtree-dir: third_party/libwebm/source git-subtree-split: e4fbea0c9751ae8aa86629b197a28d8276a2b0da --- .clang-format | 152 + .gitattributes | 6 + .gitignore | 35 + AUTHORS.TXT | 4 + Android.mk | 17 + CMakeLists.txt | 453 + CONTRIBUTING.md | 29 + LICENSE.TXT | 30 + Makefile.unix | 57 + PATENTS.TXT | 23 + README.libwebm | 143 + build/cxx_flags.cmake | 72 + build/msvc_runtime.cmake | 22 + build/x86-mingw-gcc.cmake | 26 + build/x86_64-mingw-gcc.cmake | 24 + codereview.settings | 4 + common/common.sh | 62 + common/file_util.cc | 93 + common/file_util.h | 44 + common/hdr_util.cc | 220 + common/hdr_util.h | 71 + common/indent.cc | 29 + common/indent.h | 48 + common/libwebm_util.cc | 110 + common/libwebm_util.h | 65 + common/video_frame.cc | 45 + common/video_frame.h | 68 + common/vp9_header_parser.cc | 269 + common/vp9_header_parser.h | 129 + common/vp9_header_parser_tests.cc | 181 + common/vp9_level_stats.cc | 269 + common/vp9_level_stats.h | 215 + common/vp9_level_stats_tests.cc | 191 + common/webm_constants.h | 20 + common/webm_endian.cc | 85 + common/webm_endian.h | 38 + common/webmids.h | 193 + dumpvtt.cc | 91 + hdr_util.hpp | 15 + iosbuild.sh | 207 + m2ts/tests/webm2pes_tests.cc | 158 + m2ts/vpxpes2ts.cc | 217 + m2ts/vpxpes2ts.h | 45 + m2ts/vpxpes2ts_main.cc | 33 + m2ts/vpxpes_parser.cc | 409 + m2ts/vpxpes_parser.h | 177 + m2ts/webm2pes.cc | 551 ++ m2ts/webm2pes.h | 274 + m2ts/webm2pes_main.cc | 33 + mkvmuxer.hpp | 15 + mkvmuxer/mkvmuxer.cc | 4231 +++++++++ mkvmuxer/mkvmuxer.h | 1924 ++++ mkvmuxer/mkvmuxertypes.h | 28 + mkvmuxer/mkvmuxerutil.cc | 743 ++ mkvmuxer/mkvmuxerutil.h | 115 + mkvmuxer/mkvwriter.cc | 92 + mkvmuxer/mkvwriter.h | 51 + mkvmuxer_sample.cc | 802 ++ mkvmuxertypes.hpp | 15 + mkvmuxerutil.hpp | 18 + mkvparser.hpp | 15 + mkvparser/mkvparser.cc | 8098 +++++++++++++++++ mkvparser/mkvparser.h | 1147 +++ mkvparser/mkvreader.cc | 135 + mkvparser/mkvreader.h | 45 + mkvparser_sample.cc | 459 + mkvreader.hpp | 15 + mkvwriter.hpp | 15 + sample_muxer_metadata.cc | 391 + sample_muxer_metadata.h | 137 + testing/mkvmuxer_tests.cc | 1010 ++ testing/mkvparser_tests.cc | 823 ++ testing/test_util.cc | 215 + testing/test_util.h | 88 + .../testdata/accurate_cluster_duration.webm | Bin 0 -> 376 bytes .../accurate_cluster_duration_last_frame.webm | Bin 0 -> 384 bytes .../accurate_cluster_duration_two_tracks.webm | Bin 0 -> 533 bytes .../testdata/bbb_480p_vp9_opus_1second.webm | Bin 0 -> 45863 bytes testing/testdata/block_with_additional.webm | Bin 0 -> 348 bytes testing/testdata/chapters.webm | Bin 0 -> 346 bytes testing/testdata/colour.webm | Bin 0 -> 416 bytes testing/testdata/cues_before_clusters.webm | Bin 0 -> 380 bytes testing/testdata/discard_padding.webm | Bin 0 -> 341 bytes testing/testdata/estimate_duration.webm | Bin 0 -> 365 bytes .../testdata/fixed_size_cluster_timecode.webm | Bin 0 -> 368 bytes testing/testdata/force_new_cluster.webm | Bin 0 -> 363 bytes testing/testdata/invalid/README.libwebm | 24 + .../invalid/block_ends_beyond_cluster.mkv | Bin 0 -> 10488 bytes ...lockgroup_block_ends_beyond_blockgroup.mkv | Bin 0 -> 10949 bytes .../chapters_truncated_chapter_string.mkv | Bin 0 -> 6993 bytes .../chapters_truncated_chapter_string_2.mkv | Bin 0 -> 7536 bytes .../invalid/fixed_lacing_bad_lace_size.mkv | Bin 0 -> 47806 bytes .../invalid_vp9_bitstream-bug_1416.webm | Bin 0 -> 12847 bytes .../invalid_vp9_bitstream-bug_1417.webm | Bin 0 -> 11448 bytes .../primarychromaticity_fieldtoolarge.webm | 1 + .../invalid/projection_float_overflow.webm | Bin 0 -> 172 bytes testing/testdata/long_tag_string.webm | Bin 0 -> 474 bytes testing/testdata/matroska_doctype.mkv | Bin 0 -> 307 bytes testing/testdata/max_cluster_duration.webm | Bin 0 -> 395 bytes testing/testdata/max_cluster_size.webm | Bin 0 -> 368 bytes testing/testdata/metadata_block.webm | Bin 0 -> 314 bytes testing/testdata/output_cues.webm | Bin 0 -> 394 bytes testing/testdata/projection.webm | Bin 0 -> 317 bytes testing/testdata/segment_duration.webm | Bin 0 -> 341 bytes testing/testdata/segment_info.webm | Bin 0 -> 284 bytes testing/testdata/set_cues_track_number.webm | Bin 0 -> 412 bytes .../testdata/set_pixelwidth_pixelheight.webm | Bin 0 -> 302 bytes testing/testdata/set_segment_duration.webm | Bin 0 -> 301 bytes testing/testdata/simple_block.webm | Bin 0 -> 301 bytes testing/testdata/test_stereo_left_right.webm | Bin 0 -> 11592 bytes testing/testdata/tracks.webm | Bin 0 -> 363 bytes testing/testdata/webm_doctype.webm | Bin 0 -> 285 bytes testing/video_frame_tests.cc | 75 + vttdemux.cc | 1004 ++ vttreader.h | 15 + webm_info.cc | 1336 +++ webm_parser/README.md | 325 + webm_parser/demo/demo.cc | 1161 +++ webm_parser/doxygen.config | 319 + .../00805c2543756a5fd85652d03bfbbd2eb6192ca5 | 1 + .../00d120eb143bb02c48d7c863e5826d2ad1a6da4b | 1 + .../018dee8285e9e20ca3996bb2dc0284b5c57ba75a | 1 + .../020edb59637c1e6439f19aa3a5a9d50c3377dbe9 | 1 + .../0247ce2b1a71752a3af11e1065ca90afa0df9d30 | 1 + .../028b19f7d79f5da7a2af13a0c1e2d13f7eaf24cd | 1 + .../029ab55b16df41881f8de2351205201da334550a | 1 + .../029be5e90913b19cf5890559cf3f98aa909f0a84 | Bin 0 -> 5 bytes .../02fb96539b84bbd12de84ff05cbc9dc3faa96b7e | Bin 0 -> 58 bytes .../0349f5632d21faa36b85520ad0b524d561f5e13f | Bin 0 -> 35 bytes .../036fc9daf7fb1b4274dd668cfd883248ebbad967 | 1 + .../037a4edc18e475ec81081e47277cbf51f1316680 | Bin 0 -> 20 bytes .../0481dad9a7d0e6fab0c703bba9b3268db96c6793 | Bin 0 -> 37 bytes .../04dc2407e7142f5618aa5105377925b0b0ed544b | Bin 0 -> 137 bytes .../04fa2f34ff4a4406d136e5aaba5debe7d8129a06 | Bin 0 -> 41 bytes .../054fd0041ad81cfad0a85e3c59195485492a4493 | Bin 0 -> 158 bytes .../056b83ab2457979ea021e7118ab847eba265df15 | Bin 0 -> 36 bytes .../058326151c1d4a490964d495d35adcf15178030f | Bin 0 -> 28 bytes .../05847b5be0eb200d6a6b340c939c7a654b55003e | Bin 0 -> 33 bytes .../05b600ae9a9072ac2865247e69ed0736a0c58316 | Bin 0 -> 15 bytes .../05cf976698b55df1fcd03bc07446bb9283dad9d7 | 1 + .../05d4dfda5e264fffda243f2991a86e96141f579a | 1 + .../061217fc0b6af0ec3851d9728e03ed9b30c702d5 | 1 + .../077b53a7163e51c48e8cdaf4209bb837823ffba9 | 1 + .../07c3ade9713892bb75db2d93b48ef40b262a54e6 | 1 + .../07eaf3c7437032f60c905f6f8e3dfc193491b602 | 1 + .../07f67b922b503354b2aebcdcc4ab7b6d3150b048 | Bin 0 -> 63 bytes .../08af8d91bb21835c50330e997d973cac8ff67766 | Bin 0 -> 60 bytes .../0a18f05bb16402756202160225aec9c5a654f1ae | Bin 0 -> 192 bytes .../0b60823983971ee17a2590678f0fc0762c21bb73 | Bin 0 -> 84 bytes .../0bae7f0976af0f75974047b5f2cf4c9645412066 | 1 + .../0c1862b4065eefab2535ecc6951295e38069a82e | Bin 0 -> 79 bytes .../0c2f51d5ffc69e69680bf3d6edb91d76c353ad14 | 1 + .../0c3af72d69f18103383c9cd41a7f2676a9d28a40 | Bin 0 -> 20 bytes .../0c43df7fc9d06187249187583c3c082520701289 | 1 + .../0cadf5ecf58a394560a1f6db72a2e21264445d13 | 1 + .../0cd91a4e7bec912beb3b47a685b89d5be24d0046 | 1 + .../0d0da60f91f9489af113d8484d9b6871622523d5 | 1 + .../0d30a4f88e53de1ce4bf1ec724016f2f4c11bc9b | Bin 0 -> 90 bytes .../0d6b3b1d024e7aa73fbc58b157ab53df048a2f2c | Bin 0 -> 27 bytes .../0d94310cfd8a9acdbc7fb82ad9c73cdf3f64f926 | Bin 0 -> 281 bytes .../0f5741483be8f4f6eddcb70ea418fb0c08da9443 | Bin 0 -> 20 bytes .../109e125f729f84d69c3e3a614123547a094534ad | 1 + .../10d257a4a314e20644fbc469cdebafe16c7f46a8 | 1 + .../10d951f88995a2176878501a2633b9bb4822ff96 | 1 + .../111b8f8318a269bbe0f7d9f1cebfdc1cb43c982b | 1 + .../11c122ce1f7d993f809a4eb5db17c738f279a707 | Bin 0 -> 53 bytes .../11e288617056809208561f2a9112fd0664d378d2 | 1 + .../124fde9cfe7157773d8febcbb0829914bf4d17d3 | Bin 0 -> 128 bytes .../1386cc740c80ede6dfea5f3bb1d4fe1501ab5e91 | 1 + .../13dd373ccb0c534402c6f7c4a0bdd723a26bc2c1 | 1 + .../13fc3d2b32d789c84be6349bb585c308289b4c3e | 1 + .../148357130d1e5ac4059ad2bb6c63d78e2523f7d2 | Bin 0 -> 45147 bytes .../15366a3aafe2590c2e3183c088dde4cc100cb956 | 1 + .../15feeb939fa90b25f57622a0abd9155ca88ad08b | Bin 0 -> 88 bytes .../16282b78a2018eb78544316554a92fe1003859e3 | 1 + .../1717628a6d6ea868febec5fb196edcf4b9eb284d | 1 + .../17921b1e28600e7e3faf67fc68b397ecab4e2a52 | 1 + .../17b8276355dd2368647b7756431820f5275cc3e2 | Bin 0 -> 79 bytes .../17d0aece97973ab23a467486b177ea9722e1b90b | 1 + .../17e80cf8c247d17acad56c88750da34893fcb4fd | Bin 0 -> 19 bytes .../18040e106688eb1d54323927a403439ecfde6626 | 1 + .../1813261b3141faa01431c82e06292485516d3327 | 1 + .../184ee2343d4b9f62c69383692829fd852ad8855d | 1 + .../186035a2f5c09ccd6b7d15de46f8561c98d8897d | 1 + .../186292900ad9d43881b71765158c32d78d80c8f3 | Bin 0 -> 84 bytes .../18fd762b91406d37b85a7b342a91434a15f17290 | Bin 0 -> 25 bytes .../19334eee05eeb18c549498e7ca2e792a2a4e04f3 | 1 + .../1a9a5df35779dd6e9e1de171b9bb0d316d2b64a5 | 1 + .../1ab3d30f60743c2a1d3043773aae3a04f83c07c0 | 1 + .../1ad84ed46f3fda305150bac93958a5a390e23a67 | 1 + .../1b40a997150aa03c23ecc6efe445a2d7c3dd8368 | Bin 0 -> 17 bytes .../1b6b3bab9032cd420f350b6bb252942484f6b527 | 1 + .../1b7765cb05c94581461ffcd38d38b7b272d8e9d6 | 1 + .../1b89e3fe0cbd4c2291a74bf21969a9d9d851cbf1 | 1 + .../1b95d4da08fe949a60f63cd59213d42d30dbd381 | 1 + .../1bac1200e05bb3269d019903241791c917a12bd0 | Bin 0 -> 53 bytes .../1bb8f5f81b7f6c1d58e1f7bb13fa561fe1a146bc | 1 + .../1c56068c6dd17e9a824db6da78d64f933267a8c0 | 1 + .../1c8db8b9d88dd3483b6f81e4224c4f985046e6ac | Bin 0 -> 85 bytes .../1d29a77924602a79c0f546535a885f59cbbbc405 | 1 + .../1d75a65733da627e5c401625bff522eadf098315 | 1 + .../1df9507cc2a54a369646d6d34d846d3fcc172479 | Bin 0 -> 45874 bytes .../1e7a571be5aa542c3dfec1223d2e089a3075ce1f | 1 + .../20a6b040258fbfa09bb37c6fc07106b2e43bada7 | 1 + .../20e8ca854d3c0c375dc943142a04ee2260f0d1fd | Bin 0 -> 26 bytes .../212ee7d21a8cb25249644cab4f959db111f3a3c0 | Bin 0 -> 106 bytes .../224ce7b415f7950118880fce0594734aef489fec | Bin 0 -> 45 bytes .../226e70c1beddace83862791a6555c80475640bd0 | Bin 0 -> 88 bytes .../233d53e3eb21b6ea6feebd6e59e3ef888a840f49 | Bin 0 -> 21 bytes .../237f7aee90f206d3a6b138cc908b8921b544e190 | Bin 0 -> 15 bytes .../23e1ac0f77f1283cf6e9fa044df3ec51bff27fd4 | 1 + .../23fcee6c71a8f5a22447df688724c0250fb77a70 | Bin 0 -> 20 bytes .../2459eb855d8c6ebac13cb74d996565d78130f4dd | 1 + .../259188e5fb0c09046df96f6d565c59e0d146f198 | 1 + .../26f07f8e28e7521ed282fe5c3938c1ae225816b9 | 1 + .../276425d65d58453d03a3444c9f6662d08d449af7 | 1 + .../27d62874ec87a2552e7c842da65de113aa69f7aa | Bin 0 -> 50585 bytes .../281b259280ada5d07b07a22cbe9a78c7b0fba94b | 1 + .../299ed12b98673c6c4adbcf886cb70db8adde6aa1 | 1 + .../2a40feb7480d0b31c36d5626761e948d0ae52792 | Bin 0 -> 179 bytes .../2a6d7e2b829ed28307b551eda0d96f1834bff899 | 1 + .../2a8dda90aa286175b5c683b57fae1dc7e6ac1e7e | Bin 0 -> 31596 bytes .../2a9588e6fab82016b545462cff2ed014c0345551 | 1 + .../2b1d57a8e8fa7164c9ba00957c9486010754560c | 1 + .../2b475d1a8f2fe4fbba92e1424f0ea96d95b12fb7 | 1 + .../2bb80c6d0e2eadd73018eb2a8cae37d41602ee79 | Bin 0 -> 18 bytes .../2c1389f882e256e260e37e8a67af7e32d0f4e0fc | 1 + .../2c1f94c76e4eec607cce5311323620f349912e73 | 1 + .../2c962c7fbceaacf8247ac9b70c8eeb1f814e435b | 1 + .../2c9aaabacf3a3b48dec4a85767cc5d38a1736aff | 1 + .../2cf086299f983d0afc7f95c5e0c86b657448af33 | Bin 0 -> 40 bytes .../2d0d96b95e9a3316d1ff0ef019ada509bbde4c9d | Bin 0 -> 41 bytes .../2d414d5dfd20055393df3208009840da9cfabca9 | Bin 0 -> 24 bytes .../2df967edcc00ac5c8e00037f1a43680600af5cba | Bin 0 -> 166 bytes .../2e6b09f1eca05ce2bba920fb78f9bca2a883fda0 | 1 + .../2e8bde3549723e13849b604f4deedd51c71dfef5 | Bin 0 -> 113 bytes .../2f6e92a71918d01c16762d5ca59b328f1341d326 | 1 + .../306e2cf9aebe012cb0769b1b2a6ea68af2e8ed44 | 1 + .../30f7348d35de0c47d2044736cb115972b800569d | Bin 0 -> 173 bytes .../310798b8d94a3a2fe649a4c871458b4d74fdcf32 | 1 + .../31ea606a9859bb29d7f98162d254348021e0d932 | 1 + .../3230fc31ab8d408c484aa28bbe36431f71101243 | Bin 0 -> 59 bytes .../328b7c92996e480d1f11efe847c3be4b5dc0f8eb | Bin 0 -> 207 bytes .../32e58bb3d00863115f33707e0c8af722036676fd | Bin 0 -> 50 bytes .../32fc8547b6145c502d98e5e0c296ebd05badfdb9 | 1 + .../334cddba889265a1263feb77a2afdc454ca54f5e | 1 + .../33c9f9990890d2baf0c30d74b34b486082782413 | Bin 0 -> 150 bytes .../341f3e8689e57eeab4234fcdd6d2b8f800b9ab34 | 1 + .../3457e356eac79bf1c30b59940532d360dba2c1a9 | 1 + .../351185a29bdbc7bf0d584479001fb47c32c61e5b | 1 + .../35ae0d43c6bfc5f4b45f16877157832fddafce77 | Bin 0 -> 145 bytes .../35b2f81c573b15304cb9b13f00008b460da78942 | 1 + .../35d9256a4d7ef3ee54384616178af3a8092d0981 | 1 + .../36352efd680a747f0f2c93d760559d69399a4102 | 1 + .../3685ffbcfe28b166ced0a783b012a446f9cfea46 | 1 + .../36def8b2661197d594756f116a750822ae1d65d5 | 1 + .../371950308853fb3b1b3a940c0598bfdfeb1d0f9c | 1 + .../3765e9174971dbac9cfeb7e485ca61dc4941f7a9 | 1 + .../37cae036ce7ff5d0671f32d3757579f248609c9c | 1 + .../38460ea06ee847ff3d5733415c299b44b0866e58 | 1 + .../3865de2fec3f353513d86b28e43bf936d4707f97 | 1 + .../3867b18060c307e0d04e0098f195d699e4b3294f | 1 + .../391920720c3609b7c1f7b51162ce3ec99329a0b4 | Bin 0 -> 148 bytes .../395027454c7d5babff9544414c04a39d712eed1c | 1 + .../3a258c9ed48e634ed8d495f07caf21dbc6978205 | Bin 0 -> 19 bytes .../3a397485197e6b99c77158132eaf211389615fe5 | 1 + .../3a66b34706b686fe0fa8ca8f10c829758b2ebb07 | 1 + .../3ba666ad891fa8009dfc961aa3c215bbfd2d81d2 | Bin 0 -> 16 bytes .../3ba9769c7c23cc3c80542ad123a371cf4f69e858 | Bin 0 -> 41 bytes .../3beb4432302433e303a9ccaf34637dd3a910028e | Bin 0 -> 157 bytes .../3c6cdebc450d20a58150a9c71335b45a6a5ee0e5 | 1 + .../3c867362eb6a20f573a890db87749fce3c719d14 | Bin 0 -> 44 bytes .../3cd69ddcd34bd874e3abade696f95c949c17198d | 1 + .../3de495ebac4b80064b79844f3a9453270caec251 | Bin 0 -> 11 bytes .../3e04542bcc462ded0025b53371f709d8fcd25c21 | Bin 0 -> 17 bytes .../3e94196e55a9214083bda470772d71aedc17eb5f | Bin 0 -> 60 bytes .../3ec4ac00bb967a335b5caae0ae51488f2fda6583 | Bin 0 -> 9 bytes .../3eec38c0ac3a96856d3210a0357857e70b07a9cb | 1 + .../3ef82ba46238fa46b89148a230d23e741ba5f1da | 1 + .../3fbd43f81888ca60e5bfe49b47e23cb115d5cd7d | 1 + .../3fe56e5b06653e8a37c99da190ff8d44d14bfc06 | 1 + .../3febffec3be8b1f4bcb76a01c1d713600d8e4adb | 1 + .../43380e5a9dd29ca847e9b5bfba12a0a95d94d0da | Bin 0 -> 30 bytes .../435d3ff6ddc43ac582e740ead969397fd2bfc7f5 | Bin 0 -> 24 bytes .../448120a37132488f48ab6c46a146c995948958cc | 1 + .../46db501d3e9a8808118333740a22bf9c808af239 | Bin 0 -> 13 bytes .../47a2ca2b47aa1993cb655b77a0273ff139dd2280 | Bin 0 -> 42 bytes .../490576c8f6c235e1ed921c4932bac028070c806a | 1 + .../4930b86bf0dfb02836b0319d21aa4a63a8dd894d | 1 + .../49667ff0b09901223117e74d2506bc37a5ae1580 | 1 + .../49906adf7313330e29be20d51fb36e9bdf1939c3 | Bin 0 -> 41 bytes .../49c0e3eed81c1827e755c70c9c6d9606e7f54b22 | Bin 0 -> 26 bytes .../4a3e77f5bc061fc8121ef7ebd2b8e3ebb0bdd609 | 1 + .../4a454e01ef09b175f3864b654cfc1104a850e871 | Bin 0 -> 192 bytes .../4acd9df9a7ee34e48c6c80777159b089f417a69a | Bin 0 -> 17 bytes .../4af5564129d7b4c9c4736a3e511926c29348a4a4 | 1 + .../4b3957bcbe5975399e34485cf0e5229169ab67b9 | 1 + .../4c9e40aa8a439a500f073cf5a1e060a16ad12a6f | Bin 0 -> 80 bytes .../4cb5ff0e26f5bd0bb1fca846b4843db69095f18b | 1 + .../4ce80549344cf5b0432e39e7b5382407f0763e8c | 1 + .../4d10ae0032f04fa01148a54fae6838c44e4bb5bd | 1 + .../4db2ded939a16fb1907dc2bad7acf67a64e38c0c | 1 + .../4dc3a9a9477fb452ba7a4026bbc01d4cdd3d4990 | Bin 0 -> 18 bytes .../4e1fb006a2268fa5fc70ca6b241bbc71faa6d7ff | 1 + .../4e871bbc5089f9550533641ddf6850f8722dfc36 | Bin 0 -> 86 bytes .../4eeb4507868732d3705d1ab10f6a31f70f579556 | 1 + .../4f21f3584c99e97453ebe513ed44c86530fbebba | 1 + .../5103160695cd56d281faa45d9267013488cc7bde | 1 + .../521103054e395afd0f9612b47c71cfa0e62151b9 | Bin 0 -> 163 bytes .../523511e33c8788ebb1b703d7c5ad12b5da03964e | Bin 0 -> 28 bytes .../525aedb7462311393cd01939e4734d01db48535b | 1 + .../53809012a8d5cfee839c3d0961b7fdc9468329a7 | 1 + .../53c01d820d125e57b28410ca5b3a9c72c18d4102 | 1 + .../53c27071f53df6b8d2e5c419edf95b7792b278e7 | 1 + .../53d5307f2e7852b183839fe93c20647004d151ec | Bin 0 -> 114 bytes .../5466c59c9c0eff34800e19cc92faf07469865ea9 | 1 + .../54bf8d6f1f846243b6f4de81605779b0acb78406 | 1 + .../54fd1711209fb1c0781092374132c66e79e2241b | 1 + .../5506797514c0b214f5faa9764a9e149cd1df9d28 | 1 + .../5566c6d599866f5e523c54e6575d7b9e557a2539 | Bin 0 -> 53 bytes .../55c3d55780427eb00bde252348db1c2ede22526c | 1 + .../5600abaefe804c50a353bbfdd0f9e10ffc102f81 | 1 + .../5611ddd40488973ee6c64c45ad57d6eefe6da100 | 1 + .../56b0ddd4e44492b45ebb7e9438b0a5556c244bfb | 1 + .../5729395b452092152b06fbb8976e9bf1fda06439 | Bin 0 -> 207 bytes .../57cce6fac7c0992eb338e0e538d3e0ee3fe7f1cd | 1 + .../5822672f068da89546838004ae59552e12e132f7 | Bin 0 -> 20 bytes .../59ce13e9a22e88dac879841256ff66f02cd2f8cc | 1 + .../5a020527fc80b0de64e279fc44635687e014a9cf | Bin 0 -> 122 bytes .../5a645b30948850f8efb7bdd11944bdf3112c7bfe | 1 + .../5b2f727461cde3c02ffdfb300adb0b4a09734911 | 1 + .../5b5531d859ab5bbb034345bbaa2852e9d8a1e1ef | 1 + .../5b980cb94e35769ec92a140ca00d34977e7da5ec | Bin 0 -> 10 bytes .../5bfec2fe795176646fa0babecf97c04045bd4814 | Bin 0 -> 348 bytes .../5c28d94242e9a5ba7e7324e59e5dd12f3e31e51c | Bin 0 -> 63 bytes .../5c67728ab04ba3dfe4b2636cf609f9617c4a5974 | 1 + .../5cd36053e269c97f5359b0a075d80bf1e23b5f47 | 1 + .../5d6f99c2afbf68a1b15a17e07945c11db8579e5f | Bin 0 -> 88 bytes .../5dd149b1b8509f456c16c4ac6dac3c7d6aa217ed | Bin 0 -> 99 bytes .../5e84011c6b092e9f0d6ba60f12fc0fae95a622ca | Bin 0 -> 56 bytes .../5f56766993de2b8c2db87116090af546e1add3b0 | 1 + .../60571c8be9bee3b13428613df66ff36cf54309ec | 1 + .../60ac245f262ee1ff177d351a4fe246060fb4538b | Bin 0 -> 86 bytes .../6120aafb0db1715400e47d0a61fb9f6d8665a736 | 1 + .../61953a8f7a9f0613020d297a6d350abeeaf6e58c | 1 + .../61c0a7fef46c0c1b081b1c2b40835805e5989b54 | 1 + .../620d85870206e88db0bc3ee978c24c0bbc1821ba | Bin 0 -> 60 bytes .../621131d8e28a67a6b4fb03c611f4375873d69678 | 1 + .../624cbd42e3ebf35a3a0ba55b3b4969b99054f84d | Bin 0 -> 348 bytes .../6290103200631075988aa3566d8d8922599f8b6d | 1 + .../62a329f65143f1571dc4a2fbb256800c8ce4d89f | Bin 0 -> 52 bytes .../62f8a53cfb4dd625dcb31ee2ba258db3916a3bd0 | 1 + .../6318dc574b3ad7291fee7e01bfeaa674eb0feb48 | 1 + .../6457a079debb9b532255e3e6ddf276b29808b49d | Bin 0 -> 3 bytes .../646061a4526506a0b3c62629f4f77b6072c8f9c3 | 1 + .../6538d0517865c8832ee9bec87609b7bd4facb025 | 1 + .../653de28e2f085334e30ec1c964540b432a3cfbc6 | 1 + .../657c9c42bf3e1c79a6dcb9071bf123367dc37734 | Bin 0 -> 40 bytes .../65ebba0df08a5484686905e99631a4ead845c08b | 1 + .../66dd27c22fe09842ac4c4f51fdef4bea500b6a70 | 1 + .../66e25df2bd43c840337a49e0cb5b386e9d290d80 | Bin 0 -> 59 bytes .../6714df1ac1c54d4c30add212454e9513c6880767 | 1 + .../68a6f9c2f62e9f57667de9563b5d612ec2f2c829 | Bin 0 -> 37 bytes .../69089b76cc62fe00615702bdd1f339e259194d42 | 1 + .../69d8622d24c7315f344c424297986170f7ef2243 | 1 + .../6a700d958cb3a7565d5a6b74540486874d52b4c7 | 1 + .../6a852150c5c2a12d325e78056687a8ea97b25fe7 | 1 + .../6ac8c9f754d5ff3c0cd3f3f585de99dd73944bbe | Bin 0 -> 46 bytes .../6ace27a48f12990e2169cc099007aa5fa11962d2 | 1 + .../6b10397dc3bff2ccf57be4fe3ff9d8d1acb8c76e | Bin 0 -> 156 bytes .../6c3f1d96c06b853551f685dde821142928f55acf | Bin 0 -> 25 bytes .../6c8de4d4bbb5294154c3b140d43836757b9f08ac | 1 + .../6cf122ee328af5cf6f18d5f9546b0fe761951b8d | Bin 0 -> 128 bytes .../6cfbbc25caa217e22fd7ca3e92d2051c5f661bdb | 1 + .../6d703bc900e74d1a576bebcccb63e3b2701ae86d | 1 + .../6e1afa81dfcd6833705d84b45881f085c19b2211 | 1 + .../6e244ec3b81e0d2f8b5810854859e1b8140422d9 | 1 + .../6e323b4c733df90d55c754e099fb5a13bd9701cb | 1 + .../6e36720f3f55dc0cd5d585c6770572964719b5b8 | Bin 0 -> 56 bytes .../6e8384ab115ac92bb7787121cfb03582e2d72585 | 1 + .../6ebf8b74dca1657f5f7ffadeccd629984473dcb4 | Bin 0 -> 19 bytes .../6ec18dcca0d820fff04c03c3a1bce37dd8ada41d | 1 + .../6eceebb8f46545393d217ab0111ff7dd29bc99f7 | 1 + .../6ee071f654fd6b7b25725a8bdd880bb6836e8c81 | Bin 0 -> 80 bytes .../6f1088b3d1f4cdfbc37d9b8cfc1ff4c3bde2f205 | 1 + .../6f79e36e880664c9b3b610140d2485fb6a6e5039 | 1 + .../6fbea59066b2b97fa45259aa87c40c4310070019 | 1 + .../7056b9c294f0cff7b4ace611a19363b2fc096bb3 | 1 + .../7073dca40b911fb22f738c99aff43a10ae6c4db5 | Bin 0 -> 22 bytes .../7164d8ca3f4c3d621cc5eed7b59e3495e7b017a0 | Bin 0 -> 18 bytes .../71e3e01b3cfcd4228402cbf19b5282c965f042df | 1 + .../720d36ea7f9616b1a1d47fa5b746e8657b5f973f | Bin 0 -> 188 bytes .../72ccbcbc53c7fa54247a81bd1d0e62dd0779494b | 1 + .../736c0460f94c089c9e270857a4222a21ffd9e13f | Bin 0 -> 5 bytes .../745e32f46833c075c8e68552e0fbbcdd5ec52641 | Bin 0 -> 15 bytes .../74aa7256477af2c0e2511df16376dee323f3ccb6 | Bin 0 -> 69 bytes .../7528086eea404dccc3ee5b214befbc2a95676feb | 1 + .../7528f9f08f80c8e85951aa1db2dc616e992c1a62 | Bin 0 -> 26 bytes .../75af94f9695c375e5eb620849f0213ad41e8f205 | Bin 0 -> 39 bytes .../75e1ff2e016b7b80ecb7a6a1f4c3238a2a2ed130 | Bin 0 -> 18 bytes .../76129c3bdedafb3bda93f53266bd17bffdc30682 | Bin 0 -> 90 bytes .../7670db89e0c3fe492c91458de219c3be34a3dcb7 | Bin 0 -> 118 bytes .../7673d7fdf630637c6ae2b96694a047044c1a9cb0 | Bin 0 -> 20 bytes .../76903905e654ddc59acf57380bf3dc1ea136bad9 | 1 + .../76d36e3cf3d31e0f76af08bd7ba5571f679057a1 | 1 + .../775ce646f7dbf50199b8e8df85c9441b8a0a5447 | Bin 0 -> 59 bytes .../7770a6fd33ce821e60c78b0cb67a7e8c8bb74269 | 1 + .../781b24e3433ba400df3ccddf56cb8fc7b1fd52ce | 1 + .../784db9d87b31ffb040ad212e4018b30c3535ad18 | Bin 0 -> 121 bytes .../7880e2ae55d255065ad415c4c625e1b63bfd2a93 | 1 + .../78cb3d726e4f9e8bc89b399fa514c3b600bf8e5a | Bin 0 -> 185 bytes .../79ba60931988a5974328a73fe091bdf6f5992891 | Bin 0 -> 24 bytes .../79cdb8dec1ad07b389f544a511f89b347429837e | Bin 0 -> 52 bytes .../7ab8c6a65c39907f4afa4e2b294303aebb6b22d8 | 1 + .../7b12a398a1860ac2f3930d5020e422aec061f177 | 1 + .../7bf1682743405f3d5b3433830eadae4ea311807d | 1 + .../7c7ef0e305f37f833708b375271d59300d120d84 | 1 + .../7d06bd4a7de57953ef09c2e18ca67aa7ff367c76 | 1 + .../7d31bd53bb7eb7b1f354c5f85d376390760ab16f | Bin 0 -> 32 bytes .../7d67ef4d0c681655d4b4e93e848d56865630390b | Bin 0 -> 17 bytes .../7db9fe869081fdc855913dd000de0d493f5d5192 | Bin 0 -> 33 bytes .../7f20c1e73451c3321c30223db91b891753ef77dd | 1 + .../7fd4d999983c3fbf22763853e90ec10b03c70b17 | 1 + .../80039ce7eff40359bee041d75e371b55117aad3a | 1 + .../8104a007bc88a0a8d81ce1fd26d8b2333061e920 | 1 + .../81320c48ed6eea94712c5e8594c0799fbfe30d10 | Bin 0 -> 80 bytes .../81425ca3d0afb88cc10d412dcc9795905eacd6d9 | 1 + .../819b4bd08ae7d758990aea8ab9739f3cb97fd42c | Bin 0 -> 156 bytes .../8206d92b710c04ce0bd706cec25fbb72014c79bf | Bin 0 -> 64 bytes .../822926c528d16c0a9ca4c48a25651f6a0730d797 | 1 + .../82d80b6051f0750777bdc37319851741144b3671 | 1 + .../830c73748baaa10c2016d04b408a7f481882cd89 | Bin 0 -> 88 bytes .../8315ca46618bd0771353336407396df293e8c182 | 1 + .../831c152337e8b2993b8cddfde7553a8c9e64631f | Bin 0 -> 67 bytes .../832fad0e723027e5bd74726b1722a838183dada7 | 1 + .../83b78d2ad8afd0729de45b4d9fdd765a949d8073 | 1 + .../84a04bf3e15345f5c992bc6732a42b95857631a7 | 1 + .../84d3a72c434074ee0d810fb2357047efd23f58f6 | Bin 0 -> 126 bytes .../8659fe309298ca90f201ac7ced95b9d0de510eeb | 1 + .../866cf21064b9e20eded44e2e096fd9ce6185ce86 | Bin 0 -> 8 bytes .../8864c6c9319ef120d3accb7b70c32c8f5c1ebccb | Bin 0 -> 10 bytes .../88f01cffe9972bf447a21034c45f6b943ea3ec07 | Bin 0 -> 36 bytes .../88f15d430469d209b72cd098405efd15a6d18b05 | Bin 0 -> 120 bytes .../8936b35f29084151fa3755d81030386940523c07 | 1 + .../89a42d72ffe0b23309a9da4e15b8854c1cf939ea | 1 + .../8a0cf07d2592231bd579e4399538d9f490e3e5b9 | 1 + .../8a5d1c05f894414aa5aaa66cf901c52dfcb119a1 | 1 + .../8aa514b6e6fef8d045aa049fdb52fa8adcf722d4 | Bin 0 -> 25 bytes .../8b0dcd4400fe19cf6a268af780990f47dba08189 | 1 + .../8c4655e6528071edd445715ff1559f399dc9d47c | Bin 0 -> 176 bytes .../8c695cbcc43e37ce25194c4b61b3d87488b308a1 | 1 + .../8c9dc3c5a839f90f29256ef8c8f1369bc7383817 | 1 + .../8cdcf83eb89d7e5c87cfa1b8811c95f738747a6a | 1 + .../8d0c6e6b3d74233685c3a2c1793d1a7b0ae2e34d | Bin 0 -> 18 bytes .../8d444e8b311d158f854a03ca35d2c7865cd2c46b | 1 + .../8dc289730cc03ecbbed57d04cdca538a4fd08059 | 1 + .../8dc58cfcb396a60db921901f1b0807c4f4f37619 | 1 + .../8e05be75b7c14c463af277559dc8ea7841a733c8 | 1 + .../8e7eef00a7a6719e77544b57d1d98ff83a37e55a | 1 + .../8ed6ef9af5555ce4ff12b23139add9621e7ad930 | Bin 0 -> 92 bytes .../8f39f83b71d49805a451f81a5a451f5f3d74927d | Bin 0 -> 63 bytes .../8f403efbef079f1775b63bcf1061983e4c5d2162 | 1 + .../8f8f956eaaf8f7f5e65002be888b946218f0f7ce | 1 + .../90a03241b5cc656b7c2a4ef55664b0320fa3bd43 | 1 + .../925a91e7c25a43d9da8b63eba51ed66a52d833aa | Bin 0 -> 42 bytes .../92de5c217802ec24886240188dca293325eb17c9 | 1 + .../939da66e1fc8bb9854865066ee47b6ac4b933aa9 | Bin 0 -> 107 bytes .../93a91331e7c3c060f461ae1f22e2bbf4747ffe33 | Bin 0 -> 64 bytes .../94f6dfc499cbf2e84ffe1d69cb751ada9f2f2e3a | 1 + .../9539fef8ffb9f7c542061f1af1f3f98e9a714cc3 | Bin 0 -> 1132 bytes .../953c9a616ea56067225c88fccc6423496f2501a6 | Bin 0 -> 39 bytes .../95a60268c555a77c1010d75bb44142af47635486 | 1 + .../95e410025b965cf6ab2e8e2d3559efbe71c1a972 | Bin 0 -> 44 bytes .../9671f253d1eb7cd504a5617058ce4c01a80c897e | Bin 0 -> 15 bytes .../9698d3424f1581a6bbb66c764f14c95a74b87675 | Bin 0 -> 64 bytes .../96f7f38de1f8601311733984cf51cec35500dabe | 1 + .../970ab6333aa5ccf8c2dc14bb56814f7bb4303b94 | Bin 0 -> 53 bytes .../98978113d2116dc4bdbb10265fa32bd95230bdb6 | 1 + .../989e2872d2a34de543f23c5061db68212d8258f7 | Bin 0 -> 21 bytes .../98ca9a00ad571c4454fce709d5405e5aca2a363c | 1 + .../9965361765a4151902ec04fb21d9247b3a5ed10d | 1 + .../99d6fe94a50faa50db9d7eb38d74bc3cc8417dc1 | Bin 0 -> 39 bytes .../9a6869cec3dc84f2051bfaf0ee0d3552aa221f89 | Bin 0 -> 218 bytes .../9cc1f26de1e3a7df8c7c03b95ff73ce9709c85f1 | Bin 0 -> 18 bytes .../9cc5b552abbd551485135fa87eab739a0a784057 | Bin 0 -> 26 bytes .../9cca361865a4fbff75abdbb79c1e91706780576c | 1 + .../9d8e99f07604d6cb05ef613d41cbfb93b2aff787 | 1 + .../9ecd61eaf2681a882473247a603b9f30c2663d49 | 1 + .../9f0a3b7c0814b4f80c0745161c8769f63098981b | Bin 0 -> 19 bytes .../9f711c29ccf3f54d44000d7ef6299585674be288 | Bin 0 -> 188 bytes .../9fae60819c28d4fcc88a6a1b93dcf69b4e458203 | Bin 0 -> 6 bytes .../a02431cf7c501a5b368c91e41283419d8fa9fb03 | Bin 0 -> 2 bytes .../a0ac6c3c83817637bbbcb11a5106c57aa6654afb | Bin 0 -> 12 bytes .../a15fdfa620d19a92d9eaa9f3f13010e53f902796 | 1 + .../a18e76ae792a054c2f6d0d01e0e78d58678b35e3 | 1 + .../a19d04f18f574e561d793ac0dfcffe2b38183eb1 | 1 + .../a26fb85be3d2bb8a2360bb4d9533a1651bd12d99 | Bin 0 -> 50 bytes .../a2e860fae30005a9e75b61f54f3d019c44090fcc | 1 + .../a3071bfcb7b2fd3c4286ea42e1f7940754b55697 | Bin 0 -> 56 bytes .../a4ac408fb9d6def070ad3a76312ca092863048e5 | 1 + .../a4fafc117cbfde8c240deccc8997c7966d9109ba | 1 + .../a59ffb5f6122e45136352585d3b53294a71346d0 | 1 + .../a5b3a3c48727c26dfd625f247069d2cdbfa031f0 | Bin 0 -> 52 bytes .../a5c829fbcd9fd760bc55bc8ab6901b8d401b65f6 | 1 + .../a5fbbce038cad4f5e0c0f97fa69ebc3601123e5c | Bin 0 -> 40 bytes .../a6e57b33e7a219168280e51bc98a44de40f0f9ca | 1 + .../a78afe9e4e9f02a10ebadac64171ad49749a6965 | 1 + .../a823a019c0b19c97a1d35722cd843109ab016c17 | 22 + .../a8468104c65a6002fd3a9d4ac39f3ee34c21ce4a | 1 + .../a899424027f1d69a05384355858311a6fa3940a3 | 1 + .../a8a5de5a86a16952aacdf602120f27807294c3ec | 1 + .../aa65229e62d7cb8048d2f5911226b177e8e53cab | 1 + .../aaa96713f8ccb0bbd5c8e91715d4b86e7b338676 | Bin 0 -> 36 bytes .../aae6354e5ba12ee3ad89fabfd72f5368a639d30f | Bin 0 -> 43 bytes .../ab6f3dc497f93f251ebacc153409b1eb8e05e2db | 1 + .../abf5b18d1c1155d3e455c8b781948498f364965e | Bin 0 -> 27 bytes .../abfe5dbf594a2f22173fae7ca8de28b4e3a40099 | 1 + .../ac842f2cc55d7f193273a35f8af3521bfe2317d0 | Bin 0 -> 63 bytes .../ac923c36dd85ff5cb2a0c5b29c701999c5f2eaa0 | Bin 0 -> 40 bytes .../acc6e100b519d6408a8c6d8aed19203d874a187a | Bin 0 -> 59 bytes .../aceb250195867e7d9bbf6eb6e0055251e4bb5d67 | Bin 0 -> 86 bytes .../acf1dd17e6a2848dfe17fb0d76cef2ce7d59ebdd | 1 + .../ad94c2bda2ae4afa0f7264070fd642bf37aae596 | Bin 0 -> 91 bytes .../adad2ca7ab313add6e955f704719e03d5229e4d0 | 1 + .../adb1f8cd3b68c076d81311071aee2a1c3785c7cb | 1 + .../ade30c327d9f51daf37cb3e39fdb15125b84a257 | 1 + .../ae783661f52d36f9d6b87c8394ee6f2d61dae640 | 1 + .../ae8da06c3c69076c06108219e19c8b36dba05a5c | 1 + .../aec8a29238dba3c37f45f2e2e4e64b0e7fa60a74 | 1 + .../affba8e4061bf260b1bdf8a815675cc3b4ebefa5 | Bin 0 -> 63 bytes .../b01c4e2657a230e2b600a20da6f107b0dfe2467c | 1 + .../b0798ecae4d9e56eefb0c1d95b657865938e62d6 | Bin 0 -> 37 bytes .../b0ab4f92df810edf4371baad1d4bbd95d360c607 | 1 + .../b14e5f5f6d29b6b71e7fec03eaf7d8237c0c6b2e | Bin 0 -> 178 bytes .../b1708df7f6dee0f2feb11f8b6330be0fcffc1f58 | Bin 0 -> 7 bytes .../b1af4f4e86890fad6fac96a23956405c550778be | Bin 0 -> 39 bytes .../b1efd9e687d79e3f5a75eba02bd80bedb72350ba | Bin 0 -> 59 bytes .../b25b0fbf46cef1d888fe900445c9ab95330f44cd | 1 + .../b2786e0b9d6e7b39165eb4e87e3110362e9e6660 | 1 + .../b332536a77d6efcd379cfc2f9828291516cc1ff4 | 1 + .../b33962eca397f59591dbb9668e622cf99f2d7cda | Bin 0 -> 91 bytes .../b38b6e93da1b43441f88aa53370a9f00b35c3326 | Bin 0 -> 62 bytes .../b3e01674a1e4dd78e748782fcfc3add5523f51d8 | 1 + .../b53e7ef9aad70fcd80986696ebc586c03495b8ed | 1 + .../b540412c01f960f95edd2a1bc03f1b4447f2b4f2 | Bin 0 -> 5 bytes .../b62c2c591db32b26e997aa4ece577742db84428a | 1 + .../b62d99583f30c15c3c2dbed2f69c5e45075d7640 | 1 + .../b62f98976c11d79674b019ea78a7ce4d6d78b479 | Bin 0 -> 2 bytes .../b6787dabeb5cd64ac85f1ec5a7cbeecfd48b2c5f | 1 + .../b82ebcf4d09ba28d835cb9667da603e46e3438eb | 1 + .../b85c6bc2473aa12e22f91db3546dfa9d85d8b3d1 | Bin 0 -> 27 bytes .../b86e98660980680890bcbf02cacdf568d530fa64 | Bin 0 -> 48 bytes .../b8d9beea35762009941189674c2cfcd14f81254a | 1 + .../ba517141fc9a468142a8d03d4ee395b4d4f30edc | 1 + .../bb0a596017cfc2185505d28065ab3cd238d0e2ea | 1 + .../bb6232815b373e441e2acfcca015f75c680eafac | Bin 0 -> 39 bytes .../bbdf4fa36ba9d645399f72c74033fd9c2631aebb | 1 + .../bc53c9f93974b4f13382c1d49b1e3ec374005ee2 | 1 + .../bcacc9bcd3b9cc7dbda9c52c6e4a06a756a9de90 | Bin 0 -> 45 bytes .../bcc55f432bcf39c6ffc6d7e950612b35e9ea2ec8 | Bin 0 -> 42 bytes .../bdb1cc868d6ced390f5d35c5f43da9f464fac464 | 1 + .../bf67bb08e0abde692748031c297bc1c7910542f4 | Bin 0 -> 34 bytes .../bfc07c62ef2770d53d9188b260f531a8128a5c5e | Bin 0 -> 53 bytes .../bfed121df31ff73b770ed7f27fc7f4da2fa73e0c | Bin 0 -> 24 bytes .../c01816206d93691165e6e3a1924cf9fbc9cf39bd | 1 + .../c02be525edae59ad9d0d9dcbd790629dc9aaee9b | Bin 0 -> 130 bytes .../c094ce0c13ee9a4ca37817d9f7dddc11b2d60177 | 1 + .../c0aab5486ad2e80bcc12fca9fb6653984aa68274 | 1 + .../c13ca850db259c032cf24cdf6f2833c9d74529d0 | 1 + .../c1a8da6cdc8988e6a69961413803acbd1ee935e0 | 1 + .../c25abc82a0470129f2d098ac65fabf34c4e11188 | Bin 0 -> 61 bytes .../c2787d2cf1d95cbcd8b9bcd15d50f67f7e92ad9f | Bin 0 -> 53 bytes .../c2895ff545ebdc4140951c0ca956524d7a364b77 | 1 + .../c2cc55849ff4858bf80f1a4713187618d14a496d | Bin 0 -> 35277 bytes .../c2d63b1d75cf53ee3b955bb143036ca93ef3a256 | Bin 0 -> 105 bytes .../c372a27f78a62ebc013958fa4953a8bc792db53c | 1 + .../c392963b395a7f92b3bef63fd34bc31ecd3029f3 | Bin 0 -> 108 bytes .../c3b2749ab6c4d303bfd5da9ea9c8807e9f92d259 | Bin 0 -> 25 bytes .../c4dd3c8cdd8d7c95603dd67f1cd873d5f9148b29 | 1 + .../c52fa8d16e520980e470d76e3fb4f6a612f0f3cf | Bin 0 -> 28 bytes .../c56fb214efcd707e6fa68b803d9e7686fc2f4336 | 1 + .../c5902afe54998ebc5d8d59043227d379a8c2eee0 | Bin 0 -> 16 bytes .../c6a16abeea323833079e97b1830610aa6c6eba91 | 1 + .../c6fcfd2a1f91a7f6a8c124f2637e60645be01006 | Bin 0 -> 25 bytes .../c75b02a90370df1f54de2f63a4da8db22f2cf719 | 1 + .../c77cb763d73db279aebfb42d2b5dca3d705d3df7 | Bin 0 -> 91 bytes .../c864ac8bce0353ecc7d5cb0ce5a1e77a5239201e | 1 + .../c877c08a79a7408aed779d4a430c5db1bce26314 | 1 + .../c8aff6e2e2dfb18be385483b871ac86ff6eac63f | Bin 0 -> 200 bytes .../c8c1c1f970bc809a75ad076bdb06275b6f72d078 | 1 + .../c995d52934ac188971f02d5dbdb3cdd70cb03267 | 1 + .../c9e7fc3e0e1015a1c15992447f678a495c3ea5dc | 1 + .../ca5b619ce1bbe23d519f5764d53458d2b85eb9fa | Bin 0 -> 121 bytes .../cb0556c65c7381192c94324a3b1b8afb7e33fecc | Bin 0 -> 54 bytes .../cb1a093e6810c7f6c002a2a54ea390cf769c9949 | Bin 0 -> 52 bytes .../cc1487af64aeefd7080e7678a04870dc85a7928a | Bin 0 -> 25 bytes .../cc71d2c9f5eae12acee133bd9e50d84881a1bd88 | Bin 0 -> 5 bytes .../ccb8f962426683663972534c15354f70c3b34a10 | 1 + .../cd0771c4754dfcd9c89b9b8a02df96fed974850d | 1 + .../cd8899c66cbd92ee57f94e000744b32662258ba3 | Bin 0 -> 20 bytes .../cdf83138f69f0f7c66c13b56028610ac39038b3c | 1 + .../ce0138cd7718397b365d4c15b0b14f666c307419 | 1 + .../cec5c7e50ef1d865c879563d1a6d677adb86695c | 1 + .../ceff1dfaf2de4e33d2e3c20aeb7ba4b98f97784c | 1 + .../cf04b5d28cea1971478806979b256a030a671541 | 1 + .../cf1cf6ad5b3554c3ffc86a859319445158665ea7 | 1 + .../cf2338960588d4a5f02a47f3ee96a556224aad75 | Bin 0 -> 199 bytes .../cfb09018afa0eb1a829e556d9f8bceff40cb36bc | 1 + .../cfbe3d66d7eb3199440e8e911a93044cc3165c6c | Bin 0 -> 227 bytes .../d0a91f7984904976de592cb68e8832853fbec9bc | Bin 0 -> 20 bytes .../d22f52563c16725ce4a924dc05d6ac7d64798c43 | Bin 0 -> 154 bytes .../d2709a1c9d96e72cb844eedca8bfe8440cdb0ef8 | 1 + .../d2e385f61ad4b5d45a10a5a6cb85d72e84dea54b | 1 + .../d2facb213561b30a5bcd012e4e01d0d5b0b26957 | 1 + .../d300e5e46a825e5892fae1ba3c466c836f9e1da2 | Bin 0 -> 63 bytes .../d302c69c881e230e6433283007d318ba437025c3 | 1 + .../d30c1b65bc141d1a15d4ed622ba182c96dacdf92 | Bin 0 -> 22 bytes .../d3adf09fe6fb1534157c1dc68eb61365b46b1c35 | 1 + .../d3c74d3a64dba86f98a31bb726587ec97a7e4531 | 1 + .../d3c9846ab319f12fc646c23a532c780daa9e993f | 1 + .../d3f03301b52cf4a830c7dd200ed8ccbc09e6ec94 | Bin 0 -> 18 bytes .../d4800745440dace38766db3520ffe7baa0bd78f2 | Bin 0 -> 4 bytes .../d4d6271bba704ba08c2678eb8b1bc4e457144f50 | Bin 0 -> 20 bytes .../d54020f766061e80f445690c2b5694ed8af21e9d | 1 + .../d588515f125ebe968fe6a81cb6df7cfc41969e68 | 1 + .../d5ee5e4dc8fad9f1da43102d8322beb005a047c0 | 1 + .../d62459bb217d3050bcd9e29a6327cf81f5ed68b9 | 1 + .../d6acbf1cb46845618ed0d5a322c8bd4879d16422 | 1 + .../d6fc8cdbb0f1517159531098e900e2dcc91edba0 | 1 + .../d7a9bbd9875a60edf9c528b298ea72a2fad7d3d7 | Bin 0 -> 59 bytes .../d82e70046a544e95e81f6271dd2695f2599f0574 | 1 + .../d84bc8dca7c8fcd227254c06f5b88eaaf7cd5fde | 1 + .../d8bcb7dd21205e7126e700323b1d58817e9d9a6d | Bin 0 -> 39 bytes .../d8e2628b4092b9bbab4f02041647f950503eeb48 | Bin 0 -> 33 bytes .../d8ee4c2b79863a237c432efe7d43d99c8d0afb9b | Bin 0 -> 53 bytes .../d8ee9724bf16ff336387723dcf27319c3be72e01 | 1 + .../d9379969bc956ad623e4bab8bbe47270a880a62d | 1 + .../d9ccb79b0e070dcc2f5ed8e15d99bc5ef86ecff7 | 1 + .../db83586ca6266e03067e9a9772ea728ab770ad9a | Bin 0 -> 25 bytes .../dcc63c06ed2790d1380bdb9281fe8677f439c76d | Bin 0 -> 53 bytes .../dd509e9a4660ec34b8f9dc23441c6df4ff97c34d | 1 + .../dd72f8ff3a067dc7871438b6023e1ed0a4c5787b | 1 + .../dd75880c5ad488885260f4031a763c86a8084406 | Bin 0 -> 111 bytes .../dd962d74d04aa4aed270fd8e6b0ae9c4ac35fd19 | 1 + .../dd9aa9a49dd790b2ce99c5af1933232d46a7f80d | 1 + .../ddad7630818a1caa8054d2d7280a1d01bdb33ca3 | Bin 0 -> 18 bytes .../ddb2eaf33960ce69d579a551a7b05733adcd52aa | Bin 0 -> 41 bytes .../de0d98cb997c0a0f7be127a46d8a24d8a003931d | Bin 0 -> 47 bytes .../de0ff884898c83fb880498f1b8328f78701e6534 | 1 + .../de9e0ed8ae29220e5b65e5b97eb3b254ccbe7e0c | Bin 0 -> 45106 bytes .../dec5e8ffb35aa707d127a11a49e47cb59954e969 | 1 + .../df1b205e339e7199b5094fcf0ec3b8a8ca7a692c | Bin 0 -> 53 bytes .../df254daba2299f3ff2367e284efc53a3296d136b | 1 + .../dfdcde31231b8b3d3fd4e0ef8ea88885f488c4db | Bin 0 -> 64 bytes .../e01962d7dc1b94e5c4424ec7adae16a7c03b9773 | 1 + .../e058723f2964bf1405ae043ddb99efb17d821e15 | 1 + .../e09e306ef596dcd0e44bd3ef3c5f7019c7343f84 | 1 + .../e154eb76d096c1e545dcad591a58a02c37cd71ff | 1 + .../e198d2e4f2f3528c2ff46d769a9281ebb3cfb44a | 1 + .../e2068af1e903f4a81cd6fa5f4022e62070c259ec | Bin 0 -> 39 bytes .../e2890330b5655cb277a581b8dd2eeba0d9061ba5 | 1 + .../e2e0767d055a7042c24a7acd5d5b6b7c093ad065 | 1 + .../e394a8e21e2c43c42135daa034ad5aabb06acffb | Bin 0 -> 145 bytes .../e3b200e97ec226a197e91f12103aaa53d5c37b0e | 1 + .../e45b077cda64c380ae1b0910bc81d010c27e1c93 | 1 + .../e45e71ca01ebe01b01b0ca99b8f20a756c36b967 | 1 + .../e4ff4bd938c3737a02862fc0656a1859d384d066 | 1 + .../e5a1acb7e6f71bc1a2fedaa6173764dfd04c844c | 1 + .../e6081eeff4ddb76e88e87ef9e4b5f199f5f11c28 | Bin 0 -> 63 bytes .../e73c42dd266c4d9671da0c7af09e98c02fc052fd | Bin 0 -> 86 bytes .../e7b4559a77df21b73285243a8350b844775bb380 | Bin 0 -> 21 bytes .../e7dd34a80646a8c38ae1ec3a27c1358bab13b360 | 1 + .../e80b344f943ff0ad9219277c4578d3b4100b71dc | 1 + .../e86e26200290d9d428c5e98e191ec874eb918fb6 | 1 + .../e873a3b8f5c3b716e6446df34279b837cf8d2c30 | Bin 0 -> 53 bytes .../e8fb71319db98d8e8cd131a4eb82879bfbec14d0 | Bin 0 -> 123 bytes .../e93c3f14614595a2675993438b4c1bfaafdc02d0 | 1 + .../e93faef2d77b7c467ae280ba433928d66d63ea63 | 1 + .../e95401b11d974ba63270668d3c32c29e95ae85da | 1 + .../e9723cebe688912f684bcd19b48e5bc8efafaf2a | 1 + .../e9a4389895c006d4b912e8ac1169229e6b2a66da | Bin 0 -> 56 bytes .../e9dc3d10b47ea580404c8e80b844a9978fcf4747 | 1 + .../e9e2ac24e5674e7ee424e2b270e5abe84f1a15c9 | 1 + .../ea2b4df24b526aad253ab175334cb934b9d80b83 | Bin 0 -> 30 bytes .../ea681d11486feeab2f080b06ddd2533575b7ace4 | 1 + .../ebb747925360528a9f366f9a57730883c636b2c7 | Bin 0 -> 56 bytes .../ec39abab70a8e1ff072eb082caa6ca77b1ae8087 | Bin 0 -> 138 bytes .../ecdd96d6cab2dc714a0b0ada1c4fcb18c75298e4 | 1 + .../ed008faa6c9001951f50588a1597e03931501343 | 1 + .../ed12e272a27a2fbdbfe5a6e78e49ed722ebd3c69 | 1 + .../ed799ba0608690ac68dd85c588004197b86e02bf | 1 + .../edd982c5bd3030bde8c044760e9a678c2a9306f4 | 1 + .../ee82c97e35ec92ec3b0bbf911904a050b3aca633 | Bin 0 -> 192 bytes .../ef11f14feee00e3c198015e6bc76688e970e2d8c | 1 + .../efa12e91e0d63c2353c120ca1ded7b36afbf57eb | Bin 0 -> 59 bytes .../efa14cba9bbaf749067b7bb8515a5d8a289fa389 | 1 + .../eff9bf13cd33ea50a8eacc5f2839cc4b5d67b2de | 1 + .../f0143756917f0b2e374bd03e731cd64168180054 | 1 + .../f0b9dcd4e1845f774bb0f42653b11040baee0765 | Bin 0 -> 118 bytes .../f1a41ceb420b6b3df50b10f27108c133d4d07508 | Bin 0 -> 24 bytes .../f39378fd978c6cdb4a8d08cdffc9034e2ca5b687 | 1 + .../f39707a104112d13d9d6bcfbef0efe8dfd98270c | 1 + .../f4341645a7d466113655d63c5aa00855904166c5 | 1 + .../f5511a42d83f94619ff8ca6c940cacc32bdc4834 | Bin 0 -> 192 bytes .../f57a39fa918249e6941b4e770e15a8131ac16ea5 | 1 + .../f5ac272a1dbf362e265210869aaf70ca410f1703 | 1 + .../f64c5ca1de57bcf323741f56754f53c642be8ab0 | 1 + .../f64dcdee393b4b0a3343f8e684c9db91a6eaeb6e | Bin 0 -> 53 bytes .../f69d2954da077043c6ae085e2771a702689314d5 | Bin 0 -> 82 bytes .../f74e2203adb9c94ba80f7cc3214e3b3040e5675d | Bin 0 -> 90 bytes .../f7906e7dc01323b9d3d6e298e8dc2386c8d152f6 | 1 + .../f825cac511f3dc38a5ecf3aab3691b4c49ca8f0e | Bin 0 -> 59 bytes .../f84c3f9e305172f2ae15dff0b4d955324b3a398e | Bin 0 -> 19 bytes .../f87cfad97831c33610fbbe34a04369bd93862464 | Bin 0 -> 72 bytes .../f8ebd7703fd3ba1a135b243fd947dbd61907d0f4 | 1 + .../f926f6b2337650f8b518422c2f63a8869f47c742 | 1 + .../f939897b9fbe865c96020927dc81de9dc255d385 | 1 + .../f985f995ca7d1b691e6ae3b3ef57e8b3c7aa7129 | 1 + .../fa79e8ad34cabea4d3c434cc02ea1499069fcafc | Bin 0 -> 47004 bytes .../fa7a8dfdd46845ab0fd9b5b7004e37d0232941bf | 1 + .../fcb3054fde86111e2c346aa71af2456a1705902c | 1 + .../fce30dcdf2f23b14c580c282a39e065d7aacbfe8 | 1 + .../fd15b8dd6c27bc65f90e1c988e0245b1ad7d51c5 | 1 + .../fd608012362d161cc7f3d30e1700c51f4ccef4ac | Bin 0 -> 52 bytes .../fdce5b4f3a038ce7cfeee4deb9a4644edb78189a | 3 + .../fe203731ada762e02bf843b82e33daee4c2efbf5 | 1 + .../fe46e6ef4cd5788d89a101c77123387e3fc9d206 | Bin 0 -> 80 bytes .../fe7ac2ef276b817af3487bab5fe089186ca0484b | 1 + .../fe7ef7c0e835873b7b1cd780f248114f18adf13a | Bin 0 -> 35 bytes .../ff5274cad94d590347d6cdba7637078f82e3d44a | Bin 0 -> 107 bytes .../ffff6a92363e0e55a9688d9bc025cd8dea3b50d4 | 1 + webm_parser/fuzzing/webm.dict | 152 + webm_parser/fuzzing/webm_fuzzer.cc | 76 + webm_parser/include/webm/buffer_reader.h | 138 + webm_parser/include/webm/callback.h | 363 + webm_parser/include/webm/dom_types.h | 1781 ++++ webm_parser/include/webm/element.h | 208 + webm_parser/include/webm/file_reader.h | 90 + webm_parser/include/webm/id.h | 1085 +++ webm_parser/include/webm/istream_reader.h | 99 + webm_parser/include/webm/reader.h | 94 + webm_parser/include/webm/status.h | 161 + webm_parser/include/webm/webm_parser.h | 133 + webm_parser/src/ancestory.cc | 334 + webm_parser/src/ancestory.h | 83 + webm_parser/src/audio_parser.h | 84 + webm_parser/src/bit_utils.cc | 26 + webm_parser/src/bit_utils.h | 24 + webm_parser/src/block_additions_parser.h | 30 + webm_parser/src/block_group_parser.h | 71 + webm_parser/src/block_header_parser.cc | 76 + webm_parser/src/block_header_parser.h | 65 + webm_parser/src/block_more_parser.h | 32 + webm_parser/src/block_parser.cc | 285 + webm_parser/src/block_parser.h | 126 + webm_parser/src/bool_parser.h | 95 + webm_parser/src/buffer_reader.cc | 110 + webm_parser/src/byte_parser.h | 144 + webm_parser/src/callback.cc | 155 + webm_parser/src/chapter_atom_parser.h | 42 + webm_parser/src/chapter_display_parser.h | 34 + webm_parser/src/chapters_parser.h | 28 + webm_parser/src/cluster_parser.h | 48 + webm_parser/src/colour_parser.h | 55 + .../src/content_enc_aes_settings_parser.h | 32 + webm_parser/src/content_encoding_parser.h | 38 + webm_parser/src/content_encodings_parser.h | 30 + webm_parser/src/content_encryption_parser.h | 37 + webm_parser/src/cue_point_parser.h | 38 + webm_parser/src/cue_track_positions_parser.h | 39 + webm_parser/src/cues_parser.h | 27 + webm_parser/src/date_parser.cc | 72 + webm_parser/src/date_parser.h | 64 + webm_parser/src/ebml_parser.h | 48 + webm_parser/src/edition_entry_parser.h | 35 + webm_parser/src/element_parser.h | 102 + webm_parser/src/file_reader.cc | 122 + webm_parser/src/float_parser.cc | 77 + webm_parser/src/float_parser.h | 65 + webm_parser/src/id_element_parser.cc | 49 + webm_parser/src/id_element_parser.h | 56 + webm_parser/src/id_parser.cc | 75 + webm_parser/src/id_parser.h | 45 + webm_parser/src/info_parser.h | 44 + webm_parser/src/int_parser.h | 121 + webm_parser/src/istream_reader.cc | 133 + webm_parser/src/master_parser.cc | 298 + webm_parser/src/master_parser.h | 227 + webm_parser/src/master_value_parser.h | 533 ++ webm_parser/src/mastering_metadata_parser.h | 57 + webm_parser/src/parser.h | 34 + webm_parser/src/parser_utils.cc | 34 + webm_parser/src/parser_utils.h | 64 + webm_parser/src/projection_parser.h | 40 + webm_parser/src/recursive_parser.h | 93 + webm_parser/src/seek_head_parser.h | 27 + webm_parser/src/seek_parser.h | 37 + webm_parser/src/segment_parser.cc | 86 + webm_parser/src/segment_parser.h | 53 + webm_parser/src/simple_tag_parser.h | 37 + webm_parser/src/size_parser.cc | 56 + webm_parser/src/size_parser.h | 43 + webm_parser/src/skip_callback.h | 63 + webm_parser/src/skip_parser.cc | 59 + webm_parser/src/skip_parser.h | 35 + webm_parser/src/slices_parser.h | 30 + webm_parser/src/tag_parser.h | 37 + webm_parser/src/tags_parser.h | 27 + webm_parser/src/targets_parser.h | 35 + webm_parser/src/time_slice_parser.h | 30 + webm_parser/src/track_entry_parser.h | 65 + webm_parser/src/tracks_parser.h | 27 + webm_parser/src/unknown_parser.cc | 49 + webm_parser/src/unknown_parser.h | 38 + webm_parser/src/var_int_parser.cc | 71 + webm_parser/src/var_int_parser.h | 56 + webm_parser/src/video_parser.h | 122 + webm_parser/src/virtual_block_parser.cc | 72 + webm_parser/src/virtual_block_parser.h | 69 + webm_parser/src/void_parser.cc | 49 + webm_parser/src/void_parser.h | 41 + webm_parser/src/webm_parser.cc | 282 + webm_parser/test_utils/element_parser_test.h | 114 + webm_parser/test_utils/limited_reader.cc | 113 + webm_parser/test_utils/limited_reader.h | 115 + webm_parser/test_utils/mock_callback.h | 247 + webm_parser/test_utils/parser_test.h | 108 + webm_parser/tests/audio_parser_test.cc | 161 + webm_parser/tests/bit_utils_test.cc | 24 + .../tests/block_additions_parser_test.cc | 82 + webm_parser/tests/block_group_parser_test.cc | 234 + webm_parser/tests/block_header_parser_test.cc | 55 + webm_parser/tests/block_more_parser_test.cc | 79 + webm_parser/tests/block_parser_test.cc | 865 ++ webm_parser/tests/bool_parser_test.cc | 70 + webm_parser/tests/buffer_reader_test.cc | 165 + webm_parser/tests/byte_parser_test.cc | 117 + webm_parser/tests/callback_test.cc | 148 + webm_parser/tests/chapter_atom_parser_test.cc | 209 + .../tests/chapter_display_parser_test.cc | 117 + webm_parser/tests/chapters_parser_test.cc | 54 + webm_parser/tests/cluster_parser_test.cc | 266 + webm_parser/tests/colour_parser_test.cc | 285 + .../content_enc_aes_settings_parser_test.cc | 68 + .../tests/content_encoding_parser_test.cc | 120 + .../tests/content_encodings_parser_test.cc | 82 + .../tests/content_encryption_parser_test.cc | 107 + webm_parser/tests/cue_point_parser_test.cc | 87 + .../tests/cue_track_positions_parser_test.cc | 140 + webm_parser/tests/cues_parser_test.cc | 53 + webm_parser/tests/date_parser_test.cc | 55 + webm_parser/tests/ebml_parser_test.cc | 128 + .../tests/edition_entry_parser_test.cc | 77 + webm_parser/tests/element_test.cc | 47 + webm_parser/tests/float_parser_test.cc | 63 + webm_parser/tests/id_element_parser_test.cc | 51 + webm_parser/tests/id_parser_test.cc | 56 + webm_parser/tests/info_parser_test.cc | 105 + webm_parser/tests/int_parser_test.cc | 113 + webm_parser/tests/istream_reader_test.cc | 131 + webm_parser/tests/limited_reader_test.cc | 250 + webm_parser/tests/master_parser_test.cc | 382 + webm_parser/tests/master_value_parser_test.cc | 366 + .../tests/mastering_metadata_parser_test.cc | 208 + webm_parser/tests/parser_utils_test.cc | 100 + webm_parser/tests/projection_parser_test.cc | 144 + webm_parser/tests/recursive_parser_test.cc | 90 + webm_parser/tests/seek_head_parser_test.cc | 54 + webm_parser/tests/seek_parser_test.cc | 67 + webm_parser/tests/segment_parser_test.cc | 369 + webm_parser/tests/simple_tag_parser_test.cc | 205 + webm_parser/tests/size_parser_test.cc | 61 + webm_parser/tests/skip_parser_test.cc | 42 + webm_parser/tests/slices_parser_test.cc | 80 + webm_parser/tests/tag_parser_test.cc | 92 + webm_parser/tests/tags_parser_test.cc | 61 + webm_parser/tests/targets_parser_test.cc | 102 + webm_parser/tests/time_slice_parser_test.cc | 63 + webm_parser/tests/track_entry_parser_test.cc | 245 + webm_parser/tests/tracks_parser_test.cc | 55 + webm_parser/tests/unknown_parser_test.cc | 57 + webm_parser/tests/var_int_parser_test.cc | 53 + webm_parser/tests/video_parser_test.cc | 446 + .../tests/virtual_block_parser_test.cc | 74 + webm_parser/tests/void_parser_test.cc | 56 + webm_parser/tests/webm_parser_test.cc | 339 + webm_parser/tests/webm_parser_tests.cc | 13 + webmids.hpp | 23 + webvtt/vttreader.cc | 54 + webvtt/vttreader.h | 44 + webvtt/webvttparser.cc | 706 ++ webvtt/webvttparser.h | 158 + webvttparser.h | 15 + 893 files changed, 51855 insertions(+) create mode 100644 .clang-format create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 AUTHORS.TXT create mode 100644 Android.mk create mode 100644 CMakeLists.txt create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.TXT create mode 100644 Makefile.unix create mode 100644 PATENTS.TXT create mode 100644 README.libwebm create mode 100644 build/cxx_flags.cmake create mode 100644 build/msvc_runtime.cmake create mode 100644 build/x86-mingw-gcc.cmake create mode 100644 build/x86_64-mingw-gcc.cmake create mode 100644 codereview.settings create mode 100644 common/common.sh create mode 100644 common/file_util.cc create mode 100644 common/file_util.h create mode 100644 common/hdr_util.cc create mode 100644 common/hdr_util.h create mode 100644 common/indent.cc create mode 100644 common/indent.h create mode 100644 common/libwebm_util.cc create mode 100644 common/libwebm_util.h create mode 100644 common/video_frame.cc create mode 100644 common/video_frame.h create mode 100644 common/vp9_header_parser.cc create mode 100644 common/vp9_header_parser.h create mode 100644 common/vp9_header_parser_tests.cc create mode 100644 common/vp9_level_stats.cc create mode 100644 common/vp9_level_stats.h create mode 100644 common/vp9_level_stats_tests.cc create mode 100644 common/webm_constants.h create mode 100644 common/webm_endian.cc create mode 100644 common/webm_endian.h create mode 100644 common/webmids.h create mode 100644 dumpvtt.cc create mode 100644 hdr_util.hpp create mode 100755 iosbuild.sh create mode 100644 m2ts/tests/webm2pes_tests.cc create mode 100644 m2ts/vpxpes2ts.cc create mode 100644 m2ts/vpxpes2ts.h create mode 100644 m2ts/vpxpes2ts_main.cc create mode 100644 m2ts/vpxpes_parser.cc create mode 100644 m2ts/vpxpes_parser.h create mode 100644 m2ts/webm2pes.cc create mode 100644 m2ts/webm2pes.h create mode 100644 m2ts/webm2pes_main.cc create mode 100644 mkvmuxer.hpp create mode 100644 mkvmuxer/mkvmuxer.cc create mode 100644 mkvmuxer/mkvmuxer.h create mode 100644 mkvmuxer/mkvmuxertypes.h create mode 100644 mkvmuxer/mkvmuxerutil.cc create mode 100644 mkvmuxer/mkvmuxerutil.h create mode 100644 mkvmuxer/mkvwriter.cc create mode 100644 mkvmuxer/mkvwriter.h create mode 100644 mkvmuxer_sample.cc create mode 100644 mkvmuxertypes.hpp create mode 100644 mkvmuxerutil.hpp create mode 100644 mkvparser.hpp create mode 100644 mkvparser/mkvparser.cc create mode 100644 mkvparser/mkvparser.h create mode 100644 mkvparser/mkvreader.cc create mode 100644 mkvparser/mkvreader.h create mode 100644 mkvparser_sample.cc create mode 100644 mkvreader.hpp create mode 100644 mkvwriter.hpp create mode 100644 sample_muxer_metadata.cc create mode 100644 sample_muxer_metadata.h create mode 100644 testing/mkvmuxer_tests.cc create mode 100644 testing/mkvparser_tests.cc create mode 100644 testing/test_util.cc create mode 100644 testing/test_util.h create mode 100644 testing/testdata/accurate_cluster_duration.webm create mode 100644 testing/testdata/accurate_cluster_duration_last_frame.webm create mode 100644 testing/testdata/accurate_cluster_duration_two_tracks.webm create mode 100644 testing/testdata/bbb_480p_vp9_opus_1second.webm create mode 100644 testing/testdata/block_with_additional.webm create mode 100644 testing/testdata/chapters.webm create mode 100644 testing/testdata/colour.webm create mode 100644 testing/testdata/cues_before_clusters.webm create mode 100644 testing/testdata/discard_padding.webm create mode 100644 testing/testdata/estimate_duration.webm create mode 100644 testing/testdata/fixed_size_cluster_timecode.webm create mode 100644 testing/testdata/force_new_cluster.webm create mode 100644 testing/testdata/invalid/README.libwebm create mode 100644 testing/testdata/invalid/block_ends_beyond_cluster.mkv create mode 100644 testing/testdata/invalid/blockgroup_block_ends_beyond_blockgroup.mkv create mode 100644 testing/testdata/invalid/chapters_truncated_chapter_string.mkv create mode 100644 testing/testdata/invalid/chapters_truncated_chapter_string_2.mkv create mode 100644 testing/testdata/invalid/fixed_lacing_bad_lace_size.mkv create mode 100644 testing/testdata/invalid/invalid_vp9_bitstream-bug_1416.webm create mode 100644 testing/testdata/invalid/invalid_vp9_bitstream-bug_1417.webm create mode 100644 testing/testdata/invalid/primarychromaticity_fieldtoolarge.webm create mode 100644 testing/testdata/invalid/projection_float_overflow.webm create mode 100644 testing/testdata/long_tag_string.webm create mode 100644 testing/testdata/matroska_doctype.mkv create mode 100644 testing/testdata/max_cluster_duration.webm create mode 100644 testing/testdata/max_cluster_size.webm create mode 100644 testing/testdata/metadata_block.webm create mode 100644 testing/testdata/output_cues.webm create mode 100644 testing/testdata/projection.webm create mode 100644 testing/testdata/segment_duration.webm create mode 100644 testing/testdata/segment_info.webm create mode 100644 testing/testdata/set_cues_track_number.webm create mode 100644 testing/testdata/set_pixelwidth_pixelheight.webm create mode 100644 testing/testdata/set_segment_duration.webm create mode 100644 testing/testdata/simple_block.webm create mode 100644 testing/testdata/test_stereo_left_right.webm create mode 100644 testing/testdata/tracks.webm create mode 100644 testing/testdata/webm_doctype.webm create mode 100644 testing/video_frame_tests.cc create mode 100644 vttdemux.cc create mode 100644 vttreader.h create mode 100644 webm_info.cc create mode 100644 webm_parser/README.md create mode 100644 webm_parser/demo/demo.cc create mode 100644 webm_parser/doxygen.config create mode 100644 webm_parser/fuzzing/corpus/00805c2543756a5fd85652d03bfbbd2eb6192ca5 create mode 100644 webm_parser/fuzzing/corpus/00d120eb143bb02c48d7c863e5826d2ad1a6da4b create mode 100644 webm_parser/fuzzing/corpus/018dee8285e9e20ca3996bb2dc0284b5c57ba75a create mode 100644 webm_parser/fuzzing/corpus/020edb59637c1e6439f19aa3a5a9d50c3377dbe9 create mode 100644 webm_parser/fuzzing/corpus/0247ce2b1a71752a3af11e1065ca90afa0df9d30 create mode 100644 webm_parser/fuzzing/corpus/028b19f7d79f5da7a2af13a0c1e2d13f7eaf24cd create mode 100644 webm_parser/fuzzing/corpus/029ab55b16df41881f8de2351205201da334550a create mode 100644 webm_parser/fuzzing/corpus/029be5e90913b19cf5890559cf3f98aa909f0a84 create mode 100644 webm_parser/fuzzing/corpus/02fb96539b84bbd12de84ff05cbc9dc3faa96b7e create mode 100644 webm_parser/fuzzing/corpus/0349f5632d21faa36b85520ad0b524d561f5e13f create mode 100644 webm_parser/fuzzing/corpus/036fc9daf7fb1b4274dd668cfd883248ebbad967 create mode 100644 webm_parser/fuzzing/corpus/037a4edc18e475ec81081e47277cbf51f1316680 create mode 100644 webm_parser/fuzzing/corpus/0481dad9a7d0e6fab0c703bba9b3268db96c6793 create mode 100644 webm_parser/fuzzing/corpus/04dc2407e7142f5618aa5105377925b0b0ed544b create mode 100644 webm_parser/fuzzing/corpus/04fa2f34ff4a4406d136e5aaba5debe7d8129a06 create mode 100644 webm_parser/fuzzing/corpus/054fd0041ad81cfad0a85e3c59195485492a4493 create mode 100644 webm_parser/fuzzing/corpus/056b83ab2457979ea021e7118ab847eba265df15 create mode 100644 webm_parser/fuzzing/corpus/058326151c1d4a490964d495d35adcf15178030f create mode 100644 webm_parser/fuzzing/corpus/05847b5be0eb200d6a6b340c939c7a654b55003e create mode 100644 webm_parser/fuzzing/corpus/05b600ae9a9072ac2865247e69ed0736a0c58316 create mode 100644 webm_parser/fuzzing/corpus/05cf976698b55df1fcd03bc07446bb9283dad9d7 create mode 100644 webm_parser/fuzzing/corpus/05d4dfda5e264fffda243f2991a86e96141f579a create mode 100644 webm_parser/fuzzing/corpus/061217fc0b6af0ec3851d9728e03ed9b30c702d5 create mode 100644 webm_parser/fuzzing/corpus/077b53a7163e51c48e8cdaf4209bb837823ffba9 create mode 100644 webm_parser/fuzzing/corpus/07c3ade9713892bb75db2d93b48ef40b262a54e6 create mode 100644 webm_parser/fuzzing/corpus/07eaf3c7437032f60c905f6f8e3dfc193491b602 create mode 100644 webm_parser/fuzzing/corpus/07f67b922b503354b2aebcdcc4ab7b6d3150b048 create mode 100644 webm_parser/fuzzing/corpus/08af8d91bb21835c50330e997d973cac8ff67766 create mode 100644 webm_parser/fuzzing/corpus/0a18f05bb16402756202160225aec9c5a654f1ae create mode 100644 webm_parser/fuzzing/corpus/0b60823983971ee17a2590678f0fc0762c21bb73 create mode 100644 webm_parser/fuzzing/corpus/0bae7f0976af0f75974047b5f2cf4c9645412066 create mode 100644 webm_parser/fuzzing/corpus/0c1862b4065eefab2535ecc6951295e38069a82e create mode 100644 webm_parser/fuzzing/corpus/0c2f51d5ffc69e69680bf3d6edb91d76c353ad14 create mode 100644 webm_parser/fuzzing/corpus/0c3af72d69f18103383c9cd41a7f2676a9d28a40 create mode 100644 webm_parser/fuzzing/corpus/0c43df7fc9d06187249187583c3c082520701289 create mode 100644 webm_parser/fuzzing/corpus/0cadf5ecf58a394560a1f6db72a2e21264445d13 create mode 100644 webm_parser/fuzzing/corpus/0cd91a4e7bec912beb3b47a685b89d5be24d0046 create mode 100644 webm_parser/fuzzing/corpus/0d0da60f91f9489af113d8484d9b6871622523d5 create mode 100644 webm_parser/fuzzing/corpus/0d30a4f88e53de1ce4bf1ec724016f2f4c11bc9b create mode 100644 webm_parser/fuzzing/corpus/0d6b3b1d024e7aa73fbc58b157ab53df048a2f2c create mode 100644 webm_parser/fuzzing/corpus/0d94310cfd8a9acdbc7fb82ad9c73cdf3f64f926 create mode 100644 webm_parser/fuzzing/corpus/0f5741483be8f4f6eddcb70ea418fb0c08da9443 create mode 100644 webm_parser/fuzzing/corpus/109e125f729f84d69c3e3a614123547a094534ad create mode 100644 webm_parser/fuzzing/corpus/10d257a4a314e20644fbc469cdebafe16c7f46a8 create mode 100644 webm_parser/fuzzing/corpus/10d951f88995a2176878501a2633b9bb4822ff96 create mode 100644 webm_parser/fuzzing/corpus/111b8f8318a269bbe0f7d9f1cebfdc1cb43c982b create mode 100644 webm_parser/fuzzing/corpus/11c122ce1f7d993f809a4eb5db17c738f279a707 create mode 100644 webm_parser/fuzzing/corpus/11e288617056809208561f2a9112fd0664d378d2 create mode 100644 webm_parser/fuzzing/corpus/124fde9cfe7157773d8febcbb0829914bf4d17d3 create mode 100644 webm_parser/fuzzing/corpus/1386cc740c80ede6dfea5f3bb1d4fe1501ab5e91 create mode 100644 webm_parser/fuzzing/corpus/13dd373ccb0c534402c6f7c4a0bdd723a26bc2c1 create mode 100644 webm_parser/fuzzing/corpus/13fc3d2b32d789c84be6349bb585c308289b4c3e create mode 100644 webm_parser/fuzzing/corpus/148357130d1e5ac4059ad2bb6c63d78e2523f7d2 create mode 100644 webm_parser/fuzzing/corpus/15366a3aafe2590c2e3183c088dde4cc100cb956 create mode 100644 webm_parser/fuzzing/corpus/15feeb939fa90b25f57622a0abd9155ca88ad08b create mode 100644 webm_parser/fuzzing/corpus/16282b78a2018eb78544316554a92fe1003859e3 create mode 100644 webm_parser/fuzzing/corpus/1717628a6d6ea868febec5fb196edcf4b9eb284d create mode 100644 webm_parser/fuzzing/corpus/17921b1e28600e7e3faf67fc68b397ecab4e2a52 create mode 100644 webm_parser/fuzzing/corpus/17b8276355dd2368647b7756431820f5275cc3e2 create mode 100644 webm_parser/fuzzing/corpus/17d0aece97973ab23a467486b177ea9722e1b90b create mode 100644 webm_parser/fuzzing/corpus/17e80cf8c247d17acad56c88750da34893fcb4fd create mode 100644 webm_parser/fuzzing/corpus/18040e106688eb1d54323927a403439ecfde6626 create mode 100644 webm_parser/fuzzing/corpus/1813261b3141faa01431c82e06292485516d3327 create mode 100644 webm_parser/fuzzing/corpus/184ee2343d4b9f62c69383692829fd852ad8855d create mode 100644 webm_parser/fuzzing/corpus/186035a2f5c09ccd6b7d15de46f8561c98d8897d create mode 100644 webm_parser/fuzzing/corpus/186292900ad9d43881b71765158c32d78d80c8f3 create mode 100644 webm_parser/fuzzing/corpus/18fd762b91406d37b85a7b342a91434a15f17290 create mode 100644 webm_parser/fuzzing/corpus/19334eee05eeb18c549498e7ca2e792a2a4e04f3 create mode 100644 webm_parser/fuzzing/corpus/1a9a5df35779dd6e9e1de171b9bb0d316d2b64a5 create mode 100644 webm_parser/fuzzing/corpus/1ab3d30f60743c2a1d3043773aae3a04f83c07c0 create mode 100644 webm_parser/fuzzing/corpus/1ad84ed46f3fda305150bac93958a5a390e23a67 create mode 100644 webm_parser/fuzzing/corpus/1b40a997150aa03c23ecc6efe445a2d7c3dd8368 create mode 100644 webm_parser/fuzzing/corpus/1b6b3bab9032cd420f350b6bb252942484f6b527 create mode 100644 webm_parser/fuzzing/corpus/1b7765cb05c94581461ffcd38d38b7b272d8e9d6 create mode 100644 webm_parser/fuzzing/corpus/1b89e3fe0cbd4c2291a74bf21969a9d9d851cbf1 create mode 100644 webm_parser/fuzzing/corpus/1b95d4da08fe949a60f63cd59213d42d30dbd381 create mode 100644 webm_parser/fuzzing/corpus/1bac1200e05bb3269d019903241791c917a12bd0 create mode 100644 webm_parser/fuzzing/corpus/1bb8f5f81b7f6c1d58e1f7bb13fa561fe1a146bc create mode 100644 webm_parser/fuzzing/corpus/1c56068c6dd17e9a824db6da78d64f933267a8c0 create mode 100644 webm_parser/fuzzing/corpus/1c8db8b9d88dd3483b6f81e4224c4f985046e6ac create mode 100644 webm_parser/fuzzing/corpus/1d29a77924602a79c0f546535a885f59cbbbc405 create mode 100644 webm_parser/fuzzing/corpus/1d75a65733da627e5c401625bff522eadf098315 create mode 100644 webm_parser/fuzzing/corpus/1df9507cc2a54a369646d6d34d846d3fcc172479 create mode 100644 webm_parser/fuzzing/corpus/1e7a571be5aa542c3dfec1223d2e089a3075ce1f create mode 100644 webm_parser/fuzzing/corpus/20a6b040258fbfa09bb37c6fc07106b2e43bada7 create mode 100644 webm_parser/fuzzing/corpus/20e8ca854d3c0c375dc943142a04ee2260f0d1fd create mode 100644 webm_parser/fuzzing/corpus/212ee7d21a8cb25249644cab4f959db111f3a3c0 create mode 100644 webm_parser/fuzzing/corpus/224ce7b415f7950118880fce0594734aef489fec create mode 100644 webm_parser/fuzzing/corpus/226e70c1beddace83862791a6555c80475640bd0 create mode 100644 webm_parser/fuzzing/corpus/233d53e3eb21b6ea6feebd6e59e3ef888a840f49 create mode 100644 webm_parser/fuzzing/corpus/237f7aee90f206d3a6b138cc908b8921b544e190 create mode 100644 webm_parser/fuzzing/corpus/23e1ac0f77f1283cf6e9fa044df3ec51bff27fd4 create mode 100644 webm_parser/fuzzing/corpus/23fcee6c71a8f5a22447df688724c0250fb77a70 create mode 100644 webm_parser/fuzzing/corpus/2459eb855d8c6ebac13cb74d996565d78130f4dd create mode 100644 webm_parser/fuzzing/corpus/259188e5fb0c09046df96f6d565c59e0d146f198 create mode 100644 webm_parser/fuzzing/corpus/26f07f8e28e7521ed282fe5c3938c1ae225816b9 create mode 100644 webm_parser/fuzzing/corpus/276425d65d58453d03a3444c9f6662d08d449af7 create mode 100644 webm_parser/fuzzing/corpus/27d62874ec87a2552e7c842da65de113aa69f7aa create mode 100644 webm_parser/fuzzing/corpus/281b259280ada5d07b07a22cbe9a78c7b0fba94b create mode 100644 webm_parser/fuzzing/corpus/299ed12b98673c6c4adbcf886cb70db8adde6aa1 create mode 100644 webm_parser/fuzzing/corpus/2a40feb7480d0b31c36d5626761e948d0ae52792 create mode 100644 webm_parser/fuzzing/corpus/2a6d7e2b829ed28307b551eda0d96f1834bff899 create mode 100644 webm_parser/fuzzing/corpus/2a8dda90aa286175b5c683b57fae1dc7e6ac1e7e create mode 100644 webm_parser/fuzzing/corpus/2a9588e6fab82016b545462cff2ed014c0345551 create mode 100644 webm_parser/fuzzing/corpus/2b1d57a8e8fa7164c9ba00957c9486010754560c create mode 100644 webm_parser/fuzzing/corpus/2b475d1a8f2fe4fbba92e1424f0ea96d95b12fb7 create mode 100644 webm_parser/fuzzing/corpus/2bb80c6d0e2eadd73018eb2a8cae37d41602ee79 create mode 100644 webm_parser/fuzzing/corpus/2c1389f882e256e260e37e8a67af7e32d0f4e0fc create mode 100644 webm_parser/fuzzing/corpus/2c1f94c76e4eec607cce5311323620f349912e73 create mode 100644 webm_parser/fuzzing/corpus/2c962c7fbceaacf8247ac9b70c8eeb1f814e435b create mode 100644 webm_parser/fuzzing/corpus/2c9aaabacf3a3b48dec4a85767cc5d38a1736aff create mode 100644 webm_parser/fuzzing/corpus/2cf086299f983d0afc7f95c5e0c86b657448af33 create mode 100644 webm_parser/fuzzing/corpus/2d0d96b95e9a3316d1ff0ef019ada509bbde4c9d create mode 100644 webm_parser/fuzzing/corpus/2d414d5dfd20055393df3208009840da9cfabca9 create mode 100644 webm_parser/fuzzing/corpus/2df967edcc00ac5c8e00037f1a43680600af5cba create mode 100644 webm_parser/fuzzing/corpus/2e6b09f1eca05ce2bba920fb78f9bca2a883fda0 create mode 100644 webm_parser/fuzzing/corpus/2e8bde3549723e13849b604f4deedd51c71dfef5 create mode 100644 webm_parser/fuzzing/corpus/2f6e92a71918d01c16762d5ca59b328f1341d326 create mode 100644 webm_parser/fuzzing/corpus/306e2cf9aebe012cb0769b1b2a6ea68af2e8ed44 create mode 100644 webm_parser/fuzzing/corpus/30f7348d35de0c47d2044736cb115972b800569d create mode 100644 webm_parser/fuzzing/corpus/310798b8d94a3a2fe649a4c871458b4d74fdcf32 create mode 100644 webm_parser/fuzzing/corpus/31ea606a9859bb29d7f98162d254348021e0d932 create mode 100644 webm_parser/fuzzing/corpus/3230fc31ab8d408c484aa28bbe36431f71101243 create mode 100644 webm_parser/fuzzing/corpus/328b7c92996e480d1f11efe847c3be4b5dc0f8eb create mode 100644 webm_parser/fuzzing/corpus/32e58bb3d00863115f33707e0c8af722036676fd create mode 100644 webm_parser/fuzzing/corpus/32fc8547b6145c502d98e5e0c296ebd05badfdb9 create mode 100644 webm_parser/fuzzing/corpus/334cddba889265a1263feb77a2afdc454ca54f5e create mode 100644 webm_parser/fuzzing/corpus/33c9f9990890d2baf0c30d74b34b486082782413 create mode 100644 webm_parser/fuzzing/corpus/341f3e8689e57eeab4234fcdd6d2b8f800b9ab34 create mode 100644 webm_parser/fuzzing/corpus/3457e356eac79bf1c30b59940532d360dba2c1a9 create mode 100644 webm_parser/fuzzing/corpus/351185a29bdbc7bf0d584479001fb47c32c61e5b create mode 100644 webm_parser/fuzzing/corpus/35ae0d43c6bfc5f4b45f16877157832fddafce77 create mode 100644 webm_parser/fuzzing/corpus/35b2f81c573b15304cb9b13f00008b460da78942 create mode 100644 webm_parser/fuzzing/corpus/35d9256a4d7ef3ee54384616178af3a8092d0981 create mode 100644 webm_parser/fuzzing/corpus/36352efd680a747f0f2c93d760559d69399a4102 create mode 100644 webm_parser/fuzzing/corpus/3685ffbcfe28b166ced0a783b012a446f9cfea46 create mode 100644 webm_parser/fuzzing/corpus/36def8b2661197d594756f116a750822ae1d65d5 create mode 100644 webm_parser/fuzzing/corpus/371950308853fb3b1b3a940c0598bfdfeb1d0f9c create mode 100644 webm_parser/fuzzing/corpus/3765e9174971dbac9cfeb7e485ca61dc4941f7a9 create mode 100644 webm_parser/fuzzing/corpus/37cae036ce7ff5d0671f32d3757579f248609c9c create mode 100644 webm_parser/fuzzing/corpus/38460ea06ee847ff3d5733415c299b44b0866e58 create mode 100644 webm_parser/fuzzing/corpus/3865de2fec3f353513d86b28e43bf936d4707f97 create mode 100644 webm_parser/fuzzing/corpus/3867b18060c307e0d04e0098f195d699e4b3294f create mode 100644 webm_parser/fuzzing/corpus/391920720c3609b7c1f7b51162ce3ec99329a0b4 create mode 100644 webm_parser/fuzzing/corpus/395027454c7d5babff9544414c04a39d712eed1c create mode 100644 webm_parser/fuzzing/corpus/3a258c9ed48e634ed8d495f07caf21dbc6978205 create mode 100644 webm_parser/fuzzing/corpus/3a397485197e6b99c77158132eaf211389615fe5 create mode 100644 webm_parser/fuzzing/corpus/3a66b34706b686fe0fa8ca8f10c829758b2ebb07 create mode 100644 webm_parser/fuzzing/corpus/3ba666ad891fa8009dfc961aa3c215bbfd2d81d2 create mode 100644 webm_parser/fuzzing/corpus/3ba9769c7c23cc3c80542ad123a371cf4f69e858 create mode 100644 webm_parser/fuzzing/corpus/3beb4432302433e303a9ccaf34637dd3a910028e create mode 100644 webm_parser/fuzzing/corpus/3c6cdebc450d20a58150a9c71335b45a6a5ee0e5 create mode 100644 webm_parser/fuzzing/corpus/3c867362eb6a20f573a890db87749fce3c719d14 create mode 100644 webm_parser/fuzzing/corpus/3cd69ddcd34bd874e3abade696f95c949c17198d create mode 100644 webm_parser/fuzzing/corpus/3de495ebac4b80064b79844f3a9453270caec251 create mode 100644 webm_parser/fuzzing/corpus/3e04542bcc462ded0025b53371f709d8fcd25c21 create mode 100644 webm_parser/fuzzing/corpus/3e94196e55a9214083bda470772d71aedc17eb5f create mode 100644 webm_parser/fuzzing/corpus/3ec4ac00bb967a335b5caae0ae51488f2fda6583 create mode 100644 webm_parser/fuzzing/corpus/3eec38c0ac3a96856d3210a0357857e70b07a9cb create mode 100644 webm_parser/fuzzing/corpus/3ef82ba46238fa46b89148a230d23e741ba5f1da create mode 100644 webm_parser/fuzzing/corpus/3fbd43f81888ca60e5bfe49b47e23cb115d5cd7d create mode 100644 webm_parser/fuzzing/corpus/3fe56e5b06653e8a37c99da190ff8d44d14bfc06 create mode 100644 webm_parser/fuzzing/corpus/3febffec3be8b1f4bcb76a01c1d713600d8e4adb create mode 100644 webm_parser/fuzzing/corpus/43380e5a9dd29ca847e9b5bfba12a0a95d94d0da create mode 100644 webm_parser/fuzzing/corpus/435d3ff6ddc43ac582e740ead969397fd2bfc7f5 create mode 100644 webm_parser/fuzzing/corpus/448120a37132488f48ab6c46a146c995948958cc create mode 100644 webm_parser/fuzzing/corpus/46db501d3e9a8808118333740a22bf9c808af239 create mode 100644 webm_parser/fuzzing/corpus/47a2ca2b47aa1993cb655b77a0273ff139dd2280 create mode 100644 webm_parser/fuzzing/corpus/490576c8f6c235e1ed921c4932bac028070c806a create mode 100644 webm_parser/fuzzing/corpus/4930b86bf0dfb02836b0319d21aa4a63a8dd894d create mode 100644 webm_parser/fuzzing/corpus/49667ff0b09901223117e74d2506bc37a5ae1580 create mode 100644 webm_parser/fuzzing/corpus/49906adf7313330e29be20d51fb36e9bdf1939c3 create mode 100644 webm_parser/fuzzing/corpus/49c0e3eed81c1827e755c70c9c6d9606e7f54b22 create mode 100644 webm_parser/fuzzing/corpus/4a3e77f5bc061fc8121ef7ebd2b8e3ebb0bdd609 create mode 100644 webm_parser/fuzzing/corpus/4a454e01ef09b175f3864b654cfc1104a850e871 create mode 100644 webm_parser/fuzzing/corpus/4acd9df9a7ee34e48c6c80777159b089f417a69a create mode 100644 webm_parser/fuzzing/corpus/4af5564129d7b4c9c4736a3e511926c29348a4a4 create mode 100644 webm_parser/fuzzing/corpus/4b3957bcbe5975399e34485cf0e5229169ab67b9 create mode 100644 webm_parser/fuzzing/corpus/4c9e40aa8a439a500f073cf5a1e060a16ad12a6f create mode 100644 webm_parser/fuzzing/corpus/4cb5ff0e26f5bd0bb1fca846b4843db69095f18b create mode 100644 webm_parser/fuzzing/corpus/4ce80549344cf5b0432e39e7b5382407f0763e8c create mode 100644 webm_parser/fuzzing/corpus/4d10ae0032f04fa01148a54fae6838c44e4bb5bd create mode 100644 webm_parser/fuzzing/corpus/4db2ded939a16fb1907dc2bad7acf67a64e38c0c create mode 100644 webm_parser/fuzzing/corpus/4dc3a9a9477fb452ba7a4026bbc01d4cdd3d4990 create mode 100644 webm_parser/fuzzing/corpus/4e1fb006a2268fa5fc70ca6b241bbc71faa6d7ff create mode 100644 webm_parser/fuzzing/corpus/4e871bbc5089f9550533641ddf6850f8722dfc36 create mode 100644 webm_parser/fuzzing/corpus/4eeb4507868732d3705d1ab10f6a31f70f579556 create mode 100644 webm_parser/fuzzing/corpus/4f21f3584c99e97453ebe513ed44c86530fbebba create mode 100644 webm_parser/fuzzing/corpus/5103160695cd56d281faa45d9267013488cc7bde create mode 100644 webm_parser/fuzzing/corpus/521103054e395afd0f9612b47c71cfa0e62151b9 create mode 100644 webm_parser/fuzzing/corpus/523511e33c8788ebb1b703d7c5ad12b5da03964e create mode 100644 webm_parser/fuzzing/corpus/525aedb7462311393cd01939e4734d01db48535b create mode 100644 webm_parser/fuzzing/corpus/53809012a8d5cfee839c3d0961b7fdc9468329a7 create mode 100644 webm_parser/fuzzing/corpus/53c01d820d125e57b28410ca5b3a9c72c18d4102 create mode 100644 webm_parser/fuzzing/corpus/53c27071f53df6b8d2e5c419edf95b7792b278e7 create mode 100644 webm_parser/fuzzing/corpus/53d5307f2e7852b183839fe93c20647004d151ec create mode 100644 webm_parser/fuzzing/corpus/5466c59c9c0eff34800e19cc92faf07469865ea9 create mode 100644 webm_parser/fuzzing/corpus/54bf8d6f1f846243b6f4de81605779b0acb78406 create mode 100644 webm_parser/fuzzing/corpus/54fd1711209fb1c0781092374132c66e79e2241b create mode 100644 webm_parser/fuzzing/corpus/5506797514c0b214f5faa9764a9e149cd1df9d28 create mode 100644 webm_parser/fuzzing/corpus/5566c6d599866f5e523c54e6575d7b9e557a2539 create mode 100644 webm_parser/fuzzing/corpus/55c3d55780427eb00bde252348db1c2ede22526c create mode 100644 webm_parser/fuzzing/corpus/5600abaefe804c50a353bbfdd0f9e10ffc102f81 create mode 100644 webm_parser/fuzzing/corpus/5611ddd40488973ee6c64c45ad57d6eefe6da100 create mode 100644 webm_parser/fuzzing/corpus/56b0ddd4e44492b45ebb7e9438b0a5556c244bfb create mode 100644 webm_parser/fuzzing/corpus/5729395b452092152b06fbb8976e9bf1fda06439 create mode 100644 webm_parser/fuzzing/corpus/57cce6fac7c0992eb338e0e538d3e0ee3fe7f1cd create mode 100644 webm_parser/fuzzing/corpus/5822672f068da89546838004ae59552e12e132f7 create mode 100644 webm_parser/fuzzing/corpus/59ce13e9a22e88dac879841256ff66f02cd2f8cc create mode 100644 webm_parser/fuzzing/corpus/5a020527fc80b0de64e279fc44635687e014a9cf create mode 100644 webm_parser/fuzzing/corpus/5a645b30948850f8efb7bdd11944bdf3112c7bfe create mode 100644 webm_parser/fuzzing/corpus/5b2f727461cde3c02ffdfb300adb0b4a09734911 create mode 100644 webm_parser/fuzzing/corpus/5b5531d859ab5bbb034345bbaa2852e9d8a1e1ef create mode 100644 webm_parser/fuzzing/corpus/5b980cb94e35769ec92a140ca00d34977e7da5ec create mode 100644 webm_parser/fuzzing/corpus/5bfec2fe795176646fa0babecf97c04045bd4814 create mode 100644 webm_parser/fuzzing/corpus/5c28d94242e9a5ba7e7324e59e5dd12f3e31e51c create mode 100644 webm_parser/fuzzing/corpus/5c67728ab04ba3dfe4b2636cf609f9617c4a5974 create mode 100644 webm_parser/fuzzing/corpus/5cd36053e269c97f5359b0a075d80bf1e23b5f47 create mode 100644 webm_parser/fuzzing/corpus/5d6f99c2afbf68a1b15a17e07945c11db8579e5f create mode 100644 webm_parser/fuzzing/corpus/5dd149b1b8509f456c16c4ac6dac3c7d6aa217ed create mode 100644 webm_parser/fuzzing/corpus/5e84011c6b092e9f0d6ba60f12fc0fae95a622ca create mode 100644 webm_parser/fuzzing/corpus/5f56766993de2b8c2db87116090af546e1add3b0 create mode 100644 webm_parser/fuzzing/corpus/60571c8be9bee3b13428613df66ff36cf54309ec create mode 100644 webm_parser/fuzzing/corpus/60ac245f262ee1ff177d351a4fe246060fb4538b create mode 100644 webm_parser/fuzzing/corpus/6120aafb0db1715400e47d0a61fb9f6d8665a736 create mode 100644 webm_parser/fuzzing/corpus/61953a8f7a9f0613020d297a6d350abeeaf6e58c create mode 100644 webm_parser/fuzzing/corpus/61c0a7fef46c0c1b081b1c2b40835805e5989b54 create mode 100644 webm_parser/fuzzing/corpus/620d85870206e88db0bc3ee978c24c0bbc1821ba create mode 100644 webm_parser/fuzzing/corpus/621131d8e28a67a6b4fb03c611f4375873d69678 create mode 100644 webm_parser/fuzzing/corpus/624cbd42e3ebf35a3a0ba55b3b4969b99054f84d create mode 100644 webm_parser/fuzzing/corpus/6290103200631075988aa3566d8d8922599f8b6d create mode 100644 webm_parser/fuzzing/corpus/62a329f65143f1571dc4a2fbb256800c8ce4d89f create mode 100644 webm_parser/fuzzing/corpus/62f8a53cfb4dd625dcb31ee2ba258db3916a3bd0 create mode 100644 webm_parser/fuzzing/corpus/6318dc574b3ad7291fee7e01bfeaa674eb0feb48 create mode 100644 webm_parser/fuzzing/corpus/6457a079debb9b532255e3e6ddf276b29808b49d create mode 100644 webm_parser/fuzzing/corpus/646061a4526506a0b3c62629f4f77b6072c8f9c3 create mode 100644 webm_parser/fuzzing/corpus/6538d0517865c8832ee9bec87609b7bd4facb025 create mode 100644 webm_parser/fuzzing/corpus/653de28e2f085334e30ec1c964540b432a3cfbc6 create mode 100644 webm_parser/fuzzing/corpus/657c9c42bf3e1c79a6dcb9071bf123367dc37734 create mode 100644 webm_parser/fuzzing/corpus/65ebba0df08a5484686905e99631a4ead845c08b create mode 100644 webm_parser/fuzzing/corpus/66dd27c22fe09842ac4c4f51fdef4bea500b6a70 create mode 100644 webm_parser/fuzzing/corpus/66e25df2bd43c840337a49e0cb5b386e9d290d80 create mode 100644 webm_parser/fuzzing/corpus/6714df1ac1c54d4c30add212454e9513c6880767 create mode 100644 webm_parser/fuzzing/corpus/68a6f9c2f62e9f57667de9563b5d612ec2f2c829 create mode 100644 webm_parser/fuzzing/corpus/69089b76cc62fe00615702bdd1f339e259194d42 create mode 100644 webm_parser/fuzzing/corpus/69d8622d24c7315f344c424297986170f7ef2243 create mode 100644 webm_parser/fuzzing/corpus/6a700d958cb3a7565d5a6b74540486874d52b4c7 create mode 100644 webm_parser/fuzzing/corpus/6a852150c5c2a12d325e78056687a8ea97b25fe7 create mode 100644 webm_parser/fuzzing/corpus/6ac8c9f754d5ff3c0cd3f3f585de99dd73944bbe create mode 100644 webm_parser/fuzzing/corpus/6ace27a48f12990e2169cc099007aa5fa11962d2 create mode 100644 webm_parser/fuzzing/corpus/6b10397dc3bff2ccf57be4fe3ff9d8d1acb8c76e create mode 100644 webm_parser/fuzzing/corpus/6c3f1d96c06b853551f685dde821142928f55acf create mode 100644 webm_parser/fuzzing/corpus/6c8de4d4bbb5294154c3b140d43836757b9f08ac create mode 100644 webm_parser/fuzzing/corpus/6cf122ee328af5cf6f18d5f9546b0fe761951b8d create mode 100644 webm_parser/fuzzing/corpus/6cfbbc25caa217e22fd7ca3e92d2051c5f661bdb create mode 100644 webm_parser/fuzzing/corpus/6d703bc900e74d1a576bebcccb63e3b2701ae86d create mode 100644 webm_parser/fuzzing/corpus/6e1afa81dfcd6833705d84b45881f085c19b2211 create mode 100644 webm_parser/fuzzing/corpus/6e244ec3b81e0d2f8b5810854859e1b8140422d9 create mode 100644 webm_parser/fuzzing/corpus/6e323b4c733df90d55c754e099fb5a13bd9701cb create mode 100644 webm_parser/fuzzing/corpus/6e36720f3f55dc0cd5d585c6770572964719b5b8 create mode 100644 webm_parser/fuzzing/corpus/6e8384ab115ac92bb7787121cfb03582e2d72585 create mode 100644 webm_parser/fuzzing/corpus/6ebf8b74dca1657f5f7ffadeccd629984473dcb4 create mode 100644 webm_parser/fuzzing/corpus/6ec18dcca0d820fff04c03c3a1bce37dd8ada41d create mode 100644 webm_parser/fuzzing/corpus/6eceebb8f46545393d217ab0111ff7dd29bc99f7 create mode 100644 webm_parser/fuzzing/corpus/6ee071f654fd6b7b25725a8bdd880bb6836e8c81 create mode 100644 webm_parser/fuzzing/corpus/6f1088b3d1f4cdfbc37d9b8cfc1ff4c3bde2f205 create mode 100644 webm_parser/fuzzing/corpus/6f79e36e880664c9b3b610140d2485fb6a6e5039 create mode 100644 webm_parser/fuzzing/corpus/6fbea59066b2b97fa45259aa87c40c4310070019 create mode 100644 webm_parser/fuzzing/corpus/7056b9c294f0cff7b4ace611a19363b2fc096bb3 create mode 100644 webm_parser/fuzzing/corpus/7073dca40b911fb22f738c99aff43a10ae6c4db5 create mode 100644 webm_parser/fuzzing/corpus/7164d8ca3f4c3d621cc5eed7b59e3495e7b017a0 create mode 100644 webm_parser/fuzzing/corpus/71e3e01b3cfcd4228402cbf19b5282c965f042df create mode 100644 webm_parser/fuzzing/corpus/720d36ea7f9616b1a1d47fa5b746e8657b5f973f create mode 100644 webm_parser/fuzzing/corpus/72ccbcbc53c7fa54247a81bd1d0e62dd0779494b create mode 100644 webm_parser/fuzzing/corpus/736c0460f94c089c9e270857a4222a21ffd9e13f create mode 100644 webm_parser/fuzzing/corpus/745e32f46833c075c8e68552e0fbbcdd5ec52641 create mode 100644 webm_parser/fuzzing/corpus/74aa7256477af2c0e2511df16376dee323f3ccb6 create mode 100644 webm_parser/fuzzing/corpus/7528086eea404dccc3ee5b214befbc2a95676feb create mode 100644 webm_parser/fuzzing/corpus/7528f9f08f80c8e85951aa1db2dc616e992c1a62 create mode 100644 webm_parser/fuzzing/corpus/75af94f9695c375e5eb620849f0213ad41e8f205 create mode 100644 webm_parser/fuzzing/corpus/75e1ff2e016b7b80ecb7a6a1f4c3238a2a2ed130 create mode 100644 webm_parser/fuzzing/corpus/76129c3bdedafb3bda93f53266bd17bffdc30682 create mode 100644 webm_parser/fuzzing/corpus/7670db89e0c3fe492c91458de219c3be34a3dcb7 create mode 100644 webm_parser/fuzzing/corpus/7673d7fdf630637c6ae2b96694a047044c1a9cb0 create mode 100644 webm_parser/fuzzing/corpus/76903905e654ddc59acf57380bf3dc1ea136bad9 create mode 100644 webm_parser/fuzzing/corpus/76d36e3cf3d31e0f76af08bd7ba5571f679057a1 create mode 100644 webm_parser/fuzzing/corpus/775ce646f7dbf50199b8e8df85c9441b8a0a5447 create mode 100644 webm_parser/fuzzing/corpus/7770a6fd33ce821e60c78b0cb67a7e8c8bb74269 create mode 100644 webm_parser/fuzzing/corpus/781b24e3433ba400df3ccddf56cb8fc7b1fd52ce create mode 100644 webm_parser/fuzzing/corpus/784db9d87b31ffb040ad212e4018b30c3535ad18 create mode 100644 webm_parser/fuzzing/corpus/7880e2ae55d255065ad415c4c625e1b63bfd2a93 create mode 100644 webm_parser/fuzzing/corpus/78cb3d726e4f9e8bc89b399fa514c3b600bf8e5a create mode 100644 webm_parser/fuzzing/corpus/79ba60931988a5974328a73fe091bdf6f5992891 create mode 100644 webm_parser/fuzzing/corpus/79cdb8dec1ad07b389f544a511f89b347429837e create mode 100644 webm_parser/fuzzing/corpus/7ab8c6a65c39907f4afa4e2b294303aebb6b22d8 create mode 100644 webm_parser/fuzzing/corpus/7b12a398a1860ac2f3930d5020e422aec061f177 create mode 100644 webm_parser/fuzzing/corpus/7bf1682743405f3d5b3433830eadae4ea311807d create mode 100644 webm_parser/fuzzing/corpus/7c7ef0e305f37f833708b375271d59300d120d84 create mode 100644 webm_parser/fuzzing/corpus/7d06bd4a7de57953ef09c2e18ca67aa7ff367c76 create mode 100644 webm_parser/fuzzing/corpus/7d31bd53bb7eb7b1f354c5f85d376390760ab16f create mode 100644 webm_parser/fuzzing/corpus/7d67ef4d0c681655d4b4e93e848d56865630390b create mode 100644 webm_parser/fuzzing/corpus/7db9fe869081fdc855913dd000de0d493f5d5192 create mode 100644 webm_parser/fuzzing/corpus/7f20c1e73451c3321c30223db91b891753ef77dd create mode 100644 webm_parser/fuzzing/corpus/7fd4d999983c3fbf22763853e90ec10b03c70b17 create mode 100644 webm_parser/fuzzing/corpus/80039ce7eff40359bee041d75e371b55117aad3a create mode 100644 webm_parser/fuzzing/corpus/8104a007bc88a0a8d81ce1fd26d8b2333061e920 create mode 100644 webm_parser/fuzzing/corpus/81320c48ed6eea94712c5e8594c0799fbfe30d10 create mode 100644 webm_parser/fuzzing/corpus/81425ca3d0afb88cc10d412dcc9795905eacd6d9 create mode 100644 webm_parser/fuzzing/corpus/819b4bd08ae7d758990aea8ab9739f3cb97fd42c create mode 100644 webm_parser/fuzzing/corpus/8206d92b710c04ce0bd706cec25fbb72014c79bf create mode 100644 webm_parser/fuzzing/corpus/822926c528d16c0a9ca4c48a25651f6a0730d797 create mode 100644 webm_parser/fuzzing/corpus/82d80b6051f0750777bdc37319851741144b3671 create mode 100644 webm_parser/fuzzing/corpus/830c73748baaa10c2016d04b408a7f481882cd89 create mode 100644 webm_parser/fuzzing/corpus/8315ca46618bd0771353336407396df293e8c182 create mode 100644 webm_parser/fuzzing/corpus/831c152337e8b2993b8cddfde7553a8c9e64631f create mode 100644 webm_parser/fuzzing/corpus/832fad0e723027e5bd74726b1722a838183dada7 create mode 100644 webm_parser/fuzzing/corpus/83b78d2ad8afd0729de45b4d9fdd765a949d8073 create mode 100644 webm_parser/fuzzing/corpus/84a04bf3e15345f5c992bc6732a42b95857631a7 create mode 100644 webm_parser/fuzzing/corpus/84d3a72c434074ee0d810fb2357047efd23f58f6 create mode 100644 webm_parser/fuzzing/corpus/8659fe309298ca90f201ac7ced95b9d0de510eeb create mode 100644 webm_parser/fuzzing/corpus/866cf21064b9e20eded44e2e096fd9ce6185ce86 create mode 100644 webm_parser/fuzzing/corpus/8864c6c9319ef120d3accb7b70c32c8f5c1ebccb create mode 100644 webm_parser/fuzzing/corpus/88f01cffe9972bf447a21034c45f6b943ea3ec07 create mode 100644 webm_parser/fuzzing/corpus/88f15d430469d209b72cd098405efd15a6d18b05 create mode 100644 webm_parser/fuzzing/corpus/8936b35f29084151fa3755d81030386940523c07 create mode 100644 webm_parser/fuzzing/corpus/89a42d72ffe0b23309a9da4e15b8854c1cf939ea create mode 100644 webm_parser/fuzzing/corpus/8a0cf07d2592231bd579e4399538d9f490e3e5b9 create mode 100644 webm_parser/fuzzing/corpus/8a5d1c05f894414aa5aaa66cf901c52dfcb119a1 create mode 100644 webm_parser/fuzzing/corpus/8aa514b6e6fef8d045aa049fdb52fa8adcf722d4 create mode 100644 webm_parser/fuzzing/corpus/8b0dcd4400fe19cf6a268af780990f47dba08189 create mode 100644 webm_parser/fuzzing/corpus/8c4655e6528071edd445715ff1559f399dc9d47c create mode 100644 webm_parser/fuzzing/corpus/8c695cbcc43e37ce25194c4b61b3d87488b308a1 create mode 100644 webm_parser/fuzzing/corpus/8c9dc3c5a839f90f29256ef8c8f1369bc7383817 create mode 100644 webm_parser/fuzzing/corpus/8cdcf83eb89d7e5c87cfa1b8811c95f738747a6a create mode 100644 webm_parser/fuzzing/corpus/8d0c6e6b3d74233685c3a2c1793d1a7b0ae2e34d create mode 100644 webm_parser/fuzzing/corpus/8d444e8b311d158f854a03ca35d2c7865cd2c46b create mode 100644 webm_parser/fuzzing/corpus/8dc289730cc03ecbbed57d04cdca538a4fd08059 create mode 100644 webm_parser/fuzzing/corpus/8dc58cfcb396a60db921901f1b0807c4f4f37619 create mode 100644 webm_parser/fuzzing/corpus/8e05be75b7c14c463af277559dc8ea7841a733c8 create mode 100644 webm_parser/fuzzing/corpus/8e7eef00a7a6719e77544b57d1d98ff83a37e55a create mode 100644 webm_parser/fuzzing/corpus/8ed6ef9af5555ce4ff12b23139add9621e7ad930 create mode 100644 webm_parser/fuzzing/corpus/8f39f83b71d49805a451f81a5a451f5f3d74927d create mode 100644 webm_parser/fuzzing/corpus/8f403efbef079f1775b63bcf1061983e4c5d2162 create mode 100644 webm_parser/fuzzing/corpus/8f8f956eaaf8f7f5e65002be888b946218f0f7ce create mode 100644 webm_parser/fuzzing/corpus/90a03241b5cc656b7c2a4ef55664b0320fa3bd43 create mode 100644 webm_parser/fuzzing/corpus/925a91e7c25a43d9da8b63eba51ed66a52d833aa create mode 100644 webm_parser/fuzzing/corpus/92de5c217802ec24886240188dca293325eb17c9 create mode 100644 webm_parser/fuzzing/corpus/939da66e1fc8bb9854865066ee47b6ac4b933aa9 create mode 100644 webm_parser/fuzzing/corpus/93a91331e7c3c060f461ae1f22e2bbf4747ffe33 create mode 100644 webm_parser/fuzzing/corpus/94f6dfc499cbf2e84ffe1d69cb751ada9f2f2e3a create mode 100644 webm_parser/fuzzing/corpus/9539fef8ffb9f7c542061f1af1f3f98e9a714cc3 create mode 100644 webm_parser/fuzzing/corpus/953c9a616ea56067225c88fccc6423496f2501a6 create mode 100644 webm_parser/fuzzing/corpus/95a60268c555a77c1010d75bb44142af47635486 create mode 100644 webm_parser/fuzzing/corpus/95e410025b965cf6ab2e8e2d3559efbe71c1a972 create mode 100644 webm_parser/fuzzing/corpus/9671f253d1eb7cd504a5617058ce4c01a80c897e create mode 100644 webm_parser/fuzzing/corpus/9698d3424f1581a6bbb66c764f14c95a74b87675 create mode 100644 webm_parser/fuzzing/corpus/96f7f38de1f8601311733984cf51cec35500dabe create mode 100644 webm_parser/fuzzing/corpus/970ab6333aa5ccf8c2dc14bb56814f7bb4303b94 create mode 100644 webm_parser/fuzzing/corpus/98978113d2116dc4bdbb10265fa32bd95230bdb6 create mode 100644 webm_parser/fuzzing/corpus/989e2872d2a34de543f23c5061db68212d8258f7 create mode 100644 webm_parser/fuzzing/corpus/98ca9a00ad571c4454fce709d5405e5aca2a363c create mode 100644 webm_parser/fuzzing/corpus/9965361765a4151902ec04fb21d9247b3a5ed10d create mode 100644 webm_parser/fuzzing/corpus/99d6fe94a50faa50db9d7eb38d74bc3cc8417dc1 create mode 100644 webm_parser/fuzzing/corpus/9a6869cec3dc84f2051bfaf0ee0d3552aa221f89 create mode 100644 webm_parser/fuzzing/corpus/9cc1f26de1e3a7df8c7c03b95ff73ce9709c85f1 create mode 100644 webm_parser/fuzzing/corpus/9cc5b552abbd551485135fa87eab739a0a784057 create mode 100644 webm_parser/fuzzing/corpus/9cca361865a4fbff75abdbb79c1e91706780576c create mode 100644 webm_parser/fuzzing/corpus/9d8e99f07604d6cb05ef613d41cbfb93b2aff787 create mode 100644 webm_parser/fuzzing/corpus/9ecd61eaf2681a882473247a603b9f30c2663d49 create mode 100644 webm_parser/fuzzing/corpus/9f0a3b7c0814b4f80c0745161c8769f63098981b create mode 100644 webm_parser/fuzzing/corpus/9f711c29ccf3f54d44000d7ef6299585674be288 create mode 100644 webm_parser/fuzzing/corpus/9fae60819c28d4fcc88a6a1b93dcf69b4e458203 create mode 100644 webm_parser/fuzzing/corpus/a02431cf7c501a5b368c91e41283419d8fa9fb03 create mode 100644 webm_parser/fuzzing/corpus/a0ac6c3c83817637bbbcb11a5106c57aa6654afb create mode 100644 webm_parser/fuzzing/corpus/a15fdfa620d19a92d9eaa9f3f13010e53f902796 create mode 100644 webm_parser/fuzzing/corpus/a18e76ae792a054c2f6d0d01e0e78d58678b35e3 create mode 100644 webm_parser/fuzzing/corpus/a19d04f18f574e561d793ac0dfcffe2b38183eb1 create mode 100644 webm_parser/fuzzing/corpus/a26fb85be3d2bb8a2360bb4d9533a1651bd12d99 create mode 100644 webm_parser/fuzzing/corpus/a2e860fae30005a9e75b61f54f3d019c44090fcc create mode 100644 webm_parser/fuzzing/corpus/a3071bfcb7b2fd3c4286ea42e1f7940754b55697 create mode 100644 webm_parser/fuzzing/corpus/a4ac408fb9d6def070ad3a76312ca092863048e5 create mode 100644 webm_parser/fuzzing/corpus/a4fafc117cbfde8c240deccc8997c7966d9109ba create mode 100644 webm_parser/fuzzing/corpus/a59ffb5f6122e45136352585d3b53294a71346d0 create mode 100644 webm_parser/fuzzing/corpus/a5b3a3c48727c26dfd625f247069d2cdbfa031f0 create mode 100644 webm_parser/fuzzing/corpus/a5c829fbcd9fd760bc55bc8ab6901b8d401b65f6 create mode 100644 webm_parser/fuzzing/corpus/a5fbbce038cad4f5e0c0f97fa69ebc3601123e5c create mode 100644 webm_parser/fuzzing/corpus/a6e57b33e7a219168280e51bc98a44de40f0f9ca create mode 100644 webm_parser/fuzzing/corpus/a78afe9e4e9f02a10ebadac64171ad49749a6965 create mode 100644 webm_parser/fuzzing/corpus/a823a019c0b19c97a1d35722cd843109ab016c17 create mode 100644 webm_parser/fuzzing/corpus/a8468104c65a6002fd3a9d4ac39f3ee34c21ce4a create mode 100644 webm_parser/fuzzing/corpus/a899424027f1d69a05384355858311a6fa3940a3 create mode 100644 webm_parser/fuzzing/corpus/a8a5de5a86a16952aacdf602120f27807294c3ec create mode 100644 webm_parser/fuzzing/corpus/aa65229e62d7cb8048d2f5911226b177e8e53cab create mode 100644 webm_parser/fuzzing/corpus/aaa96713f8ccb0bbd5c8e91715d4b86e7b338676 create mode 100644 webm_parser/fuzzing/corpus/aae6354e5ba12ee3ad89fabfd72f5368a639d30f create mode 100644 webm_parser/fuzzing/corpus/ab6f3dc497f93f251ebacc153409b1eb8e05e2db create mode 100644 webm_parser/fuzzing/corpus/abf5b18d1c1155d3e455c8b781948498f364965e create mode 100644 webm_parser/fuzzing/corpus/abfe5dbf594a2f22173fae7ca8de28b4e3a40099 create mode 100644 webm_parser/fuzzing/corpus/ac842f2cc55d7f193273a35f8af3521bfe2317d0 create mode 100644 webm_parser/fuzzing/corpus/ac923c36dd85ff5cb2a0c5b29c701999c5f2eaa0 create mode 100644 webm_parser/fuzzing/corpus/acc6e100b519d6408a8c6d8aed19203d874a187a create mode 100644 webm_parser/fuzzing/corpus/aceb250195867e7d9bbf6eb6e0055251e4bb5d67 create mode 100644 webm_parser/fuzzing/corpus/acf1dd17e6a2848dfe17fb0d76cef2ce7d59ebdd create mode 100644 webm_parser/fuzzing/corpus/ad94c2bda2ae4afa0f7264070fd642bf37aae596 create mode 100644 webm_parser/fuzzing/corpus/adad2ca7ab313add6e955f704719e03d5229e4d0 create mode 100644 webm_parser/fuzzing/corpus/adb1f8cd3b68c076d81311071aee2a1c3785c7cb create mode 100644 webm_parser/fuzzing/corpus/ade30c327d9f51daf37cb3e39fdb15125b84a257 create mode 100644 webm_parser/fuzzing/corpus/ae783661f52d36f9d6b87c8394ee6f2d61dae640 create mode 100644 webm_parser/fuzzing/corpus/ae8da06c3c69076c06108219e19c8b36dba05a5c create mode 100644 webm_parser/fuzzing/corpus/aec8a29238dba3c37f45f2e2e4e64b0e7fa60a74 create mode 100644 webm_parser/fuzzing/corpus/affba8e4061bf260b1bdf8a815675cc3b4ebefa5 create mode 100644 webm_parser/fuzzing/corpus/b01c4e2657a230e2b600a20da6f107b0dfe2467c create mode 100644 webm_parser/fuzzing/corpus/b0798ecae4d9e56eefb0c1d95b657865938e62d6 create mode 100644 webm_parser/fuzzing/corpus/b0ab4f92df810edf4371baad1d4bbd95d360c607 create mode 100644 webm_parser/fuzzing/corpus/b14e5f5f6d29b6b71e7fec03eaf7d8237c0c6b2e create mode 100644 webm_parser/fuzzing/corpus/b1708df7f6dee0f2feb11f8b6330be0fcffc1f58 create mode 100644 webm_parser/fuzzing/corpus/b1af4f4e86890fad6fac96a23956405c550778be create mode 100644 webm_parser/fuzzing/corpus/b1efd9e687d79e3f5a75eba02bd80bedb72350ba create mode 100644 webm_parser/fuzzing/corpus/b25b0fbf46cef1d888fe900445c9ab95330f44cd create mode 100644 webm_parser/fuzzing/corpus/b2786e0b9d6e7b39165eb4e87e3110362e9e6660 create mode 100644 webm_parser/fuzzing/corpus/b332536a77d6efcd379cfc2f9828291516cc1ff4 create mode 100644 webm_parser/fuzzing/corpus/b33962eca397f59591dbb9668e622cf99f2d7cda create mode 100644 webm_parser/fuzzing/corpus/b38b6e93da1b43441f88aa53370a9f00b35c3326 create mode 100644 webm_parser/fuzzing/corpus/b3e01674a1e4dd78e748782fcfc3add5523f51d8 create mode 100644 webm_parser/fuzzing/corpus/b53e7ef9aad70fcd80986696ebc586c03495b8ed create mode 100644 webm_parser/fuzzing/corpus/b540412c01f960f95edd2a1bc03f1b4447f2b4f2 create mode 100644 webm_parser/fuzzing/corpus/b62c2c591db32b26e997aa4ece577742db84428a create mode 100644 webm_parser/fuzzing/corpus/b62d99583f30c15c3c2dbed2f69c5e45075d7640 create mode 100644 webm_parser/fuzzing/corpus/b62f98976c11d79674b019ea78a7ce4d6d78b479 create mode 100644 webm_parser/fuzzing/corpus/b6787dabeb5cd64ac85f1ec5a7cbeecfd48b2c5f create mode 100644 webm_parser/fuzzing/corpus/b82ebcf4d09ba28d835cb9667da603e46e3438eb create mode 100644 webm_parser/fuzzing/corpus/b85c6bc2473aa12e22f91db3546dfa9d85d8b3d1 create mode 100644 webm_parser/fuzzing/corpus/b86e98660980680890bcbf02cacdf568d530fa64 create mode 100644 webm_parser/fuzzing/corpus/b8d9beea35762009941189674c2cfcd14f81254a create mode 100644 webm_parser/fuzzing/corpus/ba517141fc9a468142a8d03d4ee395b4d4f30edc create mode 100644 webm_parser/fuzzing/corpus/bb0a596017cfc2185505d28065ab3cd238d0e2ea create mode 100644 webm_parser/fuzzing/corpus/bb6232815b373e441e2acfcca015f75c680eafac create mode 100644 webm_parser/fuzzing/corpus/bbdf4fa36ba9d645399f72c74033fd9c2631aebb create mode 100644 webm_parser/fuzzing/corpus/bc53c9f93974b4f13382c1d49b1e3ec374005ee2 create mode 100644 webm_parser/fuzzing/corpus/bcacc9bcd3b9cc7dbda9c52c6e4a06a756a9de90 create mode 100644 webm_parser/fuzzing/corpus/bcc55f432bcf39c6ffc6d7e950612b35e9ea2ec8 create mode 100644 webm_parser/fuzzing/corpus/bdb1cc868d6ced390f5d35c5f43da9f464fac464 create mode 100644 webm_parser/fuzzing/corpus/bf67bb08e0abde692748031c297bc1c7910542f4 create mode 100644 webm_parser/fuzzing/corpus/bfc07c62ef2770d53d9188b260f531a8128a5c5e create mode 100644 webm_parser/fuzzing/corpus/bfed121df31ff73b770ed7f27fc7f4da2fa73e0c create mode 100644 webm_parser/fuzzing/corpus/c01816206d93691165e6e3a1924cf9fbc9cf39bd create mode 100644 webm_parser/fuzzing/corpus/c02be525edae59ad9d0d9dcbd790629dc9aaee9b create mode 100644 webm_parser/fuzzing/corpus/c094ce0c13ee9a4ca37817d9f7dddc11b2d60177 create mode 100644 webm_parser/fuzzing/corpus/c0aab5486ad2e80bcc12fca9fb6653984aa68274 create mode 100644 webm_parser/fuzzing/corpus/c13ca850db259c032cf24cdf6f2833c9d74529d0 create mode 100644 webm_parser/fuzzing/corpus/c1a8da6cdc8988e6a69961413803acbd1ee935e0 create mode 100644 webm_parser/fuzzing/corpus/c25abc82a0470129f2d098ac65fabf34c4e11188 create mode 100644 webm_parser/fuzzing/corpus/c2787d2cf1d95cbcd8b9bcd15d50f67f7e92ad9f create mode 100644 webm_parser/fuzzing/corpus/c2895ff545ebdc4140951c0ca956524d7a364b77 create mode 100644 webm_parser/fuzzing/corpus/c2cc55849ff4858bf80f1a4713187618d14a496d create mode 100644 webm_parser/fuzzing/corpus/c2d63b1d75cf53ee3b955bb143036ca93ef3a256 create mode 100644 webm_parser/fuzzing/corpus/c372a27f78a62ebc013958fa4953a8bc792db53c create mode 100644 webm_parser/fuzzing/corpus/c392963b395a7f92b3bef63fd34bc31ecd3029f3 create mode 100644 webm_parser/fuzzing/corpus/c3b2749ab6c4d303bfd5da9ea9c8807e9f92d259 create mode 100644 webm_parser/fuzzing/corpus/c4dd3c8cdd8d7c95603dd67f1cd873d5f9148b29 create mode 100644 webm_parser/fuzzing/corpus/c52fa8d16e520980e470d76e3fb4f6a612f0f3cf create mode 100644 webm_parser/fuzzing/corpus/c56fb214efcd707e6fa68b803d9e7686fc2f4336 create mode 100644 webm_parser/fuzzing/corpus/c5902afe54998ebc5d8d59043227d379a8c2eee0 create mode 100644 webm_parser/fuzzing/corpus/c6a16abeea323833079e97b1830610aa6c6eba91 create mode 100644 webm_parser/fuzzing/corpus/c6fcfd2a1f91a7f6a8c124f2637e60645be01006 create mode 100644 webm_parser/fuzzing/corpus/c75b02a90370df1f54de2f63a4da8db22f2cf719 create mode 100644 webm_parser/fuzzing/corpus/c77cb763d73db279aebfb42d2b5dca3d705d3df7 create mode 100644 webm_parser/fuzzing/corpus/c864ac8bce0353ecc7d5cb0ce5a1e77a5239201e create mode 100644 webm_parser/fuzzing/corpus/c877c08a79a7408aed779d4a430c5db1bce26314 create mode 100644 webm_parser/fuzzing/corpus/c8aff6e2e2dfb18be385483b871ac86ff6eac63f create mode 100644 webm_parser/fuzzing/corpus/c8c1c1f970bc809a75ad076bdb06275b6f72d078 create mode 100644 webm_parser/fuzzing/corpus/c995d52934ac188971f02d5dbdb3cdd70cb03267 create mode 100644 webm_parser/fuzzing/corpus/c9e7fc3e0e1015a1c15992447f678a495c3ea5dc create mode 100644 webm_parser/fuzzing/corpus/ca5b619ce1bbe23d519f5764d53458d2b85eb9fa create mode 100644 webm_parser/fuzzing/corpus/cb0556c65c7381192c94324a3b1b8afb7e33fecc create mode 100644 webm_parser/fuzzing/corpus/cb1a093e6810c7f6c002a2a54ea390cf769c9949 create mode 100644 webm_parser/fuzzing/corpus/cc1487af64aeefd7080e7678a04870dc85a7928a create mode 100644 webm_parser/fuzzing/corpus/cc71d2c9f5eae12acee133bd9e50d84881a1bd88 create mode 100644 webm_parser/fuzzing/corpus/ccb8f962426683663972534c15354f70c3b34a10 create mode 100644 webm_parser/fuzzing/corpus/cd0771c4754dfcd9c89b9b8a02df96fed974850d create mode 100644 webm_parser/fuzzing/corpus/cd8899c66cbd92ee57f94e000744b32662258ba3 create mode 100644 webm_parser/fuzzing/corpus/cdf83138f69f0f7c66c13b56028610ac39038b3c create mode 100644 webm_parser/fuzzing/corpus/ce0138cd7718397b365d4c15b0b14f666c307419 create mode 100644 webm_parser/fuzzing/corpus/cec5c7e50ef1d865c879563d1a6d677adb86695c create mode 100644 webm_parser/fuzzing/corpus/ceff1dfaf2de4e33d2e3c20aeb7ba4b98f97784c create mode 100644 webm_parser/fuzzing/corpus/cf04b5d28cea1971478806979b256a030a671541 create mode 100644 webm_parser/fuzzing/corpus/cf1cf6ad5b3554c3ffc86a859319445158665ea7 create mode 100644 webm_parser/fuzzing/corpus/cf2338960588d4a5f02a47f3ee96a556224aad75 create mode 100644 webm_parser/fuzzing/corpus/cfb09018afa0eb1a829e556d9f8bceff40cb36bc create mode 100644 webm_parser/fuzzing/corpus/cfbe3d66d7eb3199440e8e911a93044cc3165c6c create mode 100644 webm_parser/fuzzing/corpus/d0a91f7984904976de592cb68e8832853fbec9bc create mode 100644 webm_parser/fuzzing/corpus/d22f52563c16725ce4a924dc05d6ac7d64798c43 create mode 100644 webm_parser/fuzzing/corpus/d2709a1c9d96e72cb844eedca8bfe8440cdb0ef8 create mode 100644 webm_parser/fuzzing/corpus/d2e385f61ad4b5d45a10a5a6cb85d72e84dea54b create mode 100644 webm_parser/fuzzing/corpus/d2facb213561b30a5bcd012e4e01d0d5b0b26957 create mode 100644 webm_parser/fuzzing/corpus/d300e5e46a825e5892fae1ba3c466c836f9e1da2 create mode 100644 webm_parser/fuzzing/corpus/d302c69c881e230e6433283007d318ba437025c3 create mode 100644 webm_parser/fuzzing/corpus/d30c1b65bc141d1a15d4ed622ba182c96dacdf92 create mode 100644 webm_parser/fuzzing/corpus/d3adf09fe6fb1534157c1dc68eb61365b46b1c35 create mode 100644 webm_parser/fuzzing/corpus/d3c74d3a64dba86f98a31bb726587ec97a7e4531 create mode 100644 webm_parser/fuzzing/corpus/d3c9846ab319f12fc646c23a532c780daa9e993f create mode 100644 webm_parser/fuzzing/corpus/d3f03301b52cf4a830c7dd200ed8ccbc09e6ec94 create mode 100644 webm_parser/fuzzing/corpus/d4800745440dace38766db3520ffe7baa0bd78f2 create mode 100644 webm_parser/fuzzing/corpus/d4d6271bba704ba08c2678eb8b1bc4e457144f50 create mode 100644 webm_parser/fuzzing/corpus/d54020f766061e80f445690c2b5694ed8af21e9d create mode 100644 webm_parser/fuzzing/corpus/d588515f125ebe968fe6a81cb6df7cfc41969e68 create mode 100644 webm_parser/fuzzing/corpus/d5ee5e4dc8fad9f1da43102d8322beb005a047c0 create mode 100644 webm_parser/fuzzing/corpus/d62459bb217d3050bcd9e29a6327cf81f5ed68b9 create mode 100644 webm_parser/fuzzing/corpus/d6acbf1cb46845618ed0d5a322c8bd4879d16422 create mode 100644 webm_parser/fuzzing/corpus/d6fc8cdbb0f1517159531098e900e2dcc91edba0 create mode 100644 webm_parser/fuzzing/corpus/d7a9bbd9875a60edf9c528b298ea72a2fad7d3d7 create mode 100644 webm_parser/fuzzing/corpus/d82e70046a544e95e81f6271dd2695f2599f0574 create mode 100644 webm_parser/fuzzing/corpus/d84bc8dca7c8fcd227254c06f5b88eaaf7cd5fde create mode 100644 webm_parser/fuzzing/corpus/d8bcb7dd21205e7126e700323b1d58817e9d9a6d create mode 100644 webm_parser/fuzzing/corpus/d8e2628b4092b9bbab4f02041647f950503eeb48 create mode 100644 webm_parser/fuzzing/corpus/d8ee4c2b79863a237c432efe7d43d99c8d0afb9b create mode 100644 webm_parser/fuzzing/corpus/d8ee9724bf16ff336387723dcf27319c3be72e01 create mode 100644 webm_parser/fuzzing/corpus/d9379969bc956ad623e4bab8bbe47270a880a62d create mode 100644 webm_parser/fuzzing/corpus/d9ccb79b0e070dcc2f5ed8e15d99bc5ef86ecff7 create mode 100644 webm_parser/fuzzing/corpus/db83586ca6266e03067e9a9772ea728ab770ad9a create mode 100644 webm_parser/fuzzing/corpus/dcc63c06ed2790d1380bdb9281fe8677f439c76d create mode 100644 webm_parser/fuzzing/corpus/dd509e9a4660ec34b8f9dc23441c6df4ff97c34d create mode 100644 webm_parser/fuzzing/corpus/dd72f8ff3a067dc7871438b6023e1ed0a4c5787b create mode 100644 webm_parser/fuzzing/corpus/dd75880c5ad488885260f4031a763c86a8084406 create mode 100644 webm_parser/fuzzing/corpus/dd962d74d04aa4aed270fd8e6b0ae9c4ac35fd19 create mode 100644 webm_parser/fuzzing/corpus/dd9aa9a49dd790b2ce99c5af1933232d46a7f80d create mode 100644 webm_parser/fuzzing/corpus/ddad7630818a1caa8054d2d7280a1d01bdb33ca3 create mode 100644 webm_parser/fuzzing/corpus/ddb2eaf33960ce69d579a551a7b05733adcd52aa create mode 100644 webm_parser/fuzzing/corpus/de0d98cb997c0a0f7be127a46d8a24d8a003931d create mode 100644 webm_parser/fuzzing/corpus/de0ff884898c83fb880498f1b8328f78701e6534 create mode 100644 webm_parser/fuzzing/corpus/de9e0ed8ae29220e5b65e5b97eb3b254ccbe7e0c create mode 100644 webm_parser/fuzzing/corpus/dec5e8ffb35aa707d127a11a49e47cb59954e969 create mode 100644 webm_parser/fuzzing/corpus/df1b205e339e7199b5094fcf0ec3b8a8ca7a692c create mode 100644 webm_parser/fuzzing/corpus/df254daba2299f3ff2367e284efc53a3296d136b create mode 100644 webm_parser/fuzzing/corpus/dfdcde31231b8b3d3fd4e0ef8ea88885f488c4db create mode 100644 webm_parser/fuzzing/corpus/e01962d7dc1b94e5c4424ec7adae16a7c03b9773 create mode 100644 webm_parser/fuzzing/corpus/e058723f2964bf1405ae043ddb99efb17d821e15 create mode 100644 webm_parser/fuzzing/corpus/e09e306ef596dcd0e44bd3ef3c5f7019c7343f84 create mode 100644 webm_parser/fuzzing/corpus/e154eb76d096c1e545dcad591a58a02c37cd71ff create mode 100644 webm_parser/fuzzing/corpus/e198d2e4f2f3528c2ff46d769a9281ebb3cfb44a create mode 100644 webm_parser/fuzzing/corpus/e2068af1e903f4a81cd6fa5f4022e62070c259ec create mode 100644 webm_parser/fuzzing/corpus/e2890330b5655cb277a581b8dd2eeba0d9061ba5 create mode 100644 webm_parser/fuzzing/corpus/e2e0767d055a7042c24a7acd5d5b6b7c093ad065 create mode 100644 webm_parser/fuzzing/corpus/e394a8e21e2c43c42135daa034ad5aabb06acffb create mode 100644 webm_parser/fuzzing/corpus/e3b200e97ec226a197e91f12103aaa53d5c37b0e create mode 100644 webm_parser/fuzzing/corpus/e45b077cda64c380ae1b0910bc81d010c27e1c93 create mode 100644 webm_parser/fuzzing/corpus/e45e71ca01ebe01b01b0ca99b8f20a756c36b967 create mode 100644 webm_parser/fuzzing/corpus/e4ff4bd938c3737a02862fc0656a1859d384d066 create mode 100644 webm_parser/fuzzing/corpus/e5a1acb7e6f71bc1a2fedaa6173764dfd04c844c create mode 100644 webm_parser/fuzzing/corpus/e6081eeff4ddb76e88e87ef9e4b5f199f5f11c28 create mode 100644 webm_parser/fuzzing/corpus/e73c42dd266c4d9671da0c7af09e98c02fc052fd create mode 100644 webm_parser/fuzzing/corpus/e7b4559a77df21b73285243a8350b844775bb380 create mode 100644 webm_parser/fuzzing/corpus/e7dd34a80646a8c38ae1ec3a27c1358bab13b360 create mode 100644 webm_parser/fuzzing/corpus/e80b344f943ff0ad9219277c4578d3b4100b71dc create mode 100644 webm_parser/fuzzing/corpus/e86e26200290d9d428c5e98e191ec874eb918fb6 create mode 100644 webm_parser/fuzzing/corpus/e873a3b8f5c3b716e6446df34279b837cf8d2c30 create mode 100644 webm_parser/fuzzing/corpus/e8fb71319db98d8e8cd131a4eb82879bfbec14d0 create mode 100644 webm_parser/fuzzing/corpus/e93c3f14614595a2675993438b4c1bfaafdc02d0 create mode 100644 webm_parser/fuzzing/corpus/e93faef2d77b7c467ae280ba433928d66d63ea63 create mode 100644 webm_parser/fuzzing/corpus/e95401b11d974ba63270668d3c32c29e95ae85da create mode 100644 webm_parser/fuzzing/corpus/e9723cebe688912f684bcd19b48e5bc8efafaf2a create mode 100644 webm_parser/fuzzing/corpus/e9a4389895c006d4b912e8ac1169229e6b2a66da create mode 100644 webm_parser/fuzzing/corpus/e9dc3d10b47ea580404c8e80b844a9978fcf4747 create mode 100644 webm_parser/fuzzing/corpus/e9e2ac24e5674e7ee424e2b270e5abe84f1a15c9 create mode 100644 webm_parser/fuzzing/corpus/ea2b4df24b526aad253ab175334cb934b9d80b83 create mode 100644 webm_parser/fuzzing/corpus/ea681d11486feeab2f080b06ddd2533575b7ace4 create mode 100644 webm_parser/fuzzing/corpus/ebb747925360528a9f366f9a57730883c636b2c7 create mode 100644 webm_parser/fuzzing/corpus/ec39abab70a8e1ff072eb082caa6ca77b1ae8087 create mode 100644 webm_parser/fuzzing/corpus/ecdd96d6cab2dc714a0b0ada1c4fcb18c75298e4 create mode 100644 webm_parser/fuzzing/corpus/ed008faa6c9001951f50588a1597e03931501343 create mode 100644 webm_parser/fuzzing/corpus/ed12e272a27a2fbdbfe5a6e78e49ed722ebd3c69 create mode 100644 webm_parser/fuzzing/corpus/ed799ba0608690ac68dd85c588004197b86e02bf create mode 100644 webm_parser/fuzzing/corpus/edd982c5bd3030bde8c044760e9a678c2a9306f4 create mode 100644 webm_parser/fuzzing/corpus/ee82c97e35ec92ec3b0bbf911904a050b3aca633 create mode 100644 webm_parser/fuzzing/corpus/ef11f14feee00e3c198015e6bc76688e970e2d8c create mode 100644 webm_parser/fuzzing/corpus/efa12e91e0d63c2353c120ca1ded7b36afbf57eb create mode 100644 webm_parser/fuzzing/corpus/efa14cba9bbaf749067b7bb8515a5d8a289fa389 create mode 100644 webm_parser/fuzzing/corpus/eff9bf13cd33ea50a8eacc5f2839cc4b5d67b2de create mode 100644 webm_parser/fuzzing/corpus/f0143756917f0b2e374bd03e731cd64168180054 create mode 100644 webm_parser/fuzzing/corpus/f0b9dcd4e1845f774bb0f42653b11040baee0765 create mode 100644 webm_parser/fuzzing/corpus/f1a41ceb420b6b3df50b10f27108c133d4d07508 create mode 100644 webm_parser/fuzzing/corpus/f39378fd978c6cdb4a8d08cdffc9034e2ca5b687 create mode 100644 webm_parser/fuzzing/corpus/f39707a104112d13d9d6bcfbef0efe8dfd98270c create mode 100644 webm_parser/fuzzing/corpus/f4341645a7d466113655d63c5aa00855904166c5 create mode 100644 webm_parser/fuzzing/corpus/f5511a42d83f94619ff8ca6c940cacc32bdc4834 create mode 100644 webm_parser/fuzzing/corpus/f57a39fa918249e6941b4e770e15a8131ac16ea5 create mode 100644 webm_parser/fuzzing/corpus/f5ac272a1dbf362e265210869aaf70ca410f1703 create mode 100644 webm_parser/fuzzing/corpus/f64c5ca1de57bcf323741f56754f53c642be8ab0 create mode 100644 webm_parser/fuzzing/corpus/f64dcdee393b4b0a3343f8e684c9db91a6eaeb6e create mode 100644 webm_parser/fuzzing/corpus/f69d2954da077043c6ae085e2771a702689314d5 create mode 100644 webm_parser/fuzzing/corpus/f74e2203adb9c94ba80f7cc3214e3b3040e5675d create mode 100644 webm_parser/fuzzing/corpus/f7906e7dc01323b9d3d6e298e8dc2386c8d152f6 create mode 100644 webm_parser/fuzzing/corpus/f825cac511f3dc38a5ecf3aab3691b4c49ca8f0e create mode 100644 webm_parser/fuzzing/corpus/f84c3f9e305172f2ae15dff0b4d955324b3a398e create mode 100644 webm_parser/fuzzing/corpus/f87cfad97831c33610fbbe34a04369bd93862464 create mode 100644 webm_parser/fuzzing/corpus/f8ebd7703fd3ba1a135b243fd947dbd61907d0f4 create mode 100644 webm_parser/fuzzing/corpus/f926f6b2337650f8b518422c2f63a8869f47c742 create mode 100644 webm_parser/fuzzing/corpus/f939897b9fbe865c96020927dc81de9dc255d385 create mode 100644 webm_parser/fuzzing/corpus/f985f995ca7d1b691e6ae3b3ef57e8b3c7aa7129 create mode 100644 webm_parser/fuzzing/corpus/fa79e8ad34cabea4d3c434cc02ea1499069fcafc create mode 100644 webm_parser/fuzzing/corpus/fa7a8dfdd46845ab0fd9b5b7004e37d0232941bf create mode 100644 webm_parser/fuzzing/corpus/fcb3054fde86111e2c346aa71af2456a1705902c create mode 100644 webm_parser/fuzzing/corpus/fce30dcdf2f23b14c580c282a39e065d7aacbfe8 create mode 100644 webm_parser/fuzzing/corpus/fd15b8dd6c27bc65f90e1c988e0245b1ad7d51c5 create mode 100644 webm_parser/fuzzing/corpus/fd608012362d161cc7f3d30e1700c51f4ccef4ac create mode 100644 webm_parser/fuzzing/corpus/fdce5b4f3a038ce7cfeee4deb9a4644edb78189a create mode 100644 webm_parser/fuzzing/corpus/fe203731ada762e02bf843b82e33daee4c2efbf5 create mode 100644 webm_parser/fuzzing/corpus/fe46e6ef4cd5788d89a101c77123387e3fc9d206 create mode 100644 webm_parser/fuzzing/corpus/fe7ac2ef276b817af3487bab5fe089186ca0484b create mode 100644 webm_parser/fuzzing/corpus/fe7ef7c0e835873b7b1cd780f248114f18adf13a create mode 100644 webm_parser/fuzzing/corpus/ff5274cad94d590347d6cdba7637078f82e3d44a create mode 100644 webm_parser/fuzzing/corpus/ffff6a92363e0e55a9688d9bc025cd8dea3b50d4 create mode 100644 webm_parser/fuzzing/webm.dict create mode 100644 webm_parser/fuzzing/webm_fuzzer.cc create mode 100644 webm_parser/include/webm/buffer_reader.h create mode 100644 webm_parser/include/webm/callback.h create mode 100644 webm_parser/include/webm/dom_types.h create mode 100644 webm_parser/include/webm/element.h create mode 100644 webm_parser/include/webm/file_reader.h create mode 100644 webm_parser/include/webm/id.h create mode 100644 webm_parser/include/webm/istream_reader.h create mode 100644 webm_parser/include/webm/reader.h create mode 100644 webm_parser/include/webm/status.h create mode 100644 webm_parser/include/webm/webm_parser.h create mode 100644 webm_parser/src/ancestory.cc create mode 100644 webm_parser/src/ancestory.h create mode 100644 webm_parser/src/audio_parser.h create mode 100644 webm_parser/src/bit_utils.cc create mode 100644 webm_parser/src/bit_utils.h create mode 100644 webm_parser/src/block_additions_parser.h create mode 100644 webm_parser/src/block_group_parser.h create mode 100644 webm_parser/src/block_header_parser.cc create mode 100644 webm_parser/src/block_header_parser.h create mode 100644 webm_parser/src/block_more_parser.h create mode 100644 webm_parser/src/block_parser.cc create mode 100644 webm_parser/src/block_parser.h create mode 100644 webm_parser/src/bool_parser.h create mode 100644 webm_parser/src/buffer_reader.cc create mode 100644 webm_parser/src/byte_parser.h create mode 100644 webm_parser/src/callback.cc create mode 100644 webm_parser/src/chapter_atom_parser.h create mode 100644 webm_parser/src/chapter_display_parser.h create mode 100644 webm_parser/src/chapters_parser.h create mode 100644 webm_parser/src/cluster_parser.h create mode 100644 webm_parser/src/colour_parser.h create mode 100644 webm_parser/src/content_enc_aes_settings_parser.h create mode 100644 webm_parser/src/content_encoding_parser.h create mode 100644 webm_parser/src/content_encodings_parser.h create mode 100644 webm_parser/src/content_encryption_parser.h create mode 100644 webm_parser/src/cue_point_parser.h create mode 100644 webm_parser/src/cue_track_positions_parser.h create mode 100644 webm_parser/src/cues_parser.h create mode 100644 webm_parser/src/date_parser.cc create mode 100644 webm_parser/src/date_parser.h create mode 100644 webm_parser/src/ebml_parser.h create mode 100644 webm_parser/src/edition_entry_parser.h create mode 100644 webm_parser/src/element_parser.h create mode 100644 webm_parser/src/file_reader.cc create mode 100644 webm_parser/src/float_parser.cc create mode 100644 webm_parser/src/float_parser.h create mode 100644 webm_parser/src/id_element_parser.cc create mode 100644 webm_parser/src/id_element_parser.h create mode 100644 webm_parser/src/id_parser.cc create mode 100644 webm_parser/src/id_parser.h create mode 100644 webm_parser/src/info_parser.h create mode 100644 webm_parser/src/int_parser.h create mode 100644 webm_parser/src/istream_reader.cc create mode 100644 webm_parser/src/master_parser.cc create mode 100644 webm_parser/src/master_parser.h create mode 100644 webm_parser/src/master_value_parser.h create mode 100644 webm_parser/src/mastering_metadata_parser.h create mode 100644 webm_parser/src/parser.h create mode 100644 webm_parser/src/parser_utils.cc create mode 100644 webm_parser/src/parser_utils.h create mode 100644 webm_parser/src/projection_parser.h create mode 100644 webm_parser/src/recursive_parser.h create mode 100644 webm_parser/src/seek_head_parser.h create mode 100644 webm_parser/src/seek_parser.h create mode 100644 webm_parser/src/segment_parser.cc create mode 100644 webm_parser/src/segment_parser.h create mode 100644 webm_parser/src/simple_tag_parser.h create mode 100644 webm_parser/src/size_parser.cc create mode 100644 webm_parser/src/size_parser.h create mode 100644 webm_parser/src/skip_callback.h create mode 100644 webm_parser/src/skip_parser.cc create mode 100644 webm_parser/src/skip_parser.h create mode 100644 webm_parser/src/slices_parser.h create mode 100644 webm_parser/src/tag_parser.h create mode 100644 webm_parser/src/tags_parser.h create mode 100644 webm_parser/src/targets_parser.h create mode 100644 webm_parser/src/time_slice_parser.h create mode 100644 webm_parser/src/track_entry_parser.h create mode 100644 webm_parser/src/tracks_parser.h create mode 100644 webm_parser/src/unknown_parser.cc create mode 100644 webm_parser/src/unknown_parser.h create mode 100644 webm_parser/src/var_int_parser.cc create mode 100644 webm_parser/src/var_int_parser.h create mode 100644 webm_parser/src/video_parser.h create mode 100644 webm_parser/src/virtual_block_parser.cc create mode 100644 webm_parser/src/virtual_block_parser.h create mode 100644 webm_parser/src/void_parser.cc create mode 100644 webm_parser/src/void_parser.h create mode 100644 webm_parser/src/webm_parser.cc create mode 100644 webm_parser/test_utils/element_parser_test.h create mode 100644 webm_parser/test_utils/limited_reader.cc create mode 100644 webm_parser/test_utils/limited_reader.h create mode 100644 webm_parser/test_utils/mock_callback.h create mode 100644 webm_parser/test_utils/parser_test.h create mode 100644 webm_parser/tests/audio_parser_test.cc create mode 100644 webm_parser/tests/bit_utils_test.cc create mode 100644 webm_parser/tests/block_additions_parser_test.cc create mode 100644 webm_parser/tests/block_group_parser_test.cc create mode 100644 webm_parser/tests/block_header_parser_test.cc create mode 100644 webm_parser/tests/block_more_parser_test.cc create mode 100644 webm_parser/tests/block_parser_test.cc create mode 100644 webm_parser/tests/bool_parser_test.cc create mode 100644 webm_parser/tests/buffer_reader_test.cc create mode 100644 webm_parser/tests/byte_parser_test.cc create mode 100644 webm_parser/tests/callback_test.cc create mode 100644 webm_parser/tests/chapter_atom_parser_test.cc create mode 100644 webm_parser/tests/chapter_display_parser_test.cc create mode 100644 webm_parser/tests/chapters_parser_test.cc create mode 100644 webm_parser/tests/cluster_parser_test.cc create mode 100644 webm_parser/tests/colour_parser_test.cc create mode 100644 webm_parser/tests/content_enc_aes_settings_parser_test.cc create mode 100644 webm_parser/tests/content_encoding_parser_test.cc create mode 100644 webm_parser/tests/content_encodings_parser_test.cc create mode 100644 webm_parser/tests/content_encryption_parser_test.cc create mode 100644 webm_parser/tests/cue_point_parser_test.cc create mode 100644 webm_parser/tests/cue_track_positions_parser_test.cc create mode 100644 webm_parser/tests/cues_parser_test.cc create mode 100644 webm_parser/tests/date_parser_test.cc create mode 100644 webm_parser/tests/ebml_parser_test.cc create mode 100644 webm_parser/tests/edition_entry_parser_test.cc create mode 100644 webm_parser/tests/element_test.cc create mode 100644 webm_parser/tests/float_parser_test.cc create mode 100644 webm_parser/tests/id_element_parser_test.cc create mode 100644 webm_parser/tests/id_parser_test.cc create mode 100644 webm_parser/tests/info_parser_test.cc create mode 100644 webm_parser/tests/int_parser_test.cc create mode 100644 webm_parser/tests/istream_reader_test.cc create mode 100644 webm_parser/tests/limited_reader_test.cc create mode 100644 webm_parser/tests/master_parser_test.cc create mode 100644 webm_parser/tests/master_value_parser_test.cc create mode 100644 webm_parser/tests/mastering_metadata_parser_test.cc create mode 100644 webm_parser/tests/parser_utils_test.cc create mode 100644 webm_parser/tests/projection_parser_test.cc create mode 100644 webm_parser/tests/recursive_parser_test.cc create mode 100644 webm_parser/tests/seek_head_parser_test.cc create mode 100644 webm_parser/tests/seek_parser_test.cc create mode 100644 webm_parser/tests/segment_parser_test.cc create mode 100644 webm_parser/tests/simple_tag_parser_test.cc create mode 100644 webm_parser/tests/size_parser_test.cc create mode 100644 webm_parser/tests/skip_parser_test.cc create mode 100644 webm_parser/tests/slices_parser_test.cc create mode 100644 webm_parser/tests/tag_parser_test.cc create mode 100644 webm_parser/tests/tags_parser_test.cc create mode 100644 webm_parser/tests/targets_parser_test.cc create mode 100644 webm_parser/tests/time_slice_parser_test.cc create mode 100644 webm_parser/tests/track_entry_parser_test.cc create mode 100644 webm_parser/tests/tracks_parser_test.cc create mode 100644 webm_parser/tests/unknown_parser_test.cc create mode 100644 webm_parser/tests/var_int_parser_test.cc create mode 100644 webm_parser/tests/video_parser_test.cc create mode 100644 webm_parser/tests/virtual_block_parser_test.cc create mode 100644 webm_parser/tests/void_parser_test.cc create mode 100644 webm_parser/tests/webm_parser_test.cc create mode 100644 webm_parser/tests/webm_parser_tests.cc create mode 100644 webmids.hpp create mode 100644 webvtt/vttreader.cc create mode 100644 webvtt/vttreader.h create mode 100644 webvtt/webvttparser.cc create mode 100644 webvtt/webvttparser.h create mode 100644 webvttparser.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..7ce4e926b406 --- /dev/null +++ b/.clang-format @@ -0,0 +1,152 @@ +--- +Language: Cpp +# BasedOnStyle: Google +# Generated with clang-format 7.0.1 +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never +... diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..2b5cda5f04ee --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.sln eol=crlf +*.vcproj eol=crlf +*.vsprops eol=crlf +*.vcxproj eol=crlf +*.mkv -text -diff +*.webm -text -diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..2abbaa6cce01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +*.mkv +*.MKV +core +*.a +*.d +*.so* +*.o +*~ +*.swp +*.ncb +*.user +*.suo +*.exe +/*.webm +Debug +Release +*.sdf +*.opensdf +ipch +dumpvtt +mkvmuxer_sample +mkvparser_sample +vttdemux +mkvmuxer_tests +mkvparser_tests +webm_info +webm2pes +webm2pes_tests +webm2ts +vp9_header_parser_tests +vp9_level_stats_tests +Makefile +CMakeFiles +CMakeCache.txt +*.cmake diff --git a/AUTHORS.TXT b/AUTHORS.TXT new file mode 100644 index 000000000000..9686ac13eb0c --- /dev/null +++ b/AUTHORS.TXT @@ -0,0 +1,4 @@ +# Names should be added to this file like so: +# Name or Organization + +Google Inc. diff --git a/Android.mk b/Android.mk new file mode 100644 index 000000000000..b46ba101d420 --- /dev/null +++ b/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE:= libwebm +LOCAL_CPPFLAGS:=-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS +LOCAL_CPPFLAGS+=-D__STDC_LIMIT_MACROS -std=c++11 +LOCAL_C_INCLUDES:= $(LOCAL_PATH) +LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH) + +LOCAL_SRC_FILES:= common/file_util.cc \ + common/hdr_util.cc \ + mkvparser/mkvparser.cc \ + mkvparser/mkvreader.cc \ + mkvmuxer/mkvmuxer.cc \ + mkvmuxer/mkvmuxerutil.cc \ + mkvmuxer/mkvwriter.cc +include $(BUILD_STATIC_LIBRARY) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000000..a83d23bafca1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,453 @@ +## Copyright (c) 2015 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. +cmake_minimum_required(VERSION 3.2) +project(LIBWEBM CXX) + +include(GNUInstallDirs) +include("${CMAKE_CURRENT_SOURCE_DIR}/build/cxx_flags.cmake") + +if (NOT BUILD_SHARED_LIBS) + include("${CMAKE_CURRENT_SOURCE_DIR}/build/msvc_runtime.cmake") +endif () + +set(LIBWEBM_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + +# Build/test configuration flags. +option(ENABLE_WEBMTS "Enables WebM PES/TS support." ON) +option(ENABLE_WEBMINFO "Enables building webm_info." ON) +option(ENABLE_TESTS "Enables tests." OFF) +option(ENABLE_IWYU "Enables include-what-you-use support." OFF) +option(ENABLE_WERROR "Enable warnings as errors." OFF) +option(ENABLE_WEBM_PARSER "Enables new parser API." OFF) + +if(WIN32 OR CYGWIN OR MSYS) + # Allow use of rand_r() / fdopen() and other POSIX functions. + require_cxx_flag_nomsvc("-std=gnu++11") +else() + require_cxx_flag_nomsvc("-std=c++11") +endif() + +add_cxx_preproc_definition("__STDC_CONSTANT_MACROS") +add_cxx_preproc_definition("__STDC_FORMAT_MACROS") +add_cxx_preproc_definition("__STDC_LIMIT_MACROS") + +# Set up compiler flags and build properties. +include_directories("${LIBWEBM_SRC_DIR}") + +if (MSVC) + add_cxx_flag_if_supported("/W4") + # Disable MSVC warnings that suggest making code non-portable. + add_cxx_flag_if_supported("/wd4996") + if (ENABLE_WERROR) + add_cxx_flag_if_supported("/WX") + endif () +else () + add_cxx_flag_if_supported("-Wall") + add_cxx_flag_if_supported("-Wextra") + add_cxx_flag_if_supported("-Wnarrowing") + add_cxx_flag_if_supported("-Wno-deprecated") + add_cxx_flag_if_supported("-Wshorten-64-to-32") + if (ENABLE_WERROR) + add_cxx_flag_if_supported("-Werror") + endif () +endif () + +# Source list variables. +set(dumpvtt_sources "${LIBWEBM_SRC_DIR}/dumpvtt.cc") + +set(libwebm_common_sources + "${LIBWEBM_SRC_DIR}/common/file_util.cc" + "${LIBWEBM_SRC_DIR}/common/file_util.h" + "${LIBWEBM_SRC_DIR}/common/hdr_util.cc" + "${LIBWEBM_SRC_DIR}/common/hdr_util.h" + "${LIBWEBM_SRC_DIR}/common/webmids.h") + +set(mkvmuxer_sources + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxer.cc" + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxer.h" + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxertypes.h" + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxerutil.cc" + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxerutil.h" + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvwriter.cc" + "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvwriter.h" + "${LIBWEBM_SRC_DIR}/common/webmids.h") + +set(mkvmuxer_sample_sources + "${LIBWEBM_SRC_DIR}/mkvmuxer_sample.cc" + "${LIBWEBM_SRC_DIR}/sample_muxer_metadata.cc" + "${LIBWEBM_SRC_DIR}/sample_muxer_metadata.h") + +set(mkvmuxer_tests_sources + "${LIBWEBM_SRC_DIR}/testing/mkvmuxer_tests.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.h") + +set(mkvparser_sources + "${LIBWEBM_SRC_DIR}/mkvparser/mkvparser.cc" + "${LIBWEBM_SRC_DIR}/mkvparser/mkvparser.h" + "${LIBWEBM_SRC_DIR}/mkvparser/mkvreader.cc" + "${LIBWEBM_SRC_DIR}/mkvparser/mkvreader.h" + "${LIBWEBM_SRC_DIR}/common/webmids.h") + +set(mkvparser_sample_sources "${LIBWEBM_SRC_DIR}/mkvparser_sample.cc") +set(mkvparser_tests_sources + "${LIBWEBM_SRC_DIR}/testing/mkvparser_tests.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.h") + +set(vp9_header_parser_tests_sources + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser_tests.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h" + "${LIBWEBM_SRC_DIR}/testing/test_util.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.h") + +set(vp9_level_stats_tests_sources + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats_tests.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.h" + "${LIBWEBM_SRC_DIR}/testing/test_util.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.h") + +set(vttdemux_sources + "${LIBWEBM_SRC_DIR}/vttdemux.cc" + "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.cc" + "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.h") + +set(webm_parser_public_headers + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/buffer_reader.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/callback.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/dom_types.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/element.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/file_reader.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/id.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/istream_reader.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/reader.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/status.h" + "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/webm_parser.h") + +set(webm_parser_sources + ${webm_parser_public_headers} + "${LIBWEBM_SRC_DIR}/webm_parser/src/ancestory.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/ancestory.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/audio_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/bit_utils.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/bit_utils.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_additions_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_group_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_header_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_header_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_more_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/block_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/bool_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/buffer_reader.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/byte_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/callback.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/chapter_atom_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/chapter_display_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/chapters_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/cluster_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/colour_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/content_enc_aes_settings_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/content_encoding_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/content_encodings_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/content_encryption_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/cue_point_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/cue_track_positions_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/cues_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/date_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/date_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/ebml_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/edition_entry_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/element_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/file_reader.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/float_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/float_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/id_element_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/id_element_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/id_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/id_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/info_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/int_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/istream_reader.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/master_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/master_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/master_value_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/mastering_metadata_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/parser_utils.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/parser_utils.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/projection_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/recursive_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/seek_head_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/seek_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/segment_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/segment_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/simple_tag_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/size_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/size_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/skip_callback.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/skip_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/skip_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/slices_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/tag_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/tags_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/targets_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/time_slice_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/track_entry_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/tracks_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/unknown_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/unknown_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/var_int_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/var_int_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/video_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/virtual_block_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/virtual_block_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/void_parser.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/src/void_parser.h" + "${LIBWEBM_SRC_DIR}/webm_parser/src/webm_parser.cc") + +set(webm_parser_demo_sources "${LIBWEBM_SRC_DIR}/webm_parser/demo/demo.cc") +set(webm_parser_tests_sources + "${LIBWEBM_SRC_DIR}/webm_parser/tests/audio_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/bit_utils_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_additions_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_group_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_header_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_more_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/bool_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/buffer_reader_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/byte_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/callback_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/chapter_atom_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/chapter_display_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/chapters_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/cluster_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/colour_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_enc_aes_settings_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_encoding_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_encodings_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_encryption_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/cue_point_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/cue_track_positions_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/cues_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/date_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/ebml_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/edition_entry_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/element_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/float_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/id_element_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/id_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/info_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/int_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/istream_reader_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/limited_reader_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/master_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/master_value_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/mastering_metadata_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/parser_utils_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/projection_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/recursive_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/seek_head_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/seek_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/segment_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/simple_tag_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/size_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/skip_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/slices_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/tag_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/tags_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/targets_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/time_slice_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/track_entry_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/tracks_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/unknown_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/var_int_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/video_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/virtual_block_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/void_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/webm_parser_test.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/element_parser_test.h" + "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/limited_reader.cc" + "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/limited_reader.h" + "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/mock_callback.h" + "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/parser_test.h" + "${LIBWEBM_SRC_DIR}/webm_parser/tests/webm_parser_tests.cc") + +set(webm_info_sources + "${LIBWEBM_SRC_DIR}/common/indent.cc" + "${LIBWEBM_SRC_DIR}/common/indent.h" + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.h" + "${LIBWEBM_SRC_DIR}/common/webm_constants.h" + "${LIBWEBM_SRC_DIR}/common/webm_endian.cc" + "${LIBWEBM_SRC_DIR}/common/webm_endian.h" + "${LIBWEBM_SRC_DIR}/webm_info.cc") + +set(webmts_sources + "${LIBWEBM_SRC_DIR}/common/libwebm_util.cc" + "${LIBWEBM_SRC_DIR}/common/libwebm_util.h" + "${LIBWEBM_SRC_DIR}/common/video_frame.cc" + "${LIBWEBM_SRC_DIR}/common/video_frame.h" + "${LIBWEBM_SRC_DIR}/m2ts/vpxpes2ts.cc" + "${LIBWEBM_SRC_DIR}/m2ts/vpxpes2ts.h" + "${LIBWEBM_SRC_DIR}/m2ts/vpxpes_parser.cc" + "${LIBWEBM_SRC_DIR}/m2ts/vpxpes_parser.h" + "${LIBWEBM_SRC_DIR}/m2ts/webm2pes.cc" + "${LIBWEBM_SRC_DIR}/m2ts/webm2pes.h") + +set(webm2pes_sources "${LIBWEBM_SRC_DIR}/m2ts/webm2pes_main.cc") +set(webm2pes_tests_sources + "${LIBWEBM_SRC_DIR}/testing/test_util.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.h" + "${LIBWEBM_SRC_DIR}/testing/video_frame_tests.cc" + "${LIBWEBM_SRC_DIR}/m2ts/tests/webm2pes_tests.cc") +set(webm2ts_sources "${LIBWEBM_SRC_DIR}/m2ts/vpxpes2ts_main.cc") + +set(webvtt_common_sources + "${LIBWEBM_SRC_DIR}/webvtt/vttreader.cc" + "${LIBWEBM_SRC_DIR}/webvtt/vttreader.h" + "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.cc" + "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.h") + +# Targets. +add_library(mkvmuxer OBJECT ${mkvmuxer_sources}) +add_library(mkvparser OBJECT ${mkvparser_sources}) +add_library(webvtt_common OBJECT ${webvtt_common_sources}) + +add_library(webm ${libwebm_common_sources} + $ + $) + +if (WIN32) + # Use libwebm and libwebm.lib for project and library name on Windows (instead + # webm and webm.lib). + set_target_properties(webm PROPERTIES PROJECT_LABEL libwebm) + set_target_properties(webm PROPERTIES PREFIX lib) +endif () + +add_executable(mkvparser_sample ${mkvparser_sample_sources}) +target_link_libraries(mkvparser_sample LINK_PUBLIC webm) + +add_executable(mkvmuxer_sample ${mkvmuxer_sample_sources} + $) +target_link_libraries(mkvmuxer_sample LINK_PUBLIC webm) + +add_executable(dumpvtt ${dumpvtt_sources} $) +target_link_libraries(dumpvtt LINK_PUBLIC webm) + +add_executable(vttdemux ${vttdemux_sources}) +target_link_libraries(vttdemux LINK_PUBLIC webm) + +if (ENABLE_WEBMINFO) + add_executable(webm_info ${webm_info_sources}) + target_link_libraries(webm_info LINK_PUBLIC webm) +endif () + +if (ENABLE_WEBM_PARSER) + include_directories(webm_parser webm_parser/include) + add_library(webm_parser OBJECT ${webm_parser_sources}) + target_sources(webm PUBLIC $) + set_target_properties(webm PROPERTIES PUBLIC_HEADER + "${webm_parser_public_headers}") + + add_executable(webm_parser_demo ${webm_parser_demo_sources}) + target_link_libraries(webm_parser_demo LINK_PUBLIC webm) + + install(TARGETS webm + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webm) +endif () + +if (ENABLE_WEBMTS) + add_library(webmts OBJECT ${webmts_sources}) + + add_executable(webm2pes ${webm2pes_sources} $) + target_link_libraries(webm2pes LINK_PUBLIC webm) + + add_executable(webm2ts ${webm2ts_sources} $) + target_link_libraries(webm2ts LINK_PUBLIC webm) +endif () + +if (ENABLE_TESTS) + set(GTEST_SRC_DIR "${LIBWEBM_SRC_DIR}/../googletest" CACHE PATH + "Path to Googletest git repository.") + # This directory is where libwebm will build googletest dependencies. + set(GTEST_BUILD_DIR "${CMAKE_BINARY_DIR}/googletest_build") + + if (LIBWEBM_DISABLE_GTEST_CMAKE) + add_library(gtest STATIC "${GTEST_SRC_DIR}/googletest/src/gtest-all.cc") + include_directories("${GTEST_SRC_DIR}/googletest") + else () + add_subdirectory("${GTEST_SRC_DIR}" "${GTEST_BUILD_DIR}") + endif () + include_directories("${GTEST_SRC_DIR}/googletest/include") + + add_executable(mkvmuxer_tests ${mkvmuxer_tests_sources}) + target_link_libraries(mkvmuxer_tests LINK_PUBLIC gtest webm) + + add_executable(mkvparser_tests ${mkvparser_tests_sources}) + target_link_libraries(mkvparser_tests LINK_PUBLIC gtest webm) + + add_executable(vp9_header_parser_tests ${vp9_header_parser_tests_sources}) + target_link_libraries(vp9_header_parser_tests LINK_PUBLIC gtest webm) + + add_executable(vp9_level_stats_tests ${vp9_level_stats_tests_sources}) + target_link_libraries(vp9_level_stats_tests LINK_PUBLIC gtest webm) + + if (ENABLE_WEBMTS) + add_executable(webm2pes_tests ${webm2pes_tests_sources} + $) + target_link_libraries(webm2pes_tests LINK_PUBLIC gtest webm) + endif () + + if (ENABLE_WEBM_PARSER) + include_directories("${GTEST_SRC_DIR}/googlemock/include") + add_executable(webm_parser_tests ${webm_parser_tests_sources}) + target_link_libraries(webm_parser_tests LINK_PUBLIC gmock gtest webm) + endif () +endif () + +# Include-what-you-use. +if (ENABLE_IWYU) + # Make sure all the tools necessary for IWYU are present. + find_program(iwyu_path NAMES include-what-you-use) + find_program(iwyu_tool_path NAMES iwyu_tool.py) + + # Some odd behavior on cmake's part: PYTHON_EXECUTABLE and PYTHON_VERSION_* + # are set by cmake when it does its internal python check, but + # PYTHONINTERP_FOUND is empty without explicitly looking for it. + find_package(PythonInterp) + + if (iwyu_path AND iwyu_tool_path AND PYTHONINTERP_FOUND) + # Enable compilation command export (needed for iwyu_tool.py) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + # Add a custom target to run iwyu across all targets. + add_custom_target(iwyu + ALL + COMMAND "${PYTHON_EXECUTABLE}" "${iwyu_tool_path}" -p + "${CMAKE_BINARY_DIR}" + COMMENT "Running include-what-you-use..." + VERBATIM) + else () + message(STATUS "Ignoring ENABLE_IWYU because reasons:") + message(STATUS " iwyu_path=" ${iwyu_path}) + message(STATUS " iwyu_tool_path=" ${iwyu_tool_path}) + message(STATUS " PYTHONINTERP_FOUND=" ${PYTHONINTERP_FOUND}) + message(STATUS " See README.libwebm for more information.") + endif () +endif () diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..7a73a30317af --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use a [Gerrit](https://www.gerritcodereview.com) instance hosted at +https://chromium-review.googlesource.com for this purpose. See the +[WebM Project page](https://www.webmproject.org/code/contribute/submitting-patches/) +for additional details. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 000000000000..7a6f99547d4d --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,30 @@ +Copyright (c) 2010, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google 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. + diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 000000000000..0430d17a06b1 --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,57 @@ +CXX := g++ +DEFINES := -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS +DEFINES += -D__STDC_LIMIT_MACROS +INCLUDES := -I. +CXXFLAGS := -W -Wall -g -std=c++11 +ALL_CXXFLAGS := -MMD -MP $(DEFINES) $(INCLUDES) $(CXXFLAGS) +LIBWEBMA := libwebm.a +LIBWEBMSO := libwebm.so +WEBMOBJS := mkvmuxer/mkvmuxer.o mkvmuxer/mkvmuxerutil.o mkvmuxer/mkvwriter.o +WEBMOBJS += mkvparser/mkvparser.o mkvparser/mkvreader.o +WEBMOBJS += common/file_util.o common/hdr_util.o +OBJSA := $(WEBMOBJS:.o=_a.o) +OBJSSO := $(WEBMOBJS:.o=_so.o) +VTTOBJS := webvtt/vttreader.o webvtt/webvttparser.o sample_muxer_metadata.o +EXEOBJS := mkvmuxer_sample.o mkvparser_sample.o dumpvtt.o vttdemux.o +EXES := mkvparser_sample mkvmuxer_sample dumpvtt vttdemux +DEPS := $(WEBMOBJS:.o=.d) $(OBJECTS1:.o=.d) $(OBJECTS2:.o=.d) +DEPS += $(OBJECTS3:.o=.d) $(OBJECTS4:.o=.d) $(OBJSA:.o=.d) $(OBJSSO:.o=.d) +DEPS += $(VTTOBJS:.o=.d) $(EXEOBJS:.o=.d) +CLEAN := $(EXEOBJS) $(VTTOBJS) $(WEBMOBJS) $(OBJSA) $(OBJSSO) $(LIBWEBMA) +CLEAN += $(LIBWEBMSO) $(EXES) $(DEPS) $(INFOOBJS) + +all: $(EXES) + +mkvparser_sample: mkvparser_sample.o $(LIBWEBMA) + $(CXX) $^ -o $@ + +mkvmuxer_sample: mkvmuxer_sample.o $(VTTOBJS) $(LIBWEBMA) + $(CXX) $^ -o $@ + +dumpvtt: dumpvtt.o $(VTTOBJS) $(WEBMOBJS) + $(CXX) $^ -o $@ + +vttdemux: vttdemux.o $(VTTOBJS) $(LIBWEBMA) + $(CXX) $^ -o $@ + +shared: $(LIBWEBMSO) + +libwebm.a: $(OBJSA) + $(AR) rcs $@ $^ + +libwebm.so: $(OBJSSO) + $(CXX) $(ALL_CXXFLAGS) -shared $(OBJSSO) -o $(LIBWEBMSO) + +%.o: %.cc + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ +%_a.o: %.cc + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ +%_so.o: %.cc + $(CXX) -c $(ALL_CXXFLAGS) -fPIC $< -o $@ + +clean: + $(RM) -f $(CLEAN) Makefile.bak + +ifneq ($(MAKECMDGOALS), clean) + -include $(DEPS) +endif diff --git a/PATENTS.TXT b/PATENTS.TXT new file mode 100644 index 000000000000..caedf607e95a --- /dev/null +++ b/PATENTS.TXT @@ -0,0 +1,23 @@ +Additional IP Rights Grant (Patents) +------------------------------------ + +"These implementations" means the copyrightable works that implement the WebM +codecs distributed by Google as part of the WebM Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable (except as stated in this section) patent license to +make, have made, use, offer to sell, sell, import, transfer, and otherwise +run, modify and propagate the contents of these implementations of WebM, where +such license applies only to those patent claims, both currently owned by +Google and acquired in the future, licensable by Google that are necessarily +infringed by these implementations of WebM. This grant does not include claims +that would be infringed only as a consequence of further modification of these +implementations. If you or your agent or exclusive licensee institute or order +or agree to the institution of patent litigation or any other patent +enforcement activity against any entity (including a cross-claim or +counterclaim in a lawsuit) alleging that any of these implementations of WebM +or any code incorporated within any of these implementations of WebM +constitute direct or contributory patent infringement, or inducement of +patent infringement, then any patent rights granted to you under this License +for these implementations of WebM shall terminate as of the date such +litigation is filed. diff --git a/README.libwebm b/README.libwebm new file mode 100644 index 000000000000..3406f80dbd38 --- /dev/null +++ b/README.libwebm @@ -0,0 +1,143 @@ +Building Libwebm + +To build libwebm you must first create project files. To do this run cmake +and pass it the path to your libwebm repo. + +Makefile.unix can be used as a fallback on systems that cmake does not +support. + + +CMake Basics + +To generate project/make files for the default toolchain on your system simply +run cmake with the path to the libwebm repo: + +$ cmake path/to/libwebm + +On Windows the above command will produce Visual Studio project files for the +newest Visual Studio detected on the system. On Mac OS X and Linux systems, the +above command will produce a makefile. + +To control what types of projects are generated the -G parameter is added to +the cmake command line. This argument must be followed by the name of a +generator. Running cmake with the --help argument will list the available +generators for your system. + +On Mac OS X you would run the following command to generate Xcode projects: + +$ cmake path/to/libwebm -G Xcode + +On a Windows box you would run the following command to generate Visual Studio +2013 projects: + +$ cmake path/to/libwebm -G "Visual Studio 12" + +To generate 64-bit Windows Visual Studio 2013 projects: + +$ cmake path/to/libwebm "Visual Studio 12 Win64" + + +CMake Makefiles: Debugging and Optimization + +Unlike Visual Studio and Xcode projects, the build configuration for make builds +is controlled when you run cmake. The following examples demonstrate various +build configurations. + +Omitting the build type produces makefiles that use build flags containing +neither optimization nor debug flags: +$ cmake path/to/libwebm + +A makefile using release (optimized) flags is produced like this: +$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=release + +A release build with debug info can be produced as well: +$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=relwithdebinfo + +And your standard debug build will be produced using: +$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=debug + + +Tests + +To enable libwebm tests add -DENABLE_TESTS=ON CMake generation command line. For +example: + +$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON + +Libwebm tests depend on googletest. By default googletest is expected to be a +sibling directory of the Libwebm repository. To change that, update your CMake +command to be similar to the following: + +$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON \ + -DGTEST_SRC_DIR=/path/to/googletest + +The tests rely upon the LIBWEBM_TEST_DATA_PATH environment variable to locate +test input. The following example demonstrates running the muxer tests from the +build directory: + +$ LIBWEBM_TEST_DATA_PATH=path/to/libwebm/testing/testdata ./mkvmuxer_tests + +Note: Libwebm Googletest integration was built with googletest from + https://github.com/google/googletest.git at git revision + ddb8012eb48bc203aa93dcc2b22c1db516302b29. + + +CMake Include-what-you-use integration + +Include-what-you-use is an analysis tool that helps ensure libwebm includes the +C/C++ header files actually in use. To enable the integration support +ENABLE_IWYU must be turned on at cmake run time: + +$ cmake path/to/libwebm -G "Unix Makefiles" -DENABLE_IWYU=ON + +This adds the iwyu target to the build. To run include-what-you-use: + +$ make iwyu + +The following requirements must be met for ENABLE_IWYU to enable the iwyu +target: + +1. include-what-you-use and iwyu_tool.py must be in your PATH. +2. A python interpreter must be on the system and available to CMake. + +The values of the following variables are used to determine if the requirements +have been met. Values to the right of the equals sign are what a successful run +might look like: + iwyu_path=/path/to/iwyu_tool.py + iwyu_tool_path=/path/to/include-what-you-use + PYTHONINTERP_FOUND=TRUE + +An empty PYTHONINTERP_FOUND, or iwyu_path/iwyu_tool_path suffixed with NOTFOUND +are failures. + +For Include-what-you-use setup instructions, see: +https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/InstructionsForUsers.md + +If, when building the iwyu target, compile errors reporting failures loading +standard include files occur, one solution can be found here: +https://github.com/include-what-you-use/include-what-you-use/issues/100 + + +CMake cross compile +To cross compile libwebm for Windows using mingw-w64 run cmake with the +following arguments: + +$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \ + path/to/libwebm + +Note1: As of this writing googletest will not build via mingw-w64 without +disabling pthreads. +googletest hash: d225acc90bc3a8c420a9bcd1f033033c1ccd7fe0 + +To build with tests when using mingw-w64 use the following arguments when +running CMake: + +$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \ + -DENABLE_TESTS=ON -Dgtest_disable_pthreads=ON path/to/libwebm + +Note2: i686-w64-mingw32 is the default compiler. This can be controlled using +the MINGW_PREFIX variable: + +$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \ + -DMINGW_PREFIX=x86_64-w64-mingw32 path/to/libwebm + diff --git a/build/cxx_flags.cmake b/build/cxx_flags.cmake new file mode 100644 index 000000000000..1aa6a86cce97 --- /dev/null +++ b/build/cxx_flags.cmake @@ -0,0 +1,72 @@ +## Copyright (c) 2016 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. + +include(CheckCXXCompilerFlag) + +# String used to cache failed CXX flags. +set(LIBWEBM_FAILED_CXX_FLAGS) + +# Checks C++ compiler for support of $cxx_flag. Adds $cxx_flag to +# $CMAKE_CXX_FLAGS when the compile test passes. Caches $c_flag in +# $LIBWEBM_FAILED_CXX_FLAGS when the test fails. +function (add_cxx_flag_if_supported cxx_flag) + unset(CXX_FLAG_FOUND CACHE) + string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND) + unset(CXX_FLAG_FAILED CACHE) + string(FIND "${LIBWEBM_FAILED_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FAILED) + + if (${CXX_FLAG_FOUND} EQUAL -1 AND ${CXX_FLAG_FAILED} EQUAL -1) + unset(CXX_FLAG_SUPPORTED CACHE) + message("Checking CXX compiler flag support for: " ${cxx_flag}) + check_cxx_compiler_flag("${cxx_flag}" CXX_FLAG_SUPPORTED) + if (CXX_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${cxx_flag}" CACHE STRING "" + FORCE) + else () + set(LIBWEBM_FAILED_CXX_FLAGS "${LIBWEBM_FAILED_CXX_FLAGS} ${cxx_flag}" + CACHE STRING "" FORCE) + endif () + endif () +endfunction () + +# Checks CXX compiler for support of $cxx_flag and terminates generation when +# support is not present. +function (require_cxx_flag cxx_flag) + unset(CXX_FLAG_FOUND CACHE) + string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND) + + if (${CXX_FLAG_FOUND} EQUAL -1) + unset(LIBWEBM_HAVE_CXX_FLAG CACHE) + message("Checking CXX compiler flag support for: " ${cxx_flag}) + check_cxx_compiler_flag("${cxx_flag}" LIBWEBM_HAVE_CXX_FLAG) + if (NOT LIBWEBM_HAVE_CXX_FLAG) + message(FATAL_ERROR + "${PROJECT_NAME} requires support for CXX flag: ${cxx_flag}.") + endif () + set(CMAKE_CXX_FLAGS "${cxx_flag} ${CMAKE_CXX_FLAGS}" CACHE STRING "" FORCE) + endif () +endfunction () + +# Checks only non-MSVC targets for support of $cxx_flag. +function (require_cxx_flag_nomsvc cxx_flag) + if (NOT MSVC) + require_cxx_flag(${cxx_flag}) + endif () +endfunction () + +# Adds $preproc_def to CXX compiler command line (as -D$preproc_def) if not +# already present. +function (add_cxx_preproc_definition preproc_def) + unset(PREPROC_DEF_FOUND CACHE) + string(FIND "${CMAKE_CXX_FLAGS}" "${preproc_def}" PREPROC_DEF_FOUND) + + if (${PREPROC_DEF_FOUND} EQUAL -1) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${preproc_def}" CACHE STRING "" + FORCE) + endif () +endfunction () diff --git a/build/msvc_runtime.cmake b/build/msvc_runtime.cmake new file mode 100644 index 000000000000..4f53a3631885 --- /dev/null +++ b/build/msvc_runtime.cmake @@ -0,0 +1,22 @@ +## Copyright (c) 2015 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. + +if (MSVC) + # CMake defaults to producing code linked to the DLL MSVC runtime. In libwebm + # static is typically desired. Force static code generation unless the user + # running CMake set MSVC_RUNTIME to dll. + if (NOT "${MSVC_RUNTIME}" STREQUAL "dll") + foreach (flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if (${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif (${flag_var} MATCHES "/MD") + endforeach (flag_var) + endif () +endif () diff --git a/build/x86-mingw-gcc.cmake b/build/x86-mingw-gcc.cmake new file mode 100644 index 000000000000..8416eaf784f5 --- /dev/null +++ b/build/x86-mingw-gcc.cmake @@ -0,0 +1,26 @@ +## Copyright (c) 2017 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. +if (NOT LIBWEBM_BUILD_X86_MINGW_GCC_CMAKE_) +set(LIBWEBM_BUILD_X86_MINGW_GCC_CMAKE_ 1) + +set(CMAKE_SYSTEM_PROCESSOR "x86") +set(CMAKE_SYSTEM_NAME "Windows") +set(CMAKE_C_COMPILER_ARG1 "-m32") +set(CMAKE_CXX_COMPILER_ARG1 "-m32") + +if ("${CROSS}" STREQUAL "") + set(CROSS i686-w64-mingw32-) +endif () + +set(CMAKE_C_COMPILER ${CROSS}gcc) +set(CMAKE_CXX_COMPILER ${CROSS}g++) + +# Disable googletest CMake usage for mingw cross compiles. +set(LIBWEBM_DISABLE_GTEST_CMAKE 1) + +endif () # LIBWEBM_BUILD_X86_MINGW_GCC_CMAKE_ diff --git a/build/x86_64-mingw-gcc.cmake b/build/x86_64-mingw-gcc.cmake new file mode 100644 index 000000000000..9db28b744225 --- /dev/null +++ b/build/x86_64-mingw-gcc.cmake @@ -0,0 +1,24 @@ +## Copyright (c) 2017 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. +if (NOT LIBWEBM_BUILD_X86_64_MINGW_GCC_CMAKE_) +set(LIBWEBM_BUILD_X86_64_MINGW_GCC_CMAKE_ 1) + +set(CMAKE_SYSTEM_PROCESSOR "x86_64") +set(CMAKE_SYSTEM_NAME "Windows") + +if ("${CROSS}" STREQUAL "") + set(CROSS x86_64-w64-mingw32-) +endif () + +set(CMAKE_C_COMPILER ${CROSS}gcc) +set(CMAKE_CXX_COMPILER ${CROSS}g++) + +# Disable googletest CMake usage for mingw cross compiles. +set(LIBWEBM_DISABLE_GTEST_CMAKE 1) + +endif () # LIBWEBM_BUILD_X86_64_MINGW_GCC_CMAKE_ diff --git a/codereview.settings b/codereview.settings new file mode 100644 index 000000000000..ccba2eeed246 --- /dev/null +++ b/codereview.settings @@ -0,0 +1,4 @@ +# This file is used by git cl to get repository specific information. +GERRIT_HOST: True +CODE_REVIEW_SERVER: chromium-review.googlesource.com +GERRIT_SQUASH_UPLOADS: False diff --git a/common/common.sh b/common/common.sh new file mode 100644 index 000000000000..49fec7984f38 --- /dev/null +++ b/common/common.sh @@ -0,0 +1,62 @@ +#!/bin/sh +## +## Copyright (c) 2015 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. +## +set -e +devnull='> /dev/null 2>&1' + +readonly ORIG_PWD="$(pwd)" + +elog() { + echo "${0##*/} failed because: $@" 1>&2 +} + +vlog() { + if [ "${VERBOSE}" = "yes" ]; then + echo "$@" + fi +} + +# Terminates script when name of current directory does not match $1. +check_dir() { + current_dir="$(pwd)" + required_dir="$1" + if [ "${current_dir##*/}" != "${required_dir}" ]; then + elog "This script must be run from the ${required_dir} directory." + exit 1 + fi +} + +# Terminates the script when $1 is not in $PATH. Any arguments required for +# the tool being tested to return a successful exit code can be passed as +# additional arguments. +check_tool() { + tool="$1" + shift + tool_args="$@" + if ! eval "${tool}" ${tool_args} > /dev/null 2>&1; then + elog "${tool} must be in your path." + exit 1 + fi +} + +# Echoes git describe output for the directory specified by $1 to stdout. +git_describe() { + git_dir="$1" + check_git + echo $(git -C "${git_dir}" describe) +} + +# Echoes current git revision for the directory specifed by $1 to stdout. +git_revision() { + git_dir="$1" + check_git + echo $(git -C "${git_dir}" rev-parse HEAD) +} + diff --git a/common/file_util.cc b/common/file_util.cc new file mode 100644 index 000000000000..6eb6428b987a --- /dev/null +++ b/common/file_util.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/file_util.h" + +#include +#ifndef _MSC_VER +#include // close() +#endif + +#include +#include +#include +#include +#include +#include + +namespace libwebm { + +std::string GetTempFileName() { +#if !defined _MSC_VER && !defined __MINGW32__ + std::string temp_file_name_template_str = + std::string(std::getenv("TEST_TMPDIR") ? std::getenv("TEST_TMPDIR") + : ".") + + "/libwebm_temp.XXXXXX"; + char* temp_file_name_template = + new char[temp_file_name_template_str.length() + 1]; + memset(temp_file_name_template, 0, temp_file_name_template_str.length() + 1); + temp_file_name_template_str.copy(temp_file_name_template, + temp_file_name_template_str.length(), 0); + int fd = mkstemp(temp_file_name_template); + std::string temp_file_name = + (fd != -1) ? std::string(temp_file_name_template) : std::string(); + delete[] temp_file_name_template; + if (fd != -1) { + close(fd); + } + return temp_file_name; +#else + char tmp_file_name[_MAX_PATH]; +#if defined _MSC_VER || defined MINGW_HAS_SECURE_API + errno_t err = tmpnam_s(tmp_file_name); +#else + char* fname_pointer = tmpnam(tmp_file_name); + int err = (fname_pointer == &tmp_file_name[0]) ? 0 : -1; +#endif + if (err == 0) { + return std::string(tmp_file_name); + } + return std::string(); +#endif +} + +uint64_t GetFileSize(const std::string& file_name) { + uint64_t file_size = 0; +#ifndef _MSC_VER + struct stat st; + st.st_size = 0; + if (stat(file_name.c_str(), &st) == 0) { +#else + struct _stat st; + st.st_size = 0; + if (_stat(file_name.c_str(), &st) == 0) { +#endif + file_size = st.st_size; + } + return file_size; +} + +bool GetFileContents(const std::string& file_name, std::string* contents) { + std::ifstream file(file_name.c_str()); + *contents = std::string(static_cast(GetFileSize(file_name)), 0); + if (file.good() && contents->size()) { + file.read(&(*contents)[0], contents->size()); + } + return !file.fail(); +} + +TempFileDeleter::TempFileDeleter() { file_name_ = GetTempFileName(); } + +TempFileDeleter::~TempFileDeleter() { + std::ifstream file(file_name_.c_str()); + if (file.good()) { + file.close(); + std::remove(file_name_.c_str()); + } +} + +} // namespace libwebm diff --git a/common/file_util.h b/common/file_util.h new file mode 100644 index 000000000000..a87373464187 --- /dev/null +++ b/common/file_util.h @@ -0,0 +1,44 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_FILE_UTIL_H_ +#define LIBWEBM_COMMON_FILE_UTIL_H_ + +#include + +#include + +#include "mkvmuxer/mkvmuxertypes.h" // LIBWEBM_DISALLOW_COPY_AND_ASSIGN() + +namespace libwebm { + +// Returns a temporary file name. +std::string GetTempFileName(); + +// Returns size of file specified by |file_name|, or 0 upon failure. +uint64_t GetFileSize(const std::string& file_name); + +// Gets the contents file_name as a string. Returns false on error. +bool GetFileContents(const std::string& file_name, std::string* contents); + +// Manages life of temporary file specified at time of construction. Deletes +// file upon destruction. +class TempFileDeleter { + public: + TempFileDeleter(); + explicit TempFileDeleter(std::string file_name) : file_name_(file_name) {} + ~TempFileDeleter(); + const std::string& name() const { return file_name_; } + + private: + std::string file_name_; + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TempFileDeleter); +}; + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_FILE_UTIL_H_ diff --git a/common/hdr_util.cc b/common/hdr_util.cc new file mode 100644 index 000000000000..916f7170b676 --- /dev/null +++ b/common/hdr_util.cc @@ -0,0 +1,220 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "hdr_util.h" + +#include +#include +#include + +#include "mkvparser/mkvparser.h" + +namespace libwebm { +const int Vp9CodecFeatures::kValueNotPresent = INT_MAX; + +bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc, + PrimaryChromaticityPtr* muxer_pc) { + muxer_pc->reset(new (std::nothrow) + mkvmuxer::PrimaryChromaticity(parser_pc.x, parser_pc.y)); + if (!muxer_pc->get()) + return false; + return true; +} + +bool MasteringMetadataValuePresent(double value) { + return value != mkvparser::MasteringMetadata::kValueNotPresent; +} + +bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm, + mkvmuxer::MasteringMetadata* muxer_mm) { + if (MasteringMetadataValuePresent(parser_mm.luminance_max)) + muxer_mm->set_luminance_max(parser_mm.luminance_max); + if (MasteringMetadataValuePresent(parser_mm.luminance_min)) + muxer_mm->set_luminance_min(parser_mm.luminance_min); + + PrimaryChromaticityPtr r_ptr(nullptr); + PrimaryChromaticityPtr g_ptr(nullptr); + PrimaryChromaticityPtr b_ptr(nullptr); + PrimaryChromaticityPtr wp_ptr(nullptr); + + if (parser_mm.r) { + if (!CopyPrimaryChromaticity(*parser_mm.r, &r_ptr)) + return false; + } + if (parser_mm.g) { + if (!CopyPrimaryChromaticity(*parser_mm.g, &g_ptr)) + return false; + } + if (parser_mm.b) { + if (!CopyPrimaryChromaticity(*parser_mm.b, &b_ptr)) + return false; + } + if (parser_mm.white_point) { + if (!CopyPrimaryChromaticity(*parser_mm.white_point, &wp_ptr)) + return false; + } + + if (!muxer_mm->SetChromaticity(r_ptr.get(), g_ptr.get(), b_ptr.get(), + wp_ptr.get())) { + return false; + } + + return true; +} + +bool ColourValuePresent(long long value) { + return value != mkvparser::Colour::kValueNotPresent; +} + +bool CopyColour(const mkvparser::Colour& parser_colour, + mkvmuxer::Colour* muxer_colour) { + if (!muxer_colour) + return false; + + if (ColourValuePresent(parser_colour.matrix_coefficients)) + muxer_colour->set_matrix_coefficients(parser_colour.matrix_coefficients); + if (ColourValuePresent(parser_colour.bits_per_channel)) + muxer_colour->set_bits_per_channel(parser_colour.bits_per_channel); + if (ColourValuePresent(parser_colour.chroma_subsampling_horz)) { + muxer_colour->set_chroma_subsampling_horz( + parser_colour.chroma_subsampling_horz); + } + if (ColourValuePresent(parser_colour.chroma_subsampling_vert)) { + muxer_colour->set_chroma_subsampling_vert( + parser_colour.chroma_subsampling_vert); + } + if (ColourValuePresent(parser_colour.cb_subsampling_horz)) + muxer_colour->set_cb_subsampling_horz(parser_colour.cb_subsampling_horz); + if (ColourValuePresent(parser_colour.cb_subsampling_vert)) + muxer_colour->set_cb_subsampling_vert(parser_colour.cb_subsampling_vert); + if (ColourValuePresent(parser_colour.chroma_siting_horz)) + muxer_colour->set_chroma_siting_horz(parser_colour.chroma_siting_horz); + if (ColourValuePresent(parser_colour.chroma_siting_vert)) + muxer_colour->set_chroma_siting_vert(parser_colour.chroma_siting_vert); + if (ColourValuePresent(parser_colour.range)) + muxer_colour->set_range(parser_colour.range); + if (ColourValuePresent(parser_colour.transfer_characteristics)) { + muxer_colour->set_transfer_characteristics( + parser_colour.transfer_characteristics); + } + if (ColourValuePresent(parser_colour.primaries)) + muxer_colour->set_primaries(parser_colour.primaries); + if (ColourValuePresent(parser_colour.max_cll)) + muxer_colour->set_max_cll(parser_colour.max_cll); + if (ColourValuePresent(parser_colour.max_fall)) + muxer_colour->set_max_fall(parser_colour.max_fall); + + if (parser_colour.mastering_metadata) { + mkvmuxer::MasteringMetadata muxer_mm; + if (!CopyMasteringMetadata(*parser_colour.mastering_metadata, &muxer_mm)) + return false; + if (!muxer_colour->SetMasteringMetadata(muxer_mm)) + return false; + } + return true; +} + +// Format of VPx private data: +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ID Byte | Length | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +// | | +// : Bytes 1..Length of Codec Feature : +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// ID Byte Format +// ID byte is an unsigned byte. +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |X| ID | +// +-+-+-+-+-+-+-+-+ +// +// The X bit is reserved. +// +// See the following link for more information: +// http://www.webmproject.org/vp9/profiles/ +bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length, + Vp9CodecFeatures* features) { + const int kVpxCodecPrivateMinLength = 3; + if (!private_data || !features || length < kVpxCodecPrivateMinLength) + return false; + + const uint8_t kVp9ProfileId = 1; + const uint8_t kVp9LevelId = 2; + const uint8_t kVp9BitDepthId = 3; + const uint8_t kVp9ChromaSubsamplingId = 4; + const int kVpxFeatureLength = 1; + int offset = 0; + + // Set features to not set. + features->profile = Vp9CodecFeatures::kValueNotPresent; + features->level = Vp9CodecFeatures::kValueNotPresent; + features->bit_depth = Vp9CodecFeatures::kValueNotPresent; + features->chroma_subsampling = Vp9CodecFeatures::kValueNotPresent; + do { + const uint8_t id_byte = private_data[offset++]; + const uint8_t length_byte = private_data[offset++]; + if (length_byte != kVpxFeatureLength) + return false; + if (id_byte == kVp9ProfileId) { + const int priv_profile = static_cast(private_data[offset++]); + if (priv_profile < 0 || priv_profile > 3) + return false; + if (features->profile != Vp9CodecFeatures::kValueNotPresent && + features->profile != priv_profile) { + return false; + } + features->profile = priv_profile; + } else if (id_byte == kVp9LevelId) { + const int priv_level = static_cast(private_data[offset++]); + + const int kNumLevels = 14; + const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40, + 41, 50, 51, 52, 60, 61, 62}; + + for (int i = 0; i < kNumLevels; ++i) { + if (priv_level == levels[i]) { + if (features->level != Vp9CodecFeatures::kValueNotPresent && + features->level != priv_level) { + return false; + } + features->level = priv_level; + break; + } + } + if (features->level == Vp9CodecFeatures::kValueNotPresent) + return false; + } else if (id_byte == kVp9BitDepthId) { + const int priv_profile = static_cast(private_data[offset++]); + if (priv_profile != 8 && priv_profile != 10 && priv_profile != 12) + return false; + if (features->bit_depth != Vp9CodecFeatures::kValueNotPresent && + features->bit_depth != priv_profile) { + return false; + } + features->bit_depth = priv_profile; + } else if (id_byte == kVp9ChromaSubsamplingId) { + const int priv_profile = static_cast(private_data[offset++]); + if (priv_profile != 0 && priv_profile != 2 && priv_profile != 3) + return false; + if (features->chroma_subsampling != Vp9CodecFeatures::kValueNotPresent && + features->chroma_subsampling != priv_profile) { + return false; + } + features->chroma_subsampling = priv_profile; + } else { + // Invalid ID. + return false; + } + } while (offset + kVpxCodecPrivateMinLength <= length); + + return true; +} +} // namespace libwebm diff --git a/common/hdr_util.h b/common/hdr_util.h new file mode 100644 index 000000000000..78e2eeb7058e --- /dev/null +++ b/common/hdr_util.h @@ -0,0 +1,71 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_HDR_UTIL_H_ +#define LIBWEBM_COMMON_HDR_UTIL_H_ + +#include + +#include + +#include "mkvmuxer/mkvmuxer.h" + +namespace mkvparser { +struct Colour; +struct MasteringMetadata; +struct PrimaryChromaticity; +} // namespace mkvparser + +namespace libwebm { +// Utility types and functions for working with the Colour element and its +// children. Copiers return true upon success. Presence functions return true +// when the specified element is present. + +// TODO(tomfinegan): These should be moved to libwebm_utils once c++11 is +// required by libwebm. + +// Features of the VP9 codec that may be set in the CodecPrivate of a VP9 video +// stream. A value of kValueNotPresent represents that the value was not set in +// the CodecPrivate. +struct Vp9CodecFeatures { + static const int kValueNotPresent; + + Vp9CodecFeatures() + : profile(kValueNotPresent), + level(kValueNotPresent), + bit_depth(kValueNotPresent), + chroma_subsampling(kValueNotPresent) {} + ~Vp9CodecFeatures() {} + + int profile; + int level; + int bit_depth; + int chroma_subsampling; +}; + +typedef std::unique_ptr PrimaryChromaticityPtr; + +bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc, + PrimaryChromaticityPtr* muxer_pc); + +bool MasteringMetadataValuePresent(double value); + +bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm, + mkvmuxer::MasteringMetadata* muxer_mm); + +bool ColourValuePresent(long long value); + +bool CopyColour(const mkvparser::Colour& parser_colour, + mkvmuxer::Colour* muxer_colour); + +// Returns true if |features| is set to one or more valid values. +bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length, + Vp9CodecFeatures* features); + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_HDR_UTIL_H_ diff --git a/common/indent.cc b/common/indent.cc new file mode 100644 index 000000000000..87d656c44b49 --- /dev/null +++ b/common/indent.cc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common/indent.h" + +#include + +namespace libwebm { + +Indent::Indent(int indent) : indent_(indent), indent_str_() { Update(); } + +void Indent::Adjust(int indent) { + indent_ += indent; + if (indent_ < 0) + indent_ = 0; + + Update(); +} + +void Indent::Update() { indent_str_ = std::string(indent_, ' '); } + +} // namespace libwebm diff --git a/common/indent.h b/common/indent.h new file mode 100644 index 000000000000..22d3d2695ed6 --- /dev/null +++ b/common/indent.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef LIBWEBM_COMMON_INDENT_H_ +#define LIBWEBM_COMMON_INDENT_H_ + +#include + +#include "mkvmuxer/mkvmuxertypes.h" + +namespace libwebm { + +const int kIncreaseIndent = 2; +const int kDecreaseIndent = -2; + +// Used for formatting output so objects only have to keep track of spacing +// within their scope. +class Indent { + public: + explicit Indent(int indent); + + // Changes the number of spaces output. The value adjusted is relative to + // |indent_|. + void Adjust(int indent); + + std::string indent_str() const { return indent_str_; } + + private: + // Called after |indent_| is changed. This will set |indent_str_| to the + // proper number of spaces. + void Update(); + + int indent_; + std::string indent_str_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Indent); +}; + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_INDENT_H_ diff --git a/common/libwebm_util.cc b/common/libwebm_util.cc new file mode 100644 index 000000000000..d158250fb6b4 --- /dev/null +++ b/common/libwebm_util.cc @@ -0,0 +1,110 @@ +// Copyright (c) 2015 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/libwebm_util.h" + +#include +#include +#include +#include + +namespace libwebm { + +std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds) { + const double pts_seconds = nanoseconds / kNanosecondsPerSecond; + return static_cast(pts_seconds * 90000); +} + +std::int64_t Khz90TicksToNanoseconds(std::int64_t ticks) { + const double seconds = ticks / 90000.0; + return static_cast(seconds * kNanosecondsPerSecond); +} + +bool ParseVP9SuperFrameIndex(const std::uint8_t* frame, + std::size_t frame_length, Ranges* frame_ranges, + bool* error) { + if (frame == nullptr || frame_length == 0 || frame_ranges == nullptr || + error == nullptr) { + return false; + } + + bool parse_ok = false; + const std::uint8_t marker = frame[frame_length - 1]; + const std::uint32_t kHasSuperFrameIndexMask = 0xe0; + const std::uint32_t kSuperFrameMarker = 0xc0; + const std::uint32_t kLengthFieldSizeMask = 0x3; + + if ((marker & kHasSuperFrameIndexMask) == kSuperFrameMarker) { + const std::uint32_t kFrameCountMask = 0x7; + const int num_frames = (marker & kFrameCountMask) + 1; + const int length_field_size = ((marker >> 3) & kLengthFieldSizeMask) + 1; + const std::size_t index_length = 2 + length_field_size * num_frames; + + if (frame_length < index_length) { + std::fprintf(stderr, + "VP9ParseSuperFrameIndex: Invalid superframe index size.\n"); + *error = true; + return false; + } + + // Consume the super frame index. Note: it's at the end of the super frame. + const std::size_t length = frame_length - index_length; + + if (length >= index_length && + frame[frame_length - index_length] == marker) { + // Found a valid superframe index. + const std::uint8_t* byte = frame + length + 1; + + std::size_t frame_offset = 0; + + for (int i = 0; i < num_frames; ++i) { + std::uint32_t child_frame_length = 0; + + for (int j = 0; j < length_field_size; ++j) { + child_frame_length |= (*byte++) << (j * 8); + } + + if (length - frame_offset < child_frame_length) { + std::fprintf(stderr, + "ParseVP9SuperFrameIndex: Invalid superframe, sub frame " + "larger than entire frame.\n"); + *error = true; + return false; + } + + frame_ranges->push_back(Range(frame_offset, child_frame_length)); + frame_offset += child_frame_length; + } + + if (static_cast(frame_ranges->size()) != num_frames) { + std::fprintf(stderr, "VP9Parse: superframe index parse failed.\n"); + *error = true; + return false; + } + + parse_ok = true; + } else { + std::fprintf(stderr, "VP9Parse: Invalid superframe index.\n"); + *error = true; + } + } + return parse_ok; +} + +bool WriteUint8(std::uint8_t val, std::FILE* fileptr) { + if (fileptr == nullptr) + return false; + return (std::fputc(val, fileptr) == val); +} + +std::uint16_t ReadUint16(const std::uint8_t* buf) { + if (buf == nullptr) + return 0; + return ((buf[0] << 8) | buf[1]); +} + +} // namespace libwebm diff --git a/common/libwebm_util.h b/common/libwebm_util.h new file mode 100644 index 000000000000..71d805fcf912 --- /dev/null +++ b/common/libwebm_util.h @@ -0,0 +1,65 @@ +// Copyright (c) 2015 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_LIBWEBM_UTIL_H_ +#define LIBWEBM_COMMON_LIBWEBM_UTIL_H_ + +#include +#include +#include +#include +#include + +namespace libwebm { + +const double kNanosecondsPerSecond = 1000000000.0; + +// fclose functor for wrapping FILE in std::unique_ptr. +// TODO(tomfinegan): Move this to file_util once c++11 restrictions are +// relaxed. +struct FILEDeleter { + int operator()(std::FILE* f) { + if (f != nullptr) + return fclose(f); + return 0; + } +}; +typedef std::unique_ptr FilePtr; + +struct Range { + Range(std::size_t off, std::size_t len) : offset(off), length(len) {} + Range() = delete; + Range(const Range&) = default; + Range(Range&&) = default; + ~Range() = default; + const std::size_t offset; + const std::size_t length; +}; +typedef std::vector Ranges; + +// Converts |nanoseconds| to 90000 Hz clock ticks and vice versa. Each return +// the converted value. +std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds); +std::int64_t Khz90TicksToNanoseconds(std::int64_t khz90_ticks); + +// Returns true and stores frame offsets and lengths in |frame_ranges| when +// |frame| has a valid VP9 super frame index. Sets |error| to true when parsing +// fails for any reason. +bool ParseVP9SuperFrameIndex(const std::uint8_t* frame, + std::size_t frame_length, Ranges* frame_ranges, + bool* error); + +// Writes |val| to |fileptr| and returns true upon success. +bool WriteUint8(std::uint8_t val, std::FILE* fileptr); + +// Reads 2 bytes from |buf| and returns them as a uint16_t. Returns 0 when |buf| +// is a nullptr. +std::uint16_t ReadUint16(const std::uint8_t* buf); + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_LIBWEBM_UTIL_H_ diff --git a/common/video_frame.cc b/common/video_frame.cc new file mode 100644 index 000000000000..00aa8dadcf68 --- /dev/null +++ b/common/video_frame.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/video_frame.h" + +#include + +namespace libwebm { + +bool VideoFrame::Buffer::Init(std::size_t new_length) { + capacity = 0; + length = 0; + data.reset(new std::uint8_t[new_length]); + + if (data.get() == nullptr) { + fprintf(stderr, "VideoFrame: Out of memory."); + return false; + } + + capacity = new_length; + length = 0; + return true; +} + +bool VideoFrame::Init(std::size_t length) { return buffer_.Init(length); } + +bool VideoFrame::Init(std::size_t length, std::int64_t nano_pts, Codec codec) { + nanosecond_pts_ = nano_pts; + codec_ = codec; + return Init(length); +} + +bool VideoFrame::SetBufferLength(std::size_t length) { + if (length > buffer_.capacity || buffer_.data.get() == nullptr) + return false; + + buffer_.length = length; + return true; +} + +} // namespace libwebm diff --git a/common/video_frame.h b/common/video_frame.h new file mode 100644 index 000000000000..3031ef207a32 --- /dev/null +++ b/common/video_frame.h @@ -0,0 +1,68 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_VIDEO_FRAME_H_ +#define LIBWEBM_COMMON_VIDEO_FRAME_H_ + +#include +#include + +namespace libwebm { + +// VideoFrame is a storage class for compressed video frames. +class VideoFrame { + public: + enum Codec { kVP8, kVP9 }; + struct Buffer { + Buffer() = default; + ~Buffer() = default; + + // Resets |data| to be of size |new_length| bytes, sets |capacity| to + // |new_length|, sets |length| to 0 (aka empty). Returns true for success. + bool Init(std::size_t new_length); + + std::unique_ptr data; + std::size_t length = 0; + std::size_t capacity = 0; + }; + + VideoFrame() = default; + ~VideoFrame() = default; + VideoFrame(std::int64_t pts_in_nanoseconds, Codec vpx_codec) + : nanosecond_pts_(pts_in_nanoseconds), codec_(vpx_codec) {} + VideoFrame(bool keyframe, std::int64_t pts_in_nanoseconds, Codec vpx_codec) + : keyframe_(keyframe), + nanosecond_pts_(pts_in_nanoseconds), + codec_(vpx_codec) {} + bool Init(std::size_t length); + bool Init(std::size_t length, std::int64_t nano_pts, Codec codec); + + // Updates actual length of data stored in |buffer_.data| when it's been + // written via the raw pointer returned from buffer_.data.get(). + // Returns false when buffer_.data.get() return nullptr and/or when + // |length| > |buffer_.length|. Returns true otherwise. + bool SetBufferLength(std::size_t length); + + // Accessors. + const Buffer& buffer() const { return buffer_; } + bool keyframe() const { return keyframe_; } + std::int64_t nanosecond_pts() const { return nanosecond_pts_; } + Codec codec() const { return codec_; } + + // Mutators. + void set_nanosecond_pts(std::int64_t nano_pts) { nanosecond_pts_ = nano_pts; } + + private: + Buffer buffer_; + bool keyframe_ = false; + std::int64_t nanosecond_pts_ = 0; + Codec codec_ = kVP9; +}; + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_VIDEO_FRAME_H_ \ No newline at end of file diff --git a/common/vp9_header_parser.cc b/common/vp9_header_parser.cc new file mode 100644 index 000000000000..c017d7f57354 --- /dev/null +++ b/common/vp9_header_parser.cc @@ -0,0 +1,269 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/vp9_header_parser.h" + +#include + +namespace vp9_parser { + +bool Vp9HeaderParser::SetFrame(const uint8_t* frame, size_t length) { + if (!frame || length == 0) + return false; + + frame_ = frame; + frame_size_ = length; + bit_offset_ = 0; + profile_ = -1; + show_existing_frame_ = 0; + key_ = 0; + altref_ = 0; + error_resilient_mode_ = 0; + intra_only_ = 0; + reset_frame_context_ = 0; + color_space_ = 0; + color_range_ = 0; + subsampling_x_ = 0; + subsampling_y_ = 0; + refresh_frame_flags_ = 0; + return true; +} + +bool Vp9HeaderParser::ParseUncompressedHeader(const uint8_t* frame, + size_t length) { + if (!SetFrame(frame, length)) + return false; + const int frame_marker = VpxReadLiteral(2); + if (frame_marker != kVp9FrameMarker) { + fprintf(stderr, "Invalid VP9 frame_marker:%d\n", frame_marker); + return false; + } + + profile_ = ReadBit(); + profile_ |= ReadBit() << 1; + if (profile_ > 2) + profile_ += ReadBit(); + + // TODO(fgalligan): Decide how to handle show existing frames. + show_existing_frame_ = ReadBit(); + if (show_existing_frame_) + return true; + + key_ = !ReadBit(); + altref_ = !ReadBit(); + error_resilient_mode_ = ReadBit(); + if (key_) { + if (!ValidateVp9SyncCode()) { + fprintf(stderr, "Invalid Sync code!\n"); + return false; + } + + ParseColorSpace(); + ParseFrameResolution(); + ParseFrameParallelMode(); + ParseTileInfo(); + } else { + intra_only_ = altref_ ? ReadBit() : 0; + reset_frame_context_ = error_resilient_mode_ ? 0 : VpxReadLiteral(2); + if (intra_only_) { + if (!ValidateVp9SyncCode()) { + fprintf(stderr, "Invalid Sync code!\n"); + return false; + } + + if (profile_ > 0) { + ParseColorSpace(); + } else { + // NOTE: The intra-only frame header does not include the specification + // of either the color format or color sub-sampling in profile 0. VP9 + // specifies that the default color format should be YUV 4:2:0 in this + // case (normative). + color_space_ = kVpxCsBt601; + color_range_ = kVpxCrStudioRange; + subsampling_y_ = subsampling_x_ = 1; + bit_depth_ = 8; + } + + refresh_frame_flags_ = VpxReadLiteral(kRefFrames); + ParseFrameResolution(); + } else { + refresh_frame_flags_ = VpxReadLiteral(kRefFrames); + for (int i = 0; i < kRefsPerFrame; ++i) { + VpxReadLiteral(kRefFrames_LOG2); // Consume ref. + ReadBit(); // Consume ref sign bias. + } + + bool found = false; + for (int i = 0; i < kRefsPerFrame; ++i) { + if (ReadBit()) { + // Found previous reference, width and height did not change since + // last frame. + found = true; + break; + } + } + + if (!found) + ParseFrameResolution(); + } + } + return true; +} + +int Vp9HeaderParser::ReadBit() { + const size_t off = bit_offset_; + const size_t byte_offset = off >> 3; + const int bit_shift = 7 - static_cast(off & 0x7); + if (byte_offset < frame_size_) { + const int bit = (frame_[byte_offset] >> bit_shift) & 1; + bit_offset_++; + return bit; + } else { + return 0; + } +} + +int Vp9HeaderParser::VpxReadLiteral(int bits) { + int value = 0; + for (int bit = bits - 1; bit >= 0; --bit) + value |= ReadBit() << bit; + return value; +} + +bool Vp9HeaderParser::ValidateVp9SyncCode() { + const int sync_code_0 = VpxReadLiteral(8); + const int sync_code_1 = VpxReadLiteral(8); + const int sync_code_2 = VpxReadLiteral(8); + return (sync_code_0 == 0x49 && sync_code_1 == 0x83 && sync_code_2 == 0x42); +} + +void Vp9HeaderParser::ParseColorSpace() { + bit_depth_ = 0; + if (profile_ >= 2) + bit_depth_ = ReadBit() ? 12 : 10; + else + bit_depth_ = 8; + color_space_ = VpxReadLiteral(3); + if (color_space_ != kVpxCsSrgb) { + color_range_ = ReadBit(); + if (profile_ == 1 || profile_ == 3) { + subsampling_x_ = ReadBit(); + subsampling_y_ = ReadBit(); + ReadBit(); + } else { + subsampling_y_ = subsampling_x_ = 1; + } + } else { + color_range_ = kVpxCrFullRange; + if (profile_ == 1 || profile_ == 3) { + subsampling_y_ = subsampling_x_ = 0; + ReadBit(); + } + } +} + +void Vp9HeaderParser::ParseFrameResolution() { + width_ = VpxReadLiteral(16) + 1; + height_ = VpxReadLiteral(16) + 1; + if (ReadBit()) { + display_width_ = VpxReadLiteral(16) + 1; + display_height_ = VpxReadLiteral(16) + 1; + } else { + display_width_ = width_; + display_height_ = height_; + } +} + +void Vp9HeaderParser::ParseFrameParallelMode() { + if (!error_resilient_mode_) { + ReadBit(); // Consume refresh frame context + frame_parallel_mode_ = ReadBit(); + } else { + frame_parallel_mode_ = 1; + } +} + +void Vp9HeaderParser::ParseTileInfo() { + VpxReadLiteral(2); // Consume frame context index + + // loopfilter + VpxReadLiteral(6); // Consume filter level + VpxReadLiteral(3); // Consume sharpness level + + const bool mode_ref_delta_enabled = ReadBit(); + if (mode_ref_delta_enabled) { + const bool mode_ref_delta_update = ReadBit(); + if (mode_ref_delta_update) { + const int kMaxRefLFDeltas = 4; + for (int i = 0; i < kMaxRefLFDeltas; ++i) { + if (ReadBit()) + VpxReadLiteral(7); // Consume ref_deltas + sign + } + + const int kMaxModeDeltas = 2; + for (int i = 0; i < kMaxModeDeltas; ++i) { + if (ReadBit()) + VpxReadLiteral(7); // Consume mode_delta + sign + } + } + } + + // quantization + VpxReadLiteral(8); // Consume base_q + SkipDeltaQ(); // y dc + SkipDeltaQ(); // uv ac + SkipDeltaQ(); // uv dc + + // segmentation + const bool segmentation_enabled = ReadBit(); + if (!segmentation_enabled) { + const int aligned_width = AlignPowerOfTwo(width_, kMiSizeLog2); + const int mi_cols = aligned_width >> kMiSizeLog2; + const int aligned_mi_cols = AlignPowerOfTwo(mi_cols, kMiSizeLog2); + const int sb_cols = aligned_mi_cols >> 3; // to_sbs(mi_cols); + int min_log2_n_tiles, max_log2_n_tiles; + + for (max_log2_n_tiles = 0; + (sb_cols >> max_log2_n_tiles) >= kMinTileWidthB64; + max_log2_n_tiles++) { + } + max_log2_n_tiles--; + if (max_log2_n_tiles < 0) + max_log2_n_tiles = 0; + + for (min_log2_n_tiles = 0; (kMaxTileWidthB64 << min_log2_n_tiles) < sb_cols; + min_log2_n_tiles++) { + } + + // columns + const int max_log2_tile_cols = max_log2_n_tiles; + const int min_log2_tile_cols = min_log2_n_tiles; + int max_ones = max_log2_tile_cols - min_log2_tile_cols; + int log2_tile_cols = min_log2_tile_cols; + while (max_ones-- && ReadBit()) + log2_tile_cols++; + + // rows + int log2_tile_rows = ReadBit(); + if (log2_tile_rows) + log2_tile_rows += ReadBit(); + + row_tiles_ = 1 << log2_tile_rows; + column_tiles_ = 1 << log2_tile_cols; + } +} + +void Vp9HeaderParser::SkipDeltaQ() { + if (ReadBit()) + VpxReadLiteral(4); +} + +int Vp9HeaderParser::AlignPowerOfTwo(int value, int n) { + return (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1)); +} + +} // namespace vp9_parser diff --git a/common/vp9_header_parser.h b/common/vp9_header_parser.h new file mode 100644 index 000000000000..3e57514d9aa9 --- /dev/null +++ b/common/vp9_header_parser.h @@ -0,0 +1,129 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_VP9_HEADER_PARSER_H_ +#define LIBWEBM_COMMON_VP9_HEADER_PARSER_H_ + +#include +#include + +namespace vp9_parser { + +const int kVp9FrameMarker = 2; +const int kMinTileWidthB64 = 4; +const int kMaxTileWidthB64 = 64; +const int kRefFrames = 8; +const int kRefsPerFrame = 3; +const int kRefFrames_LOG2 = 3; +const int kVpxCsBt601 = 1; +const int kVpxCsSrgb = 7; +const int kVpxCrStudioRange = 0; +const int kVpxCrFullRange = 1; +const int kMiSizeLog2 = 3; + +// Class to parse the header of a VP9 frame. +class Vp9HeaderParser { + public: + Vp9HeaderParser() + : frame_(NULL), + frame_size_(0), + bit_offset_(0), + profile_(-1), + show_existing_frame_(0), + key_(0), + altref_(0), + error_resilient_mode_(0), + intra_only_(0), + reset_frame_context_(0), + bit_depth_(0), + color_space_(0), + color_range_(0), + subsampling_x_(0), + subsampling_y_(0), + refresh_frame_flags_(0), + width_(0), + height_(0), + row_tiles_(0), + column_tiles_(0), + frame_parallel_mode_(0) {} + + // Parse the VP9 uncompressed header. The return values of the remaining + // functions are only valid on success. + bool ParseUncompressedHeader(const uint8_t* frame, size_t length); + + size_t frame_size() const { return frame_size_; } + int profile() const { return profile_; } + int key() const { return key_; } + int altref() const { return altref_; } + int error_resilient_mode() const { return error_resilient_mode_; } + int bit_depth() const { return bit_depth_; } + int color_space() const { return color_space_; } + int width() const { return width_; } + int height() const { return height_; } + int display_width() const { return display_width_; } + int display_height() const { return display_height_; } + int refresh_frame_flags() const { return refresh_frame_flags_; } + int row_tiles() const { return row_tiles_; } + int column_tiles() const { return column_tiles_; } + int frame_parallel_mode() const { return frame_parallel_mode_; } + + private: + // Set the compressed VP9 frame. + bool SetFrame(const uint8_t* frame, size_t length); + + // Returns the next bit of the frame. + int ReadBit(); + + // Returns the next |bits| of the frame. + int VpxReadLiteral(int bits); + + // Returns true if the vp9 sync code is valid. + bool ValidateVp9SyncCode(); + + // Parses bit_depth_, color_space_, subsampling_x_, subsampling_y_, and + // color_range_. + void ParseColorSpace(); + + // Parses width and height of the frame. + void ParseFrameResolution(); + + // Parses frame_parallel_mode_. This function skips over some features. + void ParseFrameParallelMode(); + + // Parses row and column tiles. This function skips over some features. + void ParseTileInfo(); + void SkipDeltaQ(); + int AlignPowerOfTwo(int value, int n); + + const uint8_t* frame_; + size_t frame_size_; + size_t bit_offset_; + int profile_; + int show_existing_frame_; + int key_; + int altref_; + int error_resilient_mode_; + int intra_only_; + int reset_frame_context_; + int bit_depth_; + int color_space_; + int color_range_; + int subsampling_x_; + int subsampling_y_; + int refresh_frame_flags_; + int width_; + int height_; + int display_width_; + int display_height_; + int row_tiles_; + int column_tiles_; + int frame_parallel_mode_; +}; + +} // namespace vp9_parser + +#endif // LIBWEBM_COMMON_VP9_HEADER_PARSER_H_ diff --git a/common/vp9_header_parser_tests.cc b/common/vp9_header_parser_tests.cc new file mode 100644 index 000000000000..d13bff2689ed --- /dev/null +++ b/common/vp9_header_parser_tests.cc @@ -0,0 +1,181 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/vp9_header_parser.h" + +#include + +#include "gtest/gtest.h" + +#include "common/hdr_util.h" +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" +#include "testing/test_util.h" + +namespace { + +class Vp9HeaderParserTests : public ::testing::Test { + public: + Vp9HeaderParserTests() : is_reader_open_(false), segment_(NULL) {} + + ~Vp9HeaderParserTests() override { + CloseReader(); + if (segment_ != NULL) { + delete segment_; + segment_ = NULL; + } + } + + void CloseReader() { + if (is_reader_open_) { + reader_.Close(); + } + is_reader_open_ = false; + } + + void CreateAndLoadSegment(const std::string& filename, + int expected_doc_type_ver) { + filename_ = test::GetTestFilePath(filename); + ASSERT_EQ(0, reader_.Open(filename_.c_str())); + is_reader_open_ = true; + pos_ = 0; + mkvparser::EBMLHeader ebml_header; + ebml_header.Parse(&reader_, pos_); + ASSERT_EQ(1, ebml_header.m_version); + ASSERT_EQ(1, ebml_header.m_readVersion); + ASSERT_STREQ("webm", ebml_header.m_docType); + ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion); + ASSERT_EQ(2, ebml_header.m_docTypeReadVersion); + ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)); + ASSERT_FALSE(HasFailure()); + ASSERT_GE(0, segment_->Load()); + } + + void CreateAndLoadSegment(const std::string& filename) { + CreateAndLoadSegment(filename, 4); + } + + // Load a corrupted segment with no expectation of correctness. + void CreateAndLoadInvalidSegment(const std::string& filename) { + filename_ = test::GetTestFilePath(filename); + ASSERT_EQ(0, reader_.Open(filename_.c_str())); + is_reader_open_ = true; + pos_ = 0; + mkvparser::EBMLHeader ebml_header; + ebml_header.Parse(&reader_, pos_); + ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)); + ASSERT_GE(0, segment_->Load()); + } + + void ProcessTheFrames(bool invalid_bitstream) { + unsigned char* data = NULL; + size_t data_len = 0; + const mkvparser::Tracks* const parser_tracks = segment_->GetTracks(); + ASSERT_TRUE(parser_tracks != NULL); + const mkvparser::Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster != NULL); + + while ((cluster != NULL) && !cluster->EOS()) { + const mkvparser::BlockEntry* block_entry; + long status = cluster->GetFirst(block_entry); // NOLINT + ASSERT_EQ(0, status); + + while ((block_entry != NULL) && !block_entry->EOS()) { + const mkvparser::Block* const block = block_entry->GetBlock(); + ASSERT_TRUE(block != NULL); + const long long trackNum = block->GetTrackNumber(); // NOLINT + const mkvparser::Track* const parser_track = + parser_tracks->GetTrackByNumber( + static_cast(trackNum)); // NOLINT + ASSERT_TRUE(parser_track != NULL); + const long long track_type = parser_track->GetType(); // NOLINT + + if (track_type == mkvparser::Track::kVideo) { + const int frame_count = block->GetFrameCount(); + + for (int i = 0; i < frame_count; ++i) { + const mkvparser::Block::Frame& frame = block->GetFrame(i); + + if (static_cast(frame.len) > data_len) { + delete[] data; + data = new unsigned char[frame.len]; + ASSERT_TRUE(data != NULL); + data_len = static_cast(frame.len); + } + ASSERT_FALSE(frame.Read(&reader_, data)); + ASSERT_EQ(parser_.ParseUncompressedHeader(data, data_len), + !invalid_bitstream); + } + } + + status = cluster->GetNext(block_entry, block_entry); + ASSERT_EQ(0, status); + } + + cluster = segment_->GetNext(cluster); + } + delete[] data; + } + + protected: + mkvparser::MkvReader reader_; + bool is_reader_open_; + mkvparser::Segment* segment_; + std::string filename_; + long long pos_; // NOLINT + vp9_parser::Vp9HeaderParser parser_; +}; + +TEST_F(Vp9HeaderParserTests, VideoOnlyFile) { + ASSERT_NO_FATAL_FAILURE(CreateAndLoadSegment("test_stereo_left_right.webm")); + ProcessTheFrames(false); + EXPECT_EQ(256, parser_.width()); + EXPECT_EQ(144, parser_.height()); + EXPECT_EQ(1, parser_.column_tiles()); + EXPECT_EQ(0, parser_.frame_parallel_mode()); +} + +TEST_F(Vp9HeaderParserTests, Muxed) { + ASSERT_NO_FATAL_FAILURE( + CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4)); + ProcessTheFrames(false); + EXPECT_EQ(854, parser_.width()); + EXPECT_EQ(480, parser_.height()); + EXPECT_EQ(2, parser_.column_tiles()); + EXPECT_EQ(1, parser_.frame_parallel_mode()); +} + +TEST_F(Vp9HeaderParserTests, Invalid) { + const char* files[] = { + "invalid/invalid_vp9_bitstream-bug_1416.webm", + "invalid/invalid_vp9_bitstream-bug_1417.webm", + }; + + for (int i = 0; i < static_cast(sizeof(files) / sizeof(files[0])); ++i) { + SCOPED_TRACE(files[i]); + ASSERT_NO_FATAL_FAILURE(CreateAndLoadInvalidSegment(files[i])); + ProcessTheFrames(true); + CloseReader(); + delete segment_; + segment_ = NULL; + } +} + +TEST_F(Vp9HeaderParserTests, API) { + vp9_parser::Vp9HeaderParser parser; + uint8_t data; + EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 0)); + EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 10)); + EXPECT_FALSE(parser.ParseUncompressedHeader(&data, 0)); +} + +} // namespace + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/common/vp9_level_stats.cc b/common/vp9_level_stats.cc new file mode 100644 index 000000000000..76891e6823e4 --- /dev/null +++ b/common/vp9_level_stats.cc @@ -0,0 +1,269 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/vp9_level_stats.h" + +#include + +#include +#include + +#include "common/webm_constants.h" + +namespace vp9_parser { + +const Vp9LevelRow Vp9LevelStats::Vp9LevelTable[kNumVp9Levels] = { + {LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8, 512}, + {LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8, 768}, + {LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8, 960}, + {LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8, 1344}, + {LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8, 2048}, + {LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8, 2752}, + {LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8, 4160}, + {LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6, 4160}, + {LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4, 8384}, + {LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4, 8384}, + // CPB Size = 0 for levels 5_2 to 6_2 + {LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4, 8384}, + {LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4, 16832}, + {LEVEL_6_1, 2353004544, 35651584, 240000, 0, 8, 16, 10, 4, 16832}, + {LEVEL_6_2, 4706009088, 35651584, 480000, 0, 8, 16, 10, 4, 16832}}; + +void Vp9LevelStats::AddFrame(const Vp9HeaderParser& parser, int64_t time_ns) { + ++frames; + if (start_ns_ == -1) + start_ns_ = time_ns; + end_ns_ = time_ns; + + const int width = parser.width(); + const int height = parser.height(); + const int64_t luma_picture_size = width * height; + const int64_t luma_picture_breadth = (width > height) ? width : height; + if (luma_picture_size > max_luma_picture_size_) + max_luma_picture_size_ = luma_picture_size; + if (luma_picture_breadth > max_luma_picture_breadth_) + max_luma_picture_breadth_ = luma_picture_breadth; + + total_compressed_size_ += parser.frame_size(); + + while (!luma_window_.empty() && + luma_window_.front().first < + (time_ns - (libwebm::kNanosecondsPerSecondi - 1))) { + current_luma_size_ -= luma_window_.front().second; + luma_window_.pop(); + } + current_luma_size_ += luma_picture_size; + luma_window_.push(std::make_pair(time_ns, luma_picture_size)); + if (current_luma_size_ > max_luma_size_) { + max_luma_size_ = current_luma_size_; + max_luma_end_ns_ = luma_window_.back().first; + } + + // Record CPB stats. + // Remove all frames that are less than window size. + while (cpb_window_.size() > 3) { + current_cpb_size_ -= cpb_window_.front().second; + cpb_window_.pop(); + } + cpb_window_.push(std::make_pair(time_ns, parser.frame_size())); + + current_cpb_size_ += parser.frame_size(); + if (current_cpb_size_ > max_cpb_size_) { + max_cpb_size_ = current_cpb_size_; + max_cpb_start_ns_ = cpb_window_.front().first; + max_cpb_end_ns_ = cpb_window_.back().first; + } + + if (max_cpb_window_size_ < static_cast(cpb_window_.size())) { + max_cpb_window_size_ = cpb_window_.size(); + max_cpb_window_end_ns_ = time_ns; + } + + // Record altref stats. + if (parser.altref()) { + const int delta_altref = frames_since_last_altref; + if (first_altref) { + first_altref = false; + } else if (delta_altref < minimum_altref_distance) { + minimum_altref_distance = delta_altref; + min_altref_end_ns = time_ns; + } + frames_since_last_altref = 0; + } else { + ++frames_since_last_altref; + ++displayed_frames; + // TODO(fgalligan): Add support for other color formats. Currently assuming + // 420. + total_uncompressed_bits_ += + (luma_picture_size * parser.bit_depth() * 3) / 2; + } + + // Count max reference frames. + if (parser.key() == 1) { + frames_refreshed_ = 0; + } else { + frames_refreshed_ |= parser.refresh_frame_flags(); + + int ref_frame_count = frames_refreshed_ & 1; + for (int i = 1; i < kMaxVp9RefFrames; ++i) { + ref_frame_count += (frames_refreshed_ >> i) & 1; + } + + if (ref_frame_count > max_frames_refreshed_) + max_frames_refreshed_ = ref_frame_count; + } + + // Count max tiles. + const int tiles = parser.column_tiles(); + if (tiles > max_column_tiles_) + max_column_tiles_ = tiles; +} + +Vp9Level Vp9LevelStats::GetLevel() const { + const int64_t max_luma_sample_rate = GetMaxLumaSampleRate(); + const int64_t max_luma_picture_size = GetMaxLumaPictureSize(); + const int64_t max_luma_picture_breadth = GetMaxLumaPictureBreadth(); + const double average_bitrate = GetAverageBitRate(); + const double max_cpb_size = GetMaxCpbSize(); + const double compresion_ratio = GetCompressionRatio(); + const int max_column_tiles = GetMaxColumnTiles(); + const int min_altref_distance = GetMinimumAltrefDistance(); + const int max_ref_frames = GetMaxReferenceFrames(); + + int level_index = 0; + Vp9Level max_level = LEVEL_UNKNOWN; + const double grace_multiplier = + max_luma_sample_rate_grace_percent_ / 100.0 + 1.0; + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_luma_sample_rate <= + Vp9LevelTable[i].max_luma_sample_rate * grace_multiplier) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_luma_picture_size <= Vp9LevelTable[i].max_luma_picture_size) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_luma_picture_breadth <= Vp9LevelTable[i].max_luma_picture_breadth) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (average_bitrate <= Vp9LevelTable[i].average_bitrate) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + // Only check CPB size for levels that are defined. + if (Vp9LevelTable[i].max_cpb_size > 0 && + max_cpb_size <= Vp9LevelTable[i].max_cpb_size) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_column_tiles <= Vp9LevelTable[i].max_tiles) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_ref_frames <= Vp9LevelTable[i].max_ref_frames) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + + // Check if the current level meets the minimum altref distance requirement. + // If not, set to unknown level as we can't move up a level as the minimum + // altref distance get farther apart and we can't move down a level as we are + // already at the minimum level for all the other requirements. + if (min_altref_distance < Vp9LevelTable[level_index].min_altref_distance) + max_level = LEVEL_UNKNOWN; + + // The minimum compression ratio has the same behavior as minimum altref + // distance. + if (compresion_ratio < Vp9LevelTable[level_index].compresion_ratio) + max_level = LEVEL_UNKNOWN; + return max_level; +} + +int64_t Vp9LevelStats::GetMaxLumaSampleRate() const { return max_luma_size_; } + +int64_t Vp9LevelStats::GetMaxLumaPictureSize() const { + return max_luma_picture_size_; +} + +int64_t Vp9LevelStats::GetMaxLumaPictureBreadth() const { + return max_luma_picture_breadth_; +} + +double Vp9LevelStats::GetAverageBitRate() const { + const int64_t frame_duration_ns = end_ns_ - start_ns_; + double duration_seconds = + ((duration_ns_ == -1) ? frame_duration_ns : duration_ns_) / + libwebm::kNanosecondsPerSecond; + if (estimate_last_frame_duration_ && + (duration_ns_ == -1 || duration_ns_ <= frame_duration_ns)) { + const double sec_per_frame = frame_duration_ns / + libwebm::kNanosecondsPerSecond / + (displayed_frames - 1); + duration_seconds += sec_per_frame; + } + + return total_compressed_size_ / duration_seconds / 125.0; +} + +double Vp9LevelStats::GetMaxCpbSize() const { return max_cpb_size_ / 125.0; } + +double Vp9LevelStats::GetCompressionRatio() const { + return total_uncompressed_bits_ / + static_cast(total_compressed_size_ * 8); +} + +int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; } + +int Vp9LevelStats::GetMinimumAltrefDistance() const { + if (minimum_altref_distance != std::numeric_limits::max()) + return minimum_altref_distance; + else + return -1; +} + +int Vp9LevelStats::GetMaxReferenceFrames() const { + return max_frames_refreshed_; +} + +} // namespace vp9_parser diff --git a/common/vp9_level_stats.h b/common/vp9_level_stats.h new file mode 100644 index 000000000000..45d6f5cecb0e --- /dev/null +++ b/common/vp9_level_stats.h @@ -0,0 +1,215 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_VP9_LEVEL_STATS_H_ +#define LIBWEBM_COMMON_VP9_LEVEL_STATS_H_ + +#include +#include +#include + +#include "common/vp9_header_parser.h" + +namespace vp9_parser { + +const int kMaxVp9RefFrames = 8; + +// Defined VP9 levels. See http://www.webmproject.org/vp9/profiles/ for +// detailed information on VP9 levels. +const int kNumVp9Levels = 14; +enum Vp9Level { + LEVEL_UNKNOWN = 0, + LEVEL_1 = 10, + LEVEL_1_1 = 11, + LEVEL_2 = 20, + LEVEL_2_1 = 21, + LEVEL_3 = 30, + LEVEL_3_1 = 31, + LEVEL_4 = 40, + LEVEL_4_1 = 41, + LEVEL_5 = 50, + LEVEL_5_1 = 51, + LEVEL_5_2 = 52, + LEVEL_6 = 60, + LEVEL_6_1 = 61, + LEVEL_6_2 = 62 +}; + +struct Vp9LevelRow { + Vp9LevelRow() = default; + ~Vp9LevelRow() = default; + Vp9LevelRow(Vp9LevelRow&& other) = default; + Vp9LevelRow(const Vp9LevelRow& other) = default; + Vp9LevelRow& operator=(Vp9LevelRow&& other) = delete; + Vp9LevelRow& operator=(const Vp9LevelRow& other) = delete; + + Vp9Level level; + int64_t max_luma_sample_rate; + int64_t max_luma_picture_size; + int64_t max_luma_picture_breadth; + double average_bitrate; + double max_cpb_size; + double compresion_ratio; + int max_tiles; + int min_altref_distance; + int max_ref_frames; +}; + +// Class to determine the VP9 level of a VP9 bitstream. +class Vp9LevelStats { + public: + static const Vp9LevelRow Vp9LevelTable[kNumVp9Levels]; + + Vp9LevelStats() + : frames(0), + displayed_frames(0), + start_ns_(-1), + end_ns_(-1), + duration_ns_(-1), + max_luma_picture_size_(0), + max_luma_picture_breadth_(0), + current_luma_size_(0), + max_luma_size_(0), + max_luma_end_ns_(0), + max_luma_sample_rate_grace_percent_(1.5), + first_altref(true), + frames_since_last_altref(0), + minimum_altref_distance(std::numeric_limits::max()), + min_altref_end_ns(0), + max_cpb_window_size_(0), + max_cpb_window_end_ns_(0), + current_cpb_size_(0), + max_cpb_size_(0), + max_cpb_start_ns_(0), + max_cpb_end_ns_(0), + total_compressed_size_(0), + total_uncompressed_bits_(0), + frames_refreshed_(0), + max_frames_refreshed_(0), + max_column_tiles_(0), + estimate_last_frame_duration_(true) {} + + ~Vp9LevelStats() = default; + Vp9LevelStats(Vp9LevelStats&& other) = delete; + Vp9LevelStats(const Vp9LevelStats& other) = delete; + Vp9LevelStats& operator=(Vp9LevelStats&& other) = delete; + Vp9LevelStats& operator=(const Vp9LevelStats& other) = delete; + + // Collects stats on a VP9 frame. The frame must already be parsed by + // |parser|. |time_ns| is the start time of the frame in nanoseconds. + void AddFrame(const Vp9HeaderParser& parser, int64_t time_ns); + + // Returns the current VP9 level. All of the video frames should have been + // processed with AddFrame before calling this function. + Vp9Level GetLevel() const; + + // Returns the maximum luma samples (pixels) per second. The Alt-Ref frames + // are taken into account, therefore this number may be larger than the + // display luma samples per second + int64_t GetMaxLumaSampleRate() const; + + // The maximum frame size (width * height) in samples. + int64_t GetMaxLumaPictureSize() const; + + // The maximum frame breadth (max of width and height) in samples. + int64_t GetMaxLumaPictureBreadth() const; + + // The average bitrate of the video in kbps. + double GetAverageBitRate() const; + + // The largest data size for any 4 consecutive frames in kilobits. + double GetMaxCpbSize() const; + + // The ratio of total bytes decompressed over total bytes compressed. + double GetCompressionRatio() const; + + // The maximum number of VP9 column tiles. + int GetMaxColumnTiles() const; + + // The minimum distance in frames between two consecutive alternate reference + // frames. + int GetMinimumAltrefDistance() const; + + // The maximum number of reference frames that had to be stored. + int GetMaxReferenceFrames() const; + + // Sets the duration of the video stream in nanoseconds. If the duration is + // not explictly set by this function then this class will use end - start + // as the duration. + void set_duration(int64_t time_ns) { duration_ns_ = time_ns; } + double max_luma_sample_rate_grace_percent() const { + return max_luma_sample_rate_grace_percent_; + } + void set_max_luma_sample_rate_grace_percent(double percent) { + max_luma_sample_rate_grace_percent_ = percent; + } + bool estimate_last_frame_duration() const { + return estimate_last_frame_duration_; + } + + // If true try to estimate the last frame's duration if the stream's duration + // is not set or the stream's duration equals the last frame's timestamp. + void set_estimate_last_frame_duration(bool flag) { + estimate_last_frame_duration_ = flag; + } + + private: + int frames; + int displayed_frames; + + int64_t start_ns_; + int64_t end_ns_; + int64_t duration_ns_; + + int64_t max_luma_picture_size_; + int64_t max_luma_picture_breadth_; + + // This is used to calculate the maximum number of luma samples per second. + // The first value is the luma picture size and the second value is the time + // in nanoseconds of one frame. + std::queue> luma_window_; + int64_t current_luma_size_; + int64_t max_luma_size_; + int64_t max_luma_end_ns_; + + // MaxLumaSampleRate = (ExampleFrameRate + ExampleFrameRate / + // MinimumAltrefDistance) * MaxLumaPictureSize. For levels 1-4 + // ExampleFrameRate / MinimumAltrefDistance is non-integer, so using a sliding + // window of one frame to calculate MaxLumaSampleRate may have frames > + // (ExampleFrameRate + ExampleFrameRate / MinimumAltrefDistance) in the + // window. In order to address this issue, a grace percent of 1.5 was added. + double max_luma_sample_rate_grace_percent_; + + bool first_altref; + int frames_since_last_altref; + int minimum_altref_distance; + int64_t min_altref_end_ns; + + // This is used to calculate the maximum number of compressed bytes for four + // consecutive frames. The first value is the compressed frame size and the + // second value is the time in nanoseconds of one frame. + std::queue> cpb_window_; + int64_t max_cpb_window_size_; + int64_t max_cpb_window_end_ns_; + int64_t current_cpb_size_; + int64_t max_cpb_size_; + int64_t max_cpb_start_ns_; + int64_t max_cpb_end_ns_; + + int64_t total_compressed_size_; + int64_t total_uncompressed_bits_; + int frames_refreshed_; + int max_frames_refreshed_; + + int max_column_tiles_; + + bool estimate_last_frame_duration_; +}; + +} // namespace vp9_parser + +#endif // LIBWEBM_COMMON_VP9_LEVEL_STATS_H_ diff --git a/common/vp9_level_stats_tests.cc b/common/vp9_level_stats_tests.cc new file mode 100644 index 000000000000..0dec07174b6f --- /dev/null +++ b/common/vp9_level_stats_tests.cc @@ -0,0 +1,191 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/vp9_level_stats.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "common/hdr_util.h" +#include "common/vp9_header_parser.h" +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" +#include "testing/test_util.h" + +namespace { + +// TODO(fgalligan): Refactor this test with other test files in this directory. +class Vp9LevelStatsTests : public ::testing::Test { + public: + Vp9LevelStatsTests() : is_reader_open_(false) {} + + ~Vp9LevelStatsTests() override { CloseReader(); } + + void CloseReader() { + if (is_reader_open_) { + reader_.Close(); + } + is_reader_open_ = false; + } + + void CreateAndLoadSegment(const std::string& filename, + int expected_doc_type_ver) { + ASSERT_NE(0u, filename.length()); + filename_ = test::GetTestFilePath(filename); + ASSERT_EQ(0, reader_.Open(filename_.c_str())); + is_reader_open_ = true; + pos_ = 0; + mkvparser::EBMLHeader ebml_header; + ebml_header.Parse(&reader_, pos_); + ASSERT_EQ(1, ebml_header.m_version); + ASSERT_EQ(1, ebml_header.m_readVersion); + ASSERT_STREQ("webm", ebml_header.m_docType); + ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion); + ASSERT_EQ(2, ebml_header.m_docTypeReadVersion); + mkvparser::Segment* temp; + ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, temp)); + segment_.reset(temp); + ASSERT_FALSE(HasFailure()); + ASSERT_GE(0, segment_->Load()); + } + + void CreateAndLoadSegment(const std::string& filename) { + CreateAndLoadSegment(filename, 4); + } + + void ProcessTheFrames() { + std::vector data; + size_t data_len = 0; + const mkvparser::Tracks* const parser_tracks = segment_->GetTracks(); + ASSERT_TRUE(parser_tracks != NULL); + const mkvparser::Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster); + + while ((cluster != NULL) && !cluster->EOS()) { + const mkvparser::BlockEntry* block_entry; + long status = cluster->GetFirst(block_entry); // NOLINT + ASSERT_EQ(0, status); + + while ((block_entry != NULL) && !block_entry->EOS()) { + const mkvparser::Block* const block = block_entry->GetBlock(); + ASSERT_TRUE(block != NULL); + const long long trackNum = block->GetTrackNumber(); // NOLINT + const mkvparser::Track* const parser_track = + parser_tracks->GetTrackByNumber( + static_cast(trackNum)); // NOLINT + ASSERT_TRUE(parser_track != NULL); + const long long track_type = parser_track->GetType(); // NOLINT + + if (track_type == mkvparser::Track::kVideo) { + const int frame_count = block->GetFrameCount(); + const long long time_ns = block->GetTime(cluster); // NOLINT + + for (int i = 0; i < frame_count; ++i) { + const mkvparser::Block::Frame& frame = block->GetFrame(i); + if (static_cast(frame.len) > data.size()) { + data.resize(frame.len); + data_len = static_cast(frame.len); + } + ASSERT_FALSE(frame.Read(&reader_, &data[0])); + parser_.ParseUncompressedHeader(&data[0], data_len); + stats_.AddFrame(parser_, time_ns); + } + } + + status = cluster->GetNext(block_entry, block_entry); + ASSERT_EQ(0, status); + } + + cluster = segment_->GetNext(cluster); + } + } + + protected: + mkvparser::MkvReader reader_; + bool is_reader_open_; + std::unique_ptr segment_; + std::string filename_; + long long pos_; // NOLINT + vp9_parser::Vp9HeaderParser parser_; + vp9_parser::Vp9LevelStats stats_; +}; + +TEST_F(Vp9LevelStatsTests, VideoOnlyFile) { + CreateAndLoadSegment("test_stereo_left_right.webm"); + ProcessTheFrames(); + EXPECT_EQ(256, parser_.width()); + EXPECT_EQ(144, parser_.height()); + EXPECT_EQ(1, parser_.column_tiles()); + EXPECT_EQ(0, parser_.frame_parallel_mode()); + + EXPECT_EQ(11, stats_.GetLevel()); + EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate()); + EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize()); + EXPECT_DOUBLE_EQ(264.03233333333333, stats_.GetAverageBitRate()); + EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize()); + EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio()); + EXPECT_EQ(1, stats_.GetMaxColumnTiles()); + EXPECT_EQ(11, stats_.GetMinimumAltrefDistance()); + EXPECT_EQ(3, stats_.GetMaxReferenceFrames()); + + EXPECT_TRUE(stats_.estimate_last_frame_duration()); + stats_.set_estimate_last_frame_duration(false); + EXPECT_DOUBLE_EQ(275.512, stats_.GetAverageBitRate()); +} + +TEST_F(Vp9LevelStatsTests, Muxed) { + CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4); + ProcessTheFrames(); + EXPECT_EQ(854, parser_.width()); + EXPECT_EQ(480, parser_.height()); + EXPECT_EQ(2, parser_.column_tiles()); + EXPECT_EQ(1, parser_.frame_parallel_mode()); + + EXPECT_EQ(30, stats_.GetLevel()); + EXPECT_EQ(9838080, stats_.GetMaxLumaSampleRate()); + EXPECT_EQ(409920, stats_.GetMaxLumaPictureSize()); + EXPECT_DOUBLE_EQ(447.09394572025053, stats_.GetAverageBitRate()); + EXPECT_DOUBLE_EQ(118.464, stats_.GetMaxCpbSize()); + EXPECT_DOUBLE_EQ(241.17670131398313, stats_.GetCompressionRatio()); + EXPECT_EQ(2, stats_.GetMaxColumnTiles()); + EXPECT_EQ(9, stats_.GetMinimumAltrefDistance()); + EXPECT_EQ(3, stats_.GetMaxReferenceFrames()); + + stats_.set_estimate_last_frame_duration(false); + EXPECT_DOUBLE_EQ(468.38413361169108, stats_.GetAverageBitRate()); +} + +TEST_F(Vp9LevelStatsTests, SetDuration) { + CreateAndLoadSegment("test_stereo_left_right.webm"); + ProcessTheFrames(); + const int64_t kDurationNano = 2080000000; // 2.08 seconds + stats_.set_duration(kDurationNano); + EXPECT_EQ(256, parser_.width()); + EXPECT_EQ(144, parser_.height()); + EXPECT_EQ(1, parser_.column_tiles()); + EXPECT_EQ(0, parser_.frame_parallel_mode()); + + EXPECT_EQ(11, stats_.GetLevel()); + EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate()); + EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize()); + EXPECT_DOUBLE_EQ(264.9153846153846, stats_.GetAverageBitRate()); + EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize()); + EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio()); + EXPECT_EQ(1, stats_.GetMaxColumnTiles()); + EXPECT_EQ(11, stats_.GetMinimumAltrefDistance()); + EXPECT_EQ(3, stats_.GetMaxReferenceFrames()); +} + +} // namespace + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/common/webm_constants.h b/common/webm_constants.h new file mode 100644 index 000000000000..a082ce86aa2a --- /dev/null +++ b/common/webm_constants.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef LIBWEBM_COMMON_WEBM_CONSTANTS_H_ +#define LIBWEBM_COMMON_WEBM_CONSTANTS_H_ + +namespace libwebm { + +const double kNanosecondsPerSecond = 1000000000.0; +const int kNanosecondsPerSecondi = 1000000000; +const int kNanosecondsPerMillisecond = 1000000; + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_WEBM_CONSTANTS_H_ diff --git a/common/webm_endian.cc b/common/webm_endian.cc new file mode 100644 index 000000000000..b1ef7cad6ce1 --- /dev/null +++ b/common/webm_endian.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "common/webm_endian.h" + +#include + +namespace { + +// Swaps unsigned 32 bit values to big endian if needed. Returns |value| +// unmodified if architecture is big endian. Returns swapped bytes of |value| +// if architecture is little endian. Returns 0 otherwise. +uint32_t swap32_check_little_endian(uint32_t value) { + // Check endianness. + union { + uint32_t val32; + uint8_t c[4]; + } check; + check.val32 = 0x01234567U; + + // Check for big endian. + if (check.c[3] == 0x67) + return value; + + // Check for not little endian. + if (check.c[0] != 0x67) + return 0; + + return value << 24 | ((value << 8) & 0x0000FF00U) | + ((value >> 8) & 0x00FF0000U) | value >> 24; +} + +// Swaps unsigned 64 bit values to big endian if needed. Returns |value| +// unmodified if architecture is big endian. Returns swapped bytes of |value| +// if architecture is little endian. Returns 0 otherwise. +uint64_t swap64_check_little_endian(uint64_t value) { + // Check endianness. + union { + uint64_t val64; + uint8_t c[8]; + } check; + check.val64 = 0x0123456789ABCDEFULL; + + // Check for big endian. + if (check.c[7] == 0xEF) + return value; + + // Check for not little endian. + if (check.c[0] != 0xEF) + return 0; + + return value << 56 | ((value << 40) & 0x00FF000000000000ULL) | + ((value << 24) & 0x0000FF0000000000ULL) | + ((value << 8) & 0x000000FF00000000ULL) | + ((value >> 8) & 0x00000000FF000000ULL) | + ((value >> 24) & 0x0000000000FF0000ULL) | + ((value >> 40) & 0x000000000000FF00ULL) | value >> 56; +} + +} // namespace + +namespace libwebm { + +uint32_t host_to_bigendian(uint32_t value) { + return swap32_check_little_endian(value); +} + +uint32_t bigendian_to_host(uint32_t value) { + return swap32_check_little_endian(value); +} + +uint64_t host_to_bigendian(uint64_t value) { + return swap64_check_little_endian(value); +} + +uint64_t bigendian_to_host(uint64_t value) { + return swap64_check_little_endian(value); +} + +} // namespace libwebm diff --git a/common/webm_endian.h b/common/webm_endian.h new file mode 100644 index 000000000000..351778b7351f --- /dev/null +++ b/common/webm_endian.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef LIBWEBM_COMMON_WEBM_ENDIAN_H_ +#define LIBWEBM_COMMON_WEBM_ENDIAN_H_ + +#include + +namespace libwebm { + +// Swaps unsigned 32 bit values to big endian if needed. Returns |value| if +// architecture is big endian. Returns big endian value if architecture is +// little endian. Returns 0 otherwise. +uint32_t host_to_bigendian(uint32_t value); + +// Swaps unsigned 32 bit values to little endian if needed. Returns |value| if +// architecture is big endian. Returns little endian value if architecture is +// little endian. Returns 0 otherwise. +uint32_t bigendian_to_host(uint32_t value); + +// Swaps unsigned 64 bit values to big endian if needed. Returns |value| if +// architecture is big endian. Returns big endian value if architecture is +// little endian. Returns 0 otherwise. +uint64_t host_to_bigendian(uint64_t value); + +// Swaps unsigned 64 bit values to little endian if needed. Returns |value| if +// architecture is big endian. Returns little endian value if architecture is +// little endian. Returns 0 otherwise. +uint64_t bigendian_to_host(uint64_t value); + +} // namespace libwebm + +#endif // LIBWEBM_COMMON_WEBM_ENDIAN_H_ diff --git a/common/webmids.h b/common/webmids.h new file mode 100644 index 000000000000..fc0c2081409d --- /dev/null +++ b/common/webmids.h @@ -0,0 +1,193 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef COMMON_WEBMIDS_H_ +#define COMMON_WEBMIDS_H_ + +namespace libwebm { + +enum MkvId { + kMkvEBML = 0x1A45DFA3, + kMkvEBMLVersion = 0x4286, + kMkvEBMLReadVersion = 0x42F7, + kMkvEBMLMaxIDLength = 0x42F2, + kMkvEBMLMaxSizeLength = 0x42F3, + kMkvDocType = 0x4282, + kMkvDocTypeVersion = 0x4287, + kMkvDocTypeReadVersion = 0x4285, + kMkvVoid = 0xEC, + kMkvSignatureSlot = 0x1B538667, + kMkvSignatureAlgo = 0x7E8A, + kMkvSignatureHash = 0x7E9A, + kMkvSignaturePublicKey = 0x7EA5, + kMkvSignature = 0x7EB5, + kMkvSignatureElements = 0x7E5B, + kMkvSignatureElementList = 0x7E7B, + kMkvSignedElement = 0x6532, + // segment + kMkvSegment = 0x18538067, + // Meta Seek Information + kMkvSeekHead = 0x114D9B74, + kMkvSeek = 0x4DBB, + kMkvSeekID = 0x53AB, + kMkvSeekPosition = 0x53AC, + // Segment Information + kMkvInfo = 0x1549A966, + kMkvTimecodeScale = 0x2AD7B1, + kMkvDuration = 0x4489, + kMkvDateUTC = 0x4461, + kMkvTitle = 0x7BA9, + kMkvMuxingApp = 0x4D80, + kMkvWritingApp = 0x5741, + // Cluster + kMkvCluster = 0x1F43B675, + kMkvTimecode = 0xE7, + kMkvPrevSize = 0xAB, + kMkvBlockGroup = 0xA0, + kMkvBlock = 0xA1, + kMkvBlockDuration = 0x9B, + kMkvReferenceBlock = 0xFB, + kMkvLaceNumber = 0xCC, + kMkvSimpleBlock = 0xA3, + kMkvBlockAdditions = 0x75A1, + kMkvBlockMore = 0xA6, + kMkvBlockAddID = 0xEE, + kMkvBlockAdditional = 0xA5, + kMkvDiscardPadding = 0x75A2, + // Track + kMkvTracks = 0x1654AE6B, + kMkvTrackEntry = 0xAE, + kMkvTrackNumber = 0xD7, + kMkvTrackUID = 0x73C5, + kMkvTrackType = 0x83, + kMkvFlagEnabled = 0xB9, + kMkvFlagDefault = 0x88, + kMkvFlagForced = 0x55AA, + kMkvFlagLacing = 0x9C, + kMkvDefaultDuration = 0x23E383, + kMkvMaxBlockAdditionID = 0x55EE, + kMkvName = 0x536E, + kMkvLanguage = 0x22B59C, + kMkvCodecID = 0x86, + kMkvCodecPrivate = 0x63A2, + kMkvCodecName = 0x258688, + kMkvCodecDelay = 0x56AA, + kMkvSeekPreRoll = 0x56BB, + // video + kMkvVideo = 0xE0, + kMkvFlagInterlaced = 0x9A, + kMkvStereoMode = 0x53B8, + kMkvAlphaMode = 0x53C0, + kMkvPixelWidth = 0xB0, + kMkvPixelHeight = 0xBA, + kMkvPixelCropBottom = 0x54AA, + kMkvPixelCropTop = 0x54BB, + kMkvPixelCropLeft = 0x54CC, + kMkvPixelCropRight = 0x54DD, + kMkvDisplayWidth = 0x54B0, + kMkvDisplayHeight = 0x54BA, + kMkvDisplayUnit = 0x54B2, + kMkvAspectRatioType = 0x54B3, + kMkvColourSpace = 0x2EB524, + kMkvFrameRate = 0x2383E3, + // end video + // colour + kMkvColour = 0x55B0, + kMkvMatrixCoefficients = 0x55B1, + kMkvBitsPerChannel = 0x55B2, + kMkvChromaSubsamplingHorz = 0x55B3, + kMkvChromaSubsamplingVert = 0x55B4, + kMkvCbSubsamplingHorz = 0x55B5, + kMkvCbSubsamplingVert = 0x55B6, + kMkvChromaSitingHorz = 0x55B7, + kMkvChromaSitingVert = 0x55B8, + kMkvRange = 0x55B9, + kMkvTransferCharacteristics = 0x55BA, + kMkvPrimaries = 0x55BB, + kMkvMaxCLL = 0x55BC, + kMkvMaxFALL = 0x55BD, + // mastering metadata + kMkvMasteringMetadata = 0x55D0, + kMkvPrimaryRChromaticityX = 0x55D1, + kMkvPrimaryRChromaticityY = 0x55D2, + kMkvPrimaryGChromaticityX = 0x55D3, + kMkvPrimaryGChromaticityY = 0x55D4, + kMkvPrimaryBChromaticityX = 0x55D5, + kMkvPrimaryBChromaticityY = 0x55D6, + kMkvWhitePointChromaticityX = 0x55D7, + kMkvWhitePointChromaticityY = 0x55D8, + kMkvLuminanceMax = 0x55D9, + kMkvLuminanceMin = 0x55DA, + // end mastering metadata + // end colour + // projection + kMkvProjection = 0x7670, + kMkvProjectionType = 0x7671, + kMkvProjectionPrivate = 0x7672, + kMkvProjectionPoseYaw = 0x7673, + kMkvProjectionPosePitch = 0x7674, + kMkvProjectionPoseRoll = 0x7675, + // end projection + // audio + kMkvAudio = 0xE1, + kMkvSamplingFrequency = 0xB5, + kMkvOutputSamplingFrequency = 0x78B5, + kMkvChannels = 0x9F, + kMkvBitDepth = 0x6264, + // end audio + // ContentEncodings + kMkvContentEncodings = 0x6D80, + kMkvContentEncoding = 0x6240, + kMkvContentEncodingOrder = 0x5031, + kMkvContentEncodingScope = 0x5032, + kMkvContentEncodingType = 0x5033, + kMkvContentCompression = 0x5034, + kMkvContentCompAlgo = 0x4254, + kMkvContentCompSettings = 0x4255, + kMkvContentEncryption = 0x5035, + kMkvContentEncAlgo = 0x47E1, + kMkvContentEncKeyID = 0x47E2, + kMkvContentSignature = 0x47E3, + kMkvContentSigKeyID = 0x47E4, + kMkvContentSigAlgo = 0x47E5, + kMkvContentSigHashAlgo = 0x47E6, + kMkvContentEncAESSettings = 0x47E7, + kMkvAESSettingsCipherMode = 0x47E8, + kMkvAESSettingsCipherInitData = 0x47E9, + // end ContentEncodings + // Cueing Data + kMkvCues = 0x1C53BB6B, + kMkvCuePoint = 0xBB, + kMkvCueTime = 0xB3, + kMkvCueTrackPositions = 0xB7, + kMkvCueTrack = 0xF7, + kMkvCueClusterPosition = 0xF1, + kMkvCueBlockNumber = 0x5378, + // Chapters + kMkvChapters = 0x1043A770, + kMkvEditionEntry = 0x45B9, + kMkvChapterAtom = 0xB6, + kMkvChapterUID = 0x73C4, + kMkvChapterStringUID = 0x5654, + kMkvChapterTimeStart = 0x91, + kMkvChapterTimeEnd = 0x92, + kMkvChapterDisplay = 0x80, + kMkvChapString = 0x85, + kMkvChapLanguage = 0x437C, + kMkvChapCountry = 0x437E, + // Tags + kMkvTags = 0x1254C367, + kMkvTag = 0x7373, + kMkvSimpleTag = 0x67C8, + kMkvTagName = 0x45A3, + kMkvTagString = 0x4487 +}; + +} // namespace libwebm + +#endif // COMMON_WEBMIDS_H_ diff --git a/dumpvtt.cc b/dumpvtt.cc new file mode 100644 index 000000000000..472da5238bde --- /dev/null +++ b/dumpvtt.cc @@ -0,0 +1,91 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include +#include +#include "webvtt/vttreader.h" +#include "webvtt/webvttparser.h" + +int main(int argc, const char* argv[]) { + if (argc != 2) { + fprintf(stdout, "usage: dumpvtt \n"); + return EXIT_SUCCESS; + } + + libwebvtt::VttReader reader; + const char* const filename = argv[1]; + + if (int e = reader.Open(filename)) { + (void)e; + fprintf(stderr, "open failed\n"); + return EXIT_FAILURE; + } + + libwebvtt::Parser parser(&reader); + + if (int e = parser.Init()) { + (void)e; + fprintf(stderr, "parser init failed\n"); + return EXIT_FAILURE; + } + + for (libwebvtt::Cue cue;;) { + const int e = parser.Parse(&cue); + + if (e < 0) { // error + fprintf(stderr, "error parsing cue\n"); + return EXIT_FAILURE; + } + + if (e > 0) // EOF + return EXIT_SUCCESS; + + fprintf(stdout, "cue identifier: \"%s\"\n", cue.identifier.c_str()); + + const libwebvtt::Time& st = cue.start_time; + fprintf(stdout, "cue start time: \"HH=%i MM=%i SS=%i SSS=%i\"\n", st.hours, + st.minutes, st.seconds, st.milliseconds); + + const libwebvtt::Time& sp = cue.stop_time; + fprintf(stdout, "cue stop time: \"HH=%i MM=%i SS=%i SSS=%i\"\n", sp.hours, + sp.minutes, sp.seconds, sp.milliseconds); + + { + typedef libwebvtt::Cue::settings_t::const_iterator iter_t; + iter_t i = cue.settings.begin(); + const iter_t j = cue.settings.end(); + + if (i == j) { + fprintf(stdout, "cue setting: \n"); + } else { + while (i != j) { + const libwebvtt::Setting& setting = *i++; + fprintf(stdout, "cue setting: name=%s value=%s\n", + setting.name.c_str(), setting.value.c_str()); + } + } + } + + { + typedef libwebvtt::Cue::payload_t::const_iterator iter_t; + iter_t i = cue.payload.begin(); + const iter_t j = cue.payload.end(); + + int idx = 1; + while (i != j) { + const std::string& payload = *i++; + const char* const payload_str = payload.c_str(); + fprintf(stdout, "cue payload[%i]: \"%s\"\n", idx, payload_str); + ++idx; + } + } + + fprintf(stdout, "\n"); + fflush(stdout); + } +} diff --git a/hdr_util.hpp b/hdr_util.hpp new file mode 100644 index 000000000000..7abcb3a71741 --- /dev/null +++ b/hdr_util.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_HDR_UTIL_HPP_ +#define LIBWEBM_HDR_UTIL_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "common/hdr_util.h" + +#endif // LIBWEBM_HDR_UTIL_HPP_ diff --git a/iosbuild.sh b/iosbuild.sh new file mode 100755 index 000000000000..a183af36e8f8 --- /dev/null +++ b/iosbuild.sh @@ -0,0 +1,207 @@ +#!/bin/sh +## +## Copyright (c) 2015 The WebM project authors. All Rights Reserved. +## +## Use of this source code is governed by a BSD-style license +## that can be found in the LICENSE file in the root of the source +## tree. An additional intellectual property rights grant can be found +## in the file PATENTS. All contributing project authors may +## be found in the AUTHORS file in the root of the source tree. +## +## This script generates 'WebM.framework'. An iOS app can mux/demux WebM +## container files by including 'WebM.framework'. +## +## Run ./iosbuild.sh to generate 'WebM.framework'. By default the framework +## bundle will be created in a directory called framework. Use --out-dir to +## change the output directory. +## +## This script is based on iosbuild.sh from the libwebp project. +. $(dirname $0)/common/common.sh + +# Trap function. Cleans up build output. +cleanup() { + local readonly res=$? + cd "${ORIG_PWD}" + + for dir in ${LIBDIRS}; do + if [ -d "${dir}" ]; then + rm -rf "${dir}" + fi + done + + if [ $res -ne 0 ]; then + elog "build exited with error ($res)" + fi +} + +trap cleanup EXIT + +check_dir libwebm + +iosbuild_usage() { +cat << EOF + Usage: ${0##*/} [arguments] + --help: Display this message and exit. + --out-dir: Override output directory (default is ${OUTDIR}). + --show-build-output: Show output from each library build. + --verbose: Output information about the environment and each stage of the + build. +EOF +} + +# Extract the latest SDK version from the final field of the form: iphoneosX.Y +readonly SDK=$(xcodebuild -showsdks \ + | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}' +) + +# Extract Xcode version. +readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2) +if [ -z "${XCODE}" ]; then + echo "Xcode not available" + exit 1 +fi + +# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support. +# Note that iPhoneOS-V6 support is not available with the iOS6 SDK. +readonly INCLUDES="common/file_util.h + common/hdr_util.h + common/webmids.h + mkvmuxer/mkvmuxer.h + mkvmuxer/mkvmuxertypes.h + mkvmuxer/mkvmuxerutil.h + mkvmuxer/mkvwriter.h + mkvparser/mkvparser.h + mkvparser/mkvreader.h" +readonly PLATFORMS="iPhoneSimulator + iPhoneSimulator64 + iPhoneOS-V7 + iPhoneOS-V7s + iPhoneOS-V7-arm64" +readonly TARGETDIR="WebM.framework" +readonly DEVELOPER="$(xcode-select --print-path)" +readonly PLATFORMSROOT="${DEVELOPER}/Platforms" +readonly LIPO="$(xcrun -sdk iphoneos${SDK} -find lipo)" +LIBLIST="" +OPT_FLAGS="-DNDEBUG -O3" +readonly SDK_MAJOR_VERSION="$(echo ${SDK} | awk -F '.' '{ print $1 }')" + +if [ -z "${SDK_MAJOR_VERSION}" ]; then + elog "iOS SDK not available" + exit 1 +elif [ "${SDK_MAJOR_VERSION}" -lt "6" ]; then + elog "You need iOS SDK version 6 or above" + exit 1 +else + vlog "iOS SDK Version ${SDK}" +fi + + +# Parse the command line. +while [ -n "$1" ]; do + case "$1" in + --help) + iosbuild_usage + exit + ;; + --out-dir) + OUTDIR="$2" + shift + ;; + --enable-debug) + OPT_FLAGS="-g" + ;; + --show-build-output) + devnull= + ;; + --verbose) + VERBOSE=yes + ;; + *) + iosbuild_usage + exit 1 + ;; + esac + shift +done + +readonly OPT_FLAGS="${OPT_FLAGS}" +readonly OUTDIR="${OUTDIR:-framework}" + +if [ "${VERBOSE}" = "yes" ]; then +cat << EOF + OUTDIR=${OUTDIR} + INCLUDES=${INCLUDES} + PLATFORMS=${PLATFORMS} + TARGETDIR=${TARGETDIR} + DEVELOPER=${DEVELOPER} + LIPO=${LIPO} + OPT_FLAGS=${OPT_FLAGS} + ORIG_PWD=${ORIG_PWD} +EOF +fi + +rm -rf "${OUTDIR}/${TARGETDIR}" +mkdir -p "${OUTDIR}/${TARGETDIR}/Headers/" + +for PLATFORM in ${PLATFORMS}; do + ARCH2="" + if [ "${PLATFORM}" = "iPhoneOS-V7-arm64" ]; then + PLATFORM="iPhoneOS" + ARCH="aarch64" + ARCH2="arm64" + elif [ "${PLATFORM}" = "iPhoneOS-V7s" ]; then + PLATFORM="iPhoneOS" + ARCH="armv7s" + elif [ "${PLATFORM}" = "iPhoneOS-V7" ]; then + PLATFORM="iPhoneOS" + ARCH="armv7" + elif [ "${PLATFORM}" = "iPhoneOS-V6" ]; then + PLATFORM="iPhoneOS" + ARCH="armv6" + elif [ "${PLATFORM}" = "iPhoneSimulator64" ]; then + PLATFORM="iPhoneSimulator" + ARCH="x86_64" + else + ARCH="i386" + fi + + LIBDIR="${OUTDIR}/${PLATFORM}-${SDK}-${ARCH}" + LIBDIRS="${LIBDIRS} ${LIBDIR}" + LIBFILE="${LIBDIR}/libwebm.a" + eval mkdir -p "${LIBDIR}" ${devnull} + + DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain" + SDKROOT="${PLATFORMSROOT}/" + SDKROOT="${SDKROOT}${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/" + CXXFLAGS="-arch ${ARCH2:-${ARCH}} -isysroot ${SDKROOT} ${OPT_FLAGS} + -miphoneos-version-min=6.0" + + # enable bitcode if available + if [ "${SDK_MAJOR_VERSION}" -gt 8 ]; then + CXXFLAGS="${CXXFLAGS} -fembed-bitcode" + fi + + # Build using the legacy makefile (instead of generating via cmake). + eval make -f Makefile.unix libwebm.a CXXFLAGS=\"${CXXFLAGS}\" ${devnull} + + # copy lib and add it to LIBLIST. + eval cp libwebm.a "${LIBFILE}" ${devnull} + LIBLIST="${LIBLIST} ${LIBFILE}" + + # clean build so we can go again. + eval make -f Makefile.unix clean ${devnull} +done + +# create include sub dirs in framework dir. +readonly framework_header_dir="${OUTDIR}/${TARGETDIR}/Headers" +readonly framework_header_sub_dirs="common mkvmuxer mkvparser" +for dir in ${framework_header_sub_dirs}; do + mkdir "${framework_header_dir}/${dir}" +done + +for header_file in ${INCLUDES}; do + eval cp -p ${header_file} "${framework_header_dir}/${header_file}" ${devnull} +done + +eval ${LIPO} -create ${LIBLIST} -output "${OUTDIR}/${TARGETDIR}/WebM" ${devnull} +echo "Succesfully built ${TARGETDIR} in ${OUTDIR}." diff --git a/m2ts/tests/webm2pes_tests.cc b/m2ts/tests/webm2pes_tests.cc new file mode 100644 index 000000000000..664856f1feea --- /dev/null +++ b/m2ts/tests/webm2pes_tests.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "m2ts/webm2pes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "common/file_util.h" +#include "common/libwebm_util.h" +#include "m2ts/vpxpes_parser.h" +#include "testing/test_util.h" + +namespace { + +class Webm2PesTests : public ::testing::Test { + public: + // Constants for validating known values from input data. + const std::uint8_t kMinVideoStreamId = 0xE0; + const std::uint8_t kMaxVideoStreamId = 0xEF; + const int kPesHeaderSize = 6; + const int kPesOptionalHeaderStartOffset = kPesHeaderSize; + const int kPesOptionalHeaderSize = 9; + const int kPesOptionalHeaderMarkerValue = 0x2; + const int kWebm2PesOptHeaderRemainingSize = 6; + const int kBcmvHeaderSize = 10; + + Webm2PesTests() = default; + ~Webm2PesTests() = default; + + void CreateAndLoadTestInput() { + libwebm::Webm2Pes converter(input_file_name_, temp_file_name_.name()); + ASSERT_TRUE(converter.ConvertToFile()); + ASSERT_TRUE(parser_.Open(pes_file_name())); + } + + bool VerifyPacketStartCode(const libwebm::VpxPesParser::PesHeader& header) { + // PES packets all start with the byte sequence 0x0 0x0 0x1. + if (header.start_code[0] != 0 || header.start_code[1] != 0 || + header.start_code[2] != 1) { + return false; + } + return true; + } + + const std::string& pes_file_name() const { return temp_file_name_.name(); } + libwebm::VpxPesParser* parser() { return &parser_; } + + private: + const libwebm::TempFileDeleter temp_file_name_; + const std::string input_file_name_ = + test::GetTestFilePath("bbb_480p_vp9_opus_1second.webm"); + libwebm::VpxPesParser parser_; +}; + +TEST_F(Webm2PesTests, CreatePesFile) { CreateAndLoadTestInput(); } + +TEST_F(Webm2PesTests, CanParseFirstPacket) { + CreateAndLoadTestInput(); + libwebm::VpxPesParser::PesHeader header; + libwebm::VideoFrame frame; + ASSERT_TRUE(parser()->ParseNextPacket(&header, &frame)); + EXPECT_TRUE(VerifyPacketStartCode(header)); + + // 9 bytes: PES optional header + // 10 bytes: BCMV Header + // 83 bytes: frame + // 102 bytes total in packet length field: + const std::size_t kPesPayloadLength = 102; + EXPECT_EQ(kPesPayloadLength, header.packet_length); + + EXPECT_GE(header.stream_id, kMinVideoStreamId); + EXPECT_LE(header.stream_id, kMaxVideoStreamId); + + // Test PesOptionalHeader values. + EXPECT_EQ(kPesOptionalHeaderMarkerValue, header.opt_header.marker); + EXPECT_EQ(kWebm2PesOptHeaderRemainingSize, header.opt_header.remaining_size); + EXPECT_EQ(0, header.opt_header.scrambling); + EXPECT_EQ(0, header.opt_header.priority); + EXPECT_EQ(0, header.opt_header.data_alignment); + EXPECT_EQ(0, header.opt_header.copyright); + EXPECT_EQ(0, header.opt_header.original); + EXPECT_EQ(1, header.opt_header.has_pts); + EXPECT_EQ(0, header.opt_header.has_dts); + EXPECT_EQ(0, header.opt_header.unused_fields); + + // Test the BCMV header. + // Note: The length field of the BCMV header includes its own length. + const std::size_t kBcmvBaseLength = 10; + const std::size_t kFirstFrameLength = 83; + const libwebm::VpxPesParser::BcmvHeader kFirstBcmvHeader(kFirstFrameLength + + kBcmvBaseLength); + EXPECT_TRUE(header.bcmv_header.Valid()); + EXPECT_EQ(kFirstBcmvHeader, header.bcmv_header); + + // Parse the next packet to confirm correct parse and consumption of payload. + EXPECT_TRUE(parser()->ParseNextPacket(&header, &frame)); +} + +TEST_F(Webm2PesTests, CanMuxLargeBuffers) { + const std::size_t kBufferSize = 100 * 1024; + const std::int64_t kFakeTimestamp = libwebm::kNanosecondsPerSecond; + libwebm::VideoFrame fake_frame(kFakeTimestamp, libwebm::VideoFrame::kVP9); + ASSERT_TRUE(fake_frame.Init(kBufferSize)); + std::memset(fake_frame.buffer().data.get(), 0x80, kBufferSize); + ASSERT_TRUE(fake_frame.SetBufferLength(kBufferSize)); + libwebm::PacketDataBuffer pes_packet_buffer; + ASSERT_TRUE( + libwebm::Webm2Pes::WritePesPacket(fake_frame, &pes_packet_buffer)); + + // TODO(tomfinegan): Change VpxPesParser so it can read from a buffer, and get + // rid of this extra step. + libwebm::FilePtr pes_file(std::fopen(pes_file_name().c_str(), "wb"), + libwebm::FILEDeleter()); + ASSERT_EQ(pes_packet_buffer.size(), + fwrite(&pes_packet_buffer[0], 1, pes_packet_buffer.size(), + pes_file.get())); + fclose(pes_file.get()); + pes_file.release(); + + libwebm::VpxPesParser parser; + ASSERT_TRUE(parser.Open(pes_file_name())); + libwebm::VpxPesParser::PesHeader header; + libwebm::VideoFrame parsed_frame; + ASSERT_TRUE(parser.ParseNextPacket(&header, &parsed_frame)); + EXPECT_EQ(fake_frame.nanosecond_pts(), parsed_frame.nanosecond_pts()); + EXPECT_EQ(fake_frame.buffer().length, parsed_frame.buffer().length); + EXPECT_EQ(0, std::memcmp(fake_frame.buffer().data.get(), + parsed_frame.buffer().data.get(), kBufferSize)); +} + +TEST_F(Webm2PesTests, ParserConsumesAllInput) { + CreateAndLoadTestInput(); + libwebm::VpxPesParser::PesHeader header; + libwebm::VideoFrame frame; + while (parser()->ParseNextPacket(&header, &frame) == true) { + EXPECT_TRUE(VerifyPacketStartCode(header)); + } + EXPECT_EQ(0, parser()->BytesAvailable()); +} + +} // namespace + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/m2ts/vpxpes2ts.cc b/m2ts/vpxpes2ts.cc new file mode 100644 index 000000000000..7684b56bd783 --- /dev/null +++ b/m2ts/vpxpes2ts.cc @@ -0,0 +1,217 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "m2ts/vpxpes2ts.h" + +#include +#include +#include +#include + +namespace libwebm { +// TODO(tomfinegan): Dedupe this and PesHeaderField. +// Stores a value and its size in bits for writing into a MPEG2 TS Header. +// Maximum size is 64 bits. Users may call the Check() method to perform minimal +// validation (size > 0 and <= 64). +struct TsHeaderField { + TsHeaderField(std::uint64_t value, std::uint32_t size_in_bits, + std::uint8_t byte_index, std::uint8_t bits_to_shift) + : bits(value), + num_bits(size_in_bits), + index(byte_index), + shift(bits_to_shift) {} + TsHeaderField() = delete; + TsHeaderField(const TsHeaderField&) = default; + TsHeaderField(TsHeaderField&&) = default; + ~TsHeaderField() = default; + bool Check() const { + return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64; + } + + // Value to be stored in the field. + std::uint64_t bits; + + // Number of bits in the value. + const int num_bits; + + // Index into the header for the byte in which |bits| will be written. + const std::uint8_t index; + + // Number of bits to left shift value before or'ing. Ignored for whole bytes. + const int shift; +}; + +// Data storage for MPEG2 Transport Stream headers. +// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet +struct TsHeader { + TsHeader(bool payload_start, bool adaptation_flag, std::uint8_t counter) + : is_payload_start(payload_start), + has_adaptation(adaptation_flag), + counter_value(counter) {} + TsHeader() = delete; + TsHeader(const TsHeader&) = default; + TsHeader(TsHeader&&) = default; + ~TsHeader() = default; + + void Write(PacketDataBuffer* buffer) const; + + // Indicates the packet is the beginning of a new fragmented payload. + const bool is_payload_start; + + // Indicates the packet contains an adaptation field. + const bool has_adaptation; + + // The sync byte is the bit pattern of 0x47 (ASCII char 'G'). + const std::uint8_t kTsHeaderSyncByte = 0x47; + const std::uint8_t sync_byte = kTsHeaderSyncByte; + + // Value for |continuity_counter|. Used to detect gaps when demuxing. + const std::uint8_t counter_value; + + // Set when FEC is impossible. Always 0. + const TsHeaderField transport_error_indicator = TsHeaderField(0, 1, 1, 7); + + // This MPEG2 TS header is the start of a new payload (aka PES packet). + const TsHeaderField payload_unit_start_indicator = + TsHeaderField(is_payload_start ? 1 : 0, 1, 1, 6); + + // Set when the current packet has a higher priority than other packets with + // the same PID. Always 0 for VPX. + const TsHeaderField transport_priority = TsHeaderField(0, 1, 1, 5); + + // https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet_Identifier_.28PID.29 + // 0x0020-0x1FFA May be assigned as needed to Program Map Tables, elementary + // streams and other data tables. + // Note: Though we hard code to 0x20, this value is actually 13 bits-- the + // buffer for the header is always set to 0, so it doesn't matter in practice. + const TsHeaderField pid = TsHeaderField(0x20, 8, 2, 0); + + // Indicates scrambling key. Unused; always 0. + const TsHeaderField scrambling_control = TsHeaderField(0, 2, 3, 6); + + // Adaptation field flag. Unused; always 0. + // TODO(tomfinegan): Not sure this is OK. Might need to add support for + // writing the Adaptation Field. + const TsHeaderField adaptation_field_flag = + TsHeaderField(has_adaptation ? 1 : 0, 1, 3, 5); + + // Payload flag. All output packets created here have payloads. Always 1. + const TsHeaderField payload_flag = TsHeaderField(1, 1, 3, 4); + + // Continuity counter. Two bit field that is incremented for every packet. + const TsHeaderField continuity_counter = + TsHeaderField(counter_value, 4, 3, 3); +}; + +void TsHeader::Write(PacketDataBuffer* buffer) const { + std::uint8_t* byte = &(*buffer)[0]; + *byte = sync_byte; + + *++byte = 0; + *byte |= transport_error_indicator.bits << transport_error_indicator.shift; + *byte |= payload_unit_start_indicator.bits + << payload_unit_start_indicator.shift; + *byte |= transport_priority.bits << transport_priority.shift; + + *++byte = pid.bits & 0xff; + + *++byte = 0; + *byte |= scrambling_control.bits << scrambling_control.shift; + *byte |= adaptation_field_flag.bits << adaptation_field_flag.shift; + *byte |= payload_flag.bits << payload_flag.shift; + *byte |= continuity_counter.bits; // last 4 bits. +} + +bool VpxPes2Ts::ConvertToFile() { + output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter()); + if (output_file_ == nullptr) { + std::fprintf(stderr, "VpxPes2Ts: Cannot open %s for output.\n", + output_file_name_.c_str()); + return false; + } + pes_converter_.reset(new Webm2Pes(input_file_name_, this)); + if (pes_converter_ == nullptr) { + std::fprintf(stderr, "VpxPes2Ts: Out of memory.\n"); + return false; + } + return pes_converter_->ConvertToPacketReceiver(); +} + +bool VpxPes2Ts::ReceivePacket(const PacketDataBuffer& packet_data) { + const int kTsHeaderSize = 4; + const int kTsPayloadSize = 184; + const int kTsPacketSize = kTsHeaderSize + kTsPayloadSize; + int bytes_to_packetize = static_cast(packet_data.size()); + std::uint8_t continuity_counter = 0; + std::size_t read_pos = 0; + + ts_buffer_.reserve(kTsPacketSize); + + while (bytes_to_packetize > 0) { + if (continuity_counter > 0xf) + continuity_counter = 0; + + // Calculate payload size (need to know if we'll have to pad with an empty + // adaptation field). + int payload_size = std::min(bytes_to_packetize, kTsPayloadSize); + + // Write the TS header. + const TsHeader header( + bytes_to_packetize == static_cast(packet_data.size()), + payload_size != kTsPayloadSize, continuity_counter); + header.Write(&ts_buffer_); + int write_pos = kTsHeaderSize; + + // (pre)Pad payload with an empty adaptation field. All packets must be + // |kTsPacketSize| (188). + if (payload_size < kTsPayloadSize) { + // We need at least 2 bytes to write an empty adaptation field. + if (payload_size == (kTsPayloadSize - 1)) { + payload_size--; + } + + // Padding adaptation field: + // 8 bits: number of adaptation field bytes following this byte. + // 8 bits: unused (in this program) flags. + // This is followed by a run of 0xff to reach |kTsPayloadSize| (184) + // bytes. + const int pad_size = kTsPayloadSize - payload_size - 1 - 1; + ts_buffer_[write_pos++] = pad_size + 1; + ts_buffer_[write_pos++] = 0; + + const std::uint8_t kStuffingByte = 0xff; + for (int i = 0; i < pad_size; ++i) { + ts_buffer_[write_pos++] = kStuffingByte; + } + } + + for (int i = 0; i < payload_size; ++i) { + ts_buffer_[write_pos++] = packet_data[read_pos++]; + } + + bytes_to_packetize -= payload_size; + continuity_counter++; + + if (write_pos != kTsPacketSize) { + fprintf(stderr, "VpxPes2Ts: Invalid packet length.\n"); + return false; + } + + // Write contents of |ts_buffer_| to |output_file_|. + // TODO(tomfinegan): Writing 188 bytes at a time isn't exactly efficient... + // Fix me. + if (static_cast(std::fwrite(&ts_buffer_[0], 1, kTsPacketSize, + output_file_.get())) != kTsPacketSize) { + std::fprintf(stderr, "VpxPes2Ts: TS packet write failed.\n"); + return false; + } + } + + return true; +} + +} // namespace libwebm diff --git a/m2ts/vpxpes2ts.h b/m2ts/vpxpes2ts.h new file mode 100644 index 000000000000..2609a1f1b1a4 --- /dev/null +++ b/m2ts/vpxpes2ts.h @@ -0,0 +1,45 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_M2TS_VPXPES2TS_H_ +#define LIBWEBM_M2TS_VPXPES2TS_H_ + +#include +#include + +#include "common/libwebm_util.h" +#include "m2ts/webm2pes.h" + +namespace libwebm { + +class VpxPes2Ts : public PacketReceiverInterface { + public: + VpxPes2Ts(const std::string& input_file_name, + const std::string& output_file_name) + : input_file_name_(input_file_name), + output_file_name_(output_file_name) {} + virtual ~VpxPes2Ts() = default; + VpxPes2Ts() = delete; + VpxPes2Ts(const VpxPes2Ts&) = delete; + VpxPes2Ts(VpxPes2Ts&&) = delete; + + bool ConvertToFile(); + + private: + bool ReceivePacket(const PacketDataBuffer& packet_data) override; + + const std::string input_file_name_; + const std::string output_file_name_; + + FilePtr output_file_; + std::unique_ptr pes_converter_; + PacketDataBuffer ts_buffer_; +}; + +} // namespace libwebm + +#endif // LIBWEBM_M2TS_VPXPES2TS_H_ diff --git a/m2ts/vpxpes2ts_main.cc b/m2ts/vpxpes2ts_main.cc new file mode 100644 index 000000000000..435d8055a624 --- /dev/null +++ b/m2ts/vpxpes2ts_main.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "m2ts/vpxpes2ts.h" + +#include +#include +#include + +namespace { + +void Usage(const char* argv[]) { + printf("Usage: %s ", argv[0]); +} + +} // namespace + +int main(int argc, const char* argv[]) { + if (argc < 3) { + Usage(argv); + return EXIT_FAILURE; + } + + const std::string input_path = argv[1]; + const std::string output_path = argv[2]; + + libwebm::VpxPes2Ts converter(input_path, output_path); + return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/m2ts/vpxpes_parser.cc b/m2ts/vpxpes_parser.cc new file mode 100644 index 000000000000..4f6fe5ced5df --- /dev/null +++ b/m2ts/vpxpes_parser.cc @@ -0,0 +1,409 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "vpxpes_parser.h" + +#include +#include +#include +#include +#include + +#include "common/file_util.h" + +namespace libwebm { + +VpxPesParser::BcmvHeader::BcmvHeader(std::uint32_t len) : length(len) { + id[0] = 'B'; + id[1] = 'C'; + id[2] = 'M'; + id[3] = 'V'; +} + +bool VpxPesParser::BcmvHeader::operator==(const BcmvHeader& other) const { + return (other.length == length && other.id[0] == id[0] && + other.id[1] == id[1] && other.id[2] == id[2] && other.id[3] == id[3]); +} + +bool VpxPesParser::BcmvHeader::Valid() const { + return (length > 0 && id[0] == 'B' && id[1] == 'C' && id[2] == 'M' && + id[3] == 'V'); +} + +// TODO(tomfinegan): Break Open() into separate functions. One that opens the +// file, and one that reads one packet at a time. As things are files larger +// than the maximum availble memory for the current process cannot be loaded. +bool VpxPesParser::Open(const std::string& pes_file) { + pes_file_size_ = static_cast(libwebm::GetFileSize(pes_file)); + if (pes_file_size_ <= 0) + return false; + pes_file_data_.reserve(static_cast(pes_file_size_)); + libwebm::FilePtr file = libwebm::FilePtr(std::fopen(pes_file.c_str(), "rb"), + libwebm::FILEDeleter()); + int byte; + while ((byte = fgetc(file.get())) != EOF) { + pes_file_data_.push_back(static_cast(byte)); + } + + if (!feof(file.get()) || ferror(file.get()) || + pes_file_size_ != pes_file_data_.size()) { + return false; + } + + read_pos_ = 0; + parse_state_ = kFindStartCode; + return true; +} + +bool VpxPesParser::VerifyPacketStartCode() const { + if (read_pos_ + 2 > pes_file_data_.size()) + return false; + + // PES packets all start with the byte sequence 0x0 0x0 0x1. + if (pes_file_data_[read_pos_] != 0 || pes_file_data_[read_pos_ + 1] != 0 || + pes_file_data_[read_pos_ + 2] != 1) { + return false; + } + + return true; +} + +bool VpxPesParser::ReadStreamId(std::uint8_t* stream_id) const { + if (!stream_id || BytesAvailable() < 4) + return false; + + *stream_id = pes_file_data_[read_pos_ + 3]; + return true; +} + +bool VpxPesParser::ReadPacketLength(std::uint16_t* packet_length) const { + if (!packet_length || BytesAvailable() < 6) + return false; + + // Read and byte swap 16 bit big endian length. + *packet_length = + (pes_file_data_[read_pos_ + 4] << 8) | pes_file_data_[read_pos_ + 5]; + + return true; +} + +bool VpxPesParser::ParsePesHeader(PesHeader* header) { + if (!header || parse_state_ != kParsePesHeader) + return false; + + if (!VerifyPacketStartCode()) + return false; + + std::size_t pos = read_pos_; + for (auto& a : header->start_code) { + a = pes_file_data_[pos++]; + } + + // PES Video stream IDs start at E0. + if (!ReadStreamId(&header->stream_id)) + return false; + + if (header->stream_id < kMinVideoStreamId || + header->stream_id > kMaxVideoStreamId) + return false; + + if (!ReadPacketLength(&header->packet_length)) + return false; + + read_pos_ += kPesHeaderSize; + parse_state_ = kParsePesOptionalHeader; + return true; +} + +// TODO(tomfinegan): Make these masks constants. +bool VpxPesParser::ParsePesOptionalHeader(PesOptionalHeader* header) { + if (!header || parse_state_ != kParsePesOptionalHeader || + read_pos_ >= pes_file_size_) { + return false; + } + + std::size_t consumed = 0; + PacketData poh_buffer; + if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_], + kPesOptionalHeaderSize, + &poh_buffer, &consumed)) { + return false; + } + + std::size_t offset = 0; + header->marker = (poh_buffer[offset] & 0x80) >> 6; + header->scrambling = (poh_buffer[offset] & 0x30) >> 4; + header->priority = (poh_buffer[offset] & 0x8) >> 3; + header->data_alignment = (poh_buffer[offset] & 0xc) >> 2; + header->copyright = (poh_buffer[offset] & 0x2) >> 1; + header->original = poh_buffer[offset] & 0x1; + offset++; + + header->has_pts = (poh_buffer[offset] & 0x80) >> 7; + header->has_dts = (poh_buffer[offset] & 0x40) >> 6; + header->unused_fields = poh_buffer[offset] & 0x3f; + offset++; + + header->remaining_size = poh_buffer[offset]; + if (header->remaining_size != + static_cast(kWebm2PesOptHeaderRemainingSize)) + return false; + + size_t bytes_left = header->remaining_size; + offset++; + + if (header->has_pts) { + // Read PTS markers. Format: + // PTS: 5 bytes + // 4 bits (flag: PTS present, but no DTS): 0x2 ('0010') + // 36 bits (90khz PTS): + // top 3 bits + // marker ('1') + // middle 15 bits + // marker ('1') + // bottom 15 bits + // marker ('1') + // TODO(tomfinegan): read/store the timestamp. + header->pts_dts_flag = (poh_buffer[offset] & 0x20) >> 4; + // Check the marker bits. + if ((poh_buffer[offset + 0] & 1) != 1 || + (poh_buffer[offset + 2] & 1) != 1 || + (poh_buffer[offset + 4] & 1) != 1) { + return false; + } + + header->pts = (poh_buffer[offset] & 0xe) << 29 | + ((ReadUint16(&poh_buffer[offset + 1]) & ~1) << 14) | + (ReadUint16(&poh_buffer[offset + 3]) >> 1); + offset += 5; + bytes_left -= 5; + } + + // Validate stuffing byte(s). + for (size_t i = 0; i < bytes_left; ++i) { + if (poh_buffer[offset + i] != 0xff) + return false; + } + + read_pos_ += consumed; + parse_state_ = kParseBcmvHeader; + + return true; +} + +// Parses and validates a BCMV header. +bool VpxPesParser::ParseBcmvHeader(BcmvHeader* header) { + if (!header || parse_state_ != kParseBcmvHeader) + return false; + + PacketData bcmv_buffer; + std::size_t consumed = 0; + if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_], + kBcmvHeaderSize, &bcmv_buffer, + &consumed)) { + return false; + } + + std::size_t offset = 0; + header->id[0] = bcmv_buffer[offset++]; + header->id[1] = bcmv_buffer[offset++]; + header->id[2] = bcmv_buffer[offset++]; + header->id[3] = bcmv_buffer[offset++]; + + header->length = 0; + header->length |= bcmv_buffer[offset++] << 24; + header->length |= bcmv_buffer[offset++] << 16; + header->length |= bcmv_buffer[offset++] << 8; + header->length |= bcmv_buffer[offset++]; + + // Length stored in the BCMV header is followed by 2 bytes of 0 padding. + if (bcmv_buffer[offset++] != 0 || bcmv_buffer[offset++] != 0) + return false; + + if (!header->Valid()) + return false; + + parse_state_ = kFindStartCode; + read_pos_ += consumed; + + return true; +} + +bool VpxPesParser::FindStartCode(std::size_t origin, + std::size_t* offset) const { + if (read_pos_ + 2 >= pes_file_size_) + return false; + + const std::size_t length = pes_file_size_ - origin; + if (length < 3) + return false; + + const uint8_t* const data = &pes_file_data_[origin]; + for (std::size_t i = 0; i < length - 3; ++i) { + if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) { + *offset = origin + i; + return true; + } + } + + return false; +} + +bool VpxPesParser::IsPayloadFragmented(const PesHeader& header) const { + return (header.packet_length != 0 && + (header.packet_length - kPesOptionalHeaderSize) != + header.bcmv_header.length); +} + +bool VpxPesParser::AccumulateFragmentedPayload(std::size_t pes_packet_length, + std::size_t payload_length) { + const std::size_t first_fragment_length = + pes_packet_length - kPesOptionalHeaderSize - kBcmvHeaderSize; + for (std::size_t i = 0; i < first_fragment_length; ++i) { + payload_.push_back(pes_file_data_[read_pos_ + i]); + } + read_pos_ += first_fragment_length; + parse_state_ = kFindStartCode; + + while (payload_.size() < payload_length) { + PesHeader header; + std::size_t packet_start_pos = read_pos_; + if (!FindStartCode(read_pos_, &packet_start_pos)) { + return false; + } + parse_state_ = kParsePesHeader; + read_pos_ = packet_start_pos; + + if (!ParsePesHeader(&header)) { + return false; + } + if (!ParsePesOptionalHeader(&header.opt_header)) { + return false; + } + + const std::size_t fragment_length = + header.packet_length - kPesOptionalHeaderSize; + std::size_t consumed = 0; + if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_], + fragment_length, &payload_, + &consumed)) { + return false; + } + read_pos_ += consumed; + } + return true; +} + +bool VpxPesParser::RemoveStartCodeEmulationPreventionBytes( + const std::uint8_t* raw_data, std::size_t bytes_required, + PacketData* processed_data, std::size_t* bytes_consumed) const { + if (bytes_required == 0 || !processed_data) + return false; + + std::size_t num_zeros = 0; + std::size_t bytes_copied = 0; + const std::uint8_t* const end_of_input = + &pes_file_data_[0] + pes_file_data_.size(); + std::size_t i; + for (i = 0; bytes_copied < bytes_required; ++i) { + if (raw_data + i > end_of_input) + return false; + + bool skip = false; + + const std::uint8_t byte = raw_data[i]; + if (byte == 0) { + ++num_zeros; + } else if (byte == 0x3 && num_zeros == 2) { + skip = true; + num_zeros = 0; + } else { + num_zeros = 0; + } + + if (skip == false) { + processed_data->push_back(byte); + ++bytes_copied; + } + } + *bytes_consumed = i; + return true; +} + +int VpxPesParser::BytesAvailable() const { + return static_cast(pes_file_data_.size() - read_pos_); +} + +bool VpxPesParser::ParseNextPacket(PesHeader* header, VideoFrame* frame) { + if (!header || !frame || parse_state_ != kFindStartCode || + BytesAvailable() == 0) { + return false; + } + + std::size_t packet_start_pos = read_pos_; + if (!FindStartCode(read_pos_, &packet_start_pos)) { + return false; + } + parse_state_ = kParsePesHeader; + read_pos_ = packet_start_pos; + + if (!ParsePesHeader(header)) { + return false; + } + if (!ParsePesOptionalHeader(&header->opt_header)) { + return false; + } + if (!ParseBcmvHeader(&header->bcmv_header)) { + return false; + } + + // BCMV header length includes the length of the BCMVHeader itself. Adjust: + const std::size_t payload_length = + header->bcmv_header.length - BcmvHeader::size(); + + // Make sure there's enough input data to read the entire frame. + if (read_pos_ + payload_length > pes_file_data_.size()) { + // Need more data. + printf("VpxPesParser: Not enough data. Required: %u Available: %u\n", + static_cast(payload_length), + static_cast(pes_file_data_.size() - read_pos_)); + parse_state_ = kFindStartCode; + read_pos_ = packet_start_pos; + return false; + } + + if (IsPayloadFragmented(*header)) { + if (!AccumulateFragmentedPayload(header->packet_length, payload_length)) { + fprintf(stderr, "VpxPesParser: Failed parsing fragmented payload!\n"); + return false; + } + } else { + std::size_t consumed = 0; + if (!RemoveStartCodeEmulationPreventionBytes( + &pes_file_data_[read_pos_], payload_length, &payload_, &consumed)) { + return false; + } + read_pos_ += consumed; + } + + if (frame->buffer().capacity < payload_.size()) { + if (frame->Init(payload_.size()) == false) { + fprintf(stderr, "VpxPesParser: Out of memory.\n"); + return false; + } + } + frame->set_nanosecond_pts(Khz90TicksToNanoseconds(header->opt_header.pts)); + std::memcpy(frame->buffer().data.get(), &payload_[0], payload_.size()); + frame->SetBufferLength(payload_.size()); + + payload_.clear(); + parse_state_ = kFindStartCode; + + return true; +} + +} // namespace libwebm diff --git a/m2ts/vpxpes_parser.h b/m2ts/vpxpes_parser.h new file mode 100644 index 000000000000..d18b4b79d812 --- /dev/null +++ b/m2ts/vpxpes_parser.h @@ -0,0 +1,177 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_M2TS_VPXPES_PARSER_H_ +#define LIBWEBM_M2TS_VPXPES_PARSER_H_ + +#include +#include +#include + +#include "common/libwebm_util.h" +#include "common/video_frame.h" + +namespace libwebm { + +// Parser for VPx PES. Requires that the _entire_ PES stream can be stored in +// a std::vector and read into memory when Open() is called. +// TODO(tomfinegan): Support incremental parse. +class VpxPesParser { + public: + typedef std::vector PesFileData; + typedef std::vector PacketData; + + enum ParseState { + kFindStartCode, + kParsePesHeader, + kParsePesOptionalHeader, + kParseBcmvHeader, + }; + + struct PesOptionalHeader { + int marker = 0; + int scrambling = 0; + int priority = 0; + int data_alignment = 0; + int copyright = 0; + int original = 0; + int has_pts = 0; + int has_dts = 0; + int unused_fields = 0; + int remaining_size = 0; + int pts_dts_flag = 0; + std::uint64_t pts = 0; + int stuffing_byte = 0; + }; + + struct BcmvHeader { + BcmvHeader() = default; + ~BcmvHeader() = default; + BcmvHeader(const BcmvHeader&) = delete; + BcmvHeader(BcmvHeader&&) = delete; + + // Convenience ctor for quick validation of expected values via operator== + // after parsing input. + explicit BcmvHeader(std::uint32_t len); + + bool operator==(const BcmvHeader& other) const; + + void Reset(); + bool Valid() const; + static std::size_t size() { return 10; } + + char id[4] = {0}; + std::uint32_t length = 0; + }; + + struct PesHeader { + std::uint8_t start_code[4] = {0}; + std::uint16_t packet_length = 0; + std::uint8_t stream_id = 0; + PesOptionalHeader opt_header; + BcmvHeader bcmv_header; + }; + + // Constants for validating known values from input data. + const std::uint8_t kMinVideoStreamId = 0xE0; + const std::uint8_t kMaxVideoStreamId = 0xEF; + const std::size_t kPesHeaderSize = 6; + const std::size_t kPesOptionalHeaderStartOffset = kPesHeaderSize; + const std::size_t kPesOptionalHeaderSize = 9; + const std::size_t kPesOptionalHeaderMarkerValue = 0x2; + const std::size_t kWebm2PesOptHeaderRemainingSize = 6; + const std::size_t kBcmvHeaderSize = 10; + + VpxPesParser() = default; + ~VpxPesParser() = default; + + // Opens file specified by |pes_file_path| and reads its contents. Returns + // true after successful read of input file. + bool Open(const std::string& pes_file_path); + + // Parses the next packet in the PES. PES header information is stored in + // |header|, and the frame payload is stored in |frame|. Returns true when + // a full frame has been consumed from the PES. + bool ParseNextPacket(PesHeader* header, VideoFrame* frame); + + // PES Header parsing utility functions. + // PES Header structure: + // Start code Stream ID Packet length (16 bits) + // / / ____/ + // | | / + // Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 + // 0 0 1 X Y + bool VerifyPacketStartCode() const; + bool ReadStreamId(std::uint8_t* stream_id) const; + bool ReadPacketLength(std::uint16_t* packet_length) const; + + std::uint64_t pes_file_size() const { return pes_file_size_; } + const PesFileData& pes_file_data() const { return pes_file_data_; } + + // Returns number of unparsed bytes remaining. + int BytesAvailable() const; + + private: + // Parses and verifies the static 6 byte portion that begins every PES packet. + bool ParsePesHeader(PesHeader* header); + + // Parses a PES optional header, the optional header following the static + // header that begins the VPX PES packet. + // https://en.wikipedia.org/wiki/Packetized_elementary_stream + bool ParsePesOptionalHeader(PesOptionalHeader* header); + + // Parses and validates the BCMV header. This immediately follows the optional + // header. + bool ParseBcmvHeader(BcmvHeader* header); + + // Returns true when a start code is found and sets |offset| to the position + // of the start code relative to |pes_file_data_[read_pos_]|. + // Does not set |offset| value if the end of |pes_file_data_| is reached + // without locating a start code. + // Note: A start code is the byte sequence 0x00 0x00 0x01. + bool FindStartCode(std::size_t origin, std::size_t* offset) const; + + // Returns true when a PES packet containing a BCMV header contains only a + // portion of the frame payload length reported by the BCMV header. + bool IsPayloadFragmented(const PesHeader& header) const; + + // Parses PES and PES Optional header while accumulating payload data in + // |payload_|. + // Returns true once all payload fragments have been stored in |payload_|. + // Returns false if unable to accumulate full payload. + bool AccumulateFragmentedPayload(std::size_t pes_packet_length, + std::size_t payload_length); + + // The byte sequence 0x0 0x0 0x1 is a start code in PES. When PES muxers + // encounter 0x0 0x0 0x1 or 0x0 0x0 0x3, an additional 0x3 is inserted into + // the PES. The following change occurs: + // 0x0 0x0 0x1 => 0x0 0x0 0x3 0x1 + // 0x0 0x0 0x3 => 0x0 0x0 0x3 0x3 + // PES demuxers must reverse the change: + // 0x0 0x0 0x3 0x1 => 0x0 0x0 0x1 + // 0x0 0x0 0x3 0x3 => 0x0 0x0 0x3 + // PES optional header, BCMV header, and payload data must be preprocessed to + // avoid potentially invalid data due to the presence of inserted bytes. + // + // Removes start code emulation prevention bytes while copying data from + // |raw_data| to |processed_data|. Returns true when |bytes_required| bytes + // have been written to |processed_data|. Reports bytes consumed during the + // operation via |bytes_consumed|. + bool RemoveStartCodeEmulationPreventionBytes( + const std::uint8_t* raw_data, std::size_t bytes_required, + PacketData* processed_data, std::size_t* bytes_consumed) const; + + std::size_t pes_file_size_ = 0; + PacketData payload_; + PesFileData pes_file_data_; + std::size_t read_pos_ = 0; + ParseState parse_state_ = kFindStartCode; +}; + +} // namespace libwebm + +#endif // LIBWEBM_M2TS_VPXPES_PARSER_H_ diff --git a/m2ts/webm2pes.cc b/m2ts/webm2pes.cc new file mode 100644 index 000000000000..fc4b3144f302 --- /dev/null +++ b/m2ts/webm2pes.cc @@ -0,0 +1,551 @@ +// Copyright (c) 2015 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "m2ts/webm2pes.h" + +#include +#include +#include +#include +#include +#include + +#include "common/libwebm_util.h" + +namespace libwebm { + +const std::size_t Webm2Pes::kMaxPayloadSize = 32768; + +namespace { + +std::string ToString(const char* str) { + return std::string((str == nullptr) ? "" : str); +} + +} // namespace + +// +// PesOptionalHeader methods. +// + +void PesOptionalHeader::SetPtsBits(std::int64_t pts_90khz) { + std::uint64_t* pts_bits = &pts.bits; + *pts_bits = 0; + + // PTS is broken up and stored in 40 bits as shown: + // + // PES PTS Only flag + // / Marker Marker Marker + // | / / / + // | | | | + // 7654 321 0 765432107654321 0 765432107654321 0 + // 0010 PTS 32-30 1 PTS 29-15 1 PTS 14-0 1 + const std::uint32_t pts1 = (pts_90khz >> 30) & 0x7; + const std::uint32_t pts2 = (pts_90khz >> 15) & 0x7FFF; + const std::uint32_t pts3 = pts_90khz & 0x7FFF; + + std::uint8_t buffer[5] = {0}; + // PTS only flag. + buffer[0] |= 1 << 5; + // Top 3 bits of PTS and 1 bit marker. + buffer[0] |= pts1 << 1; + // Marker. + buffer[0] |= 1; + + // Next 15 bits of pts and 1 bit marker. + // Top 8 bits of second PTS chunk. + buffer[1] |= (pts2 >> 7) & 0xff; + // bottom 7 bits of second PTS chunk. + buffer[2] |= (pts2 << 1); + // Marker. + buffer[2] |= 1; + + // Last 15 bits of pts and 1 bit marker. + // Top 8 bits of second PTS chunk. + buffer[3] |= (pts3 >> 7) & 0xff; + // bottom 7 bits of second PTS chunk. + buffer[4] |= (pts3 << 1); + // Marker. + buffer[4] |= 1; + + // Write bits into PesHeaderField. + std::memcpy(reinterpret_cast(pts_bits), buffer, 5); +} + +// Writes fields to |buffer| and returns true. Returns false when write or +// field value validation fails. +bool PesOptionalHeader::Write(bool write_pts, PacketDataBuffer* buffer) const { + if (buffer == nullptr) { + std::fprintf(stderr, "Webm2Pes: nullptr in opt header writer.\n"); + return false; + } + + const int kHeaderSize = 9; + std::uint8_t header[kHeaderSize] = {0}; + std::uint8_t* byte = header; + + if (marker.Check() != true || scrambling.Check() != true || + priority.Check() != true || data_alignment.Check() != true || + copyright.Check() != true || original.Check() != true || + has_pts.Check() != true || has_dts.Check() != true || + pts.Check() != true || stuffing_byte.Check() != true) { + std::fprintf(stderr, "Webm2Pes: Invalid PES Optional Header field.\n"); + return false; + } + + // TODO(tomfinegan): As noted in above, the PesHeaderFields should be an + // array (or some data structure) that can be iterated over. + + // First byte of header, fields: marker, scrambling, priority, alignment, + // copyright, original. + *byte = 0; + *byte |= marker.bits << marker.shift; + *byte |= scrambling.bits << scrambling.shift; + *byte |= priority.bits << priority.shift; + *byte |= data_alignment.bits << data_alignment.shift; + *byte |= copyright.bits << copyright.shift; + *byte |= original.bits << original.shift; + + // Second byte of header, fields: has_pts, has_dts, unused fields. + *++byte = 0; + if (write_pts == true) + *byte |= has_pts.bits << has_pts.shift; + + *byte |= has_dts.bits << has_dts.shift; + + // Third byte of header, fields: remaining size of header. + *++byte = remaining_size.bits & 0xff; // Field is 8 bits wide. + + int num_stuffing_bytes = + (pts.num_bits + 7) / 8 + 1 /* always 1 stuffing byte */; + if (write_pts == true) { + // Write the PTS value as big endian and adjust stuffing byte count + // accordingly. + *++byte = pts.bits & 0xff; + *++byte = (pts.bits >> 8) & 0xff; + *++byte = (pts.bits >> 16) & 0xff; + *++byte = (pts.bits >> 24) & 0xff; + *++byte = (pts.bits >> 32) & 0xff; + num_stuffing_bytes = 1; + } + + // Add the stuffing byte(s). + for (int i = 0; i < num_stuffing_bytes; ++i) + *++byte = stuffing_byte.bits & 0xff; + + return CopyAndEscapeStartCodes(&header[0], kHeaderSize, buffer); +} + +// +// BCMVHeader methods. +// + +bool BCMVHeader::Write(PacketDataBuffer* buffer) const { + if (buffer == nullptr) { + std::fprintf(stderr, "Webm2Pes: nullptr for buffer in BCMV Write.\n"); + return false; + } + const int kBcmvSize = 4; + for (int i = 0; i < kBcmvSize; ++i) + buffer->push_back(bcmv[i]); + + // Note: The 4 byte length field must include the size of the BCMV header. + const int kRemainingBytes = 6; + const uint32_t bcmv_total_length = length + static_cast(size()); + const uint8_t bcmv_buffer[kRemainingBytes] = { + static_cast((bcmv_total_length >> 24) & 0xff), + static_cast((bcmv_total_length >> 16) & 0xff), + static_cast((bcmv_total_length >> 8) & 0xff), + static_cast(bcmv_total_length & 0xff), + 0, + 0 /* 2 bytes 0 padding */}; + + return CopyAndEscapeStartCodes(bcmv_buffer, kRemainingBytes, buffer); +} + +// +// PesHeader methods. +// + +// Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to write +// |optional_header| contents. Returns true when successful, false otherwise. +bool PesHeader::Write(bool write_pts, PacketDataBuffer* buffer) const { + if (buffer == nullptr) { + std::fprintf(stderr, "Webm2Pes: nullptr in header writer.\n"); + return false; + } + + // Write |start_code|. + const int kStartCodeLength = 4; + for (int i = 0; i < kStartCodeLength; ++i) + buffer->push_back(start_code[i]); + + // The length field here reports number of bytes following the field. The + // length of the optional header must be added to the payload length set by + // the user. + const std::size_t header_length = + packet_length + optional_header.size_in_bytes(); + if (header_length > UINT16_MAX) + return false; + + // Write |header_length| as big endian. + std::uint8_t byte = (header_length >> 8) & 0xff; + buffer->push_back(byte); + byte = header_length & 0xff; + buffer->push_back(byte); + + // Write the (not really) optional header. + if (optional_header.Write(write_pts, buffer) != true) { + std::fprintf(stderr, "Webm2Pes: PES optional header write failed."); + return false; + } + return true; +} + +// +// Webm2Pes methods. +// + +bool Webm2Pes::ConvertToFile() { + if (input_file_name_.empty() || output_file_name_.empty()) { + std::fprintf(stderr, "Webm2Pes: input and/or output file name(s) empty.\n"); + return false; + } + + output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter()); + if (output_file_ == nullptr) { + std::fprintf(stderr, "Webm2Pes: Cannot open %s for output.\n", + output_file_name_.c_str()); + return false; + } + + if (InitWebmParser() != true) { + std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n"); + return false; + } + + // Walk clusters in segment. + const mkvparser::Cluster* cluster = webm_parser_->GetFirst(); + while (cluster != nullptr && cluster->EOS() == false) { + const mkvparser::BlockEntry* block_entry = nullptr; + std::int64_t block_status = cluster->GetFirst(block_entry); + if (block_status < 0) { + std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n", + input_file_name_.c_str()); + return false; + } + + // Walk blocks in cluster. + while (block_entry != nullptr && block_entry->EOS() == false) { + const mkvparser::Block* block = block_entry->GetBlock(); + if (block->GetTrackNumber() == video_track_num_) { + const int frame_count = block->GetFrameCount(); + + // Walk frames in block. + for (int frame_num = 0; frame_num < frame_count; ++frame_num) { + const mkvparser::Block::Frame& mkvparser_frame = + block->GetFrame(frame_num); + + // Read the frame. + VideoFrame vpx_frame(block->GetTime(cluster), codec_); + if (ReadVideoFrame(mkvparser_frame, &vpx_frame) == false) { + fprintf(stderr, "Webm2Pes: frame read failed.\n"); + return false; + } + + // Write frame out as PES packet(s). + if (WritePesPacket(vpx_frame, &packet_data_) == false) { + std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n"); + return false; + } + + // Write contents of |packet_data_| to |output_file_|. + if (std::fwrite(&packet_data_[0], 1, packet_data_.size(), + output_file_.get()) != packet_data_.size()) { + std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n"); + return false; + } + bytes_written_ += packet_data_.size(); + } + } + block_status = cluster->GetNext(block_entry, block_entry); + if (block_status < 0) { + std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n", + input_file_name_.c_str()); + return false; + } + } + + cluster = webm_parser_->GetNext(cluster); + } + + std::fflush(output_file_.get()); + return true; +} + +bool Webm2Pes::ConvertToPacketReceiver() { + if (input_file_name_.empty() || packet_sink_ == nullptr) { + std::fprintf(stderr, "Webm2Pes: input file name empty or null sink.\n"); + return false; + } + + if (InitWebmParser() != true) { + std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n"); + return false; + } + + // Walk clusters in segment. + const mkvparser::Cluster* cluster = webm_parser_->GetFirst(); + while (cluster != nullptr && cluster->EOS() == false) { + const mkvparser::BlockEntry* block_entry = nullptr; + std::int64_t block_status = cluster->GetFirst(block_entry); + if (block_status < 0) { + std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n", + input_file_name_.c_str()); + return false; + } + + // Walk blocks in cluster. + while (block_entry != nullptr && block_entry->EOS() == false) { + const mkvparser::Block* block = block_entry->GetBlock(); + if (block->GetTrackNumber() == video_track_num_) { + const int frame_count = block->GetFrameCount(); + + // Walk frames in block. + for (int frame_num = 0; frame_num < frame_count; ++frame_num) { + const mkvparser::Block::Frame& mkvparser_frame = + block->GetFrame(frame_num); + + // Read the frame. + VideoFrame frame(block->GetTime(cluster), codec_); + if (ReadVideoFrame(mkvparser_frame, &frame) == false) { + fprintf(stderr, "Webm2Pes: frame read failed.\n"); + return false; + } + + // Write frame out as PES packet(s). + if (WritePesPacket(frame, &packet_data_) == false) { + std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n"); + return false; + } + if (packet_sink_->ReceivePacket(packet_data_) != true) { + std::fprintf(stderr, "Webm2Pes: ReceivePacket failed.\n"); + return false; + } + bytes_written_ += packet_data_.size(); + } + } + block_status = cluster->GetNext(block_entry, block_entry); + if (block_status < 0) { + std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n", + input_file_name_.c_str()); + return false; + } + } + + cluster = webm_parser_->GetNext(cluster); + } + + return true; +} + +bool Webm2Pes::InitWebmParser() { + if (webm_reader_.Open(input_file_name_.c_str()) != 0) { + std::fprintf(stderr, "Webm2Pes: Cannot open %s as input.\n", + input_file_name_.c_str()); + return false; + } + + using mkvparser::Segment; + Segment* webm_parser = nullptr; + if (Segment::CreateInstance(&webm_reader_, 0 /* pos */, + webm_parser /* Segment*& */) != 0) { + std::fprintf(stderr, "Webm2Pes: Cannot create WebM parser.\n"); + return false; + } + webm_parser_.reset(webm_parser); + + if (webm_parser_->Load() != 0) { + std::fprintf(stderr, "Webm2Pes: Cannot parse %s.\n", + input_file_name_.c_str()); + return false; + } + + // Make sure there's a video track. + const mkvparser::Tracks* tracks = webm_parser_->GetTracks(); + if (tracks == nullptr) { + std::fprintf(stderr, "Webm2Pes: %s has no tracks.\n", + input_file_name_.c_str()); + return false; + } + + timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale(); + + for (int track_index = 0; + track_index < static_cast(tracks->GetTracksCount()); + ++track_index) { + const mkvparser::Track* track = tracks->GetTrackByIndex(track_index); + if (track && track->GetType() == mkvparser::Track::kVideo) { + const std::string codec_id = ToString(track->GetCodecId()); + if (codec_id == std::string("V_VP8")) { + codec_ = VideoFrame::kVP8; + } else if (codec_id == std::string("V_VP9")) { + codec_ = VideoFrame::kVP9; + } else { + fprintf(stderr, "Webm2Pes: Codec must be VP8 or VP9.\n"); + return false; + } + video_track_num_ = track_index + 1; + break; + } + } + if (video_track_num_ < 1) { + std::fprintf(stderr, "Webm2Pes: No video track found in %s.\n", + input_file_name_.c_str()); + return false; + } + return true; +} + +bool Webm2Pes::ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame, + VideoFrame* frame) { + if (mkvparser_frame.len < 1 || frame == nullptr) + return false; + + const std::size_t mkv_len = static_cast(mkvparser_frame.len); + if (mkv_len > frame->buffer().capacity) { + const std::size_t new_size = 2 * mkv_len; + if (frame->Init(new_size) == false) { + std::fprintf(stderr, "Webm2Pes: Out of memory.\n"); + return false; + } + } + if (mkvparser_frame.Read(&webm_reader_, frame->buffer().data.get()) != 0) { + std::fprintf(stderr, "Webm2Pes: Error reading VPx frame!\n"); + return false; + } + return frame->SetBufferLength(mkv_len); +} + +bool Webm2Pes::WritePesPacket(const VideoFrame& frame, + PacketDataBuffer* packet_data) { + if (frame.buffer().data.get() == nullptr || frame.buffer().length < 1) + return false; + + Ranges frame_ranges; + if (frame.codec() == VideoFrame::kVP9) { + bool error = false; + const bool has_superframe_index = + ParseVP9SuperFrameIndex(frame.buffer().data.get(), + frame.buffer().length, &frame_ranges, &error); + if (error) { + std::fprintf(stderr, "Webm2Pes: Superframe index parse failed.\n"); + return false; + } + if (has_superframe_index == false) { + frame_ranges.push_back(Range(0, frame.buffer().length)); + } + } else { + frame_ranges.push_back(Range(0, frame.buffer().length)); + } + + const std::int64_t khz90_pts = + NanosecondsTo90KhzTicks(frame.nanosecond_pts()); + PesHeader header; + header.optional_header.SetPtsBits(khz90_pts); + + packet_data->clear(); + + for (const Range& packet_payload_range : frame_ranges) { + std::size_t extra_bytes = 0; + if (packet_payload_range.length > kMaxPayloadSize) { + extra_bytes = packet_payload_range.length - kMaxPayloadSize; + } + if (packet_payload_range.length + packet_payload_range.offset > + frame.buffer().length) { + std::fprintf(stderr, "Webm2Pes: Invalid frame length.\n"); + return false; + } + + // First packet of new frame. Always include PTS and BCMV header. + header.packet_length = + packet_payload_range.length - extra_bytes + BCMVHeader::size(); + if (header.Write(true, packet_data) != true) { + std::fprintf(stderr, "Webm2Pes: packet header write failed.\n"); + return false; + } + + BCMVHeader bcmv_header(static_cast(packet_payload_range.length)); + if (bcmv_header.Write(packet_data) != true) { + std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n"); + return false; + } + + // Insert the payload at the end of |packet_data|. + const std::uint8_t* const payload_start = + frame.buffer().data.get() + packet_payload_range.offset; + + const std::size_t bytes_to_copy = packet_payload_range.length - extra_bytes; + if (CopyAndEscapeStartCodes(payload_start, bytes_to_copy, packet_data) == + false) { + fprintf(stderr, "Webm2Pes: Payload write failed.\n"); + return false; + } + + std::size_t bytes_copied = bytes_to_copy; + while (extra_bytes) { + // Write PES packets for the remaining data, but omit the PTS and BCMV + // header. + const std::size_t extra_bytes_to_copy = + std::min(kMaxPayloadSize, extra_bytes); + extra_bytes -= extra_bytes_to_copy; + header.packet_length = extra_bytes_to_copy; + if (header.Write(false, packet_data) != true) { + fprintf(stderr, "Webm2pes: fragment write failed.\n"); + return false; + } + + const std::uint8_t* fragment_start = payload_start + bytes_copied; + if (CopyAndEscapeStartCodes(fragment_start, extra_bytes_to_copy, + packet_data) == false) { + fprintf(stderr, "Webm2Pes: Payload write failed.\n"); + return false; + } + + bytes_copied += extra_bytes_to_copy; + } + } + + return true; +} + +bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input, + std::size_t raw_input_length, + PacketDataBuffer* packet_buffer) { + if (raw_input == nullptr || raw_input_length < 1 || packet_buffer == nullptr) + return false; + + int num_zeros = 0; + for (std::size_t i = 0; i < raw_input_length; ++i) { + const uint8_t byte = raw_input[i]; + + if (byte == 0) { + ++num_zeros; + } else if (num_zeros >= 2 && (byte == 0x1 || byte == 0x3)) { + packet_buffer->push_back(0x3); + num_zeros = 0; + } else { + num_zeros = 0; + } + + packet_buffer->push_back(byte); + } + + return true; +} + +} // namespace libwebm diff --git a/m2ts/webm2pes.h b/m2ts/webm2pes.h new file mode 100644 index 000000000000..6dcb0fdd9312 --- /dev/null +++ b/m2ts/webm2pes.h @@ -0,0 +1,274 @@ +// Copyright (c) 2015 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_M2TS_WEBM2PES_H_ +#define LIBWEBM_M2TS_WEBM2PES_H_ + +#include +#include +#include +#include +#include + +#include "common/libwebm_util.h" +#include "common/video_frame.h" +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" + +// Webm2pes +// +// Webm2pes consumes a WebM file containing a VP8 or VP9 video stream and +// outputs a PES stream suitable for inclusion in a MPEG2 Transport Stream. +// +// In the simplest case the PES stream output by Webm2pes consists of a sequence +// of PES packets with the following structure: +// | PES Header w/PTS | BCMV Header | Payload (VPx frame) | +// +// More typically the output will look like the following due to the PES +// payload size limitations caused by the format of the PES header. +// The PES header contains only 2 bytes of storage for expressing payload size. +// VPx PES streams containing fragmented packets look like this: +// +// | PH PTS | BCMV | Payload fragment 1 | PH | Payload fragment 2 | ... +// +// PH = PES Header +// PH PTS = PES Header with PTS +// BCMV = BCMV Header +// +// Note that start codes are properly escaped by Webm2pes, and start code +// emulation prevention bytes must be stripped from the output stream before +// it can be parsed. + +namespace libwebm { + +// Stores a value and its size in bits for writing into a PES Optional Header. +// Maximum size is 64 bits. Users may call the Check() method to perform minimal +// validation (size > 0 and <= 64). +struct PesHeaderField { + PesHeaderField(std::uint64_t value, std::uint32_t size_in_bits, + std::uint8_t byte_index, std::uint8_t bits_to_shift) + : bits(value), + num_bits(size_in_bits), + index(byte_index), + shift(bits_to_shift) {} + PesHeaderField() = delete; + PesHeaderField(const PesHeaderField&) = default; + PesHeaderField(PesHeaderField&&) = default; + ~PesHeaderField() = default; + bool Check() const { + return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64; + } + + // Value to be stored in the field. + std::uint64_t bits; + + // Number of bits in the value. + const int num_bits; + + // Index into the header for the byte in which |bits| will be written. + const std::uint8_t index; + + // Number of bits to shift value before or'ing. + const int shift; +}; + +// Data is stored in buffers before being written to output files. +typedef std::vector PacketDataBuffer; + +// Storage for PES Optional Header values. Fields written in order using sizes +// specified. +struct PesOptionalHeader { + // TODO(tomfinegan): The fields could be in an array, which would allow the + // code writing the optional header to iterate over the fields instead of + // having code for dealing with each one. + + // 2 bits (marker): 2 ('10') + const PesHeaderField marker = PesHeaderField(2, 2, 0, 6); + + // 2 bits (no scrambling): 0x0 ('00') + const PesHeaderField scrambling = PesHeaderField(0, 2, 0, 4); + + // 1 bit (priority): 0x0 ('0') + const PesHeaderField priority = PesHeaderField(0, 1, 0, 3); + + // TODO(tomfinegan): The BCMV header could be considered a sync word, and this + // field should be 1 when a sync word follows the packet. Clarify. + // 1 bit (data alignment): 0x0 ('0') + const PesHeaderField data_alignment = PesHeaderField(0, 1, 0, 2); + + // 1 bit (copyright): 0x0 ('0') + const PesHeaderField copyright = PesHeaderField(0, 1, 0, 1); + + // 1 bit (original/copy): 0x0 ('0') + const PesHeaderField original = PesHeaderField(0, 1, 0, 0); + + // 1 bit (has_pts): 0x1 ('1') + const PesHeaderField has_pts = PesHeaderField(1, 1, 1, 7); + + // 1 bit (has_dts): 0x0 ('0') + const PesHeaderField has_dts = PesHeaderField(0, 1, 1, 6); + + // 6 bits (unused fields): 0x0 ('000000') + const PesHeaderField unused = PesHeaderField(0, 6, 1, 0); + + // 8 bits (size of remaining data in the Header). + const PesHeaderField remaining_size = PesHeaderField(6, 8, 2, 0); + + // PTS: 5 bytes + // 4 bits (flag: PTS present, but no DTS): 0x2 ('0010') + // 36 bits (90khz PTS): + // top 3 bits + // marker ('1') + // middle 15 bits + // marker ('1') + // bottom 15 bits + // marker ('1') + PesHeaderField pts = PesHeaderField(0, 40, 3, 0); + + PesHeaderField stuffing_byte = PesHeaderField(0xFF, 8, 8, 0); + + // PTS omitted in fragments. Size remains unchanged: More stuffing bytes. + bool fragment = false; + + static std::size_t size_in_bytes() { return 9; } + + // Writes |pts_90khz| to |pts| per format described at its declaration above. + void SetPtsBits(std::int64_t pts_90khz); + + // Writes fields to |buffer| and returns true. Returns false when write or + // field value validation fails. + bool Write(bool write_pts, PacketDataBuffer* buffer) const; +}; + +// Describes custom 10 byte header that immediately follows the PES Optional +// Header in each PES packet output by Webm2Pes: +// 4 byte 'B' 'C' 'M' 'V' +// 4 byte big-endian length of frame +// 2 bytes 0 padding +struct BCMVHeader { + explicit BCMVHeader(std::uint32_t frame_length) : length(frame_length) {} + BCMVHeader() = delete; + BCMVHeader(const BCMVHeader&) = delete; + BCMVHeader(BCMVHeader&&) = delete; + ~BCMVHeader() = default; + const std::uint8_t bcmv[4] = {'B', 'C', 'M', 'V'}; + const std::uint32_t length; + + static std::size_t size() { return 10; } + + // Write the BCMV Header into |buffer|. Caller responsible for ensuring + // destination buffer is of size >= BCMVHeader::size(). + bool Write(PacketDataBuffer* buffer) const; + bool Write(uint8_t* buffer); +}; + +struct PesHeader { + const std::uint8_t start_code[4] = { + 0x00, 0x00, + 0x01, // 0x000001 is the PES packet start code prefix. + 0xE0}; // 0xE0 is the minimum video stream ID. + std::uint16_t packet_length = 0; // Number of bytes _after_ this field. + PesOptionalHeader optional_header; + std::size_t size() const { + return optional_header.size_in_bytes() + + 6 /* start_code + packet_length */ + packet_length; + } + + // Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to + // write |optional_header| contents. Returns true when successful, false + // otherwise. + bool Write(bool write_pts, PacketDataBuffer* buffer) const; +}; + +class PacketReceiverInterface { + public: + virtual ~PacketReceiverInterface() {} + virtual bool ReceivePacket(const PacketDataBuffer& packet) = 0; +}; + +// Converts the VP9 track of a WebM file to a Packetized Elementary Stream +// suitable for use in a MPEG2TS. +// https://en.wikipedia.org/wiki/Packetized_elementary_stream +// https://en.wikipedia.org/wiki/MPEG_transport_stream +class Webm2Pes { + public: + static const std::size_t kMaxPayloadSize; + + Webm2Pes(const std::string& input_file, const std::string& output_file) + : input_file_name_(input_file), output_file_name_(output_file) {} + Webm2Pes(const std::string& input_file, PacketReceiverInterface* packet_sink) + : input_file_name_(input_file), packet_sink_(packet_sink) {} + + Webm2Pes() = delete; + Webm2Pes(const Webm2Pes&) = delete; + Webm2Pes(Webm2Pes&&) = delete; + ~Webm2Pes() = default; + + // Converts the VPx video stream to a PES file and returns true. Returns false + // to report failure. + bool ConvertToFile(); + + // Converts the VPx video stream to a sequence of PES packets, and calls the + // PacketReceiverInterface::ReceivePacket() once for each VPx frame. The + // packet sent to the receiver may contain multiple PES packets. Returns only + // after full conversion or error. Returns true for success, and false when + // an error occurs. + bool ConvertToPacketReceiver(); + + // Writes |vpx_frame| out as PES packet[s] and stores output in |packet_data|. + // Returns true for success, false for failure. + static bool WritePesPacket(const VideoFrame& frame, + PacketDataBuffer* packet_data); + + uint64_t bytes_written() const { return bytes_written_; } + + private: + bool InitWebmParser(); + bool ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame, + VideoFrame* frame); + + const std::string input_file_name_; + const std::string output_file_name_; + std::unique_ptr webm_parser_; + mkvparser::MkvReader webm_reader_; + FilePtr output_file_; + + // Video track num in the WebM file. + int video_track_num_ = 0; + + // Video codec reported by CodecName from Video TrackEntry. + VideoFrame::Codec codec_; + + // Input timecode scale. + std::int64_t timecode_scale_ = 1000000; + + // Packet sink; when constructed with a PacketReceiverInterface*, packet and + // type of packet are sent to |packet_sink_| instead of written to an output + // file. + PacketReceiverInterface* packet_sink_ = nullptr; + + PacketDataBuffer packet_data_; + + std::uint64_t bytes_written_ = 0; +}; + +// Copies |raw_input_length| bytes from |raw_input| to |packet_buffer| while +// escaping start codes. Returns true when bytes are successfully copied. +// A start code is the 3 byte sequence 0x00 0x00 0x01. When +// the sequence is encountered, the value 0x03 is inserted. To avoid +// any ambiguity at reassembly time, the same is done for the sequence +// 0x00 0x00 0x03. So, the following transformation occurs for when either +// of the noted sequences is encountered: +// +// 0x00 0x00 0x01 => 0x00 0x00 0x03 0x01 +// 0x00 0x00 0x03 => 0x00 0x00 0x03 0x03 +bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input, + std::size_t raw_input_length, + PacketDataBuffer* packet_buffer); +} // namespace libwebm + +#endif // LIBWEBM_M2TS_WEBM2PES_H_ diff --git a/m2ts/webm2pes_main.cc b/m2ts/webm2pes_main.cc new file mode 100644 index 000000000000..075e55cd5c27 --- /dev/null +++ b/m2ts/webm2pes_main.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2015 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "m2ts/webm2pes.h" + +#include +#include +#include + +namespace { + +void Usage(const char* argv[]) { + printf("Usage: %s ", argv[0]); +} + +} // namespace + +int main(int argc, const char* argv[]) { + if (argc < 3) { + Usage(argv); + return EXIT_FAILURE; + } + + const std::string input_path = argv[1]; + const std::string output_path = argv[2]; + + libwebm::Webm2Pes converter(input_path, output_path); + return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/mkvmuxer.hpp b/mkvmuxer.hpp new file mode 100644 index 000000000000..092592baa553 --- /dev/null +++ b/mkvmuxer.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_MKVMUXER_HPP_ +#define LIBWEBM_MKVMUXER_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "mkvmuxer/mkvmuxer.h" + +#endif // LIBWEBM_MKVMUXER_HPP_ diff --git a/mkvmuxer/mkvmuxer.cc b/mkvmuxer/mkvmuxer.cc new file mode 100644 index 000000000000..183d2d1db962 --- /dev/null +++ b/mkvmuxer/mkvmuxer.cc @@ -0,0 +1,4231 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "mkvmuxer/mkvmuxer.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/webmids.h" +#include "mkvmuxer/mkvmuxerutil.h" +#include "mkvmuxer/mkvwriter.h" +#include "mkvparser/mkvparser.h" + +namespace mkvmuxer { + +const float PrimaryChromaticity::kChromaticityMin = 0.0f; +const float PrimaryChromaticity::kChromaticityMax = 1.0f; +const float MasteringMetadata::kMinLuminance = 0.0f; +const float MasteringMetadata::kMinLuminanceMax = 999.99f; +const float MasteringMetadata::kMaxLuminanceMax = 9999.99f; +const float MasteringMetadata::kValueNotPresent = FLT_MAX; +const uint64_t Colour::kValueNotPresent = UINT64_MAX; + +namespace { + +const char kDocTypeWebm[] = "webm"; +const char kDocTypeMatroska[] = "matroska"; + +// Deallocate the string designated by |dst|, and then copy the |src| +// string to |dst|. The caller owns both the |src| string and the +// |dst| copy (hence the caller is responsible for eventually +// deallocating the strings, either directly, or indirectly via +// StrCpy). Returns true if the source string was successfully copied +// to the destination. +bool StrCpy(const char* src, char** dst_ptr) { + if (dst_ptr == NULL) + return false; + + char*& dst = *dst_ptr; + + delete[] dst; + dst = NULL; + + if (src == NULL) + return true; + + const size_t size = strlen(src) + 1; + + dst = new (std::nothrow) char[size]; // NOLINT + if (dst == NULL) + return false; + + strcpy(dst, src); // NOLINT + return true; +} + +typedef std::unique_ptr PrimaryChromaticityPtr; +bool CopyChromaticity(const PrimaryChromaticity* src, + PrimaryChromaticityPtr* dst) { + if (!dst) + return false; + + dst->reset(new (std::nothrow) PrimaryChromaticity(src->x(), src->y())); + if (!dst->get()) + return false; + + return true; +} + +} // namespace + +/////////////////////////////////////////////////////////////// +// +// IMkvWriter Class + +IMkvWriter::IMkvWriter() {} + +IMkvWriter::~IMkvWriter() {} + +bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version, + const char* const doc_type) { + // Level 0 + uint64_t size = + EbmlElementSize(libwebm::kMkvEBMLVersion, static_cast(1)); + size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, static_cast(1)); + size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, static_cast(4)); + size += + EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, static_cast(8)); + size += EbmlElementSize(libwebm::kMkvDocType, doc_type); + size += EbmlElementSize(libwebm::kMkvDocTypeVersion, + static_cast(doc_type_version)); + size += + EbmlElementSize(libwebm::kMkvDocTypeReadVersion, static_cast(2)); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size)) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion, + static_cast(1))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion, + static_cast(1))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength, + static_cast(4))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength, + static_cast(8))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvDocType, doc_type)) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion, + static_cast(doc_type_version))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion, + static_cast(2))) { + return false; + } + + return true; +} + +bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) { + return WriteEbmlHeader(writer, doc_type_version, kDocTypeWebm); +} + +bool WriteEbmlHeader(IMkvWriter* writer) { + return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion); +} + +bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst, + int64_t start, int64_t size) { + // TODO(vigneshv): Check if this is a reasonable value. + const uint32_t kBufSize = 2048; + uint8_t* buf = new uint8_t[kBufSize]; + int64_t offset = start; + while (size > 0) { + const int64_t read_len = (size > kBufSize) ? kBufSize : size; + if (source->Read(offset, static_cast(read_len), buf)) + return false; + dst->Write(buf, static_cast(read_len)); + offset += read_len; + size -= read_len; + } + delete[] buf; + return true; +} + +/////////////////////////////////////////////////////////////// +// +// Frame Class + +Frame::Frame() + : add_id_(0), + additional_(NULL), + additional_length_(0), + duration_(0), + duration_set_(false), + frame_(NULL), + is_key_(false), + length_(0), + track_number_(0), + timestamp_(0), + discard_padding_(0), + reference_block_timestamp_(0), + reference_block_timestamp_set_(false) {} + +Frame::~Frame() { + delete[] frame_; + delete[] additional_; +} + +bool Frame::CopyFrom(const Frame& frame) { + delete[] frame_; + frame_ = NULL; + length_ = 0; + if (frame.length() > 0 && frame.frame() != NULL && + !Init(frame.frame(), frame.length())) { + return false; + } + add_id_ = 0; + delete[] additional_; + additional_ = NULL; + additional_length_ = 0; + if (frame.additional_length() > 0 && frame.additional() != NULL && + !AddAdditionalData(frame.additional(), frame.additional_length(), + frame.add_id())) { + return false; + } + duration_ = frame.duration(); + duration_set_ = frame.duration_set(); + is_key_ = frame.is_key(); + track_number_ = frame.track_number(); + timestamp_ = frame.timestamp(); + discard_padding_ = frame.discard_padding(); + reference_block_timestamp_ = frame.reference_block_timestamp(); + reference_block_timestamp_set_ = frame.reference_block_timestamp_set(); + return true; +} + +bool Frame::Init(const uint8_t* frame, uint64_t length) { + uint8_t* const data = + new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT + if (!data) + return false; + + delete[] frame_; + frame_ = data; + length_ = length; + + memcpy(frame_, frame, static_cast(length_)); + return true; +} + +bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length, + uint64_t add_id) { + uint8_t* const data = + new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT + if (!data) + return false; + + delete[] additional_; + additional_ = data; + additional_length_ = length; + add_id_ = add_id; + + memcpy(additional_, additional, static_cast(additional_length_)); + return true; +} + +bool Frame::IsValid() const { + if (length_ == 0 || !frame_) { + return false; + } + if ((additional_length_ != 0 && !additional_) || + (additional_ != NULL && additional_length_ == 0)) { + return false; + } + if (track_number_ == 0 || track_number_ > kMaxTrackNumber) { + return false; + } + if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) { + return false; + } + return true; +} + +bool Frame::CanBeSimpleBlock() const { + return additional_ == NULL && discard_padding_ == 0 && duration_ == 0; +} + +void Frame::set_duration(uint64_t duration) { + duration_ = duration; + duration_set_ = true; +} + +void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) { + reference_block_timestamp_ = reference_block_timestamp; + reference_block_timestamp_set_ = true; +} + +/////////////////////////////////////////////////////////////// +// +// CuePoint Class + +CuePoint::CuePoint() + : time_(0), + track_(0), + cluster_pos_(0), + block_number_(1), + output_block_number_(true) {} + +CuePoint::~CuePoint() {} + +bool CuePoint::Write(IMkvWriter* writer) const { + if (!writer || track_ < 1 || cluster_pos_ < 1) + return false; + + uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition, + static_cast(cluster_pos_)); + size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast(track_)); + if (output_block_number_ && block_number_ > 1) + size += EbmlElementSize(libwebm::kMkvCueBlockNumber, + static_cast(block_number_)); + const uint64_t track_pos_size = + EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size; + const uint64_t payload_size = + EbmlElementSize(libwebm::kMkvCueTime, static_cast(time_)) + + track_pos_size; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvCueTime, + static_cast(time_))) { + return false; + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size)) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack, + static_cast(track_))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition, + static_cast(cluster_pos_))) { + return false; + } + if (output_block_number_ && block_number_ > 1) { + if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber, + static_cast(block_number_))) { + return false; + } + } + + const int64_t stop_position = writer->Position(); + if (stop_position < 0) + return false; + + if (stop_position - payload_position != static_cast(payload_size)) + return false; + + return true; +} + +uint64_t CuePoint::PayloadSize() const { + uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition, + static_cast(cluster_pos_)); + size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast(track_)); + if (output_block_number_ && block_number_ > 1) + size += EbmlElementSize(libwebm::kMkvCueBlockNumber, + static_cast(block_number_)); + const uint64_t track_pos_size = + EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size; + const uint64_t payload_size = + EbmlElementSize(libwebm::kMkvCueTime, static_cast(time_)) + + track_pos_size; + + return payload_size; +} + +uint64_t CuePoint::Size() const { + const uint64_t payload_size = PayloadSize(); + return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) + + payload_size; +} + +/////////////////////////////////////////////////////////////// +// +// Cues Class + +Cues::Cues() + : cue_entries_capacity_(0), + cue_entries_size_(0), + cue_entries_(NULL), + output_block_number_(true) {} + +Cues::~Cues() { + if (cue_entries_) { + for (int32_t i = 0; i < cue_entries_size_; ++i) { + CuePoint* const cue = cue_entries_[i]; + delete cue; + } + delete[] cue_entries_; + } +} + +bool Cues::AddCue(CuePoint* cue) { + if (!cue) + return false; + + if ((cue_entries_size_ + 1) > cue_entries_capacity_) { + // Add more CuePoints. + const int32_t new_capacity = + (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2; + + if (new_capacity < 1) + return false; + + CuePoint** const cues = + new (std::nothrow) CuePoint*[new_capacity]; // NOLINT + if (!cues) + return false; + + for (int32_t i = 0; i < cue_entries_size_; ++i) { + cues[i] = cue_entries_[i]; + } + + delete[] cue_entries_; + + cue_entries_ = cues; + cue_entries_capacity_ = new_capacity; + } + + cue->set_output_block_number(output_block_number_); + cue_entries_[cue_entries_size_++] = cue; + return true; +} + +CuePoint* Cues::GetCueByIndex(int32_t index) const { + if (cue_entries_ == NULL) + return NULL; + + if (index >= cue_entries_size_) + return NULL; + + return cue_entries_[index]; +} + +uint64_t Cues::Size() { + uint64_t size = 0; + for (int32_t i = 0; i < cue_entries_size_; ++i) + size += GetCueByIndex(i)->Size(); + size += EbmlMasterElementSize(libwebm::kMkvCues, size); + return size; +} + +bool Cues::Write(IMkvWriter* writer) const { + if (!writer) + return false; + + uint64_t size = 0; + for (int32_t i = 0; i < cue_entries_size_; ++i) { + const CuePoint* const cue = GetCueByIndex(i); + + if (!cue) + return false; + + size += cue->Size(); + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + for (int32_t i = 0; i < cue_entries_size_; ++i) { + const CuePoint* const cue = GetCueByIndex(i); + + if (!cue->Write(writer)) + return false; + } + + const int64_t stop_position = writer->Position(); + if (stop_position < 0) + return false; + + if (stop_position - payload_position != static_cast(size)) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////// +// +// ContentEncAESSettings Class + +ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {} + +uint64_t ContentEncAESSettings::Size() const { + const uint64_t payload = PayloadSize(); + const uint64_t size = + EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) + + payload; + return size; +} + +bool ContentEncAESSettings::Write(IMkvWriter* writer) const { + const uint64_t payload = PayloadSize(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings, + payload)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode, + static_cast(cipher_mode_))) { + return false; + } + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(payload)) + return false; + + return true; +} + +uint64_t ContentEncAESSettings::PayloadSize() const { + uint64_t size = EbmlElementSize(libwebm::kMkvAESSettingsCipherMode, + static_cast(cipher_mode_)); + return size; +} + +/////////////////////////////////////////////////////////////// +// +// ContentEncoding Class + +ContentEncoding::ContentEncoding() + : enc_algo_(5), + enc_key_id_(NULL), + encoding_order_(0), + encoding_scope_(1), + encoding_type_(1), + enc_key_id_length_(0) {} + +ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; } + +bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) { + if (!id || length < 1) + return false; + + delete[] enc_key_id_; + + enc_key_id_ = + new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT + if (!enc_key_id_) + return false; + + memcpy(enc_key_id_, id, static_cast(length)); + enc_key_id_length_ = length; + + return true; +} + +uint64_t ContentEncoding::Size() const { + const uint64_t encryption_size = EncryptionSize(); + const uint64_t encoding_size = EncodingSize(0, encryption_size); + const uint64_t encodings_size = + EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) + + encoding_size; + + return encodings_size; +} + +bool ContentEncoding::Write(IMkvWriter* writer) const { + const uint64_t encryption_size = EncryptionSize(); + const uint64_t encoding_size = EncodingSize(0, encryption_size); + const uint64_t size = + EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) + + encoding_size; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding, + encoding_size)) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder, + static_cast(encoding_order_))) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope, + static_cast(encoding_scope_))) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType, + static_cast(encoding_type_))) + return false; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption, + encryption_size)) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo, + static_cast(enc_algo_))) { + return false; + } + if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_, + enc_key_id_length_)) + return false; + + if (!enc_aes_settings_.Write(writer)) + return false; + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(size)) + return false; + + return true; +} + +uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size, + uint64_t encryption_size) const { + // TODO(fgalligan): Add support for compression settings. + if (compresion_size != 0) + return 0; + + uint64_t encoding_size = 0; + + if (encryption_size > 0) { + encoding_size += + EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) + + encryption_size; + } + encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingType, + static_cast(encoding_type_)); + encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingScope, + static_cast(encoding_scope_)); + encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingOrder, + static_cast(encoding_order_)); + + return encoding_size; +} + +uint64_t ContentEncoding::EncryptionSize() const { + const uint64_t aes_size = enc_aes_settings_.Size(); + + uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID, + enc_key_id_, enc_key_id_length_); + encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo, + static_cast(enc_algo_)); + + return encryption_size + aes_size; +} + +/////////////////////////////////////////////////////////////// +// +// Track Class + +Track::Track(unsigned int* seed) + : codec_id_(NULL), + codec_private_(NULL), + language_(NULL), + max_block_additional_id_(0), + name_(NULL), + number_(0), + type_(0), + uid_(MakeUID(seed)), + codec_delay_(0), + seek_pre_roll_(0), + default_duration_(0), + codec_private_length_(0), + content_encoding_entries_(NULL), + content_encoding_entries_size_(0) {} + +Track::~Track() { + delete[] codec_id_; + delete[] codec_private_; + delete[] language_; + delete[] name_; + + if (content_encoding_entries_) { + for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { + ContentEncoding* const encoding = content_encoding_entries_[i]; + delete encoding; + } + delete[] content_encoding_entries_; + } +} + +bool Track::AddContentEncoding() { + const uint32_t count = content_encoding_entries_size_ + 1; + + ContentEncoding** const content_encoding_entries = + new (std::nothrow) ContentEncoding*[count]; // NOLINT + if (!content_encoding_entries) + return false; + + ContentEncoding* const content_encoding = + new (std::nothrow) ContentEncoding(); // NOLINT + if (!content_encoding) { + delete[] content_encoding_entries; + return false; + } + + for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { + content_encoding_entries[i] = content_encoding_entries_[i]; + } + + delete[] content_encoding_entries_; + + content_encoding_entries_ = content_encoding_entries; + content_encoding_entries_[content_encoding_entries_size_] = content_encoding; + content_encoding_entries_size_ = count; + return true; +} + +ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const { + if (content_encoding_entries_ == NULL) + return NULL; + + if (index >= content_encoding_entries_size_) + return NULL; + + return content_encoding_entries_[index]; +} + +uint64_t Track::PayloadSize() const { + uint64_t size = + EbmlElementSize(libwebm::kMkvTrackNumber, static_cast(number_)); + size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast(uid_)); + size += EbmlElementSize(libwebm::kMkvTrackType, static_cast(type_)); + if (codec_id_) + size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_); + if (codec_private_) + size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_, + codec_private_length_); + if (language_) + size += EbmlElementSize(libwebm::kMkvLanguage, language_); + if (name_) + size += EbmlElementSize(libwebm::kMkvName, name_); + if (max_block_additional_id_) { + size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID, + static_cast(max_block_additional_id_)); + } + if (codec_delay_) { + size += EbmlElementSize(libwebm::kMkvCodecDelay, + static_cast(codec_delay_)); + } + if (seek_pre_roll_) { + size += EbmlElementSize(libwebm::kMkvSeekPreRoll, + static_cast(seek_pre_roll_)); + } + if (default_duration_) { + size += EbmlElementSize(libwebm::kMkvDefaultDuration, + static_cast(default_duration_)); + } + + if (content_encoding_entries_size_ > 0) { + uint64_t content_encodings_size = 0; + for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { + ContentEncoding* const encoding = content_encoding_entries_[i]; + content_encodings_size += encoding->Size(); + } + + size += EbmlMasterElementSize(libwebm::kMkvContentEncodings, + content_encodings_size) + + content_encodings_size; + } + + return size; +} + +uint64_t Track::Size() const { + uint64_t size = PayloadSize(); + size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size); + return size; +} + +bool Track::Write(IMkvWriter* writer) const { + if (!writer) + return false; + + // mandatory elements without a default value. + if (!type_ || !codec_id_) + return false; + + // AV1 tracks require a CodecPrivate. See + // https://github.com/Matroska-Org/matroska-specification/blob/av1-mappin/codec/av1.md + // TODO(tomfinegan): Update the above link to the AV1 Matroska mappings to + // point to a stable version once it is finalized, or our own WebM mappings + // page on webmproject.org should we decide to release them. + if (!strcmp(codec_id_, Tracks::kAv1CodecId) && !codec_private_) + return false; + + // |size| may be bigger than what is written out in this function because + // derived classes may write out more data in the Track element. + const uint64_t payload_size = PayloadSize(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size)) + return false; + + uint64_t size = + EbmlElementSize(libwebm::kMkvTrackNumber, static_cast(number_)); + size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast(uid_)); + size += EbmlElementSize(libwebm::kMkvTrackType, static_cast(type_)); + if (codec_id_) + size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_); + if (codec_private_) + size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_, + static_cast(codec_private_length_)); + if (language_) + size += EbmlElementSize(libwebm::kMkvLanguage, language_); + if (name_) + size += EbmlElementSize(libwebm::kMkvName, name_); + if (max_block_additional_id_) + size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID, + static_cast(max_block_additional_id_)); + if (codec_delay_) + size += EbmlElementSize(libwebm::kMkvCodecDelay, + static_cast(codec_delay_)); + if (seek_pre_roll_) + size += EbmlElementSize(libwebm::kMkvSeekPreRoll, + static_cast(seek_pre_roll_)); + if (default_duration_) + size += EbmlElementSize(libwebm::kMkvDefaultDuration, + static_cast(default_duration_)); + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber, + static_cast(number_))) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID, + static_cast(uid_))) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvTrackType, + static_cast(type_))) + return false; + if (max_block_additional_id_) { + if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID, + static_cast(max_block_additional_id_))) { + return false; + } + } + if (codec_delay_) { + if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay, + static_cast(codec_delay_))) + return false; + } + if (seek_pre_roll_) { + if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll, + static_cast(seek_pre_roll_))) + return false; + } + if (default_duration_) { + if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration, + static_cast(default_duration_))) + return false; + } + if (codec_id_) { + if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_)) + return false; + } + if (codec_private_) { + if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_, + static_cast(codec_private_length_))) + return false; + } + if (language_) { + if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_)) + return false; + } + if (name_) { + if (!WriteEbmlElement(writer, libwebm::kMkvName, name_)) + return false; + } + + int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(size)) + return false; + + if (content_encoding_entries_size_ > 0) { + uint64_t content_encodings_size = 0; + for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { + ContentEncoding* const encoding = content_encoding_entries_[i]; + content_encodings_size += encoding->Size(); + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings, + content_encodings_size)) + return false; + + for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { + ContentEncoding* const encoding = content_encoding_entries_[i]; + if (!encoding->Write(writer)) + return false; + } + } + + stop_position = writer->Position(); + if (stop_position < 0) + return false; + return true; +} + +bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) { + if (!codec_private || length < 1) + return false; + + delete[] codec_private_; + + codec_private_ = + new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT + if (!codec_private_) + return false; + + memcpy(codec_private_, codec_private, static_cast(length)); + codec_private_length_ = length; + + return true; +} + +void Track::set_codec_id(const char* codec_id) { + if (codec_id) { + delete[] codec_id_; + + const size_t length = strlen(codec_id) + 1; + codec_id_ = new (std::nothrow) char[length]; // NOLINT + if (codec_id_) { +#ifdef _MSC_VER + strcpy_s(codec_id_, length, codec_id); +#else + strcpy(codec_id_, codec_id); +#endif + } + } +} + +// TODO(fgalligan): Vet the language parameter. +void Track::set_language(const char* language) { + if (language) { + delete[] language_; + + const size_t length = strlen(language) + 1; + language_ = new (std::nothrow) char[length]; // NOLINT + if (language_) { +#ifdef _MSC_VER + strcpy_s(language_, length, language); +#else + strcpy(language_, language); +#endif + } + } +} + +void Track::set_name(const char* name) { + if (name) { + delete[] name_; + + const size_t length = strlen(name) + 1; + name_ = new (std::nothrow) char[length]; // NOLINT + if (name_) { +#ifdef _MSC_VER + strcpy_s(name_, length, name); +#else + strcpy(name_, name); +#endif + } + } +} + +/////////////////////////////////////////////////////////////// +// +// Colour and its child elements + +uint64_t PrimaryChromaticity::PrimaryChromaticitySize( + libwebm::MkvId x_id, libwebm::MkvId y_id) const { + return EbmlElementSize(x_id, x_) + EbmlElementSize(y_id, y_); +} + +bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id, + libwebm::MkvId y_id) const { + if (!Valid()) { + return false; + } + return WriteEbmlElement(writer, x_id, x_) && + WriteEbmlElement(writer, y_id, y_); +} + +bool PrimaryChromaticity::Valid() const { + return (x_ >= kChromaticityMin && x_ <= kChromaticityMax && + y_ >= kChromaticityMin && y_ <= kChromaticityMax); +} + +uint64_t MasteringMetadata::MasteringMetadataSize() const { + uint64_t size = PayloadSize(); + + if (size > 0) + size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size); + + return size; +} + +bool MasteringMetadata::Valid() const { + if (luminance_min_ != kValueNotPresent) { + if (luminance_min_ < kMinLuminance || luminance_min_ > kMinLuminanceMax || + luminance_min_ > luminance_max_) { + return false; + } + } + if (luminance_max_ != kValueNotPresent) { + if (luminance_max_ < kMinLuminance || luminance_max_ > kMaxLuminanceMax || + luminance_max_ < luminance_min_) { + return false; + } + } + if (r_ && !r_->Valid()) + return false; + if (g_ && !g_->Valid()) + return false; + if (b_ && !b_->Valid()) + return false; + if (white_point_ && !white_point_->Valid()) + return false; + + return true; +} + +bool MasteringMetadata::Write(IMkvWriter* writer) const { + const uint64_t size = PayloadSize(); + + // Don't write an empty element. + if (size == 0) + return true; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size)) + return false; + if (luminance_max_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max_)) { + return false; + } + if (luminance_min_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min_)) { + return false; + } + if (r_ && !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX, + libwebm::kMkvPrimaryRChromaticityY)) { + return false; + } + if (g_ && !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX, + libwebm::kMkvPrimaryGChromaticityY)) { + return false; + } + if (b_ && !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX, + libwebm::kMkvPrimaryBChromaticityY)) { + return false; + } + if (white_point_ && + !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX, + libwebm::kMkvWhitePointChromaticityY)) { + return false; + } + + return true; +} + +bool MasteringMetadata::SetChromaticity( + const PrimaryChromaticity* r, const PrimaryChromaticity* g, + const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) { + PrimaryChromaticityPtr r_ptr(nullptr); + if (r) { + if (!CopyChromaticity(r, &r_ptr)) + return false; + } + PrimaryChromaticityPtr g_ptr(nullptr); + if (g) { + if (!CopyChromaticity(g, &g_ptr)) + return false; + } + PrimaryChromaticityPtr b_ptr(nullptr); + if (b) { + if (!CopyChromaticity(b, &b_ptr)) + return false; + } + PrimaryChromaticityPtr wp_ptr(nullptr); + if (white_point) { + if (!CopyChromaticity(white_point, &wp_ptr)) + return false; + } + + r_ = r_ptr.release(); + g_ = g_ptr.release(); + b_ = b_ptr.release(); + white_point_ = wp_ptr.release(); + return true; +} + +uint64_t MasteringMetadata::PayloadSize() const { + uint64_t size = 0; + + if (luminance_max_ != kValueNotPresent) + size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max_); + if (luminance_min_ != kValueNotPresent) + size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min_); + + if (r_) { + size += r_->PrimaryChromaticitySize(libwebm::kMkvPrimaryRChromaticityX, + libwebm::kMkvPrimaryRChromaticityY); + } + if (g_) { + size += g_->PrimaryChromaticitySize(libwebm::kMkvPrimaryGChromaticityX, + libwebm::kMkvPrimaryGChromaticityY); + } + if (b_) { + size += b_->PrimaryChromaticitySize(libwebm::kMkvPrimaryBChromaticityX, + libwebm::kMkvPrimaryBChromaticityY); + } + if (white_point_) { + size += white_point_->PrimaryChromaticitySize( + libwebm::kMkvWhitePointChromaticityX, + libwebm::kMkvWhitePointChromaticityY); + } + + return size; +} + +uint64_t Colour::ColourSize() const { + uint64_t size = PayloadSize(); + + if (size > 0) + size += EbmlMasterElementSize(libwebm::kMkvColour, size); + + return size; +} + +bool Colour::Valid() const { + if (mastering_metadata_ && !mastering_metadata_->Valid()) + return false; + if (matrix_coefficients_ != kValueNotPresent && + !IsMatrixCoefficientsValueValid(matrix_coefficients_)) { + return false; + } + if (chroma_siting_horz_ != kValueNotPresent && + !IsChromaSitingHorzValueValid(chroma_siting_horz_)) { + return false; + } + if (chroma_siting_vert_ != kValueNotPresent && + !IsChromaSitingVertValueValid(chroma_siting_vert_)) { + return false; + } + if (range_ != kValueNotPresent && !IsColourRangeValueValid(range_)) + return false; + if (transfer_characteristics_ != kValueNotPresent && + !IsTransferCharacteristicsValueValid(transfer_characteristics_)) { + return false; + } + if (primaries_ != kValueNotPresent && !IsPrimariesValueValid(primaries_)) + return false; + + return true; +} + +bool Colour::Write(IMkvWriter* writer) const { + const uint64_t size = PayloadSize(); + + // Don't write an empty element. + if (size == 0) + return true; + + // Don't write an invalid element. + if (!Valid()) + return false; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size)) + return false; + + if (matrix_coefficients_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients, + static_cast(matrix_coefficients_))) { + return false; + } + if (bits_per_channel_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel, + static_cast(bits_per_channel_))) { + return false; + } + if (chroma_subsampling_horz_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz, + static_cast(chroma_subsampling_horz_))) { + return false; + } + if (chroma_subsampling_vert_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert, + static_cast(chroma_subsampling_vert_))) { + return false; + } + + if (cb_subsampling_horz_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz, + static_cast(cb_subsampling_horz_))) { + return false; + } + if (cb_subsampling_vert_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert, + static_cast(cb_subsampling_vert_))) { + return false; + } + if (chroma_siting_horz_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz, + static_cast(chroma_siting_horz_))) { + return false; + } + if (chroma_siting_vert_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert, + static_cast(chroma_siting_vert_))) { + return false; + } + if (range_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvRange, + static_cast(range_))) { + return false; + } + if (transfer_characteristics_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics, + static_cast(transfer_characteristics_))) { + return false; + } + if (primaries_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvPrimaries, + static_cast(primaries_))) { + return false; + } + if (max_cll_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvMaxCLL, + static_cast(max_cll_))) { + return false; + } + if (max_fall_ != kValueNotPresent && + !WriteEbmlElement(writer, libwebm::kMkvMaxFALL, + static_cast(max_fall_))) { + return false; + } + + if (mastering_metadata_ && !mastering_metadata_->Write(writer)) + return false; + + return true; +} + +bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) { + std::unique_ptr mm_ptr(new MasteringMetadata()); + if (!mm_ptr.get()) + return false; + + mm_ptr->set_luminance_max(mastering_metadata.luminance_max()); + mm_ptr->set_luminance_min(mastering_metadata.luminance_min()); + + if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(), + mastering_metadata.b(), + mastering_metadata.white_point())) { + return false; + } + + delete mastering_metadata_; + mastering_metadata_ = mm_ptr.release(); + return true; +} + +uint64_t Colour::PayloadSize() const { + uint64_t size = 0; + + if (matrix_coefficients_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvMatrixCoefficients, + static_cast(matrix_coefficients_)); + } + if (bits_per_channel_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvBitsPerChannel, + static_cast(bits_per_channel_)); + } + if (chroma_subsampling_horz_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz, + static_cast(chroma_subsampling_horz_)); + } + if (chroma_subsampling_vert_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert, + static_cast(chroma_subsampling_vert_)); + } + if (cb_subsampling_horz_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvCbSubsamplingHorz, + static_cast(cb_subsampling_horz_)); + } + if (cb_subsampling_vert_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvCbSubsamplingVert, + static_cast(cb_subsampling_vert_)); + } + if (chroma_siting_horz_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvChromaSitingHorz, + static_cast(chroma_siting_horz_)); + } + if (chroma_siting_vert_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvChromaSitingVert, + static_cast(chroma_siting_vert_)); + } + if (range_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvRange, static_cast(range_)); + } + if (transfer_characteristics_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvTransferCharacteristics, + static_cast(transfer_characteristics_)); + } + if (primaries_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvPrimaries, + static_cast(primaries_)); + } + if (max_cll_ != kValueNotPresent) { + size += EbmlElementSize(libwebm::kMkvMaxCLL, static_cast(max_cll_)); + } + if (max_fall_ != kValueNotPresent) { + size += + EbmlElementSize(libwebm::kMkvMaxFALL, static_cast(max_fall_)); + } + + if (mastering_metadata_) + size += mastering_metadata_->MasteringMetadataSize(); + + return size; +} + +/////////////////////////////////////////////////////////////// +// +// Projection element + +uint64_t Projection::ProjectionSize() const { + uint64_t size = PayloadSize(); + + if (size > 0) + size += EbmlMasterElementSize(libwebm::kMkvProjection, size); + + return size; +} + +bool Projection::Write(IMkvWriter* writer) const { + const uint64_t size = PayloadSize(); + + // Don't write an empty element. + if (size == 0) + return true; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvProjection, size)) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionType, + static_cast(type_))) { + return false; + } + + if (private_data_length_ > 0 && private_data_ != NULL && + !WriteEbmlElement(writer, libwebm::kMkvProjectionPrivate, private_data_, + private_data_length_)) { + return false; + } + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseYaw, pose_yaw_)) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPosePitch, + pose_pitch_)) { + return false; + } + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseRoll, pose_roll_)) { + return false; + } + + return true; +} + +bool Projection::SetProjectionPrivate(const uint8_t* data, + uint64_t data_length) { + if (data == NULL || data_length == 0) { + return false; + } + + if (data_length != static_cast(data_length)) { + return false; + } + + uint8_t* new_private_data = + new (std::nothrow) uint8_t[static_cast(data_length)]; + if (new_private_data == NULL) { + return false; + } + + delete[] private_data_; + private_data_ = new_private_data; + private_data_length_ = data_length; + memcpy(private_data_, data, static_cast(data_length)); + + return true; +} + +uint64_t Projection::PayloadSize() const { + uint64_t size = + EbmlElementSize(libwebm::kMkvProjection, static_cast(type_)); + + if (private_data_length_ > 0 && private_data_ != NULL) { + size += EbmlElementSize(libwebm::kMkvProjectionPrivate, private_data_, + private_data_length_); + } + + size += EbmlElementSize(libwebm::kMkvProjectionPoseYaw, pose_yaw_); + size += EbmlElementSize(libwebm::kMkvProjectionPosePitch, pose_pitch_); + size += EbmlElementSize(libwebm::kMkvProjectionPoseRoll, pose_roll_); + + return size; +} + +/////////////////////////////////////////////////////////////// +// +// VideoTrack Class + +VideoTrack::VideoTrack(unsigned int* seed) + : Track(seed), + display_height_(0), + display_width_(0), + pixel_height_(0), + pixel_width_(0), + crop_left_(0), + crop_right_(0), + crop_top_(0), + crop_bottom_(0), + frame_rate_(0.0), + height_(0), + stereo_mode_(0), + alpha_mode_(0), + width_(0), + colour_space_(NULL), + colour_(NULL), + projection_(NULL) {} + +VideoTrack::~VideoTrack() { + delete colour_; + delete projection_; +} + +bool VideoTrack::SetStereoMode(uint64_t stereo_mode) { + if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst && + stereo_mode != kTopBottomRightIsFirst && + stereo_mode != kTopBottomLeftIsFirst && + stereo_mode != kSideBySideRightIsFirst) + return false; + + stereo_mode_ = stereo_mode; + return true; +} + +bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) { + if (alpha_mode != kNoAlpha && alpha_mode != kAlpha) + return false; + + alpha_mode_ = alpha_mode; + return true; +} + +uint64_t VideoTrack::PayloadSize() const { + const uint64_t parent_size = Track::PayloadSize(); + + uint64_t size = VideoPayloadSize(); + size += EbmlMasterElementSize(libwebm::kMkvVideo, size); + + return parent_size + size; +} + +bool VideoTrack::Write(IMkvWriter* writer) const { + if (!Track::Write(writer)) + return false; + + const uint64_t size = VideoPayloadSize(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlElement( + writer, libwebm::kMkvPixelWidth, + static_cast((pixel_width_ > 0) ? pixel_width_ : width_))) + return false; + if (!WriteEbmlElement( + writer, libwebm::kMkvPixelHeight, + static_cast((pixel_height_ > 0) ? pixel_height_ : height_))) + return false; + if (display_width_ > 0) { + if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth, + static_cast(display_width_))) + return false; + } + if (display_height_ > 0) { + if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight, + static_cast(display_height_))) + return false; + } + if (crop_left_ > 0) { + if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft, + static_cast(crop_left_))) + return false; + } + if (crop_right_ > 0) { + if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight, + static_cast(crop_right_))) + return false; + } + if (crop_top_ > 0) { + if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop, + static_cast(crop_top_))) + return false; + } + if (crop_bottom_ > 0) { + if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom, + static_cast(crop_bottom_))) + return false; + } + if (stereo_mode_ > kMono) { + if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode, + static_cast(stereo_mode_))) + return false; + } + if (alpha_mode_ > kNoAlpha) { + if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode, + static_cast(alpha_mode_))) + return false; + } + if (colour_space_) { + if (!WriteEbmlElement(writer, libwebm::kMkvColourSpace, colour_space_)) + return false; + } + if (frame_rate_ > 0.0) { + if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate, + static_cast(frame_rate_))) { + return false; + } + } + if (colour_) { + if (!colour_->Write(writer)) + return false; + } + if (projection_) { + if (!projection_->Write(writer)) + return false; + } + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(size)) { + return false; + } + + return true; +} + +void VideoTrack::set_colour_space(const char* colour_space) { + if (colour_space) { + delete[] colour_space_; + + const size_t length = strlen(colour_space) + 1; + colour_space_ = new (std::nothrow) char[length]; // NOLINT + if (colour_space_) { +#ifdef _MSC_VER + strcpy_s(colour_space_, length, colour_space); +#else + strcpy(colour_space_, colour_space); +#endif + } + } +} + +bool VideoTrack::SetColour(const Colour& colour) { + std::unique_ptr colour_ptr(new Colour()); + if (!colour_ptr.get()) + return false; + + if (colour.mastering_metadata()) { + if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata())) + return false; + } + + colour_ptr->set_matrix_coefficients(colour.matrix_coefficients()); + colour_ptr->set_bits_per_channel(colour.bits_per_channel()); + colour_ptr->set_chroma_subsampling_horz(colour.chroma_subsampling_horz()); + colour_ptr->set_chroma_subsampling_vert(colour.chroma_subsampling_vert()); + colour_ptr->set_cb_subsampling_horz(colour.cb_subsampling_horz()); + colour_ptr->set_cb_subsampling_vert(colour.cb_subsampling_vert()); + colour_ptr->set_chroma_siting_horz(colour.chroma_siting_horz()); + colour_ptr->set_chroma_siting_vert(colour.chroma_siting_vert()); + colour_ptr->set_range(colour.range()); + colour_ptr->set_transfer_characteristics(colour.transfer_characteristics()); + colour_ptr->set_primaries(colour.primaries()); + colour_ptr->set_max_cll(colour.max_cll()); + colour_ptr->set_max_fall(colour.max_fall()); + delete colour_; + colour_ = colour_ptr.release(); + return true; +} + +bool VideoTrack::SetProjection(const Projection& projection) { + std::unique_ptr projection_ptr(new Projection()); + if (!projection_ptr.get()) + return false; + + if (projection.private_data()) { + if (!projection_ptr->SetProjectionPrivate( + projection.private_data(), projection.private_data_length())) { + return false; + } + } + + projection_ptr->set_type(projection.type()); + projection_ptr->set_pose_yaw(projection.pose_yaw()); + projection_ptr->set_pose_pitch(projection.pose_pitch()); + projection_ptr->set_pose_roll(projection.pose_roll()); + delete projection_; + projection_ = projection_ptr.release(); + return true; +} + +uint64_t VideoTrack::VideoPayloadSize() const { + uint64_t size = EbmlElementSize( + libwebm::kMkvPixelWidth, + static_cast((pixel_width_ > 0) ? pixel_width_ : width_)); + size += EbmlElementSize( + libwebm::kMkvPixelHeight, + static_cast((pixel_height_ > 0) ? pixel_height_ : height_)); + if (display_width_ > 0) + size += EbmlElementSize(libwebm::kMkvDisplayWidth, + static_cast(display_width_)); + if (display_height_ > 0) + size += EbmlElementSize(libwebm::kMkvDisplayHeight, + static_cast(display_height_)); + if (crop_left_ > 0) + size += EbmlElementSize(libwebm::kMkvPixelCropLeft, + static_cast(crop_left_)); + if (crop_right_ > 0) + size += EbmlElementSize(libwebm::kMkvPixelCropRight, + static_cast(crop_right_)); + if (crop_top_ > 0) + size += EbmlElementSize(libwebm::kMkvPixelCropTop, + static_cast(crop_top_)); + if (crop_bottom_ > 0) + size += EbmlElementSize(libwebm::kMkvPixelCropBottom, + static_cast(crop_bottom_)); + if (stereo_mode_ > kMono) + size += EbmlElementSize(libwebm::kMkvStereoMode, + static_cast(stereo_mode_)); + if (alpha_mode_ > kNoAlpha) + size += EbmlElementSize(libwebm::kMkvAlphaMode, + static_cast(alpha_mode_)); + if (frame_rate_ > 0.0) + size += EbmlElementSize(libwebm::kMkvFrameRate, + static_cast(frame_rate_)); + if (colour_space_) + size += EbmlElementSize(libwebm::kMkvColourSpace, colour_space_); + if (colour_) + size += colour_->ColourSize(); + if (projection_) + size += projection_->ProjectionSize(); + + return size; +} + +/////////////////////////////////////////////////////////////// +// +// AudioTrack Class + +AudioTrack::AudioTrack(unsigned int* seed) + : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {} + +AudioTrack::~AudioTrack() {} + +uint64_t AudioTrack::PayloadSize() const { + const uint64_t parent_size = Track::PayloadSize(); + + uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency, + static_cast(sample_rate_)); + size += + EbmlElementSize(libwebm::kMkvChannels, static_cast(channels_)); + if (bit_depth_ > 0) + size += + EbmlElementSize(libwebm::kMkvBitDepth, static_cast(bit_depth_)); + size += EbmlMasterElementSize(libwebm::kMkvAudio, size); + + return parent_size + size; +} + +bool AudioTrack::Write(IMkvWriter* writer) const { + if (!Track::Write(writer)) + return false; + + // Calculate AudioSettings size. + uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency, + static_cast(sample_rate_)); + size += + EbmlElementSize(libwebm::kMkvChannels, static_cast(channels_)); + if (bit_depth_ > 0) + size += + EbmlElementSize(libwebm::kMkvBitDepth, static_cast(bit_depth_)); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency, + static_cast(sample_rate_))) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvChannels, + static_cast(channels_))) + return false; + if (bit_depth_ > 0) + if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth, + static_cast(bit_depth_))) + return false; + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(size)) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////// +// +// Tracks Class + +const char Tracks::kOpusCodecId[] = "A_OPUS"; +const char Tracks::kVorbisCodecId[] = "A_VORBIS"; +const char Tracks::kAv1CodecId[] = "V_AV1"; +const char Tracks::kVp8CodecId[] = "V_VP8"; +const char Tracks::kVp9CodecId[] = "V_VP9"; +const char Tracks::kWebVttCaptionsId[] = "D_WEBVTT/CAPTIONS"; +const char Tracks::kWebVttDescriptionsId[] = "D_WEBVTT/DESCRIPTIONS"; +const char Tracks::kWebVttMetadataId[] = "D_WEBVTT/METADATA"; +const char Tracks::kWebVttSubtitlesId[] = "D_WEBVTT/SUBTITLES"; + +Tracks::Tracks() + : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {} + +Tracks::~Tracks() { + if (track_entries_) { + for (uint32_t i = 0; i < track_entries_size_; ++i) { + Track* const track = track_entries_[i]; + delete track; + } + delete[] track_entries_; + } +} + +bool Tracks::AddTrack(Track* track, int32_t number) { + if (number < 0 || wrote_tracks_) + return false; + + // This muxer only supports track numbers in the range [1, 126], in + // order to be able (to use Matroska integer representation) to + // serialize the block header (of which the track number is a part) + // for a frame using exactly 4 bytes. + + if (number > 0x7E) + return false; + + uint32_t track_num = number; + + if (track_num > 0) { + // Check to make sure a track does not already have |track_num|. + for (uint32_t i = 0; i < track_entries_size_; ++i) { + if (track_entries_[i]->number() == track_num) + return false; + } + } + + const uint32_t count = track_entries_size_ + 1; + + Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT + if (!track_entries) + return false; + + for (uint32_t i = 0; i < track_entries_size_; ++i) { + track_entries[i] = track_entries_[i]; + } + + delete[] track_entries_; + + // Find the lowest availible track number > 0. + if (track_num == 0) { + track_num = count; + + // Check to make sure a track does not already have |track_num|. + bool exit = false; + do { + exit = true; + for (uint32_t i = 0; i < track_entries_size_; ++i) { + if (track_entries[i]->number() == track_num) { + track_num++; + exit = false; + break; + } + } + } while (!exit); + } + track->set_number(track_num); + + track_entries_ = track_entries; + track_entries_[track_entries_size_] = track; + track_entries_size_ = count; + return true; +} + +const Track* Tracks::GetTrackByIndex(uint32_t index) const { + if (track_entries_ == NULL) + return NULL; + + if (index >= track_entries_size_) + return NULL; + + return track_entries_[index]; +} + +Track* Tracks::GetTrackByNumber(uint64_t track_number) const { + const int32_t count = track_entries_size(); + for (int32_t i = 0; i < count; ++i) { + if (track_entries_[i]->number() == track_number) + return track_entries_[i]; + } + + return NULL; +} + +bool Tracks::TrackIsAudio(uint64_t track_number) const { + const Track* const track = GetTrackByNumber(track_number); + + if (track->type() == kAudio) + return true; + + return false; +} + +bool Tracks::TrackIsVideo(uint64_t track_number) const { + const Track* const track = GetTrackByNumber(track_number); + + if (track->type() == kVideo) + return true; + + return false; +} + +bool Tracks::Write(IMkvWriter* writer) const { + uint64_t size = 0; + const int32_t count = track_entries_size(); + for (int32_t i = 0; i < count; ++i) { + const Track* const track = GetTrackByIndex(i); + + if (!track) + return false; + + size += track->Size(); + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + for (int32_t i = 0; i < count; ++i) { + const Track* const track = GetTrackByIndex(i); + if (!track->Write(writer)) + return false; + } + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(size)) + return false; + + wrote_tracks_ = true; + return true; +} + +/////////////////////////////////////////////////////////////// +// +// Chapter Class + +bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); } + +void Chapter::set_time(const Segment& segment, uint64_t start_ns, + uint64_t end_ns) { + const SegmentInfo* const info = segment.GetSegmentInfo(); + const uint64_t timecode_scale = info->timecode_scale(); + start_timecode_ = start_ns / timecode_scale; + end_timecode_ = end_ns / timecode_scale; +} + +bool Chapter::add_string(const char* title, const char* language, + const char* country) { + if (!ExpandDisplaysArray()) + return false; + + Display& d = displays_[displays_count_++]; + d.Init(); + + if (!d.set_title(title)) + return false; + + if (!d.set_language(language)) + return false; + + if (!d.set_country(country)) + return false; + + return true; +} + +Chapter::Chapter() { + // This ctor only constructs the object. Proper initialization is + // done in Init() (called in Chapters::AddChapter()). The only + // reason we bother implementing this ctor is because we had to + // declare it as private (along with the dtor), in order to prevent + // clients from creating Chapter instances (a privelege we grant + // only to the Chapters class). Doing no initialization here also + // means that creating arrays of chapter objects is more efficient, + // because we only initialize each new chapter object as it becomes + // active on the array. +} + +Chapter::~Chapter() {} + +void Chapter::Init(unsigned int* seed) { + id_ = NULL; + start_timecode_ = 0; + end_timecode_ = 0; + displays_ = NULL; + displays_size_ = 0; + displays_count_ = 0; + uid_ = MakeUID(seed); +} + +void Chapter::ShallowCopy(Chapter* dst) const { + dst->id_ = id_; + dst->start_timecode_ = start_timecode_; + dst->end_timecode_ = end_timecode_; + dst->uid_ = uid_; + dst->displays_ = displays_; + dst->displays_size_ = displays_size_; + dst->displays_count_ = displays_count_; +} + +void Chapter::Clear() { + StrCpy(NULL, &id_); + + while (displays_count_ > 0) { + Display& d = displays_[--displays_count_]; + d.Clear(); + } + + delete[] displays_; + displays_ = NULL; + + displays_size_ = 0; +} + +bool Chapter::ExpandDisplaysArray() { + if (displays_size_ > displays_count_) + return true; // nothing to do yet + + const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_; + + Display* const displays = new (std::nothrow) Display[size]; // NOLINT + if (displays == NULL) + return false; + + for (int idx = 0; idx < displays_count_; ++idx) { + displays[idx] = displays_[idx]; // shallow copy + } + + delete[] displays_; + + displays_ = displays; + displays_size_ = size; + + return true; +} + +uint64_t Chapter::WriteAtom(IMkvWriter* writer) const { + uint64_t payload_size = + EbmlElementSize(libwebm::kMkvChapterStringUID, id_) + + EbmlElementSize(libwebm::kMkvChapterUID, static_cast(uid_)) + + EbmlElementSize(libwebm::kMkvChapterTimeStart, + static_cast(start_timecode_)) + + EbmlElementSize(libwebm::kMkvChapterTimeEnd, + static_cast(end_timecode_)); + + for (int idx = 0; idx < displays_count_; ++idx) { + const Display& d = displays_[idx]; + payload_size += d.WriteDisplay(NULL); + } + + const uint64_t atom_size = + EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) + + payload_size; + + if (writer == NULL) + return atom_size; + + const int64_t start = writer->Position(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID, + static_cast(uid_))) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart, + static_cast(start_timecode_))) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd, + static_cast(end_timecode_))) + return 0; + + for (int idx = 0; idx < displays_count_; ++idx) { + const Display& d = displays_[idx]; + + if (!d.WriteDisplay(writer)) + return 0; + } + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != atom_size) + return 0; + + return atom_size; +} + +void Chapter::Display::Init() { + title_ = NULL; + language_ = NULL; + country_ = NULL; +} + +void Chapter::Display::Clear() { + StrCpy(NULL, &title_); + StrCpy(NULL, &language_); + StrCpy(NULL, &country_); +} + +bool Chapter::Display::set_title(const char* title) { + return StrCpy(title, &title_); +} + +bool Chapter::Display::set_language(const char* language) { + return StrCpy(language, &language_); +} + +bool Chapter::Display::set_country(const char* country) { + return StrCpy(country, &country_); +} + +uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const { + uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_); + + if (language_) + payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_); + + if (country_) + payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_); + + const uint64_t display_size = + EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) + + payload_size; + + if (writer == NULL) + return display_size; + + const int64_t start = writer->Position(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay, + payload_size)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_)) + return 0; + + if (language_) { + if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_)) + return 0; + } + + if (country_) { + if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_)) + return 0; + } + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != display_size) + return 0; + + return display_size; +} + +/////////////////////////////////////////////////////////////// +// +// Chapters Class + +Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {} + +Chapters::~Chapters() { + while (chapters_count_ > 0) { + Chapter& chapter = chapters_[--chapters_count_]; + chapter.Clear(); + } + + delete[] chapters_; + chapters_ = NULL; +} + +int Chapters::Count() const { return chapters_count_; } + +Chapter* Chapters::AddChapter(unsigned int* seed) { + if (!ExpandChaptersArray()) + return NULL; + + Chapter& chapter = chapters_[chapters_count_++]; + chapter.Init(seed); + + return &chapter; +} + +bool Chapters::Write(IMkvWriter* writer) const { + if (writer == NULL) + return false; + + const uint64_t payload_size = WriteEdition(NULL); // return size only + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size)) + return false; + + const int64_t start = writer->Position(); + + if (WriteEdition(writer) == 0) // error + return false; + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != payload_size) + return false; + + return true; +} + +bool Chapters::ExpandChaptersArray() { + if (chapters_size_ > chapters_count_) + return true; // nothing to do yet + + const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_; + + Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT + if (chapters == NULL) + return false; + + for (int idx = 0; idx < chapters_count_; ++idx) { + const Chapter& src = chapters_[idx]; + Chapter* const dst = chapters + idx; + src.ShallowCopy(dst); + } + + delete[] chapters_; + + chapters_ = chapters; + chapters_size_ = size; + + return true; +} + +uint64_t Chapters::WriteEdition(IMkvWriter* writer) const { + uint64_t payload_size = 0; + + for (int idx = 0; idx < chapters_count_; ++idx) { + const Chapter& chapter = chapters_[idx]; + payload_size += chapter.WriteAtom(NULL); + } + + const uint64_t edition_size = + EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) + + payload_size; + + if (writer == NULL) // return size only + return edition_size; + + const int64_t start = writer->Position(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size)) + return 0; // error + + for (int idx = 0; idx < chapters_count_; ++idx) { + const Chapter& chapter = chapters_[idx]; + + const uint64_t chapter_size = chapter.WriteAtom(writer); + if (chapter_size == 0) // error + return 0; + } + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != edition_size) + return 0; + + return edition_size; +} + +// Tag Class + +bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) { + if (!ExpandSimpleTagsArray()) + return false; + + SimpleTag& st = simple_tags_[simple_tags_count_++]; + st.Init(); + + if (!st.set_tag_name(tag_name)) + return false; + + if (!st.set_tag_string(tag_string)) + return false; + + return true; +} + +Tag::Tag() { + simple_tags_ = NULL; + simple_tags_size_ = 0; + simple_tags_count_ = 0; +} + +Tag::~Tag() {} + +void Tag::ShallowCopy(Tag* dst) const { + dst->simple_tags_ = simple_tags_; + dst->simple_tags_size_ = simple_tags_size_; + dst->simple_tags_count_ = simple_tags_count_; +} + +void Tag::Clear() { + while (simple_tags_count_ > 0) { + SimpleTag& st = simple_tags_[--simple_tags_count_]; + st.Clear(); + } + + delete[] simple_tags_; + simple_tags_ = NULL; + + simple_tags_size_ = 0; +} + +bool Tag::ExpandSimpleTagsArray() { + if (simple_tags_size_ > simple_tags_count_) + return true; // nothing to do yet + + const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_; + + SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT + if (simple_tags == NULL) + return false; + + for (int idx = 0; idx < simple_tags_count_; ++idx) { + simple_tags[idx] = simple_tags_[idx]; // shallow copy + } + + delete[] simple_tags_; + + simple_tags_ = simple_tags; + simple_tags_size_ = size; + + return true; +} + +uint64_t Tag::Write(IMkvWriter* writer) const { + uint64_t payload_size = 0; + + for (int idx = 0; idx < simple_tags_count_; ++idx) { + const SimpleTag& st = simple_tags_[idx]; + payload_size += st.Write(NULL); + } + + const uint64_t tag_size = + EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size; + + if (writer == NULL) + return tag_size; + + const int64_t start = writer->Position(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvTag, payload_size)) + return 0; + + for (int idx = 0; idx < simple_tags_count_; ++idx) { + const SimpleTag& st = simple_tags_[idx]; + + if (!st.Write(writer)) + return 0; + } + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != tag_size) + return 0; + + return tag_size; +} + +// Tag::SimpleTag + +void Tag::SimpleTag::Init() { + tag_name_ = NULL; + tag_string_ = NULL; +} + +void Tag::SimpleTag::Clear() { + StrCpy(NULL, &tag_name_); + StrCpy(NULL, &tag_string_); +} + +bool Tag::SimpleTag::set_tag_name(const char* tag_name) { + return StrCpy(tag_name, &tag_name_); +} + +bool Tag::SimpleTag::set_tag_string(const char* tag_string) { + return StrCpy(tag_string, &tag_string_); +} + +uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const { + uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_); + + payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_); + + const uint64_t simple_tag_size = + EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) + + payload_size; + + if (writer == NULL) + return simple_tag_size; + + const int64_t start = writer->Position(); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_)) + return 0; + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != simple_tag_size) + return 0; + + return simple_tag_size; +} + +// Tags Class + +Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {} + +Tags::~Tags() { + while (tags_count_ > 0) { + Tag& tag = tags_[--tags_count_]; + tag.Clear(); + } + + delete[] tags_; + tags_ = NULL; +} + +int Tags::Count() const { return tags_count_; } + +Tag* Tags::AddTag() { + if (!ExpandTagsArray()) + return NULL; + + Tag& tag = tags_[tags_count_++]; + + return &tag; +} + +bool Tags::Write(IMkvWriter* writer) const { + if (writer == NULL) + return false; + + uint64_t payload_size = 0; + + for (int idx = 0; idx < tags_count_; ++idx) { + const Tag& tag = tags_[idx]; + payload_size += tag.Write(NULL); + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size)) + return false; + + const int64_t start = writer->Position(); + + for (int idx = 0; idx < tags_count_; ++idx) { + const Tag& tag = tags_[idx]; + + const uint64_t tag_size = tag.Write(writer); + if (tag_size == 0) // error + return 0; + } + + const int64_t stop = writer->Position(); + + if (stop >= start && uint64_t(stop - start) != payload_size) + return false; + + return true; +} + +bool Tags::ExpandTagsArray() { + if (tags_size_ > tags_count_) + return true; // nothing to do yet + + const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_; + + Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT + if (tags == NULL) + return false; + + for (int idx = 0; idx < tags_count_; ++idx) { + const Tag& src = tags_[idx]; + Tag* const dst = tags + idx; + src.ShallowCopy(dst); + } + + delete[] tags_; + + tags_ = tags; + tags_size_ = size; + + return true; +} + +/////////////////////////////////////////////////////////////// +// +// Cluster class + +Cluster::Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale, + bool write_last_frame_with_duration, bool fixed_size_timecode) + : blocks_added_(0), + finalized_(false), + fixed_size_timecode_(fixed_size_timecode), + header_written_(false), + payload_size_(0), + position_for_cues_(cues_pos), + size_position_(-1), + timecode_(timecode), + timecode_scale_(timecode_scale), + write_last_frame_with_duration_(write_last_frame_with_duration), + writer_(NULL) {} + +Cluster::~Cluster() { + // Delete any stored frames that are left behind. This will happen if the + // Cluster was not Finalized for whatever reason. + while (!stored_frames_.empty()) { + while (!stored_frames_.begin()->second.empty()) { + delete stored_frames_.begin()->second.front(); + stored_frames_.begin()->second.pop_front(); + } + stored_frames_.erase(stored_frames_.begin()->first); + } +} + +bool Cluster::Init(IMkvWriter* ptr_writer) { + if (!ptr_writer) { + return false; + } + writer_ = ptr_writer; + return true; +} + +bool Cluster::AddFrame(const Frame* const frame) { + return QueueOrWriteFrame(frame); +} + +bool Cluster::AddFrame(const uint8_t* data, uint64_t length, + uint64_t track_number, uint64_t abs_timecode, + bool is_key) { + Frame frame; + if (!frame.Init(data, length)) + return false; + frame.set_track_number(track_number); + frame.set_timestamp(abs_timecode); + frame.set_is_key(is_key); + return QueueOrWriteFrame(&frame); +} + +bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length, + const uint8_t* additional, + uint64_t additional_length, + uint64_t add_id, uint64_t track_number, + uint64_t abs_timecode, bool is_key) { + if (!additional || additional_length == 0) { + return false; + } + Frame frame; + if (!frame.Init(data, length) || + !frame.AddAdditionalData(additional, additional_length, add_id)) { + return false; + } + frame.set_track_number(track_number); + frame.set_timestamp(abs_timecode); + frame.set_is_key(is_key); + return QueueOrWriteFrame(&frame); +} + +bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, + int64_t discard_padding, + uint64_t track_number, + uint64_t abs_timecode, bool is_key) { + Frame frame; + if (!frame.Init(data, length)) + return false; + frame.set_discard_padding(discard_padding); + frame.set_track_number(track_number); + frame.set_timestamp(abs_timecode); + frame.set_is_key(is_key); + return QueueOrWriteFrame(&frame); +} + +bool Cluster::AddMetadata(const uint8_t* data, uint64_t length, + uint64_t track_number, uint64_t abs_timecode, + uint64_t duration_timecode) { + Frame frame; + if (!frame.Init(data, length)) + return false; + frame.set_track_number(track_number); + frame.set_timestamp(abs_timecode); + frame.set_duration(duration_timecode); + frame.set_is_key(true); // All metadata blocks are keyframes. + return QueueOrWriteFrame(&frame); +} + +void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; } + +bool Cluster::Finalize() { + return !write_last_frame_with_duration_ && Finalize(false, 0); +} + +bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) { + if (!writer_ || finalized_) + return false; + + if (write_last_frame_with_duration_) { + // Write out held back Frames. This essentially performs a k-way merge + // across all tracks in the increasing order of timestamps. + while (!stored_frames_.empty()) { + Frame* frame = stored_frames_.begin()->second.front(); + + // Get the next frame to write (frame with least timestamp across all + // tracks). + for (FrameMapIterator frames_iterator = ++stored_frames_.begin(); + frames_iterator != stored_frames_.end(); ++frames_iterator) { + if (frames_iterator->second.front()->timestamp() < frame->timestamp()) { + frame = frames_iterator->second.front(); + } + } + + // Set the duration if it's the last frame for the track. + if (set_last_frame_duration && + stored_frames_[frame->track_number()].size() == 1 && + !frame->duration_set()) { + frame->set_duration(duration - frame->timestamp()); + if (!frame->is_key() && !frame->reference_block_timestamp_set()) { + frame->set_reference_block_timestamp( + last_block_timestamp_[frame->track_number()]); + } + } + + // Write the frame and remove it from |stored_frames_|. + const bool wrote_frame = DoWriteFrame(frame); + stored_frames_[frame->track_number()].pop_front(); + if (stored_frames_[frame->track_number()].empty()) { + stored_frames_.erase(frame->track_number()); + } + delete frame; + if (!wrote_frame) + return false; + } + } + + if (size_position_ == -1) + return false; + + if (writer_->Seekable()) { + const int64_t pos = writer_->Position(); + + if (writer_->Position(size_position_)) + return false; + + if (WriteUIntSize(writer_, payload_size(), 8)) + return false; + + if (writer_->Position(pos)) + return false; + } + + finalized_ = true; + + return true; +} + +uint64_t Cluster::Size() const { + const uint64_t element_size = + EbmlMasterElementSize(static_cast(libwebm::kMkvCluster), + uint64_t{0xFFFFFFFFFFFFFFFFU}) + + payload_size_; + return element_size; +} + +bool Cluster::PreWriteBlock() { + if (finalized_) + return false; + + if (!header_written_) { + if (!WriteClusterHeader()) + return false; + } + + return true; +} + +void Cluster::PostWriteBlock(uint64_t element_size) { + AddPayloadSize(element_size); + ++blocks_added_; +} + +int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const { + const int64_t cluster_timecode = this->Cluster::timecode(); + const int64_t rel_timecode = + static_cast(abs_timecode) - cluster_timecode; + + if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode) + return -1; + + return rel_timecode; +} + +bool Cluster::DoWriteFrame(const Frame* const frame) { + if (!frame || !frame->IsValid()) + return false; + + if (!PreWriteBlock()) + return false; + + const uint64_t element_size = WriteFrame(writer_, frame, this); + if (element_size == 0) + return false; + + PostWriteBlock(element_size); + last_block_timestamp_[frame->track_number()] = frame->timestamp(); + return true; +} + +bool Cluster::QueueOrWriteFrame(const Frame* const frame) { + if (!frame || !frame->IsValid()) + return false; + + // If |write_last_frame_with_duration_| is not set, then write the frame right + // away. + if (!write_last_frame_with_duration_) { + return DoWriteFrame(frame); + } + + // Queue the current frame. + uint64_t track_number = frame->track_number(); + Frame* const frame_to_store = new Frame(); + frame_to_store->CopyFrom(*frame); + stored_frames_[track_number].push_back(frame_to_store); + + // Iterate through all queued frames in the current track except the last one + // and write it if it is okay to do so (i.e.) no other track has an held back + // frame with timestamp <= the timestamp of the frame in question. + std::vector::iterator> frames_to_erase; + for (std::list::iterator + current_track_iterator = stored_frames_[track_number].begin(), + end = --stored_frames_[track_number].end(); + current_track_iterator != end; ++current_track_iterator) { + const Frame* const frame_to_write = *current_track_iterator; + bool okay_to_write = true; + for (FrameMapIterator track_iterator = stored_frames_.begin(); + track_iterator != stored_frames_.end(); ++track_iterator) { + if (track_iterator->first == track_number) { + continue; + } + if (track_iterator->second.front()->timestamp() < + frame_to_write->timestamp()) { + okay_to_write = false; + break; + } + } + if (okay_to_write) { + const bool wrote_frame = DoWriteFrame(frame_to_write); + delete frame_to_write; + if (!wrote_frame) + return false; + frames_to_erase.push_back(current_track_iterator); + } else { + break; + } + } + for (std::vector::iterator>::iterator iterator = + frames_to_erase.begin(); + iterator != frames_to_erase.end(); ++iterator) { + stored_frames_[track_number].erase(*iterator); + } + return true; +} + +bool Cluster::WriteClusterHeader() { + if (finalized_) + return false; + + if (WriteID(writer_, libwebm::kMkvCluster)) + return false; + + // Save for later. + size_position_ = writer_->Position(); + + // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8 + // bytes because we do not know how big our cluster will be. + if (SerializeInt(writer_, kEbmlUnknownValue, 8)) + return false; + + if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(), + fixed_size_timecode_ ? 8 : 0)) { + return false; + } + AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(), + fixed_size_timecode_ ? 8 : 0)); + header_written_ = true; + + return true; +} + +/////////////////////////////////////////////////////////////// +// +// SeekHead Class + +SeekHead::SeekHead() : start_pos_(0ULL) { + for (int32_t i = 0; i < kSeekEntryCount; ++i) { + seek_entry_id_[i] = 0; + seek_entry_pos_[i] = 0; + } +} + +SeekHead::~SeekHead() {} + +bool SeekHead::Finalize(IMkvWriter* writer) const { + if (writer->Seekable()) { + if (start_pos_ == -1) + return false; + + uint64_t payload_size = 0; + uint64_t entry_size[kSeekEntryCount]; + + for (int32_t i = 0; i < kSeekEntryCount; ++i) { + if (seek_entry_id_[i] != 0) { + entry_size[i] = EbmlElementSize(libwebm::kMkvSeekID, + static_cast(seek_entry_id_[i])); + entry_size[i] += EbmlElementSize( + libwebm::kMkvSeekPosition, static_cast(seek_entry_pos_[i])); + + payload_size += + EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) + + entry_size[i]; + } + } + + // No SeekHead elements + if (payload_size == 0) + return true; + + const int64_t pos = writer->Position(); + if (writer->Position(start_pos_)) + return false; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size)) + return false; + + for (int32_t i = 0; i < kSeekEntryCount; ++i) { + if (seek_entry_id_[i] != 0) { + if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i])) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvSeekID, + static_cast(seek_entry_id_[i]))) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition, + static_cast(seek_entry_pos_[i]))) + return false; + } + } + + const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize(); + const uint64_t total_size = + EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) + + total_entry_size; + const int64_t size_left = total_size - (writer->Position() - start_pos_); + + const uint64_t bytes_written = WriteVoidElement(writer, size_left); + if (!bytes_written) + return false; + + if (writer->Position(pos)) + return false; + } + + return true; +} + +bool SeekHead::Write(IMkvWriter* writer) { + const uint64_t entry_size = kSeekEntryCount * MaxEntrySize(); + const uint64_t size = + EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size); + + start_pos_ = writer->Position(); + + const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size); + if (!bytes_written) + return false; + + return true; +} + +bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) { + for (int32_t i = 0; i < kSeekEntryCount; ++i) { + if (seek_entry_id_[i] == 0) { + seek_entry_id_[i] = id; + seek_entry_pos_[i] = pos; + return true; + } + } + return false; +} + +uint32_t SeekHead::GetId(int index) const { + if (index < 0 || index >= kSeekEntryCount) + return UINT_MAX; + return seek_entry_id_[index]; +} + +uint64_t SeekHead::GetPosition(int index) const { + if (index < 0 || index >= kSeekEntryCount) + return ULLONG_MAX; + return seek_entry_pos_[index]; +} + +bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) { + if (index < 0 || index >= kSeekEntryCount) + return false; + seek_entry_id_[index] = id; + seek_entry_pos_[index] = position; + return true; +} + +uint64_t SeekHead::MaxEntrySize() const { + const uint64_t max_entry_payload_size = + EbmlElementSize(libwebm::kMkvSeekID, + static_cast(UINT64_C(0xffffffff))) + + EbmlElementSize(libwebm::kMkvSeekPosition, + static_cast(UINT64_C(0xffffffffffffffff))); + const uint64_t max_entry_size = + EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) + + max_entry_payload_size; + + return max_entry_size; +} + +/////////////////////////////////////////////////////////////// +// +// SegmentInfo Class + +SegmentInfo::SegmentInfo() + : duration_(-1.0), + muxing_app_(NULL), + timecode_scale_(1000000ULL), + writing_app_(NULL), + date_utc_(LLONG_MIN), + duration_pos_(-1) {} + +SegmentInfo::~SegmentInfo() { + delete[] muxing_app_; + delete[] writing_app_; +} + +bool SegmentInfo::Init() { + int32_t major; + int32_t minor; + int32_t build; + int32_t revision; + GetVersion(&major, &minor, &build, &revision); + char temp[256]; +#ifdef _MSC_VER + sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major, + minor, build, revision); +#else + snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major, + minor, build, revision); +#endif + + const size_t app_len = strlen(temp) + 1; + + delete[] muxing_app_; + + muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT + if (!muxing_app_) + return false; + +#ifdef _MSC_VER + strcpy_s(muxing_app_, app_len, temp); +#else + strcpy(muxing_app_, temp); +#endif + + set_writing_app(temp); + if (!writing_app_) + return false; + return true; +} + +bool SegmentInfo::Finalize(IMkvWriter* writer) const { + if (!writer) + return false; + + if (duration_ > 0.0) { + if (writer->Seekable()) { + if (duration_pos_ == -1) + return false; + + const int64_t pos = writer->Position(); + + if (writer->Position(duration_pos_)) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvDuration, + static_cast(duration_))) + return false; + + if (writer->Position(pos)) + return false; + } + } + + return true; +} + +bool SegmentInfo::Write(IMkvWriter* writer) { + if (!writer || !muxing_app_ || !writing_app_) + return false; + + uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, + static_cast(timecode_scale_)); + if (duration_ > 0.0) + size += + EbmlElementSize(libwebm::kMkvDuration, static_cast(duration_)); + if (date_utc_ != LLONG_MIN) + size += EbmlDateElementSize(libwebm::kMkvDateUTC); + size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_); + size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_); + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size)) + return false; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, + static_cast(timecode_scale_))) + return false; + + if (duration_ > 0.0) { + // Save for later + duration_pos_ = writer->Position(); + + if (!WriteEbmlElement(writer, libwebm::kMkvDuration, + static_cast(duration_))) + return false; + } + + if (date_utc_ != LLONG_MIN) + WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_); + + if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_)) + return false; + if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_)) + return false; + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(size)) + return false; + + return true; +} + +void SegmentInfo::set_muxing_app(const char* app) { + if (app) { + const size_t length = strlen(app) + 1; + char* temp_str = new (std::nothrow) char[length]; // NOLINT + if (!temp_str) + return; + +#ifdef _MSC_VER + strcpy_s(temp_str, length, app); +#else + strcpy(temp_str, app); +#endif + + delete[] muxing_app_; + muxing_app_ = temp_str; + } +} + +void SegmentInfo::set_writing_app(const char* app) { + if (app) { + const size_t length = strlen(app) + 1; + char* temp_str = new (std::nothrow) char[length]; // NOLINT + if (!temp_str) + return; + +#ifdef _MSC_VER + strcpy_s(temp_str, length, app); +#else + strcpy(temp_str, app); +#endif + + delete[] writing_app_; + writing_app_ = temp_str; + } +} + +/////////////////////////////////////////////////////////////// +// +// Segment Class + +Segment::Segment() + : chunk_count_(0), + chunk_name_(NULL), + chunk_writer_cluster_(NULL), + chunk_writer_cues_(NULL), + chunk_writer_header_(NULL), + chunking_(false), + chunking_base_name_(NULL), + cluster_list_(NULL), + cluster_list_capacity_(0), + cluster_list_size_(0), + cues_position_(kAfterClusters), + cues_track_(0), + force_new_cluster_(false), + frames_(NULL), + frames_capacity_(0), + frames_size_(0), + has_video_(false), + header_written_(false), + last_block_duration_(0), + last_timestamp_(0), + max_cluster_duration_(kDefaultMaxClusterDuration), + max_cluster_size_(0), + mode_(kFile), + new_cuepoint_(false), + output_cues_(true), + accurate_cluster_duration_(false), + fixed_size_cluster_timecode_(false), + estimate_file_duration_(false), + ebml_header_size_(0), + payload_pos_(0), + size_position_(0), + doc_type_version_(kDefaultDocTypeVersion), + doc_type_version_written_(0), + duration_(0.0), + writer_cluster_(NULL), + writer_cues_(NULL), + writer_header_(NULL) { + const time_t curr_time = time(NULL); + seed_ = static_cast(curr_time); +#ifdef _WIN32 + srand(seed_); +#endif +} + +Segment::~Segment() { + if (cluster_list_) { + for (int32_t i = 0; i < cluster_list_size_; ++i) { + Cluster* const cluster = cluster_list_[i]; + delete cluster; + } + delete[] cluster_list_; + } + + if (frames_) { + for (int32_t i = 0; i < frames_size_; ++i) { + Frame* const frame = frames_[i]; + delete frame; + } + delete[] frames_; + } + + delete[] chunk_name_; + delete[] chunking_base_name_; + + if (chunk_writer_cluster_) { + chunk_writer_cluster_->Close(); + delete chunk_writer_cluster_; + } + if (chunk_writer_cues_) { + chunk_writer_cues_->Close(); + delete chunk_writer_cues_; + } + if (chunk_writer_header_) { + chunk_writer_header_->Close(); + delete chunk_writer_header_; + } +} + +void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index, + uint64_t* cues_size) { + CuePoint* const cue_point = cues_.GetCueByIndex(index); + if (cue_point == NULL) + return; + const uint64_t old_cue_point_size = cue_point->Size(); + const uint64_t cluster_pos = cue_point->cluster_pos() + diff; + cue_point->set_cluster_pos(cluster_pos); // update the new cluster position + // New size of the cue is computed as follows + // Let a = current sum of size of all CuePoints + // Let b = Increase in Cue Point's size due to this iteration + // Let c = Increase in size of Cues Element's length due to this iteration + // (This is computed as CodedSize(a + b) - CodedSize(a)) + // Let d = b + c. Now d is the |diff| passed to the next recursive call. + // Let e = a + b. Now e is the |cues_size| passed to the next recursive + // call. + const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size; + const uint64_t cue_size_diff = + GetCodedUIntSize(*cues_size + cue_point_size_diff) - + GetCodedUIntSize(*cues_size); + *cues_size += cue_point_size_diff; + diff = cue_size_diff + cue_point_size_diff; + if (diff > 0) { + for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) { + MoveCuesBeforeClustersHelper(diff, i, cues_size); + } + } +} + +void Segment::MoveCuesBeforeClusters() { + const uint64_t current_cue_size = cues_.Size(); + uint64_t cue_size = 0; + for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) + cue_size += cues_.GetCueByIndex(i)->Size(); + for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) + MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size); + + // Adjust the Seek Entry to reflect the change in position + // of Cluster and Cues + int32_t cluster_index = 0; + int32_t cues_index = 0; + for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) { + if (seek_head_.GetId(i) == libwebm::kMkvCluster) + cluster_index = i; + if (seek_head_.GetId(i) == libwebm::kMkvCues) + cues_index = i; + } + seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues, + seek_head_.GetPosition(cluster_index)); + seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster, + cues_.Size() + seek_head_.GetPosition(cues_index)); +} + +bool Segment::Init(IMkvWriter* ptr_writer) { + if (!ptr_writer) { + return false; + } + writer_cluster_ = ptr_writer; + writer_cues_ = ptr_writer; + writer_header_ = ptr_writer; + memset(&track_frames_written_, 0, + sizeof(track_frames_written_[0]) * kMaxTrackNumber); + memset(&last_track_timestamp_, 0, + sizeof(last_track_timestamp_[0]) * kMaxTrackNumber); + return segment_info_.Init(); +} + +bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader, + IMkvWriter* writer) { + if (!writer->Seekable() || chunking_) + return false; + const int64_t cluster_offset = + cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster); + + // Copy the headers. + if (!ChunkedCopy(reader, writer, 0, cluster_offset)) + return false; + + // Recompute cue positions and seek entries. + MoveCuesBeforeClusters(); + + // Write cues and seek entries. + // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the + // second time with a different writer object. But the name Finalize() doesn't + // indicate something we want to call more than once. So consider renaming it + // to write() or some such. + if (!cues_.Write(writer) || !seek_head_.Finalize(writer)) + return false; + + // Copy the Clusters. + if (!ChunkedCopy(reader, writer, cluster_offset, + cluster_end_offset_ - cluster_offset)) + return false; + + // Update the Segment size in case the Cues size has changed. + const int64_t pos = writer->Position(); + const int64_t segment_size = writer->Position() - payload_pos_; + if (writer->Position(size_position_) || + WriteUIntSize(writer, segment_size, 8) || writer->Position(pos)) + return false; + return true; +} + +bool Segment::Finalize() { + if (WriteFramesAll() < 0) + return false; + + // In kLive mode, call Cluster::Finalize only if |accurate_cluster_duration_| + // is set. In all other modes, always call Cluster::Finalize. + if ((mode_ == kLive ? accurate_cluster_duration_ : true) && + cluster_list_size_ > 0) { + // Update last cluster's size + Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; + + // For the last frame of the last Cluster, we don't write it as a BlockGroup + // with Duration unless the frame itself has duration set explicitly. + if (!old_cluster || !old_cluster->Finalize(false, 0)) + return false; + } + + if (mode_ == kFile) { + if (chunking_ && chunk_writer_cluster_) { + chunk_writer_cluster_->Close(); + chunk_count_++; + } + + double duration = + (static_cast(last_timestamp_) + last_block_duration_) / + segment_info_.timecode_scale(); + if (duration_ > 0.0) { + duration = duration_; + } else { + if (last_block_duration_ == 0 && estimate_file_duration_) { + const int num_tracks = static_cast(tracks_.track_entries_size()); + for (int i = 0; i < num_tracks; ++i) { + if (track_frames_written_[i] < 2) + continue; + + // Estimate the duration for the last block of a Track. + const double nano_per_frame = + static_cast(last_track_timestamp_[i]) / + (track_frames_written_[i] - 1); + const double track_duration = + (last_track_timestamp_[i] + nano_per_frame) / + segment_info_.timecode_scale(); + if (track_duration > duration) + duration = track_duration; + } + } + } + segment_info_.set_duration(duration); + if (!segment_info_.Finalize(writer_header_)) + return false; + + if (output_cues_) + if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset())) + return false; + + if (chunking_) { + if (!chunk_writer_cues_) + return false; + + char* name = NULL; + if (!UpdateChunkName("cues", &name)) + return false; + + const bool cues_open = chunk_writer_cues_->Open(name); + delete[] name; + if (!cues_open) + return false; + } + + cluster_end_offset_ = writer_cluster_->Position(); + + // Write the seek headers and cues + if (output_cues_) + if (!cues_.Write(writer_cues_)) + return false; + + if (!seek_head_.Finalize(writer_header_)) + return false; + + if (writer_header_->Seekable()) { + if (size_position_ == -1) + return false; + + const int64_t segment_size = MaxOffset(); + if (segment_size < 1) + return false; + + const int64_t pos = writer_header_->Position(); + UpdateDocTypeVersion(); + if (doc_type_version_ != doc_type_version_written_) { + if (writer_header_->Position(0)) + return false; + + const char* const doc_type = + DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska; + if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type)) + return false; + if (writer_header_->Position() != ebml_header_size_) + return false; + + doc_type_version_written_ = doc_type_version_; + } + + if (writer_header_->Position(size_position_)) + return false; + + if (WriteUIntSize(writer_header_, segment_size, 8)) + return false; + + if (writer_header_->Position(pos)) + return false; + } + + if (chunking_) { + // Do not close any writers until the segment size has been written, + // otherwise the size may be off. + if (!chunk_writer_cues_ || !chunk_writer_header_) + return false; + + chunk_writer_cues_->Close(); + chunk_writer_header_->Close(); + } + } + + return true; +} + +Track* Segment::AddTrack(int32_t number) { + Track* const track = new (std::nothrow) Track(&seed_); // NOLINT + + if (!track) + return NULL; + + if (!tracks_.AddTrack(track, number)) { + delete track; + return NULL; + } + + return track; +} + +Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); } + +Tag* Segment::AddTag() { return tags_.AddTag(); } + +uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) { + VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT + if (!track) + return 0; + + track->set_type(Tracks::kVideo); + track->set_codec_id(Tracks::kVp8CodecId); + track->set_width(width); + track->set_height(height); + + if (!tracks_.AddTrack(track, number)) { + delete track; + return 0; + } + has_video_ = true; + + return track->number(); +} + +bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) { + if (cluster_list_size_ < 1) + return false; + + const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; + if (!cluster) + return false; + + CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT + if (!cue) + return false; + + cue->set_time(timestamp / segment_info_.timecode_scale()); + cue->set_block_number(cluster->blocks_added()); + cue->set_cluster_pos(cluster->position_for_cues()); + cue->set_track(track); + if (!cues_.AddCue(cue)) { + delete cue; + return false; + } + + new_cuepoint_ = false; + return true; +} + +uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels, + int32_t number) { + AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT + if (!track) + return 0; + + track->set_type(Tracks::kAudio); + track->set_codec_id(Tracks::kVorbisCodecId); + track->set_sample_rate(sample_rate); + track->set_channels(channels); + + if (!tracks_.AddTrack(track, number)) { + delete track; + return 0; + } + + return track->number(); +} + +bool Segment::AddFrame(const uint8_t* data, uint64_t length, + uint64_t track_number, uint64_t timestamp, bool is_key) { + if (!data) + return false; + + Frame frame; + if (!frame.Init(data, length)) + return false; + frame.set_track_number(track_number); + frame.set_timestamp(timestamp); + frame.set_is_key(is_key); + return AddGenericFrame(&frame); +} + +bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length, + const uint8_t* additional, + uint64_t additional_length, + uint64_t add_id, uint64_t track_number, + uint64_t timestamp, bool is_key) { + if (!data || !additional) + return false; + + Frame frame; + if (!frame.Init(data, length) || + !frame.AddAdditionalData(additional, additional_length, add_id)) { + return false; + } + frame.set_track_number(track_number); + frame.set_timestamp(timestamp); + frame.set_is_key(is_key); + return AddGenericFrame(&frame); +} + +bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, + int64_t discard_padding, + uint64_t track_number, + uint64_t timestamp, bool is_key) { + if (!data) + return false; + + Frame frame; + if (!frame.Init(data, length)) + return false; + frame.set_discard_padding(discard_padding); + frame.set_track_number(track_number); + frame.set_timestamp(timestamp); + frame.set_is_key(is_key); + return AddGenericFrame(&frame); +} + +bool Segment::AddMetadata(const uint8_t* data, uint64_t length, + uint64_t track_number, uint64_t timestamp_ns, + uint64_t duration_ns) { + if (!data) + return false; + + Frame frame; + if (!frame.Init(data, length)) + return false; + frame.set_track_number(track_number); + frame.set_timestamp(timestamp_ns); + frame.set_duration(duration_ns); + frame.set_is_key(true); // All metadata blocks are keyframes. + return AddGenericFrame(&frame); +} + +bool Segment::AddGenericFrame(const Frame* frame) { + if (!frame) + return false; + + if (!CheckHeaderInfo()) + return false; + + // Check for non-monotonically increasing timestamps. + if (frame->timestamp() < last_timestamp_) + return false; + + // Check if the track number is valid. + if (!tracks_.GetTrackByNumber(frame->track_number())) + return false; + + if (frame->discard_padding() != 0) + doc_type_version_ = 4; + + if (cluster_list_size_ > 0) { + const uint64_t timecode_scale = segment_info_.timecode_scale(); + const uint64_t frame_timecode = frame->timestamp() / timecode_scale; + + const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1]; + const uint64_t last_cluster_timecode = last_cluster->timecode(); + + const uint64_t rel_timecode = frame_timecode - last_cluster_timecode; + if (rel_timecode > kMaxBlockTimecode) { + force_new_cluster_ = true; + } + } + + // If the segment has a video track hold onto audio frames to make sure the + // audio that is associated with the start time of a video key-frame is + // muxed into the same cluster. + if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) && + !force_new_cluster_) { + Frame* const new_frame = new (std::nothrow) Frame(); + if (!new_frame || !new_frame->CopyFrom(*frame)) { + delete new_frame; + return false; + } + if (!QueueFrame(new_frame)) { + delete new_frame; + return false; + } + track_frames_written_[frame->track_number() - 1]++; + return true; + } + + if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(), + frame->is_key())) { + return false; + } + + if (cluster_list_size_ < 1) + return false; + + Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; + if (!cluster) + return false; + + // If the Frame is not a SimpleBlock, then set the reference_block_timestamp + // if it is not set already. + bool frame_created = false; + if (!frame->CanBeSimpleBlock() && !frame->is_key() && + !frame->reference_block_timestamp_set()) { + Frame* const new_frame = new (std::nothrow) Frame(); + if (!new_frame || !new_frame->CopyFrom(*frame)) { + delete new_frame; + return false; + } + new_frame->set_reference_block_timestamp( + last_track_timestamp_[frame->track_number() - 1]); + frame = new_frame; + frame_created = true; + } + + if (!cluster->AddFrame(frame)) + return false; + + if (new_cuepoint_ && cues_track_ == frame->track_number()) { + if (!AddCuePoint(frame->timestamp(), cues_track_)) + return false; + } + + last_timestamp_ = frame->timestamp(); + last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); + last_block_duration_ = frame->duration(); + track_frames_written_[frame->track_number() - 1]++; + + if (frame_created) + delete frame; + return true; +} + +void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; } + +void Segment::AccurateClusterDuration(bool accurate_cluster_duration) { + accurate_cluster_duration_ = accurate_cluster_duration; +} + +void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) { + fixed_size_cluster_timecode_ = fixed_size_cluster_timecode; +} + +bool Segment::SetChunking(bool chunking, const char* filename) { + if (chunk_count_ > 0) + return false; + + if (chunking) { + if (!filename) + return false; + + // Check if we are being set to what is already set. + if (chunking_ && !strcmp(filename, chunking_base_name_)) + return true; + + const size_t name_length = strlen(filename) + 1; + char* const temp = new (std::nothrow) char[name_length]; // NOLINT + if (!temp) + return false; + +#ifdef _MSC_VER + strcpy_s(temp, name_length, filename); +#else + strcpy(temp, filename); +#endif + + delete[] chunking_base_name_; + chunking_base_name_ = temp; + + if (!UpdateChunkName("chk", &chunk_name_)) + return false; + + if (!chunk_writer_cluster_) { + chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT + if (!chunk_writer_cluster_) + return false; + } + + if (!chunk_writer_cues_) { + chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT + if (!chunk_writer_cues_) + return false; + } + + if (!chunk_writer_header_) { + chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT + if (!chunk_writer_header_) + return false; + } + + if (!chunk_writer_cluster_->Open(chunk_name_)) + return false; + + const size_t header_length = strlen(filename) + strlen(".hdr") + 1; + char* const header = new (std::nothrow) char[header_length]; // NOLINT + if (!header) + return false; + +#ifdef _MSC_VER + strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_); + strcat_s(header, header_length, ".hdr"); +#else + strcpy(header, chunking_base_name_); + strcat(header, ".hdr"); +#endif + if (!chunk_writer_header_->Open(header)) { + delete[] header; + return false; + } + + writer_cluster_ = chunk_writer_cluster_; + writer_cues_ = chunk_writer_cues_; + writer_header_ = chunk_writer_header_; + + delete[] header; + } + + chunking_ = chunking; + + return true; +} + +bool Segment::CuesTrack(uint64_t track_number) { + const Track* const track = GetTrackByNumber(track_number); + if (!track) + return false; + + cues_track_ = track_number; + return true; +} + +void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; } + +Track* Segment::GetTrackByNumber(uint64_t track_number) const { + return tracks_.GetTrackByNumber(track_number); +} + +bool Segment::WriteSegmentHeader() { + UpdateDocTypeVersion(); + + const char* const doc_type = + DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska; + if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type)) + return false; + doc_type_version_written_ = doc_type_version_; + ebml_header_size_ = static_cast(writer_header_->Position()); + + // Write "unknown" (-1) as segment size value. If mode is kFile, Segment + // will write over duration when the file is finalized. + if (WriteID(writer_header_, libwebm::kMkvSegment)) + return false; + + // Save for later. + size_position_ = writer_header_->Position(); + + // Write "unknown" (EBML coded -1) as segment size value. We need to write 8 + // bytes because if we are going to overwrite the segment size later we do + // not know how big our segment will be. + if (SerializeInt(writer_header_, kEbmlUnknownValue, 8)) + return false; + + payload_pos_ = writer_header_->Position(); + + if (mode_ == kFile && writer_header_->Seekable()) { + // Set the duration > 0.0 so SegmentInfo will write out the duration. When + // the muxer is done writing we will set the correct duration and have + // SegmentInfo upadte it. + segment_info_.set_duration(1.0); + + if (!seek_head_.Write(writer_header_)) + return false; + } + + if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset())) + return false; + if (!segment_info_.Write(writer_header_)) + return false; + + if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset())) + return false; + if (!tracks_.Write(writer_header_)) + return false; + + if (chapters_.Count() > 0) { + if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset())) + return false; + if (!chapters_.Write(writer_header_)) + return false; + } + + if (tags_.Count() > 0) { + if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset())) + return false; + if (!tags_.Write(writer_header_)) + return false; + } + + if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) { + if (!chunk_writer_header_) + return false; + + chunk_writer_header_->Close(); + } + + header_written_ = true; + + return true; +} + +// Here we are testing whether to create a new cluster, given a frame +// having time frame_timestamp_ns. +// +int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns, + bool is_key) const { + if (force_new_cluster_) + return 1; + + // If no clusters have been created yet, then create a new cluster + // and write this frame immediately, in the new cluster. This path + // should only be followed once, the first time we attempt to write + // a frame. + + if (cluster_list_size_ <= 0) + return 1; + + // There exists at least one cluster. We must compare the frame to + // the last cluster, in order to determine whether the frame is + // written to the existing cluster, or that a new cluster should be + // created. + + const uint64_t timecode_scale = segment_info_.timecode_scale(); + const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale; + + const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1]; + const uint64_t last_cluster_timecode = last_cluster->timecode(); + + // For completeness we test for the case when the frame's timecode + // is less than the cluster's timecode. Although in principle that + // is allowed, this muxer doesn't actually write clusters like that, + // so this indicates a bug somewhere in our algorithm. + + if (frame_timecode < last_cluster_timecode) // should never happen + return -1; + + // If the frame has a timestamp significantly larger than the last + // cluster (in Matroska, cluster-relative timestamps are serialized + // using a 16-bit signed integer), then we cannot write this frame + // to that cluster, and so we must create a new cluster. + + const int64_t delta_timecode = frame_timecode - last_cluster_timecode; + + if (delta_timecode > kMaxBlockTimecode) + return 2; + + // We decide to create a new cluster when we have a video keyframe. + // This will flush queued (audio) frames, and write the keyframe + // immediately, in the newly-created cluster. + + if (is_key && tracks_.TrackIsVideo(track_number)) + return 1; + + // Create a new cluster if we have accumulated too many frames + // already, where "too many" is defined as "the total time of frames + // in the cluster exceeds a threshold". + + const uint64_t delta_ns = delta_timecode * timecode_scale; + + if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_) + return 1; + + // This is similar to the case above, with the difference that a new + // cluster is created when the size of the current cluster exceeds a + // threshold. + + const uint64_t cluster_size = last_cluster->payload_size(); + + if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_) + return 1; + + // There's no need to create a new cluster, so emit this frame now. + + return 0; +} + +bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) { + const int32_t new_size = cluster_list_size_ + 1; + + if (new_size > cluster_list_capacity_) { + // Add more clusters. + const int32_t new_capacity = + (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2; + Cluster** const clusters = + new (std::nothrow) Cluster*[new_capacity]; // NOLINT + if (!clusters) + return false; + + for (int32_t i = 0; i < cluster_list_size_; ++i) { + clusters[i] = cluster_list_[i]; + } + + delete[] cluster_list_; + + cluster_list_ = clusters; + cluster_list_capacity_ = new_capacity; + } + + if (!WriteFramesLessThan(frame_timestamp_ns)) + return false; + + if (cluster_list_size_ > 0) { + // Update old cluster's size + Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; + + if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns)) + return false; + } + + if (output_cues_) + new_cuepoint_ = true; + + if (chunking_ && cluster_list_size_ > 0) { + chunk_writer_cluster_->Close(); + chunk_count_++; + + if (!UpdateChunkName("chk", &chunk_name_)) + return false; + if (!chunk_writer_cluster_->Open(chunk_name_)) + return false; + } + + const uint64_t timecode_scale = segment_info_.timecode_scale(); + const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale; + + uint64_t cluster_timecode = frame_timecode; + + if (frames_size_ > 0) { + const Frame* const f = frames_[0]; // earliest queued frame + const uint64_t ns = f->timestamp(); + const uint64_t tc = ns / timecode_scale; + + if (tc < cluster_timecode) + cluster_timecode = tc; + } + + Cluster*& cluster = cluster_list_[cluster_list_size_]; + const int64_t offset = MaxOffset(); + cluster = new (std::nothrow) + Cluster(cluster_timecode, offset, segment_info_.timecode_scale(), + accurate_cluster_duration_, fixed_size_cluster_timecode_); + if (!cluster) + return false; + + if (!cluster->Init(writer_cluster_)) + return false; + + cluster_list_size_ = new_size; + return true; +} + +bool Segment::DoNewClusterProcessing(uint64_t track_number, + uint64_t frame_timestamp_ns, bool is_key) { + for (;;) { + // Based on the characteristics of the current frame and current + // cluster, decide whether to create a new cluster. + const int result = TestFrame(track_number, frame_timestamp_ns, is_key); + if (result < 0) // error + return false; + + // Always set force_new_cluster_ to false after TestFrame. + force_new_cluster_ = false; + + // A non-zero result means create a new cluster. + if (result > 0 && !MakeNewCluster(frame_timestamp_ns)) + return false; + + // Write queued (audio) frames. + const int frame_count = WriteFramesAll(); + if (frame_count < 0) // error + return false; + + // Write the current frame to the current cluster (if TestFrame + // returns 0) or to a newly created cluster (TestFrame returns 1). + if (result <= 1) + return true; + + // TestFrame returned 2, which means there was a large time + // difference between the cluster and the frame itself. Do the + // test again, comparing the frame to the new cluster. + } +} + +bool Segment::CheckHeaderInfo() { + if (!header_written_) { + if (!WriteSegmentHeader()) + return false; + + if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset())) + return false; + + if (output_cues_ && cues_track_ == 0) { + // Check for a video track + for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) { + const Track* const track = tracks_.GetTrackByIndex(i); + if (!track) + return false; + + if (tracks_.TrackIsVideo(track->number())) { + cues_track_ = track->number(); + break; + } + } + + // Set first track found + if (cues_track_ == 0) { + const Track* const track = tracks_.GetTrackByIndex(0); + if (!track) + return false; + + cues_track_ = track->number(); + } + } + } + return true; +} + +void Segment::UpdateDocTypeVersion() { + for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) { + const Track* track = tracks_.GetTrackByIndex(index); + if (track == NULL) + break; + if ((track->codec_delay() || track->seek_pre_roll()) && + doc_type_version_ < 4) { + doc_type_version_ = 4; + break; + } + } +} + +bool Segment::UpdateChunkName(const char* ext, char** name) const { + if (!name || !ext) + return false; + + char ext_chk[64]; +#ifdef _MSC_VER + sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); +#else + snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); +#endif + + const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1; + char* const str = new (std::nothrow) char[length]; // NOLINT + if (!str) + return false; + +#ifdef _MSC_VER + strcpy_s(str, length - strlen(ext_chk), chunking_base_name_); + strcat_s(str, length, ext_chk); +#else + strcpy(str, chunking_base_name_); + strcat(str, ext_chk); +#endif + + delete[] * name; + *name = str; + + return true; +} + +int64_t Segment::MaxOffset() { + if (!writer_header_) + return -1; + + int64_t offset = writer_header_->Position() - payload_pos_; + + if (chunking_) { + for (int32_t i = 0; i < cluster_list_size_; ++i) { + Cluster* const cluster = cluster_list_[i]; + offset += cluster->Size(); + } + + if (writer_cues_) + offset += writer_cues_->Position(); + } + + return offset; +} + +bool Segment::QueueFrame(Frame* frame) { + const int32_t new_size = frames_size_ + 1; + + if (new_size > frames_capacity_) { + // Add more frames. + const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2; + + if (new_capacity < 1) + return false; + + Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT + if (!frames) + return false; + + for (int32_t i = 0; i < frames_size_; ++i) { + frames[i] = frames_[i]; + } + + delete[] frames_; + frames_ = frames; + frames_capacity_ = new_capacity; + } + + frames_[frames_size_++] = frame; + + return true; +} + +int Segment::WriteFramesAll() { + if (frames_ == NULL) + return 0; + + if (cluster_list_size_ < 1) + return -1; + + Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; + + if (!cluster) + return -1; + + for (int32_t i = 0; i < frames_size_; ++i) { + Frame*& frame = frames_[i]; + // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the + // places where |doc_type_version_| needs to be updated. + if (frame->discard_padding() != 0) + doc_type_version_ = 4; + if (!cluster->AddFrame(frame)) { + delete frame; + continue; + } + + if (new_cuepoint_ && cues_track_ == frame->track_number()) { + if (!AddCuePoint(frame->timestamp(), cues_track_)) { + delete frame; + continue; + } + } + + if (frame->timestamp() > last_timestamp_) { + last_timestamp_ = frame->timestamp(); + last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); + } + + delete frame; + frame = NULL; + } + + const int result = frames_size_; + frames_size_ = 0; + + return result; +} + +bool Segment::WriteFramesLessThan(uint64_t timestamp) { + // Check |cluster_list_size_| to see if this is the first cluster. If it is + // the first cluster the audio frames that are less than the first video + // timesatmp will be written in a later step. + if (frames_size_ > 0 && cluster_list_size_ > 0) { + if (!frames_) + return false; + + Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; + if (!cluster) + return false; + + int32_t shift_left = 0; + + // TODO(fgalligan): Change this to use the durations of frames instead of + // the next frame's start time if the duration is accurate. + for (int32_t i = 1; i < frames_size_; ++i) { + const Frame* const frame_curr = frames_[i]; + + if (frame_curr->timestamp() > timestamp) + break; + + const Frame* const frame_prev = frames_[i - 1]; + if (frame_prev->discard_padding() != 0) + doc_type_version_ = 4; + if (!cluster->AddFrame(frame_prev)) { + delete frame_prev; + continue; + } + + if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) { + if (!AddCuePoint(frame_prev->timestamp(), cues_track_)) { + delete frame_prev; + continue; + } + } + + ++shift_left; + if (frame_prev->timestamp() > last_timestamp_) { + last_timestamp_ = frame_prev->timestamp(); + last_track_timestamp_[frame_prev->track_number() - 1] = + frame_prev->timestamp(); + } + + delete frame_prev; + } + + if (shift_left > 0) { + if (shift_left >= frames_size_) + return false; + + const int32_t new_frames_size = frames_size_ - shift_left; + for (int32_t i = 0; i < new_frames_size; ++i) { + frames_[i] = frames_[i + shift_left]; + } + + frames_size_ = new_frames_size; + } + } + + return true; +} + +bool Segment::DocTypeIsWebm() const { + const int kNumCodecIds = 9; + + // TODO(vigneshv): Tweak .clang-format. + const char* kWebmCodecIds[kNumCodecIds] = { + Tracks::kOpusCodecId, Tracks::kVorbisCodecId, + Tracks::kAv1CodecId, Tracks::kVp8CodecId, + Tracks::kVp9CodecId, Tracks::kWebVttCaptionsId, + Tracks::kWebVttDescriptionsId, Tracks::kWebVttMetadataId, + Tracks::kWebVttSubtitlesId}; + + const int num_tracks = static_cast(tracks_.track_entries_size()); + for (int track_index = 0; track_index < num_tracks; ++track_index) { + const Track* const track = tracks_.GetTrackByIndex(track_index); + const std::string codec_id = track->codec_id(); + + bool id_is_webm = false; + for (int id_index = 0; id_index < kNumCodecIds; ++id_index) { + if (codec_id == kWebmCodecIds[id_index]) { + id_is_webm = true; + break; + } + } + + if (!id_is_webm) + return false; + } + + return true; +} + +} // namespace mkvmuxer diff --git a/mkvmuxer/mkvmuxer.h b/mkvmuxer/mkvmuxer.h new file mode 100644 index 000000000000..f2db37714539 --- /dev/null +++ b/mkvmuxer/mkvmuxer.h @@ -0,0 +1,1924 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef MKVMUXER_MKVMUXER_H_ +#define MKVMUXER_MKVMUXER_H_ + +#include + +#include +#include +#include + +#include "common/webmids.h" +#include "mkvmuxer/mkvmuxertypes.h" + +// For a description of the WebM elements see +// http://www.webmproject.org/code/specs/container/. + +namespace mkvparser { +class IMkvReader; +} // namespace mkvparser + +namespace mkvmuxer { + +class MkvWriter; +class Segment; + +const uint64_t kMaxTrackNumber = 126; + +/////////////////////////////////////////////////////////////// +// Interface used by the mkvmuxer to write out the Mkv data. +class IMkvWriter { + public: + // Writes out |len| bytes of |buf|. Returns 0 on success. + virtual int32 Write(const void* buf, uint32 len) = 0; + + // Returns the offset of the output position from the beginning of the + // output. + virtual int64 Position() const = 0; + + // Set the current File position. Returns 0 on success. + virtual int32 Position(int64 position) = 0; + + // Returns true if the writer is seekable. + virtual bool Seekable() const = 0; + + // Element start notification. Called whenever an element identifier is about + // to be written to the stream. |element_id| is the element identifier, and + // |position| is the location in the WebM stream where the first octet of the + // element identifier will be written. + // Note: the |MkvId| enumeration in webmids.hpp defines element values. + virtual void ElementStartNotify(uint64 element_id, int64 position) = 0; + + protected: + IMkvWriter(); + virtual ~IMkvWriter(); + + private: + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(IMkvWriter); +}; + +// Writes out the EBML header for a WebM file, but allows caller to specify +// DocType. This function must be called before any other libwebm writing +// functions are called. +bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version, + const char* const doc_type); + +// Writes out the EBML header for a WebM file. This function must be called +// before any other libwebm writing functions are called. +bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version); + +// Deprecated. Writes out EBML header with doc_type_version as +// kDefaultDocTypeVersion. Exists for backward compatibility. +bool WriteEbmlHeader(IMkvWriter* writer); + +// Copies in Chunk from source to destination between the given byte positions +bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst, int64_t start, + int64_t size); + +/////////////////////////////////////////////////////////////// +// Class to hold data the will be written to a block. +class Frame { + public: + Frame(); + ~Frame(); + + // Sets this frame's contents based on |frame|. Returns true on success. On + // failure, this frame's existing contents may be lost. + bool CopyFrom(const Frame& frame); + + // Copies |frame| data into |frame_|. Returns true on success. + bool Init(const uint8_t* frame, uint64_t length); + + // Copies |additional| data into |additional_|. Returns true on success. + bool AddAdditionalData(const uint8_t* additional, uint64_t length, + uint64_t add_id); + + // Returns true if the frame has valid parameters. + bool IsValid() const; + + // Returns true if the frame can be written as a SimpleBlock based on current + // parameters. + bool CanBeSimpleBlock() const; + + uint64_t add_id() const { return add_id_; } + const uint8_t* additional() const { return additional_; } + uint64_t additional_length() const { return additional_length_; } + void set_duration(uint64_t duration); + uint64_t duration() const { return duration_; } + bool duration_set() const { return duration_set_; } + const uint8_t* frame() const { return frame_; } + void set_is_key(bool key) { is_key_ = key; } + bool is_key() const { return is_key_; } + uint64_t length() const { return length_; } + void set_track_number(uint64_t track_number) { track_number_ = track_number; } + uint64_t track_number() const { return track_number_; } + void set_timestamp(uint64_t timestamp) { timestamp_ = timestamp; } + uint64_t timestamp() const { return timestamp_; } + void set_discard_padding(int64_t discard_padding) { + discard_padding_ = discard_padding; + } + int64_t discard_padding() const { return discard_padding_; } + void set_reference_block_timestamp(int64_t reference_block_timestamp); + int64_t reference_block_timestamp() const { + return reference_block_timestamp_; + } + bool reference_block_timestamp_set() const { + return reference_block_timestamp_set_; + } + + private: + // Id of the Additional data. + uint64_t add_id_; + + // Pointer to additional data. Owned by this class. + uint8_t* additional_; + + // Length of the additional data. + uint64_t additional_length_; + + // Duration of the frame in nanoseconds. + uint64_t duration_; + + // Flag indicating that |duration_| has been set. Setting duration causes the + // frame to be written out as a Block with BlockDuration instead of as a + // SimpleBlock. + bool duration_set_; + + // Pointer to the data. Owned by this class. + uint8_t* frame_; + + // Flag telling if the data should set the key flag of a block. + bool is_key_; + + // Length of the data. + uint64_t length_; + + // Mkv track number the data is associated with. + uint64_t track_number_; + + // Timestamp of the data in nanoseconds. + uint64_t timestamp_; + + // Discard padding for the frame. + int64_t discard_padding_; + + // Reference block timestamp. + int64_t reference_block_timestamp_; + + // Flag indicating if |reference_block_timestamp_| has been set. + bool reference_block_timestamp_set_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Frame); +}; + +/////////////////////////////////////////////////////////////// +// Class to hold one cue point in a Cues element. +class CuePoint { + public: + CuePoint(); + ~CuePoint(); + + // Returns the size in bytes for the entire CuePoint element. + uint64_t Size() const; + + // Output the CuePoint element to the writer. Returns true on success. + bool Write(IMkvWriter* writer) const; + + void set_time(uint64_t time) { time_ = time; } + uint64_t time() const { return time_; } + void set_track(uint64_t track) { track_ = track; } + uint64_t track() const { return track_; } + void set_cluster_pos(uint64_t cluster_pos) { cluster_pos_ = cluster_pos; } + uint64_t cluster_pos() const { return cluster_pos_; } + void set_block_number(uint64_t block_number) { block_number_ = block_number; } + uint64_t block_number() const { return block_number_; } + void set_output_block_number(bool output_block_number) { + output_block_number_ = output_block_number; + } + bool output_block_number() const { return output_block_number_; } + + private: + // Returns the size in bytes for the payload of the CuePoint element. + uint64_t PayloadSize() const; + + // Absolute timecode according to the segment time base. + uint64_t time_; + + // The Track element associated with the CuePoint. + uint64_t track_; + + // The position of the Cluster containing the Block. + uint64_t cluster_pos_; + + // Number of the Block within the Cluster, starting from 1. + uint64_t block_number_; + + // If true the muxer will write out the block number for the cue if the + // block number is different than the default of 1. Default is set to true. + bool output_block_number_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(CuePoint); +}; + +/////////////////////////////////////////////////////////////// +// Cues element. +class Cues { + public: + Cues(); + ~Cues(); + + // Adds a cue point to the Cues element. Returns true on success. + bool AddCue(CuePoint* cue); + + // Returns the cue point by index. Returns NULL if there is no cue point + // match. + CuePoint* GetCueByIndex(int32_t index) const; + + // Returns the total size of the Cues element + uint64_t Size(); + + // Output the Cues element to the writer. Returns true on success. + bool Write(IMkvWriter* writer) const; + + int32_t cue_entries_size() const { return cue_entries_size_; } + void set_output_block_number(bool output_block_number) { + output_block_number_ = output_block_number; + } + bool output_block_number() const { return output_block_number_; } + + private: + // Number of allocated elements in |cue_entries_|. + int32_t cue_entries_capacity_; + + // Number of CuePoints in |cue_entries_|. + int32_t cue_entries_size_; + + // CuePoint list. + CuePoint** cue_entries_; + + // If true the muxer will write out the block number for the cue if the + // block number is different than the default of 1. Default is set to true. + bool output_block_number_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cues); +}; + +/////////////////////////////////////////////////////////////// +// ContentEncAESSettings element +class ContentEncAESSettings { + public: + enum { kCTR = 1 }; + + ContentEncAESSettings(); + ~ContentEncAESSettings() {} + + // Returns the size in bytes for the ContentEncAESSettings element. + uint64_t Size() const; + + // Writes out the ContentEncAESSettings element to |writer|. Returns true on + // success. + bool Write(IMkvWriter* writer) const; + + uint64_t cipher_mode() const { return cipher_mode_; } + + private: + // Returns the size in bytes for the payload of the ContentEncAESSettings + // element. + uint64_t PayloadSize() const; + + // Sub elements + uint64_t cipher_mode_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncAESSettings); +}; + +/////////////////////////////////////////////////////////////// +// ContentEncoding element +// Elements used to describe if the track data has been encrypted or +// compressed with zlib or header stripping. +// Currently only whole frames can be encrypted with AES. This dictates that +// ContentEncodingOrder will be 0, ContentEncodingScope will be 1, +// ContentEncodingType will be 1, and ContentEncAlgo will be 5. +class ContentEncoding { + public: + ContentEncoding(); + ~ContentEncoding(); + + // Sets the content encryption id. Copies |length| bytes from |id| to + // |enc_key_id_|. Returns true on success. + bool SetEncryptionID(const uint8_t* id, uint64_t length); + + // Returns the size in bytes for the ContentEncoding element. + uint64_t Size() const; + + // Writes out the ContentEncoding element to |writer|. Returns true on + // success. + bool Write(IMkvWriter* writer) const; + + uint64_t enc_algo() const { return enc_algo_; } + uint64_t encoding_order() const { return encoding_order_; } + uint64_t encoding_scope() const { return encoding_scope_; } + uint64_t encoding_type() const { return encoding_type_; } + ContentEncAESSettings* enc_aes_settings() { return &enc_aes_settings_; } + + private: + // Returns the size in bytes for the encoding elements. + uint64_t EncodingSize(uint64_t compresion_size, + uint64_t encryption_size) const; + + // Returns the size in bytes for the encryption elements. + uint64_t EncryptionSize() const; + + // Track element names + uint64_t enc_algo_; + uint8_t* enc_key_id_; + uint64_t encoding_order_; + uint64_t encoding_scope_; + uint64_t encoding_type_; + + // ContentEncAESSettings element. + ContentEncAESSettings enc_aes_settings_; + + // Size of the ContentEncKeyID data in bytes. + uint64_t enc_key_id_length_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); +}; + +/////////////////////////////////////////////////////////////// +// Colour element. +class PrimaryChromaticity { + public: + static const float kChromaticityMin; + static const float kChromaticityMax; + + PrimaryChromaticity(float x_val, float y_val) : x_(x_val), y_(y_val) {} + PrimaryChromaticity() : x_(0), y_(0) {} + ~PrimaryChromaticity() {} + + // Returns sum of |x_id| and |y_id| element id sizes and payload sizes. + uint64_t PrimaryChromaticitySize(libwebm::MkvId x_id, + libwebm::MkvId y_id) const; + bool Valid() const; + bool Write(IMkvWriter* writer, libwebm::MkvId x_id, + libwebm::MkvId y_id) const; + + float x() const { return x_; } + void set_x(float new_x) { x_ = new_x; } + float y() const { return y_; } + void set_y(float new_y) { y_ = new_y; } + + private: + float x_; + float y_; +}; + +class MasteringMetadata { + public: + static const float kValueNotPresent; + static const float kMinLuminance; + static const float kMinLuminanceMax; + static const float kMaxLuminanceMax; + + MasteringMetadata() + : luminance_max_(kValueNotPresent), + luminance_min_(kValueNotPresent), + r_(NULL), + g_(NULL), + b_(NULL), + white_point_(NULL) {} + ~MasteringMetadata() { + delete r_; + delete g_; + delete b_; + delete white_point_; + } + + // Returns total size of the MasteringMetadata element. + uint64_t MasteringMetadataSize() const; + bool Valid() const; + bool Write(IMkvWriter* writer) const; + + // Copies non-null chromaticity. + bool SetChromaticity(const PrimaryChromaticity* r, + const PrimaryChromaticity* g, + const PrimaryChromaticity* b, + const PrimaryChromaticity* white_point); + const PrimaryChromaticity* r() const { return r_; } + const PrimaryChromaticity* g() const { return g_; } + const PrimaryChromaticity* b() const { return b_; } + const PrimaryChromaticity* white_point() const { return white_point_; } + + float luminance_max() const { return luminance_max_; } + void set_luminance_max(float luminance_max) { + luminance_max_ = luminance_max; + } + float luminance_min() const { return luminance_min_; } + void set_luminance_min(float luminance_min) { + luminance_min_ = luminance_min; + } + + private: + // Returns size of MasteringMetadata child elements. + uint64_t PayloadSize() const; + + float luminance_max_; + float luminance_min_; + PrimaryChromaticity* r_; + PrimaryChromaticity* g_; + PrimaryChromaticity* b_; + PrimaryChromaticity* white_point_; +}; + +class Colour { + public: + enum MatrixCoefficients { + kGbr = 0, + kBt709 = 1, + kUnspecifiedMc = 2, + kReserved = 3, + kFcc = 4, + kBt470bg = 5, + kSmpte170MMc = 6, + kSmpte240MMc = 7, + kYcocg = 8, + kBt2020NonConstantLuminance = 9, + kBt2020ConstantLuminance = 10, + }; + enum ChromaSitingHorz { + kUnspecifiedCsh = 0, + kLeftCollocated = 1, + kHalfCsh = 2, + }; + enum ChromaSitingVert { + kUnspecifiedCsv = 0, + kTopCollocated = 1, + kHalfCsv = 2, + }; + enum Range { + kUnspecifiedCr = 0, + kBroadcastRange = 1, + kFullRange = 2, + kMcTcDefined = 3, // Defined by MatrixCoefficients/TransferCharacteristics. + }; + enum TransferCharacteristics { + kIturBt709Tc = 1, + kUnspecifiedTc = 2, + kReservedTc = 3, + kGamma22Curve = 4, + kGamma28Curve = 5, + kSmpte170MTc = 6, + kSmpte240MTc = 7, + kLinear = 8, + kLog = 9, + kLogSqrt = 10, + kIec6196624 = 11, + kIturBt1361ExtendedColourGamut = 12, + kIec6196621 = 13, + kIturBt202010bit = 14, + kIturBt202012bit = 15, + kSmpteSt2084 = 16, + kSmpteSt4281Tc = 17, + kAribStdB67Hlg = 18, + }; + enum Primaries { + kReservedP0 = 0, + kIturBt709P = 1, + kUnspecifiedP = 2, + kReservedP3 = 3, + kIturBt470M = 4, + kIturBt470Bg = 5, + kSmpte170MP = 6, + kSmpte240MP = 7, + kFilm = 8, + kIturBt2020 = 9, + kSmpteSt4281P = 10, + kJedecP22Phosphors = 22, + }; + static const uint64_t kValueNotPresent; + Colour() + : matrix_coefficients_(kValueNotPresent), + bits_per_channel_(kValueNotPresent), + chroma_subsampling_horz_(kValueNotPresent), + chroma_subsampling_vert_(kValueNotPresent), + cb_subsampling_horz_(kValueNotPresent), + cb_subsampling_vert_(kValueNotPresent), + chroma_siting_horz_(kValueNotPresent), + chroma_siting_vert_(kValueNotPresent), + range_(kValueNotPresent), + transfer_characteristics_(kValueNotPresent), + primaries_(kValueNotPresent), + max_cll_(kValueNotPresent), + max_fall_(kValueNotPresent), + mastering_metadata_(NULL) {} + ~Colour() { delete mastering_metadata_; } + + // Returns total size of the Colour element. + uint64_t ColourSize() const; + bool Valid() const; + bool Write(IMkvWriter* writer) const; + + // Deep copies |mastering_metadata|. + bool SetMasteringMetadata(const MasteringMetadata& mastering_metadata); + + const MasteringMetadata* mastering_metadata() const { + return mastering_metadata_; + } + + uint64_t matrix_coefficients() const { return matrix_coefficients_; } + void set_matrix_coefficients(uint64_t matrix_coefficients) { + matrix_coefficients_ = matrix_coefficients; + } + uint64_t bits_per_channel() const { return bits_per_channel_; } + void set_bits_per_channel(uint64_t bits_per_channel) { + bits_per_channel_ = bits_per_channel; + } + uint64_t chroma_subsampling_horz() const { return chroma_subsampling_horz_; } + void set_chroma_subsampling_horz(uint64_t chroma_subsampling_horz) { + chroma_subsampling_horz_ = chroma_subsampling_horz; + } + uint64_t chroma_subsampling_vert() const { return chroma_subsampling_vert_; } + void set_chroma_subsampling_vert(uint64_t chroma_subsampling_vert) { + chroma_subsampling_vert_ = chroma_subsampling_vert; + } + uint64_t cb_subsampling_horz() const { return cb_subsampling_horz_; } + void set_cb_subsampling_horz(uint64_t cb_subsampling_horz) { + cb_subsampling_horz_ = cb_subsampling_horz; + } + uint64_t cb_subsampling_vert() const { return cb_subsampling_vert_; } + void set_cb_subsampling_vert(uint64_t cb_subsampling_vert) { + cb_subsampling_vert_ = cb_subsampling_vert; + } + uint64_t chroma_siting_horz() const { return chroma_siting_horz_; } + void set_chroma_siting_horz(uint64_t chroma_siting_horz) { + chroma_siting_horz_ = chroma_siting_horz; + } + uint64_t chroma_siting_vert() const { return chroma_siting_vert_; } + void set_chroma_siting_vert(uint64_t chroma_siting_vert) { + chroma_siting_vert_ = chroma_siting_vert; + } + uint64_t range() const { return range_; } + void set_range(uint64_t range) { range_ = range; } + uint64_t transfer_characteristics() const { + return transfer_characteristics_; + } + void set_transfer_characteristics(uint64_t transfer_characteristics) { + transfer_characteristics_ = transfer_characteristics; + } + uint64_t primaries() const { return primaries_; } + void set_primaries(uint64_t primaries) { primaries_ = primaries; } + uint64_t max_cll() const { return max_cll_; } + void set_max_cll(uint64_t max_cll) { max_cll_ = max_cll; } + uint64_t max_fall() const { return max_fall_; } + void set_max_fall(uint64_t max_fall) { max_fall_ = max_fall; } + + private: + // Returns size of Colour child elements. + uint64_t PayloadSize() const; + + uint64_t matrix_coefficients_; + uint64_t bits_per_channel_; + uint64_t chroma_subsampling_horz_; + uint64_t chroma_subsampling_vert_; + uint64_t cb_subsampling_horz_; + uint64_t cb_subsampling_vert_; + uint64_t chroma_siting_horz_; + uint64_t chroma_siting_vert_; + uint64_t range_; + uint64_t transfer_characteristics_; + uint64_t primaries_; + uint64_t max_cll_; + uint64_t max_fall_; + + MasteringMetadata* mastering_metadata_; +}; + +/////////////////////////////////////////////////////////////// +// Projection element. +class Projection { + public: + enum ProjectionType { + kTypeNotPresent = -1, + kRectangular = 0, + kEquirectangular = 1, + kCubeMap = 2, + kMesh = 3, + }; + static const uint64_t kValueNotPresent; + Projection() + : type_(kRectangular), + pose_yaw_(0.0), + pose_pitch_(0.0), + pose_roll_(0.0), + private_data_(NULL), + private_data_length_(0) {} + ~Projection() { delete[] private_data_; } + + uint64_t ProjectionSize() const; + bool Write(IMkvWriter* writer) const; + + bool SetProjectionPrivate(const uint8_t* private_data, + uint64_t private_data_length); + + ProjectionType type() const { return type_; } + void set_type(ProjectionType type) { type_ = type; } + float pose_yaw() const { return pose_yaw_; } + void set_pose_yaw(float pose_yaw) { pose_yaw_ = pose_yaw; } + float pose_pitch() const { return pose_pitch_; } + void set_pose_pitch(float pose_pitch) { pose_pitch_ = pose_pitch; } + float pose_roll() const { return pose_roll_; } + void set_pose_roll(float pose_roll) { pose_roll_ = pose_roll; } + uint8_t* private_data() const { return private_data_; } + uint64_t private_data_length() const { return private_data_length_; } + + private: + // Returns size of VideoProjection child elements. + uint64_t PayloadSize() const; + + ProjectionType type_; + float pose_yaw_; + float pose_pitch_; + float pose_roll_; + uint8_t* private_data_; + uint64_t private_data_length_; +}; + +/////////////////////////////////////////////////////////////// +// Track element. +class Track { + public: + // The |seed| parameter is used to synthesize a UID for the track. + explicit Track(unsigned int* seed); + virtual ~Track(); + + // Adds a ContentEncoding element to the Track. Returns true on success. + virtual bool AddContentEncoding(); + + // Returns the ContentEncoding by index. Returns NULL if there is no + // ContentEncoding match. + ContentEncoding* GetContentEncodingByIndex(uint32_t index) const; + + // Returns the size in bytes for the payload of the Track element. + virtual uint64_t PayloadSize() const; + + // Returns the size in bytes of the Track element. + virtual uint64_t Size() const; + + // Output the Track element to the writer. Returns true on success. + virtual bool Write(IMkvWriter* writer) const; + + // Sets the CodecPrivate element of the Track element. Copies |length| + // bytes from |codec_private| to |codec_private_|. Returns true on success. + bool SetCodecPrivate(const uint8_t* codec_private, uint64_t length); + + void set_codec_id(const char* codec_id); + const char* codec_id() const { return codec_id_; } + const uint8_t* codec_private() const { return codec_private_; } + void set_language(const char* language); + const char* language() const { return language_; } + void set_max_block_additional_id(uint64_t max_block_additional_id) { + max_block_additional_id_ = max_block_additional_id; + } + uint64_t max_block_additional_id() const { return max_block_additional_id_; } + void set_name(const char* name); + const char* name() const { return name_; } + void set_number(uint64_t number) { number_ = number; } + uint64_t number() const { return number_; } + void set_type(uint64_t type) { type_ = type; } + uint64_t type() const { return type_; } + void set_uid(uint64_t uid) { uid_ = uid; } + uint64_t uid() const { return uid_; } + void set_codec_delay(uint64_t codec_delay) { codec_delay_ = codec_delay; } + uint64_t codec_delay() const { return codec_delay_; } + void set_seek_pre_roll(uint64_t seek_pre_roll) { + seek_pre_roll_ = seek_pre_roll; + } + uint64_t seek_pre_roll() const { return seek_pre_roll_; } + void set_default_duration(uint64_t default_duration) { + default_duration_ = default_duration; + } + uint64_t default_duration() const { return default_duration_; } + + uint64_t codec_private_length() const { return codec_private_length_; } + uint32_t content_encoding_entries_size() const { + return content_encoding_entries_size_; + } + + private: + // Track element names. + char* codec_id_; + uint8_t* codec_private_; + char* language_; + uint64_t max_block_additional_id_; + char* name_; + uint64_t number_; + uint64_t type_; + uint64_t uid_; + uint64_t codec_delay_; + uint64_t seek_pre_roll_; + uint64_t default_duration_; + + // Size of the CodecPrivate data in bytes. + uint64_t codec_private_length_; + + // ContentEncoding element list. + ContentEncoding** content_encoding_entries_; + + // Number of ContentEncoding elements added. + uint32_t content_encoding_entries_size_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track); +}; + +/////////////////////////////////////////////////////////////// +// Track that has video specific elements. +class VideoTrack : public Track { + public: + // Supported modes for stereo 3D. + enum StereoMode { + kMono = 0, + kSideBySideLeftIsFirst = 1, + kTopBottomRightIsFirst = 2, + kTopBottomLeftIsFirst = 3, + kSideBySideRightIsFirst = 11 + }; + + enum AlphaMode { kNoAlpha = 0, kAlpha = 1 }; + + // The |seed| parameter is used to synthesize a UID for the track. + explicit VideoTrack(unsigned int* seed); + virtual ~VideoTrack(); + + // Returns the size in bytes for the payload of the Track element plus the + // video specific elements. + virtual uint64_t PayloadSize() const; + + // Output the VideoTrack element to the writer. Returns true on success. + virtual bool Write(IMkvWriter* writer) const; + + // Sets the video's stereo mode. Returns true on success. + bool SetStereoMode(uint64_t stereo_mode); + + // Sets the video's alpha mode. Returns true on success. + bool SetAlphaMode(uint64_t alpha_mode); + + void set_display_height(uint64_t height) { display_height_ = height; } + uint64_t display_height() const { return display_height_; } + void set_display_width(uint64_t width) { display_width_ = width; } + uint64_t display_width() const { return display_width_; } + void set_pixel_height(uint64_t height) { pixel_height_ = height; } + uint64_t pixel_height() const { return pixel_height_; } + void set_pixel_width(uint64_t width) { pixel_width_ = width; } + uint64_t pixel_width() const { return pixel_width_; } + + void set_crop_left(uint64_t crop_left) { crop_left_ = crop_left; } + uint64_t crop_left() const { return crop_left_; } + void set_crop_right(uint64_t crop_right) { crop_right_ = crop_right; } + uint64_t crop_right() const { return crop_right_; } + void set_crop_top(uint64_t crop_top) { crop_top_ = crop_top; } + uint64_t crop_top() const { return crop_top_; } + void set_crop_bottom(uint64_t crop_bottom) { crop_bottom_ = crop_bottom; } + uint64_t crop_bottom() const { return crop_bottom_; } + + void set_frame_rate(double frame_rate) { frame_rate_ = frame_rate; } + double frame_rate() const { return frame_rate_; } + void set_height(uint64_t height) { height_ = height; } + uint64_t height() const { return height_; } + uint64_t stereo_mode() { return stereo_mode_; } + uint64_t alpha_mode() { return alpha_mode_; } + void set_width(uint64_t width) { width_ = width; } + uint64_t width() const { return width_; } + void set_colour_space(const char* colour_space); + const char* colour_space() const { return colour_space_; } + + Colour* colour() { return colour_; } + + // Deep copies |colour|. + bool SetColour(const Colour& colour); + + Projection* projection() { return projection_; } + + // Deep copies |projection|. + bool SetProjection(const Projection& projection); + + private: + // Returns the size in bytes of the Video element. + uint64_t VideoPayloadSize() const; + + // Video track element names. + uint64_t display_height_; + uint64_t display_width_; + uint64_t pixel_height_; + uint64_t pixel_width_; + uint64_t crop_left_; + uint64_t crop_right_; + uint64_t crop_top_; + uint64_t crop_bottom_; + double frame_rate_; + uint64_t height_; + uint64_t stereo_mode_; + uint64_t alpha_mode_; + uint64_t width_; + char* colour_space_; + + Colour* colour_; + Projection* projection_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack); +}; + +/////////////////////////////////////////////////////////////// +// Track that has audio specific elements. +class AudioTrack : public Track { + public: + // The |seed| parameter is used to synthesize a UID for the track. + explicit AudioTrack(unsigned int* seed); + virtual ~AudioTrack(); + + // Returns the size in bytes for the payload of the Track element plus the + // audio specific elements. + virtual uint64_t PayloadSize() const; + + // Output the AudioTrack element to the writer. Returns true on success. + virtual bool Write(IMkvWriter* writer) const; + + void set_bit_depth(uint64_t bit_depth) { bit_depth_ = bit_depth; } + uint64_t bit_depth() const { return bit_depth_; } + void set_channels(uint64_t channels) { channels_ = channels; } + uint64_t channels() const { return channels_; } + void set_sample_rate(double sample_rate) { sample_rate_ = sample_rate; } + double sample_rate() const { return sample_rate_; } + + private: + // Audio track element names. + uint64_t bit_depth_; + uint64_t channels_; + double sample_rate_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack); +}; + +/////////////////////////////////////////////////////////////// +// Tracks element +class Tracks { + public: + // Audio and video type defined by the Matroska specs. + enum { kVideo = 0x1, kAudio = 0x2 }; + + static const char kOpusCodecId[]; + static const char kVorbisCodecId[]; + static const char kAv1CodecId[]; + static const char kVp8CodecId[]; + static const char kVp9CodecId[]; + static const char kWebVttCaptionsId[]; + static const char kWebVttDescriptionsId[]; + static const char kWebVttMetadataId[]; + static const char kWebVttSubtitlesId[]; + + Tracks(); + ~Tracks(); + + // Adds a Track element to the Tracks object. |track| will be owned and + // deleted by the Tracks object. Returns true on success. |number| is the + // number to use for the track. |number| must be >= 0. If |number| == 0 + // then the muxer will decide on the track number. + bool AddTrack(Track* track, int32_t number); + + // Returns the track by index. Returns NULL if there is no track match. + const Track* GetTrackByIndex(uint32_t idx) const; + + // Search the Tracks and return the track that matches |tn|. Returns NULL + // if there is no track match. + Track* GetTrackByNumber(uint64_t track_number) const; + + // Returns true if the track number is an audio track. + bool TrackIsAudio(uint64_t track_number) const; + + // Returns true if the track number is a video track. + bool TrackIsVideo(uint64_t track_number) const; + + // Output the Tracks element to the writer. Returns true on success. + bool Write(IMkvWriter* writer) const; + + uint32_t track_entries_size() const { return track_entries_size_; } + + private: + // Track element list. + Track** track_entries_; + + // Number of Track elements added. + uint32_t track_entries_size_; + + // Whether or not Tracks element has already been written via IMkvWriter. + mutable bool wrote_tracks_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks); +}; + +/////////////////////////////////////////////////////////////// +// Chapter element +// +class Chapter { + public: + // Set the identifier for this chapter. (This corresponds to the + // Cue Identifier line in WebVTT.) + // TODO(matthewjheaney): the actual serialization of this item in + // MKV is pending. + bool set_id(const char* id); + + // Converts the nanosecond start and stop times of this chapter to + // their corresponding timecode values, and stores them that way. + void set_time(const Segment& segment, uint64_t start_time_ns, + uint64_t end_time_ns); + + // Sets the uid for this chapter. Primarily used to enable + // deterministic output from the muxer. + void set_uid(const uint64_t uid) { uid_ = uid; } + + // Add a title string to this chapter, per the semantics described + // here: + // http://www.matroska.org/technical/specs/index.html + // + // The title ("chapter string") is a UTF-8 string. + // + // The language has ISO 639-2 representation, described here: + // http://www.loc.gov/standards/iso639-2/englangn.html + // http://www.loc.gov/standards/iso639-2/php/English_list.php + // If you specify NULL as the language value, this implies + // English ("eng"). + // + // The country value corresponds to the codes listed here: + // http://www.iana.org/domains/root/db/ + // + // The function returns false if the string could not be allocated. + bool add_string(const char* title, const char* language, const char* country); + + private: + friend class Chapters; + + // For storage of chapter titles that differ by language. + class Display { + public: + // Establish representation invariant for new Display object. + void Init(); + + // Reclaim resources, in anticipation of destruction. + void Clear(); + + // Copies the title to the |title_| member. Returns false on + // error. + bool set_title(const char* title); + + // Copies the language to the |language_| member. Returns false + // on error. + bool set_language(const char* language); + + // Copies the country to the |country_| member. Returns false on + // error. + bool set_country(const char* country); + + // If |writer| is non-NULL, serialize the Display sub-element of + // the Atom into the stream. Returns the Display element size on + // success, 0 if error. + uint64_t WriteDisplay(IMkvWriter* writer) const; + + private: + char* title_; + char* language_; + char* country_; + }; + + Chapter(); + ~Chapter(); + + // Establish the representation invariant for a newly-created + // Chapter object. The |seed| parameter is used to create the UID + // for this chapter atom. + void Init(unsigned int* seed); + + // Copies this Chapter object to a different one. This is used when + // expanding a plain array of Chapter objects (see Chapters). + void ShallowCopy(Chapter* dst) const; + + // Reclaim resources used by this Chapter object, pending its + // destruction. + void Clear(); + + // If there is no storage remaining on the |displays_| array for a + // new display object, creates a new, longer array and copies the + // existing Display objects to the new array. Returns false if the + // array cannot be expanded. + bool ExpandDisplaysArray(); + + // If |writer| is non-NULL, serialize the Atom sub-element into the + // stream. Returns the total size of the element on success, 0 if + // error. + uint64_t WriteAtom(IMkvWriter* writer) const; + + // The string identifier for this chapter (corresponds to WebVTT cue + // identifier). + char* id_; + + // Start timecode of the chapter. + uint64_t start_timecode_; + + // Stop timecode of the chapter. + uint64_t end_timecode_; + + // The binary identifier for this chapter. + uint64_t uid_; + + // The Atom element can contain multiple Display sub-elements, as + // the same logical title can be rendered in different languages. + Display* displays_; + + // The physical length (total size) of the |displays_| array. + int displays_size_; + + // The logical length (number of active elements) on the |displays_| + // array. + int displays_count_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapter); +}; + +/////////////////////////////////////////////////////////////// +// Chapters element +// +class Chapters { + public: + Chapters(); + ~Chapters(); + + Chapter* AddChapter(unsigned int* seed); + + // Returns the number of chapters that have been added. + int Count() const; + + // Output the Chapters element to the writer. Returns true on success. + bool Write(IMkvWriter* writer) const; + + private: + // Expands the chapters_ array if there is not enough space to contain + // another chapter object. Returns true on success. + bool ExpandChaptersArray(); + + // If |writer| is non-NULL, serialize the Edition sub-element of the + // Chapters element into the stream. Returns the Edition element + // size on success, 0 if error. + uint64_t WriteEdition(IMkvWriter* writer) const; + + // Total length of the chapters_ array. + int chapters_size_; + + // Number of active chapters on the chapters_ array. + int chapters_count_; + + // Array for storage of chapter objects. + Chapter* chapters_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapters); +}; + +/////////////////////////////////////////////////////////////// +// Tag element +// +class Tag { + public: + bool add_simple_tag(const char* tag_name, const char* tag_string); + + private: + // Tags calls Clear and the destructor of Tag + friend class Tags; + + // For storage of simple tags + class SimpleTag { + public: + // Establish representation invariant for new SimpleTag object. + void Init(); + + // Reclaim resources, in anticipation of destruction. + void Clear(); + + // Copies the title to the |tag_name_| member. Returns false on + // error. + bool set_tag_name(const char* tag_name); + + // Copies the language to the |tag_string_| member. Returns false + // on error. + bool set_tag_string(const char* tag_string); + + // If |writer| is non-NULL, serialize the SimpleTag sub-element of + // the Atom into the stream. Returns the SimpleTag element size on + // success, 0 if error. + uint64_t Write(IMkvWriter* writer) const; + + private: + char* tag_name_; + char* tag_string_; + }; + + Tag(); + ~Tag(); + + // Copies this Tag object to a different one. This is used when + // expanding a plain array of Tag objects (see Tags). + void ShallowCopy(Tag* dst) const; + + // Reclaim resources used by this Tag object, pending its + // destruction. + void Clear(); + + // If there is no storage remaining on the |simple_tags_| array for a + // new display object, creates a new, longer array and copies the + // existing SimpleTag objects to the new array. Returns false if the + // array cannot be expanded. + bool ExpandSimpleTagsArray(); + + // If |writer| is non-NULL, serialize the Tag sub-element into the + // stream. Returns the total size of the element on success, 0 if + // error. + uint64_t Write(IMkvWriter* writer) const; + + // The Atom element can contain multiple SimpleTag sub-elements + SimpleTag* simple_tags_; + + // The physical length (total size) of the |simple_tags_| array. + int simple_tags_size_; + + // The logical length (number of active elements) on the |simple_tags_| + // array. + int simple_tags_count_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tag); +}; + +/////////////////////////////////////////////////////////////// +// Tags element +// +class Tags { + public: + Tags(); + ~Tags(); + + Tag* AddTag(); + + // Returns the number of tags that have been added. + int Count() const; + + // Output the Tags element to the writer. Returns true on success. + bool Write(IMkvWriter* writer) const; + + private: + // Expands the tags_ array if there is not enough space to contain + // another tag object. Returns true on success. + bool ExpandTagsArray(); + + // Total length of the tags_ array. + int tags_size_; + + // Number of active tags on the tags_ array. + int tags_count_; + + // Array for storage of tag objects. + Tag* tags_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tags); +}; + +/////////////////////////////////////////////////////////////// +// Cluster element +// +// Notes: +// |Init| must be called before any other method in this class. +class Cluster { + public: + // |timecode| is the absolute timecode of the cluster. |cues_pos| is the + // position for the cluster within the segment that should be written in + // the cues element. |timecode_scale| is the timecode scale of the segment. + Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale, + bool write_last_frame_with_duration = false, + bool fixed_size_timecode = false); + ~Cluster(); + + bool Init(IMkvWriter* ptr_writer); + + // Adds a frame to be output in the file. The frame is written out through + // |writer_| if successful. Returns true on success. + bool AddFrame(const Frame* frame); + + // Adds a frame to be output in the file. The frame is written out through + // |writer_| if successful. Returns true on success. + // Inputs: + // data: Pointer to the data + // length: Length of the data + // track_number: Track to add the data to. Value returned by Add track + // functions. The range of allowed values is [1, 126]. + // timecode: Absolute (not relative to cluster) timestamp of the + // frame, expressed in timecode units. + // is_key: Flag telling whether or not this frame is a key frame. + bool AddFrame(const uint8_t* data, uint64_t length, uint64_t track_number, + uint64_t timecode, // timecode units (absolute) + bool is_key); + + // Adds a frame to be output in the file. The frame is written out through + // |writer_| if successful. Returns true on success. + // Inputs: + // data: Pointer to the data + // length: Length of the data + // additional: Pointer to the additional data + // additional_length: Length of the additional data + // add_id: Value of BlockAddID element + // track_number: Track to add the data to. Value returned by Add track + // functions. The range of allowed values is [1, 126]. + // abs_timecode: Absolute (not relative to cluster) timestamp of the + // frame, expressed in timecode units. + // is_key: Flag telling whether or not this frame is a key frame. + bool AddFrameWithAdditional(const uint8_t* data, uint64_t length, + const uint8_t* additional, + uint64_t additional_length, uint64_t add_id, + uint64_t track_number, uint64_t abs_timecode, + bool is_key); + + // Adds a frame to be output in the file. The frame is written out through + // |writer_| if successful. Returns true on success. + // Inputs: + // data: Pointer to the data. + // length: Length of the data. + // discard_padding: DiscardPadding element value. + // track_number: Track to add the data to. Value returned by Add track + // functions. The range of allowed values is [1, 126]. + // abs_timecode: Absolute (not relative to cluster) timestamp of the + // frame, expressed in timecode units. + // is_key: Flag telling whether or not this frame is a key frame. + bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, + int64_t discard_padding, + uint64_t track_number, uint64_t abs_timecode, + bool is_key); + + // Writes a frame of metadata to the output medium; returns true on + // success. + // Inputs: + // data: Pointer to the data + // length: Length of the data + // track_number: Track to add the data to. Value returned by Add track + // functions. The range of allowed values is [1, 126]. + // timecode: Absolute (not relative to cluster) timestamp of the + // metadata frame, expressed in timecode units. + // duration: Duration of metadata frame, in timecode units. + // + // The metadata frame is written as a block group, with a duration + // sub-element but no reference time sub-elements (indicating that + // it is considered a keyframe, per Matroska semantics). + bool AddMetadata(const uint8_t* data, uint64_t length, uint64_t track_number, + uint64_t timecode, uint64_t duration); + + // Increments the size of the cluster's data in bytes. + void AddPayloadSize(uint64_t size); + + // Closes the cluster so no more data can be written to it. Will update the + // cluster's size if |writer_| is seekable. Returns true on success. This + // variant of Finalize() fails when |write_last_frame_with_duration_| is set + // to true. + bool Finalize(); + + // Closes the cluster so no more data can be written to it. Will update the + // cluster's size if |writer_| is seekable. Returns true on success. + // Inputs: + // set_last_frame_duration: Boolean indicating whether or not the duration + // of the last frame should be set. If set to + // false, the |duration| value is ignored and + // |write_last_frame_with_duration_| will not be + // honored. + // duration: Duration of the Cluster in timecode scale. + bool Finalize(bool set_last_frame_duration, uint64_t duration); + + // Returns the size in bytes for the entire Cluster element. + uint64_t Size() const; + + // Given |abs_timecode|, calculates timecode relative to most recent timecode. + // Returns -1 on failure, or a relative timecode. + int64_t GetRelativeTimecode(int64_t abs_timecode) const; + + int64_t size_position() const { return size_position_; } + int32_t blocks_added() const { return blocks_added_; } + uint64_t payload_size() const { return payload_size_; } + int64_t position_for_cues() const { return position_for_cues_; } + uint64_t timecode() const { return timecode_; } + uint64_t timecode_scale() const { return timecode_scale_; } + void set_write_last_frame_with_duration(bool write_last_frame_with_duration) { + write_last_frame_with_duration_ = write_last_frame_with_duration; + } + bool write_last_frame_with_duration() const { + return write_last_frame_with_duration_; + } + + private: + // Iterator type for the |stored_frames_| map. + typedef std::map >::iterator FrameMapIterator; + + // Utility method that confirms that blocks can still be added, and that the + // cluster header has been written. Used by |DoWriteFrame*|. Returns true + // when successful. + bool PreWriteBlock(); + + // Utility method used by the |DoWriteFrame*| methods that handles the book + // keeping required after each block is written. + void PostWriteBlock(uint64_t element_size); + + // Does some verification and calls WriteFrame. + bool DoWriteFrame(const Frame* const frame); + + // Either holds back the given frame, or writes it out depending on whether or + // not |write_last_frame_with_duration_| is set. + bool QueueOrWriteFrame(const Frame* const frame); + + // Outputs the Cluster header to |writer_|. Returns true on success. + bool WriteClusterHeader(); + + // Number of blocks added to the cluster. + int32_t blocks_added_; + + // Flag telling if the cluster has been closed. + bool finalized_; + + // Flag indicating whether the cluster's timecode will always be written out + // using 8 bytes. + bool fixed_size_timecode_; + + // Flag telling if the cluster's header has been written. + bool header_written_; + + // The size of the cluster elements in bytes. + uint64_t payload_size_; + + // The file position used for cue points. + const int64_t position_for_cues_; + + // The file position of the cluster's size element. + int64_t size_position_; + + // The absolute timecode of the cluster. + const uint64_t timecode_; + + // The timecode scale of the Segment containing the cluster. + const uint64_t timecode_scale_; + + // Flag indicating whether the last frame of the cluster should be written as + // a Block with Duration. If set to true, then it will result in holding back + // of frames and the parameterized version of Finalize() must be called to + // finish writing the Cluster. + bool write_last_frame_with_duration_; + + // Map used to hold back frames, if required. Track number is the key. + std::map > stored_frames_; + + // Map from track number to the timestamp of the last block written for that + // track. + std::map last_block_timestamp_; + + // Pointer to the writer object. Not owned by this class. + IMkvWriter* writer_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cluster); +}; + +/////////////////////////////////////////////////////////////// +// SeekHead element +class SeekHead { + public: + SeekHead(); + ~SeekHead(); + + // TODO(fgalligan): Change this to reserve a certain size. Then check how + // big the seek entry to be added is as not every seek entry will be the + // maximum size it could be. + // Adds a seek entry to be written out when the element is finalized. |id| + // must be the coded mkv element id. |pos| is the file position of the + // element. Returns true on success. + bool AddSeekEntry(uint32_t id, uint64_t pos); + + // Writes out SeekHead and SeekEntry elements. Returns true on success. + bool Finalize(IMkvWriter* writer) const; + + // Returns the id of the Seek Entry at the given index. Returns -1 if index is + // out of range. + uint32_t GetId(int index) const; + + // Returns the position of the Seek Entry at the given index. Returns -1 if + // index is out of range. + uint64_t GetPosition(int index) const; + + // Sets the Seek Entry id and position at given index. + // Returns true on success. + bool SetSeekEntry(int index, uint32_t id, uint64_t position); + + // Reserves space by writing out a Void element which will be updated with + // a SeekHead element later. Returns true on success. + bool Write(IMkvWriter* writer); + + // We are going to put a cap on the number of Seek Entries. + const static int32_t kSeekEntryCount = 5; + + private: + // Returns the maximum size in bytes of one seek entry. + uint64_t MaxEntrySize() const; + + // Seek entry id element list. + uint32_t seek_entry_id_[kSeekEntryCount]; + + // Seek entry pos element list. + uint64_t seek_entry_pos_[kSeekEntryCount]; + + // The file position of SeekHead element. + int64_t start_pos_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead); +}; + +/////////////////////////////////////////////////////////////// +// Segment Information element +class SegmentInfo { + public: + SegmentInfo(); + ~SegmentInfo(); + + // Will update the duration if |duration_| is > 0.0. Returns true on success. + bool Finalize(IMkvWriter* writer) const; + + // Sets |muxing_app_| and |writing_app_|. + bool Init(); + + // Output the Segment Information element to the writer. Returns true on + // success. + bool Write(IMkvWriter* writer); + + void set_duration(double duration) { duration_ = duration; } + double duration() const { return duration_; } + void set_muxing_app(const char* app); + const char* muxing_app() const { return muxing_app_; } + void set_timecode_scale(uint64_t scale) { timecode_scale_ = scale; } + uint64_t timecode_scale() const { return timecode_scale_; } + void set_writing_app(const char* app); + const char* writing_app() const { return writing_app_; } + void set_date_utc(int64_t date_utc) { date_utc_ = date_utc; } + int64_t date_utc() const { return date_utc_; } + + private: + // Segment Information element names. + // Initially set to -1 to signify that a duration has not been set and should + // not be written out. + double duration_; + // Set to libwebm-%d.%d.%d.%d, major, minor, build, revision. + char* muxing_app_; + uint64_t timecode_scale_; + // Initially set to libwebm-%d.%d.%d.%d, major, minor, build, revision. + char* writing_app_; + // LLONG_MIN when DateUTC is not set. + int64_t date_utc_; + + // The file position of the duration element. + int64_t duration_pos_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo); +}; + +/////////////////////////////////////////////////////////////// +// This class represents the main segment in a WebM file. Currently only +// supports one Segment element. +// +// Notes: +// |Init| must be called before any other method in this class. +class Segment { + public: + enum Mode { kLive = 0x1, kFile = 0x2 }; + + enum CuesPosition { + kAfterClusters = 0x0, // Position Cues after Clusters - Default + kBeforeClusters = 0x1 // Position Cues before Clusters + }; + + static const uint32_t kDefaultDocTypeVersion = 4; + static const uint64_t kDefaultMaxClusterDuration = 30000000000ULL; + + Segment(); + ~Segment(); + + // Initializes |SegmentInfo| and returns result. Always returns false when + // |ptr_writer| is NULL. + bool Init(IMkvWriter* ptr_writer); + + // Adds a generic track to the segment. Returns the newly-allocated + // track object (which is owned by the segment) on success, NULL on + // error. |number| is the number to use for the track. |number| + // must be >= 0. If |number| == 0 then the muxer will decide on the + // track number. + Track* AddTrack(int32_t number); + + // Adds a Vorbis audio track to the segment. Returns the number of the track + // on success, 0 on error. |number| is the number to use for the audio track. + // |number| must be >= 0. If |number| == 0 then the muxer will decide on + // the track number. + uint64_t AddAudioTrack(int32_t sample_rate, int32_t channels, int32_t number); + + // Adds an empty chapter to the chapters of this segment. Returns + // non-NULL on success. After adding the chapter, the caller should + // populate its fields via the Chapter member functions. + Chapter* AddChapter(); + + // Adds an empty tag to the tags of this segment. Returns + // non-NULL on success. After adding the tag, the caller should + // populate its fields via the Tag member functions. + Tag* AddTag(); + + // Adds a cue point to the Cues element. |timestamp| is the time in + // nanoseconds of the cue's time. |track| is the Track of the Cue. This + // function must be called after AddFrame to calculate the correct + // BlockNumber for the CuePoint. Returns true on success. + bool AddCuePoint(uint64_t timestamp, uint64_t track); + + // Adds a frame to be output in the file. Returns true on success. + // Inputs: + // data: Pointer to the data + // length: Length of the data + // track_number: Track to add the data to. Value returned by Add track + // functions. + // timestamp: Timestamp of the frame in nanoseconds from 0. + // is_key: Flag telling whether or not this frame is a key frame. + bool AddFrame(const uint8_t* data, uint64_t length, uint64_t track_number, + uint64_t timestamp_ns, bool is_key); + + // Writes a frame of metadata to the output medium; returns true on + // success. + // Inputs: + // data: Pointer to the data + // length: Length of the data + // track_number: Track to add the data to. Value returned by Add track + // functions. + // timecode: Absolute timestamp of the metadata frame, expressed + // in nanosecond units. + // duration: Duration of metadata frame, in nanosecond units. + // + // The metadata frame is written as a block group, with a duration + // sub-element but no reference time sub-elements (indicating that + // it is considered a keyframe, per Matroska semantics). + bool AddMetadata(const uint8_t* data, uint64_t length, uint64_t track_number, + uint64_t timestamp_ns, uint64_t duration_ns); + + // Writes a frame with additional data to the output medium; returns true on + // success. + // Inputs: + // data: Pointer to the data. + // length: Length of the data. + // additional: Pointer to additional data. + // additional_length: Length of additional data. + // add_id: Additional ID which identifies the type of additional data. + // track_number: Track to add the data to. Value returned by Add track + // functions. + // timestamp: Absolute timestamp of the frame, expressed in nanosecond + // units. + // is_key: Flag telling whether or not this frame is a key frame. + bool AddFrameWithAdditional(const uint8_t* data, uint64_t length, + const uint8_t* additional, + uint64_t additional_length, uint64_t add_id, + uint64_t track_number, uint64_t timestamp, + bool is_key); + + // Writes a frame with DiscardPadding to the output medium; returns true on + // success. + // Inputs: + // data: Pointer to the data. + // length: Length of the data. + // discard_padding: DiscardPadding element value. + // track_number: Track to add the data to. Value returned by Add track + // functions. + // timestamp: Absolute timestamp of the frame, expressed in nanosecond + // units. + // is_key: Flag telling whether or not this frame is a key frame. + bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, + int64_t discard_padding, + uint64_t track_number, uint64_t timestamp, + bool is_key); + + // Writes a Frame to the output medium. Chooses the correct way of writing + // the frame (Block vs SimpleBlock) based on the parameters passed. + // Inputs: + // frame: frame object + bool AddGenericFrame(const Frame* frame); + + // Adds a VP8 video track to the segment. Returns the number of the track on + // success, 0 on error. |number| is the number to use for the video track. + // |number| must be >= 0. If |number| == 0 then the muxer will decide on + // the track number. + uint64_t AddVideoTrack(int32_t width, int32_t height, int32_t number); + + // This function must be called after Finalize() if you need a copy of the + // output with Cues written before the Clusters. It will return false if the + // writer is not seekable of if chunking is set to true. + // Input parameters: + // reader - an IMkvReader object created with the same underlying file of the + // current writer object. Make sure to close the existing writer + // object before creating this so that all the data is properly + // flushed and available for reading. + // writer - an IMkvWriter object pointing to a *different* file than the one + // pointed by the current writer object. This file will contain the + // Cues element before the Clusters. + bool CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader, + IMkvWriter* writer); + + // Sets which track to use for the Cues element. Must have added the track + // before calling this function. Returns true on success. |track_number| is + // returned by the Add track functions. + bool CuesTrack(uint64_t track_number); + + // This will force the muxer to create a new Cluster when the next frame is + // added. + void ForceNewClusterOnNextFrame(); + + // Writes out any frames that have not been written out. Finalizes the last + // cluster. May update the size and duration of the segment. May output the + // Cues element. May finalize the SeekHead element. Returns true on success. + bool Finalize(); + + // Returns the Cues object. + Cues* GetCues() { return &cues_; } + + // Returns the Segment Information object. + const SegmentInfo* GetSegmentInfo() const { return &segment_info_; } + SegmentInfo* GetSegmentInfo() { return &segment_info_; } + + // Search the Tracks and return the track that matches |track_number|. + // Returns NULL if there is no track match. + Track* GetTrackByNumber(uint64_t track_number) const; + + // Toggles whether to output a cues element. + void OutputCues(bool output_cues); + + // Toggles whether to write the last frame in each Cluster with Duration. + void AccurateClusterDuration(bool accurate_cluster_duration); + + // Toggles whether to write the Cluster Timecode using exactly 8 bytes. + void UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode); + + // Sets if the muxer will output files in chunks or not. |chunking| is a + // flag telling whether or not to turn on chunking. |filename| is the base + // filename for the chunk files. The header chunk file will be named + // |filename|.hdr and the data chunks will be named + // |filename|_XXXXXX.chk. Chunking implies that the muxer will be writing + // to files so the muxer will use the default MkvWriter class to control + // what data is written to what files. Returns true on success. + // TODO: Should we change the IMkvWriter Interface to add Open and Close? + // That will force the interface to be dependent on files. + bool SetChunking(bool chunking, const char* filename); + + bool chunking() const { return chunking_; } + uint64_t cues_track() const { return cues_track_; } + void set_max_cluster_duration(uint64_t max_cluster_duration) { + max_cluster_duration_ = max_cluster_duration; + } + uint64_t max_cluster_duration() const { return max_cluster_duration_; } + void set_max_cluster_size(uint64_t max_cluster_size) { + max_cluster_size_ = max_cluster_size; + } + uint64_t max_cluster_size() const { return max_cluster_size_; } + void set_mode(Mode mode) { mode_ = mode; } + Mode mode() const { return mode_; } + CuesPosition cues_position() const { return cues_position_; } + bool output_cues() const { return output_cues_; } + void set_estimate_file_duration(bool estimate_duration) { + estimate_file_duration_ = estimate_duration; + } + bool estimate_file_duration() const { return estimate_file_duration_; } + const SegmentInfo* segment_info() const { return &segment_info_; } + void set_duration(double duration) { duration_ = duration; } + double duration() const { return duration_; } + + // Returns true when codec IDs are valid for WebM. + bool DocTypeIsWebm() const; + + private: + // Checks if header information has been output and initialized. If not it + // will output the Segment element and initialize the SeekHead elment and + // Cues elements. + bool CheckHeaderInfo(); + + // Sets |doc_type_version_| based on the current element requirements. + void UpdateDocTypeVersion(); + + // Sets |name| according to how many chunks have been written. |ext| is the + // file extension. |name| must be deleted by the calling app. Returns true + // on success. + bool UpdateChunkName(const char* ext, char** name) const; + + // Returns the maximum offset within the segment's payload. When chunking + // this function is needed to determine offsets of elements within the + // chunked files. Returns -1 on error. + int64_t MaxOffset(); + + // Adds the frame to our frame array. + bool QueueFrame(Frame* frame); + + // Output all frames that are queued. Returns -1 on error, otherwise + // it returns the number of frames written. + int WriteFramesAll(); + + // Output all frames that are queued that have an end time that is less + // then |timestamp|. Returns true on success and if there are no frames + // queued. + bool WriteFramesLessThan(uint64_t timestamp); + + // Outputs the segment header, Segment Information element, SeekHead element, + // and Tracks element to |writer_|. + bool WriteSegmentHeader(); + + // Given a frame with the specified timestamp (nanosecond units) and + // keyframe status, determine whether a new cluster should be + // created, before writing enqueued frames and the frame itself. The + // function returns one of the following values: + // -1 = error: an out-of-order frame was detected + // 0 = do not create a new cluster, and write frame to the existing cluster + // 1 = create a new cluster, and write frame to that new cluster + // 2 = create a new cluster, and re-run test + int TestFrame(uint64_t track_num, uint64_t timestamp_ns, bool key) const; + + // Create a new cluster, using the earlier of the first enqueued + // frame, or the indicated time. Returns true on success. + bool MakeNewCluster(uint64_t timestamp_ns); + + // Checks whether a new cluster needs to be created, and if so + // creates a new cluster. Returns false if creation of a new cluster + // was necessary but creation was not successful. + bool DoNewClusterProcessing(uint64_t track_num, uint64_t timestamp_ns, + bool key); + + // Adjusts Cue Point values (to place Cues before Clusters) so that they + // reflect the correct offsets. + void MoveCuesBeforeClusters(); + + // This function recursively computes the correct cluster offsets (this is + // done to move the Cues before Clusters). It recursively updates the change + // in size (which indicates a change in cluster offset) until no sizes change. + // Parameters: + // diff - indicates the difference in size of the Cues element that needs to + // accounted for. + // index - index in the list of Cues which is currently being adjusted. + // cue_size - sum of size of all the CuePoint elements. + void MoveCuesBeforeClustersHelper(uint64_t diff, int index, + uint64_t* cue_size); + + // Seeds the random number generator used to make UIDs. + unsigned int seed_; + + // WebM elements + Cues cues_; + SeekHead seek_head_; + SegmentInfo segment_info_; + Tracks tracks_; + Chapters chapters_; + Tags tags_; + + // Number of chunks written. + int chunk_count_; + + // Current chunk filename. + char* chunk_name_; + + // Default MkvWriter object created by this class used for writing clusters + // out in separate files. + MkvWriter* chunk_writer_cluster_; + + // Default MkvWriter object created by this class used for writing Cues + // element out to a file. + MkvWriter* chunk_writer_cues_; + + // Default MkvWriter object created by this class used for writing the + // Matroska header out to a file. + MkvWriter* chunk_writer_header_; + + // Flag telling whether or not the muxer is chunking output to multiple + // files. + bool chunking_; + + // Base filename for the chunked files. + char* chunking_base_name_; + + // File position offset where the Clusters end. + int64_t cluster_end_offset_; + + // List of clusters. + Cluster** cluster_list_; + + // Number of cluster pointers allocated in the cluster list. + int32_t cluster_list_capacity_; + + // Number of clusters in the cluster list. + int32_t cluster_list_size_; + + // Indicates whether Cues should be written before or after Clusters + CuesPosition cues_position_; + + // Track number that is associated with the cues element for this segment. + uint64_t cues_track_; + + // Tells the muxer to force a new cluster on the next Block. + bool force_new_cluster_; + + // List of stored audio frames. These variables are used to store frames so + // the muxer can follow the guideline "Audio blocks that contain the video + // key frame's timecode should be in the same cluster as the video key frame + // block." + Frame** frames_; + + // Number of frame pointers allocated in the frame list. + int32_t frames_capacity_; + + // Number of frames in the frame list. + int32_t frames_size_; + + // Flag telling if a video track has been added to the segment. + bool has_video_; + + // Flag telling if the segment's header has been written. + bool header_written_; + + // Duration of the last block in nanoseconds. + uint64_t last_block_duration_; + + // Last timestamp in nanoseconds added to a cluster. + uint64_t last_timestamp_; + + // Last timestamp in nanoseconds by track number added to a cluster. + uint64_t last_track_timestamp_[kMaxTrackNumber]; + + // Number of frames written per track. + uint64_t track_frames_written_[kMaxTrackNumber]; + + // Maximum time in nanoseconds for a cluster duration. This variable is a + // guideline and some clusters may have a longer duration. Default is 30 + // seconds. + uint64_t max_cluster_duration_; + + // Maximum size in bytes for a cluster. This variable is a guideline and + // some clusters may have a larger size. Default is 0 which signifies that + // the muxer will decide the size. + uint64_t max_cluster_size_; + + // The mode that segment is in. If set to |kLive| the writer must not + // seek backwards. + Mode mode_; + + // Flag telling the muxer that a new cue point should be added. + bool new_cuepoint_; + + // TODO(fgalligan): Should we add support for more than one Cues element? + // Flag whether or not the muxer should output a Cues element. + bool output_cues_; + + // Flag whether or not the last frame in each Cluster will have a Duration + // element in it. + bool accurate_cluster_duration_; + + // Flag whether or not to write the Cluster Timecode using exactly 8 bytes. + bool fixed_size_cluster_timecode_; + + // Flag whether or not to estimate the file duration. + bool estimate_file_duration_; + + // The size of the EBML header, used to validate the header if + // WriteEbmlHeader() is called more than once. + int32_t ebml_header_size_; + + // The file position of the segment's payload. + int64_t payload_pos_; + + // The file position of the element's size. + int64_t size_position_; + + // Current DocTypeVersion (|doc_type_version_|) and that written in + // WriteSegmentHeader(). + // WriteEbmlHeader() will be called from Finalize() if |doc_type_version_| + // differs from |doc_type_version_written_|. + uint32_t doc_type_version_; + uint32_t doc_type_version_written_; + + // If |duration_| is > 0, then explicitly set the duration of the segment. + double duration_; + + // Pointer to the writer objects. Not owned by this class. + IMkvWriter* writer_cluster_; + IMkvWriter* writer_cues_; + IMkvWriter* writer_header_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment); +}; + +} // namespace mkvmuxer + +#endif // MKVMUXER_MKVMUXER_H_ diff --git a/mkvmuxer/mkvmuxertypes.h b/mkvmuxer/mkvmuxertypes.h new file mode 100644 index 000000000000..e5db121605f6 --- /dev/null +++ b/mkvmuxer/mkvmuxertypes.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef MKVMUXER_MKVMUXERTYPES_H_ +#define MKVMUXER_MKVMUXERTYPES_H_ + +namespace mkvmuxer { +typedef unsigned char uint8; +typedef short int16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +} // namespace mkvmuxer + +// Copied from Chromium basictypes.h +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#endif // MKVMUXER_MKVMUXERTYPES_HPP_ diff --git a/mkvmuxer/mkvmuxerutil.cc b/mkvmuxer/mkvmuxerutil.cc new file mode 100644 index 000000000000..bd2f76913811 --- /dev/null +++ b/mkvmuxer/mkvmuxerutil.cc @@ -0,0 +1,743 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "mkvmuxer/mkvmuxerutil.h" + +#ifdef __ANDROID__ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "common/webmids.h" +#include "mkvmuxer/mkvmuxer.h" +#include "mkvmuxer/mkvwriter.h" + +namespace mkvmuxer { + +namespace { + +// Date elements are always 8 octets in size. +const int kDateElementSize = 8; + +uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, + uint64 timecode_scale) { + uint64 block_additional_elem_size = 0; + uint64 block_addid_elem_size = 0; + uint64 block_more_payload_size = 0; + uint64 block_more_elem_size = 0; + uint64 block_additions_payload_size = 0; + uint64 block_additions_elem_size = 0; + if (frame->additional()) { + block_additional_elem_size = + EbmlElementSize(libwebm::kMkvBlockAdditional, frame->additional(), + frame->additional_length()); + block_addid_elem_size = EbmlElementSize( + libwebm::kMkvBlockAddID, static_cast(frame->add_id())); + + block_more_payload_size = + block_addid_elem_size + block_additional_elem_size; + block_more_elem_size = + EbmlMasterElementSize(libwebm::kMkvBlockMore, block_more_payload_size) + + block_more_payload_size; + block_additions_payload_size = block_more_elem_size; + block_additions_elem_size = + EbmlMasterElementSize(libwebm::kMkvBlockAdditions, + block_additions_payload_size) + + block_additions_payload_size; + } + + uint64 discard_padding_elem_size = 0; + if (frame->discard_padding() != 0) { + discard_padding_elem_size = + EbmlElementSize(libwebm::kMkvDiscardPadding, + static_cast(frame->discard_padding())); + } + + const uint64 reference_block_timestamp = + frame->reference_block_timestamp() / timecode_scale; + uint64 reference_block_elem_size = 0; + if (!frame->is_key()) { + reference_block_elem_size = + EbmlElementSize(libwebm::kMkvReferenceBlock, reference_block_timestamp); + } + + const uint64 duration = frame->duration() / timecode_scale; + uint64 block_duration_elem_size = 0; + if (duration > 0) + block_duration_elem_size = + EbmlElementSize(libwebm::kMkvBlockDuration, duration); + + const uint64 block_payload_size = 4 + frame->length(); + const uint64 block_elem_size = + EbmlMasterElementSize(libwebm::kMkvBlock, block_payload_size) + + block_payload_size; + + const uint64 block_group_payload_size = + block_elem_size + block_additions_elem_size + block_duration_elem_size + + discard_padding_elem_size + reference_block_elem_size; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockGroup, + block_group_payload_size)) { + return 0; + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlock, block_payload_size)) + return 0; + + if (WriteUInt(writer, frame->track_number())) + return 0; + + if (SerializeInt(writer, timecode, 2)) + return 0; + + // For a Block, flags is always 0. + if (SerializeInt(writer, 0, 1)) + return 0; + + if (writer->Write(frame->frame(), static_cast(frame->length()))) + return 0; + + if (frame->additional()) { + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockAdditions, + block_additions_payload_size)) { + return 0; + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockMore, + block_more_payload_size)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvBlockAddID, + static_cast(frame->add_id()))) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvBlockAdditional, + frame->additional(), frame->additional_length())) { + return 0; + } + } + + if (frame->discard_padding() != 0 && + !WriteEbmlElement(writer, libwebm::kMkvDiscardPadding, + static_cast(frame->discard_padding()))) { + return false; + } + + if (!frame->is_key() && !WriteEbmlElement(writer, libwebm::kMkvReferenceBlock, + reference_block_timestamp)) { + return false; + } + + if (duration > 0 && + !WriteEbmlElement(writer, libwebm::kMkvBlockDuration, duration)) { + return false; + } + return EbmlMasterElementSize(libwebm::kMkvBlockGroup, + block_group_payload_size) + + block_group_payload_size; +} + +uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame, + int64 timecode) { + if (WriteID(writer, libwebm::kMkvSimpleBlock)) + return 0; + + const int32 size = static_cast(frame->length()) + 4; + if (WriteUInt(writer, size)) + return 0; + + if (WriteUInt(writer, static_cast(frame->track_number()))) + return 0; + + if (SerializeInt(writer, timecode, 2)) + return 0; + + uint64 flags = 0; + if (frame->is_key()) + flags |= 0x80; + + if (SerializeInt(writer, flags, 1)) + return 0; + + if (writer->Write(frame->frame(), static_cast(frame->length()))) + return 0; + + return GetUIntSize(libwebm::kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + + frame->length(); +} + +} // namespace + +int32 GetCodedUIntSize(uint64 value) { + if (value < 0x000000000000007FULL) + return 1; + else if (value < 0x0000000000003FFFULL) + return 2; + else if (value < 0x00000000001FFFFFULL) + return 3; + else if (value < 0x000000000FFFFFFFULL) + return 4; + else if (value < 0x00000007FFFFFFFFULL) + return 5; + else if (value < 0x000003FFFFFFFFFFULL) + return 6; + else if (value < 0x0001FFFFFFFFFFFFULL) + return 7; + return 8; +} + +int32 GetUIntSize(uint64 value) { + if (value < 0x0000000000000100ULL) + return 1; + else if (value < 0x0000000000010000ULL) + return 2; + else if (value < 0x0000000001000000ULL) + return 3; + else if (value < 0x0000000100000000ULL) + return 4; + else if (value < 0x0000010000000000ULL) + return 5; + else if (value < 0x0001000000000000ULL) + return 6; + else if (value < 0x0100000000000000ULL) + return 7; + return 8; +} + +int32 GetIntSize(int64 value) { + // Doubling the requested value ensures positive values with their high bit + // set are written with 0-padding to avoid flipping the signedness. + const uint64 v = (value < 0) ? value ^ -1LL : value; + return GetUIntSize(2 * v); +} + +uint64 EbmlMasterElementSize(uint64 type, uint64 value) { + // Size of EBML ID + int32 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += GetCodedUIntSize(value); + + return ebml_size; +} + +uint64 EbmlElementSize(uint64 type, int64 value) { + // Size of EBML ID + int32 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += GetIntSize(value); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64 EbmlElementSize(uint64 type, uint64 value) { + return EbmlElementSize(type, value, 0); +} + +uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size) { + // Size of EBML ID + uint64 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += (fixed_size > 0) ? fixed_size : GetUIntSize(value); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64 EbmlElementSize(uint64 type, float /* value */) { + // Size of EBML ID + uint64 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += sizeof(float); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64 EbmlElementSize(uint64 type, const char* value) { + if (!value) + return 0; + + // Size of EBML ID + uint64 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += strlen(value); + + // Size of Datasize + ebml_size += GetCodedUIntSize(strlen(value)); + + return ebml_size; +} + +uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) { + if (!value) + return 0; + + // Size of EBML ID + uint64 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += size; + + // Size of Datasize + ebml_size += GetCodedUIntSize(size); + + return ebml_size; +} + +uint64 EbmlDateElementSize(uint64 type) { + // Size of EBML ID + uint64 ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += kDateElementSize; + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size) { + if (!writer || size < 1 || size > 8) + return -1; + + for (int32 i = 1; i <= size; ++i) { + const int32 byte_count = size - i; + const int32 bit_count = byte_count * 8; + + const int64 bb = value >> bit_count; + const uint8 b = static_cast(bb); + + const int32 status = writer->Write(&b, 1); + + if (status < 0) + return status; + } + + return 0; +} + +int32 SerializeFloat(IMkvWriter* writer, float f) { + if (!writer) + return -1; + + assert(sizeof(uint32) == sizeof(float)); + // This union is merely used to avoid a reinterpret_cast from float& to + // uint32& which will result in violation of strict aliasing. + union U32 { + uint32 u32; + float f; + } value; + value.f = f; + + for (int32 i = 1; i <= 4; ++i) { + const int32 byte_count = 4 - i; + const int32 bit_count = byte_count * 8; + + const uint8 byte = static_cast(value.u32 >> bit_count); + + const int32 status = writer->Write(&byte, 1); + + if (status < 0) + return status; + } + + return 0; +} + +int32 WriteUInt(IMkvWriter* writer, uint64 value) { + if (!writer) + return -1; + + int32 size = GetCodedUIntSize(value); + + return WriteUIntSize(writer, value, size); +} + +int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) { + if (!writer || size < 0 || size > 8) + return -1; + + if (size > 0) { + const uint64 bit = 1LL << (size * 7); + + if (value > (bit - 2)) + return -1; + + value |= bit; + } else { + size = 1; + int64 bit; + + for (;;) { + bit = 1LL << (size * 7); + const uint64 max = bit - 2; + + if (value <= max) + break; + + ++size; + } + + if (size > 8) + return false; + + value |= bit; + } + + return SerializeInt(writer, value, size); +} + +int32 WriteID(IMkvWriter* writer, uint64 type) { + if (!writer) + return -1; + + writer->ElementStartNotify(type, writer->Position()); + + const int32 size = GetUIntSize(type); + + return SerializeInt(writer, type, size); +} + +bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, size)) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) { + return WriteEbmlElement(writer, type, value, 0); +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value, + uint64 fixed_size) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + uint64 size = GetUIntSize(value); + if (fixed_size > 0) { + if (size > fixed_size) + return false; + size = fixed_size; + } + if (WriteUInt(writer, size)) + return false; + + if (SerializeInt(writer, value, static_cast(size))) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return 0; + + const uint64 size = GetIntSize(value); + if (WriteUInt(writer, size)) + return false; + + if (SerializeInt(writer, value, static_cast(size))) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, 4)) + return false; + + if (SerializeFloat(writer, value)) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) { + if (!writer || !value) + return false; + + if (WriteID(writer, type)) + return false; + + const uint64 length = strlen(value); + if (WriteUInt(writer, length)) + return false; + + if (writer->Write(value, static_cast(length))) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value, + uint64 size) { + if (!writer || !value || size < 1) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, size)) + return false; + + if (writer->Write(value, static_cast(size))) + return false; + + return true; +} + +bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, kDateElementSize)) + return false; + + if (SerializeInt(writer, value, kDateElementSize)) + return false; + + return true; +} + +uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame, + Cluster* cluster) { + if (!writer || !frame || !frame->IsValid() || !cluster || + !cluster->timecode_scale()) + return 0; + + // Technically the timecode for a block can be less than the + // timecode for the cluster itself (remember that block timecode + // is a signed, 16-bit integer). However, as a simplification we + // only permit non-negative cluster-relative timecodes for blocks. + const int64 relative_timecode = cluster->GetRelativeTimecode( + frame->timestamp() / cluster->timecode_scale()); + if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode) + return 0; + + return frame->CanBeSimpleBlock() + ? WriteSimpleBlock(writer, frame, relative_timecode) + : WriteBlock(writer, frame, relative_timecode, + cluster->timecode_scale()); +} + +uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) { + if (!writer) + return false; + + // Subtract one for the void ID and the coded size. + uint64 void_entry_size = size - 1 - GetCodedUIntSize(size - 1); + uint64 void_size = EbmlMasterElementSize(libwebm::kMkvVoid, void_entry_size) + + void_entry_size; + + if (void_size != size) + return 0; + + const int64 payload_position = writer->Position(); + if (payload_position < 0) + return 0; + + if (WriteID(writer, libwebm::kMkvVoid)) + return 0; + + if (WriteUInt(writer, void_entry_size)) + return 0; + + const uint8 value = 0; + for (int32 i = 0; i < static_cast(void_entry_size); ++i) { + if (writer->Write(&value, 1)) + return 0; + } + + const int64 stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast(void_size)) + return 0; + + return void_size; +} + +void GetVersion(int32* major, int32* minor, int32* build, int32* revision) { + *major = 0; + *minor = 3; + *build = 0; + *revision = 0; +} + +uint64 MakeUID(unsigned int* seed) { + uint64 uid = 0; + +#ifdef __MINGW32__ + srand(*seed); +#endif + + for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values + uid <<= 8; + +// TODO(fgalligan): Move random number generation to platform specific code. +#ifdef _MSC_VER + (void)seed; + const int32 nn = rand(); +#elif __ANDROID__ + (void)seed; + int32 temp_num = 1; + int fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + read(fd, &temp_num, sizeof(temp_num)); + close(fd); + } + const int32 nn = temp_num; +#elif defined __MINGW32__ + const int32 nn = rand(); +#else + const int32 nn = rand_r(seed); +#endif + const int32 n = 0xFF & (nn >> 4); // throw away low-order bits + + uid |= n; + } + + return uid; +} + +bool IsMatrixCoefficientsValueValid(uint64_t value) { + switch (value) { + case mkvmuxer::Colour::kGbr: + case mkvmuxer::Colour::kBt709: + case mkvmuxer::Colour::kUnspecifiedMc: + case mkvmuxer::Colour::kReserved: + case mkvmuxer::Colour::kFcc: + case mkvmuxer::Colour::kBt470bg: + case mkvmuxer::Colour::kSmpte170MMc: + case mkvmuxer::Colour::kSmpte240MMc: + case mkvmuxer::Colour::kYcocg: + case mkvmuxer::Colour::kBt2020NonConstantLuminance: + case mkvmuxer::Colour::kBt2020ConstantLuminance: + return true; + } + return false; +} + +bool IsChromaSitingHorzValueValid(uint64_t value) { + switch (value) { + case mkvmuxer::Colour::kUnspecifiedCsh: + case mkvmuxer::Colour::kLeftCollocated: + case mkvmuxer::Colour::kHalfCsh: + return true; + } + return false; +} + +bool IsChromaSitingVertValueValid(uint64_t value) { + switch (value) { + case mkvmuxer::Colour::kUnspecifiedCsv: + case mkvmuxer::Colour::kTopCollocated: + case mkvmuxer::Colour::kHalfCsv: + return true; + } + return false; +} + +bool IsColourRangeValueValid(uint64_t value) { + switch (value) { + case mkvmuxer::Colour::kUnspecifiedCr: + case mkvmuxer::Colour::kBroadcastRange: + case mkvmuxer::Colour::kFullRange: + case mkvmuxer::Colour::kMcTcDefined: + return true; + } + return false; +} + +bool IsTransferCharacteristicsValueValid(uint64_t value) { + switch (value) { + case mkvmuxer::Colour::kIturBt709Tc: + case mkvmuxer::Colour::kUnspecifiedTc: + case mkvmuxer::Colour::kReservedTc: + case mkvmuxer::Colour::kGamma22Curve: + case mkvmuxer::Colour::kGamma28Curve: + case mkvmuxer::Colour::kSmpte170MTc: + case mkvmuxer::Colour::kSmpte240MTc: + case mkvmuxer::Colour::kLinear: + case mkvmuxer::Colour::kLog: + case mkvmuxer::Colour::kLogSqrt: + case mkvmuxer::Colour::kIec6196624: + case mkvmuxer::Colour::kIturBt1361ExtendedColourGamut: + case mkvmuxer::Colour::kIec6196621: + case mkvmuxer::Colour::kIturBt202010bit: + case mkvmuxer::Colour::kIturBt202012bit: + case mkvmuxer::Colour::kSmpteSt2084: + case mkvmuxer::Colour::kSmpteSt4281Tc: + case mkvmuxer::Colour::kAribStdB67Hlg: + return true; + } + return false; +} + +bool IsPrimariesValueValid(uint64_t value) { + switch (value) { + case mkvmuxer::Colour::kReservedP0: + case mkvmuxer::Colour::kIturBt709P: + case mkvmuxer::Colour::kUnspecifiedP: + case mkvmuxer::Colour::kReservedP3: + case mkvmuxer::Colour::kIturBt470M: + case mkvmuxer::Colour::kIturBt470Bg: + case mkvmuxer::Colour::kSmpte170MP: + case mkvmuxer::Colour::kSmpte240MP: + case mkvmuxer::Colour::kFilm: + case mkvmuxer::Colour::kIturBt2020: + case mkvmuxer::Colour::kSmpteSt4281P: + case mkvmuxer::Colour::kJedecP22Phosphors: + return true; + } + return false; +} + +} // namespace mkvmuxer diff --git a/mkvmuxer/mkvmuxerutil.h b/mkvmuxer/mkvmuxerutil.h new file mode 100644 index 000000000000..3355428bd1e5 --- /dev/null +++ b/mkvmuxer/mkvmuxerutil.h @@ -0,0 +1,115 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef MKVMUXER_MKVMUXERUTIL_H_ +#define MKVMUXER_MKVMUXERUTIL_H_ + +#include "mkvmuxertypes.h" + +#include "stdint.h" + +namespace mkvmuxer { +class Cluster; +class Frame; +class IMkvWriter; + +// TODO(tomfinegan): mkvmuxer:: integer types continue to be used here because +// changing them causes pain for downstream projects. It would be nice if a +// solution that allows removal of the mkvmuxer:: integer types while avoiding +// pain for downstream users of libwebm. Considering that mkvmuxerutil.{cc,h} +// are really, for the great majority of cases, EBML size calculation and writer +// functions, perhaps a more EBML focused utility would be the way to go as a +// first step. + +const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL; +const int64 kMaxBlockTimecode = 0x07FFFLL; + +// Writes out |value| in Big Endian order. Returns 0 on success. +int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size); + +// Writes out |f| in Big Endian order. Returns 0 on success. +int32 SerializeFloat(IMkvWriter* writer, float f); + +// Returns the size in bytes of the element. +int32 GetUIntSize(uint64 value); +int32 GetIntSize(int64 value); +int32 GetCodedUIntSize(uint64 value); +uint64 EbmlMasterElementSize(uint64 type, uint64 value); +uint64 EbmlElementSize(uint64 type, int64 value); +uint64 EbmlElementSize(uint64 type, uint64 value); +uint64 EbmlElementSize(uint64 type, float value); +uint64 EbmlElementSize(uint64 type, const char* value); +uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size); +uint64 EbmlDateElementSize(uint64 type); + +// Returns the size in bytes of the element assuming that the element was +// written using |fixed_size| bytes. If |fixed_size| is set to zero, then it +// computes the necessary number of bytes based on |value|. +uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size); + +// Creates an EBML coded number from |value| and writes it out. The size of +// the coded number is determined by the value of |value|. |value| must not +// be in a coded form. Returns 0 on success. +int32 WriteUInt(IMkvWriter* writer, uint64 value); + +// Creates an EBML coded number from |value| and writes it out. The size of +// the coded number is determined by the value of |size|. |value| must not +// be in a coded form. Returns 0 on success. +int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size); + +// Output an Mkv master element. Returns true if the element was written. +bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 value, uint64 size); + +// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the +// ID to |SerializeInt|. Returns 0 on success. +int32 WriteID(IMkvWriter* writer, uint64 type); + +// Output an Mkv non-master element. Returns true if the element was written. +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value); +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value); +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value); +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value); +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value, + uint64 size); +bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value); + +// Output an Mkv non-master element using fixed size. The element will be +// written out using exactly |fixed_size| bytes. If |fixed_size| is set to zero +// then it computes the necessary number of bytes based on |value|. Returns true +// if the element was written. +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value, + uint64 fixed_size); + +// Output a Mkv Frame. It decides the correct element to write (Block vs +// SimpleBlock) based on the parameters of the Frame. +uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame, + Cluster* cluster); + +// Output a void element. |size| must be the entire size in bytes that will be +// void. The function will calculate the size of the void header and subtract +// it from |size|. +uint64 WriteVoidElement(IMkvWriter* writer, uint64 size); + +// Returns the version number of the muxer in |major|, |minor|, |build|, +// and |revision|. +void GetVersion(int32* major, int32* minor, int32* build, int32* revision); + +// Returns a random number to be used for UID, using |seed| to seed +// the random-number generator (see POSIX rand_r() for semantics). +uint64 MakeUID(unsigned int* seed); + +// Colour field validation helpers. All return true when |value| is valid. +bool IsMatrixCoefficientsValueValid(uint64_t value); +bool IsChromaSitingHorzValueValid(uint64_t value); +bool IsChromaSitingVertValueValid(uint64_t value); +bool IsColourRangeValueValid(uint64_t value); +bool IsTransferCharacteristicsValueValid(uint64_t value); +bool IsPrimariesValueValid(uint64_t value); + +} // namespace mkvmuxer + +#endif // MKVMUXER_MKVMUXERUTIL_H_ diff --git a/mkvmuxer/mkvwriter.cc b/mkvmuxer/mkvwriter.cc new file mode 100644 index 000000000000..d668384d85f4 --- /dev/null +++ b/mkvmuxer/mkvwriter.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "mkvmuxer/mkvwriter.h" + +#include + +#ifdef _MSC_VER +#include // for _SH_DENYWR +#endif + +namespace mkvmuxer { + +MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {} + +MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {} + +MkvWriter::~MkvWriter() { Close(); } + +int32 MkvWriter::Write(const void* buffer, uint32 length) { + if (!file_) + return -1; + + if (length == 0) + return 0; + + if (buffer == NULL) + return -1; + + const size_t bytes_written = fwrite(buffer, 1, length, file_); + + return (bytes_written == length) ? 0 : -1; +} + +bool MkvWriter::Open(const char* filename) { + if (filename == NULL) + return false; + + if (file_) + return false; + +#ifdef _MSC_VER + file_ = _fsopen(filename, "wb", _SH_DENYWR); +#else + file_ = fopen(filename, "wb"); +#endif + if (file_ == NULL) + return false; + return true; +} + +void MkvWriter::Close() { + if (file_ && writer_owns_file_) { + fclose(file_); + } + file_ = NULL; +} + +int64 MkvWriter::Position() const { + if (!file_) + return 0; + +#ifdef _MSC_VER + return _ftelli64(file_); +#else + return ftell(file_); +#endif +} + +int32 MkvWriter::Position(int64 position) { + if (!file_) + return -1; + +#ifdef _MSC_VER + return _fseeki64(file_, position, SEEK_SET); +#elif defined(_WIN32) + return fseeko64(file_, static_cast(position), SEEK_SET); +#else + return fseeko(file_, static_cast(position), SEEK_SET); +#endif +} + +bool MkvWriter::Seekable() const { return true; } + +void MkvWriter::ElementStartNotify(uint64, int64) {} + +} // namespace mkvmuxer diff --git a/mkvmuxer/mkvwriter.h b/mkvmuxer/mkvwriter.h new file mode 100644 index 000000000000..4227c63748aa --- /dev/null +++ b/mkvmuxer/mkvwriter.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef MKVMUXER_MKVWRITER_H_ +#define MKVMUXER_MKVWRITER_H_ + +#include + +#include "mkvmuxer/mkvmuxer.h" +#include "mkvmuxer/mkvmuxertypes.h" + +namespace mkvmuxer { + +// Default implementation of the IMkvWriter interface on Windows. +class MkvWriter : public IMkvWriter { + public: + MkvWriter(); + explicit MkvWriter(FILE* fp); + virtual ~MkvWriter(); + + // IMkvWriter interface + virtual int64 Position() const; + virtual int32 Position(int64 position); + virtual bool Seekable() const; + virtual int32 Write(const void* buffer, uint32 length); + virtual void ElementStartNotify(uint64 element_id, int64 position); + + // Creates and opens a file for writing. |filename| is the name of the file + // to open. This function will overwrite the contents of |filename|. Returns + // true on success. + bool Open(const char* filename); + + // Closes an opened file. + void Close(); + + private: + // File handle to output file. + FILE* file_; + bool writer_owns_file_; + + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter); +}; + +} // namespace mkvmuxer + +#endif // MKVMUXER_MKVWRITER_H_ diff --git a/mkvmuxer_sample.cc b/mkvmuxer_sample.cc new file mode 100644 index 000000000000..0b84d7255634 --- /dev/null +++ b/mkvmuxer_sample.cc @@ -0,0 +1,802 @@ +// Copyright (c) 2011 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include + +#include +#include +#include +#include +#include +#include + +// libwebm common includes. +#include "common/file_util.h" +#include "common/hdr_util.h" + +// libwebm mkvparser includes +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" + +// libwebm mkvmuxer includes +#include "mkvmuxer/mkvmuxer.h" +#include "mkvmuxer/mkvmuxertypes.h" +#include "mkvmuxer/mkvwriter.h" + +#include "sample_muxer_metadata.h" + +namespace { + +void Usage() { + printf("Usage: mkvmuxer_sample -i input -o output [options]\n"); + printf("\n"); + printf("Main options:\n"); + printf(" -h | -? show help\n"); + printf(" -video >0 outputs video\n"); + printf(" -audio >0 outputs audio\n"); + printf(" -live >0 puts the muxer into live mode\n"); + printf(" 0 puts the muxer into file mode\n"); + printf(" -output_cues >0 outputs cues element\n"); + printf(" -cues_on_video_track >0 outputs cues on video track\n"); + printf(" -cues_on_audio_track >0 outputs cues on audio track\n"); + printf(" -max_cluster_duration in seconds\n"); + printf(" -max_cluster_size in bytes\n"); + printf(" -switch_tracks >0 switches tracks in output\n"); + printf(" -audio_track_number >0 Changes the audio track number\n"); + printf(" -video_track_number >0 Changes the video track number\n"); + printf(" -chunking Chunk output\n"); + printf(" -copy_tags >0 Copies the tags\n"); + printf(" -accurate_cluster_duration "); + printf(">0 Writes the last frame in each cluster with Duration\n"); + printf(" -fixed_size_cluster_timecode "); + printf(">0 Writes the cluster timecode using exactly 8 bytes\n"); + printf(" -copy_input_duration >0 Copies the input duration\n"); + printf("\n"); + printf("Video options:\n"); + printf(" -display_width Display width in pixels\n"); + printf(" -display_height Display height in pixels\n"); + printf(" -pixel_width Override pixel width\n"); + printf(" -pixel_height Override pixel height\n"); + printf(" -projection_type Set/override projection type:\n"); + printf(" 0: Rectangular\n"); + printf(" 1: Equirectangular\n"); + printf(" 2: Cube map\n"); + printf(" 3: Mesh\n"); + printf(" -projection_file Override projection private data\n"); + printf(" with contents of this file\n"); + printf(" -projection_pose_yaw Projection pose yaw\n"); + printf(" -projection_pose_pitch Projection pose pitch\n"); + printf(" -projection_pose_roll Projection pose roll\n"); + printf(" -stereo_mode 3D video mode\n"); + printf("\n"); + printf("VP9 options:\n"); + printf(" -profile VP9 profile\n"); + printf(" -level VP9 level\n"); + printf("\n"); + printf("Cues options:\n"); + printf(" -output_cues_block_number >0 outputs cue block number\n"); + printf(" -cues_before_clusters >0 puts Cues before Clusters\n"); + printf("\n"); + printf("Metadata options:\n"); + printf(" -webvtt-subtitles "); + printf("add WebVTT subtitles as metadata track\n"); + printf(" -webvtt-captions "); + printf("add WebVTT captions as metadata track\n"); + printf(" -webvtt-descriptions "); + printf("add WebVTT descriptions as metadata track\n"); + printf(" -webvtt-metadata "); + printf("add WebVTT subtitles as metadata track\n"); + printf(" -webvtt-chapters "); + printf("add WebVTT chapters as MKV chapters element\n"); +} + +struct MetadataFile { + const char* name; + SampleMuxerMetadata::Kind kind; +}; + +typedef std::list metadata_files_t; + +// Cache the WebVTT filenames specified as command-line args. +bool LoadMetadataFiles(const metadata_files_t& files, + SampleMuxerMetadata* metadata) { + typedef metadata_files_t::const_iterator iter_t; + + iter_t i = files.begin(); + const iter_t j = files.end(); + + while (i != j) { + const metadata_files_t::value_type& v = *i++; + + if (!metadata->Load(v.name, v.kind)) + return false; + } + + return true; +} + +int ParseArgWebVTT(char* argv[], int* argv_index, int argc_check, + metadata_files_t* metadata_files) { + int& i = *argv_index; + + enum { kCount = 5 }; + struct Arg { + const char* name; + SampleMuxerMetadata::Kind kind; + }; + const Arg args[kCount] = { + {"-webvtt-subtitles", SampleMuxerMetadata::kSubtitles}, + {"-webvtt-captions", SampleMuxerMetadata::kCaptions}, + {"-webvtt-descriptions", SampleMuxerMetadata::kDescriptions}, + {"-webvtt-metadata", SampleMuxerMetadata::kMetadata}, + {"-webvtt-chapters", SampleMuxerMetadata::kChapters}}; + + for (int idx = 0; idx < kCount; ++idx) { + const Arg& arg = args[idx]; + + if (strcmp(arg.name, argv[i]) != 0) // no match + continue; + + ++i; // consume arg name here + + if (i > argc_check) { + printf("missing value for %s\n", arg.name); + return -1; // error + } + + MetadataFile f; + f.name = argv[i]; // arg value is consumed via caller's loop idx + f.kind = arg.kind; + + metadata_files->push_back(f); + return 1; // successfully parsed WebVTT arg + } + + return 0; // not a WebVTT arg +} + +bool CopyVideoProjection(const mkvparser::Projection& parser_projection, + mkvmuxer::Projection* muxer_projection) { + typedef mkvmuxer::Projection::ProjectionType MuxerProjType; + const int kTypeNotPresent = mkvparser::Projection::kTypeNotPresent; + if (parser_projection.type != kTypeNotPresent) { + muxer_projection->set_type( + static_cast(parser_projection.type)); + } + if (parser_projection.private_data && + parser_projection.private_data_length > 0) { + if (!muxer_projection->SetProjectionPrivate( + parser_projection.private_data, + parser_projection.private_data_length)) { + return false; + } + } + + const float kValueNotPresent = mkvparser::Projection::kValueNotPresent; + if (parser_projection.pose_yaw != kValueNotPresent) + muxer_projection->set_pose_yaw(parser_projection.pose_yaw); + if (parser_projection.pose_pitch != kValueNotPresent) + muxer_projection->set_pose_pitch(parser_projection.pose_pitch); + if (parser_projection.pose_roll != kValueNotPresent) + muxer_projection->set_pose_roll(parser_projection.pose_roll); + return true; +} +} // end namespace + +int main(int argc, char* argv[]) { + char* input = NULL; + char* output = NULL; + + // Segment variables + bool output_video = true; + bool output_audio = true; + bool live_mode = false; + bool output_cues = true; + bool cues_before_clusters = false; + bool cues_on_video_track = true; + bool cues_on_audio_track = false; + uint64_t max_cluster_duration = 0; + uint64_t max_cluster_size = 0; + bool switch_tracks = false; + int audio_track_number = 0; // 0 tells muxer to decide. + int video_track_number = 0; // 0 tells muxer to decide. + bool chunking = false; + bool copy_tags = false; + const char* chunk_name = NULL; + bool accurate_cluster_duration = false; + bool fixed_size_cluster_timecode = false; + bool copy_input_duration = false; + + bool output_cues_block_number = true; + + uint64_t display_width = 0; + uint64_t display_height = 0; + uint64_t pixel_width = 0; + uint64_t pixel_height = 0; + uint64_t stereo_mode = 0; + const char* projection_file = 0; + int64_t projection_type = mkvparser::Projection::kTypeNotPresent; + float projection_pose_roll = mkvparser::Projection::kValueNotPresent; + float projection_pose_pitch = mkvparser::Projection::kValueNotPresent; + float projection_pose_yaw = mkvparser::Projection::kValueNotPresent; + int vp9_profile = -1; // No profile set. + int vp9_level = -1; // No level set. + + metadata_files_t metadata_files; + + const int argc_check = argc - 1; + for (int i = 1; i < argc; ++i) { + char* end; + + if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) { + Usage(); + return EXIT_SUCCESS; + } else if (!strcmp("-i", argv[i]) && i < argc_check) { + input = argv[++i]; + } else if (!strcmp("-o", argv[i]) && i < argc_check) { + output = argv[++i]; + } else if (!strcmp("-video", argv[i]) && i < argc_check) { + output_video = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-audio", argv[i]) && i < argc_check) { + output_audio = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-live", argv[i]) && i < argc_check) { + live_mode = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-output_cues", argv[i]) && i < argc_check) { + output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-cues_before_clusters", argv[i]) && i < argc_check) { + cues_before_clusters = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-cues_on_video_track", argv[i]) && i < argc_check) { + cues_on_video_track = strtol(argv[++i], &end, 10) == 0 ? false : true; + if (cues_on_video_track) + cues_on_audio_track = false; + } else if (!strcmp("-cues_on_audio_track", argv[i]) && i < argc_check) { + cues_on_audio_track = strtol(argv[++i], &end, 10) == 0 ? false : true; + if (cues_on_audio_track) + cues_on_video_track = false; + } else if (!strcmp("-max_cluster_duration", argv[i]) && i < argc_check) { + const double seconds = strtod(argv[++i], &end); + max_cluster_duration = static_cast(seconds * 1000000000.0); + } else if (!strcmp("-max_cluster_size", argv[i]) && i < argc_check) { + max_cluster_size = strtol(argv[++i], &end, 10); + } else if (!strcmp("-switch_tracks", argv[i]) && i < argc_check) { + switch_tracks = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-audio_track_number", argv[i]) && i < argc_check) { + audio_track_number = static_cast(strtol(argv[++i], &end, 10)); + } else if (!strcmp("-video_track_number", argv[i]) && i < argc_check) { + video_track_number = static_cast(strtol(argv[++i], &end, 10)); + } else if (!strcmp("-chunking", argv[i]) && i < argc_check) { + chunking = true; + chunk_name = argv[++i]; + } else if (!strcmp("-copy_tags", argv[i]) && i < argc_check) { + copy_tags = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-accurate_cluster_duration", argv[i]) && + i < argc_check) { + accurate_cluster_duration = + strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-fixed_size_cluster_timecode", argv[i]) && + i < argc_check) { + fixed_size_cluster_timecode = + strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-copy_input_duration", argv[i]) && i < argc_check) { + copy_input_duration = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-display_width", argv[i]) && i < argc_check) { + display_width = strtol(argv[++i], &end, 10); + } else if (!strcmp("-display_height", argv[i]) && i < argc_check) { + display_height = strtol(argv[++i], &end, 10); + } else if (!strcmp("-pixel_width", argv[i]) && i < argc_check) { + pixel_width = strtol(argv[++i], &end, 10); + } else if (!strcmp("-pixel_height", argv[i]) && i < argc_check) { + pixel_height = strtol(argv[++i], &end, 10); + } else if (!strcmp("-stereo_mode", argv[i]) && i < argc_check) { + stereo_mode = strtol(argv[++i], &end, 10); + } else if (!strcmp("-projection_type", argv[i]) && i < argc_check) { + projection_type = strtol(argv[++i], &end, 10); + } else if (!strcmp("-projection_file", argv[i]) && i < argc_check) { + projection_file = argv[++i]; + } else if (!strcmp("-projection_pose_roll", argv[i]) && i < argc_check) { + projection_pose_roll = strtof(argv[++i], &end); + } else if (!strcmp("-projection_pose_pitch", argv[i]) && i < argc_check) { + projection_pose_pitch = strtof(argv[++i], &end); + } else if (!strcmp("-projection_pose_yaw", argv[i]) && i < argc_check) { + projection_pose_yaw = strtof(argv[++i], &end); + } else if (!strcmp("-profile", argv[i]) && i < argc_check) { + vp9_profile = static_cast(strtol(argv[++i], &end, 10)); + } else if (!strcmp("-level", argv[i]) && i < argc_check) { + vp9_level = static_cast(strtol(argv[++i], &end, 10)); + } else if (!strcmp("-output_cues_block_number", argv[i]) && + i < argc_check) { + output_cues_block_number = + strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (int e = ParseArgWebVTT(argv, &i, argc_check, &metadata_files)) { + if (e < 0) + return EXIT_FAILURE; + } + } + + if (input == NULL || output == NULL) { + Usage(); + return EXIT_FAILURE; + } + + // Get parser header info + mkvparser::MkvReader reader; + + if (reader.Open(input)) { + printf("\n Filename is invalid or error while opening.\n"); + return EXIT_FAILURE; + } + + long long pos = 0; + mkvparser::EBMLHeader ebml_header; + long long ret = ebml_header.Parse(&reader, pos); + if (ret) { + printf("\n EBMLHeader::Parse() failed."); + return EXIT_FAILURE; + } + + mkvparser::Segment* parser_segment_; + ret = mkvparser::Segment::CreateInstance(&reader, pos, parser_segment_); + if (ret) { + printf("\n Segment::CreateInstance() failed."); + return EXIT_FAILURE; + } + + const std::unique_ptr parser_segment(parser_segment_); + ret = parser_segment->Load(); + if (ret < 0) { + printf("\n Segment::Load() failed."); + return EXIT_FAILURE; + } + + const mkvparser::SegmentInfo* const segment_info = parser_segment->GetInfo(); + if (segment_info == NULL) { + printf("\n Segment::GetInfo() failed."); + return EXIT_FAILURE; + } + const long long timeCodeScale = segment_info->GetTimeCodeScale(); + + // Set muxer header info + mkvmuxer::MkvWriter writer; + + const std::string temp_file = + cues_before_clusters ? libwebm::GetTempFileName() : output; + if (!writer.Open(temp_file.c_str())) { + printf("\n Filename is invalid or error while opening.\n"); + return EXIT_FAILURE; + } + + // Set Segment element attributes + mkvmuxer::Segment muxer_segment; + + if (!muxer_segment.Init(&writer)) { + printf("\n Could not initialize muxer segment!\n"); + return EXIT_FAILURE; + } + + muxer_segment.AccurateClusterDuration(accurate_cluster_duration); + muxer_segment.UseFixedSizeClusterTimecode(fixed_size_cluster_timecode); + + if (live_mode) + muxer_segment.set_mode(mkvmuxer::Segment::kLive); + else + muxer_segment.set_mode(mkvmuxer::Segment::kFile); + + if (chunking) + muxer_segment.SetChunking(true, chunk_name); + + if (max_cluster_duration > 0) + muxer_segment.set_max_cluster_duration(max_cluster_duration); + if (max_cluster_size > 0) + muxer_segment.set_max_cluster_size(max_cluster_size); + muxer_segment.OutputCues(output_cues); + + // Set SegmentInfo element attributes + mkvmuxer::SegmentInfo* const info = muxer_segment.GetSegmentInfo(); + info->set_timecode_scale(timeCodeScale); + info->set_writing_app("mkvmuxer_sample"); + + const mkvparser::Tags* const tags = parser_segment->GetTags(); + if (copy_tags && tags) { + for (int i = 0; i < tags->GetTagCount(); i++) { + const mkvparser::Tags::Tag* const tag = tags->GetTag(i); + mkvmuxer::Tag* muxer_tag = muxer_segment.AddTag(); + + for (int j = 0; j < tag->GetSimpleTagCount(); j++) { + const mkvparser::Tags::SimpleTag* const simple_tag = + tag->GetSimpleTag(j); + muxer_tag->add_simple_tag(simple_tag->GetTagName(), + simple_tag->GetTagString()); + } + } + } + + // Set Tracks element attributes + const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks(); + unsigned long i = 0; + uint64_t vid_track = 0; // no track added + uint64_t aud_track = 0; // no track added + + using mkvparser::Track; + + while (i != parser_tracks->GetTracksCount()) { + unsigned long track_num = i++; + if (switch_tracks) + track_num = i % parser_tracks->GetTracksCount(); + + const Track* const parser_track = parser_tracks->GetTrackByIndex(track_num); + + if (parser_track == NULL) + continue; + + // TODO(fgalligan): Add support for language to parser. + const char* const track_name = parser_track->GetNameAsUTF8(); + + const long long track_type = parser_track->GetType(); + + if (track_type == Track::kVideo && output_video) { + // Get the video track from the parser + const mkvparser::VideoTrack* const pVideoTrack = + static_cast(parser_track); + const long long width = pVideoTrack->GetWidth(); + const long long height = pVideoTrack->GetHeight(); + + // Add the video track to the muxer + vid_track = muxer_segment.AddVideoTrack(static_cast(width), + static_cast(height), + video_track_number); + if (!vid_track) { + printf("\n Could not add video track.\n"); + return EXIT_FAILURE; + } + + mkvmuxer::VideoTrack* const video = static_cast( + muxer_segment.GetTrackByNumber(vid_track)); + if (!video) { + printf("\n Could not get video track.\n"); + return EXIT_FAILURE; + } + + if (pVideoTrack->GetColour()) { + mkvmuxer::Colour muxer_colour; + if (!libwebm::CopyColour(*pVideoTrack->GetColour(), &muxer_colour)) + return EXIT_FAILURE; + if (!video->SetColour(muxer_colour)) + return EXIT_FAILURE; + } + + if (pVideoTrack->GetProjection() || + projection_type != mkvparser::Projection::kTypeNotPresent) { + mkvmuxer::Projection muxer_projection; + const mkvparser::Projection* const parser_projection = + pVideoTrack->GetProjection(); + typedef mkvmuxer::Projection::ProjectionType MuxerProjType; + if (parser_projection && + !CopyVideoProjection(*parser_projection, &muxer_projection)) { + printf("\n Unable to copy video projection.\n"); + return EXIT_FAILURE; + } + // Override the values that came from parser if set on command line. + if (projection_type != mkvparser::Projection::kTypeNotPresent) { + muxer_projection.set_type( + static_cast(projection_type)); + if (projection_type == mkvparser::Projection::kRectangular && + projection_file != NULL) { + printf("\n Rectangular projection must not have private data.\n"); + return EXIT_FAILURE; + } else if ((projection_type == mkvparser::Projection::kCubeMap || + projection_type == mkvparser::Projection::kMesh) && + projection_file == NULL) { + printf("\n Mesh or CubeMap projection must have private data.\n"); + return EXIT_FAILURE; + } + if (projection_file != NULL) { + std::string contents; + if (!libwebm::GetFileContents(projection_file, &contents) || + contents.size() == 0) { + printf("\n Failed to read file \"%s\" or file is empty\n", + projection_file); + return EXIT_FAILURE; + } + if (!muxer_projection.SetProjectionPrivate( + reinterpret_cast(&contents[0]), + contents.size())) { + printf("\n Failed to SetProjectionPrivate of length %zu.\n", + contents.size()); + return EXIT_FAILURE; + } + } + } + const float kValueNotPresent = mkvparser::Projection::kValueNotPresent; + if (projection_pose_yaw != kValueNotPresent) + muxer_projection.set_pose_yaw(projection_pose_yaw); + if (projection_pose_pitch != kValueNotPresent) + muxer_projection.set_pose_pitch(projection_pose_pitch); + if (projection_pose_roll != kValueNotPresent) + muxer_projection.set_pose_roll(projection_pose_roll); + + if (!video->SetProjection(muxer_projection)) + return EXIT_FAILURE; + } + + if (track_name) + video->set_name(track_name); + + video->set_codec_id(pVideoTrack->GetCodecId()); + + if (display_width > 0) + video->set_display_width(display_width); + if (display_height > 0) + video->set_display_height(display_height); + if (pixel_width > 0) + video->set_pixel_width(pixel_width); + if (pixel_height > 0) + video->set_pixel_height(pixel_height); + if (stereo_mode > 0) + video->SetStereoMode(stereo_mode); + + const double rate = pVideoTrack->GetFrameRate(); + if (rate > 0.0) { + video->set_frame_rate(rate); + } + + size_t parser_private_size; + const unsigned char* const parser_private_data = + pVideoTrack->GetCodecPrivate(parser_private_size); + + if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kAv1CodecId)) { + if (parser_private_data == NULL || parser_private_size == 0) { + printf("AV1 input track has no CodecPrivate. %s is invalid.", input); + return EXIT_FAILURE; + } + } + + if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kVp9CodecId) && + (vp9_profile >= 0 || vp9_level >= 0)) { + const int kMaxVp9PrivateSize = 6; + unsigned char vp9_private_data[kMaxVp9PrivateSize]; + int vp9_private_size = 0; + if (vp9_profile >= 0) { + if (vp9_profile < 0 || vp9_profile > 3) { + printf("\n VP9 profile(%d) is not valid.\n", vp9_profile); + return EXIT_FAILURE; + } + const uint8_t kVp9ProfileId = 1; + const uint8_t kVp9ProfileIdLength = 1; + vp9_private_data[vp9_private_size++] = kVp9ProfileId; + vp9_private_data[vp9_private_size++] = kVp9ProfileIdLength; + vp9_private_data[vp9_private_size++] = vp9_profile; + } + + if (vp9_level >= 0) { + const int kNumLevels = 14; + const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40, + 41, 50, 51, 52, 60, 61, 62}; + bool level_is_valid = false; + for (int i = 0; i < kNumLevels; ++i) { + if (vp9_level == levels[i]) { + level_is_valid = true; + break; + } + } + if (!level_is_valid) { + printf("\n VP9 level(%d) is not valid.\n", vp9_level); + return EXIT_FAILURE; + } + const uint8_t kVp9LevelId = 2; + const uint8_t kVp9LevelIdLength = 1; + vp9_private_data[vp9_private_size++] = kVp9LevelId; + vp9_private_data[vp9_private_size++] = kVp9LevelIdLength; + vp9_private_data[vp9_private_size++] = vp9_level; + } + if (!video->SetCodecPrivate(vp9_private_data, vp9_private_size)) { + printf("\n Could not add video private data.\n"); + return EXIT_FAILURE; + } + } else if (parser_private_data && parser_private_size > 0) { + if (!video->SetCodecPrivate(parser_private_data, parser_private_size)) { + printf("\n Could not add video private data.\n"); + return EXIT_FAILURE; + } + } + } else if (track_type == Track::kAudio && output_audio) { + // Get the audio track from the parser + const mkvparser::AudioTrack* const pAudioTrack = + static_cast(parser_track); + const long long channels = pAudioTrack->GetChannels(); + const double sample_rate = pAudioTrack->GetSamplingRate(); + + // Add the audio track to the muxer + aud_track = muxer_segment.AddAudioTrack(static_cast(sample_rate), + static_cast(channels), + audio_track_number); + if (!aud_track) { + printf("\n Could not add audio track.\n"); + return EXIT_FAILURE; + } + + mkvmuxer::AudioTrack* const audio = static_cast( + muxer_segment.GetTrackByNumber(aud_track)); + if (!audio) { + printf("\n Could not get audio track.\n"); + return EXIT_FAILURE; + } + + if (track_name) + audio->set_name(track_name); + + audio->set_codec_id(pAudioTrack->GetCodecId()); + + size_t private_size; + const unsigned char* const private_data = + pAudioTrack->GetCodecPrivate(private_size); + if (private_size > 0) { + if (!audio->SetCodecPrivate(private_data, private_size)) { + printf("\n Could not add audio private data.\n"); + return EXIT_FAILURE; + } + } + + const long long bit_depth = pAudioTrack->GetBitDepth(); + if (bit_depth > 0) + audio->set_bit_depth(bit_depth); + + if (pAudioTrack->GetCodecDelay()) + audio->set_codec_delay(pAudioTrack->GetCodecDelay()); + if (pAudioTrack->GetSeekPreRoll()) + audio->set_seek_pre_roll(pAudioTrack->GetSeekPreRoll()); + } + } + + // We have created all the video and audio tracks. If any WebVTT + // files were specified as command-line args, then parse them and + // add a track to the output file corresponding to each metadata + // input file. + + SampleMuxerMetadata metadata; + + if (!metadata.Init(&muxer_segment)) { + printf("\n Could not initialize metadata cache.\n"); + return EXIT_FAILURE; + } + + if (!LoadMetadataFiles(metadata_files, &metadata)) + return EXIT_FAILURE; + + if (!metadata.AddChapters()) + return EXIT_FAILURE; + + // Set Cues element attributes + mkvmuxer::Cues* const cues = muxer_segment.GetCues(); + cues->set_output_block_number(output_cues_block_number); + if (cues_on_video_track && vid_track) + muxer_segment.CuesTrack(vid_track); + if (cues_on_audio_track && aud_track) + muxer_segment.CuesTrack(aud_track); + + // Write clusters + unsigned char* data = NULL; + long data_len = 0; + + const mkvparser::Cluster* cluster = parser_segment->GetFirst(); + + while (cluster != NULL && !cluster->EOS()) { + const mkvparser::BlockEntry* block_entry; + + long status = cluster->GetFirst(block_entry); + + if (status) { + printf("\n Could not get first block of cluster.\n"); + return EXIT_FAILURE; + } + + while (block_entry != NULL && !block_entry->EOS()) { + const mkvparser::Block* const block = block_entry->GetBlock(); + const long long trackNum = block->GetTrackNumber(); + const mkvparser::Track* const parser_track = + parser_tracks->GetTrackByNumber(static_cast(trackNum)); + + // When |parser_track| is NULL, it means that the track number in the + // Block is invalid (i.e.) the was no TrackEntry corresponding to the + // track number. So we reject the file. + if (!parser_track) { + return EXIT_FAILURE; + } + + const long long track_type = parser_track->GetType(); + const long long time_ns = block->GetTime(cluster); + + // Flush any metadata frames to the output file, before we write + // the current block. + if (!metadata.Write(time_ns)) + return EXIT_FAILURE; + + if ((track_type == Track::kAudio && output_audio) || + (track_type == Track::kVideo && output_video)) { + const int frame_count = block->GetFrameCount(); + + for (int i = 0; i < frame_count; ++i) { + const mkvparser::Block::Frame& frame = block->GetFrame(i); + + if (frame.len > data_len) { + delete[] data; + data = new unsigned char[frame.len]; + if (!data) + return EXIT_FAILURE; + data_len = frame.len; + } + + if (frame.Read(&reader, data)) + return EXIT_FAILURE; + + mkvmuxer::Frame muxer_frame; + if (!muxer_frame.Init(data, frame.len)) + return EXIT_FAILURE; + muxer_frame.set_track_number(track_type == Track::kAudio ? aud_track + : vid_track); + if (block->GetDiscardPadding()) + muxer_frame.set_discard_padding(block->GetDiscardPadding()); + muxer_frame.set_timestamp(time_ns); + muxer_frame.set_is_key(block->IsKey()); + if (!muxer_segment.AddGenericFrame(&muxer_frame)) { + printf("\n Could not add frame.\n"); + return EXIT_FAILURE; + } + } + } + + status = cluster->GetNext(block_entry, block_entry); + + if (status) { + printf("\n Could not get next block of cluster.\n"); + return EXIT_FAILURE; + } + } + + cluster = parser_segment->GetNext(cluster); + } + + // We have exhausted all video and audio frames in the input file. + // Flush any remaining metadata frames to the output file. + if (!metadata.Write(-1)) + return EXIT_FAILURE; + + if (copy_input_duration) { + const double input_duration = + static_cast(segment_info->GetDuration()) / timeCodeScale; + muxer_segment.set_duration(input_duration); + } + + if (!muxer_segment.Finalize()) { + printf("Finalization of segment failed.\n"); + return EXIT_FAILURE; + } + + reader.Close(); + writer.Close(); + + if (cues_before_clusters) { + if (reader.Open(temp_file.c_str())) { + printf("\n Filename is invalid or error while opening.\n"); + return EXIT_FAILURE; + } + if (!writer.Open(output)) { + printf("\n Filename is invalid or error while opening.\n"); + return EXIT_FAILURE; + } + if (!muxer_segment.CopyAndMoveCuesBeforeClusters(&reader, &writer)) { + printf("\n Unable to copy and move cues before clusters.\n"); + return EXIT_FAILURE; + } + reader.Close(); + writer.Close(); + remove(temp_file.c_str()); + } + + delete[] data; + + return EXIT_SUCCESS; +} diff --git a/mkvmuxertypes.hpp b/mkvmuxertypes.hpp new file mode 100644 index 000000000000..78478f45c035 --- /dev/null +++ b/mkvmuxertypes.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_MKVMUXERTYPES_HPP_ +#define LIBWEBM_MKVMUXERTYPES_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "mkvmuxer/mkvmuxertypes.h" + +#endif // LIBWEBM_MKVMUXERTYPES_HPP_ diff --git a/mkvmuxerutil.hpp b/mkvmuxerutil.hpp new file mode 100644 index 000000000000..a26ba18a6115 --- /dev/null +++ b/mkvmuxerutil.hpp @@ -0,0 +1,18 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_MKVMUXERUTIL_HPP_ +#define LIBWEBM_MKVMUXERUTIL_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "mkvmuxer/mkvmuxerutil.h" + +using mkvmuxer::EbmlElementSize; +using mkvmuxer::EbmlMasterElementSize; + +#endif // LIBWEBM_MKVMUXERUTIL_HPP_ diff --git a/mkvparser.hpp b/mkvparser.hpp new file mode 100644 index 000000000000..3f86292041ff --- /dev/null +++ b/mkvparser.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_MKVPARSER_HPP_ +#define LIBWEBM_MKVPARSER_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "mkvparser/mkvparser.h" + +#endif // LIBWEBM_MKVPARSER_HPP_ diff --git a/mkvparser/mkvparser.cc b/mkvparser/mkvparser.cc new file mode 100644 index 000000000000..e7cf332cccda --- /dev/null +++ b/mkvparser/mkvparser.cc @@ -0,0 +1,8098 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "mkvparser/mkvparser.h" + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#include // _isnan() / _finite() +#define MSC_COMPAT +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "common/webmids.h" + +namespace mkvparser { +const long long kStringElementSizeLimit = 20 * 1000 * 1000; +const float MasteringMetadata::kValueNotPresent = FLT_MAX; +const long long Colour::kValueNotPresent = LLONG_MAX; +const float Projection::kValueNotPresent = FLT_MAX; + +#ifdef MSC_COMPAT +inline bool isnan(double val) { return !!_isnan(val); } +inline bool isinf(double val) { return !_finite(val); } +#else +inline bool isnan(double val) { return std::isnan(val); } +inline bool isinf(double val) { return std::isinf(val); } +#endif // MSC_COMPAT + +template +Type* SafeArrayAlloc(unsigned long long num_elements, + unsigned long long element_size) { + if (num_elements == 0 || element_size == 0) + return NULL; + + const size_t kMaxAllocSize = 0x80000000; // 2GiB + const unsigned long long num_bytes = num_elements * element_size; + if (element_size > (kMaxAllocSize / num_elements)) + return NULL; + if (num_bytes != static_cast(num_bytes)) + return NULL; + + return new (std::nothrow) Type[static_cast(num_bytes)]; +} + +void GetVersion(int& major, int& minor, int& build, int& revision) { + major = 1; + minor = 1; + build = 0; + revision = 0; +} + +long long ReadUInt(IMkvReader* pReader, long long pos, long& len) { + if (!pReader || pos < 0) + return E_FILE_FORMAT_INVALID; + + len = 1; + unsigned char b; + int status = pReader->Read(pos, 1, &b); + + if (status < 0) // error or underflow + return status; + + if (status > 0) // interpreted as "underflow" + return E_BUFFER_NOT_FULL; + + if (b == 0) // we can't handle u-int values larger than 8 bytes + return E_FILE_FORMAT_INVALID; + + unsigned char m = 0x80; + + while (!(b & m)) { + m >>= 1; + ++len; + } + + long long result = b & (~m); + ++pos; + + for (int i = 1; i < len; ++i) { + status = pReader->Read(pos, 1, &b); + + if (status < 0) { + len = 1; + return status; + } + + if (status > 0) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result <<= 8; + result |= b; + + ++pos; + } + + return result; +} + +// Reads an EBML ID and returns it. +// An ID must at least 1 byte long, cannot exceed 4, and its value must be +// greater than 0. +// See known EBML values and EBMLMaxIDLength: +// http://www.matroska.org/technical/specs/index.html +// Returns the ID, or a value less than 0 to report an error while reading the +// ID. +long long ReadID(IMkvReader* pReader, long long pos, long& len) { + if (pReader == NULL || pos < 0) + return E_FILE_FORMAT_INVALID; + + // Read the first byte. The length in bytes of the ID is determined by + // finding the first set bit in the first byte of the ID. + unsigned char temp_byte = 0; + int read_status = pReader->Read(pos, 1, &temp_byte); + + if (read_status < 0) + return E_FILE_FORMAT_INVALID; + else if (read_status > 0) // No data to read. + return E_BUFFER_NOT_FULL; + + if (temp_byte == 0) // ID length > 8 bytes; invalid file. + return E_FILE_FORMAT_INVALID; + + int bit_pos = 0; + const int kMaxIdLengthInBytes = 4; + const int kCheckByte = 0x80; + + // Find the first bit that's set. + bool found_bit = false; + for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) { + if ((kCheckByte >> bit_pos) & temp_byte) { + found_bit = true; + break; + } + } + + if (!found_bit) { + // The value is too large to be a valid ID. + return E_FILE_FORMAT_INVALID; + } + + // Read the remaining bytes of the ID (if any). + const int id_length = bit_pos + 1; + long long ebml_id = temp_byte; + for (int i = 1; i < id_length; ++i) { + ebml_id <<= 8; + read_status = pReader->Read(pos + i, 1, &temp_byte); + + if (read_status < 0) + return E_FILE_FORMAT_INVALID; + else if (read_status > 0) + return E_BUFFER_NOT_FULL; + + ebml_id |= temp_byte; + } + + len = id_length; + return ebml_id; +} + +long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) { + if (!pReader || pos < 0) + return E_FILE_FORMAT_INVALID; + + long long total, available; + + int status = pReader->Length(&total, &available); + if (status < 0 || (total >= 0 && available > total)) + return E_FILE_FORMAT_INVALID; + + len = 1; + + if (pos >= available) + return pos; // too few bytes available + + unsigned char b; + + status = pReader->Read(pos, 1, &b); + + if (status != 0) + return status; + + if (b == 0) // we can't handle u-int values larger than 8 bytes + return E_FILE_FORMAT_INVALID; + + unsigned char m = 0x80; + + while (!(b & m)) { + m >>= 1; + ++len; + } + + return 0; // success +} + +// TODO(vigneshv): This function assumes that unsigned values never have their +// high bit set. +long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) { + if (!pReader || pos < 0 || (size <= 0) || (size > 8)) + return E_FILE_FORMAT_INVALID; + + long long result = 0; + + for (long long i = 0; i < size; ++i) { + unsigned char b; + + const long status = pReader->Read(pos, 1, &b); + + if (status < 0) + return status; + + result <<= 8; + result |= b; + + ++pos; + } + + return result; +} + +long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_, + double& result) { + if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8))) + return E_FILE_FORMAT_INVALID; + + const long size = static_cast(size_); + + unsigned char buf[8]; + + const int status = pReader->Read(pos, size, buf); + + if (status < 0) // error + return status; + + if (size == 4) { + union { + float f; + unsigned long ff; + }; + + ff = 0; + + for (int i = 0;;) { + ff |= buf[i]; + + if (++i >= 4) + break; + + ff <<= 8; + } + + result = f; + } else { + union { + double d; + unsigned long long dd; + }; + + dd = 0; + + for (int i = 0;;) { + dd |= buf[i]; + + if (++i >= 8) + break; + + dd <<= 8; + } + + result = d; + } + + if (mkvparser::isinf(result) || mkvparser::isnan(result)) + return E_FILE_FORMAT_INVALID; + + return 0; +} + +long UnserializeInt(IMkvReader* pReader, long long pos, long long size, + long long& result_ref) { + if (!pReader || pos < 0 || size < 1 || size > 8) + return E_FILE_FORMAT_INVALID; + + signed char first_byte = 0; + const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte); + + if (status < 0) + return status; + + unsigned long long result = first_byte; + ++pos; + + for (long i = 1; i < size; ++i) { + unsigned char b; + + const long status = pReader->Read(pos, 1, &b); + + if (status < 0) + return status; + + result <<= 8; + result |= b; + + ++pos; + } + + result_ref = static_cast(result); + return 0; +} + +long UnserializeString(IMkvReader* pReader, long long pos, long long size, + char*& str) { + delete[] str; + str = NULL; + + if (size >= LONG_MAX || size < 0 || size > kStringElementSizeLimit) + return E_FILE_FORMAT_INVALID; + + // +1 for '\0' terminator + const long required_size = static_cast(size) + 1; + + str = SafeArrayAlloc(1, required_size); + if (str == NULL) + return E_FILE_FORMAT_INVALID; + + unsigned char* const buf = reinterpret_cast(str); + + const long status = pReader->Read(pos, static_cast(size), buf); + + if (status) { + delete[] str; + str = NULL; + + return status; + } + + str[required_size - 1] = '\0'; + return 0; +} + +long ParseElementHeader(IMkvReader* pReader, long long& pos, long long stop, + long long& id, long long& size) { + if (stop >= 0 && pos >= stop) + return E_FILE_FORMAT_INVALID; + + long len; + + id = ReadID(pReader, pos, len); + + if (id < 0) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume id + + if (stop >= 0 && pos >= stop) + return E_FILE_FORMAT_INVALID; + + size = ReadUInt(pReader, pos, len); + + if (size < 0 || len < 1 || len > 8) { + // Invalid: Negative payload size, negative or 0 length integer, or integer + // larger than 64 bits (libwebm cannot handle them). + return E_FILE_FORMAT_INVALID; + } + + // Avoid rolling over pos when very close to LLONG_MAX. + const unsigned long long rollover_check = + static_cast(pos) + len; + if (rollover_check > LLONG_MAX) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume length of size + + // pos now designates payload + + if (stop >= 0 && pos > stop) + return E_FILE_FORMAT_INVALID; + + return 0; // success +} + +bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id, + long long& val) { + if (!pReader || pos < 0) + return false; + + long long total = 0; + long long available = 0; + + const long status = pReader->Length(&total, &available); + if (status < 0 || (total >= 0 && available > total)) + return false; + + long len = 0; + + const long long id = ReadID(pReader, pos, len); + if (id < 0 || (available - pos) > len) + return false; + + if (static_cast(id) != expected_id) + return false; + + pos += len; // consume id + + const long long size = ReadUInt(pReader, pos, len); + if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len) + return false; + + pos += len; // consume length of size of payload + + val = UnserializeUInt(pReader, pos, size); + if (val < 0) + return false; + + pos += size; // consume size of payload + + return true; +} + +bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id, + unsigned char*& buf, size_t& buflen) { + if (!pReader || pos < 0) + return false; + + long long total = 0; + long long available = 0; + + long status = pReader->Length(&total, &available); + if (status < 0 || (total >= 0 && available > total)) + return false; + + long len = 0; + const long long id = ReadID(pReader, pos, len); + if (id < 0 || (available - pos) > len) + return false; + + if (static_cast(id) != expected_id) + return false; + + pos += len; // consume id + + const long long size = ReadUInt(pReader, pos, len); + if (size < 0 || len <= 0 || len > 8 || (available - pos) > len) + return false; + + unsigned long long rollover_check = + static_cast(pos) + len; + if (rollover_check > LLONG_MAX) + return false; + + pos += len; // consume length of size of payload + + rollover_check = static_cast(pos) + size; + if (rollover_check > LLONG_MAX) + return false; + + if ((pos + size) > available) + return false; + + if (size >= LONG_MAX) + return false; + + const long buflen_ = static_cast(size); + + buf = SafeArrayAlloc(1, buflen_); + if (!buf) + return false; + + status = pReader->Read(pos, buflen_, buf); + if (status != 0) + return false; + + buflen = buflen_; + + pos += size; // consume size of payload + return true; +} + +EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); } + +EBMLHeader::~EBMLHeader() { delete[] m_docType; } + +void EBMLHeader::Init() { + m_version = 1; + m_readVersion = 1; + m_maxIdLength = 4; + m_maxSizeLength = 8; + + if (m_docType) { + delete[] m_docType; + m_docType = NULL; + } + + m_docTypeVersion = 1; + m_docTypeReadVersion = 1; +} + +long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) { + if (!pReader) + return E_FILE_FORMAT_INVALID; + + long long total, available; + + long status = pReader->Length(&total, &available); + + if (status < 0) // error + return status; + + pos = 0; + + // Scan until we find what looks like the first byte of the EBML header. + const long long kMaxScanBytes = (available >= 1024) ? 1024 : available; + const unsigned char kEbmlByte0 = 0x1A; + unsigned char scan_byte = 0; + + while (pos < kMaxScanBytes) { + status = pReader->Read(pos, 1, &scan_byte); + + if (status < 0) // error + return status; + else if (status > 0) + return E_BUFFER_NOT_FULL; + + if (scan_byte == kEbmlByte0) + break; + + ++pos; + } + + long len = 0; + const long long ebml_id = ReadID(pReader, pos, len); + + if (ebml_id == E_BUFFER_NOT_FULL) + return E_BUFFER_NOT_FULL; + + if (len != 4 || ebml_id != libwebm::kMkvEBML) + return E_FILE_FORMAT_INVALID; + + // Move read pos forward to the EBML header size field. + pos += 4; + + // Read length of size field. + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return E_FILE_FORMAT_INVALID; + else if (result > 0) // need more data + return E_BUFFER_NOT_FULL; + + if (len < 1 || len > 8) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && ((total - pos) < len)) + return E_FILE_FORMAT_INVALID; + + if ((available - pos) < len) + return pos + len; // try again later + + // Read the EBML header size. + result = ReadUInt(pReader, pos, len); + + if (result < 0) // error + return result; + + pos += len; // consume size field + + // pos now designates start of payload + + if ((total >= 0) && ((total - pos) < result)) + return E_FILE_FORMAT_INVALID; + + if ((available - pos) < result) + return pos + result; + + const long long end = pos + result; + + Init(); + + while (pos < end) { + long long id, size; + + status = ParseElementHeader(pReader, pos, end, id, size); + + if (status < 0) // error + return status; + + if (size == 0) + return E_FILE_FORMAT_INVALID; + + if (id == libwebm::kMkvEBMLVersion) { + m_version = UnserializeUInt(pReader, pos, size); + + if (m_version <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvEBMLReadVersion) { + m_readVersion = UnserializeUInt(pReader, pos, size); + + if (m_readVersion <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvEBMLMaxIDLength) { + m_maxIdLength = UnserializeUInt(pReader, pos, size); + + if (m_maxIdLength <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvEBMLMaxSizeLength) { + m_maxSizeLength = UnserializeUInt(pReader, pos, size); + + if (m_maxSizeLength <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvDocType) { + if (m_docType) + return E_FILE_FORMAT_INVALID; + + status = UnserializeString(pReader, pos, size, m_docType); + + if (status) // error + return status; + } else if (id == libwebm::kMkvDocTypeVersion) { + m_docTypeVersion = UnserializeUInt(pReader, pos, size); + + if (m_docTypeVersion <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvDocTypeReadVersion) { + m_docTypeReadVersion = UnserializeUInt(pReader, pos, size); + + if (m_docTypeReadVersion <= 0) + return E_FILE_FORMAT_INVALID; + } + + pos += size; + } + + if (pos != end) + return E_FILE_FORMAT_INVALID; + + // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid. + if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0) + return E_FILE_FORMAT_INVALID; + + // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid. + if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 || + m_maxSizeLength > 8) + return E_FILE_FORMAT_INVALID; + + return 0; +} + +Segment::Segment(IMkvReader* pReader, long long elem_start, + // long long elem_size, + long long start, long long size) + : m_pReader(pReader), + m_element_start(elem_start), + // m_element_size(elem_size), + m_start(start), + m_size(size), + m_pos(start), + m_pUnknownSize(0), + m_pSeekHead(NULL), + m_pInfo(NULL), + m_pTracks(NULL), + m_pCues(NULL), + m_pChapters(NULL), + m_pTags(NULL), + m_clusters(NULL), + m_clusterCount(0), + m_clusterPreloadCount(0), + m_clusterSize(0) {} + +Segment::~Segment() { + const long count = m_clusterCount + m_clusterPreloadCount; + + Cluster** i = m_clusters; + Cluster** j = m_clusters + count; + + while (i != j) { + Cluster* const p = *i++; + delete p; + } + + delete[] m_clusters; + + delete m_pTracks; + delete m_pInfo; + delete m_pCues; + delete m_pChapters; + delete m_pTags; + delete m_pSeekHead; +} + +long long Segment::CreateInstance(IMkvReader* pReader, long long pos, + Segment*& pSegment) { + if (pReader == NULL || pos < 0) + return E_PARSE_FAILED; + + pSegment = NULL; + + long long total, available; + + const long status = pReader->Length(&total, &available); + + if (status < 0) // error + return status; + + if (available < 0) + return -1; + + if ((total >= 0) && (available > total)) + return -1; + + // I would assume that in practice this loop would execute + // exactly once, but we allow for other elements (e.g. Void) + // to immediately follow the EBML header. This is fine for + // the source filter case (since the entire file is available), + // but in the splitter case over a network we should probably + // just give up early. We could for example decide only to + // execute this loop a maximum of, say, 10 times. + // TODO: + // There is an implied "give up early" by only parsing up + // to the available limit. We do do that, but only if the + // total file size is unknown. We could decide to always + // use what's available as our limit (irrespective of whether + // we happen to know the total file length). This would have + // as its sense "parse this much of the file before giving up", + // which a slightly different sense from "try to parse up to + // 10 EMBL elements before giving up". + + for (;;) { + if ((total >= 0) && (pos >= total)) + return E_FILE_FORMAT_INVALID; + + // Read ID + long len; + long long result = GetUIntLength(pReader, pos, len); + + if (result) // error, or too few available bytes + return result; + + if ((total >= 0) && ((pos + len) > total)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > available) + return pos + len; + + const long long idpos = pos; + const long long id = ReadID(pReader, pos, len); + + if (id < 0) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume ID + + // Read Size + + result = GetUIntLength(pReader, pos, len); + + if (result) // error, or too few available bytes + return result; + + if ((total >= 0) && ((pos + len) > total)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > available) + return pos + len; + + long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return size; + + pos += len; // consume length of size of element + + // Pos now points to start of payload + + // Handle "unknown size" for live streaming of webm files. + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (id == libwebm::kMkvSegment) { + if (size == unknown_size) + size = -1; + + else if (total < 0) + size = -1; + + else if ((pos + size) > total) + size = -1; + + pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size); + if (pSegment == NULL) + return E_PARSE_FAILED; + + return 0; // success + } + + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && ((pos + size) > total)) + return E_FILE_FORMAT_INVALID; + + if ((pos + size) > available) + return pos + size; + + pos += size; // consume payload + } +} + +long long Segment::ParseHeaders() { + // Outermost (level 0) segment object has been constructed, + // and pos designates start of payload. We need to find the + // inner (level 1) elements. + long long total, available; + + const int status = m_pReader->Length(&total, &available); + + if (status < 0) // error + return status; + + if (total > 0 && available > total) + return E_FILE_FORMAT_INVALID; + + const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size; + + if ((segment_stop >= 0 && total >= 0 && segment_stop > total) || + (segment_stop >= 0 && m_pos > segment_stop)) { + return E_FILE_FORMAT_INVALID; + } + + for (;;) { + if ((total >= 0) && (m_pos >= total)) + break; + + if ((segment_stop >= 0) && (m_pos >= segment_stop)) + break; + + long long pos = m_pos; + const long long element_start = pos; + + // Avoid rolling over pos when very close to LLONG_MAX. + unsigned long long rollover_check = pos + 1ULL; + if (rollover_check > LLONG_MAX) + return E_FILE_FORMAT_INVALID; + + if ((pos + 1) > available) + return (pos + 1); + + long len; + long long result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return result; + + if (result > 0) { + // MkvReader doesn't have enough data to satisfy this read attempt. + return (pos + 1); + } + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > available) + return pos + len; + + const long long idpos = pos; + const long long id = ReadID(m_pReader, idpos, len); + + if (id < 0) + return E_FILE_FORMAT_INVALID; + + if (id == libwebm::kMkvCluster) + break; + + pos += len; // consume ID + + if ((pos + 1) > available) + return (pos + 1); + + // Read Size + result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return result; + + if (result > 0) { + // MkvReader doesn't have enough data to satisfy this read attempt. + return (pos + 1); + } + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > available) + return pos + len; + + const long long size = ReadUInt(m_pReader, pos, len); + + if (size < 0 || len < 1 || len > 8) { + // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or + // len > 8 is true instead of checking this _everywhere_. + return size; + } + + pos += len; // consume length of size of element + + // Avoid rolling over pos when very close to LLONG_MAX. + rollover_check = static_cast(pos) + size; + if (rollover_check > LLONG_MAX) + return E_FILE_FORMAT_INVALID; + + const long long element_size = size + pos - element_start; + + // Pos now points to start of payload + + if ((segment_stop >= 0) && ((pos + size) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + // We read EBML elements either in total or nothing at all. + + if ((pos + size) > available) + return pos + size; + + if (id == libwebm::kMkvInfo) { + if (m_pInfo) + return E_FILE_FORMAT_INVALID; + + m_pInfo = new (std::nothrow) + SegmentInfo(this, pos, size, element_start, element_size); + + if (m_pInfo == NULL) + return -1; + + const long status = m_pInfo->Parse(); + + if (status) + return status; + } else if (id == libwebm::kMkvTracks) { + if (m_pTracks) + return E_FILE_FORMAT_INVALID; + + m_pTracks = new (std::nothrow) + Tracks(this, pos, size, element_start, element_size); + + if (m_pTracks == NULL) + return -1; + + const long status = m_pTracks->Parse(); + + if (status) + return status; + } else if (id == libwebm::kMkvCues) { + if (m_pCues == NULL) { + m_pCues = new (std::nothrow) + Cues(this, pos, size, element_start, element_size); + + if (m_pCues == NULL) + return -1; + } + } else if (id == libwebm::kMkvSeekHead) { + if (m_pSeekHead == NULL) { + m_pSeekHead = new (std::nothrow) + SeekHead(this, pos, size, element_start, element_size); + + if (m_pSeekHead == NULL) + return -1; + + const long status = m_pSeekHead->Parse(); + + if (status) + return status; + } + } else if (id == libwebm::kMkvChapters) { + if (m_pChapters == NULL) { + m_pChapters = new (std::nothrow) + Chapters(this, pos, size, element_start, element_size); + + if (m_pChapters == NULL) + return -1; + + const long status = m_pChapters->Parse(); + + if (status) + return status; + } + } else if (id == libwebm::kMkvTags) { + if (m_pTags == NULL) { + m_pTags = new (std::nothrow) + Tags(this, pos, size, element_start, element_size); + + if (m_pTags == NULL) + return -1; + + const long status = m_pTags->Parse(); + + if (status) + return status; + } + } + + m_pos = pos + size; // consume payload + } + + if (segment_stop >= 0 && m_pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + if (m_pInfo == NULL) // TODO: liberalize this behavior + return E_FILE_FORMAT_INVALID; + + if (m_pTracks == NULL) + return E_FILE_FORMAT_INVALID; + + return 0; // success +} + +long Segment::LoadCluster(long long& pos, long& len) { + for (;;) { + const long result = DoLoadCluster(pos, len); + + if (result <= 1) + return result; + } +} + +long Segment::DoLoadCluster(long long& pos, long& len) { + if (m_pos < 0) + return DoLoadClusterUnknownSize(pos, len); + + long long total, avail; + + long status = m_pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + if (total >= 0 && avail > total) + return E_FILE_FORMAT_INVALID; + + const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size; + + long long cluster_off = -1; // offset relative to start of segment + long long cluster_size = -1; // size of cluster payload + + for (;;) { + if ((total >= 0) && (m_pos >= total)) + return 1; // no more clusters + + if ((segment_stop >= 0) && (m_pos >= segment_stop)) + return 1; // no more clusters + + pos = m_pos; + + // Read ID + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long idpos = pos; + const long long id = ReadID(m_pReader, idpos, len); + + if (id < 0) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume ID + + // Read Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(m_pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + pos += len; // consume length of size of element + + // pos now points to start of payload + + if (size == 0) { + // Missing element payload: move on. + m_pos = pos; + continue; + } + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if ((segment_stop >= 0) && (size != unknown_size) && + ((pos + size) > segment_stop)) { + return E_FILE_FORMAT_INVALID; + } + + if (id == libwebm::kMkvCues) { + if (size == unknown_size) { + // Cues element of unknown size: Not supported. + return E_FILE_FORMAT_INVALID; + } + + if (m_pCues == NULL) { + const long long element_size = (pos - idpos) + size; + + m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size); + if (m_pCues == NULL) + return -1; + } + + m_pos = pos + size; // consume payload + continue; + } + + if (id != libwebm::kMkvCluster) { + // Besides the Segment, Libwebm allows only cluster elements of unknown + // size. Fail the parse upon encountering a non-cluster element reporting + // unknown size. + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + m_pos = pos + size; // consume payload + continue; + } + + // We have a cluster. + + cluster_off = idpos - m_start; // relative pos + + if (size != unknown_size) + cluster_size = size; + + break; + } + + if (cluster_off < 0) { + // No cluster, die. + return E_FILE_FORMAT_INVALID; + } + + long long pos_; + long len_; + + status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_); + + if (status < 0) { // error, or underflow + pos = pos_; + len = len_; + + return status; + } + + // status == 0 means "no block entries found" + // status > 0 means "found at least one block entry" + + // TODO: + // The issue here is that the segment increments its own + // pos ptr past the most recent cluster parsed, and then + // starts from there to parse the next cluster. If we + // don't know the size of the current cluster, then we + // must either parse its payload (as we do below), looking + // for the cluster (or cues) ID to terminate the parse. + // This isn't really what we want: rather, we really need + // a way to create the curr cluster object immediately. + // The pity is that cluster::parse can determine its own + // boundary, and we largely duplicate that same logic here. + // + // Maybe we need to get rid of our look-ahead preloading + // in source::parse??? + // + // As we're parsing the blocks in the curr cluster + //(in cluster::parse), we should have some way to signal + // to the segment that we have determined the boundary, + // so it can adjust its own segment::m_pos member. + // + // The problem is that we're asserting in asyncreadinit, + // because we adjust the pos down to the curr seek pos, + // and the resulting adjusted len is > 2GB. I'm suspicious + // that this is even correct, but even if it is, we can't + // be loading that much data in the cache anyway. + + const long idx = m_clusterCount; + + if (m_clusterPreloadCount > 0) { + if (idx >= m_clusterSize) + return E_FILE_FORMAT_INVALID; + + Cluster* const pCluster = m_clusters[idx]; + if (pCluster == NULL || pCluster->m_index >= 0) + return E_FILE_FORMAT_INVALID; + + const long long off = pCluster->GetPosition(); + if (off < 0) + return E_FILE_FORMAT_INVALID; + + if (off == cluster_off) { // preloaded already + if (status == 0) // no entries found + return E_FILE_FORMAT_INVALID; + + if (cluster_size >= 0) + pos += cluster_size; + else { + const long long element_size = pCluster->GetElementSize(); + + if (element_size <= 0) + return E_FILE_FORMAT_INVALID; // TODO: handle this case + + pos = pCluster->m_element_start + element_size; + } + + pCluster->m_index = idx; // move from preloaded to loaded + ++m_clusterCount; + --m_clusterPreloadCount; + + m_pos = pos; // consume payload + if (segment_stop >= 0 && m_pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + return 0; // success + } + } + + if (status == 0) { // no entries found + if (cluster_size >= 0) + pos += cluster_size; + + if ((total >= 0) && (pos >= total)) { + m_pos = total; + return 1; // no more clusters + } + + if ((segment_stop >= 0) && (pos >= segment_stop)) { + m_pos = segment_stop; + return 1; // no more clusters + } + + m_pos = pos; + return 2; // try again + } + + // status > 0 means we have an entry + + Cluster* const pCluster = Cluster::Create(this, idx, cluster_off); + if (pCluster == NULL) + return -1; + + if (!AppendCluster(pCluster)) { + delete pCluster; + return -1; + } + + if (cluster_size >= 0) { + pos += cluster_size; + + m_pos = pos; + + if (segment_stop > 0 && m_pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + return 0; + } + + m_pUnknownSize = pCluster; + m_pos = -pos; + + return 0; // partial success, since we have a new cluster + + // status == 0 means "no block entries found" + // pos designates start of payload + // m_pos has NOT been adjusted yet (in case we need to come back here) +} + +long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) { + if (m_pos >= 0 || m_pUnknownSize == NULL) + return E_PARSE_FAILED; + + const long status = m_pUnknownSize->Parse(pos, len); + + if (status < 0) // error or underflow + return status; + + if (status == 0) // parsed a block + return 2; // continue parsing + + const long long start = m_pUnknownSize->m_element_start; + const long long size = m_pUnknownSize->GetElementSize(); + + if (size < 0) + return E_FILE_FORMAT_INVALID; + + pos = start + size; + m_pos = pos; + + m_pUnknownSize = 0; + + return 2; // continue parsing +} + +bool Segment::AppendCluster(Cluster* pCluster) { + if (pCluster == NULL || pCluster->m_index < 0) + return false; + + const long count = m_clusterCount + m_clusterPreloadCount; + + long& size = m_clusterSize; + const long idx = pCluster->m_index; + + if (size < count || idx != m_clusterCount) + return false; + + if (count >= size) { + const long n = (size <= 0) ? 2048 : 2 * size; + + Cluster** const qq = new (std::nothrow) Cluster*[n]; + if (qq == NULL) + return false; + + Cluster** q = qq; + Cluster** p = m_clusters; + Cluster** const pp = p + count; + + while (p != pp) + *q++ = *p++; + + delete[] m_clusters; + + m_clusters = qq; + size = n; + } + + if (m_clusterPreloadCount > 0) { + Cluster** const p = m_clusters + m_clusterCount; + if (*p == NULL || (*p)->m_index >= 0) + return false; + + Cluster** q = p + m_clusterPreloadCount; + if (q >= (m_clusters + size)) + return false; + + for (;;) { + Cluster** const qq = q - 1; + if ((*qq)->m_index >= 0) + return false; + + *q = *qq; + q = qq; + + if (q == p) + break; + } + } + + m_clusters[idx] = pCluster; + ++m_clusterCount; + return true; +} + +bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) { + if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount) + return false; + + const long count = m_clusterCount + m_clusterPreloadCount; + + long& size = m_clusterSize; + if (size < count) + return false; + + if (count >= size) { + const long n = (size <= 0) ? 2048 : 2 * size; + + Cluster** const qq = new (std::nothrow) Cluster*[n]; + if (qq == NULL) + return false; + Cluster** q = qq; + + Cluster** p = m_clusters; + Cluster** const pp = p + count; + + while (p != pp) + *q++ = *p++; + + delete[] m_clusters; + + m_clusters = qq; + size = n; + } + + if (m_clusters == NULL) + return false; + + Cluster** const p = m_clusters + idx; + + Cluster** q = m_clusters + count; + if (q < p || q >= (m_clusters + size)) + return false; + + while (q > p) { + Cluster** const qq = q - 1; + + if ((*qq)->m_index >= 0) + return false; + + *q = *qq; + q = qq; + } + + m_clusters[idx] = pCluster; + ++m_clusterPreloadCount; + return true; +} + +long Segment::Load() { + if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0) + return E_PARSE_FAILED; + + // Outermost (level 0) segment object has been constructed, + // and pos designates start of payload. We need to find the + // inner (level 1) elements. + + const long long header_status = ParseHeaders(); + + if (header_status < 0) // error + return static_cast(header_status); + + if (header_status > 0) // underflow + return E_BUFFER_NOT_FULL; + + if (m_pInfo == NULL || m_pTracks == NULL) + return E_FILE_FORMAT_INVALID; + + for (;;) { + const long status = LoadCluster(); + + if (status < 0) // error + return status; + + if (status >= 1) // no more clusters + return 0; + } +} + +SeekHead::Entry::Entry() : id(0), pos(0), element_start(0), element_size(0) {} + +SeekHead::SeekHead(Segment* pSegment, long long start, long long size_, + long long element_start, long long element_size) + : m_pSegment(pSegment), + m_start(start), + m_size(size_), + m_element_start(element_start), + m_element_size(element_size), + m_entries(0), + m_entry_count(0), + m_void_elements(0), + m_void_element_count(0) {} + +SeekHead::~SeekHead() { + delete[] m_entries; + delete[] m_void_elements; +} + +long SeekHead::Parse() { + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long pos = m_start; + const long long stop = m_start + m_size; + + // first count the seek head entries + + long long entry_count = 0; + long long void_element_count = 0; + + while (pos < stop) { + long long id, size; + + const long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (id == libwebm::kMkvSeek) { + ++entry_count; + if (entry_count > INT_MAX) + return E_PARSE_FAILED; + } else if (id == libwebm::kMkvVoid) { + ++void_element_count; + if (void_element_count > INT_MAX) + return E_PARSE_FAILED; + } + + pos += size; // consume payload + + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + if (entry_count > 0) { + m_entries = new (std::nothrow) Entry[entry_count]; + + if (m_entries == NULL) + return -1; + } + + if (void_element_count > 0) { + m_void_elements = new (std::nothrow) VoidElement[void_element_count]; + + if (m_void_elements == NULL) + return -1; + } + + // now parse the entries and void elements + + Entry* pEntry = m_entries; + VoidElement* pVoidElement = m_void_elements; + + pos = m_start; + + while (pos < stop) { + const long long idpos = pos; + + long long id, size; + + const long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (id == libwebm::kMkvSeek && entry_count > 0) { + if (ParseEntry(pReader, pos, size, pEntry)) { + Entry& e = *pEntry++; + + e.element_start = idpos; + e.element_size = (pos + size) - idpos; + } + } else if (id == libwebm::kMkvVoid && void_element_count > 0) { + VoidElement& e = *pVoidElement++; + + e.element_start = idpos; + e.element_size = (pos + size) - idpos; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries); + assert(count_ >= 0); + assert(static_cast(count_) <= entry_count); + + m_entry_count = static_cast(count_); + + count_ = ptrdiff_t(pVoidElement - m_void_elements); + assert(count_ >= 0); + assert(static_cast(count_) <= void_element_count); + + m_void_element_count = static_cast(count_); + + return 0; +} + +int SeekHead::GetCount() const { return m_entry_count; } + +const SeekHead::Entry* SeekHead::GetEntry(int idx) const { + if (idx < 0) + return 0; + + if (idx >= m_entry_count) + return 0; + + return m_entries + idx; +} + +int SeekHead::GetVoidElementCount() const { return m_void_element_count; } + +const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const { + if (idx < 0) + return 0; + + if (idx >= m_void_element_count) + return 0; + + return m_void_elements + idx; +} + +long Segment::ParseCues(long long off, long long& pos, long& len) { + if (m_pCues) + return 0; // success + + if (off < 0) + return -1; + + long long total, avail; + + const int status = m_pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + assert((total < 0) || (avail <= total)); + + pos = m_start + off; + + if ((total < 0) || (pos >= total)) + return 1; // don't bother parsing cues + + const long long element_start = pos; + const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size; + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // underflow (weird) + { + len = 1; + return E_BUFFER_NOT_FULL; + } + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long idpos = pos; + + const long long id = ReadID(m_pReader, idpos, len); + + if (id != libwebm::kMkvCues) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume ID + assert((segment_stop < 0) || (pos <= segment_stop)); + + // Read Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // underflow (weird) + { + len = 1; + return E_BUFFER_NOT_FULL; + } + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(m_pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + if (size == 0) // weird, although technically not illegal + return 1; // done + + pos += len; // consume length of size of element + assert((segment_stop < 0) || (pos <= segment_stop)); + + // Pos now points to start of payload + + const long long element_stop = pos + size; + + if ((segment_stop >= 0) && (element_stop > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && (element_stop > total)) + return 1; // don't bother parsing anymore + + len = static_cast(size); + + if (element_stop > avail) + return E_BUFFER_NOT_FULL; + + const long long element_size = element_stop - element_start; + + m_pCues = + new (std::nothrow) Cues(this, pos, size, element_start, element_size); + if (m_pCues == NULL) + return -1; + + return 0; // success +} + +bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_, + Entry* pEntry) { + if (size_ <= 0) + return false; + + long long pos = start; + const long long stop = start + size_; + + long len; + + // parse the container for the level-1 element ID + + const long long seekIdId = ReadID(pReader, pos, len); + if (seekIdId < 0) + return false; + + if (seekIdId != libwebm::kMkvSeekID) + return false; + + if ((pos + len) > stop) + return false; + + pos += len; // consume SeekID id + + const long long seekIdSize = ReadUInt(pReader, pos, len); + + if (seekIdSize <= 0) + return false; + + if ((pos + len) > stop) + return false; + + pos += len; // consume size of field + + if ((pos + seekIdSize) > stop) + return false; + + pEntry->id = ReadID(pReader, pos, len); // payload + + if (pEntry->id <= 0) + return false; + + if (len != seekIdSize) + return false; + + pos += seekIdSize; // consume SeekID payload + + const long long seekPosId = ReadID(pReader, pos, len); + + if (seekPosId != libwebm::kMkvSeekPosition) + return false; + + if ((pos + len) > stop) + return false; + + pos += len; // consume id + + const long long seekPosSize = ReadUInt(pReader, pos, len); + + if (seekPosSize <= 0) + return false; + + if ((pos + len) > stop) + return false; + + pos += len; // consume size + + if ((pos + seekPosSize) > stop) + return false; + + pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize); + + if (pEntry->pos < 0) + return false; + + pos += seekPosSize; // consume payload + + if (pos != stop) + return false; + + return true; +} + +Cues::Cues(Segment* pSegment, long long start_, long long size_, + long long element_start, long long element_size) + : m_pSegment(pSegment), + m_start(start_), + m_size(size_), + m_element_start(element_start), + m_element_size(element_size), + m_cue_points(NULL), + m_count(0), + m_preload_count(0), + m_pos(start_) {} + +Cues::~Cues() { + const long n = m_count + m_preload_count; + + CuePoint** p = m_cue_points; + CuePoint** const q = p + n; + + while (p != q) { + CuePoint* const pCP = *p++; + assert(pCP); + + delete pCP; + } + + delete[] m_cue_points; +} + +long Cues::GetCount() const { + if (m_cue_points == NULL) + return -1; + + return m_count; // TODO: really ignore preload count? +} + +bool Cues::DoneParsing() const { + const long long stop = m_start + m_size; + return (m_pos >= stop); +} + +bool Cues::Init() const { + if (m_cue_points) + return true; + + if (m_count != 0 || m_preload_count != 0) + return false; + + IMkvReader* const pReader = m_pSegment->m_pReader; + + const long long stop = m_start + m_size; + long long pos = m_start; + + long cue_points_size = 0; + + while (pos < stop) { + const long long idpos = pos; + + long len; + + const long long id = ReadID(pReader, pos, len); + if (id < 0 || (pos + len) > stop) { + return false; + } + + pos += len; // consume ID + + const long long size = ReadUInt(pReader, pos, len); + if (size < 0 || (pos + len > stop)) { + return false; + } + + pos += len; // consume Size field + if (pos + size > stop) { + return false; + } + + if (id == libwebm::kMkvCuePoint) { + if (!PreloadCuePoint(cue_points_size, idpos)) + return false; + } + + pos += size; // skip payload + } + return true; +} + +bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const { + if (m_count != 0) + return false; + + if (m_preload_count >= cue_points_size) { + const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size; + + CuePoint** const qq = new (std::nothrow) CuePoint*[n]; + if (qq == NULL) + return false; + + CuePoint** q = qq; // beginning of target + + CuePoint** p = m_cue_points; // beginning of source + CuePoint** const pp = p + m_preload_count; // end of source + + while (p != pp) + *q++ = *p++; + + delete[] m_cue_points; + + m_cue_points = qq; + cue_points_size = n; + } + + CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos); + if (pCP == NULL) + return false; + + m_cue_points[m_preload_count++] = pCP; + return true; +} + +bool Cues::LoadCuePoint() const { + const long long stop = m_start + m_size; + + if (m_pos >= stop) + return false; // nothing else to do + + if (!Init()) { + m_pos = stop; + return false; + } + + IMkvReader* const pReader = m_pSegment->m_pReader; + + while (m_pos < stop) { + const long long idpos = m_pos; + + long len; + + const long long id = ReadID(pReader, m_pos, len); + if (id < 0 || (m_pos + len) > stop) + return false; + + m_pos += len; // consume ID + + const long long size = ReadUInt(pReader, m_pos, len); + if (size < 0 || (m_pos + len) > stop) + return false; + + m_pos += len; // consume Size field + if ((m_pos + size) > stop) + return false; + + if (id != libwebm::kMkvCuePoint) { + m_pos += size; // consume payload + if (m_pos > stop) + return false; + + continue; + } + + if (m_preload_count < 1) + return false; + + CuePoint* const pCP = m_cue_points[m_count]; + if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos))) + return false; + + if (!pCP->Load(pReader)) { + m_pos = stop; + return false; + } + ++m_count; + --m_preload_count; + + m_pos += size; // consume payload + if (m_pos > stop) + return false; + + return true; // yes, we loaded a cue point + } + + return false; // no, we did not load a cue point +} + +bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP, + const CuePoint::TrackPosition*& pTP) const { + if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0) + return false; + + CuePoint** const ii = m_cue_points; + CuePoint** i = ii; + + CuePoint** const jj = ii + m_count; + CuePoint** j = jj; + + pCP = *i; + if (pCP == NULL) + return false; + + if (time_ns <= pCP->GetTime(m_pSegment)) { + pTP = pCP->Find(pTrack); + return (pTP != NULL); + } + + while (i < j) { + // INVARIANT: + //[ii, i) <= time_ns + //[i, j) ? + //[j, jj) > time_ns + + CuePoint** const k = i + (j - i) / 2; + if (k >= jj) + return false; + + CuePoint* const pCP = *k; + if (pCP == NULL) + return false; + + const long long t = pCP->GetTime(m_pSegment); + + if (t <= time_ns) + i = k + 1; + else + j = k; + + if (i > j) + return false; + } + + if (i != j || i > jj || i <= ii) + return false; + + pCP = *--i; + + if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns) + return false; + + // TODO: here and elsewhere, it's probably not correct to search + // for the cue point with this time, and then search for a matching + // track. In principle, the matching track could be on some earlier + // cue point, and with our current algorithm, we'd miss it. To make + // this bullet-proof, we'd need to create a secondary structure, + // with a list of cue points that apply to a track, and then search + // that track-based structure for a matching cue point. + + pTP = pCP->Find(pTrack); + return (pTP != NULL); +} + +const CuePoint* Cues::GetFirst() const { + if (m_cue_points == NULL || m_count == 0) + return NULL; + + CuePoint* const* const pp = m_cue_points; + if (pp == NULL) + return NULL; + + CuePoint* const pCP = pp[0]; + if (pCP == NULL || pCP->GetTimeCode() < 0) + return NULL; + + return pCP; +} + +const CuePoint* Cues::GetLast() const { + if (m_cue_points == NULL || m_count <= 0) + return NULL; + + const long index = m_count - 1; + + CuePoint* const* const pp = m_cue_points; + if (pp == NULL) + return NULL; + + CuePoint* const pCP = pp[index]; + if (pCP == NULL || pCP->GetTimeCode() < 0) + return NULL; + + return pCP; +} + +const CuePoint* Cues::GetNext(const CuePoint* pCurr) const { + if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL || + m_count < 1) { + return NULL; + } + + long index = pCurr->m_index; + if (index >= m_count) + return NULL; + + CuePoint* const* const pp = m_cue_points; + if (pp == NULL || pp[index] != pCurr) + return NULL; + + ++index; + + if (index >= m_count) + return NULL; + + CuePoint* const pNext = pp[index]; + + if (pNext == NULL || pNext->GetTimeCode() < 0) + return NULL; + + return pNext; +} + +const BlockEntry* Cues::GetBlock(const CuePoint* pCP, + const CuePoint::TrackPosition* pTP) const { + if (pCP == NULL || pTP == NULL) + return NULL; + + return m_pSegment->GetBlock(*pCP, *pTP); +} + +const BlockEntry* Segment::GetBlock(const CuePoint& cp, + const CuePoint::TrackPosition& tp) { + Cluster** const ii = m_clusters; + Cluster** i = ii; + + const long count = m_clusterCount + m_clusterPreloadCount; + + Cluster** const jj = ii + count; + Cluster** j = jj; + + while (i < j) { + // INVARIANT: + //[ii, i) < pTP->m_pos + //[i, j) ? + //[j, jj) > pTP->m_pos + + Cluster** const k = i + (j - i) / 2; + assert(k < jj); + + Cluster* const pCluster = *k; + assert(pCluster); + + // const long long pos_ = pCluster->m_pos; + // assert(pos_); + // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1); + + const long long pos = pCluster->GetPosition(); + assert(pos >= 0); + + if (pos < tp.m_pos) + i = k + 1; + else if (pos > tp.m_pos) + j = k; + else + return pCluster->GetEntry(cp, tp); + } + + assert(i == j); + // assert(Cluster::HasBlockEntries(this, tp.m_pos)); + + Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos); //, -1); + if (pCluster == NULL) + return NULL; + + const ptrdiff_t idx = i - m_clusters; + + if (!PreloadCluster(pCluster, idx)) { + delete pCluster; + return NULL; + } + assert(m_clusters); + assert(m_clusterPreloadCount > 0); + assert(m_clusters[idx] == pCluster); + + return pCluster->GetEntry(cp, tp); +} + +const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) { + if (requested_pos < 0) + return 0; + + Cluster** const ii = m_clusters; + Cluster** i = ii; + + const long count = m_clusterCount + m_clusterPreloadCount; + + Cluster** const jj = ii + count; + Cluster** j = jj; + + while (i < j) { + // INVARIANT: + //[ii, i) < pTP->m_pos + //[i, j) ? + //[j, jj) > pTP->m_pos + + Cluster** const k = i + (j - i) / 2; + assert(k < jj); + + Cluster* const pCluster = *k; + assert(pCluster); + + // const long long pos_ = pCluster->m_pos; + // assert(pos_); + // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1); + + const long long pos = pCluster->GetPosition(); + assert(pos >= 0); + + if (pos < requested_pos) + i = k + 1; + else if (pos > requested_pos) + j = k; + else + return pCluster; + } + + assert(i == j); + // assert(Cluster::HasBlockEntries(this, tp.m_pos)); + + Cluster* const pCluster = Cluster::Create(this, -1, requested_pos); + if (pCluster == NULL) + return NULL; + + const ptrdiff_t idx = i - m_clusters; + + if (!PreloadCluster(pCluster, idx)) { + delete pCluster; + return NULL; + } + assert(m_clusters); + assert(m_clusterPreloadCount > 0); + assert(m_clusters[idx] == pCluster); + + return pCluster; +} + +CuePoint::CuePoint(long idx, long long pos) + : m_element_start(0), + m_element_size(0), + m_index(idx), + m_timecode(-1 * pos), + m_track_positions(NULL), + m_track_positions_count(0) { + assert(pos > 0); +} + +CuePoint::~CuePoint() { delete[] m_track_positions; } + +bool CuePoint::Load(IMkvReader* pReader) { + // odbgstream os; + // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl; + + if (m_timecode >= 0) // already loaded + return true; + + assert(m_track_positions == NULL); + assert(m_track_positions_count == 0); + + long long pos_ = -m_timecode; + const long long element_start = pos_; + + long long stop; + + { + long len; + + const long long id = ReadID(pReader, pos_, len); + if (id != libwebm::kMkvCuePoint) + return false; + + pos_ += len; // consume ID + + const long long size = ReadUInt(pReader, pos_, len); + assert(size >= 0); + + pos_ += len; // consume Size field + // pos_ now points to start of payload + + stop = pos_ + size; + } + + const long long element_size = stop - element_start; + + long long pos = pos_; + + // First count number of track positions + unsigned long long track_positions_count = 0; + while (pos < stop) { + long len; + + const long long id = ReadID(pReader, pos, len); + if ((id < 0) || (pos + len > stop)) { + return false; + } + + pos += len; // consume ID + + const long long size = ReadUInt(pReader, pos, len); + if ((size < 0) || (pos + len > stop)) { + return false; + } + + pos += len; // consume Size field + if ((pos + size) > stop) { + return false; + } + + if (id == libwebm::kMkvCueTime) + m_timecode = UnserializeUInt(pReader, pos, size); + + else if (id == libwebm::kMkvCueTrackPositions) { + ++track_positions_count; + if (track_positions_count > UINT_MAX) + return E_PARSE_FAILED; + } + + pos += size; // consume payload + } + + m_track_positions_count = static_cast(track_positions_count); + + if (m_timecode < 0 || m_track_positions_count <= 0) { + return false; + } + + // os << "CuePoint::Load(cont'd): idpos=" << idpos + // << " timecode=" << m_timecode + // << endl; + + m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count]; + if (m_track_positions == NULL) + return false; + + // Now parse track positions + + TrackPosition* p = m_track_positions; + pos = pos_; + + while (pos < stop) { + long len; + + const long long id = ReadID(pReader, pos, len); + if (id < 0 || (pos + len) > stop) + return false; + + pos += len; // consume ID + + const long long size = ReadUInt(pReader, pos, len); + assert(size >= 0); + assert((pos + len) <= stop); + + pos += len; // consume Size field + assert((pos + size) <= stop); + + if (id == libwebm::kMkvCueTrackPositions) { + TrackPosition& tp = *p++; + if (!tp.Parse(pReader, pos, size)) { + return false; + } + } + + pos += size; // consume payload + if (pos > stop) + return false; + } + + assert(size_t(p - m_track_positions) == m_track_positions_count); + + m_element_start = element_start; + m_element_size = element_size; + + return true; +} + +bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_, + long long size_) { + const long long stop = start_ + size_; + long long pos = start_; + + m_track = -1; + m_pos = -1; + m_block = 1; // default + + while (pos < stop) { + long len; + + const long long id = ReadID(pReader, pos, len); + if ((id < 0) || ((pos + len) > stop)) { + return false; + } + + pos += len; // consume ID + + const long long size = ReadUInt(pReader, pos, len); + if ((size < 0) || ((pos + len) > stop)) { + return false; + } + + pos += len; // consume Size field + if ((pos + size) > stop) { + return false; + } + + if (id == libwebm::kMkvCueTrack) + m_track = UnserializeUInt(pReader, pos, size); + else if (id == libwebm::kMkvCueClusterPosition) + m_pos = UnserializeUInt(pReader, pos, size); + else if (id == libwebm::kMkvCueBlockNumber) + m_block = UnserializeUInt(pReader, pos, size); + + pos += size; // consume payload + } + + if ((m_pos < 0) || (m_track <= 0)) { + return false; + } + + return true; +} + +const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const { + if (pTrack == NULL) { + return NULL; + } + + const long long n = pTrack->GetNumber(); + + const TrackPosition* i = m_track_positions; + const TrackPosition* const j = i + m_track_positions_count; + + while (i != j) { + const TrackPosition& p = *i++; + + if (p.m_track == n) + return &p; + } + + return NULL; // no matching track number found +} + +long long CuePoint::GetTimeCode() const { return m_timecode; } + +long long CuePoint::GetTime(const Segment* pSegment) const { + assert(pSegment); + assert(m_timecode >= 0); + + const SegmentInfo* const pInfo = pSegment->GetInfo(); + assert(pInfo); + + const long long scale = pInfo->GetTimeCodeScale(); + assert(scale >= 1); + + const long long time = scale * m_timecode; + + return time; +} + +bool Segment::DoneParsing() const { + if (m_size < 0) { + long long total, avail; + + const int status = m_pReader->Length(&total, &avail); + + if (status < 0) // error + return true; // must assume done + + if (total < 0) + return false; // assume live stream + + return (m_pos >= total); + } + + const long long stop = m_start + m_size; + + return (m_pos >= stop); +} + +const Cluster* Segment::GetFirst() const { + if ((m_clusters == NULL) || (m_clusterCount <= 0)) + return &m_eos; + + Cluster* const pCluster = m_clusters[0]; + assert(pCluster); + + return pCluster; +} + +const Cluster* Segment::GetLast() const { + if ((m_clusters == NULL) || (m_clusterCount <= 0)) + return &m_eos; + + const long idx = m_clusterCount - 1; + + Cluster* const pCluster = m_clusters[idx]; + assert(pCluster); + + return pCluster; +} + +unsigned long Segment::GetCount() const { return m_clusterCount; } + +const Cluster* Segment::GetNext(const Cluster* pCurr) { + assert(pCurr); + assert(pCurr != &m_eos); + assert(m_clusters); + + long idx = pCurr->m_index; + + if (idx >= 0) { + assert(m_clusterCount > 0); + assert(idx < m_clusterCount); + assert(pCurr == m_clusters[idx]); + + ++idx; + + if (idx >= m_clusterCount) + return &m_eos; // caller will LoadCluster as desired + + Cluster* const pNext = m_clusters[idx]; + assert(pNext); + assert(pNext->m_index >= 0); + assert(pNext->m_index == idx); + + return pNext; + } + + assert(m_clusterPreloadCount > 0); + + long long pos = pCurr->m_element_start; + + assert(m_size >= 0); // TODO + const long long stop = m_start + m_size; // end of segment + + { + long len; + + long long result = GetUIntLength(m_pReader, pos, len); + assert(result == 0); + assert((pos + len) <= stop); // TODO + if (result != 0) + return NULL; + + const long long id = ReadID(m_pReader, pos, len); + if (id != libwebm::kMkvCluster) + return NULL; + + pos += len; // consume ID + + // Read Size + result = GetUIntLength(m_pReader, pos, len); + assert(result == 0); // TODO + assert((pos + len) <= stop); // TODO + + const long long size = ReadUInt(m_pReader, pos, len); + assert(size > 0); // TODO + // assert((pCurr->m_size <= 0) || (pCurr->m_size == size)); + + pos += len; // consume length of size of element + assert((pos + size) <= stop); // TODO + + // Pos now points to start of payload + + pos += size; // consume payload + } + + long long off_next = 0; + + while (pos < stop) { + long len; + + long long result = GetUIntLength(m_pReader, pos, len); + assert(result == 0); + assert((pos + len) <= stop); // TODO + if (result != 0) + return NULL; + + const long long idpos = pos; // pos of next (potential) cluster + + const long long id = ReadID(m_pReader, idpos, len); + if (id < 0) + return NULL; + + pos += len; // consume ID + + // Read Size + result = GetUIntLength(m_pReader, pos, len); + assert(result == 0); // TODO + assert((pos + len) <= stop); // TODO + + const long long size = ReadUInt(m_pReader, pos, len); + assert(size >= 0); // TODO + + pos += len; // consume length of size of element + assert((pos + size) <= stop); // TODO + + // Pos now points to start of payload + + if (size == 0) // weird + continue; + + if (id == libwebm::kMkvCluster) { + const long long off_next_ = idpos - m_start; + + long long pos_; + long len_; + + const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_); + + assert(status >= 0); + + if (status > 0) { + off_next = off_next_; + break; + } + } + + pos += size; // consume payload + } + + if (off_next <= 0) + return 0; + + Cluster** const ii = m_clusters + m_clusterCount; + Cluster** i = ii; + + Cluster** const jj = ii + m_clusterPreloadCount; + Cluster** j = jj; + + while (i < j) { + // INVARIANT: + //[0, i) < pos_next + //[i, j) ? + //[j, jj) > pos_next + + Cluster** const k = i + (j - i) / 2; + assert(k < jj); + + Cluster* const pNext = *k; + assert(pNext); + assert(pNext->m_index < 0); + + // const long long pos_ = pNext->m_pos; + // assert(pos_); + // pos = pos_ * ((pos_ < 0) ? -1 : 1); + + pos = pNext->GetPosition(); + + if (pos < off_next) + i = k + 1; + else if (pos > off_next) + j = k; + else + return pNext; + } + + assert(i == j); + + Cluster* const pNext = Cluster::Create(this, -1, off_next); + if (pNext == NULL) + return NULL; + + const ptrdiff_t idx_next = i - m_clusters; // insertion position + + if (!PreloadCluster(pNext, idx_next)) { + delete pNext; + return NULL; + } + assert(m_clusters); + assert(idx_next < m_clusterSize); + assert(m_clusters[idx_next] == pNext); + + return pNext; +} + +long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult, + long long& pos, long& len) { + assert(pCurr); + assert(!pCurr->EOS()); + assert(m_clusters); + + pResult = 0; + + if (pCurr->m_index >= 0) { // loaded (not merely preloaded) + assert(m_clusters[pCurr->m_index] == pCurr); + + const long next_idx = pCurr->m_index + 1; + + if (next_idx < m_clusterCount) { + pResult = m_clusters[next_idx]; + return 0; // success + } + + // curr cluster is last among loaded + + const long result = LoadCluster(pos, len); + + if (result < 0) // error or underflow + return result; + + if (result > 0) // no more clusters + { + // pResult = &m_eos; + return 1; + } + + pResult = GetLast(); + return 0; // success + } + + assert(m_pos > 0); + + long long total, avail; + + long status = m_pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + assert((total < 0) || (avail <= total)); + + const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size; + + // interrogate curr cluster + + pos = pCurr->m_element_start; + + if (pCurr->m_element_size >= 0) + pos += pCurr->m_element_size; + else { + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id = ReadUInt(m_pReader, pos, len); + + if (id != libwebm::kMkvCluster) + return -1; + + pos += len; // consume ID + + // Read Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(m_pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + pos += len; // consume size field + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size == unknown_size) // TODO: should never happen + return E_FILE_FORMAT_INVALID; // TODO: resolve this + + // assert((pCurr->m_size <= 0) || (pCurr->m_size == size)); + + if ((segment_stop >= 0) && ((pos + size) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + // Pos now points to start of payload + + pos += size; // consume payload (that is, the current cluster) + if (segment_stop >= 0 && pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + // By consuming the payload, we are assuming that the curr + // cluster isn't interesting. That is, we don't bother checking + // whether the payload of the curr cluster is less than what + // happens to be available (obtained via IMkvReader::Length). + // Presumably the caller has already dispensed with the current + // cluster, and really does want the next cluster. + } + + // pos now points to just beyond the last fully-loaded cluster + + for (;;) { + const long status = DoParseNext(pResult, pos, len); + + if (status <= 1) + return status; + } +} + +long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) { + long long total, avail; + + long status = m_pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + assert((total < 0) || (avail <= total)); + + const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size; + + // Parse next cluster. This is strictly a parsing activity. + // Creation of a new cluster object happens later, after the + // parsing is done. + + long long off_next = 0; + long long cluster_size = -1; + + for (;;) { + if ((total >= 0) && (pos >= total)) + return 1; // EOF + + if ((segment_stop >= 0) && (pos >= segment_stop)) + return 1; // EOF + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long idpos = pos; // absolute + const long long idoff = pos - m_start; // relative + + const long long id = ReadID(m_pReader, idpos, len); // absolute + + if (id < 0) // error + return static_cast(id); + + if (id == 0) // weird + return -1; // generic error + + pos += len; // consume ID + + // Read Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(m_pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + pos += len; // consume length of size of element + + // Pos now points to start of payload + + if (size == 0) // weird + continue; + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if ((segment_stop >= 0) && (size != unknown_size) && + ((pos + size) > segment_stop)) { + return E_FILE_FORMAT_INVALID; + } + + if (id == libwebm::kMkvCues) { + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + const long long element_stop = pos + size; + + if ((segment_stop >= 0) && (element_stop > segment_stop)) + return E_FILE_FORMAT_INVALID; + + const long long element_start = idpos; + const long long element_size = element_stop - element_start; + + if (m_pCues == NULL) { + m_pCues = new (std::nothrow) + Cues(this, pos, size, element_start, element_size); + if (m_pCues == NULL) + return false; + } + + pos += size; // consume payload + if (segment_stop >= 0 && pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + continue; + } + + if (id != libwebm::kMkvCluster) { // not a Cluster ID + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + pos += size; // consume payload + if (segment_stop >= 0 && pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + continue; + } + + // We have a cluster. + off_next = idoff; + + if (size != unknown_size) + cluster_size = size; + + break; + } + + assert(off_next > 0); // have cluster + + // We have parsed the next cluster. + // We have not created a cluster object yet. What we need + // to do now is determine whether it has already be preloaded + //(in which case, an object for this cluster has already been + // created), and if not, create a new cluster object. + + Cluster** const ii = m_clusters + m_clusterCount; + Cluster** i = ii; + + Cluster** const jj = ii + m_clusterPreloadCount; + Cluster** j = jj; + + while (i < j) { + // INVARIANT: + //[0, i) < pos_next + //[i, j) ? + //[j, jj) > pos_next + + Cluster** const k = i + (j - i) / 2; + assert(k < jj); + + const Cluster* const pNext = *k; + assert(pNext); + assert(pNext->m_index < 0); + + pos = pNext->GetPosition(); + assert(pos >= 0); + + if (pos < off_next) + i = k + 1; + else if (pos > off_next) + j = k; + else { + pResult = pNext; + return 0; // success + } + } + + assert(i == j); + + long long pos_; + long len_; + + status = Cluster::HasBlockEntries(this, off_next, pos_, len_); + + if (status < 0) { // error or underflow + pos = pos_; + len = len_; + + return status; + } + + if (status > 0) { // means "found at least one block entry" + Cluster* const pNext = Cluster::Create(this, + -1, // preloaded + off_next); + if (pNext == NULL) + return -1; + + const ptrdiff_t idx_next = i - m_clusters; // insertion position + + if (!PreloadCluster(pNext, idx_next)) { + delete pNext; + return -1; + } + assert(m_clusters); + assert(idx_next < m_clusterSize); + assert(m_clusters[idx_next] == pNext); + + pResult = pNext; + return 0; // success + } + + // status == 0 means "no block entries found" + + if (cluster_size < 0) { // unknown size + const long long payload_pos = pos; // absolute pos of cluster payload + + for (;;) { // determine cluster size + if ((total >= 0) && (pos >= total)) + break; + + if ((segment_stop >= 0) && (pos >= segment_stop)) + break; // no more clusters + + // Read ID + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long idpos = pos; + const long long id = ReadID(m_pReader, idpos, len); + + if (id < 0) // error (or underflow) + return static_cast(id); + + // This is the distinguished set of ID's we use to determine + // that we have exhausted the sub-element's inside the cluster + // whose ID we parsed earlier. + + if (id == libwebm::kMkvCluster || id == libwebm::kMkvCues) + break; + + pos += len; // consume ID (of sub-element) + + // Read Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(m_pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(m_pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + pos += len; // consume size field of element + + // pos now points to start of sub-element's payload + + if (size == 0) // weird + continue; + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; // not allowed for sub-elements + + if ((segment_stop >= 0) && ((pos + size) > segment_stop)) // weird + return E_FILE_FORMAT_INVALID; + + pos += size; // consume payload of sub-element + if (segment_stop >= 0 && pos > segment_stop) + return E_FILE_FORMAT_INVALID; + } // determine cluster size + + cluster_size = pos - payload_pos; + assert(cluster_size >= 0); // TODO: handle cluster_size = 0 + + pos = payload_pos; // reset and re-parse original cluster + } + + pos += cluster_size; // consume payload + if (segment_stop >= 0 && pos > segment_stop) + return E_FILE_FORMAT_INVALID; + + return 2; // try to find a cluster that follows next +} + +const Cluster* Segment::FindCluster(long long time_ns) const { + if ((m_clusters == NULL) || (m_clusterCount <= 0)) + return &m_eos; + + { + Cluster* const pCluster = m_clusters[0]; + assert(pCluster); + assert(pCluster->m_index == 0); + + if (time_ns <= pCluster->GetTime()) + return pCluster; + } + + // Binary search of cluster array + + long i = 0; + long j = m_clusterCount; + + while (i < j) { + // INVARIANT: + //[0, i) <= time_ns + //[i, j) ? + //[j, m_clusterCount) > time_ns + + const long k = i + (j - i) / 2; + assert(k < m_clusterCount); + + Cluster* const pCluster = m_clusters[k]; + assert(pCluster); + assert(pCluster->m_index == k); + + const long long t = pCluster->GetTime(); + + if (t <= time_ns) + i = k + 1; + else + j = k; + + assert(i <= j); + } + + assert(i == j); + assert(i > 0); + assert(i <= m_clusterCount); + + const long k = i - 1; + + Cluster* const pCluster = m_clusters[k]; + assert(pCluster); + assert(pCluster->m_index == k); + assert(pCluster->GetTime() <= time_ns); + + return pCluster; +} + +const Tracks* Segment::GetTracks() const { return m_pTracks; } +const SegmentInfo* Segment::GetInfo() const { return m_pInfo; } +const Cues* Segment::GetCues() const { return m_pCues; } +const Chapters* Segment::GetChapters() const { return m_pChapters; } +const Tags* Segment::GetTags() const { return m_pTags; } +const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; } + +long long Segment::GetDuration() const { + assert(m_pInfo); + return m_pInfo->GetDuration(); +} + +Chapters::Chapters(Segment* pSegment, long long payload_start, + long long payload_size, long long element_start, + long long element_size) + : m_pSegment(pSegment), + m_start(payload_start), + m_size(payload_size), + m_element_start(element_start), + m_element_size(element_size), + m_editions(NULL), + m_editions_size(0), + m_editions_count(0) {} + +Chapters::~Chapters() { + while (m_editions_count > 0) { + Edition& e = m_editions[--m_editions_count]; + e.Clear(); + } + delete[] m_editions; +} + +long Chapters::Parse() { + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long pos = m_start; // payload start + const long long stop = pos + m_size; // payload stop + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (size == 0) // weird + continue; + + if (id == libwebm::kMkvEditionEntry) { + status = ParseEdition(pos, size); + + if (status < 0) // error + return status; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +int Chapters::GetEditionCount() const { return m_editions_count; } + +const Chapters::Edition* Chapters::GetEdition(int idx) const { + if (idx < 0) + return NULL; + + if (idx >= m_editions_count) + return NULL; + + return m_editions + idx; +} + +bool Chapters::ExpandEditionsArray() { + if (m_editions_size > m_editions_count) + return true; // nothing else to do + + const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size; + + Edition* const editions = new (std::nothrow) Edition[size]; + + if (editions == NULL) + return false; + + for (int idx = 0; idx < m_editions_count; ++idx) { + m_editions[idx].ShallowCopy(editions[idx]); + } + + delete[] m_editions; + m_editions = editions; + + m_editions_size = size; + return true; +} + +long Chapters::ParseEdition(long long pos, long long size) { + if (!ExpandEditionsArray()) + return -1; + + Edition& e = m_editions[m_editions_count++]; + e.Init(); + + return e.Parse(m_pSegment->m_pReader, pos, size); +} + +Chapters::Edition::Edition() {} + +Chapters::Edition::~Edition() {} + +int Chapters::Edition::GetAtomCount() const { return m_atoms_count; } + +const Chapters::Atom* Chapters::Edition::GetAtom(int index) const { + if (index < 0) + return NULL; + + if (index >= m_atoms_count) + return NULL; + + return m_atoms + index; +} + +void Chapters::Edition::Init() { + m_atoms = NULL; + m_atoms_size = 0; + m_atoms_count = 0; +} + +void Chapters::Edition::ShallowCopy(Edition& rhs) const { + rhs.m_atoms = m_atoms; + rhs.m_atoms_size = m_atoms_size; + rhs.m_atoms_count = m_atoms_count; +} + +void Chapters::Edition::Clear() { + while (m_atoms_count > 0) { + Atom& a = m_atoms[--m_atoms_count]; + a.Clear(); + } + + delete[] m_atoms; + m_atoms = NULL; + + m_atoms_size = 0; +} + +long Chapters::Edition::Parse(IMkvReader* pReader, long long pos, + long long size) { + const long long stop = pos + size; + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (size == 0) + continue; + + if (id == libwebm::kMkvChapterAtom) { + status = ParseAtom(pReader, pos, size); + + if (status < 0) // error + return status; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos, + long long size) { + if (!ExpandAtomsArray()) + return -1; + + Atom& a = m_atoms[m_atoms_count++]; + a.Init(); + + return a.Parse(pReader, pos, size); +} + +bool Chapters::Edition::ExpandAtomsArray() { + if (m_atoms_size > m_atoms_count) + return true; // nothing else to do + + const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size; + + Atom* const atoms = new (std::nothrow) Atom[size]; + + if (atoms == NULL) + return false; + + for (int idx = 0; idx < m_atoms_count; ++idx) { + m_atoms[idx].ShallowCopy(atoms[idx]); + } + + delete[] m_atoms; + m_atoms = atoms; + + m_atoms_size = size; + return true; +} + +Chapters::Atom::Atom() {} + +Chapters::Atom::~Atom() {} + +unsigned long long Chapters::Atom::GetUID() const { return m_uid; } + +const char* Chapters::Atom::GetStringUID() const { return m_string_uid; } + +long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; } + +long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; } + +long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const { + return GetTime(pChapters, m_start_timecode); +} + +long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const { + return GetTime(pChapters, m_stop_timecode); +} + +int Chapters::Atom::GetDisplayCount() const { return m_displays_count; } + +const Chapters::Display* Chapters::Atom::GetDisplay(int index) const { + if (index < 0) + return NULL; + + if (index >= m_displays_count) + return NULL; + + return m_displays + index; +} + +void Chapters::Atom::Init() { + m_string_uid = NULL; + m_uid = 0; + m_start_timecode = -1; + m_stop_timecode = -1; + + m_displays = NULL; + m_displays_size = 0; + m_displays_count = 0; +} + +void Chapters::Atom::ShallowCopy(Atom& rhs) const { + rhs.m_string_uid = m_string_uid; + rhs.m_uid = m_uid; + rhs.m_start_timecode = m_start_timecode; + rhs.m_stop_timecode = m_stop_timecode; + + rhs.m_displays = m_displays; + rhs.m_displays_size = m_displays_size; + rhs.m_displays_count = m_displays_count; +} + +void Chapters::Atom::Clear() { + delete[] m_string_uid; + m_string_uid = NULL; + + while (m_displays_count > 0) { + Display& d = m_displays[--m_displays_count]; + d.Clear(); + } + + delete[] m_displays; + m_displays = NULL; + + m_displays_size = 0; +} + +long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) { + const long long stop = pos + size; + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (size == 0) // 0 length payload, skip. + continue; + + if (id == libwebm::kMkvChapterDisplay) { + status = ParseDisplay(pReader, pos, size); + + if (status < 0) // error + return status; + } else if (id == libwebm::kMkvChapterStringUID) { + status = UnserializeString(pReader, pos, size, m_string_uid); + + if (status < 0) // error + return status; + } else if (id == libwebm::kMkvChapterUID) { + long long val; + status = UnserializeInt(pReader, pos, size, val); + + if (status < 0) // error + return status; + + m_uid = static_cast(val); + } else if (id == libwebm::kMkvChapterTimeStart) { + const long long val = UnserializeUInt(pReader, pos, size); + + if (val < 0) // error + return static_cast(val); + + m_start_timecode = val; + } else if (id == libwebm::kMkvChapterTimeEnd) { + const long long val = UnserializeUInt(pReader, pos, size); + + if (val < 0) // error + return static_cast(val); + + m_stop_timecode = val; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +long long Chapters::Atom::GetTime(const Chapters* pChapters, + long long timecode) { + if (pChapters == NULL) + return -1; + + Segment* const pSegment = pChapters->m_pSegment; + + if (pSegment == NULL) // weird + return -1; + + const SegmentInfo* const pInfo = pSegment->GetInfo(); + + if (pInfo == NULL) + return -1; + + const long long timecode_scale = pInfo->GetTimeCodeScale(); + + if (timecode_scale < 1) // weird + return -1; + + if (timecode < 0) + return -1; + + const long long result = timecode_scale * timecode; + + return result; +} + +long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos, + long long size) { + if (!ExpandDisplaysArray()) + return -1; + + Display& d = m_displays[m_displays_count++]; + d.Init(); + + return d.Parse(pReader, pos, size); +} + +bool Chapters::Atom::ExpandDisplaysArray() { + if (m_displays_size > m_displays_count) + return true; // nothing else to do + + const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size; + + Display* const displays = new (std::nothrow) Display[size]; + + if (displays == NULL) + return false; + + for (int idx = 0; idx < m_displays_count; ++idx) { + m_displays[idx].ShallowCopy(displays[idx]); + } + + delete[] m_displays; + m_displays = displays; + + m_displays_size = size; + return true; +} + +Chapters::Display::Display() {} + +Chapters::Display::~Display() {} + +const char* Chapters::Display::GetString() const { return m_string; } + +const char* Chapters::Display::GetLanguage() const { return m_language; } + +const char* Chapters::Display::GetCountry() const { return m_country; } + +void Chapters::Display::Init() { + m_string = NULL; + m_language = NULL; + m_country = NULL; +} + +void Chapters::Display::ShallowCopy(Display& rhs) const { + rhs.m_string = m_string; + rhs.m_language = m_language; + rhs.m_country = m_country; +} + +void Chapters::Display::Clear() { + delete[] m_string; + m_string = NULL; + + delete[] m_language; + m_language = NULL; + + delete[] m_country; + m_country = NULL; +} + +long Chapters::Display::Parse(IMkvReader* pReader, long long pos, + long long size) { + const long long stop = pos + size; + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (size == 0) // No payload. + continue; + + if (id == libwebm::kMkvChapString) { + status = UnserializeString(pReader, pos, size, m_string); + + if (status) + return status; + } else if (id == libwebm::kMkvChapLanguage) { + status = UnserializeString(pReader, pos, size, m_language); + + if (status) + return status; + } else if (id == libwebm::kMkvChapCountry) { + status = UnserializeString(pReader, pos, size, m_country); + + if (status) + return status; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size, + long long element_start, long long element_size) + : m_pSegment(pSegment), + m_start(payload_start), + m_size(payload_size), + m_element_start(element_start), + m_element_size(element_size), + m_tags(NULL), + m_tags_size(0), + m_tags_count(0) {} + +Tags::~Tags() { + while (m_tags_count > 0) { + Tag& t = m_tags[--m_tags_count]; + t.Clear(); + } + delete[] m_tags; +} + +long Tags::Parse() { + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long pos = m_start; // payload start + const long long stop = pos + m_size; // payload stop + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) + return status; + + if (size == 0) // 0 length tag, read another + continue; + + if (id == libwebm::kMkvTag) { + status = ParseTag(pos, size); + + if (status < 0) + return status; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + return 0; +} + +int Tags::GetTagCount() const { return m_tags_count; } + +const Tags::Tag* Tags::GetTag(int idx) const { + if (idx < 0) + return NULL; + + if (idx >= m_tags_count) + return NULL; + + return m_tags + idx; +} + +bool Tags::ExpandTagsArray() { + if (m_tags_size > m_tags_count) + return true; // nothing else to do + + const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size; + + Tag* const tags = new (std::nothrow) Tag[size]; + + if (tags == NULL) + return false; + + for (int idx = 0; idx < m_tags_count; ++idx) { + m_tags[idx].ShallowCopy(tags[idx]); + } + + delete[] m_tags; + m_tags = tags; + + m_tags_size = size; + return true; +} + +long Tags::ParseTag(long long pos, long long size) { + if (!ExpandTagsArray()) + return -1; + + Tag& t = m_tags[m_tags_count++]; + t.Init(); + + return t.Parse(m_pSegment->m_pReader, pos, size); +} + +Tags::Tag::Tag() {} + +Tags::Tag::~Tag() {} + +int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; } + +const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const { + if (index < 0) + return NULL; + + if (index >= m_simple_tags_count) + return NULL; + + return m_simple_tags + index; +} + +void Tags::Tag::Init() { + m_simple_tags = NULL; + m_simple_tags_size = 0; + m_simple_tags_count = 0; +} + +void Tags::Tag::ShallowCopy(Tag& rhs) const { + rhs.m_simple_tags = m_simple_tags; + rhs.m_simple_tags_size = m_simple_tags_size; + rhs.m_simple_tags_count = m_simple_tags_count; +} + +void Tags::Tag::Clear() { + while (m_simple_tags_count > 0) { + SimpleTag& d = m_simple_tags[--m_simple_tags_count]; + d.Clear(); + } + + delete[] m_simple_tags; + m_simple_tags = NULL; + + m_simple_tags_size = 0; +} + +long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) { + const long long stop = pos + size; + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) + return status; + + if (size == 0) // 0 length tag, read another + continue; + + if (id == libwebm::kMkvSimpleTag) { + status = ParseSimpleTag(pReader, pos, size); + + if (status < 0) + return status; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos, + long long size) { + if (!ExpandSimpleTagsArray()) + return -1; + + SimpleTag& st = m_simple_tags[m_simple_tags_count++]; + st.Init(); + + return st.Parse(pReader, pos, size); +} + +bool Tags::Tag::ExpandSimpleTagsArray() { + if (m_simple_tags_size > m_simple_tags_count) + return true; // nothing else to do + + const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size; + + SimpleTag* const displays = new (std::nothrow) SimpleTag[size]; + + if (displays == NULL) + return false; + + for (int idx = 0; idx < m_simple_tags_count; ++idx) { + m_simple_tags[idx].ShallowCopy(displays[idx]); + } + + delete[] m_simple_tags; + m_simple_tags = displays; + + m_simple_tags_size = size; + return true; +} + +Tags::SimpleTag::SimpleTag() {} + +Tags::SimpleTag::~SimpleTag() {} + +const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; } + +const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; } + +void Tags::SimpleTag::Init() { + m_tag_name = NULL; + m_tag_string = NULL; +} + +void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const { + rhs.m_tag_name = m_tag_name; + rhs.m_tag_string = m_tag_string; +} + +void Tags::SimpleTag::Clear() { + delete[] m_tag_name; + m_tag_name = NULL; + + delete[] m_tag_string; + m_tag_string = NULL; +} + +long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos, + long long size) { + const long long stop = pos + size; + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (size == 0) // weird + continue; + + if (id == libwebm::kMkvTagName) { + status = UnserializeString(pReader, pos, size, m_tag_name); + + if (status) + return status; + } else if (id == libwebm::kMkvTagString) { + status = UnserializeString(pReader, pos, size, m_tag_string); + + if (status) + return status; + } + + pos += size; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_, + long long element_start, long long element_size) + : m_pSegment(pSegment), + m_start(start), + m_size(size_), + m_element_start(element_start), + m_element_size(element_size), + m_pMuxingAppAsUTF8(NULL), + m_pWritingAppAsUTF8(NULL), + m_pTitleAsUTF8(NULL) {} + +SegmentInfo::~SegmentInfo() { + delete[] m_pMuxingAppAsUTF8; + m_pMuxingAppAsUTF8 = NULL; + + delete[] m_pWritingAppAsUTF8; + m_pWritingAppAsUTF8 = NULL; + + delete[] m_pTitleAsUTF8; + m_pTitleAsUTF8 = NULL; +} + +long SegmentInfo::Parse() { + assert(m_pMuxingAppAsUTF8 == NULL); + assert(m_pWritingAppAsUTF8 == NULL); + assert(m_pTitleAsUTF8 == NULL); + + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long pos = m_start; + const long long stop = m_start + m_size; + + m_timecodeScale = 1000000; + m_duration = -1; + + while (pos < stop) { + long long id, size; + + const long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (id == libwebm::kMkvTimecodeScale) { + m_timecodeScale = UnserializeUInt(pReader, pos, size); + + if (m_timecodeScale <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvDuration) { + const long status = UnserializeFloat(pReader, pos, size, m_duration); + + if (status < 0) + return status; + + if (m_duration < 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvMuxingApp) { + const long status = + UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8); + + if (status) + return status; + } else if (id == libwebm::kMkvWritingApp) { + const long status = + UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8); + + if (status) + return status; + } else if (id == libwebm::kMkvTitle) { + const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8); + + if (status) + return status; + } + + pos += size; + + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + const double rollover_check = m_duration * m_timecodeScale; + if (rollover_check > static_cast(LLONG_MAX)) + return E_FILE_FORMAT_INVALID; + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + return 0; +} + +long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; } + +long long SegmentInfo::GetDuration() const { + if (m_duration < 0) + return -1; + + assert(m_timecodeScale >= 1); + + const double dd = double(m_duration) * double(m_timecodeScale); + const long long d = static_cast(dd); + + return d; +} + +const char* SegmentInfo::GetMuxingAppAsUTF8() const { + return m_pMuxingAppAsUTF8; +} + +const char* SegmentInfo::GetWritingAppAsUTF8() const { + return m_pWritingAppAsUTF8; +} + +const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; } + +/////////////////////////////////////////////////////////////// +// ContentEncoding element +ContentEncoding::ContentCompression::ContentCompression() + : algo(0), settings(NULL), settings_len(0) {} + +ContentEncoding::ContentCompression::~ContentCompression() { + delete[] settings; +} + +ContentEncoding::ContentEncryption::ContentEncryption() + : algo(0), + key_id(NULL), + key_id_len(0), + signature(NULL), + signature_len(0), + sig_key_id(NULL), + sig_key_id_len(0), + sig_algo(0), + sig_hash_algo(0) {} + +ContentEncoding::ContentEncryption::~ContentEncryption() { + delete[] key_id; + delete[] signature; + delete[] sig_key_id; +} + +ContentEncoding::ContentEncoding() + : compression_entries_(NULL), + compression_entries_end_(NULL), + encryption_entries_(NULL), + encryption_entries_end_(NULL), + encoding_order_(0), + encoding_scope_(1), + encoding_type_(0) {} + +ContentEncoding::~ContentEncoding() { + ContentCompression** comp_i = compression_entries_; + ContentCompression** const comp_j = compression_entries_end_; + + while (comp_i != comp_j) { + ContentCompression* const comp = *comp_i++; + delete comp; + } + + delete[] compression_entries_; + + ContentEncryption** enc_i = encryption_entries_; + ContentEncryption** const enc_j = encryption_entries_end_; + + while (enc_i != enc_j) { + ContentEncryption* const enc = *enc_i++; + delete enc; + } + + delete[] encryption_entries_; +} + +const ContentEncoding::ContentCompression* +ContentEncoding::GetCompressionByIndex(unsigned long idx) const { + const ptrdiff_t count = compression_entries_end_ - compression_entries_; + assert(count >= 0); + + if (idx >= static_cast(count)) + return NULL; + + return compression_entries_[idx]; +} + +unsigned long ContentEncoding::GetCompressionCount() const { + const ptrdiff_t count = compression_entries_end_ - compression_entries_; + assert(count >= 0); + + return static_cast(count); +} + +const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex( + unsigned long idx) const { + const ptrdiff_t count = encryption_entries_end_ - encryption_entries_; + assert(count >= 0); + + if (idx >= static_cast(count)) + return NULL; + + return encryption_entries_[idx]; +} + +unsigned long ContentEncoding::GetEncryptionCount() const { + const ptrdiff_t count = encryption_entries_end_ - encryption_entries_; + assert(count >= 0); + + return static_cast(count); +} + +long ContentEncoding::ParseContentEncAESSettingsEntry( + long long start, long long size, IMkvReader* pReader, + ContentEncAESSettings* aes) { + assert(pReader); + assert(aes); + + long long pos = start; + const long long stop = start + size; + + while (pos < stop) { + long long id, size; + const long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + if (id == libwebm::kMkvAESSettingsCipherMode) { + aes->cipher_mode = UnserializeUInt(pReader, pos, size); + if (aes->cipher_mode != 1) + return E_FILE_FORMAT_INVALID; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + return 0; +} + +long ContentEncoding::ParseContentEncodingEntry(long long start, long long size, + IMkvReader* pReader) { + assert(pReader); + + long long pos = start; + const long long stop = start + size; + + // Count ContentCompression and ContentEncryption elements. + long long compression_count = 0; + long long encryption_count = 0; + + while (pos < stop) { + long long id, size; + const long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + if (id == libwebm::kMkvContentCompression) { + ++compression_count; + if (compression_count > INT_MAX) + return E_PARSE_FAILED; + } + + if (id == libwebm::kMkvContentEncryption) { + ++encryption_count; + if (encryption_count > INT_MAX) + return E_PARSE_FAILED; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (compression_count <= 0 && encryption_count <= 0) + return -1; + + if (compression_count > 0) { + compression_entries_ = + new (std::nothrow) ContentCompression*[compression_count]; + if (!compression_entries_) + return -1; + compression_entries_end_ = compression_entries_; + } + + if (encryption_count > 0) { + encryption_entries_ = + new (std::nothrow) ContentEncryption*[encryption_count]; + if (!encryption_entries_) { + delete[] compression_entries_; + compression_entries_ = NULL; + return -1; + } + encryption_entries_end_ = encryption_entries_; + } + + pos = start; + while (pos < stop) { + long long id, size; + long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + if (id == libwebm::kMkvContentEncodingOrder) { + encoding_order_ = UnserializeUInt(pReader, pos, size); + } else if (id == libwebm::kMkvContentEncodingScope) { + encoding_scope_ = UnserializeUInt(pReader, pos, size); + if (encoding_scope_ < 1) + return -1; + } else if (id == libwebm::kMkvContentEncodingType) { + encoding_type_ = UnserializeUInt(pReader, pos, size); + } else if (id == libwebm::kMkvContentCompression) { + ContentCompression* const compression = + new (std::nothrow) ContentCompression(); + if (!compression) + return -1; + + status = ParseCompressionEntry(pos, size, pReader, compression); + if (status) { + delete compression; + return status; + } + assert(compression_count > 0); + *compression_entries_end_++ = compression; + } else if (id == libwebm::kMkvContentEncryption) { + ContentEncryption* const encryption = + new (std::nothrow) ContentEncryption(); + if (!encryption) + return -1; + + status = ParseEncryptionEntry(pos, size, pReader, encryption); + if (status) { + delete encryption; + return status; + } + assert(encryption_count > 0); + *encryption_entries_end_++ = encryption; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + return 0; +} + +long ContentEncoding::ParseCompressionEntry(long long start, long long size, + IMkvReader* pReader, + ContentCompression* compression) { + assert(pReader); + assert(compression); + + long long pos = start; + const long long stop = start + size; + + bool valid = false; + + while (pos < stop) { + long long id, size; + const long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + if (id == libwebm::kMkvContentCompAlgo) { + long long algo = UnserializeUInt(pReader, pos, size); + if (algo < 0) + return E_FILE_FORMAT_INVALID; + compression->algo = algo; + valid = true; + } else if (id == libwebm::kMkvContentCompSettings) { + if (size <= 0) + return E_FILE_FORMAT_INVALID; + + const size_t buflen = static_cast(size); + unsigned char* buf = SafeArrayAlloc(1, buflen); + if (buf == NULL) + return -1; + + const int read_status = + pReader->Read(pos, static_cast(buflen), buf); + if (read_status) { + delete[] buf; + return status; + } + + // There should be only one settings element per content compression. + if (compression->settings != NULL) { + delete[] buf; + return E_FILE_FORMAT_INVALID; + } + + compression->settings = buf; + compression->settings_len = buflen; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + // ContentCompAlgo is mandatory + if (!valid) + return E_FILE_FORMAT_INVALID; + + return 0; +} + +long ContentEncoding::ParseEncryptionEntry(long long start, long long size, + IMkvReader* pReader, + ContentEncryption* encryption) { + assert(pReader); + assert(encryption); + + long long pos = start; + const long long stop = start + size; + + while (pos < stop) { + long long id, size; + const long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + if (id == libwebm::kMkvContentEncAlgo) { + encryption->algo = UnserializeUInt(pReader, pos, size); + if (encryption->algo != 5) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvContentEncKeyID) { + delete[] encryption->key_id; + encryption->key_id = NULL; + encryption->key_id_len = 0; + + if (size <= 0) + return E_FILE_FORMAT_INVALID; + + const size_t buflen = static_cast(size); + unsigned char* buf = SafeArrayAlloc(1, buflen); + if (buf == NULL) + return -1; + + const int read_status = + pReader->Read(pos, static_cast(buflen), buf); + if (read_status) { + delete[] buf; + return status; + } + + encryption->key_id = buf; + encryption->key_id_len = buflen; + } else if (id == libwebm::kMkvContentSignature) { + delete[] encryption->signature; + encryption->signature = NULL; + encryption->signature_len = 0; + + if (size <= 0) + return E_FILE_FORMAT_INVALID; + + const size_t buflen = static_cast(size); + unsigned char* buf = SafeArrayAlloc(1, buflen); + if (buf == NULL) + return -1; + + const int read_status = + pReader->Read(pos, static_cast(buflen), buf); + if (read_status) { + delete[] buf; + return status; + } + + encryption->signature = buf; + encryption->signature_len = buflen; + } else if (id == libwebm::kMkvContentSigKeyID) { + delete[] encryption->sig_key_id; + encryption->sig_key_id = NULL; + encryption->sig_key_id_len = 0; + + if (size <= 0) + return E_FILE_FORMAT_INVALID; + + const size_t buflen = static_cast(size); + unsigned char* buf = SafeArrayAlloc(1, buflen); + if (buf == NULL) + return -1; + + const int read_status = + pReader->Read(pos, static_cast(buflen), buf); + if (read_status) { + delete[] buf; + return status; + } + + encryption->sig_key_id = buf; + encryption->sig_key_id_len = buflen; + } else if (id == libwebm::kMkvContentSigAlgo) { + encryption->sig_algo = UnserializeUInt(pReader, pos, size); + } else if (id == libwebm::kMkvContentSigHashAlgo) { + encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size); + } else if (id == libwebm::kMkvContentEncAESSettings) { + const long status = ParseContentEncAESSettingsEntry( + pos, size, pReader, &encryption->aes_settings); + if (status) + return status; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + return 0; +} + +Track::Track(Segment* pSegment, long long element_start, long long element_size) + : m_pSegment(pSegment), + m_element_start(element_start), + m_element_size(element_size), + content_encoding_entries_(NULL), + content_encoding_entries_end_(NULL) {} + +Track::~Track() { + Info& info = const_cast(m_info); + info.Clear(); + + ContentEncoding** i = content_encoding_entries_; + ContentEncoding** const j = content_encoding_entries_end_; + + while (i != j) { + ContentEncoding* const encoding = *i++; + delete encoding; + } + + delete[] content_encoding_entries_; +} + +long Track::Create(Segment* pSegment, const Info& info, long long element_start, + long long element_size, Track*& pResult) { + if (pResult) + return -1; + + Track* const pTrack = + new (std::nothrow) Track(pSegment, element_start, element_size); + + if (pTrack == NULL) + return -1; // generic error + + const int status = info.Copy(pTrack->m_info); + + if (status) { // error + delete pTrack; + return status; + } + + pResult = pTrack; + return 0; // success +} + +Track::Info::Info() + : uid(0), + defaultDuration(0), + codecDelay(0), + seekPreRoll(0), + nameAsUTF8(NULL), + language(NULL), + codecId(NULL), + codecNameAsUTF8(NULL), + codecPrivate(NULL), + codecPrivateSize(0), + lacing(false) {} + +Track::Info::~Info() { Clear(); } + +void Track::Info::Clear() { + delete[] nameAsUTF8; + nameAsUTF8 = NULL; + + delete[] language; + language = NULL; + + delete[] codecId; + codecId = NULL; + + delete[] codecPrivate; + codecPrivate = NULL; + codecPrivateSize = 0; + + delete[] codecNameAsUTF8; + codecNameAsUTF8 = NULL; +} + +int Track::Info::CopyStr(char* Info::*str, Info& dst_) const { + if (str == static_cast(NULL)) + return -1; + + char*& dst = dst_.*str; + + if (dst) // should be NULL already + return -1; + + const char* const src = this->*str; + + if (src == NULL) + return 0; + + const size_t len = strlen(src); + + dst = SafeArrayAlloc(1, len + 1); + + if (dst == NULL) + return -1; + + strcpy(dst, src); + + return 0; +} + +int Track::Info::Copy(Info& dst) const { + if (&dst == this) + return 0; + + dst.type = type; + dst.number = number; + dst.defaultDuration = defaultDuration; + dst.codecDelay = codecDelay; + dst.seekPreRoll = seekPreRoll; + dst.uid = uid; + dst.lacing = lacing; + dst.settings = settings; + + // We now copy the string member variables from src to dst. + // This involves memory allocation so in principle the operation + // can fail (indeed, that's why we have Info::Copy), so we must + // report this to the caller. An error return from this function + // therefore implies that the copy was only partially successful. + + if (int status = CopyStr(&Info::nameAsUTF8, dst)) + return status; + + if (int status = CopyStr(&Info::language, dst)) + return status; + + if (int status = CopyStr(&Info::codecId, dst)) + return status; + + if (int status = CopyStr(&Info::codecNameAsUTF8, dst)) + return status; + + if (codecPrivateSize > 0) { + if (codecPrivate == NULL) + return -1; + + if (dst.codecPrivate) + return -1; + + if (dst.codecPrivateSize != 0) + return -1; + + dst.codecPrivate = SafeArrayAlloc(1, codecPrivateSize); + + if (dst.codecPrivate == NULL) + return -1; + + memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize); + dst.codecPrivateSize = codecPrivateSize; + } + + return 0; +} + +const BlockEntry* Track::GetEOS() const { return &m_eos; } + +long Track::GetType() const { return m_info.type; } + +long Track::GetNumber() const { return m_info.number; } + +unsigned long long Track::GetUid() const { return m_info.uid; } + +const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; } + +const char* Track::GetLanguage() const { return m_info.language; } + +const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; } + +const char* Track::GetCodecId() const { return m_info.codecId; } + +const unsigned char* Track::GetCodecPrivate(size_t& size) const { + size = m_info.codecPrivateSize; + return m_info.codecPrivate; +} + +bool Track::GetLacing() const { return m_info.lacing; } + +unsigned long long Track::GetDefaultDuration() const { + return m_info.defaultDuration; +} + +unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; } + +unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; } + +long Track::GetFirst(const BlockEntry*& pBlockEntry) const { + const Cluster* pCluster = m_pSegment->GetFirst(); + + for (int i = 0;;) { + if (pCluster == NULL) { + pBlockEntry = GetEOS(); + return 1; + } + + if (pCluster->EOS()) { + if (m_pSegment->DoneParsing()) { + pBlockEntry = GetEOS(); + return 1; + } + + pBlockEntry = 0; + return E_BUFFER_NOT_FULL; + } + + long status = pCluster->GetFirst(pBlockEntry); + + if (status < 0) // error + return status; + + if (pBlockEntry == 0) { // empty cluster + pCluster = m_pSegment->GetNext(pCluster); + continue; + } + + for (;;) { + const Block* const pBlock = pBlockEntry->GetBlock(); + assert(pBlock); + + const long long tn = pBlock->GetTrackNumber(); + + if ((tn == m_info.number) && VetEntry(pBlockEntry)) + return 0; + + const BlockEntry* pNextEntry; + + status = pCluster->GetNext(pBlockEntry, pNextEntry); + + if (status < 0) // error + return status; + + if (pNextEntry == 0) + break; + + pBlockEntry = pNextEntry; + } + + ++i; + + if (i >= 100) + break; + + pCluster = m_pSegment->GetNext(pCluster); + } + + // NOTE: if we get here, it means that we didn't find a block with + // a matching track number. We interpret that as an error (which + // might be too conservative). + + pBlockEntry = GetEOS(); // so we can return a non-NULL value + return 1; +} + +long Track::GetNext(const BlockEntry* pCurrEntry, + const BlockEntry*& pNextEntry) const { + assert(pCurrEntry); + assert(!pCurrEntry->EOS()); //? + + const Block* const pCurrBlock = pCurrEntry->GetBlock(); + assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number); + if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number) + return -1; + + const Cluster* pCluster = pCurrEntry->GetCluster(); + assert(pCluster); + assert(!pCluster->EOS()); + + long status = pCluster->GetNext(pCurrEntry, pNextEntry); + + if (status < 0) // error + return status; + + for (int i = 0;;) { + while (pNextEntry) { + const Block* const pNextBlock = pNextEntry->GetBlock(); + assert(pNextBlock); + + if (pNextBlock->GetTrackNumber() == m_info.number) + return 0; + + pCurrEntry = pNextEntry; + + status = pCluster->GetNext(pCurrEntry, pNextEntry); + + if (status < 0) // error + return status; + } + + pCluster = m_pSegment->GetNext(pCluster); + + if (pCluster == NULL) { + pNextEntry = GetEOS(); + return 1; + } + + if (pCluster->EOS()) { + if (m_pSegment->DoneParsing()) { + pNextEntry = GetEOS(); + return 1; + } + + // TODO: there is a potential O(n^2) problem here: we tell the + // caller to (pre)load another cluster, which he does, but then he + // calls GetNext again, which repeats the same search. This is + // a pathological case, since the only way it can happen is if + // there exists a long sequence of clusters none of which contain a + // block from this track. One way around this problem is for the + // caller to be smarter when he loads another cluster: don't call + // us back until you have a cluster that contains a block from this + // track. (Of course, that's not cheap either, since our caller + // would have to scan the each cluster as it's loaded, so that + // would just push back the problem.) + + pNextEntry = NULL; + return E_BUFFER_NOT_FULL; + } + + status = pCluster->GetFirst(pNextEntry); + + if (status < 0) // error + return status; + + if (pNextEntry == NULL) // empty cluster + continue; + + ++i; + + if (i >= 100) + break; + } + + // NOTE: if we get here, it means that we didn't find a block with + // a matching track number after lots of searching, so we give + // up trying. + + pNextEntry = GetEOS(); // so we can return a non-NULL value + return 1; +} + +bool Track::VetEntry(const BlockEntry* pBlockEntry) const { + assert(pBlockEntry); + const Block* const pBlock = pBlockEntry->GetBlock(); + assert(pBlock); + assert(pBlock->GetTrackNumber() == m_info.number); + if (!pBlock || pBlock->GetTrackNumber() != m_info.number) + return false; + + // This function is used during a seek to determine whether the + // frame is a valid seek target. This default function simply + // returns true, which means all frames are valid seek targets. + // It gets overridden by the VideoTrack class, because only video + // keyframes can be used as seek target. + + return true; +} + +long Track::Seek(long long time_ns, const BlockEntry*& pResult) const { + const long status = GetFirst(pResult); + + if (status < 0) // buffer underflow, etc + return status; + + assert(pResult); + + if (pResult->EOS()) + return 0; + + const Cluster* pCluster = pResult->GetCluster(); + assert(pCluster); + assert(pCluster->GetIndex() >= 0); + + if (time_ns <= pResult->GetBlock()->GetTime(pCluster)) + return 0; + + Cluster** const clusters = m_pSegment->m_clusters; + assert(clusters); + + const long count = m_pSegment->GetCount(); // loaded only, not preloaded + assert(count > 0); + + Cluster** const i = clusters + pCluster->GetIndex(); + assert(i); + assert(*i == pCluster); + assert(pCluster->GetTime() <= time_ns); + + Cluster** const j = clusters + count; + + Cluster** lo = i; + Cluster** hi = j; + + while (lo < hi) { + // INVARIANT: + //[i, lo) <= time_ns + //[lo, hi) ? + //[hi, j) > time_ns + + Cluster** const mid = lo + (hi - lo) / 2; + assert(mid < hi); + + pCluster = *mid; + assert(pCluster); + assert(pCluster->GetIndex() >= 0); + assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters)); + + const long long t = pCluster->GetTime(); + + if (t <= time_ns) + lo = mid + 1; + else + hi = mid; + + assert(lo <= hi); + } + + assert(lo == hi); + assert(lo > i); + assert(lo <= j); + + while (lo > i) { + pCluster = *--lo; + assert(pCluster); + assert(pCluster->GetTime() <= time_ns); + + pResult = pCluster->GetEntry(this); + + if ((pResult != 0) && !pResult->EOS()) + return 0; + + // landed on empty cluster (no entries) + } + + pResult = GetEOS(); // weird + return 0; +} + +const ContentEncoding* Track::GetContentEncodingByIndex( + unsigned long idx) const { + const ptrdiff_t count = + content_encoding_entries_end_ - content_encoding_entries_; + assert(count >= 0); + + if (idx >= static_cast(count)) + return NULL; + + return content_encoding_entries_[idx]; +} + +unsigned long Track::GetContentEncodingCount() const { + const ptrdiff_t count = + content_encoding_entries_end_ - content_encoding_entries_; + assert(count >= 0); + + return static_cast(count); +} + +long Track::ParseContentEncodingsEntry(long long start, long long size) { + IMkvReader* const pReader = m_pSegment->m_pReader; + assert(pReader); + + long long pos = start; + const long long stop = start + size; + + // Count ContentEncoding elements. + long long count = 0; + while (pos < stop) { + long long id, size; + const long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + // pos now designates start of element + if (id == libwebm::kMkvContentEncoding) { + ++count; + if (count > INT_MAX) + return E_PARSE_FAILED; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (count <= 0) + return -1; + + content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count]; + if (!content_encoding_entries_) + return -1; + + content_encoding_entries_end_ = content_encoding_entries_; + + pos = start; + while (pos < stop) { + long long id, size; + long status = ParseElementHeader(pReader, pos, stop, id, size); + if (status < 0) // error + return status; + + // pos now designates start of element + if (id == libwebm::kMkvContentEncoding) { + ContentEncoding* const content_encoding = + new (std::nothrow) ContentEncoding(); + if (!content_encoding) + return -1; + + status = content_encoding->ParseContentEncodingEntry(pos, size, pReader); + if (status) { + delete content_encoding; + return status; + } + + *content_encoding_entries_end_++ = content_encoding; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + return 0; +} + +Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {} + +BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; } + +const Block* Track::EOSBlock::GetBlock() const { return NULL; } + +bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos, + long long value_size, bool is_x, + PrimaryChromaticity** chromaticity) { + if (!reader) + return false; + + if (!*chromaticity) + *chromaticity = new PrimaryChromaticity(); + + if (!*chromaticity) + return false; + + PrimaryChromaticity* pc = *chromaticity; + float* value = is_x ? &pc->x : &pc->y; + + double parser_value = 0; + const long long parse_status = + UnserializeFloat(reader, read_pos, value_size, parser_value); + + // Valid range is [0, 1]. Make sure the double is representable as a float + // before casting. + if (parse_status < 0 || parser_value < 0.0 || parser_value > 1.0 || + (parser_value > 0.0 && parser_value < FLT_MIN)) + return false; + + *value = static_cast(parser_value); + + return true; +} + +bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start, + long long mm_size, MasteringMetadata** mm) { + if (!reader || *mm) + return false; + + std::unique_ptr mm_ptr(new MasteringMetadata()); + if (!mm_ptr.get()) + return false; + + const long long mm_end = mm_start + mm_size; + long long read_pos = mm_start; + + while (read_pos < mm_end) { + long long child_id = 0; + long long child_size = 0; + + const long long status = + ParseElementHeader(reader, read_pos, mm_end, child_id, child_size); + if (status < 0) + return false; + + if (child_id == libwebm::kMkvLuminanceMax) { + double value = 0; + const long long value_parse_status = + UnserializeFloat(reader, read_pos, child_size, value); + if (value < -FLT_MAX || value > FLT_MAX || + (value > 0.0 && value < FLT_MIN)) { + return false; + } + mm_ptr->luminance_max = static_cast(value); + if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 || + mm_ptr->luminance_max > 9999.99) { + return false; + } + } else if (child_id == libwebm::kMkvLuminanceMin) { + double value = 0; + const long long value_parse_status = + UnserializeFloat(reader, read_pos, child_size, value); + if (value < -FLT_MAX || value > FLT_MAX || + (value > 0.0 && value < FLT_MIN)) { + return false; + } + mm_ptr->luminance_min = static_cast(value); + if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 || + mm_ptr->luminance_min > 999.9999) { + return false; + } + } else { + bool is_x = false; + PrimaryChromaticity** chromaticity; + switch (child_id) { + case libwebm::kMkvPrimaryRChromaticityX: + case libwebm::kMkvPrimaryRChromaticityY: + is_x = child_id == libwebm::kMkvPrimaryRChromaticityX; + chromaticity = &mm_ptr->r; + break; + case libwebm::kMkvPrimaryGChromaticityX: + case libwebm::kMkvPrimaryGChromaticityY: + is_x = child_id == libwebm::kMkvPrimaryGChromaticityX; + chromaticity = &mm_ptr->g; + break; + case libwebm::kMkvPrimaryBChromaticityX: + case libwebm::kMkvPrimaryBChromaticityY: + is_x = child_id == libwebm::kMkvPrimaryBChromaticityX; + chromaticity = &mm_ptr->b; + break; + case libwebm::kMkvWhitePointChromaticityX: + case libwebm::kMkvWhitePointChromaticityY: + is_x = child_id == libwebm::kMkvWhitePointChromaticityX; + chromaticity = &mm_ptr->white_point; + break; + default: + return false; + } + const bool value_parse_status = PrimaryChromaticity::Parse( + reader, read_pos, child_size, is_x, chromaticity); + if (!value_parse_status) + return false; + } + + read_pos += child_size; + if (read_pos > mm_end) + return false; + } + + *mm = mm_ptr.release(); + return true; +} + +bool Colour::Parse(IMkvReader* reader, long long colour_start, + long long colour_size, Colour** colour) { + if (!reader || *colour) + return false; + + std::unique_ptr colour_ptr(new Colour()); + if (!colour_ptr.get()) + return false; + + const long long colour_end = colour_start + colour_size; + long long read_pos = colour_start; + + while (read_pos < colour_end) { + long long child_id = 0; + long long child_size = 0; + + const long status = + ParseElementHeader(reader, read_pos, colour_end, child_id, child_size); + if (status < 0) + return false; + + if (child_id == libwebm::kMkvMatrixCoefficients) { + colour_ptr->matrix_coefficients = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->matrix_coefficients < 0) + return false; + } else if (child_id == libwebm::kMkvBitsPerChannel) { + colour_ptr->bits_per_channel = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->bits_per_channel < 0) + return false; + } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) { + colour_ptr->chroma_subsampling_horz = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->chroma_subsampling_horz < 0) + return false; + } else if (child_id == libwebm::kMkvChromaSubsamplingVert) { + colour_ptr->chroma_subsampling_vert = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->chroma_subsampling_vert < 0) + return false; + } else if (child_id == libwebm::kMkvCbSubsamplingHorz) { + colour_ptr->cb_subsampling_horz = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->cb_subsampling_horz < 0) + return false; + } else if (child_id == libwebm::kMkvCbSubsamplingVert) { + colour_ptr->cb_subsampling_vert = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->cb_subsampling_vert < 0) + return false; + } else if (child_id == libwebm::kMkvChromaSitingHorz) { + colour_ptr->chroma_siting_horz = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->chroma_siting_horz < 0) + return false; + } else if (child_id == libwebm::kMkvChromaSitingVert) { + colour_ptr->chroma_siting_vert = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->chroma_siting_vert < 0) + return false; + } else if (child_id == libwebm::kMkvRange) { + colour_ptr->range = UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->range < 0) + return false; + } else if (child_id == libwebm::kMkvTransferCharacteristics) { + colour_ptr->transfer_characteristics = + UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->transfer_characteristics < 0) + return false; + } else if (child_id == libwebm::kMkvPrimaries) { + colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->primaries < 0) + return false; + } else if (child_id == libwebm::kMkvMaxCLL) { + colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->max_cll < 0) + return false; + } else if (child_id == libwebm::kMkvMaxFALL) { + colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size); + if (colour_ptr->max_fall < 0) + return false; + } else if (child_id == libwebm::kMkvMasteringMetadata) { + if (!MasteringMetadata::Parse(reader, read_pos, child_size, + &colour_ptr->mastering_metadata)) + return false; + } else { + return false; + } + + read_pos += child_size; + if (read_pos > colour_end) + return false; + } + *colour = colour_ptr.release(); + return true; +} + +bool Projection::Parse(IMkvReader* reader, long long start, long long size, + Projection** projection) { + if (!reader || *projection) + return false; + + std::unique_ptr projection_ptr(new Projection()); + if (!projection_ptr.get()) + return false; + + const long long end = start + size; + long long read_pos = start; + + while (read_pos < end) { + long long child_id = 0; + long long child_size = 0; + + const long long status = + ParseElementHeader(reader, read_pos, end, child_id, child_size); + if (status < 0) + return false; + + if (child_id == libwebm::kMkvProjectionType) { + long long projection_type = kTypeNotPresent; + projection_type = UnserializeUInt(reader, read_pos, child_size); + if (projection_type < 0) + return false; + + projection_ptr->type = static_cast(projection_type); + } else if (child_id == libwebm::kMkvProjectionPrivate) { + if (projection_ptr->private_data != NULL) + return false; + unsigned char* data = SafeArrayAlloc(1, child_size); + + if (data == NULL) + return false; + + const int status = + reader->Read(read_pos, static_cast(child_size), data); + + if (status) { + delete[] data; + return false; + } + + projection_ptr->private_data = data; + projection_ptr->private_data_length = static_cast(child_size); + } else { + double value = 0; + const long long value_parse_status = + UnserializeFloat(reader, read_pos, child_size, value); + // Make sure value is representable as a float before casting. + if (value_parse_status < 0 || value < -FLT_MAX || value > FLT_MAX || + (value > 0.0 && value < FLT_MIN)) { + return false; + } + + switch (child_id) { + case libwebm::kMkvProjectionPoseYaw: + projection_ptr->pose_yaw = static_cast(value); + break; + case libwebm::kMkvProjectionPosePitch: + projection_ptr->pose_pitch = static_cast(value); + break; + case libwebm::kMkvProjectionPoseRoll: + projection_ptr->pose_roll = static_cast(value); + break; + default: + return false; + } + } + + read_pos += child_size; + if (read_pos > end) + return false; + } + + *projection = projection_ptr.release(); + return true; +} + +VideoTrack::VideoTrack(Segment* pSegment, long long element_start, + long long element_size) + : Track(pSegment, element_start, element_size), + m_colour_space(NULL), + m_colour(NULL), + m_projection(NULL) {} + +VideoTrack::~VideoTrack() { + delete[] m_colour_space; + delete m_colour; + delete m_projection; +} + +long VideoTrack::Parse(Segment* pSegment, const Info& info, + long long element_start, long long element_size, + VideoTrack*& pResult) { + if (pResult) + return -1; + + if (info.type != Track::kVideo) + return -1; + + long long width = 0; + long long height = 0; + long long display_width = 0; + long long display_height = 0; + long long display_unit = 0; + long long stereo_mode = 0; + + double rate = 0.0; + std::unique_ptr colour_space_ptr; + + IMkvReader* const pReader = pSegment->m_pReader; + + const Settings& s = info.settings; + assert(s.start >= 0); + assert(s.size >= 0); + + long long pos = s.start; + assert(pos >= 0); + + const long long stop = pos + s.size; + + std::unique_ptr colour_ptr; + std::unique_ptr projection_ptr; + + while (pos < stop) { + long long id, size; + + const long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (id == libwebm::kMkvPixelWidth) { + width = UnserializeUInt(pReader, pos, size); + + if (width <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvPixelHeight) { + height = UnserializeUInt(pReader, pos, size); + + if (height <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvDisplayWidth) { + display_width = UnserializeUInt(pReader, pos, size); + + if (display_width <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvDisplayHeight) { + display_height = UnserializeUInt(pReader, pos, size); + + if (display_height <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvDisplayUnit) { + display_unit = UnserializeUInt(pReader, pos, size); + + if (display_unit < 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvStereoMode) { + stereo_mode = UnserializeUInt(pReader, pos, size); + + if (stereo_mode < 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvFrameRate) { + const long status = UnserializeFloat(pReader, pos, size, rate); + + if (status < 0) + return status; + + if (rate <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvColour) { + Colour* colour = NULL; + if (!Colour::Parse(pReader, pos, size, &colour)) { + return E_FILE_FORMAT_INVALID; + } else { + colour_ptr.reset(colour); + } + } else if (id == libwebm::kMkvProjection) { + Projection* projection = NULL; + if (!Projection::Parse(pReader, pos, size, &projection)) { + return E_FILE_FORMAT_INVALID; + } else { + projection_ptr.reset(projection); + } + } else if (id == libwebm::kMkvColourSpace) { + char* colour_space = NULL; + const long status = UnserializeString(pReader, pos, size, colour_space); + if (status < 0) + return status; + colour_space_ptr.reset(colour_space); + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + VideoTrack* const pTrack = + new (std::nothrow) VideoTrack(pSegment, element_start, element_size); + + if (pTrack == NULL) + return -1; // generic error + + const int status = info.Copy(pTrack->m_info); + + if (status) { // error + delete pTrack; + return status; + } + + pTrack->m_width = width; + pTrack->m_height = height; + pTrack->m_display_width = display_width; + pTrack->m_display_height = display_height; + pTrack->m_display_unit = display_unit; + pTrack->m_stereo_mode = stereo_mode; + pTrack->m_rate = rate; + pTrack->m_colour = colour_ptr.release(); + pTrack->m_colour_space = colour_space_ptr.release(); + pTrack->m_projection = projection_ptr.release(); + + pResult = pTrack; + return 0; // success +} + +bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const { + return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey(); +} + +long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const { + const long status = GetFirst(pResult); + + if (status < 0) // buffer underflow, etc + return status; + + assert(pResult); + + if (pResult->EOS()) + return 0; + + const Cluster* pCluster = pResult->GetCluster(); + assert(pCluster); + assert(pCluster->GetIndex() >= 0); + + if (time_ns <= pResult->GetBlock()->GetTime(pCluster)) + return 0; + + Cluster** const clusters = m_pSegment->m_clusters; + assert(clusters); + + const long count = m_pSegment->GetCount(); // loaded only, not pre-loaded + assert(count > 0); + + Cluster** const i = clusters + pCluster->GetIndex(); + assert(i); + assert(*i == pCluster); + assert(pCluster->GetTime() <= time_ns); + + Cluster** const j = clusters + count; + + Cluster** lo = i; + Cluster** hi = j; + + while (lo < hi) { + // INVARIANT: + //[i, lo) <= time_ns + //[lo, hi) ? + //[hi, j) > time_ns + + Cluster** const mid = lo + (hi - lo) / 2; + assert(mid < hi); + + pCluster = *mid; + assert(pCluster); + assert(pCluster->GetIndex() >= 0); + assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters)); + + const long long t = pCluster->GetTime(); + + if (t <= time_ns) + lo = mid + 1; + else + hi = mid; + + assert(lo <= hi); + } + + assert(lo == hi); + assert(lo > i); + assert(lo <= j); + + pCluster = *--lo; + assert(pCluster); + assert(pCluster->GetTime() <= time_ns); + + pResult = pCluster->GetEntry(this, time_ns); + + if ((pResult != 0) && !pResult->EOS()) // found a keyframe + return 0; + + while (lo != i) { + pCluster = *--lo; + assert(pCluster); + assert(pCluster->GetTime() <= time_ns); + + pResult = pCluster->GetEntry(this, time_ns); + + if ((pResult != 0) && !pResult->EOS()) + return 0; + } + + // weird: we're on the first cluster, but no keyframe found + // should never happen but we must return something anyway + + pResult = GetEOS(); + return 0; +} + +Colour* VideoTrack::GetColour() const { return m_colour; } + +Projection* VideoTrack::GetProjection() const { return m_projection; } + +long long VideoTrack::GetWidth() const { return m_width; } + +long long VideoTrack::GetHeight() const { return m_height; } + +long long VideoTrack::GetDisplayWidth() const { + return m_display_width > 0 ? m_display_width : GetWidth(); +} + +long long VideoTrack::GetDisplayHeight() const { + return m_display_height > 0 ? m_display_height : GetHeight(); +} + +long long VideoTrack::GetDisplayUnit() const { return m_display_unit; } + +long long VideoTrack::GetStereoMode() const { return m_stereo_mode; } + +double VideoTrack::GetFrameRate() const { return m_rate; } + +AudioTrack::AudioTrack(Segment* pSegment, long long element_start, + long long element_size) + : Track(pSegment, element_start, element_size) {} + +long AudioTrack::Parse(Segment* pSegment, const Info& info, + long long element_start, long long element_size, + AudioTrack*& pResult) { + if (pResult) + return -1; + + if (info.type != Track::kAudio) + return -1; + + IMkvReader* const pReader = pSegment->m_pReader; + + const Settings& s = info.settings; + assert(s.start >= 0); + assert(s.size >= 0); + + long long pos = s.start; + assert(pos >= 0); + + const long long stop = pos + s.size; + + double rate = 8000.0; // MKV default + long long channels = 1; + long long bit_depth = 0; + + while (pos < stop) { + long long id, size; + + long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (id == libwebm::kMkvSamplingFrequency) { + status = UnserializeFloat(pReader, pos, size, rate); + + if (status < 0) + return status; + + if (rate <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvChannels) { + channels = UnserializeUInt(pReader, pos, size); + + if (channels <= 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvBitDepth) { + bit_depth = UnserializeUInt(pReader, pos, size); + + if (bit_depth <= 0) + return E_FILE_FORMAT_INVALID; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + AudioTrack* const pTrack = + new (std::nothrow) AudioTrack(pSegment, element_start, element_size); + + if (pTrack == NULL) + return -1; // generic error + + const int status = info.Copy(pTrack->m_info); + + if (status) { + delete pTrack; + return status; + } + + pTrack->m_rate = rate; + pTrack->m_channels = channels; + pTrack->m_bitDepth = bit_depth; + + pResult = pTrack; + return 0; // success +} + +double AudioTrack::GetSamplingRate() const { return m_rate; } + +long long AudioTrack::GetChannels() const { return m_channels; } + +long long AudioTrack::GetBitDepth() const { return m_bitDepth; } + +Tracks::Tracks(Segment* pSegment, long long start, long long size_, + long long element_start, long long element_size) + : m_pSegment(pSegment), + m_start(start), + m_size(size_), + m_element_start(element_start), + m_element_size(element_size), + m_trackEntries(NULL), + m_trackEntriesEnd(NULL) {} + +long Tracks::Parse() { + assert(m_trackEntries == NULL); + assert(m_trackEntriesEnd == NULL); + + const long long stop = m_start + m_size; + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long count = 0; + long long pos = m_start; + + while (pos < stop) { + long long id, size; + + const long status = ParseElementHeader(pReader, pos, stop, id, size); + + if (status < 0) // error + return status; + + if (size == 0) // weird + continue; + + if (id == libwebm::kMkvTrackEntry) { + ++count; + if (count > INT_MAX) + return E_PARSE_FAILED; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + if (count <= 0) + return 0; // success + + m_trackEntries = new (std::nothrow) Track*[count]; + + if (m_trackEntries == NULL) + return -1; + + m_trackEntriesEnd = m_trackEntries; + + pos = m_start; + + while (pos < stop) { + const long long element_start = pos; + + long long id, payload_size; + + const long status = + ParseElementHeader(pReader, pos, stop, id, payload_size); + + if (status < 0) // error + return status; + + if (payload_size == 0) // weird + continue; + + const long long payload_stop = pos + payload_size; + assert(payload_stop <= stop); // checked in ParseElement + + const long long element_size = payload_stop - element_start; + + if (id == libwebm::kMkvTrackEntry) { + Track*& pTrack = *m_trackEntriesEnd; + pTrack = NULL; + + const long status = ParseTrackEntry(pos, payload_size, element_start, + element_size, pTrack); + if (status) + return status; + + if (pTrack) + ++m_trackEntriesEnd; + } + + pos = payload_stop; + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + return 0; // success +} + +unsigned long Tracks::GetTracksCount() const { + const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries; + assert(result >= 0); + + return static_cast(result); +} + +long Tracks::ParseTrackEntry(long long track_start, long long track_size, + long long element_start, long long element_size, + Track*& pResult) const { + if (pResult) + return -1; + + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long pos = track_start; + const long long track_stop = track_start + track_size; + + Track::Info info; + + info.type = 0; + info.number = 0; + info.uid = 0; + info.defaultDuration = 0; + + Track::Settings v; + v.start = -1; + v.size = -1; + + Track::Settings a; + a.start = -1; + a.size = -1; + + Track::Settings e; // content_encodings_settings; + e.start = -1; + e.size = -1; + + long long lacing = 1; // default is true + + while (pos < track_stop) { + long long id, size; + + const long status = ParseElementHeader(pReader, pos, track_stop, id, size); + + if (status < 0) // error + return status; + + if (size < 0) + return E_FILE_FORMAT_INVALID; + + const long long start = pos; + + if (id == libwebm::kMkvVideo) { + v.start = start; + v.size = size; + } else if (id == libwebm::kMkvAudio) { + a.start = start; + a.size = size; + } else if (id == libwebm::kMkvContentEncodings) { + e.start = start; + e.size = size; + } else if (id == libwebm::kMkvTrackUID) { + if (size > 8) + return E_FILE_FORMAT_INVALID; + + info.uid = 0; + + long long pos_ = start; + const long long pos_end = start + size; + + while (pos_ != pos_end) { + unsigned char b; + + const int status = pReader->Read(pos_, 1, &b); + + if (status) + return status; + + info.uid <<= 8; + info.uid |= b; + + ++pos_; + } + } else if (id == libwebm::kMkvTrackNumber) { + const long long num = UnserializeUInt(pReader, pos, size); + + if ((num <= 0) || (num > 127)) + return E_FILE_FORMAT_INVALID; + + info.number = static_cast(num); + } else if (id == libwebm::kMkvTrackType) { + const long long type = UnserializeUInt(pReader, pos, size); + + if ((type <= 0) || (type > 254)) + return E_FILE_FORMAT_INVALID; + + info.type = static_cast(type); + } else if (id == libwebm::kMkvName) { + const long status = + UnserializeString(pReader, pos, size, info.nameAsUTF8); + + if (status) + return status; + } else if (id == libwebm::kMkvLanguage) { + const long status = UnserializeString(pReader, pos, size, info.language); + + if (status) + return status; + } else if (id == libwebm::kMkvDefaultDuration) { + const long long duration = UnserializeUInt(pReader, pos, size); + + if (duration < 0) + return E_FILE_FORMAT_INVALID; + + info.defaultDuration = static_cast(duration); + } else if (id == libwebm::kMkvCodecID) { + const long status = UnserializeString(pReader, pos, size, info.codecId); + + if (status) + return status; + } else if (id == libwebm::kMkvFlagLacing) { + lacing = UnserializeUInt(pReader, pos, size); + + if ((lacing < 0) || (lacing > 1)) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvCodecPrivate) { + delete[] info.codecPrivate; + info.codecPrivate = NULL; + info.codecPrivateSize = 0; + + const size_t buflen = static_cast(size); + + if (buflen) { + unsigned char* buf = SafeArrayAlloc(1, buflen); + + if (buf == NULL) + return -1; + + const int status = pReader->Read(pos, static_cast(buflen), buf); + + if (status) { + delete[] buf; + return status; + } + + info.codecPrivate = buf; + info.codecPrivateSize = buflen; + } + } else if (id == libwebm::kMkvCodecName) { + const long status = + UnserializeString(pReader, pos, size, info.codecNameAsUTF8); + + if (status) + return status; + } else if (id == libwebm::kMkvCodecDelay) { + info.codecDelay = UnserializeUInt(pReader, pos, size); + } else if (id == libwebm::kMkvSeekPreRoll) { + info.seekPreRoll = UnserializeUInt(pReader, pos, size); + } + + pos += size; // consume payload + if (pos > track_stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != track_stop) + return E_FILE_FORMAT_INVALID; + + if (info.number <= 0) // not specified + return E_FILE_FORMAT_INVALID; + + if (GetTrackByNumber(info.number)) + return E_FILE_FORMAT_INVALID; + + if (info.type <= 0) // not specified + return E_FILE_FORMAT_INVALID; + + info.lacing = (lacing > 0) ? true : false; + + if (info.type == Track::kVideo) { + if (v.start < 0) + return E_FILE_FORMAT_INVALID; + + if (a.start >= 0) + return E_FILE_FORMAT_INVALID; + + info.settings = v; + + VideoTrack* pTrack = NULL; + + const long status = VideoTrack::Parse(m_pSegment, info, element_start, + element_size, pTrack); + + if (status) + return status; + + pResult = pTrack; + assert(pResult); + + if (e.start >= 0) + pResult->ParseContentEncodingsEntry(e.start, e.size); + } else if (info.type == Track::kAudio) { + if (a.start < 0) + return E_FILE_FORMAT_INVALID; + + if (v.start >= 0) + return E_FILE_FORMAT_INVALID; + + info.settings = a; + + AudioTrack* pTrack = NULL; + + const long status = AudioTrack::Parse(m_pSegment, info, element_start, + element_size, pTrack); + + if (status) + return status; + + pResult = pTrack; + assert(pResult); + + if (e.start >= 0) + pResult->ParseContentEncodingsEntry(e.start, e.size); + } else { + // neither video nor audio - probably metadata or subtitles + + if (a.start >= 0) + return E_FILE_FORMAT_INVALID; + + if (v.start >= 0) + return E_FILE_FORMAT_INVALID; + + if (info.type == Track::kMetadata && e.start >= 0) + return E_FILE_FORMAT_INVALID; + + info.settings.start = -1; + info.settings.size = 0; + + Track* pTrack = NULL; + + const long status = + Track::Create(m_pSegment, info, element_start, element_size, pTrack); + + if (status) + return status; + + pResult = pTrack; + assert(pResult); + } + + return 0; // success +} + +Tracks::~Tracks() { + Track** i = m_trackEntries; + Track** const j = m_trackEntriesEnd; + + while (i != j) { + Track* const pTrack = *i++; + delete pTrack; + } + + delete[] m_trackEntries; +} + +const Track* Tracks::GetTrackByNumber(long tn) const { + if (tn < 0) + return NULL; + + Track** i = m_trackEntries; + Track** const j = m_trackEntriesEnd; + + while (i != j) { + Track* const pTrack = *i++; + + if (pTrack == NULL) + continue; + + if (tn == pTrack->GetNumber()) + return pTrack; + } + + return NULL; // not found +} + +const Track* Tracks::GetTrackByIndex(unsigned long idx) const { + const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries; + + if (idx >= static_cast(count)) + return NULL; + + return m_trackEntries[idx]; +} + +long Cluster::Load(long long& pos, long& len) const { + if (m_pSegment == NULL) + return E_PARSE_FAILED; + + if (m_timecode >= 0) // at least partially loaded + return 0; + + if (m_pos != m_element_start || m_element_size >= 0) + return E_PARSE_FAILED; + + IMkvReader* const pReader = m_pSegment->m_pReader; + long long total, avail; + const int status = pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + if (total >= 0 && (avail > total || m_pos > total)) + return E_FILE_FORMAT_INVALID; + + pos = m_pos; + + long long cluster_size = -1; + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error or underflow + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id_ = ReadID(pReader, pos, len); + + if (id_ < 0) // error + return static_cast(id_); + + if (id_ != libwebm::kMkvCluster) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume id + + // read cluster size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return static_cast(cluster_size); + + if (size == 0) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume length of size of element + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size != unknown_size) + cluster_size = size; + + // pos points to start of payload + long long timecode = -1; + long long new_pos = -1; + bool bBlock = false; + + long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size; + + for (;;) { + if ((cluster_stop >= 0) && (pos >= cluster_stop)) + break; + + // Parse ID + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((cluster_stop >= 0) && ((pos + len) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id = ReadID(pReader, pos, len); + + if (id < 0) // error + return static_cast(id); + + if (id == 0) + return E_FILE_FORMAT_INVALID; + + // This is the distinguished set of ID's we use to determine + // that we have exhausted the sub-element's inside the cluster + // whose ID we parsed earlier. + + if (id == libwebm::kMkvCluster) + break; + + if (id == libwebm::kMkvCues) + break; + + pos += len; // consume ID field + + // Parse Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((cluster_stop >= 0) && ((pos + len) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume size field + + if ((cluster_stop >= 0) && (pos > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + // pos now points to start of payload + + if (size == 0) + continue; + + if ((cluster_stop >= 0) && ((pos + size) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if (id == libwebm::kMkvTimecode) { + len = static_cast(size); + + if ((pos + size) > avail) + return E_BUFFER_NOT_FULL; + + timecode = UnserializeUInt(pReader, pos, size); + + if (timecode < 0) // error (or underflow) + return static_cast(timecode); + + new_pos = pos + size; + + if (bBlock) + break; + } else if (id == libwebm::kMkvBlockGroup) { + bBlock = true; + break; + } else if (id == libwebm::kMkvSimpleBlock) { + bBlock = true; + break; + } + + pos += size; // consume payload + if (cluster_stop >= 0 && pos > cluster_stop) + return E_FILE_FORMAT_INVALID; + } + + if (cluster_stop >= 0 && pos > cluster_stop) + return E_FILE_FORMAT_INVALID; + + if (timecode < 0) // no timecode found + return E_FILE_FORMAT_INVALID; + + if (!bBlock) + return E_FILE_FORMAT_INVALID; + + m_pos = new_pos; // designates position just beyond timecode payload + m_timecode = timecode; // m_timecode >= 0 means we're partially loaded + + if (cluster_size >= 0) + m_element_size = cluster_stop - m_element_start; + + return 0; +} + +long Cluster::Parse(long long& pos, long& len) const { + long status = Load(pos, len); + + if (status < 0) + return status; + + if (m_pos < m_element_start || m_timecode < 0) + return E_PARSE_FAILED; + + const long long cluster_stop = + (m_element_size < 0) ? -1 : m_element_start + m_element_size; + + if ((cluster_stop >= 0) && (m_pos >= cluster_stop)) + return 1; // nothing else to do + + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long total, avail; + + status = pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + if (total >= 0 && avail > total) + return E_FILE_FORMAT_INVALID; + + pos = m_pos; + + for (;;) { + if ((cluster_stop >= 0) && (pos >= cluster_stop)) + break; + + if ((total >= 0) && (pos >= total)) { + if (m_element_size < 0) + m_element_size = pos - m_element_start; + + break; + } + + // Parse ID + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((cluster_stop >= 0) && ((pos + len) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id = ReadID(pReader, pos, len); + + if (id < 0) + return E_FILE_FORMAT_INVALID; + + // This is the distinguished set of ID's we use to determine + // that we have exhausted the sub-element's inside the cluster + // whose ID we parsed earlier. + + if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) { + if (m_element_size < 0) + m_element_size = pos - m_element_start; + + break; + } + + pos += len; // consume ID field + + // Parse Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) + return E_BUFFER_NOT_FULL; + + if ((cluster_stop >= 0) && ((pos + len) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume size field + + if ((cluster_stop >= 0) && (pos > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + // pos now points to start of payload + + if (size == 0) + continue; + + // const long long block_start = pos; + const long long block_stop = pos + size; + + if (cluster_stop >= 0) { + if (block_stop > cluster_stop) { + if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) { + return E_FILE_FORMAT_INVALID; + } + + pos = cluster_stop; + break; + } + } else if ((total >= 0) && (block_stop > total)) { + m_element_size = total - m_element_start; + pos = total; + break; + } else if (block_stop > avail) { + len = static_cast(size); + return E_BUFFER_NOT_FULL; + } + + Cluster* const this_ = const_cast(this); + + if (id == libwebm::kMkvBlockGroup) + return this_->ParseBlockGroup(size, pos, len); + + if (id == libwebm::kMkvSimpleBlock) + return this_->ParseSimpleBlock(size, pos, len); + + pos += size; // consume payload + if (cluster_stop >= 0 && pos > cluster_stop) + return E_FILE_FORMAT_INVALID; + } + + if (m_element_size < 1) + return E_FILE_FORMAT_INVALID; + + m_pos = pos; + if (cluster_stop >= 0 && m_pos > cluster_stop) + return E_FILE_FORMAT_INVALID; + + if (m_entries_count > 0) { + const long idx = m_entries_count - 1; + + const BlockEntry* const pLast = m_entries[idx]; + if (pLast == NULL) + return E_PARSE_FAILED; + + const Block* const pBlock = pLast->GetBlock(); + if (pBlock == NULL) + return E_PARSE_FAILED; + + const long long start = pBlock->m_start; + + if ((total >= 0) && (start > total)) + return E_PARSE_FAILED; // defend against trucated stream + + const long long size = pBlock->m_size; + + const long long stop = start + size; + if (cluster_stop >= 0 && stop > cluster_stop) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && (stop > total)) + return E_PARSE_FAILED; // defend against trucated stream + } + + return 1; // no more entries +} + +long Cluster::ParseSimpleBlock(long long block_size, long long& pos, + long& len) { + const long long block_start = pos; + const long long block_stop = pos + block_size; + + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long total, avail; + + long status = pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + assert((total < 0) || (avail <= total)); + + // parse track number + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((pos + len) > block_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long track = ReadUInt(pReader, pos, len); + + if (track < 0) // error + return static_cast(track); + + if (track == 0) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume track number + + if ((pos + 2) > block_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + 2) > avail) { + len = 2; + return E_BUFFER_NOT_FULL; + } + + pos += 2; // consume timecode + + if ((pos + 1) > block_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + unsigned char flags; + + status = pReader->Read(pos, 1, &flags); + + if (status < 0) { // error or underflow + len = 1; + return status; + } + + ++pos; // consume flags byte + assert(pos <= avail); + + if (pos >= block_stop) + return E_FILE_FORMAT_INVALID; + + const int lacing = int(flags & 0x06) >> 1; + + if ((lacing != 0) && (block_stop > avail)) { + len = static_cast(block_stop - pos); + return E_BUFFER_NOT_FULL; + } + + status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size, + 0); // DiscardPadding + + if (status != 0) + return status; + + m_pos = block_stop; + + return 0; // success +} + +long Cluster::ParseBlockGroup(long long payload_size, long long& pos, + long& len) { + const long long payload_start = pos; + const long long payload_stop = pos + payload_size; + + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long total, avail; + + long status = pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + assert((total < 0) || (avail <= total)); + + if ((total >= 0) && (payload_stop > total)) + return E_FILE_FORMAT_INVALID; + + if (payload_stop > avail) { + len = static_cast(payload_size); + return E_BUFFER_NOT_FULL; + } + + long long discard_padding = 0; + + while (pos < payload_stop) { + // parse sub-block element ID + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((pos + len) > payload_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id = ReadID(pReader, pos, len); + + if (id < 0) // error + return static_cast(id); + + if (id == 0) // not a valid ID + return E_FILE_FORMAT_INVALID; + + pos += len; // consume ID field + + // Parse Size + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((pos + len) > payload_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + pos += len; // consume size field + + // pos now points to start of sub-block group payload + + if (pos > payload_stop) + return E_FILE_FORMAT_INVALID; + + if (size == 0) // weird + continue; + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; + + if (id == libwebm::kMkvDiscardPadding) { + status = UnserializeInt(pReader, pos, size, discard_padding); + + if (status < 0) // error + return status; + } + + if (id != libwebm::kMkvBlock) { + pos += size; // consume sub-part of block group + + if (pos > payload_stop) + return E_FILE_FORMAT_INVALID; + + continue; + } + + const long long block_stop = pos + size; + + if (block_stop > payload_stop) + return E_FILE_FORMAT_INVALID; + + // parse track number + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((pos + len) > block_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long track = ReadUInt(pReader, pos, len); + + if (track < 0) // error + return static_cast(track); + + if (track == 0) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume track number + + if ((pos + 2) > block_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + 2) > avail) { + len = 2; + return E_BUFFER_NOT_FULL; + } + + pos += 2; // consume timecode + + if ((pos + 1) > block_stop) + return E_FILE_FORMAT_INVALID; + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + unsigned char flags; + + status = pReader->Read(pos, 1, &flags); + + if (status < 0) { // error or underflow + len = 1; + return status; + } + + ++pos; // consume flags byte + assert(pos <= avail); + + if (pos >= block_stop) + return E_FILE_FORMAT_INVALID; + + const int lacing = int(flags & 0x06) >> 1; + + if ((lacing != 0) && (block_stop > avail)) { + len = static_cast(block_stop - pos); + return E_BUFFER_NOT_FULL; + } + + pos = block_stop; // consume block-part of block group + if (pos > payload_stop) + return E_FILE_FORMAT_INVALID; + } + + if (pos != payload_stop) + return E_FILE_FORMAT_INVALID; + + status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size, + discard_padding); + if (status != 0) + return status; + + m_pos = payload_stop; + + return 0; // success +} + +long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const { + assert(m_pos >= m_element_start); + + pEntry = NULL; + + if (index < 0) + return -1; // generic error + + if (m_entries_count < 0) + return E_BUFFER_NOT_FULL; + + assert(m_entries); + assert(m_entries_size > 0); + assert(m_entries_count <= m_entries_size); + + if (index < m_entries_count) { + pEntry = m_entries[index]; + assert(pEntry); + + return 1; // found entry + } + + if (m_element_size < 0) // we don't know cluster end yet + return E_BUFFER_NOT_FULL; // underflow + + const long long element_stop = m_element_start + m_element_size; + + if (m_pos >= element_stop) + return 0; // nothing left to parse + + return E_BUFFER_NOT_FULL; // underflow, since more remains to be parsed +} + +Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) { + if (!pSegment || off < 0) + return NULL; + + const long long element_start = pSegment->m_start + off; + + Cluster* const pCluster = + new (std::nothrow) Cluster(pSegment, idx, element_start); + + return pCluster; +} + +Cluster::Cluster() + : m_pSegment(NULL), + m_element_start(0), + m_index(0), + m_pos(0), + m_element_size(0), + m_timecode(0), + m_entries(NULL), + m_entries_size(0), + m_entries_count(0) // means "no entries" +{} + +Cluster::Cluster(Segment* pSegment, long idx, long long element_start + /* long long element_size */) + : m_pSegment(pSegment), + m_element_start(element_start), + m_index(idx), + m_pos(element_start), + m_element_size(-1 /* element_size */), + m_timecode(-1), + m_entries(NULL), + m_entries_size(0), + m_entries_count(-1) // means "has not been parsed yet" +{} + +Cluster::~Cluster() { + if (m_entries_count <= 0) { + delete[] m_entries; + return; + } + + BlockEntry** i = m_entries; + BlockEntry** const j = m_entries + m_entries_count; + + while (i != j) { + BlockEntry* p = *i++; + assert(p); + + delete p; + } + + delete[] m_entries; +} + +bool Cluster::EOS() const { return (m_pSegment == NULL); } + +long Cluster::GetIndex() const { return m_index; } + +long long Cluster::GetPosition() const { + const long long pos = m_element_start - m_pSegment->m_start; + assert(pos >= 0); + + return pos; +} + +long long Cluster::GetElementSize() const { return m_element_size; } + +long Cluster::HasBlockEntries( + const Segment* pSegment, + long long off, // relative to start of segment payload + long long& pos, long& len) { + assert(pSegment); + assert(off >= 0); // relative to segment + + IMkvReader* const pReader = pSegment->m_pReader; + + long long total, avail; + + long status = pReader->Length(&total, &avail); + + if (status < 0) // error + return status; + + assert((total < 0) || (avail <= total)); + + pos = pSegment->m_start + off; // absolute + + if ((total >= 0) && (pos >= total)) + return 0; // we don't even have a complete cluster + + const long long segment_stop = + (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size; + + long long cluster_stop = -1; // interpreted later to mean "unknown size" + + { + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // need more data + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && ((pos + len) > total)) + return 0; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id = ReadID(pReader, pos, len); + + if (id < 0) // error + return static_cast(id); + + if (id != libwebm::kMkvCluster) + return E_PARSE_FAILED; + + pos += len; // consume Cluster ID field + + // read size field + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // weird + return E_BUFFER_NOT_FULL; + + if ((segment_stop >= 0) && ((pos + len) > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && ((pos + len) > total)) + return 0; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + if (size == 0) + return 0; // cluster does not have entries + + pos += len; // consume size field + + // pos now points to start of payload + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size != unknown_size) { + cluster_stop = pos + size; + assert(cluster_stop >= 0); + + if ((segment_stop >= 0) && (cluster_stop > segment_stop)) + return E_FILE_FORMAT_INVALID; + + if ((total >= 0) && (cluster_stop > total)) + // return E_FILE_FORMAT_INVALID; //too conservative + return 0; // cluster does not have any entries + } + } + + for (;;) { + if ((cluster_stop >= 0) && (pos >= cluster_stop)) + return 0; // no entries detected + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + long long result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // need more data + return E_BUFFER_NOT_FULL; + + if ((cluster_stop >= 0) && ((pos + len) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long id = ReadID(pReader, pos, len); + + if (id < 0) // error + return static_cast(id); + + // This is the distinguished set of ID's we use to determine + // that we have exhausted the sub-element's inside the cluster + // whose ID we parsed earlier. + + if (id == libwebm::kMkvCluster) + return 0; // no entries found + + if (id == libwebm::kMkvCues) + return 0; // no entries found + + pos += len; // consume id field + + if ((cluster_stop >= 0) && (pos >= cluster_stop)) + return E_FILE_FORMAT_INVALID; + + // read size field + + if ((pos + 1) > avail) { + len = 1; + return E_BUFFER_NOT_FULL; + } + + result = GetUIntLength(pReader, pos, len); + + if (result < 0) // error + return static_cast(result); + + if (result > 0) // underflow + return E_BUFFER_NOT_FULL; + + if ((cluster_stop >= 0) && ((pos + len) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > avail) + return E_BUFFER_NOT_FULL; + + const long long size = ReadUInt(pReader, pos, len); + + if (size < 0) // error + return static_cast(size); + + pos += len; // consume size field + + // pos now points to start of payload + + if ((cluster_stop >= 0) && (pos > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if (size == 0) // weird + continue; + + const long long unknown_size = (1LL << (7 * len)) - 1; + + if (size == unknown_size) + return E_FILE_FORMAT_INVALID; // not supported inside cluster + + if ((cluster_stop >= 0) && ((pos + size) > cluster_stop)) + return E_FILE_FORMAT_INVALID; + + if (id == libwebm::kMkvBlockGroup) + return 1; // have at least one entry + + if (id == libwebm::kMkvSimpleBlock) + return 1; // have at least one entry + + pos += size; // consume payload + if (cluster_stop >= 0 && pos > cluster_stop) + return E_FILE_FORMAT_INVALID; + } +} + +long long Cluster::GetTimeCode() const { + long long pos; + long len; + + const long status = Load(pos, len); + + if (status < 0) // error + return status; + + return m_timecode; +} + +long long Cluster::GetTime() const { + const long long tc = GetTimeCode(); + + if (tc < 0) + return tc; + + const SegmentInfo* const pInfo = m_pSegment->GetInfo(); + assert(pInfo); + + const long long scale = pInfo->GetTimeCodeScale(); + assert(scale >= 1); + + const long long t = m_timecode * scale; + + return t; +} + +long long Cluster::GetFirstTime() const { + const BlockEntry* pEntry; + + const long status = GetFirst(pEntry); + + if (status < 0) // error + return status; + + if (pEntry == NULL) // empty cluster + return GetTime(); + + const Block* const pBlock = pEntry->GetBlock(); + assert(pBlock); + + return pBlock->GetTime(this); +} + +long long Cluster::GetLastTime() const { + const BlockEntry* pEntry; + + const long status = GetLast(pEntry); + + if (status < 0) // error + return status; + + if (pEntry == NULL) // empty cluster + return GetTime(); + + const Block* const pBlock = pEntry->GetBlock(); + assert(pBlock); + + return pBlock->GetTime(this); +} + +long Cluster::CreateBlock(long long id, + long long pos, // absolute pos of payload + long long size, long long discard_padding) { + if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock) + return E_PARSE_FAILED; + + if (m_entries_count < 0) { // haven't parsed anything yet + assert(m_entries == NULL); + assert(m_entries_size == 0); + + m_entries_size = 1024; + m_entries = new (std::nothrow) BlockEntry*[m_entries_size]; + if (m_entries == NULL) + return -1; + + m_entries_count = 0; + } else { + assert(m_entries); + assert(m_entries_size > 0); + assert(m_entries_count <= m_entries_size); + + if (m_entries_count >= m_entries_size) { + const long entries_size = 2 * m_entries_size; + + BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size]; + if (entries == NULL) + return -1; + + BlockEntry** src = m_entries; + BlockEntry** const src_end = src + m_entries_count; + + BlockEntry** dst = entries; + + while (src != src_end) + *dst++ = *src++; + + delete[] m_entries; + + m_entries = entries; + m_entries_size = entries_size; + } + } + + if (id == libwebm::kMkvBlockGroup) + return CreateBlockGroup(pos, size, discard_padding); + else + return CreateSimpleBlock(pos, size); +} + +long Cluster::CreateBlockGroup(long long start_offset, long long size, + long long discard_padding) { + assert(m_entries); + assert(m_entries_size > 0); + assert(m_entries_count >= 0); + assert(m_entries_count < m_entries_size); + + IMkvReader* const pReader = m_pSegment->m_pReader; + + long long pos = start_offset; + const long long stop = start_offset + size; + + // For WebM files, there is a bias towards previous reference times + //(in order to support alt-ref frames, which refer back to the previous + // keyframe). Normally a 0 value is not possible, but here we tenatively + // allow 0 as the value of a reference frame, with the interpretation + // that this is a "previous" reference time. + + long long prev = 1; // nonce + long long next = 0; // nonce + long long duration = -1; // really, this is unsigned + + long long bpos = -1; + long long bsize = -1; + + while (pos < stop) { + long len; + const long long id = ReadID(pReader, pos, len); + if (id < 0 || (pos + len) > stop) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume ID + + const long long size = ReadUInt(pReader, pos, len); + assert(size >= 0); // TODO + assert((pos + len) <= stop); + + pos += len; // consume size + + if (id == libwebm::kMkvBlock) { + if (bpos < 0) { // Block ID + bpos = pos; + bsize = size; + } + } else if (id == libwebm::kMkvBlockDuration) { + if (size > 8) + return E_FILE_FORMAT_INVALID; + + duration = UnserializeUInt(pReader, pos, size); + + if (duration < 0) + return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvReferenceBlock) { + if (size > 8 || size <= 0) + return E_FILE_FORMAT_INVALID; + const long size_ = static_cast(size); + + long long time; + + long status = UnserializeInt(pReader, pos, size_, time); + assert(status == 0); + if (status != 0) + return -1; + + if (time <= 0) // see note above + prev = time; + else + next = time; + } + + pos += size; // consume payload + if (pos > stop) + return E_FILE_FORMAT_INVALID; + } + if (bpos < 0) + return E_FILE_FORMAT_INVALID; + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + assert(bsize >= 0); + + const long idx = m_entries_count; + + BlockEntry** const ppEntry = m_entries + idx; + BlockEntry*& pEntry = *ppEntry; + + pEntry = new (std::nothrow) + BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding); + + if (pEntry == NULL) + return -1; // generic error + + BlockGroup* const p = static_cast(pEntry); + + const long status = p->Parse(); + + if (status == 0) { // success + ++m_entries_count; + return 0; + } + + delete pEntry; + pEntry = 0; + + return status; +} + +long Cluster::CreateSimpleBlock(long long st, long long sz) { + assert(m_entries); + assert(m_entries_size > 0); + assert(m_entries_count >= 0); + assert(m_entries_count < m_entries_size); + + const long idx = m_entries_count; + + BlockEntry** const ppEntry = m_entries + idx; + BlockEntry*& pEntry = *ppEntry; + + pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz); + + if (pEntry == NULL) + return -1; // generic error + + SimpleBlock* const p = static_cast(pEntry); + + const long status = p->Parse(); + + if (status == 0) { + ++m_entries_count; + return 0; + } + + delete pEntry; + pEntry = 0; + + return status; +} + +long Cluster::GetFirst(const BlockEntry*& pFirst) const { + if (m_entries_count <= 0) { + long long pos; + long len; + + const long status = Parse(pos, len); + + if (status < 0) { // error + pFirst = NULL; + return status; + } + + if (m_entries_count <= 0) { // empty cluster + pFirst = NULL; + return 0; + } + } + + assert(m_entries); + + pFirst = m_entries[0]; + assert(pFirst); + + return 0; // success +} + +long Cluster::GetLast(const BlockEntry*& pLast) const { + for (;;) { + long long pos; + long len; + + const long status = Parse(pos, len); + + if (status < 0) { // error + pLast = NULL; + return status; + } + + if (status > 0) // no new block + break; + } + + if (m_entries_count <= 0) { + pLast = NULL; + return 0; + } + + assert(m_entries); + + const long idx = m_entries_count - 1; + + pLast = m_entries[idx]; + assert(pLast); + + return 0; +} + +long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const { + assert(pCurr); + assert(m_entries); + assert(m_entries_count > 0); + + size_t idx = pCurr->GetIndex(); + assert(idx < size_t(m_entries_count)); + assert(m_entries[idx] == pCurr); + + ++idx; + + if (idx >= size_t(m_entries_count)) { + long long pos; + long len; + + const long status = Parse(pos, len); + + if (status < 0) { // error + pNext = NULL; + return status; + } + + if (status > 0) { + pNext = NULL; + return 0; + } + + assert(m_entries); + assert(m_entries_count > 0); + assert(idx < size_t(m_entries_count)); + } + + pNext = m_entries[idx]; + assert(pNext); + + return 0; +} + +long Cluster::GetEntryCount() const { return m_entries_count; } + +const BlockEntry* Cluster::GetEntry(const Track* pTrack, + long long time_ns) const { + assert(pTrack); + + if (m_pSegment == NULL) // this is the special EOS cluster + return pTrack->GetEOS(); + + const BlockEntry* pResult = pTrack->GetEOS(); + + long index = 0; + + for (;;) { + if (index >= m_entries_count) { + long long pos; + long len; + + const long status = Parse(pos, len); + assert(status >= 0); + + if (status > 0) // completely parsed, and no more entries + return pResult; + + if (status < 0) // should never happen + return 0; + + assert(m_entries); + assert(index < m_entries_count); + } + + const BlockEntry* const pEntry = m_entries[index]; + assert(pEntry); + assert(!pEntry->EOS()); + + const Block* const pBlock = pEntry->GetBlock(); + assert(pBlock); + + if (pBlock->GetTrackNumber() != pTrack->GetNumber()) { + ++index; + continue; + } + + if (pTrack->VetEntry(pEntry)) { + if (time_ns < 0) // just want first candidate block + return pEntry; + + const long long ns = pBlock->GetTime(this); + + if (ns > time_ns) + return pResult; + + pResult = pEntry; // have a candidate + } else if (time_ns >= 0) { + const long long ns = pBlock->GetTime(this); + + if (ns > time_ns) + return pResult; + } + + ++index; + } +} + +const BlockEntry* Cluster::GetEntry(const CuePoint& cp, + const CuePoint::TrackPosition& tp) const { + assert(m_pSegment); + const long long tc = cp.GetTimeCode(); + + if (tp.m_block > 0) { + const long block = static_cast(tp.m_block); + const long index = block - 1; + + while (index >= m_entries_count) { + long long pos; + long len; + + const long status = Parse(pos, len); + + if (status < 0) // TODO: can this happen? + return NULL; + + if (status > 0) // nothing remains to be parsed + return NULL; + } + + const BlockEntry* const pEntry = m_entries[index]; + assert(pEntry); + assert(!pEntry->EOS()); + + const Block* const pBlock = pEntry->GetBlock(); + assert(pBlock); + + if ((pBlock->GetTrackNumber() == tp.m_track) && + (pBlock->GetTimeCode(this) == tc)) { + return pEntry; + } + } + + long index = 0; + + for (;;) { + if (index >= m_entries_count) { + long long pos; + long len; + + const long status = Parse(pos, len); + + if (status < 0) // TODO: can this happen? + return NULL; + + if (status > 0) // nothing remains to be parsed + return NULL; + + assert(m_entries); + assert(index < m_entries_count); + } + + const BlockEntry* const pEntry = m_entries[index]; + assert(pEntry); + assert(!pEntry->EOS()); + + const Block* const pBlock = pEntry->GetBlock(); + assert(pBlock); + + if (pBlock->GetTrackNumber() != tp.m_track) { + ++index; + continue; + } + + const long long tc_ = pBlock->GetTimeCode(this); + + if (tc_ < tc) { + ++index; + continue; + } + + if (tc_ > tc) + return NULL; + + const Tracks* const pTracks = m_pSegment->GetTracks(); + assert(pTracks); + + const long tn = static_cast(tp.m_track); + const Track* const pTrack = pTracks->GetTrackByNumber(tn); + + if (pTrack == NULL) + return NULL; + + const long long type = pTrack->GetType(); + + if (type == 2) // audio + return pEntry; + + if (type != 1) // not video + return NULL; + + if (!pBlock->IsKey()) + return NULL; + + return pEntry; + } +} + +BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {} +BlockEntry::~BlockEntry() {} +const Cluster* BlockEntry::GetCluster() const { return m_pCluster; } +long BlockEntry::GetIndex() const { return m_index; } + +SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start, + long long size) + : BlockEntry(pCluster, idx), m_block(start, size, 0) {} + +long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); } +BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; } +const Block* SimpleBlock::GetBlock() const { return &m_block; } + +BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start, + long long block_size, long long prev, long long next, + long long duration, long long discard_padding) + : BlockEntry(pCluster, idx), + m_block(block_start, block_size, discard_padding), + m_prev(prev), + m_next(next), + m_duration(duration) {} + +long BlockGroup::Parse() { + const long status = m_block.Parse(m_pCluster); + + if (status) + return status; + + m_block.SetKey((m_prev > 0) && (m_next <= 0)); + + return 0; +} + +BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; } +const Block* BlockGroup::GetBlock() const { return &m_block; } +long long BlockGroup::GetPrevTimeCode() const { return m_prev; } +long long BlockGroup::GetNextTimeCode() const { return m_next; } +long long BlockGroup::GetDurationTimeCode() const { return m_duration; } + +Block::Block(long long start, long long size_, long long discard_padding) + : m_start(start), + m_size(size_), + m_track(0), + m_timecode(-1), + m_flags(0), + m_frames(NULL), + m_frame_count(-1), + m_discard_padding(discard_padding) {} + +Block::~Block() { delete[] m_frames; } + +long Block::Parse(const Cluster* pCluster) { + if (pCluster == NULL) + return -1; + + if (pCluster->m_pSegment == NULL) + return -1; + + assert(m_start >= 0); + assert(m_size >= 0); + assert(m_track <= 0); + assert(m_frames == NULL); + assert(m_frame_count <= 0); + + long long pos = m_start; + const long long stop = m_start + m_size; + + long len; + + IMkvReader* const pReader = pCluster->m_pSegment->m_pReader; + + m_track = ReadUInt(pReader, pos, len); + + if (m_track <= 0) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > stop) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume track number + + if ((stop - pos) < 2) + return E_FILE_FORMAT_INVALID; + + long status; + long long value; + + status = UnserializeInt(pReader, pos, 2, value); + + if (status) + return E_FILE_FORMAT_INVALID; + + if (value < SHRT_MIN) + return E_FILE_FORMAT_INVALID; + + if (value > SHRT_MAX) + return E_FILE_FORMAT_INVALID; + + m_timecode = static_cast(value); + + pos += 2; + + if ((stop - pos) <= 0) + return E_FILE_FORMAT_INVALID; + + status = pReader->Read(pos, 1, &m_flags); + + if (status) + return E_FILE_FORMAT_INVALID; + + const int lacing = int(m_flags & 0x06) >> 1; + + ++pos; // consume flags byte + + if (lacing == 0) { // no lacing + if (pos > stop) + return E_FILE_FORMAT_INVALID; + + m_frame_count = 1; + m_frames = new (std::nothrow) Frame[m_frame_count]; + if (m_frames == NULL) + return -1; + + Frame& f = m_frames[0]; + f.pos = pos; + + const long long frame_size = stop - pos; + + if (frame_size > LONG_MAX || frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + f.len = static_cast(frame_size); + + return 0; // success + } + + if (pos >= stop) + return E_FILE_FORMAT_INVALID; + + unsigned char biased_count; + + status = pReader->Read(pos, 1, &biased_count); + + if (status) + return E_FILE_FORMAT_INVALID; + + ++pos; // consume frame count + if (pos > stop) + return E_FILE_FORMAT_INVALID; + + m_frame_count = int(biased_count) + 1; + + m_frames = new (std::nothrow) Frame[m_frame_count]; + if (m_frames == NULL) + return -1; + + if (!m_frames) + return E_FILE_FORMAT_INVALID; + + if (lacing == 1) { // Xiph + Frame* pf = m_frames; + Frame* const pf_end = pf + m_frame_count; + + long long size = 0; + int frame_count = m_frame_count; + + while (frame_count > 1) { + long frame_size = 0; + + for (;;) { + unsigned char val; + + if (pos >= stop) + return E_FILE_FORMAT_INVALID; + + status = pReader->Read(pos, 1, &val); + + if (status) + return E_FILE_FORMAT_INVALID; + + ++pos; // consume xiph size byte + + frame_size += val; + + if (val < 255) + break; + } + + Frame& f = *pf++; + assert(pf < pf_end); + if (pf >= pf_end) + return E_FILE_FORMAT_INVALID; + + f.pos = 0; // patch later + + if (frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + f.len = frame_size; + size += frame_size; // contribution of this frame + + --frame_count; + } + + if (pf >= pf_end || pos > stop) + return E_FILE_FORMAT_INVALID; + + { + Frame& f = *pf++; + + if (pf != pf_end) + return E_FILE_FORMAT_INVALID; + + f.pos = 0; // patch later + + const long long total_size = stop - pos; + + if (total_size < size) + return E_FILE_FORMAT_INVALID; + + const long long frame_size = total_size - size; + + if (frame_size > LONG_MAX || frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + f.len = static_cast(frame_size); + } + + pf = m_frames; + while (pf != pf_end) { + Frame& f = *pf++; + assert((pos + f.len) <= stop); + + if ((pos + f.len) > stop) + return E_FILE_FORMAT_INVALID; + + f.pos = pos; + pos += f.len; + } + + assert(pos == stop); + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + } else if (lacing == 2) { // fixed-size lacing + if (pos >= stop) + return E_FILE_FORMAT_INVALID; + + const long long total_size = stop - pos; + + if ((total_size % m_frame_count) != 0) + return E_FILE_FORMAT_INVALID; + + const long long frame_size = total_size / m_frame_count; + + if (frame_size > LONG_MAX || frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + Frame* pf = m_frames; + Frame* const pf_end = pf + m_frame_count; + + while (pf != pf_end) { + assert((pos + frame_size) <= stop); + if ((pos + frame_size) > stop) + return E_FILE_FORMAT_INVALID; + + Frame& f = *pf++; + + f.pos = pos; + f.len = static_cast(frame_size); + + pos += frame_size; + } + + assert(pos == stop); + if (pos != stop) + return E_FILE_FORMAT_INVALID; + + } else { + assert(lacing == 3); // EBML lacing + + if (pos >= stop) + return E_FILE_FORMAT_INVALID; + + long long size = 0; + int frame_count = m_frame_count; + + long long frame_size = ReadUInt(pReader, pos, len); + + if (frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + if (frame_size > LONG_MAX) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > stop) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume length of size of first frame + + if ((pos + frame_size) > stop) + return E_FILE_FORMAT_INVALID; + + Frame* pf = m_frames; + Frame* const pf_end = pf + m_frame_count; + + { + Frame& curr = *pf; + + curr.pos = 0; // patch later + + curr.len = static_cast(frame_size); + size += curr.len; // contribution of this frame + } + + --frame_count; + + while (frame_count > 1) { + if (pos >= stop) + return E_FILE_FORMAT_INVALID; + + assert(pf < pf_end); + if (pf >= pf_end) + return E_FILE_FORMAT_INVALID; + + const Frame& prev = *pf++; + assert(prev.len == frame_size); + if (prev.len != frame_size) + return E_FILE_FORMAT_INVALID; + + assert(pf < pf_end); + if (pf >= pf_end) + return E_FILE_FORMAT_INVALID; + + Frame& curr = *pf; + + curr.pos = 0; // patch later + + const long long delta_size_ = ReadUInt(pReader, pos, len); + + if (delta_size_ < 0) + return E_FILE_FORMAT_INVALID; + + if ((pos + len) > stop) + return E_FILE_FORMAT_INVALID; + + pos += len; // consume length of (delta) size + if (pos > stop) + return E_FILE_FORMAT_INVALID; + + const long exp = 7 * len - 1; + const long long bias = (1LL << exp) - 1LL; + const long long delta_size = delta_size_ - bias; + + frame_size += delta_size; + + if (frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + if (frame_size > LONG_MAX) + return E_FILE_FORMAT_INVALID; + + curr.len = static_cast(frame_size); + // Check if size + curr.len could overflow. + if (size > LLONG_MAX - curr.len) { + return E_FILE_FORMAT_INVALID; + } + size += curr.len; // contribution of this frame + + --frame_count; + } + + // parse last frame + if (frame_count > 0) { + if (pos > stop || pf >= pf_end) + return E_FILE_FORMAT_INVALID; + + const Frame& prev = *pf++; + assert(prev.len == frame_size); + if (prev.len != frame_size) + return E_FILE_FORMAT_INVALID; + + if (pf >= pf_end) + return E_FILE_FORMAT_INVALID; + + Frame& curr = *pf++; + if (pf != pf_end) + return E_FILE_FORMAT_INVALID; + + curr.pos = 0; // patch later + + const long long total_size = stop - pos; + + if (total_size < size) + return E_FILE_FORMAT_INVALID; + + frame_size = total_size - size; + + if (frame_size > LONG_MAX || frame_size <= 0) + return E_FILE_FORMAT_INVALID; + + curr.len = static_cast(frame_size); + } + + pf = m_frames; + while (pf != pf_end) { + Frame& f = *pf++; + if ((pos + f.len) > stop) + return E_FILE_FORMAT_INVALID; + + f.pos = pos; + pos += f.len; + } + + if (pos != stop) + return E_FILE_FORMAT_INVALID; + } + + return 0; // success +} + +long long Block::GetTimeCode(const Cluster* pCluster) const { + if (pCluster == 0) + return m_timecode; + + const long long tc0 = pCluster->GetTimeCode(); + assert(tc0 >= 0); + + // Check if tc0 + m_timecode would overflow. + if (tc0 < 0 || LLONG_MAX - tc0 < m_timecode) { + return -1; + } + + const long long tc = tc0 + m_timecode; + + return tc; // unscaled timecode units +} + +long long Block::GetTime(const Cluster* pCluster) const { + assert(pCluster); + + const long long tc = GetTimeCode(pCluster); + + const Segment* const pSegment = pCluster->m_pSegment; + const SegmentInfo* const pInfo = pSegment->GetInfo(); + assert(pInfo); + + const long long scale = pInfo->GetTimeCodeScale(); + assert(scale >= 1); + + // Check if tc * scale could overflow. + if (tc != 0 && scale > LLONG_MAX / tc) { + return -1; + } + const long long ns = tc * scale; + + return ns; +} + +long long Block::GetTrackNumber() const { return m_track; } + +bool Block::IsKey() const { + return ((m_flags & static_cast(1 << 7)) != 0); +} + +void Block::SetKey(bool bKey) { + if (bKey) + m_flags |= static_cast(1 << 7); + else + m_flags &= 0x7F; +} + +bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); } + +Block::Lacing Block::GetLacing() const { + const int value = int(m_flags & 0x06) >> 1; + return static_cast(value); +} + +int Block::GetFrameCount() const { return m_frame_count; } + +const Block::Frame& Block::GetFrame(int idx) const { + assert(idx >= 0); + assert(idx < m_frame_count); + + const Frame& f = m_frames[idx]; + assert(f.pos > 0); + assert(f.len > 0); + + return f; +} + +long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const { + assert(pReader); + assert(buf); + + const long status = pReader->Read(pos, len, buf); + return status; +} + +long long Block::GetDiscardPadding() const { return m_discard_padding; } + +} // namespace mkvparser diff --git a/mkvparser/mkvparser.h b/mkvparser/mkvparser.h new file mode 100644 index 000000000000..848d01f03ece --- /dev/null +++ b/mkvparser/mkvparser.h @@ -0,0 +1,1147 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef MKVPARSER_MKVPARSER_H_ +#define MKVPARSER_MKVPARSER_H_ + +#include + +namespace mkvparser { + +const int E_PARSE_FAILED = -1; +const int E_FILE_FORMAT_INVALID = -2; +const int E_BUFFER_NOT_FULL = -3; + +class IMkvReader { + public: + virtual int Read(long long pos, long len, unsigned char* buf) = 0; + virtual int Length(long long* total, long long* available) = 0; + + protected: + virtual ~IMkvReader() {} +}; + +template +Type* SafeArrayAlloc(unsigned long long num_elements, + unsigned long long element_size); +long long GetUIntLength(IMkvReader*, long long, long&); +long long ReadUInt(IMkvReader*, long long, long&); +long long ReadID(IMkvReader* pReader, long long pos, long& len); +long long UnserializeUInt(IMkvReader*, long long pos, long long size); + +long UnserializeFloat(IMkvReader*, long long pos, long long size, double&); +long UnserializeInt(IMkvReader*, long long pos, long long size, + long long& result); + +long UnserializeString(IMkvReader*, long long pos, long long size, char*& str); + +long ParseElementHeader(IMkvReader* pReader, + long long& pos, // consume id and size fields + long long stop, // if you know size of element's parent + long long& id, long long& size); + +bool Match(IMkvReader*, long long&, unsigned long, long long&); +bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&); + +void GetVersion(int& major, int& minor, int& build, int& revision); + +struct EBMLHeader { + EBMLHeader(); + ~EBMLHeader(); + long long m_version; + long long m_readVersion; + long long m_maxIdLength; + long long m_maxSizeLength; + char* m_docType; + long long m_docTypeVersion; + long long m_docTypeReadVersion; + + long long Parse(IMkvReader*, long long&); + void Init(); +}; + +class Segment; +class Track; +class Cluster; + +class Block { + Block(const Block&); + Block& operator=(const Block&); + + public: + const long long m_start; + const long long m_size; + + Block(long long start, long long size, long long discard_padding); + ~Block(); + + long Parse(const Cluster*); + + long long GetTrackNumber() const; + long long GetTimeCode(const Cluster*) const; // absolute, but not scaled + long long GetTime(const Cluster*) const; // absolute, and scaled (ns) + bool IsKey() const; + void SetKey(bool); + bool IsInvisible() const; + + enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml }; + Lacing GetLacing() const; + + int GetFrameCount() const; // to index frames: [0, count) + + struct Frame { + long long pos; // absolute offset + long len; + + long Read(IMkvReader*, unsigned char*) const; + }; + + const Frame& GetFrame(int frame_index) const; + + long long GetDiscardPadding() const; + + private: + long long m_track; // Track::Number() + short m_timecode; // relative to cluster + unsigned char m_flags; + + Frame* m_frames; + int m_frame_count; + + protected: + const long long m_discard_padding; +}; + +class BlockEntry { + BlockEntry(const BlockEntry&); + BlockEntry& operator=(const BlockEntry&); + + protected: + BlockEntry(Cluster*, long index); + + public: + virtual ~BlockEntry(); + + bool EOS() const { return (GetKind() == kBlockEOS); } + const Cluster* GetCluster() const; + long GetIndex() const; + virtual const Block* GetBlock() const = 0; + + enum Kind { kBlockEOS, kBlockSimple, kBlockGroup }; + virtual Kind GetKind() const = 0; + + protected: + Cluster* const m_pCluster; + const long m_index; +}; + +class SimpleBlock : public BlockEntry { + SimpleBlock(const SimpleBlock&); + SimpleBlock& operator=(const SimpleBlock&); + + public: + SimpleBlock(Cluster*, long index, long long start, long long size); + long Parse(); + + Kind GetKind() const; + const Block* GetBlock() const; + + protected: + Block m_block; +}; + +class BlockGroup : public BlockEntry { + BlockGroup(const BlockGroup&); + BlockGroup& operator=(const BlockGroup&); + + public: + BlockGroup(Cluster*, long index, + long long block_start, // absolute pos of block's payload + long long block_size, // size of block's payload + long long prev, long long next, long long duration, + long long discard_padding); + + long Parse(); + + Kind GetKind() const; + const Block* GetBlock() const; + + long long GetPrevTimeCode() const; // relative to block's time + long long GetNextTimeCode() const; // as above + long long GetDurationTimeCode() const; + + private: + Block m_block; + const long long m_prev; + const long long m_next; + const long long m_duration; +}; + +/////////////////////////////////////////////////////////////// +// ContentEncoding element +// Elements used to describe if the track data has been encrypted or +// compressed with zlib or header stripping. +class ContentEncoding { + public: + enum { kCTR = 1 }; + + ContentEncoding(); + ~ContentEncoding(); + + // ContentCompression element names + struct ContentCompression { + ContentCompression(); + ~ContentCompression(); + + unsigned long long algo; + unsigned char* settings; + long long settings_len; + }; + + // ContentEncAESSettings element names + struct ContentEncAESSettings { + ContentEncAESSettings() : cipher_mode(kCTR) {} + ~ContentEncAESSettings() {} + + unsigned long long cipher_mode; + }; + + // ContentEncryption element names + struct ContentEncryption { + ContentEncryption(); + ~ContentEncryption(); + + unsigned long long algo; + unsigned char* key_id; + long long key_id_len; + unsigned char* signature; + long long signature_len; + unsigned char* sig_key_id; + long long sig_key_id_len; + unsigned long long sig_algo; + unsigned long long sig_hash_algo; + + ContentEncAESSettings aes_settings; + }; + + // Returns ContentCompression represented by |idx|. Returns NULL if |idx| + // is out of bounds. + const ContentCompression* GetCompressionByIndex(unsigned long idx) const; + + // Returns number of ContentCompression elements in this ContentEncoding + // element. + unsigned long GetCompressionCount() const; + + // Parses the ContentCompression element from |pReader|. |start| is the + // starting offset of the ContentCompression payload. |size| is the size in + // bytes of the ContentCompression payload. |compression| is where the parsed + // values will be stored. + long ParseCompressionEntry(long long start, long long size, + IMkvReader* pReader, + ContentCompression* compression); + + // Returns ContentEncryption represented by |idx|. Returns NULL if |idx| + // is out of bounds. + const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const; + + // Returns number of ContentEncryption elements in this ContentEncoding + // element. + unsigned long GetEncryptionCount() const; + + // Parses the ContentEncAESSettings element from |pReader|. |start| is the + // starting offset of the ContentEncAESSettings payload. |size| is the + // size in bytes of the ContentEncAESSettings payload. |encryption| is + // where the parsed values will be stored. + long ParseContentEncAESSettingsEntry(long long start, long long size, + IMkvReader* pReader, + ContentEncAESSettings* aes); + + // Parses the ContentEncoding element from |pReader|. |start| is the + // starting offset of the ContentEncoding payload. |size| is the size in + // bytes of the ContentEncoding payload. Returns true on success. + long ParseContentEncodingEntry(long long start, long long size, + IMkvReader* pReader); + + // Parses the ContentEncryption element from |pReader|. |start| is the + // starting offset of the ContentEncryption payload. |size| is the size in + // bytes of the ContentEncryption payload. |encryption| is where the parsed + // values will be stored. + long ParseEncryptionEntry(long long start, long long size, + IMkvReader* pReader, ContentEncryption* encryption); + + unsigned long long encoding_order() const { return encoding_order_; } + unsigned long long encoding_scope() const { return encoding_scope_; } + unsigned long long encoding_type() const { return encoding_type_; } + + private: + // Member variables for list of ContentCompression elements. + ContentCompression** compression_entries_; + ContentCompression** compression_entries_end_; + + // Member variables for list of ContentEncryption elements. + ContentEncryption** encryption_entries_; + ContentEncryption** encryption_entries_end_; + + // ContentEncoding element names + unsigned long long encoding_order_; + unsigned long long encoding_scope_; + unsigned long long encoding_type_; + + // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); + ContentEncoding(const ContentEncoding&); + ContentEncoding& operator=(const ContentEncoding&); +}; + +class Track { + Track(const Track&); + Track& operator=(const Track&); + + public: + class Info; + static long Create(Segment*, const Info&, long long element_start, + long long element_size, Track*&); + + enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 }; + + Segment* const m_pSegment; + const long long m_element_start; + const long long m_element_size; + virtual ~Track(); + + long GetType() const; + long GetNumber() const; + unsigned long long GetUid() const; + const char* GetNameAsUTF8() const; + const char* GetLanguage() const; + const char* GetCodecNameAsUTF8() const; + const char* GetCodecId() const; + const unsigned char* GetCodecPrivate(size_t&) const; + bool GetLacing() const; + unsigned long long GetDefaultDuration() const; + unsigned long long GetCodecDelay() const; + unsigned long long GetSeekPreRoll() const; + + const BlockEntry* GetEOS() const; + + struct Settings { + long long start; + long long size; + }; + + class Info { + public: + Info(); + ~Info(); + int Copy(Info&) const; + void Clear(); + long type; + long number; + unsigned long long uid; + unsigned long long defaultDuration; + unsigned long long codecDelay; + unsigned long long seekPreRoll; + char* nameAsUTF8; + char* language; + char* codecId; + char* codecNameAsUTF8; + unsigned char* codecPrivate; + size_t codecPrivateSize; + bool lacing; + Settings settings; + + private: + Info(const Info&); + Info& operator=(const Info&); + int CopyStr(char* Info::*str, Info&) const; + }; + + long GetFirst(const BlockEntry*&) const; + long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; + virtual bool VetEntry(const BlockEntry*) const; + virtual long Seek(long long time_ns, const BlockEntry*&) const; + + const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; + unsigned long GetContentEncodingCount() const; + + long ParseContentEncodingsEntry(long long start, long long size); + + protected: + Track(Segment*, long long element_start, long long element_size); + + Info m_info; + + class EOSBlock : public BlockEntry { + public: + EOSBlock(); + + Kind GetKind() const; + const Block* GetBlock() const; + }; + + EOSBlock m_eos; + + private: + ContentEncoding** content_encoding_entries_; + ContentEncoding** content_encoding_entries_end_; +}; + +struct PrimaryChromaticity { + PrimaryChromaticity() : x(0), y(0) {} + ~PrimaryChromaticity() {} + static bool Parse(IMkvReader* reader, long long read_pos, + long long value_size, bool is_x, + PrimaryChromaticity** chromaticity); + float x; + float y; +}; + +struct MasteringMetadata { + static const float kValueNotPresent; + + MasteringMetadata() + : r(NULL), + g(NULL), + b(NULL), + white_point(NULL), + luminance_max(kValueNotPresent), + luminance_min(kValueNotPresent) {} + ~MasteringMetadata() { + delete r; + delete g; + delete b; + delete white_point; + } + + static bool Parse(IMkvReader* reader, long long element_start, + long long element_size, + MasteringMetadata** mastering_metadata); + + PrimaryChromaticity* r; + PrimaryChromaticity* g; + PrimaryChromaticity* b; + PrimaryChromaticity* white_point; + float luminance_max; + float luminance_min; +}; + +struct Colour { + static const long long kValueNotPresent; + + // Unless otherwise noted all values assigned upon construction are the + // equivalent of unspecified/default. + Colour() + : matrix_coefficients(kValueNotPresent), + bits_per_channel(kValueNotPresent), + chroma_subsampling_horz(kValueNotPresent), + chroma_subsampling_vert(kValueNotPresent), + cb_subsampling_horz(kValueNotPresent), + cb_subsampling_vert(kValueNotPresent), + chroma_siting_horz(kValueNotPresent), + chroma_siting_vert(kValueNotPresent), + range(kValueNotPresent), + transfer_characteristics(kValueNotPresent), + primaries(kValueNotPresent), + max_cll(kValueNotPresent), + max_fall(kValueNotPresent), + mastering_metadata(NULL) {} + ~Colour() { + delete mastering_metadata; + mastering_metadata = NULL; + } + + static bool Parse(IMkvReader* reader, long long element_start, + long long element_size, Colour** colour); + + long long matrix_coefficients; + long long bits_per_channel; + long long chroma_subsampling_horz; + long long chroma_subsampling_vert; + long long cb_subsampling_horz; + long long cb_subsampling_vert; + long long chroma_siting_horz; + long long chroma_siting_vert; + long long range; + long long transfer_characteristics; + long long primaries; + long long max_cll; + long long max_fall; + + MasteringMetadata* mastering_metadata; +}; + +struct Projection { + enum ProjectionType { + kTypeNotPresent = -1, + kRectangular = 0, + kEquirectangular = 1, + kCubeMap = 2, + kMesh = 3, + }; + static const float kValueNotPresent; + Projection() + : type(kTypeNotPresent), + private_data(NULL), + private_data_length(0), + pose_yaw(kValueNotPresent), + pose_pitch(kValueNotPresent), + pose_roll(kValueNotPresent) {} + ~Projection() { delete[] private_data; } + static bool Parse(IMkvReader* reader, long long element_start, + long long element_size, Projection** projection); + + ProjectionType type; + unsigned char* private_data; + size_t private_data_length; + float pose_yaw; + float pose_pitch; + float pose_roll; +}; + +class VideoTrack : public Track { + VideoTrack(const VideoTrack&); + VideoTrack& operator=(const VideoTrack&); + + VideoTrack(Segment*, long long element_start, long long element_size); + + public: + virtual ~VideoTrack(); + static long Parse(Segment*, const Info&, long long element_start, + long long element_size, VideoTrack*&); + + long long GetWidth() const; + long long GetHeight() const; + long long GetDisplayWidth() const; + long long GetDisplayHeight() const; + long long GetDisplayUnit() const; + long long GetStereoMode() const; + double GetFrameRate() const; + + bool VetEntry(const BlockEntry*) const; + long Seek(long long time_ns, const BlockEntry*&) const; + + Colour* GetColour() const; + + Projection* GetProjection() const; + + const char* GetColourSpace() const { return m_colour_space; } + + private: + long long m_width; + long long m_height; + long long m_display_width; + long long m_display_height; + long long m_display_unit; + long long m_stereo_mode; + char* m_colour_space; + double m_rate; + + Colour* m_colour; + Projection* m_projection; +}; + +class AudioTrack : public Track { + AudioTrack(const AudioTrack&); + AudioTrack& operator=(const AudioTrack&); + + AudioTrack(Segment*, long long element_start, long long element_size); + + public: + static long Parse(Segment*, const Info&, long long element_start, + long long element_size, AudioTrack*&); + + double GetSamplingRate() const; + long long GetChannels() const; + long long GetBitDepth() const; + + private: + double m_rate; + long long m_channels; + long long m_bitDepth; +}; + +class Tracks { + Tracks(const Tracks&); + Tracks& operator=(const Tracks&); + + public: + Segment* const m_pSegment; + const long long m_start; + const long long m_size; + const long long m_element_start; + const long long m_element_size; + + Tracks(Segment*, long long start, long long size, long long element_start, + long long element_size); + + ~Tracks(); + + long Parse(); + + unsigned long GetTracksCount() const; + + const Track* GetTrackByNumber(long tn) const; + const Track* GetTrackByIndex(unsigned long idx) const; + + private: + Track** m_trackEntries; + Track** m_trackEntriesEnd; + + long ParseTrackEntry(long long payload_start, long long payload_size, + long long element_start, long long element_size, + Track*&) const; +}; + +class Chapters { + Chapters(const Chapters&); + Chapters& operator=(const Chapters&); + + public: + Segment* const m_pSegment; + const long long m_start; + const long long m_size; + const long long m_element_start; + const long long m_element_size; + + Chapters(Segment*, long long payload_start, long long payload_size, + long long element_start, long long element_size); + + ~Chapters(); + + long Parse(); + + class Atom; + class Edition; + + class Display { + friend class Atom; + Display(); + Display(const Display&); + ~Display(); + Display& operator=(const Display&); + + public: + const char* GetString() const; + const char* GetLanguage() const; + const char* GetCountry() const; + + private: + void Init(); + void ShallowCopy(Display&) const; + void Clear(); + long Parse(IMkvReader*, long long pos, long long size); + + char* m_string; + char* m_language; + char* m_country; + }; + + class Atom { + friend class Edition; + Atom(); + Atom(const Atom&); + ~Atom(); + Atom& operator=(const Atom&); + + public: + unsigned long long GetUID() const; + const char* GetStringUID() const; + + long long GetStartTimecode() const; + long long GetStopTimecode() const; + + long long GetStartTime(const Chapters*) const; + long long GetStopTime(const Chapters*) const; + + int GetDisplayCount() const; + const Display* GetDisplay(int index) const; + + private: + void Init(); + void ShallowCopy(Atom&) const; + void Clear(); + long Parse(IMkvReader*, long long pos, long long size); + static long long GetTime(const Chapters*, long long timecode); + + long ParseDisplay(IMkvReader*, long long pos, long long size); + bool ExpandDisplaysArray(); + + char* m_string_uid; + unsigned long long m_uid; + long long m_start_timecode; + long long m_stop_timecode; + + Display* m_displays; + int m_displays_size; + int m_displays_count; + }; + + class Edition { + friend class Chapters; + Edition(); + Edition(const Edition&); + ~Edition(); + Edition& operator=(const Edition&); + + public: + int GetAtomCount() const; + const Atom* GetAtom(int index) const; + + private: + void Init(); + void ShallowCopy(Edition&) const; + void Clear(); + long Parse(IMkvReader*, long long pos, long long size); + + long ParseAtom(IMkvReader*, long long pos, long long size); + bool ExpandAtomsArray(); + + Atom* m_atoms; + int m_atoms_size; + int m_atoms_count; + }; + + int GetEditionCount() const; + const Edition* GetEdition(int index) const; + + private: + long ParseEdition(long long pos, long long size); + bool ExpandEditionsArray(); + + Edition* m_editions; + int m_editions_size; + int m_editions_count; +}; + +class Tags { + Tags(const Tags&); + Tags& operator=(const Tags&); + + public: + Segment* const m_pSegment; + const long long m_start; + const long long m_size; + const long long m_element_start; + const long long m_element_size; + + Tags(Segment*, long long payload_start, long long payload_size, + long long element_start, long long element_size); + + ~Tags(); + + long Parse(); + + class Tag; + class SimpleTag; + + class SimpleTag { + friend class Tag; + SimpleTag(); + SimpleTag(const SimpleTag&); + ~SimpleTag(); + SimpleTag& operator=(const SimpleTag&); + + public: + const char* GetTagName() const; + const char* GetTagString() const; + + private: + void Init(); + void ShallowCopy(SimpleTag&) const; + void Clear(); + long Parse(IMkvReader*, long long pos, long long size); + + char* m_tag_name; + char* m_tag_string; + }; + + class Tag { + friend class Tags; + Tag(); + Tag(const Tag&); + ~Tag(); + Tag& operator=(const Tag&); + + public: + int GetSimpleTagCount() const; + const SimpleTag* GetSimpleTag(int index) const; + + private: + void Init(); + void ShallowCopy(Tag&) const; + void Clear(); + long Parse(IMkvReader*, long long pos, long long size); + + long ParseSimpleTag(IMkvReader*, long long pos, long long size); + bool ExpandSimpleTagsArray(); + + SimpleTag* m_simple_tags; + int m_simple_tags_size; + int m_simple_tags_count; + }; + + int GetTagCount() const; + const Tag* GetTag(int index) const; + + private: + long ParseTag(long long pos, long long size); + bool ExpandTagsArray(); + + Tag* m_tags; + int m_tags_size; + int m_tags_count; +}; + +class SegmentInfo { + SegmentInfo(const SegmentInfo&); + SegmentInfo& operator=(const SegmentInfo&); + + public: + Segment* const m_pSegment; + const long long m_start; + const long long m_size; + const long long m_element_start; + const long long m_element_size; + + SegmentInfo(Segment*, long long start, long long size, + long long element_start, long long element_size); + + ~SegmentInfo(); + + long Parse(); + + long long GetTimeCodeScale() const; + long long GetDuration() const; // scaled + const char* GetMuxingAppAsUTF8() const; + const char* GetWritingAppAsUTF8() const; + const char* GetTitleAsUTF8() const; + + private: + long long m_timecodeScale; + double m_duration; + char* m_pMuxingAppAsUTF8; + char* m_pWritingAppAsUTF8; + char* m_pTitleAsUTF8; +}; + +class SeekHead { + SeekHead(const SeekHead&); + SeekHead& operator=(const SeekHead&); + + public: + Segment* const m_pSegment; + const long long m_start; + const long long m_size; + const long long m_element_start; + const long long m_element_size; + + SeekHead(Segment*, long long start, long long size, long long element_start, + long long element_size); + + ~SeekHead(); + + long Parse(); + + struct Entry { + Entry(); + + // the SeekHead entry payload + long long id; + long long pos; + + // absolute pos of SeekEntry ID + long long element_start; + + // SeekEntry ID size + size size + payload + long long element_size; + }; + + int GetCount() const; + const Entry* GetEntry(int idx) const; + + struct VoidElement { + // absolute pos of Void ID + long long element_start; + + // ID size + size size + payload size + long long element_size; + }; + + int GetVoidElementCount() const; + const VoidElement* GetVoidElement(int idx) const; + + private: + Entry* m_entries; + int m_entry_count; + + VoidElement* m_void_elements; + int m_void_element_count; + + static bool ParseEntry(IMkvReader*, + long long pos, // payload + long long size, Entry*); +}; + +class Cues; +class CuePoint { + friend class Cues; + + CuePoint(long, long long); + ~CuePoint(); + + CuePoint(const CuePoint&); + CuePoint& operator=(const CuePoint&); + + public: + long long m_element_start; + long long m_element_size; + + bool Load(IMkvReader*); + + long long GetTimeCode() const; // absolute but unscaled + long long GetTime(const Segment*) const; // absolute and scaled (ns units) + + struct TrackPosition { + long long m_track; + long long m_pos; // of cluster + long long m_block; + // codec_state //defaults to 0 + // reference = clusters containing req'd referenced blocks + // reftime = timecode of the referenced block + + bool Parse(IMkvReader*, long long, long long); + }; + + const TrackPosition* Find(const Track*) const; + + private: + const long m_index; + long long m_timecode; + TrackPosition* m_track_positions; + size_t m_track_positions_count; +}; + +class Cues { + friend class Segment; + + Cues(Segment*, long long start, long long size, long long element_start, + long long element_size); + ~Cues(); + + Cues(const Cues&); + Cues& operator=(const Cues&); + + public: + Segment* const m_pSegment; + const long long m_start; + const long long m_size; + const long long m_element_start; + const long long m_element_size; + + bool Find( // lower bound of time_ns + long long time_ns, const Track*, const CuePoint*&, + const CuePoint::TrackPosition*&) const; + + const CuePoint* GetFirst() const; + const CuePoint* GetLast() const; + const CuePoint* GetNext(const CuePoint*) const; + + const BlockEntry* GetBlock(const CuePoint*, + const CuePoint::TrackPosition*) const; + + bool LoadCuePoint() const; + long GetCount() const; // loaded only + // long GetTotal() const; //loaded + preloaded + bool DoneParsing() const; + + private: + bool Init() const; + bool PreloadCuePoint(long&, long long) const; + + mutable CuePoint** m_cue_points; + mutable long m_count; + mutable long m_preload_count; + mutable long long m_pos; +}; + +class Cluster { + friend class Segment; + + Cluster(const Cluster&); + Cluster& operator=(const Cluster&); + + public: + Segment* const m_pSegment; + + public: + static Cluster* Create(Segment*, + long index, // index in segment + long long off); // offset relative to segment + // long long element_size); + + Cluster(); // EndOfStream + ~Cluster(); + + bool EOS() const; + + long long GetTimeCode() const; // absolute, but not scaled + long long GetTime() const; // absolute, and scaled (nanosecond units) + long long GetFirstTime() const; // time (ns) of first (earliest) block + long long GetLastTime() const; // time (ns) of last (latest) block + + long GetFirst(const BlockEntry*&) const; + long GetLast(const BlockEntry*&) const; + long GetNext(const BlockEntry* curr, const BlockEntry*& next) const; + + const BlockEntry* GetEntry(const Track*, long long ns = -1) const; + const BlockEntry* GetEntry(const CuePoint&, + const CuePoint::TrackPosition&) const; + // const BlockEntry* GetMaxKey(const VideoTrack*) const; + + // static bool HasBlockEntries(const Segment*, long long); + + static long HasBlockEntries(const Segment*, long long idoff, long long& pos, + long& size); + + long GetEntryCount() const; + + long Load(long long& pos, long& size) const; + + long Parse(long long& pos, long& size) const; + long GetEntry(long index, const mkvparser::BlockEntry*&) const; + + protected: + Cluster(Segment*, long index, long long element_start); + // long long element_size); + + public: + const long long m_element_start; + long long GetPosition() const; // offset relative to segment + + long GetIndex() const; + long long GetElementSize() const; + // long long GetPayloadSize() const; + + // long long Unparsed() const; + + private: + long m_index; + mutable long long m_pos; + // mutable long long m_size; + mutable long long m_element_size; + mutable long long m_timecode; + mutable BlockEntry** m_entries; + mutable long m_entries_size; + mutable long m_entries_count; + + long ParseSimpleBlock(long long, long long&, long&); + long ParseBlockGroup(long long, long long&, long&); + + long CreateBlock(long long id, long long pos, long long size, + long long discard_padding); + long CreateBlockGroup(long long start_offset, long long size, + long long discard_padding); + long CreateSimpleBlock(long long, long long); +}; + +class Segment { + friend class Cues; + friend class Track; + friend class VideoTrack; + + Segment(const Segment&); + Segment& operator=(const Segment&); + + private: + Segment(IMkvReader*, long long elem_start, + // long long elem_size, + long long pos, long long size); + + public: + IMkvReader* const m_pReader; + const long long m_element_start; + // const long long m_element_size; + const long long m_start; // posn of segment payload + const long long m_size; // size of segment payload + Cluster m_eos; // TODO: make private? + + static long long CreateInstance(IMkvReader*, long long, Segment*&); + ~Segment(); + + long Load(); // loads headers and all clusters + + // for incremental loading + // long long Unparsed() const; + bool DoneParsing() const; + long long ParseHeaders(); // stops when first cluster is found + // long FindNextCluster(long long& pos, long& size) const; + long LoadCluster(long long& pos, long& size); // load one cluster + long LoadCluster(); + + long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos, + long& size); + + const SeekHead* GetSeekHead() const; + const Tracks* GetTracks() const; + const SegmentInfo* GetInfo() const; + const Cues* GetCues() const; + const Chapters* GetChapters() const; + const Tags* GetTags() const; + + long long GetDuration() const; + + unsigned long GetCount() const; + const Cluster* GetFirst() const; + const Cluster* GetLast() const; + const Cluster* GetNext(const Cluster*); + + const Cluster* FindCluster(long long time_nanoseconds) const; + // const BlockEntry* Seek(long long time_nanoseconds, const Track*) const; + + const Cluster* FindOrPreloadCluster(long long pos); + + long ParseCues(long long cues_off, // offset relative to start of segment + long long& parse_pos, long& parse_len); + + private: + long long m_pos; // absolute file posn; what has been consumed so far + Cluster* m_pUnknownSize; + + SeekHead* m_pSeekHead; + SegmentInfo* m_pInfo; + Tracks* m_pTracks; + Cues* m_pCues; + Chapters* m_pChapters; + Tags* m_pTags; + Cluster** m_clusters; + long m_clusterCount; // number of entries for which m_index >= 0 + long m_clusterPreloadCount; // number of entries for which m_index < 0 + long m_clusterSize; // array size + + long DoLoadCluster(long long&, long&); + long DoLoadClusterUnknownSize(long long&, long&); + long DoParseNext(const Cluster*&, long long&, long&); + + bool AppendCluster(Cluster*); + bool PreloadCluster(Cluster*, ptrdiff_t); + + // void ParseSeekHead(long long pos, long long size); + // void ParseSeekEntry(long long pos, long long size); + // void ParseCues(long long); + + const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&); +}; + +} // namespace mkvparser + +inline long mkvparser::Segment::LoadCluster() { + long long pos; + long size; + + return LoadCluster(pos, size); +} + +#endif // MKVPARSER_MKVPARSER_H_ diff --git a/mkvparser/mkvreader.cc b/mkvparser/mkvreader.cc new file mode 100644 index 000000000000..9d19c1be569a --- /dev/null +++ b/mkvparser/mkvreader.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2010 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "mkvparser/mkvreader.h" + +#include + +#include + +namespace mkvparser { + +MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {} + +MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) { + GetFileSize(); +} + +MkvReader::~MkvReader() { + if (reader_owns_file_) + Close(); + m_file = NULL; +} + +int MkvReader::Open(const char* fileName) { + if (fileName == NULL) + return -1; + + if (m_file) + return -1; + +#ifdef _MSC_VER + const errno_t e = fopen_s(&m_file, fileName, "rb"); + + if (e) + return -1; // error +#else + m_file = fopen(fileName, "rb"); + + if (m_file == NULL) + return -1; +#endif + return !GetFileSize(); +} + +bool MkvReader::GetFileSize() { + if (m_file == NULL) + return false; +#ifdef _MSC_VER + int status = _fseeki64(m_file, 0L, SEEK_END); + + if (status) + return false; // error + + m_length = _ftelli64(m_file); +#else + fseek(m_file, 0L, SEEK_END); + m_length = ftell(m_file); +#endif + assert(m_length >= 0); + + if (m_length < 0) + return false; + +#ifdef _MSC_VER + status = _fseeki64(m_file, 0L, SEEK_SET); + + if (status) + return false; // error +#else + fseek(m_file, 0L, SEEK_SET); +#endif + + return true; +} + +void MkvReader::Close() { + if (m_file != NULL) { + fclose(m_file); + m_file = NULL; + } +} + +int MkvReader::Length(long long* total, long long* available) { + if (m_file == NULL) + return -1; + + if (total) + *total = m_length; + + if (available) + *available = m_length; + + return 0; +} + +int MkvReader::Read(long long offset, long len, unsigned char* buffer) { + if (m_file == NULL) + return -1; + + if (offset < 0) + return -1; + + if (len < 0) + return -1; + + if (len == 0) + return 0; + + if (offset >= m_length) + return -1; + +#ifdef _MSC_VER + const int status = _fseeki64(m_file, offset, SEEK_SET); + + if (status) + return -1; // error +#elif defined(_WIN32) + fseeko64(m_file, static_cast(offset), SEEK_SET); +#else + fseeko(m_file, static_cast(offset), SEEK_SET); +#endif + + const size_t size = fread(buffer, 1, len, m_file); + + if (size < size_t(len)) + return -1; // error + + return 0; // success +} + +} // namespace mkvparser diff --git a/mkvparser/mkvreader.h b/mkvparser/mkvreader.h new file mode 100644 index 000000000000..9831ecf64586 --- /dev/null +++ b/mkvparser/mkvreader.h @@ -0,0 +1,45 @@ +// Copyright (c) 2010 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef MKVPARSER_MKVREADER_H_ +#define MKVPARSER_MKVREADER_H_ + +#include + +#include "mkvparser/mkvparser.h" + +namespace mkvparser { + +class MkvReader : public IMkvReader { + public: + MkvReader(); + explicit MkvReader(FILE* fp); + virtual ~MkvReader(); + + int Open(const char*); + void Close(); + + virtual int Read(long long position, long length, unsigned char* buffer); + virtual int Length(long long* total, long long* available); + + private: + MkvReader(const MkvReader&); + MkvReader& operator=(const MkvReader&); + + // Determines the size of the file. This is called either by the constructor + // or by the Open function depending on file ownership. Returns true on + // success. + bool GetFileSize(); + + long long m_length; + FILE* m_file; + bool reader_owns_file_; +}; + +} // namespace mkvparser + +#endif // MKVPARSER_MKVREADER_H_ diff --git a/mkvparser_sample.cc b/mkvparser_sample.cc new file mode 100644 index 000000000000..f285c3e50a75 --- /dev/null +++ b/mkvparser_sample.cc @@ -0,0 +1,459 @@ +// Copyright (c) 2010 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// This sample application demonstrates how to use the Matroska parser +// library, which allows clients to handle a Matroska format file. +#include +#include +#include +#include + +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" + +namespace { +const wchar_t* utf8towcs(const char* str) { + if (str == NULL) + return NULL; + + // TODO: this probably requires that the locale be + // configured somehow: + + const size_t size = mbstowcs(NULL, str, 0); + + if (size == 0 || size == static_cast(-1)) + return NULL; + + wchar_t* const val = new (std::nothrow) wchar_t[size + 1]; + if (val == NULL) + return NULL; + + mbstowcs(val, str, size); + val[size] = L'\0'; + + return val; +} + +bool InputHasCues(const mkvparser::Segment* const segment) { + const mkvparser::Cues* const cues = segment->GetCues(); + if (cues == NULL) + return false; + + while (!cues->DoneParsing()) + cues->LoadCuePoint(); + + const mkvparser::CuePoint* const cue_point = cues->GetFirst(); + if (cue_point == NULL) + return false; + + return true; +} + +bool MasteringMetadataValuePresent(double value) { + return value != mkvparser::MasteringMetadata::kValueNotPresent; +} + +bool ColourValuePresent(long long value) { + return value != mkvparser::Colour::kValueNotPresent; +} +} // namespace + +int main(int argc, char* argv[]) { + if (argc == 1) { + printf("Mkv Parser Sample Application\n"); + printf(" Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + mkvparser::MkvReader reader; + + if (reader.Open(argv[1])) { + printf("\n Filename is invalid or error while opening.\n"); + return EXIT_FAILURE; + } + + int maj, min, build, rev; + + mkvparser::GetVersion(maj, min, build, rev); + printf("\t\t libwebm version: %d.%d.%d.%d\n", maj, min, build, rev); + + long long pos = 0; + + mkvparser::EBMLHeader ebmlHeader; + + long long ret = ebmlHeader.Parse(&reader, pos); + if (ret < 0) { + printf("\n EBMLHeader::Parse() failed."); + return EXIT_FAILURE; + } + + printf("\t\t\t EBML Header\n"); + printf("\t\tEBML Version\t\t: %lld\n", ebmlHeader.m_version); + printf("\t\tEBML MaxIDLength\t: %lld\n", ebmlHeader.m_maxIdLength); + printf("\t\tEBML MaxSizeLength\t: %lld\n", ebmlHeader.m_maxSizeLength); + printf("\t\tDoc Type\t\t: %s\n", ebmlHeader.m_docType); + printf("\t\tPos\t\t\t: %lld\n", pos); + + typedef mkvparser::Segment seg_t; + seg_t* pSegment_; + + ret = seg_t::CreateInstance(&reader, pos, pSegment_); + if (ret) { + printf("\n Segment::CreateInstance() failed."); + return EXIT_FAILURE; + } + + const std::unique_ptr pSegment(pSegment_); + + ret = pSegment->Load(); + if (ret < 0) { + printf("\n Segment::Load() failed."); + return EXIT_FAILURE; + } + + const mkvparser::SegmentInfo* const pSegmentInfo = pSegment->GetInfo(); + if (pSegmentInfo == NULL) { + printf("\n Segment::GetInfo() failed."); + return EXIT_FAILURE; + } + + const long long timeCodeScale = pSegmentInfo->GetTimeCodeScale(); + const long long duration_ns = pSegmentInfo->GetDuration(); + + const char* const pTitle_ = pSegmentInfo->GetTitleAsUTF8(); + const wchar_t* const pTitle = utf8towcs(pTitle_); + + const char* const pMuxingApp_ = pSegmentInfo->GetMuxingAppAsUTF8(); + const wchar_t* const pMuxingApp = utf8towcs(pMuxingApp_); + + const char* const pWritingApp_ = pSegmentInfo->GetWritingAppAsUTF8(); + const wchar_t* const pWritingApp = utf8towcs(pWritingApp_); + + printf("\n"); + printf("\t\t\t Segment Info\n"); + printf("\t\tTimeCodeScale\t\t: %lld \n", timeCodeScale); + printf("\t\tDuration\t\t: %lld\n", duration_ns); + + const double duration_sec = double(duration_ns) / 1000000000; + printf("\t\tDuration(secs)\t\t: %7.3lf\n", duration_sec); + + if (pTitle == NULL) + printf("\t\tTrack Name\t\t: NULL\n"); + else { + printf("\t\tTrack Name\t\t: %ls\n", pTitle); + delete[] pTitle; + } + + if (pMuxingApp == NULL) + printf("\t\tMuxing App\t\t: NULL\n"); + else { + printf("\t\tMuxing App\t\t: %ls\n", pMuxingApp); + delete[] pMuxingApp; + } + + if (pWritingApp == NULL) + printf("\t\tWriting App\t\t: NULL\n"); + else { + printf("\t\tWriting App\t\t: %ls\n", pWritingApp); + delete[] pWritingApp; + } + + // pos of segment payload + printf("\t\tPosition(Segment)\t: %lld\n", pSegment->m_start); + + // size of segment payload + printf("\t\tSize(Segment)\t\t: %lld\n", pSegment->m_size); + + const mkvparser::Tracks* pTracks = pSegment->GetTracks(); + + unsigned long track_num = 0; + const unsigned long num_tracks = pTracks->GetTracksCount(); + + printf("\n\t\t\t Track Info\n"); + + while (track_num != num_tracks) { + const mkvparser::Track* const pTrack = + pTracks->GetTrackByIndex(track_num++); + + if (pTrack == NULL) + continue; + + const long trackType = pTrack->GetType(); + const long trackNumber = pTrack->GetNumber(); + const unsigned long long trackUid = pTrack->GetUid(); + const wchar_t* const pTrackName = utf8towcs(pTrack->GetNameAsUTF8()); + + printf("\t\tTrack Type\t\t: %ld\n", trackType); + printf("\t\tTrack Number\t\t: %ld\n", trackNumber); + printf("\t\tTrack Uid\t\t: %lld\n", trackUid); + + if (pTrackName == NULL) + printf("\t\tTrack Name\t\t: NULL\n"); + else { + printf("\t\tTrack Name\t\t: %ls \n", pTrackName); + delete[] pTrackName; + } + + const char* const pCodecId = pTrack->GetCodecId(); + + if (pCodecId == NULL) + printf("\t\tCodec Id\t\t: NULL\n"); + else + printf("\t\tCodec Id\t\t: %s\n", pCodecId); + + size_t codec_private_size = 0; + if (pTrack->GetCodecPrivate(codec_private_size)) { + printf("\t\tCodec private length: %u bytes\n", + static_cast(codec_private_size)); + } + + const char* const pCodecName_ = pTrack->GetCodecNameAsUTF8(); + const wchar_t* const pCodecName = utf8towcs(pCodecName_); + + if (pCodecName == NULL) + printf("\t\tCodec Name\t\t: NULL\n"); + else { + printf("\t\tCodec Name\t\t: %ls\n", pCodecName); + delete[] pCodecName; + } + + if (trackType == mkvparser::Track::kVideo) { + const mkvparser::VideoTrack* const pVideoTrack = + static_cast(pTrack); + + const long long width = pVideoTrack->GetWidth(); + printf("\t\tVideo Width\t\t: %lld\n", width); + + const long long height = pVideoTrack->GetHeight(); + printf("\t\tVideo Height\t\t: %lld\n", height); + + const double rate = pVideoTrack->GetFrameRate(); + printf("\t\tVideo Rate\t\t: %f\n", rate); + + const mkvparser::Colour* const colour = pVideoTrack->GetColour(); + if (colour) { + printf("\t\tVideo Colour:\n"); + if (ColourValuePresent(colour->matrix_coefficients)) + printf("\t\t\tMatrixCoefficients: %lld\n", + colour->matrix_coefficients); + if (ColourValuePresent(colour->bits_per_channel)) + printf("\t\t\tBitsPerChannel: %lld\n", colour->bits_per_channel); + if (ColourValuePresent(colour->chroma_subsampling_horz)) + printf("\t\t\tChromaSubsamplingHorz: %lld\n", + colour->chroma_subsampling_horz); + if (ColourValuePresent(colour->chroma_subsampling_vert)) + printf("\t\t\tChromaSubsamplingVert: %lld\n", + colour->chroma_subsampling_vert); + if (ColourValuePresent(colour->cb_subsampling_horz)) + printf("\t\t\tCbSubsamplingHorz: %lld\n", + colour->cb_subsampling_horz); + if (ColourValuePresent(colour->cb_subsampling_vert)) + printf("\t\t\tCbSubsamplingVert: %lld\n", + colour->cb_subsampling_vert); + if (ColourValuePresent(colour->chroma_siting_horz)) + printf("\t\t\tChromaSitingHorz: %lld\n", colour->chroma_siting_horz); + if (ColourValuePresent(colour->chroma_siting_vert)) + printf("\t\t\tChromaSitingVert: %lld\n", colour->chroma_siting_vert); + if (ColourValuePresent(colour->range)) + printf("\t\t\tRange: %lld\n", colour->range); + if (ColourValuePresent(colour->transfer_characteristics)) + printf("\t\t\tTransferCharacteristics: %lld\n", + colour->transfer_characteristics); + if (ColourValuePresent(colour->primaries)) + printf("\t\t\tPrimaries: %lld\n", colour->primaries); + if (ColourValuePresent(colour->max_cll)) + printf("\t\t\tMaxCLL: %lld\n", colour->max_cll); + if (ColourValuePresent(colour->max_fall)) + printf("\t\t\tMaxFALL: %lld\n", colour->max_fall); + if (colour->mastering_metadata) { + const mkvparser::MasteringMetadata* const mm = + colour->mastering_metadata; + printf("\t\t\tMastering Metadata:\n"); + if (MasteringMetadataValuePresent(mm->luminance_max)) + printf("\t\t\t\tLuminanceMax: %f\n", mm->luminance_max); + if (MasteringMetadataValuePresent(mm->luminance_min)) + printf("\t\t\t\tLuminanceMin: %f\n", mm->luminance_min); + if (mm->r) { + printf("\t\t\t\t\tPrimaryRChromaticityX: %f\n", mm->r->x); + printf("\t\t\t\t\tPrimaryRChromaticityY: %f\n", mm->r->y); + } + if (mm->g) { + printf("\t\t\t\t\tPrimaryGChromaticityX: %f\n", mm->g->x); + printf("\t\t\t\t\tPrimaryGChromaticityY: %f\n", mm->g->y); + } + if (mm->b) { + printf("\t\t\t\t\tPrimaryBChromaticityX: %f\n", mm->b->x); + printf("\t\t\t\t\tPrimaryBChromaticityY: %f\n", mm->b->y); + } + if (mm->white_point) { + printf("\t\t\t\t\tWhitePointChromaticityX: %f\n", + mm->white_point->x); + printf("\t\t\t\t\tWhitePointChromaticityY: %f\n", + mm->white_point->y); + } + } + } + + const mkvparser::Projection* const projection = + pVideoTrack->GetProjection(); + if (projection) { + printf("\t\tVideo Projection:\n"); + if (projection->type != mkvparser::Projection::kTypeNotPresent) + printf("\t\t\tProjectionType: %d\n", + static_cast(projection->type)); + if (projection->private_data) { + printf("\t\t\tProjectionPrivate: %u bytes\n", + static_cast(projection->private_data_length)); + } + if (projection->pose_yaw != mkvparser::Projection::kValueNotPresent) + printf("\t\t\tProjectionPoseYaw: %f\n", projection->pose_yaw); + if (projection->pose_pitch != mkvparser::Projection::kValueNotPresent) + printf("\t\t\tProjectionPosePitch: %f\n", projection->pose_pitch); + if (projection->pose_roll != mkvparser::Projection::kValueNotPresent) + printf("\t\t\tProjectionPosePitch: %f\n", projection->pose_roll); + } + } + + if (trackType == mkvparser::Track::kAudio) { + const mkvparser::AudioTrack* const pAudioTrack = + static_cast(pTrack); + + const long long channels = pAudioTrack->GetChannels(); + printf("\t\tAudio Channels\t\t: %lld\n", channels); + + const long long bitDepth = pAudioTrack->GetBitDepth(); + printf("\t\tAudio BitDepth\t\t: %lld\n", bitDepth); + + const double sampleRate = pAudioTrack->GetSamplingRate(); + printf("\t\tAddio Sample Rate\t: %.3f\n", sampleRate); + + const long long codecDelay = pAudioTrack->GetCodecDelay(); + printf("\t\tAudio Codec Delay\t\t: %lld\n", codecDelay); + + const long long seekPreRoll = pAudioTrack->GetSeekPreRoll(); + printf("\t\tAudio Seek Pre Roll\t\t: %lld\n", seekPreRoll); + } + } + + printf("\n\n\t\t\t Cluster Info\n"); + const unsigned long clusterCount = pSegment->GetCount(); + + printf("\t\tCluster Count\t: %ld\n\n", clusterCount); + + if (clusterCount == 0) { + printf("\t\tSegment has no clusters.\n"); + return EXIT_FAILURE; + } + + const mkvparser::Cluster* pCluster = pSegment->GetFirst(); + + while (pCluster != NULL && !pCluster->EOS()) { + const long long timeCode = pCluster->GetTimeCode(); + printf("\t\tCluster Time Code\t: %lld\n", timeCode); + + const long long time_ns = pCluster->GetTime(); + printf("\t\tCluster Time (ns)\t: %lld\n", time_ns); + + const mkvparser::BlockEntry* pBlockEntry; + + long status = pCluster->GetFirst(pBlockEntry); + + if (status < 0) // error + { + printf("\t\tError parsing first block of cluster\n"); + fflush(stdout); + return EXIT_FAILURE; + } + + while (pBlockEntry != NULL && !pBlockEntry->EOS()) { + const mkvparser::Block* const pBlock = pBlockEntry->GetBlock(); + const long long trackNum = pBlock->GetTrackNumber(); + const unsigned long tn = static_cast(trackNum); + const mkvparser::Track* const pTrack = pTracks->GetTrackByNumber(tn); + + if (pTrack == NULL) + printf("\t\t\tBlock\t\t:UNKNOWN TRACK TYPE\n"); + else { + const long long trackType = pTrack->GetType(); + const int frameCount = pBlock->GetFrameCount(); + const long long time_ns = pBlock->GetTime(pCluster); + const long long discard_padding = pBlock->GetDiscardPadding(); + + printf("\t\t\tBlock\t\t:%s,%s,%15lld,%lld\n", + (trackType == mkvparser::Track::kVideo) ? "V" : "A", + pBlock->IsKey() ? "I" : "P", time_ns, discard_padding); + + for (int i = 0; i < frameCount; ++i) { + const mkvparser::Block::Frame& theFrame = pBlock->GetFrame(i); + const long size = theFrame.len; + const long long offset = theFrame.pos; + printf("\t\t\t %15ld,%15llx\n", size, offset); + } + } + + status = pCluster->GetNext(pBlockEntry, pBlockEntry); + + if (status < 0) { + printf("\t\t\tError parsing next block of cluster\n"); + fflush(stdout); + return EXIT_FAILURE; + } + } + + pCluster = pSegment->GetNext(pCluster); + } + + if (InputHasCues(pSegment.get())) { + // Walk them. + const mkvparser::Cues* const cues = pSegment->GetCues(); + const mkvparser::CuePoint* cue = cues->GetFirst(); + int cue_point_num = 1; + + printf("\t\tCues\n"); + do { + for (track_num = 0; track_num < num_tracks; ++track_num) { + const mkvparser::Track* const track = + pTracks->GetTrackByIndex(track_num); + const mkvparser::CuePoint::TrackPosition* const track_pos = + cue->Find(track); + + if (track_pos != NULL) { + const char track_type = + (track->GetType() == mkvparser::Track::kVideo) ? 'V' : 'A'; + printf( + "\t\t\tCue Point %4d Track %3lu(%c) Time %14lld " + "Block %4lld Pos %8llx\n", + cue_point_num, track->GetNumber(), track_type, + cue->GetTime(pSegment.get()), track_pos->m_block, + track_pos->m_pos); + } + } + + cue = cues->GetNext(cue); + ++cue_point_num; + } while (cue != NULL); + } + + const mkvparser::Tags* const tags = pSegment->GetTags(); + if (tags && tags->GetTagCount() > 0) { + printf("\t\tTags\n"); + for (int i = 0; i < tags->GetTagCount(); ++i) { + const mkvparser::Tags::Tag* const tag = tags->GetTag(i); + printf("\t\t\tTag\n"); + for (int j = 0; j < tag->GetSimpleTagCount(); j++) { + const mkvparser::Tags::SimpleTag* const simple_tag = + tag->GetSimpleTag(j); + printf("\t\t\t\tSimple Tag \"%s\" Value \"%s\"\n", + simple_tag->GetTagName(), simple_tag->GetTagString()); + } + } + } + + fflush(stdout); + return EXIT_SUCCESS; +} diff --git a/mkvreader.hpp b/mkvreader.hpp new file mode 100644 index 000000000000..d1158729805d --- /dev/null +++ b/mkvreader.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_MKVREADER_HPP_ +#define LIBWEBM_MKVREADER_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "mkvparser/mkvreader.h" + +#endif // LIBWEBM_MKVREADER_HPP_ diff --git a/mkvwriter.hpp b/mkvwriter.hpp new file mode 100644 index 000000000000..9927b3e07b5b --- /dev/null +++ b/mkvwriter.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_MKVWRITER_HPP_ +#define LIBWEBM_MKVWRITER_HPP_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "mkvmuxer/mkvwriter.h" + +#endif // LIBWEBM_MKVWRITER_HPP_ diff --git a/sample_muxer_metadata.cc b/sample_muxer_metadata.cc new file mode 100644 index 000000000000..6eadcfddc330 --- /dev/null +++ b/sample_muxer_metadata.cc @@ -0,0 +1,391 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// This sample application demonstrates how to use the matroska parser +// library, which allows clients to handle a matroska format file. + +#include "sample_muxer_metadata.h" + +#include +#include + +#include "mkvmuxer/mkvmuxer.h" +#include "webvtt/vttreader.h" + +SampleMuxerMetadata::SampleMuxerMetadata() : segment_(NULL) {} + +bool SampleMuxerMetadata::Init(mkvmuxer::Segment* segment) { + if (segment == NULL || segment_ != NULL) + return false; + + segment_ = segment; + return true; +} + +bool SampleMuxerMetadata::Load(const char* file, Kind kind) { + if (kind == kChapters) + return LoadChapters(file); + + uint64_t track_num; + + if (!AddTrack(kind, &track_num)) { + printf("Unable to add track for WebVTT file \"%s\"\n", file); + return false; + } + + return Parse(file, kind, track_num); +} + +bool SampleMuxerMetadata::AddChapters() { + typedef cue_list_t::const_iterator iter_t; + iter_t i = chapter_cues_.begin(); + const iter_t j = chapter_cues_.end(); + + while (i != j) { + const cue_t& chapter = *i++; + + if (!AddChapter(chapter)) + return false; + } + + return true; +} + +bool SampleMuxerMetadata::Write(int64_t time_ns) { + typedef cues_set_t::iterator iter_t; + + iter_t i = cues_set_.begin(); + const iter_t j = cues_set_.end(); + + while (i != j) { + const cues_set_t::value_type& v = *i; + + if (time_ns >= 0 && v > time_ns) + return true; // nothing else to do just yet + + if (!v.Write(segment_)) { + printf("\nCould not add metadata.\n"); + return false; // error + } + + cues_set_.erase(i++); + } + + return true; +} + +bool SampleMuxerMetadata::LoadChapters(const char* file) { + if (!chapter_cues_.empty()) { + printf("Support for more than one chapters file is not yet implemented\n"); + return false; + } + + cue_list_t cues; + + if (!ParseChapters(file, &cues)) + return false; + + // TODO(matthewjheaney): support more than one chapters file + chapter_cues_.swap(cues); + + return true; +} + +bool SampleMuxerMetadata::ParseChapters(const char* file, + cue_list_t* cues_ptr) { + cue_list_t& cues = *cues_ptr; + cues.clear(); + + libwebvtt::VttReader r; + int e = r.Open(file); + + if (e) { + printf("Unable to open WebVTT file: \"%s\"\n", file); + return false; + } + + libwebvtt::Parser p(&r); + e = p.Init(); + + if (e < 0) { // error + printf("Error parsing WebVTT file: \"%s\"\n", file); + return false; + } + + libwebvtt::Time t; + t.hours = -1; + + for (;;) { + cue_t c; + e = p.Parse(&c); + + if (e < 0) { // error + printf("Error parsing WebVTT file: \"%s\"\n", file); + return false; + } + + if (e > 0) // EOF + return true; + + if (c.start_time < t) { + printf("bad WebVTT cue timestamp (out-of-order)\n"); + return false; + } + + if (c.stop_time < c.start_time) { + printf("bad WebVTT cue timestamp (stop < start)\n"); + return false; + } + + t = c.start_time; + cues.push_back(c); + } +} + +bool SampleMuxerMetadata::AddChapter(const cue_t& cue) { + // TODO(matthewjheaney): support language and country + + mkvmuxer::Chapter* const chapter = segment_->AddChapter(); + + if (chapter == NULL) { + printf("Unable to add chapter\n"); + return false; + } + + if (cue.identifier.empty()) { + chapter->set_id(NULL); + } else { + const char* const id = cue.identifier.c_str(); + if (!chapter->set_id(id)) { + printf("Unable to set chapter id\n"); + return false; + } + } + + typedef libwebvtt::presentation_t time_ms_t; + const time_ms_t start_time_ms = cue.start_time.presentation(); + const time_ms_t stop_time_ms = cue.stop_time.presentation(); + + enum { kNsPerMs = 1000000 }; + const uint64_t start_time_ns = start_time_ms * kNsPerMs; + const uint64_t stop_time_ns = stop_time_ms * kNsPerMs; + + chapter->set_time(*segment_, start_time_ns, stop_time_ns); + + typedef libwebvtt::Cue::payload_t::const_iterator iter_t; + iter_t i = cue.payload.begin(); + const iter_t j = cue.payload.end(); + + std::string title; + + for (;;) { + title += *i++; + + if (i == j) + break; + + enum { kLF = '\x0A' }; + title += kLF; + } + + if (!chapter->add_string(title.c_str(), NULL, NULL)) { + printf("Unable to set chapter title\n"); + return false; + } + + return true; +} + +bool SampleMuxerMetadata::AddTrack(Kind kind, uint64_t* track_num) { + *track_num = 0; + + // Track number value 0 means "let muxer choose track number" + mkvmuxer::Track* const track = segment_->AddTrack(0); + + if (track == NULL) // error + return false; + + // Return the track number value chosen by the muxer + *track_num = track->number(); + + int type; + const char* codec_id; + + switch (kind) { + case kSubtitles: + type = 0x11; + codec_id = "D_WEBVTT/SUBTITLES"; + break; + + case kCaptions: + type = 0x11; + codec_id = "D_WEBVTT/CAPTIONS"; + break; + + case kDescriptions: + type = 0x21; + codec_id = "D_WEBVTT/DESCRIPTIONS"; + break; + + case kMetadata: + type = 0x21; + codec_id = "D_WEBVTT/METADATA"; + break; + + default: + return false; + } + + track->set_type(type); + track->set_codec_id(codec_id); + + // TODO(matthewjheaney): set name and language + + return true; +} + +bool SampleMuxerMetadata::Parse(const char* file, Kind /* kind */, + uint64_t track_num) { + libwebvtt::VttReader r; + int e = r.Open(file); + + if (e) { + printf("Unable to open WebVTT file: \"%s\"\n", file); + return false; + } + + libwebvtt::Parser p(&r); + + e = p.Init(); + + if (e < 0) { // error + printf("Error parsing WebVTT file: \"%s\"\n", file); + return false; + } + + SortableCue cue; + cue.track_num = track_num; + + libwebvtt::Time t; + t.hours = -1; + + for (;;) { + cue_t& c = cue.cue; + e = p.Parse(&c); + + if (e < 0) { // error + printf("Error parsing WebVTT file: \"%s\"\n", file); + return false; + } + + if (e > 0) // EOF + return true; + + if (c.start_time >= t) { + t = c.start_time; + } else { + printf("bad WebVTT cue timestamp (out-of-order)\n"); + return false; + } + + if (c.stop_time < c.start_time) { + printf("bad WebVTT cue timestamp (stop < start)\n"); + return false; + } + + cues_set_.insert(cue); + } +} + +void SampleMuxerMetadata::MakeFrame(const cue_t& c, std::string* pf) { + pf->clear(); + WriteCueIdentifier(c.identifier, pf); + WriteCueSettings(c.settings, pf); + WriteCuePayload(c.payload, pf); +} + +void SampleMuxerMetadata::WriteCueIdentifier(const std::string& identifier, + std::string* pf) { + pf->append(identifier); + pf->push_back('\x0A'); // LF +} + +void SampleMuxerMetadata::WriteCueSettings(const cue_t::settings_t& settings, + std::string* pf) { + if (settings.empty()) { + pf->push_back('\x0A'); // LF + return; + } + + typedef cue_t::settings_t::const_iterator iter_t; + + iter_t i = settings.begin(); + const iter_t j = settings.end(); + + for (;;) { + const libwebvtt::Setting& setting = *i++; + + pf->append(setting.name); + pf->push_back(':'); + pf->append(setting.value); + + if (i == j) + break; + + pf->push_back(' '); // separate settings with whitespace + } + + pf->push_back('\x0A'); // LF +} + +void SampleMuxerMetadata::WriteCuePayload(const cue_t::payload_t& payload, + std::string* pf) { + typedef cue_t::payload_t::const_iterator iter_t; + + iter_t i = payload.begin(); + const iter_t j = payload.end(); + + while (i != j) { + const std::string& line = *i++; + pf->append(line); + pf->push_back('\x0A'); // LF + } +} + +bool SampleMuxerMetadata::SortableCue::Write(mkvmuxer::Segment* segment) const { + // Cue start time expressed in milliseconds + const int64_t start_ms = cue.start_time.presentation(); + + // Cue start time expressed in nanoseconds (MKV time) + const int64_t start_ns = start_ms * 1000000; + + // Cue stop time expressed in milliseconds + const int64_t stop_ms = cue.stop_time.presentation(); + + // Cue stop time expressed in nanonseconds + const int64_t stop_ns = stop_ms * 1000000; + + // Metadata blocks always specify the block duration. + const int64_t duration_ns = stop_ns - start_ns; + + std::string frame; + MakeFrame(cue, &frame); + + typedef const uint8_t* data_t; + const data_t buf = reinterpret_cast(frame.data()); + const uint64_t len = frame.length(); + + mkvmuxer::Frame muxer_frame; + if (!muxer_frame.Init(buf, len)) + return 0; + muxer_frame.set_track_number(track_num); + muxer_frame.set_timestamp(start_ns); + muxer_frame.set_duration(duration_ns); + muxer_frame.set_is_key(true); // All metadata frames are keyframes. + return segment->AddGenericFrame(&muxer_frame); +} \ No newline at end of file diff --git a/sample_muxer_metadata.h b/sample_muxer_metadata.h new file mode 100644 index 000000000000..d76ccf54aac1 --- /dev/null +++ b/sample_muxer_metadata.h @@ -0,0 +1,137 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifndef SAMPLE_MUXER_METADATA_H_ // NOLINT +#define SAMPLE_MUXER_METADATA_H_ + +#include + +#include +#include +#include + +#include "webvtt/webvttparser.h" + +namespace mkvmuxer { +class Chapter; +class Frame; +class Segment; +class Track; +} // namespace mkvmuxer + +class SampleMuxerMetadata { + public: + enum Kind { kSubtitles, kCaptions, kDescriptions, kMetadata, kChapters }; + + SampleMuxerMetadata(); + + // Bind this metadata object to the muxer instance. Returns false + // if segment equals NULL, or Init has already been called. + bool Init(mkvmuxer::Segment* segment); + + // Parse the WebVTT file |filename| having the indicated |kind|, and + // create a corresponding track (or chapters element) in the + // segment. Returns false on error. + bool Load(const char* filename, Kind kind); + + bool AddChapters(); + + // Write any WebVTT cues whose time is less or equal to |time_ns| as + // a metadata block in its corresponding track. If |time_ns| is + // negative, write all remaining cues. Returns false on error. + bool Write(int64_t time_ns); + + private: + typedef libwebvtt::Cue cue_t; + + // Used to sort cues as they are loaded. + struct SortableCue { + bool operator>(int64_t time_ns) const { + // Cue start time expressed in milliseconds + const int64_t start_ms = cue.start_time.presentation(); + + // Cue start time expressed in nanoseconds (MKV time) + const int64_t start_ns = start_ms * 1000000; + + return (start_ns > time_ns); + } + + bool operator<(const SortableCue& rhs) const { + if (cue.start_time < rhs.cue.start_time) + return true; + + if (cue.start_time > rhs.cue.start_time) + return false; + + return (track_num < rhs.track_num); + } + + // Write this cue as a metablock to |segment|. Returns false on + // error. + bool Write(mkvmuxer::Segment* segment) const; + + uint64_t track_num; + cue_t cue; + }; + + typedef std::multiset cues_set_t; + typedef std::list cue_list_t; + + // Parse the WebVTT cues in the named |file|, returning false on + // error. We handle chapters as a special case, because they are + // stored in their own, dedicated level-1 element. + bool LoadChapters(const char* file); + + // Parse the WebVTT chapters in |file| to populate |cues|. Returns + // false on error. + static bool ParseChapters(const char* file, cue_list_t* cues); + + // Adds WebVTT cue |chapter| to the chapters element of the output + // file's segment element. Returns false on error. + bool AddChapter(const cue_t& chapter); + + // Add a metadata track to the segment having the indicated |kind|, + // returning the |track_num| that has been chosen for this track. + // Returns false on error. + bool AddTrack(Kind kind, uint64_t* track_num); + + // Parse the WebVTT |file| having the indicated |kind| and + // |track_num|, adding each parsed cue to cues set. Returns false + // on error. + bool Parse(const char* file, Kind kind, uint64_t track_num); + + // Converts a WebVTT cue to a Matroska metadata block. + static void MakeFrame(const cue_t& cue, std::string* frame); + + // Populate the cue identifier part of the metadata block. + static void WriteCueIdentifier(const std::string& identifier, + std::string* frame); + + // Populate the cue settings part of the metadata block. + static void WriteCueSettings(const cue_t::settings_t& settings, + std::string* frame); + + // Populate the payload part of the metadata block. + static void WriteCuePayload(const cue_t::payload_t& payload, + std::string* frame); + + mkvmuxer::Segment* segment_; + + // Set of cues ordered by time and then by track number. + cues_set_t cues_set_; + + // The cues that will be used to populate the Chapters level-1 + // element of the output file. + cue_list_t chapter_cues_; + + // Disable copy ctor and copy assign. + SampleMuxerMetadata(const SampleMuxerMetadata&); + SampleMuxerMetadata& operator=(const SampleMuxerMetadata&); +}; + +#endif // SAMPLE_MUXER_METADATA_H_ // NOLINT diff --git a/testing/mkvmuxer_tests.cc b/testing/mkvmuxer_tests.cc new file mode 100644 index 000000000000..3374058a5559 --- /dev/null +++ b/testing/mkvmuxer_tests.cc @@ -0,0 +1,1010 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "common/file_util.h" +#include "common/libwebm_util.h" +#include "mkvmuxer/mkvmuxer.h" +#include "mkvmuxer/mkvwriter.h" +#include "mkvparser/mkvreader.h" +#include "testing/test_util.h" + +using mkvmuxer::AudioTrack; +using mkvmuxer::Chapter; +using mkvmuxer::Frame; +using mkvmuxer::MkvWriter; +using mkvmuxer::Segment; +using mkvmuxer::SegmentInfo; +using mkvmuxer::Tag; +using mkvmuxer::Track; +using mkvmuxer::VideoTrack; + +namespace test { + +// Base class containing boiler plate stuff. +class MuxerTest : public testing::Test { + public: + MuxerTest() { Init(); } + + ~MuxerTest() { CloseWriter(); } + + // Simple init function for use by constructor. Calls made here to allow use + // of ASSERT_* macros-- this is necessary here because all failures in Init() + // are fatal, but the ASSERT_* gtest macros cannot be used in a constructor. + void Init() { + ASSERT_TRUE(GetTestDataDir().length() > 0); + filename_ = libwebm::GetTempFileName(); + ASSERT_GT(filename_.length(), 0u); + temp_file_ = libwebm::FilePtr(std::fopen(filename_.c_str(), "wb"), + libwebm::FILEDeleter()); + ASSERT_TRUE(temp_file_.get() != nullptr); + writer_.reset(new MkvWriter(temp_file_.get())); + is_writer_open_ = true; + memset(dummy_data_, 0, kFrameLength); + } + + void AddDummyFrameAndFinalize(int track_number) { + EXPECT_TRUE(segment_.AddFrame(&dummy_data_[0], kFrameLength, track_number, + 0, false)); + EXPECT_TRUE(segment_.Finalize()); + } + + void AddVideoTrack() { + const int vid_track = static_cast( + segment_.AddVideoTrack(kWidth, kHeight, kVideoTrackNumber)); + ASSERT_EQ(kVideoTrackNumber, vid_track); + VideoTrack* const video = + dynamic_cast(segment_.GetTrackByNumber(vid_track)); + ASSERT_TRUE(video != NULL); + video->set_uid(kVideoTrackNumber); + } + + void AddAudioTrack() { + const int aud_track = static_cast( + segment_.AddAudioTrack(kSampleRate, kChannels, kAudioTrackNumber)); + ASSERT_EQ(kAudioTrackNumber, aud_track); + AudioTrack* const audio = + dynamic_cast(segment_.GetTrackByNumber(aud_track)); + ASSERT_TRUE(audio != NULL); + audio->set_uid(kAudioTrackNumber); + audio->set_codec_id(kOpusCodecId); + } + + void CloseWriter() { + if (is_writer_open_) + writer_->Close(); + is_writer_open_ = false; + } + + bool SegmentInit(bool output_cues, bool accurate_cluster_duration, + bool fixed_size_cluster_timecode) { + if (!segment_.Init(writer_.get())) + return false; + SegmentInfo* const info = segment_.GetSegmentInfo(); + info->set_writing_app(kAppString); + info->set_muxing_app(kAppString); + segment_.OutputCues(output_cues); + segment_.AccurateClusterDuration(accurate_cluster_duration); + segment_.UseFixedSizeClusterTimecode(fixed_size_cluster_timecode); + return true; + } + + protected: + virtual void TearDown() { + remove(filename_.c_str()); + testing::Test::TearDown(); + } + + std::unique_ptr writer_; + bool is_writer_open_ = false; + Segment segment_; + std::string filename_; + libwebm::FilePtr temp_file_; + std::uint8_t dummy_data_[kFrameLength]; +}; + +TEST_F(MuxerTest, SegmentInfo) { + EXPECT_TRUE(SegmentInit(false, false, false)); + SegmentInfo* const info = segment_.GetSegmentInfo(); + info->set_timecode_scale(kTimeCodeScale); + info->set_duration(2.345); + EXPECT_STREQ(kAppString, info->muxing_app()); + EXPECT_STREQ(kAppString, info->writing_app()); + EXPECT_EQ(static_cast(kTimeCodeScale), info->timecode_scale()); + EXPECT_DOUBLE_EQ(2.345, info->duration()); + AddVideoTrack(); + + AddDummyFrameAndFinalize(kVideoTrackNumber); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("segment_info.webm"), filename_)); +} + +TEST_F(MuxerTest, AddTracks) { + EXPECT_TRUE(SegmentInit(false, false, false)); + + // Add a Video Track + AddVideoTrack(); + VideoTrack* const video = + dynamic_cast(segment_.GetTrackByNumber(kVideoTrackNumber)); + ASSERT_TRUE(video != NULL); + EXPECT_EQ(static_cast(kWidth), video->width()); + EXPECT_EQ(static_cast(kHeight), video->height()); + video->set_name(kTrackName); + video->set_display_width(kWidth - 10); + video->set_display_height(kHeight - 10); + video->set_frame_rate(0.5); + EXPECT_STREQ(kTrackName, video->name()); + const uint64_t kDisplayWidth = kWidth - 10; + EXPECT_EQ(kDisplayWidth, video->display_width()); + const uint64_t kDisplayHeight = kHeight - 10; + EXPECT_EQ(kDisplayHeight, video->display_height()); + EXPECT_DOUBLE_EQ(0.5, video->frame_rate()); + EXPECT_EQ(static_cast(kVideoTrackNumber), video->uid()); + + // Add an Audio Track + const int aud_track = static_cast( + segment_.AddAudioTrack(kSampleRate, kChannels, kAudioTrackNumber)); + EXPECT_EQ(kAudioTrackNumber, aud_track); + AudioTrack* const audio = + dynamic_cast(segment_.GetTrackByNumber(aud_track)); + EXPECT_EQ(kSampleRate, audio->sample_rate()); + EXPECT_EQ(static_cast(kChannels), audio->channels()); + ASSERT_TRUE(audio != NULL); + audio->set_name(kTrackName); + audio->set_bit_depth(kBitDepth); + audio->set_uid(kAudioTrackNumber); + EXPECT_STREQ(kTrackName, audio->name()); + EXPECT_EQ(static_cast(kBitDepth), audio->bit_depth()); + EXPECT_EQ(static_cast(kAudioTrackNumber), audio->uid()); + + AddDummyFrameAndFinalize(kVideoTrackNumber); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("tracks.webm"), filename_)); +} + +TEST_F(MuxerTest, AddChapters) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + // Add a Chapter + Chapter* chapter = segment_.AddChapter(); + EXPECT_TRUE(chapter->set_id("unit_test")); + chapter->set_time(segment_, 0, 1000000000); + EXPECT_TRUE(chapter->add_string("unit_test", "english", "us")); + chapter->set_uid(1); + + AddDummyFrameAndFinalize(kVideoTrackNumber); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("chapters.webm"), filename_)); +} + +TEST_F(MuxerTest, SimpleBlock) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + // Valid Frame + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + + // Valid Frame + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + + // Invalid Frame - Non monotonically increasing timestamp + EXPECT_FALSE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 1, false)); + + // Invalid Frame - Null pointer + EXPECT_FALSE(segment_.AddFrame(NULL, 0, kVideoTrackNumber, 8000000, false)); + + // Invalid Frame - Invalid track number + EXPECT_FALSE(segment_.AddFrame(NULL, 0, kInvalidTrackNumber, 8000000, false)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("simple_block.webm"), filename_)); +} + +TEST_F(MuxerTest, SimpleBlockWithAddGenericFrame) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + Frame frame; + frame.Init(dummy_data_, kFrameLength); + frame.set_track_number(kVideoTrackNumber); + frame.set_is_key(false); + + // Valid Frame + frame.set_timestamp(0); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + + // Valid Frame + frame.set_timestamp(2000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + + // Invalid Frame - Non monotonically increasing timestamp + frame.set_timestamp(1); + EXPECT_FALSE(segment_.AddGenericFrame(&frame)); + + // Invalid Frame - Invalid track number + frame.set_track_number(kInvalidTrackNumber); + frame.set_timestamp(8000000); + EXPECT_FALSE(segment_.AddGenericFrame(&frame)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("simple_block.webm"), filename_)); +} + +TEST_F(MuxerTest, MetadataBlock) { + EXPECT_TRUE(SegmentInit(false, false, false)); + Track* const track = segment_.AddTrack(kMetadataTrackNumber); + track->set_uid(kMetadataTrackNumber); + track->set_type(kMetadataTrackType); + track->set_codec_id(kMetadataCodecId); + + // Valid Frame + EXPECT_TRUE(segment_.AddMetadata(dummy_data_, kFrameLength, + kMetadataTrackNumber, 0, 2000000)); + + // Valid Frame + EXPECT_TRUE(segment_.AddMetadata(dummy_data_, kFrameLength, + kMetadataTrackNumber, 2000000, 6000000)); + + // Invalid Frame - Non monotonically increasing timestamp + EXPECT_FALSE(segment_.AddMetadata(dummy_data_, kFrameLength, + kMetadataTrackNumber, 1, 2000000)); + + // Invalid Frame - Null pointer + EXPECT_FALSE(segment_.AddMetadata(NULL, 0, kMetadataTrackNumber, 0, 8000000)); + + // Invalid Frame - Invalid track number + EXPECT_FALSE(segment_.AddMetadata(NULL, 0, kInvalidTrackNumber, 0, 8000000)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("metadata_block.webm"), filename_)); +} + +TEST_F(MuxerTest, TrackType) { + EXPECT_TRUE(SegmentInit(false, false, false)); + Track* const track = segment_.AddTrack(kMetadataTrackNumber); + track->set_uid(kMetadataTrackNumber); + track->set_codec_id(kMetadataCodecId); + + // Invalid Frame - Incomplete track information (Track Type not set). + EXPECT_FALSE(segment_.AddMetadata(dummy_data_, kFrameLength, + kMetadataTrackNumber, 0, 2000000)); + + track->set_type(kMetadataTrackType); + + // Valid Frame + EXPECT_TRUE(segment_.AddMetadata(dummy_data_, kFrameLength, + kMetadataTrackNumber, 0, 2000000)); + + segment_.Finalize(); + CloseWriter(); +} + +TEST_F(MuxerTest, BlockWithAdditional) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + // Valid Frame + EXPECT_TRUE(segment_.AddFrameWithAdditional(dummy_data_, kFrameLength, + dummy_data_, kFrameLength, 1, + kVideoTrackNumber, 0, true)); + + // Valid Frame + EXPECT_TRUE(segment_.AddFrameWithAdditional( + dummy_data_, kFrameLength, dummy_data_, kFrameLength, 1, + kVideoTrackNumber, 2000000, false)); + + // Invalid Frame - Non monotonically increasing timestamp + EXPECT_FALSE(segment_.AddFrameWithAdditional(dummy_data_, kFrameLength, + dummy_data_, kFrameLength, 1, + kVideoTrackNumber, 1, false)); + + // Invalid Frame - Null frame pointer + EXPECT_FALSE( + segment_.AddFrameWithAdditional(NULL, 0, dummy_data_, kFrameLength, 1, + kVideoTrackNumber, 3000000, false)); + + // Invalid Frame - Null additional pointer + EXPECT_FALSE(segment_.AddFrameWithAdditional(dummy_data_, kFrameLength, NULL, + 0, 1, kVideoTrackNumber, 4000000, + false)); + + // Invalid Frame - Invalid track number + EXPECT_FALSE(segment_.AddFrameWithAdditional( + dummy_data_, kFrameLength, dummy_data_, kFrameLength, 1, + kInvalidTrackNumber, 8000000, false)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("block_with_additional.webm"), filename_)); +} + +TEST_F(MuxerTest, BlockAdditionalWithAddGenericFrame) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + Frame frame; + frame.Init(dummy_data_, kFrameLength); + frame.AddAdditionalData(dummy_data_, kFrameLength, 1); + frame.set_track_number(kVideoTrackNumber); + frame.set_is_key(true); + + // Valid Frame + frame.set_timestamp(0); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + + // Valid Frame + frame.set_timestamp(2000000); + frame.set_is_key(false); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + + // Invalid Frame - Non monotonically increasing timestamp + frame.set_timestamp(1); + EXPECT_FALSE(segment_.AddGenericFrame(&frame)); + + // Invalid Frame - Invalid track number + frame.set_track_number(kInvalidTrackNumber); + frame.set_timestamp(4000000); + EXPECT_FALSE(segment_.AddGenericFrame(&frame)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("block_with_additional.webm"), filename_)); +} + +TEST_F(MuxerTest, SegmentDurationComputation) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + Frame frame; + frame.Init(dummy_data_, kFrameLength); + frame.set_track_number(kVideoTrackNumber); + frame.set_timestamp(0); + frame.set_is_key(false); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + frame.set_timestamp(2000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + frame.set_timestamp(4000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + frame.set_timestamp(6000000); + frame.set_duration(2000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.Finalize(); + + // SegmentInfo's duration is in timecode scale + EXPECT_EQ(8, segment_.GetSegmentInfo()->duration()); + + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("segment_duration.webm"), filename_)); +} + +TEST_F(MuxerTest, SetSegmentDuration) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + + segment_.set_duration(10500.0); + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("set_segment_duration.webm"), filename_)); +} + +TEST_F(MuxerTest, ForceNewCluster) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + segment_.ForceNewClusterOnNextFrame(); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 4000000, false)); + segment_.ForceNewClusterOnNextFrame(); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 6000000, false)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("force_new_cluster.webm"), filename_)); +} + +TEST_F(MuxerTest, OutputCues) { + EXPECT_TRUE(SegmentInit(true, false, false)); + AddVideoTrack(); + + EXPECT_TRUE( + segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, true)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 4000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 6000000, true)); + EXPECT_TRUE(segment_.AddCuePoint(4000000, kVideoTrackNumber)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("output_cues.webm"), filename_)); +} + +TEST_F(MuxerTest, CuesBeforeClusters) { + EXPECT_TRUE(SegmentInit(true, false, false)); + AddVideoTrack(); + + EXPECT_TRUE( + segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, true)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 4000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 6000000, true)); + segment_.Finalize(); + CloseWriter(); +#ifdef _MSC_VER + // Close the output file: the MS run time won't allow mkvparser::MkvReader + // to open a file for reading when it's still open for writing. + temp_file_.reset(); +#endif + mkvparser::MkvReader reader; + ASSERT_EQ(0, reader.Open(filename_.c_str())); + MkvWriter cues_writer; + std::string cues_filename = libwebm::GetTempFileName(); + ASSERT_GT(cues_filename.length(), 0u); + cues_writer.Open(cues_filename.c_str()); + EXPECT_TRUE(segment_.CopyAndMoveCuesBeforeClusters(&reader, &cues_writer)); + reader.Close(); + cues_writer.Close(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("cues_before_clusters.webm"), + cues_filename)); + MkvParser parser; + ASSERT_TRUE(ParseMkvFileReleaseParser(cues_filename, &parser)); + int64_t cues_offset = 0; + ASSERT_TRUE(HasCuePoints(parser.segment, &cues_offset)); + ASSERT_GT(cues_offset, 0); + ASSERT_TRUE(ValidateCues(parser.segment, parser.reader)); + remove(cues_filename.c_str()); +} + +TEST_F(MuxerTest, MaxClusterSize) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + const uint64_t kMaxClusterSize = 20; + segment_.set_max_cluster_size(kMaxClusterSize); + EXPECT_EQ(kMaxClusterSize, segment_.max_cluster_size()); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, 1, kVideoTrackNumber, 0, false)); + EXPECT_TRUE( + segment_.AddFrame(dummy_data_, 1, kVideoTrackNumber, 2000000, false)); + EXPECT_TRUE( + segment_.AddFrame(dummy_data_, 1, kVideoTrackNumber, 4000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 6000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 8000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 9000000, false)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("max_cluster_size.webm"), filename_)); +} + +TEST_F(MuxerTest, MaxClusterDuration) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + const uint64_t kMaxClusterDuration = 4000000; + segment_.set_max_cluster_duration(kMaxClusterDuration); + + EXPECT_EQ(kMaxClusterDuration, segment_.max_cluster_duration()); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 4000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 6000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 8000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 9000000, false)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("max_cluster_duration.webm"), filename_)); +} + +TEST_F(MuxerTest, SetCuesTrackNumber) { + const uint64_t kTrackNumber = 10; + EXPECT_TRUE(SegmentInit(true, false, false)); + const uint64_t vid_track = + segment_.AddVideoTrack(kWidth, kHeight, kTrackNumber); + EXPECT_EQ(kTrackNumber, vid_track); + segment_.GetTrackByNumber(vid_track)->set_uid(kVideoTrackNumber); + EXPECT_TRUE(segment_.CuesTrack(vid_track)); + + EXPECT_EQ(vid_track, segment_.cues_track()); + EXPECT_TRUE( + segment_.AddFrame(dummy_data_, kFrameLength, kTrackNumber, 0, true)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kTrackNumber, + 2000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kTrackNumber, + 4000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kTrackNumber, + 6000000, true)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kTrackNumber, + 8000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kTrackNumber, + 9000000, false)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("set_cues_track_number.webm"), filename_)); +} + +TEST_F(MuxerTest, BlockWithDiscardPadding) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddAudioTrack(); + + int timecode = 1000; + // 12810000 == 0xc37710, should be 0-extended to avoid changing the sign. + // The next two should be written as 1 byte. + std::array values = {{12810000, 127, -128}}; + for (const std::int64_t discard_padding : values) { + EXPECT_TRUE(segment_.AddFrameWithDiscardPadding( + dummy_data_, kFrameLength, discard_padding, kAudioTrackNumber, timecode, + true)) + << "discard_padding: " << discard_padding; + timecode += 1000; + } + + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("discard_padding.webm"), filename_)); +} + +TEST_F(MuxerTest, AccurateClusterDuration) { + EXPECT_TRUE(SegmentInit(false, true, false)); + AddVideoTrack(); + + Frame frame; + frame.Init(dummy_data_, kFrameLength); + frame.set_track_number(kVideoTrackNumber); + frame.set_timestamp(0); + frame.set_is_key(true); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.ForceNewClusterOnNextFrame(); + frame.set_timestamp(2000000); + frame.set_is_key(false); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + frame.set_timestamp(4000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.ForceNewClusterOnNextFrame(); + frame.set_timestamp(6000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("accurate_cluster_duration.webm"), + filename_)); +} + +// Tests AccurateClusterDuration flag with the duration of the very last block +// of the file set explicitly. +TEST_F(MuxerTest, AccurateClusterDurationExplicitLastFrameDuration) { + EXPECT_TRUE(SegmentInit(false, true, false)); + AddVideoTrack(); + + Frame frame; + frame.Init(dummy_data_, kFrameLength); + frame.set_track_number(kVideoTrackNumber); + frame.set_timestamp(0); + frame.set_is_key(true); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.ForceNewClusterOnNextFrame(); + frame.set_timestamp(2000000); + frame.set_is_key(false); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + frame.set_timestamp(4000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.ForceNewClusterOnNextFrame(); + frame.set_timestamp(6000000); + frame.set_duration(2000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.Finalize(); + + // SegmentInfo's duration is in timecode scale + EXPECT_EQ(8, segment_.GetSegmentInfo()->duration()); + + CloseWriter(); + + EXPECT_TRUE(CompareFiles( + GetTestFilePath("accurate_cluster_duration_last_frame.webm"), filename_)); +} + +TEST_F(MuxerTest, AccurateClusterDurationTwoTracks) { + EXPECT_TRUE(SegmentInit(false, true, false)); + AddVideoTrack(); + AddAudioTrack(); + + Frame video_frame; + video_frame.Init(dummy_data_, kFrameLength); + video_frame.set_track_number(kVideoTrackNumber); + Frame audio_frame; + audio_frame.Init(dummy_data_, kFrameLength); + audio_frame.set_track_number(kAudioTrackNumber); + std::array cluster_timestamps = {{0, 40000000}}; + for (const std::uint64_t cluster_timestamp : cluster_timestamps) { + // Add video and audio frames with timestamp 0. + video_frame.set_timestamp(cluster_timestamp); + video_frame.set_is_key(true); + EXPECT_TRUE(segment_.AddGenericFrame(&video_frame)); + audio_frame.set_timestamp(cluster_timestamp); + audio_frame.set_is_key(true); + EXPECT_TRUE(segment_.AddGenericFrame(&audio_frame)); + + // Add 3 consecutive audio frames. + std::array audio_timestamps = { + {10000000, 20000000, 30000000}}; + for (const std::uint64_t audio_timestamp : audio_timestamps) { + audio_frame.set_timestamp(cluster_timestamp + audio_timestamp); + // Explicitly set duration for the very last audio frame. + if (cluster_timestamp == 40000000 && audio_timestamp == 30000000) { + audio_frame.set_duration(10000000); + } + EXPECT_TRUE(segment_.AddGenericFrame(&audio_frame)); + } + + // Add a video frame with timestamp 33ms. + video_frame.set_is_key(false); + // Explicitly set duration for the very last video frame. + if (cluster_timestamp == 40000000) { + video_frame.set_duration(7000000); + } + video_frame.set_timestamp(cluster_timestamp + 33000000); + EXPECT_TRUE(segment_.AddGenericFrame(&video_frame)); + segment_.ForceNewClusterOnNextFrame(); + } + segment_.Finalize(); + + // SegmentInfo's duration is in timecode scale + EXPECT_EQ(80, segment_.GetSegmentInfo()->duration()); + + CloseWriter(); + + EXPECT_TRUE(CompareFiles( + GetTestFilePath("accurate_cluster_duration_two_tracks.webm"), filename_)); +} + +TEST_F(MuxerTest, AccurateClusterDurationWithoutFinalizingCluster) { + EXPECT_TRUE(SegmentInit(false, true, false)); + AddVideoTrack(); + + // Add a couple of frames and then bail out without finalizing the Segment + // (and thereby not finalizing the Cluster). The expectation here is that + // there shouldn't be any leaks. The test will fail under valgrind if there's + // a leak. + Frame video_frame; + video_frame.Init(dummy_data_, kFrameLength); + video_frame.set_track_number(kVideoTrackNumber); + video_frame.set_timestamp(0); + video_frame.set_is_key(true); + EXPECT_TRUE(segment_.AddGenericFrame(&video_frame)); + video_frame.set_timestamp(33000000); + EXPECT_TRUE(segment_.AddGenericFrame(&video_frame)); + + CloseWriter(); +} + +TEST_F(MuxerTest, UseFixedSizeClusterTimecode) { + EXPECT_TRUE(SegmentInit(false, false, true)); + AddVideoTrack(); + + Frame frame; + frame.Init(dummy_data_, kFrameLength); + frame.set_track_number(kVideoTrackNumber); + frame.set_timestamp(0); + frame.set_is_key(true); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.ForceNewClusterOnNextFrame(); + frame.set_timestamp(2000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.ForceNewClusterOnNextFrame(); + frame.set_timestamp(4000000); + EXPECT_TRUE(segment_.AddGenericFrame(&frame)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("fixed_size_cluster_timecode.webm"), + filename_)); +} + +TEST_F(MuxerTest, DocTypeWebm) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + Track* const vid_track = segment_.GetTrackByNumber(kVideoTrackNumber); + vid_track->set_codec_id(kVP9CodecId); + AddDummyFrameAndFinalize(kVideoTrackNumber); + EXPECT_TRUE(CompareFiles(GetTestFilePath("webm_doctype.webm"), filename_)); +} + +TEST_F(MuxerTest, DocTypeMatroska) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + Track* const vid_track = segment_.GetTrackByNumber(kVideoTrackNumber); + vid_track->set_codec_id("V_SOMETHING_NOT_IN_WEBM"); + AddDummyFrameAndFinalize(kVideoTrackNumber); + EXPECT_TRUE(CompareFiles(GetTestFilePath("matroska_doctype.mkv"), filename_)); +} + +TEST_F(MuxerTest, Colour) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + mkvmuxer::PrimaryChromaticity muxer_pc(.1, .2); + mkvmuxer::MasteringMetadata muxer_mm; + muxer_mm.set_luminance_min(30.0); + muxer_mm.set_luminance_max(40.0); + ASSERT_TRUE( + muxer_mm.SetChromaticity(&muxer_pc, &muxer_pc, &muxer_pc, &muxer_pc)); + + mkvmuxer::Colour muxer_colour; + muxer_colour.set_matrix_coefficients(mkvmuxer::Colour::kGbr); + muxer_colour.set_bits_per_channel(1); + muxer_colour.set_chroma_subsampling_horz(2); + muxer_colour.set_chroma_subsampling_vert(3); + muxer_colour.set_cb_subsampling_horz(4); + muxer_colour.set_cb_subsampling_vert(5); + muxer_colour.set_chroma_siting_horz(mkvmuxer::Colour::kLeftCollocated); + muxer_colour.set_chroma_siting_vert(mkvmuxer::Colour::kTopCollocated); + muxer_colour.set_range(mkvmuxer::Colour::kFullRange); + muxer_colour.set_transfer_characteristics(mkvmuxer::Colour::kLog); + muxer_colour.set_primaries(mkvmuxer::Colour::kSmpteSt4281P); + muxer_colour.set_max_cll(11); + muxer_colour.set_max_fall(12); + ASSERT_TRUE(muxer_colour.SetMasteringMetadata(muxer_mm)); + + VideoTrack* const video_track = + dynamic_cast(segment_.GetTrackByNumber(kVideoTrackNumber)); + ASSERT_TRUE(video_track != nullptr); + ASSERT_TRUE(video_track->SetColour(muxer_colour)); + ASSERT_NO_FATAL_FAILURE(AddDummyFrameAndFinalize(kVideoTrackNumber)); + + MkvParser parser; + ASSERT_TRUE(ParseMkvFileReleaseParser(filename_, &parser)); + + const mkvparser::VideoTrack* const parser_track = + static_cast( + parser.segment->GetTracks()->GetTrackByIndex(0)); + const mkvparser::Colour* parser_colour = parser_track->GetColour(); + ASSERT_TRUE(parser_colour != nullptr); + EXPECT_EQ(static_cast(muxer_colour.matrix_coefficients()), + parser_colour->matrix_coefficients); + EXPECT_EQ(static_cast(muxer_colour.bits_per_channel()), + parser_colour->bits_per_channel); + EXPECT_EQ(static_cast(muxer_colour.chroma_subsampling_horz()), + parser_colour->chroma_subsampling_horz); + EXPECT_EQ(static_cast(muxer_colour.chroma_subsampling_vert()), + parser_colour->chroma_subsampling_vert); + EXPECT_EQ(static_cast(muxer_colour.cb_subsampling_horz()), + parser_colour->cb_subsampling_horz); + EXPECT_EQ(static_cast(muxer_colour.cb_subsampling_vert()), + parser_colour->cb_subsampling_vert); + EXPECT_EQ(static_cast(muxer_colour.chroma_siting_horz()), + parser_colour->chroma_siting_horz); + EXPECT_EQ(static_cast(muxer_colour.chroma_siting_vert()), + parser_colour->chroma_siting_vert); + EXPECT_EQ(static_cast(muxer_colour.range()), parser_colour->range); + EXPECT_EQ(static_cast(muxer_colour.transfer_characteristics()), + parser_colour->transfer_characteristics); + EXPECT_EQ(static_cast(muxer_colour.primaries()), + parser_colour->primaries); + EXPECT_EQ(static_cast(muxer_colour.max_cll()), + parser_colour->max_cll); + EXPECT_EQ(static_cast(muxer_colour.max_fall()), + parser_colour->max_fall); + + const mkvparser::MasteringMetadata* const parser_mm = + parser_colour->mastering_metadata; + ASSERT_TRUE(parser_mm != nullptr); + EXPECT_FLOAT_EQ(muxer_mm.luminance_min(), parser_mm->luminance_min); + EXPECT_FLOAT_EQ(muxer_mm.luminance_max(), parser_mm->luminance_max); + EXPECT_FLOAT_EQ(muxer_mm.r()->x(), parser_mm->r->x); + EXPECT_FLOAT_EQ(muxer_mm.r()->y(), parser_mm->r->y); + EXPECT_FLOAT_EQ(muxer_mm.g()->x(), parser_mm->g->x); + EXPECT_FLOAT_EQ(muxer_mm.g()->y(), parser_mm->g->y); + EXPECT_FLOAT_EQ(muxer_mm.b()->x(), parser_mm->b->x); + EXPECT_FLOAT_EQ(muxer_mm.b()->y(), parser_mm->b->y); + EXPECT_FLOAT_EQ(muxer_mm.white_point()->x(), parser_mm->white_point->x); + EXPECT_FLOAT_EQ(muxer_mm.white_point()->y(), parser_mm->white_point->y); + EXPECT_TRUE(CompareFiles(GetTestFilePath("colour.webm"), filename_)); +} + +TEST_F(MuxerTest, ColourPartial) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + mkvmuxer::Colour muxer_colour; + muxer_colour.set_matrix_coefficients( + mkvmuxer::Colour::kBt2020NonConstantLuminance); + + VideoTrack* const video_track = + dynamic_cast(segment_.GetTrackByNumber(kVideoTrackNumber)); + ASSERT_TRUE(video_track != nullptr); + ASSERT_TRUE(video_track->SetColour(muxer_colour)); + ASSERT_NO_FATAL_FAILURE(AddDummyFrameAndFinalize(kVideoTrackNumber)); + + MkvParser parser; + ASSERT_TRUE(ParseMkvFileReleaseParser(filename_, &parser)); + + const mkvparser::VideoTrack* const parser_track = + static_cast( + parser.segment->GetTracks()->GetTrackByIndex(0)); + const mkvparser::Colour* parser_colour = parser_track->GetColour(); + EXPECT_EQ(static_cast(muxer_colour.matrix_coefficients()), + parser_colour->matrix_coefficients); +} + +TEST_F(MuxerTest, Projection) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + + mkvmuxer::Projection muxer_proj; + muxer_proj.set_type(mkvmuxer::Projection::kRectangular); + muxer_proj.set_pose_yaw(1); + muxer_proj.set_pose_pitch(2); + muxer_proj.set_pose_roll(3); + const uint8_t muxer_proj_private[1] = {4}; + const uint64_t muxer_proj_private_length = 1; + ASSERT_TRUE(muxer_proj.SetProjectionPrivate(&muxer_proj_private[0], + muxer_proj_private_length)); + + VideoTrack* const video_track = + static_cast(segment_.GetTrackByNumber(kVideoTrackNumber)); + ASSERT_TRUE(video_track != nullptr); + ASSERT_TRUE(video_track->SetProjection(muxer_proj)); + ASSERT_NO_FATAL_FAILURE(AddDummyFrameAndFinalize(kVideoTrackNumber)); + + MkvParser parser; + ASSERT_TRUE(ParseMkvFileReleaseParser(filename_, &parser)); + + const mkvparser::VideoTrack* const parser_track = + static_cast( + parser.segment->GetTracks()->GetTrackByIndex(0)); + + const mkvparser::Projection* const parser_proj = + parser_track->GetProjection(); + ASSERT_TRUE(parser_proj != nullptr); + EXPECT_FLOAT_EQ(muxer_proj.pose_yaw(), parser_proj->pose_yaw); + EXPECT_FLOAT_EQ(muxer_proj.pose_pitch(), parser_proj->pose_pitch); + EXPECT_FLOAT_EQ(muxer_proj.pose_roll(), parser_proj->pose_roll); + ASSERT_TRUE(parser_proj->private_data != nullptr); + EXPECT_EQ(static_cast(muxer_proj.private_data_length()), + parser_proj->private_data_length); + + EXPECT_EQ(muxer_proj.private_data()[0], parser_proj->private_data[0]); + typedef mkvparser::Projection::ProjectionType ParserProjType; + EXPECT_EQ(static_cast(muxer_proj.type()), parser_proj->type); + EXPECT_TRUE(CompareFiles(GetTestFilePath("projection.webm"), filename_)); +} + +TEST_F(MuxerTest, EstimateDuration) { + EXPECT_TRUE(SegmentInit(false, false, false)); + segment_.set_estimate_file_duration(true); + AddVideoTrack(); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 4000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 6000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 8000000, false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 9000000, false)); + segment_.Finalize(); + + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("estimate_duration.webm"), filename_)); +} + +TEST_F(MuxerTest, SetPixelWidthPixelHeight) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + VideoTrack* const video_track = + static_cast(segment_.GetTrackByNumber(kVideoTrackNumber)); + ASSERT_TRUE(video_track != nullptr); + video_track->set_pixel_width(500); + video_track->set_pixel_height(650); + + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, + 2000000, false)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("set_pixelwidth_pixelheight.webm"), + filename_)); +} + +TEST_F(MuxerTest, LongTagString) { + EXPECT_TRUE(SegmentInit(false, false, false)); + AddVideoTrack(); + Tag* const tag = segment_.AddTag(); + // 160 needs two bytes when varint encoded. + const std::string dummy_string(160, '0'); + tag->add_simple_tag("long_tag", dummy_string.c_str()); + + EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0, + false)); + + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE(CompareFiles(GetTestFilePath("long_tag_string.webm"), filename_)); +} + +} // namespace test + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/testing/mkvparser_tests.cc b/testing/mkvparser_tests.cc new file mode 100644 index 000000000000..9c26a1ea025c --- /dev/null +++ b/testing/mkvparser_tests.cc @@ -0,0 +1,823 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include + +#include "common/hdr_util.h" +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" +#include "testing/test_util.h" + +using mkvparser::AudioTrack; +using mkvparser::Block; +using mkvparser::BlockEntry; +using mkvparser::BlockGroup; +using mkvparser::Cluster; +using mkvparser::CuePoint; +using mkvparser::Cues; +using mkvparser::MkvReader; +using mkvparser::Segment; +using mkvparser::SegmentInfo; +using mkvparser::Track; +using mkvparser::Tracks; +using mkvparser::VideoTrack; + +namespace test { + +// Base class containing boiler plate stuff. +class ParserTest : public testing::Test { + public: + ParserTest() : is_reader_open_(false), segment_(NULL) { + memset(dummy_data_, -1, kFrameLength); + memset(gold_frame_, 0, kFrameLength); + } + + virtual ~ParserTest() { + CloseReader(); + if (segment_ != NULL) { + delete segment_; + segment_ = NULL; + } + } + + void CloseReader() { + if (is_reader_open_) { + reader_.Close(); + } + is_reader_open_ = false; + } + + bool CreateAndLoadSegment(const std::string& filename, + int expected_doc_type_ver) { + filename_ = GetTestFilePath(filename); + if (reader_.Open(filename_.c_str())) { + return false; + } + is_reader_open_ = true; + pos_ = 0; + mkvparser::EBMLHeader ebml_header; + ebml_header.Parse(&reader_, pos_); + EXPECT_EQ(1, ebml_header.m_version); + EXPECT_EQ(1, ebml_header.m_readVersion); + EXPECT_STREQ("webm", ebml_header.m_docType); + EXPECT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion); + EXPECT_EQ(2, ebml_header.m_docTypeReadVersion); + + if (mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)) { + return false; + } + return !HasFailure() && segment_->Load() >= 0; + } + + bool CreateAndLoadSegment(const std::string& filename) { + return CreateAndLoadSegment(filename, 4); + } + + void CreateSegmentNoHeaderChecks(const std::string& filename) { + filename_ = GetTestFilePath(filename); + ASSERT_NE(0u, filename_.length()); + ASSERT_EQ(0, reader_.Open(filename_.c_str())); + mkvparser::EBMLHeader ebml_header; + ASSERT_EQ(0, ebml_header.Parse(&reader_, pos_)); + ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)); + } + + void CompareBlockContents(const Cluster* const cluster, + const Block* const block, std::uint64_t timestamp, + int track_number, bool is_key, int frame_count) { + ASSERT_TRUE(block != NULL); + EXPECT_EQ(track_number, block->GetTrackNumber()); + EXPECT_EQ(static_cast(timestamp), block->GetTime(cluster)); + EXPECT_EQ(is_key, block->IsKey()); + EXPECT_EQ(frame_count, block->GetFrameCount()); + const Block::Frame& frame = block->GetFrame(0); + EXPECT_EQ(kFrameLength, frame.len); + std::memset(dummy_data_, -1, kFrameLength); + frame.Read(&reader_, dummy_data_); + EXPECT_EQ(0, std::memcmp(gold_frame_, dummy_data_, kFrameLength)); + } + + void CompareCuePointContents(const Track* const track, + const CuePoint* const cue_point, + std::uint64_t timestamp, int track_number, + std::uint64_t pos) { + ASSERT_TRUE(cue_point != NULL); + EXPECT_EQ(static_cast(timestamp), cue_point->GetTime(segment_)); + const CuePoint::TrackPosition* const track_position = + cue_point->Find(track); + EXPECT_EQ(track_number, track_position->m_track); + EXPECT_EQ(static_cast(pos), track_position->m_pos); + } + + protected: + MkvReader reader_; + bool is_reader_open_; + Segment* segment_; + std::string filename_; + long long pos_; + std::uint8_t dummy_data_[kFrameLength]; + std::uint8_t gold_frame_[kFrameLength]; +}; + +TEST_F(ParserTest, SegmentInfo) { + ASSERT_TRUE(CreateAndLoadSegment("segment_info.webm")); + const SegmentInfo* const info = segment_->GetInfo(); + EXPECT_EQ(kTimeCodeScale, info->GetTimeCodeScale()); + EXPECT_STREQ(kAppString, info->GetMuxingAppAsUTF8()); + EXPECT_STREQ(kAppString, info->GetWritingAppAsUTF8()); +} + +TEST_F(ParserTest, TrackEntries) { + ASSERT_TRUE(CreateAndLoadSegment("tracks.webm")); + const Tracks* const tracks = segment_->GetTracks(); + const unsigned int kTracksCount = 2; + EXPECT_EQ(kTracksCount, tracks->GetTracksCount()); + for (int i = 0; i < 2; ++i) { + const Track* const track = tracks->GetTrackByIndex(i); + ASSERT_TRUE(track != NULL); + EXPECT_STREQ(kTrackName, track->GetNameAsUTF8()); + if (track->GetType() == Track::kVideo) { + const VideoTrack* const video_track = + dynamic_cast(track); + EXPECT_EQ(kWidth, static_cast(video_track->GetWidth())); + EXPECT_EQ(kHeight, static_cast(video_track->GetHeight())); + EXPECT_STREQ(kVP8CodecId, video_track->GetCodecId()); + EXPECT_DOUBLE_EQ(kVideoFrameRate, video_track->GetFrameRate()); + const unsigned int kTrackUid = 1; + EXPECT_EQ(kTrackUid, video_track->GetUid()); + } else if (track->GetType() == Track::kAudio) { + const AudioTrack* const audio_track = + dynamic_cast(track); + EXPECT_EQ(kSampleRate, audio_track->GetSamplingRate()); + EXPECT_EQ(kChannels, audio_track->GetChannels()); + EXPECT_EQ(kBitDepth, audio_track->GetBitDepth()); + EXPECT_STREQ(kVorbisCodecId, audio_track->GetCodecId()); + const unsigned int kTrackUid = 2; + EXPECT_EQ(kTrackUid, audio_track->GetUid()); + } + } +} + +TEST_F(ParserTest, SimpleBlock) { + ASSERT_TRUE(CreateAndLoadSegment("simple_block.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + // Get the cluster + const Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + + // Get the first block + const BlockEntry* block_entry; + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + CompareBlockContents(cluster, block_entry->GetBlock(), 0, kVideoTrackNumber, + false, 1); + + // Get the second block + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + CompareBlockContents(cluster, block_entry->GetBlock(), 2000000, + kVideoTrackNumber, false, 1); + + // End of Stream + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + ASSERT_EQ(NULL, block_entry); + cluster = segment_->GetNext(cluster); + EXPECT_TRUE(cluster->EOS()); +} + +TEST_F(ParserTest, MultipleClusters) { + ASSERT_TRUE(CreateAndLoadSegment("force_new_cluster.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + // Get the first cluster + const Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + + // Get the first block + const BlockEntry* block_entry; + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + CompareBlockContents(cluster, block_entry->GetBlock(), 0, kVideoTrackNumber, + false, 1); + + // Get the second cluster + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + EXPECT_EQ(NULL, block_entry); + cluster = segment_->GetNext(cluster); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + + // Get the second block + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + CompareBlockContents(cluster, block_entry->GetBlock(), 2000000, + kVideoTrackNumber, false, 1); + + // Get the third block + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + CompareBlockContents(cluster, block_entry->GetBlock(), 4000000, + kVideoTrackNumber, false, 1); + + // Get the third cluster + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + EXPECT_EQ(NULL, block_entry); + cluster = segment_->GetNext(cluster); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + + // Get the fourth block + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + CompareBlockContents(cluster, block_entry->GetBlock(), 6000000, + kVideoTrackNumber, false, 1); + + // End of Stream + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + EXPECT_EQ(NULL, block_entry); + cluster = segment_->GetNext(cluster); + EXPECT_TRUE(cluster->EOS()); +} + +TEST_F(ParserTest, BlockGroup) { + ASSERT_TRUE(CreateAndLoadSegment("metadata_block.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + // Get the cluster + const Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + + // Get the first block + const BlockEntry* block_entry; + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + EXPECT_EQ(BlockEntry::Kind::kBlockGroup, block_entry->GetKind()); + const BlockGroup* block_group = static_cast(block_entry); + EXPECT_EQ(2, block_group->GetDurationTimeCode()); + CompareBlockContents(cluster, block_group->GetBlock(), 0, + kMetadataTrackNumber, true, 1); + + // Get the second block + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + EXPECT_EQ(BlockEntry::Kind::kBlockGroup, block_entry->GetKind()); + block_group = static_cast(block_entry); + EXPECT_EQ(6, block_group->GetDurationTimeCode()); + CompareBlockContents(cluster, block_group->GetBlock(), 2000000, + kMetadataTrackNumber, true, 1); + + // End of Stream + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + EXPECT_EQ(NULL, block_entry); + cluster = segment_->GetNext(cluster); + EXPECT_TRUE(cluster->EOS()); +} + +TEST_F(ParserTest, Cues) { + ASSERT_TRUE(CreateAndLoadSegment("output_cues.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + const Track* const track = segment_->GetTracks()->GetTrackByIndex(0); + const Cues* const cues = segment_->GetCues(); + ASSERT_TRUE(cues != NULL); + while (!cues->DoneParsing()) { + cues->LoadCuePoint(); + } + EXPECT_EQ(3, cues->GetCount()); + + // Get first Cue Point + const CuePoint* cue_point = cues->GetFirst(); + CompareCuePointContents(track, cue_point, 0, kVideoTrackNumber, 206); + + // Get second Cue Point + cue_point = cues->GetNext(cue_point); + CompareCuePointContents(track, cue_point, 6000000, kVideoTrackNumber, 269); + + // Get third (also last) Cue Point + cue_point = cues->GetNext(cue_point); + const CuePoint* last_cue_point = cues->GetLast(); + EXPECT_TRUE(cue_point == last_cue_point); + CompareCuePointContents(track, cue_point, 4000000, kVideoTrackNumber, 269); + + EXPECT_TRUE(ValidateCues(segment_, &reader_)); +} + +TEST_F(ParserTest, CuesBeforeClusters) { + ASSERT_TRUE(CreateAndLoadSegment("cues_before_clusters.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + const Track* const track = segment_->GetTracks()->GetTrackByIndex(0); + const Cues* const cues = segment_->GetCues(); + ASSERT_TRUE(cues != NULL); + while (!cues->DoneParsing()) { + cues->LoadCuePoint(); + } + EXPECT_EQ(2, cues->GetCount()); + + // Get first Cue Point + const CuePoint* cue_point = cues->GetFirst(); + CompareCuePointContents(track, cue_point, 0, kVideoTrackNumber, 238); + + // Get second (also last) Cue Point + cue_point = cues->GetNext(cue_point); + const CuePoint* last_cue_point = cues->GetLast(); + EXPECT_TRUE(cue_point == last_cue_point); + CompareCuePointContents(track, cue_point, 6000000, kVideoTrackNumber, 301); + + EXPECT_TRUE(ValidateCues(segment_, &reader_)); +} + +TEST_F(ParserTest, CuesTrackNumber) { + ASSERT_TRUE(CreateAndLoadSegment("set_cues_track_number.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + const Track* const track = segment_->GetTracks()->GetTrackByIndex(0); + const Cues* const cues = segment_->GetCues(); + ASSERT_TRUE(cues != NULL); + while (!cues->DoneParsing()) { + cues->LoadCuePoint(); + } + EXPECT_EQ(2, cues->GetCount()); + + // Get first Cue Point + const CuePoint* cue_point = cues->GetFirst(); + CompareCuePointContents(track, cue_point, 0, 10, 206); + + // Get second (also last) Cue Point + cue_point = cues->GetNext(cue_point); + const CuePoint* last_cue_point = cues->GetLast(); + EXPECT_TRUE(cue_point == last_cue_point); + CompareCuePointContents(track, cue_point, 6000000, 10, 269); + + EXPECT_TRUE(ValidateCues(segment_, &reader_)); +} + +TEST_F(ParserTest, Opus) { + ASSERT_TRUE(CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4)); + const unsigned int kTracksCount = 2; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + // -------------------------------------------------------------------------- + // Track Header validation. + const Tracks* const tracks = segment_->GetTracks(); + EXPECT_EQ(kTracksCount, tracks->GetTracksCount()); + for (int i = 0; i < 2; ++i) { + const Track* const track = tracks->GetTrackByIndex(i); + ASSERT_TRUE(track != NULL); + + EXPECT_EQ(NULL, track->GetNameAsUTF8()); + EXPECT_STREQ("und", track->GetLanguage()); + EXPECT_EQ(i + 1, track->GetNumber()); + EXPECT_FALSE(track->GetLacing()); + + if (track->GetType() == Track::kVideo) { + const VideoTrack* const video_track = + dynamic_cast(track); + EXPECT_EQ(854, static_cast(video_track->GetWidth())); + EXPECT_EQ(480, static_cast(video_track->GetHeight())); + EXPECT_STREQ(kVP9CodecId, video_track->GetCodecId()); + EXPECT_DOUBLE_EQ(0., video_track->GetFrameRate()); + EXPECT_EQ(41666666, + static_cast(video_track->GetDefaultDuration())); // 24.000 + const unsigned int kVideoUid = kVideoTrackNumber; + EXPECT_EQ(kVideoUid, video_track->GetUid()); + const unsigned int kCodecDelay = 0; + EXPECT_EQ(kCodecDelay, video_track->GetCodecDelay()); + const unsigned int kSeekPreRoll = 0; + EXPECT_EQ(kSeekPreRoll, video_track->GetSeekPreRoll()); + + size_t video_codec_private_size; + EXPECT_EQ(NULL, video_track->GetCodecPrivate(video_codec_private_size)); + const unsigned int kPrivateSize = 0; + EXPECT_EQ(kPrivateSize, video_codec_private_size); + } else if (track->GetType() == Track::kAudio) { + const AudioTrack* const audio_track = + dynamic_cast(track); + EXPECT_EQ(48000, audio_track->GetSamplingRate()); + EXPECT_EQ(6, audio_track->GetChannels()); + EXPECT_EQ(32, audio_track->GetBitDepth()); + EXPECT_STREQ(kOpusCodecId, audio_track->GetCodecId()); + EXPECT_EQ(kAudioTrackNumber, static_cast(audio_track->GetUid())); + const unsigned int kDefaultDuration = 0; + EXPECT_EQ(kDefaultDuration, audio_track->GetDefaultDuration()); + EXPECT_EQ(kOpusCodecDelay, audio_track->GetCodecDelay()); + EXPECT_EQ(kOpusSeekPreroll, audio_track->GetSeekPreRoll()); + + size_t audio_codec_private_size; + EXPECT_TRUE(audio_track->GetCodecPrivate(audio_codec_private_size) != + NULL); + EXPECT_GE(audio_codec_private_size, kOpusPrivateDataSizeMinimum); + } + } + + // -------------------------------------------------------------------------- + // Parse the file to do block-level validation. + const Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + + for (; cluster != NULL && !cluster->EOS(); + cluster = segment_->GetNext(cluster)) { + // Get the first block + const BlockEntry* block_entry; + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + + while (block_entry != NULL && !block_entry->EOS()) { + const Block* const block = block_entry->GetBlock(); + ASSERT_TRUE(block != NULL); + EXPECT_FALSE(block->IsInvisible()); + EXPECT_EQ(Block::kLacingNone, block->GetLacing()); + + const std::uint32_t track_number = + static_cast(block->GetTrackNumber()); + const Track* const track = tracks->GetTrackByNumber(track_number); + ASSERT_TRUE(track != NULL); + EXPECT_EQ(track->GetNumber(), block->GetTrackNumber()); + const unsigned int kContentEncodingCount = 0; + EXPECT_EQ(kContentEncodingCount, + track->GetContentEncodingCount()); // no encryption + + const std::int64_t track_type = track->GetType(); + EXPECT_TRUE(track_type == Track::kVideo || track_type == Track::kAudio); + if (track_type == Track::kVideo) { + EXPECT_EQ(BlockEntry::kBlockSimple, block_entry->GetKind()); + EXPECT_EQ(0, block->GetDiscardPadding()); + } else { + EXPECT_TRUE(block->IsKey()); + const std::int64_t kLastAudioTimecode = 1001; + const std::int64_t timecode = block->GetTimeCode(cluster); + // Only the final Opus block should have discard padding. + if (timecode == kLastAudioTimecode) { + EXPECT_EQ(BlockEntry::kBlockGroup, block_entry->GetKind()); + EXPECT_EQ(13500000, block->GetDiscardPadding()); + } else { + EXPECT_EQ(BlockEntry::kBlockSimple, block_entry->GetKind()); + EXPECT_EQ(0, block->GetDiscardPadding()); + } + } + + const int frame_count = block->GetFrameCount(); + const Block::Frame& frame = block->GetFrame(0); + EXPECT_EQ(1, frame_count); + EXPECT_GT(frame.len, 0); + + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + } + } + + ASSERT_TRUE(cluster != NULL); + EXPECT_TRUE(cluster->EOS()); +} + +TEST_F(ParserTest, DiscardPadding) { + // Test an artificial file with some extreme DiscardPadding values. + const std::string file = "discard_padding.webm"; + ASSERT_TRUE(CreateAndLoadSegment(file, 4)); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + // -------------------------------------------------------------------------- + // Track Header validation. + const Tracks* const tracks = segment_->GetTracks(); + EXPECT_EQ(kTracksCount, tracks->GetTracksCount()); + const Track* const track = tracks->GetTrackByIndex(0); + ASSERT_TRUE(track != NULL); + + EXPECT_STREQ(NULL, track->GetNameAsUTF8()); + EXPECT_EQ(NULL, track->GetLanguage()); + EXPECT_EQ(kAudioTrackNumber, track->GetNumber()); + EXPECT_TRUE(track->GetLacing()); + + EXPECT_EQ(Track::kAudio, track->GetType()); + const AudioTrack* const audio_track = dynamic_cast(track); + EXPECT_EQ(30, audio_track->GetSamplingRate()); + EXPECT_EQ(2, audio_track->GetChannels()); + EXPECT_STREQ(kOpusCodecId, audio_track->GetCodecId()); + EXPECT_EQ(kAudioTrackNumber, static_cast(audio_track->GetUid())); + const unsigned int kDefaultDuration = 0; + EXPECT_EQ(kDefaultDuration, audio_track->GetDefaultDuration()); + const unsigned int kCodecDelay = 0; + EXPECT_EQ(kCodecDelay, audio_track->GetCodecDelay()); + const unsigned int kSeekPreRoll = 0; + EXPECT_EQ(kSeekPreRoll, audio_track->GetSeekPreRoll()); + + size_t audio_codec_private_size; + EXPECT_EQ(NULL, audio_track->GetCodecPrivate(audio_codec_private_size)); + const unsigned int kPrivateSize = 0; + EXPECT_EQ(kPrivateSize, audio_codec_private_size); + + // -------------------------------------------------------------------------- + // Parse the file to do block-level validation. + const Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster != NULL); + EXPECT_FALSE(cluster->EOS()); + const unsigned int kSegmentCount = 1; + EXPECT_EQ(kSegmentCount, segment_->GetCount()); + + // Get the first block + const BlockEntry* block_entry; + EXPECT_EQ(0, cluster->GetFirst(block_entry)); + ASSERT_TRUE(block_entry != NULL); + EXPECT_FALSE(block_entry->EOS()); + + const std::array discard_padding = {{12810000, 127, -128}}; + int index = 0; + while (block_entry != NULL && !block_entry->EOS()) { + const Block* const block = block_entry->GetBlock(); + ASSERT_TRUE(block != NULL); + EXPECT_FALSE(block->IsInvisible()); + EXPECT_EQ(Block::kLacingNone, block->GetLacing()); + + const std::uint32_t track_number = + static_cast(block->GetTrackNumber()); + const Track* const track = tracks->GetTrackByNumber(track_number); + ASSERT_TRUE(track != NULL); + EXPECT_EQ(track->GetNumber(), block->GetTrackNumber()); + const unsigned int kContentEncodingCount = 0; + EXPECT_EQ(kContentEncodingCount, + track->GetContentEncodingCount()); // no encryption + + const std::int64_t track_type = track->GetType(); + EXPECT_EQ(Track::kAudio, track_type); + EXPECT_TRUE(block->IsKey()); + + // All blocks have DiscardPadding. + EXPECT_EQ(BlockEntry::kBlockGroup, block_entry->GetKind()); + ASSERT_LT(index, static_cast(discard_padding.size())); + EXPECT_EQ(discard_padding[index], block->GetDiscardPadding()); + ++index; + + const int frame_count = block->GetFrameCount(); + const Block::Frame& frame = block->GetFrame(0); + EXPECT_EQ(1, frame_count); + EXPECT_GT(frame.len, 0); + + EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry)); + } + + cluster = segment_->GetNext(cluster); + ASSERT_TRUE(cluster != NULL); + EXPECT_TRUE(cluster->EOS()); +} + +TEST_F(ParserTest, StereoModeParsedCorrectly) { + ASSERT_TRUE(CreateAndLoadSegment("test_stereo_left_right.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + + const VideoTrack* const video_track = dynamic_cast( + segment_->GetTracks()->GetTrackByIndex(0)); + + EXPECT_EQ(1, video_track->GetStereoMode()); + EXPECT_EQ(256, video_track->GetWidth()); + EXPECT_EQ(144, video_track->GetHeight()); + EXPECT_EQ(128, video_track->GetDisplayWidth()); + EXPECT_EQ(144, video_track->GetDisplayHeight()); +} + +TEST_F(ParserTest, CanParseColour) { + ASSERT_TRUE(CreateAndLoadSegment("colour.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + const VideoTrack* const video_track = dynamic_cast( + segment_->GetTracks()->GetTrackByIndex(0)); + + const mkvparser::Colour* const colour = video_track->GetColour(); + ASSERT_TRUE(colour != nullptr); + EXPECT_EQ(0u, colour->matrix_coefficients); + EXPECT_EQ(1u, colour->bits_per_channel); + EXPECT_EQ(2u, colour->chroma_subsampling_horz); + EXPECT_EQ(3u, colour->chroma_subsampling_vert); + EXPECT_EQ(4u, colour->cb_subsampling_horz); + EXPECT_EQ(5u, colour->cb_subsampling_vert); + EXPECT_EQ(1u, colour->chroma_siting_horz); + EXPECT_EQ(1u, colour->chroma_siting_vert); + EXPECT_EQ(2u, colour->range); + EXPECT_EQ(9u, colour->transfer_characteristics); + EXPECT_EQ(10u, colour->primaries); + EXPECT_EQ(11u, colour->max_cll); + EXPECT_EQ(12u, colour->max_fall); + + const mkvparser::MasteringMetadata* const mm = + video_track->GetColour()->mastering_metadata; + ASSERT_TRUE(mm != nullptr); + ASSERT_TRUE(mm->r != nullptr); + ASSERT_TRUE(mm->g != nullptr); + ASSERT_TRUE(mm->b != nullptr); + ASSERT_TRUE(mm->white_point != nullptr); + EXPECT_FLOAT_EQ(.1, mm->r->x); + EXPECT_FLOAT_EQ(.2, mm->r->y); + EXPECT_FLOAT_EQ(.1, mm->g->x); + EXPECT_FLOAT_EQ(.2, mm->g->y); + EXPECT_FLOAT_EQ(.1, mm->b->x); + EXPECT_FLOAT_EQ(.2, mm->b->y); + EXPECT_FLOAT_EQ(.1, mm->white_point->x); + EXPECT_FLOAT_EQ(.2, mm->white_point->y); + EXPECT_FLOAT_EQ(30.0, mm->luminance_min); + EXPECT_FLOAT_EQ(40.0, mm->luminance_max); +} + +TEST_F(ParserTest, CanParseProjection) { + ASSERT_TRUE(CreateAndLoadSegment("projection.webm")); + const unsigned int kTracksCount = 1; + EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount()); + const VideoTrack* const video_track = + static_cast(segment_->GetTracks()->GetTrackByIndex(0)); + + const mkvparser::Projection* const projection = video_track->GetProjection(); + ASSERT_TRUE(projection != nullptr); + EXPECT_EQ(mkvparser::Projection::kRectangular, projection->type); + EXPECT_FLOAT_EQ(1, projection->pose_yaw); + EXPECT_FLOAT_EQ(2, projection->pose_pitch); + EXPECT_FLOAT_EQ(3, projection->pose_roll); + EXPECT_EQ(1u, projection->private_data_length); + ASSERT_TRUE(projection->private_data != nullptr); + EXPECT_EQ(4u, projection->private_data[0]); +} + +TEST_F(ParserTest, Vp9CodecLevelTest) { + const int kCodecPrivateLength = 3; + const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile); + EXPECT_EQ(11, features.level); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, + features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecProfileTest) { + const int kCodecPrivateLength = 3; + const uint8_t good_codec_private_profile[kCodecPrivateLength] = {1, 1, 1}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(1, features.profile); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, + features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecBitDepthTest) { + const int kCodecPrivateLength = 3; + const uint8_t good_codec_private_profile[kCodecPrivateLength] = {3, 1, 8}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level); + EXPECT_EQ(8, features.bit_depth); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, + features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecChromaSubsamplingTest) { + const int kCodecPrivateLength = 3; + const uint8_t good_codec_private_profile[kCodecPrivateLength] = {4, 1, 0}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth); + EXPECT_EQ(0, features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecProfileLevelTest) { + const int kCodecPrivateLength = 6; + const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(1, features.profile); + EXPECT_EQ(11, features.level); +} + +TEST_F(ParserTest, Vp9CodecAllTest) { + const int kCodecPrivateLength = 12; + const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11, + 3, 1, 8, 4, 1, 0}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(1, features.profile); + EXPECT_EQ(11, features.level); + EXPECT_EQ(8, features.bit_depth); + EXPECT_EQ(0, features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecPrivateBadTest) { + const int kCodecPrivateLength = 3; + libwebm::Vp9CodecFeatures features; + // Test invalid codec private data; all of these should return false. + const uint8_t bad_codec_private[kCodecPrivateLength] = {0, 0, 0}; + EXPECT_FALSE( + libwebm::ParseVpxCodecPrivate(NULL, kCodecPrivateLength, &features)); + EXPECT_FALSE( + libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features)); + EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], + kCodecPrivateLength, &features)); + const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11}; + + // Test parse of codec private chunks, but lie about length. + EXPECT_FALSE( + libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features)); + EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], 0, + &features)); + EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], + kCodecPrivateLength, NULL)); +} + +TEST_F(ParserTest, InvalidTruncatedChapterString) { + ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks( + "invalid/chapters_truncated_chapter_string.mkv")); + EXPECT_EQ(mkvparser::E_PARSE_FAILED, segment_->Load()); +} + +TEST_F(ParserTest, InvalidTruncatedChapterString2) { + ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks( + "invalid/chapters_truncated_chapter_string_2.mkv")); + EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load()); +} + +TEST_F(ParserTest, InvalidFixedLacingSize) { + ASSERT_NO_FATAL_FAILURE( + CreateSegmentNoHeaderChecks("invalid/fixed_lacing_bad_lace_size.mkv")); + ASSERT_EQ(0, segment_->Load()); + const mkvparser::BlockEntry* block_entry = NULL; + EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, + segment_->GetFirst()->GetFirst(block_entry)); +} + +TEST_F(ParserTest, InvalidBlockEndsBeyondCluster) { + ASSERT_NO_FATAL_FAILURE( + CreateSegmentNoHeaderChecks("invalid/block_ends_beyond_cluster.mkv")); + ASSERT_EQ(0, segment_->Load()); + const mkvparser::BlockEntry* block_entry = NULL; + EXPECT_EQ(0, segment_->GetFirst()->GetFirst(block_entry)); + EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, + segment_->GetFirst()->GetNext(block_entry, block_entry)); +} + +TEST_F(ParserTest, InvalidBlockGroupBlockEndsBlockGroup) { + ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks( + "invalid/blockgroup_block_ends_beyond_blockgroup.mkv")); + ASSERT_EQ(0, segment_->Load()); + const mkvparser::BlockEntry* block_entry = NULL; + EXPECT_EQ(0, segment_->GetFirst()->GetFirst(block_entry)); + EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, + segment_->GetFirst()->GetNext(block_entry, block_entry)); +} + +TEST_F(ParserTest, InvalidProjectionFloatOverflow) { + ASSERT_NO_FATAL_FAILURE( + CreateSegmentNoHeaderChecks("invalid/projection_float_overflow.webm")); + EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load()); +} + +TEST_F(ParserTest, InvalidPrimaryChromaticityParseFail) { + ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks( + "invalid/primarychromaticity_fieldtoolarge.webm")); + EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load()); +} + +} // namespace test + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/testing/test_util.cc b/testing/test_util.cc new file mode 100644 index 000000000000..8789b3325b24 --- /dev/null +++ b/testing/test_util.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "testing/test_util.h" + +#include +#include +#include +#include +#include +#include + +#include "common/libwebm_util.h" +#include "common/webmids.h" + +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" + +namespace test { + +std::string GetTestDataDir() { + const char* test_data_path = std::getenv("LIBWEBM_TEST_DATA_PATH"); + return test_data_path ? std::string(test_data_path) : std::string(); +} + +std::string GetTestFilePath(const std::string& name) { + const std::string libwebm_testdata_dir = GetTestDataDir(); + return libwebm_testdata_dir + "/" + name; +} + +bool CompareFiles(const std::string& file1, const std::string& file2) { + const std::size_t kBlockSize = 4096; + std::uint8_t buf1[kBlockSize] = {0}; + std::uint8_t buf2[kBlockSize] = {0}; + + libwebm::FilePtr f1 = + libwebm::FilePtr(std::fopen(file1.c_str(), "rb"), libwebm::FILEDeleter()); + libwebm::FilePtr f2 = + libwebm::FilePtr(std::fopen(file2.c_str(), "rb"), libwebm::FILEDeleter()); + + if (!f1.get() || !f2.get()) { + // Files cannot match if one or both couldn't be opened. + return false; + } + + do { + const std::size_t r1 = std::fread(buf1, 1, kBlockSize, f1.get()); + const std::size_t r2 = std::fread(buf2, 1, kBlockSize, f2.get()); + + // TODO(fgalligan): Add output of which byte differs. + if (r1 != r2 || std::memcmp(buf1, buf2, r1)) { + return 0; // Files are not equal + } + } while (!std::feof(f1.get()) && !std::feof(f2.get())); + + return std::feof(f1.get()) && std::feof(f2.get()); +} + +bool HasCuePoints(const mkvparser::Segment* segment, + std::int64_t* cues_offset) { + if (!segment || !cues_offset) { + return false; + } + using mkvparser::SeekHead; + const SeekHead* const seek_head = segment->GetSeekHead(); + if (!seek_head) { + return false; + } + + std::int64_t offset = 0; + for (int i = 0; i < seek_head->GetCount(); ++i) { + const SeekHead::Entry* const entry = seek_head->GetEntry(i); + if (entry->id == libwebm::kMkvCues) { + offset = entry->pos; + } + } + + if (offset <= 0) { + // No Cues found. + return false; + } + + *cues_offset = offset; + return true; +} + +bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader) { + if (!segment) { + return false; + } + + std::int64_t cues_offset = 0; + if (!HasCuePoints(segment, &cues_offset)) { + // No cues to validate, everything is OK. + return true; + } + + // Parse Cues. + long long cues_pos = 0; // NOLINT + long cues_len = 0; // NOLINT + if (segment->ParseCues(cues_offset, cues_pos, cues_len)) { + return false; + } + + // Get a pointer to the video track if it exists. Otherwise, we assume + // that Cues are based on the first track (which is true for all our test + // files). + const mkvparser::Tracks* const tracks = segment->GetTracks(); + const mkvparser::Track* cues_track = tracks->GetTrackByIndex(0); + for (int i = 1; i < static_cast(tracks->GetTracksCount()); ++i) { + const mkvparser::Track* const track = tracks->GetTrackByIndex(i); + if (track->GetType() == mkvparser::Track::kVideo) { + cues_track = track; + break; + } + } + + // Iterate through Cues and verify if they are pointing to the correct + // Cluster position. + const mkvparser::Cues* const cues = segment->GetCues(); + const mkvparser::CuePoint* cue_point = NULL; + while (cues->LoadCuePoint()) { + if (!cue_point) { + cue_point = cues->GetFirst(); + } else { + cue_point = cues->GetNext(cue_point); + } + const mkvparser::CuePoint::TrackPosition* const track_position = + cue_point->Find(cues_track); + const long long cluster_pos = track_position->m_pos + // NOLINT + segment->m_start; + + // If a cluster does not begin at |cluster_pos|, then the file is + // incorrect. + long length; // NOLINT + const std::int64_t id = mkvparser::ReadID(reader, cluster_pos, length); + if (id != libwebm::kMkvCluster) { + return false; + } + } + return true; +} + +MkvParser::~MkvParser() { + delete segment; + delete reader; +} + +bool ParseMkvFileReleaseParser(const std::string& webm_file, + MkvParser* parser_out) { + parser_out->reader = new (std::nothrow) mkvparser::MkvReader; + mkvparser::MkvReader& reader = *parser_out->reader; + if (!parser_out->reader || reader.Open(webm_file.c_str()) < 0) { + return false; + } + + long long pos = 0; // NOLINT + mkvparser::EBMLHeader ebml_header; + if (ebml_header.Parse(&reader, pos)) { + return false; + } + + using mkvparser::Segment; + Segment* segment_ptr = nullptr; + if (Segment::CreateInstance(&reader, pos, segment_ptr)) { + return false; + } + + std::unique_ptr segment(segment_ptr); + long result; + if ((result = segment->Load()) < 0) { + return false; + } + + const mkvparser::Cluster* cluster = segment->GetFirst(); + if (!cluster || cluster->EOS()) { + return false; + } + + while (cluster && cluster->EOS() == false) { + if (cluster->GetTimeCode() < 0) { + return false; + } + + const mkvparser::BlockEntry* block = nullptr; + if (cluster->GetFirst(block) < 0) { + return false; + } + + while (block != NULL && block->EOS() == false) { + if (cluster->GetNext(block, block) < 0) { + return false; + } + } + + cluster = segment->GetNext(cluster); + } + + parser_out->segment = segment.release(); + return true; +} + +bool ParseMkvFile(const std::string& webm_file) { + MkvParser parser; + const bool result = ParseMkvFileReleaseParser(webm_file, &parser); + delete parser.segment; + delete parser.reader; + return result; +} + +} // namespace test diff --git a/testing/test_util.h b/testing/test_util.h new file mode 100644 index 000000000000..5f85ec74e764 --- /dev/null +++ b/testing/test_util.h @@ -0,0 +1,88 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_TESTING_TEST_UTIL_H_ +#define LIBWEBM_TESTING_TEST_UTIL_H_ + +#include + +#include +#include + +namespace mkvparser { +class IMkvReader; +class MkvReader; +class Segment; +} // namespace mkvparser + +namespace test { + +// constants for muxer and parser tests +const char kAppString[] = "mkvmuxer_unit_tests"; +const char kOpusCodecId[] = "A_OPUS"; +const char kVorbisCodecId[] = "A_VORBIS"; +const int kAudioTrackNumber = 2; +const int kBitDepth = 2; +const int kChannels = 2; +const double kDuration = 2.345; +const int kFrameLength = 10; +const int kHeight = 180; +const int kInvalidTrackNumber = 100; +const std::uint64_t kOpusCodecDelay = 6500000; +const std::size_t kOpusPrivateDataSizeMinimum = 19; +const std::uint64_t kOpusSeekPreroll = 80000000; +const char kMetadataCodecId[] = "D_WEBVTT/METADATA"; +const int kMetadataTrackNumber = 3; +const int kMetadataTrackType = 0x21; +const int kSampleRate = 30; +const int kTimeCodeScale = 1000; +const char kTrackName[] = "unit_test"; +const char kVP8CodecId[] = "V_VP8"; +const char kVP9CodecId[] = "V_VP9"; +const double kVideoFrameRate = 0.5; +const int kVideoTrackNumber = 1; +const int kWidth = 320; + +// Returns the path to the test data directory by reading and returning the +// contents the LIBWEBM_TESTDATA_DIR environment variable. +std::string GetTestDataDir(); + +// Returns the absolute path to the file of |name| in LIBWEBM_TESTDATA_DIR. +std::string GetTestFilePath(const std::string& name); + +// Byte-wise comparison of two files |file1| and |file2|. Returns true if the +// files match exactly, false otherwise. +bool CompareFiles(const std::string& file1, const std::string& file2); + +// Returns true and sets |cues_offset| to the cues location within the MKV file +// parsed by |segment| when the MKV file has cue points. +bool HasCuePoints(const mkvparser::Segment* segment, std::int64_t* cues_offset); + +// Validates cue points. Assumes caller has already called Load() on |segment|. +// Returns true when: +// All cue points point at clusters, OR +// Data parsed by |segment| has no cue points. +bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader); + +// Parses |webm_file| using mkvparser and returns true when file parses +// successfully (all clusters and blocks can be successfully walked). Second +// variant allows further interaction with the parsed file via transferring +// ownership of the mkvparser Segment and MkvReader to the caller via +// |parser_out|. +struct MkvParser { + MkvParser() = default; + ~MkvParser(); + mkvparser::Segment* segment = nullptr; + mkvparser::MkvReader* reader = nullptr; +}; +bool ParseMkvFile(const std::string& webm_file); +bool ParseMkvFileReleaseParser(const std::string& webm_file, + MkvParser* parser_out); + +} // namespace test + +#endif // LIBWEBM_TESTING_TEST_UTIL_H_ \ No newline at end of file diff --git a/testing/testdata/accurate_cluster_duration.webm b/testing/testdata/accurate_cluster_duration.webm new file mode 100644 index 0000000000000000000000000000000000000000..fffe83d7d6dc99a35e159619f07a88f86018391a GIT binary patch literal 376 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~YMh`*X*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CW4 z0}Kql4U==T%W_LAQj6kC^D;}~OHzwVio+eTiGtiTXWgvpjf}-d8yTA$8QWUJ;==+g z9<*<0VszNmxCP`)h|d@#o;NZqn7pvB5yh>u8<~*QXg_acT8yS*!8DK_CL~?I8<|kG L2|sUSL)QiXqj_gR literal 0 HcmV?d00001 diff --git a/testing/testdata/accurate_cluster_duration_last_frame.webm b/testing/testdata/accurate_cluster_duration_last_frame.webm new file mode 100644 index 0000000000000000000000000000000000000000..8104bfcc8a8430f2edba000c83782e600b530810 GIT binary patch literal 384 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~Y#sERz*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CVP zpgFz`lXJ7na!V^xi{eZ3GE3r1Qj1H9!yU1Sg4{G`-K^`4jKxPA8Jim!+giio!vZWG zv~Or)blBCn1>{VK&ln`0H!>`kys)nk#jUd&nUK_IKW}7OjHY72G>{%9BwfE7nNYP! SKW}7%C__@k@Vk)(s0;vbpK4?P literal 0 HcmV?d00001 diff --git a/testing/testdata/accurate_cluster_duration_two_tracks.webm b/testing/testdata/accurate_cluster_duration_two_tracks.webm new file mode 100644 index 0000000000000000000000000000000000000000..cf6985910621cc87d27b61d22334342593784e9c GIT binary patch literal 533 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~Y#;1b5vrAU_ z?(Pm=-6HC_GA(#bV;+Pj7P2lI$Xg5H$vbZ=1@hj$*$dW3E&;M)m)7--&HPRdE}bn- z3m6!D8z$#wm*tjLq!z`O=4F<|m!uY#6o)%v69u_x_qtiv8ySm_HZnFhGPbpb#fJr0 zJZRt0#OScAam%{7*BhCDa!k#QOl@tB@%{m!!4EsPwm5!ZV3^;?1o8#M;|vAQ8yOb& zH8C(WApEkpuMv&k#K46j-^3t-#9uIZA?3w literal 0 HcmV?d00001 diff --git a/testing/testdata/bbb_480p_vp9_opus_1second.webm b/testing/testdata/bbb_480p_vp9_opus_1second.webm new file mode 100644 index 0000000000000000000000000000000000000000..c3657a74df59e00c813e923a0fa6a243737c8932 GIT binary patch literal 45863 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 X8Q16!*&6xe4}22`pw!2|l literal 0 HcmV?d00001 diff --git a/testing/testdata/block_with_additional.webm b/testing/testdata/block_with_additional.webm new file mode 100644 index 0000000000000000000000000000000000000000..2f1b22cc9169f5c437af71aee0ae5f7fcbb527e5 GIT binary patch literal 348 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~YMjb)l*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CUk zpgFz`lXJ7na!V^xi{eZ3GE3r1Qj1H9!yU1Sg4{G`-K^`4jKxPA8Jim!+giio!vZWG zv~Or)blBCn1>{VK&lrN9H!>_(vaqia#jT|aCob!M*T}fE3n9E4PMj4$o*y=*chS- zkQKYMu5WDScXDv)Y_V@(VDN32oSR*iTUwD?6knQ`SrT88T3k{b?ubnk-0HAZPAy-MMw!=CF`XglR__87DR}Olo3&(J-qO kA?{q$o|>1QlUbbMT-Q`u4DuBuSQvz#H!>{lYeexE0O-zk{Qv*} literal 0 HcmV?d00001 diff --git a/testing/testdata/colour.webm b/testing/testdata/colour.webm new file mode 100644 index 0000000000000000000000000000000000000000..580cb50aad5e262b9a80ffd04199c9760e7cd629 GIT binary patch literal 416 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~Y#sWd#*(Ixe zcXtP`ZV~ldnHIdJF%QBM3t5*96{)TRBWC-2V$QZi0ktuXbBXj81MwZZRjjW;Dfnqy= zcqdSNS0iWW?nbWAJ&oLy4YfS=FHj9OD%RjKJ literal 0 HcmV?d00001 diff --git a/testing/testdata/cues_before_clusters.webm b/testing/testdata/cues_before_clusters.webm new file mode 100644 index 0000000000000000000000000000000000000000..c634b179d97144cae2c776ae343a2db5911d13c9 GIT binary patch literal 380 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~YMjt`n*(E!C zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CVz zm)XmF8z$#wm*tjLq!z`O=4F<|m!uY#6o)%v69u_x{<^u>8<~oaHZnCgGPSii#`_0^ z20!fF+T!?ufnk0l6Ue0y&oOvBZ)8|7ePLe{igQaBwJ;nm7g#U_Q=+jRt7roNjM!yI literal 0 HcmV?d00001 diff --git a/testing/testdata/estimate_duration.webm b/testing/testdata/estimate_duration.webm new file mode 100644 index 0000000000000000000000000000000000000000..c7547414d478801c213e036c825215b064d30f3a GIT binary patch literal 365 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~YMq5GO*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CVP zoik^B8z$#wm*tjLq!z`O=4F<|m!uY#6o)%v69u_x&bnFG8ySm_HZnFhGPbpb#fJr0 vJZRt0#OScAaSO;>y1mjGF@OY8c^W_~9Jm(CW4 z1_lP-hRM0vWx1slsYUUnd6^~gC8@;>y1mjGF@OY8c^W_~9Jm(CW4 z0}Kql4U==T%W_LAQj6kC^D;}~OHzwVio+eTiGtiTXWgvpjf}-d8yTA$8QWUJ;==+g x9<*<0VszNmxCP`)h|d^=pEoir?rTJGDU!7E^F}6gX%L?YNduB{plNLA$^nK3VF&;K literal 0 HcmV?d00001 diff --git a/testing/testdata/invalid/README.libwebm b/testing/testdata/invalid/README.libwebm new file mode 100644 index 000000000000..f9e9413b0d98 --- /dev/null +++ b/testing/testdata/invalid/README.libwebm @@ -0,0 +1,24 @@ +Why the files in this directory are considered invalid: + +block_ends_beyond_cluster.mkv - + File containing a single cluster with two simple blocks. One valid, and the + second reporting a size that would cause the block to end far beyond the end + of its parent cluster. + +blockgroup_block_ends_beyond_blockgroup.mkv - + File containing a single cluster and two blockgroups. The first blockgroup is + valid. The second blockgroup contains a block reporting a size that spans well + past the block and the end of the file. + +chapters_truncated_chapter_string.mkv - + File with a Chapters element that ends with a ChapterAtom whose ChapterDisplay + element contains a truncated ChapterString. + +chapters_truncated_chapter_string_2.mkv - + Nearly identical to chapters_truncated_chapter_string.mkv, but with a void + element and a partial cluster. Causes mkvparser to fail in a slightly + different manner. + +fixed_lacing_bad_lace_size.mkv - + File containing a BlockGroup with fixed lacing, but reports a total laced size + that is not evenly divisible by the number of laced frames. diff --git a/testing/testdata/invalid/block_ends_beyond_cluster.mkv b/testing/testdata/invalid/block_ends_beyond_cluster.mkv new file mode 100644 index 0000000000000000000000000000000000000000..3035c8c8235d8eb32053f6d0b6f18c43701b4004 GIT binary patch literal 10488 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 frWD;QZQFktNT((ies%kUS=$EV!+ zT>G3R9caG9$+9WXnT}Fz)nF^Mtcya>Xh&(gj%^IswDBQ9HZeX*DQs%jwR}h*A;$0G zj@p(si9g0b@?GNheSg2_ectCD-<^MVABz9x;NIByw5_^1CqJyl?(*2!q{psF6o*)b zVP+Pton6)QfcxW~6ElfNORKvdNhM~-uWPCdKiEGv><{OAPJ98fJ}@y`n)$`b<(0tN z#0kikW`6#{jg<-4bsWyZgzE<`^v(kN|Jb(n?es+6F42fL9B4T3?l=&CcW4`|4F}#c z2iCo3D>oKyIM8sQ;XuQIh64=;8V>wt4*1Xg(Ze?_d~0H5EZS2%Xqg(-YzvKGPUd8E zGlIZduMqrTD~CVT{**mDV$<9ZMI!_v1a025-E>`xqSzR!^frAIX$8}Ak&{84R7ZRm z@fe2@CJMm{FLd5ry7j_i(N1+b@ZeE#6J5^S@yO>bXM`eEcMGazjpe^~@AO|+eR<8( zU3Z)=4HiysIT-ZcO2iJ#MfWUtZ2tSDQ{89RPk8KckDd0|fg>Jsz+>z`dh7<$Te_vl zFasAoW@^ulWKUmwo7mEw=xu4=(Q(9M^@qQ~8jSGIGt3P`(9Vj#i+=n4=_tcAG4Qec z;IqrM+gWBw`r^s?4QD4x0qrx)`lAOX3K`?KUz#5e{N%gif$04n(?37Hgz5JOb1yD< z0T8t8@9XwYc!6p`EC^z11WmRl+dBl1;d8UEb}{SQ4@}U^(3!iBmbPsvGGFnQ&b2?j z;57l^z1;mzOn9rR3H>9iC7ezqcl5>*1F^+JYridT?xqToc|0n~;h>WnQVj-(xOxzSHFwc&B6|*KnZW!2iDk{+sZQpT&=Nycj>_G4Wr*|H;y! z6+isJvBUXiN;8KpeWdI$`ysmDV~h7q&S0!8{&8?0>iDexOK|K_0Iodp{6Kkf{P?nU z#X4}C!V?beQ&r$mt^23t$>$cz@2g`bq9PCuetEBeXMy?9U&5EmQ!L(m*Y$POJW<3j zC;Xe*M?bewv;e(ldu?G%2Q3(uD!gi zSj#O*iqyuDnxi~<=tsW>^zMy!4%MKEf(1ns>d@Z|qz3{0@Z6F6>d|3EkU74F`^@?N z-vjz@uidh?MmJPKyduh6xWtvChGlF2!FjPc6llrM^0DNl_~w^BH2A{m8YO@xihwz{<|x}9`15ReYT1fkec%&S+%XJjIIF@} zxi6giuiTqH+;OaMF^i^LzX+FLoOa!;($cbP*Dh|v zG^k^#84fDN7N3zzyYK+ejGU}%IvJ>zbX8OJ2thfTib%+yn&s%j5x6uIjMR(@r8cdhlK>@9kWtGr z^MGg_Z8dZkLi$b{h6YuQ-A;x^coHHItjyV&O;##TK;$h`r+Ju=$t~h_6CPT8PFsUQ z5<*%kr`muJke1AjLEkhWt0h%K&H4*zNzGLKr42Jj7nk0Z%~3=1Q&8hVVwjGZ88-sg R_LXkZHZw3pd7Wmc{xAEtW+eas literal 0 HcmV?d00001 diff --git a/testing/testdata/invalid/fixed_lacing_bad_lace_size.mkv b/testing/testdata/invalid/fixed_lacing_bad_lace_size.mkv new file mode 100644 index 0000000000000000000000000000000000000000..bbe2025cb67798030c68fd5ec8094476a717aa64 GIT binary patch literal 47806 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 literal 0 HcmV?d00001 diff --git a/testing/testdata/invalid/invalid_vp9_bitstream-bug_1416.webm b/testing/testdata/invalid/invalid_vp9_bitstream-bug_1416.webm new file mode 100644 index 0000000000000000000000000000000000000000..ac76dce86be63a13600d01d598f3dfbf563acfa7 GIT binary patch literal 12847 zcmeIzF-yZh6u|MO-#|erF8u-yZ*_5K+QC5(EO8VSbZ{*0eN8Pcg1dslxwsS^bnqMa z3F4+sE`ErYZ$JGm zz4zbC%GUj{&$nZ_##XoGvXu4SRKF)zrTf-t%SAD?+8v+zfRAH;v6*PlGrJqD*|cE|MbC@ZdIOsQaiL1 zj^sLD@@v}+pU?88B3~-5-M1Vl2q1vKp9r`_tJ&9u>wMd)p(X0#3Aol9ugl^+R!$`1 z!bU~^7<3VsQ-Rks@2r{A1CTZX2q1s}0tg_000IagfB*srAbGe|^iJb=w~A(aqU&_W0(rc9?r%Ef-)u8L(2V7F-mBUTpPASXzf zbZI<bJ*)lqxa+OQ zZr*#HHPO|VMLzX&aS7!u&z$!_mc^qw+`hUmx?6W?eHlMgt(Eb#E&FvbAH9s89unj0 z!?OLj-pVc(IT#sezeY6Fv(es9^Kw&ujiev8l$9@ubaPF68id!hUorqa2q1t!slaEJ zwOeVKBmxK^fB*srAb5M-?O4)3E zcSAJ^1+Q)q^<0@&TAUi6oLgXNU}`BAvMw7cYqJi@FuvZ%SbVhQ^t*z^&5ew0tzq$D v0hSM-l42VgId(P9E-PSUU}OQAUsl|aniq4w^W6Xc3``6R2_Oq*FfafBm1;6r literal 0 HcmV?d00001 diff --git a/testing/testdata/long_tag_string.webm b/testing/testdata/long_tag_string.webm new file mode 100644 index 0000000000000000000000000000000000000000..e8d665300a45b8d7d162586df521105ef3739a56 GIT binary patch literal 474 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~Y##Ms8vrBgR z?(Pm=-6HC_GA(#bV;+Pj7P2lI$Xg5H356U^2lCGO?(PAqly}}%8oZ{7vHQ(BurWjv zAS-rhUEkQu@8sap*<#+!327$W3$B&AQ&m zSbVgRvAL14tu-t@EWqMH`-Ub)hh2?ZK+bg7UR><3IsJsgde_ArIr(|%@g<4rF6|Br X3g(@Uk7=)iUGA!BoW+@&d2};{{?+# zm#p&L-5tETMbvX;TJW02JP1!LWL-9pw-&;acivVCW8cUGD$@ literal 0 HcmV?d00001 diff --git a/testing/testdata/max_cluster_duration.webm b/testing/testdata/max_cluster_duration.webm new file mode 100644 index 0000000000000000000000000000000000000000..9caad9c1bd578d86ab6219b099959a0c9449b42b GIT binary patch literal 395 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~Y#%Mv`*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CVP z0R{%&hRM0vWx1slsYUUnd6^~gC8@;>y1mjGF@OY8c^W_~9Jm(CVP z0R{%&hRM0vWx1slsYUUnd6^~gC8@E-Y3Zj@G6bl0bk{adbjckkiz-r(=0`Y;0;Q&dw K@bgAabmaiqEn~R= literal 0 HcmV?d00001 diff --git a/testing/testdata/metadata_block.webm b/testing/testdata/metadata_block.webm new file mode 100644 index 0000000000000000000000000000000000000000..a9c686e00176aaa08629a07d34359515a98f7e89 GIT binary patch literal 314 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~YMlM0$*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CVP zpgFz`lXJ7na!V^xi{eZ3GE3r1Qj1H9!yU1Sg4{HJ-Q4Sq%*96=nVTCG+a|iihr2q3 sg@ow)x`sHqIEFZaTnh0VgYNT2h6R%s_BEq8cXlHaM34zdcy=Qj0I^+K761SM literal 0 HcmV?d00001 diff --git a/testing/testdata/output_cues.webm b/testing/testdata/output_cues.webm new file mode 100644 index 0000000000000000000000000000000000000000..49e2126139be5f2aded6a0e960ddfdb87658370f GIT binary patch literal 394 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~Y#wbDG*(E!D zcXtP`ZV~ldnHIdJF%QBM3t5*9Fn-?1u(+?0fuRB6IuM@;!3E2+An}p3 s3qNmUL)Q-S_R8Jen;RLnw*f=gO^m!C9t)fY0DEeDrvLx| literal 0 HcmV?d00001 diff --git a/testing/testdata/projection.webm b/testing/testdata/projection.webm new file mode 100644 index 0000000000000000000000000000000000000000..0f13e8a6573b2494fb38220563b305a560593891 GIT binary patch literal 317 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJh~YMqWYR*(Ixd zcXtP`ZV~ldnHIdJF%QBM3t5*9;>y1mjGF@OY8c^W_~9Jm(CXZ z1_lP-hRM0vWx1slsYUUnd6^~gC8@;>y1mjGF@OY8c^W_~9Jm(CVP zpgFz`lXJ7na!V^xi{eZ3GE3r1Qj1H9!yU1Sg4{G`-K^`4jKxPA8Jim!+giio!vZWG zv~Or)blBCn1>{VK<R)H!>{lYeaD=h|h#10^+kE@fS>62ozyM68YW8GP{up0G9M# A9RL6T literal 0 HcmV?d00001 diff --git a/testing/testdata/segment_info.webm b/testing/testdata/segment_info.webm new file mode 100644 index 0000000000000000000000000000000000000000..0fde8f0687bfb5ceba15cf0255ce4bbf64a14e94 GIT binary patch literal 284 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJi0_H-f&iOIG>r z?hanvBI>y^EqG019)u?rvMw9QTLaba`W|!rbR-_iim*!=b#FwNNmlTIPViN_qY0kP?*BiNt zk2W$kH!`-hhQ)^kSUhOo(8TDlt8oj+nGl~b7(Z`hSlrjez|eqj9f;3_;DY5@koc(D o*@)53jHDeDEVFlaZ*FAR-uAtb>to}&-94Kd*|xWXI8BVa0R0AeKL7v# literal 0 HcmV?d00001 diff --git a/testing/testdata/set_pixelwidth_pixelheight.webm b/testing/testdata/set_pixelwidth_pixelheight.webm new file mode 100644 index 0000000000000000000000000000000000000000..e81c8bcd4f22f3b2844aaceb84929b1c34f5455b GIT binary patch literal 302 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJi0_e}cZVOIG>r z?hanvBI>y^EqG019)u?rvMw9QTMOaIJ8vrm^3K263(-d=39@3B*7c3e{7w!ooh=SP zV|*JX=Vq7XmR6(|#h2z~mc*B&7MB!0JMWx9RL6T literal 0 HcmV?d00001 diff --git a/testing/testdata/set_segment_duration.webm b/testing/testdata/set_segment_duration.webm new file mode 100644 index 0000000000000000000000000000000000000000..100684bfe66394ce10e356d19c83c629088c11b2 GIT binary patch literal 301 zcmb1gy}x+AQ(GgW({~{L)X3uWxsk)EsiizMDc7kT$Zc(8k_c`{XJi0_zkr z?hanvBI>y^EqG019)u?rvMw9QTMOaIJ8vrm^3J{43(-d=39@3B*7c3e{7w!ooh@!E z0t~(llXJ7na!V^xi{eZ3GE3r1Qj1H9!yU1Sg4{G`-K^`4jKxPA8Jim!+giio!vZWG gv~Or)blBCn1>{Vq&y=4xGA!R literal 0 HcmV?d00001 diff --git a/testing/testdata/simple_block.webm b/testing/testdata/simple_block.webm new file mode 100644 index 0000000000000000000000000000000000000000..c311e681162cb971db6fad8b79e703a03c7114d4 GIT binary patch literal 301 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 qe~^=BFtXlr z?hanvBI>y^EqG019)u?rvMw9QTMOaIJ8vrm^3J{43(-d=39@3B*7c3e{7w!ooh|ka z3=F;vlXJ7na!V^xi{eZ3GE3r1Qj1H9!yU1Sg4{G`-K^`4jKxPA8Jim!+giio!vZWH bv~Or)blBCn1>{Vq&xD^hGA!(frame.buffer().data.get()); + *write_ptr = 0xFF; + EXPECT_TRUE(frame.SetBufferLength(1)); + EXPECT_EQ(frame.buffer().length, frame.buffer().capacity); +} + +TEST(VideoFrameTests, OverloadsTest) { + const bool kKeyframe = true; + + // Test VideoFrame::VideoFrame(bool keyframe, int64_t nano_pts, Codec c). + libwebm::VideoFrame keyframe(kKeyframe, kPts, kCodec); + EXPECT_EQ(kKeyframe, keyframe.keyframe()); + EXPECT_EQ(kPts, keyframe.nanosecond_pts()); + EXPECT_EQ(kCodec, keyframe.codec()); + EXPECT_EQ(kEmptySize, keyframe.buffer().capacity); + EXPECT_EQ(kEmptySize, keyframe.buffer().length); + EXPECT_EQ(nullptr, keyframe.buffer().data.get()); + + // Test VideoFrame::Init(std::size_t length). + EXPECT_TRUE(keyframe.Init(kSize)); + EXPECT_EQ(kKeyframe, keyframe.keyframe()); + EXPECT_EQ(kPts, keyframe.nanosecond_pts()); + EXPECT_EQ(kCodec, keyframe.codec()); + EXPECT_NE(nullptr, keyframe.buffer().data.get()); + + // Test VideoFrame::Init(size_t length, int64_t nano_pts, Codec c). + EXPECT_TRUE(keyframe.Init(kSize, kPts + 1, libwebm::VideoFrame::kVP9)); + EXPECT_EQ(kSize, keyframe.buffer().capacity); + EXPECT_GT(kSize, keyframe.buffer().length); + EXPECT_NE(kPts, keyframe.nanosecond_pts()); + EXPECT_NE(kCodec, keyframe.codec()); +} + +} // namespace diff --git a/vttdemux.cc b/vttdemux.cc new file mode 100644 index 000000000000..186783b08fb7 --- /dev/null +++ b/vttdemux.cc @@ -0,0 +1,1004 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include +#include +#include +#include +#include +#include +#include + +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" +#include "webvtt/webvttparser.h" + +using std::string; + +namespace libwebm { +namespace vttdemux { + +typedef long long mkvtime_t; // NOLINT +typedef long long mkvpos_t; // NOLINT +typedef std::unique_ptr segment_ptr_t; + +// WebVTT metadata tracks have a type (encoded in the CodecID for the track). +// We use |type| to synthesize a filename for the out-of-band WebVTT |file|. +struct MetadataInfo { + enum Type { kSubtitles, kCaptions, kDescriptions, kMetadata, kChapters } type; + FILE* file; +}; + +// We use a map, indexed by track number, to collect information about +// each track in the input file. +typedef std::map metadata_map_t; // NOLINT + +// The distinguished key value we use to store the chapters +// information in the metadata map. +enum { kChaptersKey = 0 }; + +// The data from the original WebVTT Cue is stored as a WebM block. +// The FrameParser is used to parse the lines of text out from the +// block, in order to reconstruct the original WebVTT Cue. +class FrameParser : public libwebvtt::LineReader { + public: + // Bind the FrameParser instance to a WebM block. + explicit FrameParser(const mkvparser::BlockGroup* block_group); + virtual ~FrameParser(); + + // The Webm block (group) to which this instance is bound. We + // treat the payload of the block as a stream of characters. + const mkvparser::BlockGroup* const block_group_; + + protected: + // Read the next character from the character stream (the payload + // of the WebM block). We increment the stream pointer |pos_| as + // each character from the stream is consumed. + virtual int GetChar(char* c); + + // End-of-line handling requires that we put a character back into + // the stream. Here we need only decrement the stream pointer |pos_| + // to unconsume the character. + virtual void UngetChar(char c); + + // The current position in the character stream (the payload of the block). + mkvpos_t pos_; + + // The position of the end of the character stream. When the current + // position |pos_| equals the end position |pos_end_|, the entire + // stream (block payload) has been consumed and end-of-stream is indicated. + mkvpos_t pos_end_; + + private: + // Disable copy ctor and copy assign + FrameParser(const FrameParser&); + FrameParser& operator=(const FrameParser&); +}; + +// The data from the original WebVTT Cue is stored as an MKV Chapters +// Atom element (the cue payload is stored as a Display sub-element). +// The ChapterAtomParser is used to parse the lines of text out from +// the String sub-element of the Display element (though it would be +// admittedly odd if there were more than one line). +class ChapterAtomParser : public libwebvtt::LineReader { + public: + explicit ChapterAtomParser(const mkvparser::Chapters::Display* display); + virtual ~ChapterAtomParser(); + + const mkvparser::Chapters::Display* const display_; + + protected: + // Read the next character from the character stream (the title + // member of the atom's display). We increment the stream pointer + // |str_| as each character from the stream is consumed. + virtual int GetChar(char* c); + + // End-of-line handling requires that we put a character back into + // the stream. Here we need only decrement the stream pointer |str_| + // to unconsume the character. + virtual void UngetChar(char c); + + // The current position in the character stream (the title of the + // atom's display). + const char* str_; + + // The position of the end of the character stream. When the current + // position |str_| equals the end position |str_end_|, the entire + // stream (title of the display) has been consumed and end-of-stream + // is indicated. + const char* str_end_; + + private: + ChapterAtomParser(const ChapterAtomParser&); + ChapterAtomParser& operator=(const ChapterAtomParser&); +}; + +// Parse the EBML header of the WebM input file, to determine whether we +// actually have a WebM file. Returns false if this is not a WebM file. +bool ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos); + +// Parse the Segment of the input file and load all of its clusters. +// Returns false if there was an error parsing the file. +bool ParseSegment(mkvparser::IMkvReader* reader, mkvpos_t pos, + segment_ptr_t* segment); + +// If |segment| has a Chapters element (in which case, there will be a +// corresponding entry in |metadata_map|), convert the MKV chapters to +// WebVTT chapter cues and write them to the output file. Returns +// false on error. +bool WriteChaptersFile(const metadata_map_t& metadata_map, + const mkvparser::Segment* segment); + +// Convert an MKV Chapters Atom to a WebVTT cue and write it to the +// output |file|. Returns false on error. +bool WriteChaptersCue(FILE* file, const mkvparser::Chapters* chapters, + const mkvparser::Chapters::Atom* atom, + const mkvparser::Chapters::Display* display); + +// Write the Cue Identifier line of the WebVTT cue, if it's present. +// Returns false on error. +bool WriteChaptersCueIdentifier(FILE* file, + const mkvparser::Chapters::Atom* atom); + +// Use the timecodes from the chapters |atom| to write just the +// timings line of the WebVTT cue. Returns false on error. +bool WriteChaptersCueTimings(FILE* file, const mkvparser::Chapters* chapters, + const mkvparser::Chapters::Atom* atom); + +// Parse the String sub-element of the |display| and write the payload +// of the WebVTT cue. Returns false on error. +bool WriteChaptersCuePayload(FILE* file, + const mkvparser::Chapters::Display* display); + +// Iterate over the tracks of the input file (and any chapters +// element) and cache information about each metadata track. +void BuildMap(const mkvparser::Segment* segment, metadata_map_t* metadata_map); + +// For each track listed in the cache, synthesize its output filename +// and open a file handle that designates the out-of-band file. +// Returns false if we were unable to open an output file for a track. +bool OpenFiles(metadata_map_t* metadata_map, const char* filename); + +// Close the file handle for each track in the cache. +void CloseFiles(metadata_map_t* metadata_map); + +// Iterate over the clusters of the input file, and write a WebVTT cue +// for each metadata block. Returns false if processing of a cluster +// failed. +bool WriteFiles(const metadata_map_t& m, mkvparser::Segment* s); + +// Write the WebVTT header for each track in the cache. We do this +// immediately before writing the actual WebVTT cues. Returns false +// if the write failed. +bool InitializeFiles(const metadata_map_t& metadata_map); + +// Iterate over the blocks of the |cluster|, writing a WebVTT cue to +// its associated output file for each block of metadata. Returns +// false if processing a block failed, or there was a parse error. +bool ProcessCluster(const metadata_map_t& metadata_map, + const mkvparser::Cluster* cluster); + +// Look up this track number in the cache, and if found (meaning this +// is a metadata track), write a WebVTT cue to the associated output +// file. Returns false if writing the WebVTT cue failed. +bool ProcessBlockEntry(const metadata_map_t& metadata_map, + const mkvparser::BlockEntry* block_entry); + +// Parse the lines of text from the |block_group| to reconstruct the +// original WebVTT cue, and write it to the associated output |file|. +// Returns false if there was an error writing to the output file. +bool WriteCue(FILE* file, const mkvparser::BlockGroup* block_group); + +// Consume a line of text from the character stream, and if the line +// is not empty write the cue identifier to the associated output +// file. Returns false if there was an error writing to the file. +bool WriteCueIdentifier(FILE* f, FrameParser* parser); + +// Consume a line of text from the character stream (which holds any +// cue settings) and write the cue timings line for this cue to the +// associated output file. Returns false if there was an error +// writing to the file. +bool WriteCueTimings(FILE* f, FrameParser* parser); + +// Write the timestamp (representating either the start time or stop +// time of the cue) to the output file. Returns false if there was an +// error writing to the file. +bool WriteCueTime(FILE* f, mkvtime_t time_ns); + +// Consume the remaining lines of text from the character stream +// (these lines are the actual payload of the WebVTT cue), and write +// them to the associated output file. Returns false if there was an +// error writing to the file. +bool WriteCuePayload(FILE* f, FrameParser* parser); +} // namespace vttdemux + +namespace vttdemux { + +FrameParser::FrameParser(const mkvparser::BlockGroup* block_group) + : block_group_(block_group) { + const mkvparser::Block* const block = block_group->GetBlock(); + const mkvparser::Block::Frame& f = block->GetFrame(0); + + // The beginning and end of the character stream corresponds to the + // position of this block's frame within the WebM input file. + + pos_ = f.pos; + pos_end_ = f.pos + f.len; +} + +FrameParser::~FrameParser() {} + +int FrameParser::GetChar(char* c) { + if (pos_ >= pos_end_) // end-of-stream + return 1; // per the semantics of libwebvtt::Reader::GetChar + + const mkvparser::Cluster* const cluster = block_group_->GetCluster(); + const mkvparser::Segment* const segment = cluster->m_pSegment; + mkvparser::IMkvReader* const reader = segment->m_pReader; + + unsigned char* const buf = reinterpret_cast(c); + const int result = reader->Read(pos_, 1, buf); + + if (result < 0) // error + return -1; + + ++pos_; // consume this character in the stream + return 0; +} + +void FrameParser::UngetChar(char /* c */) { + // All we need to do here is decrement the position in the stream. + // The next time GetChar is called the same character will be + // re-read from the input file. + --pos_; +} + +ChapterAtomParser::ChapterAtomParser( + const mkvparser::Chapters::Display* display) + : display_(display) { + str_ = display->GetString(); + if (str_ == NULL) + return; + const size_t len = strlen(str_); + str_end_ = str_ + len; +} + +ChapterAtomParser::~ChapterAtomParser() {} + +int ChapterAtomParser::GetChar(char* c) { + if (str_ == NULL || str_ >= str_end_) // end-of-stream + return 1; // per the semantics of libwebvtt::Reader::GetChar + + *c = *str_++; // consume this character in the stream + return 0; +} + +void ChapterAtomParser::UngetChar(char /* c */) { + // All we need to do here is decrement the position in the stream. + // The next time GetChar is called the same character will be + // re-read from the input file. + --str_; +} + +} // namespace vttdemux + +bool vttdemux::ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos) { + mkvparser::EBMLHeader h; + const mkvpos_t status = h.Parse(reader, *pos); + + if (status) { + printf("error parsing EBML header\n"); + return false; + } + + if (h.m_docType == NULL || strcmp(h.m_docType, "webm") != 0) { + printf("bad doctype\n"); + return false; + } + + return true; // success +} + +bool vttdemux::ParseSegment(mkvparser::IMkvReader* reader, mkvpos_t pos, + segment_ptr_t* segment_ptr) { + // We first create the segment object. + + mkvparser::Segment* p; + const mkvpos_t create = mkvparser::Segment::CreateInstance(reader, pos, p); + + if (create) { + printf("error parsing segment element\n"); + return false; + } + + segment_ptr->reset(p); + + // Now parse all of the segment's sub-elements, in toto. + + const long status = p->Load(); // NOLINT + + if (status < 0) { + printf("error loading segment\n"); + return false; + } + + return true; +} + +void vttdemux::BuildMap(const mkvparser::Segment* segment, + metadata_map_t* map_ptr) { + metadata_map_t& m = *map_ptr; + m.clear(); + + if (segment->GetChapters()) { + MetadataInfo info; + info.file = NULL; + info.type = MetadataInfo::kChapters; + + m[kChaptersKey] = info; + } + + const mkvparser::Tracks* const tt = segment->GetTracks(); + if (tt == NULL) + return; + + const long tc = tt->GetTracksCount(); // NOLINT + if (tc <= 0) + return; + + // Iterate over the tracks in the intput file. We determine whether + // a track holds metadata by inspecting its CodecID. + + for (long idx = 0; idx < tc; ++idx) { // NOLINT + const mkvparser::Track* const t = tt->GetTrackByIndex(idx); + + if (t == NULL) // weird + continue; + + const long tn = t->GetNumber(); // NOLINT + + if (tn <= 0) // weird + continue; + + const char* const codec_id = t->GetCodecId(); + + if (codec_id == NULL) // weird + continue; + + MetadataInfo info; + info.file = NULL; + + if (strcmp(codec_id, "D_WEBVTT/SUBTITLES") == 0) { + info.type = MetadataInfo::kSubtitles; + } else if (strcmp(codec_id, "D_WEBVTT/CAPTIONS") == 0) { + info.type = MetadataInfo::kCaptions; + } else if (strcmp(codec_id, "D_WEBVTT/DESCRIPTIONS") == 0) { + info.type = MetadataInfo::kDescriptions; + } else if (strcmp(codec_id, "D_WEBVTT/METADATA") == 0) { + info.type = MetadataInfo::kMetadata; + } else { + continue; + } + + m[tn] = info; // create an entry in the cache for this track + } +} + +bool vttdemux::OpenFiles(metadata_map_t* metadata_map, const char* filename) { + if (metadata_map == NULL || metadata_map->empty()) + return false; + + if (filename == NULL) + return false; + + // Find the position of the filename extension. We synthesize the + // output filename from the directory path and basename of the input + // filename. + + const char* const ext = strrchr(filename, '.'); + + if (ext == NULL) // TODO(matthewjheaney): liberalize? + return false; + + // Remember whether a track of this type has already been seen (the + // map key) by keeping a count (the map item). We quality the + // output filename with the track number if there is more than one + // track having a given type. + + std::map exists; + + typedef metadata_map_t::iterator iter_t; + + metadata_map_t& m = *metadata_map; + const iter_t ii = m.begin(); + const iter_t j = m.end(); + + // Make a first pass over the cache to determine whether there is + // more than one track corresponding to a given metadata type. + + iter_t i = ii; + while (i != j) { + const metadata_map_t::value_type& v = *i++; + const MetadataInfo& info = v.second; + const MetadataInfo::Type type = info.type; + ++exists[type]; + } + + // Make a second pass over the cache, synthesizing the filename of + // each output file (from the input file basename, the input track + // metadata type, and its track number if necessary), and then + // opening a WebVTT output file having that filename. + + i = ii; + while (i != j) { + metadata_map_t::value_type& v = *i++; + MetadataInfo& info = v.second; + const MetadataInfo::Type type = info.type; + + // Start with the basename of the input file. + + string name(filename, ext); + + // Next append the metadata kind. + + switch (type) { + case MetadataInfo::kSubtitles: + name += "_SUBTITLES"; + break; + + case MetadataInfo::kCaptions: + name += "_CAPTIONS"; + break; + + case MetadataInfo::kDescriptions: + name += "_DESCRIPTIONS"; + break; + + case MetadataInfo::kMetadata: + name += "_METADATA"; + break; + + case MetadataInfo::kChapters: + name += "_CHAPTERS"; + break; + + default: + return false; + } + + // If there is more than one metadata track having a given type + // (the WebVTT-in-WebM spec doesn't preclude this), then qualify + // the output filename with the input track number. + + if (exists[type] > 1) { + enum { kLen = 33 }; + char str[kLen]; // max 126 tracks, so only 4 chars really needed +#ifndef _MSC_VER + snprintf(str, kLen, "%ld", v.first); // track number +#else + _snprintf_s(str, sizeof(str), kLen, "%ld", v.first); // track number +#endif + name += str; + } + + // Finally append the output filename extension. + + name += ".vtt"; + + // We have synthesized the full output filename, so attempt to + // open the WebVTT output file. + + info.file = fopen(name.c_str(), "wb"); + const bool success = (info.file != NULL); + + if (!success) { + printf("unable to open output file %s\n", name.c_str()); + return false; + } + } + + return true; +} + +void vttdemux::CloseFiles(metadata_map_t* metadata_map) { + if (metadata_map == NULL) + return; + + metadata_map_t& m = *metadata_map; + + typedef metadata_map_t::iterator iter_t; + + iter_t i = m.begin(); + const iter_t j = m.end(); + + // Gracefully close each output file, to ensure all output gets + // propertly flushed. + + while (i != j) { + metadata_map_t::value_type& v = *i++; + MetadataInfo& info = v.second; + + if (info.file != NULL) { + fclose(info.file); + info.file = NULL; + } + } +} + +bool vttdemux::WriteFiles(const metadata_map_t& m, mkvparser::Segment* s) { + // First write the WebVTT header. + + InitializeFiles(m); + + if (!WriteChaptersFile(m, s)) + return false; + + // Now iterate over the clusters, writing the WebVTT cue as we parse + // each metadata block. + + const mkvparser::Cluster* cluster = s->GetFirst(); + + while (cluster != NULL && !cluster->EOS()) { + if (!ProcessCluster(m, cluster)) + return false; + + cluster = s->GetNext(cluster); + } + + return true; +} + +bool vttdemux::InitializeFiles(const metadata_map_t& m) { + // Write the WebVTT header for each output file in the cache. + + typedef metadata_map_t::const_iterator iter_t; + iter_t i = m.begin(); + const iter_t j = m.end(); + + while (i != j) { + const metadata_map_t::value_type& v = *i++; + const MetadataInfo& info = v.second; + FILE* const f = info.file; + + if (fputs("WEBVTT\n", f) < 0) { + printf("unable to initialize output file\n"); + return false; + } + } + + return true; +} + +bool vttdemux::WriteChaptersFile(const metadata_map_t& m, + const mkvparser::Segment* s) { + const metadata_map_t::const_iterator info_iter = m.find(kChaptersKey); + if (info_iter == m.end()) // no chapters, so nothing to do + return true; + + const mkvparser::Chapters* const chapters = s->GetChapters(); + if (chapters == NULL) // weird + return true; + + const MetadataInfo& info = info_iter->second; + FILE* const file = info.file; + + const int edition_count = chapters->GetEditionCount(); + + if (edition_count <= 0) // weird + return true; // nothing to do + + if (edition_count > 1) { + // TODO(matthewjheaney): figure what to do here + printf("more than one chapter edition detected\n"); + return false; + } + + const mkvparser::Chapters::Edition* const edition = chapters->GetEdition(0); + + const int atom_count = edition->GetAtomCount(); + + for (int idx = 0; idx < atom_count; ++idx) { + const mkvparser::Chapters::Atom* const atom = edition->GetAtom(idx); + const int display_count = atom->GetDisplayCount(); + + if (display_count <= 0) + continue; + + if (display_count > 1) { + // TODO(matthewjheaney): handle case of multiple languages + printf("more than 1 display in atom detected\n"); + return false; + } + + const mkvparser::Chapters::Display* const display = atom->GetDisplay(0); + + if (const char* language = display->GetLanguage()) { + if (strcmp(language, "eng") != 0) { + // TODO(matthewjheaney): handle case of multiple languages. + + // We must create a separate webvtt file for each language. + // This isn't a simple problem (which is why we defer it for + // now), because there's nothing in the header that tells us + // what languages we have as cues. We must parse the displays + // of each atom to determine that. + + // One solution is to make two passes over the input data. + // First parse the displays, creating an in-memory cache of + // all the chapter cues, sorted according to their language. + // After we have read all of the chapter atoms from the input + // file, we can then write separate output files for each + // language. + + printf("only English-language chapter cues are supported\n"); + return false; + } + } + + if (!WriteChaptersCue(file, chapters, atom, display)) + return false; + } + + return true; +} + +bool vttdemux::WriteChaptersCue(FILE* f, const mkvparser::Chapters* chapters, + const mkvparser::Chapters::Atom* atom, + const mkvparser::Chapters::Display* display) { + // We start a new cue by writing a cue separator (an empty line) + // into the stream. + + if (fputc('\n', f) < 0) + return false; + + // A WebVTT Cue comprises 3 things: a cue identifier, followed by + // the cue timings, followed by the payload of the cue. We write + // each part of the cue in sequence. + + if (!WriteChaptersCueIdentifier(f, atom)) + return false; + + if (!WriteChaptersCueTimings(f, chapters, atom)) + return false; + + if (!WriteChaptersCuePayload(f, display)) + return false; + + return true; +} + +bool vttdemux::WriteChaptersCueIdentifier( + FILE* f, const mkvparser::Chapters::Atom* atom) { + const char* const identifier = atom->GetStringUID(); + + if (identifier == NULL) + return true; // nothing else to do + + if (fprintf(f, "%s\n", identifier) < 0) + return false; + + return true; +} + +bool vttdemux::WriteChaptersCueTimings(FILE* f, + const mkvparser::Chapters* chapters, + const mkvparser::Chapters::Atom* atom) { + const mkvtime_t start_ns = atom->GetStartTime(chapters); + + if (start_ns < 0) + return false; + + const mkvtime_t stop_ns = atom->GetStopTime(chapters); + + if (stop_ns < 0) + return false; + + if (!WriteCueTime(f, start_ns)) + return false; + + if (fputs(" --> ", f) < 0) + return false; + + if (!WriteCueTime(f, stop_ns)) + return false; + + if (fputc('\n', f) < 0) + return false; + + return true; +} + +bool vttdemux::WriteChaptersCuePayload( + FILE* f, const mkvparser::Chapters::Display* display) { + // Bind a Chapter parser object to the display, which allows us to + // extract each line of text from the title-part of the display. + ChapterAtomParser parser(display); + + int count = 0; // count of lines of payload text written to output file + for (string line;;) { + const int e = parser.GetLine(&line); + + if (e < 0) // error (only -- we allow EOS here) + return false; + + if (line.empty()) // TODO(matthewjheaney): retain this check? + break; + + if (fprintf(f, "%s\n", line.c_str()) < 0) + return false; + + ++count; + } + + if (count <= 0) // WebVTT cue requires non-empty payload + return false; + + return true; +} + +bool vttdemux::ProcessCluster(const metadata_map_t& m, + const mkvparser::Cluster* c) { + // Visit the blocks in this cluster, writing a WebVTT cue for each + // metadata block. + + const mkvparser::BlockEntry* block_entry; + + long result = c->GetFirst(block_entry); // NOLINT + if (result < 0) { + printf("bad cluster (unable to get first block)\n"); + return false; + } + + while (block_entry != NULL && !block_entry->EOS()) { + if (!ProcessBlockEntry(m, block_entry)) + return false; + + result = c->GetNext(block_entry, block_entry); + if (result < 0) { // error + printf("bad cluster (unable to get next block)\n"); + return false; + } + } + + return true; +} + +bool vttdemux::ProcessBlockEntry(const metadata_map_t& m, + const mkvparser::BlockEntry* block_entry) { + // If the track number for this block is in the cache, then we have + // a metadata block, so write the WebVTT cue to the output file. + + const mkvparser::Block* const block = block_entry->GetBlock(); + const long long tn = block->GetTrackNumber(); // NOLINT + + typedef metadata_map_t::const_iterator iter_t; + const iter_t i = m.find(static_cast(tn)); + + if (i == m.end()) // not a metadata track + return true; // nothing else to do + + if (block_entry->GetKind() != mkvparser::BlockEntry::kBlockGroup) + return false; // weird + + typedef mkvparser::BlockGroup BG; + const BG* const block_group = static_cast(block_entry); + + const MetadataInfo& info = i->second; + FILE* const f = info.file; + + return WriteCue(f, block_group); +} + +bool vttdemux::WriteCue(FILE* f, const mkvparser::BlockGroup* block_group) { + // Bind a FrameParser object to the block, which allows us to + // extract each line of text from the payload of the block. + FrameParser parser(block_group); + + // We start a new cue by writing a cue separator (an empty line) + // into the stream. + + if (fputc('\n', f) < 0) + return false; + + // A WebVTT Cue comprises 3 things: a cue identifier, followed by + // the cue timings, followed by the payload of the cue. We write + // each part of the cue in sequence. + + if (!WriteCueIdentifier(f, &parser)) + return false; + + if (!WriteCueTimings(f, &parser)) + return false; + + if (!WriteCuePayload(f, &parser)) + return false; + + return true; +} + +bool vttdemux::WriteCueIdentifier(FILE* f, FrameParser* parser) { + string line; + int e = parser->GetLine(&line); + + if (e) // error or EOS + return false; + + // If the cue identifier line is empty, this means that the original + // WebVTT cue did not have a cue identifier, so we don't bother + // writing an extra line terminator to the output file (though doing + // so would be harmless). + + if (!line.empty()) { + if (fputs(line.c_str(), f) < 0) + return false; + + if (fputc('\n', f) < 0) + return false; + } + + return true; +} + +bool vttdemux::WriteCueTimings(FILE* f, FrameParser* parser) { + const mkvparser::BlockGroup* const block_group = parser->block_group_; + const mkvparser::Cluster* const cluster = block_group->GetCluster(); + const mkvparser::Block* const block = block_group->GetBlock(); + + // A WebVTT Cue "timings" line comprises two parts: the start and + // stop time for this cue, followed by the (optional) cue settings, + // such as orientation of the rendered text or its size. Only the + // settings part of the cue timings line is stored in the WebM + // block. We reconstruct the start and stop times of the WebVTT cue + // from the timestamp and duration of the WebM block. + + const mkvtime_t start_ns = block->GetTime(cluster); + + if (!WriteCueTime(f, start_ns)) + return false; + + if (fputs(" --> ", f) < 0) + return false; + + const mkvtime_t duration_timecode = block_group->GetDurationTimeCode(); + + if (duration_timecode < 0) + return false; + + const mkvparser::Segment* const segment = cluster->m_pSegment; + const mkvparser::SegmentInfo* const info = segment->GetInfo(); + + if (info == NULL) + return false; + + const mkvtime_t timecode_scale = info->GetTimeCodeScale(); + + if (timecode_scale <= 0) + return false; + + const mkvtime_t duration_ns = duration_timecode * timecode_scale; + const mkvtime_t stop_ns = start_ns + duration_ns; + + if (!WriteCueTime(f, stop_ns)) + return false; + + string line; + int e = parser->GetLine(&line); + + if (e) // error or EOS + return false; + + if (!line.empty()) { + if (fputc(' ', f) < 0) + return false; + + if (fputs(line.c_str(), f) < 0) + return false; + } + + if (fputc('\n', f) < 0) + return false; + + return true; +} + +bool vttdemux::WriteCueTime(FILE* f, mkvtime_t time_ns) { + mkvtime_t ms = time_ns / 1000000; // WebVTT time has millisecond resolution + + mkvtime_t sec = ms / 1000; + ms -= sec * 1000; + + mkvtime_t min = sec / 60; + sec -= 60 * min; + + mkvtime_t hr = min / 60; + min -= 60 * hr; + + if (hr > 0) { + if (fprintf(f, "%02lld:", hr) < 0) + return false; + } + + if (fprintf(f, "%02lld:%02lld.%03lld", min, sec, ms) < 0) + return false; + + return true; +} + +bool vttdemux::WriteCuePayload(FILE* f, FrameParser* parser) { + int count = 0; // count of lines of payload text written to output file + for (string line;;) { + const int e = parser->GetLine(&line); + + if (e < 0) // error (only -- we allow EOS here) + return false; + + if (line.empty()) // TODO(matthewjheaney): retain this check? + break; + + if (fprintf(f, "%s\n", line.c_str()) < 0) + return false; + + ++count; + } + + if (count <= 0) // WebVTT cue requires non-empty payload + return false; + + return true; +} + +} // namespace libwebm + +int main(int argc, const char* argv[]) { + if (argc != 2) { + printf("usage: vttdemux \n"); + return EXIT_SUCCESS; + } + + const char* const filename = argv[1]; + mkvparser::MkvReader reader; + + int e = reader.Open(filename); + + if (e) { // error + printf("unable to open file\n"); + return EXIT_FAILURE; + } + + libwebm::vttdemux::mkvpos_t pos; + + if (!libwebm::vttdemux::ParseHeader(&reader, &pos)) + return EXIT_FAILURE; + + libwebm::vttdemux::segment_ptr_t segment_ptr; + + if (!libwebm::vttdemux::ParseSegment(&reader, pos, &segment_ptr)) + return EXIT_FAILURE; + + libwebm::vttdemux::metadata_map_t metadata_map; + + BuildMap(segment_ptr.get(), &metadata_map); + + if (metadata_map.empty()) { + printf("no WebVTT metadata found\n"); + return EXIT_FAILURE; + } + + if (!OpenFiles(&metadata_map, filename)) { + CloseFiles(&metadata_map); // nothing to flush, so not strictly necessary + return EXIT_FAILURE; + } + + if (!WriteFiles(metadata_map, segment_ptr.get())) { + CloseFiles(&metadata_map); // might as well flush what we do have + return EXIT_FAILURE; + } + + CloseFiles(&metadata_map); + + return EXIT_SUCCESS; +} diff --git a/vttreader.h b/vttreader.h new file mode 100644 index 000000000000..2e7cc4b5479f --- /dev/null +++ b/vttreader.h @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_VTTREADER_H_ +#define LIBWEBM_VTTREADER_H_ + +// This file is a wrapper for the file included immediately after this comment. +// New projects should not include this file: include the file included below. +#include "webvtt/vttreader.h" + +#endif // LIBWEBM_VTTREADER_H_ diff --git a/webm_info.cc b/webm_info.cc new file mode 100644 index 000000000000..7cc7272daad4 --- /dev/null +++ b/webm_info.cc @@ -0,0 +1,1336 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common/hdr_util.h" +#include "common/indent.h" +#include "common/vp9_header_parser.h" +#include "common/vp9_level_stats.h" +#include "common/webm_constants.h" +#include "common/webm_endian.h" + +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" + +namespace { + +using libwebm::Indent; +using libwebm::kNanosecondsPerSecond; +using libwebm::kNanosecondsPerSecondi; +using mkvparser::ContentEncoding; +using std::string; +using std::wstring; + +const char VERSION_STRING[] = "1.0.4.5"; + +struct Options { + Options(); + + // Returns true if |value| matches -|option| or -no|option|. + static bool MatchesBooleanOption(const string& option, const string& value); + + // Set all of the member variables to |value|. + void SetAll(bool value); + + bool output_video; + bool output_audio; + bool output_size; + bool output_offset; + bool output_seconds; + bool output_ebml_header; + bool output_segment; + bool output_seekhead; + bool output_segment_info; + bool output_tracks; + bool output_clusters; + bool output_blocks; + bool output_codec_info; + bool output_clusters_size; + bool output_encrypted_info; + bool output_cues; + bool output_frame_stats; + bool output_vp9_level; +}; + +Options::Options() + : output_video(true), + output_audio(true), + output_size(false), + output_offset(false), + output_seconds(true), + output_ebml_header(true), + output_segment(true), + output_seekhead(false), + output_segment_info(true), + output_tracks(true), + output_clusters(false), + output_blocks(false), + output_codec_info(false), + output_clusters_size(false), + output_encrypted_info(false), + output_cues(false), + output_frame_stats(false), + output_vp9_level(false) {} + +void Options::SetAll(bool value) { + output_video = value; + output_audio = value; + output_size = value; + output_offset = value; + output_ebml_header = value; + output_seconds = value; + output_segment = value; + output_segment_info = value; + output_tracks = value; + output_clusters = value; + output_blocks = value; + output_codec_info = value; + output_clusters_size = value; + output_encrypted_info = value; + output_cues = value; + output_frame_stats = value; + output_vp9_level = value; +} + +bool Options::MatchesBooleanOption(const string& option, const string& value) { + const string opt = "-" + option; + const string noopt = "-no" + option; + return value == opt || value == noopt; +} + +struct FrameStats { + FrameStats() + : frames(0), + displayed_frames(0), + first_altref(true), + frames_since_last_altref(0), + minimum_altref_distance(std::numeric_limits::max()), + min_altref_end_ns(0), + max_window_size(0), + max_window_end_ns(0) {} + + int frames; + int displayed_frames; + + bool first_altref; + int frames_since_last_altref; + int minimum_altref_distance; + int64_t min_altref_end_ns; + + std::queue window; + int64_t max_window_size; + int64_t max_window_end_ns; +}; + +void Usage() { + printf("Usage: webm_info [options] -i input\n"); + printf("\n"); + printf("Main options:\n"); + printf(" -h | -? show help\n"); + printf(" -v show version\n"); + printf(" -all Enable all output options.\n"); + printf(" -video Output video tracks (true)\n"); + printf(" -audio Output audio tracks (true)\n"); + printf(" -size Output element sizes (false)\n"); + printf(" -offset Output element offsets (false)\n"); + printf(" -times_seconds Output times as seconds (true)\n"); + printf(" -ebml_header Output EBML header (true)\n"); + printf(" -segment Output Segment (true)\n"); + printf(" -seekhead Output SeekHead (false)\n"); + printf(" -segment_info Output SegmentInfo (true)\n"); + printf(" -tracks Output Tracks (true)\n"); + printf(" -clusters Output Clusters (false)\n"); + printf(" -blocks Output Blocks (false)\n"); + printf(" -codec_info Output video codec information (false)\n"); + printf(" -clusters_size Output Total Clusters size (false)\n"); + printf(" -encrypted_info Output encrypted frame info (false)\n"); + printf(" -cues Output Cues entries (false)\n"); + printf(" -frame_stats Output frame stats (VP9)(false)\n"); + printf(" -vp9_level Output VP9 level(false)\n"); + printf("\nOutput options may be negated by prefixing 'no'.\n"); +} + +// TODO(fgalligan): Add support for non-ascii. +wstring UTF8ToWideString(const char* str) { + wstring wstr; + + if (str == NULL) + return wstr; + + string temp_str(str, strlen(str)); + wstr.assign(temp_str.begin(), temp_str.end()); + + return wstr; +} + +string ToString(const char* str) { return string((str == NULL) ? "" : str); } + +void OutputEBMLHeader(const mkvparser::EBMLHeader& ebml, FILE* o, + Indent* indent) { + fprintf(o, "EBML Header:\n"); + indent->Adjust(libwebm::kIncreaseIndent); + fprintf(o, "%sEBMLVersion : %lld\n", indent->indent_str().c_str(), + ebml.m_version); + fprintf(o, "%sEBMLReadVersion : %lld\n", indent->indent_str().c_str(), + ebml.m_readVersion); + fprintf(o, "%sEBMLMaxIDLength : %lld\n", indent->indent_str().c_str(), + ebml.m_maxIdLength); + fprintf(o, "%sEBMLMaxSizeLength : %lld\n", indent->indent_str().c_str(), + ebml.m_maxSizeLength); + fprintf(o, "%sDoc Type : %s\n", indent->indent_str().c_str(), + ebml.m_docType); + fprintf(o, "%sDocTypeVersion : %lld\n", indent->indent_str().c_str(), + ebml.m_docTypeVersion); + fprintf(o, "%sDocTypeReadVersion: %lld\n", indent->indent_str().c_str(), + ebml.m_docTypeReadVersion); + indent->Adjust(libwebm::kDecreaseIndent); +} + +void OutputSegment(const mkvparser::Segment& segment, const Options& options, + FILE* o) { + fprintf(o, "Segment:"); + if (options.output_offset) + fprintf(o, " @: %lld", segment.m_element_start); + if (options.output_size) + fprintf(o, " size: %lld", + segment.m_size + segment.m_start - segment.m_element_start); + fprintf(o, "\n"); +} + +bool OutputSeekHead(const mkvparser::Segment& segment, const Options& options, + FILE* o, Indent* indent) { + const mkvparser::SeekHead* const seekhead = segment.GetSeekHead(); + if (!seekhead) { + // SeekHeads are optional. + return true; + } + + fprintf(o, "%sSeekHead:", indent->indent_str().c_str()); + if (options.output_offset) + fprintf(o, " @: %lld", seekhead->m_element_start); + if (options.output_size) + fprintf(o, " size: %lld", seekhead->m_element_size); + fprintf(o, "\n"); + + indent->Adjust(libwebm::kIncreaseIndent); + + for (int i = 0; i < seekhead->GetCount(); ++i) { + const mkvparser::SeekHead::Entry* const entry = seekhead->GetEntry(i); + if (!entry) { + fprintf(stderr, "Error retrieving SeekHead entry #%d\n", i); + return false; + } + + fprintf(o, "%sEntry[%d]", indent->indent_str().c_str(), i); + if (options.output_offset) + fprintf(o, " @: %lld", entry->element_start); + if (options.output_size) + fprintf(o, " size: %lld", entry->element_size); + fprintf(o, "\n"); + + indent->Adjust(libwebm::kIncreaseIndent); + std::string entry_indent = indent->indent_str(); + // TODO(jzern): 1) known ids could be stringified. 2) ids could be + // reencoded to EBML for ease of lookup. + fprintf(o, "%sSeek ID : %llx\n", entry_indent.c_str(), entry->id); + fprintf(o, "%sSeek position : %lld\n", entry_indent.c_str(), entry->pos); + indent->Adjust(libwebm::kDecreaseIndent); + } + + for (int i = 0; i < seekhead->GetVoidElementCount(); ++i) { + const mkvparser::SeekHead::VoidElement* const entry = + seekhead->GetVoidElement(i); + if (!entry) { + fprintf(stderr, "Error retrieving SeekHead void element #%d\n", i); + return false; + } + + fprintf(o, "%sVoid element[%d]", indent->indent_str().c_str(), i); + if (options.output_offset) + fprintf(o, " @: %lld", entry->element_start); + if (options.output_size) + fprintf(o, " size: %lld", entry->element_size); + fprintf(o, "\n"); + } + + indent->Adjust(libwebm::kDecreaseIndent); + return true; +} + +bool OutputSegmentInfo(const mkvparser::Segment& segment, + const Options& options, FILE* o, Indent* indent) { + const mkvparser::SegmentInfo* const segment_info = segment.GetInfo(); + if (!segment_info) { + fprintf(stderr, "SegmentInfo was NULL.\n"); + return false; + } + + const int64_t timecode_scale = segment_info->GetTimeCodeScale(); + const int64_t duration_ns = segment_info->GetDuration(); + const wstring title = UTF8ToWideString(segment_info->GetTitleAsUTF8()); + const wstring muxing_app = + UTF8ToWideString(segment_info->GetMuxingAppAsUTF8()); + const wstring writing_app = + UTF8ToWideString(segment_info->GetWritingAppAsUTF8()); + + fprintf(o, "%sSegmentInfo:", indent->indent_str().c_str()); + if (options.output_offset) + fprintf(o, " @: %lld", segment_info->m_element_start); + if (options.output_size) + fprintf(o, " size: %lld", segment_info->m_element_size); + fprintf(o, "\n"); + + indent->Adjust(libwebm::kIncreaseIndent); + fprintf(o, "%sTimecodeScale : %" PRId64 " \n", indent->indent_str().c_str(), + timecode_scale); + if (options.output_seconds) + fprintf(o, "%sDuration(secs): %g\n", indent->indent_str().c_str(), + duration_ns / kNanosecondsPerSecond); + else + fprintf(o, "%sDuration(nano): %" PRId64 "\n", indent->indent_str().c_str(), + duration_ns); + + if (!title.empty()) + fprintf(o, "%sTitle : %ls\n", indent->indent_str().c_str(), + title.c_str()); + if (!muxing_app.empty()) + fprintf(o, "%sMuxingApp : %ls\n", indent->indent_str().c_str(), + muxing_app.c_str()); + if (!writing_app.empty()) + fprintf(o, "%sWritingApp : %ls\n", indent->indent_str().c_str(), + writing_app.c_str()); + indent->Adjust(libwebm::kDecreaseIndent); + return true; +} + +bool OutputTracks(const mkvparser::Segment& segment, const Options& options, + FILE* o, Indent* indent) { + const mkvparser::Tracks* const tracks = segment.GetTracks(); + if (!tracks) { + fprintf(stderr, "Tracks was NULL.\n"); + return false; + } + + fprintf(o, "%sTracks:", indent->indent_str().c_str()); + if (options.output_offset) + fprintf(o, " @: %lld", tracks->m_element_start); + if (options.output_size) + fprintf(o, " size: %lld", tracks->m_element_size); + fprintf(o, "\n"); + + unsigned int i = 0; + const unsigned long j = tracks->GetTracksCount(); + while (i != j) { + const mkvparser::Track* const track = tracks->GetTrackByIndex(i++); + if (track == NULL) + continue; + + indent->Adjust(libwebm::kIncreaseIndent); + fprintf(o, "%sTrack:", indent->indent_str().c_str()); + if (options.output_offset) + fprintf(o, " @: %lld", track->m_element_start); + if (options.output_size) + fprintf(o, " size: %lld", track->m_element_size); + fprintf(o, "\n"); + + const int64_t track_type = track->GetType(); + const int64_t track_number = track->GetNumber(); + const wstring track_name = UTF8ToWideString(track->GetNameAsUTF8()); + + indent->Adjust(libwebm::kIncreaseIndent); + fprintf(o, "%sTrackType : %" PRId64 "\n", indent->indent_str().c_str(), + track_type); + fprintf(o, "%sTrackNumber : %" PRId64 "\n", indent->indent_str().c_str(), + track_number); + if (!track_name.empty()) + fprintf(o, "%sName : %ls\n", indent->indent_str().c_str(), + track_name.c_str()); + + const char* const codec_id = track->GetCodecId(); + if (codec_id) + fprintf(o, "%sCodecID : %s\n", indent->indent_str().c_str(), + codec_id); + + const wstring codec_name = UTF8ToWideString(track->GetCodecNameAsUTF8()); + if (!codec_name.empty()) + fprintf(o, "%sCodecName : %ls\n", indent->indent_str().c_str(), + codec_name.c_str()); + + size_t private_size; + const unsigned char* const private_data = + track->GetCodecPrivate(private_size); + if (private_data) { + fprintf(o, "%sPrivateData(size): %d\n", indent->indent_str().c_str(), + static_cast(private_size)); + + if (track_type == mkvparser::Track::kVideo) { + const std::string codec_id = ToString(track->GetCodecId()); + const std::string v_vp9 = "V_VP9"; + if (codec_id == v_vp9) { + libwebm::Vp9CodecFeatures features; + if (!libwebm::ParseVpxCodecPrivate(private_data, + static_cast(private_size), + &features)) { + fprintf(stderr, "Error parsing VpxCodecPrivate.\n"); + return false; + } + if (features.profile != -1) + fprintf(o, "%sVP9 profile : %d\n", + indent->indent_str().c_str(), features.profile); + if (features.level != -1) + fprintf(o, "%sVP9 level : %d\n", + indent->indent_str().c_str(), features.level); + if (features.bit_depth != -1) + fprintf(o, "%sVP9 bit_depth : %d\n", + indent->indent_str().c_str(), features.bit_depth); + if (features.chroma_subsampling != -1) + fprintf(o, "%sVP9 chroma subsampling : %d\n", + indent->indent_str().c_str(), features.chroma_subsampling); + } + } + } + + const uint64_t default_duration = track->GetDefaultDuration(); + if (default_duration > 0) + fprintf(o, "%sDefaultDuration: %" PRIu64 "\n", + indent->indent_str().c_str(), default_duration); + + if (track->GetContentEncodingCount() > 0) { + // Only check the first content encoding. + const ContentEncoding* const encoding = + track->GetContentEncodingByIndex(0); + if (!encoding) { + printf("Could not get first ContentEncoding.\n"); + return false; + } + + fprintf(o, "%sContentEncodingOrder : %lld\n", + indent->indent_str().c_str(), encoding->encoding_order()); + fprintf(o, "%sContentEncodingScope : %lld\n", + indent->indent_str().c_str(), encoding->encoding_scope()); + fprintf(o, "%sContentEncodingType : %lld\n", + indent->indent_str().c_str(), encoding->encoding_type()); + + if (encoding->GetEncryptionCount() > 0) { + // Only check the first encryption. + const ContentEncoding::ContentEncryption* const encryption = + encoding->GetEncryptionByIndex(0); + if (!encryption) { + printf("Could not get first ContentEncryption.\n"); + return false; + } + + fprintf(o, "%sContentEncAlgo : %lld\n", + indent->indent_str().c_str(), encryption->algo); + + if (encryption->key_id_len > 0) { + fprintf(o, "%sContentEncKeyID : ", indent->indent_str().c_str()); + for (int k = 0; k < encryption->key_id_len; ++k) { + fprintf(o, "0x%02x, ", encryption->key_id[k]); + } + fprintf(o, "\n"); + } + + if (encryption->signature_len > 0) { + fprintf(o, "%sContentSignature : 0x", + indent->indent_str().c_str()); + for (int k = 0; k < encryption->signature_len; ++k) { + fprintf(o, "%x", encryption->signature[k]); + } + fprintf(o, "\n"); + } + + if (encryption->sig_key_id_len > 0) { + fprintf(o, "%sContentSigKeyID : 0x", + indent->indent_str().c_str()); + for (int k = 0; k < encryption->sig_key_id_len; ++k) { + fprintf(o, "%x", encryption->sig_key_id[k]); + } + fprintf(o, "\n"); + } + + fprintf(o, "%sContentSigAlgo : %lld\n", + indent->indent_str().c_str(), encryption->sig_algo); + fprintf(o, "%sContentSigHashAlgo : %lld\n", + indent->indent_str().c_str(), encryption->sig_hash_algo); + + const ContentEncoding::ContentEncAESSettings& aes = + encryption->aes_settings; + fprintf(o, "%sCipherMode : %lld\n", + indent->indent_str().c_str(), aes.cipher_mode); + } + } + + if (track_type == mkvparser::Track::kVideo) { + const mkvparser::VideoTrack* const video_track = + static_cast(track); + const int64_t width = video_track->GetWidth(); + const int64_t height = video_track->GetHeight(); + const int64_t display_width = video_track->GetDisplayWidth(); + const int64_t display_height = video_track->GetDisplayHeight(); + const int64_t display_unit = video_track->GetDisplayUnit(); + const double frame_rate = video_track->GetFrameRate(); + fprintf(o, "%sPixelWidth : %" PRId64 "\n", indent->indent_str().c_str(), + width); + fprintf(o, "%sPixelHeight : %" PRId64 "\n", indent->indent_str().c_str(), + height); + if (frame_rate > 0.0) + fprintf(o, "%sFrameRate : %g\n", indent->indent_str().c_str(), + video_track->GetFrameRate()); + if (display_unit > 0 || display_width != width || + display_height != height) { + fprintf(o, "%sDisplayWidth : %" PRId64 "\n", + indent->indent_str().c_str(), display_width); + fprintf(o, "%sDisplayHeight : %" PRId64 "\n", + indent->indent_str().c_str(), display_height); + fprintf(o, "%sDisplayUnit : %" PRId64 "\n", + indent->indent_str().c_str(), display_unit); + } + + const mkvparser::Colour* const colour = video_track->GetColour(); + if (colour) { + // TODO(fgalligan): Add support for Colour's address and size. + fprintf(o, "%sColour:\n", indent->indent_str().c_str()); + indent->Adjust(libwebm::kIncreaseIndent); + + const int64_t matrix_coefficients = colour->matrix_coefficients; + const int64_t bits_per_channel = colour->bits_per_channel; + const int64_t chroma_subsampling_horz = colour->chroma_subsampling_horz; + const int64_t chroma_subsampling_vert = colour->chroma_subsampling_vert; + const int64_t cb_subsampling_horz = colour->cb_subsampling_horz; + const int64_t cb_subsampling_vert = colour->cb_subsampling_vert; + const int64_t chroma_siting_horz = colour->chroma_siting_horz; + const int64_t chroma_siting_vert = colour->chroma_siting_vert; + const int64_t range = colour->range; + const int64_t transfer_characteristics = + colour->transfer_characteristics; + const int64_t primaries = colour->primaries; + const int64_t max_cll = colour->max_cll; + const int64_t max_fall = colour->max_fall; + if (matrix_coefficients != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sMatrixCoefficients : %" PRId64 "\n", + indent->indent_str().c_str(), matrix_coefficients); + if (bits_per_channel != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sBitsPerChannel : %" PRId64 "\n", + indent->indent_str().c_str(), bits_per_channel); + if (chroma_subsampling_horz != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sChromaSubsamplingHorz : %" PRId64 "\n", + indent->indent_str().c_str(), chroma_subsampling_horz); + if (chroma_subsampling_vert != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sChromaSubsamplingVert : %" PRId64 "\n", + indent->indent_str().c_str(), chroma_subsampling_vert); + if (cb_subsampling_horz != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sCbSubsamplingHorz : %" PRId64 "\n", + indent->indent_str().c_str(), cb_subsampling_horz); + if (cb_subsampling_vert != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sCbSubsamplingVert : %" PRId64 "\n", + indent->indent_str().c_str(), cb_subsampling_vert); + if (chroma_siting_horz != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sChromaSitingHorz : %" PRId64 "\n", + indent->indent_str().c_str(), chroma_siting_horz); + if (chroma_siting_vert != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sChromaSitingVert : %" PRId64 "\n", + indent->indent_str().c_str(), chroma_siting_vert); + if (range != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sRange : %" PRId64 "\n", + indent->indent_str().c_str(), range); + if (transfer_characteristics != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sTransferCharacteristics : %" PRId64 "\n", + indent->indent_str().c_str(), transfer_characteristics); + if (primaries != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sPrimaries : %" PRId64 "\n", + indent->indent_str().c_str(), primaries); + if (max_cll != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sMaxCLL : %" PRId64 "\n", + indent->indent_str().c_str(), max_cll); + if (max_fall != mkvparser::Colour::kValueNotPresent) + fprintf(o, "%sMaxFALL : %" PRId64 "\n", + indent->indent_str().c_str(), max_fall); + + const mkvparser::MasteringMetadata* const metadata = + colour->mastering_metadata; + if (metadata) { + // TODO(fgalligan): Add support for MasteringMetadata's address and + // size. + fprintf(o, "%sMasteringMetadata:\n", indent->indent_str().c_str()); + indent->Adjust(libwebm::kIncreaseIndent); + + const mkvparser::PrimaryChromaticity* const red = metadata->r; + const mkvparser::PrimaryChromaticity* const green = metadata->g; + const mkvparser::PrimaryChromaticity* const blue = metadata->b; + const mkvparser::PrimaryChromaticity* const white = + metadata->white_point; + const float max = metadata->luminance_max; + const float min = metadata->luminance_min; + if (red) { + fprintf(o, "%sPrimaryRChromaticityX : %g\n", + indent->indent_str().c_str(), red->x); + fprintf(o, "%sPrimaryRChromaticityY : %g\n", + indent->indent_str().c_str(), red->y); + } + if (green) { + fprintf(o, "%sPrimaryGChromaticityX : %g\n", + indent->indent_str().c_str(), green->x); + fprintf(o, "%sPrimaryGChromaticityY : %g\n", + indent->indent_str().c_str(), green->y); + } + if (blue) { + fprintf(o, "%sPrimaryBChromaticityX : %g\n", + indent->indent_str().c_str(), blue->x); + fprintf(o, "%sPrimaryBChromaticityY : %g\n", + indent->indent_str().c_str(), blue->y); + } + if (white) { + fprintf(o, "%sWhitePointChromaticityX : %g\n", + indent->indent_str().c_str(), white->x); + fprintf(o, "%sWhitePointChromaticityY : %g\n", + indent->indent_str().c_str(), white->y); + } + if (max != mkvparser::MasteringMetadata::kValueNotPresent) + fprintf(o, "%sLuminanceMax : %g\n", + indent->indent_str().c_str(), max); + if (min != mkvparser::MasteringMetadata::kValueNotPresent) + fprintf(o, "%sLuminanceMin : %g\n", + indent->indent_str().c_str(), min); + indent->Adjust(libwebm::kDecreaseIndent); + } + indent->Adjust(libwebm::kDecreaseIndent); + } + + const mkvparser::Projection* const projection = + video_track->GetProjection(); + if (projection) { + fprintf(o, "%sProjection:\n", indent->indent_str().c_str()); + indent->Adjust(libwebm::kIncreaseIndent); + + const int projection_type = static_cast(projection->type); + const int kTypeNotPresent = + static_cast(mkvparser::Projection::kTypeNotPresent); + const float kValueNotPresent = mkvparser::Projection::kValueNotPresent; + if (projection_type != kTypeNotPresent) + fprintf(o, "%sProjectionType : %d\n", + indent->indent_str().c_str(), projection_type); + if (projection->private_data) + fprintf(o, "%sProjectionPrivate(size) : %d\n", + indent->indent_str().c_str(), + static_cast(projection->private_data_length)); + if (projection->pose_yaw != kValueNotPresent) + fprintf(o, "%sProjectionPoseYaw : %g\n", + indent->indent_str().c_str(), projection->pose_yaw); + if (projection->pose_pitch != kValueNotPresent) + fprintf(o, "%sProjectionPosePitch : %g\n", + indent->indent_str().c_str(), projection->pose_pitch); + if (projection->pose_roll != kValueNotPresent) + fprintf(o, "%sProjectionPoseRoll : %g\n", + indent->indent_str().c_str(), projection->pose_roll); + indent->Adjust(libwebm::kDecreaseIndent); + } + } else if (track_type == mkvparser::Track::kAudio) { + const mkvparser::AudioTrack* const audio_track = + static_cast(track); + const int64_t channels = audio_track->GetChannels(); + const int64_t bit_depth = audio_track->GetBitDepth(); + const uint64_t codec_delay = audio_track->GetCodecDelay(); + const uint64_t seek_preroll = audio_track->GetSeekPreRoll(); + fprintf(o, "%sChannels : %" PRId64 "\n", + indent->indent_str().c_str(), channels); + if (bit_depth > 0) + fprintf(o, "%sBitDepth : %" PRId64 "\n", + indent->indent_str().c_str(), bit_depth); + fprintf(o, "%sSamplingFrequency: %g\n", indent->indent_str().c_str(), + audio_track->GetSamplingRate()); + if (codec_delay) + fprintf(o, "%sCodecDelay : %" PRIu64 "\n", + indent->indent_str().c_str(), codec_delay); + if (seek_preroll) + fprintf(o, "%sSeekPreRoll : %" PRIu64 "\n", + indent->indent_str().c_str(), seek_preroll); + } + indent->Adjust(libwebm::kDecreaseIndent * 2); + } + + return true; +} + +// libvpx reference: vp9/vp9_dx_iface.c +void ParseSuperframeIndex(const uint8_t* data, size_t data_sz, + uint32_t sizes[8], int* count) { + const uint8_t marker = data[data_sz - 1]; + *count = 0; + + if ((marker & 0xe0) == 0xc0) { + const int frames = (marker & 0x7) + 1; + const int mag = ((marker >> 3) & 0x3) + 1; + const size_t index_sz = 2 + mag * frames; + + if (data_sz >= index_sz && data[data_sz - index_sz] == marker) { + // found a valid superframe index + const uint8_t* x = data + data_sz - index_sz + 1; + + for (int i = 0; i < frames; ++i) { + uint32_t this_sz = 0; + + for (int j = 0; j < mag; ++j) { + this_sz |= (*x++) << (j * 8); + } + sizes[i] = this_sz; + } + *count = frames; + } + } +} + +void PrintVP9Info(const uint8_t* data, int size, FILE* o, int64_t time_ns, + FrameStats* stats, vp9_parser::Vp9HeaderParser* parser, + vp9_parser::Vp9LevelStats* level_stats) { + if (size < 1) + return; + + uint32_t sizes[8]; + int i = 0, count = 0; + ParseSuperframeIndex(data, size, sizes, &count); + + // Remove all frames that are less than window size. + while (!stats->window.empty() && + stats->window.front() < (time_ns - (kNanosecondsPerSecondi - 1))) + stats->window.pop(); + + do { + const size_t frame_length = (count > 0) ? sizes[i] : size; + if (frame_length > static_cast(std::numeric_limits::max()) || + static_cast(frame_length) > size) { + fprintf(o, " invalid VP9 frame size (%u)\n", + static_cast(frame_length)); + return; + } + if (!parser->ParseUncompressedHeader(data, frame_length)) + return; + level_stats->AddFrame(*parser, time_ns); + + // const int frame_marker = (data[0] >> 6) & 0x3; + const int version = parser->profile(); + const int key = parser->key(); + const int altref_frame = parser->altref(); + const int error_resilient_mode = parser->error_resilient_mode(); + const int row_tiles = parser->row_tiles(); + const int column_tiles = parser->column_tiles(); + const int frame_parallel_mode = parser->frame_parallel_mode(); + + if (key && + !(size >= 4 && data[1] == 0x49 && data[2] == 0x83 && data[3] == 0x42)) { + fprintf(o, " invalid VP9 signature"); + return; + } + + stats->window.push(time_ns); + ++stats->frames; + + if (altref_frame) { + const int delta_altref = stats->frames_since_last_altref; + if (stats->first_altref) { + stats->first_altref = false; + } else if (delta_altref < stats->minimum_altref_distance) { + stats->minimum_altref_distance = delta_altref; + stats->min_altref_end_ns = time_ns; + } + stats->frames_since_last_altref = 0; + } else { + ++stats->frames_since_last_altref; + ++stats->displayed_frames; + } + + if (count > 0) { + fprintf(o, " packed [%d]: {", i); + } + + fprintf(o, " key:%d v:%d altref:%d errm:%d rt:%d ct:%d fpm:%d", key, + version, altref_frame, error_resilient_mode, row_tiles, + column_tiles, frame_parallel_mode); + + if (key) { + if (size > 4) { + fprintf(o, " cs:%d", parser->color_space()); + } + if (parser->display_width() != parser->width() || + parser->display_height() != parser->height()) { + fprintf(o, " dw:%d dh:%d", parser->display_width(), + parser->display_height()); + } + } + + if (count > 0) { + fprintf(o, " size: %u }", sizes[i]); + data += sizes[i]; + size -= sizes[i]; + } + ++i; + } while (i < count); + + if (stats->max_window_size < static_cast(stats->window.size())) { + stats->max_window_size = stats->window.size(); + stats->max_window_end_ns = time_ns; + } +} + +void PrintVP8Info(const uint8_t* data, int size, FILE* o) { + if (size < 3) + return; + + const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); + const int key = !(bits & 0x1); + const int altref_frame = !((bits >> 4) & 0x1); + const int version = (bits >> 1) & 0x7; + const int partition_length = (bits >> 5) & 0x7FFFF; + if (key && + !(size >= 6 && data[3] == 0x9d && data[4] == 0x01 && data[5] == 0x2a)) { + fprintf(o, " invalid VP8 signature"); + return; + } + fprintf(o, " key:%d v:%d altref:%d partition_length:%d", key, version, + altref_frame, partition_length); +} + +// Prints the partition offsets of the sub-sample encryption. |data| must point +// to an encrypted frame just after the signal byte. Returns the number of +// bytes read from the sub-sample partition information. +int PrintSubSampleEncryption(const uint8_t* data, int size, FILE* o) { + int read_end = sizeof(uint64_t); + + // Skip past IV. + if (size < read_end) + return 0; + data += sizeof(uint64_t); + + // Read number of partitions. + read_end += sizeof(uint8_t); + if (size < read_end) + return 0; + const int num_partitions = data[0]; + data += sizeof(uint8_t); + + // Read partitions. + for (int i = 0; i < num_partitions; ++i) { + read_end += sizeof(uint32_t); + if (size < read_end) + return 0; + uint32_t partition_offset; + memcpy(&partition_offset, data, sizeof(partition_offset)); + partition_offset = libwebm::bigendian_to_host(partition_offset); + fprintf(o, " off[%d]:%u", i, partition_offset); + data += sizeof(uint32_t); + } + + return read_end; +} + +bool OutputCluster(const mkvparser::Cluster& cluster, + const mkvparser::Tracks& tracks, const Options& options, + FILE* o, mkvparser::MkvReader* reader, Indent* indent, + int64_t* clusters_size, FrameStats* stats, + vp9_parser::Vp9HeaderParser* parser, + vp9_parser::Vp9LevelStats* level_stats) { + if (clusters_size) { + // Load the Cluster. + const mkvparser::BlockEntry* block_entry; + long status = cluster.GetFirst(block_entry); + if (status) { + fprintf(stderr, "Could not get first Block of Cluster.\n"); + return false; + } + + *clusters_size += cluster.GetElementSize(); + } + + if (options.output_clusters) { + const int64_t time_ns = cluster.GetTime(); + const int64_t duration_ns = cluster.GetLastTime() - cluster.GetFirstTime(); + + fprintf(o, "%sCluster:", indent->indent_str().c_str()); + if (options.output_offset) + fprintf(o, " @: %lld", cluster.m_element_start); + if (options.output_size) + fprintf(o, " size: %lld", cluster.GetElementSize()); + fprintf(o, "\n"); + indent->Adjust(libwebm::kIncreaseIndent); + if (options.output_seconds) + fprintf(o, "%sTimecode (sec) : %g\n", indent->indent_str().c_str(), + time_ns / kNanosecondsPerSecond); + else + fprintf(o, "%sTimecode (nano): %" PRId64 "\n", + indent->indent_str().c_str(), time_ns); + if (options.output_seconds) + fprintf(o, "%sDuration (sec) : %g\n", indent->indent_str().c_str(), + duration_ns / kNanosecondsPerSecond); + else + fprintf(o, "%sDuration (nano): %" PRId64 "\n", + indent->indent_str().c_str(), duration_ns); + + fprintf(o, "%s# Blocks : %ld\n", indent->indent_str().c_str(), + cluster.GetEntryCount()); + } + + if (options.output_blocks) { + const mkvparser::BlockEntry* block_entry; + long status = cluster.GetFirst(block_entry); + if (status) { + fprintf(stderr, "Could not get first Block of Cluster.\n"); + return false; + } + + std::vector vector_data; + while (block_entry != NULL && !block_entry->EOS()) { + const mkvparser::Block* const block = block_entry->GetBlock(); + if (!block) { + fprintf(stderr, "Could not getblock entry.\n"); + return false; + } + + const unsigned int track_number = + static_cast(block->GetTrackNumber()); + const mkvparser::Track* track = tracks.GetTrackByNumber(track_number); + if (!track) { + fprintf(stderr, "Could not get Track.\n"); + return false; + } + + const int64_t track_type = track->GetType(); + if ((track_type == mkvparser::Track::kVideo && options.output_video) || + (track_type == mkvparser::Track::kAudio && options.output_audio)) { + const int64_t time_ns = block->GetTime(&cluster); + const bool is_key = block->IsKey(); + + if (block_entry->GetKind() == mkvparser::BlockEntry::kBlockGroup) { + fprintf(o, "%sBlockGroup:\n", indent->indent_str().c_str()); + indent->Adjust(libwebm::kIncreaseIndent); + } + + fprintf(o, "%sBlock: type:%s frame:%s", indent->indent_str().c_str(), + track_type == mkvparser::Track::kVideo ? "V" : "A", + is_key ? "I" : "P"); + if (options.output_seconds) + fprintf(o, " secs:%5g", time_ns / kNanosecondsPerSecond); + else + fprintf(o, " nano:%10" PRId64, time_ns); + + if (options.output_offset) + fprintf(o, " @_payload: %lld", block->m_start); + if (options.output_size) + fprintf(o, " size_payload: %lld", block->m_size); + + const uint8_t KEncryptedBit = 0x1; + const uint8_t kSubSampleBit = 0x2; + const int kSignalByteSize = 1; + bool encrypted_stream = false; + if (options.output_encrypted_info) { + if (track->GetContentEncodingCount() > 0) { + // Only check the first content encoding. + const ContentEncoding* const encoding = + track->GetContentEncodingByIndex(0); + if (encoding) { + if (encoding->GetEncryptionCount() > 0) { + const ContentEncoding::ContentEncryption* const encryption = + encoding->GetEncryptionByIndex(0); + if (encryption) { + const ContentEncoding::ContentEncAESSettings& aes = + encryption->aes_settings; + if (aes.cipher_mode == 1) { + encrypted_stream = true; + } + } + } + } + } + + if (encrypted_stream) { + const mkvparser::Block::Frame& frame = block->GetFrame(0); + if (frame.len > static_cast(vector_data.size())) { + vector_data.resize(frame.len + 1024); + } + + unsigned char* data = &vector_data[0]; + if (frame.Read(reader, data) < 0) { + fprintf(stderr, "Could not read frame.\n"); + return false; + } + + const bool encrypted_frame = !!(data[0] & KEncryptedBit); + const bool sub_sample_encrypt = !!(data[0] & kSubSampleBit); + fprintf(o, " enc: %d", encrypted_frame ? 1 : 0); + fprintf(o, " sub: %d", sub_sample_encrypt ? 1 : 0); + + if (encrypted_frame) { + uint64_t iv; + memcpy(&iv, data + kSignalByteSize, sizeof(iv)); + fprintf(o, " iv: %" PRIx64, iv); + } + } + } + + if (options.output_codec_info) { + const int frame_count = block->GetFrameCount(); + + if (frame_count > 1) { + fprintf(o, "\n"); + indent->Adjust(libwebm::kIncreaseIndent); + } + + for (int i = 0; i < frame_count; ++i) { + if (track_type == mkvparser::Track::kVideo) { + const mkvparser::Block::Frame& frame = block->GetFrame(i); + if (frame.len > static_cast(vector_data.size())) { + vector_data.resize(frame.len + 1024); + } + + unsigned char* data = &vector_data[0]; + if (frame.Read(reader, data) < 0) { + fprintf(stderr, "Could not read frame.\n"); + return false; + } + + if (frame_count > 1) + fprintf(o, "\n%sVP8 data :", indent->indent_str().c_str()); + + bool encrypted_frame = false; + bool sub_sample_encrypt = false; + int frame_size = static_cast(frame.len); + + int frame_offset = 0; + if (encrypted_stream) { + if (data[0] & KEncryptedBit) { + encrypted_frame = true; + if (data[0] & kSubSampleBit) { + sub_sample_encrypt = true; + data += kSignalByteSize; + frame_size -= kSignalByteSize; + frame_offset = + PrintSubSampleEncryption(data, frame_size, o); + } + } else { + frame_offset = kSignalByteSize; + } + } + + if (!encrypted_frame || sub_sample_encrypt) { + data += frame_offset; + frame_size -= frame_offset; + + const string codec_id = ToString(track->GetCodecId()); + if (codec_id == "V_VP8") { + PrintVP8Info(data, frame_size, o); + } else if (codec_id == "V_VP9") { + PrintVP9Info(data, frame_size, o, time_ns, stats, parser, + level_stats); + } + } + } + } + + if (frame_count > 1) + indent->Adjust(libwebm::kDecreaseIndent); + } + + if (block_entry->GetKind() == mkvparser::BlockEntry::kBlockGroup) { + const int64_t discard_padding = block->GetDiscardPadding(); + if (discard_padding != 0) { + fprintf(o, "\n%sDiscardPadding: %10" PRId64, + indent->indent_str().c_str(), discard_padding); + } + indent->Adjust(libwebm::kDecreaseIndent); + } + + fprintf(o, "\n"); + } + + status = cluster.GetNext(block_entry, block_entry); + if (status) { + printf("\n Could not get next block of cluster.\n"); + return false; + } + } + } + + if (options.output_clusters) + indent->Adjust(libwebm::kDecreaseIndent); + + return true; +} + +bool OutputCues(const mkvparser::Segment& segment, + const mkvparser::Tracks& tracks, const Options& options, + FILE* o, Indent* indent) { + const mkvparser::Cues* const cues = segment.GetCues(); + if (cues == NULL) + return true; + + // Load all of the cue points. + while (!cues->DoneParsing()) + cues->LoadCuePoint(); + + // Confirm that the input has cue points. + const mkvparser::CuePoint* const first_cue = cues->GetFirst(); + if (first_cue == NULL) { + fprintf(o, "%sNo cue points.\n", indent->indent_str().c_str()); + return true; + } + + // Input has cue points, dump them: + fprintf(o, "%sCues:", indent->indent_str().c_str()); + if (options.output_offset) + fprintf(o, " @:%lld", cues->m_element_start); + if (options.output_size) + fprintf(o, " size:%lld", cues->m_element_size); + fprintf(o, "\n"); + + const mkvparser::CuePoint* cue_point = first_cue; + int cue_point_num = 1; + const int num_tracks = static_cast(tracks.GetTracksCount()); + indent->Adjust(libwebm::kIncreaseIndent); + + do { + for (int track_num = 0; track_num < num_tracks; ++track_num) { + const mkvparser::Track* const track = tracks.GetTrackByIndex(track_num); + const mkvparser::CuePoint::TrackPosition* const track_pos = + cue_point->Find(track); + + if (track_pos != NULL) { + const char track_type = + (track->GetType() == mkvparser::Track::kVideo) ? 'V' : 'A'; + fprintf(o, "%sCue Point:%d type:%c track:%d", + indent->indent_str().c_str(), cue_point_num, track_type, + static_cast(track->GetNumber())); + + if (options.output_seconds) { + fprintf(o, " secs:%g", + cue_point->GetTime(&segment) / kNanosecondsPerSecond); + } else { + fprintf(o, " nano:%lld", cue_point->GetTime(&segment)); + } + + if (options.output_blocks) + fprintf(o, " block:%lld", track_pos->m_block); + + if (options.output_offset) + fprintf(o, " @:%lld", track_pos->m_pos); + + fprintf(o, "\n"); + } + } + + cue_point = cues->GetNext(cue_point); + ++cue_point_num; + } while (cue_point != NULL); + + indent->Adjust(libwebm::kDecreaseIndent); + return true; +} + +} // namespace + +int main(int argc, char* argv[]) { + string input; + Options options; + + const int argc_check = argc - 1; + for (int i = 1; i < argc; ++i) { + if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) { + Usage(); + return EXIT_SUCCESS; + } else if (!strcmp("-v", argv[i])) { + printf("version: %s\n", VERSION_STRING); + } else if (!strcmp("-i", argv[i]) && i < argc_check) { + input = argv[++i]; + } else if (!strcmp("-all", argv[i])) { + options.SetAll(true); + } else if (Options::MatchesBooleanOption("video", argv[i])) { + options.output_video = !strcmp("-video", argv[i]); + } else if (Options::MatchesBooleanOption("audio", argv[i])) { + options.output_audio = !strcmp("-audio", argv[i]); + } else if (Options::MatchesBooleanOption("size", argv[i])) { + options.output_size = !strcmp("-size", argv[i]); + } else if (Options::MatchesBooleanOption("offset", argv[i])) { + options.output_offset = !strcmp("-offset", argv[i]); + } else if (Options::MatchesBooleanOption("times_seconds", argv[i])) { + options.output_seconds = !strcmp("-times_seconds", argv[i]); + } else if (Options::MatchesBooleanOption("ebml_header", argv[i])) { + options.output_ebml_header = !strcmp("-ebml_header", argv[i]); + } else if (Options::MatchesBooleanOption("segment", argv[i])) { + options.output_segment = !strcmp("-segment", argv[i]); + } else if (Options::MatchesBooleanOption("seekhead", argv[i])) { + options.output_seekhead = !strcmp("-seekhead", argv[i]); + } else if (Options::MatchesBooleanOption("segment_info", argv[i])) { + options.output_segment_info = !strcmp("-segment_info", argv[i]); + } else if (Options::MatchesBooleanOption("tracks", argv[i])) { + options.output_tracks = !strcmp("-tracks", argv[i]); + } else if (Options::MatchesBooleanOption("clusters", argv[i])) { + options.output_clusters = !strcmp("-clusters", argv[i]); + } else if (Options::MatchesBooleanOption("blocks", argv[i])) { + options.output_blocks = !strcmp("-blocks", argv[i]); + } else if (Options::MatchesBooleanOption("codec_info", argv[i])) { + options.output_codec_info = !strcmp("-codec_info", argv[i]); + } else if (Options::MatchesBooleanOption("clusters_size", argv[i])) { + options.output_clusters_size = !strcmp("-clusters_size", argv[i]); + } else if (Options::MatchesBooleanOption("encrypted_info", argv[i])) { + options.output_encrypted_info = !strcmp("-encrypted_info", argv[i]); + } else if (Options::MatchesBooleanOption("cues", argv[i])) { + options.output_cues = !strcmp("-cues", argv[i]); + } else if (Options::MatchesBooleanOption("frame_stats", argv[i])) { + options.output_frame_stats = !strcmp("-frame_stats", argv[i]); + } else if (Options::MatchesBooleanOption("vp9_level", argv[i])) { + options.output_vp9_level = !strcmp("-vp9_level", argv[i]); + } + } + + if (argc < 3 || input.empty()) { + Usage(); + return EXIT_FAILURE; + } + + std::unique_ptr reader( + new (std::nothrow) mkvparser::MkvReader()); // NOLINT + if (reader->Open(input.c_str())) { + fprintf(stderr, "Error opening file:%s\n", input.c_str()); + return EXIT_FAILURE; + } + + long long int pos = 0; + std::unique_ptr ebml_header( + new (std::nothrow) mkvparser::EBMLHeader()); // NOLINT + if (ebml_header->Parse(reader.get(), pos) < 0) { + fprintf(stderr, "Error parsing EBML header.\n"); + return EXIT_FAILURE; + } + + Indent indent(0); + FILE* out = stdout; + + if (options.output_ebml_header) + OutputEBMLHeader(*ebml_header.get(), out, &indent); + + mkvparser::Segment* temp_segment; + if (mkvparser::Segment::CreateInstance(reader.get(), pos, temp_segment)) { + fprintf(stderr, "Segment::CreateInstance() failed.\n"); + return EXIT_FAILURE; + } + std::unique_ptr segment(temp_segment); + + if (segment->Load() < 0) { + fprintf(stderr, "Segment::Load() failed.\n"); + return EXIT_FAILURE; + } + + if (options.output_segment) { + OutputSegment(*(segment.get()), options, out); + indent.Adjust(libwebm::kIncreaseIndent); + } + + if (options.output_seekhead) + if (!OutputSeekHead(*(segment.get()), options, out, &indent)) + return EXIT_FAILURE; + + if (options.output_segment_info) + if (!OutputSegmentInfo(*(segment.get()), options, out, &indent)) + return EXIT_FAILURE; + + if (options.output_tracks) + if (!OutputTracks(*(segment.get()), options, out, &indent)) + return EXIT_FAILURE; + + const mkvparser::Tracks* const tracks = segment->GetTracks(); + if (!tracks) { + fprintf(stderr, "Could not get Tracks.\n"); + return EXIT_FAILURE; + } + + // If Cues are before the clusters output them first. + if (options.output_cues) { + const mkvparser::Cluster* cluster = segment->GetFirst(); + const mkvparser::Cues* const cues = segment->GetCues(); + if (cluster != NULL && cues != NULL) { + if (cues->m_element_start < cluster->m_element_start) { + if (!OutputCues(*segment, *tracks, options, out, &indent)) { + return EXIT_FAILURE; + } + options.output_cues = false; + } + } + } + + if (options.output_clusters) + fprintf(out, "%sClusters (count):%ld\n", indent.indent_str().c_str(), + segment->GetCount()); + + int64_t clusters_size = 0; + FrameStats stats; + vp9_parser::Vp9HeaderParser parser; + vp9_parser::Vp9LevelStats level_stats; + const mkvparser::Cluster* cluster = segment->GetFirst(); + while (cluster != NULL && !cluster->EOS()) { + if (!OutputCluster(*cluster, *tracks, options, out, reader.get(), &indent, + &clusters_size, &stats, &parser, &level_stats)) + return EXIT_FAILURE; + cluster = segment->GetNext(cluster); + } + + if (options.output_clusters_size) + fprintf(out, "%sClusters (size):%" PRId64 "\n", indent.indent_str().c_str(), + clusters_size); + + if (options.output_cues) + if (!OutputCues(*segment, *tracks, options, out, &indent)) + return EXIT_FAILURE; + + // TODO(fgalligan): Add support for VP8. + if (options.output_frame_stats && + stats.minimum_altref_distance != std::numeric_limits::max()) { + const double actual_fps = + stats.frames / + (segment->GetInfo()->GetDuration() / kNanosecondsPerSecond); + const double displayed_fps = + stats.displayed_frames / + (segment->GetInfo()->GetDuration() / kNanosecondsPerSecond); + fprintf(out, "\nActual fps:%g Displayed fps:%g\n", actual_fps, + displayed_fps); + + fprintf(out, "Minimum Altref Distance:%d at:%g seconds\n", + stats.minimum_altref_distance, + stats.min_altref_end_ns / kNanosecondsPerSecond); + + // TODO(fgalligan): Add support for window duration other than 1 second. + const double sec_end = stats.max_window_end_ns / kNanosecondsPerSecond; + const double sec_start = + stats.max_window_end_ns > kNanosecondsPerSecondi ? sec_end - 1.0 : 0.0; + fprintf(out, "Maximum Window:%g-%g seconds Window fps:%" PRId64 "\n", + sec_start, sec_end, stats.max_window_size); + } + + if (options.output_vp9_level) { + level_stats.set_duration(segment->GetInfo()->GetDuration()); + const vp9_parser::Vp9Level level = level_stats.GetLevel(); + fprintf(out, "VP9 Level:%d\n", level); + fprintf( + out, + "mlsr:%" PRId64 " mlps:%" PRId64 " mlpb:%" PRId64 + " abr:%g mcs:%g cr:%g mct:%d" + " mad:%d mrf:%d\n", + level_stats.GetMaxLumaSampleRate(), level_stats.GetMaxLumaPictureSize(), + level_stats.GetMaxLumaPictureBreadth(), level_stats.GetAverageBitRate(), + level_stats.GetMaxCpbSize(), level_stats.GetCompressionRatio(), + level_stats.GetMaxColumnTiles(), level_stats.GetMinimumAltrefDistance(), + level_stats.GetMaxReferenceFrames()); + } + return EXIT_SUCCESS; +} diff --git a/webm_parser/README.md b/webm_parser/README.md new file mode 100644 index 000000000000..f3d37ff56c73 --- /dev/null +++ b/webm_parser/README.md @@ -0,0 +1,325 @@ +# WebM Parser {#mainpage} + +# Introduction + +This WebM parser is a C++11-based parser that aims to be a safe and complete +parser for WebM. It supports all WebM elements (from the old deprecated ones to +the newest ones like `Colour`), including recursive elements like `ChapterAtom` +and `SimpleTag`. It supports incremental parsing; parsing may be stopped at any +point and resumed later as needed. It also supports starting at an arbitrary +WebM element, so parsing need not start from the beginning of the file. + +The parser (`WebmParser`) works by being fed input data from a data source (an +instance of `Reader`) that represents a WebM file. The parser will parse the +WebM data into various data structures that represent the encoded WebM elements, +and then call corresponding `Callback` event methods as the data structures are +parsed. + +# Building + +CMake support has been added to the root libwebm `CMakeLists.txt` file. Simply +enable the `ENABLE_WEBM_PARSER` feature if using the interactive CMake builder, +or alternatively pass the `-DENABLE_WEBM_PARSER:BOOL=ON` flag from the command +line. By default, this parser is not enabled when building libwebm, so you must +explicitly enable it. + +Alternatively, the following illustrates the minimal commands necessary to +compile the code into a static library without CMake: + +```.sh +c++ -Iinclude -I. -std=c++11 -c src/*.cc +ar rcs libwebm.a *.o +``` + +# Using the parser + +There are 3 basic components in the parser that are used: `Reader`, `Callback`, +and `WebmParser`. + +## `Reader` + +The `Reader` interface acts as a data source for the parser. You may subclass it +and implement your own data source if you wish. Alternatively, use the +`FileReader`, `IstreamReader`, or `BufferReader` if you wish to read from a +`FILE*`, `std::istream`, or `std::vector`, respectively. + +The parser supports `Reader` implementations that do short reads. If +`Reader::Skip()` or `Reader::Read()` do a partial read (returning +`Status::kOkPartial`), the parser will call them again in an attempt to read +more data. If no data is available, the `Reader` may return some other status +(like `Status::kWouldBlock`) to indicate that no data is available. In this +situation, the parser will stop parsing and return the status it received. +Parsing may be resumed later when more data is available. + +When the `Reader` has reached the end of the WebM document and no more data is +available, it should return `Status::kEndOfFile`. This will cause parsing to +stop. If the file ends at a valid location (that is, there aren't any elements +that have specified a size that indicates the file ended prematurely), the +parser will translate `Status::kEndOfFile` into `Status::kOkCompleted` and +return it. If the file ends prematurely, the parser will return +`Status::kEndOfFile` to indicate that. + +Note that if the WebM file contains elements that have an unknown size (or a +seek has been performed and the parser doesn't know the size of the root +element(s)), and the parser is parsing them and hits end-of-file, the parser may +still call `Reader::Read()`/`Reader::Skip()` multiple times (even though they've +already reported `Status::kEndOfFile`) as nested parsers terminate parsing. +Because of this, `Reader::Read()`/`Reader::Skip()` implementations should be +able to handle being called multiple times after the file's end has been +reached, and they should consistently return `Status::kEndOfFile`. + +The three provided readers (`FileReader`, `IstreamReader`, and `BufferReader`) +are blocking implementations (they won't return `Status::kWouldBlock`), so if +you're using them the parser will run until it entirely consumes all their data +(unless, of course, you request the parser to stop via `Callback`... see the +next section). + +## `Callback` + +As the parser progresses through the file, it builds objects (see +`webm/dom_types.h`) that represent parsed data structures. The parser then +notifies the `Callback` implementation as objects complete parsing. For some +data structures (like frames or Void elements), the parser notifies the +`Callback` and requests it to consume the data directly from the `Reader` (this +is done for structures that can be large/frequent binary blobs in order to allow +you to read the data directly into the object/type of your choice, rather than +just reading them into a `std::vector` and making you copy it into +a different object if you wanted to work with something other than +`std::vector`). + +The parser was designed to parse the data into objects that are small enough +that the `Callback` can be quickly and frequently notified as soon as the object +is ready, but large enough that the objects received by the `Callback` are still +useful. Having `Callback` events for every tiny integer/float/string/etc. +element would require too much assembly and work to be useful to most users, and +pasing the file into a single DOM tree (or a small handful of large conglomerate +structures) would unnecessarily delay video playback or consume too much memory +on smaller devices. + +The parser may call the following methods while nearly anywhere in the file: + +- `Callback::OnElementBegin()`: This is called for every element that the + parser encounters. This is primarily useful if you want to skip some + elements or build a map of every element in the file. +- `Callback::OnUnknownElement()`: This is called when an element is either not + a valid/recognized WebM element, or it is a WebM element but is improperly + nested (e.g. an EBMLVersion element inside of a Segment element). The parser + doesn't know how to handle the element; it could just skip it but instead + defers to the `Callback` to decide how it should be handled. The default + implementation just skips the element. +- `Callback::OnVoid()`: Void elements can appear anywhere in any master + element. This method will be called to handle the Void element. + +The parser may call the following methods in the proper nesting order, as shown +in the list. A `*Begin()` method will always be matched up with its +corresponding `*End()` method (unless a seek has been performed). The parser +will only call the methods in the proper nesting order as specified in the WebM +DOM. For example, `Callback::OnEbml()` will never be called in between +`Callback::OnSegmentBegin()`/`Callback::OnSegmentEnd()` (since the EBML element +is not a child of the Segment element), and `Callback::OnTrackEntry()` will only +ever be called in between +`Callback::OnSegmentBegin()`/`Callback::OnSegmentEnd()` (since the TrackEntry +element is a (grand-)child of the Segment element and must be contained by a +Segment element). `Callback::OnFrame()` is listed twice because it will be +called to handle frames contained in both SimpleBlock and Block elements. + +- `Callback::OnEbml()` +- `Callback::OnSegmentBegin()` + - `Callback::OnSeek()` + - `Callback::OnInfo()` + - `Callback::OnClusterBegin()` + - `Callback::OnSimpleBlockBegin()` + - `Callback::OnFrame()` + - `Callback::OnSimpleBlockEnd()` + - `Callback::OnBlockGroupBegin()` + - `Callback::OnBlockBegin()` + - `Callback::OnFrame()` + - `Callback::OnBlockEnd()` + - `Callback::OnBlockGroupEnd()` + - `Callback::OnClusterEnd()` + - `Callback::OnTrackEntry()` + - `Callback::OnCuePoint()` + - `Callback::OnEditionEntry()` + - `Callback::OnTag()` +- `Callback::OnSegmentEnd()` + +Only `Callback::OnFrame()` (and no other `Callback` methods) will be called in +between `Callback::OnSimpleBlockBegin()`/`Callback::OnSimpleBlockEnd()` or +`Callback::OnBlockBegin()`/`Callback::OnBlockEnd()`, since the SimpleBlock and +Block elements are not master elements only contain frames. + +Note that seeking into the middle of the file may cause the parser to skip some +`*Begin()` methods. For example, if a seek is performed to a SimpleBlock +element, `Callback::OnSegmentBegin()` and `Callback::OnClusterBegin()` will not +be called. In this situation, the full sequence of callback events would be +(assuming the file ended after the SimpleBlock): +`Callback::OnSimpleBlockBegin()`, `Callback::OnFrame()` (for every frame in the +SimpleBlock), `Callback::OnSimpleBlockEnd()`, `Callback::OnClusterEnd()`, and +`Callback::OnSegmentEnd()`. Since the Cluster and Segment elements were skipped, +the `Cluster` DOM object may have some members marked as absent, and the +`*End()` events for the Cluster and Segment elements will have metadata with +unknown header position, header length, and body size (see `kUnknownHeaderSize`, +`kUnknownElementSize`, and `kUnknownElementPosition`). + +When a `Callback` method has completed, it should return `Status::kOkCompleted` +to allow parsing to continue. If you would like parsing to stop, return any +other status code (except `Status::kEndOfFile`, since that's treated somewhat +specially and is intended for `Reader`s to use), which the parser will return. +If you return a non-parsing-error status code (.e.g. `Status::kOkPartial`, +`Status::kWouldBlock`, etc. or your own status code with a value > 0), parsing +may be resumed again. When parsing is resumed, the parser will call the same +callback method again (and once again, you may return `Status::kOkCompleted` to +let parsing continue or some other value to stop parsing). + +You may subclass the `Callback` element and override methods which you are +interested in receiving events for. By default, methods taking an `Action` +parameter will set it to `Action::kRead` so the entire file is parsed. The +`Callback::OnFrame()` method will just skip over the frame bytes by default. + +## `WebmParser` + +The actual parsing work is done with `WebmParser`. Simply construct a +`WebmParser` and call `WebmParser::Feed()` (providing it a `Callback` and +`Reader` instance) to parse a file. It will return `Status::kOkCompleted` when +the entire file has been successfully parsed. `WebmParser::Feed()` doesn't store +any internal references to the `Callback` or `Reader`. + +If you wish to start parsing from the middle of a file, call +`WebmParser::DidSeek()` before calling `WebmParser::Feed()` to prepare the +parser to receive data starting at an arbitrary point in the file. When seeking, +you should seek to the beginning of a WebM element; seeking to a location that +is not the start of a WebM element (e.g. seeking to a frame, rather than its +containing SimpleBlock/Block element) will cause parsing to fail. Calling +`WebmParser::DidSeek()` will reset the state of the parser and clear any +internal errors, so a `WebmParser` instance may be reused (even if it has +previously failed to parse a file). + +## Building your program + +The following program is a small program that completely parses a file from +stdin: + +```.cc +#include +#include +#include + +int main() { + webm::Callback callback; + webm::FileReader reader(std::freopen(nullptr, "rb", stdin)); + webm::WebmParser parser; + parser.Feed(&callback, &reader); +} +``` + +It completely parses the input file, but we need to make a new class that +derives from `Callback` if we want to receive any parsing events. So if we +change it to: + +```.cc +#include +#include + +#include +#include +#include +#include + +class MyCallback : public webm::Callback { + public: + webm::Status OnElementBegin(const webm::ElementMetadata& metadata, + webm::Action* action) override { + std::cout << "Element ID = 0x" + << std::hex << static_cast(metadata.id); + std::cout << std::dec; // Reset to decimal mode. + std::cout << " at position "; + if (metadata.position == webm::kUnknownElementPosition) { + // The position will only be unknown if we've done a seek. But since we + // aren't seeking in this demo, this will never be the case. However, this + // if-statement is included for completeness. + std::cout << ""; + } else { + std::cout << metadata.position; + } + std::cout << " with header size "; + if (metadata.header_size == webm::kUnknownHeaderSize) { + // The header size will only be unknown if we've done a seek. But since we + // aren't seeking in this demo, this will never be the case. However, this + // if-statement is included for completeness. + std::cout << ""; + } else { + std::cout << metadata.header_size; + } + std::cout << " and body size "; + if (metadata.size == webm::kUnknownElementSize) { + // WebM master elements may have an unknown size, though this is rare. + std::cout << ""; + } else { + std::cout << metadata.size; + } + std::cout << '\n'; + + *action = webm::Action::kRead; + return webm::Status(webm::Status::kOkCompleted); + } +}; + +int main() { + MyCallback callback; + webm::FileReader reader(std::freopen(nullptr, "rb", stdin)); + webm::WebmParser parser; + webm::Status status = parser.Feed(&callback, &reader); + if (status.completed_ok()) { + std::cout << "Parsing successfully completed\n"; + } else { + std::cout << "Parsing failed with status code: " << status.code << '\n'; + } +} +``` + +This will output information about every element in the entire file: it's ID, +position, header size, and body size. The status of the parse is also checked +and reported. + +For a more complete example, see `demo/demo.cc`, which parses an entire file and +prints out all of its information. That example overrides every `Callback` +method to show exactly what information is available while parsing and how to +access it. The example is verbose, but that's primarily due to pretty-printing +and string formatting operations. + +When compiling your program, add the `include` directory to your compiler's +header search paths and link to the compiled library. Be sure your compiler has +C++11 mode enabled (`-std=c++11` in clang++ or g++). + +# Testing + +Unit tests are located in the `tests` directory. Google Test and Google Mock are +used as testing frameworks. Building and running the tests will be supported in +the upcoming CMake scripts, but they can currently be built and run by manually +compiling them (and linking to Google Test and Google Mock). + +# Fuzzing + +The parser has been fuzzed with [AFL](http://lcamtuf.coredump.cx/afl/) and +[libFuzzer](http://llvm.org/docs/LibFuzzer.html). If you wish to fuzz the parser +with AFL or libFuzzer but don't want to write an executable that exercises the +parsing API, you may use `fuzzing/webm_fuzzer.cc`. + +When compiling for fuzzing, define the macro +`WEBM_FUZZER_BYTE_ELEMENT_SIZE_LIMIT` to be some integer in order to limit the +maximum size of ASCII/UTF-8/binary elements. It's too easy for the fuzzer to +generate elements that claim to have a ridiculously massive size, which will +cause allocations to fail or the program to allocate too much memory. AFL will +terminate the process if it allocates too much memory (by default, 50 MB), and +the [Address Sanitizer doesn't throw `std::bad_alloc` when an allocation fails] +(https://github.com/google/sanitizers/issues/295). Defining +`WEBM_FUZZER_BYTE_ELEMENT_SIZE_LIMIT` to a low number (say, 1024) will cause the +ASCII/UTF-8/binary element parsers to return `Status::kNotEnoughMemory` if the +element's size exceeds `WEBM_FUZZER_BYTE_ELEMENT_SIZE_LIMIT`, which will avoid +false positives when fuzzing. The parser expects `std::string` and `std::vector` +to throw `std::bad_alloc` when an allocation fails, which doesn't necessarily +happen due to the fuzzers' limitations. + +You may also define the macro `WEBM_FUZZER_SEEK_FIRST` to have +`fuzzing/webm_fuzzer.cc` call `WebmParser::DidSeek()` before doing any parsing. +This will test the seeking code paths. diff --git a/webm_parser/demo/demo.cc b/webm_parser/demo/demo.cc new file mode 100644 index 000000000000..47ecbb0de166 --- /dev/null +++ b/webm_parser/demo/demo.cc @@ -0,0 +1,1161 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include +#include +#include +#include + +#include "webm/callback.h" +#include "webm/file_reader.h" +#include "webm/status.h" +#include "webm/webm_parser.h" + +// We use pretty much everything in the webm namespace. Just pull +// it all in. +using namespace webm; // NOLINT + +template +std::ostream& PrintUnknownEnumValue(std::ostream& os, T value) { + return os << std::to_string(static_cast(value)) << " (?)"; +} + +// Overloads for operator<< for pretty printing enums. +std::ostream& operator<<(std::ostream& os, Id id) { + switch (id) { + case Id::kEbml: + return os << "EBML"; + case Id::kEbmlVersion: + return os << "EBMLVersion"; + case Id::kEbmlReadVersion: + return os << "EBMLReadVersion"; + case Id::kEbmlMaxIdLength: + return os << "EBMLMaxIDLength"; + case Id::kEbmlMaxSizeLength: + return os << "EBMLMaxSizeLength"; + case Id::kDocType: + return os << "DocType"; + case Id::kDocTypeVersion: + return os << "DocTypeVersion"; + case Id::kDocTypeReadVersion: + return os << "DocTypeReadVersion"; + case Id::kVoid: + return os << "Void"; + case Id::kSegment: + return os << "Segment"; + case Id::kSeekHead: + return os << "SeekHead"; + case Id::kSeek: + return os << "Seek"; + case Id::kSeekId: + return os << "SeekID"; + case Id::kSeekPosition: + return os << "SeekPosition"; + case Id::kInfo: + return os << "Info"; + case Id::kTimecodeScale: + return os << "TimecodeScale"; + case Id::kDuration: + return os << "Duration"; + case Id::kDateUtc: + return os << "DateUTC"; + case Id::kTitle: + return os << "Title"; + case Id::kMuxingApp: + return os << "MuxingApp"; + case Id::kWritingApp: + return os << "WritingApp"; + case Id::kCluster: + return os << "Cluster"; + case Id::kTimecode: + return os << "Timecode"; + case Id::kPrevSize: + return os << "PrevSize"; + case Id::kSimpleBlock: + return os << "SimpleBlock"; + case Id::kBlockGroup: + return os << "BlockGroup"; + case Id::kBlock: + return os << "Block"; + case Id::kBlockVirtual: + return os << "BlockVirtual"; + case Id::kBlockAdditions: + return os << "BlockAdditions"; + case Id::kBlockMore: + return os << "BlockMore"; + case Id::kBlockAddId: + return os << "BlockAddID"; + case Id::kBlockAdditional: + return os << "BlockAdditional"; + case Id::kBlockDuration: + return os << "BlockDuration"; + case Id::kReferenceBlock: + return os << "ReferenceBlock"; + case Id::kDiscardPadding: + return os << "DiscardPadding"; + case Id::kSlices: + return os << "Slices"; + case Id::kTimeSlice: + return os << "TimeSlice"; + case Id::kLaceNumber: + return os << "LaceNumber"; + case Id::kTracks: + return os << "Tracks"; + case Id::kTrackEntry: + return os << "TrackEntry"; + case Id::kTrackNumber: + return os << "TrackNumber"; + case Id::kTrackUid: + return os << "TrackUID"; + case Id::kTrackType: + return os << "TrackType"; + case Id::kFlagEnabled: + return os << "FlagEnabled"; + case Id::kFlagDefault: + return os << "FlagDefault"; + case Id::kFlagForced: + return os << "FlagForced"; + case Id::kFlagLacing: + return os << "FlagLacing"; + case Id::kDefaultDuration: + return os << "DefaultDuration"; + case Id::kName: + return os << "Name"; + case Id::kLanguage: + return os << "Language"; + case Id::kCodecId: + return os << "CodecID"; + case Id::kCodecPrivate: + return os << "CodecPrivate"; + case Id::kCodecName: + return os << "CodecName"; + case Id::kCodecDelay: + return os << "CodecDelay"; + case Id::kSeekPreRoll: + return os << "SeekPreRoll"; + case Id::kVideo: + return os << "Video"; + case Id::kFlagInterlaced: + return os << "FlagInterlaced"; + case Id::kStereoMode: + return os << "StereoMode"; + case Id::kAlphaMode: + return os << "AlphaMode"; + case Id::kPixelWidth: + return os << "PixelWidth"; + case Id::kPixelHeight: + return os << "PixelHeight"; + case Id::kPixelCropBottom: + return os << "PixelCropBottom"; + case Id::kPixelCropTop: + return os << "PixelCropTop"; + case Id::kPixelCropLeft: + return os << "PixelCropLeft"; + case Id::kPixelCropRight: + return os << "PixelCropRight"; + case Id::kDisplayWidth: + return os << "DisplayWidth"; + case Id::kDisplayHeight: + return os << "DisplayHeight"; + case Id::kDisplayUnit: + return os << "DisplayUnit"; + case Id::kAspectRatioType: + return os << "AspectRatioType"; + case Id::kFrameRate: + return os << "FrameRate"; + case Id::kColour: + return os << "Colour"; + case Id::kMatrixCoefficients: + return os << "MatrixCoefficients"; + case Id::kBitsPerChannel: + return os << "BitsPerChannel"; + case Id::kChromaSubsamplingHorz: + return os << "ChromaSubsamplingHorz"; + case Id::kChromaSubsamplingVert: + return os << "ChromaSubsamplingVert"; + case Id::kCbSubsamplingHorz: + return os << "CbSubsamplingHorz"; + case Id::kCbSubsamplingVert: + return os << "CbSubsamplingVert"; + case Id::kChromaSitingHorz: + return os << "ChromaSitingHorz"; + case Id::kChromaSitingVert: + return os << "ChromaSitingVert"; + case Id::kRange: + return os << "Range"; + case Id::kTransferCharacteristics: + return os << "TransferCharacteristics"; + case Id::kPrimaries: + return os << "Primaries"; + case Id::kMaxCll: + return os << "MaxCLL"; + case Id::kMaxFall: + return os << "MaxFALL"; + case Id::kMasteringMetadata: + return os << "MasteringMetadata"; + case Id::kPrimaryRChromaticityX: + return os << "PrimaryRChromaticityX"; + case Id::kPrimaryRChromaticityY: + return os << "PrimaryRChromaticityY"; + case Id::kPrimaryGChromaticityX: + return os << "PrimaryGChromaticityX"; + case Id::kPrimaryGChromaticityY: + return os << "PrimaryGChromaticityY"; + case Id::kPrimaryBChromaticityX: + return os << "PrimaryBChromaticityX"; + case Id::kPrimaryBChromaticityY: + return os << "PrimaryBChromaticityY"; + case Id::kWhitePointChromaticityX: + return os << "WhitePointChromaticityX"; + case Id::kWhitePointChromaticityY: + return os << "WhitePointChromaticityY"; + case Id::kLuminanceMax: + return os << "LuminanceMax"; + case Id::kLuminanceMin: + return os << "LuminanceMin"; + case Id::kProjection: + return os << "Projection"; + case Id::kProjectionType: + return os << "kProjectionType"; + case Id::kProjectionPrivate: + return os << "kProjectionPrivate"; + case Id::kProjectionPoseYaw: + return os << "kProjectionPoseYaw"; + case Id::kProjectionPosePitch: + return os << "kProjectionPosePitch"; + case Id::kProjectionPoseRoll: + return os << "ProjectionPoseRoll"; + case Id::kAudio: + return os << "Audio"; + case Id::kSamplingFrequency: + return os << "SamplingFrequency"; + case Id::kOutputSamplingFrequency: + return os << "OutputSamplingFrequency"; + case Id::kChannels: + return os << "Channels"; + case Id::kBitDepth: + return os << "BitDepth"; + case Id::kContentEncodings: + return os << "ContentEncodings"; + case Id::kContentEncoding: + return os << "ContentEncoding"; + case Id::kContentEncodingOrder: + return os << "ContentEncodingOrder"; + case Id::kContentEncodingScope: + return os << "ContentEncodingScope"; + case Id::kContentEncodingType: + return os << "ContentEncodingType"; + case Id::kContentEncryption: + return os << "ContentEncryption"; + case Id::kContentEncAlgo: + return os << "ContentEncAlgo"; + case Id::kContentEncKeyId: + return os << "ContentEncKeyID"; + case Id::kContentEncAesSettings: + return os << "ContentEncAESSettings"; + case Id::kAesSettingsCipherMode: + return os << "AESSettingsCipherMode"; + case Id::kCues: + return os << "Cues"; + case Id::kCuePoint: + return os << "CuePoint"; + case Id::kCueTime: + return os << "CueTime"; + case Id::kCueTrackPositions: + return os << "CueTrackPositions"; + case Id::kCueTrack: + return os << "CueTrack"; + case Id::kCueClusterPosition: + return os << "CueClusterPosition"; + case Id::kCueRelativePosition: + return os << "CueRelativePosition"; + case Id::kCueDuration: + return os << "CueDuration"; + case Id::kCueBlockNumber: + return os << "CueBlockNumber"; + case Id::kChapters: + return os << "Chapters"; + case Id::kEditionEntry: + return os << "EditionEntry"; + case Id::kChapterAtom: + return os << "ChapterAtom"; + case Id::kChapterUid: + return os << "ChapterUID"; + case Id::kChapterStringUid: + return os << "ChapterStringUID"; + case Id::kChapterTimeStart: + return os << "ChapterTimeStart"; + case Id::kChapterTimeEnd: + return os << "ChapterTimeEnd"; + case Id::kChapterDisplay: + return os << "ChapterDisplay"; + case Id::kChapString: + return os << "ChapString"; + case Id::kChapLanguage: + return os << "ChapLanguage"; + case Id::kChapCountry: + return os << "ChapCountry"; + case Id::kTags: + return os << "Tags"; + case Id::kTag: + return os << "Tag"; + case Id::kTargets: + return os << "Targets"; + case Id::kTargetTypeValue: + return os << "TargetTypeValue"; + case Id::kTargetType: + return os << "TargetType"; + case Id::kTagTrackUid: + return os << "TagTrackUID"; + case Id::kSimpleTag: + return os << "SimpleTag"; + case Id::kTagName: + return os << "TagName"; + case Id::kTagLanguage: + return os << "TagLanguage"; + case Id::kTagDefault: + return os << "TagDefault"; + case Id::kTagString: + return os << "TagString"; + case Id::kTagBinary: + return os << "TagBinary"; + default: + return PrintUnknownEnumValue(os, id); + } +} + +std::ostream& operator<<(std::ostream& os, Lacing value) { + switch (value) { + case Lacing::kNone: + return os << "0 (none)"; + case Lacing::kXiph: + return os << "2 (Xiph)"; + case Lacing::kFixed: + return os << "4 (fixed)"; + case Lacing::kEbml: + return os << "6 (EBML)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, MatrixCoefficients value) { + switch (value) { + case MatrixCoefficients::kRgb: + return os << "0 (identity, RGB/XYZ)"; + case MatrixCoefficients::kBt709: + return os << "1 (Rec. ITU-R BT.709-5)"; + case MatrixCoefficients::kUnspecified: + return os << "2 (unspecified)"; + case MatrixCoefficients::kFcc: + return os << "4 (US FCC)"; + case MatrixCoefficients::kBt470Bg: + return os << "5 (Rec. ITU-R BT.470-6 System B, G)"; + case MatrixCoefficients::kSmpte170M: + return os << "6 (SMPTE 170M)"; + case MatrixCoefficients::kSmpte240M: + return os << "7 (SMPTE 240M)"; + case MatrixCoefficients::kYCgCo: + return os << "8 (YCgCo)"; + case MatrixCoefficients::kBt2020NonconstantLuminance: + return os << "9 (Rec. ITU-R BT.2020, non-constant luma)"; + case MatrixCoefficients::kBt2020ConstantLuminance: + return os << "10 (Rec. ITU-R BT.2020 , constant luma)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, Range value) { + switch (value) { + case Range::kUnspecified: + return os << "0 (unspecified)"; + case Range::kBroadcast: + return os << "1 (broadcast)"; + case Range::kFull: + return os << "2 (full)"; + case Range::kDerived: + return os << "3 (defined by MatrixCoefficients/TransferCharacteristics)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, TransferCharacteristics value) { + switch (value) { + case TransferCharacteristics::kBt709: + return os << "1 (Rec. ITU-R BT.709-6)"; + case TransferCharacteristics::kUnspecified: + return os << "2 (unspecified)"; + case TransferCharacteristics::kGamma22curve: + return os << "4 (gamma 2.2, Rec. ITU‑R BT.470‑6 System M)"; + case TransferCharacteristics::kGamma28curve: + return os << "5 (gamma 2.8, Rec. ITU‑R BT.470-6 System B, G)"; + case TransferCharacteristics::kSmpte170M: + return os << "6 (SMPTE 170M)"; + case TransferCharacteristics::kSmpte240M: + return os << "7 (SMPTE 240M)"; + case TransferCharacteristics::kLinear: + return os << "8 (linear)"; + case TransferCharacteristics::kLog: + return os << "9 (log, 100:1 range)"; + case TransferCharacteristics::kLogSqrt: + return os << "10 (log, 316.2:1 range)"; + case TransferCharacteristics::kIec6196624: + return os << "11 (IEC 61966-2-4)"; + case TransferCharacteristics::kBt1361ExtendedColourGamut: + return os << "12 (Rec. ITU-R BT.1361, extended colour gamut)"; + case TransferCharacteristics::kIec6196621: + return os << "13 (IEC 61966-2-1, sRGB or sYCC)"; + case TransferCharacteristics::k10BitBt2020: + return os << "14 (Rec. ITU-R BT.2020-2, 10-bit)"; + case TransferCharacteristics::k12BitBt2020: + return os << "15 (Rec. ITU-R BT.2020-2, 12-bit)"; + case TransferCharacteristics::kSmpteSt2084: + return os << "16 (SMPTE ST 2084)"; + case TransferCharacteristics::kSmpteSt4281: + return os << "17 (SMPTE ST 428-1)"; + case TransferCharacteristics::kAribStdB67Hlg: + return os << "18 (ARIB STD-B67/Rec. ITU-R BT.[HDR-TV] HLG)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, Primaries value) { + switch (value) { + case Primaries::kBt709: + return os << "1 (Rec. ITU‑R BT.709-6)"; + case Primaries::kUnspecified: + return os << "2 (unspecified)"; + case Primaries::kBt470M: + return os << "4 (Rec. ITU‑R BT.470‑6 System M)"; + case Primaries::kBt470Bg: + return os << "5 (Rec. ITU‑R BT.470‑6 System B, G)"; + case Primaries::kSmpte170M: + return os << "6 (SMPTE 170M)"; + case Primaries::kSmpte240M: + return os << "7 (SMPTE 240M)"; + case Primaries::kFilm: + return os << "8 (generic film)"; + case Primaries::kBt2020: + return os << "9 (Rec. ITU-R BT.2020-2)"; + case Primaries::kSmpteSt4281: + return os << "10 (SMPTE ST 428-1)"; + case Primaries::kJedecP22Phosphors: + return os << "22 (EBU Tech. 3213-E/JEDEC P22 phosphors)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, ProjectionType value) { + switch (value) { + case ProjectionType::kRectangular: + return os << "0 (rectangular)"; + case ProjectionType::kEquirectangular: + return os << "1 (equirectangular)"; + case ProjectionType::kCubeMap: + return os << "2 (cube map)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, FlagInterlaced value) { + switch (value) { + case FlagInterlaced::kUnspecified: + return os << "0 (unspecified)"; + case FlagInterlaced::kInterlaced: + return os << "1 (interlaced)"; + case FlagInterlaced::kProgressive: + return os << "2 (progressive)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, StereoMode value) { + switch (value) { + case StereoMode::kMono: + return os << "0 (mono)"; + case StereoMode::kSideBySideLeftFirst: + return os << "1 (side-by-side, left eye first)"; + case StereoMode::kTopBottomRightFirst: + return os << "2 (top-bottom, right eye first)"; + case StereoMode::kTopBottomLeftFirst: + return os << "3 (top-bottom, left eye first)"; + case StereoMode::kCheckboardRightFirst: + return os << "4 (checkboard, right eye first)"; + case StereoMode::kCheckboardLeftFirst: + return os << "5 (checkboard, left eye first)"; + case StereoMode::kRowInterleavedRightFirst: + return os << "6 (row interleaved, right eye first)"; + case StereoMode::kRowInterleavedLeftFirst: + return os << "7 (row interleaved, left eye first)"; + case StereoMode::kColumnInterleavedRightFirst: + return os << "8 (column interleaved, right eye first)"; + case StereoMode::kColumnInterleavedLeftFirst: + return os << "9 (column interleaved, left eye first)"; + case StereoMode::kAnaglyphCyanRed: + return os << "10 (anaglyph, cyan/red)"; + case StereoMode::kSideBySideRightFirst: + return os << "11 (side-by-side, right eye first)"; + case StereoMode::kAnaglyphGreenMagenta: + return os << "12 (anaglyph, green/magenta)"; + case StereoMode::kBlockLacedLeftFirst: + return os << "13 (block laced, left eye first)"; + case StereoMode::kBlockLacedRightFirst: + return os << "14 (block laced, right eye first)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, DisplayUnit value) { + switch (value) { + case DisplayUnit::kPixels: + return os << "0 (pixels)"; + case DisplayUnit::kCentimeters: + return os << "1 (centimeters)"; + case DisplayUnit::kInches: + return os << "2 (inches)"; + case DisplayUnit::kDisplayAspectRatio: + return os << "3 (display aspect ratio)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, AspectRatioType value) { + switch (value) { + case AspectRatioType::kFreeResizing: + return os << "0 (free resizing)"; + case AspectRatioType::kKeep: + return os << "1 (keep aspect ratio)"; + case AspectRatioType::kFixed: + return os << "2 (fixed)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, AesSettingsCipherMode value) { + switch (value) { + case AesSettingsCipherMode::kCtr: + return os << "1 (CTR)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, ContentEncAlgo value) { + switch (value) { + case ContentEncAlgo::kOnlySigned: + return os << "0 (only signed, not encrypted)"; + case ContentEncAlgo::kDes: + return os << "1 (DES)"; + case ContentEncAlgo::k3Des: + return os << "2 (3DES)"; + case ContentEncAlgo::kTwofish: + return os << "3 (Twofish)"; + case ContentEncAlgo::kBlowfish: + return os << "4 (Blowfish)"; + case ContentEncAlgo::kAes: + return os << "5 (AES)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, ContentEncodingType value) { + switch (value) { + case ContentEncodingType::kCompression: + return os << "0 (compression)"; + case ContentEncodingType::kEncryption: + return os << "1 (encryption)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +std::ostream& operator<<(std::ostream& os, TrackType value) { + switch (value) { + case TrackType::kVideo: + return os << "1 (video)"; + case TrackType::kAudio: + return os << "2 (audio)"; + case TrackType::kComplex: + return os << "3 (complex)"; + case TrackType::kLogo: + return os << "16 (logo)"; + case TrackType::kSubtitle: + return os << "17 (subtitle)"; + case TrackType::kButtons: + return os << "18 (buttons)"; + case TrackType::kControl: + return os << "32 (control)"; + default: + return PrintUnknownEnumValue(os, value); + } +} + +// For binary elements, just print out its size. +std::ostream& operator<<(std::ostream& os, + const std::vector& value) { + return os << '<' << value.size() << " bytes>"; +} + +class DemoCallback : public Callback { + public: + int indent = 0; + int spaces_per_indent = 2; + + void PrintElementMetadata(const std::string& name, + const ElementMetadata& metadata) { + // Since we aren't doing any seeking in this demo, we don't have to worry + // about kUnknownHeaderSize or kUnknownElementPosition when adding the + // position and sizes. + const std::uint64_t header_start = metadata.position; + const std::uint64_t header_end = header_start + metadata.header_size; + const std::uint64_t body_start = header_end; + std::cout << std::string(indent * spaces_per_indent, ' ') << name; + // The ContentEncAESSettings element has the longest name (out of all other + // master elements) at 21 characters. It's also the deepest master element + // at a level of 6. Insert enough whitespace so there's room for it. + std::cout << std::string(21 + 6 * spaces_per_indent - + indent * spaces_per_indent - name.size(), + ' ') + << " header: [" << header_start << ", " << header_end + << ") body: [" << body_start << ", "; + if (metadata.size != kUnknownElementSize) { + const std::uint64_t body_end = body_start + metadata.size; + std::cout << body_end; + } else { + std::cout << '?'; + } + std::cout << ")\n"; + } + + template + void PrintMandatoryElement(const std::string& name, + const Element& element) { + std::cout << std::string(indent * spaces_per_indent, ' ') << name; + if (!element.is_present()) { + std::cout << " (implicit)"; + } + std::cout << ": " << element.value() << '\n'; + } + + template + void PrintMandatoryElement(const std::string& name, + const std::vector>& elements) { + for (const Element& element : elements) { + PrintMandatoryElement(name, element); + } + } + + template + void PrintOptionalElement(const std::string& name, + const Element& element) { + if (element.is_present()) { + std::cout << std::string(indent * spaces_per_indent, ' ') << name << ": " + << element.value() << '\n'; + } + } + + template + void PrintOptionalElement(const std::string& name, + const std::vector>& elements) { + for (const Element& element : elements) { + PrintOptionalElement(name, element); + } + } + + void PrintMasterElement(const BlockAdditions& block_additions) { + PrintMasterElement("BlockMore", block_additions.block_mores); + } + + void PrintMasterElement(const BlockMore& block_more) { + PrintMandatoryElement("BlockAddID", block_more.id); + PrintMandatoryElement("BlockAdditional", block_more.data); + } + + void PrintMasterElement(const Slices& slices) { + PrintMasterElement("TimeSlice", slices.slices); + } + + void PrintMasterElement(const TimeSlice& time_slice) { + PrintOptionalElement("LaceNumber", time_slice.lace_number); + } + + void PrintMasterElement(const Video& video) { + PrintMandatoryElement("FlagInterlaced", video.interlaced); + PrintOptionalElement("StereoMode", video.stereo_mode); + PrintOptionalElement("AlphaMode", video.alpha_mode); + PrintMandatoryElement("PixelWidth", video.pixel_width); + PrintMandatoryElement("PixelHeight", video.pixel_height); + PrintOptionalElement("PixelCropBottom", video.pixel_crop_bottom); + PrintOptionalElement("PixelCropTop", video.pixel_crop_top); + PrintOptionalElement("PixelCropLeft", video.pixel_crop_left); + PrintOptionalElement("PixelCropRight", video.pixel_crop_right); + PrintOptionalElement("DisplayWidth", video.display_width); + PrintOptionalElement("DisplayHeight", video.display_height); + PrintOptionalElement("DisplayUnit", video.display_unit); + PrintOptionalElement("AspectRatioType", video.aspect_ratio_type); + PrintOptionalElement("FrameRate", video.frame_rate); + PrintMasterElement("Colour", video.colour); + PrintMasterElement("Projection", video.projection); + } + + void PrintMasterElement(const Colour& colour) { + PrintOptionalElement("MatrixCoefficients", colour.matrix_coefficients); + PrintOptionalElement("BitsPerChannel", colour.bits_per_channel); + PrintOptionalElement("ChromaSubsamplingHorz", colour.chroma_subsampling_x); + PrintOptionalElement("ChromaSubsamplingVert", colour.chroma_subsampling_y); + PrintOptionalElement("CbSubsamplingHorz", colour.cb_subsampling_x); + PrintOptionalElement("CbSubsamplingVert", colour.cb_subsampling_y); + PrintOptionalElement("ChromaSitingHorz", colour.chroma_siting_x); + PrintOptionalElement("ChromaSitingVert", colour.chroma_siting_y); + PrintOptionalElement("Range", colour.range); + PrintOptionalElement("TransferCharacteristics", + colour.transfer_characteristics); + PrintOptionalElement("Primaries", colour.primaries); + PrintOptionalElement("MaxCLL", colour.max_cll); + PrintOptionalElement("MaxFALL", colour.max_fall); + PrintMasterElement("MasteringMetadata", colour.mastering_metadata); + } + + void PrintMasterElement(const MasteringMetadata& mastering_metadata) { + PrintOptionalElement("PrimaryRChromaticityX", + mastering_metadata.primary_r_chromaticity_x); + PrintOptionalElement("PrimaryRChromaticityY", + mastering_metadata.primary_r_chromaticity_y); + PrintOptionalElement("PrimaryGChromaticityX", + mastering_metadata.primary_g_chromaticity_x); + PrintOptionalElement("PrimaryGChromaticityY", + mastering_metadata.primary_g_chromaticity_y); + PrintOptionalElement("PrimaryBChromaticityX", + mastering_metadata.primary_b_chromaticity_x); + PrintOptionalElement("PrimaryBChromaticityY", + mastering_metadata.primary_b_chromaticity_y); + PrintOptionalElement("WhitePointChromaticityX", + mastering_metadata.white_point_chromaticity_x); + PrintOptionalElement("WhitePointChromaticityY", + mastering_metadata.white_point_chromaticity_y); + PrintOptionalElement("LuminanceMax", mastering_metadata.luminance_max); + PrintOptionalElement("LuminanceMin", mastering_metadata.luminance_min); + } + + void PrintMasterElement(const Projection& projection) { + PrintMandatoryElement("ProjectionType", projection.type); + PrintOptionalElement("ProjectionPrivate", projection.projection_private); + PrintMandatoryElement("ProjectionPoseYaw", projection.pose_yaw); + PrintMandatoryElement("ProjectionPosePitch", projection.pose_pitch); + PrintMandatoryElement("ProjectionPoseRoll", projection.pose_roll); + } + + void PrintMasterElement(const Audio& audio) { + PrintMandatoryElement("SamplingFrequency", audio.sampling_frequency); + PrintOptionalElement("OutputSamplingFrequency", audio.output_frequency); + PrintMandatoryElement("Channels", audio.channels); + PrintOptionalElement("BitDepth", audio.bit_depth); + } + + void PrintMasterElement(const ContentEncodings& content_encodings) { + PrintMasterElement("ContentEncoding", content_encodings.encodings); + } + + void PrintMasterElement(const ContentEncoding& content_encoding) { + PrintMandatoryElement("ContentEncodingOrder", content_encoding.order); + PrintMandatoryElement("ContentEncodingScope", content_encoding.scope); + PrintMandatoryElement("ContentEncodingType", content_encoding.type); + PrintMasterElement("ContentEncryption", content_encoding.encryption); + } + + void PrintMasterElement(const ContentEncryption& content_encryption) { + PrintOptionalElement("ContentEncAlgo", content_encryption.algorithm); + PrintOptionalElement("ContentEncKeyID", content_encryption.key_id); + PrintMasterElement("ContentEncAESSettings", + content_encryption.aes_settings); + } + + void PrintMasterElement( + const ContentEncAesSettings& content_enc_aes_settings) { + PrintMandatoryElement("AESSettingsCipherMode", + content_enc_aes_settings.aes_settings_cipher_mode); + } + + void PrintMasterElement(const CueTrackPositions& cue_track_positions) { + PrintMandatoryElement("CueTrack", cue_track_positions.track); + PrintMandatoryElement("CueClusterPosition", + cue_track_positions.cluster_position); + PrintOptionalElement("CueRelativePosition", + cue_track_positions.relative_position); + PrintOptionalElement("CueDuration", cue_track_positions.duration); + PrintOptionalElement("CueBlockNumber", cue_track_positions.block_number); + } + + void PrintMasterElement(const ChapterAtom& chapter_atom) { + PrintMandatoryElement("ChapterUID", chapter_atom.uid); + PrintOptionalElement("ChapterStringUID", chapter_atom.string_uid); + PrintMandatoryElement("ChapterTimeStart", chapter_atom.time_start); + PrintOptionalElement("ChapterTimeEnd", chapter_atom.time_end); + PrintMasterElement("ChapterDisplay", chapter_atom.displays); + PrintMasterElement("ChapterAtom", chapter_atom.atoms); + } + + void PrintMasterElement(const ChapterDisplay& chapter_display) { + PrintMandatoryElement("ChapString", chapter_display.string); + PrintMandatoryElement("ChapLanguage", chapter_display.languages); + PrintOptionalElement("ChapCountry", chapter_display.countries); + } + + void PrintMasterElement(const Targets& targets) { + PrintOptionalElement("TargetTypeValue", targets.type_value); + PrintOptionalElement("TargetType", targets.type); + PrintMandatoryElement("TagTrackUID", targets.track_uids); + } + + void PrintMasterElement(const SimpleTag& simple_tag) { + PrintMandatoryElement("TagName", simple_tag.name); + PrintMandatoryElement("TagLanguage", simple_tag.language); + PrintMandatoryElement("TagDefault", simple_tag.is_default); + PrintOptionalElement("TagString", simple_tag.string); + PrintOptionalElement("TagBinary", simple_tag.binary); + PrintMasterElement("SimpleTag", simple_tag.tags); + } + + // When printing a master element that's wrapped in Element<>, peel off the + // Element<> wrapper and print the underlying master element if it's present. + template + void PrintMasterElement(const std::string& name, const Element& element) { + if (element.is_present()) { + std::cout << std::string(indent * spaces_per_indent, ' ') << name << "\n"; + ++indent; + PrintMasterElement(element.value()); + --indent; + } + } + + template + void PrintMasterElement(const std::string& name, + const std::vector>& elements) { + for (const Element& element : elements) { + PrintMasterElement(name, element); + } + } + + template + void PrintValue(const std::string& name, const T& value) { + std::cout << std::string(indent * spaces_per_indent, ' ') << name << ": " + << value << '\n'; + } + + Status OnElementBegin(const ElementMetadata& metadata, + Action* action) override { + // Print out metadata for some level 1 elements that don't have explicit + // callbacks. + switch (metadata.id) { + case Id::kSeekHead: + indent = 1; + PrintElementMetadata("SeekHead", metadata); + break; + case Id::kTracks: + indent = 1; + PrintElementMetadata("Tracks", metadata); + break; + case Id::kCues: + indent = 1; + PrintElementMetadata("Cues", metadata); + break; + case Id::kChapters: + indent = 1; + PrintElementMetadata("Chapters", metadata); + break; + case Id::kTags: + indent = 1; + PrintElementMetadata("Tags", metadata); + break; + default: + break; + } + + *action = Action::kRead; + return Status(Status::kOkCompleted); + } + + Status OnUnknownElement(const ElementMetadata& metadata, Reader* reader, + std::uint64_t* bytes_remaining) override { + // Output unknown elements without any indentation because we aren't + // tracking which element contains them. + int original_indent = indent; + indent = 0; + PrintElementMetadata("UNKNOWN_ELEMENT!", metadata); + indent = original_indent; + // The base class's implementation will just skip the element via + // Reader::Skip(). + return Callback::OnUnknownElement(metadata, reader, bytes_remaining); + } + + Status OnEbml(const ElementMetadata& metadata, const Ebml& ebml) override { + indent = 0; + PrintElementMetadata("EBML", metadata); + indent = 1; + PrintMandatoryElement("EBMLVersion", ebml.ebml_version); + PrintMandatoryElement("EBMLReadVersion", ebml.ebml_read_version); + PrintMandatoryElement("EBMLMaxIDLength", ebml.ebml_max_id_length); + PrintMandatoryElement("EBMLMaxSizeLength", ebml.ebml_max_size_length); + PrintMandatoryElement("DocType", ebml.doc_type); + PrintMandatoryElement("DocTypeVersion", ebml.doc_type_version); + PrintMandatoryElement("DocTypeReadVersion", ebml.doc_type_read_version); + return Status(Status::kOkCompleted); + } + + Status OnVoid(const ElementMetadata& metadata, Reader* reader, + std::uint64_t* bytes_remaining) override { + // Output Void elements without any indentation because we aren't tracking + // which element contains them. + int original_indent = indent; + indent = 0; + PrintElementMetadata("Void", metadata); + indent = original_indent; + // The base class's implementation will just skip the element via + // Reader::Skip(). + return Callback::OnVoid(metadata, reader, bytes_remaining); + } + + Status OnSegmentBegin(const ElementMetadata& metadata, + Action* action) override { + indent = 0; + PrintElementMetadata("Segment", metadata); + indent = 1; + *action = Action::kRead; + return Status(Status::kOkCompleted); + } + + Status OnSeek(const ElementMetadata& metadata, const Seek& seek) override { + indent = 2; + PrintElementMetadata("Seek", metadata); + indent = 3; + PrintMandatoryElement("SeekID", seek.id); + PrintMandatoryElement("SeekPosition", seek.position); + return Status(Status::kOkCompleted); + } + + Status OnInfo(const ElementMetadata& metadata, const Info& info) override { + indent = 1; + PrintElementMetadata("Info", metadata); + indent = 2; + PrintMandatoryElement("TimecodeScale", info.timecode_scale); + PrintOptionalElement("Duration", info.duration); + PrintOptionalElement("DateUTC", info.date_utc); + PrintOptionalElement("Title", info.title); + PrintOptionalElement("MuxingApp", info.muxing_app); + PrintOptionalElement("WritingApp", info.writing_app); + return Status(Status::kOkCompleted); + } + + Status OnClusterBegin(const ElementMetadata& metadata, const Cluster& cluster, + Action* action) override { + indent = 1; + PrintElementMetadata("Cluster", metadata); + // A properly muxed file will have Timecode and PrevSize first before any + // SimpleBlock or BlockGroups. The parser takes advantage of this and delays + // calling OnClusterBegin() until it hits the first SimpleBlock or + // BlockGroup child (or the Cluster ends if it's empty). It's possible for + // an improperly muxed file to have Timecode or PrevSize after the first + // block, in which case they'll be absent here and may be accessed in + // OnClusterEnd() when the Cluster and all its children have been fully + // parsed. In this demo we assume the file has been properly muxed. + indent = 2; + PrintMandatoryElement("Timecode", cluster.timecode); + PrintOptionalElement("PrevSize", cluster.previous_size); + *action = Action::kRead; + return Status(Status::kOkCompleted); + } + + Status OnSimpleBlockBegin(const ElementMetadata& metadata, + const SimpleBlock& simple_block, + Action* action) override { + indent = 2; + PrintElementMetadata("SimpleBlock", metadata); + indent = 3; + PrintValue("track number", simple_block.track_number); + PrintValue("frames", simple_block.num_frames); + PrintValue("timecode", simple_block.timecode); + PrintValue("lacing", simple_block.lacing); + std::string flags = (simple_block.is_visible) ? "visible" : "invisible"; + if (simple_block.is_key_frame) + flags += ", key frame"; + if (simple_block.is_discardable) + flags += ", discardable"; + PrintValue("flags", flags); + *action = Action::kRead; + return Status(Status::kOkCompleted); + } + + Status OnSimpleBlockEnd(const ElementMetadata& /* metadata */, + const SimpleBlock& /* simple_block */) override { + return Status(Status::kOkCompleted); + } + + Status OnBlockGroupBegin(const ElementMetadata& metadata, + Action* action) override { + indent = 2; + PrintElementMetadata("BlockGroup", metadata); + *action = Action::kRead; + return Status(Status::kOkCompleted); + } + + Status OnBlockBegin(const ElementMetadata& metadata, const Block& block, + Action* action) override { + indent = 3; + PrintElementMetadata("Block", metadata); + indent = 4; + PrintValue("track number", block.track_number); + PrintValue("frames", block.num_frames); + PrintValue("timecode", block.timecode); + PrintValue("lacing", block.lacing); + PrintValue("flags", (block.is_visible) ? "visible" : "invisible"); + *action = Action::kRead; + return Status(Status::kOkCompleted); + } + + Status OnBlockEnd(const ElementMetadata& /* metadata */, + const Block& /* block */) override { + return Status(Status::kOkCompleted); + } + + Status OnBlockGroupEnd(const ElementMetadata& /* metadata */, + const BlockGroup& block_group) override { + if (block_group.virtual_block.is_present()) { + std::cout << std::string(indent * spaces_per_indent, ' ') + << "BlockVirtual\n"; + indent = 4; + PrintValue("track number", + block_group.virtual_block.value().track_number); + PrintValue("timecode", block_group.virtual_block.value().timecode); + } + indent = 3; + PrintMasterElement("BlockAdditions", block_group.additions); + PrintOptionalElement("BlockDuration", block_group.duration); + PrintOptionalElement("ReferenceBlock", block_group.references); + PrintOptionalElement("DiscardPadding", block_group.discard_padding); + PrintMasterElement("Slices", block_group.slices); + // BlockGroup::block has been set, but we've already printed it in + // OnBlockBegin(). + return Status(Status::kOkCompleted); + } + + Status OnFrame(const FrameMetadata& metadata, Reader* reader, + std::uint64_t* bytes_remaining) override { + PrintValue("frame byte range", + '[' + std::to_string(metadata.position) + ", " + + std::to_string(metadata.position + metadata.size) + ')'); + // The base class's implementation will just skip the frame via + // Reader::Skip(). + return Callback::OnFrame(metadata, reader, bytes_remaining); + } + + Status OnClusterEnd(const ElementMetadata& /* metadata */, + const Cluster& /* cluster */) override { + // The Cluster and all its children have been fully parsed at this point. If + // the file wasn't properly muxed and Timecode or PrevSize were missing in + // OnClusterBegin(), they'll be set here (if the Cluster contained them). In + // this demo we already handled them, though. + return Status(Status::kOkCompleted); + } + + Status OnTrackEntry(const ElementMetadata& metadata, + const TrackEntry& track_entry) override { + indent = 2; + PrintElementMetadata("TrackEntry", metadata); + indent = 3; + PrintMandatoryElement("TrackNumber", track_entry.track_number); + PrintMandatoryElement("TrackUID", track_entry.track_uid); + PrintMandatoryElement("TrackType", track_entry.track_type); + PrintMandatoryElement("FlagEnabled", track_entry.is_enabled); + PrintMandatoryElement("FlagDefault", track_entry.is_default); + PrintMandatoryElement("FlagForced", track_entry.is_forced); + PrintMandatoryElement("FlagLacing", track_entry.uses_lacing); + PrintOptionalElement("DefaultDuration", track_entry.default_duration); + PrintOptionalElement("Name", track_entry.name); + PrintOptionalElement("Language", track_entry.language); + PrintMandatoryElement("CodecID", track_entry.codec_id); + PrintOptionalElement("CodecPrivate", track_entry.codec_private); + PrintOptionalElement("CodecName", track_entry.codec_name); + PrintOptionalElement("CodecDelay", track_entry.codec_delay); + PrintMandatoryElement("SeekPreRoll", track_entry.seek_pre_roll); + PrintMasterElement("Video", track_entry.video); + PrintMasterElement("Audio", track_entry.audio); + PrintMasterElement("ContentEncodings", track_entry.content_encodings); + return Status(Status::kOkCompleted); + } + + Status OnCuePoint(const ElementMetadata& metadata, + const CuePoint& cue_point) override { + indent = 2; + PrintElementMetadata("CuePoint", metadata); + indent = 3; + PrintMandatoryElement("CueTime", cue_point.time); + PrintMasterElement("CueTrackPositions", cue_point.cue_track_positions); + return Status(Status::kOkCompleted); + } + + Status OnEditionEntry(const ElementMetadata& metadata, + const EditionEntry& edition_entry) override { + indent = 2; + PrintElementMetadata("EditionEntry", metadata); + indent = 3; + PrintMasterElement("ChapterAtom", edition_entry.atoms); + return Status(Status::kOkCompleted); + } + + Status OnTag(const ElementMetadata& metadata, const Tag& tag) override { + indent = 2; + PrintElementMetadata("Tag", metadata); + indent = 3; + PrintMasterElement("Targets", tag.targets); + PrintMasterElement("SimpleTag", tag.tags); + return Status(Status::kOkCompleted); + } + + Status OnSegmentEnd(const ElementMetadata& /* metadata */) override { + return Status(Status::kOkCompleted); + } +}; + +int main(int argc, char* argv[]) { + if ((argc != 1 && argc != 2) || + (argc == 2 && argv[1] == std::string("--help"))) { + std::cerr << "Usage:\n" + << argv[0] << " [path-to-webm-file]\n\n" + << "Prints info for the WebM file specified in the command line. " + "If no file is\n" + << "specified, stdin is used as input.\n"; + return EXIT_FAILURE; + } + + FILE* file = (argc == 2) ? std::fopen(argv[1], "rb") + : std::freopen(nullptr, "rb", stdin); + if (!file) { + std::cerr << "File cannot be opened\n"; + return EXIT_FAILURE; + } + + FileReader reader(file); + DemoCallback callback; + WebmParser parser; + Status status = parser.Feed(&callback, &reader); + if (!status.completed_ok()) { + std::cerr << "Parsing error; status code: " << status.code << '\n'; + return EXIT_FAILURE; + } + + return 0; +} diff --git a/webm_parser/doxygen.config b/webm_parser/doxygen.config new file mode 100644 index 000000000000..f2aae8a0edab --- /dev/null +++ b/webm_parser/doxygen.config @@ -0,0 +1,319 @@ +# Doxyfile 1.8.11 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "WebM Parser" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = MatroskaID{1}="[\1](https://www.webmproject.org/docs/container/#\1) ([Matroska definition](https://matroska.org/technical/specs/index.html#\1))" \ + WebMID{1}="[\1](https://www.webmproject.org/docs/container/#\1)" \ + WebMTable{7}="| Type | Level | Mandatory | Multiple | Recursive | Value range | Default value |\n| ---- | ----- | --------- | -------- | --------- | ----------- | ------------- |\n| \1 | \2 | \3 | \4 | \5 | \6 | \7 |\n" +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = README.md include/webm +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = README.md +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/webm_parser/fuzzing/corpus/00805c2543756a5fd85652d03bfbbd2eb6192ca5 b/webm_parser/fuzzing/corpus/00805c2543756a5fd85652d03bfbbd2eb6192ca5 new file mode 100644 index 000000000000..d78507e0e3bf --- /dev/null +++ b/webm_parser/fuzzing/corpus/00805c2543756a5fd85652d03bfbbd2eb6192ca5 @@ -0,0 +1 @@ +S€g‘T®kŒ®Šm€‡b@„P1S€g”T®k®m€Šb@‡P5„Gáÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/00d120eb143bb02c48d7c863e5826d2ad1a6da4b b/webm_parser/fuzzing/corpus/00d120eb143bb02c48d7c863e5826d2ad1a6da4b new file mode 100644 index 000000000000..2d4403c7df20 --- /dev/null +++ b/webm_parser/fuzzing/corpus/00d120eb143bb02c48d7c863e5826d2ad1a6da4b @@ -0,0 +1 @@ +S€g‹T®k†®„c¢! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/018dee8285e9e20ca3996bb2dc0284b5c57ba75a b/webm_parser/fuzzing/corpus/018dee8285e9e20ca3996bb2dc0284b5c57ba75a new file mode 100644 index 000000000000..29205220f5dd --- /dev/null +++ b/webm_parser/fuzzing/corpus/018dee8285e9e20ca3996bb2dc0284b5c57ba75a @@ -0,0 +1 @@ +S€g…C¶u€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/020edb59637c1e6439f19aa3a5a9d50c3377dbe9 b/webm_parser/fuzzing/corpus/020edb59637c1e6439f19aa3a5a9d50c3377dbe9 new file mode 100644 index 000000000000..c19460644f05 --- /dev/null +++ b/webm_parser/fuzzing/corpus/020edb59637c1e6439f19aa3a5a9d50c3377dbe9 @@ -0,0 +1 @@ +S€g“T®kŽ®Œm€‰b@†P3ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/0247ce2b1a71752a3af11e1065ca90afa0df9d30 b/webm_parser/fuzzing/corpus/0247ce2b1a71752a3af11e1065ca90afa0df9d30 new file mode 100644 index 000000000000..42b852596791 --- /dev/null +++ b/webm_parser/fuzzing/corpus/0247ce2b1a71752a3af11e1065ca90afa0df9d30 @@ -0,0 +1 @@ +S€gC¶u‹ ‰Ž‡è…̃ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/028b19f7d79f5da7a2af13a0c1e2d13f7eaf24cd b/webm_parser/fuzzing/corpus/028b19f7d79f5da7a2af13a0c1e2d13f7eaf24cd new file mode 100644 index 000000000000..863b42bec232 --- /dev/null +++ b/webm_parser/fuzzing/corpus/028b19f7d79f5da7a2af13a0c1e2d13f7eaf24cd @@ -0,0 +1 @@ +S€gTÿk†à®ˆTº„ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/029ab55b16df41881f8de2351205201da334550a b/webm_parser/fuzzing/corpus/029ab55b16df41881f8de2351205201da334550a new file mode 100644 index 000000000000..27523d474de3 --- /dev/null +++ b/webm_parser/fuzzing/corpus/029ab55b16df41881f8de2351205201da334550a @@ -0,0 +1 @@ +S€gŽC§p‰E¹†¶„VT! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/029be5e90913b19cf5890559cf3f98aa909f0a84 b/webm_parser/fuzzing/corpus/029be5e90913b19cf5890559cf3f98aa909f0a84 new file mode 100644 index 0000000000000000000000000000000000000000..75abefb3957f55eaeabd6182c18423924b4f04b1 GIT binary patch literal 5 McmZ1^b>Ec%00zDR;Q#;t literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/02fb96539b84bbd12de84ff05cbc9dc3faa96b7e b/webm_parser/fuzzing/corpus/02fb96539b84bbd12de84ff05cbc9dc3faa96b7e new file mode 100644 index 0000000000000000000000000000000000000000..f6d373818e5f7363f27e6e530ef091b7b25b496f GIT binary patch literal 58 zcmYd3wPAyl>;1(YPVN%H4e1j^Jy)i6yF6kkPfg7Q3gl0fcivVy;kg4t*z}OZ4u-|8 OLMCAqv-ULJ`v3spz!`7= literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/0349f5632d21faa36b85520ad0b524d561f5e13f b/webm_parser/fuzzing/corpus/0349f5632d21faa36b85520ad0b524d561f5e13f new file mode 100644 index 0000000000000000000000000000000000000000..f3df0a462649d7548105f209addcdec9b4f74954 GIT binary patch literal 35 ncmWGxaF7UYNS`4VvMzh_x{0|BeMt=etsGhc3>z7fz!Vbz>$nWt literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/036fc9daf7fb1b4274dd668cfd883248ebbad967 b/webm_parser/fuzzing/corpus/036fc9daf7fb1b4274dd668cfd883248ebbad967 new file mode 100644 index 000000000000..5d97ec821198 --- /dev/null +++ b/webm_parser/fuzzing/corpus/036fc9daf7fb1b4274dd668cfd883248ebbad967 @@ -0,0 +1 @@ +S€gS»kŠ»ƒ³»ƒ³ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/037a4edc18e475ec81081e47277cbf51f1316680 b/webm_parser/fuzzing/corpus/037a4edc18e475ec81081e47277cbf51f1316680 new file mode 100644 index 0000000000000000000000000000000000000000..3109c3157447173a710f49210c32c5c44db8712c GIT binary patch literal 20 bcmb0LZbHAapzrLGE55r07#P@t*8oXIMvzD^OeBQ?B*rA_ zxiYOo>-t7PkOCM7EF2o8?8wUobCI^=C)TCUe_QwBCt&L1?ni!ct#{C!c zon6xHySpQJbt7Z&nnos3&y{IgwXSazU|?Wmbm?sI?fZPlC9&f_6jZMSN-(DZ01E3x A761SM literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/056b83ab2457979ea021e7118ab847eba265df15 b/webm_parser/fuzzing/corpus/056b83ab2457979ea021e7118ab847eba265df15 new file mode 100644 index 0000000000000000000000000000000000000000..ef730c6298b5cbe6f82d56a9d68d675289cba7d4 GIT binary patch literal 36 rcmb0LZb;uC@4T&a^@5cPTbmdpg2Q$uN* literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/058326151c1d4a490964d495d35adcf15178030f b/webm_parser/fuzzing/corpus/058326151c1d4a490964d495d35adcf15178030f new file mode 100644 index 0000000000000000000000000000000000000000..e6d60cc489522cef25f4c6101e9e8a6ba892adda GIT binary patch literal 28 kcmb0LZb+XY6TCZn^6rV-`@cIde02EWz_7`IA-JLe0Jn$?dH?_b literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/05847b5be0eb200d6a6b340c939c7a654b55003e b/webm_parser/fuzzing/corpus/05847b5be0eb200d6a6b340c939c7a654b55003e new file mode 100644 index 0000000000000000000000000000000000000000..c9282128dc6d97d8f6bbefb765e49ed364694a6c GIT binary patch literal 33 ncmb0LZb+Xa6mmFydU5fTPkLwKg)iw062AH!`-SpP1>=-q_*-7HUj( PX>D&zOFz*96iWvHb?qCg literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/08af8d91bb21835c50330e997d973cac8ff67766 b/webm_parser/fuzzing/corpus/08af8d91bb21835c50330e997d973cac8ff67766 new file mode 100644 index 0000000000000000000000000000000000000000..7fab9be0b7740e7daa6403ec116145f28cbd83ad GIT binary patch literal 60 zcmb0LZb)A&@4T&a-hw#`TP1+JUa^pnMg|~Q#L&{nP+YvQVYUOqZwH3bb=e*3+8(ro OENa-$nA_*j@E-uqP8oCn literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/0a18f05bb16402756202160225aec9c5a654f1ae b/webm_parser/fuzzing/corpus/0a18f05bb16402756202160225aec9c5a654f1ae new file mode 100644 index 0000000000000000000000000000000000000000..11f4d8c0ec2a6b05d4711f9ab457abd742f435be GIT binary patch literal 192 zcmb1gy}!7_smXz*JT)m-BDf*l;h3QB?2=yJ-2w~@?7?e*BqJk+2$QJi%Crux>l?w+ z$Q&kl=WV5J&%xpdCX-mmy6o~4^iY@5OI0?x||dR=z{*$nl2 Zz#>8+htnq)7X#V6$p?TWd&Vh{H~@H9GI#(0 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/0b60823983971ee17a2590678f0fc0762c21bb73 b/webm_parser/fuzzing/corpus/0b60823983971ee17a2590678f0fc0762c21bb73 new file mode 100644 index 0000000000000000000000000000000000000000..14fc6def90c1d0ae325b7194eb13511d8bb32681 GIT binary patch literal 84 zcmb1gy}!7_smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfuJ}ZDi1s(!&lI7#J3}HUUWn R<`*op9qutKZ~#(_OaNX;7l!}< literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/0bae7f0976af0f75974047b5f2cf4c9645412066 b/webm_parser/fuzzing/corpus/0bae7f0976af0f75974047b5f2cf4c9645412066 new file mode 100644 index 000000000000..beeec12917f4 --- /dev/null +++ b/webm_parser/fuzzing/corpus/0bae7f0976af0f75974047b5f2cf4c9645412066 @@ -0,0 +1 @@ +S€gœC§p—E¹”¶’Ä€aT€€€€€€€€€€€€€ƒèÌ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/0c1862b4065eefab2535ecc6951295e38069a82e b/webm_parser/fuzzing/corpus/0c1862b4065eefab2535ecc6951295e38069a82e new file mode 100644 index 0000000000000000000000000000000000000000..95342c8b12d93c6a8958ae3ab785e7adc88050b5 GIT binary patch literal 79 zcmb1RXlQC`S`5T9nwnC9R8v#%Y7ohg0wRGl0|V3B|Bs(M30{5h$&-su+3R01G%zsi YW_ruO^!L(Dv{~Qe085phu0P~P1D*ylh literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/0c2f51d5ffc69e69680bf3d6edb91d76c353ad14 b/webm_parser/fuzzing/corpus/0c2f51d5ffc69e69680bf3d6edb91d76c353ad14 new file mode 100644 index 000000000000..f0ad094812d8 --- /dev/null +++ b/webm_parser/fuzzing/corpus/0c2f51d5ffc69e69680bf3d6edb91d76c353ad14 @@ -0,0 +1 @@ +S€g€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/0c3af72d69f18103383c9cd41a7f2676a9d28a40 b/webm_parser/fuzzing/corpus/0c3af72d69f18103383c9cd41a7f2676a9d28a40 new file mode 100644 index 0000000000000000000000000000000000000000..74f05504c850033d3b9513e85d34f4e5916be494 GIT binary patch literal 20 bcmb0LZbPE)kHH}Q7 zo-5O~YF*zbz`(%B=+fEZ+xPiUN@B--YLkG+a zAP045Z2>!oN#1#RZQFCOHjuv{OeV3Ab=lqPfTC>I!BS{^CXmazK~}PD2NDd70swuZ BUKaoW literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/0f5741483be8f4f6eddcb70ea418fb0c08da9443 b/webm_parser/fuzzing/corpus/0f5741483be8f4f6eddcb70ea418fb0c08da9443 new file mode 100644 index 0000000000000000000000000000000000000000..34838b0cbaec462c36272ce1b6f44553eea51688 GIT binary patch literal 20 bcmb0LZbZg+q6)mJ~Bx&P#PcY5d1>ldf1H>YFs z_^J61Ub|_Y{o6hb-n@3d58$N#XgCi3@ZvE%s=;4={>AgtvHdGiM#DucaWA0kB)88oJSrFz!-MArAd+7;<0>6 zVCC+%L*xTTbq5C`5{hN;J{lS5r1=8>efW z-d|+`*KqJa7C;+-V>-8ENg7^t>@3n+CDX%sOJWvaQgN!D&WW5+(pc%JOpl44Je`$O z`V3v4Oka&vibRA8%bFGwyCXQ|0JBWJYp`>ww^fQV>!LHL1Xfz5SP^(FEnpNKDYHnA z3NV*dicvC-C$+&JnLgKgNHbccNJ!{i2|9x)9<6gU5RT+CU9(xGSTj6gWZ)py&x3vc41)Qks0u^l_HrSun%3QXYG+M>mzgB z3|>pOjWi&pQOmJ3UMH_dY9L&au7~kqub>PS9xY(IEzi- zvho)2NDrg2uAN{rYw$TssID#LLmMsybB7o9dP_eX$tFizO2M;Akr|!sV*PM*Sw;k& z6YW%)gp)>QKyIY}D7znCfz_>7HNT(1i3bfO#WRAR23sCnnX#c|U2F$)D%oG!NLr>< zL8|mFBxYKsg}y1XOH^Z_U$zeYnd7F+gBc0ZsNvYwC7N!&8joZfkLBgpmTDzCH<8Iw ztyPoWT8?tSYNtc2UnI3nKntc?(X;RcxYah&bGQULZ_++T(3~-5GS16{Bd|DWWc&)M zrmD+IkuM?YLXIlGKZd$i50p_4q%+D=MoD{pkL*c456G)|FmK>udZ^y8Om4m53pNL* zm0AkKGv>tk+#9IaZ4TOpS?t3sju$#%O^48B2l6gBDdH4ze{%Z5>H(c5v@z1P%~eSe z>3IYP^2|bD!?92%Wvyd<9C{H>^UG+4);43%WsX}pRmG9D++IO*k$3p4AoDdfGg~&0 z`&+vhVLD%stLVhGDf`ZRfOM?kkt|&(3fni;YDOv&2UIOAp9|<6>}HU(QiZ$Wf&nm* zB1)dDF3&71n7r0)vavl5a9NYY2^ z<+Ib(V(i*MuDb@X=AJe8953{Ial^4X_f3Wo0j999tJ9>bYtgL}8b}<3;~G)PlX~Tu zr4-CK$o1->@|t_Hcei_={}E2_WpRt7xjicvRtAweWtF1b zIR#67c}BN>c(QL(F*q^*WC1s0Rx546JXnr} zmT5KKP2>{O10avod9q9kT})<|2<2OqY1uc`*uT16($(kJmTDzC_rxIEkOePnsg?)G zV0;41{CZ|^fE3UWcrV$pR108j>`>R|!BnfAR1u=WEd18G)6?GedEte%Cm3hnQ^M99 zduI?qB1OdO!E5bTqI(;Tc{rn-Ya!{trRDdVTr6T1xDL!>omU4!1014{f7ewHq%$ff zr=uB6v|=Cw!U88P`xeZHGL3F^0$Ok&@c3pX#j=(xqOww@!(_$k z1fp&urLk2}Lb65IusT5!1&M=mj{{~>=RVCXs7 z!(t60(gu%xX{0t2yl|@418<=D0`a|P?Zm(a-q5U#PQ-|o{lVk;slp17nsc-nlpHr@v g)v~TdIN$_y=WyMfnKT2}@mmAM*`M4V7IC=xALxY<`Tzg` literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/15366a3aafe2590c2e3183c088dde4cc100cb956 b/webm_parser/fuzzing/corpus/15366a3aafe2590c2e3183c088dde4cc100cb956 new file mode 100644 index 000000000000..acb4e5910b25 --- /dev/null +++ b/webm_parser/fuzzing/corpus/15366a3aafe2590c2e3183c088dde4cc100cb956 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶³sÄVTA‘@…@B€@”@C¶@sĶ@sĶ@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/15feeb939fa90b25f57622a0abd9155ca88ad08b b/webm_parser/fuzzing/corpus/15feeb939fa90b25f57622a0abd9155ca88ad08b new file mode 100644 index 0000000000000000000000000000000000000000..e8c0a654e695292780bc3e92b7f31b5fde8010b0 GIT binary patch literal 88 zcmb0LP7hhva7`?G-TCVZ3=G9bfkZP9?*!rwAP!x{z%U0$D?e^-2+jjCl(t$mv;pbl aML)ZY)%-&I`P4ip0FR|Ej=#3QW$ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/18fd762b91406d37b85a7b342a91434a15f17290 b/webm_parser/fuzzing/corpus/18fd762b91406d37b85a7b342a91434a15f17290 new file mode 100644 index 0000000000000000000000000000000000000000..26c44c98a73a43f7ceb0808aa1657bcdaa033740 GIT binary patch literal 25 hcmaFU5OSu$sZDF;JBD}f-nGAB5MW@iy7cbd8vwAQ4YvRQ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/19334eee05eeb18c549498e7ca2e792a2a4e04f3 b/webm_parser/fuzzing/corpus/19334eee05eeb18c549498e7ca2e792a2a4e04f3 new file mode 100644 index 000000000000..cc25e67125d8 --- /dev/null +++ b/webm_parser/fuzzing/corpus/19334eee05eeb18c549498e7ca2e792a2a4e04f3 @@ -0,0 +1 @@ +S€gC§p‹E¹ˆ¶†€„C| \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/1a9a5df35779dd6e9e1de171b9bb0d316d2b64a5 b/webm_parser/fuzzing/corpus/1a9a5df35779dd6e9e1de171b9bb0d316d2b64a5 new file mode 100644 index 000000000000..729397426f98 --- /dev/null +++ b/webm_parser/fuzzing/corpus/1a9a5df35779dd6e9e1de171b9bb0d316d2b64a5 @@ -0,0 +1 @@ +S€g‘T®kŒ®Šàˆ#ƒã„@Û \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/1ab3d30f60743c2a1d3043773aae3a04f83c07c0 b/webm_parser/fuzzing/corpus/1ab3d30f60743c2a1d3043773aae3a04f83c07c0 new file mode 100644 index 000000000000..9682a0f2ab0c --- /dev/null +++ b/webm_parser/fuzzing/corpus/1ab3d30f60743c2a1d3043773aae3a04f83c07c0 @@ -0,0 +1 @@ +Sg€„IŒû@D‰ÉÛ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/1ad84ed46f3fda305150bac93958a5a390e23a67 b/webm_parser/fuzzing/corpus/1ad84ed46f3fda305150bac93958a5a390e23a67 new file mode 100644 index 000000000000..7e0f66f6f1ab --- /dev/null +++ b/webm_parser/fuzzing/corpus/1ad84ed46f3fda305150bac93958a5a390e23a67 @@ -0,0 +1 @@ +S€g¼C§p·E¹´¶²€°…@helloC|…lang0C|…area0C|…lang1C|…lang2C|…area1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/1b40a997150aa03c23ecc6efe445a2d7c3dd8368 b/webm_parser/fuzzing/corpus/1b40a997150aa03c23ecc6efe445a2d7c3dd8368 new file mode 100644 index 0000000000000000000000000000000000000000..b5d9ab5ff318fcd361ed0e8592579006a23cfcd7 GIT binary patch literal 17 Ycmb0LZb{V0RgK(0RpXp0EGYm0H8n+ Lb)o;J|L#BmEzK0U literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/1bb8f5f81b7f6c1d58e1f7bb13fa561fe1a146bc b/webm_parser/fuzzing/corpus/1bb8f5f81b7f6c1d58e1f7bb13fa561fe1a146bc new file mode 100644 index 000000000000..b3204bca9b32 --- /dev/null +++ b/webm_parser/fuzzing/corpus/1bb8f5f81b7f6c1d58e1f7bb13fa561fe1a146bc @@ -0,0 +1 @@ +S€g‘C¶uŒ Šu¡‡¦…îƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/1c56068c6dd17e9a824db6da78d64f933267a8c0 b/webm_parser/fuzzing/corpus/1c56068c6dd17e9a824db6da78d64f933267a8c0 new file mode 100644 index 000000000000..bd3b28e3f80d --- /dev/null +++ b/webm_parser/fuzzing/corpus/1c56068c6dd17e9a824db6da78d64f933267a8c0 @@ -0,0 +1 @@ +S€gTÃgŠss‡cÀ„hgÈ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/1c8db8b9d88dd3483b6f81e4224c4f985046e6ac b/webm_parser/fuzzing/corpus/1c8db8b9d88dd3483b6f81e4224c4f985046e6ac new file mode 100644 index 0000000000000000000000000000000000000000..57d98442b6c5f48f3adbed433baffd83cd40fcde GIT binary patch literal 85 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 u|Iyg{59s?Z{P)KGzcu#y|FyCAU--MmUf+M literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/212ee7d21a8cb25249644cab4f959db111f3a3c0 b/webm_parser/fuzzing/corpus/212ee7d21a8cb25249644cab4f959db111f3a3c0 new file mode 100644 index 0000000000000000000000000000000000000000..0474655b121f0ee9fab71f67c89ca7a368be343b GIT binary patch literal 106 zcmb0LP7hhva7`?G-TCVZ3=G9b|1vO01UIDj3OFw>=x}WYitPmA4j>L)#lSEJNGm^X yZV1i;GL*JjHSB5wGLjbo3DvfahOkvY#%>^f0K^X!TDLVbFyuBUOk!wcs0RR9z9Cis literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/224ce7b415f7950118880fce0594734aef489fec b/webm_parser/fuzzing/corpus/224ce7b415f7950118880fce0594734aef489fec new file mode 100644 index 0000000000000000000000000000000000000000..35aedfe3fa41fe68f36914a3cc1de5cd28781fd1 GIT binary patch literal 45 zcmb0LZb+Xa6mmFydU5fT_PyeNl(ajI; z-z$3Pe-saezx(2QI(*?@e|h)Q?IpdBPn(s~gK^*JGc`&_X!I}lKi9KxN$$Gq_ufI) zwRgd2Cz}K_yxbC>(nZ-a-gJ_E*{K@)UNBw;<^kT_ge99NZ(GB>51JX66Md8;^@)(Q zmy%)(em%wa1M@C{sh;_@c&b3<-!q~&X@dGJv8xXZpE5P<+CO_0I^GkN$T|;9DHCT2Z)3U%80x=dstA@D@jWtS{7~xj0dI?G+}`h-nao3N}fDLfaa=C z6bZ@dW3MH)(YeOVFe50S>T;$3uLc3s*T(TOzRm@G9=Zr;;2x+$zFXcGB8s)lh*s%C zZGCEiG3mTBa;`#Bfj((efKe7EvC;>|9#~Vwp6<`YhDrxHI+Uo~voz?@9nEtxNY7jj z-5fdQCoH|(nlgZk{Kz(CTqg1)7)BO`SrSqDf*va%V=4DuZjCb_Qw>d+xr1nsaj70{ zISww++nk`!)t`kZ^szkAcsAfoH8=}x?F?!0WT>3rrbOils$nBjS&WbckT3K~LmLrP zJ0JS`k5n=ku|$2#zT#Or>wE95L)FV!ZYJXjzOelSk0W6{l)x*wa{RfkYDN>Iu24Y9 zxHI$^==l|_@h!L!U&}!@L@^bqXr+cFuI4yKQ{aDm4kCO=`pUwW+;qqqDU4EV*<#d_ zvs>ZlkLijW$>Cn~+C9hU8GU!Yi~;Q(@H)qWT|G_?Y}BGd($&h0r&uwpwG~3Zw1PwU z#8_%x@5e(Gast6{NNQ{7M-1pq3reCK6pg~_1wRywDPlyc^nppAS}wKOz|OIqn2k5# zh1DI~i7~1oJ^^vhdGi6NipG^$Sn@-T_Fq@SXO1;hC7^@NvsCtU2l6JC6@Bp3rxvOA zBj&aJsI;A!;Oh~++aJrq1pktmDL9}sQ$SzwES)R)!qx^BHrvk^*@P*%a{PUSf>II7s?svX zh*KNJ*(!tYg|w7!#}}eIWk)&Igk@bH_%lOBPH5=JodHZQ%WUOWgh@ZJQT23SJ@C$u z)RU_0H#%hIQO95fD(#(GA9WGy$AP#ffl@uOo-8}1RRgA^5w};+6{pia3E?Tx*QHy0OZ@i4d zEL&;G2FQ?H`qz;j4W&ovcZ?FJ$Ig}LDO(P08FovbJa@M*aat^f_GL7Ku zo=P7Wd*Bro;x;80MjqAT$xt~Z5G5*;4PQA63XqpQn2C|h<&fimgn@bTi8THj2cFnn z_BV%;_yBd;`XB@o& z2Ek7OrGC9FS!fA`yQxa5j`X5Lecaz*3p`8bO6ySd8-1?GVz!^)%=usuiu{rt))IAv z0!qfsHYEka1M2{L)t|{6*&XN#(2tMk%%&XNE!ni_sW#nXTa^=T+oL zDjJT06YA=}T8HpJu)-B%t!$gtzB?PP8nLhA*w=A^5T=7RyA#kM`89|Lx&u$gvZ4>3`qU!ze#E@CAC<}e^iB!1zc)g3<$2m> z9s#BmUK1wxxoOD(?+r3OzyYP10{V()>0HSdLoQ|N$I!Zt6JQ~6EPG%zb$iY%RxpU1 zr3wX}(}ET3BO$VXzOG>j`Yp)un{~aJ{uBNZOIcF)V z7*ODXl0@UD>#4m}+(;_WHmPir%EvgtPy(z?6)Bu^Q@tc9Ht4xKlM4A2gP&O0l?-|qO65#$OJ3hitVs! zf?Kz2#mY`$PcmFR(hhjh$Fi04MAl)khQb8LvK6s{w_3q+bc-h`maW)ioUO=4d_4=o z)Rdu6J$JM&iM6)3;;KT1Sbno#+&9=0i&b!igLc|DPMpeA%wcjgFy`8wX$8%g{V`X?xwaHBbpP_I1lJ0eUP zNWB$S6dLTYBC|3+w6(z4abIUnn2=OIhBic@FH(D};p5?pe<0wuh(f)&3JH(8To^|H H%d6XGTQCI{ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/281b259280ada5d07b07a22cbe9a78c7b0fba94b b/webm_parser/fuzzing/corpus/281b259280ada5d07b07a22cbe9a78c7b0fba94b new file mode 100644 index 000000000000..d59afe9f6a4a --- /dev/null +++ b/webm_parser/fuzzing/corpus/281b259280ada5d07b07a22cbe9a78c7b0fba94b @@ -0,0 +1 @@ +S€g”C¶u u¡Š¦ƒî¦ƒî \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/299ed12b98673c6c4adbcf886cb70db8adde6aa1 b/webm_parser/fuzzing/corpus/299ed12b98673c6c4adbcf886cb70db8adde6aa1 new file mode 100644 index 000000000000..2afc78536f24 --- /dev/null +++ b/webm_parser/fuzzing/corpus/299ed12b98673c6c4adbcf886cb70db8adde6aa1 @@ -0,0 +1 @@ +S€g–T®k‘®m€Œb@‰P3†GçƒGè€S€gg—T®kÀ®m€b@ŠP3‡Gç„Gè \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2a40feb7480d0b31c36d5626761e948d0ae52792 b/webm_parser/fuzzing/corpus/2a40feb7480d0b31c36d5626761e948d0ae52792 new file mode 100644 index 0000000000000000000000000000000000000000..6828d16aeaaa3ab1120c9338fd4d51b0fad84414 GIT binary patch literal 179 zcmb1gm2$nmxWlQ*fu%e(DOV!6A>HAa;OW^VSA2I1FfgzOuK|*bjDo(iOL}1^<0_Op>=(uAV{MqlmpfQl5BepHc2>cQuLCp~mLKH|89N-t+;`h)~tONz;;Rci;9>?({@i?Bz zWHY;KMg06tG82#e-m_c^Mv^{?(UZja+{aO~Uo>F@M1IQh{3dkz=< zqvqK7&6BsVg~ne$eg4p$rswI?YUglaY#V%ro5F@S{n`K4TZV^Z(|fzO4l?h42aI;` z3A*9bO=1cMWzPiHN&e-aTKs#!co~>GM0XXI?4Go59rJ-SBQOW~(3yHeNZKk%F$P}@ z{Jvt{1u*3+zZFjzsQf)5dQ~RCW=UNGF+$3;sB8V~r5Iilm6Ua^7?qazSuqHx;O)rj z=6di!BiW%y$dHUEs$+zOYWmocmRu#&go-G<><6T@Jo6NRYOef5F%eli_e!`;#ua9U z5kZKm!z=wi76gE;h2v#>?Lgup=n|c&F`kD}BP)71va8r0XlOqq2t_Y(`XWSsD!Jj^;TDq$duenJsgEz|yHJ$^bp{ zBj4z_oRlZ%n9LPDV=Uk@1sU^l@6;8}fV^rb!psG*LB^$dv^ozCsJA&mpSS*0SQxlG z$hcPxsG{H#VuxKc?@4+S}wLfQV@*EFMvQKvL8D((n92Kt#7U+KpAS-8F?Wblv7 z<~mPwWsXYJ9}nXL%uNx4hQe24Ea)>9;Z4ey=yO8SE^DOZ6!p%|(IC$K3P*iRJ4cbP zgz^|`x^GHj(Z_Oele<(cQ#InGTCiW3khH+7EeRWnaCxn;lEo!|>Rhv~5CF1QCIv`$ z{7HuFlG+OFh)Jmc%wBt#7yK^8D|KK}r)l>3)Q~@ zse*AO7M8-0qvx-S;j_kiRVAQ<-7|mg0%xeaiOY&Qc&gKjWPOWyZ96LcNesri#pu?@ za?b3!bQJ;pDKVVUN*W+7-!u169q3xvXX-_hOPTgD>=}VY+Plpx#*kb%M9xx5gWL4r z#>IrAHSYc71^HfLHjan!LAs@B9AAxkNuL{j665k!29jM048n^~V@xla)=#KM%h%SN zGcT$bP$lw}Q=pZ`LaGA&A(gg_v5WFlOOuPbYb&IseB1sjK^y&9PlpN&xiEnFLn=AO z=5_9RV1pz>i3au;hNPNO<$t5&e5mwyY6I&QsW+{R)&vS9Zghs29vo^=Z+P%Z;X`6t z@MH;NJo__Lb8ewnz<%jcJWI!q1X`UOXzT@soggcmiv2(s78-cAVu}myVZ5O~V+6(? zHpYzEA3`)p%Xr_6UOZa`=OqZRWx6*fj~wnn>-j4eAf zbyED8+G7IdQxY8SgZdCm?PEA=jxeHf0-6Gn#-mrjXtX9707;mym?MRM7zJRf1RG}=2yZ=y4*E`T8%ocNUc6=xuJf8*hB6nJ z^7*7l0v@?&;>Nzh#4bh7vbH2ZICUj9A{nx4NBxAKp)zPR{9KH-PA_}|sYq(aXYugK zX0JyCSdepIKLz{pNX-i69B{l3DQ7w=%NXiYIELe-`d0sV9z=jzPFk_d$Cs!%A{H>} zQ)u8EVV4W;nRCQ~CTlSXq2w)&m(G%O}=T-XK}E}X0J!G>Je}oh1jmI zEy2-ib~%DEIVN)f^9&+c&Put?*V$2lGBSHB6%A>C6jqW|MpM38l+HJ#lFz@Vp}oe{ zdXjb>>M{M;eIJTfAQuxWF|i02nV-j5Gc)v>Ayq{B3j^5tS_JCbA_%uzNAE~c|9*nR NxI6p*?V_Rb*?)8?I2!-} literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/2a9588e6fab82016b545462cff2ed014c0345551 b/webm_parser/fuzzing/corpus/2a9588e6fab82016b545462cff2ed014c0345551 new file mode 100644 index 000000000000..014063669c72 --- /dev/null +++ b/webm_parser/fuzzing/corpus/2a9588e6fab82016b545462cff2ed014c0345551 @@ -0,0 +1 @@ +S€gT®kˆ®†à„T°ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2b1d57a8e8fa7164c9ba00957c9486010754560c b/webm_parser/fuzzing/corpus/2b1d57a8e8fa7164c9ba00957c9486010754560c new file mode 100644 index 000000000000..298598f506ff --- /dev/null +++ b/webm_parser/fuzzing/corpus/2b1d57a8e8fa7164c9ba00957c9486010754560c @@ -0,0 +1 @@ +S€g–T®k‘®m€Œb@‰P5†GçƒGè€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2b475d1a8f2fe4fbba92e1424f0ea96d95b12fb7 b/webm_parser/fuzzing/corpus/2b475d1a8f2fe4fbba92e1424f0ea96d95b12fb7 new file mode 100644 index 000000000000..7e8be98a0cce --- /dev/null +++ b/webm_parser/fuzzing/corpus/2b475d1a8f2fe4fbba92e1424f0ea96d95b12fb7 @@ -0,0 +1 @@ +S€g˜T®k“®‘m€Žb@„P1b@€P1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2bb80c6d0e2eadd73018eb2a8cae37d41602ee79 b/webm_parser/fuzzing/corpus/2bb80c6d0e2eadd73018eb2a8cae37d41602ee79 new file mode 100644 index 0000000000000000000000000000000000000000..8f07d6cb7b32238aea8fcd2f3c36497b65894ecd GIT binary patch literal 18 Zcmb0LZb)#y?1^`9{2dMx6 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/2c1389f882e256e260e37e8a67af7e32d0f4e0fc b/webm_parser/fuzzing/corpus/2c1389f882e256e260e37e8a67af7e32d0f4e0fc new file mode 100644 index 000000000000..671040876640 --- /dev/null +++ b/webm_parser/fuzzing/corpus/2c1389f882e256e260e37e8a67af7e32d0f4e0fc @@ -0,0 +1 @@ +µÿÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2c1f94c76e4eec607cce5311323620f349912e73 b/webm_parser/fuzzing/corpus/2c1f94c76e4eec607cce5311323620f349912e73 new file mode 100644 index 000000000000..26995d28b55c --- /dev/null +++ b/webm_parser/fuzzing/corpus/2c1f94c76e4eec607cce5311323620f349912e73 @@ -0,0 +1 @@ +S€gŽS»k‰»‡·…ðƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2c962c7fbceaacf8247ac9b70c8eeb1f814e435b b/webm_parser/fuzzing/corpus/2c962c7fbceaacf8247ac9b70c8eeb1f814e435b new file mode 100644 index 000000000000..856cb5be8a85 --- /dev/null +++ b/webm_parser/fuzzing/corpus/2c962c7fbceaacf8247ac9b70c8eeb1f814e435b @@ -0,0 +1 @@ +S€gŒM›t‡M»„S¬ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2c9aaabacf3a3b48dec4a85767cc5d38a1736aff b/webm_parser/fuzzing/corpus/2c9aaabacf3a3b48dec4a85767cc5d38a1736aff new file mode 100644 index 000000000000..28edff60145c --- /dev/null +++ b/webm_parser/fuzzing/corpus/2c9aaabacf3a3b48dec4a85767cc5d38a1736aff @@ -0,0 +1 @@ +S€gC¶uˆ †Ž„y‚Ì€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/2cf086299f983d0afc7f95c5e0c86b657448af33 b/webm_parser/fuzzing/corpus/2cf086299f983d0afc7f95c5e0c86b657448af33 new file mode 100644 index 0000000000000000000000000000000000000000..13f182651c52ad51d29cced172d6655989304382 GIT binary patch literal 40 qcmb0LZb)A&@4T&a-hw#`TN@iTIxzfpU?^SG(Br@`8^-Q)U;qGO_YjT% literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/2d0d96b95e9a3316d1ff0ef019ada509bbde4c9d b/webm_parser/fuzzing/corpus/2d0d96b95e9a3316d1ff0ef019ada509bbde4c9d new file mode 100644 index 0000000000000000000000000000000000000000..adcb9e1bc6b01befbfaafa7958240dfef5411231 GIT binary patch literal 41 xcmWF$aF7UYNS`70rX^$@L-yo#6LTBX`;r*`TRF4@7&kH|IkYe`T`+891^`G;4+sDN literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/2d414d5dfd20055393df3208009840da9cfabca9 b/webm_parser/fuzzing/corpus/2d414d5dfd20055393df3208009840da9cfabca9 new file mode 100644 index 0000000000000000000000000000000000000000..8e5d23f1644f6073d164607f522edfca80a16bfc GIT binary patch literal 24 fcmb0LZb+XZ@4T(Fe{pXkLklwl10y376EiaaXC4Me literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/2df967edcc00ac5c8e00037f1a43680600af5cba b/webm_parser/fuzzing/corpus/2df967edcc00ac5c8e00037f1a43680600af5cba new file mode 100644 index 0000000000000000000000000000000000000000..fb0e9b1cf79d446a592be2c1c379804fbc5ec676 GIT binary patch literal 166 zcmb1gm2$nmxWlQ*fu%e(DOV!6A>HAa;OvqszPkk&7}$f?2(U0PG79?6F6o7dq%Z(w z7@0(+T>qnL{J&D(d0VOH%Crux>l+1uY8WBxg#wDMARQptw&x(t^7j|FI|=*;ib(*) ffOfV*#6>+k=S iZv;sr*g&%w7@6dqFO{}E2Wymf-UekdiG{4o?gjuD@FJ=J literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/2f6e92a71918d01c16762d5ca59b328f1341d326 b/webm_parser/fuzzing/corpus/2f6e92a71918d01c16762d5ca59b328f1341d326 new file mode 100644 index 000000000000..3393c8ded1aa --- /dev/null +++ b/webm_parser/fuzzing/corpus/2f6e92a71918d01c16762d5ca59b328f1341d326 @@ -0,0 +1 @@ +S€g»k \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/306e2cf9aebe012cb0769b1b2a6ea68af2e8ed44 b/webm_parser/fuzzing/corpus/306e2cf9aebe012cb0769b1b2a6ea68af2e8ed44 new file mode 100644 index 000000000000..60a0f4849d3c --- /dev/null +++ b/webm_parser/fuzzing/corpus/306e2cf9aebe012cb0769b1b2a6ea68af2e8ed44 @@ -0,0 +1 @@ +áEߣˆB‚@webmS€g£*M'›t€I©f€C¶u€TSkk»€®€C§p€TÃg€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/30f7348d35de0c47d2044736cb115972b800569d b/webm_parser/fuzzing/corpus/30f7348d35de0c47d2044736cb115972b800569d new file mode 100644 index 0000000000000000000000000000000000000000..d992dbe02907cc1dec120daeb7a6e24b3d75bc0c GIT binary patch literal 173 zcmb1o=X!r}hf|XSOL=Nit`vfkD-qm~K2_d%Tj_-74h*Xu7#6oSG62B>2L`w*kP?Su zg1)m$dVO~bFfgzOuK|*bj2I$JqMj?$I<&5D1WO}xn1D8b%xjPcE^T`bl5R+61ligE T<}itctjq3R2NY+!F2Dc)@JTjw literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/310798b8d94a3a2fe649a4c871458b4d74fdcf32 b/webm_parser/fuzzing/corpus/310798b8d94a3a2fe649a4c871458b4d74fdcf32 new file mode 100644 index 000000000000..e3c32aa6faa4 --- /dev/null +++ b/webm_parser/fuzzing/corpus/310798b8d94a3a2fe649a4c871458b4d74fdcf32 @@ -0,0 +1 @@ +S€gC§pˆE¹…¶ƒ’ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/31ea606a9859bb29d7f98162d254348021e0d932 b/webm_parser/fuzzing/corpus/31ea606a9859bb29d7f98162d254348021e0d932 new file mode 100644 index 000000000000..038f1a9198af --- /dev/null +++ b/webm_parser/fuzzing/corpus/31ea606a9859bb29d7f98162d254348021e0d932 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶³sÄVTA‘@’@€@…@B€@…@C¶@sĶ@€ÿÿÿ¶@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3230fc31ab8d408c484aa28bbe36431f71101243 b/webm_parser/fuzzing/corpus/3230fc31ab8d408c484aa28bbe36431f71101243 new file mode 100644 index 0000000000000000000000000000000000000000..cad0aa0757408781be3d0440f1feb48082e2eaec GIT binary patch literal 59 zcmb1gy}!7_smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfuJ}ZDas~#jXGA!4!hW$OHg} C5EKCb literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/328b7c92996e480d1f11efe847c3be4b5dc0f8eb b/webm_parser/fuzzing/corpus/328b7c92996e480d1f11efe847c3be4b5dc0f8eb new file mode 100644 index 0000000000000000000000000000000000000000..b229bcb7d68ada960850496dc15d3d0f4a1f4c22 GIT binary patch literal 207 zcmb0LZb+Xh;JmzGg6q!SZQTu>tqu&%bqzoy0hE~|6mmFydU5fT1_u@(<-E;72q?s|&4H!(Nh2$Wg7S{Q zcx+Ox_ZRrh|Ns9G)7d0~ E0rl?w+ v$Q&kl=WVHN&%xqAW+{ZpBo?wRyL%l_ll+1x7#Nx4owt>?JqL>;m`q|J>$1Do0Y%xa1BpOJvAlKJjVQn+u|o>v765?d BE$;vT literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/35b2f81c573b15304cb9b13f00008b460da78942 b/webm_parser/fuzzing/corpus/35b2f81c573b15304cb9b13f00008b460da78942 new file mode 100644 index 000000000000..85f4be21d1fe --- /dev/null +++ b/webm_parser/fuzzing/corpus/35b2f81c573b15304cb9b13f00008b460da78942 @@ -0,0 +1 @@ +S€gˆC¶uƒ«ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/35d9256a4d7ef3ee54384616178af3a8092d0981 b/webm_parser/fuzzing/corpus/35d9256a4d7ef3ee54384616178af3a8092d0981 new file mode 100644 index 000000000000..42f1817f039c --- /dev/null +++ b/webm_parser/fuzzing/corpus/35d9256a4d7ef3ee54384616178af3a8092d0981 @@ -0,0 +1 @@ +S€gˆTÃgƒss€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/36352efd680a747f0f2c93d760559d69399a4102 b/webm_parser/fuzzing/corpus/36352efd680a747f0f2c93d760559d69399a4102 new file mode 100644 index 000000000000..d438e9c26002 --- /dev/null +++ b/webm_parser/fuzzing/corpus/36352efd680a747f0f2c93d760559d69399a4102 @@ -0,0 +1 @@ +S€gŠC¶u… ƒ›ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3685ffbcfe28b166ced0a783b012a446f9cfea46 b/webm_parser/fuzzing/corpus/3685ffbcfe28b166ced0a783b012a446f9cfea46 new file mode 100644 index 000000000000..fe6660b7f487 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3685ffbcfe28b166ced0a783b012a446f9cfea46 @@ -0,0 +1 @@ +S€gŒS»k‡»…·ƒ÷ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/36def8b2661197d594756f116a750822ae1d65d5 b/webm_parser/fuzzing/corpus/36def8b2661197d594756f116a750822ae1d65d5 new file mode 100644 index 000000000000..50d822aed413 --- /dev/null +++ b/webm_parser/fuzzing/corpus/36def8b2661197d594756f116a750822ae1d65d5 @@ -0,0 +1 @@ +S€g‘T®kŒ®Šm€‡b@„P3 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/371950308853fb3b1b3a940c0598bfdfeb1d0f9c b/webm_parser/fuzzing/corpus/371950308853fb3b1b3a940c0598bfdfeb1d0f9c new file mode 100644 index 000000000000..d12faf884315 --- /dev/null +++ b/webm_parser/fuzzing/corpus/371950308853fb3b1b3a940c0598bfdfeb1d0f9c @@ -0,0 +1 @@ +S€g‹C¶u† „u¢ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3765e9174971dbac9cfeb7e485ca61dc4941f7a9 b/webm_parser/fuzzing/corpus/3765e9174971dbac9cfeb7e485ca61dc4941f7a9 new file mode 100644 index 000000000000..4c306d90b136 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3765e9174971dbac9cfeb7e485ca61dc4941f7a9 @@ -0,0 +1 @@ +S€gžT®k™®—m€”b@‘P5ŽGç‹Gèˆ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/37cae036ce7ff5d0671f32d3757579f248609c9c b/webm_parser/fuzzing/corpus/37cae036ce7ff5d0671f32d3757579f248609c9c new file mode 100644 index 000000000000..9183f013cc32 --- /dev/null +++ b/webm_parser/fuzzing/corpus/37cae036ce7ff5d0671f32d3757579f248609c9c @@ -0,0 +1 @@ +S€g”T®k®m€Šb*‡P0„Gâ!S€gŠT®k…®ƒ¹ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/38460ea06ee847ff3d5733415c299b44b0866e58 b/webm_parser/fuzzing/corpus/38460ea06ee847ff3d5733415c299b44b0866e58 new file mode 100644 index 000000000000..6456fa51768d --- /dev/null +++ b/webm_parser/fuzzing/corpus/38460ea06ee847ff3d5733415c299b44b0866e58 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶žsÄVTA‘@’@€@…@B€@…@C¶@sĶ@€ÿÿÿ¶@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3865de2fec3f353513d86b28e43bf936d4707f97 b/webm_parser/fuzzing/corpus/3865de2fec3f353513d86b28e43bf936d4707f97 new file mode 100644 index 000000000000..f2150f606fad --- /dev/null +++ b/webm_parser/fuzzing/corpus/3865de2fec3f353513d86b28e43bf936d4707f97 @@ -0,0 +1 @@ +€gS€g½C§p¸E¹µ¶³sÄVTA‘ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3867b18060c307e0d04e0098f195d699e4b3294f b/webm_parser/fuzzing/corpus/3867b18060c307e0d04e0098f195d699e4b3294f new file mode 100644 index 000000000000..f0cfd0a28cf3 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3867b18060c307e0d04e0098f195d699e4b3294f @@ -0,0 +1 @@ +S€g»TÃg¶ss³gțȄ#‡dgÈ„]‡egÈ„D‡dgÈ„]‡egÈ„D‡gÈ€D‡dgÈ€g \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/391920720c3609b7c1f7b51162ce3ec99329a0b4 b/webm_parser/fuzzing/corpus/391920720c3609b7c1f7b51162ce3ec99329a0b4 new file mode 100644 index 0000000000000000000000000000000000000000..fb057c8c30a8643cb6093734cee16e0a0621908d GIT binary patch literal 148 zcmb0LZb;uF;JmzGyX(#^+cq_9Xmw!CNX^M9ajt32Nz6+(aIR}jEJ{r@fbtEYv=K<$ zP|EfG;tsDS2bS{Gq+AK0QGc)+RflF2P-|Ygq4OWGumn(FL%PE;LEqUWy}r8z7#IKy C20VoT literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/395027454c7d5babff9544414c04a39d712eed1c b/webm_parser/fuzzing/corpus/395027454c7d5babff9544414c04a39d712eed1c new file mode 100644 index 000000000000..e34ef6fa97cb --- /dev/null +++ b/webm_parser/fuzzing/corpus/395027454c7d5babff9544414c04a39d712eed1c @@ -0,0 +1 @@ +€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶;€€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ€¶¶€€¶ÿ€¶€¶ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3a258c9ed48e634ed8d495f07caf21dbc6978205 b/webm_parser/fuzzing/corpus/3a258c9ed48e634ed8d495f07caf21dbc6978205 new file mode 100644 index 0000000000000000000000000000000000000000..a14b82b42581aa263f09a7c985969b3ba5fdb406 GIT binary patch literal 19 acmb0LZbHAa;OvqszPkk&7}&8%`od+`0F^K@3IYXsA5~H!1B27&MwT}Y3_w99kIa4Az literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/3ec4ac00bb967a335b5caae0ae51488f2fda6583 b/webm_parser/fuzzing/corpus/3ec4ac00bb967a335b5caae0ae51488f2fda6583 new file mode 100644 index 0000000000000000000000000000000000000000..b8043e818e3446a8da0baf7602765ad78eec6612 GIT binary patch literal 9 QcmaFE^oEInfuZ3)01)H@TmS$7 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/3eec38c0ac3a96856d3210a0357857e70b07a9cb b/webm_parser/fuzzing/corpus/3eec38c0ac3a96856d3210a0357857e70b07a9cb new file mode 100644 index 000000000000..25f027372258 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3eec38c0ac3a96856d3210a0357857e70b07a9cb @@ -0,0 +1 @@ +S€g™TÃg‰ss†cÀ€cÀ€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3ef82ba46238fa46b89148a230d23e741ba5f1da b/webm_parser/fuzzing/corpus/3ef82ba46238fa46b89148a230d23e741ba5f1da new file mode 100644 index 000000000000..134a456f8ffc --- /dev/null +++ b/webm_parser/fuzzing/corpus/3ef82ba46238fa46b89148a230d23e741ba5f1da @@ -0,0 +1 @@ +S€g˜T®k“®‘m€b@„P1b@€b@€P1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3fbd43f81888ca60e5bfe49b47e23cb115d5cd7d b/webm_parser/fuzzing/corpus/3fbd43f81888ca60e5bfe49b47e23cb115d5cd7d new file mode 100644 index 000000000000..75b0d29e6d14 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3fbd43f81888ca60e5bfe49b47e23cb115d5cd7d @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶¬sÄV…@B€@…@C¶@sĶ@sä¶@sĶ@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3fe56e5b06653e8a37c99da190ff8d44d14bfc06 b/webm_parser/fuzzing/corpus/3fe56e5b06653e8a37c99da190ff8d44d14bfc06 new file mode 100644 index 000000000000..7a3c719f0165 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3fe56e5b06653e8a37c99da190ff8d44d14bfc06 @@ -0,0 +1 @@ +S€G™T®k”®’m€bŒP‰S€g“C¶uŽ ŒŽŠèƒÌá€èGƒÌâ€Gç€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/3febffec3be8b1f4bcb76a01c1d713600d8e4adb b/webm_parser/fuzzing/corpus/3febffec3be8b1f4bcb76a01c1d713600d8e4adb new file mode 100644 index 000000000000..5f0a13d1eb93 --- /dev/null +++ b/webm_parser/fuzzing/corpus/3febffec3be8b1f4bcb76a01c1d713600d8e4adb @@ -0,0 +1 @@ +S€gŠC§p…E¹‚¶€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/43380e5a9dd29ca847e9b5bfba12a0a95d94d0da b/webm_parser/fuzzing/corpus/43380e5a9dd29ca847e9b5bfba12a0a95d94d0da new file mode 100644 index 0000000000000000000000000000000000000000..1c630fc4cde9e55595fda6d1a211ac1243e5229c GIT binary patch literal 30 mcmb0LZb+Xg7P2mT%DPDpCTwtEWZdPz$P}`{fgxm<0|NlO)eAHL literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/435d3ff6ddc43ac582e740ead969397fd2bfc7f5 b/webm_parser/fuzzing/corpus/435d3ff6ddc43ac582e740ead969397fd2bfc7f5 new file mode 100644 index 0000000000000000000000000000000000000000..8d8a6f49518d666cd6a281aaf02b9d9dabb41b83 GIT binary patch literal 24 fcmb0LZb)}{DDS+j)ZzAGheig58*Qu-|AQLW)92O(04Bi&!T4G5|#h} literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/49c0e3eed81c1827e755c70c9c6d9606e7f54b22 b/webm_parser/fuzzing/corpus/49c0e3eed81c1827e755c70c9c6d9606e7f54b22 new file mode 100644 index 0000000000000000000000000000000000000000..cc061ed58617aa22fef64d9cea9b87d2a694a2d5 GIT binary patch literal 26 hcmb0LZb+Xh;JmzGg6q!SZQTu>tqu&%H4V;n4FHjT3j6>7 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/4a3e77f5bc061fc8121ef7ebd2b8e3ebb0bdd609 b/webm_parser/fuzzing/corpus/4a3e77f5bc061fc8121ef7ebd2b8e3ebb0bdd609 new file mode 100644 index 000000000000..336e6fb261df --- /dev/null +++ b/webm_parser/fuzzing/corpus/4a3e77f5bc061fc8121ef7ebd2b8e3ebb0bdd609 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶³sÄVTA‘@’@€@…@B€€€€j€€€€€€€€€€€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/4a454e01ef09b175f3864b654cfc1104a850e871 b/webm_parser/fuzzing/corpus/4a454e01ef09b175f3864b654cfc1104a850e871 new file mode 100644 index 0000000000000000000000000000000000000000..f57bbfdda993be030eab9a33d500481843240efb GIT binary patch literal 192 zcmb1gy}!7_smXz*JT)m-BDf*l;h3QB?2=yJ-2w~@?7?e*BqJk+2$QJi%Crux>l?w+ z$Q&kl=WV5J&%xpdCX-mmy6o~4^iY@5OI0?x||dR=z{+3fXu Yz#>8+htnq)7X#V6$p=8<$wxur0C@T`bpQYW literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/4acd9df9a7ee34e48c6c80777159b089f417a69a b/webm_parser/fuzzing/corpus/4acd9df9a7ee34e48c6c80777159b089f417a69a new file mode 100644 index 0000000000000000000000000000000000000000..1df1e83120c7fe57128d72e8615733ce8fdf4df0 GIT binary patch literal 17 Ycmb0LZbr{ZrOCwE^XwCq0;uya51i=o;Gq literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/4eeb4507868732d3705d1ab10f6a31f70f579556 b/webm_parser/fuzzing/corpus/4eeb4507868732d3705d1ab10f6a31f70f579556 new file mode 100644 index 000000000000..652484d43895 --- /dev/null +++ b/webm_parser/fuzzing/corpus/4eeb4507868732d3705d1ab10f6a31f70f579556 @@ -0,0 +1 @@ +S€g“C¶uŽ ŒŽŠèƒÌèƒÌS€gC¶uˆ †Ž„è‚΀ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/4f21f3584c99e97453ebe513ed44c86530fbebba b/webm_parser/fuzzing/corpus/4f21f3584c99e97453ebe513ed44c86530fbebba new file mode 100644 index 000000000000..b37544dc89ca --- /dev/null +++ b/webm_parser/fuzzing/corpus/4f21f3584c99e97453ebe513ed44c86530fbebba @@ -0,0 +1 @@ +S€g¼C§p‹E¹ˆ¶†€„C|›C§p‹E¹ˆ¶†€„C|! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5103160695cd56d281faa45d9267013488cc7bde b/webm_parser/fuzzing/corpus/5103160695cd56d281faa45d9267013488cc7bde new file mode 100644 index 000000000000..2a707f72c5a2 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5103160695cd56d281faa45d9267013488cc7bde @@ -0,0 +1 @@ +S€gT®kŠ®ƒ×®ƒ× \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/521103054e395afd0f9612b47c71cfa0e62151b9 b/webm_parser/fuzzing/corpus/521103054e395afd0f9612b47c71cfa0e62151b9 new file mode 100644 index 0000000000000000000000000000000000000000..5849044fca819585d552fe08c6d960ef17b34fd0 GIT binary patch literal 163 zcmb1gy}!7_smXz*JT)m-BDf*l;h3QB?2=yJ-2w~@?7?e*BqJk+2$QJi%CrYs*EfQt zL7WZ{M~H!uN#1!|Y1?zKID*L}7P2n8dmT`e?K)UWEN@+QW1~xFV`Jl+#y4+(6bkTd QXl(3m_{X@8;mcGt065P#^#A|> literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/523511e33c8788ebb1b703d7c5ad12b5da03964e b/webm_parser/fuzzing/corpus/523511e33c8788ebb1b703d7c5ad12b5da03964e new file mode 100644 index 0000000000000000000000000000000000000000..99e77993ee608f7043592be0df6654146a901c1e GIT binary patch literal 28 kcmb0LZb+Xl7P2mT(z*$`4ZTSYT>+-;?$2A?UoP%Er93q$S0cC}-Qk$)eL>&ZCB4481sE9EgVz8_Mn((~CQ;9oX&qYE eH-e=R9H3zgj7;**+e+J>gQXBmCb5up+1&v0ks=QO literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5466c59c9c0eff34800e19cc92faf07469865ea9 b/webm_parser/fuzzing/corpus/5466c59c9c0eff34800e19cc92faf07469865ea9 new file mode 100644 index 000000000000..2b86b64afb95 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5466c59c9c0eff34800e19cc92faf07469865ea9 @@ -0,0 +1 @@ +VT¢€€ˆƒ€€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/54bf8d6f1f846243b6f4de81605779b0acb78406 b/webm_parser/fuzzing/corpus/54bf8d6f1f846243b6f4de81605779b0acb78406 new file mode 100644 index 000000000000..9b4a32ed85f7 --- /dev/null +++ b/webm_parser/fuzzing/corpus/54bf8d6f1f846243b6f4de81605779b0acb78406 @@ -0,0 +1 @@ +€€€€€€€€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/54fd1711209fb1c0781092374132c66e79e2241b b/webm_parser/fuzzing/corpus/54fd1711209fb1c0781092374132c66e79e2241b new file mode 100644 index 000000000000..7937c68fbcf7 --- /dev/null +++ b/webm_parser/fuzzing/corpus/54fd1711209fb1c0781092374132c66e79e2241b @@ -0,0 +1 @@ +g \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5506797514c0b214f5faa9764a9e149cd1df9d28 b/webm_parser/fuzzing/corpus/5506797514c0b214f5faa9764a9e149cd1df9d28 new file mode 100644 index 000000000000..bfc500457fdf --- /dev/null +++ b/webm_parser/fuzzing/corpus/5506797514c0b214f5faa9764a9e149cd1df9d28 @@ -0,0 +1 @@ +S€gŒT®k‡®…׃ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5566c6d599866f5e523c54e6575d7b9e557a2539 b/webm_parser/fuzzing/corpus/5566c6d599866f5e523c54e6575d7b9e557a2539 new file mode 100644 index 0000000000000000000000000000000000000000..8578677e2113331f3e2c7a6516c3c27da6c86af8 GIT binary patch literal 53 wcmb0LZb;uC@4T&a^@5cPTNgGjb71)Gz{m*246_kzrqV@?|N9)6Uo`v&07G9DkpKVy literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/55c3d55780427eb00bde252348db1c2ede22526c b/webm_parser/fuzzing/corpus/55c3d55780427eb00bde252348db1c2ede22526c new file mode 100644 index 000000000000..25c7cc755ca0 --- /dev/null +++ b/webm_parser/fuzzing/corpus/55c3d55780427eb00bde252348db1c2ede22526c @@ -0,0 +1 @@ +€ diff --git a/webm_parser/fuzzing/corpus/5600abaefe804c50a353bbfdd0f9e10ffc102f81 b/webm_parser/fuzzing/corpus/5600abaefe804c50a353bbfdd0f9e10ffc102f81 new file mode 100644 index 000000000000..2b5e073b26df --- /dev/null +++ b/webm_parser/fuzzing/corpus/5600abaefe804c50a353bbfdd0f9e10ffc102f81 @@ -0,0 +1 @@ +S¸S€g‘TkŒ®Šm€‡b@„P1S€g”T®k®m€Šb@€Ð5„Gáÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5611ddd40488973ee6c64c45ad57d6eefe6da100 b/webm_parser/fuzzing/corpus/5611ddd40488973ee6c64c45ad57d6eefe6da100 new file mode 100644 index 000000000000..34d12c7c56a9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5611ddd40488973ee6c64c45ad57d6eefe6da100 @@ -0,0 +1 @@ +S€gŠI©f…*×±ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/56b0ddd4e44492b45ebb7e9438b0a5556c244bfb b/webm_parser/fuzzing/corpus/56b0ddd4e44492b45ebb7e9438b0a5556c244bfb new file mode 100644 index 000000000000..2da35a1d59c0 --- /dev/null +++ b/webm_parser/fuzzing/corpus/56b0ddd4e44492b45ebb7e9438b0a5556c244bfb @@ -0,0 +1 @@ +S€g‹S»k†»„·€·€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5729395b452092152b06fbb8976e9bf1fda06439 b/webm_parser/fuzzing/corpus/5729395b452092152b06fbb8976e9bf1fda06439 new file mode 100644 index 0000000000000000000000000000000000000000..df3e05b5af142d0f680d0ca0901f423bc4f9b706 GIT binary patch literal 207 zcmb0LZb+Xh;JmzGg6q!SZQTu>tqu&%DhJtL-Nrk ztIXu1jf_Cd1X8dUq;-eu&aK6rh GA{YR-R#Q0u literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/57cce6fac7c0992eb338e0e538d3e0ee3fe7f1cd b/webm_parser/fuzzing/corpus/57cce6fac7c0992eb338e0e538d3e0ee3fe7f1cd new file mode 100644 index 000000000000..ab69784bb967 --- /dev/null +++ b/webm_parser/fuzzing/corpus/57cce6fac7c0992eb338e0e538d3e0ee3fe7f1cd @@ -0,0 +1 @@ +ñEߣˆB‚@webmS€g£*M'›t€I©f€C¶u€ÔSkk»€®€C§p€TÃg€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5822672f068da89546838004ae59552e12e132f7 b/webm_parser/fuzzing/corpus/5822672f068da89546838004ae59552e12e132f7 new file mode 100644 index 0000000000000000000000000000000000000000..b5b45fedae59fc3126ae69d84ae60ba3c06748c0 GIT binary patch literal 20 bcmb1gy}x*#Q(J>mlLLcOdxKMJLjwZX1CVWR@We}>!! GAO`@!tSABi literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5a645b30948850f8efb7bdd11944bdf3112c7bfe b/webm_parser/fuzzing/corpus/5a645b30948850f8efb7bdd11944bdf3112c7bfe new file mode 100644 index 000000000000..2d85ba9dfa8a --- /dev/null +++ b/webm_parser/fuzzing/corpus/5a645b30948850f8efb7bdd11944bdf3112c7bfe @@ -0,0 +1 @@ +S€gTÃgŠss‡cÀ„cÅÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5b2f727461cde3c02ffdfb300adb0b4a09734911 b/webm_parser/fuzzing/corpus/5b2f727461cde3c02ffdfb300adb0b4a09734911 new file mode 100644 index 000000000000..741671552a2e --- /dev/null +++ b/webm_parser/fuzzing/corpus/5b2f727461cde3c02ffdfb300adb0b4a09734911 @@ -0,0 +1 @@ +S€gT®kŠ®ˆà†S¸ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5b5531d859ab5bbb034345bbaa2852e9d8a1e1ef b/webm_parser/fuzzing/corpus/5b5531d859ab5bbb034345bbaa2852e9d8a1e1ef new file mode 100644 index 000000000000..5b800b8f5ac6 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5b5531d859ab5bbb034345bbaa2852e9d8a1e1ef @@ -0,0 +1 @@ +S€g‘T®kŒ®Šm€‡b@„P1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5b980cb94e35769ec92a140ca00d34977e7da5ec b/webm_parser/fuzzing/corpus/5b980cb94e35769ec92a140ca00d34977e7da5ec new file mode 100644 index 0000000000000000000000000000000000000000..180ef6b3a4a5dae75bce7cf7d72e59455e685cda GIT binary patch literal 10 OcmZo@00D+p1_b~ZIRmBu literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5bfec2fe795176646fa0babecf97c04045bd4814 b/webm_parser/fuzzing/corpus/5bfec2fe795176646fa0babecf97c04045bd4814 new file mode 100644 index 0000000000000000000000000000000000000000..9e406d3a2f528624ca1659172ecfc08b82715137 GIT binary patch literal 348 zcmb0LZb<(v7P2n;+qy5;6&M&9i;n_Hre+|az7t3=bbyG^RX~z)4v4AzxVe!fI1k8l zRN6YH(Wwo{a!y_Zl2UE!Xmkk!DP;}Y4J6qfDlo8xYyz^_fh^YfKn6!{g8~D0k^@j8 zz!1o1lmObl0A&B{bu)7trX@K{4lrzF3@~bB3NUVD4lr#Af7r+hQZxgkX!5#=xea|u z4lN)>NhTnQNy-%@y|}}v$suG@YErI5LwfK^Y3FUFZIDugvvVC70T~Ezh5!Hn literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5c28d94242e9a5ba7e7324e59e5dd12f3e31e51c b/webm_parser/fuzzing/corpus/5c28d94242e9a5ba7e7324e59e5dd12f3e31e51c new file mode 100644 index 0000000000000000000000000000000000000000..68b4ebc8cb661b187f2b3afa4e38266143571ebe GIT binary patch literal 63 zcmb1gy}!7_smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfuJ}ZDe2&1_B0##jOntK(Nh$ MWj0Xq0hnR}0IaAI)&Kwi literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5c67728ab04ba3dfe4b2636cf609f9617c4a5974 b/webm_parser/fuzzing/corpus/5c67728ab04ba3dfe4b2636cf609f9617c4a5974 new file mode 100644 index 000000000000..f14a6f6a1ee8 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5c67728ab04ba3dfe4b2636cf609f9617c4a5974 @@ -0,0 +1 @@ +S€g”T®k®m€Šb@‡P0„Gâ!S€gŠT®k…®ƒ¹ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5cd36053e269c97f5359b0a075d80bf1e23b5f47 b/webm_parser/fuzzing/corpus/5cd36053e269c97f5359b0a075d80bf1e23b5f47 new file mode 100644 index 000000000000..985bba444f55 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5cd36053e269c97f5359b0a075d80bf1e23b5f47 @@ -0,0 +1 @@ +S€gŒC¶u‡ …u¡‚¦€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/5d6f99c2afbf68a1b15a17e07945c11db8579e5f b/webm_parser/fuzzing/corpus/5d6f99c2afbf68a1b15a17e07945c11db8579e5f new file mode 100644 index 0000000000000000000000000000000000000000..85a0c9302c8e4d809c6d1a38aed9bf05312009de GIT binary patch literal 88 zcmb0LZb-i-7P2n;;=1$K6&M(bj{=EiAl?bY9Y7qq3P{WWV&%up4Z(RphSJtK4Q)U= XSpZ0=wskawfs};p1`-dUn1K-hye$}I literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5dd149b1b8509f456c16c4ac6dac3c7d6aa217ed b/webm_parser/fuzzing/corpus/5dd149b1b8509f456c16c4ac6dac3c7d6aa217ed new file mode 100644 index 0000000000000000000000000000000000000000..102660dbf5d6908dbe3eb011a2cf1146d938ae6e GIT binary patch literal 99 zcmb0LZb)A&@4T&a-hw#`TN@b|;v|3~^BIG82rw`(G6f$1QOq*IyR*A@1DR}_fds>% umPUzSM#hHpi6EUl3%V9{_$HPVSu#AZ7rGW>R6C|%Uh=KugkBOveq literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5e84011c6b092e9f0d6ba60f12fc0fae95a622ca b/webm_parser/fuzzing/corpus/5e84011c6b092e9f0d6ba60f12fc0fae95a622ca new file mode 100644 index 0000000000000000000000000000000000000000..d4df4df8c4b6a47b487f4ed563cb78ce5430d8fd GIT binary patch literal 56 zcmb1gy}!7_smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfuJ}ZDinv05J$91=PUJ$OHfx C#SfhT literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/5f56766993de2b8c2db87116090af546e1add3b0 b/webm_parser/fuzzing/corpus/5f56766993de2b8c2db87116090af546e1add3b0 new file mode 100644 index 000000000000..c90364d0a2d2 --- /dev/null +++ b/webm_parser/fuzzing/corpus/5f56766993de2b8c2db87116090af546e1add3b0 @@ -0,0 +1 @@ +S€gŽS»k‰»‡·…÷ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/60571c8be9bee3b13428613df66ff36cf54309ec b/webm_parser/fuzzing/corpus/60571c8be9bee3b13428613df66ff36cf54309ec new file mode 100644 index 000000000000..bfd35dceee4c --- /dev/null +++ b/webm_parser/fuzzing/corpus/60571c8be9bee3b13428613df66ff36cf54309ec @@ -0,0 +1 @@ +S€g”TÃgssŒcÀ‰hÊ€cÊ€cÅ€S€gTÃgŠss‡cÀ„cÊ! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/60ac245f262ee1ff177d351a4fe246060fb4538b b/webm_parser/fuzzing/corpus/60ac245f262ee1ff177d351a4fe246060fb4538b new file mode 100644 index 0000000000000000000000000000000000000000..21219e91111dae7829f2c60fc4ca295282aec47d GIT binary patch literal 86 zcmb0LZb<(v7P2n;+qy5;6&M&9i;n_Hre+|)yc0+;bbyG^RX~z)4v4AzxVe!fI1k8l cRN6YHQ39xcCP@F3bxv(SLFY*iCM0hF0Blkk+W-In literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6120aafb0db1715400e47d0a61fb9f6d8665a736 b/webm_parser/fuzzing/corpus/6120aafb0db1715400e47d0a61fb9f6d8665a736 new file mode 100644 index 000000000000..2fc4e39beccc --- /dev/null +++ b/webm_parser/fuzzing/corpus/6120aafb0db1715400e47d0a61fb9f6d8665a736 @@ -0,0 +1 @@ +S€gŠC¶u…çƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/61953a8f7a9f0613020d297a6d350abeeaf6e58c b/webm_parser/fuzzing/corpus/61953a8f7a9f0613020d297a6d350abeeaf6e58c new file mode 100644 index 000000000000..478128c71dab --- /dev/null +++ b/webm_parser/fuzzing/corpus/61953a8f7a9f0613020d297a6d350abeeaf6e58c @@ -0,0 +1 @@ +S€g“C¶uŽ ŒŽŠèƒÌèƒÌ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/61c0a7fef46c0c1b081b1c2b40835805e5989b54 b/webm_parser/fuzzing/corpus/61c0a7fef46c0c1b081b1c2b40835805e5989b54 new file mode 100644 index 000000000000..b5ac3f692144 --- /dev/null +++ b/webm_parser/fuzzing/corpus/61c0a7fef46c0c1b081b1c2b40835805e5989b54 @@ -0,0 +1 @@ +ä \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/620d85870206e88db0bc3ee978c24c0bbc1821ba b/webm_parser/fuzzing/corpus/620d85870206e88db0bc3ee978c24c0bbc1821ba new file mode 100644 index 0000000000000000000000000000000000000000..4749f9c35d3e1c577cf9165027fae03d21096cdd GIT binary patch literal 60 zcmb0LZb;uP6mmFyTXFH`6YVYO$}1aF(oeL+wl}5%DVKIQ&xN6-w>|wt`iTaJU}HJ} DVP71! literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/621131d8e28a67a6b4fb03c611f4375873d69678 b/webm_parser/fuzzing/corpus/621131d8e28a67a6b4fb03c611f4375873d69678 new file mode 100644 index 000000000000..d037403d522f --- /dev/null +++ b/webm_parser/fuzzing/corpus/621131d8e28a67a6b4fb03c611f4375873d69678 @@ -0,0 +1 @@ +S€gŽC¶u‰ ‡Ž…èƒÌ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/624cbd42e3ebf35a3a0ba55b3b4969b99054f84d b/webm_parser/fuzzing/corpus/624cbd42e3ebf35a3a0ba55b3b4969b99054f84d new file mode 100644 index 0000000000000000000000000000000000000000..0b518b97103ca60382df4ba846ce636d866c56f1 GIT binary patch literal 348 zcmb0LZb<(v7P2n;+qy5;6&M&9i;n_Hre+|)yc0+;bbyG^RX~z)4v4AzxVe!fI1k8l zRN6YH(Wwo{a!y_Zl2UE!Xmkk!DP;}Y4J6qfDlo8xYyz^_fh^YfKn6!{g8~D0k^@j8 zz!1o1lmObl0A&B{bu)7trX@K{4lrzF3@~bB3NUVD4lr#Af7r+hQZxgkX!5#=xea|u z4lN)>NhTnQNy-%@y|}}v$suG@YErI5LwfK^Y3FUFZGPjUYo8!^~!x?ZDXNzz9;kz=36Ut^*?g61Z^g literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6290103200631075988aa3566d8d8922599f8b6d b/webm_parser/fuzzing/corpus/6290103200631075988aa3566d8d8922599f8b6d new file mode 100644 index 000000000000..52f240dea799 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6290103200631075988aa3566d8d8922599f8b6d @@ -0,0 +1 @@ +€ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/62a329f65143f1571dc4a2fbb256800c8ce4d89f b/webm_parser/fuzzing/corpus/62a329f65143f1571dc4a2fbb256800c8ce4d89f new file mode 100644 index 0000000000000000000000000000000000000000..eca1ba98fb0f374a519138ec2af23f2e60357f7c GIT binary patch literal 52 zcmb0LZb;uP6mmFyTXFH`^b;Fg7dIxlR5d2Kv@|lLv^OTZv^FxPpP1>=-k6epqS>Xr H1;_#bX#N*p literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/62f8a53cfb4dd625dcb31ee2ba258db3916a3bd0 b/webm_parser/fuzzing/corpus/62f8a53cfb4dd625dcb31ee2ba258db3916a3bd0 new file mode 100644 index 000000000000..a3a36dc27014 --- /dev/null +++ b/webm_parser/fuzzing/corpus/62f8a53cfb4dd625dcb31ee2ba258db3916a3bd0 @@ -0,0 +1 @@ +S€gTÃg˜ss•gÈ’E£€gÈ€D„€D‡€D…€gÈ€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6318dc574b3ad7291fee7e01bfeaa674eb0feb48 b/webm_parser/fuzzing/corpus/6318dc574b3ad7291fee7e01bfeaa674eb0feb48 new file mode 100644 index 000000000000..baa7b64c3fcc --- /dev/null +++ b/webm_parser/fuzzing/corpus/6318dc574b3ad7291fee7e01bfeaa674eb0feb48 @@ -0,0 +1 @@ +S€g˜T®k“®‘m€Žb@„P1b@„P1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6457a079debb9b532255e3e6ddf276b29808b49d b/webm_parser/fuzzing/corpus/6457a079debb9b532255e3e6ddf276b29808b49d new file mode 100644 index 0000000000000000000000000000000000000000..640d4a1fc416ccab4adefa47c14fc3de049bc343 GIT binary patch literal 3 KcmdnZ!T@)8f`fki_EFCJ?p2fn_#O0zxqX E039w6ivR!s literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6714df1ac1c54d4c30add212454e9513c6880767 b/webm_parser/fuzzing/corpus/6714df1ac1c54d4c30add212454e9513c6880767 new file mode 100644 index 000000000000..20b7616ee483 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6714df1ac1c54d4c30add212454e9513c6880767 @@ -0,0 +1 @@ +S€gŒT®k‡®…àƒ° \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/68a6f9c2f62e9f57667de9563b5d612ec2f2c829 b/webm_parser/fuzzing/corpus/68a6f9c2f62e9f57667de9563b5d612ec2f2c829 new file mode 100644 index 0000000000000000000000000000000000000000..d8309eadaad21768e51f3687c556830f154cb48f GIT binary patch literal 37 ccmb1gz5m96!Kn#|+Z*0AIJGvQbDJ2M02{Lp2mk;8 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/69089b76cc62fe00615702bdd1f339e259194d42 b/webm_parser/fuzzing/corpus/69089b76cc62fe00615702bdd1f339e259194d42 new file mode 100644 index 000000000000..f925002653e1 --- /dev/null +++ b/webm_parser/fuzzing/corpus/69089b76cc62fe00615702bdd1f339e259194d42 @@ -0,0 +1 @@ +S€gŽT®k‰®‡à…šƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/69d8622d24c7315f344c424297986170f7ef2243 b/webm_parser/fuzzing/corpus/69d8622d24c7315f344c424297986170f7ef2243 new file mode 100644 index 000000000000..6182f76ecc21 --- /dev/null +++ b/webm_parser/fuzzing/corpus/69d8622d24c7315f344c424297986170f7ef2243 @@ -0,0 +1 @@ +S€g“C¶uŽ ŒŽŠèƒÌèƒÌS€gC¶uˆ †Ž„è‚Ì€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6a700d958cb3a7565d5a6b74540486874d52b4c7 b/webm_parser/fuzzing/corpus/6a700d958cb3a7565d5a6b74540486874d52b4c7 new file mode 100644 index 000000000000..3c4a4a45fe50 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6a700d958cb3a7565d5a6b74540486874d52b4c7 @@ -0,0 +1 @@ +S€g¼C§p·E¹´¶²€ …@helloC|…lang0C~…area0C|…lang€…S|1C–T®k‘®m€Œb@‰P2€ÿ†G*lang2C~ƒGè…€area1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6a852150c5c2a12d325e78056687a8ea97b25fe7 b/webm_parser/fuzzing/corpus/6a852150c5c2a12d325e78056687a8ea97b25fe7 new file mode 100644 index 000000000000..ca14558f9018 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6a852150c5c2a12d325e78056687a8ea97b25fe7 @@ -0,0 +1 @@ +S€g‡T®k‚®€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6ac8c9f754d5ff3c0cd3f3f585de99dd73944bbe b/webm_parser/fuzzing/corpus/6ac8c9f754d5ff3c0cd3f3f585de99dd73944bbe new file mode 100644 index 0000000000000000000000000000000000000000..a7d88f00e039c8d3dbc55416ebd2ddd617e70d35 GIT binary patch literal 46 lcmb0LZb+XZ@4T(Fe{pXk149cl1H)TJ1||Up2}}uQW&mCX4SN6p literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6ace27a48f12990e2169cc099007aa5fa11962d2 b/webm_parser/fuzzing/corpus/6ace27a48f12990e2169cc099007aa5fa11962d2 new file mode 100644 index 000000000000..f5449305e7b2 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6ace27a48f12990e2169cc099007aa5fa11962d2 @@ -0,0 +1 @@ +S€g™T®k”®’m€b@ŒT2€P2€P3P5€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6b10397dc3bff2ccf57be4fe3ff9d8d1acb8c76e b/webm_parser/fuzzing/corpus/6b10397dc3bff2ccf57be4fe3ff9d8d1acb8c76e new file mode 100644 index 0000000000000000000000000000000000000000..db9ed48f183cf09af04deaf5a474b00b51071c79 GIT binary patch literal 156 zcmb1gy}x*+Q(GgG)AvRer%w({3`|a+8(H2sFgP_iu#~4JfPsOL(WSG+x9{^Im&A_$P*A-RD8ZbT ypYPiMVk9LNg*!qRRWiZ5vuE#~xw(;X`;_kvjEo;07@0meFfwm)U}OodXaoROJ3Y_< literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6c3f1d96c06b853551f685dde821142928f55acf b/webm_parser/fuzzing/corpus/6c3f1d96c06b853551f685dde821142928f55acf new file mode 100644 index 0000000000000000000000000000000000000000..91f4e815dc5e992252f3e1131081f7fb8e1c3df8 GIT binary patch literal 25 gcmb0LZb+XZ@4T(Fe{pXk149ct0|O%?6B9Et0BrUKO8@`> literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6c8de4d4bbb5294154c3b140d43836757b9f08ac b/webm_parser/fuzzing/corpus/6c8de4d4bbb5294154c3b140d43836757b9f08ac new file mode 100644 index 000000000000..8aefea242d5c --- /dev/null +++ b/webm_parser/fuzzing/corpus/6c8de4d4bbb5294154c3b140d43836757b9f08ac @@ -0,0 +1 @@ +S€g‰I©f„{©! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6cf122ee328af5cf6f18d5f9546b0fe761951b8d b/webm_parser/fuzzing/corpus/6cf122ee328af5cf6f18d5f9546b0fe761951b8d new file mode 100644 index 0000000000000000000000000000000000000000..56346390ca16d37bc5078685da9d23a6f3ec802f GIT binary patch literal 128 zcmb0LP7hhva7`?G-TCVZ3=G9b6&NIf8`7tUg)lS&`8$EQ1BgRcF)+*l(#nsU8-nwI z45h7e8rpz#@*)M1b=m#vdUG4Pk{sFt3|ibDH7bIP=n@NAm)*LqnL)L!qakb+Q0;CY SegMQf8y_nCXJE)}0I~tKAS%BA literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/6cfbbc25caa217e22fd7ca3e92d2051c5f661bdb b/webm_parser/fuzzing/corpus/6cfbbc25caa217e22fd7ca3e92d2051c5f661bdb new file mode 100644 index 000000000000..bc69d35561d9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6cfbbc25caa217e22fd7ca3e92d2051c5f661bdb @@ -0,0 +1 @@ +S€gC¶uˆ †Ž„è‚Ì€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6d703bc900e74d1a576bebcccb63e3b2701ae86d b/webm_parser/fuzzing/corpus/6d703bc900e74d1a576bebcccb63e3b2701ae86d new file mode 100644 index 000000000000..e0bdcf240a40 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6d703bc900e74d1a576bebcccb63e3b2701ae86d @@ -0,0 +1 @@ +S€g˜T®k“®‘m€Žb@„P1b@€b@€P1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6e1afa81dfcd6833705d84b45881f085c19b2211 b/webm_parser/fuzzing/corpus/6e1afa81dfcd6833705d84b45881f085c19b2211 new file mode 100644 index 000000000000..670dd7c2849a --- /dev/null +++ b/webm_parser/fuzzing/corpus/6e1afa81dfcd6833705d84b45881f085c19b2211 @@ -0,0 +1 @@ +S€gC¶uŠ ˆu¡…¦ƒ¥! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6e244ec3b81e0d2f8b5810854859e1b8140422d9 b/webm_parser/fuzzing/corpus/6e244ec3b81e0d2f8b5810854859e1b8140422d9 new file mode 100644 index 000000000000..2e7c1a4d82d4 --- /dev/null +++ b/webm_parser/fuzzing/corpus/6e244ec3b81e0d2f8b5810854859e1b8140422d9 @@ -0,0 +1 @@ +S€gûû \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6e323b4c733df90d55c754e099fb5a13bd9701cb b/webm_parser/fuzzing/corpus/6e323b4c733df90d55c754e099fb5a13bd9701cb new file mode 100644 index 000000000000..89e0e79cf86b --- /dev/null +++ b/webm_parser/fuzzing/corpus/6e323b4c733df90d55c754e099fb5a13bd9701cb @@ -0,0 +1 @@ +S€gT®kŠ®ˆà†T°ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/6e36720f3f55dc0cd5d585c6770572964719b5b8 b/webm_parser/fuzzing/corpus/6e36720f3f55dc0cd5d585c6770572964719b5b8 new file mode 100644 index 0000000000000000000000000000000000000000..376c75bf6dda4c072fb3f485354108b4331ac6bd GIT binary patch literal 56 zcmb1gy}!7_smXz*JT)m-BDf*l;h3QB?2=yJ-2w~@?7?e*BqNhp$hz$AbwDQDLm%J=k012l8mH+?% literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/745e32f46833c075c8e68552e0fbbcdd5ec52641 b/webm_parser/fuzzing/corpus/745e32f46833c075c8e68552e0fbbcdd5ec52641 new file mode 100644 index 0000000000000000000000000000000000000000..518d182473dd1c5e91cd6335c392ac3cd9a2db1f GIT binary patch literal 15 WcmaFU@TTDng8&1=n?{B?4C?_rBL%K&AL;79;=j8=ETz78Wwz>F7V=7}j7|{Bd7A@+ TP;n#6J_k0SLe^~#DnJSV*Tfm{ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/7528086eea404dccc3ee5b214befbc2a95676feb b/webm_parser/fuzzing/corpus/7528086eea404dccc3ee5b214befbc2a95676feb new file mode 100644 index 000000000000..40b7db2f24bf --- /dev/null +++ b/webm_parser/fuzzing/corpus/7528086eea404dccc3ee5b214befbc2a95676feb @@ -0,0 +1 @@ +S€g‘T®kŒ®Šàˆ#ƒã„ÉÛ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/7528f9f08f80c8e85951aa1db2dc616e992c1a62 b/webm_parser/fuzzing/corpus/7528f9f08f80c8e85951aa1db2dc616e992c1a62 new file mode 100644 index 0000000000000000000000000000000000000000..87b29be574d05ccba453b19a81980a7e026a7966 GIT binary patch literal 26 gcmb0LZb+Xh;JmzGg6q!SZQTu>tqu&%bqzoS0Fj6a{r~^~ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/75af94f9695c375e5eb620849f0213ad41e8f205 b/webm_parser/fuzzing/corpus/75af94f9695c375e5eb620849f0213ad41e8f205 new file mode 100644 index 0000000000000000000000000000000000000000..dcf5dc6230bd43e82a37793d15d9b8986c15c9d0 GIT binary patch literal 39 rcmb0LZb)A&@4T&a-hw#`TN@dGU{MQ*DqYwx+kxS?14HSehCT-XC~^+G literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/75e1ff2e016b7b80ecb7a6a1f4c3238a2a2ed130 b/webm_parser/fuzzing/corpus/75e1ff2e016b7b80ecb7a6a1f4c3238a2a2ed130 new file mode 100644 index 0000000000000000000000000000000000000000..9dcbbb5378a1aacabbdb810fa911dfac0075ee0d GIT binary patch literal 18 Zcmb0LZbBDf)arM&aD(k0Iw7#UYPFfuJ}ZJZ5cE^uH0QVb9Z%0pGn P3{}kl1dCgn7#NrUlt&#h literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/7670db89e0c3fe492c91458de219c3be34a3dcb7 b/webm_parser/fuzzing/corpus/7670db89e0c3fe492c91458de219c3be34a3dcb7 new file mode 100644 index 0000000000000000000000000000000000000000..c57ba43505f93cfa45f1a323909032b712f81583 GIT binary patch literal 118 zcmb1gy}P)>smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfy%eZDL?x&|2UiFx!Fg?vfxsV}w M5m2bLi9wYE0G47W=l}o! literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/7880e2ae55d255065ad415c4c625e1b63bfd2a93 b/webm_parser/fuzzing/corpus/7880e2ae55d255065ad415c4c625e1b63bfd2a93 new file mode 100644 index 000000000000..e2fb6ac284dd --- /dev/null +++ b/webm_parser/fuzzing/corpus/7880e2ae55d255065ad415c4c625e1b63bfd2a93 @@ -0,0 +1 @@ +DzS€g–T®k‘®m€Œb@‰P5ÿ†GçƒGè€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/78cb3d726e4f9e8bc89b399fa514c3b600bf8e5a b/webm_parser/fuzzing/corpus/78cb3d726e4f9e8bc89b399fa514c3b600bf8e5a new file mode 100644 index 0000000000000000000000000000000000000000..fc42e545b9c0cdbc7cbf8c829256a3924f604014 GIT binary patch literal 185 zcmb1gy}$UsQwY9{_2~I0X`Cl6T%#+V&jG6bs&+-3>C5Z8M0^DBx_hyr9>0 fCy>MLx(6&I1T}?$ffs6u2D*6q36Of1c91v#FWN7w literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/79ba60931988a5974328a73fe091bdf6f5992891 b/webm_parser/fuzzing/corpus/79ba60931988a5974328a73fe091bdf6f5992891 new file mode 100644 index 0000000000000000000000000000000000000000..c6837fd70eb1e2459775b9f76b88daa1d659f0a1 GIT binary patch literal 24 gcmaFUaHb)|sZDF;JBD}f-nGAB5MW@Ce)sMT0HbCM#sB~S literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/79cdb8dec1ad07b389f544a511f89b347429837e b/webm_parser/fuzzing/corpus/79cdb8dec1ad07b389f544a511f89b347429837e new file mode 100644 index 0000000000000000000000000000000000000000..68e6b7d0bb5a77d5cc4925adab439e5e9ac1545f GIT binary patch literal 52 zcmb0LZb;uP6mmFyTXFH`^b;Fg7dIxlR5d2Kv@|lfv^OTZv^FxPpO^_`rlg-}c4=<` FvH)fN7e4?1 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/7ab8c6a65c39907f4afa4e2b294303aebb6b22d8 b/webm_parser/fuzzing/corpus/7ab8c6a65c39907f4afa4e2b294303aebb6b22d8 new file mode 100644 index 000000000000..fddf510be6b9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/7ab8c6a65c39907f4afa4e2b294303aebb6b22d8 @@ -0,0 +1 @@ +°€€€€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/7b12a398a1860ac2f3930d5020e422aec061f177 b/webm_parser/fuzzing/corpus/7b12a398a1860ac2f3930d5020e422aec061f177 new file mode 100644 index 000000000000..fed31c6f1e9b --- /dev/null +++ b/webm_parser/fuzzing/corpus/7b12a398a1860ac2f3930d5020e422aec061f177 @@ -0,0 +1 @@ +S€gŠT®k…®ƒ¹ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/7bf1682743405f3d5b3433830eadae4ea311807d b/webm_parser/fuzzing/corpus/7bf1682743405f3d5b3433830eadae4ea311807d new file mode 100644 index 000000000000..d8666eb112c7 --- /dev/null +++ b/webm_parser/fuzzing/corpus/7bf1682743405f3d5b3433830eadae4ea311807d @@ -0,0 +1 @@ +S€g»TÃg¶ss³gÈ©È„#‡dgÈ„]‡egÈ„D‡dgÈ„]‡egÈ„D‡gÈ„D‡dgÈg \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/7c7ef0e305f37f833708b375271d59300d120d84 b/webm_parser/fuzzing/corpus/7c7ef0e305f37f833708b375271d59300d120d84 new file mode 100644 index 000000000000..c9cedc0700d9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/7c7ef0e305f37f833708b375271d59300d120d84 @@ -0,0 +1 @@ +S€gŽT®k‰®‡á…Ÿƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/7d06bd4a7de57953ef09c2e18ca67aa7ff367c76 b/webm_parser/fuzzing/corpus/7d06bd4a7de57953ef09c2e18ca67aa7ff367c76 new file mode 100644 index 000000000000..6cce7cb531be --- /dev/null +++ b/webm_parser/fuzzing/corpus/7d06bd4a7de57953ef09c2e18ca67aa7ff367c76 @@ -0,0 +1 @@ +S€gTÃgŠss‡gÈ„E£! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/7d31bd53bb7eb7b1f354c5f85d376390760ab16f b/webm_parser/fuzzing/corpus/7d31bd53bb7eb7b1f354c5f85d376390760ab16f new file mode 100644 index 0000000000000000000000000000000000000000..f3569497024588939604a090742b6946f6f48d0e GIT binary patch literal 32 ocmb0LZb+Xa;JmzGy6es<+a?`p2n%VL=)f?^fuX^HVVeU30N(8kkpKVy literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/7d67ef4d0c681655d4b4e93e848d56865630390b b/webm_parser/fuzzing/corpus/7d67ef4d0c681655d4b4e93e848d56865630390b new file mode 100644 index 0000000000000000000000000000000000000000..ca4a486f2feea1c22fecb0080a008788eecc7c3b GIT binary patch literal 17 Wcmb0LZbXkqV=Cu5L w-v$sPDX}Qr5yGgF3ErJOd-u%Ejf~r;e0N}E{OG{Q^ud9Vd6NSpOK?Ra09D^T&j0`b literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/8206d92b710c04ce0bd706cec25fbb72014c79bf b/webm_parser/fuzzing/corpus/8206d92b710c04ce0bd706cec25fbb72014c79bf new file mode 100644 index 0000000000000000000000000000000000000000..a1233fdeb8e444d75a7b0df4bc7d06036c55917a GIT binary patch literal 64 zcmb0LZb;uP6mmFyTXFH`^b@mP7dIxlR5d2Kv@|lfv^OTZv^FxPpJ)LxQy^3-gi3=@ F=>VR^9QObK literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/822926c528d16c0a9ca4c48a25651f6a0730d797 b/webm_parser/fuzzing/corpus/822926c528d16c0a9ca4c48a25651f6a0730d797 new file mode 100644 index 000000000000..f4269d08a4a5 --- /dev/null +++ b/webm_parser/fuzzing/corpus/822926c528d16c0a9ca4c48a25651f6a0730d797 @@ -0,0 +1 @@ +S€gŽC§p‰E¹†¶„VT!S€g–C§p‘E¹Ž¶@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/82d80b6051f0750777bdc37319851741144b3671 b/webm_parser/fuzzing/corpus/82d80b6051f0750777bdc37319851741144b3671 new file mode 100644 index 000000000000..43b170b730fc --- /dev/null +++ b/webm_parser/fuzzing/corpus/82d80b6051f0750777bdc37319851741144b3671 @@ -0,0 +1 @@ +S€g‹C¶u† „Ž‚è€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/830c73748baaa10c2016d04b408a7f481882cd89 b/webm_parser/fuzzing/corpus/830c73748baaa10c2016d04b408a7f481882cd89 new file mode 100644 index 0000000000000000000000000000000000000000..11b45e96bd0ba5ca55b121a9b309d94a0b472259 GIT binary patch literal 88 zcmb0LP7hhva7`?G-TCVZ3=G9bfkZP9?*!rwAP!x{z%U0$D?e^-2+jjCl(t$mv;pbl aO+Z4mt)n4q6_BwTh#vs)Lml;DR E0O0%+f&c&j literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/832fad0e723027e5bd74726b1722a838183dada7 b/webm_parser/fuzzing/corpus/832fad0e723027e5bd74726b1722a838183dada7 new file mode 100644 index 000000000000..3545ab8820fc --- /dev/null +++ b/webm_parser/fuzzing/corpus/832fad0e723027e5bd74726b1722a838183dada7 @@ -0,0 +1 @@ +S€gTÃgŠss‡gÈ„D„ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/83b78d2ad8afd0729de45b4d9fdd765a949d8073 b/webm_parser/fuzzing/corpus/83b78d2ad8afd0729de45b4d9fdd765a949d8073 new file mode 100644 index 000000000000..5cb98fecf82c --- /dev/null +++ b/webm_parser/fuzzing/corpus/83b78d2ad8afd0729de45b4d9fdd765a949d8073 @@ -0,0 +1 @@ +S€g¼C§p·E¹´¶²€ …@helloC|…lang0C~…area0C|…lang1C|C§p·E \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/84a04bf3e15345f5c992bc6732a42b95857631a7 b/webm_parser/fuzzing/corpus/84a04bf3e15345f5c992bc6732a42b95857631a7 new file mode 100644 index 000000000000..e89d0d1c79d5 --- /dev/null +++ b/webm_parser/fuzzing/corpus/84a04bf3e15345f5c992bc6732a42b95857631a7 @@ -0,0 +1 @@ +S€g“C¶uŽ ŒŽŠèƒÌèƒÌ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/84d3a72c434074ee0d810fb2357047efd23f58f6 b/webm_parser/fuzzing/corpus/84d3a72c434074ee0d810fb2357047efd23f58f6 new file mode 100644 index 0000000000000000000000000000000000000000..e22f95bb90bfd374c1a26435c268d5b8f3731996 GIT binary patch literal 126 zcmb0LZb+Xa6mmFydU5fT4lF>*d7A?_P>5xl155FfMph677=S!BkO|X3CQNkQ*#}X<2%(q& DM@KWl literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/8659fe309298ca90f201ac7ced95b9d0de510eeb b/webm_parser/fuzzing/corpus/8659fe309298ca90f201ac7ced95b9d0de510eeb new file mode 100644 index 000000000000..77449c79eacc --- /dev/null +++ b/webm_parser/fuzzing/corpus/8659fe309298ca90f201ac7ced95b9d0de510eeb @@ -0,0 +1 @@ +S€gˆC¶uƒ« \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/866cf21064b9e20eded44e2e096fd9ce6185ce86 b/webm_parser/fuzzing/corpus/866cf21064b9e20eded44e2e096fd9ce6185ce86 new file mode 100644 index 0000000000000000000000000000000000000000..f3a4ddd54547ba8ef656d7e09c50a189e71ee06d GIT binary patch literal 8 PcmZ>N#K6kHaOMmE3hM&L literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/8864c6c9319ef120d3accb7b70c32c8f5c1ebccb b/webm_parser/fuzzing/corpus/8864c6c9319ef120d3accb7b70c32c8f5c1ebccb new file mode 100644 index 0000000000000000000000000000000000000000..d6f7c00cff5a3d6c26a5ed8a2cc604e1024f33eb GIT binary patch literal 10 Rcmey(ETHg*fq^mo4*(Mg1CIaz literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/88f01cffe9972bf447a21034c45f6b943ea3ec07 b/webm_parser/fuzzing/corpus/88f01cffe9972bf447a21034c45f6b943ea3ec07 new file mode 100644 index 0000000000000000000000000000000000000000..97754f40b8cc1dc10921db2c6bfbe6fb217fa964 GIT binary patch literal 36 rcmb0LZb;uC@4T&a^@5cPTZ0)Sn!HAa;OW^VSA2I1FfatK5ny3pWEAwBUD68{U=LoC z!T^+DWD=Ef{g0~g|4Mo1ZKa+o(>k=SZxjToVT7{5>OpdC&p}K^`TL98odo^^4Uqr} t0?5k#V&HBh%tmseP{ZmD3usrI^G*)^)D~>ST+$4gdgJIm`e6 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/8c695cbcc43e37ce25194c4b61b3d87488b308a1 b/webm_parser/fuzzing/corpus/8c695cbcc43e37ce25194c4b61b3d87488b308a1 new file mode 100644 index 000000000000..6219e9f2d660 --- /dev/null +++ b/webm_parser/fuzzing/corpus/8c695cbcc43e37ce25194c4b61b3d87488b308a1 @@ -0,0 +1 @@ +S€g‘T®kŒ®Šm€‡b@„P1S€g”T®k®m€Šb@‡P5€GáÿS€g™T®k”®’m€b@ŒP5€P5€Ps€P5€S€g˜T®k“®‘m€Žb@„P1b@„P1S€gT®kˆ®†à„Tº \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/8c9dc3c5a839f90f29256ef8c8f1369bc7383817 b/webm_parser/fuzzing/corpus/8c9dc3c5a839f90f29256ef8c8f1369bc7383817 new file mode 100644 index 000000000000..85078ca15bc3 --- /dev/null +++ b/webm_parser/fuzzing/corpus/8c9dc3c5a839f90f29256ef8c8f1369bc7383817 @@ -0,0 +1 @@ +WA¢€€ˆƒ€€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/8cdcf83eb89d7e5c87cfa1b8811c95f738747a6a b/webm_parser/fuzzing/corpus/8cdcf83eb89d7e5c87cfa1b8811c95f738747a6a new file mode 100644 index 000000000000..8db4eecd9151 --- /dev/null +++ b/webm_parser/fuzzing/corpus/8cdcf83eb89d7e5c87cfa1b8811c95f738747a6a @@ -0,0 +1 @@ +S€g‰I©f„M€! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/8d0c6e6b3d74233685c3a2c1793d1a7b0ae2e34d b/webm_parser/fuzzing/corpus/8d0c6e6b3d74233685c3a2c1793d1a7b0ae2e34d new file mode 100644 index 0000000000000000000000000000000000000000..b4ef3d53814af60837b8df408c239a37dc5d1bfc GIT binary patch literal 18 Zcmb0LZb=-q->WY6J?k N7avJK(E=1p2LO8E8~*?R literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/8f403efbef079f1775b63bcf1061983e4c5d2162 b/webm_parser/fuzzing/corpus/8f403efbef079f1775b63bcf1061983e4c5d2162 new file mode 100644 index 000000000000..7a0e07094e40 --- /dev/null +++ b/webm_parser/fuzzing/corpus/8f403efbef079f1775b63bcf1061983e4c5d2162 @@ -0,0 +1 @@ +S€g”T®k®m€Šb@‡P5„Gâ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/8f8f956eaaf8f7f5e65002be888b946218f0f7ce b/webm_parser/fuzzing/corpus/8f8f956eaaf8f7f5e65002be888b946218f0f7ce new file mode 100644 index 000000000000..7ebec698d390 --- /dev/null +++ b/webm_parser/fuzzing/corpus/8f8f956eaaf8f7f5e65002be888b946218f0f7ce @@ -0,0 +1 @@ +€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ„€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€!ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€!ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ„€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€!ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶Š¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶Š¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶¶ÿ€¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ„€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€!ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶Š¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶Š¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶¶ÿ€¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ„€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€wÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶¶€¶ÿ¶€m¶ÿ¶€¶ÿ¶¶®®Ý€¶ÿ¶€¶ÿ¶€¶€µ¶€¶Œÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€å¶€¶€¶ÿ¶€Eߣ¶€¶À¶ÿ¶€m¶ÿ¶€¶ÿ¶¶®®Ý€¶ÿ¶€¶ÿ¶€¶€µ¶€¶Œÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/90a03241b5cc656b7c2a4ef55664b0320fa3bd43 b/webm_parser/fuzzing/corpus/90a03241b5cc656b7c2a4ef55664b0320fa3bd43 new file mode 100644 index 000000000000..ac884827ed53 --- /dev/null +++ b/webm_parser/fuzzing/corpus/90a03241b5cc656b7c2a4ef55664b0320fa3bd43 @@ -0,0 +1 @@ +S€g”C¶u u¡Š¦ƒî¦€¦€î \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/925a91e7c25a43d9da8b63eba51ed66a52d833aa b/webm_parser/fuzzing/corpus/925a91e7c25a43d9da8b63eba51ed66a52d833aa new file mode 100644 index 0000000000000000000000000000000000000000..8eac37e5ef74069e5e8510831b96fd5b166c33ae GIT binary patch literal 42 wcmWGxaF7UYNS`70rX^$@L-yo#6LTBX`;r*`TRF4@7&bB{IkYe`0U1op08%avcmMzZ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/92de5c217802ec24886240188dca293325eb17c9 b/webm_parser/fuzzing/corpus/92de5c217802ec24886240188dca293325eb17c9 new file mode 100644 index 000000000000..8237f0b5241e --- /dev/null +++ b/webm_parser/fuzzing/corpus/92de5c217802ec24886240188dca293325eb17c9 @@ -0,0 +1 @@ +S€g‹T®k†®„Sn! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/939da66e1fc8bb9854865066ee47b6ac4b933aa9 b/webm_parser/fuzzing/corpus/939da66e1fc8bb9854865066ee47b6ac4b933aa9 new file mode 100644 index 0000000000000000000000000000000000000000..f5179b1469473fda4c255ff5293545fefccaf5b0 GIT binary patch literal 107 zcmb0LZb<(l;JmzGyX(#^+cq^UXmw!CD9K69ajt94Nz6+(0Mdy?sfi$3iuL~D1`GgH jWa#{-F3%{Z)j2(}6(OH!DCK&8K~G;-0|NuY0tW{G281q@ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/93a91331e7c3c060f461ae1f22e2bbf4747ffe33 b/webm_parser/fuzzing/corpus/93a91331e7c3c060f461ae1f22e2bbf4747ffe33 new file mode 100644 index 0000000000000000000000000000000000000000..1b2868432336c55b158218032f22420e556f7ebd GIT binary patch literal 64 zcmb0LZb;uP6mmFyTXFH`^b;GbTvsPkLwKg)iwEpjONp56pO+PWyrM Q?9$ran3jH`1t^vd0HqTgssI20 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/94f6dfc499cbf2e84ffe1d69cb751ada9f2f2e3a b/webm_parser/fuzzing/corpus/94f6dfc499cbf2e84ffe1d69cb751ada9f2f2e3a new file mode 100644 index 000000000000..30a5c63cef53 --- /dev/null +++ b/webm_parser/fuzzing/corpus/94f6dfc499cbf2e84ffe1d69cb751ada9f2f2e3a @@ -0,0 +1 @@ +S€gŠC¶u… ƒ› \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/9539fef8ffb9f7c542061f1af1f3f98e9a714cc3 b/webm_parser/fuzzing/corpus/9539fef8ffb9f7c542061f1af1f3f98e9a714cc3 new file mode 100644 index 0000000000000000000000000000000000000000..0ea96501d095ffe58d28cd53c8dc7e925d5453ab GIT binary patch literal 1132 zcmb0LZb)A&@4T&a-hw#`TN@dGU{MQ*DqYwx%Yor{3q$FmhCT-d381`PE8=g@oDNv@0G`95B2Pja=pK}!>P%E zr93q$mzrLqL3j{y>+cpCMuS9fIxx~()Z6+>7rbaX(;$SI3}|gXf#_KT^gjLsMO*;Z z;!ToR6F+`yC4xce7(DbZn8oMOD9s>;lSFW1`btodvE;eKYevS^ F4gl^Cr49f9 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/953c9a616ea56067225c88fccc6423496f2501a6 b/webm_parser/fuzzing/corpus/953c9a616ea56067225c88fccc6423496f2501a6 new file mode 100644 index 0000000000000000000000000000000000000000..249824c23f9a4957c59810fe13936e1c0415b5cd GIT binary patch literal 39 scmb0#y}!7_smXz*JT)m-BDf)as=V{I(h1KU7#27%EN*RN00JNj04dcCp#T5? literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/95a60268c555a77c1010d75bb44142af47635486 b/webm_parser/fuzzing/corpus/95a60268c555a77c1010d75bb44142af47635486 new file mode 100644 index 000000000000..a95f40a96121 --- /dev/null +++ b/webm_parser/fuzzing/corpus/95a60268c555a77c1010d75bb44142af47635486 @@ -0,0 +1 @@ +€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€³ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€wÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/95e410025b965cf6ab2e8e2d3559efbe71c1a972 b/webm_parser/fuzzing/corpus/95e410025b965cf6ab2e8e2d3559efbe71c1a972 new file mode 100644 index 0000000000000000000000000000000000000000..8eddf69a2e5f1ec12fae1699db3bdca27c20988f GIT binary patch literal 44 icmb1gb^6rsropMT;Z2hRgA)jUM&?H`IJHA$8JPgLv=XiW literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/9671f253d1eb7cd504a5617058ce4c01a80c897e b/webm_parser/fuzzing/corpus/9671f253d1eb7cd504a5617058ce4c01a80c897e new file mode 100644 index 0000000000000000000000000000000000000000..f0b837c7238932d2580ce21ecaf91ca5832e646d GIT binary patch literal 15 WcmaFS@TTDng8&1=n?{B?4C?_q=LU%Y literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/9698d3424f1581a6bbb66c764f14c95a74b87675 b/webm_parser/fuzzing/corpus/9698d3424f1581a6bbb66c764f14c95a74b87675 new file mode 100644 index 0000000000000000000000000000000000000000..3aedc3202faeaf2bd038236efb773b42e8e06efd GIT binary patch literal 64 zcmb0LZb;uP6mmFyTXFH`^b;Fg7dIxlR835BX=!9|X>Uw+X>DXoKQR-?Oi4e{4dkV! MpJ)N{(jZhi0H^OAHUIzs literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/96f7f38de1f8601311733984cf51cec35500dabe b/webm_parser/fuzzing/corpus/96f7f38de1f8601311733984cf51cec35500dabe new file mode 100644 index 000000000000..1230f4cafdee --- /dev/null +++ b/webm_parser/fuzzing/corpus/96f7f38de1f8601311733984cf51cec35500dabe @@ -0,0 +1 @@ +Uª ÿM \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/970ab6333aa5ccf8c2dc14bb56814f7bb4303b94 b/webm_parser/fuzzing/corpus/970ab6333aa5ccf8c2dc14bb56814f7bb4303b94 new file mode 100644 index 0000000000000000000000000000000000000000..cf0c884ea028132a858fe50e2b94edea51e918e6 GIT binary patch literal 53 zcmb0LZb;uC@4T&a^@5cPTbmd-7#J3{G&3+TlrC&u=D>guVq_{^)cC*8f%%040{|4F B6G#96 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/98978113d2116dc4bdbb10265fa32bd95230bdb6 b/webm_parser/fuzzing/corpus/98978113d2116dc4bdbb10265fa32bd95230bdb6 new file mode 100644 index 000000000000..0da58250726b --- /dev/null +++ b/webm_parser/fuzzing/corpus/98978113d2116dc4bdbb10265fa32bd95230bdb6 @@ -0,0 +1 @@ +S€g”C¶u u¡Š¦ƒî¦ƒîS€gC¶uŠ ˆu¡…¦ƒ¥!S€g”C¶u u¡Š¦ƒî¦ƒîS€gC¶uŠ ˆu¡…¦ƒ¥! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/989e2872d2a34de543f23c5061db68212d8258f7 b/webm_parser/fuzzing/corpus/989e2872d2a34de543f23c5061db68212d8258f7 new file mode 100644 index 0000000000000000000000000000000000000000..3430234146c1fc024a47a73e204e5826e3506e54 GIT binary patch literal 21 ccmdnl{6FQvf5jC*>ivJk#{Ucq|Nlw@0G2Tf)Bpeg literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/98ca9a00ad571c4454fce709d5405e5aca2a363c b/webm_parser/fuzzing/corpus/98ca9a00ad571c4454fce709d5405e5aca2a363c new file mode 100644 index 000000000000..938e0bc5115c --- /dev/null +++ b/webm_parser/fuzzing/corpus/98ca9a00ad571c4454fce709d5405e5aca2a363c @@ -0,0 +1 @@ +Eߣ„Bóÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/9965361765a4151902ec04fb21d9247b3a5ed10d b/webm_parser/fuzzing/corpus/9965361765a4151902ec04fb21d9247b3a5ed10d new file mode 100644 index 000000000000..6ec73d46382f --- /dev/null +++ b/webm_parser/fuzzing/corpus/9965361765a4151902ec04fb21d9247b3a5ed10d @@ -0,0 +1 @@ +S€g‘T®kŒ®Šàˆ#ƒã„@ÉÛ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/99d6fe94a50faa50db9d7eb38d74bc3cc8417dc1 b/webm_parser/fuzzing/corpus/99d6fe94a50faa50db9d7eb38d74bc3cc8417dc1 new file mode 100644 index 0000000000000000000000000000000000000000..0dcb93ea81d8547efa4fa962e4c52740d419ad28 GIT binary patch literal 39 lcmb0LZb)Aw>bWv)uGaO94KAGu3=A%bAOg;+UTMp~0007F3ibd1 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/9a6869cec3dc84f2051bfaf0ee0d3552aa221f89 b/webm_parser/fuzzing/corpus/9a6869cec3dc84f2051bfaf0ee0d3552aa221f89 new file mode 100644 index 0000000000000000000000000000000000000000..32ad695ce24e33c9ce8a47e7d5520a686679671a GIT binary patch literal 218 zcmb1gy}P)>smXz*T*~$S;tnqmD>W%sBDf*l;h3QB?2=yJ-2w~@?7^#nBx55E5kcRT zX&qYEzkt**$vbZ=ZF>%4G9s8j*^OW!QO}id1_3=xVj=6YyVn7=v0Vr21@UJi_)Idv byR*9wf#f%XAMgWay3k3iG literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/9cca361865a4fbff75abdbb79c1e91706780576c b/webm_parser/fuzzing/corpus/9cca361865a4fbff75abdbb79c1e91706780576c new file mode 100644 index 000000000000..67357290814e --- /dev/null +++ b/webm_parser/fuzzing/corpus/9cca361865a4fbff75abdbb79c1e91706780576c @@ -0,0 +1 @@ +S€gŽT®k‰®‡à…°ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/9d8e99f07604d6cb05ef613d41cbfb93b2aff787 b/webm_parser/fuzzing/corpus/9d8e99f07604d6cb05ef613d41cbfb93b2aff787 new file mode 100644 index 000000000000..96aa349010ad --- /dev/null +++ b/webm_parser/fuzzing/corpus/9d8e99f07604d6cb05ef613d41cbfb93b2aff787 @@ -0,0 +1 @@ +S€gŒT®k‡®…àƒšÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/9ecd61eaf2681a882473247a603b9f30c2663d49 b/webm_parser/fuzzing/corpus/9ecd61eaf2681a882473247a603b9f30c2663d49 new file mode 100644 index 000000000000..256c70d00542 --- /dev/null +++ b/webm_parser/fuzzing/corpus/9ecd61eaf2681a882473247a603b9f30c2663d49 @@ -0,0 +1 @@ +S€g“C¶uŽ ŒŽŠèÿÌèƒÌ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/9f0a3b7c0814b4f80c0745161c8769f63098981b b/webm_parser/fuzzing/corpus/9f0a3b7c0814b4f80c0745161c8769f63098981b new file mode 100644 index 0000000000000000000000000000000000000000..fe2403bb5bd5e979f6ecf9d5cc8500927a1f3498 GIT binary patch literal 19 Zcmb0LZb((2A3K|)qJP?PG31rYTv5jIQIYm literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/9fae60819c28d4fcc88a6a1b93dcf69b4e458203 b/webm_parser/fuzzing/corpus/9fae60819c28d4fcc88a6a1b93dcf69b4e458203 new file mode 100644 index 0000000000000000000000000000000000000000..9194d9d28c18324534d0f6c3e97c273b4c41f0c3 GIT binary patch literal 6 NcmaFU!obMH3;+l$0e1iZ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/a02431cf7c501a5b368c91e41283419d8fa9fb03 b/webm_parser/fuzzing/corpus/a02431cf7c501a5b368c91e41283419d8fa9fb03 new file mode 100644 index 0000000000000000000000000000000000000000..17706ff6b3fca632b077b72117a5df5883610154 GIT binary patch literal 2 JcmYdc0002W0A&CG literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/a0ac6c3c83817637bbbcb11a5106c57aa6654afb b/webm_parser/fuzzing/corpus/a0ac6c3c83817637bbbcb11a5106c57aa6654afb new file mode 100644 index 0000000000000000000000000000000000000000..a5d2ab48564fd72270ff8c82a4da85aa7aa85e88 GIT binary patch literal 12 Tcmb1gy}!8Ksj0;ylYs#MAgu&q literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/a15fdfa620d19a92d9eaa9f3f13010e53f902796 b/webm_parser/fuzzing/corpus/a15fdfa620d19a92d9eaa9f3f13010e53f902796 new file mode 100644 index 000000000000..d5b108f3cfec --- /dev/null +++ b/webm_parser/fuzzing/corpus/a15fdfa620d19a92d9eaa9f3f13010e53f902796 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶@…@B€@…XC¶@sĶ@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a18e76ae792a054c2f6d0d01e0e78d58678b35e3 b/webm_parser/fuzzing/corpus/a18e76ae792a054c2f6d0d01e0e78d58678b35e3 new file mode 100644 index 000000000000..135e70d3231c --- /dev/null +++ b/webm_parser/fuzzing/corpus/a18e76ae792a054c2f6d0d01e0e78d58678b35e3 @@ -0,0 +1 @@ +S€gŠT®k…®ƒ†! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a19d04f18f574e561d793ac0dfcffe2b38183eb1 b/webm_parser/fuzzing/corpus/a19d04f18f574e561d793ac0dfcffe2b38183eb1 new file mode 100644 index 000000000000..cb84077f43f8 --- /dev/null +++ b/webm_parser/fuzzing/corpus/a19d04f18f574e561d793ac0dfcffe2b38183eb1 @@ -0,0 +1 @@ +"µœŸ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a26fb85be3d2bb8a2360bb4d9533a1651bd12d99 b/webm_parser/fuzzing/corpus/a26fb85be3d2bb8a2360bb4d9533a1651bd12d99 new file mode 100644 index 0000000000000000000000000000000000000000..6fb743bb3862fb5a09391f4e0f7eeeca3a86c2fa GIT binary patch literal 50 ucmb1gy}z)-smXz*Ts0|IBDf)as=V{I(h1KU7*;zlEN*R-a+P930So}RO$}!N literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/a2e860fae30005a9e75b61f54f3d019c44090fcc b/webm_parser/fuzzing/corpus/a2e860fae30005a9e75b61f54f3d019c44090fcc new file mode 100644 index 000000000000..124e7d63bc69 --- /dev/null +++ b/webm_parser/fuzzing/corpus/a2e860fae30005a9e75b61f54f3d019c44090fcc @@ -0,0 +1 @@ +D…€ðVT€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a3071bfcb7b2fd3c4286ea42e1f7940754b55697 b/webm_parser/fuzzing/corpus/a3071bfcb7b2fd3c4286ea42e1f7940754b55697 new file mode 100644 index 0000000000000000000000000000000000000000..f16be68912be0d06827175bdafb1e0bb292420a9 GIT binary patch literal 56 vcmb0LZb¶;¶8¶5¶2¶/¶,¶)¶&¶#¶ ¶¶¶¶¶¶¶ ¶¶¶¶~ÿ¶~ü¶~ù¶~ö¶~ó¶~ð¶~í¶~ê¶~ç¶~ä¶~á¶~Þ¶~Û¶~ض~Õ¶~Ò¶~϶~̶~ɶ~ƶ~ö~À¶~½¶~º¶~·¶~´¶~±¶~®¶~«¶~¨¶~¥¶~¢¶~Ÿ¶~œ¶~™¶~–¶~“¶~¶~¶~Š¶~‡¶~„¶~¶~~¶~{¶~x¶~u¶~r¶~o¶~l¶~i¶~f¶~c¶~`¶~]¶~Z¶~W¶~T¶~Q¶~N¶~K¶~H¶~E¶~B¶~?¶~<¶~9¶~6¶~3¶~0¶~-¶~*¶~'¶~$¶~!¶~¶~¶~¶~¶~¶~¶~ ¶~ ¶~¶~¶~¶}ý¶}ú¶}÷¶}ô¶}ñ¶}î¶}ë¶}è¶}å¶}â¶}߶}ܶ}Ù¶}Ö¶}Ó¶}ж}Ͷ}ʶ}Ƕ}Ķ}Á¶}¾¶}»¶}¸¶}µ¶}²¶}¯¶}¬¶}©¶}¦¶}£¶} ¶}¶}š¶}—¶}”¶}‘¶}Ž¶}‹¶}ˆ¶}…¶}‚¶}¶}|¶}y¶}v¶}s¶}p¶}m¶}j¶}g¶}d¶}a¶}^¶}[¶}X¶}U¶}R¶}O¶}L¶}I¶}F¶}C¶}@¶}=¶}:¶}7¶}4¶}1¶}.¶}+¶}(¶}%¶}"¶}¶}¶}¶}¶}¶}¶} ¶} +¶}¶}¶}¶|þ¶|û¶|ø¶|õ¶|ò¶|ï¶|ì¶|é¶|æ¶|ã¶|à¶|ݶ|Ú¶|׶|Ô¶|Ѷ|ζ|˶|ȶ|Ŷ|¶|¿¶|¼¶|¹¶|¶¶|³¶|°¶|­¶|ª¶|§¶|¤¶|¡¶|ž¶|›¶|˜¶|•¶|’¶|¶|Œ¶|‰¶|†¶|ƒ¶|€¶|}¶|z¶|w¶|t¶|q¶|n¶|k¶|h¶|e¶|b¶|_¶|\¶|Y¶|V¶|S¶|P¶|M¶|J¶|G¶|D¶|A¶|>¶|;¶|8¶|5¶|2¶|/¶|,¶|)¶|&¶|#¶| ¶|¶|¶|¶|¶|¶|¶| ¶|¶|¶|¶{ÿ¶{ü¶{ù¶{ö¶{ó¶{ð¶{í¶{ê¶{ç¶{ä¶{á¶{Þ¶{Û¶{ض{Õ¶{Ò¶{϶{̶{ɶ{ƶ{ö{À¶{½¶{º¶{·¶{´¶{±¶{®¶{«¶{¨¶{¥¶{¢¶{Ÿ¶{œ¶{™¶{–¶{“¶{¶{¶{Š¶{‡¶{„¶{¶{~¶{{¶{x¶{u¶{r¶{o¶{l¶{i¶{f¶{c¶{`¶{]¶{Z¶{W¶{T¶{Q¶{N¶{K¶{H¶{E¶{B¶{?¶{<¶{9¶{6¶{3¶{0¶{-¶{*¶{'¶{$¶{!¶{¶{¶{¶{¶{¶{¶{ ¶{ ¶{¶{¶{¶zý¶zú¶z÷¶zô¶zñ¶zî¶zë¶zè¶zå¶zâ¶z߶zܶzÙ¶zÖ¶zÓ¶zжzͶzʶzǶzĶzÁ¶z¾¶z»¶z¸¶zµ¶z²¶z¯¶z¬¶z©¶z¦¶z£¶z ¶z¶zš¶z—¶z”¶z‘¶zŽ¶z‹¶zˆ¶z…¶z‚¶z¶z|¶zy¶zv¶zs¶zp¶zm¶zj¶zg¶zd¶za¶z^¶z[¶zX¶zU¶zR¶zO¶zL¶zI¶zF¶zC¶z@¶z=¶z:¶z7¶z4¶z1¶z.¶z+¶z(¶z%¶z"¶z¶z¶z¶z¶z¶z¶z ¶z +¶z¶z¶z¶yþ¶yû¶yø¶yõ¶yò¶yï¶yì¶yé¶yæ¶yã¶yà¶yݶyÚ¶y׶yÔ¶yѶyζy˶yȶyŶy¶y¿¶y¼¶y¹¶y¶¶y³¶y°¶y­¶yª¶y§¶y¤¶y¡¶yž¶y›¶y˜¶y•¶y’¶y¶yŒ¶y‰¶y†¶yƒ¶y€¶y}¶yz¶yw¶yt¶yq¶yn¶yk¶yh¶ye¶yb¶y_¶y\¶yY¶yV¶yS¶yP¶yM¶yJ¶yG¶yD¶yA¶y>¶y;¶y8¶y5¶y2¶y/¶y,¶y)¶y&¶y#¶y ¶y¶y¶y¶y¶y¶y¶y ¶y¶y¶y¶xÿ¶xü¶xù¶xö¶xó¶xð¶xí¶xê¶xç¶xä¶xá¶xÞ¶xÛ¶xضxÕ¶xÒ¶x϶x̶xɶxƶxöxÀ¶x½¶xº¶x·¶x´¶x±¶x®¶x«¶x¨¶x¥¶x¢¶xŸ¶xœ¶x™¶x–¶x“¶x¶x¶xŠ¶x‡¶x„¶x¶x~¶x{¶xx¶xu¶xr¶xo¶xl¶xi¶xf¶xc¶x`¶x]¶xZ¶xW¶xT¶xQ¶xN¶xK¶xH¶xE¶xB¶x?¶x<¶x9¶x6¶x3¶x0¶x-¶x*¶x'¶x$¶x!¶x¶x¶x¶x¶x¶x¶x ¶x ¶x¶x¶x¶wý¶wú¶w÷¶wô¶wñ¶wî¶wë¶wè¶wå¶wâ¶w߶wܶwÙ¶wÖ¶wÓ¶wжwͶwʶwǶwĶwÁ¶w¾¶w»¶w¸¶wµ¶w²¶w¯¶w¬¶w©¶w¦¶w£¶w ¶w¶wš¶w—¶w”¶w‘¶wŽ¶w‹¶wˆ¶w…¶w‚¶w¶w|¶wy¶wv¶ws¶wp¶wm¶wj¶wg¶wd¶wa¶w^¶w[¶wX¶wU¶wR¶wO¶wL¶wI¶wF¶wC¶w@¶w=¶w:¶w7¶w4¶w1¶w.¶w+¶w(¶w%¶w"¶w¶w¶w¶w¶w¶w¶w ¶w +¶w¶w¶w¶vþ¶vû¶vø¶võ¶vò¶vï¶vì¶vé¶væ¶vã¶và¶vݶvÚ¶v׶vÔ¶vѶvζv˶vȶvŶv¶v¿¶v¼¶v¹¶v¶¶v³¶v°¶v­¶vª¶v§¶v¤¶v¡¶vž¶v›¶v˜¶v•¶v’¶v¶vŒ¶v‰¶v†¶vƒ¶v€¶v}¶vz¶vw¶vt¶vq¶vn¶vk¶vh¶ve¶vb¶v_¶v\¶vY¶vV¶vS¶vP¶vM¶vJ¶vG¶vD¶vA¶v>¶v;¶v8¶v5¶v2¶v/¶v,¶v)¶v&¶v#¶v ¶v¶v¶v¶v¶v¶v¶v ¶v¶v¶v¶uÿ¶uü¶uù¶uö¶uó¶uð¶uí¶uê¶uç¶uä¶uá¶uÞ¶uÛ¶uضuÕ¶uÒ¶u϶u̶uɶuƶuöuÀ¶u½¶uº¶u·¶u´¶u±¶u®¶u«¶u¨¶u¥¶u¢¶uŸ¶uœ¶u™¶u–¶u“¶u¶u¶uŠ¶u‡¶u„¶u¶u~¶u{¶ux¶uu¶ur¶uo¶ul¶ui¶uf¶uc¶u`¶u]¶uZ¶uW¶uT¶uQ¶uN¶uK¶uH¶uE¶uB¶u?¶u<¶u9¶u6¶u3¶u0¶u-¶u*¶u'¶u$¶u!¶u¶u¶u¶u¶u¶u¶u ¶u ¶u¶u¶u¶tý¶tú¶t÷¶tô¶tñ¶tî¶të¶tè¶tå¶tâ¶t߶tܶtÙ¶tÖ¶tÓ¶tжtͶtʶtǶtĶtÁ¶t¾¶t»¶t¸¶tµ¶t²¶t¯¶t¬¶t©¶t¦¶t£¶t ¶t¶tš¶t—¶t”¶t‘¶tŽ¶t‹¶tˆ¶t…¶t‚¶t¶t|¶ty¶tv¶ts¶tp¶tm¶tj¶tg¶td¶ta¶t^¶t[¶tX¶tU¶tR¶tO¶tL¶tI¶tF¶tC¶t@¶t=¶t:¶t7¶t4¶t1¶t.¶t+¶t(¶t%¶t"¶t¶t¶t¶t¶t¶t¶t ¶t +¶t¶t¶t¶sþ¶sû¶sø¶sõ¶sò¶sï¶sì¶sé¶sæ¶sã¶sà¶sݶsÚ¶s׶sÔ¶sѶsζs˶sȶsŶs¶s¿¶s¼¶s¹¶s¶¶s³¶s°¶s­¶sª¶s§¶s¤¶s¡¶sž¶s›¶s˜¶s•¶s’¶s¶sŒ¶s‰¶s†¶sƒ¶s€¶s}¶sz¶sw¶st¶sq¶sn¶sk¶sh¶se¶sb¶s_¶s\¶sY¶sV¶sS¶sP¶sM¶sJ¶sG¶sD¶sA¶s>¶s;¶s8¶s5¶s2¶s/¶s,¶s)¶s&¶s#¶s ¶s¶s¶s¶s¶s¶s¶s ¶s¶s¶s¶rÿ¶rü¶rù¶rö¶ró¶rð¶rí¶rê¶rç¶rä¶rá¶rÞ¶rÛ¶rضrÕ¶rÒ¶r϶r̶rɶrƶrörÀ¶r½¶rº¶r·¶r´¶r±¶r®¶r«¶r¨¶r¥¶r¢¶rŸ¶rœ¶r™¶r–¶r“¶r¶r¶rŠ¶r‡¶r„¶r¶r~¶r{¶rx¶ru¶rr¶ro¶rl¶ri¶rf¶rc¶r`¶r]¶rZ¶rW¶rT¶rQ¶rN¶rK¶rH¶rE¶rB¶r?¶r<¶r9¶r6¶r3¶r0¶r-¶r*¶r'¶r$¶r!¶r¶r¶r¶r¶r¶r¶r ¶r ¶r¶r¶r¶qý¶qú¶q÷¶qô¶qñ¶qî¶që¶qè¶qå¶qâ¶q߶qܶqÙ¶qÖ¶qÓ¶qжqͶqʶqǶqĶqÁ¶q¾¶q»¶q¸¶qµ¶q²¶q¯¶q¬¶q©¶q¦¶q£¶q ¶q¶qš¶q—¶q”¶q‘¶qŽ¶q‹¶qˆ¶q…¶q‚¶q¶q|¶qy¶qv¶qs¶qp¶qm¶qj¶qg¶qd¶qa¶q^¶q[¶qX¶qU¶qR¶qO¶qL¶qI¶qF¶qC¶q@¶q=¶q:¶q7¶q4¶q1¶q.¶q+¶q(¶q%¶q"¶q¶q¶q¶q¶q¶q¶q ¶q +¶q¶q¶q¶pþ¶pû¶pø¶põ¶pò¶pï¶pì¶pé¶pæ¶pã¶pà¶pݶpÚ¶p׶pÔ¶pѶpζp˶pȶpŶp¶p¿¶p¼¶p¹¶p¶¶p³¶p°¶p­¶pª¶p§¶p¤¶p¡¶pž¶p›¶p˜¶p•¶p’¶p¶pŒ¶p‰¶p†¶pƒ¶p€¶p}¶pz¶pw¶pt¶pq¶pn¶pk¶ph¶pe¶pb¶p_¶p\¶pY¶pV¶pS¶pP¶pM¶pJ¶pG¶pD¶pA¶p>¶p;¶p8¶p5¶p2¶p/¶p,¶p)¶p&¶p#¶p ¶p¶p¶p¶p¶p¶p¶p ¶p¶p¶p¶oÿ¶oü¶où¶oö¶oó¶oð¶oí¶oê¶oç¶oä¶oá¶oÞ¶oÛ¶oضoÕ¶oÒ¶o϶o̶oɶoƶoöoÀ¶o½¶oº¶o·¶o´¶o±¶o®¶o«¶o¨¶o¥¶o¢¶oŸ¶oœ¶o™¶o–¶o“¶o¶o¶oŠ¶o‡¶o„¶o¶o~¶o{¶ox¶ou¶or¶oo¶ol¶oi¶of¶oc¶o`¶o]¶oZ¶oW¶oT¶oQ¶oN¶oK¶oH¶oE¶oB¶o?¶o<¶o9¶o6¶o3¶o0¶o-¶o*¶o'¶o$¶o!¶o¶o¶o¶o¶o¶o¶o ¶o ¶o¶o¶o¶ný¶nú¶n÷¶nô¶nñ¶nî¶në¶nè¶nå¶nâ¶n߶nܶnÙ¶nÖ¶nÓ¶nжnͶnʶnǶnĶnÁ¶n¾¶n»¶n¸¶nµ¶n²¶n¯¶n¬¶n©¶n¦¶n£¶n ¶n¶nš¶n—¶n”¶n‘¶nŽ¶n‹¶nˆ¶n…¶n‚¶n¶n|¶ny¶nv¶ns¶np¶nm¶nj¶ng¶nd¶na¶n^¶n[¶nX¶nU¶nR¶nO¶nL¶nI¶nF¶nC¶n@¶n=¶n:¶n7¶n4¶n1¶n.¶n+¶n(¶n%¶n"¶n¶n¶n¶n¶n¶n¶n ¶n +¶n¶n¶n¶mþ¶mû¶mø¶mõ¶mò¶mï¶mì¶mé¶mæ¶mã¶mà¶mݶmÚ¶m׶mÔ¶mѶmζm˶mȶmŶm¶m¿¶m¼¶m¹¶m¶¶m³¶m°¶m­¶mª¶m§¶m¤¶m¡¶mž¶m›¶m˜¶m•¶m’¶m¶mŒ¶m‰¶m†¶mƒ¶m€¶m}¶mz¶mw¶mt¶mq¶mn¶mk¶mh¶me¶mb¶m_¶m\¶mY¶mV¶mS¶mP¶mM¶mJ¶mG¶mD¶mA¶m>¶m;¶m8¶m5¶m2¶m/¶m,¶m)¶m&¶m#¶m ¶m¶m¶m¶m¶m¶m¶m ¶m¶m¶m¶lÿ¶lü¶lù¶lö¶ló¶lð¶lí¶lê¶lç¶lä¶lá¶lÞ¶lÛ¶lضlÕ¶lÒ¶l϶l̶lɶlƶlölÀ¶l½¶lº¶l·¶l´¶l±¶l®¶l«¶l¨¶l¥¶l¢¶lŸ¶lœ¶l™¶l–¶l“¶l¶l¶lŠ¶l‡¶l„¶l¶l~¶l{¶lx¶lu¶lr¶lo¶ll¶li¶lf¶lc¶l`¶l]¶lZ¶lW¶lT¶lQ¶lN¶lK¶lH¶lE¶lB¶l?¶l<¶l9¶l6¶l3¶l0¶l-¶l*¶l'¶l$¶l!¶l¶l¶l¶l¶l¶l¶l ¶l ¶l¶l¶l¶ký¶kú¶k÷¶kô¶kñ¶kî¶kë¶kè¶kå¶kâ¶k߶kܶkÙ¶kÖ¶kÓ¶kжkͶkʶkǶkĶkÁ¶k¾¶k»¶k¸¶kµ¶k²¶k¯¶k¬¶k©¶k¦¶k£¶k ¶k¶kš¶k—¶k”¶k‘¶kŽ¶k‹¶kˆ¶k…¶k‚¶k¶k|¶ky¶kv¶ks¶kp¶km¶kj¶kg¶kd¶ka¶k^¶k[¶kX¶kU¶kR¶kO¶kL¶kI¶kF¶kC¶k@¶k=¶k:¶k7¶k4¶k1¶k.¶k+¶k(¶k%¶k"¶k¶k¶k¶k¶k¶k¶k ¶k +¶k¶k¶k¶jþ¶jû¶jø¶jõ¶jò¶jï¶jì¶jé¶jæ¶jã¶jà¶jݶjÚ¶j׶jÔ¶jѶjζj˶jȶjŶj¶j¿¶j¼¶j¹¶j¶¶j³¶j°¶j­¶jª¶j§¶j¤¶j¡¶jž¶j›¶j˜¶j•¶j’¶j¶jŒ¶j‰¶j†¶jƒ¶j€¶j}¶jz¶jw¶jt¶jq¶jn¶jk¶jh¶je¶jb¶j_¶j\¶jY¶jV¶jS¶jP¶jM¶jJ¶jG¶jD¶jA¶j>¶j;¶j8¶j5¶j2¶j/¶j,¶j)¶j&¶j#¶j ¶j¶j¶j¶j¶j¶j¶j ¶j¶j¶j¶iÿ¶iü¶iù¶iö¶ió¶ið¶ií¶iê¶iç¶iä¶iá¶iÞ¶iÛ¶iضiÕ¶iÒ¶i϶i̶iɶiƶiöiÀ¶i½¶iº¶i·¶i´¶i±¶i®¶i«¶i¨¶i¥¶i¢¶iŸ¶iœ¶i™¶i–¶i“¶i¶i¶iŠ¶i‡¶i„¶i¶i~¶i{¶ix¶iu¶ir¶io¶il¶ii¶if¶ic¶i`¶i]¶iZ¶iW¶iT¶iQ¶iN¶iK¶iH¶iE¶iB¶i?¶i<¶i9¶i6¶i3¶i0¶i-¶i*¶i'¶i$¶i!¶i¶i¶i¶i¶i¶i¶i ¶i ¶i¶i¶i¶hý¶hú¶h÷¶hô¶hñ¶hî¶hë¶hè¶hå¶hâ¶h߶hܶhÙ¶hÖ¶hÓ¶hжhͶhʶhǶhĶhÁ¶h¾¶h»¶h¸¶hµ¶h²¶h¯¶h¬¶h©¶h¦¶h£¶h ¶h¶hš¶h—¶h”¶h‘¶hŽ¶h‹¶hˆ¶h…¶h‚¶h¶h|¶hy¶hv¶hs¶hp¶hm¶hj¶hg¶hd¶ha¶h^¶h[¶hX¶hU¶hR¶hO¶hL¶hI¶hF¶hC¶h@¶h=¶h:¶h7¶h4¶h1¶h.¶h+¶h(¶h%¶h"¶h¶h¶h¶h¶h¶h¶h ¶h +¶h¶h¶h¶gþ¶gû¶gø¶gõ¶gò¶gï¶gì¶gé¶gæ¶gã¶gà¶gݶgÚ¶g׶gÔ¶gѶgζg˶gȶgŶg¶g¿¶g¼¶g¹¶g¶¶g³¶g°¶g­¶gª¶g§¶g¤¶g¡¶gž¶g›¶g˜¶g•¶g’¶g¶gŒ¶g‰¶g†¶gƒ¶g€¶g}¶gz¶gw¶gt¶gq¶gn¶gk¶gh¶ge¶gb¶g_¶g\¶gY¶gV¶gS¶gP¶gM¶gJ¶gG¶gD¶gA¶g>¶g;¶g8¶g5¶g2¶g/¶g,¶g)¶g&¶g#¶g ¶g¶g¶g¶g¶g¶g¶g ¶g¶g¶g¶fÿ¶fü¶fù¶fö¶fó¶fð¶fí¶fê¶fç¶fä¶fá¶fÞ¶fÛ¶fضfÕ¶fÒ¶f϶f̶fɶfƶföfÀ¶f½¶fº¶f·¶f´¶f±¶f®¶f«¶f¨¶f¥¶f¢¶fŸ¶fœ¶f™¶f–¶f“¶f¶f¶fŠ¶f‡¶f„¶f¶f~¶f{¶fx¶fu¶fr¶fo¶fl¶fi¶ff¶fc¶f`¶f]¶fZ¶fW¶fT¶fQ¶fN¶fK¶fH¶fE¶fB¶f?¶f<¶f9¶f6¶f3¶f0¶f-¶f*¶f'¶f$¶f!¶f¶f¶f¶f¶f¶f¶f ¶f ¶f¶f¶f¶eý¶eú¶e÷¶eô¶eñ¶eî¶eë¶eè¶eå¶eâ¶e߶eܶeÙ¶eÖ¶eÓ¶eжeͶeʶeǶeĶeÁ¶e¾¶e»¶e¸¶eµ¶e²¶e¯¶e¬¶e©¶e¦¶e£¶e ¶e¶eš¶e—¶e”¶e‘¶eŽ¶e‹¶eˆ¶e…¶e‚¶e¶e|¶ey¶ev¶es¶ep¶em¶ej¶eg¶ed¶ea¶e^¶e[¶eX¶eU¶eR¶eO¶eL¶eI¶eF¶eC¶e@¶e=¶e:¶e7¶e4¶e1¶e.¶e+¶e(¶e%¶e"¶e¶e¶e¶e¶e¶e¶e ¶e +¶e¶e¶e¶dþ¶dû¶dø¶dõ¶dò¶dï¶dì¶dé¶dæ¶dã¶dà¶dݶdÚ¶d׶dÔ¶dѶdζd˶dȶdŶd¶d¿¶d¼¶d¹¶d¶¶d³¶d°¶d­¶dª¶d§¶d¤¶d¡¶dž¶d›¶d˜¶d•¶d’¶d¶dŒ¶d‰¶d†¶dƒ¶d€¶d}¶dz¶dw¶dt¶dq¶dn¶dk¶dh¶de¶db¶d_¶d\¶dY¶dV¶dS¶dP¶dM¶dJ¶dG¶dD¶dA¶d>¶d;¶d8¶d5¶d2¶d/¶d,¶d)¶d&¶d#¶d ¶d¶d¶d¶d¶d¶d¶d ¶d¶d¶d¶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{¶cx¶cu¶cr¶co¶cl¶ci¶cf¶cc¶c`¶c]¶cZ¶cW¶cT¶cQ¶cN¶cK¶cH¶cE¶cB¶c?¶c<¶c9¶c6¶c3¶c0¶c-¶c*¶c'¶c$¶c!¶c¶c¶c¶c¶c¶c¶c ¶c ¶c¶c¶c¶bý¶bú¶b÷¶bô¶bñ¶bî¶bë¶bè¶bå¶bâ¶b߶bܶbÙ¶bÖ¶bÓ¶bжbͶbʶbǶbĶbÁ¶b¾¶b»¶b¸¶bµ¶b²¶b¯¶b¬¶b©¶b¦¶b£¶b ¶b¶bš¶b—¶b”¶b‘¶bŽ¶b‹¶bˆ¶b…¶b‚¶b¶b|¶by¶bv¶bs¶bp¶bm¶bj¶bg¶bd¶ba¶b^¶b[¶bX¶bU¶bR¶bO¶bL¶bI¶bF¶bC¶b@¶b=¶b:¶b7¶b4¶b1¶b.¶b+¶b(¶b%¶b"¶b¶b¶b¶b¶b¶b¶b ¶b +¶b¶b¶b¶aþ¶aû¶aø¶aõ¶aò¶aï¶aì¶aé¶aæ¶aã¶aà¶aݶaÚ¶a׶aÔ¶aѶaζa˶aȶaŶa¶a¿¶a¼¶a¹¶a¶¶a³¶a°¶a­¶aª¶a§¶a¤¶a¡¶až¶a›¶a˜¶a•¶a’¶a¶aŒ¶a‰¶a†¶aƒ¶a€¶a}¶az¶aw¶at¶aq¶an¶ak¶ah¶ae¶ab¶a_¶a\¶aY¶aV¶aS¶aP¶aM¶aJ¶aG¶aD¶aA¶a>¶a;¶a8¶a5¶a2¶a/¶a,¶a)¶a&¶a#¶a ¶a¶a¶a¶a¶a¶a¶a ¶a¶a¶a¶`ÿ¶`ü¶`ù¶`ö¶`ó¶`ð¶`í¶`ê¶`ç¶`ä¶`á¶`Þ¶`Û¶`ض`Õ¶`Ò¶`϶`̶`ɶ`ƶ`ö`À¶`½¶`º¶`·¶`´¶`±¶`®¶`«¶`¨¶`¥¶`¢¶`Ÿ¶`œ¶`™¶`–¶`“¶`¶`¶`Š¶`‡¶`„¶`¶`~¶`{¶`x¶`u¶`r¶`o¶`l¶`i¶`f¶`c¶``¶`]¶`Z¶`W¶`T¶`Q¶`N¶`K¶`H¶`E¶`B¶`?¶`<¶`9¶`6¶`3¶`0¶`-¶`*¶`'¶`$¶`!¶`¶`¶`¶`¶`¶`¶` ¶` ¶`¶`¶`¶_ý¶_ú¶_÷¶_ô¶_ñ¶_î¶_ë¶_è¶_å¶_â¶_߶_ܶ_Ù¶_Ö¶_Ó¶_ж_Ͷ_ʶ_Ƕ_Ķ_Á¶_¾¶_»¶_¸¶_µ¶_²¶_¯¶_¬¶_©¶_¦¶_£¶_ ¶_¶_š¶_—¶_”¶_‘¶_Ž¶_‹¶_ˆ¶_…¶_‚¶_¶_|¶_y¶_v¶_s¶_p¶_m¶_j¶_g¶_d¶_a¶_^¶_[¶_X¶_U¶_R¶_O¶_L¶_I¶_F¶_C¶_@¶_=¶_:¶_7¶_4¶_1¶_.¶_+¶_(¶_%¶_"¶_¶_¶_¶_¶_¶_¶_ ¶_ +¶_¶_¶_¶^þ¶^û¶^ø¶^õ¶^ò¶^ï¶^ì¶^é¶^æ¶^ã¶^à¶^ݶ^Ú¶^׶^Ô¶^Ѷ^ζ^˶^ȶ^Ŷ^¶^¿¶^¼¶^¹¶^¶¶^³¶^°¶^­¶^ª¶^§¶^¤¶^¡¶^ž¶^›¶^˜¶^•¶^’¶^¶^Œ¶^‰¶^†¶^ƒ¶^€¶^}¶^z¶^w¶^t¶^q¶^n¶^k¶^h¶^e¶^b¶^_¶^\¶^Y¶^V¶^S¶^P¶^M¶^J¶^G¶^D¶^A¶^>¶^;¶^8¶^5¶^2¶^/¶^,¶^)¶^&¶^#¶^ ¶^¶^¶^¶^¶^¶^¶^ ¶^¶^¶^¶]ÿ¶]ü¶]ù¶]ö¶]ó¶]ð¶]í¶]ê¶]ç¶]ä¶]á¶]Þ¶]Û¶]ض]Õ¶]Ò¶]϶]̶]ɶ]ƶ]ö]À¶]½¶]º¶]·¶]´¶]±¶]®¶]«¶]¨¶]¥¶]¢¶]Ÿ¶]œ¶]™¶]–¶]“¶]¶]¶]Š¶]‡¶]„¶]¶]~¶]{¶]x¶]u¶]r¶]o¶]l¶]i¶]f¶]c¶]`¶]]¶]Z¶]W¶]T¶]Q¶]N¶]K¶]H¶]E¶]B¶]?¶]<¶]9¶]6¶]3¶]0¶]-¶]*¶]'¶]$¶]!¶]¶]¶]¶]¶]¶]¶] ¶] ¶]¶]¶]¶\ý¶\ú¶\÷¶\ô¶\ñ¶\î¶\ë¶\è¶\å¶\â¶\߶\ܶ\Ù¶\Ö¶\Ó¶\ж\Ͷ\ʶ\Ƕ\Ķ\Á¶\¾¶\»¶\¸¶\µ¶\²¶\¯¶\¬¶\©¶\¦¶\£¶\ ¶\¶\š¶\—¶\”¶\‘¶\Ž¶\‹¶\ˆ¶\…¶\‚¶\¶\|¶\y¶\v¶\s¶\p¶\m¶\j¶\g¶\d¶\a¶\^¶\[¶\X¶\U¶\R¶\O¶\L¶\I¶\F¶\C¶\@¶\=¶\:¶\7¶\4¶\1¶\.¶\+¶\(¶\%¶\"¶\¶\¶\¶\¶\¶\¶\ ¶\ +¶\¶\¶\¶[þ¶[û¶[ø¶[õ¶[ò¶[ï¶[ì¶[é¶[æ¶[ã¶[à¶[ݶ[Ú¶[׶[Ô¶[Ѷ[ζ[˶[ȶ[Ŷ[¶[¿¶[¼¶[¹¶[¶¶[³¶[°¶[­¶[ª¶[§¶[¤¶[¡¶[ž¶[›¶[˜¶[•¶[’¶[¶[Œ¶[‰¶[†¶[ƒ¶[€¶[}¶[z¶[w¶[t¶[q¶[n¶[k¶[h¶[e¶[b¶[_¶[\¶[Y¶[V¶[S¶[P¶[M¶[J¶[G¶[D¶[A¶[>¶[;¶[8¶[5¶[2¶[/¶[,¶[)¶[&¶[#¶[ ¶[¶[¶[¶[¶[¶[¶[ ¶[¶[¶[¶Zÿ¶Zü¶Zù¶Zö¶Zó¶Zð¶Zí¶Zê¶Zç¶Zä¶Zá¶ZÞ¶ZÛ¶ZضZÕ¶ZÒ¶Z϶Z̶ZɶZƶZöZÀ¶Z½¶Zº¶Z·¶Z´¶Z±¶Z®¶Z«¶Z¨¶Z¥¶Z¢¶ZŸ¶Zœ¶Z™¶Z–¶Z“¶Z¶Z¶ZŠ¶Z‡¶Z„¶Z¶Z~¶Z{¶Zx¶Zu¶Zr¶Zo¶Zl¶Zi¶Zf¶Zc¶Z`¶Z]¶ZZ¶ZW¶ZT¶ZQ¶ZN¶ZK¶ZH¶ZE¶ZB¶Z?¶Z<¶Z9¶Z6¶Z3¶Z0¶Z-¶Z*¶Z'¶Z$¶Z!¶Z¶Z¶Z¶Z¶Z¶Z¶Z ¶Z ¶Z¶Z¶Z¶Yý¶Yú¶Y÷¶Yô¶Yñ¶Yî¶Yë¶Yè¶Yå¶Yâ¶Y߶YܶYÙ¶YÖ¶YÓ¶YжYͶYʶYǶYĶYÁ¶Y¾¶Y»¶Y¸¶Yµ¶Y²¶Y¯¶Y¬¶Y©¶Y¦¶Y£¶Y ¶Y¶Yš¶Y—¶Y”¶Y‘¶YŽ¶Y‹¶Yˆ¶Y…¶Y‚¶Y¶Y|¶Yy¶Yv¶Ys¶Yp¶Ym¶Yj¶Yg¶Yd¶Ya¶Y^¶Y[¶YX¶YU¶YR¶YO¶YL¶YI¶YF¶YC¶Y@¶Y=¶Y:¶Y7¶Y4¶Y1¶Y.¶Y+¶Y(¶Y%¶Y"¶Y¶Y¶Y¶Y¶Y¶Y¶Y ¶Y +¶Y¶Y¶Y¶Xþ¶Xû¶Xø¶Xõ¶Xò¶Xï¶Xì¶Xé¶Xæ¶Xã¶Xà¶XݶXÚ¶X׶XÔ¶XѶXζX˶XȶXŶX¶X¿¶X¼¶X¹¶X¶¶X³¶X°¶X­¶Xª¶X§¶X¤¶X¡¶Xž¶X›¶X˜¶X•¶X’¶X¶XŒ¶X‰¶X†¶Xƒ¶X€¶X}¶Xz¶Xw¶Xt¶Xq¶Xn¶Xk¶Xh¶Xe¶Xb¶X_¶X\¶XY¶XV¶XS¶XP¶XM¶XJ¶XG¶XD¶XA¶X>¶X;¶X8¶X5¶X2¶X/¶X,¶X)¶X&¶X#¶X ¶X¶X¶X¶X¶X¶X¶X ¶X¶X¶X¶Wÿ¶Wü¶Wù¶Wö¶Wó¶Wð¶Wí¶Wê¶Wç¶Wä¶Wá¶WÞ¶WÛ¶WضWÕ¶WÒ¶W϶W̶WɶWƶWöWÀ¶W½¶Wº¶W·¶W´¶W±¶W®¶W«¶W¨¶W¥¶W¢¶WŸ¶Wœ¶W™¶W–¶W“¶W¶W¶WŠ¶W‡¶W„¶W¶W~¶W{¶Wx¶Wu¶Wr¶Wo¶Wl¶Wi¶Wf¶Wc¶W`¶W]¶WZ¶WW¶WT¶WQ¶WN¶WK¶WH¶WE¶WB¶W?¶W<¶W9¶W6¶W3¶W0¶W-¶W*¶W'¶W$¶W!¶W¶W¶W¶W¶W¶W¶W ¶W ¶W¶W¶W¶Vý¶Vú¶V÷¶Vô¶Vñ¶Vî¶Vë¶Vè¶Vå¶Vâ¶V߶VܶVÙ¶VÖ¶VÓ¶VжVͶVʶVǶVĶVÁ¶V¾¶V»¶V¸¶Vµ¶V²¶V¯¶V¬¶V©¶V¦¶V£¶V ¶V¶Vš¶V—¶V”¶V‘¶VŽ¶V‹¶Vˆ¶V…¶V‚¶V¶V|¶Vy¶Vv¶Vs¶Vp¶Vm¶Vj¶Vg¶Vd¶Va¶V^¶V[¶VX¶VU¶VR¶VO¶VL¶VI¶VF¶VC¶V@¶V=¶V:¶V7¶V4¶V1¶V.¶V+¶V(¶V%¶V"¶V¶V¶V¶V¶V¶V¶V ¶V +¶V¶V¶V¶Uþ¶Uû¶Uø¶Uõ¶Uò¶Uï¶Uì¶Ué¶Uæ¶Uã¶Uà¶UݶUÚ¶U׶UÔ¶UѶUζU˶UȶUŶU¶U¿¶U¼¶U¹¶U¶¶U³¶U°¶U­¶Uª¶U§¶U¤¶U¡¶Už¶U›¶U˜¶U•¶U’¶U¶UŒ¶U‰¶U†¶Uƒ¶U€¶U}¶Uz¶Uw¶Ut¶Uq¶Un¶Uk¶Uh¶Ue¶Ub¶U_¶U\¶UY¶UV¶US¶UP¶UM¶UJ¶UG¶UD¶UA¶U>¶U;¶U8¶U5¶U2¶U/¶U,¶U)¶U&¶U#¶U ¶U¶U¶U¶U¶U¶U¶U ¶U¶U¶U¶Tÿ¶Tü¶Tù¶Tö¶Tó¶Tð¶Tí¶Tê¶Tç¶Tä¶Tá¶TÞ¶TÛ¶TضTÕ¶TÒ¶T϶T̶TɶTƶTöTÀ¶T½¶Tº¶T·¶T´¶T±¶T®¶T«¶T¨¶T¥¶T¢¶TŸ¶Tœ¶T™¶T–¶T“¶T¶T¶TŠ¶T‡¶T„¶T¶T~¶T{¶Tx¶Tu¶Tr¶To¶Tl¶Ti¶Tf¶Tc¶T`¶T]¶TZ¶TW¶TT¶TQ¶TN¶TK¶TH¶TE¶TB¶T?¶T<¶T9¶T6¶T3¶T0¶T-¶T*¶T'¶T$¶T!¶T¶T¶T¶T¶T¶T¶T ¶T ¶T¶T¶T¶Sý¶Sú¶S÷¶Sô¶Sñ¶Sî¶Së¶Sè¶Så¶Sâ¶S߶SܶSÙ¶SÖ¶SÓ¶SжSͶSʶSǶSĶSÁ¶S¾¶S»¶S¸¶Sµ¶S²¶S¯¶S¬¶S©¶S¦¶S£¶S ¶S¶Sš¶S—¶S”¶S‘¶SŽ¶S‹¶Sˆ¶S…¶S‚¶S¶S|¶Sy¶Sv¶Ss¶Sp¶Sm¶Sj¶Sg¶Sd¶Sa¶S^¶S[¶SX¶SU¶SR¶SO¶SL¶SI¶SF¶SC¶S@¶S=¶S:¶S7¶S4¶S1¶S.¶S+¶S(¶S%¶S"¶S¶S¶S¶S¶S¶S¶S ¶S +¶S¶S¶S¶Rþ¶Rû¶Rø¶Rõ¶Rò¶Rï¶Rì¶Ré¶Ræ¶Rã¶Rà¶RݶRÚ¶R׶RÔ¶RѶRζR˶RȶRŶR¶R¿¶R¼¶R¹¶R¶¶R³¶R°¶R­¶Rª¶R§¶R¤¶R¡¶Rž¶R›¶R˜¶R•¶R’¶R¶RŒ¶R‰¶R†¶Rƒ¶R€¶R}¶Rz¶Rw¶Rt¶Rq¶Rn¶Rk¶Rh¶Re¶Rb¶R_¶R\¶RY¶RV¶RS¶RP¶RM¶RJ¶RG¶RD¶RA¶R>¶R;¶R8¶R5¶R2¶R/¶R,¶R)¶R&¶R#¶R ¶R¶R¶R¶R¶R¶R¶R ¶R¶R¶R¶Qÿ¶Qü¶Qù¶Qö¶Qó¶Qð¶Qí¶Qê¶Qç¶Qä¶Qá¶QÞ¶QÛ¶QضQÕ¶QÒ¶Q϶Q̶QɶQƶQöQÀ¶Q½¶Qº¶Q·¶Q´¶Q±¶Q®¶Q«¶Q¨¶Q¥¶Q¢¶QŸ¶Qœ¶Q™¶Q–¶Q“¶Q¶Q¶QŠ¶Q‡¶Q„¶Q¶Q~¶Q{¶Qx¶Qu¶Qr¶Qo¶Ql¶Qi¶Qf¶Qc¶Q`¶Q]¶QZ¶QW¶QT¶QQ¶QN¶QK¶QH¶QE¶QB¶Q?¶Q<¶Q9¶Q6¶Q3¶Q0¶Q-¶Q*¶Q'¶Q$¶Q!¶Q¶Q¶Q¶Q¶Q¶Q¶Q ¶Q ¶Q¶Q¶Q¶Pý¶Pú¶P÷¶Pô¶Pñ¶Pî¶Pë¶Pè¶På¶Pâ¶P߶PܶPÙ¶PÖ¶PÓ¶PжPͶPʶPǶPĶPÁ¶P¾¶P»¶P¸¶Pµ¶P²¶P¯¶P¬¶P©¶P¦¶P£¶P ¶P¶Pš¶P—¶P”¶P‘¶PŽ¶P‹¶Pˆ¶P…¶P‚¶P¶P|¶Py¶Pv¶Ps¶Pp¶Pm¶Pj¶Pg¶Pd¶Pa¶P^¶P[¶PX¶PU¶PR¶PO¶PL¶PI¶PF¶PC¶P@¶P=¶P:¶P7¶P4¶P1¶P.¶P+¶P(¶P%¶P"¶P¶P¶P¶P¶P¶P¶P ¶P +¶P¶P¶P¶Oþ¶Oû¶Oø¶Oõ¶Oò¶Oï¶Oì¶Oé¶Oæ¶Oã¶Oà¶OݶOÚ¶O׶OÔ¶OѶOζO˶OȶOŶO¶O¿¶O¼¶O¹¶O¶¶O³¶O°¶O­¶Oª¶O§¶O¤¶O¡¶Ož¶O›¶O˜¶O•¶O’¶O¶OŒ¶O‰¶O†¶Oƒ¶O€¶O}¶Oz¶Ow¶Ot¶Oq¶On¶Ok¶Oh¶Oe¶Ob¶O_¶O\¶OY¶OV¶OS¶OP¶OM¶OJ¶OG¶OD¶OA¶O>¶O;¶O8¶O5¶O2¶O/¶O,¶O)¶O&¶O#¶O ¶O¶O¶O¶O¶O¶O¶O ¶O¶O¶O¶Nÿ¶Nü¶Nù¶Nö¶Nó¶Nð¶Ní¶Nê¶Nç¶Nä¶Ná¶NÞ¶NÛ¶NضNÕ¶NÒ¶N϶N̶NɶNƶNöNÀ¶N½¶Nº¶N·¶N´¶N±¶N®¶N«¶N¨¶N¥¶N¢¶NŸ¶Nœ¶N™¶N–¶N“¶N¶N¶NŠ¶N‡¶N„¶N¶N~¶N{¶Nx¶Nu¶Nr¶No¶Nl¶Ni¶Nf¶Nc¶N`¶N]¶NZ¶NW¶NT¶NQ¶NN¶NK¶NH¶NE¶NB¶N?¶N<¶N9¶N6¶N3¶N0¶N-¶N*¶N'¶N$¶N!¶N¶N¶N¶N¶N¶N¶N ¶N ¶N¶N¶N¶Mý¶Mú¶M÷¶Mô¶Mñ¶Mî¶Më¶Mè¶Må¶Mâ¶M߶MܶMÙ¶MÖ¶MÓ¶MжMͶMʶMǶMĶMÁ¶M¾¶M»¶M¸¶Mµ¶M²¶M¯¶M¬¶M©¶M¦¶M£¶M ¶M¶Mš¶M—¶M”¶M‘¶MŽ¶M‹¶Mˆ¶M…¶M‚¶M¶M|¶My¶Mv¶Ms¶Mp¶Mm¶Mj¶Mg¶Md¶Ma¶M^¶M[¶MX¶MU¶MR¶MO¶ML¶MI¶MF¶MC¶M@¶M=¶M:¶M7¶M4¶M1¶M.¶M+¶M(¶M%¶M"¶M¶M¶M¶M¶M¶M¶M ¶M +¶M¶M¶M¶Lþ¶Lû¶Lø¶Lõ¶Lò¶Lï¶Lì¶Lé¶Læ¶Lã¶Là¶LݶLÚ¶L׶LÔ¶LѶLζL˶LȶLŶL¶L¿¶L¼¶L¹¶L¶¶L³¶L°¶L­¶Lª¶L§¶L¤¶L¡¶Lž¶L›¶L˜¶L•¶L’¶L¶LŒ¶L‰¶L†¶Lƒ¶L€¶L}¶Lz¶Lw¶Lt¶Lq¶Ln¶Lk¶Lh¶Le¶Lb¶L_¶L\¶LY¶LV¶LS¶LP¶LM¶LJ¶LG¶LD¶LA¶L>¶L;¶L8¶L5¶L2¶L/¶L,¶L)¶L&¶L#¶L ¶L¶L¶L¶L¶L¶L¶L ¶L¶L¶L¶Kÿ¶Kü¶Kù¶Kö¶Kó¶Kð¶Kí¶Kê¶Kç¶Kä¶Ká¶KÞ¶KÛ¶KضKÕ¶KÒ¶K϶K̶KɶKƶKöKÀ¶K½¶Kº¶K·¶K´¶K±¶K®¶K«¶K¨¶K¥¶K¢¶KŸ¶Kœ¶K™¶K–¶K“¶K¶K¶KŠ¶K‡¶K„¶K¶K~¶K{¶Kx¶Ku¶Kr¶Ko¶Kl¶Ki¶Kf¶Kc¶K`¶K]¶KZ¶KW¶KT¶KQ¶KN¶KK¶KH¶KE¶KB¶K?¶K<¶K9¶K6¶K3¶K0¶K-¶K*¶K'¶K$¶K!¶K¶K¶K¶K¶K¶K¶K ¶K ¶K¶K¶K¶Jý¶Jú¶J÷¶Jô¶Jñ¶Jî¶Jë¶Jè¶Jå¶Jâ¶J߶JܶJÙ¶JÖ¶JÓ¶JжJͶJʶJǶJĶJÁ¶J¾¶J»¶J¸¶Jµ¶J²¶J¯¶J¬¶J©¶J¦¶J£¶J ¶J¶Jš¶J—¶J”¶J‘¶JŽ¶J‹¶Jˆ¶J…¶J‚¶J¶J|¶Jy¶Jv¶Js¶Jp¶Jm¶Jj¶Jg¶Jd¶Ja¶J^¶J[¶JX¶JU¶JR¶JO¶JL¶JI¶JF¶JC¶J@¶J=¶J:¶J7¶J4¶J1¶J.¶J+¶J(¶J%¶J"¶J¶J¶J¶J¶J¶J¶J ¶J +¶J¶J¶J¶Iþ¶Iû¶Iø¶Iõ¶Iò¶Iï¶Iì¶Ié¶Iæ¶Iã¶Ià¶IݶIÚ¶I׶IÔ¶IѶIζI˶IȶIŶI¶I¿¶I¼¶I¹¶I¶¶I³¶I°¶I­¶Iª¶I§¶I¤¶I¡¶Iž¶I›¶I˜¶I•¶I’¶I¶IŒ¶I‰¶I†¶Iƒ¶I€¶I}¶Iz¶Iw¶It¶Iq¶In¶Ik¶Ih¶Ie¶Ib¶I_¶I\¶IY¶IV¶IS¶IP¶IM¶IJ¶IG¶ID¶IA¶I>¶I;¶I8¶I5¶I2¶I/¶I,¶I)¶I&¶I#¶I ¶I¶I¶I¶I¶I¶I¶I ¶I¶I¶I¶Hÿ¶Hü¶Hù¶Hö¶Hó¶Hð¶Hí¶Hê¶Hç¶Hä¶Há¶HÞ¶HÛ¶HضHÕ¶HÒ¶H϶H̶HɶHƶHöHÀ¶H½¶Hº¶H·¶H´¶H±¶H®¶H«¶H¨¶H¥¶H¢¶HŸ¶Hœ¶H™¶H–¶H“¶H¶H¶HŠ¶H‡¶H„¶H¶H~¶H{¶Hx¶Hu¶Hr¶Ho¶Hl¶Hi¶Hf¶Hc¶H`¶H]¶HZ¶HW¶HT¶HQ¶HN¶HK¶HH¶HE¶HB¶H?¶H<¶H9¶H6¶H3¶H0¶H-¶H*¶H'¶H$¶H!¶H¶H¶H¶H¶H¶H¶H ¶H ¶H¶H¶H¶Gý¶Gú¶G÷¶Gô¶Gñ¶Gî¶Gë¶Gè¶Gå¶Gâ¶G߶GܶGÙ¶GÖ¶GÓ¶GжGͶGʶGǶGĶGÁ¶G¾¶G»¶G¸¶Gµ¶G²¶G¯¶G¬¶G©¶G¦¶G£¶G ¶G¶Gš¶G—¶G”¶G‘¶GŽ¶G‹¶Gˆ¶G…¶G‚¶G¶G|¶Gy¶Gv¶Gs¶Gp¶Gm¶Gj¶Gg¶Gd¶Ga¶G^¶G[¶GX¶GU¶GR¶GO¶GL¶GI¶GF¶GC¶G@¶G=¶G:¶G7¶G4¶G1¶G.¶G+¶G(¶G%¶G"¶G¶G¶G¶G¶G¶G¶G ¶G +¶G¶G¶G¶Fþ¶Fû¶Fø¶Fõ¶Fò¶Fï¶Fì¶Fé¶Fæ¶Fã¶Fà¶FݶFÚ¶F׶FÔ¶FѶFζF˶FȶFŶF¶F¿¶F¼¶F¹¶F¶¶F³¶F°¶F­¶Fª¶F§¶F¤¶F¡¶Fž¶F›¶F˜¶F•¶F’¶F¶FŒ¶F‰¶F†¶Fƒ¶F€¶F}¶Fz¶Fw¶Ft¶Fq¶Fn¶Fk¶Fh¶Fe¶Fb¶F_¶F\¶FY¶FV¶FS¶FP¶FM¶FJ¶FG¶FD¶FA¶F>¶F;¶F8¶F5¶F2¶F/¶F,¶F)¶F&¶F#¶F ¶F¶F¶F¶F¶F¶F¶F ¶F¶F¶F¶Eÿ¶Eü¶Eù¶Eö¶Eó¶Eð¶Eí¶Eê¶Eç¶Eä¶Eá¶EÞ¶EÛ¶EضEÕ¶EÒ¶E϶E̶EɶEƶEöEÀ¶E½¶Eº¶E·¶E´¶E±¶E®¶E«¶E¨¶E¥¶E¢¶EŸ¶Eœ¶E™¶E–¶E“¶E¶E¶EŠ¶E‡¶E„¶E¶E~¶E{¶Ex¶Eu¶Er¶Eo¶El¶Ei¶Ef¶Ec¶E`¶E]¶EZ¶EW¶ET¶EQ¶EN¶EK¶EH¶EE¶EB¶E?¶E<¶E9¶E6¶E3¶E0¶E-¶E*¶E'¶E$¶E!¶E¶E¶E¶E¶E¶E¶E ¶E ¶E¶E¶E¶Dý¶Dú¶D÷¶Dô¶Dñ¶Dî¶Dë¶Dè¶Då¶Dâ¶D߶DܶDÙ¶DÖ¶DÓ¶DжDͶDʶDǶDĶDÁ¶D¾¶D»¶D¸¶Dµ¶D²¶D¯¶D¬¶D©¶D¦¶D£¶D ¶D¶Dš¶D—¶D”¶D‘¶DŽ¶D‹¶Dˆ¶D…¶D‚¶D¶D|¶Dy¶Dv¶Ds¶Dp¶Dm¶Dj¶Dg¶Dd¶Da¶D^¶D[¶DX¶DU¶DR¶DO¶DL¶DI¶DF¶DC¶D@¶D=¶D:¶D7¶D4¶D1¶D.¶D+¶D(¶D%¶D"¶D¶D¶D¶D¶D¶D¶D ¶D +¶D¶D¶D¶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}¶Cz¶Cw¶Ct¶Cq¶Cn¶Ck¶Ch¶Ce¶Cb¶C_¶C\¶CY¶CV¶CS¶CP¶CM¶CJ¶CG¶CD¶CA¶C>¶C;¶C8¶C5¶C2¶C/¶C,¶C)¶C&¶C#¶C ¶C¶C¶C¶C¶C¶C¶C ¶C¶C¶C¶Bÿ¶Bü¶Bù¶Bö¶Bó¶Bð¶Bí¶Bê¶Bç¶Bä¶Bá¶BÞ¶BÛ¶BضBÕ¶BÒ¶B϶B̶BɶBƶBöBÀ¶B½¶Bº¶B·¶B´¶B±¶B®¶B«¶B¨¶B¥¶B¢¶BŸ¶Bœ¶B™¶B–¶B“¶B¶B¶BŠ¶B‡¶B„¶B¶B~¶B{¶Bx¶Bu¶Br¶Bo¶Bl¶Bi¶Bf¶Bc¶B`¶B]¶BZ¶BW¶BT¶BQ¶BN¶BK¶BH¶BE¶BB¶B?¶B<¶B9¶B6¶B3¶B0¶B-¶B*¶B'¶B$¶B!¶B¶B¶B¶B¶B¶B¶B ¶B ¶B¶B¶B¶Aý¶Aú¶A÷¶Aô¶Añ¶Aî¶Aë¶Aè¶Aå¶Aâ¶A߶AܶAÙ¶AÖ¶AÓ¶AжAͶAʶAǶAĶAÁ¶A¾¶A»¶A¸¶Aµ¶A²¶A¯¶A¬¶A©¶A¦¶A£¶A ¶A¶Aš¶A—¶A”¶A‘¶AŽ¶A‹¶Aˆ¶A…¶A‚¶A¶A|¶Ay¶Av¶As¶Ap¶Am¶Aj¶Ag¶Ad¶Aa¶A^¶A[¶AX¶AU¶AR¶AO¶AL¶AI¶AF¶AC¶A@¶A=¶A:¶A7¶A4¶A1¶A.¶A+¶A(¶A%¶A"¶A¶A¶A¶A¶A¶A¶A ¶A +¶A¶A¶A¶@þ¶@û¶@ø¶@õ¶@ò¶@ï¶@ì¶@é¶@æ¶@ã¶@à¶@ݶ@Ú¶@׶@Ô¶@Ѷ@ζ@˶@ȶ@Ŷ@¶@¿¶@¼¶@¹¶@¶¶@³¶@°¶@­¶@ª¶@§¶@¤¶@¡¶@ž¶@›¶@˜¶@•¶@’¶@¶@Œ¶@‰¶@†¶@ƒ¶@€¶þ¶ü¶ú¶ø¶ö¶ô¶ò¶ð¶î¶ì¶ê¶è¶æ¶ä¶â¶à¶Þ¶Ü¶Ú¶Ø¶Ö¶Ô¶Ò¶Ð¶Î¶Ì¶Ê¶È¶Æ¶Ä¶Â¶À¶¾¶¼¶º¶¸¶¶¶´¶²¶°¶®¶¬¶ª¶¨¶¦¶¤¶¢¶ ¶ž¶œ¶š¶˜¶–¶”¶’¶¶Ž¶Œ¶Š¶ˆ¶†¶„¶‚¶€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a8468104c65a6002fd3a9d4ac39f3ee34c21ce4a b/webm_parser/fuzzing/corpus/a8468104c65a6002fd3a9d4ac39f3ee34c21ce4a new file mode 100644 index 000000000000..9b15205b0f01 --- /dev/null +++ b/webm_parser/fuzzing/corpus/a8468104c65a6002fd3a9d4ac39f3ee34c21ce4a @@ -0,0 +1 @@ +S€gTÃgŠss‡cÀ„hÊÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a899424027f1d69a05384355858311a6fa3940a3 b/webm_parser/fuzzing/corpus/a899424027f1d69a05384355858311a6fa3940a3 new file mode 100644 index 000000000000..3ef41ca7db10 --- /dev/null +++ b/webm_parser/fuzzing/corpus/a899424027f1d69a05384355858311a6fa3940a3 @@ -0,0 +1 @@ +S€gT®kˆ®†à„T° \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/a8a5de5a86a16952aacdf602120f27807294c3ec b/webm_parser/fuzzing/corpus/a8a5de5a86a16952aacdf602120f27807294c3ec new file mode 100644 index 000000000000..865b44e18eb8 --- /dev/null +++ b/webm_parser/fuzzing/corpus/a8a5de5a86a16952aacdf602120f27807294c3ec @@ -0,0 +1 @@ +S€g‘TÃgŒss‰cÀ†hʃ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/aa65229e62d7cb8048d2f5911226b177e8e53cab b/webm_parser/fuzzing/corpus/aa65229e62d7cb8048d2f5911226b177e8e53cab new file mode 100644 index 000000000000..80476c0b5e41 --- /dev/null +++ b/webm_parser/fuzzing/corpus/aa65229e62d7cb8048d2f5911226b177e8e53cab @@ -0,0 +1 @@ +S€gŠT®k…®ƒ×ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/aaa96713f8ccb0bbd5c8e91715d4b86e7b338676 b/webm_parser/fuzzing/corpus/aaa96713f8ccb0bbd5c8e91715d4b86e7b338676 new file mode 100644 index 0000000000000000000000000000000000000000..1fa146d48d189d5b2614660e01131c0fd5ef8709 GIT binary patch literal 36 qcmZp>zt~l(VV;xI_Xej=4h*GE)(vkQ7@V4bxV^!twc$+@BNG53^$wT- literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/aae6354e5ba12ee3ad89fabfd72f5368a639d30f b/webm_parser/fuzzing/corpus/aae6354e5ba12ee3ad89fabfd72f5368a639d30f new file mode 100644 index 0000000000000000000000000000000000000000..fb16013b54bdad20ba17c32f1b56b30c5650364f GIT binary patch literal 43 wcmb1gy}!7_smXz*JT)m-BDf)as=V{I(h1KU7*;zlEN*QCa#bkO2TG!WwP> literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/adad2ca7ab313add6e955f704719e03d5229e4d0 b/webm_parser/fuzzing/corpus/adad2ca7ab313add6e955f704719e03d5229e4d0 new file mode 100644 index 000000000000..2105af1f86eb --- /dev/null +++ b/webm_parser/fuzzing/corpus/adad2ca7ab313add6e955f704719e03d5229e4d0 @@ -0,0 +1 @@ +ã \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/adb1f8cd3b68c076d81311071aee2a1c3785c7cb b/webm_parser/fuzzing/corpus/adb1f8cd3b68c076d81311071aee2a1c3785c7cb new file mode 100644 index 000000000000..4710f7fa08d0 --- /dev/null +++ b/webm_parser/fuzzing/corpus/adb1f8cd3b68c076d81311071aee2a1c3785c7cb @@ -0,0 +1 @@ +S€gŒT®k‡®…œƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/ade30c327d9f51daf37cb3e39fdb15125b84a257 b/webm_parser/fuzzing/corpus/ade30c327d9f51daf37cb3e39fdb15125b84a257 new file mode 100644 index 000000000000..4bc05f44a597 --- /dev/null +++ b/webm_parser/fuzzing/corpus/ade30c327d9f51daf37cb3e39fdb15125b84a257 @@ -0,0 +1 @@ +S€gT®kˆ®†à„S¸ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/ae783661f52d36f9d6b87c8394ee6f2d61dae640 b/webm_parser/fuzzing/corpus/ae783661f52d36f9d6b87c8394ee6f2d61dae640 new file mode 100644 index 000000000000..d5fe0e25baf9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/ae783661f52d36f9d6b87c8394ee6f2d61dae640 @@ -0,0 +1 @@ +S€g“M›tŽM»‹S«ˆMatroska \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/ae8da06c3c69076c06108219e19c8b36dba05a5c b/webm_parser/fuzzing/corpus/ae8da06c3c69076c06108219e19c8b36dba05a5c new file mode 100644 index 000000000000..78a347afda00 --- /dev/null +++ b/webm_parser/fuzzing/corpus/ae8da06c3c69076c06108219e19c8b36dba05a5c @@ -0,0 +1 @@ +Eߣ8S€g;€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/aec8a29238dba3c37f45f2e2e4e64b0e7fa60a74 b/webm_parser/fuzzing/corpus/aec8a29238dba3c37f45f2e2e4e64b0e7fa60a74 new file mode 100644 index 000000000000..359bfe65e803 --- /dev/null +++ b/webm_parser/fuzzing/corpus/aec8a29238dba3c37f45f2e2e4e64b0e7fa60a74 @@ -0,0 +1 @@ +S€gI©f‹D‰ˆ?ùãw›—ô¨ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/affba8e4061bf260b1bdf8a815675cc3b4ebefa5 b/webm_parser/fuzzing/corpus/affba8e4061bf260b1bdf8a815675cc3b4ebefa5 new file mode 100644 index 0000000000000000000000000000000000000000..94b2f2d3efe51f4f1ecea9333640b97f42a85f1a GIT binary patch literal 63 mcmey|@Wz2*b&~^wQ+tEcXJk$*l>Zu1^^)04dDO) literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b0ab4f92df810edf4371baad1d4bbd95d360c607 b/webm_parser/fuzzing/corpus/b0ab4f92df810edf4371baad1d4bbd95d360c607 new file mode 100644 index 000000000000..02f58e20212a --- /dev/null +++ b/webm_parser/fuzzing/corpus/b0ab4f92df810edf4371baad1d4bbd95d360c607 @@ -0,0 +1 @@ +S€gŒC¶u‡ …›ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b14e5f5f6d29b6b71e7fec03eaf7d8237c0c6b2e b/webm_parser/fuzzing/corpus/b14e5f5f6d29b6b71e7fec03eaf7d8237c0c6b2e new file mode 100644 index 0000000000000000000000000000000000000000..32796f0599c2b336bc9abb5292f06732adf58edd GIT binary patch literal 178 zcmb1gm2$nmxWlQ*fu%e(DOV!6A>HAa;OW^VSA2I1FfgzOuK|*bjDo(iOL}19BiKa{l)E00{?-gNB{+ambOA9 oL_Jq8dG5f-xY~h{X>qI6KG*xoX$?$bA?u{FyVn7AvPE4705do_b%7 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b1efd9e687d79e3f5a75eba02bd80bedb72350ba b/webm_parser/fuzzing/corpus/b1efd9e687d79e3f5a75eba02bd80bedb72350ba new file mode 100644 index 0000000000000000000000000000000000000000..348fa2cfb3e9f71a21700a5479c8cd8c3c2d95b1 GIT binary patch literal 59 zcmZ?0zqrGx$$_OjH7QpjxFLO|yz{owCC?og8CN?nGA(XtWdMT3txX_mftCZyY@o~n HFvSD_dHNDb literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b25b0fbf46cef1d888fe900445c9ab95330f44cd b/webm_parser/fuzzing/corpus/b25b0fbf46cef1d888fe900445c9ab95330f44cd new file mode 100644 index 000000000000..b59c5945f9f6 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b25b0fbf46cef1d888fe900445c9ab95330f44cd @@ -0,0 +1 @@ +™ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b2786e0b9d6e7b39165eb4e87e3110362e9e6660 b/webm_parser/fuzzing/corpus/b2786e0b9d6e7b39165eb4e87e3110362e9e6660 new file mode 100644 index 000000000000..6004cde05e03 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b2786e0b9d6e7b39165eb4e87e3110362e9e6660 @@ -0,0 +1 @@ +EߣˆB‚@webmS€g©C¶u¤ç@«@ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b332536a77d6efcd379cfc2f9828291516cc1ff4 b/webm_parser/fuzzing/corpus/b332536a77d6efcd379cfc2f9828291516cc1ff4 new file mode 100644 index 000000000000..20826470d462 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b332536a77d6efcd379cfc2f9828291516cc1ff4 @@ -0,0 +1 @@ +S€g–T®k‘®m€Œb@‰P5†Gçƒ'è€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b33962eca397f59591dbb9668e622cf99f2d7cda b/webm_parser/fuzzing/corpus/b33962eca397f59591dbb9668e622cf99f2d7cda new file mode 100644 index 0000000000000000000000000000000000000000..9f6ee1d38de50d4f27ee9a58f44e7a36e81f3001 GIT binary patch literal 91 zcmb0LZb-i-7P2n;;=1$K6&M(bj{=EiAl?bY9Y7qq3P{WWV&%up4Z(RphSJtK4Q)U= bc@dCMZR=bWv)tJd|60t^g{j4qumzI~q$xg`FFg6fq(aptuAeBTBTBPp>c+!4a4 F0svFg80i22 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b3e01674a1e4dd78e748782fcfc3add5523f51d8 b/webm_parser/fuzzing/corpus/b3e01674a1e4dd78e748782fcfc3add5523f51d8 new file mode 100644 index 000000000000..34164076ecce --- /dev/null +++ b/webm_parser/fuzzing/corpus/b3e01674a1e4dd78e748782fcfc3add5523f51d8 @@ -0,0 +1 @@ +× \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b53e7ef9aad70fcd80986696ebc586c03495b8ed b/webm_parser/fuzzing/corpus/b53e7ef9aad70fcd80986696ebc586c03495b8ed new file mode 100644 index 000000000000..ee865d870be6 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b53e7ef9aad70fcd80986696ebc586c03495b8ed @@ -0,0 +1 @@ +GáEߣˆB‚@webmS€g£*M'›t€I©f€C¶u€TSkk»€®€C§p€TÀ†g€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b540412c01f960f95edd2a1bc03f1b4447f2b4f2 b/webm_parser/fuzzing/corpus/b540412c01f960f95edd2a1bc03f1b4447f2b4f2 new file mode 100644 index 0000000000000000000000000000000000000000..b77bc28047a8b07d8c779d1ee9d8fed9babefe91 GIT binary patch literal 5 McmZ21b>Ec%00zSW;s5{u literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b62c2c591db32b26e997aa4ece577742db84428a b/webm_parser/fuzzing/corpus/b62c2c591db32b26e997aa4ece577742db84428a new file mode 100644 index 000000000000..64a70accefa9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b62c2c591db32b26e997aa4ece577742db84428a @@ -0,0 +1 @@ +S€g“C¶uŽ ŒŽ‡èƒÌè€Ì \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b62d99583f30c15c3c2dbed2f69c5e45075d7640 b/webm_parser/fuzzing/corpus/b62d99583f30c15c3c2dbed2f69c5e45075d7640 new file mode 100644 index 000000000000..edf7ec3ed605 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b62d99583f30c15c3c2dbed2f69c5e45075d7640 @@ -0,0 +1 @@ +{©€gˆC¶uƒ« \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b62f98976c11d79674b019ea78a7ce4d6d78b479 b/webm_parser/fuzzing/corpus/b62f98976c11d79674b019ea78a7ce4d6d78b479 new file mode 100644 index 0000000000000000000000000000000000000000..6a4f18607c79a88e0389dc87354e03c5a70114fe GIT binary patch literal 2 JcmWe&0000Y01*HH literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b6787dabeb5cd64ac85f1ec5a7cbeecfd48b2c5f b/webm_parser/fuzzing/corpus/b6787dabeb5cd64ac85f1ec5a7cbeecfd48b2c5f new file mode 100644 index 000000000000..d04309562a69 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b6787dabeb5cd64ac85f1ec5a7cbeecfd48b2c5f @@ -0,0 +1 @@ +S€g¼C§p·E¹´¶‘|°…@hello€€€€€€€€€…@B€@…@C¶@ sÄ€€€€€€„€–€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b82ebcf4d09ba28d835cb9667da603e46e3438eb b/webm_parser/fuzzing/corpus/b82ebcf4d09ba28d835cb9667da603e46e3438eb new file mode 100644 index 000000000000..cea92d654374 --- /dev/null +++ b/webm_parser/fuzzing/corpus/b82ebcf4d09ba28d835cb9667da603e46e3438eb @@ -0,0 +1 @@ +S€gC§p‹E¹ˆ¶†€„C~! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/b85c6bc2473aa12e22f91db3546dfa9d85d8b3d1 b/webm_parser/fuzzing/corpus/b85c6bc2473aa12e22f91db3546dfa9d85d8b3d1 new file mode 100644 index 0000000000000000000000000000000000000000..5f82c67367f79531e35244973b239742c5abce54 GIT binary patch literal 27 icmb0LZb+Xd@4T&a;)4E#y^RbEE$j>ojEqc7%*+6Yzz2K) literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b86e98660980680890bcbf02cacdf568d530fa64 b/webm_parser/fuzzing/corpus/b86e98660980680890bcbf02cacdf568d530fa64 new file mode 100644 index 0000000000000000000000000000000000000000..207162b0fcfadbdaefa3a1c1a65546d49a0a642d GIT binary patch literal 48 xcmb1gy}!7_smXz5N@`NBL~uj;N_ppPrAwYWFfz<`U}Rk2z_7r91;PSROaQbh56%Dp literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/b8d9beea35762009941189674c2cfcd14f81254a b/webm_parser/fuzzing/corpus/b8d9beea35762009941189674c2cfcd14f81254a new file mode 100644 index 000000000000..25ec34a2745b --- /dev/null +++ b/webm_parser/fuzzing/corpus/b8d9beea35762009941189674c2cfcd14f81254a @@ -0,0 +1 @@ +S€gŒT®k‡®…ˆƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/ba517141fc9a468142a8d03d4ee395b4d4f30edc b/webm_parser/fuzzing/corpus/ba517141fc9a468142a8d03d4ee395b4d4f30edc new file mode 100644 index 000000000000..aaea60de440b --- /dev/null +++ b/webm_parser/fuzzing/corpus/ba517141fc9a468142a8d03d4ee395b4d4f30edc @@ -0,0 +1 @@ +S€cŽDg‰ss†cS€g‡SÀ»€gÈk€‚»€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/bb0a596017cfc2185505d28065ab3cd238d0e2ea b/webm_parser/fuzzing/corpus/bb0a596017cfc2185505d28065ab3cd238d0e2ea new file mode 100644 index 000000000000..1379811f3349 --- /dev/null +++ b/webm_parser/fuzzing/corpus/bb0a596017cfc2185505d28065ab3cd238d0e2ea @@ -0,0 +1 @@ +S€gˆC§pƒE¹€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/bb6232815b373e441e2acfcca015f75c680eafac b/webm_parser/fuzzing/corpus/bb6232815b373e441e2acfcca015f75c680eafac new file mode 100644 index 0000000000000000000000000000000000000000..04a597c1fd890f6592515eba17bf3bfeb880c412 GIT binary patch literal 39 pcmb0LZb)Aw>bWv)uGaO94KAGu3=A%bK%#mjknn8)65)+LOAiSE literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/bdb1cc868d6ced390f5d35c5f43da9f464fac464 b/webm_parser/fuzzing/corpus/bdb1cc868d6ced390f5d35c5f43da9f464fac464 new file mode 100644 index 000000000000..44bb8cdefaaf --- /dev/null +++ b/webm_parser/fuzzing/corpus/bdb1cc868d6ced390f5d35c5f43da9f464fac464 @@ -0,0 +1 @@ +S€g»TÃg¶ss³gÈ©È„#‡dgÈ„]‡egÈ„D‡dgÈ„]‡egÈ„D‡gÈ€D‡dgÈ€g \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/bf67bb08e0abde692748031c297bc1c7910542f4 b/webm_parser/fuzzing/corpus/bf67bb08e0abde692748031c297bc1c7910542f4 new file mode 100644 index 0000000000000000000000000000000000000000..8705e840a040fe30cfa298e381147ed9243e7dbf GIT binary patch literal 34 ocmb0LZb+Xi6mmFyMse}f(?501AT-z5oCK literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/bfc07c62ef2770d53d9188b260f531a8128a5c5e b/webm_parser/fuzzing/corpus/bfc07c62ef2770d53d9188b260f531a8128a5c5e new file mode 100644 index 0000000000000000000000000000000000000000..341f94d40edac96a722c4e54285f104fcde590b4 GIT binary patch literal 53 zcmb1gl~~;2{-_~1-KohTWK(KVu0(J{`bv4{ZA*?gFfy)oU}Rd`+Qa|^3mgPW7ye)N I|D6K^03MALdjJ3c literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/bfed121df31ff73b770ed7f27fc7f4da2fa73e0c b/webm_parser/fuzzing/corpus/bfed121df31ff73b770ed7f27fc7f4da2fa73e0c new file mode 100644 index 0000000000000000000000000000000000000000..7b2c56135ecc05d31bbbf416cad52f293bdaf670 GIT binary patch literal 24 gcmeZmIMWc~)TXua9mBhK@7mum2rw{6zkBxv0F-_VCjbBd literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c01816206d93691165e6e3a1924cf9fbc9cf39bd b/webm_parser/fuzzing/corpus/c01816206d93691165e6e3a1924cf9fbc9cf39bd new file mode 100644 index 000000000000..b99d57aef0ef --- /dev/null +++ b/webm_parser/fuzzing/corpus/c01816206d93691165e6e3a1924cf9fbc9cf39bd @@ -0,0 +1 @@ +S€–gT®k‘®m€Œb@‰P5ÿ†GçƒGè€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c02be525edae59ad9d0d9dcbd790629dc9aaee9b b/webm_parser/fuzzing/corpus/c02be525edae59ad9d0d9dcbd790629dc9aaee9b new file mode 100644 index 0000000000000000000000000000000000000000..67629fee4e0804c5e06a022a58fbc0969ef07fd5 GIT binary patch literal 130 zcmWGxaF7UYNS`70rX^&ZL~we@x`u0F+3U_IW0|30QCeQ!? literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c094ce0c13ee9a4ca37817d9f7dddc11b2d60177 b/webm_parser/fuzzing/corpus/c094ce0c13ee9a4ca37817d9f7dddc11b2d60177 new file mode 100644 index 000000000000..9b44fb09eaeb --- /dev/null +++ b/webm_parser/fuzzing/corpus/c094ce0c13ee9a4ca37817d9f7dddc11b2d60177 @@ -0,0 +1 @@ +S€g‘C¶uŒ Š¡‡¦…î:ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c0aab5486ad2e80bcc12fca9fb6653984aa68274 b/webm_parser/fuzzing/corpus/c0aab5486ad2e80bcc12fca9fb6653984aa68274 new file mode 100644 index 000000000000..d643dc17a03c --- /dev/null +++ b/webm_parser/fuzzing/corpus/c0aab5486ad2e80bcc12fca9fb6653984aa68274 @@ -0,0 +1 @@ +Eߣÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c13ca850db259c032cf24cdf6f2833c9d74529d0 b/webm_parser/fuzzing/corpus/c13ca850db259c032cf24cdf6f2833c9d74529d0 new file mode 100644 index 000000000000..46ddda2ab261 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c13ca850db259c032cf24cdf6f2833c9d74529d0 @@ -0,0 +1 @@ +EߣˆB‚@webmS€g©C¶uÿÿÿ€« \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c1a8da6cdc8988e6a69961413803acbd1ee935e0 b/webm_parser/fuzzing/corpus/c1a8da6cdc8988e6a69961413803acbd1ee935e0 new file mode 100644 index 000000000000..80a14f76ff89 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c1a8da6cdc8988e6a69961413803acbd1ee935e0 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶³sÄVTA‘@’@€@…@B€@…@C¶@sĶ@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c25abc82a0470129f2d098ac65fabf34c4e11188 b/webm_parser/fuzzing/corpus/c25abc82a0470129f2d098ac65fabf34c4e11188 new file mode 100644 index 0000000000000000000000000000000000000000..774e5f1d44e65ed4fb68d7981dcde0c4428830b5 GIT binary patch literal 61 qcmb1gy?>y=>3f6ICkFa6e)AJ7@#K%c{QCZW%lG5A#f!s%d~EO?ZVF4f>F4k%j4W-EhaSegc9M1X z+hMkYO_CXI_splTQMODpon&8fs>QxL%$I<citz$ltW(4LSA39Sngrv2S z#4-5&!0#v4T>w+P@>}s#fy%!}L@&w&j9F6GNQ@XVE$Z4odMN|%iAsz*PmD^7|Ew4U zRM4_#cJn;=z?p22BveQS^XlkfA)DSerA1Q-IiVs7ufl|sGapua@nl3D#=Gf$fl-vp9MLL$q^-|Y(3r4Z z$(+5Aq{t`C3Sl%06Q1cK#-2E*;-2oW#E!}UIarLST+E7+$SOIy~(1e*2utCP9dA2$acF4CWKtDGB zR9G0fJjl3b4M|18CddxEu3GWrs2t#CMCAjrhJ{Gw9NCr;aCKVjjN3Ak!5K>h({!wO zEUfLLk9q-@m!sS$#ua+u&l4h!gnlT!Wag zO{m}>4x92k(UdhR(SJUK4;bzoF>oq$bn*p$Pf7S8^D=b8D$(}mbtQ`VC z_CisR=$`(I1KA|BHH?FkQURDf_b@m7&SI55FzIuZU2S$m$PQ-pCov0e!V7BVPhwoE zKu-$Qe*&q3dCfGI;*{C**JAplv7S{4=pgewOmBEv(Fad`t|E>8h;eN_D*Z`J z^7RqD+aJp{)pctZA=y)6IH46aKx*F;_mVo`v9PYxi$k_D?Q_`D14Y=oW)vex76y@{ zlybr~Ex59ou(!s&%U+NV60>qXgb&m$P2=e3q?h>pz)xaazS=;tNrHiR@iNBrqG|tt zy0?6-jXBe#iU8FjUo{C@t1qN1&>vE1ZH!Hnr(2jT@~*Xzmg4R4UkO@i&we`8Fp!f0 zZ2dzj(8dC{OZ9?9P*FhfSXkQ=E`J`bcO43sT~lf(#uo-z!fzcpq1g<8PA4;Bn4VIH5X>Ii7 zvDJTbr0fj%hzr`sNC1ykLN0^0gmrGua!cdyH^GJ0 zgEBGhd$86d**1oW*8}vt4oo>Pvo25)ONE5pu~HqMZUV*=EWKJyGci^5CEb z2_yaU1lbd05kST?8_u06_~K<*MfxCJ@OBQdn|s!bbED_sE+_q3BWQBJ`a9UBxk{k3_MG{6vOnR4J7YO@Eb#G>@$Owk z&_l}$1!kYC&&HWRu|()&(;UWav%&V z2XwA%iB{AA$*nkELet}z7KmLK$X~G1%FAQLmc$!EPLbwaRfqm$>6c<2igpDJuzZ@* z(_GZTi46hfWB^MR)O0NwuyBGd#@2_b=p9(2Y@W=5Jt^v)D2Z_|?f)a93RHS0Dr#&I za!N&zeBwWy%pw$5FCK^r(ImO+sTvrkHY~z%CS89$`Pr+6f?A5xUj;a!k@v4EF-gLZ z%t5V7Y*vNYPun41nD96I+d|-}ta=9DG{V}St0L-h+Xq(!A|>UNeIkj{#JhGRW&qai znjzsUDtp10w4>N;ofBSHo_RI~JQVMBtUMYS89KCD+k3-YlZ%Zj}k2HUhz Re+x{^h^GWRu(2WF{SWi<_Z$EK literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c2d63b1d75cf53ee3b955bb143036ca93ef3a256 b/webm_parser/fuzzing/corpus/c2d63b1d75cf53ee3b955bb143036ca93ef3a256 new file mode 100644 index 0000000000000000000000000000000000000000..3fbf8e455f7c8f5dfbcbbaf011c590c30d07d951 GIT binary patch literal 105 zcmWeCo*O));jl!AmPBwvdbdpQ?(DYREt}KT(i@7WCLd_YIMv9Y=z4!~hf|XSOL=Ni zZZAmPVnJW^*(D9>Ct6$=Z*LIwT$$D&@4T(FA!J>4Lu29zkSPrU&dUoLghE*x|Z!>$VBSM;aN!S{)dj8XQ=Fl=C(R G?*9PR_$~GT literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c3b2749ab6c4d303bfd5da9ea9c8807e9f92d259 b/webm_parser/fuzzing/corpus/c3b2749ab6c4d303bfd5da9ea9c8807e9f92d259 new file mode 100644 index 0000000000000000000000000000000000000000..e94d73726c06e1c08e1c5d1fe063748f511becd5 GIT binary patch literal 25 gcmb0LZb+XZ@4T(Fe{pXk0|PT-3nK#q6B9Et0Bh0)M*si- literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c4dd3c8cdd8d7c95603dd67f1cd873d5f9148b29 b/webm_parser/fuzzing/corpus/c4dd3c8cdd8d7c95603dd67f1cd873d5f9148b29 new file mode 100644 index 000000000000..c5fa78456dbd --- /dev/null +++ b/webm_parser/fuzzing/corpus/c4dd3c8cdd8d7c95603dd67f1cd873d5f9148b29 @@ -0,0 +1 @@ +< \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c52fa8d16e520980e470d76e3fb4f6a612f0f3cf b/webm_parser/fuzzing/corpus/c52fa8d16e520980e470d76e3fb4f6a612f0f3cf new file mode 100644 index 0000000000000000000000000000000000000000..d614d45172342f15795b9c098c67b2caff5526e4 GIT binary patch literal 28 jcmZ?W+5A7{!D3gb`~MXeuK=>&|5t4M-^9T1|F1Lv?w1fB literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c56fb214efcd707e6fa68b803d9e7686fc2f4336 b/webm_parser/fuzzing/corpus/c56fb214efcd707e6fa68b803d9e7686fc2f4336 new file mode 100644 index 000000000000..e03fe14ec1f5 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c56fb214efcd707e6fa68b803d9e7686fc2f4336 @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶žsÄVTA‘@’@€@…@B€@…@C¶@sĶ@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c5902afe54998ebc5d8d59043227d379a8c2eee0 b/webm_parser/fuzzing/corpus/c5902afe54998ebc5d8d59043227d379a8c2eee0 new file mode 100644 index 0000000000000000000000000000000000000000..d71d57311e3d3751c094da6c16012c0ea39ce6d2 GIT binary patch literal 16 XcmWI9?9^7!uJw-L-MfD2cke0yL$L_C literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c6a16abeea323833079e97b1830610aa6c6eba91 b/webm_parser/fuzzing/corpus/c6a16abeea323833079e97b1830610aa6c6eba91 new file mode 100644 index 000000000000..b48967d06577 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c6a16abeea323833079e97b1830610aa6c6eba91 @@ -0,0 +1 @@ +€gk‹®TEߣˆB‚@®‰á‡xµ„@ÉÛwebmS€g©C¶uÿÿÿ€« \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c6fcfd2a1f91a7f6a8c124f2637e60645be01006 b/webm_parser/fuzzing/corpus/c6fcfd2a1f91a7f6a8c124f2637e60645be01006 new file mode 100644 index 0000000000000000000000000000000000000000..5aaebfbfad5e9923a59246a7ba0f117d60f8cc3c GIT binary patch literal 25 gcmb0LZb+XZ@4T(Fe{pXk149cl0|O%?6B9Et0Bq6*M*si- literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c75b02a90370df1f54de2f63a4da8db22f2cf719 b/webm_parser/fuzzing/corpus/c75b02a90370df1f54de2f63a4da8db22f2cf719 new file mode 100644 index 000000000000..0cb101b2669a --- /dev/null +++ b/webm_parser/fuzzing/corpus/c75b02a90370df1f54de2f63a4da8db22f2cf719 @@ -0,0 +1 @@ +ŸEßE£‚B; \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c77cb763d73db279aebfb42d2b5dca3d705d3df7 b/webm_parser/fuzzing/corpus/c77cb763d73db279aebfb42d2b5dca3d705d3df7 new file mode 100644 index 0000000000000000000000000000000000000000..cae00316302d693b0b9e0b931f3c1962017f332e GIT binary patch literal 91 zcmb0LZb-i-7P2n;;=1$K6&M(bj{=EiAl?bY9Y7qq3P{WWV&%up4Z(RphSJtK4Q)U= Zc@dCMZR=U0RS6-8UX+R literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c864ac8bce0353ecc7d5cb0ce5a1e77a5239201e b/webm_parser/fuzzing/corpus/c864ac8bce0353ecc7d5cb0ce5a1e77a5239201e new file mode 100644 index 000000000000..d74502749303 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c864ac8bce0353ecc7d5cb0ce5a1e77a5239201e @@ -0,0 +1 @@ + ÿM \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c877c08a79a7408aed779d4a430c5db1bce26314 b/webm_parser/fuzzing/corpus/c877c08a79a7408aed779d4a430c5db1bce26314 new file mode 100644 index 000000000000..de29891d259b --- /dev/null +++ b/webm_parser/fuzzing/corpus/c877c08a79a7408aed779d4a430c5db1bce26314 @@ -0,0 +1 @@ +S€gŒI©f‡D‰„@ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c8aff6e2e2dfb18be385483b871ac86ff6eac63f b/webm_parser/fuzzing/corpus/c8aff6e2e2dfb18be385483b871ac86ff6eac63f new file mode 100644 index 0000000000000000000000000000000000000000..d553636e9bdc1adf7fa7d002b5131c1568b3299c GIT binary patch literal 200 zcmWG>YSUWzj^W+AckR;e-buOMU;N*x$$_OjH7QpjxFOx)n4s_MieBH{0t^i7!E1md zBO_3x1Vw~N)N^H8ht`#iAZeGXki+Sdi;D$#85oiefV5?t0*N!pJ8vs(dk$ub1@F%8 z1{uk=8N_E4aJE`r(CfMr$YFQg0~Qj3n!>=q3pGUpE?xmL9w?E10;IvE6(j)wgF literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/c8c1c1f970bc809a75ad076bdb06275b6f72d078 b/webm_parser/fuzzing/corpus/c8c1c1f970bc809a75ad076bdb06275b6f72d078 new file mode 100644 index 000000000000..f2f8214fa3a6 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c8c1c1f970bc809a75ad076bdb06275b6f72d078 @@ -0,0 +1 @@ +Eߣ„B÷ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c995d52934ac188971f02d5dbdb3cdd70cb03267 b/webm_parser/fuzzing/corpus/c995d52934ac188971f02d5dbdb3cdd70cb03267 new file mode 100644 index 000000000000..fe179c910fce --- /dev/null +++ b/webm_parser/fuzzing/corpus/c995d52934ac188971f02d5dbdb3cdd70cb03267 @@ -0,0 +1 @@ +Tº…éJœœœœœœ¼œ"…¼¿À>sÅœŠ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/c9e7fc3e0e1015a1c15992447f678a495c3ea5dc b/webm_parser/fuzzing/corpus/c9e7fc3e0e1015a1c15992447f678a495c3ea5dc new file mode 100644 index 000000000000..b784d77e81b6 --- /dev/null +++ b/webm_parser/fuzzing/corpus/c9e7fc3e0e1015a1c15992447f678a495c3ea5dc @@ -0,0 +1 @@ +S€g–C§p‘E¹Ž¶@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/ca5b619ce1bbe23d519f5764d53458d2b85eb9fa b/webm_parser/fuzzing/corpus/ca5b619ce1bbe23d519f5764d53458d2b85eb9fa new file mode 100644 index 0000000000000000000000000000000000000000..a7b7c191c7fd26e721d3c481961dc633ef4ec4da GIT binary patch literal 121 zcmb1=dgr>h!>P%Er93q$S0cC}-Qk#^lhxAagYUlyWC2 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/cb0556c65c7381192c94324a3b1b8afb7e33fecc b/webm_parser/fuzzing/corpus/cb0556c65c7381192c94324a3b1b8afb7e33fecc new file mode 100644 index 0000000000000000000000000000000000000000..2a7063ef0146a6a5804b15cd30a1b3c10b9af5df GIT binary patch literal 54 zcmb0LZb;uC@4T&a^@5cPTN!|0QA;zBEM3^V$bn(D!v@CR4vZkoRI2!5QRDwU2j&+J F3;-@Y6m9?j literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/cb1a093e6810c7f6c002a2a54ea390cf769c9949 b/webm_parser/fuzzing/corpus/cb1a093e6810c7f6c002a2a54ea390cf769c9949 new file mode 100644 index 0000000000000000000000000000000000000000..6f2ab374cd29e4b9c92bd12e83cc6050e42eca01 GIT binary patch literal 52 zcmb1gl?ZM~U)B9fZ{x5Z4 F1OV?-6GQ+2 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/cc1487af64aeefd7080e7678a04870dc85a7928a b/webm_parser/fuzzing/corpus/cc1487af64aeefd7080e7678a04870dc85a7928a new file mode 100644 index 0000000000000000000000000000000000000000..815b9079dbd6453e1a4b8152c03f4398ff600e49 GIT binary patch literal 25 gcmb0LZb+XZ@4T(Fe{pX!1A`Sa0|O%?6B9Et0BQ&Y{{R30 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/cc71d2c9f5eae12acee133bd9e50d84881a1bd88 b/webm_parser/fuzzing/corpus/cc71d2c9f5eae12acee133bd9e50d84881a1bd88 new file mode 100644 index 0000000000000000000000000000000000000000..2f87371ebbd0e56c014bb2350503f9e4ba6dd627 GIT binary patch literal 5 Mcmdlbb>Ec%00%Y!`2YX_ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/ccb8f962426683663972534c15354f70c3b34a10 b/webm_parser/fuzzing/corpus/ccb8f962426683663972534c15354f70c3b34a10 new file mode 100644 index 000000000000..3b41d17a7093 --- /dev/null +++ b/webm_parser/fuzzing/corpus/ccb8f962426683663972534c15354f70c3b34a10 @@ -0,0 +1 @@ +S€g”T®k®m€Šb@‡P5„Gáÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/cd0771c4754dfcd9c89b9b8a02df96fed974850d b/webm_parser/fuzzing/corpus/cd0771c4754dfcd9c89b9b8a02df96fed974850d new file mode 100644 index 000000000000..519a31282891 --- /dev/null +++ b/webm_parser/fuzzing/corpus/cd0771c4754dfcd9c89b9b8a02df96fed974850d @@ -0,0 +1 @@ +’!EߣˆB‚@webm!S€g*M£'›t€I©f€C¶u€TSkk»€®€C§p€TÃg€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/cd8899c66cbd92ee57f94e000744b32662258ba3 b/webm_parser/fuzzing/corpus/cd8899c66cbd92ee57f94e000744b32662258ba3 new file mode 100644 index 0000000000000000000000000000000000000000..42db6e81709065c9e3930fe1220be1492d759171 GIT binary patch literal 20 bcmb0LZbDWx;^f9wMwj#xGl9&M^b_4cDmDE? z3rO-vYh!z3T0W4|-k2_dYRUxH#f=bCT7agsHzoryBgm9?h$-z5Q-JzGhGFUlx&piY I7F-4Z07<4=!2kdN literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/cfb09018afa0eb1a829e556d9f8bceff40cb36bc b/webm_parser/fuzzing/corpus/cfb09018afa0eb1a829e556d9f8bceff40cb36bc new file mode 100644 index 000000000000..31fc35236cbf --- /dev/null +++ b/webm_parser/fuzzing/corpus/cfb09018afa0eb1a829e556d9f8bceff40cb36bc @@ -0,0 +1 @@ +S€gI©f‹Daˆ4Vxš¼Þð \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/cfbe3d66d7eb3199440e8e911a93044cc3165c6c b/webm_parser/fuzzing/corpus/cfbe3d66d7eb3199440e8e911a93044cc3165c6c new file mode 100644 index 0000000000000000000000000000000000000000..ba138c42534da6ab228e210074377d8453d47d64 GIT binary patch literal 227 zcmb1gm2$nmxWlQ*fu%e(DOV!6A>H9vIf&{P3OStKRb1SjetLGv72n+g4D2UbTv~(I z2rw`(G79?6F6jjcGO!1)NnrqqF^Nhwy8cHoE4fd|CB3t_xGfoEV!KQ*i2A=$-g#T8 z=gPDWt?L^FL57J!Ibc&jl5NkymdM{<-0mdsA851$P!OoK6(S+(d0=-#`ibm@rqxTH mJ1{b?c2Hzw+TF0YRcfE>edV+UCb5upQrX?>fQGO|T?YW808iTh literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d0a91f7984904976de592cb68e8832853fbec9bc b/webm_parser/fuzzing/corpus/d0a91f7984904976de592cb68e8832853fbec9bc new file mode 100644 index 0000000000000000000000000000000000000000..d61a53a0aa62dd1dbde0784c1a4bf218aa1c3567 GIT binary patch literal 20 bcmb0LZbp*k5s-QY7G?&xK3ohI IAYcUn0FJE+j{pDw literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d2709a1c9d96e72cb844eedca8bfe8440cdb0ef8 b/webm_parser/fuzzing/corpus/d2709a1c9d96e72cb844eedca8bfe8440cdb0ef8 new file mode 100644 index 000000000000..c41d905e02b3 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d2709a1c9d96e72cb844eedca8bfe8440cdb0ef8 @@ -0,0 +1 @@ +S€gI©f‹Daˆþܺ˜vT2 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d2e385f61ad4b5d45a10a5a6cb85d72e84dea54b b/webm_parser/fuzzing/corpus/d2e385f61ad4b5d45a10a5a6cb85d72e84dea54b new file mode 100644 index 000000000000..433cbfd64221 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d2e385f61ad4b5d45a10a5a6cb85d72e84dea54b @@ -0,0 +1 @@ +@p€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d2facb213561b30a5bcd012e4e01d0d5b0b26957 b/webm_parser/fuzzing/corpus/d2facb213561b30a5bcd012e4e01d0d5b0b26957 new file mode 100644 index 000000000000..26ca8613e95e --- /dev/null +++ b/webm_parser/fuzzing/corpus/d2facb213561b30a5bcd012e4e01d0d5b0b26957 @@ -0,0 +1 @@ +S€gT®kŠ®ˆà†T²ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d300e5e46a825e5892fae1ba3c466c836f9e1da2 b/webm_parser/fuzzing/corpus/d300e5e46a825e5892fae1ba3c466c836f9e1da2 new file mode 100644 index 0000000000000000000000000000000000000000..7b6f1d84b7ad23bf5571722a44cccdb532a33d72 GIT binary patch literal 63 zcmb0LZb;uP6mmFyTXFH`^b;Fg7dIxlR5d2Kv^FvTak5KmCu91FnJ(>(EeN6Z;v?xN IT7Y8d0D7Mr{Qv*} literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d302c69c881e230e6433283007d318ba437025c3 b/webm_parser/fuzzing/corpus/d302c69c881e230e6433283007d318ba437025c3 new file mode 100644 index 000000000000..055ab798c1ee --- /dev/null +++ b/webm_parser/fuzzing/corpus/d302c69c881e230e6433283007d318ba437025c3 @@ -0,0 +1 @@ +S€g‘T®kŒ®Šm€‡b@„P3ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d30c1b65bc141d1a15d4ed622ba182c96dacdf92 b/webm_parser/fuzzing/corpus/d30c1b65bc141d1a15d4ed622ba182c96dacdf92 new file mode 100644 index 0000000000000000000000000000000000000000..aeb5e2ec68749015d56020ae50a50e2d443f81dd GIT binary patch literal 22 dcmb0LZb+Xb6mmGdx45_~`9OQ}sTL0g1^{W92^9bU literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d3adf09fe6fb1534157c1dc68eb61365b46b1c35 b/webm_parser/fuzzing/corpus/d3adf09fe6fb1534157c1dc68eb61365b46b1c35 new file mode 100644 index 000000000000..ad8336b995c9 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d3adf09fe6fb1534157c1dc68eb61365b46b1c35 @@ -0,0 +1 @@ +Eߣ’’pàÓ’ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d3c74d3a64dba86f98a31bb726587ec97a7e4531 b/webm_parser/fuzzing/corpus/d3c74d3a64dba86f98a31bb726587ec97a7e4531 new file mode 100644 index 000000000000..c2ce861d5949 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d3c74d3a64dba86f98a31bb726587ec97a7e4531 @@ -0,0 +1 @@ +€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€–ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€D¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ;€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶;€€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€IIIIÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€timeout_exitcode¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ²€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ;€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€timeout_exit¶ÿ¶€¶ÿ¶;€€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ²€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ€¶¶€€¶ÿ€¶€¶ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d3c9846ab319f12fc646c23a532c780daa9e993f b/webm_parser/fuzzing/corpus/d3c9846ab319f12fc646c23a532c780daa9e993f new file mode 100644 index 000000000000..69860029fb1e --- /dev/null +++ b/webm_parser/fuzzing/corpus/d3c9846ab319f12fc646c23a532c780daa9e993f @@ -0,0 +1 @@ +S€g™T®k”®’m€b@ŒP1€P2€P3€P5€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d3f03301b52cf4a830c7dd200ed8ccbc09e6ec94 b/webm_parser/fuzzing/corpus/d3f03301b52cf4a830c7dd200ed8ccbc09e6ec94 new file mode 100644 index 0000000000000000000000000000000000000000..3910637501cd5884b2e5e75ca12f0efa6d728c70 GIT binary patch literal 18 Zcmb1gJ-@i!N#H*NQ?Nv9gQw`~1^_>j2BiQ1 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d4800745440dace38766db3520ffe7baa0bd78f2 b/webm_parser/fuzzing/corpus/d4800745440dace38766db3520ffe7baa0bd78f2 new file mode 100644 index 0000000000000000000000000000000000000000..78d6b02b53cd448daf5cbf9d0cf9a12fc1471670 GIT binary patch literal 4 LcmdnI>plYj2D}1I literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d4d6271bba704ba08c2678eb8b1bc4e457144f50 b/webm_parser/fuzzing/corpus/d4d6271bba704ba08c2678eb8b1bc4e457144f50 new file mode 100644 index 0000000000000000000000000000000000000000..5715e682c795a47f2f488480a82a332164beb323 GIT binary patch literal 20 acmZ>+<9wZbl!1X^bv*+M149OoXaE2|r3Cl@ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d54020f766061e80f445690c2b5694ed8af21e9d b/webm_parser/fuzzing/corpus/d54020f766061e80f445690c2b5694ed8af21e9d new file mode 100644 index 000000000000..d0e8288a9f52 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d54020f766061e80f445690c2b5694ed8af21e9d @@ -0,0 +1 @@ +G†\ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d588515f125ebe968fe6a81cb6df7cfc41969e68 b/webm_parser/fuzzing/corpus/d588515f125ebe968fe6a81cb6df7cfc41969e68 new file mode 100644 index 000000000000..4b30c90c8797 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d588515f125ebe968fe6a81cb6df7cfc41969e68 @@ -0,0 +1 @@ +EߣÿEߣ2 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d5ee5e4dc8fad9f1da43102d8322beb005a047c0 b/webm_parser/fuzzing/corpus/d5ee5e4dc8fad9f1da43102d8322beb005a047c0 new file mode 100644 index 000000000000..d51648b2a91e --- /dev/null +++ b/webm_parser/fuzzing/corpus/d5ee5e4dc8fad9f1da43102d8322beb005a047c0 @@ -0,0 +1 @@ +Eß¾ˆB‚@webmS€g•C¶uÿÿÿÿ«@ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d62459bb217d3050bcd9e29a6327cf81f5ed68b9 b/webm_parser/fuzzing/corpus/d62459bb217d3050bcd9e29a6327cf81f5ed68b9 new file mode 100644 index 000000000000..c4ec3c43e194 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d62459bb217d3050bcd9e29a6327cf81f5ed68b9 @@ -0,0 +1 @@ +€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ‚€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ‚€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€³ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶„¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€wÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶¶€¶ÿ¶€m¶ÿ¶€¶ÿ¶¶®®Ý€¶ÿ¶€¶ÿ¶€¶€µ¶€¶Œÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€å¶€¶€¶ÿ¶€Eߣ¶€¶À¶ÿ¶€m¶ÿ¶€¶ÿ¶¶®®Ý€¶ÿ¶€¶ÿ¶€¶€µ¶€¶Œÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d6acbf1cb46845618ed0d5a322c8bd4879d16422 b/webm_parser/fuzzing/corpus/d6acbf1cb46845618ed0d5a322c8bd4879d16422 new file mode 100644 index 000000000000..479a8773e7d4 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d6acbf1cb46845618ed0d5a322c8bd4879d16422 @@ -0,0 +1 @@ +S€g»TÃg¶ss³gÈ©È„#‡dgÈ„]‡egÈ„D*dgÈ„]‡egÈ„D‡gÈ€D‡dgÈg \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d6fc8cdbb0f1517159531098e900e2dcc91edba0 b/webm_parser/fuzzing/corpus/d6fc8cdbb0f1517159531098e900e2dcc91edba0 new file mode 100644 index 000000000000..d7384b79dcdb --- /dev/null +++ b/webm_parser/fuzzing/corpus/d6fc8cdbb0f1517159531098e900e2dcc91edba0 @@ -0,0 +1 @@ +S€g‡S»k‚»€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d7a9bbd9875a60edf9c528b298ea72a2fad7d3d7 b/webm_parser/fuzzing/corpus/d7a9bbd9875a60edf9c528b298ea72a2fad7d3d7 new file mode 100644 index 0000000000000000000000000000000000000000..e2c81b6263c26dbd886ff3d34f6da1320200f7d5 GIT binary patch literal 59 qcmb0LZb;uC@4T&a^@5cPTbmewU{OmmjDtn2bkUOkeGbeo92fxZ#u-`w literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d82e70046a544e95e81f6271dd2695f2599f0574 b/webm_parser/fuzzing/corpus/d82e70046a544e95e81f6271dd2695f2599f0574 new file mode 100644 index 000000000000..a2fe29ed63c2 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d82e70046a544e95e81f6271dd2695f2599f0574 @@ -0,0 +1 @@ +S€gŽTÃg‰ss†cÀ€gÈ€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d84bc8dca7c8fcd227254c06f5b88eaaf7cd5fde b/webm_parser/fuzzing/corpus/d84bc8dca7c8fcd227254c06f5b88eaaf7cd5fde new file mode 100644 index 000000000000..f5d0f36ac129 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d84bc8dca7c8fcd227254c06f5b88eaaf7cd5fde @@ -0,0 +1 @@ +è \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d8bcb7dd21205e7126e700323b1d58817e9d9a6d b/webm_parser/fuzzing/corpus/d8bcb7dd21205e7126e700323b1d58817e9d9a6d new file mode 100644 index 0000000000000000000000000000000000000000..b32e7048f1954efc84213f63893709ec9b630467 GIT binary patch literal 39 pcmb0LZb)A&@4T&a-hw#`TN@dGU{MQ*DqYyn=fLpWfvFTk0{|wq4i5kT literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d8e2628b4092b9bbab4f02041647f950503eeb48 b/webm_parser/fuzzing/corpus/d8e2628b4092b9bbab4f02041647f950503eeb48 new file mode 100644 index 0000000000000000000000000000000000000000..6aa71bd89d56f1ea903f3c0a41d6907b6d3dbf15 GIT binary patch literal 33 pcmb0LZb+Xa;JmzGy6es<+a?tsX$T8xnCQST$$_E4fnl2i0|4#K4FmuH literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d8ee4c2b79863a237c432efe7d43d99c8d0afb9b b/webm_parser/fuzzing/corpus/d8ee4c2b79863a237c432efe7d43d99c8d0afb9b new file mode 100644 index 0000000000000000000000000000000000000000..db8723cceaa75949e972dc0e9df272a916dcd3fa GIT binary patch literal 53 vcmb0LZb;uC@4T&a^@5cPTNgGjb6}Y5z{vO;f|1!wrHdN>_c<`XX!s8RHlGzS literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/d8ee9724bf16ff336387723dcf27319c3be72e01 b/webm_parser/fuzzing/corpus/d8ee9724bf16ff336387723dcf27319c3be72e01 new file mode 100644 index 000000000000..ce72a85b7c07 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d8ee9724bf16ff336387723dcf27319c3be72e01 @@ -0,0 +1 @@ +S€gT®kŠ®ˆà†Tºƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d9379969bc956ad623e4bab8bbe47270a880a62d b/webm_parser/fuzzing/corpus/d9379969bc956ad623e4bab8bbe47270a880a62d new file mode 100644 index 000000000000..40bdf1a53e3f --- /dev/null +++ b/webm_parser/fuzzing/corpus/d9379969bc956ad623e4bab8bbe47270a880a62d @@ -0,0 +1 @@ +S€g‹C¶u† „u¢ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/d9ccb79b0e070dcc2f5ed8e15d99bc5ef86ecff7 b/webm_parser/fuzzing/corpus/d9ccb79b0e070dcc2f5ed8e15d99bc5ef86ecff7 new file mode 100644 index 000000000000..4c2c858832c2 --- /dev/null +++ b/webm_parser/fuzzing/corpus/d9ccb79b0e070dcc2f5ed8e15d99bc5ef86ecff7 @@ -0,0 +1 @@ +EߣˆB‚@webmS€g©C¶u¤ç@«@£…ÿÿÿ @ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/db83586ca6266e03067e9a9772ea728ab770ad9a b/webm_parser/fuzzing/corpus/db83586ca6266e03067e9a9772ea728ab770ad9a new file mode 100644 index 0000000000000000000000000000000000000000..0b49ef89517f704123b3c1577885e871cc69d4dd GIT binary patch literal 25 gcmb0LZb+XZ@4T(Fe{pXk14GMq1_nk(CMITP0Ci>uI{*Lx literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/dcc63c06ed2790d1380bdb9281fe8677f439c76d b/webm_parser/fuzzing/corpus/dcc63c06ed2790d1380bdb9281fe8677f439c76d new file mode 100644 index 0000000000000000000000000000000000000000..c5769be7800f5e5cc5fb1d77053056f5d9b23e0c GIT binary patch literal 53 xcmb0LZb;uC@4T&a^@5cPTbmewU{OmmkStx;yvzZ?p60+X+kuhs9T5Lk000s=5|jV{ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/dd509e9a4660ec34b8f9dc23441c6df4ff97c34d b/webm_parser/fuzzing/corpus/dd509e9a4660ec34b8f9dc23441c6df4ff97c34d new file mode 100644 index 000000000000..2a77375b70cf --- /dev/null +++ b/webm_parser/fuzzing/corpus/dd509e9a4660ec34b8f9dc23441c6df4ff97c34d @@ -0,0 +1 @@ +S€g“C§pŽE¹„E¼E¹„E¼ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/dd72f8ff3a067dc7871438b6023e1ed0a4c5787b b/webm_parser/fuzzing/corpus/dd72f8ff3a067dc7871438b6023e1ed0a4c5787b new file mode 100644 index 000000000000..dec5b012e936 --- /dev/null +++ b/webm_parser/fuzzing/corpus/dd72f8ff3a067dc7871438b6023e1ed0a4c5787b @@ -0,0 +1 @@ +S€gŽ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/dd75880c5ad488885260f4031a763c86a8084406 b/webm_parser/fuzzing/corpus/dd75880c5ad488885260f4031a763c86a8084406 new file mode 100644 index 0000000000000000000000000000000000000000..ad235541cb0db474960b55f717cd274512861346 GIT binary patch literal 111 zcmb0LZb;uF;JmzGyX(#^+cq_9XmyZiV9iL)$;o%FX_WxVE|zy@+*Ueo!JLJyjSN7r zs0BooE^L_Xz>t%emu}!(*P2-LJJr#^*@2;SQA15DP_)neQMv;|x*^aEkQyVfn$$!? E0L11ifB*mh literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/dd962d74d04aa4aed270fd8e6b0ae9c4ac35fd19 b/webm_parser/fuzzing/corpus/dd962d74d04aa4aed270fd8e6b0ae9c4ac35fd19 new file mode 100644 index 000000000000..dcb3af455b37 --- /dev/null +++ b/webm_parser/fuzzing/corpus/dd962d74d04aa4aed270fd8e6b0ae9c4ac35fd19 @@ -0,0 +1 @@ +€€¶€€€¶€¶€¶ÿ6€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€ÿ¶¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€ÿ¶¶€¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€Óÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿö€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ–€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿJIII|¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€³ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€;€¶ÿ¶€€€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶ÿ¶€€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€¶€¶ÿ¶€¶ÿ¶€¶€wÿ¶ÿ¶€¶ÿ€€¶€¶€¶ÿ¶€¶€¶ÿ¶€¶ÿ¶€¶€¶ÿ¶€€€¶€¶€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/dd9aa9a49dd790b2ce99c5af1933232d46a7f80d b/webm_parser/fuzzing/corpus/dd9aa9a49dd790b2ce99c5af1933232d46a7f80d new file mode 100644 index 000000000000..28fd4f5fe5d7 --- /dev/null +++ b/webm_parser/fuzzing/corpus/dd9aa9a49dd790b2ce99c5af1933232d46a7f80d @@ -0,0 +1 @@ +S€gŒI©f‡D‰„@ÉÛ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/ddad7630818a1caa8054d2d7280a1d01bdb33ca3 b/webm_parser/fuzzing/corpus/ddad7630818a1caa8054d2d7280a1d01bdb33ca3 new file mode 100644 index 0000000000000000000000000000000000000000..5fdbe5b3e5897b00919553724d12d92bcda59647 GIT binary patch literal 18 acmWH!yV1yc>#Z!|HWSnYbIML#|czZ)*;)xcQs>UP$ D>#h}u literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/de0ff884898c83fb880498f1b8328f78701e6534 b/webm_parser/fuzzing/corpus/de0ff884898c83fb880498f1b8328f78701e6534 new file mode 100644 index 000000000000..b9f4c9d25963 --- /dev/null +++ b/webm_parser/fuzzing/corpus/de0ff884898c83fb880498f1b8328f78701e6534 @@ -0,0 +1 @@ +¦€Ý€ç¦€¦€Ý€ç¦€ÝuÝu \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/de9e0ed8ae29220e5b65e5b97eb3b254ccbe7e0c b/webm_parser/fuzzing/corpus/de9e0ed8ae29220e5b65e5b97eb3b254ccbe7e0c new file mode 100644 index 0000000000000000000000000000000000000000..d48be42d1366c674dc4c5fd94cf9e9ed723d452a GIT binary patch literal 45106 zcmeHQJB}np5akG%xL8OW0T-Wqwv z%!;hZnGUsfx@Wrc_u|EiPu0wR_T9%HZl8Yr`_H!zf8Sp2kKgXE=x4v)zx#LpF?#dk z_U7#c!y5)KbN@%f{#^bobMp-H_+@2}ZRYDK=Q_L8=gWb3DLNCM=$lWzrN;{AFF*eD zeHUVYJGWM{&;XEhqdbezzao1s|M*dHgUgH#=70=2sdanpoX9?`$IRH=o~cPN4pzT| z&5-XDD)|&F(qJ2l)(GeQ$HD;GPUm?OzXmvQ13YzX!8+)f8T(mL(OpI003D=&(q|Tc zNzW%eC++cCCcUwknQ%T(qCt4(mbUL8SPAdQF)N%hxs#EVXVYKb-K^QL6i6;hF#w>1 zHWcp4ia^WO{A!G#3EN!a=E?#;s6nWzFQoOFGm?wl2uB&`8b z8ZFqU`0D5{Ehjb@nsax+Ssfsm_kOrVtLV|CB~Zh;=-*k<5DEp9AO2d=apcp?m=(td zOI~iqyx|lFtAkh5tWrv^fS}zkt$vS4zSl$!OD$pMpj1SfaShLr=xLz5NXo4^+VbH5Fw@Kf6IWAoM;@( z**I9b0u2S6kW$DIQgNJm-wdvb!I901q80ahwB-#|(n{IvBL}O4Q>dUlv-Parr4a(dP*tf%0U^6w>JwC|OYy zM$aWSwA~2Eo9$&EGNd(UuEnfjecM0;IfA-*dRlViJRW`3WZ7aVb8bI6uqn+St3I}qc1;G+lH+W_mVLVa>!G~wUfX*{fT?Yc5ryg@!2$P+1Szt z(%2y-rn}>~roo=r9NdRehi*s#h{UozN7-G_J)PQ+tq)O7IT!IXs$$s`IuY$l}M7p7yV4;T<9}lQ$QJ>u%JSZ5&FzC3#j7_ zdfuyC`IF9!j3x?eucj4*#%OJm?kN}6jVfl`jc|^q%nf!P@f3F>v0g-5i*b+xeAPQ> zP-lU)$p3Lc+XAa3*w#q5k!Lori~9jR+VUiF2f&aiY|9Qg3b zXO`z-rM0{xL<)JCp80IYV&9LsR)ogIy7`0rGi46+PMI*Y`|$3n9xqo;%RjG}jNa_f zjHnvB(e8}JJ2pktxPwDUB~lKQv;1(tk*kvREz+qNRXiE9xx?^v+|S)yj$6^%(WNW)9Yf}vaA-wE~gDr_q} z*Jl>sLeFrh88Iu4pEmwxgbP~+&UOXz}brz%pvVWY5TO(SPt(6F$@H#6s}PZ@!lh%iOj5Y3duOe46~jGv4s;{?@k7E z*@*&M3O5R)MS_AviAJg~sBg|CHgqxxpu6n`NJdyN5nE%q#6w_#s~XxJ#^J^Xg}=;cb3D^StF?WAoQIxIdQK9>0pUQQ-W!XV3Fq8XbZZ_YN(p&`XXci+@4(6@ z{T$X=o`;puIAB}1MC)iAM&wY5;hLYTD@pjJ0kl#2gycdc!=<_O?4#Efgx_NU^M!jJUMP_)kNOcK iO_OMmC{}>%DfeFrH&isX5msN#xOUObLFOxe8S;O4yVZ~9R&aY literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/e01962d7dc1b94e5c4424ec7adae16a7c03b9773 b/webm_parser/fuzzing/corpus/e01962d7dc1b94e5c4424ec7adae16a7c03b9773 new file mode 100644 index 000000000000..8d7f32a34129 --- /dev/null +++ b/webm_parser/fuzzing/corpus/e01962d7dc1b94e5c4424ec7adae16a7c03b9773 @@ -0,0 +1 @@ +€€X \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e058723f2964bf1405ae043ddb99efb17d821e15 b/webm_parser/fuzzing/corpus/e058723f2964bf1405ae043ddb99efb17d821e15 new file mode 100644 index 000000000000..a2946136ac0a --- /dev/null +++ b/webm_parser/fuzzing/corpus/e058723f2964bf1405ae043ddb99efb17d821e15 @@ -0,0 +1 @@ +S€gC§pŠE¹‡¶…‘ƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e09e306ef596dcd0e44bd3ef3c5f7019c7343f84 b/webm_parser/fuzzing/corpus/e09e306ef596dcd0e44bd3ef3c5f7019c7343f84 new file mode 100644 index 000000000000..1abbf627f698 --- /dev/null +++ b/webm_parser/fuzzing/corpus/e09e306ef596dcd0e44bd3ef3c5f7019c7343f84 @@ -0,0 +1 @@ +S€gT®k‹®‰á‡xµ„Û \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e154eb76d096c1e545dcad591a58a02c37cd71ff b/webm_parser/fuzzing/corpus/e154eb76d096c1e545dcad591a58a02c37cd71ff new file mode 100644 index 000000000000..f575e8bfe468 --- /dev/null +++ b/webm_parser/fuzzing/corpus/e154eb76d096c1e545dcad591a58a02c37cd71ff @@ -0,0 +1 @@ +!EߣˆB‚@webmS€g£*M'›t€I©f€C¶u€TSkk»€®€C§p€TÃg€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e198d2e4f2f3528c2ff46d769a9281ebb3cfb44a b/webm_parser/fuzzing/corpus/e198d2e4f2f3528c2ff46d769a9281ebb3cfb44a new file mode 100644 index 000000000000..75c0df8fe21c --- /dev/null +++ b/webm_parser/fuzzing/corpus/e198d2e4f2f3528c2ff46d769a9281ebb3cfb44a @@ -0,0 +1 @@ +S€g”T®k®m€Šb@‡P5„Gâ!S€g”T®k®m€Šb@‡P5„Gâ!S€g›T®k–®”m€‘S€g›T®k–®”m€‘ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e2068af1e903f4a81cd6fa5f4022e62070c259ec b/webm_parser/fuzzing/corpus/e2068af1e903f4a81cd6fa5f4022e62070c259ec new file mode 100644 index 0000000000000000000000000000000000000000..8ceb55924f014418eb8bd28cc64f4b9ed0bd1fe4 GIT binary patch literal 39 vcmb1gy}!7_smXz*JT)m-BDf)as=V{I(h1KU7*;zlEN*RNc=MlWfdc~oH%||# literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/e2890330b5655cb277a581b8dd2eeba0d9061ba5 b/webm_parser/fuzzing/corpus/e2890330b5655cb277a581b8dd2eeba0d9061ba5 new file mode 100644 index 000000000000..ffdb9c04b902 --- /dev/null +++ b/webm_parser/fuzzing/corpus/e2890330b5655cb277a581b8dd2eeba0d9061ba5 @@ -0,0 +1 @@ +S€g”T®k®m€Šb@‡P5„Gá \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e2e0767d055a7042c24a7acd5d5b6b7c093ad065 b/webm_parser/fuzzing/corpus/e2e0767d055a7042c24a7acd5d5b6b7c093ad065 new file mode 100644 index 000000000000..145b54c473aa --- /dev/null +++ b/webm_parser/fuzzing/corpus/e2e0767d055a7042c24a7acd5d5b6b7c093ad065 @@ -0,0 +1 @@ +S€gŽT®k‰®‡à…ºƒ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e394a8e21e2c43c42135daa034ad5aabb06acffb b/webm_parser/fuzzing/corpus/e394a8e21e2c43c42135daa034ad5aabb06acffb new file mode 100644 index 0000000000000000000000000000000000000000..3bfae2e67aa80ab8d4e2c601ba0fa78cbb791722 GIT binary patch literal 145 zcmb1gy}!7_smXz*JT)m-BDf*l;h3QB?2=yJ-2w~@?7?e*BqJk+2$QJi%Crux>l?w+ x$Q&kl=b5E#&q3l+K%wE&uH4H0A#3t88g-MtQ|fbBY1H&6hX4*+w+DPsTt literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/e3b200e97ec226a197e91f12103aaa53d5c37b0e b/webm_parser/fuzzing/corpus/e3b200e97ec226a197e91f12103aaa53d5c37b0e new file mode 100644 index 000000000000..a2f27b71f83b --- /dev/null +++ b/webm_parser/fuzzing/corpus/e3b200e97ec226a197e91f12103aaa53d5c37b0e @@ -0,0 +1 @@ +S€g½C§p¸E¹µ¶³sÄV…@¦¶@sä¶@sĶ@sĶ@sĶ@sĶ@sÄ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e45b077cda64c380ae1b0910bc81d010c27e1c93 b/webm_parser/fuzzing/corpus/e45b077cda64c380ae1b0910bc81d010c27e1c93 new file mode 100644 index 000000000000..e05b783445bb --- /dev/null +++ b/webm_parser/fuzzing/corpus/e45b077cda64c380ae1b0910bc81d010c27e1c93 @@ -0,0 +1 @@ +Sx€g%T:kŒ®Šm€‡b@„1 \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e45e71ca01ebe01b01b0ca99b8f20a756c36b967 b/webm_parser/fuzzing/corpus/e45e71ca01ebe01b01b0ca99b8f20a756c36b967 new file mode 100644 index 000000000000..3d1f9b0fd39e --- /dev/null +++ b/webm_parser/fuzzing/corpus/e45e71ca01ebe01b01b0ca99b8f20a756c36b967 @@ -0,0 +1 @@ +$ö€TÃg€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e4ff4bd938c3737a02862fc0656a1859d384d066 b/webm_parser/fuzzing/corpus/e4ff4bd938c3737a02862fc0656a1859d384d066 new file mode 100644 index 000000000000..635b8234da7b --- /dev/null +++ b/webm_parser/fuzzing/corpus/e4ff4bd938c3737a02862fc0656a1859d384d066 @@ -0,0 +1 @@ +S€gC§pˆE¹…¶ƒ’ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e5a1acb7e6f71bc1a2fedaa6173764dfd04c844c b/webm_parser/fuzzing/corpus/e5a1acb7e6f71bc1a2fedaa6173764dfd04c844c new file mode 100644 index 000000000000..ca4a19c166b5 --- /dev/null +++ b/webm_parser/fuzzing/corpus/e5a1acb7e6f71bc1a2fedaa6173764dfd04c844c @@ -0,0 +1 @@ +S€g–T®k‘®m€Œb@‰P5†GçƒGè€S€gg—T®kÀ®m€b@ŠP5‡Gâ„;G \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e6081eeff4ddb76e88e87ef9e4b5f199f5f11c28 b/webm_parser/fuzzing/corpus/e6081eeff4ddb76e88e87ef9e4b5f199f5f11c28 new file mode 100644 index 0000000000000000000000000000000000000000..7b80d1b94193a114f9c97e55056007080c2f1123 GIT binary patch literal 63 zcmb0LZb;uP6mmFyTXFH`^b;Fg7dIxlR5d2Kv^Fxhw6-=Ucd9d{pP1>Q+1LURYD{)% PZ7)8Oexk*ty)hjCZ_pcr literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/e73c42dd266c4d9671da0c7af09e98c02fc052fd b/webm_parser/fuzzing/corpus/e73c42dd266c4d9671da0c7af09e98c02fc052fd new file mode 100644 index 0000000000000000000000000000000000000000..18e6ae2a8a3ee230a64894152cc04768d999e2b2 GIT binary patch literal 86 zcmb0L{+K>N)N^H8x0LJs#T`yf4lL!VE{Prg?(CXT7GfZflq(V3knV6y@a*i8Ufy09KF(qyPW_ literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/e7dd34a80646a8c38ae1ec3a27c1358bab13b360 b/webm_parser/fuzzing/corpus/e7dd34a80646a8c38ae1ec3a27c1358bab13b360 new file mode 100644 index 000000000000..8a7f10e54402 --- /dev/null +++ b/webm_parser/fuzzing/corpus/e7dd34a80646a8c38ae1ec3a27c1358bab13b360 @@ -0,0 +1 @@ +S€gŠT®k…®ƒƒÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e80b344f943ff0ad9219277c4578d3b4100b71dc b/webm_parser/fuzzing/corpus/e80b344f943ff0ad9219277c4578d3b4100b71dc new file mode 100644 index 000000000000..0f27b93a130a --- /dev/null +++ b/webm_parser/fuzzing/corpus/e80b344f943ff0ad9219277c4578d3b4100b71dc @@ -0,0 +1 @@ +Eߣÿø£b \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e86e26200290d9d428c5e98e191ec874eb918fb6 b/webm_parser/fuzzing/corpus/e86e26200290d9d428c5e98e191ec874eb918fb6 new file mode 100644 index 000000000000..dedb463b8d3b --- /dev/null +++ b/webm_parser/fuzzing/corpus/e86e26200290d9d428c5e98e191ec874eb918fb6 @@ -0,0 +1 @@ +S€g‘T®kŒ®Šm€‡b@„P2ÿ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/e873a3b8f5c3b716e6446df34279b837cf8d2c30 b/webm_parser/fuzzing/corpus/e873a3b8f5c3b716e6446df34279b837cf8d2c30 new file mode 100644 index 0000000000000000000000000000000000000000..40a9456476463b9d98ec50d9daddfb054617bd64 GIT binary patch literal 53 zcmb1gl?ZM~U)B9fZ{x9ut GU<3dI{S(Xp literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/e8fb71319db98d8e8cd131a4eb82879bfbec14d0 b/webm_parser/fuzzing/corpus/e8fb71319db98d8e8cd131a4eb82879bfbec14d0 new file mode 100644 index 0000000000000000000000000000000000000000..2bf9e0dee6c869d026ee3a4a84a7ecd98bcd3b39 GIT binary patch literal 123 zcmb0LZb<(o7P2n;l?w+ z$Q&kl=WV5J&%xpdCX-mmy6o~4^iY@5OI0?x||dR=z{+3fXu Yz#>8+htnq)7X#V6$p=8<8K*$v0C_Poe*gdg literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/ef11f14feee00e3c198015e6bc76688e970e2d8c b/webm_parser/fuzzing/corpus/ef11f14feee00e3c198015e6bc76688e970e2d8c new file mode 100644 index 000000000000..414c1b37892b --- /dev/null +++ b/webm_parser/fuzzing/corpus/ef11f14feee00e3c198015e6bc76688e970e2d8c @@ -0,0 +1 @@ +S€g¯€ø \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/efa12e91e0d63c2353c120ca1ded7b36afbf57eb b/webm_parser/fuzzing/corpus/efa12e91e0d63c2353c120ca1ded7b36afbf57eb new file mode 100644 index 0000000000000000000000000000000000000000..1673fc014bb32ef779b186b9182b48db984f9c09 GIT binary patch literal 59 zcmb1gy}!7_smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfuJ}ZDas~#jQ<9)B*>V*({7q E0By+;;{X5v literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/efa14cba9bbaf749067b7bb8515a5d8a289fa389 b/webm_parser/fuzzing/corpus/efa14cba9bbaf749067b7bb8515a5d8a289fa389 new file mode 100644 index 000000000000..7c34b9999308 --- /dev/null +++ b/webm_parser/fuzzing/corpus/efa14cba9bbaf749067b7bb8515a5d8a289fa389 @@ -0,0 +1 @@ +S€g‰I©f„WA! \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/eff9bf13cd33ea50a8eacc5f2839cc4b5d67b2de b/webm_parser/fuzzing/corpus/eff9bf13cd33ea50a8eacc5f2839cc4b5d67b2de new file mode 100644 index 000000000000..5f0ecb9fec3e --- /dev/null +++ b/webm_parser/fuzzing/corpus/eff9bf13cd33ea50a8eacc5f2839cc4b5d67b2de @@ -0,0 +1 @@ +Sg€„IŒë@D‡‰ÉÛ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/f0143756917f0b2e374bd03e731cd64168180054 b/webm_parser/fuzzing/corpus/f0143756917f0b2e374bd03e731cd64168180054 new file mode 100644 index 000000000000..6773d0f9407f --- /dev/null +++ b/webm_parser/fuzzing/corpus/f0143756917f0b2e374bd03e731cd64168180054 @@ -0,0 +1 @@ +S€g‰C¶u„ ‚¢€ \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/f0b9dcd4e1845f774bb0f42653b11040baee0765 b/webm_parser/fuzzing/corpus/f0b9dcd4e1845f774bb0f42653b11040baee0765 new file mode 100644 index 0000000000000000000000000000000000000000..71caf1466ad9ea6986f3a40cd9de87ec5e54f3ec GIT binary patch literal 118 zcmb1gy}!7_smXz*JT)m-BDf*l;h3QB?2=yJ-2w~@?7?e*BqJk+2$QJi%Crux>l?w+ h$Q&kl=WV5J&%xpdCX-mmy6ol?w+ z$Q&kl=WVHN&%xqAW+{ZpBo?wRyL%l_lsmXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfy%eZDIg|1r7qU9T;CObYKsj IC*`^Z01W*Ss{jB1 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/f69d2954da077043c6ae085e2771a702689314d5 b/webm_parser/fuzzing/corpus/f69d2954da077043c6ae085e2771a702689314d5 new file mode 100644 index 0000000000000000000000000000000000000000..8474e60c2fe71ec9abe325e54942299169c6eeee GIT binary patch literal 82 zcmb0LZb;uC@4T&a^@5cPTP37i&o6Fw()iE76fDu&!~g_~TAG1m=|Tyh!k1zp>$0D$ dd-UMJECB`vMyB8$5b6MkYVZ_Y-N3v-008}39Af|g literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/f74e2203adb9c94ba80f7cc3214e3b3040e5675d b/webm_parser/fuzzing/corpus/f74e2203adb9c94ba80f7cc3214e3b3040e5675d new file mode 100644 index 0000000000000000000000000000000000000000..0caab79590748c65879cdaff3693feb4c784c10c GIT binary patch literal 90 zcmb1gy}!7_smXz*JT)y>BDf)arM&aD(k0Iw7#UYPFfuJ}ZDas~#jQ;sYJmgGY@h^$ IBA}WH0AVd0Jpcdz literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/f7906e7dc01323b9d3d6e298e8dc2386c8d152f6 b/webm_parser/fuzzing/corpus/f7906e7dc01323b9d3d6e298e8dc2386c8d152f6 new file mode 100644 index 000000000000..d70e0accd88f --- /dev/null +++ b/webm_parser/fuzzing/corpus/f7906e7dc01323b9d3d6e298e8dc2386c8d152f6 @@ -0,0 +1 @@ +×À€gˆ¶Cƒu« \ No newline at end of file diff --git a/webm_parser/fuzzing/corpus/f825cac511f3dc38a5ecf3aab3691b4c49ca8f0e b/webm_parser/fuzzing/corpus/f825cac511f3dc38a5ecf3aab3691b4c49ca8f0e new file mode 100644 index 0000000000000000000000000000000000000000..8ea43ca7857536a4c131eafd2d4872c9ebaf7981 GIT binary patch literal 59 zcmb1gy}!7_smXz*JT)m-BDf)arM&aD(k0Iw7#UYPFfuJ}ZDas~#jQ;sYJmgGZ=eK( GVgdkj#1i%Z literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/f84c3f9e305172f2ae15dff0b4d955324b3a398e b/webm_parser/fuzzing/corpus/f84c3f9e305172f2ae15dff0b4d955324b3a398e new file mode 100644 index 0000000000000000000000000000000000000000..c1987a9265e781d5c8bf1ac117208c9c9c356761 GIT binary patch literal 19 acmb0LZbeZ`PW$%+`*W2SePv5*eZtjkU?)6Le zKltpX`|aQU*Wl!}d%p)?`j3X=;Ex}D042UI4||ONDUy;G^#w7_XQ@0bKbco{Fao zRQ}l!hTvSkR*oo7sYAZHej*YUu8B%YAd{jJNryv5*)BhH?iEH}SiVp`;P#h+nsn8Z z8Zc<~zVQp8-o+EnHoFRWn}PsXE4SZ4=)l0x9fJcxo~MXHOL?FYOk(Gwl8xKO@fxQO zSDAq|96X=`XajJ}$95`6`!Z3?fV9p4}L+M&8N9hf~6bw%<3i`^#V7!E-jS;V@mK9{Jbhl|rI?Z&IK~-c? zU#dmp7QR{;l9rNQz;pv~+xqO>AAO8lYH8SQDR=GAT^w@|MKR$d6uZ{kLyHasi;oiD zohhWIE?MH7*4*>91z4w47k!5Egp`4aY;v-f~ZHetRVQz8Fo4_(V2ye4M(!g z5<`o?z_d|RaU0qp9V4lI`wX91gRi0cNJiz_5i2eQ1CPbgzP}AC6(`Edsw2?3koDI-57_>$#%tet2W7(|UCoOlLZn$8@sl7d2JN z+@`clX#trg^Mfg^HaS4Scwr?@DbJKv>jO;DRJw;CCQ~u$K#L#ES%7 zduSg(7syuufa`%SaYwju87%r8O!<0NC$t(BTxE4aYYgU&Mn8IGQ0arXj4iBAkP?&C z2@dVV(3Pn>s}m%#1*qrGcbJz6>?|t^{NphcW_e-2dgM*a$`jPgnf^lF6Py5F2>m6` zKV?FLmBhpcZX>MLh_9w!J7*w!e9|7DbhwBEcEue6?MEF35)_3#SsRWfQfje^oKDW< z1#assUFpnHi_Olq?-#BrC}7(BG@9u%zjoSIzxix8^qV}wG#X-BXwuYz?&45E?&~TC z?^!{}Tjv4Ibh#wgz{&fj?q|US(DB?gIDl(ylC@}XpP}e%Sbapo6~20^P3&)@TSR8q zjN>0!158<%o-JJ9J3s)zh+)bHSulW2@;#cVGkAhkjpY12ED@ExKYHY?76opD?uT~o z(wckV_Qv(?u>c=19O5S>{g$vrX{5O&oplhcxo6G22I(=tPFjI2AXAi%oik> zQvtmMgA_?RBu%deS0_evV4^b%8JiLf94kXoea$^Sy8Ae{249m)&Wgym9Eii}jNg$87-@W1DiDDJ?WzdXCrE6h--r;*|#X^#PsrT8L%%ktp!9 zFSdvjmDcKPMqpr^U0Ke&{{xMCoUn3|M(X3d90mT4?sHjPuCIi%q5;4|O`->IC%QK;jAA zOpIl90$OFEOlKWg9@EJRUq363jGRepE6OyCWU4G@mZ)@1O$Dq#o=)cmmHPzA*jD6o zkIFD1@Uh2$D^61@ZqGkhh)kek#}k)56OVKd#&3tvhL?(?Ik`gu*Yj6-3k9Tm<16y; z6r5z241uAp1>VGXevXHcgWyG6XUKgjb;^ zv)(Q6se9u~Amfm9g#qhX4GoEVbBdJheqZ zyPQWqmafUH@&)E_Z+wj>!Njaw0d$FP>*rPSE*|IHkASqwfI~Y`EiMahC4gUPJ#1jj zD-MLE2~M}CmM~m1Yi$@*s1Um>fETvEBMl59VAw13kXZ)@a2gE_?}ItHDL@0^NO`Le z1;N#q+R3##p+%-ZpMZoS3|eCjgNcln9D@r+Wk>~?`Glh>gUI?IGJ%ez2s)9Zy@u@J z_}9Ci{=GfCdiBTl{+b7Six54-yH?Ma%{>F{p?;gY0J&xVu*!}KHm}0+68UK7>j-l%hjsLa3{z&o7 yMIhqOGkh{h(Lgw=3x)6ezY&ZBOY4*rU;JfDlq^6 literal 0 HcmV?d00001 diff --git a/webm_parser/fuzzing/corpus/ffff6a92363e0e55a9688d9bc025cd8dea3b50d4 b/webm_parser/fuzzing/corpus/ffff6a92363e0e55a9688d9bc025cd8dea3b50d4 new file mode 100644 index 000000000000..975d9b36e2af --- /dev/null +++ b/webm_parser/fuzzing/corpus/ffff6a92363e0e55a9688d9bc025cd8dea3b50d4 @@ -0,0 +1 @@ +S€gTÃgŠss‡gÈ„D…! \ No newline at end of file diff --git a/webm_parser/fuzzing/webm.dict b/webm_parser/fuzzing/webm.dict new file mode 100644 index 000000000000..7660ce80ec81 --- /dev/null +++ b/webm_parser/fuzzing/webm.dict @@ -0,0 +1,152 @@ +# Element IDs. +IdEbml = "\x1A\x45\xDF\xA3" +IdEbmlVersion = "\x42\x86" +IdEbmlReadVersion = "\x42\xF7" +IdEbmlMaxIdLength = "\x42\xF2" +IdEbmlMaxSizeLength = "\x42\xF3" +IdDocType = "\x42\x82" +IdDocTypeVersion = "\x42\x87" +IdDocTypeReadVersion = "\x42\x85" +IdVoid = "\xEC" +IdSegment = "\x18\x53\x80\x67" +IdSeekHead = "\x11\x4D\x9B\x74" +IdSeek = "\x4D\xBB" +IdSeekId = "\x53\xAB" +IdSeekPosition = "\x53\xAC" +IdInfo = "\x15\x49\xA9\x66" +IdTimecodeScale = "\x2A\xD7\xB1" +IdDuration = "\x44\x89" +IdDateUtc = "\x44\x61" +IdTitle = "\x7B\xA9" +IdMuxingApp = "\x4D\x80" +IdWritingApp = "\x57\x41" +IdCluster = "\x1F\x43\xB6\x75" +IdTimecode = "\xE7" +IdPrevSize = "\xAB" +IdSimpleBlock = "\xA3" +IdBlockGroup = "\xA0" +IdBlock = "\xA1" +IdBlockVirtual = "\xA2" +IdBlockAdditions = "\x75\xA1" +IdBlockMore = "\xA6" +IdBlockAddId = "\xEE" +IdBlockAdditional = "\xA5" +IdBlockDuration = "\x9B" +IdReferenceBlock = "\xFB" +IdDiscardPadding = "\x75\xA2" +IdSlices = "\x8E" +IdTimeSlice = "\xE8" +IdLaceNumber = "\xCC" +IdTracks = "\x16\x54\xAE\x6B" +IdTrackEntry = "\xAE" +IdTrackNumber = "\xD7" +IdTrackUid = "\x73\xC5" +IdTrackType = "\x83" +IdFlagEnabled = "\xB9" +IdFlagDefault = "\x88" +IdFlagForced = "\x55\xAA" +IdFlagLacing = "\x9C" +IdDefaultDuration = "\x23\xE3\x83" +IdName = "\x53\x6E" +IdLanguage = "\x22\xB5\x9C" +IdCodecId = "\x86" +IdCodecPrivate = "\x63\xA2" +IdCodecName = "\x25\x86\x88" +IdCodecDelay = "\x56\xAA" +IdSeekPreRoll = "\x56\xBB" +IdVideo = "\xE0" +IdFlagInterlaced = "\x9A" +IdStereoMode = "\x53\xB8" +IdAlphaMode = "\x53\xC0" +IdPixelWidth = "\xB0" +IdPixelHeight = "\xBA" +IdPixelCropBottom = "\x54\xAA" +IdPixelCropTop = "\x54\xBB" +IdPixelCropLeft = "\x54\xCC" +IdPixelCropRight = "\x54\xDD" +IdDisplayWidth = "\x54\xB0" +IdDisplayHeight = "\x54\xBA" +IdDisplayUnit = "\x54\xB2" +IdAspectRatioType = "\x54\xB3" +IdFrameRate = "\x23\x83\xE3" +IdColour = "\x55\xB0" +IdMatrixCoefficients = "\x55\xB1" +IdBitsPerChannel = "\x55\xB2" +IdChromaSubsamplingHorz = "\x55\xB3" +IdChromaSubsamplingVert = "\x55\xB4" +IdCbSubsamplingHorz = "\x55\xB5" +IdCbSubsamplingVert = "\x55\xB6" +IdChromaSitingHorz = "\x55\xB7" +IdChromaSitingVert = "\x55\xB8" +IdRange = "\x55\xB9" +IdTransferCharacteristics = "\x55\xBA" +IdPrimaries = "\x55\xBB" +IdMaxCll = "\x55\xBC" +IdMaxFall = "\x55\xBD" +IdMasteringMetadata = "\x55\xD0" +IdPrimaryRChromaticityX = "\x55\xD1" +IdPrimaryRChromaticityY = "\x55\xD2" +IdPrimaryGChromaticityX = "\x55\xD3" +IdPrimaryGChromaticityY = "\x55\xD4" +IdPrimaryBChromaticityX = "\x55\xD5" +IdPrimaryBChromaticityY = "\x55\xD6" +IdWhitePointChromaticityX = "\x55\xD7" +IdWhitePointChromaticityY = "\x55\xD8" +IdLuminanceMax = "\x55\xD9" +IdLuminanceMin = "\x55\xDA" +IdProjection = "\x76\x70" +IdProjectionType = "\x76\x71" +IdProjectionPrivate = "\x76\x72" +IdProjectionPoseYaw = "\x76\x73" +IdProjectionPosePitch = "\x76\x74" +IdProjectionPoseRoll = "\x76\x75" +IdAudio = "\xE1" +IdSamplingFrequency = "\xB5" +IdOutputSamplingFrequency = "\x78\xB5" +IdChannels = "\x9F" +IdBitDepth = "\x62\x64" +IdContentEncodings = "\x6D\x80" +IdContentEncoding = "\x62\x40" +IdContentEncodingOrder = "\x50\x31" +IdContentEncodingScope = "\x50\x32" +IdContentEncodingType = "\x50\x33" +IdContentEncryption = "\x50\x35" +IdContentEncAlgo = "\x47\xE1" +IdContentEncKeyId = "\x47\xE2" +IdContentEncAesSettings = "\x47\xE7" +IdAesSettingsCipherMode = "\x47\xE8" +IdCues = "\x1C\x53\xBB\x6B" +IdCuePoint = "\xBB" +IdCueTime = "\xB3" +IdCueTrackPositions = "\xB7" +IdCueTrack = "\xF7" +IdCueClusterPosition = "\xF1" +IdCueRelativePosition = "\xF0" +IdCueDuration = "\xB2" +IdCueBlockNumber = "\x53\x78" +IdChapters = "\x10\x43\xA7\x70" +IdEditionEntry = "\x45\xB9" +IdChapterAtom = "\xB6" +IdChapterUid = "\x73\xC4" +IdChapterStringUid = "\x56\x54" +IdChapterTimeStart = "\x91" +IdChapterTimeEnd = "\x92" +IdChapterDisplay = "\x80" +IdChapString = "\x85" +IdChapLanguage = "\x43\x7C" +IdChapCountry = "\x43\x7E" +IdTags = "\x12\x54\xC3\x67" +IdTag = "\x73\x73" +IdTargets = "\x63\xC0" +IdTargetTypeValue = "\x68\xCA" +IdTargetType = "\x63\xCA" +IdTagTrackUid = "\x63\xC5" +IdSimpleTag = "\x67\xC8" +IdTagName = "\x45\xA3" +IdTagLanguage = "\x44\x7A" +IdTagDefault = "\x44\x84" +IdTagString = "\x44\x87" +IdTagBinary = "\x44\x85" + +# Interesting sizes. +SizeUnknown = "\xFF" diff --git a/webm_parser/fuzzing/webm_fuzzer.cc b/webm_parser/fuzzing/webm_fuzzer.cc new file mode 100644 index 000000000000..bfc4f6271a63 --- /dev/null +++ b/webm_parser/fuzzing/webm_fuzzer.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include +#include +#include +#include +#include +#include +#include + +#include "webm/buffer_reader.h" +#include "webm/callback.h" +#include "webm/file_reader.h" +#include "webm/reader.h" +#include "webm/status.h" +#include "webm/webm_parser.h" + +using webm::BufferReader; +using webm::Callback; +using webm::FileReader; +using webm::Reader; +using webm::Status; +using webm::WebmParser; + +static int Run(Reader* reader) { + Callback callback; + WebmParser parser; + +#if WEBM_FUZZER_SEEK_FIRST + parser.DidSeek(); +#endif + + Status status(-1); + try { + status = parser.Feed(&callback, reader); + } catch (std::bad_alloc&) { + // Failed allocations are okay. MSan doesn't throw std::bad_alloc, but + // someday it might. + } + + // BufferReader/FileReader should never return either of the following codes, + // which means the parser never should too: + assert(status.code != Status::kWouldBlock); + assert(status.code != Status::kOkPartial); + + // Only the following ranges have status codes defined: + assert((-1031 <= status.code && status.code <= -1025) || + (-3 <= status.code && status.code <= 0)); + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, + std::size_t size) { + BufferReader reader(std::vector(data, data + size)); + return Run(&reader); +} + +#if __AFL_COMPILER +int main(int argc, char* argv[]) { + FILE* file = (argc == 2) ? std::fopen(argv[1], "rb") + : std::freopen(nullptr, "rb", stdin); + if (!file) { + std::cerr << "File cannot be opened\n"; + return EXIT_FAILURE; + } + + FileReader reader(file); + return Run(&reader); +} +#endif diff --git a/webm_parser/include/webm/buffer_reader.h b/webm_parser/include/webm/buffer_reader.h new file mode 100644 index 000000000000..d479b5cb4406 --- /dev/null +++ b/webm_parser/include/webm/buffer_reader.h @@ -0,0 +1,138 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef INCLUDE_WEBM_BUFFER_READER_H_ +#define INCLUDE_WEBM_BUFFER_READER_H_ + +#include +#include +#include +#include + +#include "./reader.h" +#include "./status.h" + +/** + \file + A `Reader` implementation that reads from a `std::vector`. + */ + +namespace webm { + +/** + \addtogroup PUBLIC_API + @{ + */ + +/** + A simple reader that reads data from a buffer of bytes. + */ +class BufferReader : public Reader { + public: + /** + Constructs a new, empty reader. + */ + BufferReader() = default; + + /** + Constructs a new reader by copying the provided reader into the new reader. + + \param other The source reader to copy. + */ + BufferReader(const BufferReader& other) = default; + + /** + Copies the provided reader into this reader. + + \param other The source reader to copy. May be equal to `*this`, in which + case this is a no-op. + \return `*this`. + */ + BufferReader& operator=(const BufferReader& other) = default; + + /** + Constructs a new reader by moving the provided reader into the new reader. + + \param other The source reader to move. After moving, it will be reset to an + empty stream. + */ + BufferReader(BufferReader&&); + + /** + Moves the provided reader into this reader. + + \param other The source reader to move. After moving, it will be reset to an + empty stream. May be equal to `*this`, in which case this is a no-op. + \return `*this`. + */ + BufferReader& operator=(BufferReader&&); + + /** + Creates a new `BufferReader` populated with the provided bytes. + + \param bytes Bytes that are assigned to the internal buffer and used as the + source which is read from. + */ + BufferReader(std::initializer_list bytes); + + /** + Creates a new `BufferReader` populated with the provided data. + + \param vector A vector of bytes that is copied to the internal buffer and + used as the source which is read from. + */ + explicit BufferReader(const std::vector& vector); + + /** + Creates a new `BufferReader` populated with the provided data. + + \param vector A vector of bytes that is moved to the internal buffer and used + as the source which is read from. + */ + explicit BufferReader(std::vector&& vector); + + /** + Resets the reader to read from the given list of bytes, starting at the + beginning. + + This makes `reader = {1, 2, 3};` effectively equivalent to `reader = + BufferReader({1, 2, 3});`. + + \param bytes Bytes that are assigned to the internal buffer and used as the + source which is read from. + \return `*this`. + */ + BufferReader& operator=(std::initializer_list bytes); + + Status Read(std::size_t num_to_read, std::uint8_t* buffer, + std::uint64_t* num_actually_read) override; + + Status Skip(std::uint64_t num_to_skip, + std::uint64_t* num_actually_skipped) override; + + std::uint64_t Position() const override; + + /** + Gets the total size of the buffer. + */ + std::size_t size() const { return data_.size(); } + + private: + // Stores the byte buffer from which data is read. + std::vector data_; + + // The position of the reader in the byte buffer. + std::size_t pos_ = 0; +}; + +/** + @} + */ + +} // namespace webm + +#endif // INCLUDE_WEBM_BUFFER_READER_H_ diff --git a/webm_parser/include/webm/callback.h b/webm_parser/include/webm/callback.h new file mode 100644 index 000000000000..f70f1f89b222 --- /dev/null +++ b/webm_parser/include/webm/callback.h @@ -0,0 +1,363 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef INCLUDE_WEBM_CALLBACK_H_ +#define INCLUDE_WEBM_CALLBACK_H_ + +#include + +#include "./dom_types.h" +#include "./reader.h" +#include "./status.h" + +/** + \file + The main callback type that receives parsing events. + */ + +namespace webm { + +/** + \addtogroup PUBLIC_API + @{ + */ + +/** + The action to be performed when parsing an element. + */ +enum class Action { + /** + Read and parse the element. + */ + kRead, + + /** + Skip the element. Skipped elements are not parsed or stored, and the callback + is not given any further notifications regarding the element. + */ + kSkip, +}; + +/** + A callback that receives parsing events. + + Every method that returns a `Status` should return `Status::kOkCompleted` when + the method has completed and parsing should continue. Returning any other value + will cause parsing to stop. Parsing may be resumed if the returned status was + not a parsing error (see `Status::is_parsing_error()`). When parsing is + resumed, the same `Callback` method will be called again. + + Methods that take a `Reader` expect the implementation to consume (either via + `Reader::Read()` or `Reader::Skip()`) the specified number of bytes before + returning `Status::kOkCompleted`. Default implementations will call + `Reader::Skip()` to skip the specified number of bytes and the resulting + `Status` will be returned (unless it's `Status::kOkPartial`, in which case + `Reader::Skip()` will be called again to skip more data). + + Throwing an exception from the member functions is permitted, though if the + exception will be caught and parsing resumed, then the reader should not + advance its position (for methods that take a `Reader`) before the exception is + thrown. When parsing is resumed, the same `Callback` method will be called + again. + + Users should derive from this class and override member methods as needed. + */ +class Callback { + public: + virtual ~Callback() = default; + + /** + Called when the parser starts a new element. This is called after the + elements ID and size has been parsed, but before any of its body has been + read (or validated). + + Defaults to `Action::kRead` and returning `Status::kOkCompleted`. + + \param metadata Metadata about the element that has just been encountered. + \param[out] action The action that should be taken when handling this + element. Will not be null. + */ + virtual Status OnElementBegin(const ElementMetadata& metadata, + Action* action); + + /** + Called when the parser encounters an unknown element. + + Defaults to calling (and returning the result of) `Reader::Skip()`. + + \param metadata Metadata about the element. + \param reader The reader that should be used to consume data. Will not be + null. + \param[in,out] bytes_remaining The number of remaining bytes that need to be + consumed for the element. Will not be null. + \return `Status::kOkCompleted` when the element has been fully consumed and + `bytes_remaining` is now zero. + */ + virtual Status OnUnknownElement(const ElementMetadata& metadata, + Reader* reader, + std::uint64_t* bytes_remaining); + + /** + Called when the parser encounters an `Id::kEbml` element and it has been + fully parsed. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param ebml The parsed element. + */ + virtual Status OnEbml(const ElementMetadata& metadata, const Ebml& ebml); + + /** + Called when the parser encounters an Id::kVoid element. + + Defaults to calling (and returning the result of) Reader::Skip. + + \param metadata Metadata about the element. + \param reader The reader that should be used to consume data. Will not be + null. + \param[in,out] bytes_remaining The number of remaining bytes that need to be + consumed for the element. Will not be null. + \return `Status::kOkCompleted` when the element has been fully consumed and + `bytes_remaining` is now zero. + */ + virtual Status OnVoid(const ElementMetadata& metadata, Reader* reader, + std::uint64_t* bytes_remaining); + + /** + Called when the parser starts an `Id::kSegment` element. + + Defaults to `Action::kRead` and returning `Status::kOkCompleted`. + + \param metadata Metadata about the element that has just been encountered. + \param[out] action The action that should be taken when handling this + element. Will not be null. + */ + virtual Status OnSegmentBegin(const ElementMetadata& metadata, + Action* action); + + /** + Called when the parser encounters an `Id::kSeek` element and it has been + fully parsed. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param seek The parsed element. + */ + virtual Status OnSeek(const ElementMetadata& metadata, const Seek& seek); + + /** + Called when the parser encounters an `Id::kInfo` element and it has been + fully parsed. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param info The parsed element. + */ + virtual Status OnInfo(const ElementMetadata& metadata, const Info& info); + + /** + Called when the parser starts an `Id::kCluster` element. + + Because Cluster elements should start with a Timecode (and optionally + PrevSize) child, this method is not invoked until a child BlockGroup or + SimpleBlock element is encountered (or the Cluster ends if no such child + exists). + + Defaults to `Action::kRead` and returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param cluster The element, as it has currently been parsed. + \param[out] action The action that should be taken when handling this + element. Will not be null. + */ + virtual Status OnClusterBegin(const ElementMetadata& metadata, + const Cluster& cluster, Action* action); + + /** + Called when the parser starts an `Id::kSimpleBlock` element. + + Defaults to `Action::kRead` and returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param simple_block The parsed SimpleBlock header. + \param[out] action The action that should be taken when handling this + element. Will not be null. + */ + virtual Status OnSimpleBlockBegin(const ElementMetadata& metadata, + const SimpleBlock& simple_block, + Action* action); + + /** + Called when the parser finishes an `Id::kSimpleBlock` element. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param simple_block The parsed SimpleBlock header. + */ + virtual Status OnSimpleBlockEnd(const ElementMetadata& metadata, + const SimpleBlock& simple_block); + + /** + Called when the parser starts an `Id::kBlockGroup` element. + + Defaults to `Action::kRead` and returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param[out] action The action that should be taken when handling this + element. Will not be null. + */ + virtual Status OnBlockGroupBegin(const ElementMetadata& metadata, + Action* action); + + /** + Called when the parser starts an `Id::kBlock` element. + + Defaults to `Action::kRead` and returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param block The parsed Block header. + \param[out] action The action that should be taken when handling this + element. Will not be null. + */ + virtual Status OnBlockBegin(const ElementMetadata& metadata, + const Block& block, Action* action); + + /** + Called when the parser finishes an `Id::Block` element. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param block The parsed Block header. + */ + virtual Status OnBlockEnd(const ElementMetadata& metadata, + const Block& block); + + /** + Called when the parser finishes an `Id::kBlockGroup` element. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param block_group The parsed element. + */ + virtual Status OnBlockGroupEnd(const ElementMetadata& metadata, + const BlockGroup& block_group); + + /** + Called when the parser encounters a frame within a `Id::kBlock` or + `Id::kSimpleBlock` element. + + Defaults to calling (and returning the result of) `Reader::Skip`. + + \param metadata Metadata about the frame. + \param reader The reader that should be used to consume data. Will not be + null. + \param[in,out] bytes_remaining The number of remaining bytes that need to be + consumed for the frame. Will not be null. + \return `Status::kOkCompleted` when the frame has been fully consumed and + `bytes_remaining` is now zero. + */ + virtual Status OnFrame(const FrameMetadata& metadata, Reader* reader, + std::uint64_t* bytes_remaining); + + /** + Called when the parser finishes an `Id::kCluster` element. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param cluster The parsed element. + */ + virtual Status OnClusterEnd(const ElementMetadata& metadata, + const Cluster& cluster); + + /** + Called when the parser starts an `Id::kTrackEntry` element. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param track_entry The parsed element. + */ + virtual Status OnTrackEntry(const ElementMetadata& metadata, + const TrackEntry& track_entry); + + /** + Called when the parser encounters an `Id::kCuePoint` element and it has been + fully parsed. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param cue_point The parsed element. + */ + virtual Status OnCuePoint(const ElementMetadata& metadata, + const CuePoint& cue_point); + + /** + Called when the parser encounters an `Id::kEditionEntry` element and it has + been fully parsed. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param edition_entry The parsed element. + */ + virtual Status OnEditionEntry(const ElementMetadata& metadata, + const EditionEntry& edition_entry); + + /** + Called when the parser encounters an `Id::kTag` element and it has been fully + parsed. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + \param tag The parsed element. + */ + virtual Status OnTag(const ElementMetadata& metadata, const Tag& tag); + + /** + Called when the parser finishes an `Id::kSegment` element. + + Defaults to returning `Status::kOkCompleted`. + + \param metadata Metadata about the element. + */ + virtual Status OnSegmentEnd(const ElementMetadata& metadata); + + protected: + /** + Calls (and returns the result of) `Reader::Skip()`, skipping (up to) the + requested number of bytes. + + Unlike `Reader::Skip()`, this method may be called with `*bytes_remaining == + 0`, which will result in `Status::kOkCompleted`. `Reader::Skip()` will be + called multiple times if it returns `Status::kOkPartial` until it returns a + different status (indicating the requested number of bytes has been fully + skipped or some error occurred). + + \param reader The reader that should be used to skip data. Must not be null. + \param[in,out] bytes_remaining The number of remaining bytes that need to be + skipped. Must not be null. May be zero. + \return The result of `Reader::Skip()`. + */ + static Status Skip(Reader* reader, std::uint64_t* bytes_remaining); +}; + +/** + @} + */ + +} // namespace webm + +#endif // INCLUDE_WEBM_CALLBACK_H_ diff --git a/webm_parser/include/webm/dom_types.h b/webm_parser/include/webm/dom_types.h new file mode 100644 index 000000000000..7ae4a0c3ee52 --- /dev/null +++ b/webm_parser/include/webm/dom_types.h @@ -0,0 +1,1781 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef INCLUDE_WEBM_DOM_TYPES_H_ +#define INCLUDE_WEBM_DOM_TYPES_H_ + +#include +#include +#include + +#include "./element.h" +#include "./id.h" + +/** + \file + Data structures representing parsed DOM objects. + + For more information on each type and member, see the WebM specification for + the element that each type/member represents. + */ + +namespace webm { + +/** + \addtogroup PUBLIC_API + @{ + */ + +/** + Metadata for a single frame. + */ +struct FrameMetadata { + /** + Metadata for the EBML element (\WebMID{Block} or \WebMID{SimpleBlock}) that + contains this frame. + */ + ElementMetadata parent_element; + + /** + Absolute byte position (from the beginning of the byte stream/file) of the + frame start. + */ + std::uint64_t position; + + /** + Size (in bytes) of the frame. + */ + std::uint64_t size; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const FrameMetadata& other) const { + return parent_element == other.parent_element && + position == other.position && size == other.size; + } +}; + +/** + A parsed \WebMID{BlockMore} element. + */ +struct BlockMore { + /** + A parsed \WebMID{BlockAddID} element. + */ + Element id{1}; + + /** + A parsed \WebMID{BlockAdditional} element. + */ + Element> data; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const BlockMore& other) const { + return id == other.id && data == other.data; + } +}; + +/** + A parsed \WebMID{BlockAdditions} element. + */ +struct BlockAdditions { + /** + Parsed \WebMID{BlockMore} elements. + */ + std::vector> block_mores; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const BlockAdditions& other) const { + return block_mores == other.block_mores; + } +}; + +/** + A parsed \WebMID{TimeSlice} element (deprecated). + */ +struct TimeSlice { + /** + A parsed \WebMID{LaceNumber} element (deprecated). + */ + Element lace_number; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const TimeSlice& other) const { + return lace_number == other.lace_number; + } +}; + +/** + A parsed \WebMID{Slices} element (deprecated). + */ +struct Slices { + /** + Parsed \WebMID{TimeSlice} elements (deprecated). + */ + std::vector> slices; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Slices& other) const { return slices == other.slices; } +}; + +/** + A parsed \WebMID{BlockVirtual} element. + */ +struct VirtualBlock { + /** + The virtual block's track number. + */ + std::uint64_t track_number; + + /** + The timecode of the virtual block. + */ + std::int16_t timecode; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const VirtualBlock& other) const { + return track_number == other.track_number && timecode == other.timecode; + } +}; + +/** + The frame lacing used in a block. + */ +enum class Lacing : std::uint8_t { + /** + No lacing is used. + */ + kNone = 0x00, + + /** + Xiph-style lacing is used. + */ + kXiph = 0x02, + + /** + Fixed-lacing is used, where each frame has the same, fixed size. + */ + kFixed = 0x04, + + /** + EBML-style lacing is used. + */ + kEbml = 0x06, +}; + +/** + A parsed \WebMID{Block} element. + */ +struct Block { + /** + The block's track number. + */ + std::uint64_t track_number; + + /** + The number of frames in the block. + */ + int num_frames; + + /** + The timecode of the block (relative to the containing \WebMID{Cluster}'s + timecode). + */ + std::int16_t timecode; + + /** + The lacing used to store frames in the block. + */ + Lacing lacing; + + /** + True if the frames are visible, false if they are invisible. + */ + bool is_visible; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Block& other) const { + return track_number == other.track_number && + num_frames == other.num_frames && timecode == other.timecode && + lacing == other.lacing && is_visible == other.is_visible; + } +}; + +/** + A parsed \WebMID{SimpleBlock} element. + */ +struct SimpleBlock : public Block { + /** + True if the frames are all key frames. + */ + bool is_key_frame; + + /** + True if frames can be discarded during playback if needed. + */ + bool is_discardable; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const SimpleBlock& other) const { + return Block::operator==(other) && is_key_frame == other.is_key_frame && + is_discardable == other.is_discardable; + } +}; + +/** + A parsed \WebMID{BlockGroup} element. + */ +struct BlockGroup { + /** + A parsed \WebMID{Block} element. + */ + Element block; + + /** + A parsed \WebMID{BlockVirtual} element. + */ + Element virtual_block; + + /** + A parsed \WebMID{BlockAdditions} element. + */ + Element additions; + + /** + A parsed \WebMID{BlockDuration} element. + */ + Element duration; + + /** + Parsed \WebMID{ReferenceBlock} elements. + */ + std::vector> references; + + /** + A parsed \WebMID{DiscardPadding} element. + */ + Element discard_padding; + + /** + A parsed \WebMID{Slices} element (deprecated). + */ + Element slices; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const BlockGroup& other) const { + return block == other.block && virtual_block == other.virtual_block && + additions == other.additions && duration == other.duration && + references == other.references && + discard_padding == other.discard_padding && slices == other.slices; + } +}; + +/** + A parsed \WebMID{Cluster} element. + */ +struct Cluster { + /** + A parsed \WebMID{Timecode} element. + */ + Element timecode; + + /** + A parsed \WebMID{PrevSize} element. + */ + Element previous_size; + + /** + Parsed \WebMID{SimpleBlock} elements. + */ + std::vector> simple_blocks; + + /** + Parsed \WebMID{BlockGroup} elements. + */ + std::vector> block_groups; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Cluster& other) const { + return timecode == other.timecode && previous_size == other.previous_size && + simple_blocks == other.simple_blocks && + block_groups == other.block_groups; + } +}; + +/** + A parsed \WebMID{EBML} element. + */ +struct Ebml { + /** + A parsed \WebMID{EBMLVersion} element. + */ + Element ebml_version{1}; + + /** + A parsed \WebMID{EBMLReadVersion} element. + */ + Element ebml_read_version{1}; + + /** + A parsed \WebMID{EBMLMaxIDLength} element. + */ + Element ebml_max_id_length{4}; + + /** + A parsed \WebMID{EBMLMaxSizeLength} element. + */ + Element ebml_max_size_length{8}; + + /** + A parsed \WebMID{DocType} element. + */ + Element doc_type{"matroska"}; + + /** + A parsed \WebMID{DocTypeVersion} element. + */ + Element doc_type_version{1}; + + /** + A parsed \WebMID{DocTypeReadVersion} element. + */ + Element doc_type_read_version{1}; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Ebml& other) const { + return ebml_version == other.ebml_version && + ebml_read_version == other.ebml_read_version && + ebml_max_id_length == other.ebml_max_id_length && + ebml_max_size_length == other.ebml_max_size_length && + doc_type == other.doc_type && + doc_type_version == other.doc_type_version && + doc_type_read_version == other.doc_type_read_version; + } +}; + +/** + A parsed \WebMID{Info} element. + */ +struct Info { + /** + A parsed \WebMID{TimecodeScale} element. + */ + Element timecode_scale{1000000}; + + /** + A parsed \WebMID{Duration} element. + */ + Element duration; + + /** + A parsed \WebMID{DateUTC} element. + */ + Element date_utc; + + /** + A parsed \WebMID{Title} element. + */ + Element title; + + /** + A parsed \WebMID{MuxingApp} element. + */ + Element muxing_app; + + /** + A parsed \WebMID{WritingApp} element. + */ + Element writing_app; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Info& other) const { + return timecode_scale == other.timecode_scale && + duration == other.duration && date_utc == other.date_utc && + title == other.title && muxing_app == other.muxing_app && + writing_app == other.writing_app; + } +}; + +/** + A parsed \WebMID{Seek} element. + */ +struct Seek { + /** + A parsed \WebMID{SeekID} element. + */ + Element id; + + /** + A parsed \WebMID{SeekPosition} element. + */ + Element position; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Seek& other) const { + return id == other.id && position == other.position; + } +}; + +/** + A parsed \WebMID{Audio} element. + */ +struct Audio { + /** + A parsed \WebMID{SamplingFrequency} element. + */ + Element sampling_frequency{8000}; + + /** + A parsed \WebMID{OutputSamplingFrequency} element. + */ + Element output_frequency{8000}; + + /** + A parsed \WebMID{Channels} element. + */ + Element channels{1}; + + /** + A parsed \WebMID{BitDepth} element. + */ + Element bit_depth; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Audio& other) const { + return sampling_frequency == other.sampling_frequency && + output_frequency == other.output_frequency && + channels == other.channels && bit_depth == other.bit_depth; + } +}; + +/** + A parsed \WebMID{MasteringMetadata} element. + */ +struct MasteringMetadata { + /** + A parsed \WebMID{PrimaryRChromaticityX} element. + */ + Element primary_r_chromaticity_x; + + /** + A parsed \WebMID{PrimaryRChromaticityY} element. + */ + Element primary_r_chromaticity_y; + + /** + A parsed \WebMID{PrimaryGChromaticityX} element. + */ + Element primary_g_chromaticity_x; + + /** + A parsed \WebMID{PrimaryGChromaticityY} element. + */ + Element primary_g_chromaticity_y; + + /** + A parsed \WebMID{PrimaryBChromaticityX} element. + */ + Element primary_b_chromaticity_x; + + /** + A parsed \WebMID{PrimaryBChromaticityY} element. + */ + Element primary_b_chromaticity_y; + + /** + A parsed \WebMID{WhitePointChromaticityX} element. + */ + Element white_point_chromaticity_x; + + /** + A parsed \WebMID{WhitePointChromaticityY} element. + */ + Element white_point_chromaticity_y; + + /** + A parsed \WebMID{LuminanceMax} element. + */ + Element luminance_max; + + /** + A parsed \WebMID{LuminanceMin} element. + */ + Element luminance_min; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const MasteringMetadata& other) const { + return primary_r_chromaticity_x == other.primary_r_chromaticity_x && + primary_r_chromaticity_y == other.primary_r_chromaticity_y && + primary_g_chromaticity_x == other.primary_g_chromaticity_x && + primary_g_chromaticity_y == other.primary_g_chromaticity_y && + primary_b_chromaticity_x == other.primary_b_chromaticity_x && + primary_b_chromaticity_y == other.primary_b_chromaticity_y && + white_point_chromaticity_x == other.white_point_chromaticity_x && + white_point_chromaticity_y == other.white_point_chromaticity_y && + luminance_max == other.luminance_max && + luminance_min == other.luminance_min; + } +}; + +/** + A parsed \WebMID{MatrixCoefficients} element. + + Matroska/WebM adopted these values from Table 4 of ISO/IEC 23001-8:2013/DCOR1. + See that document for further details. + */ +enum class MatrixCoefficients : std::uint64_t { + /** + The identity matrix. + + Typically used for GBR (often referred to as RGB); however, may also be used + for YZX (often referred to as XYZ). + */ + kRgb = 0, + + /** + Rec. ITU-R BT.709-5. + */ + kBt709 = 1, + + /** + Image characteristics are unknown or are determined by the application. + */ + kUnspecified = 2, + + /** + United States Federal Communications Commission Title 47 Code of Federal + Regulations (2003) 73.682 (a) (20). + */ + kFcc = 4, + + /** + Rec. ITU-R BT.470‑6 System B, G (historical). + */ + kBt470Bg = 5, + + /** + Society of Motion Picture and Television Engineers 170M (2004). + */ + kSmpte170M = 6, + + /** + Society of Motion Picture and Television Engineers 240M (1999). + */ + kSmpte240M = 7, + + /** + YCgCo. + */ + kYCgCo = 8, + + /** + Rec. ITU-R BT.2020 (non-constant luminance). + */ + kBt2020NonconstantLuminance = 9, + + /** + Rec. ITU-R BT.2020 (constant luminance). + */ + kBt2020ConstantLuminance = 10, +}; + +/** + A parsed \WebMID{Range} element. + */ +enum class Range : std::uint64_t { + /** + Unspecified. + */ + kUnspecified = 0, + + /** + Broadcast range. + */ + kBroadcast = 1, + + /** + Full range (no clipping). + */ + kFull = 2, + + /** + Defined by MatrixCoefficients/TransferCharacteristics. + */ + kDerived = 3, +}; + +/** + A parsed \WebMID{TransferCharacteristics} element. + + Matroska/WebM adopted these values from Table 3 of ISO/IEC 23001-8:2013/DCOR1. + See that document for further details. + */ +enum class TransferCharacteristics : std::uint64_t { + /** + Rec. ITU-R BT.709-6. + */ + kBt709 = 1, + + /** + Image characteristics are unknown or are determined by the application. + */ + kUnspecified = 2, + + /** + Rec. ITU‑R BT.470‑6 System M (historical) with assumed display gamma 2.2. + */ + kGamma22curve = 4, + + /** + Rec. ITU‑R BT.470-6 System B, G (historical) with assumed display gamma 2.8. + */ + kGamma28curve = 5, + + /** + Society of Motion Picture and Television Engineers 170M (2004). + */ + kSmpte170M = 6, + + /** + Society of Motion Picture and Television Engineers 240M (1999). + */ + kSmpte240M = 7, + + /** + Linear transfer characteristics. + */ + kLinear = 8, + + /** + Logarithmic transfer characteristic (100:1 range). + */ + kLog = 9, + + /** + Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range). + */ + kLogSqrt = 10, + + /** + IEC 61966-2-4. + */ + kIec6196624 = 11, + + /** + Rec. ITU‑R BT.1361-0 extended colour gamut system (historical). + */ + kBt1361ExtendedColourGamut = 12, + + /** + IEC 61966-2-1 sRGB or sYCC. + */ + kIec6196621 = 13, + + /** + Rec. ITU-R BT.2020-2 (10-bit system). + */ + k10BitBt2020 = 14, + + /** + Rec. ITU-R BT.2020-2 (12-bit system). + */ + k12BitBt2020 = 15, + + /** + Society of Motion Picture and Television Engineers ST 2084. + */ + kSmpteSt2084 = 16, + + /** + Society of Motion Picture and Television Engineers ST 428-1. + */ + kSmpteSt4281 = 17, + + /** + Association of Radio Industries and Businesses (ARIB) STD-B67. + */ + kAribStdB67Hlg = 18, +}; + +/** + A parsed \WebMID{Primaries} element. + + Matroska/WebM adopted these values from Table 2 of ISO/IEC 23001-8:2013/DCOR1. + See that document for further details. + */ +enum class Primaries : std::uint64_t { + /** + Rec. ITU‑R BT.709-6. + */ + kBt709 = 1, + + /** + Image characteristics are unknown or are determined by the application. + */ + kUnspecified = 2, + + /** + Rec. ITU‑R BT.470‑6 System M (historical). + */ + kBt470M = 4, + + /** + Rec. ITU‑R BT.470‑6 System B, G (historical). + */ + kBt470Bg = 5, + + /** + Society of Motion Picture and Television Engineers 170M (2004). + */ + kSmpte170M = 6, + + /** + Society of Motion Picture and Television Engineers 240M (1999). + */ + kSmpte240M = 7, + + /** + Generic film. + */ + kFilm = 8, + + /** + Rec. ITU-R BT.2020-2. + */ + kBt2020 = 9, + + /** + Society of Motion Picture and Television Engineers ST 428-1. + */ + kSmpteSt4281 = 10, + + /** + Society of Motion Picture and Television Engineers RP 431-2 (a.k.a. DCI-P3). + */ + kSmpteRp431 = 11, + + /** + Society of Motion Picture and Television Engineers EG 432-1 + (a.k.a. DCI-P3 D65). + */ + kSmpteEg432 = 12, + + /** + JEDEC P22 phosphors/EBU Tech. 3213-E (1975). + */ + kJedecP22Phosphors = 22, +}; + +/** + A parsed \WebMID{Colour} element. + */ +struct Colour { + /** + A parsed \WebMID{MatrixCoefficients} element. + */ + Element matrix_coefficients{ + MatrixCoefficients::kUnspecified}; + + /** + A parsed \WebMID{BitsPerChannel} element. + */ + Element bits_per_channel{0}; + + /** + A parsed \WebMID{ChromaSubsamplingHorz} element. + */ + Element chroma_subsampling_x; + + /** + A parsed \WebMID{ChromaSubsamplingVert} element. + */ + Element chroma_subsampling_y; + + /** + A parsed \WebMID{CbSubsamplingHorz} element. + */ + Element cb_subsampling_x; + + /** + A parsed \WebMID{CbSubsamplingVert} element. + */ + Element cb_subsampling_y; + + /** + A parsed \WebMID{ChromaSitingHorz} element. + */ + Element chroma_siting_x{0}; + + /** + A parsed \WebMID{ChromaSitingVert} element. + */ + Element chroma_siting_y{0}; + + /** + A parsed \WebMID{Range} element. + */ + Element range{Range::kUnspecified}; + + /** + A parsed \WebMID{TransferCharacteristics} element. + */ + Element transfer_characteristics{ + TransferCharacteristics::kUnspecified}; + + /** + A parsed \WebMID{Primaries} element. + */ + Element primaries{Primaries::kUnspecified}; + + /** + A parsed \WebMID{MaxCLL} element. + */ + Element max_cll; + + /** + A parsed \WebMID{MaxFALL} element. + */ + Element max_fall; + + /** + A parsed \WebMID{MasteringMetadata} element. + */ + Element mastering_metadata; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Colour& other) const { + return matrix_coefficients == other.matrix_coefficients && + bits_per_channel == other.bits_per_channel && + chroma_subsampling_x == other.chroma_subsampling_x && + chroma_subsampling_y == other.chroma_subsampling_y && + cb_subsampling_x == other.cb_subsampling_x && + cb_subsampling_y == other.cb_subsampling_y && + chroma_siting_x == other.chroma_siting_x && + chroma_siting_y == other.chroma_siting_y && range == other.range && + transfer_characteristics == other.transfer_characteristics && + primaries == other.primaries && max_cll == other.max_cll && + max_fall == other.max_fall && + mastering_metadata == other.mastering_metadata; + } +}; + +/** + A parsed \WebMID{ProjectionType} element. + */ +enum class ProjectionType : std::uint64_t { + /** + Rectangular. + */ + kRectangular = 0, + + /** + Equirectangular. + */ + kEquirectangular = 1, + + /** + Cube map. + */ + kCubeMap = 2, + + /** + Mesh. + */ + kMesh = 3, +}; + +/** + A parsed \WebMID{Projection} element. + */ +struct Projection { + /** + A parsed \WebMID{ProjectionType} element. + */ + Element type; + + /** + A parsed \WebMID{ProjectionPrivate} element. + */ + Element> projection_private; + + /** + A parsed \WebMID{ProjectionPoseYaw} element. + */ + Element pose_yaw; + + /** + A parsed \WebMID{ProjectionPosePitch} element. + */ + Element pose_pitch; + + /** + A parsed \WebMID{ProjectionPoseRoll} element. + */ + Element pose_roll; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Projection& other) const { + return type == other.type && + projection_private == other.projection_private && + pose_yaw == other.pose_yaw && pose_pitch == other.pose_pitch && + pose_roll == other.pose_roll; + } +}; + +/** + A parsed \WebMID{FlagInterlaced} element. + */ +enum class FlagInterlaced : std::uint64_t { + /** + Unspecified. + */ + kUnspecified = 0, + + /** + Interlaced. + */ + kInterlaced = 1, + + /** + Progressive. + */ + kProgressive = 2, +}; + +/** + A parsed \WebMID{StereoMode} element. + */ +enum class StereoMode : std::uint64_t { + /** + Mono. + */ + kMono = 0, + + /** + Side-by-side, left eye is first. + */ + kSideBySideLeftFirst = 1, + + /** + Top-bottom, right eye is first. + */ + kTopBottomRightFirst = 2, + + /** + Top-bottom, left eye is first. + */ + kTopBottomLeftFirst = 3, + + /** + Checkboard, right eye is first. + */ + kCheckboardRightFirst = 4, + + /** + Checkboard, left eye is first. + */ + kCheckboardLeftFirst = 5, + + /** + Row interleaved, right eye is first. + */ + kRowInterleavedRightFirst = 6, + + /** + Row interleaved, left eye is first. + */ + kRowInterleavedLeftFirst = 7, + + /** + Column interleaved, right eye is first. + */ + kColumnInterleavedRightFirst = 8, + + /** + Column interleaved, left eye is first. + */ + kColumnInterleavedLeftFirst = 9, + + /** + Anaglyph (cyan/read). + */ + kAnaglyphCyanRed = 10, + + /** + Side-by-side, right eye is first. + */ + kSideBySideRightFirst = 11, + + /** + Anaglyph (green/magenta). + */ + kAnaglyphGreenMagenta = 12, + + /** + Both eyes are laced in one block, left eye is first. + */ + kBlockLacedLeftFirst = 13, + + /** + Both eyes are laced in one block, right eye is first. + */ + kBlockLacedRightFirst = 14, + + /** + Stereo, but the layout for the left and right eyes is application dependent + and must be determined from other data (like the ProjectionPrivate element). + */ + kStereoCustom = 15, +}; + +/** + A parsed \WebMID{DisplayUnit} element. + + Note that WebM only supports pixel display units. Centimeters, inches, and + display aspect ratio aren't supported. + */ +enum class DisplayUnit : std::uint64_t { + // The only value legal value in WebM is 0 (pixels). + /** + Pixels. + + This is the only option supported by WebM. + */ + kPixels = 0, + + /** + Centimeters. + */ + kCentimeters = 1, + + /** + Inches. + */ + kInches = 2, + + /** + Display aspect ratio. + */ + kDisplayAspectRatio = 3, +}; + +/** + A parsed \WebMID{AspectRatioType} element. + */ +enum class AspectRatioType : std::uint64_t { + /** + Free resizing. + */ + kFreeResizing = 0, + + /** + Keep aspect ratio. + */ + kKeep = 1, + + /** + Fixed aspect ratio. + */ + kFixed = 2, +}; + +/** + A parsed \WebMID{Video} element. + */ +struct Video { + /** + A parsed \WebMID{FlagInterlaced} element. + */ + Element interlaced{FlagInterlaced::kUnspecified}; + + /** + A parsed \WebMID{StereoMode} element. + */ + Element stereo_mode{StereoMode::kMono}; + + /** + A parsed \WebMID{AlphaMode} element. + */ + Element alpha_mode{0}; + + /** + A parsed \WebMID{PixelWidth} element. + */ + Element pixel_width; + + /** + A parsed \WebMID{PixelHeight} element. + */ + Element pixel_height; + + /** + A parsed \WebMID{PixelCropBottom} element. + */ + Element pixel_crop_bottom{0}; + + /** + A parsed \WebMID{PixelCropTop} element. + */ + Element pixel_crop_top{0}; + + /** + A parsed \WebMID{PixelCropLeft} element. + */ + Element pixel_crop_left{0}; + + /** + A parsed \WebMID{PixelCropRight} element. + */ + Element pixel_crop_right{0}; + + /** + A parsed \WebMID{DisplayWidth} element. + */ + Element display_width; + + /** + A parsed \WebMID{DisplayHeight} element. + */ + Element display_height; + + /** + A parsed \WebMID{DisplayUnit} element. + */ + Element display_unit{DisplayUnit::kPixels}; + + /** + A parsed \WebMID{AspectRatioType} element. + */ + Element aspect_ratio_type{AspectRatioType::kFreeResizing}; + + /** + A parsed \WebMID{FrameRate} element (deprecated). + */ + Element frame_rate; + + /** + A parsed \WebMID{Colour} element. + */ + Element colour; + + /** + A parsed \WebMID{Projection} element. + */ + Element projection; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const Video& other) const { + return interlaced == other.interlaced && stereo_mode == other.stereo_mode && + alpha_mode == other.alpha_mode && pixel_width == other.pixel_width && + pixel_height == other.pixel_height && + pixel_crop_bottom == other.pixel_crop_bottom && + pixel_crop_top == other.pixel_crop_top && + pixel_crop_left == other.pixel_crop_left && + pixel_crop_right == other.pixel_crop_right && + display_width == other.display_width && + display_height == other.display_height && + display_unit == other.display_unit && + aspect_ratio_type == other.aspect_ratio_type && + frame_rate == other.frame_rate && colour == other.colour && + projection == other.projection; + } +}; + +/** + A parsed \WebMID{AESSettingsCipherMode} element. + */ +enum class AesSettingsCipherMode : std::uint64_t { + /** + Counter (CTR). + */ + kCtr = 1, +}; + +/** + A parsed \WebMID{ContentEncAESSettings} element. + */ +struct ContentEncAesSettings { + /** + A parsed \WebMID{AESSettingsCipherMode} element. + */ + Element aes_settings_cipher_mode{ + AesSettingsCipherMode::kCtr}; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const ContentEncAesSettings& other) const { + return aes_settings_cipher_mode == other.aes_settings_cipher_mode; + } +}; + +/** + A parsed \WebMID{ContentEncAlgo} element. + */ +enum class ContentEncAlgo : std::uint64_t { + /** + The contents have been signed but not encrypted. + */ + kOnlySigned = 0, + + /** + DES encryption. + */ + kDes = 1, + + /** + 3DES encryption. + */ + k3Des = 2, + + /** + Twofish encryption. + */ + kTwofish = 3, + + /** + Blowfish encryption. + */ + kBlowfish = 4, + + /** + AES encryption. + */ + kAes = 5, +}; + +/** + A parsed \WebMID{ContentEncryption} element. + */ +struct ContentEncryption { + /** + A parsed \WebMID{ContentEncAlgo} element. + */ + Element algorithm{ContentEncAlgo::kOnlySigned}; + + /** + A parsed \WebMID{ContentEncKeyID} element. + */ + Element> key_id; + + /** + A parsed \WebMID{ContentEncAESSettings} element. + */ + Element aes_settings; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const ContentEncryption& other) const { + return algorithm == other.algorithm && key_id == other.key_id && + aes_settings == other.aes_settings; + } +}; + +/** + A parsed \WebMID{ContentEncodingType} element. + */ +enum class ContentEncodingType : std::uint64_t { + /** + Compression. + */ + kCompression = 0, + + /** + Encryption. + */ + kEncryption = 1, +}; + +/** + A parsed \WebMID{ContentEncoding} element. + */ +struct ContentEncoding { + /** + A parsed \WebMID{ContentEncodingOrder} element. + */ + Element order{0}; + + /** + A parsed \WebMID{ContentEncodingScope} element. + */ + Element scope{1}; + + /** + A parsed \WebMID{ContentEncodingType} element. + */ + Element type{ContentEncodingType::kCompression}; + + /** + A parsed \WebMID{ContentEncryption} element. + */ + Element encryption; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const ContentEncoding& other) const { + return order == other.order && scope == other.scope && type == other.type && + encryption == other.encryption; + } +}; + +/** + A parsed \WebMID{ContentEncodings} element. + */ +struct ContentEncodings { + /** + Parsed \WebMID{ContentEncoding} elements. + */ + std::vector> encodings; + + /** + Returns true if every member within the two objects are equal. + */ + bool operator==(const ContentEncodings& other) const { + return encodings == other.encodings; + } +}; + +/** + A parsed \WebMID{TrackType} element. + */ +enum class TrackType : std::uint64_t { + /** + Video. + */ + kVideo = 0x01, + + /** + Audio. + */ + kAudio = 0x02, + + /** + Complex. + */ + kComplex = 0x03, + + /** + Logo. + */ + kLogo = 0x10, + + /** + Subtitle. + */ + kSubtitle = 0x11, + + /** + Buttons. + */ + kButtons = 0x12, + + /** + Control. + */ + kControl = 0x20, +}; + +/** + A parsed \WebMID{TrackEntry} element. + */ +struct TrackEntry { + /** + A parsed \WebMID{TrackNumber} element. + */ + Element track_number; + + /** + A parsed \WebMID{TrackUID} element. + */ + Element track_uid; + + /** + A parsed \WebMID{TrackType} element. + */ + Element track_type; + + /** + A parsed \WebMID{FlagEnabled} element. + */ + Element is_enabled{true}; + + /** + A parsed \WebMID{FlagDefault} element. + */ + Element is_default{true}; + + /** + A parsed \WebMID{FlagForced} element. + */ + Element is_forced{false}; + + /** + A parsed \WebMID{FlagLacing} element. + */ + Element uses_lacing{true}; + + /** + A parsed \WebMID{DefaultDuration} element. + */ + Element default_duration; + + /** + A parsed \WebMID{Name} element. + */ + Element name; + + /** + A parsed \WebMID{Language} element. + */ + Element language{"eng"}; + + /** + A parsed \WebMID{CodecID} element. + */ + Element codec_id; + + /** + A parsed \WebMID{CodecPrivate} element. + */ + Element> codec_private; + + /** + A parsed \WebMID{CodecName} element. + */ + Element codec_name; + + /** + A parsed \WebMID{CodecDelay} element. + */ + Element codec_delay{0}; + + /** + A parsed \WebMID{SeekPreRoll} element. + */ + Element seek_pre_roll{0}; + + /** + A parsed \WebMID{Video} element. + */ + Element