diff --git a/.gitignore b/.gitignore index ea1f474..5ffa482 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ components.x/ components/ main/main.cpp.bak2 webfs/ +webserver/font-awesome/fonts/fontawesome-webfont.ttf.orig +webserver/font-awesome/fonts/fontawesome-webfont.woff.orig + diff --git a/README.md b/README.md index f4d2302..878707d 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,5 @@ hosts. Please see my website, https://eaw.app for more documentation and recent updates. -The firmware is undergoing rapid advances and will initially only be included as releases. +The source code for the mz25key is identical to the SharpKey, just the build time configuration via the KConfig menu. The provided sdkconfig +is setup so that the source code will build the mz25key. diff --git a/docs/OMOTON_K8508.pdf b/docs/OMOTON_K8508.pdf new file mode 100644 index 0000000..681af9c --- /dev/null +++ b/docs/OMOTON_K8508.pdf @@ -0,0 +1,10594 @@ +%PDF-1.6 % +1 0 obj <>/OCGs[6 0 R 98 0 R 188 0 R 280 0 R 370 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + 2018-01-12T12:33:55+09:00 + Adobe Illustrator CS6 (Windows) + 2018-03-28T15:48:42+08:00 + 2018-03-28T15:48:42+08:00 + + + + 256 + 184 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAuAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4qk955iis6vcm3t4jK 0Mck9wIuTKxFBVKVPHpXFUKvnXSmpxvNPavSl6hrvT+XxxVdH5x0+WSCOK4spHunaK2VLtWMjopd lSiHkQoqaYqmA1C+JoLaMnwErf8AVPFVVZ9Tb/j1iHzmb/qniqoHv+8MX/I1v+qeKu533++Yv+Rr f9U8VUbu8vbaJZDBGwaSKKglbrLIsYP9325VxVW533++Yv8Aka3/AFTxV3O+/wB8xf8AI1v+qeKu 533++Yv+Rrf9U8Vdzvv98xf8jW/6p4q7nff75i/5Gt/1TxV3O+/3zF/yNb/qnirud9/vmL/ka3/V PFVGC8vZpbmMQRg28giJ9VtyY0kr/d/8WUxVW533++Yv+Rrf9U8VUL3UvqMPr3r2trDUL6s1x6a8 j0HJkAqcVQx8y6aCAb3TwWJC1vF3I60+HtiqmfNmjgoDqOmgytwjH11PiajNxX4dzSNjT2PhiqYQ Xk1xH6luLeaOtOcc5YV+YQ4qqc77/fMX/I1v+qeKqN3eXtvEshgjYNJFFQSt1lkWMH+77cq4qrc7 7/fMX/I1v+qeKu533++Yv+Rrf9U8VQlvrIudRu9OhEbXlgIzdR8pBw9ZSyfEYgrVA/ZJp3xV2lay uq2YvNP9Ge2LyRiQPIvxxOY3FGiB2ZSMVa1XW00qCKe/9KGKaeK2jblK9ZZ3Eca0SJj8TMBXoO+K oznff75i/wCRrf8AVPFXc77/AHzF/wAjW/6p4q3DNK0rxSoqMiq3wsWBDFh3Vf5cVVsVYX5m8rx+ YrFrOS8ns0W4nLtBwrIjO6tG/NWqjA7jFUpg/LC0gdzBqdyqzEmWLhbsjcihccTHtzMMdSN/hG9N sVTfyl+XFroWoS6jJeS3l1LG0TSTLEHKs6ueTIqk/YHXFWZJGiCiimKt4q7FXYqgtY/3kj/5ibX/ AKiY8VeZa6/lGy1zU7m4utMglkupBNJL5i1DS3Mg9JmjaIVhD0U14sB9kU+I4pTLS/OukaNCbS31 LQYreQrLF9a8wT3EhaQl2YPcwu3pGFeaUPGtQNt8VT7SPMvmbV7T61YWej3cBLx/WLPVZbiESota cxZJUcvhJWpHgeyhF/WfP3pt/uP0r1TEeI+u3PFZfTBAP+ifEhlqK7ELvudsVU/rH5ilbf8A3H6O rmI/W/8ATbogTVFDH/oo5JSuxocVXRXf5glIhLpekhyq+sy6jckBv2+KmyFR4VIxVkOKoLT/APev U/8AmJX/AKhocVSXzDrNxZ3TqrRAco0QSzGH7ZjBoA8dePMlj22rQYqkWo64l5avBeQ2N3A3rH0Z 7gyITFIIo+StKR8TNX5dK4qhk/QMTlE0zSUj9SSM8XQVRI6v0kpVm2HiMVaLaNc+kJ9M0lyWjX45 EIDSI00pBaT7PFtj3YkHFV/6UalodPWwtHjZJUpMQP33PhGaTLwqleZ6qexxVPtJ124up4SJIeEs gEYjuDK/DmqkshkkH8w6eG9cVZBrH+8kf/MTa/8AUTHiq7WJZItJvZYmKSR28rI46hlQkEYqlcei FZpvWF9IhcmIx31yPhLEjrcL2p2xVKJPLXmVZrqaG6JMgRbZWe+HEKan1GGpHlsWpQDt71VW6Hpe pwmT9KPcXMD1eA2UmoRbuxYks15MrA12piqKXQZ54VJOouKgkjUpR8SNXoWJFGHTtiqqmg34mYk3 3pcQFVtQnch+W5PGaPbj/n4KpvoazRx3NtLI8ht5UjBkdpGFbeJ2HNyWb43Y7nFUWn+903/GKL/i UmKq+KpPECTKB19ab/k62Kpjb2wjHJt3P4Yqr4q7FXYq7FXYqgtY/wB5I/8AmJtf+omPFWH66/mA 6pcRWkOvLE83wSWsWjSQgbAtH9aZ5OGxryFfi6eCqDjh81oJBJ/iGf4vRjb6t5fIU1YGRKBTx2BP L2264q6UedR6qWc/mONfrMEaOtv5fEbRgFJJkDAt6bcebVHLfZRvRS3aXHnVdPEbHzQ04DfvJoNA 9UjkgX7JMWwBpXchjXcCihXgufNvphPU8yu8fMvK9toqljG8h4U+FfjAABUUpx3HxYqh5X87D1JY 5fNACz+p6Ah8vsWWYD4ELE/BFv1IPz2xSzfy5JePo9v9cW7Fyi8JHv1gS4crtzdbYmLf/JxQq6f/ AL16n/zEr/1DQ4qkmsPCNTuklnjgSVVR/VuvqocFFFB8EnLr7Ur3rsqxxLgKy1u42kjMZLrfVBey Pxuv7ro6HhH74q6CQn04kuY0Vlt44nN6eCLE73EBc+l9mJfgJ/nIGKrXlWSFuNwkYnAosl6dhduW VG/d/ahl+OYdhiq71xO00oYyxOk0k6Le1cRTKIpY2pFszSIJCf5RiqeadDIJbaRp47p3mje5lhuj cKZVCRlynppx2ULXlt0pvirItY/3kj/5ibX/AKiY8Vdrv/HE1D/mGm/5NnFUWssTSPGrgyR05qDU ry3FR2riq7FULDb3sUSRLNGVjUKCYmrQCn+/MVVreIxRcGbkxZmJAoKuxY7VPjiq6SWKJecjhF6c mNBv88VQmn/716n/AMxK/wDUNDiqsn+903/GKL/iUmKq+KoDToqtLIe00wX5+o2Ko/FWI6l541ix 1m6sh5W1K6soOXpahbKHWXjGr/Cm1Bz5pua/CKA8hirpvOuu0kNt5Xvm9KWKMrP+6ZlkM4Z04LMG 4tAg60/eAkqu+KoPS/PXnS9gheXyZPZSyJG7wXFwwZC10IGBZYGT4Y29X7VaDcAb4qj/AC95r8z6 rqMVtdeWpdLtvTeSe8uJW4fC3EJEpiRmYk1+ML8OKsrxVBax/vJH/wAxNr/1Ex4qwzVdS/KFNV1G 41ARHVIZPRvz6Ny03JWU8VVELMC3GvDY++KqcV1+TEVxMqx2iyoBcyl4ZuI4uSGBZeNQw3A9q7Ux V6DBDFBDHDEoSKJQkaDoFUUA+7FV+KuxV2KuxVKfrgshq9yYZJ/TuU/cwhS7Vt4BtyKjv3OKsen8 y6VqF4ANN1uMy0YtHbvGpeBgyVWvPl8IYbUpiqnHqWneu0X6N8wKavGkhhco4uWV5CrAttyPxcqE UPhiqO0bUo724jsks9dslHwJLdxNFEAsfLdzX/V36n78VTuXQ4peHqXV03psHT96RRl6HYYqhpvK OmTo0c0txIjCUFWlJH79ucnX+ZsVVLHyvpdlN6tvzUl/UK8gFLAAdAB/KMVRWsf7yR/8xNr/ANRM eKrddnSHSpzJbTXccvGCSC34+oVmYREjkyD4Q9Tv0xVjK+ZL+nqrp2v8Wj9R5fQ00CgIABq1Sfir Qe+Kppo13fak04ZtUsFgoOd3HZKrklgQnprJXjw+L5jvWiqZLZXDCq6rcMPYWx6bf75xVd+j7v8A 6udz/wADbf8AVHFVraZcsKNqVww2NClsdwaj/dOKoi0s1thJ+8eWSZ/UllkpyZuITooVR8KAbDFX J/vdN/xii/4lJiqviqQa5O8GhStHqMulu1w6rdwW4unBaVhT0iko38abYqxsa/6ayA+btRkeHlzb 9Ehlozeip+C13VHYNUHtX7NcVVG8xehcnTX8zahJdqwjaX9En7QLPu4txF8SIVr0qa4qpDXLl1HH zdfjgkhcnR6EiIF2f4rcCvH7PY9gcVRA1xwY4m81Xglij9SYtpRBkVOTPQegPiK/srvt0xVSi1y4 CzV82XszR1VyNJACkSrEXFIN1BceO3xdBiq208ysHhuJfNN7JbPIV9OTSGSvpMC67QhhyWoBpirL 7u9tr7Sba7tmLwTT2jRsVZDT6zH1VgrKfEEVxViWu38ttqEq/pm009ZJ2RI57MzHkXIoJeFKsXUn c0Hh1xVL/wBMygIP8T6cH9VuZk08qrIY1kEaDgu6qa1qevtiqY+XpL7VbtTZa9YahAnCaSBbNEZo ebK3x8B9orx5Abb7eCqM87Xb6XHY3AuLDR0MxWSWa3e8WVdiYRHGsbKzUrz7U6b4qkkGrapFLHHe eYdHlZhUCLSp4w3GeNWoTJKKUJi69TXqpGKsr0DU9M1SFobX6nfXNmFivZUVox6qij/C0W2/7NTT FUxuLUosbCzt0pND8at8Q/er0/dj9eKoe8tra6sfMEF1ClxA7nnBIgkVqWkJAKNs246HFWAXPlzy 48IuINB8uTksoaTVzHaIAFaEek0VvKH48AnGg6Ur2xW0Ra+V9Eu9S28t+T7iW4JErw3JadletaAW h5couJ6/eN8VZDdavd6Z5gttEsPq9vpsCRJFbl1ThGojQBIhGT6ahqFuYp0piqDPn3WXeM21z5Xm WUPJHGNZfm1uGAEqEW1G2rXalf2sVZHqs+uSPaSaOqz2cjRGWWOSIfuXLGR1LhwxC8SgFAamp6Yq 1FL50Lxc7bTxFT96TNN6hPHb4REVG/X4jiq6VtYbT66pHbxS/XLT0ltpHkUp68W7F0jNeVe3TFUP 5sk0jUdNudGk1DT4botCXiveEyrxkSVecJkiNSBVdxvQ4qwm2/L+whWO3n1fy9LCbeiRjT/ToHBj SRFN9IvAuhHGnjQjFV83krTrh7ia51zy8/qiYllsKAc0PF25X7hirDk1diBQBeuKsp8seRdEsYbe 9njsrzUEkaeG/s4DbR1k+IsierOKsSSTy3qcVZXirsVdiqgn+903/GKL/iUmKq+Ksf8AME3o6Gz/ AF6908i6ak+m2rXtwaSueAgSG6ZgQPi+DpirH7S7vLp0ZfMPmT055XiRJNFMHAsjMGbnp8bqi0+F m2rQGuKoRNUvFuBGfM3mpwyvIXPl0hB6dPhJ/RYNW4kqB1+lahKY/Xppr0MmveYY4pU9U240WRYg JeKqOcmnmRSpkB486rQ8tgcKELa6ldkc28x+ZWEQjuCsmgOpMUdEeM/7jfiaVviZV+P+XiMUrIda kFwVk1rzUzQBkkB0GUI4UuS9U06jHcD4DvQUHUlWkV6xiV7d/MXmKaXko+sJpcr8TJ0AaOxMR48h WoPH9rvihk88ckejWqyTy3Tie0JnnRY5WrcxkF0VYwpp24jFWKa7cm2v5x9cubVhLJOUh097yNo0 kLsKx2k7VIRuVHr8VBvTFUivNUZVhuU17UYEZ1adP0LJKAIlHJSi6fzi9StanbwxV6Bod7Y6vYLd WVnG8IPpl54ZrZmYAEnhPbxNQ16gUxVK/OVtOq2XoSjSnZ2X6xaQfW3YHj8DJ9XlopP7VP14qxua 9u4LWG5n1e+PLi/7rRZX5lwZFUwpYySKoReJB79wcVZKvmrR4rKOWTT5ZqJV5Bp96rMVTmT6f1UM KjtT2xVMrO6tNSso7y1skjhFxGnKWKWCQFJlBIjmhif5Gm+KohwTBrwUVYu1BWlT9Ti774qwy8eW LSJI7u50uxaQU/3MQvd28n7yUN8BkiKAtXbme/jUqsk8oWbyI99NNpd9HxSO2uNPszbMskXJJwS0 s1V5AcQKcdwS2KpVrT3I85Mim2UMiSR+ojvKAjwI8nITBPTDSopQwkk78v5VUus7Tznq1vNJpWp+ VL+hMkiHS7jg7tskkhF0TzNGBNPHFOz0qOOOKNY41CRoAqIoAUKBQAAdAMULsVQWsf7yR/8AMTa/ 9RMeKsZ88Wmm29zbX122j2y3UogEuo2BunlcQTvIhkWROK+nGp5EbKrVO4oqkL3vla9a0iOo+WZW EESx2s+nVY1Y3AeFTKr+my1ZVoRXeuKoZvMHkl3jeLWfK/qKZ5pVbT+Q4QjiasJVMbIppvuQNhTF WcWdl5rS3gW2vtMis1RRHHBZSqgSuwQfWKAcKUxVWhtvOiN+81DT5ULMafVJVIUy1VQRP2i2rT7W /TbFVL6n599JVOqad6gC8nFnLQkM5bb19qqUH0Hx2VTy1W5W3QXTpJcAfvHjUohPspLEffiq1P8A e6b/AIxRf8SkxVXxVItZZxo7COa+hdriUK+moklwSHc0AkSRaGnUjFWLXM18I1RdX83AxDk8ken2 xL1am/KyNTUdFHetKYq0l7dxyCL6/wCbpOB51extv3pDSHiG+qrTrTsKBfclVSijv47i2uH1nzlO eNWgazs1j4mXgRIFs03qOx5cdxtvirIrDy7qN5aQXI8y65CGPIxzpZRSHi3R0a0BUGnTbY4qvfyZ qTWclsPNutI0j8zcq1j6oBXiUUm0Khe+y1r3xVGWPl7ULe+huptf1C6SGvKzk+qiCSsZT4+ECydT z2cb+22Ko7WP95I/+Ym1/wComPFWCeZbec6pOY77V7BWElV06xF5CxDyuWDfULxg/wAIqA3XivVq FVAy3N0IBcre64sjhkmjh0gSOpaSsbNC2mycGSNwo4jiwHIgnFU98o6ffXv+5RdTu7+xZ3As9Rs4 LKtVpTidPtZ+K12IPXqTviqYeZEEIiURLZyTxzRxXFrF9YdW4hweH1eUfsbVG5oO9Cqx/VJpo5be 2Oo31vcW7usssOm+qsnNVeP1B9Rk+FRKoqKVod6g0VZ0tpVQfqNqajrWn/MrFVK5teKxt9Tt0pNC eSn4h+9Xcfux+vFVK4Kiz8wlhVQXLA1G31OLw3xVgPlbWLpNEnumvtUtnaeRfUt9OkvZeLPK0Ylh S3kcLGpCqQo5cQamtMVZhH540m1SK2u11Se5WNC8y6PqP7zkB8QWO3YDl/KOnemKpRft6/nS3uI5 5I7eVYpDbGNwS/K24O3OP4JFDcfTZwd68fh3VUf8Q3AgkY+Y9X+CIOT+gZgwCpyZqG0oX2NV8dgu Ks20WC9hsFF5fSajK5Mi3EsSQPwbdVMaKlKDxFcVR2KoLWP95I/+Ym1/6iY8VSXzdPeoyLbahqdi QYWrp1gLwf7tqGrDNs3EBvD4enLdVILDVNbvX4JrHmCMM8YBm0L0KGaTkByltx8KrVW/lHU1piq1 dX1Fop5V1jzEBdTqtvXQpg0HBasvBrWvBqfbZe+x8FWQaTpOt3Vra3J8xagY1V1ZLi0gt5ZDyK1l jaFCtKbUVa7Hp1VRVvoXmGNZVk8xTzBkKws1vbBkPHiGJCUbejbjFVraD5lMlsw8yzosK8J1FtbH 1vjLcm5I3FuJC/DQe2Ko/R9O1OzMxvtUk1L1D+79SOOPh8btt6YFfhdV3/lHetVUUn+903/GKL/i UmKq+KqFl/ct/wAZZv8Ak62Kq+KuxV2KuxV2KuxVBax/vJH/AMxNr/1Ex4qwnX5Xj1OULc6nY+o8 kfKy0/65CzFpeLhjYXgBBYV33anYHFUdp3lfVZrG2lj1i6agqJJ4bWKRl5qxV4pLFCKhONWXkATT scVTrSdFu7KzEF2Y9TmBJN3cCFJCD0BW3t4Y9vZBiqWebYryJbUWlrJDLKzR+tp0UVxKgbj8TJNC 6cB3PE4qlujaZquqet6Op6hD6PpiQXdjb2tW5IxKerp6cuQiYMF2HM9PhoqyjTNIubSyjguViv5l LF7ub00d+TFt1igjQca8RRegxVUubXisbfU7dKTQnkp+IfvV3H7sfrxVSn5i08wFJntnDMVuI+Je Mizi+NeSutV6iqnFWGWEXmrUbMytrHmKeWNqGG2XRLeQCQsAWEkQjPDiD8L/AEN1ZVklj5X1k2SF vM2twPIVmEU/6LklhJ5FomZbWVG+3xPxN0HE9yqltvLr2jeYZLdtZ1DW7dV4izuo7FecszIwCyww WzBhz+Gp4Abe6qsisdd1O5CGXQL60qyI4mezJTmwBJ9O4eqpWrFa7dKnbFVnmDXbrTp7eKGIN6zx KzFQ9EdyskhBki+GIULUqTXYYqhI/N+rtcQxf4V1R45uNLyJrFoAG/aPO5jl4j/jHX2xVHSXs17o 1vcz2c1hK91bhrS59P1U43aLv6TyJRqchRuh7HbFUt86WV7cBDbafq16VMTA6VqK2BBQSmhDXFuG 34hgdmqv8uyqSQWHmAgRvpHmNIryNhcO2sWrSQMshlHClyd2MSqCjfYehpvRVq20/W4fUC6R5odS 6yD1tXs2JYOCAP8ATG4p8RLAHotKHoVVUaXrQsrYfo/zGZRPKXT9K2vIBlCcpH+sfFH+7BVRuC1e INaKsi0jVNYW5i02bQdQhtYy0Q1S5uLKZSkYbhI/G4edvU4D9ivxCtPioqn+KuxVQT/e6b/jFF/x KTFVfFUFp0wpLG3UTTcf+RrYqjcVdirsVdirsVdiqW6xMCkMY3/0m25f8j0xVKb/AMp39xePcQal PZhpGk9KH6oyGpJ3FxaXB3Db/F1+jFUbp2hz2Zm9TjeiVgyi4aL93tQhDHbx7Hr8VcVRv1P/AJcb X/gv+vWKpfqnl+e9kt2hK2BgLlntjFycOvHi3rW8q8f2thWoG+KpXZ+RtUtrd4Trt9PyYsskx09n Wtdgw08bV3oa/dtiqd6dpE1pZpbzJHfyIWJurkxCVuTFgG9G3hj+EHiKJ0G9TU4qrvYF+FLO2j4y RvzU7jg4bb92N9sVUJCRb6+VryDsRxAJr9Ti6A98VYLY2FxeWtut1pdpc3BkSRorm+e0AI5q7wNF DWWgrUMv0+CrO/L2j6Xp9s0ljH6bXIVrlVuJLlfUQUIV5GboaioAr3xViXmiIS+bHrDFI8aLxaWf 0dnEYIIWIkR/zSepUHoviqp2mhgyPz0OwXg6Mjx63ctIXDhizN6Ybj/KK+x2OKs01by9Y6nNBNO8 qPbSxTxmJuP7y3ZmiY7GvEu23Q964ql8n5d+UZWV5rJpZFqQ7Tz1q1K0pIAK06DFVb9BaXo+nejp 0JhjlvLWSQNJJIS3rxLWsjOeijFU9xV2KuxV2KuxV2KuxVQT/e6b/jFF/wASkxVXxVJ4usv/ABmm /wCTrYqjYbynwyf8F/XFUUro4qpBxVvFXYqteREFWYDFUNNeE/DHsP5sVS28+xD/AMxNt/yfTFDG tctr+W8naz1HUkpcFXSDVLOIJyndWYJKknEKv2UPhSlRilBMnmKZysb6qBw5c/0zYKTwjAUgKj0L MlG6LUk74qyHyzf+ZYrOGJtIu54JXLpe3d/a3DemxFG5R9VpulK7d8VZdirsVdirsVSedXa18wLH USFnCEGhr9Tipvirzhb+a10RJ9f1DQxNJOfRl1iAy27KrTAiHjM/FqqSx5eO3cqp3pHme6mnt9O0 PWfLVLiNZbazhimVmMg5840WUckIq3IDFV2tyWa+ZGgu5IBrUlsr1ePnDxQwK7FS4cwc33j6n+bY 0VQmh69+VUthcRapqPla7eE+o62MMMKxx/DHyeJ5J2FGIBaoAFK4pp6Fper6Vq1r9b0y7hvbXkye vbusickNGXkpIqDihF4qgtY/3kj/AOYm1/6iY8VRuKuxV2KuxV2KuxV2KqCf73Tf8Yov+JSYqr4q k0XWX/jNN/ydbFVTFCT6l5u8uaXdG21G/js5140WbknINTdCRRgK7la070xS03nzy0gBbVUVOUkZ cluCtDTmGanFachSp37YqlGq/mDOl1XSr3SLrT1SNpXub6aCVOTurNxjguFKfu/hYkVNRiqhF+Yp ke2jWbRJGlZBMy6nMKCR2CFP9DblyQKRyK7mlejFVbb+ftSkktgw0QxXAekkWo3Mi1jKo3FvqIUk SSKtCQd/oxVOdE15tZsBO5tecd5bKBZTyXCcGkidCzyQ25DsrVK8dttzirKrnSNKuq/WbKCevX1I kfvy/aB/a3xVCnyl5VJQnRrEmJQkZ+rQ/CoNQF+HYVOKplBBDBDHBBGsUEShIokAVFRRRVVRsAB0 GKr8VdirsVQ8+pafBcxWs1zHHczU9KFmAdqmgoD4nYe+KqFkiSXGqo6hka4CsrCoINtCCCDiqKit LWIMIoUjD05hVC1p0rQYqq4qotZWbS+s0EZmqG9Qopaq0oa0rUUGKq2KuxV2KpJPrmjalFNDp97D dy2V5aRXaQurmNzcx0DgHbocVTvFXYq7FXYq7FXYqgdU1zRtJEB1O9hsxcyCGAzuqc5GIAUVPiRi qun+903/ABii/wCJSYqr4qxrU73SdLQT6lqU1nHcTTCPjGroCJSKVEUlKlh9o9TiqCbzR5SVo1bX p19VmRCYgFLJx5Ly+r0B/eL18RirIf0O3/LdcfdB/wBUsVd+h2/5brj7oP8AqlirR0QEkm8nqRQm kHQf88sVb/QpIp9dnp0pSClP+RWKu/Q7f8t1x90H/VLFUHqekvFbIyXs4Y3NtvSDqbiMV/usVS+9 82+XrK7mtLvzDcwTwc/WV7dAFEYLMxb6tx4DifirTYitcVUx508rtN6K+ZpjIAGIEUeykAgk/V6A MGFPHtiro/OvlmSFZk8x3Jjab6vX6uopJVhRgbaqglaAnY7U6iqrTedvKql1fzRLG0bMkivFEhVk BLAhrcdh9PbFVO68/eUbUxCfzNcKJiREwt1ZWIleBqMtqV+CWJlff4SPipirI7SGS8tIbu31W5e3 uI1lhfhbrVHAZTRoARUHviqr+jrv/q53P/A23/VHFUmnuItJXU7y+1W7jt1u4oy8cMczFpIYFWqx 27t1bc0oBue5xVDHzl5Z4Rv/AIkuCksv1dHECFfU4u/FiLai1ET0r3FOu2KqR8++URLFEfM8/qTU Cr6CVBaMSqH/ANG/d1Q1HOlcVT/T6ajZx3lnq9zLbTAmN+EC1oSp2aAEUIpuMVRP6Pu/+rnc/wDA 23/VHFXfo+7/AOrnc/8AA23/AFRxV36Pu/8Aq53P/A23/VHFUDq2nXK2qEajcfFc2tfhthubiMV2 hGKo79H3f/Vzuf8Agbb/AKo4q79H3f8A1c7n/gbb/qjirv0fd/8AVzuf+Btv+qOKu/R93/1c7n/g bb/qjirv0fd/9XO5/wCBtv8Aqjirv0fd/wDVzuf+Btv+qOKtHTro9dTuf+Btv+qOKrrOGSK7nWSd 7hjHGecgQECr7fu1QU+jFUZirBPP3mnVtBs9OGm3Fvby3t5PE73IgZQokLMwE11ZE8F5N8HM7dMV YnL+bPmL1Le7truzaxD+nc27tpbHksRkkT6zHqnpxuOIHF1/bXc0JCmlzfmtr6R0Op6fJJdSpHp7 BtLUSqx4c0H6VblyK1HftxxWlSH819dexM0t/ZW6FVnFzImn0ELq7KSi6s1KhoviJ7ilQ3wq0h7b 81PNhil9fUNPildYILfkmm/DczElWITWW5rIsT8E2Y9QTxoQtKT/AJr+b7qOeew1LThaRVjeX09O ZlMSx+rNVtXWPjylA4k8kqOXUVVpH2n5l63dSSi+8y6N5eigMgMt9HZThnjcVj42+quQVSaMNWm/ hUYrT0GDV9P1fy7ZX9hfQ6lbyz2g+u29PSkZbmNXKgFuPxA7V2woY95k8w+a4dUuLfSrxYY7YsWS a0tZxIKqwWNzf2rKQKr8Uf6qlVB3Xm/zSrXMVvfQUgb4JltbNk4luhQ6mjfArcTXjUrUbYqvt/Mv ne4vYoku41F4jzWcJsbZnaNVdxTjqdWJXiAyjifYVoqjJ9S/Mdy5t45IU9JXRZNOgkbmxNVqNRjF U5LXt8JodxiqZaZr3mKCCU6hpF7du7vJEsFvbQGNOShY25Xcqu3xE8gR0NQNqqouTzRqKxTOvlzU meJAyRD6rWQnh8Cn1+NRzNeRA+E+1VUbp+r3V3cNFLpd3ZoFJWef0eBIJFP3cjtXao26YqhLy2uL mDWILdJHkkuox+6uHtHA+rwVZZkBYEfj0xVIv0R5skLyPDfxyLFSOJdXXgz8lBr+52PEEg09qDqF Vn6J82/pFJ/qd5wErRV/TbFPQeUSGUwmHiW+HiF6hdgd8VcNJ83LLZ+nb34WL0o5mbWA1QvCSRmV oXEnxM0ddmZVr8NcVXS6Z5vZyv1O89J4gSV1niQ6bhNoQRy9MAsG/aOKrG0zzo8ouPql5DLs5hXW QYiUIHAg25H7xYhWg25NShxV0uieao4pLaO1vp0liMJm/TkisP3rOHV/SDq9D9odvh7DFWSyXOo3 GkxyajZixufrlupgWUTrxF1HxYOAn2h2ptiqI1PzH5e0qaODU9TtLCaZS8UdzPHCWUEKSodlruwG KpfP+YnkO3WNpvMOnok1fRY3MVHoxQ8Dyo3xqV274qqx+efJkhkC65Y/uUWSYm4jARHPFGck0UMd lr1xVfJ518mxsyS67p8boxV1e6hUqymhBBYEEYq2/nLyghYPrlgpR/SkrdQ/C/8AK3xbHfvirv8A GXlD1JYv05p/qQV9dPrUHJOLBG5jl8NGIBr3xVfZebfKt9PDBZazY3M9wXFvFDcxO8hjJD8FViW4 0NaYqjk/3um/4xRf8SkxVXxVh3nK70+HTIIb61vbqOe4mUJp181hN/ekEc1uLNnWjVKhjsOhxVie nedfJDXkd9a2+qyXNxcenx/T8UkbTwIrBAjaoYpFPPjxANTsy4pThfzu8vt6xFlIscSlvWe+0gJV ahlYi9bieQC/M70xWkRp35weVrmWN39K00+Vys+oyX2mmGCT0TL6c/p3LuJCUKBUDbg70GK0nw/M DyIeFPMWmsHcxKwu4CpcAkryD0r8J29sUOh/MHyHNC00XmPTJIYxyeVbyAoooW+JudBsK4qqHzr5 L9Ra65p/q1KKPrMPOokETADlXaSit4Nsd8VRF9d2t5pdtdWk0dxazz2ckM8TB43RriMhlZSQwPiM VYXr2t6bbX1xJDpGk3GpG4mgWS5gvFLFao/qTR2VxxJR1XurbgEjFUO/mO0tre6Nno2jRNEEMAaG +RGRqsObJYGhVXVqJy3JG1K4qnUVn5z9K3vrTQvLn15VrBOLi4URqw4gI4s+ZBQ/5PWnuVUykuvz EMSejpukiUGkokvrjjsTuhW1J3FOuKrGufzL9SPjp+jGMqPVJvboFW5mvH/RDyHCh7b/AH4qqx3H 5g0T1LDSQTJ+8C3lyeMXw/Zra/E32vAdMVRenS+a2uqalbWENrxU87a4mlkLFW5ji8MS0DcaHluK 7Cm6qG1BrlbfVvqzXCTNeQqslp6PrJWGAFlFx+6PuDWuKsTa582i0aZ9Q8yIoilLuYfLwIZEdaE1 4cibfkKbAyDlQcgilfaap5o9S3uzda5Jbxy/vY5Y9CMc8aM7FSYpFK8wgVWVgfiFe9FDIv8AGt36 ip/hvU6t/laedypYCguya/CR/ZiqnceeLyJo1Ty1qcxkTkEWTTgwYOy8WD3a/wAtaqT1xVcPO16Q jr5b1Jo3Z1DB7A0ClQrGl19luRNa7U3xVuPzreOrM3lvU4+BIdXbT+WyF6gLdt3AXr1I7VIVRSar NqekJczadc6YwvLZFguzAXYC4iPMG3luE4mtPtV26YqhfPXm/T/K9ra3d3p8l/8AWJhBEkJtVkDE F9vrU1uDshPwkn2wGVJAtA6F5303XdOlvINMEMFoZRJFM1vO4EaxuSi2L3oPL1fs15bdOlUG0EUm UWv20ljNetaKloscEjs8Nwrss4qn7poBJtyFRx+E1BoQcKpfF568tTQpcpDVJGAVjY34bky86EG1 DA8d98VZKLaYdLS2Hfqe3/PPFUNqbPZ6fdXr2NtILWKScpy3PpqX6mPqaYqxX8u/zFTzVqk1pJok WmyQ2qXsUkdzBdFklbiK+kPgbxDb+2Ks6T/e6b/jFF/xKTFVfFWF+dZ9Gh0+2k1Q3iqk101ubO+/ R5aUM1Iy5ntlZmBPAE9u2KsY/SXkaW2je4TWbQLMYVL6wiP6oUMql0v92ZJSy79j/k1VTW0/Mfyl p1nb2aWczhT9XHq3WnzS0QgBpZJLsu1exY1NN8VRR/MXynGDIdNKq3ESMH06o5sqxcqXPR+TFT4K 3tVVFWP5heUrq4MUkKWiqHeeaeWw4Rqic2aT07iRgOo6dQa7b4qm/wDiPyWr+mNS04NyEfATQV5l WIWgPUqrbe2Ktf4o8lCaWH9KaeJYZTDNH60PJZQRVWFdiC4r7nFUbqojFjCIwAn1i04helPrMdKU xVi2seb/ADFa3k0VrAzrFLMgT9D6rMHWL4gwniUR/Y2FKhz9k9sVUH86eaSpMcDBuasldF1Zh6Do 8gJK0+MBArDqG2IGwKq3SPOPm6X1nvYi8MUIlpFoWrQSFl+2ipO3xM3E8QD4VxVDHz550hULPYO0 0qj0lj0XV2VSSppI0YmH2CfD4utBiqKPm3zswkmigj+rIjOA+kassp3NPg+0SAvRVJPt3VVLDzZ5 wu9ThsBbrG9xGHjkl0rVIYR1esk8lI46xjjwYhg+Ks6i9X0k9YqZeI9QoCF5U34g1NK4qx7WNLGp 2GtWZit5lkuo6xXdst5Cw9CCoeBivIfTirAI9Fb0R+j9KjtFaWW4a3PlVF5r6XFkcGaNPVdZXQOC KhitOpxVHafY6TFJPB5h8srqwjkje3kt/Ln1dDJMxkdxV7ioReCtWhqveuKpmbL8v1tltx5AYwCp +rjRoeCgPJHypx4/tMaLU0etKE4ra68HkKFaS+RpZROKSBNFWSoAMYD0TurEAeB+eKr5NP8Ay/aJ bdvJTGO5DK0A0hQtJFi5FwE4ivCMVP8AL/k4rbmtfIk8TW0vkqV7XhzNvJpIMPwrzoIypXkPSFKD rQDfFU/ht9Mg8v2i6bYDTLNri2eOyEAtuHK6QmsQC8STudsVQ/nC/wBEgSNNW06HUIUeHjHMgkPO cui+mpR15BUcksV274CWQDcK+XLPWV0e20iGESM1XiiRFEhj9UkgKqkFRTYlq/s0+LG0Vs3oWp6J q1zNHb6eIlTlxlaMqG9NgrCpVRUcwfhJ2OINqY0nf6OsP+WeP/gRhQ79HWH/ACzx/wDAjFVr6Xps iMj2sTIwKsrICCDsQQcVUdN8vaDpcjyaZptrYySDjI9vDHEWANaEoBXFUSn+903/ABii/wCJSYqr 4qw3zjcaOllaw6m14FlnuTCtlcR2pLo5NGeSWDx2+KleuKsabVPJk09uPU1lXDrbb6hFGokQLxST ldAFnUE99gxOKoabzD+XMkV5E8OpRfWSqz3Iv7eORaOUZ1YXgZac6MQPxGKsjuvzA8paWiWj6fLM tvGscjB7GUoFU7Ss1xXkqqGYHejA+NFXXH5l+S0glmWxNwlu1CYzYnqWCspadRxbs1d+Q8cVdN+Z Xk2NXaSwaqMOQP1EEuxI/anG/wAJO/Xt0OKsm0a603U4WuINPMNu4DQzSJFwmRySHjaNn5K1A1e9 RiqJ1j/eOP8A5ibX/qJjxVi+peZ/MMV3P9QiuLpbWd4bi0hsoZpOHqUV0JvYT9gHsSevDahVS+18 w+eRO0b3bXCs7RRH9CNGaxjkRyOoAM7RxvSi9SDx4jiVXXXmvzcJeSXBt7WWohM+jsh+N+MZ9aXU Io/g5jkSu9KimKuHmjz1yHqOYAsrJN6uhTfsioC8dRI4ulWB5Ejavhiq+TXvPkUMLtcUYvG1yZNF lREjU0nqwvmWP4WDLyY0IbcqfhVVv8Red24BY3R44gsytpDtzmrT1V4ag3GI8hRNz8J+PwVTXyzq XmDUdTlN3eJ9Vsk9G4tP0e1q7TtQhxI11c0CgH4ePfrtiqZAXBbVfQEZP1qP1FlDFTH9Xg9QDhvy KVA9+xxV5pamB5ksBFbfvp+CMNN15UFwJFSrSMwVY1V2FSwX9qoFcVWHTLaVfrVzb2P1n1oo4Wl0 /W6rJI6yp9qQkLRKl1+EOFBptiq2+GlG/Fy1vZq9JZRJNpeuisbepwCbhQwRP3lBv4AEYqmml6Bd a/F9e06DTOESmKt3Z6rbSlyUckCeSF/TLJX7JB8ahqqo1fyyuEia3Sx0UWohEcMHp3ojFaF1Mfr8 OBPPYDuOvdVM9O8is16zaxbWEtmFDwx2v1tHEwkaSrepM6FayMfsjf8ABVPLjT7TT9It7OzT07eK 5tQicmalbqMndix6++KpB568r+d9Z1fS59A1ex02wtlc3qXdjHeyM/JShT1BWlKghXQ+/gCGQIS8 +UPzfa49c+c9OSYAKJk0aPmVB6EtMTQ9x92CimwyPyhonmXTBdtr2p22pz3BjMcltZpZ04KebScS 3qO7GpOw8AOmEBiSyLCh2KuxV2KqCf73Tf8AGKL/AIlJiqvirD/N2rxadZ2nq29vOtzcXMMbXEST hZSzGPjG0sTvyIpxSpOKsUi89OZVWTTdP9Hg1z8NoxYhRwjYL6h35NXavw1HXFVmiedTesEex0qW ZGRLn07P0TG+4k5CSdgjcl5KrNyoRtXFVC3/ADHE1tDIumWCtO3G6LWLrVqhHoBK6ks7cBVzvXqQ RiqO/wAYXIEkdnYaV6remjRG0kVBIZeNJHR2+Fq1DFfhHxbg4qqP5vmFqxutL05GLPJcFrflE6Re m7EMJGUkRmT4qsK/IjFVafz9r9vDZi2WxtYWRjOkltL8LOWWJOIniKGpVj8LVXp7Ksq0/V5NV8uW t3MYzcNc23qiIFVFbqMrRWZzuhB679cVY9r3kfQrzWrjUrnUdIikmMit9YsLOSUryIMbyyMGZVcG vviqX3f5f+UpmX1tU0Ivcz85nk06xMk80Y9Ib8x8cbSEbDYnFVT/AJV35TVQYdW0qGASMLyNLDTT HPYs3rG0l5KxEYaMMCrD7JqD1Cto/RvJPkG0nnOofoO+mcKFKWlpbyKPBijMWDBV6+GKplH5R/Kc Oksel6N6luqskgjtiyLuFIalQvxGmK2vby5+VpeS5ax0gtLKJ5JStv8AFLyeQSE/zBndgeu598VR eheXvIFlectDsNNhvIi8gNokIkQyAI7Dh8S8gAG8cVbv2QLfcyq11K0CFpJIhy421PiiDHr2I4n9 rauKvO9NlgSzkuYtQgR/qnC3t4/MOtOvwRAuvoyRFouHIcWEZbfoD1VW3F3CbyO3m1G1RLNI4nLe Y9aDl7qItUExAS8Sx4PyJp/KRTFK6TVOHrSfpG1hupkjZw3mDVqFUkdHWFpIEUAEORxArQFqLRsU ONvbp66WuqH/AEgyRUTzLrDH0xOv7xZGjbjKGZQVWp+IrVu6m2S+RfNVnDpqrfXduI5WpEEv73Up lYERgO11DHIoNPiJ/a8SScUMosfN3lm/vRY2epQT3ZPEQI4LEhDJQD/VUn6MVRWsf7yR/wDMTa/9 RMeKo3FXYq7FXYq7FXYq7FVBP97pv+MUX/EpMVV8VYN55Ty+9nZrq8l0hjmu57UWghLl4nJJrMGo wr8JFPfFWMfov8vpoJaW2qsyiReSpAJI2t4iGQMACqyINuX7s7bg4qmVjd+RodS+ty2t9fNdM0ZF zawzwovqcFfkibIPSHE8j8JFduiqMu/Mn5cxtEsvl1pDUJHx01H4AjnvRfhG5+kN4HFVc+Z/IUCx 236FMdWV4rb6lEh5s6pVUNPi/dq225Cgiu2Kui8z/l/FJ9Xj0YxIWaAMLBViIfZqNxA4Mq1r0IHt iqhP5t/LgiGW50SvKFHhd7CNqQupK7gNxWgYe1MVZXPFpqaXC+nxxR28lxZkegqqpAniVfs7bKAM Vef6zJY3PmG9LNA9tbXDG4tTNo7xSO08UDQz+vDJcRFvt9jVqculFUri1nTOENudftpbpyZVma88 uCSaWT0v3RRIeDMGVgGp1rufhoqpSXWnTJqAhu7QRcfUtJ4bjy58QZ2BVKwUKhJilZNuv7RriqHm nsEvI1g1e0fUQjAAXHlj1pFqJI56taEgUYDt0rxb7WKU00vRNX1NLiWynOqw2hls3WE+XplWTkAI 34WqpG8S8JVX6GGKEevlbzNKOR0ie3YAHcaEfiCE8hS3b4uVVP8ArnqBiqc+StG13T9ZE95pL26y 2yQzXDtplAwRXZgbOCGVi0g4FT8GwKjFWTerdRNqbW8Qmf63ErqZBEVjaCASOGIIqiVYA9aUqMVe fWGo39xKY5NVkPFI3u0j1+0dYgjBpGVlt45DQRkGvGor0xVyaxeg28k2rBQjuzD/ABDa/HC0bFHo LdQ/xRDqRTkxqQKYqm1t54vNLsjDcpZXH1VDK89zrNq03F3HBmPpxjjxc/F8hvXFVdPzHvWiDfUt N9UnaP8ATFuQw5stVbhv0B+nffbFV3/KyJjNLGlrpzCOQxKf0va1Zgx+HiFJDBOLFf8AKpviqq35 gyemrrBpoBUOTJq1uoKFwodCFaqsh5qTTqBt2VZFqE8VxptvPEyvHLPZujoQykNcRkEMNiMVTHFX Yq7FXYq7FXYq7FVBP97pv+MUX/EpMVV8VYn5q0TWNXsbaLTkglWK5ne4iuZ/RjcF2UIym2vFdSGa uwI7HFWMv+WnmF1CCGyhEFwZLKRLss0cEhHqQgSWDBYvic+klFP2TszYqp2/5decfWCXFvprWxWV zKtwgnE8samvJNNiC0mQfGtGpQ0NAMVWr+XPnJp2MtppXF7lJZH+sghowjRk8P0cKSIDz2ahYk1F dlWRy/lR5ekCmOSW1cS+tWKOzZg2xAEj27SfAd1YnkPHtiqov5X6GsUMS3NwkcEkcsaxpaRUMUpm UD07dKfE3ahp9NVVunflZotnEkZvbu4CJJEPVFtvFJUcDxhXoG+0PiO5JqzVVTaPQ7DRdFjsrFAk P12CU0VEq0l2jHaNUXavEUGwAGKqy+YfK76v+hzeWy6tK7Kti5CTSNGpkYojUZ+KoWqtemKpj9Tt Nv3Ee24+BdqfRirvqVny5ehHypx5cFrx8OnTFVr6fYOKPbRMNtiikfDTj27UFMVVo4oo6+mipyNW 4gCp8TTFV2KuxVAWSlrnVVB4k3AAYUNK20O++Kse/wAAXpmZz5iuihIKx/VNMCqACpVaWtRy5Gvf 5Yq035e3DTI/6euAiUAjFnpe67Fl5G0L0cglt96nFV/+A75pzNL5huJCSoKmy0v7APJowfqteLe+ /viqbaP5bisrEW99MNWlDs4urmC2jejHlx4wRxR7Hp8OKoxdF0dJDIlhbrIxJZxEgJJIY1NO5FcV XforS6g/U4KgUB9NNgDXw8RXFVLVUSOxiRFCotzaBVUUAH1mPoBiqE1fVtftrp4dN0qG+VYhIrve LbkuSRxKGNyBt9rFVHT9e16a9jivdLt7W0JcS3C30crR8QaVjEa15MANm2+jdVPDdWwNDMgPShYf 59sVctzbMvJZUK77hhT4TRvuPXFXG5tgeJlQGtKFhWo7Yq76xb8gvqpyPReQqaf7WKtwXEFxEs0E izRNXjJGwZTQ0NCNuuKqaf73Tf8AGKL/AIlJiqvirF/MM2vw20Muk3otOL3RmRrQXayfvar/ALut yhA5U3oSd+wKqrp3mi4FrbJe2N9NcuKTXMdk8MZaoFfTMkrJ9rpU+PTFVa282R3C8k0rU0ozqRJa uhBTj2Yj7XLbFVJPOkT8v9w+rDiOTVs2qBwL9ASa7Up1qaYqvPnCENEh0rUxLN6nGP6qxI9Ib8iD QV6Ka79sVWDzrB6vA6TqijiDzNqQOTFKJSvLlSXl06Bv5TirUPne3lkRBpGrL6gQhms3oA/jSvTu emKprq7KbRKEGlza1p/zEx4q821nXLhNfvLcXX1WIXM3G6/SbxSBoiVEXptYycUdpQPtkL2P8yqA Pmm4t47cPrfOJxDJC8usus5WTjz9RBpoNFZaLUVYVJpXFXS+Zb+L0mOt0SRAk5fWCODlpRyRhppD AqqMGIArt/rKqs/mW+tLeS1fWApkhtpIpZdao/7yIuOL/o1tqnrvyG5A6Yqvi8xa0Ll3vL9o3VpR LGmqM8CzExjgrLpwoiu4Rg+6dffFUNF5j1h6CXVWT0nW3JXWS5dg4YBuGmCrsrHiF3YAD5Ks+8uL 5rtWihktkutNnKSte3GotcTxq0fxKi/VUDqGUU5OCak17Yqnmn/716n/AMxK/wDUNDiqNxV2KuxV 2KuxV2KoLWP95I/+Ym1/6iY8VYB+YP5e+Yte8xS3+nJaray2P1WUySWweWgkYRyJcadengXZf928 ab8SeqkMNb8h/MphJjs9Mt2kt0WaCNtOAaVJAAvMaL0KL6han29gAN8FJtGWn5J6wqwLPpOmnkyy 3n7ywo7JLK7RuBpFJFcPH2X7P7JHxtItQtvyS85t6puxp0c0s0RW5iexeVIeLLMAf0PEOR+A1pU8 QtVG+K23ffkf5o1Czke7g09tSSRikjPp8nrxymrq8h0ZfTKt8SkRsak1PSjSbZlY+R5p7vTxq3kr y6yRqEur5ZvVlj4s/EwxmyiDbNy+0nxMaDbcoZ/ZWNnY2sdpZQJbW0QIjhiUIi1NTQDbcmuKHJ/v dN/xii/4lJiqviqT3ujx6lYOo9NLtHuFt7iWITCPnIwf4CVDBhsRXFWKSfk/YzFBM+nvHEjJbg6V bck5Ozda0+y5FOPXfFU10r8sfK1vbxJqGnWF9PBJ6tvKtlFAIiY0jIRVrT4YlHWtNjtiqOP5feRi HU6DYkSqySD0I6MrsHZTt0LKDirSfl35ESf6wugWKz9pBBHUbg+Hio+7FVqflv5AjeN4/LunI8Lm WJltogVdgAWWi7E0GKrx+XvkUMrDQbEMkTQIRBHtE5JZOn2SXO3viqqdD0bRtKW00myhsLZ722la G3RY0Mj3MXJiFAFTTfFW7jynYzyySfXNQiMkjTFYr65jUM7KxoquAB8Oy9AKjucVUf8ABOmeksQv dUVET01A1K8rxoBUn1ak/D1O/wB+Kqn+D7Dnz+u6lyFaf7kLunxBgTT1KV+M/h4DFVMeSdN5xu17 qbmJw6BtRuyKhWXf95uCHNQfbwFFU5srNLS2SBZJJRHULJM5kkoxrQu258MVV8VdiqC0/wD3r1P/ AJiV/wCoaHFUbirsVdirsVdirsVQWsf7yR/8xNr/ANRMeKoTWNG1m9uFksteutLiAAeCCG0kBI5V NbiGVhy5Cu/bbvVVAw+VvMsYlDeb9QcO5ZP9HsPhBJIFWt2rSv4dMVTLTNK1S0uDLd61c6jGY+Ag mitUUN8P7ysMUb8vhPfjudulFU0xV2KuxV2KqCf73Tf8Yov+JSYqr4qoWX9y3/GWb/k62Kq+KuxV 2KuxV2KuxVJpYdWi08DUrqG6lN3alHgga3UL9Zj2KtLPX78VTnFXYq7FXYq7FXYqg7yHV3u7V7O6 ghtEJ+uQywNLJIKjZJBLGE2r1VvwoVWtP/3r1P8A5iV/6hocVRuKuxV2KuxV2KuNabde2KpH6GtQ aWF1e8gvbg31u0ctvbtbKIjdR8EKNLPUr/NXfwxVPMVdirsVdirsVdiqFvItTeSA2dzFBGpJuFkh aUuOwUiSPj3rsfo7qr0/3um/4xRf8SkxVXxVRaysmYs1vGWJqSUUkk/RirX1Cx/5Zov+AX+mKu+o WP8AyzRf8Av9MVd9Qsf+WaL/AIBf6Yq76hY/8s0X/AL/AExV31Cx/wCWaL/gF/pirvqFj/yzRf8A AL/TFWm07T2FGtYSKg0MancGoPTscVb+oWP/ACzRf8Av9MVd9Qsf+WaL/gF/pirvqFj/AMs0X/AL /TFXfULH/lmi/wCAX+mKu+oWP/LNF/wC/wBMVd9Qsf8Almi/4Bf6Yq76hY/8s0X/AAC/0xVoadp4 JItYQWNWIjXc0pU7eAxVv6hY/wDLNF/wC/0xV31Cx/5Zov8AgF/pirvqFj/yzRf8Av8ATFXfULH/ AJZov+AX+mKu+oWP/LNF/wAAv9MVd9Qsf+WaL/gF/pirTadp7CjWsJFQaGNTuDUHp44q39Qsf+Wa L/gF/pirvqFj/wAs0X/AL/TFXfULH/lmi/4Bf6Yq76hY/wDLNF/wC/0xV31Cx/5Zov8AgF/pirvq Fj/yzRf8Av8ATFXfULH/AJZov+AX+mKqkUEEVRFGsdevEBa/dir/AP/Z + + + + Adobe PDF library 10.01 + application/pdf + + + Administrator + + + + + 013.01.001,X5键盘说明书OMOTON-1 + + + 1 + False + False + + 841.889648 + 595.275391 + Pixels + + + + + ArialMT + Arial + Regular + Open Type + Version 3.00 + False + arial_0.ttf + + + + + + Cyan + Magenta + Yellow + Black + + + + + + 默认色板组 + 0 + + + + xmp.did:9D6213CB51F7E711BEE1A066FE3B8D58 + uuid:a9c9cda3-a094-4e26-816e-bdd379e5d693 + xmp.did:9D6213CB51F7E711BEE1A066FE3B8D58 + proof:pdf + + + + + saved + xmp.iid:9D6213CB51F7E711BEE1A066FE3B8D58 + 2018-01-12T12:33:52+08:00 + Adobe Illustrator CS6 (Windows) + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 8 0 obj <>/Resources<>/Font<>/ProcSet[/PDF/Text]/Properties<>>>/Thumb 375 0 R/TrimBox[0.0 0.0 841.89 595.275]/Type/Page>> endobj 457 0 obj <>/ProcSet[/PDF/Text]>>/Rotate 0/Type/Page>> endobj 458 0 obj <>stream +HWɒH+th I^m`.3K / hryY!谨̗/_^LIft'q:͢I n<~%q2k7|o K<-M4oQo$JJ/˻kb5osbqatPaYf #;g es;feWVE:߄';qÞ8ґpX"q|]#)ot[57D!3v0`U Fd:^ה??rS_S m/̣_ + V+#&ܸ.vTYdU2cY=՗خRRr0I}_߬iz˝w:2@_XܶFTeE/X78`H<\; 0}x_:}S 6Q+=@Թ;@(7G %|ul,krlt!d;5='L{V$U#WJ%5DoayRhWl;oui(qΣ펇$ZM[X\ 8+ɟ~iTh,a*]n&m%RrB5FbP eh//~\3S Hb3 .b5ok7sE89F]& t< +D21ӧtI_TNv1Eұ֌|8x*6tL3h~I!d&Qkmb#.+"t{>O_{:ynuK/$*R$ Կv8.Y59K>;]<k獡1jr^1b0]&E/φ9%JإQiɇQsGMZխ~`h |jB:W6ړfBq \RH +؆+JWuZbw^(5fH@Hem]o~!8Uov'*T's;y؊L7d?ӑ"!~7y[շm~\tAiCUc.nGN2؆fxe95h  ly}BhUd)~žN^EI02̢jIư +_L)BЦ@bV?GHy-I|pIS_.,etH(uD:fq7&ͳ8;ܨ1iӃah7`W7tΙz/'Ӑg!W5m, +&N T MYrΈ@ _Ēy @9!kSKI-%zERг"R,С[Ѵy)Hݲl5bǷ.A`_/kNNInCqhŚrt,9/!h=ҢM3O2@#A](nuUCr!NxJTݮ6%k҉o#t/wC)~abB:= +W>ӏ s ͜7 ֙N-$vGs-˟;jЎzSBgujqkɶ<ːà~t +Uy.TSڛ,Xbh/O Vu +h)seVF6õ,cPcE}r16k'lRZ(;ћdv9\,:N4dYF+;A`'AAH (8R*6 'T ZlnQC~âMVΤUGM%b%f E7 >^rEx&t/軋T> endobj 455 0 obj <> endobj 456 0 obj <> endobj 462 0 obj <> endobj 460 0 obj <> endobj 461 0 obj <>stream +HuPj0 +tM!nt ݘ{յPFq:i2H=ޓSܐK ?8 ZG#v(:i(dOj*!?3'Nn^_J9z؄QOE]6tk (1"\,)>jCu}iH78[0l'Uyϙ#L58w +endstream endobj 463 0 obj <>stream +HTOHTA潷=QddzBk|cak'/C޺UPAle!A :ԡCB:7fF03of~@љ-B/ i3EHО #vϵqpB!3:d XytMs}MvzۙrהWbo[Sx;q O3K{ˌyMrܝr}5/F~U@GMM\im^rzoG!QJT֢^z~9NoU򜗮E>8vc/rNaZсEo 'pؒsa)8 &w k l;xd> endobj 372 0 obj <>stream +HdWˮIWt93H,YF-A\#ƀ}9Yux?>?t_>H~0;_?O9 +H834/gWpn^xn Wov齍U_|YzV3nΆ[eʍY$e*@LڙMN!w.JϿ<ٽmNΙ) <,Jwxpq4L^rfa_'鬓c:N*['[9ԋ|6RM["]qjh;g/|;)VbCSzÐwB~D =u]0i`<76"u2>gy RW8],$H_lҒ :7CƕH'[]FIn7UO@2M?ɵpJ[RSx/^y<M- $3VgJSF!,,C;0Qgʁ0Rg`/YN) k!( 0Xp%4SFB*um"6`0 r]JmdY2Ul^#T- szX +K&V +Ay+dgr- +F3]v|*`ا )P]QʱEfU 1qPtMsx~V5  +$MVv+c9~VwULT!{Im T.(p+4@u.kA7 ^AKx1֑%*U^pFn.fԖ`pQ9&5JhO[t\mjyDRjIhlFhL" +ύm´3.̋`G_~}"vhLڨ%t ٬z-,[H(3vߚfЯؖԀ"#1DL蹤IH@~^V|q {RF|=RPVh* ?645A@BbULcsiM(D8"ȡ3" KLTuz*ߋclCeQ.::;c +9Tj2 [ ! {cLJ o[]+jŽ䂰63T:Ki.iT=mVzEg4L^ d*`] 0XAW.(oh +{UsLI<VR>ui:)5AyRyɶ7 }Tm.WYЙ ͌nJLRaRhYXTkPof6e=f1G^ÿk, ]mhhg('ymy$D.Ka^{Ec؋Zյ P~ V;MQ37wK}5C86'v~J@Tc',ڰz5 iSwzlZ Xkk3㭯(wڸ*|ⶮZ$a>4_0"Y 4F M2?7TK.i & +o06EU>)ڴJz po/x` #! ~ \™nj4s6FYs׮- Ɇoht30$]퉈ez=VVx!5:i;F"y`N4rEcv\HC*p(sj*$f>c_: 7HL>z+2"#QVDc~AkĘnV1"WJ[dJj A=lmJ8ͼѻi݂QbX2"Egz3 )M_ޓ 9R|I,iC?*Lv/rhuGK-yWײzȢ4iat}*) ,0;T`ha/e0iYQfo<\J$9)?rv^&*Q6+ +c!_ɡ4uM®:[PES\Y~E҃+L*z|0-ˍo-%&GrnBIpfMABɬĶj $^ WXMYCL<_ o, 1T4—oبO_ ;m~m<ת&X~))2'8NMbzKIj8V2n-~SB}_ >2fZrNUlKtJ:iF,wdO쐟thfq^(Q_MP!bGmg/bF"0Е4iFӉ鄮iH#^@HZ5e.@`´}kEt.5ÒE4<$ {%-(5MH*v+}Ӄf tw.[MEoӋɧ@+ͧ SƦSYͼ.jSȂNt +t +9O/2nlF%ʇRy.%f *[T`iT˨n#EZr*]M&`CPtH Ƹ|Jjz ֕4ɬP6+W*P'lJT/cs*yȜoqKj`s9XlExpjjXooz?E яjY^ kˎ6̷QOWBT(99 +TnnM^+K5ASt뒃v9?Rq/`1,Gl+s eGDRC`U@2}֍ܶȘy[ j>PlLӪ&"*-seonvGFtV]%ݵ9 +83fv6wO`oAW|X[n\?&t1tڶlLag%wZ(JdoT'-B5I 8YG 2Mn4UQXSʋ?ZYvUV;eo)[)Fo3gjY/d_}ؙVJX7|Ao$+.x}nVdQo5 Wi@?<ĬGUw*}nЀs"u7SUo#ZֈVo(Gzowun[z[5~~}~qG~ZV^pOvg6e`U.WMklToozoco Fs +f۾Yߩ߶ i.f!4R)-O!v +{? +z){3o.s:ΙGNQd+֒ئyJ۶ }>GiUVcգ{kxY={)J܏xnvOX +h[HeOñu|`'ԉt`lxZ\-K(x`h3u(ìhGDVCi,WcBW2 CbmN?kc++qBWzjʼnb)~~ޯXہW )ȄڛW:l(ϊ9Cg\u}Gbdy!*࿵, Z,oKv{*GFbl֏@fZy!GU("vnpcT(>l:[LV|[XrM~m#l.1\NwdME_[3xQknzVAYXϳN}5fvGM~8s>\P8@k?0ͳ\vWbђvq5)nFQj>uԋUxة᥺FLѪm!r-/~T80f I&B x4sOWPus?ݶYQs#sñݍsuzDjؗmۜSpnEY +h•Z?v??LH0xBGP];W%eW^%8D eD@~=:|v@&!:ajSdrl +UT8zZ-UW)qo>~g{+ptឰJe_.q5yYİmbRej~}vLUD4?͸u]m{\Eqpxq5>SS8[ƿQQA.nd1_g (- +e{Fϣx ʴ<1tz +My\kOUadozMG/0%[wl<\RY +T3w.*nH{:j]7J(>>r۶ i8J;2M"u +I>Sj1- UPnZMo~(+9;gÛO3Mkc@@]G)3mW܇Up>PqDn|ыrz%P3Yh7&M@^اqiq'h,tp"h͙l琨[eD92VYEVnN~L3BB}N.Lo@/Dܦg+D}mtZ&$hgv6pM4,v}+xbaFvR&~K='%~ вL2=3ɖPYۖTb w M2FvgwT!׌WHhjz_A6?RŸ]} +=@(=Є63P#괸@P *.!Ls*ont=\4ԋHzN#Wt\-!RN˛Z8fMg7voUyTˉZs\$۹]JD=?#b%7T]ayͻ",E1oܕuoGj83%"۞~a)wWVqtVNK6$jR^+ ~l#bXc()8#7g߾GNNJ%Wϼ(uy.pZ@bw e=UӶ:`Z+#I@qωϒrCKRԫqd.fǕN4} ڞlLsw.u9\S:^&Vpʜ@(:'Tkߐ)+~rtR= L/+(8imU's,TPW[k}n1@&wֿ~EdU pr_HUpg73xo37@MXw2ZԯI}֫ۀ!u3AAZ{M)i<̔\ET +"¶SE#_0b):'r4=>`R@m9lGWbe_aCW&Ž4c3GJ}3$8_ _1$6($ \mzUq&UGƣ:Bg>p4cz"f +?TGyƣ:AUD.Yu\D{Z%>n>o/xUNj:qUGpWuvUѬ r̫+ ٮ^?V ;I`^>gѶ /Lr֙$GC%|]w m^rlqyCx*8ʗ0\`iaήWg{Yɬ:kvcUD%mM⢹YVY/Xز?㊅xfW*D^v`_D][3&+ruRuhԠSمx Q֋=u C@U92D'roSVw꧌1 yP,FqGYU %onkr>n#SԖvl(I:ѪFi:ɠ̓/^j[Y{16D;LU|u)VөkC[G`- v$'8R͢PpDTXv *{hqnl̴#vB4@cZ؝<eghkk>յifop).nPfw.ֵW.Q\n*I}xJ6TbށҸ_,3Gb":zBcBg엓L&Sc*IGw"?23 ea%@2+3=X<<`9~H=ys6ye)3 O# bNk{dhY=#6`3c/iy)Z:'%ti)h +Knn:HڴfiXfanFϘj +J(x<)懃LF5.e11#AK.U!2ء-CLkTjЭ=Cvt+JgU띺}ԿX|:^^f.LCCOakH@ȄcsHQ~t95<P,XADQAEuJVBl5.}(=~ S"1IЂ#\vIpBkD5g%[H<1~I2Û@+}̹gps?+WpmDK]>|Ѿ!^aClP^zq|~_E,oQheܧ`o*" *Z;8pgr u2Wjoo{b+ dyjϕ;שk&LLu( +l ΙHyU.+Ң 4q8yҪEW.R~Ƈݍ -uߓI Y%T +|w?yӯ|#*3k* 顦o1#x,䲪rKVz^FC-9^h.B{̢ +Ecmޔ~L <(qr $3ȷIlc+&Yl\^ذ+YgNlNvH }JB3R/w{gǸELQyE==Y"0$(\d@](Dfv$E:?VmWAPo bp(Ioψt̖ìr}uH]WWJQygo4t]_"0bBgxDFRL3QSݱˮK 8t<:}nҳa/+:;r/Km;C+[~ZCR0% Nk 4o2*1h#C;0 ק2n=;t$eya)BTY G94uãpbUC"# \THo(l߭) +bHSxZe8tʺVgEҺ.Vt{@mkSW5Nܜw+S# L1>rp2=@`9"#p;T>M CSJ]Zzk=jC )>1t' +S~U9]E5q JRF76 9&IY%ė|]IbfY! ^ a1(Sa/VRelg}Ca >଺@Y;$ yP pʹ)%&DAPղ{WN}y%k, + I1RpKtC1JT賆 ' Ŋ+^!:Z9G4ag*|en71-Mכ aGBFBwQm$tz$T ͞#o&“3Z#W\dDsWF鵹2 ]mpeh4ve4Df('gHeȿ2z"hh|grgWwxdtX>>VF?-Sydh| w8u>9>5WFϐZU8/MJig^):I"TJ]%ɖ8n_ 0Mp2EԦh{<)=@K릴OJ+RZw>)-w(үWE#8EZuhUw7Þ^ O^T]W/^Z٬Wh֋f nD<]'jhָ*5bED2h"U$9ks +A7-A Kj.Q﹐*(#bx*Y;RϱooзcB(y@-`8+r /8?j/sH {wfJ<1q[ӵY'ͪ ƾ 8.)Q⚃'*ĩƂKl7l* YTc2TW= u(H xq9K_: OlZd_sK,<Ȩe*~4 +ev mיG|"3е},cXiP"\=Fdjo{CH6w(zZr~ׇl?":Mglti<< uyksQ2Ҋo8 ~)] [{i?y-1*7M(N垖%2$f2 (r5DPZ`(V3q +9 Isk*l"dkDr3s<}8a:*Tko{*TtR+I[+:]SzYٕfymXz>g ?(z!rglO>gzC3D{׳_מz9>}kQI;nOIk{KJ٧a)z=%&<%-yK:cf#wrkZc[șaOMs}<5I}l5S%[Ӏ#?5OI']Wǻ|J~bFIUSuOIqKs+˪GbV;;@SlThƌ6>0JQ:O+?mW $~|a)}/!3sNl+toѲ`T>LX\LM}fSI0oL׷VgslTK=|] g{ "@|%9xvOj$e+y!ْ>(DEX9~ag<bw9jxy:jc$Yr{(;r|9i42[j%v,F1{on  #ad%=#zDzW{^^؅WWVDT{Z^8BK|:7^S| wCŷ?EkNTʱ‰ +X +ZQSy/jF6fOE#X>J1y]J60;N|]2[3OD]xϡWE\=n*M^pvC2͇![f)y1zMz3zj^r F!2>4nFSޤ7}2:2sh}ݠet.C 2: btLe^F?p31]oetF}t8۬M޽nK~]/-k\FG=g{0zje3:BjRzEKUq)NJRzUʗq[ґ/o=c=(/01:*ތ[Y1 F7\HڥtLCݦtD@ҽpK(%/IE+Rޤں,+z!M,&:~HFD릪iчԑ '7Y=8N}.m]NP/O7Eaqb`jc+}G98] '|,i'E^_h^c3TbyobuR!]{GX^!O?yYrAA$k bw/k!_hrv'jw][ĻfuԔm0 if&$-*= {`4 AF̖#7ӈrцm1IuoiFhۉltb:_|[Ls}ԔrY}نZF@ \tlmN]!d +g(⸬cf[)k qlWt3(briԴ.oS`y }PPÁױ;5η=Y%H^Ó~Q]e.{/V젿( +~ܥ"o Ǯy{>ކ-A$M 3<*6P^ߚWR![s!>bި51`R G'۸p>j7.);C= t[<-)zF^bSn9țg-Ɔq۠I_!1p'E3%KIR4f`CWuN-6!*t@LB-LYY0X +!rȲ[\zdoǮa$m8Ų3"J"D| |l\'>o,:`6Ȱz6݋Mi( OE.h pC7W9|]hoټv-1D)ֈ6Z؂ 5 si!>WMs9\+,k4T$FPmoat,v8)k=WCFѮO`X\Q!^E}kOLȇr8U + yb>axaEܢgG\1XBdnY'0zf=&JH)Fs$<'f+FTIq8򝴡; Hx$,&~1&Vè%u2x<̃ hREWdG?@cJfF|=Qy|!w/N{ $2>ٷBS*ģzO)8mTiwaKU(N/l9X{? &mo皘Qq)Р$- vS8CA*մv(Ҹ%='^x[ T +Wx Nj9Ճ_i2#z#{X L  kLȳS'0ReWB*竘6TZ RVHmoņVy1x ws*hP $Z"Pv8 EG~$3 + +E1u@цQg(/ѕl*slBZ;| RF_9P^ SU`Uӟ!]&_ǺwW>$%@CX$?">ܤ"аMh0mlHCݳ-uT쳸>˧YhQTXxMG75ԡqK6TONBd=ׂ2> SA~*h/~IX +zhVfe{ +8WRD6mKbu3 Y|Tg&G=Ru13m'hN[M2)>S7ioq eB>$L=O3"1~AFBQLEص*?BK%ILoIc#4X}Q=A<mFpJXFL 7@d%Hݚ蔐 NmcJi~' 3'q;Vq ]VR7/rMoc%0CғU<Ɇ֢k,%[񨰊٣aH۲tWI;)|Cc 4^7/膁rRO# 0,cxo`kow]S?^e&ľMJofyeFЋh\S@G4Ps+R&Ž썎RXwI io)7 ]8b_wj 1 TyT'9#n߁Ҙ!zzgdg׈*$G13#ަgeK0ҍ.2`cNH=sղCZ9}8Yt 4Af-"ъ֐2JyC=j>_nFr$_V9HƽRZ32>[9nqdnwJB97w n,wMy)?E]|JIFt `;"WLd(K_DT㣓u'zW:YNU^^ /[7:^H.(:'Q$(6"sR ;;!t{T3LX2꽖y V5cfX_fO|F35{ e=}䂾R T{Ӯj|+ʴ05M~|;bg:`4//$}ն?wWevsn,mK*ڳˆ Gd`k{ƚH_"33/1m,.3/2('Ձ"xdMH)|=KPkWܣpjkH"2xU8fv҂DkC| f[Wgbۚ͢U؛?sAiϞv.vϗ D|F~F}kyu1eh's=ﵻˍR +Vxb8GRIt^-{/Z{C:)n9E²g鐷z<2c8WIg$HqΟzqgH˷Tp$uWiwՕ@bT?v܌:y|FTb[,;^9I~ۻ1[?zZK +Dbtϔdd?Jjz~P |]i'ޭimW1=#%Wmȑ(27ϣ ]%_*} (Yq< 9XhB{1y[{nET :eI'liO⚥wAFvw%[{#( Q|Q0G{~٢~ސ N!SeNP麕1,Y_ +o`H"[;?xHI#,* *x()O\`T|l?an[ Wn$w4ՇZ 6\aAqPZMF6JYc5 n-3!ASHP&U=}!E ӻ|6GÊ.X9vF"nĮfv ,c(@o +R*UH&'C+we?_%شCr]жЉ2C|j`hTljǵJ&AU^`c)S$dtSc3@$~C𹴫q#2U$_ ~)"3D5 ;4nm{%!uQ !Zr(VQ""`Cm3BQtAG% ]DP[ +#WB}:"PS}H +H)Uj$dcx2ISS'T$(B))R`T:P{h%~]D+Le4uѸX!I S2Ju5i3Er lj8Z\\/Eʹ4I}ASM/Y:'i@?r;0tZss\wvEg={ß:sȓN9&ҙs@{9Q?~NPnEC-;n+t1v;GA|Ny뜄y;s!;%uơi +hqT͑^0H4:O\s!G:7k}49ha=Ëiz_Xs-ⳮEUƴaaA)wo1q+Pp{ky37zk +$8bC| ~Jܕbgl(WYmwp^b0WĠ/H cgnH8IRvܽP"!j֐s4ިOL_^#(llgșYo1Ɍ{PL,@(4 ukq1!wnM:-0TQ@dVFZ^FOԈZ[߳UZ:ug:cI5\/0Ջ>1j U>Uܨ^qZU!B7c n8@Kl+M4YRYbx"G7L=~wҳ^]3 ;tcT;WgH=91UH+4VuϷ8z~Z~%ꗛ\V8iꙃQX?W?=e()BCU\ni*\\S?E:{^$C:?`E);2RQd.R-M0z=@4?<(Bѡ_Y y i62*+@.egu RW]Y&N2\Jּhȓ;su׀mt|f]J_U@9;$h+E p'btm"~}^A%58%Belqe.yk@I+J TZ]2m<\B!!ˊ~&Q$}%XUojQ#+y|6U)&Еx+gqm6QBxsFwҧ6WNe82 +SS `MU>-.p_$ْʘm{!]du7~:md].sClHR5wLg!8M%q2u&D qX$lCpP( +6N1 q|j=,+&`s]ӑgV׽"\&0d~'\w&nq*E5 w\ R#} I+_wr_Cܫ+5פT(%4Ce$a8@[,̍.Rd[t^.҆l9( +H^G;(>CVaa9ޏ;kia}@cy9BB2,m@Hs&}p$hHV J5QR + +݉nT8w2/Vd`.ܓ3Nq2Kf}3A ȒL|CLH4-V` zj?5t{H&R7qk&< ~[[vZqR[-T4j>m\ AɹX*xlM. 0Ć*h$8tuQ^Lsm_ +b7s!8x bɹ)n\0`j.UU_Q7F p~jY%9u1Q5WT{>?/HUk2; . kEcbZ&4VJܠ.̴q[ +7㩘oq0V#Ykomx jȧc"B It$-p1#B*"9:;"=}UÏedº 4WKݡWp3IMMTk?QlU/)rhXY G|B,R c!ɰbC-^^>k}fg-SKﰴrJV +l3sP|EyB)=pj^/:?;!␐x-~w!F׭ג GuwJI.; .Y .g0VH &c^B=N=q=S#^nj@*ݮQ+{@G5r/PLʽJAVnQ{Qa5}?V=~n5^,lRztd .WkaރΎc ¥Ě-(3KR,öJDUUw㻤윛c{zAe)c{RO]G;I=6^97gzzI'J>~4MʰboBc 'HP4u3EGPa@8R V[1MsK K,NYeM/c `_Bn vs bŘ?z]T +Z6w{~@?㸭7|oŧnBcIS#&7 G A5>R +.jK֞aqbe@EhsUHPk?^L(=Y=I/-A.T;e̡m&^!x֐Y;s=|cmߧǰ3:hYGLF߶R +C%;_oQCPudzw9k쿹;`xp pK p~3)`0N 3Un$)n '% )-Fh-DdНG+6u$},hkR4Grt,x/T4G$ {LsLӎtLL&^K/fK_K枣Hs?uӲ̡_*l!IK>.ZϹA RE9 ]ҕbhK6$GKn%1+u PAU7)h~Af_I(`6=0O?6yh"t9UD"knqY8),SZ j큡V*sTMrh_;.fKſ%.ȅU,] 7Uɭ3S>1v{LHsCEtTi_9oe;FO +t[S:^j0 h鷿ƫEcӧ8SRLJ|uzBF$L<ª,=]9$7fG\ GI N>eLꅝ`3 8^7C_8R#Fj?,9ǿ(O9<QyjfӜ=;իxuѢUGF6 ; CGƅ+Bsa^疇ߏcT<υvMȮr)|N~3LMj""Hv7OO" LCg^{:Ge7=P䆸O N  DĎCؙ8Ԗ؊rkemό:AƝmE0佮qum& (Ovܖu~P ϦR=~/SjXۼn`vۣ6E +PHX1GL vb) (ޫH )|z\@%: H{\c'hx%[I)2 }(` +6n?a%+s702μިDKlsB}^RjvȊŐvrޞ}:4?NR.Ml:U)Ϝ_FK/R‹.գ_ԧm#Q+f?yctTJ}my6a^ǁtg{ᆬFƋ 6(|i`LN?4T"|h`Y{Ó^. /. . H  /s;4GmJÂ>Aw XxHpe(Lp7]KFh+|][wq@X첀N,db~yYs}F$z _IåA:)hp']G:mX2%ڳ<;)d"@Wsv RpR;GASTijR;$,oO?qj '_ka_pgG:G9H9I*K9iK@O GoBHZ%t;3hQ?,uSO޵9PejDṨsr)(lMYB-ΖC9ĩW_.|sRFm05ХхСkS{p[BִEM k ߻ѬfKWm6yxڲ3o8qLdzgFpdoQ8h?'R8(:ST(J&d|2t*.[*xz e[iH_W̎`kzeՄ]h<34C#@5r<", qh!TdΤ0CڠT]NbS_'ܟ(4ÔQ5jC.T?..i:]-c#,eu %ZGp\#c/>2oN":5D = "K;IPKԫ-SPK}ZOԕ<WA5UUj* C̙X-'4U2˻Jt4"2*ځ KjnZ\{J8G` RV!݀ԔbH)׈YzAFs3EUH挀ԍִbj𵻨T H0Df*YUBOO.iyT`COq@~ix1TMup;,wZc (>\\UrUsdz*Ft)?d0@_T. o9cHgk1x?dEZ `:7xH|բM5֦oH"/uQ<0Hm?p {u#]Y>^C?P޼Dlr`#⑉C3#S&ބ Y&Lw}31EybΫ HL9=/y\7g\⁽q 9D+_vXAgXy^約[RR3/=H2%11}G&CqMO L@Tg`Sh0u傷< +q^ʷK{^bTq祛6y;3q;4CS=稒bj1TOi*1$&WD)Z}[sī&?3YY[-)zfB(DDlAm!kFPCW!,&*81.6ţ!yFvjpT 4% h F2ڲb,M}v?r&ΆX@>Rؿs@+wt _@PxR*Q@TC>,#ꈟ>(Q9%nZ\z)>[!t漂IL27}xE `ep'|Ȟ#hIB=4@ y&C5~D@qK4=hd(L_YeQM@ȪlbƘ*x=MZYW7RJ7P>I7xwʊiM|\w>(M&KUFDW >98qDf$X2_i633nNZ.8$KI[&{%z_ ^Xf$R,|ۅ<*v +NZ 9Xrt@5 zHo2%x +-IuHDj{,]W-G)tl]:; (Sա#4k-DfXm{[ m`/ǽ ^z0t4M#'bI_ͦc-oEde4[=@zZ +.V*ތwG%ؒOUUOg%U+yݤz(tދ7@zKGAj_sjM{21KzNk`U!#d^o2ddJ!J!/4%vu5 $H.lr!Tfp!xп^s fr.DH_Ȅ@ŎǤ|hu%bp%5C"l<.?"dh\$B:Dxfu %Bn;Pg B_.ڃ +[k4vz0IPA4q¿Ž m$?)aKl.shnY,4 +vuQ2%_WCljչQvIm+Z,3~i&mGϪw]-Hf5Ecu?ysnmQiVM' &m6N-{&~pX<+ T 鋂 *-GS*nKez ||G鳛j\ddCKʇV̵?jl _fL=q1róA#]?3<4huqi"d Z;wJɵ}Zmjɔ351̆6ōSdg|ocͮ4](7%P!๞(7_ S2/@N5 V5Ք=ΣAM]fՉz|;v 4NcDUC}%+wǐ&r|gL 鮖Zv8>\dԇ}xX,3[.kH5±>5⒆]>=ۚl ֨ xh"N^)aLWNrM=>,Բ즄@`Ww vwS,v8XOҽaɷ{Y,#̣ +:^>xW3lWTFWwdzSUC?!QxT @M +nᰜRRmܵO?BKςh3aw]gĆnS㤭~r>h`,35U; )ڥ~AiS F`UW`vd>Gr%A,5Ln[3HBi7r^l-Bi N,iC·)a%y Eؑ!ѹXPzN]bmrE6,(q?R-!esUaKx ])*"YaZ&吅K@gtB +=C}m7!~ȱ4m~CWp ZXO6iwR룩C?Z[ ɶ%sWнv/5tc2u*4t W2ϥ3NIpW;ްmQv\!]fK N[&sv\/sŲg^܀Ȏ +3O\]{oהT,uDV"i3aJBzOcUVkv\9o0 d;\ Ojkqs`)29R]pKԽC68iHT1(Etzj??͏'%2Mܺ0b;ŸJTrYo|_u+B%i/!ԏs4;ɨՇ17o}.Vp@`lr@J)y%,3k]_{[OQ9IɒHw| d_/hj_u*1u- l<[wTυTv'8N{4qD| j5G*qU_B9oqɊ]f>#*FC[r/Biطg-03Fi!+zĵOUϙN `P̤T D1ˡ/ |QL!J7cH^؇.Y~s۲pgmQ:xyž:=WrXkV{Wyh1i?(s=Gu +n|}'7\9c}pyzN9!jVGjq7 aC2W`ZHYfe2-j dC"B0{b qoOHM`2g FX GYD .F+po|*To_Mh;QDeާHS_R m߻BM9\M: AOO8pDeFr pLǼ;Ag +饏ۮdͅqϢj@ ~@ +6~&7No_%r/r|犷 #md1LS +~jnSXʰ ^, aRY(-( 6|Ҙwst\- 5杄[ >e8$ y*}bDԏSXN<ɂ߄~IZƥ+k l!H9DR#JO:d^ʉ1 + W!*P𤏚 }. 6OY o kFuکX#QhjsP +tDB.8GctрmЄDGm``I< m]㽁p + P"r \[\Lo|0/O)%:li!.9E/4u4YP>P2 m84ضvQp| re2|%삕)tъJUdAgn.N]lhLuOYi<ŶfcvnԀ`V8lDd^rP܀\QPl]xOڳA1815:JƸU %8(f ^mc&'>ٷ"$ 4(^߽EKA+ {{zL?_ҐՆa9?x!gPApjj<,W]Q\6 +hl*rMͬGߡ"]vpnx||@vM]J1Lb/SM'w0Cxpd$X,W&yrp*4И&@u/BXzHN GSwN[׻heA'lm^-#S< ȕl^D3I_%xG脅Dg\/}AlE7%8%Fݮ7rlυNz9 +AsݞI/c2KRx &c_iLnDl߲5 +2/ws?|0꫄Cv +?9[ njz{ggZ1trW@y(r`[/6x'_ݹ^FnăSCRּ`:$oތȄBQ*s<\pKD2(Ϫ=8z=>~(LσQ)>\ZͤD +eB/E/8ja HM2E )1kUs Mslm g؇,aYW&Ӛ;vOe#B?ߓwLڿ>ppAƱM([j6isU%>u'rC ԅp +>(%r%[~J#~°T8xM<ZhբpJz?MBFBbhg#F{ŠMe,Wq3a +2Ѕ*J/鬑kv'q#LCLuv'\;5 6vxL#.qV ))^,Uؖ{ٟi>|>XHbրɬ2P(h/{Bpuul;1+XRAGКI;`%J:VXI?Xj!/,EUpNⶰlG ?qPƜ5wj!cO$9)j9ؗaRf{.jI/$ޘ%X!ܫ_vEv".Az_=Rjw4dԇ|ԩuo}&߉W9@p~r#2NoxfD-{a. D_W)d%r҅owe k_,8vU.X< NX4sE!I䩎JG;J60F_u+Nh~WB+Bqz4T U# c*LRuA#AM7H +&5V~[(8Eg>i4Ţ-~8;nzl!w> 2'bRB|| F2LԦȣ~t,8Hyud+kSWS.:>=mt[y#hzJRX,Dn40 ԚdeW4x Tz:wU'tkG(*\KR +)ƨ$ޯ.ri/E =%a|.'.gƩ'Ft9QN h2(aˁ;-3ؘ܄ nKS!1֯_] 榣6tG!8`q5Lp+/J}5 +'HoOd 80-8 em˜\tԖq]j:5T8&0~ yi&'_48TNE3M̗\.^;- E%>`hK:mȝs; #'4s mSO dԚEմmk}u2̿d,umU?-A"Vݞr8o@*/%[ 1#aYMxD~79 }f\ӆN%oFx:dBF6jAY+C\^gڃgAP">8RA#C^e/yէ"04@a,)}f(zu5keL7qADaeh`ve[Ze\ jMȓ}44}MՅ׍yWf&J+$q8zNVO"_qRﳵ>*} ́t3`k{Ƴf2!m:2HOݺ\LN[bK VaZkη],Xb+e9U~T> <  3-l;-W4"h }JzdigPSW۾^~rѪ9gS9<Ѯv@v졁)%=DgL;;pgNm Tnݛ*֝-,hk{_%}YmKgN=ρPNk.<ċǀ,0;E´Mp^֮ Fq%uV=Я\D{pn=oG +u Ξt' y W =^⠏]ueaQh@uStjII~m/݂v,6Zmj}8Y0E O9VM3z{O1^QՋ=m1Q(J KAQ.S)O^{_52o?,jFta7\[kj`?z]:w#+[{.ȇuW b'|_CY/ULĭ{eo؅j\ $`H_iKMJɶ~z:-:ԣidz tքi|ZMFҸ>4Lr6A-|הKy3萲/1'qqNV%hNzh9s0h0v3 9 +OG%xW`쪾z82cZkIuVFITAat]Fb 5^Y3OW9|BqLG8K 'zVL{&4'y9/Ys:DӣIJ1\V4L{f^Uɖg߻Xyˌ/YgπkТd(9b-=t K:WCT[c=cIKH͌(Q2(-dl1ߟFbA[3nVa[qtoH +<߷|}"ԢUze=H(d*+\ Xtjm[)7n_Xbr_DT: +ERӝZ +5kW'mDK L+bvPhE7:onP)ȢKD.Gl.#`ˊz;MNsvAG8BFQ: (Ҥn.ltqRg쑡]ˇ:7ɬ0KSШQMUfIgHhPP{ʓY+.w ڼxB w$V%qguRHcqQ7f"Mă_^<'.Y'w"n₿0T+ߣ\'ȵ*쵋Na&bWL%B7b 3 q;:2al,iH ׈uqn{m ^KkX4B +\Q#"`zA ni=nWU$'e|P5`b>l{sM 3GvY C7&)!k_iA#NܠK=~{{Iޕb 8tvvlun\GGEQ8tgk#:@W|ňLO @$ 2cAt^ڦ1TNYڡzWH (ύ~a33F&W\HkFJ-lHM;r5R|0~\`gp5иZ)-#yD7B.%4= +6ddkRо,q";433ӤБd܋\@6%$̩m=oV@ 2}/C]b@{#B}kXpY_0}bDێc4;-͹+ԗ1O 5M;{%1T7N̹^[磝N3 ;rT9*d)dm^n1;Y~XE`F]7pнys\\hLG.b +uqzUWl~䁎-hc>#5}ڰeSG/6G= -R2 Ϡfz|4Ky#uƠײ5j YNxfԮHlbVqHbpX4`*t8HJ<7;?K<m=LM>wx * cmA! n{ :l;9FZ< uc#^7̦@Uϩ#(.06'ёeFVc-d/qO\׿ U;;)T_Jɶ2KIt(5Rz)UIIGl~)ǑY*CZ/23tAf)AsK"wCﯜTq)Je'*CK~RS\_2 P8^*_a#*7L] |ɔk;\.*l.*nR`TG1o 1(/bW-~7{R̯ k{B8Pow$$1yQ8Bc4`e= PH*8e4ͥ,e9%:T:|`Mps W m +g#ϥ4>.\4A~/ž/=ǔuщ()8>fP=$~0leEܻX0”h~ UĞ1K{fZT& %:X@KhY C)lH @#h۩E]Y󺔭γ^/7nB婍2FsiH`&bU(5:Y`t䳠@KeL~zum= +>VrX/ztJ?^l73":QZlܶM~j7D>H4vQ)u 46~@7 2.d'k3T|Y,X|z֧9N#M#U3ƕy祓#Klt7)1͢~PCj$H\{ $^2f 2f0?gZL&#5ݶQs'/ZŊB^{SǶFc~[1dkc/;^g&csZvL +=W虒Pi30|Yֿ?g3\OnxU4g [vPΞ )N Rs6H%XÐsƆv ֙Scm*E[cEzO?ك^x"ĝݜŲx'T~`)ɰ,ĵƫ\e^(hJEH=s^pV(˘~D_4ٺFjG0xVY/Lf%sGݯ^OOLCk=1 ^Hs џ\pߒ Y^yE8Q ;st}ɪYOgu,݆+!.;fq4rAcOϫuݯ͐ht9n+ ?ڍ@1^^r\~쐐(G +NGR%i3 _[^;zm\+We"ɬT:x™̈́吒]M}ɆGmg*Ic{/`Ap<Ϡ˾™XC,HtӤndCėNSXU)p5OX}cj~r (Ҭi#q)|8 goN[-G0Bda ۙA`@5>,7jq7xbH'w8'lfx5'`% |YX,oy IJiw6gTY ~w{K3m8a)$Wȿɿ&B˗/ 1]6LsWP0b1[!&M;3CXw,z0ڣČ,bIEFw|)FCs/JEmR-&)hx-@E'm3њt43Oһ2Rdg(|yG G'?Ln6Ž;Zlp,=!nςWi(b1gUecń"X?m#ƲOqQ:}E9 &\VlVSjNmYs`z:=jrae3tXd8L.֞Xq)y_" ퟌ@ ˭o˚; b},ͱh& ] uSO "M&U쌳澤G[B2=&%ig29]}Q1FmNdj8¨ukC-HJ}pXᱰ<$Gp 3i&WdE|i#cm5,E~w w}.h㞃ʒҙa[S2f79QP,iEUpH%W\o0Zԛ&ˠ3v31E=ܘd ,yUiUiRojk +@G2f#Y*P +55Hz_nNř۔Jw?߾.[19=9M, ʨ#j᫹9fmztepLuNZ'kr>jey̲%]:[BCCd~&856(z +@iZe1yA"y7i]7?k z5 e C-q^9J <: XT陇}_l%LXBUZ,Z:jEr7z1HUocg0"I%h,Bݢ*Ђ2{7s :3G~ůFցv: &<[2ckv.u)-6Wո"sjܤBZڲi93*$Hk,:PkHI%N#"x]Y\;\W_s,\A=]e,^$?(zANCQ(Q.R ż&~dX=Ε #ȉz77qP LML &y~]nV 9כ(/Edqy(y|A%%EIP%f w<3D.:B"l\ꛌ0xƇbL7ό7!pWԋ`W󙇂iC4EcΤSrmP#s6eU+SmW-e8=mV׌Yn6\P=m@zmd +6=r LKp!yDE[$Ե92 TrTAΪ08Z<BLΧ5$)C #Π1Ǧ0nomJ5z3ugl+kK%+[*@tG5-ATWJGͪW(kZV4{KngiZeIHbdm3F&y6A߼gX#š +~t#/K[-ږ0+gl+HpV֙GKtGViJI#E FJ!,=i29S x /|،R#M@'r0 ǫ> (w8j @q.% Zf1Y};fs4òWѻ#S 3 Rzb&jq&-tU(4ftar|lH%sʎt|Ǐk.Y^."s˙Έ =8S<[Ȧp?J$C-Qm0LQ=5r/峽"S_E> ڳ|x*5+Ӎ_ xW\@z(XybP¸B^w iMYI(N|0=I\6)&I\v#[E#qnm Qyz> Z?P,}8qNJ\fYiœDz ~XV;ZlD,ZAN#EI"u=dnKD5wJ:ŌgdNc<[ΤT(t鯌jc?VٓktASD +N6W` Y ͲqV af{іb 3@nY7NC22blFg5kEDB$2òvؓ=o'FvQ@+K U@ +T:AZ_)-? H̦b-+'gB-*"V}abOsTLиj~W.x4- URȢJ:TЉ}Mؘ'J5uX><}~,8ӫԼ +cUvDw\JIitOc@\b!5h`k9ΥOJzļ`4È8z̺. BS;%N-,-ΚSO漏[+oKӻ *w$ Q^B +&'& +cȾARE˜aþ~d5D ^."F5̌J_6u'vbwx$G`1pNA'h& ks'2L%[ QNmGAqG0sK +֑8O=%6>-N'ܮ+ &P_YCu*#Cr߉zB{\n J[OtSү<Wwq=_(,a[y;;xie5䑷ʿd:S\&j +΁@;K'''8yсu Fk_LAϰh5sy/xeӭ1⊓A(Gkf3{\Lfr< ى/dfZ.=!vbY:.ߘQ$G9;7%jle{VTzq=ku-ѤF}bU{bY٪RFI|E [N P]T@.ڊ[Z>}+P[=ءʎ:0ظA+H SZyxĹdͨa!)[) |V1]r:anݮU*b :0GKUٜ?H$fN$)7=k˄,B'..N!4ţ$ @LZ'!ݳGNWy- HV '2b!.A#5L# qX?25}D.XѣgV?\$!$St"L'%1PkgcѨݡ^->{"c6WRtU4'bxQcl2(/}(1ANۮxuW,ήF K-1pbp 1YөjsjhM2/%ƢfrҺkM˛agH<ǪY=4>ŗ +h0P7]*ܧ2N@"* E?"Tz^*kS_UurOŌ:1j[FS 7ӴG&jhZZOwwיÑ 3Fz)6(2K*F3%V4Z-7E* LPx 1'dCEnzKOLJzFD6 S#x# EbMUxVVYz'{gVؔrjlu +j8]`,!"}CMz5vgvw}߷F*JT4HT +)h|C0GjdTEp+faAFL6qE[m6kaSW۸)p5xLvbn)IA,-qbZ #v$҄' +++U~b,qJ^OwZvW !.#k=H?dI:7hbAe*?T2TzVC2~w,ylWK~xN* wPCl>[Ef?RM,&:4 _vr92b,!o=0cBkTMV) I%,MJ} G|yL$( z%E &A&h6{_ܻl"`'X&n!͵K9% }h߇[XcP^F+$pQnnY֖xê?j-gtrL×Jb-)?,_DXB8)Jd|)O6oWx RPy^s=#WT6JG!^ԋ^ןj|F`*hVq4~'lڒ|rG8 +r[i߼㑟r˵xQWT$]W2ޤ %~)]ōCtGA|B]"Kn_RdݍFZm=LG L .5mbW.7\ 3d!qsΓU+!h}|E6πxьp 6,dp52wxC ~y +-34I0$\EA?AeXö1/[:R\W$O b4Oz +84ȧE#&#1 +O7TNwsgT._(Ʊj=lwc5Wc.u<390b\y֘Lu!@2vWt[y\%ũ-W2궲ނzWU~,JE~:RYFG5ѯFH)*.Hx;za"욕-leǎ;%(_'J6~cOMBdjj\B+C|?d^aP j6yq\0] *V{Kw~*q + TrA?a +w9&>K_ЪCgƆ%s5#qgUwArm 71zE~eQ̖F˛u;*ٲTo +\Ѓ@Z+t uP}f +܁ l2m ݲ=\Y8QxЮzkJkt4ĩILCOD[vJ/ 9̰\][\T>"g -8qfM !91s9d|*RKW=Bs%@hUn<-Xzֺu[`w 6yg1S?uW(1;!\ u?". %qA:Hg}il?0Garn %N\ӧsm\FKRxHxyX]`.ua s؅j}Anwyy|]e+Czϧz>,O6м`tXL B+s/˜&  >w0>YHSSӸH0|̽܏QbWC<ϫV nWpHg"G>2e{”^!`k?ƾRpbtACrخx xĝC%)!IocqpBXҰRCRb=E }#z%2@`(?-lتZKً*\XUrZ]yoX.iҞ M>ojhQ +.?PavnZ0=vz ZhQ~o{(QLp>)qJ_&o7V)Gt֮gozzepo{yDh7tn0 UR]LJx +V}5;]˰90PRe|v5auEn!q5 ɀx@71,Ôb:׍yR`kj7Q}22t~fGwr[m OSCCy->;r?۰8m&F˱X9HC8+%lmge[ufI^J?.5٫7"!Bdɐ\Kx~A4d ViG#lS;+k#Jc̳S-cctP#Ew7@=A5kgſj̮P, ,&n+*G,5AVB"H/JH.e Ah~r|]hEG~@1~~GbА1ת$-Fvݹ̓N٢[O0`,OTu="ԋ\n*Mlf(luƯàV"يWyvq+l-_n +Xyarq+anHKZKNݏyq qsLvP>H!Ho`I GsVꎟ +iG8 yo۪ozhйU6F'=Jtuq.PZBO~I>V)K=Vp.*M1?r,e5d',LJF='Ɋ=*ǻZ3]X"͍&)0WfYZH1^nBM{-lWj>ݹ﷚%VA 8B{c2j_ÎbU~l ~߄}5~P(uysi}Sv-[W* ny +"C6b=4ig$DTR =(hVnj>Z.s"& !H kΏ8D:p& +9IaH; o+-?;+mK+TXMբ949XbzYntl8֜Spź,WCX/,W@1j5b":6=h`wpˠ\(zR [Pxl"JdgsT?Ւcm}^?C0^$XO0@UEf&3"#|@ReyM !3xm"a'рQqyX%…z%Q?aaZ1(|z*~Td9p/Fjwi9Y|yg˳=K>L,`5Ybh]D?( 8]D:[l|Gc!vKRyXDq_W'Ef6n5C]E̲0t+JCY+5.TenQ|J0qNw,oSY:ig+9]&8> r|${>Fy+ x Ȭt;I<<}ROpr+3];Lr@oL!L 0DEd7)cAl _z  T_b!"ȝ\I@ndl`Y.X"(AM BNE%v4CTQr݂7 c:a4|fCAJ+Anj^"S)Ο'|h;0GzЧ\,ِ؇Ub?UC:"\)ּP*,)(uk _0tu\zy)B(]=\~S0r15͌H*T*ib<~l0VM:=wOsR@GcHSy2z;[1 +P<2T$Gְf%J> Xbwiy&/[&BRFR;C^W]x}{#9~ȾDp[jDK)lѧj>1 šT3DN0P/r+,hpCQ,)N(ӒYruv;DʷkZw8`-i$S s j +yKJ>KʓCȪ>PYp>Z%Ys-E@rCκrT=p1j;)շ܊PNˌ_!9@ÄÓ<W˛U6P7~6_ N=M)5F{^uaLT}e7:|Z+ +;5gY΢8hb3yd&[Y+2%_sycQl-u'>zƧ5Y:+{&S>c-c^X{zHRQXA \ZsR3cvJk.,ƯL$ r0$rD,mXM3fz'\c_Bj}!UIʮ3"`ހ0/rQYخnB_I&m  +S&P& /K!7ϲe1יNE. Hƃ9898kCe383]6@ÙL [ a -t46\SUSUphVhIfOX{1]?<Z%B@e!C-ٙ5 |Ķ$~o[Jv9f;] +R&|XFg* j Sq|U3S5G5+Onc:3Цm;p +-aW޼1<Nn-76؛]4 IU4JYu@=ꀣSu@iSWqW],n _@]]vo8Ugp ߬9 ]wd$,QvXұt[RgR.:FWLd1&H55Gǚa@Q#t;k>}"F88M5NXb'x\Zw[#zc@79߮ +7^`ͲLYeBAu}6%H9n}ݴ[ڍHOq E]OQ_q޲K/)BLU@TT}@L:d + B[zUt@W| _r}첝(kR_H!/^:wYǺ-)*ɖ$Hboc"> ݍ + Dq}{$ +P +YtنiE!  6aXٰ訁xb-6Ke';7ā>gtUk%|ɒ7!X'57**??wof]}{ݭ~KIqC/WpU$w?^^~GvF{}E1sR7yd55Mj\Ag;o\_*y)LB4: u,B6Mk{mEĪUuhT<~C*)},5&&v9"jp|:ϵLsBEh\YjA^FUc`1c~$\/g\0U)ղ6+RɴY4HSFP'`ZPDžG0zk0w$elD:gF[z[0`Sګͺ:T(?P IR7lXsQ qVU@]iِ_REnUk;R!d^&RSUOQ"6xAAZQ⪪AFHG 6Ժ dž beyIVwfՖf[t8~x 'tl.8bdI1~]V('gK%5UU X @ ds?N$H^*z9X?ё^<7g7goA:{+7:l?qzGz;u<<}]Op}|~: +Z{ G3FY<76+y 1 T`/Ԫ( 5:YZt4 h&"2|/Z&UMq%Y4hPsfB]\#-'50 ߛu a ۻO]%{/(d*H{C-F_1btXdHWu ~u W͓'Wo[6h$v@{ϚzX-EfEȡР^ޜE|dg"*X:,8BqވNm!G_qFh$?-\DpĝY(i.H"@X/ IJqn o\rOh:>lZ ІQG+K#(*=o}nŜI& ב́ 55[,׺ z$Pֲ JѭfKdڜo f[vE׊|}U<`$IQrfU~! +2g'"D! f .mI>81'% 6cSZ -9,+JSRxBcmsp "-,=h:6uy6 `*Itsְ3,V1>ed[r +lǍN2>¡lL4ƺh6\!#+9CU.g( J2̌|<$alAR^j5nQ͔_Lqu9/R NCQ3oQ{QY`ii֭ӎE؋EGtͅi]@Jٷ@2+3`kzߤ5|zwi~y Zf:,9ߖLwi)&Szߏ=`GH_ +4b,9#_&MwDK$ dn}H!އ{:B AhӲq \; XH`K.GaB~t:mX{&qq- BzB:]bqZ"C}TdG0&'@`)Z Ԯ2Xdfe=A+ق94!ɹd2fȫ~4z7I}-Q srhuAS p abE-9QFhi”OP5Qh(tdܴLJ41èj../ r=VOEk֙}¬:H *G0ֺVw6T[3IoyMEJ }oab +mm#R> Ha ;ݐXC`Uu*!vX +mwV,‡L7iZ@Yo3պ8\0< ݮbBNY\A4w٦8L.q] XJQ7|i zmsV>L)0H,2yOU`S'fېq[G0W^0ԫAv/c+Ox1d@* EG؝)Pƣ:‹fqrXGu"ڃR$^`Y;]B֦.TdܲIo:(7Uup('ѻCEN*pU O-kYrwfW:h&;5Ye&1/\Y)Q[o*ɍl{B'̑^fD0_ +%_AvZC))̩ ϶UכRr{`s`U[LJzYet^m,*Sgn9+x'fO"Mȭ+֦q8d.)OI?ig\Kewx@mfv"<azW.7݊fBz}?㸡hŤ0')%h~V3A;E;L ^t{AVlADj:6e[(:v*H>|61羱4x7X|Hzcaͨ): 8򶋦:/bBŠårA7m9ȂM7P\BM$l!^_UdWđ%}Hg9SgYaܓu4Qin.5vNMsVmu5نBbvKfUy>Ȥ;3.ٗN$?Nvq^lh[W]爵xR^9Qx:^yA-Q/)6YvӸz\S˶GLt¸ZQ)qTFTtm)ϔqUv,Kr8ir3gSv(:;I-nD ~G]#d/ gS3U*WCzv^K>ڈX 10M5#͑A }9lW{bg45ߧ.6q>8؟g +DBB8&񻗃7d)D.[z1*WP$oD!'!:91%?R-ÔVo~h,-5^⸱7kªs<ܪ:L&[pk^1.NGysO%+!|D ;^7:CњPq9#An0'` Ag@ FPH̖bpUV}+5ovfgG A!p*\[xf0DG :{5(FYVMkOs_Jy>yD{,uYfp6kCȅWFiN))"١X]\c$ Ơ`)3ֺ9'ߐ4C +݃8܏ޢYp{'i +JכV}=0aGoz+|tX[ŵH! -KTR9xI@Ϥ?3$}H%\;>n_NiYj`C. CP +1wڛ!eֹ0sӡ\J/:r/KY h8,0l塐l.BnrtQ4·/$ C#u]#C +sE Z#&$`ZR->u#>h +E&6C?kjm7ܱ.bs یbةqMV/48],7-YOp| W HdlHR(H +/ƙ,҇qwoPT^=`M)@h@JȐiEHGI=4&69W/[0uQ{q. ,Iӱ$'l,8T !Tp + :!A/okSkЩ[AZ䈁(1!B A{_hUƀZ@3uL Hm>D:Ql1)~Fx eY-׍F G䥌ZJvRj^يkmVd6e^ĔaNRd'wq-@(;>8S*AoxdYxRZaIkcwgtmvK6L0Z:#Z{p/}2\CyAS.Be$l0-fͮg- :}ܴʫSsA'-VvB-C>pʗ̮NZ֨b + }Yl6w58l=ҥVyoDU#++pIhC 'wGD#&م+ʡ\v, 2j(ZH1dl`wL h\ -V-dKܕЯԀr/A:ͤ c>) N>x<~bg Zatm32`v.?3<>r5!v^mx=8$Ng׸vEh!M0 ݢɐru{ o)9{~H.t]KXPb#17ԨeJuGPJ@Z|Lh}|j?)w;:iCTpehl&MsrI"Z/V|uZ_V$* +KܠW9,6)nY>◢Vh);)QB} EU1 + +pr2cs+8J̓WBdqn/j tb;#@yrC cpRW96GY롭+Bcl~ך@j\EU#B/1"r&F5⡙z GO>i(*ׄ[/ֹC4V~|*6uPr`jǞP[P:\-!.v"ɸ8;os<nzga+RVLE}N"<XYtjzwn^C@e-:mXܶJdr>0)wg|{l}Dz"$d)! )[XzӰh3KĞ>Y$#VhRl6E'U#S!ohɜg:Bkt~DW:I_OЯWdA$}z&{#+8`whбf> }Ψ皔yk'Re\ޚC[U[>G `06'5 i'<-~y2y+M)#z佞7MgX1>ҋyivd䛓Bf +֥Fxx>e]+'+'D3:hՌ 9~J'Q=zz)Vvo?1+qv bd+C兀Z +˩#}Sy[QygҵuOVCGa;i;d&'%28W5b^F ECK!8-@Bo}qpt1 Tak4flg2@uo78fAT[渥P<<{z+%rTj^M+h&#6~&H%i6}lM`ܡcuzfu\K hj}EFy;1} _|9=j1Z#Mfp MyoVJ=8dDؤWErKw fmސB%坛$Ixv:fHiîoS5313@ьvrnl5D qOZ-sA%(}OTA  +7,9P <7r!JMTsIP:@rhCJnҸ ͆G-3V@ Uom]PN&xOJnBoVx.pGyd4aa@LepU|]pDf"l<$\x f + +Bh +NJﲄ{"߼Il'&T+>JOI͸.k[u/RWej$}u%c+?ItPi(;o~Zp]5tqO|VtS~}kG(ZަreMmwLsgR6Dd(7!yWbTx~F +)9ٲ7c{;V.ÅbI0UC y$:wVpҭl~WYЊ>![MKJ;*_""uF.uʜ #R'0se+Ai+LjE]::2u,q뷓1׺l:5NgQ +,p!B v`#2,䮀t2QOlS)3<-}cէ)BhYv|SӉ6d90j(:dD\@G]Ŭ|¥U:DB +>PYOvV(<Gk䦱ĎhU +h8ir"o]B͈mBwn6q[\V*,Q۲,YvBU飯s]BUɦWo1G5$oUZ0t7ZÛ9Wj `KS>uy9w&f6xhG9b+`{ۭr./&]`VFt#qGhEO Ns^r7w7݂r4.ͽ%E(Dd1wߞ'qnIrQrQD%tf;\<\GOߑlkI|uIsu'k] Y֤ {zK#x@=}g&\IrHSKAɀ4ICfkv劕,(ǥ`4hRh&^H*Wn|!O%Ts!}<=lzK-=99Q%E&OdENV$B,$L!"RӠH-DFNB¶D!4*~T&wb³( LFjtiy2aqgH/ؔȿ{'!q"8iiY+p^ir3ͽc->< N2ӣF7O 2P;9l }S2bƸ +Do[. QW5 +K>Esx2,Q1"D&^'UJHxMdZcvW̝Szr TҼX% Bf0'5{g)mfV.~^4%Yt +3*VQB+to.IcY/U +1[᡼=Ӏd L?Gʓ-j;^we( ,]z~*Lx%]HE9yAսpZՄ)_؉0ږq: HE?kUku(AD<pW[`p.ZjZN=)*>}ݢ7uMfj $hz7d1Zr#5Rj#BY "S +(ZJ\I Tά%ava˦6vLZԁ2.".mcohFHt.Զ߀˨tR1O.C3Ti }7:cC,O7x)/2u1GU .)o ,+OQtrflIk +U%쯩f k(;%" v{ +fvlkGrSx'[2PUq VqL:4ֶ⌷,4$0nGP  +u<]'dBWUBۡ_<@?qgX$xZ +Z~"{̉eBJ)X.eJ^jOcFO:,wB| Cdp!9zP̠M xK.a:9;:qCzά}݇X%W|y&UiыP!<Ç+'$%ܶG0d`kMQ0^r>TjzK8`SLkm̀-Db=x=vJzf)q#mіv΢:Y0vc&֍B ;*݆MY ;jLS=^8 4zyg?{EF[:3T#+S/'ȌsHu9d!^J}y$Ut.{ѳ6\VJz.j --Ko6xIwNm}3u&͞Q~9_gr2)s #$$ mf9'5]HHgLð &]C`b^$A ,|q_y1\m8u?"+JHMK].KuHWU~qgS:Ge($SM8ǟk!U6 +yEI\w- +&x/6IG\Q3KZ.Rl_|䐚`e˚3>R?:Iufr:;7^Yuea%qndzACY pL-Yxt +0l r~O!E3%FV=;>(Ҧ\gDo2ˋ*2EΜ)CV.}GG2ExXKK8AwPLJ460u(؄Eq_/OBoK +WdUI8(P?aDT]bűRC.Zc ,&97yl7bb0W6ѹ$<ϥsW`0_ʅk gS0SCr!>C*茮GFGWL\,fp!iαЈGG^_y!cJ@T^'h{gp9uh zYo[롼m\g~{n4E}['%RQl\#6Dž/ݏB"δڜMn#2ngJ(JԾԉ8)q`yflOv$W?/#8y(=V~iB!Uf(+nfި\E"I.|Ϯn~"6i"Ex*( ]޹$)kž! T0$\dZb}%iхȖ{Om1A\෢ӝ>geҙ*GaP u!Ž NT ɀtG(RAq Vw-{:\Yo e!IL.`ݮ%%Dkh_<(}ÒL LPz'Ԥ?N6G#xzRЈŕ:R%eB'vq{FgŽ1z`)DR OgHipUñc &EwZȁD¥it9ge%kHՕ@;QjQ+H[:@ZY%.Jt -]yo@ /dUHBא 堎/3S בɲ"/=E1d9,8NUil٩Nyyb,:=%wNȧY_ &Y@4UVW8Ωf8 أGJã^xJ.7`eEi9kBU%ѭ W| l0Jʺ*4|o}q )W-<&I=AvtHQLY-4PK<Chqxva]TJ}rH>PkkMKUWgG{UguY)mY66"ҭfFo\Ձ3WVu '18왇<`5~}9dpD"jieRՋi[FMEhB-QTr1y7!v@7Aÿ|>in _6 }P/zl_A槜rm"\pDIDC񖪬.\XYvȆ䐣f2═.hq^ i hik(ѯс@Oi\EOGBV蜜"F\_@݁H#Z癫ÜP8ʬZ G\(&|)D +8:{nOzjNLJdE{ݟ}z\=۝=(:3)C.ãА, %2a9+(,Om(#*#Sj@a7VȬO"|z4-(Uh\!V7F^=1%ҋ)SWC72~=15z#}#]a<&ݯͨlpg:{_q?~2Qmĵ;[r\MڵR6MWo=WS޿h}!ï7nﯟQFO +Iw$Ri聋?m(Sjqw\ B3A=6?N)sruxN +Żn;cm13wR_o#m'c;i >΋nYeg>Cejg Z!Z3LxVozKJzOwQ 0"n^X vA4'ZxA#uTyfSLk)i[<'?FVYo۠`'v4C.7S_jiԻPM1e:L,Jow@DgU:uc+ĭ3ߵj"'=d~ojmf!#Wg:/Hb;VÜ:zI 2 (cofxϴ\eêծ O"]@%n7~bvlb$*1$cYt&{&!;K̹Xxz,=oKE"Ax+6d0V?q%j3 Xy{RUyb< ԚRf*H-]W%IR<4ޟ7V Xu _V&ID0Kx|jjւ;`;*Ű}g{Ȟ38U{پdK啦Deav4uyhFWȸ IN'("5HAN163|`K}õՋQHJ"辂߄7t|@09^UBuEՙ]SUhr܄떧ƅmSNW1nDTIȏT*+@S7~iPbjt \ c>?ܳcJO)Y_?/,D2_׼Ƃi_ػ([x 0lHx\Ⱦ3t|܊RzLJkKu_I$&14~ yǤ(UTոk~vN6FdzQ3%?kH7tFf~6m |jItJ(7\; l@m&k[OJkEl _Qa8^7 +Kib;z;u,ti="PJ[Kj:Cʬ|$DH`oi;GyƩU5ؼZH蜏W!dΣz̧L3bqeIE=NcX2h^ޗ\D-J3ZY߉OCƐ.BӼRPHT>=TP a]h9ګ#,';rjcQrfhLNzO1zU0B|2) +zг*u[% !3aI|hYǥ]lj ;i.n43MgaQI60Ʊ֜:uauwiq| ˕dFQСnG kn)V0k+O(jT#-Aq]?~;8[x)L0:d?@p5D_ iQp:ʮ1RSKKd%2ѳܺޖNg{%<^s?;i2 Vm֓ƯLȵ1oɴ>`] poyӣYtUnl_Qm`>=gm BG#7 +zBSA@dv];_3ڣ47gMʬ^=)M1qm@vD! n~7nh bj;~KD|ٰuQ"2&O*Ыy,P/6&X8q:66lߧ?@ !Éƀƍ9ME!K Ztr,$DZA_}H%nlE9CëbJysRa"JVQG;-Fܸ\ w7w7H[sD7́Np,J$dN Wc Vo(udaX,n6g`PK@: _Y:'R3J6qYlк8]:8 >mgQ/%JUB`B>]q azB. y0諔0̘b_eGmWN/!lh†K3Cz HBog a$x a裄A r0Cˆ@*d`&:^grAK'^:NJ!lPo:`u =niTcA*!L1ԭin|TQu` |64o0 4co)o469E ]V(À\3ڟgkbBL6a<)2)3z( />" 6,M\80h#'ͱL N" +vQD&XAGTx\lhF9ufHwոqiVĐi;|brpUsF«884 + =xce{ex?i)2 3y!B)\:1QpZBV"s7t6J-E r>S؏*!|lx#b?ne:jo@2i_[g>t>jZ܃\U%.qU;\+Iu z`q@gmr ӄ LL\r+a :*5ɨ{CgVO"Yt"u|~U}_!ZXf3hahX~RQd>>K0x`e{MKn}d@"=^b}Z1m-Cש +զw`CL MEk'/AFEL}vnALA ⎽A& ^S-AC7:+mG]+/ׇ:&2O` LIB[4@ւ3;W>>_aF D*6OїJ×5P(I|;uv :R=$kFGR)dYRVV6Fe١! Pݡmgix߾HY1/ՈKS6dg|YfSi&G +;T Vv7P\ÿh+B9Ĵp^?z\֝w`Q4 +L7XI/Y Ƥ[9Hڠl&Y4CMs LTYy\5XQR,ӃmMNF)e9ahj+iws0$9]|>/w@m\sk^~+၅.bK@q~g.<^jX{ dߥ9^\V||(<5{p*ge'VZs)/UIɤji]K\L+xI`B%B>kt_=LlG'|q;(x%VxȨhn) +̨~_1?BggdֲHX-~"rX;XGA 5-T*<|})aA2>`5`[xAu8kڞ_q ;^iiX2 +< ؏]fX ba(%jY̸r7,ONb)-\B(ΣOnjh/?3RO>RRsQĪg k\êK+SC:3Uu3'A99.5kC\!O.KYHb6=%4iaȾ$R:oov.OZ(%g=P";y~n7A`.T|'"QDWt!䶡76=;~Wϟ `) ڳ׫fnV~c!g{NxNyR3PX un1 Φ'oQ9?j,Vߣ4QR(~ښokuj>MOUBߕU\^~R|ǡgHjv!ݾq:ИJC(3y)5d*3vPK%"I ]yH2`1y|+D99K6_O9įJ9&/*[`y~|a^W֡0~_ˤ^1y;YVYjWhU-MGVKT캨a.YY2S79v_p{,jNDbr72p,f![ST°kzL~rW@Q o~5Rǣ?@!IGɞZ(>ʙ5h@iCˡGq9jWq䣸 ~W˔髸 1u{Gq ԜbP\3n]Iz,~֑\k;P.,%\\^]\%K.Gr-})j]ӴGr ʚ1)\8N}$7e֛7|&ڙ[A0zRmV%d= +4#}Z{HףbO؆Ji݊kҳ< Ehq"Z]jlbPA=.Po=!nk&1}}]]Sk**Ica{'ph 57}'JUDqYP['@/lz r@X*bV +:H# 8Uܥ$@UjJ=ȲecD(8&`0$ +c5 .o4+=Bq1*`*6U3z9 LP6;XLԙeʹ A;Ym='GOvCZ,haJ`@908=*Aub֦{3zg`QQ e$y;9"h0_cXCb@q6Zvo2@VC'2j3-q(W99?pj.(%̫_N}9_CZG?nƛɆP7KW9ji%=@m%8~ +13'{`SUaQepJEz4 DOX.o4,LR.#}.XfkDGIZXF`3T`,׬[p8֢klIbHE.i[{'.7WrBPD02(sPd̓~~/m%o:gxڻ 4SEFk^mϡ4 FqE3Wor*\J$򰅋|H1"QbO_\I?|rqD˓bz$mPn |gwQ̨}a{cҡ|", +C1YgVIPa?P72%rmF ,DOxؘ6_ "!x*kə>6 G}᫮4LX$*12׾;vcY=D#$بюJI{L)xa|(@E7_>mc,/c*>Rj`X9蜐Tu|0R`[d1D0|S:ժ3T,^ B`@r.(%΍ +"mu) u 9 $9/V_ BwvUYiQ7KGl/@o`jQ\Et'SD,HBgj+vBAz?x yf FfINk5gV>|l0Ps44RxϣoϷ˚EyYl\Gv.xKvgϱ_Ϟ+S犡lCOM=<^XQsI z:tl݀d_= ڱ2q=^-El?VV_ܳ"1vʋyBNA{Ⱦ A׋c%vvuneQtD]=aXH ++XMb +\T Ă/%Σ erGXF1IdkVCg|}:]YitY?ֹng\;ϸ1udiw fCb:!9.y1=U|y):sdaIm}wG!N8֖-WFcWoaRV蟻dUL+AX ³GusEH:IaҝdXHFtϥh X2#j}fI&+c/bz 5ԐܠJAm 㬀cBR![AYbmJ<ح) Pi& L/eڬQآ0ޥkL fg H[8A+tGUyw܏!XdЄ-FMQz.2ZO+)uxZ$/+*+xwzI#IM'3*َG#2m&~ vynMpҪ= 7ʝ[p 74ބ`ʯ 1[+HXIk33|k7\%oq򍯰wvVO JEz}EG7 8H|g(?Γh'|j%Άޥ|"<^bzl;"~pO+T*y6xcʈ Drs)a%*PS^}> g峏ًw!k8Y䎟C XŘ+Z4lx,fVۀ{6Ђ% q32kaw"/9Pgq Nx%Фk0L 5hLg~H %~Ayȏ/522/SRA{]Ƈ+$WV4,2c\XO~n_>wp\#k3)XQU@K="^PֽJG+ک˴k'S`DԱ1,y(I \י<;ũ=kC1>wL})4RNdHyctR*tlDeS iw8&F0}#b]hﭥNeq"|rN3x /zt儚!9ZJ7tyGuHI+/\`80#Tˢݖ?ŭ] Z6r3 l;؁H͙t/\b p4r bK 3X84؉%NJ~Pj']6c_{1ĝcwk:lmJ{P.5S"nG7 ]/2l h k L 4$h8":0f.نaWWTֈ1)Ј.ƕ* cjt1) iERGq@{FkP@Q3>~t)>( 6+Yŀ:os 1i茘)ܠAJ:D3`5$ICuɃȠ94*Z`Mq +@_$2 +MCџ}!-+¤.&˞;|G@)ȒFQ|]qy{{nA @.6 u:1W+ѶSnh{yyLP#IKeWI=JˊT>+,F[iFz|6G>YU#h8C8:kj |\`Vޏ޽QA:*c)vUUh 5-Mٲ.l >f]V2Į_j5J.jU(oC:]8 Da\{/dQρ4]nEBp =! 2>e0-@еppxx-bQ$Vcvpȑ3IۘIWz;_ QѠᲪWCe~EsA$%*ݺ?}oC\bd~N㱷տ1kO0F VAèvn\_C6M3ėtxpN+]>؞`}x +u-Ϛc{^ޫQDJ[t<[bdceORS(ͮ!)|"Xi(R*_e ;%4`PS6SM!I:_h"b $8aŃ7OpTE.+DBSAV*[zP׺K{6Uaw;gE3/[LTׅZFUcǎu +_74Q1 zZ7 Ƌ"S8򶫽nuy-#A6B'#n5:hmʖcZ`͞O/xU 9?pSjKQ̒RQΐ0s3ܰ@EߣV N.Bta q>q+KN wq{Į{YAXSYWK<" %\:"HnP&kdގ :oWW+.{Jg Mz^?/ٔE.@;|8EvODX2՛6vc-J򙖩HZ Xۯ5D*@妇vL"z#Y'BLBc~Ug<:QEZE++ P9C6&._5gT<+ ix 8sS⾜C-2?r8ze6+F-BlzIt _P@ۻu{xRGC"h"V?CsLt:-sIkJ6C| 52 L~EK3.`dNʦ̅7$͈5Ss Fz4{e7q;.,ZLѩ:{yu 4>E-啢)'V +bK5R̹R_jb'21,Y˾M ڧ RG2gjMvt1JYc.*ʀËhl/{7~4>Q]&\Nps5opE.?J߹ӥꮧjHcUڳpp/' OT\R[`jnv%a)*3.`T?f6-k1y1Ϙ"<ޣ`/^hQ@|[^TD -}Kk^U ]h烡 8iNg&?KU£]#Dw^hd#]ikJ<28 #rq6yU5c[u٤űF\;Mau'/R-)Ãgxj2fWtnLۋ[TpPPz :Fs0R& ꌈx&'b*L2,Ǯ аI\=&zOO_!>njo˻ ĝRI\[k28q`5iHGƩ^8a}mjPѧN霛.?BVnxoܪXK[MRPXX(H/miB7h'^/b3Zu(ӥPݰD-N10q + 4%Y p=V1xw!yJ +Mq$%W5e?[5!>O5N3Җ,^s&X &#)&jHљZ`>CG8*E5 mܜ +jHdMR StY+8EaGb播7Als\GX(tnc ޖ"o, V̤9 )R8G1/^ax7ǟ&+Vǭ+TZ>!DP`D1fX(bsW0< p!_-@dYxKraD`ʋD^X1*߂v5U5^-iT:vϊ{Tu 吂]{:S>!JRxaԛ/.hX^WZyMB|>Z.&RP/Q-Y @ӓ F1*ʿD*otKk@{:ne mY[Z ѶVHr;vs"͝}Zo|%e:Vxm^12{s$U ԒZw«1=׉rw]6mFME3{0-Y6ȚccݠM)=۟8I=ƹ>ޏܽQҹ}fԆ"*MǶIDVZsnIa<{ciMFPDn2"m.8FW~i<:PZńܗVi0VrZ:VkV+s*/ZkZ=I]9 +4Vg*mWR=c{pTƋT2/R\N= (B @Ũ"˨_FC7ApvxʚQcdTF#èdNjQY/ԡQy`T=U3aT̨#iҕ0*LÚ.2^hh#<j%3xE)Ye`)e -IaUQ\f }'˄3, k=썫d}jj4֍(: +?:ժǍP@ڞ텵!f ȀX̢*U+\: +KgL߭[Q޼J9d x6̴ºE&:m2hV eۢ zZ^W7[o"{Ze9PөL[iu)ݞu ŽhXOduE{~P\"*@b-ӾtWlYE>zj %M޹MuiZyٟ:]Nm1jv"O 3Xꕽo:oY +rTBnEx8PF@fr6v$?YMaNQMN.i{E{,ƒ>݊:7lMQuYh'qIjI|8O[ESU-11y\'Nf~:o$RkC {$֪hZ≾iu̍l~NS1*+*Lus@^dgQ?4GO:E:x{mzN?(EXph EUs6kASZn+bH߂ݩ0#rRkݜӟmVwTɝ&j#,1yvljev"}6яIm*dD㬯 åp.M.¨i[`ҽ49t۪]JH@x=AQPgٓ/܎݊-K󍧢KK(J!ShuSGG.)_Dn\vu7Ipw>ۧ+Q-< 'ۅm)S2[vR͢h4p#~  +ݣb_hEKҿܧK&\رb@O2pB,-Z "3b4ᣱGE@g Cփnϓ7@Zf{o\QP$ZO?CeBP7ٮ@j8t++c3mjGr=yҎ{REe~Ј}`/9ABi6-Sg1Ef*i6΂fL;#X6ٛ{zlUh G+ NHS8/zUS]Sm dtZf8#݀vK*咅tw(49x5MgdIJoed& mnB,_Bq$oqd 4]y2uA@FU/qzp!UhFIhvkUn}JSfQUa;P)݆.u +Y/|IԦ,lY7Ƿ= z7n;w82mUyFSgK@;3B)F`8F!w[7~>?KU6~>c}RQS6ߢJD, *9qBvLd*0P?C/qLXw:ǖ/;eWj>&坟K'{_l66Hj۴,?mT$N"5%(^^ˇJ֡J=cPhm[اr U,zIYY뭬u 1L( /h6 +LA]Pzq^Q(A|SίQ-yݟ\G|02XyftXۤ F}o9;q/$DU}O +*1Ee *&eg|9f+lT4[1!t/Ř6Pۖ>pje^8Ƥ :j u QI|$ +ǐ:/#O^1:YC)/8Le7I_ΝiY_xFA/MSW/jm_Uz9K5e7yx'p5UC:XDV_g'TӤ>M8T6' +(T6Zݙ> QQHcfg3 ?¤&>> -0sX9e^RI ]Gݡh\9٦MrY‡whJIG oeP/h"]v`bsR{$Ϝ=:^nڛ̩1:zWʜ󡽲$>?$r[ u +]@ +1|pEz#!@PSf T}}p懿!O5K"B¹]TnWF.\\2,;IgSj } COInjU[tbEbI%|) +uCEG0ÎA9\Xzv9ӅțF"5#ܐ$#J hMܰ[3 \Fo8KR֎Rn~oWγ(lE*o"Ä/~2m5liݤ*ϗ`q1Q&^;C&ZwZk^m|Ғ(+HAE +X0 ${![V + 0E9Ѧ.iDfS5Kdse֭Vt+*g1zІ# Alj&:*׌)cHr/_b/:wHHKC+MQeCSetBM'\޼rU:ITg<)i!:t'3&jC p7a*rܬp5&{I*eMQ: }5: @9q+a9l 7.n-%9xv<$ox~C5ok/pu]{,Pa5z'˪9}PV[[@b >x88о8-1b/98 AANXަ[ЦǞ87bzG3/:&z#ZPCU:IY*%%nEG1g A Z\oмXS܁l+4X7spP'ZB\;#KW5TnMLL}2 +B=׽Ճ+cow62cVH2W(~]34?z q| -uOQѢo9".zz81X&9%-J$R a`#_hjTRDTb1ӧmgvRtdKO_ݾW9 tGQv*liz2fJiׄ@ 2 NUI,)L6uE +ͺsg^7K)Y֨Ξd K(HV0~ u4xβu} DgB3v]ބ&JK_= hungץ$Zy-4hp@˻zж1,V7]9<%$j9 EREbjx\[Rq<+x֤8zK1x^O>[I5]+jd\$)˭~^`}at}AҚ==M}_ }Z81dV2p4rDg.G{aXU0MgJผ T=6t?n]句i8COHNsN"1;c,\]JzFŔ'ߘ1#fz;^: 1SD$qܡ` + +@Yښ&c<|.k[ړ/é~“Hn2j$m0aAzI54ntaZwMQ3c̷Q cMv ɼTB*~eUb/@BDw(K#UTݩ>.NXQ%E Ui^JE:^foFRwb; UU +']y )YZU /]6nSk68D>8wfL![OSz⧦=JԜ^s9O`KuQ]Pm]:yMogZv"v&bh9\fR_]S,MzbYEZt +ɤ$Wzlf).exucub@[H{UR׽i!1ר/;ck)J}*o;;ײ?.NU:f$orVW#Jr#_XzвL*=6Q;~!}a=vQ!]se+t[NB"?v5ȟŸ j[1NX'·Cܧk]W.ez9~tGPֻ.zQtC5n.\뫲*5l`cV͖[_B.3wn|j,?X╓5邑AS=I8fExD:Pgs[E-m^cl@OSna8 )3֨gT_PtCvuC֢ ϻQ٨r헮Tk1W- Ci\ͥD44 /d'nNWx8$#̇rW9}Ǯ +l~"!#TE*%ስk*mҹ*/¾7d[bM)7h]D?@V\FV԰ Dz7Rb]gp vdl*wK3[y&L3dq}ؽ*>X 8'x?qQ3\\ov(d%EBu3բ`6窯25&ykRgoޗzKT ЮșqTMi2Kn{]ښ&wvC4$5\[*▶yȻȠ3:;6=BǝCGݐb=fmj< D`uF ).(9;np#L Ҙ1Cm:z9/T+ >kmQo!"Iwd1;ƎΔ:r6Jc"`eumUulSUo;C{5&7'fJʎ72".t>ͥ#;]ij Jm#YCsSLOy#QN[H>LyuIa;06l^a +;ۧ1+Og=6+Wf+w~XY`CҚ+U[A{K{+hchQy=(9=Kr-Iia(k]LyZ=N띬PS/=}Z +tgs6 iAуZ3a(h+8Ly;]+xCxL;#h +M]PJ쓂`S0k%<} _mHc5$*;7;RA: RmEEC)c\m­afL zeUpZh>]F}919Uz̥VNB=$njaH?CwG_urZ0XComΣ)QۮAzL9x "pLQH %H7"&9Av 7̳Plwj 25>7xPM+Qe6,IbrF s˂5v؅ +=)P~+Gu~~7}ڼ_g~}ոXpՂIVDXkyIU۝[@x6ӂ:̞<9Yhg/;ͨ>>rTB. :hx@U* = ǹoV9q $?MMv=X-'S<oJý;kSEP 30+?g tNP9fX 旛<C +HArr6Yf^ГȹYba9r|,8g8{d͌y((vO6MGვ'ɚ-#恳*'n =.?sԐ2>֖ >nf)YFh=~fNZT%Ers3]E;͆pdx9>[ J<:Z9Yw ο#iȳg9רw }hBk-=6Oϣsvf +q_UbljgӤQ2cgg\įRuǍ1&|Q}g'=LJ%uNzx VnM.N +Wt;5Y\6^5BsA;>GsG@SJJ譞[/+Ă-z/[k< j,@9_ yF?wse,f +Uz.pN@}A::e_h JrvsFsvzio H3 VƑ90Dw&go'umֻͬ*'&Wm-Cb@qر7 [Kի>\b*8b{q> {MCbܹҮUc'2pp׽[A37A"o yZs&V=FϮ`c-qdq~(hӖ,X \Ԝ&NfJu͸^PRq<.YN|5yM]҉I9U; Ҫ6Y{hF3axTZjH/!&fLn~ q{Ey m{E lȀjˈTΨݙZP8KxDŽ󬖋yX~fI+󘃵FM3 ȒC}.v*yUQίꯂ"ּ$-V*ۮُYdqG2ZiX+{@`@Cp7QK#@\Y&4 +-σ[0W% MEvA >~u x_QѠcG걠AzLQ`dȝx4盰'G.+k,q3;W385F?& +o@/#kpU*{OݒKy`IH2zQN_QF8fĆI<ߒȷ.yU/ePTf8n?F+^S֯J[êI6J]хhM^Q;s0@NgP!rFWl0prA`fT=fwf/(TS!$)RP!A>HPr)r} &&tLYS 9r8&Ui0rDZ'B0l9"AbⰬ^zL LvbbĐ^L:a"LL)(1!%CS1"1X!l?L%1b&11񉒘Dnn'&|a"/yɒH[&bYڏyHHCNJ4LVɊW8+^IVhɊ"?`ɊzQɊ<Ǿ(%+EɊz]iHSdEa(O$+"ɊǾ!+{"_艈,5-/o7'^r_ѷ@w>,3-ؑ\Tcd+f}իWJF WQ݅Yj-v&ơ7%^1WղCk6]OqpE&sR|zsӔءFM06UUq_DPU +ԤߘMB&ͱQV:WZj\(xGOk^u6 +_(2=&"g$˭[YU;~vf2v6LJP`TdY:rFROc:P^0ڳ%\[N5*ByZ(,oF;fN**<@< BNFKXX"*&XNbǘ_J>C3`ʗ,UB9hT +|[Ɯh2^Q_$X_YS[/2)+0imc9/]Z9Mǹ3k%$[s:NUn(>.\䊼S[?drxG{Y̑#:Jv4~ifI~N|#c;U%UefVBZʣx"dykpC՞7_#<%'׽g|j5t:*-0AcEhd]*gtAQ(Aw5S߈RP\jg-R!ϏUJ t>Cbkk)z4gݳu#[*6.u X^-^;ѹTDbbEϓ޺\ɯ޺`S_¯Mo]0G{,yrn#h] ]h݋m^h~{YtEB&qnB#Fqۋ\'%Fo^ cy# +o^d} Xw1O/\)Z1%"rи{D0XqO-4ξ&?o,ٷB͢q3j\ue)rNryAdB8hTX@uu 7ҟ؆E)LGwFg w'dƆ20;r:)w:[f&7[Mps)0ՅjJ!owՏ8 1tv"ī 1-pnֹz1k]֯{f@Ly"nړFɜh҉YMMHD D \F7^ Tg8oXa۹f +gʝ-XYSf3r>gʖIJI;%рwpKf=ZXrx͹N1j4%&)oTmIGvh Y[tGYR-ِʴ&&$bc3+R_ $z)nRM7g9O {8y<3 _ROE3iS׀s )g>.1! 2mþXKQ]i\;ӟ>ϹεQ7WFT+*OBOt\7T8u G5 H} vD0$8}jN0W ">τ_EgL202'H;y k`"kD/ &dL$N3u )^o󎚊?6 <:ڏ7N?/%ƱҦ.2i FT x3:?ƴ1 %i-R3QQ2rZQ}Kѧ lE>ꖕ_yNWGi1" +crU (S~-;g؎uE]a!EfJDeS( N62kPDN};55ae"#V}э5NoqCq뒲rPUQc#0'SH^*oP-OЦMe7ZRw; HP߿ÿ S?~)KFB. ųx/I+ԍ3SiO1aŪA}]w\= P =AIƌ +;WҲ/&lUEM{jҙ +s +Z3&,sD>fwȃ\Zp1Tx\q +.ֱvQ c 6-U(l,o]="cƕc O\5Rw25b-j( ݃W+fzK/PCnx~!FKAxAFVKL$!]/3tpƫ~"3 *]Js*}2N6F]]_¸Y8|s֔)-rP_@ܪ˸#,|]iݹ+?wd5q[pt2bW4\&fbsUg N^udK &``X-#ԩ=r^#D'o[LePmxU0An,螦@LdN?&]'oJM_X%)FfbFwoi-Zg/;N$|諡WW^z:%-,\ !;x|7ȗ7"Pa'Hfw5kŬ O"YXA]n 9_׊u.7#kJU&u1µO_px~-N\ +l2\)o[ZPՙ` DWT8<3Ж/ٌEJyu:u)sps_F PZ1.1[N ^XqfT +ZLnra)t*H-d_c%5@]_5Sʮ ˊL#z[!O#3bv&^6rpxha[:ki"2}7ŕoc5> OvB-? MkSʓV*ş]FbGՃL  +K&cݱJh\tUWWURډJ ő9_0.7zVw|Fsqs-Q9Zú.+?|93A=|3`[<+g/kt| eys2t c`ڲ 'aBgܣ^Ba3̱*/$av|جuCes?٬%͗[cՋFp,p>sn},T` /iYvƄrAW>g~3A; O%'<5Q Xwq uUNu4`Sg&hŅNv4A.Ou4Po3c:XQUdquԔ7 +I@ꙻhI iwWe8JȡvIBjb[! +ҽ;6ʷU $b2lx>8_sC\.Rٶs9d4ס"EBD }+:- T:ʅ&-\4٬[&fN7ڞY.s$;:O}9BA:~!SZ$3QAu0nh^RTknR;I T ndIr~4/缓@"?v')-]lE=hMF/#6qCU=xJ +_B +.5w+{wuA^7baY߇Մ; 7`At[Cb}wS`tWLjaR.r_ K]\e+.[)V~kxvbnҭ +gė'W|qdIhYHmvj=JC\5 7~. O1М?ώ>c~Yۭ!eʭ +FV u48= r5.;g{og #7ʉ KIGYȂp6 ? +wOrQ'<5\*7b/2%YP%h,\&ގhvb\pn=.r3&WF9ʵBU 9wZ4l+NCw!L+Mٝl*7K":56;rTF_~Ab? ZQ`F!73QCo*YD9g_ d]XqmJzS&Nztd EBZG4؁OG i®}1E9ABAػ)6Y:&0.Ak>i&Jr"ؙzdSȺC3@n+ 1;)I=y݁?"bĆ~h=f剚5ə`{o1ppepՉ2;KOôh,k\ܗPmjire唭!&RKGǶke' ;af;N<5p?d_:('1L|N )₞bZLdcV`J_h/\gzբ`A;$ku-R_h0B)@1=0$lvNʹ(]kb^5$8jA&ݖN^=f# +GkPΗtcwaPP5:ɏa:;v8`w}htg:b,t +Ѻޜ7W vɆۯLd3z a^sU[|g ʾQS$[?gSۿ> X-φI@|_~/`v2c#Lbp'fAY[ 04psD`plW泶gV(ĽGs(R׿G^iA:un$S<ޘ?1Ez)}8r6iYz#JɑlC3$Gp>) +|X2GPސY&GGedzwi4_;HfKϲVmxԨ&&Xۛ\Ť`ۑBiOʚ?Hj[3Gp|zvo|UBHDP}ivǰ>jpgR5nخ; ep.cw[fK?1gr{DI+䳛֚fT cS\){8uI;T:`h)3G/1ը "7731s@A0$+! dX>!M}+1oM"]}U_X7D ;so˒_ nYv yav⦟4W6N + YA tAw8pR2 +\q"{$O/LGWsx:m&`fĜ;WcOj(5R`|C`&iraٽDB'L#}_͂(9:edȣSIzFj}@H5%B%(Lb"k10[OЬ U",bɰs֘*B+{S'>* ~n Iay +$װ+sFI@vvĤ/H{ah_yW48fhyrX^ Ycb1sZ#$B yùX&Zyw<=PןX@`XbЭK 8BA( W z(ӻ504@ *U 4]]Dpn0VzLvŽAĬ7O=Хw3Shϸ:[ sN3ɡ_?2E2JBwɓ2QMBwgЮk8MʹI/^5/\wަE ;CW=2`RAi,mteKmLgtS\JU +guRzh:J>s{!?))AaL%u¤Pxu[]ػd2C: 9Uۉ{ cNSm+Y-Ht't cs6 SM5pVIQ p6/v9l +o]W;5`;rQ뗄_z7fPO'n51Z9h(V*F߹R¯;hڭ3 KDDJֱrmsWd,sJ?~&`i6Ab +^ ?Ris.'G>_/!=J_B(2xD2p?r/>)]]hkQLe:lyI 6f_fZ,?]D=D4J؉Rq7Ag@tHK2n|[zڏ1t/[O9{Ch'4k=e. q&OZ`-v<=߭qFK]PTJ{zKT TUZ=U qWw^$tN +caFfM$$nUؖ7ZjuR|;)Yފ2┧(8BݗKJlIψÔ0Ι֦`{! U&W:i˵@_mʼVvճ-DK0eѯr*=Nd^p? `?iE!E_ *.NpftRk{e^8m u~]q\(X佰&Jq߆hN˰(ehBfAک:gAU- +$"RPmq⻬^r%Y9'=h7Ϥܢh|39 0Jg幻Lؾ| 5έu]=)SvDA2"'A ~_X^0.(ۂ<=730G/hI\*3b^"F ;^h7i&-E%M;FYrT t4M鬪M;"VeA')Cޔ=;gvRv6Kئs؂k3qzz/aINZcԸSۗd\o[[TCΏcJZ& +ƪL ;V+mŎԃ<2ĥC1w,h3ՐΫ1 RVX耇t0zk2zӡFt"z]RDȩ9ϲF~-0e4 eH/뺌a2Xatp? @U.s9^y>V͟Թ)т%ڣ)ťu*,Nέkͥ-|jt)Vjtq|S&mHaq6F? jHK[`~,pk07[Uk}QjJw6@dV̷VQLJ"S}Z-4h0bd[p5ЫguuՕ 76NRIkjP|FzT髨H-\Ȓ.w 6dBit-{*0>4ei7G4=yn6I@ od穔"Iv +36L_tnz%Q2xЙX =L,1>fU9K6}[\REr<#V&OnX՝υe1Lub87Ӷr~A!e?Dgɼ̫Y, qOYS6&7yHcAd[«xr[ɘ%.ܢ[<*^bbvO~(sZ]`6 -AwΪ"~Kv>t5=l,J"nje8JU})LƢX4d.m>ezPƎ`lVv"3QnOݞW8ٶ%HD/z*qb7i9N؊IvMp{vYiE퓦Z;:[0׎3=mc3XYcÐuQF-KMD|vd.$h?=?;59i9D4\+:<"PIZ7nu$670K=dR/TsL0yzZ8{V#hF^LldXfרuL\\7T>&nҋf;rSx=:F``Ey10"|F^Qzdş"Y )2Yo';Ao?' О K8|RTk"⑻g@73~D2g~*p1pV*; 싥&O`81bbAy*JM{ #ʹTXyؕT񭲢M1.E͠Dۿ|[@84#;K4_o%F?Q^TI_}AUH) U޾i8fGIiWWW Pľ???;(mk~~sp㭚fg|O]"`Nꀭv~1*b *'QlDFjxpG8psf[B + D`!$Ľk`YU* &aX+Ϙoϥ-`tYsr$< wcP~W 7<.q " êJkuQ2<\\R\dX>!6%Mw@{ȎXJ*lUF+s߽k$A g9~/@) Ќ}e•߸.r"‘v +`l\C2ِU6p^kPV|Ue;J%̴ŹWsewRܐ$6o JR=~O0y~+s0Ζ0MiY@eփ9+EHtDo.[gwPs[XE z㖤MqUj,gu~ړM;ZJ +wOKJ=qRN`+$#ԏs*V{ʤ#hDZOvFב4ryE>ǮQ/s0C;l8nM{>| xAXX>okFcļj|Yq^rkE6'j:5Q?1lSwbcM8tB^rJ< erA]T>xkG>FXKc}U+幱CdKrpx5q^q$vxX\3E9]!~nFEDY; ~JG=`YSNKdJZe\r? 4 +TLRNcQ\odQS6W]l6)(#"@ NY7B;o\QF.b".֧l{?8[Y,@rokG?Wӭgr&Ƥ`%Y볦k[ +9^zɋScJ(Xj2)&AE+gg3(yy:hs`me1{{>njOFv=wZD9fXՋMz%# TqE/+z%ϒ,ԎSnư0`x͜^B //ТzQCD XW<[OoK`~ܘAsdykY`Edeߍ9;m,v9'Gca(3ʧonZpY_y'lg~~=]ZhMQ3v|Bel3$h|D;O ӽ>u׋]Ox~GG F}u ҃F  y5m% Bg +`KCCb-؅ _ _zSI"0([1z<[Zu;4^|s?%E ʹG]vYRA|P;lt$jgjr ۙPFv vֈy%{4wPKeqޠ|5r䇼Qͮ"v9pd%ET7䝏̃[V, x? _~h.;f%c0 ƣZ(nbtlQiH\}ފ̫%y],О+i$מ9FZ 0H.:M>VB + I),<ߕ3|fx2ie&`+0%X;+x:fhʐ +G JJӌ +zKr1ݹbys"0׊`:{Vɵ$E{"~+91~Jhh^$%08K2r$/osGy[/+V#Nv$H:cvmImUG}R)K ~ ܚp2:\~󀣍ΕN )mTWG=<+ܔG87'@I޴P9Y_BˠhKwo5YxQ^'e X!N%^* -;n`7b 7R^\w[E迮Ei1c7p%gMyq{D> >/ř|\53ӕLM_!?)ivdrXffN`4ln8tԵBymEWAZ(@aFN BT\ѥY)O'2jhϲeٌ-z$kTTSJMV%˒_`c ,w8BH(o# 9B\rF'%H_l4 +RHj豅OdǴ|2&v &OG8Xx m⃲Ux9< -ְ#3.c%1* n,Z+Đ;suNܱ*cEؔb-ꓤ|]]zNh;f-G=O9]ZncI&:[be6rHQ(nn_Chß* +/%LC2ǘ\ªNT d8X׃o’Җre('KJN,fKK[*ouc;d4_Wgڰ/N5 ey)ϸkԮCJ]kq{Yt<99o.ʳ}%5q"srƱV l? omQV/~kKҭ`A]F#JKVDh>m9wDșGp%7UxγX2 wHjVXxs>[ktgxd}4zf3~=~4׊[,P{3-X_ol毿( g6ek^a7W?9x7gZ?8 |8&*NLעDD3P Υ(2=\<]Nu*p̦g>Ñ;ݴP=xu!ރDb_oiz RX@UZ6-< S܃tZ ,Q)L] ao=g㿱.9;Sk]"Y|ߊh9O}Y6Y39fM=ΝUQ=s_e17B O,t@dW lb{%oYAcӱigyvCMXU"0J$=W 7 +NP{ ΀GlOIw KRX!,$ ٹ7R|Y;Nf&c9c }!-gpm gvpV#\56%IΘ5`%bhOM!Df_ծQ=a:_sֳM5ϭdmsߌ"?rjX6ҭ%[Qa|AK_nvՒM?8zqdL-ܷs +CW癝=+p=_]SgZt=7С>9'ޝVyzvВbHB⥻Y{2+63ڋab,bH+ c;`C|R!H1?Qx'I-s>n #ȳ*4^B 35ٜz >~2F, I\m}ඟ-4l +lWK% Jo@ `=x+IվARrjl"UHMۍ=o%77ɦ`\snyMkS֊fkЌ~'NԓuoήJ@_Cɹ١k䙓diRN l'L\ASL#eX #aҹ|#zF< ^Y(M͚vrTv&\'휼D|JN +ܞØU>w4 -Nd%.*LmSes +ZE۲ 5wte46q^t-A(@DBo aȸnr6(&&H^ثSQfƒZY}Έ5~`Qg%yԌ1?Qε3}׿B"Ҁ(9o/Oy'~D}XAnϦ+dot|wZ,3[TI`!AW bcDgU( j=4do%+U3 $bD@kq+uhωy.אi@@} ϘmqxxhjaAj/yI^&[}9+h(6Yfn'[qWLd#&eAG̐5PM\b0OJo XE~VjR< yߖy_sbN)L+b\Bc=Op:)κ,,=?$>/KΊ>`Y yVµF|ճ#S5et + 8;1hPVNZt )-rEU# =l`Ed3j9wj*yO7nA\4Ύ~!-灶s1}0F\ (Eu2+ivxcO6oB^zl>U( +K YPE^9XƥDVN5 wBtc^R}pKq4ݵ?Ok$dWYYu5Ao:3SaҪ-R4oJΠ<F*<@$-#oda1ܘuM)Z˜ ,p=DD˹&0FգWʛfydK;LJn+3%@t E>uj1L+DB6rf2P\!|3(Z]=сJ;PNߌs'S֡;zi1|!j4J$9UCo,\rw~Yz ,BB;JNVcEhUUr,,V5Pߨ +& +si[2 ԄޝoϾUNV&w\F8/J|p=75#4 Rqɚ0B ۗ,A5´L' x@fV}џ'v)'T#3sb{W3 -(1yzT$ K\%'EĘ7 Yl qZ-? HP-@JxG$EC?Ե33+|Dn| _91 YFucd +g*S>;o\&CShd9 N-Y,jsVlWI$ + H}9?(;6h%Kl_$}Tkm= -QxfS0ȭ/I=^9Q"HR/0&<21w!*}Ej?n֍qu!EdY+!0DU@΢9g\[R`Ǟ!"@׏ǐCK̘i4$ \Q$$BԜ0QdjO)gMp7w]ٻ.1q틧 YS7àc67Qm81(.DN>QJ:4-.ܘ.N^T4%] E4kGS&9<↘w l6ϸ#3ib2wc@_abߖ9, +:Xʽ5= PaȮ2-CABuuw)i4nٝ8!{PAlTCeqKa~&e۟s0-0hYn#B;hp'm->i)i>UEk~]8 zl4 Ky G0G3wzߨ_ I֍ӠUƇ\f{0tdqPb,%x0KuY=#; e`%3?ퟜ}C1܉uXZ.Kr F?԰ qҋ\BSeQnd1`c-csQ <سьۏ\٬?_-r]YQpVy]eaoXzB_9'Yyݺ|HyiG/QorH㑵XkyQTlQsVƖŬ.>'y?~ S￿)oc粔 ξ zS7/F~;VnU)iMqN yXeX+a+f;Y'CLoH؍e63=qRX7 pJ##1V#88N0fM>4=F 8N7­\+)5aZ8:i[E&=x~ǰϢA;NxQ\^Z&~'ye2>:7Ω{0)>+S-ӉӫYY 4tO#Mݳ[Ad-P@N!X/ax|g{#繿#/]BDnmܹNyƥ.(#+JsfwA}Vgg +WP>ቕ8G]PgHV$qK#M+(_thHPjvKKގxb !"+ +E%OJY!Gi3tg}67hC8g7ow萪w [HwY.B|!`JU +KN +4푢`%j0qSŤUvydt)*N|`ԛ&8q_jCbr,ޗ9EqZ7WoT 9fN)J_s!142q%*[9q%%odӇ8?W*aUhA]ٴFk|=W%;mWa=w$\>%mGk RB+=4~ܕ9pˁ̙`'*"n0̖_Yľyg-āi/$46?ٙ que{,q %]ɵ"<vv6lbU<q.CrO Wk߆܌eȚZ1&~`0 6y8}dt[,ȫэݗ4$!%N?w_:RS.pCxܙ@ȖS \A@GI}҄<)rO_KzPlu +'=S +m\k v({#l + As} 7 V% 0{!+$# w +KP*G$ p%/A`P KT=Tw 7/EG4LނáCrSזHFǴϭ3MV獇0 _^ A`ʘS\Ar [enA}/Γ5J 8{qn*PŇfjr jD?κe0Y,{M_RL̆cВ}gEHХhVKJ ˍр$viĨ@177m?|{#% 陘 $4N̩f:疅53lLdv2?4sO'>njfFRq.bi6vno`';9{Z#'j(Hkk5`q.2a`o+v Y]I'͝򟼏M|5$d`>^HhÈ(~lcbPt7fdn@Y- Y9f6En5Bgx\_bpfP̓V&r a@o-`'pRFsv +Э=?]y;o4GIZ3FWQI~=~cx%$Xfb넋? +"Ȗ1u}^+)+aq\RAWYԢB8jc-w_c٫;+ {V -]|FPؑK 5}Jn%S/ϳ%3\&L_K7Q hW '(Y]Iky@h2FTFe`M<=[n#%o%nźLH2L;O~PBlYNJ܄4+{zD-(.((xWG06j2sd`Gs>4{,ri;jB {.ROb=JMv5}7rGEmwLbgm S<^B2]ӎ q0Drn5.K|Iъ$;\(>g(6kȒ {ҮA5,v$ӈ;cD(S6hhW <,py"=]q**؁64k򁉮g y!ۏ#lyc$1$y.<&^W3hHM0x'\#I!{WN>!;c;KA1,jb'mdOnxBDJj.\ ze}Tiq𸗓>_ W"T +}:mj_Y>h&qG`l6b%\v R":S93l߂k93bvy)]1_,gJ% $*/2 +rE!hnfO#&u:hC +2\rZ8@1O_N~U$ɇHVѿ`9oNCgTϰgёrD:~#$V-&r{r?T(|/F*ByòαRiC1UGT^}fJ-)v\\7<+ i!IC6#Xǖx٘%/ fI9oM|psB[\>ɳ1G P^sM!R!)܎|r nNBUFJܰyal2"n@[1=w9/Y\f73&bdž>+!17ߊp|螜83Ferxٜ/$ N2@*D?#ˉ197N~ΰ~ݙCqQ1(~UӐa9*VlsVQ +7~e. #V?Ld_ W?1{ٱؑ¼m!,o6"`5Vk실H=[<#q +q8+jҋ!Wv2FJ UQBu#%Nn gc + pkbӘz p%Kh\ޑN!fP1 8 Eh^AV+CfRӫlcڜ:rH4]q+[~SVBۑ;v=KNJ%[1\F)HDB/Vh~BUF\*&5Ecwgya2r h@],9XGFHC0XjVEģi9o~.3- +GOkh~7(Ԃzn&7$"b2"hBMl)H߱SC1A+Sbя3cY0S/ҴIں(犒j| _lWKe+ +A.APu'Pl$ uy^MHR,'+ӛN]WFWpҙ-؂;!)5c! BmM42d3@RGr-S7ϼqް *OOlYAV̫sf6nNR▧-Bry=ᖙKi%~_rMW>\ anM4ZPA} AsjzC-C>Բ#?Z~5./p[v~яܲb9ܢ=;%Q/.rZ"'fů;%E<~ս`~qBڟϡMЗ_$b℗Q +aukP2ɰeXűv<jbBrr#rYaEgog^ tINȳnn^ PϬiё4.,co@7枹~;++VcKL[ԖͶmofrJ8'ȓH*^tE0HVK"̔z>Q; 5QFyaȀ>PUyG@ob"N%p4g]c}?V9Se:9X؛tJ쬎뭙=*zaeUX?t &RY677w&[->!#; 8$ 0][z'"do3+[\Tl g +>%рWV5%xI\zdD5729$ē]ln|#V 7C]7CTme.iy]N{)I6X$g^:"ܺuI^ySd4Ri F9%e0΂6:rWE2 7D)^`5w\Dbdst^֝>mH둁Hj-Aɞ@$9Tyey w.x(I~װCR(sDɤ)# F;FJkbC\۽B} bsV(3B|l5FEB YC.R(>~mB-ZƋrb(s` {_,FQoFoqPf\ j~K6#)Z`Oܗ;h,!! WwI|Cq|ȠϝC̈ Lڬ57bn0q6i7d6I .G"XdXg>2|mt*0 Sg_ p?vG[ C , "scPb&Z/>d/ $j6(.KNZđ"hx(z"P'h acXօ1 >~)zPʧY +=KIvA+[&uw.D6\_C@VLU}D\_'ZQg6ePƐE6zM!޷?zD,lo1@*- +ca MLW5Kee X}=Ҁ)8|1FQׁUbJ=M^`(,Qw^M-XdBh!K;Po$В<9g=cFi3Q{V]+tdyl!X c("4ʩp'?60; Z?B0ۼlz rJ(!~LJJ̠ŅBU=vG6j+w[L+iG58YPTvQ)mfqv8pH;':Kz&\$l-1_$0e"As]7++rXbfYeAgi*IeH-`\ WΊckXE c<ٱfAџU,rPŖsNZINx蟾 L4) /y +i3[KqE..5C +"&1jAɵD[IکP*ﴵ"B 7PE``j%N +Jnݺ2wfѧBʈ{&\ɵR&5Nhu$ha"{ã +I2Ud3<`|'.\I<18,'wox2e{gF}9VEr[ dLocb2keeVQ_2.kȵy3Q%f]XARorJ%fO,!ؘ@q`V3?->/MuFN#b4E܆Eǜ!M 1ikhΦ/'ϯmg=NI)RoQ):^@#&jce &7aiElU8&s-0mڌJ@:d8dX;b|lQEgw3ZLwX,pEoh-4I/  H  H v4滜٭ c.z/,Zǵ1P_S˗w1r1H g}4kMn,ȅXC&hH峗( &a&껝ijy13}q)c]Pd *~S3HhǨWJM-n :n =F$LOjv̪fț0Jz.=Xuf4M8x]⑆< ZLr&k$bbMl* dGYwC6т=É6 +yV7IEoM=uh$|;soK Egx+ξ=#dKU/yϣF"Ŕ`G]å-qe\jG> J"ϗIE({\dV ^"/;5.[4nbWP죞 ۼ&;ɒJ+^cI"hwZYzJ~1+-EἧPVg+}h--_@ŹʼAYY9惛9f!3[UcfC?7_hmݸފ=fŁB2OFg*^FfCI|p\l)N] б= -hSmC!XK~[ewO~I~[E[xyn)[6W sB|v)>4 B(߸POKcga j=#UG8Shk1"#o`bcAbb]f7"kP 4a(ȿzZ㿈^+cA'zN埈Wq7#@%=wuEtؤ<|I&H'sA (#ҿ CwLj~c1ny8J <,&4D,q?I;Akmj+%X< yOZ <8Bn3 >GrUhEX*ɃD$S3JȊ"i:oW'*^ɒ6ɮM~lbA ^:~C,%ߺ a79׷AQPCc#etľ{GQ揾]J&ukRZjY)Fл^eOzN5[1sb&|(†{mo({$;L[‰`eY ˼~ts + +>W +y+)h\MzAkq%8rCnO;ǒ)HA (5ЎyEi@n֦H9gJꕔFNKBq٦ 4 0%]Pۚ|Ǻwf.t:2.] u֟7,"gQuΕ`%܃ekN>_@8ߧj\ fP">6&CH;ntժ]$Dܰ >Ba𒵕 +ceosBAmݯնܺ~jiaZ p PmeXlNvz0j!SQ5lVvg&I}fJ'8^AZBe%/7w]llȱMIxd(|LڊWhauԇԂ}TCxvPƜL(7_nzzs>YC=\*!9o}-h?՘L:641 fSW2NY5TgQSr@qico ͥ4nE;ɣctnlysPNdKtZ '8 *CdJR_wǾ{&έP1> n(;.M'tR0 #:Drz90JDEKN[cL84]jrAA rvJN(ǰА_Ԃ;H䍹lmrRЯ4GIgvZF'w~w$ +(T{& i!NX8d"m_/Ւ+G}w M1*Se{SR5 +›[s̴i0}%GQ3k`˛KռeNI]@bQtzv]u -XiتFo4;C2+\cB;Q&zP#LIΉ-Qw.Nzg/2<8H +8TF"[:^]!M 5 75FN0CP+@7u Mw:Eڒ22FoNP[d[FnYJh0\IIQ[x]DѦW:KÑ<#bf>C-K.hPæT|\;ɬrj8 +s>1_iK pK}WKE!8rG6G˪A"u$ú{(U \T(b㉱L?RQ!)t^L +Qb 9)v]>e*Of6)^Zζ(ԥ)17uP:i.l$JAfG O4N8Z M}41v8;*<6h%J־sYQpQ,drgyIR,xR7] @7/hM~r3S5l!f,dJzlg\EG+o<Zg0@QtQjYtLO$¬M} ">!q,b[=ځZ}U}h=Le@`FEs9e$`V\ 6}4(=7X(C~KWoۯ>vD"\ &z=% S'a:{1P5}6ʼn3zQ=[ $ʃ[catrS˲m$G,%q8Md fG]~}7}`3W,yz:q-ߨJ&WOỔRan~e`]FFnOx>J<%yEV#IE>)W,f]< +.n]B]<6 qL(}|';%Z^*)u=-Uh̗Q;P#(HCV'ZRIQ_R/)`vg%Ip6`)ӓR9/aEPA'Yt) +*T1`Q`:V" Ɋ6_+˲aA 3T`c*AoV,i{"AYor[Tޯ*bqwr_z狖`YI.YIRKp7)~Dܛ`v'((2{G@7'DbpʌM&';LD``c=#VTѪ Jۗ&&GL'VČtxI-V}݅*Ri]3w˴ o<Ӡҹ["t:o6p}ъ$:4bɲufIh@=ro;g4䬬O>\JN8% 5$vXjz1bHAi ڟ[!P`OAioc0ފ)~qNL!L"COOgX!BXbA|<-3n:mφ㦳|MG@42@$Gig0 frW$ + WYs{6[~=U-4P@cw ŞٙG!U>NEZ#vΘu2tӖZX֛ +)U 5樣SFhjrʖEcܷe_\1lI'auQCT=Ka匶~ȧG}mDn(rg +fSr' Q=Y{PѨa(CFGrw}$}ƳH1G%By93I L0QΛ]4 'f߉zk#hY9(]R k9KhVzᴈ&렧$l"%Ï: no4l1/pVE$,d?zs>"Ǖ[t{3ݯ /l錜qvɕ?r'lR6QM<ۙ%4C*`dG!R@jY!,y:,Äu~HަG30 +p`%Eo͈;I4ʁo +%IIY0 r[DDHnWBb5gAs'rV,X`@M*۫6phB_j*23>. +?#`Ӡf/@*,8B(wCXܾqiי͉CoRS}=iCN t^1/(δa,Z 4NjfxCd. +8V OCl !O1WNX4wp$V9r-ȱ]t%i:j544 +ufy Tخ'^:Wq|4Rhf.j_w>GihV nYS/Oq +Uk +2sq!G(k;E}ҶIs:X ȃhIsf} iN5< [ pn3MZ@m>]u)_gY_;:-zD&<퀊P6=}('<4 ~5'qXoDz4Q:q$>0 TtKD pW.9ONl&^ (.?=D}Z.D]uM&%΁]E}[Ӳ~!٭WejRؒYl6e^cxY*-F-wT?EQ8M֭0^^ĬO"ڈiE;5m-57{5׆mpo[|'# ff +L8X ͬQcxRNM\u:36@{D%j*ԝ*ui'Lҹ +*/~G<3%E?UB)b;t34!/H {ّ P#sb $EisbG~(ʥQYucOf@c!麰B$O#EiPFRSA%4P)M< wډ38&8+%ADRW"x@ƕ+7aݩ[4~Igzx ufE;,:'v%; bUrp B8_rqD&9 cz]--4x)| NligP>ny Vu0NE=2.*?KvuvM_G3 yu©_]'OFydjFxUԗ,.V 5<ԫNuGA +]Nۺ dU[+RgxViByMvc~ܮ +a0~wι^ Zb\S3 +|5.K,*A +3|m]/t+ (}Q<]Lr*.u1X~opw}__)lLyY;S:ɇio;7% !-:DRWv[rˬI}S>A3F]xx]#ѣ"^*I$a_8P{:&beE+~ ^3[!!Z@rbĩ+(w=~n "\ySB |NEKr/ )ӅoHn-~/H)~hSwukx’ '=#pTӠ#vKbC]"fŸm0_0i='.3pNMqA^xMmpiH +eLecG3V1Hr wGɋHKVMQXy4)Ozx!MZdj#Ax=kG#y)4֭~>4=s!{\Be`e!eWe^dۤrLsFSrgh/.*H3+V/#AƋC}as'd?.yh{+^RiM'|OyhߚPE#1xdEc>ncֆs5"0WԾxzib݇'A Dܼp?"#86hS8.M0x@dAdnxVAx#xt1"(Yx<7Žr3I"ZU_e qyT^b =Zj$nձ\٣)(@&gGsq&G+j77r-0SP<ҡ!.{ =$}`MH\ȕuMX@4sثˮY?>BB߃YQ qyPhiPz+N+uE+MK|yil-ZPX$/ل4v_sЀ/r-}+EkO˅r$M@(#Y`ϼn^ \LjSYu7djz%؛rFC| %5M,06:5XT_б%f7,tu"GE"f#窴$.ső"pZPd.lڐMЭqʅ<8.M` T㍹1ҍ9  6&1^r߹Q^,U+Xo234hsyVVw,j c Ҡ vDN4i +(q!3H\JLItlџA)5\҈3rGAA6wxΊO#13,.Qqm|9YɐYlg"uKi=w((Mm^[nKih0M gQ8tf'm0 ŗ>k'BjֽC. +^›hbQ\+gP-e*%Q4#E fqmv|Q<1 ʾQwbmiLnUӨED: T.~@c\a:O E!{j;]HNNA&x`4Fo>,vkI JZ 9aB]fʠ1M"YV~s[x&eb} tҔC奸Gkb\т +Z6 _f"zтD=gVЂvτDqH--(KB1xh32hA-蟋uM +h3+hLgyC+RPLIS +w>3)f.>+\1 +Z[x,`џQ7"-^Ȋc.!"<^ vyXA߀'S,q4jqwauS*"dX~@VptXXXoOUO7(N#((PYah.SE1 +8()$!xmH{?҉;"OOF_Fxh4QW9OQP{0Z(d)1USCc3;pe>GxW:6ˀf;14+NTlSNl#9bCۼVtҿ6m5g2rRZsUHy]'7A75MdK#![`_ EWT=YlFV=V}J8sE3UL/7W/䐨~oͬ#b¢<=tI4Rf-vF=JW +% {`TL|DU=J>|l|Q=9Q9 +3̾+U oGEQn"!kmDNNJqql>DHٝibjʦn~Rv?ʏ*F@V^#sa@Gbjdhm*PNGn}<"C2mg1L8TJqh߿lvbO# 9YHy`LoeLGk ?{K&:"dIҧw>R0C0{鴕P8\{GJ{FaL15?AA"h$r`5~6܍\F ƦZZ^ps1GC6El]~hPK}u:=&wՆ'iIQ- !b bbBt,,S#roir0m 19$r2Ks"]" Cֻo L׍'{Lzf^"jYS~ohL%8:lƃ7F4Q…RY3,m?5=z]C!ԯ_YX@19c)<6l4_#OdpS&cBC?bo5Nݼr~8Ԥoiv"f[*pHdzZF^@lUD0Pwh2RXJ")R(;o R/)&iB`1ki-P.& 2M0-a5‹ ,نm+|6G> 򚴷_[G0=Ԙբ(=qI8L5F/{կ/}g8,TDU9/k"'+Q VuDH1s^quy{T?/ 6M2%DŽ\AXփk~uR,`8:/lwqOHov46;Mϟ,р- FcOǥe* VU! VpvP-*47Y#½yb|؇jo.T!-Aqw҃N'G?'Cap5Bx5Vp*)f]:{̩e$%9m-KR +DdAh^bnض'-c +vY qTgF6V[x,Inw/]BOR_kD8ȼk1~4Ian*|[tP;6PqO1^ |gϧJt=T˟?*s[]z2>)|ybqHW/RC JE?DY~dmN"!*g ,@Jۍ: -e*qH0Oe`[q+#Z4S/c?A_~vA{v-VPޓs=̰t|im6Dx^omln Csjuv^‡XAu=&Y؊YYnw,5W 4RyWo~w$|)RA!$bC' ۋԞԸRڞ +Mfl7!'Dzm3в5(Mw$SBe";[Z#i;,*i}1 WWFݝ*@Ƣ2NWܵvenh%Yv-+EM I/vML5j[!Us3s6y[QFV^zҜ6D8](R}}2AH].^i<,f[\#;GV~ӌҵvAUZi)eIQeKJ`HtݳZ<˓܊jsW?6;eĤ'}M]<]$jI!) ^^LX~{0f&]}*"qf|=ޘbt nxHoZ69O6Q7c*Կ?3C/1,3:t߭fMTO:yu)U -y߹׿gZr$qؾOQmYaYToE:xvX(JBm[y=l3Z9\ .ujIf)qYdWE`g C*Vfc0GɽᴆwI=QP ̪=4aΉ#*qA?#qn<+7tNW'P_hSщg3!E YrP8Q7Ic;`@ktsC)GH`s'rZʕ`r%ZH}^:vڹyMY9ܠP*as_;1q 㸮#MR`ӝU'Q N xcФrdI`/}wx<&>H.s$eUJ4;Nj+R~DbM|#-۾{aౖZ%(L7:p>A(NU- Ih:+7pvFHq%7e]UA674oU)~j*/g2Z+NT4eUW,diVviucn9s*['/Cff\G/׉+P_xf3܌[:-J2+9EVgb +H&Lύ +U0{9gI0B5gTZeՓö;cV%!k ~cNMWBWWC$S)!ٌ޺ԫp. wt + ک:PT*3,!)űǨ~܏2MF$sFoJqw8^cpkU(K7Ju gV]k5hn~ +Eh*Zyg0Br} NU'U‾ܰn,<"OJ#ԣUCaEe%mzNF?%u$]VN|빣.vURr=2{8Mwm.ݱq4W\m>Ox&Xj4ԾnEZX!~rŤ{@;'nzG/Xp$QpDl1$ \|0Ng)>ж>Uf\mu9s˹W cu޽>p_+F̈́G@QE#5딴*u_4y;I]G6ue8(;+"8Ik D2CxϞ;O\:'rP(5T6l*[' j'l֩GiZ,s ϭ1-<E"p^L>#fw>2A(j-Jx&Pi1&b+Z ?\Ic( ?)3Ad?KW;O;VVJQd|.ypҬ*p$ag?FlME9@.A=iOH}SMV-}Z^A.")4Ӊ5Tek9BomVjdm +z# =tG;2ba'(51}b.--*hH +hT!9?\J3o|W;GG!SA;'9ʔe É?>+L9Xh灉IФx;Ѷ D>p`9۵#\v88ev?SătIIS;PFR2VXhFgSyb,2YqG*IMESctCVg +Jb-H +&; ,khVrԎ,OϨ +of-fN;* +I+EB<+&QMpAZnh[io>?*'ArtǶwmw@P!NG=΁%ToR yZQf0Qpx:h(L'¥&"2?nN,x#I8("yC7Zdh J¦:'ܥ'>\#߶}ӱbcCC -Vv$ C7 75imDo* HäBxA!. +b-偳?> ,[Wj,0JL՚[^1Xlߩ!#x}x%8=S^Hr Ȃ}dcV?{}zYhbYa>NA_H DJy/h_zR|icGz`8ʷ8|LiXZexNTv}ү+O9S§_Q6+??3ԗ)gfM 3A@5_GwDSдJRV+Vv+g&-#u1VjzZp>{SWEkJBDˑቓK$.F+\+:)W4ax]lWIe7)\<gHE֢6 $"#ȱ\#NcFZ&.q.'L sYl^s|FqqN#_8 +sdQ|>Hɱ˕.Q0 @e׹"'!`r +f̌~4HIEZc6 Xr8/F0wUw}p/ 1)jɈ2V4QxoIG/b_۸}{Nemj?ۏ,7uUvr|4OȗVߚ;gߵ??ʯk,{NFK W`B`^Y +=ZSIM %ߔ­R}"F;<l4@[ZFn5ġbE.G3;B$%D&RZ Sew-!MC:_&Õ`h8%'͖iW XI~2` t6Y +gˑn4F0co̞< e˄Ņ&?)@_dlc&%YNxTо>'ҚBEw,:wk%ݙdc* W6(7K11&'89h]XnՓ Ԟ/&/ĀЃG;+߽۟zњ +ac࿽3rM<0uUFnu&xxKY7Y6uGӃ\ Ud5irYð;8IZ{]z*;Ju;Sgʔ"%`cY';*^Y!]{R9EUU\XYez5p)SRlSo68 pٶԓ&` kOf %]'h%IU8 +.=p<6l'Ď8ޯz'Mhɪt{c6sxIy_QXG>Ib H7$ڍ(뭉J D[,Zacm +ݚLܕ4 Miy̮ɂ؎"L"Nyx +0[ +"pnCE:q8Pu=IĜ&epaj.\]W$ԙW:88ݬ"=q;*9Cܤn1Ws +ӽHtn<+I0FD1L/}0ZXwa梍.+ mm)YS}̂QE#@ճšP²)G +7G +vġPF )sٙ+Gx_!Efy.DԹ4уKgsnn;~SM #XW5(=~7q($qB}* ̾P,3GpW~_m__}u}`JpB>!:K#􁖸"p<3}`5ϡc=u}>| &66j%XT;lާЦ +Z/'Z> R/` 50o|VF*"YLW fisfNiQ*=boqϮв1#6Ut:b?F{#@ф+J`blmb+obu8AZR[-Aw`x$曗n' }x-zX;UTXhHM*rڜ%xkno^фkD +&+!~5}nȓNo׽38c|Mq8Ã˕?W2K>ber[D)nȮG[c斣fOubi/aݟLQ5$͇P)oa~FŶ d\S9E) nHF"~)AsxKv.y^ċaɚNv@b$|(bmdWmdӊv`3@@'Nr_dkr@xѵ / `Skؽ!6\HGkLRU@n{ntD@ +dD(V`hL$扻?7/\{зlqi؎c*0 17~<} ;M;ϭ<5 eFuVri>v%t^Vd6fE rP8)T+L%X&o%f3wPWP hYf V1o4 +< w7gz֫EIcU} .Z:vi]bmcͯ6Z}(BvpÑU#\Fz %Oo›Y@#k;zd-pvJu,lLe˕i%*[z([)lM%ʖ%m' +m2s,mGl=-[wH[]e+TBS#(m/ Җ Xy + J[^@h7Goc,u|nWGҧ0mi9 ն -OV[:B[nh9t_OVѵG qKc_clI @9G]%PKjq H`q 0Kh[~|-ءm d]|m{mжDkm r~-ӈږ ~v-QG$nGʊ9-J#-1$CcieTyŭjZ2qKږߦd=5$m+*W*з~eRE25!JѶ̻юm?ږWF=ߡmJOǥƷߐH7d/xgJيě?(k>cC$PBvx(Y_S4bXԼζ#Լ6K^:~gM%ȁEs䰻:үvzQPx +/T#N臾FҍVgv-7,f! SW yŀ\mAAXT*Pa&ǖ=~W˪ |M0V&e1=a n-XLS4FU0W)H@*#ӄX]sh;Lhd.oK$Sj!;A X(17^ҼFоBs;$ +h=VEvN~]3w5\!GfPGtI l˹zʒVVda8H|wQA#7oJ0:XjnQG;V%ޝEa|ʱnÒ8tإA)ԉ I=e6kp/-4l/cfH"!FŴH̎}GL$Ok$Ā@-E-lrWfl',k|VdܟITW0?Y?1p?I^vϒ\?_kzk@I/A0KpYӄ1DV`MO"ٝy{`E1l۲ދ,OqP/\ >? 6tׇIe @ +KK({~=Eg*S_84W8 z?cM<{oU٘]`֩+Hvo\Q-P|?\bxoQQB}f$k90oՌ< +ɅO]+ O#ͅcV0XYDGXki7Jbu`T;H38 t0v/#YO ~*`f9CqK4ㇰ/ s|`PllOѭ^/=1xsm+}dl_ucPԿƽsRsb,2ؿž32ҟZ0FW[~FmY(ePpaȺl,( &u"[&Wwԟnֈ0iF:TN ^U~mM'i&ѩPTgFRV4jD$b[O{{iFQ\bDm|{30-n\K6elPeO,|V.Nj̯ e{nEǕMB#e`#|ڋ"$4+j3KYfȱg ?Ïq!.k^"#o绫E.%?̷Daʗ_o{wx˷D/?bXt(9INsFrO?䕥PKB+7ݿ׵GqXOXuUk'5|pZVdCѾ,)S3/ ,J^oub8a}1+ -~@Q=ш@ȫ!h8Fjѧf+utiFO!]^=n{߾i{B>5_]U[ޯ﫵 z\{.txK}NXI5ٮ~l+"%i$?^rcFbFb?u1y\t}3bꊶ-{\n#W68Nik3\[5fO zԼ%j2 +˒JU/t.`SuD19Z(J%/ӵ{-m\$"겯0CMLE\RMv~:u\E!)m?YGҏң7ۅ#Xoۢm*w3L>MdM#(aїO,ZqnQW{Iv07E}sSu{LmZ5I#D,,v<,@'W0L9=ÁWԯQn,F0ØGlX5o5r94R҈_ńǯܞvMF~7t#-y +"IYDP="i- +l{ɩ~>}G1QxZEA\gOP/()xfzy ]H]T!͙ OˎK-cjOq]$X8-3iOZՆ{@nHxht,bt/A^rBKm} -;=oR[ӨZ |zU:jL:Ms BhpU HthڝyrWfSSGjQtة>{yhQ>Oٙ O:=̎s$^KALS72Fi_`a FՎrO֊6GG=sC;,{v:ZTXw:ٿ:zw=&H/^C~`wm"43٪6Ohp9JQ8󤥺IJشҙ];xNɕ+E$'lXif[#oy!\97˰'f?֩`(jQ- +1T wHjIf7oU\ B`Ǖ9T[h\"dɍ)Nj&f>P/g4" "*)0-NblęS=`XMFwVdN_?VDM@)o>\Bwumڧ;xceFA^QKY?DIdcMIٞ 4YHaKUHMf3o[ Y"Rb2iTlRk +fUtVYh#LI{$CVo.P%ʑu`SV^Hɻ^8DX_S&}ɨRӶF +NaI{7(SX3Izh}gKqLAժ,SPT:U13e,bKCU,xqmȁIuk*Er0RPERԩ(mMr\ @JZK.l.Mn:͢ +|Lv9 +iLnD2 vG\Ɋ_[L]U7tWK->oxrhG^ 2GUSnK:))8j~z"v'@C4y5nLAF0-WjÅhrSkMB^3fgP)UΓ +J/;vΒ=g"/N%a}lx8'[U O_/m?W:=p@~ LAH9Z)\ @qY"^ (x4(@~A7HvȀ@t+tJ%[M݀ + aCUX4,w WVk`';j\&uIcXPv c/Ҽ񟏺._/,R7 Cƨ '7>*l;#Mȗ!$ lWtCMPiVr(z|Z|4Aɢjмi(]\akb+:۽rJýw_,ɳ`2J-umfAZڼ!eQ3טXd<| 53;3 <3׼ =)-tY4{9дƂ WO8`Vr Ѯb$"4Ύx'V=휰L-uv5 yaBOv-*K6v8 88^kǥljOjS +qcKu4q]:q'J[&tiu)xw@]jKE +)NgPWZQ +<4IÑv{\A4{Ejr8X[ѿ5!L.zVJ!#r☗cFxޠquQr49)c!O/y8h.;nkcĖrQr:J ^BJ}8EKU2`36KzlOvoœ ߫bӻ6,t"8M$ˈϖF-l4X5ڸ{K +܉ |֚d `tnse1Z-)BO.17%2.7",/ j-v5qsd~vzV^-dje +"څJyXFTj[NW ¡˕T(Z.2iTqAtDChږ Fmz'js:Rm8-5u"1}Z$b+m;AA2([sF~j{ϣw-rbg8[۔p}lFi|\0aƂPjɵ+al e4jP5Od^ +I&)PK"2jFQmءF+jUOmc^hU4%d$qu5C@+mq_ yJQ8$n1W41g9BC[T'%q4eb?K8y;X?6"ZGc\7h B I?3:p;/1msM0Ւ3B#2 I=* |OGZ?BB' mK0Z}' X?6ۑяՎcp#Sw5"F맴<Ώ wOb&Z>Lǩ!8:?Xk:?mcU=ZK8?J>֏XZ?~;<gv.q\8Y<OPxbwhl-RҽnrP86V1_V2;mM#n z"'w燤  R.3= "޺^Qʟ9 'źlW=zBn=8g'^+ zg +6tKcb,1GuLk skq<ɤS>(A]8LBPMЖXgyLi`,|v,R_qls˻0>1\2nib` ~ &u$`8|fiPV9i5ryfC}_|/D~=Rbjȑs~)G]H9Ċ#a*,;_.yN~>лLCoa-JGXqGArr  +{Iat=zWrEȨGKl+z ȬAZ6 _EUHܳT$ns`wO-H ѐ")T#T$R$ WSS,sA"|AClT ;r . 1,'p1qą|˃6v?cCOC]DF 8L*8@tM%+`ͳ&3aRsAC|T[ᑰJ>Dfamj j?p1t<7S1Პg) -Ry}&V6&A5 +CO0 X(J.6,HN o8}*6Yi$*ɝ4>qcUujt5/a'^=Iw$o + g@5ܲؑtn j +˶3EVQ'sf]_ hۓe/^߫&b2ʧ7+3E`B`uPJ$aG+Ux {+%0w{QԊF5K4&ܾBap9t4nsFOF=F\jU6y@- MxA,*Ջ-q: [FZFFh;|S4-x?O E28yƳR <+:(!Vir|bӕ l"ڡ +NwjJ5޳hDsL]We{PnV#{e(S59]8ćE*M~G"]*E>@?0LkFf?($ D^O"k%FT77QI@Y#IWe` +bOטE +y!$*:nɅȁh^bq0&w"oBuCGB + +{\ZK NSI%w=llQZLmPQB"XKO ? փ`MM u0!7N>{nX%4,~.W'j$<\VjGoێiqgİ/d\w%:y9Uvb6m/WBbfSx3@2ZdA)OI$\ ~;ogUT_ds*qUy>EՂAk? +mRS#ﮝ+(V_K"Lo3 *ɥs%oqiR2zu>lʽF=| +#*?0jMMl63KT.6*gZV*~5Szs)ڇchǾYYܡCa;,peT2ӑm÷*d\.QR.gv#{5u03+n_3 ~<[ԛ uL44ob wx8'}$6  r*|Fjt%R1OkyyDw|0``F$Pj8U=Äpm},"?M'1%?UJ<i%C0(_M& _sWN@_]%uFpS +1|x("D5ee7<1P1`1u"q + WPu /^ *A7[+OPqT@AK+p눃uRFA]TOPq4^pzq%n"€í ߢ8Qr#C0C < 7ꨜqK4653!'D]TW'x ( +5/jG=X`aT-9v뀎rnoye~l/dY㉪ԓbtWMn.LQ\zczQCdž2QJZn(a[Yt|gk?qB۬JHb;sE$BP%>(E9I 'uRL^˩:6y^Wl5bLڔ;bCxOH(+-=5 F ?wRc|_,S%ܚ"q^= [j?v 1C\";/W[*-$伪qE$S!C6OH*mzC;Ӧ y;$:*8c7uXzc(\EBI[~S6SD/zK#slqJz;w3,VuIʥVzHx3SX>k>ڵˑSw)3ǥ0ney&iaqZP}?g!b [Da". o_w"A.5OOih-= ] -Ẫ1[jpāK^x }7skk۸(KMc " K +w}PQU'Qs.sV&L ?X;`6탾y4JV[_Q< V*7U"xy+'6D#/xsdxۚa^g?;>dQr4nǡÞra Otُr|% J/Q<&g*"Џd+Cs!r<TYiT @GGk֡PaQqO2cĵ%qa;n:(äӪCd:N)%Ur"g@X$D.~AHRd&4t2yB6'&@jґ[Ҧjk̭+/`p+o\;_K-7EGlM^:h~JWbbحW3D ep4;ItDhAMoޚ>O !PpI;?#4UIiV1MLӤ0E}~1wRNr1f-JoIgx^w7FO`BU~pѴu,ZxL**,~;ͭ}Cվ$;KfRgiSCƿ^JK$+`tj޺˴ڍZFxǹQ5rѠTfwHem}$H:,/6B~z +vHFF(!eܠ#覵d:|P/>LRoX4#ESdi)eFGZVEi(鼐R)-&}:LȊGSujKK4L}iIƆ}6Y='vL=%n*QyHRסu[Z[#eUQ[0^NG[uz:H*7);.1y'ڲ3,hL@. 7 +#Lr-V`fUTTzjh^'4XTcS8!5DP,Gu |;0,jzsΡ7x5XXjz)I` *who}2iq0r%7{i3kNT"| >cZ¼BVMA^E-R:0ISL +hHZ-SA2IVl|HB튒WNWqRy9u2B#U*ɍ-{yzaoxU;z@IQqv\+xn힮˰֙!QdRbW<euKc2]QI ,~r6[ٓHrԞؒ&>lŵ$OЀCmzO$qH8li8eI!cis-ߑ߃F+ +ۊu X{x@Y)ʰT:bNhU4-kHA(82n^" J=!*8 9kұ@Gz4;BkE;K n/p7<._o_oߦAo|k,Ws6O/d#>nn LBvO B|}o&kaAB#P5_=r0!CQg"Z35%@/iCs($k Ŀq.d$p[ ,=g29yIճg"X*LSEGϚ:uNl zА?Ud.Ha7ڣ&5],N9kzKA]{wU4? +X;zm:m4m%[uҐ*iÅ2 +j +[YzR6aJwod ̪#귶,a?JȊ00>4~5椨!0*.{D R(J0VX)8LNf34 :ZO27UQҶ)v׵>o콦oKzfEEq9tWv7fWi/$˺8p[j?VC̮0]zZNKLu1R~-K3fڅ;櫅 "Td Ctd݇N)ġ+pzdO9szO&༢I!V-z%a]EԴ7vS=& +\֜n0.֪mz"'YL_U^]Wd1}\ks& !;Srqqx%"9w,RN8ưSsjJb!eWxt$Qmżopt[5efD(5ma{A`醿RU8GnV{_G7_D主>{R +R{(&Yt@\?+Pqx9UK\Ux};,Kds%Sݐ`=|`ĻoC,E7+HES2q#>s<,kW[Lyo KZiw\GfFHPUWgv +ӽ6NW v~[J0l {y7ϟ?PMl q ,c.$FzrYW5O|h+T$Yh~q +q "Ezy@m{b lԇFX泓MIiՙXC97);xՀ^5[ ]ٰ.qHc"Q{솜гDѮ@j˳]ZREp<fA__^koߧ7X 0R={,PJkh7Msr%q6LuȺX.# n#WlXF@DIhWfbِ[PJdJ]-:lU %kxd4Iɮ +f(!%NKBsԸ +Ʊ| +``Jug֧ gܐU ]O0+nF%-wuPn;GЊ +8C`O.CCE-f4nUH[7~,=:%ÛPwfZGZƤBAWԢKxaNouRǣЎxܕle6μ ~%1R!`2a%lc{x +TWY[ uvL6|!bǢdt%;*.0`ӵja=}n"Xe$zc&. 萅gi|-ͪWB~KfXgFU(-GL3XXV%  ->Y7iP^`O1 O0u6WMHTͩR3^\J3-o2/=DnP*,DrMsEI]ɪSj4dnEӍKJ +vJ.܊B@]$Kp{LJ +`hFMŴSƴY$/1]JF3ťGZ&J~vu%`;u%OQ?܄n:pq%1p%W֝e_;RXؚWXb*2״?-+,/tX]:a (R0VѪ+]\J!3OfMa <㐕QUOUU:SU-c^ʼ|U!*GT-$*(*cl 啂lI(}E76Y1kByHiʑ[KkĞȅ9]%[+ٺb*! :ſZ-^ -i7xoO8p F" 7`5Є@I֥}jg;4姱bUǝRy tϏ>.H6LȤc??_AMOt{:z +̏_:^xfS2b0I,hi^\ir⢩jHȣy1:5UfrFUEo0F!}]g;1CQ^FD4Rދ98&Vx$KLD%J:8UEm4OSpI(GXjT*qSXYw- ԊR{]BunYp`Zdm4~[Uciʇ !CA{f`DBq#]ouvCREP!*㢢ѴH txOnlZX:@nE\c㵪=2=;2'6u^A--k uLl ZO5$~ozXҖkF̔ZU[؋QH;9l-N>{ 1ŘK$d(#,0JfF{/Q^s͑i .iHfM2[YR,,3T&)s])U]9֌3oM`\y2nūn7S0e'60#caB\U!ڨ {D_m;F,&+MwJ1MeҡL]#mș;8?@ LCLWH-%TiR_51ai(KM MvNx5MQY쥧+Pq/)m.n wW:vrhg@< 3'BW:4Ph61΀]*qSS;v3\NlU* +X/xb> hwqX >7xR;>v&=4 +3+* -$h+xTUnc)RǺ~RW,]ֳ,N PnRA&T!98̌VS\cƲD>RH|/#PuhLTf@ҶXXVmt[Ҩ)oWFm,ø5c]N;ƞKmI1j_|kr?sxRB)I?u+ɒQSj\@ w#QIo- )"@wCc.+!^X4N(xXQ$ɡsBj[=ե$Q?[Az%"'T :c>3<%~-Y(<g/=ڂdl> u3V.y秭enfz>،{f(q؜7>ll榵i}Lfk/$`:Y8;^6dsyt=9$.?-sYN׭Ҁ4gOvV} +1PW{̚SAiwe=+4@(ӄ7Y )DNxS;<5Qqt41Z}n +M֭ЀUy@`[i}M3 +VAOvk`Ma8|CJͫ3wZȱ{tU'"* u&Ec.hGRo*ѺHEJ xT@`<=FX! +p<\l\ +sC>@/55A;P?xP.4o?%[*j}z-Ɇ%J,C1zYn{SJ}w/ak|P'{5?, +Ѭix8s Z7.2\C*2uҠ}X*SXSuR#7;ϲeV% J*4oFGh܆AԶPLsҦͤH<-\Ҙ{M_f6kmQŎ7*ȇܣNS4KT`x4 -8sj*a7BF{cS"o̮T*ʻy =6R ~TW;?!@hl['n0+6Sѫ-jX2ESat˵VL3j"͈ɉ&Nh \E;P?/aPSPP>SuHRnjrXYg~IHHCj(=9_; /8{R>Q9)(KǴAh.͸&}w'gkc%%ݞfaAkҷ.U>j07Fw"'u IHR +߭k4u{{E3}|-+,cAO*o"c:L/Q4΢"Zz˄[`EdwUhP$|꛰9AM: +ucX8f-, h#l]EEG+:<0d%x?@#pij~ + 5>#MRw +5UG1<ox~t:Rqw< +<>G3&"~Ϛp`O܁`޸qHgP%|4?}CO42imn Џ##]L։K3ڜ3R>}ŒT V$E>LYd@m +e9j=>aN0,r*Ɂ5|©DhIfvHu: +5Uwr o}ga@ +s]X[FkG_©T,*oUg̉+jg~!ijc&4PN4;]ЂU]v.CaLBTM-n8Aɵqh3W}bkz5QRgŤW Lt܁hLTz?ʡ Ozw?աcNYsjrH7SJrpZTx,8?tXWe" ◰`،&c7&zv5%R +M2v8ۘ",^+FWa7/c֜<$k%pso}E.9],XQEp#+X៞)s5{uBzwzB{YiKxa˒)N:.i44ą)HfKF=,8ЭƗmqatnխrC!BMG Q[F;RdQ&]F +,C&WHޘ-6}Վ`'lsZ& Jp Q胃*UpHg@aF*ǍheC#Tz$ ijס ,bu؅m [Ȉ(&`B]Ւj#z(gO/kNΐ%9cD'ẅ́n)h>[(:⯨zɰpkUV9LςbqFwR)txQe灝3 Ry)zkF ryHw7>\O'( +HT~s^8_=Iݥ@֛ѮUE4i\E/F[^DB9hv2?2ѓ ?v`I͕ +NLL7~ + +0cٓ +B +* + +NW\B 52( :7ˉao&(fXoQ(l /w?cR{zO49omM,"hfpStRC0 cD˓ #,r94^¤k#xi2dY/K\[ gz?dz?o/R>OTM,K5qGB~PL0dmƲG[$=aLLS鮒cr#xo+d< +atʥt5h<ԚY]J&ʠ⃆7+H72Gs_Cu3^>lU׎, +yvdܴ0;ܥ +8_? ŕ:e8WRA)=c3Es hpk,seҔ0,Po 5&aDaA>$GC"_ K(?ޗB^(=pK+q==袮 穨#o|UyIVY6Zj׫f z5*/hSx&ϢϿ^|?Qk hAQ=,-K%YyQ&ErQP^'%`_}Hp_L)$"sExem%f^ BUFa[U@+]R$ 3黡U F R)xbהLV'f@TcPAB RH4T:=1Hɇ;P +۳3#=giV:#w,[l$3VT"up KM; SݒP-Q' qk{6rŅQ.[̠ 0/C6,v1,cR[ݎ2˅z|A]uCӸeŸ1$ih6`n4n֣S74ByzRd|$3؜l!L9dvuuuʒREZP)<=?^JyU`P7w%cG @UGo,'d'b]N45KLV^:5尩SIwŋu\&ėϜ'-*["Nr  +,\IŚa˘eJUX'ۙV\ + s<{]ѹV(gMK1wȠD(R"ъ=sĿ;*Mn2}9&;dVPkD[mhT W'm4Y:3Mi5%պXQJ{c5^ Ʒe5vp]0QR9&efMS:`n8Nc6>.va{d/Hє}Ό4ծ4PL>QmS̀TgĂ~vA1V.Xl}NL ' >&80Sѻ$&ce/-~d>/xQˏ I3Kc5`QX6)6K =fC‹E&95 s\W:ܑ9JFum$I؀;@{-0V=>MTZ41JUB0Ew|`cD[)OʹiӷX 2zE=GO\!e]vtJ +gQ#y]L~9;=#0vҼXaE5Y$?~sUWzcVrq3-u',a},-Ca-pPWq#Xk'|٫Uyn?kSyPEn-Uu63@8jp5 C}K*Xh>&z</Q:)ҥC *⛚Ͼک[_&#z`L7enD/6iqY3q&N>\)ݽ^rp'!5f 2yyIu3((B؁̡r&QyS-*(-gHͤhS5]{LfS]xT|glB :HiϜJ--yzD!Tr;o0s_PVQU;OA>)LC?2j+('@Sbm5I g[{cDiT#j.Q] USvuP5եͨ.^Nq[8,.BzE$b/WΘ $e3..@_&AmG>sS.Ŕ79@b&$帷 EIP'k]rdhv#5>5&y@ AG"l +uQ cHVikQ]5CuZ}g|UuZI 2acS|,_6 U4 _V[E0 x=Ʋhx |*7w6zŦRBi@N BJ`UR9.`=EJTq9pBACukPBWO1/2p@#_]x}l zA=!nt=B8B8r d ䷏* r^ X{^ֵyAXg^*[1.#I!b(8q r\_YǍ[[O=-:JN pr^xUuyJ*.daMyǜbySn/Z k iL(Ӭ{_}_b1VnRƿ '$3 )S? I';׳( aT_3Y+b\Fai-v)SCaDMnHP7l?4o(IPD7, 5Qk=(#\M=W' +TSM*0~en"gbqM}-l z۰}xd()0Ԗ%Ms-".4Ghz(Y 0/EgLS@"f]P|ET3P9!֡>rX Ra" J0A"TG^7F?|(t@lw."y2c\~\il~Ы=9m~0?47EA`46;(vC+ᳬt 8\=޶kTHa{/\E@X^uTč( +=0|К{z.ܧB +O "ly]YG;ڮ1-gwR8TEFj8t"L ;nы +T?rryRli[}dQ@\O&>L-@5J'[z3|,[J*{H!qA|ƨ N4FTv|D7uʙf@+zaxx7LH@ZX%:Yp@w؍Y޹뻏/Gw(58:s;fi Raټ r=O" c# ^QJ 2( [7 +˕qonxGb{Rn9/ ?v2wz'󎠗Ri[w6TY@=&2{(4^ˉ}gFvCSi)0vhƛO=!\42Zg^m$]׏:bVY51X"p<^tfb#f.|O|̒`ta.:mc9>S.W/Ee.)Ȥ,>K`8R]8TW +|W3@v{3hfgC`s9޹,sbcv<5vl^/PEF ;>˦=SzXOjWrꦽ|k[j'+WM +Y 5٣L5]V)ٯMWrҰ'w$!nw^ű28GX"CeHS`@l8rM ʒOK@rjh9 4îkSAk@lOD|\hsR#R#*m|ŽnТ N;2Ad.( bbP +"]VC>5Gi {feb6rIѰ*Qm^U]z>~P/yOt%ʏCIM,Qz!"E\7u(~D!gD3r `o29#_#{%|$H0/ iNDĬ巣=/썷pg8?Y H5ncD`!C.keU:.H#X +gOŧO7?g\њ|Q𧽭jRzF61P?ܚuS4\zfV%?MĵP3 zɰȫ󕸱 Jc >kzE +uq/z) +E?+TBJtI=#%رx\?rm29o}{ƕL;hw"}C{R%CtF5{Nܽn46p͛jK#Ն,//!Z8w˭Kal7|V8<) +,&+q~w CEyGـkOb!lпР|i],45V"Q^\{xϊP#=n֦wgLy'\#q )t* cd6 i1 9#2z#H;a0mX]UcGеNLXYI!Y˻-:g })`&Ҕ6蠖 +6禓T.%E/fOob4 MQU:e:fHf#+Z 1fP*DhW>*ѵʍFܻ 'fӎ0U jfL%;ճ?`Quo!uc~VxWY,Cp<}? dt|m L(!M"^D {*(`}Gu`hH]]2$ɇbUCvTvNnԌPsZ]4g7Y!mŵs\{@.4vJ#NGçYeaUX $' 4h'SD'yޢhY!$ߵP/6,D? Y +4(3lvrXAьD.?4 J3(Y5.J-)W545è7g13 ͢uP=M)H7~?ZK+]pAfk|3DJ뢋\J&6S + N4)7ReYC= +{65׳,{ITp;ǸN:=?ʡ{yF+znq*i2oUKK~/ab<[t]cK] ,.?p{T!g`|\)ק;>HtArĘ=tEK,g)<˸neOZ-QyE 3}Ч]^ iѺQ .2$:̉DgMry\_l2]Fq9FS Xy=:@Cf,I%l(XmzeN_Q^9,/ٷ&yܚ#JGxj^G?8s +;&yFғ:D(I + ~4'Di,g*By@o7ՌCR4{We2sTw*Ҙxh匒zv)8}c EUr[ŚXBv8h ˊm+jϬ'J: dG}Õ:A)vT2Y;1+KY,o +?Bjaq",zXf8%IlH:рwG5lTF +Qe&CJy63q_"3Wd!"zk;KLٳ(8@Tz@mUKTl +1ȌB Ҩ  +>^Oxb\+D忬SH Mh̢&M$E]/^[sVhK($v>h'eA2S։d~J}!E[#UCx@Fޝ!?٢gg=#JEM$ry)=l; a m^h|Wx"AC,3/\nû*^E2A$\]Wq1N@6An# {c7vk^S/1*d/u";XnԒ]9?TsQ0tܶc.͉Ycw?+#@WEH_- }EBm{u W|H1bh@n$]_fxZXhcYyJZw1/jK#!i(Q]0O>Az?$_JkM2Qc(!kc\_JT(,'D@-tYI=9L|8(Ƹ4htL,emQx*W-B߬dAZ& +p;K;RM!K9kb~ FBa gAHlDLA#hf'"{mԍځ g0fy(|B?LR% ; YGalOS%.!)Ԝ0͵Pݻe7fkE9xvY9x"a6}׭GedFej˚BXܞfEl7)'߱Vu vLdNRu5ڱߛT_;T=)͟#RNIO6; F" +?ƪ8p?/k;\?l#q뫿9f;mI )qeC3$-D˘Q8VEkG>hHL?gV[8v>p48eX=GzLAU.-cUXI,ɝ)G\A. K+aen3s50j=&M">%5]bRPPF.8u..Z볫[`aS\쉲+`P|(cBvi,IN$Oj4:_(uf{Q ~ոsfW;p&ݢW =1hH`_Ԉ#zZİv2j/9!F(}_4BRSvYt0ĥ--_K%ULFFP<%vR+l.}kd>_l2[gC^ֿIDF t"rXݠXʙaKQ`:Q4e hbQoNڦH6+s;FS6alU DAGG8uX́&ka/? +\ k7GV~xn0 ?f0\ FXpM$0+JFpƸu4 T; ^EaCYa W6mEXm$P#0fɸCo n]vvAv5A=SUAW7X3HZ]V]9+N%sVE 'r0g+j.p5Wh&p*؊udMԶ0g8s=F^ ʤ桳+g:#\ُ`w+kߏ\ ;D }}+I܊i-,s)9hn&o b^dʝ+Kߵ{ڟw= gn D>s|7"<(tY6RioGW'q3ÌiRբ5M_ise;rV( NO%^` eXkF.;LBo>|o?Ӣ_ÄnvUuV9rаc?4T_՗QsGF uR&̸Oʢ0clVYLΛGl -#tְit+KeSyq &hr4Ub&LbSY= !$31%Ȃe +K{㈟4c` ~SN ?ƘDE1 +|OC %M {Λ֒9dtA_TϻO2}kt[x'9QWסXBdG_Ll&<5@7{G̾(Z8nSt?}ZxB˴+vAP-Γ[#_XV:뤚qw$z݋ f{v4~// ?7K1bNl\0cB펉a% `̃^i/cpD#Md Iڀe{Rf;S4ZQ;$}FHInbd#w4Ct\Eu +ki؈ծ)"B?) LE]pG!ډ{q@ѝF;LTֲ^CFSbyNH5܃d Uն'UI=]'B/Q${ *糷Q ;0Ť]J(%}mp 7*EUvT M7QWR98l+?S_9Epv"PĬ_$ӉKkMO~P[zW Ut%ߤf_9v L96}k4 چspƴzYdPY^O"xWV)nZ:B+Lyd84gREdoU tYk\.;mz# +8w7ID/1(J >KZ8{pܨtiܘ[bƹ5ԆqʌXZҔI}kCd8uܛF1~Utb8a#Y| +GђDlv0W[ x<Z%icr<I_04ϘnIÈgXB a!AuPљauYM:`.vCa`VlQ-C1n<"gRQF1-Q*b-O G縺mo~'w^TcjYvt9Uqڌ{uӅg ?+ͺTsw()TȊg=%!/g]KH;q +ώv~n>FUkxwJ$.֑ϒM7V֨Ih%.䮞zbn!Hp-1/h64bݗ-qYg2^L⬐piԐS!a4N'gφX텧y[i#i]D'Jz[ak2~HqV|2d+m XZ*3~Z\ǣ^06%xw-ԪT9|Rd8ͱ4ƽE0F{zB |ÁLvsִeؖO1M4d+n,%M )/풛VjW61&{4ME2$wT}- YBFϷHZTs]iʯ%޿ŽщwĨ~+&ycq 6Xip\+ @u>+Jiȸa"OٓjGj +lʶT2!qx>O61≸C|룩:e +LRWDhYEM/+%[BN GRw=_s+E,* 8@2g%x Emu6P/dg yOퟁlź𞦺$]ŹQ/&̞Gyx6HbT;_yvFôLţ.:UeCcg]'&͒/Iz 삗V ΚɫH 2[\,gjJzs YHHYÞ6עr.Q%𨻖M3 e:\-ݗGFr]˟(p-#\̿ D))Ta@ MF"ԸIlZ[6!y7??NKĸV:2${ ǟ3d'8?#yKJ@:#F^ anH쥴"%YE0]&MaI>~fy/hjGl757;|qΰJtFSw -_|Ɣ>8^S Fa}& >xCֽiK/;SyWm5hq'lR\+U4jU۸WЍ%DmһzM}4v7G.+k0LE%ޭ:8tœv6J1`4qݺpq[C' q%nSnxȖ4̀SOv$W@tߧ $8gpWqdo1$2n4*i.yƯPCoVO]|ރг&HWJƵmOq`M;Ҡ}µ*ڍ(̈́PƠ+LoiIT0G -pG5`Y୕ gYӍNgTzHi<3?(3ݧf^5h2 Pw3,me#?|:(MHٞYX-x"sݜhd5 $[=ϰkm=yYnXl.H޽|~@d:dvOiA[+i>RA~fFNj&Y8;u 2$sZ6ɀz=趢Z%r[|^)O $B~: u|ڻfss`끶PǰwAJ͆ptT5$iajpˋ>I4ndj._)U/Y&A +f4}c՜[(q0,%2 D(7aa<;SW;1z͂\Lnb_Aw3[8}5 *'yba %cm ԠtZ)Z}L;S?:>{ISbr@7sxY$SiIcz(9[`_d8%lA5mUzӥliFC01\HҼވȂ9&VYZGy&:!Yn5FaLQ=Y3 +tZ +n UxFAYn.vpӪed1USOl1[BOdD`o##͕y>+K^qC5B:=.լg/mq%qPU P$\ 8RdBxTU뺰^}#ukWy9Cu[&6#pӔL_6+(0M0/kgWIers BĻ[/ 3TD$*۳{1]95|9V6"/[ևƱvK̷GqxW5CxHEu}ӯP~zG +rh;ew,ܳq:x5n27ҕq*%޸/NSU<_"g;sTr{xiDU{g=Aeg"Ey +0ug< =p1` ݽ\M m o Z3-SMDl|0XCn{'du2£4 y8vLs*1 +]j^X|+?Q^w/vZ_M\SU)Y ySF_럿shrT +WAr^edy>Ȟ/;0c3/:mEKjk+(dt\Gm +cp:TWkmG9(s.!Ͼ@Jea<2#Pwдl 3s8XŤ3/1e^?g]3jr|ȃXIt{f Zw``8cw/z()0Ϙ`(0 +O·2cD\TLC(҉N-S0F;`"zW']3tFw%v詪-ڧa[!wC +<A:r```$Yo3s=r]i`FJT2x{{iyh+uo|:P=hÂ֥OrÅ`uK p{zr0.["$O8z2VaW8^seae#\fT G[XXTvCzm{7fNvqBf8qV&ioYDO< ӥRКGjlm h书o}ĹFZ6I6/GXZÄb5 >}*}YΞ :4*s\b!*JA8ox + MdIrdD</Pe1t2zZH_~&{CbsHN1ثxϴ\?rΌDe{NT +$%eaDD;#$Bh,lAΒ֝qL~,ѣcUH0I"ʚ%7iRo^wvUӹAY\GA@gG_Ȗ9m#E9;SHEYA'_m?ݼ%+_*WR#]h vu0rc=h+0Q~U~з2S=0(V5HDO׺H#)gk@xtrCwu%] 㺠Skl(I6qgr<]ũmj*U;"qVI[Bq)f%aKIq]qٓzF2Ys١,  ]R'Jα[uj"S )Lļnai.D UJ + +50M5h7mϩZf&p?%Z>d!B&؜Aϧ4Ҋm'mhS' +nOn" ӵAbp5wnFZvh<6H *J=qhdNsO%dٝ[+jsر_ѡ.icIl' xK$)݅ 6Y+oZ.U#AR3"8zNa?l 2{{,}u6Q1N!ͯfDq8Qɓ<3a7ը[kK +}%{ԁI,O:l:ҋ6wi6.qaQ-\)AE%^+U\]d|%ź9 eN/(f_d<$-I)Rk k4E&պK2iOJEge$VTsi4cnLŷ&jq&+f+y."0YS߹up/Mc."Ȕ#i.Y[dTz|NuT9z׹܅I+`c^/}c\D%ֳKv[2jZ_- S~bN\4D㎰r?YlS4]O?Ǭ?mv. l\A6J/huW$dѠzr'-^W#;@nqo.` +#ٲ߬Ȑw(fG1NE#!oz7*̂1{ngO06Sk5ݟ^P(VBrT ޅf(w(2m6o=հE^пueR]\ҟQ(fŅ4 ={FULQko0ȿ> @$a;F؊8c}OҳũP0ʥ3o fO_/׮.|9Z~7Tbzˏk{gO#mcwSʁ9VN.(-}dRbui,ӭdx~r_anaswAјKc!{PgSExɭ~yN/;9.o kljR&Nx\%׿C71 mYm.FXϝsbGfyaF3WŽ\K.ZkA0(h =KrzX _nZ"w{=MW ػ$%[FfE]bn"WhLO)Gՙgn0c +qw(6zߡĭ_6fd^u_iIڟ/;z>ᯏ|㿟+E=ZNhPt5]<`"QeN|%t Xi)FP -}ROtBgvYOmρ&:rnXiyM3'uxzoX {UO^7UӳzsޞmYn8N69&}a'n΍Qs뎼 {a4cl +l0wi79WVg_3咫{Z + Z1XT]+\83ǿ&ʺTfb-E.(`<5Rh5 -#Gl C&$aM}G 5}{?`K_JGG +dXvcQjeoXwX( Qi;gKxJi.vԂT`j{Yu3e29$RP2#}/gY_h~&XN_㳷?5'j)<[݌N=<?L N?LyLU0LbL3PpYf . +`;%*̢2GphB|>.5ɠ3Xe +7gGkX +BC4Y&8 S6K_$")Ŵv1fss#_RkXx0֨]AkpuukٹIhchdyL,dYn> Z$yZ+PL"Lk.AfO2t,8S_A @&|MQ0;&94=O?f0l^|)@ Y!A՞$,p^[Dj#6i]]|f0&lflio ngLr3>ͮxkz L㛿Bx kPs(| 7^Qـ@X@jPf3eYzN݃!96YJg}+ ;,:B\Z NާR|Smbue'G9dg+7^s0t3x|g~ϢGxs"Bf'4呤`ީ1r?Odt4.b(_M{} :mܳ:4leL<{It }\;+[*}VrmڷkuNJQ=m +$r;r wlL!ex{9Hp!}98L*ܓmd€SIDǻzCn,J'Jۮ,,X$A3'`}@r6ftϥ\|GdIQ6ȿnrU1I\Uȵ{ JWMŐ7!(#ϚkM3,\TiZpSyB1W9A0;o^b`rldNn@qx=ҕkl OBͧXwcDSӆ1_ؔ&Wd6/1 +8ZHh;qpBoNσN)~ZT2|Yrlce}OkJ;PUA fDQ$xkk%F{؀L|JqNsx* 2*>< KԌwGd&Hy_ɭFtgE Dy)9F("㎣A9Pǎ}˼fv|RFAFn!0s}mVïs̔||hxU1n&Ċy:йOXJ'hYpdUѕ^D~6Es Ф=YQ ePnЏ\xGߕl#Bs. X =l .yq_A'   +uV'}`8HcXd| C,e-G+}܏0jO%ئط^j9|nD4tq}LX5 MYt1gM)g-but%NL^F5#ANO۴8se\ L}uA;rhe8~'* >~}voz붐߯PoeruJ$:U +DT]S._=Ż +B^da]qE73Հ,O?_rKɇ] l_Bl 9o~0Gw/$8EE%3sZ.]f@.h*n*v٧*݃>j3&U7(cZ}nS:}GiKYc5/.0~^QVd/{ 7W /[}ڌ]{ a]C.$"gC#ma ] X*>18ޔg;Y+]ǫz  ͿЄf!BG Bh2'XKΎD.~k:t7%+!?_f}s';+cҎBIt/Ϩj-':M^-UbJRUOTVr ;zF}`" +_vݎBk((d"T,Ƙ* +ӽtɍ=v/6#;wo[*ɘnʱ"TdK[Cdc RcRdn!  756v>p=yQ6X2:>JgJ-J^TKÁ*hĨ8ɇɅY8$⍕VK#~dUU xoSpc0kOZĪ +1ccH[k2 h>b^>.9rsa3SV9+ߙBi 6mSoؖ+]v5Q!3fu* 7(Y +,A+ vtn>Vg.f|P,*.G?kղX ^cxk9Y=\I4ۺX_xhM˓K=|گ#L|RBnͭA^]S>o6/:\CEL[O.(߰ #/7d.Wy-Ō3-C &=lwVVqa=aoӀ2pq;#WeT.r _. yXaK?܈2U.sv$괂|Ō#= +2d5smH+52c@(]rSRVz_QXPUQ: +TȭBYkk>ٯo4RFíN3u2!N=繞7C]BS%<Ä`O`'`^S86R-qsMV U;;l 4O4j*a 5M⃰#xtPfoi9z85/evfMbH)&~gU:Uov.g:x@@_83N&#Py0lAߘΔYVQ;"{_é;l7h #пgƒ;hf?eH#4=j?5"qwaF=^om~홗aI%zi7q}MI{l]p + v(Ȼs%dڊ%h;Cw+2 F*Ȱd6 +J;\+ U<ϗV~3I]GJIt:AU>^(_PU"Ѷ)Y( \-Oro`[p +;>d;g,Vttс -o|}F\*6l{|U bVp"Z٣@zt\GGQ:ܡ[z]{ ]`Wj}hO ^/u_E%Y<rL3*^y.,}&g9hC㿌y`,u\`gm2Hg܏t'ɻ̚8Irm)m7\9+)42(G!'z_bchO&ƖK +MǨ +׽LAIϑE=B@_e w;J 7hu^cTdYșHx`2&rİD4Q&dfa<w HT˰Mu Bce+I. z(KtdFnTCUujjA,s @Xll5&}ugV3<{RuPN꽈n - PQGٗG ln]#|ۻsqo*#K<{g] ; 6vǶ' rDL鲻5i^| %иjh_5Լ577JfOPz}kȁogkVsыsiDsnʉ0T!-$/8/k~0I i5v@$yYwOsf慔 l_toǹ..PzÀ:,OױOh.L̻2 ++bzF3{O10ì@qa=oUJ)p`8)؝`!r KrTMT\ixA3o-&Ԉ2EgmH+52c@(]rSRVz_QvS(~`ٛĬ|0<*h Niu[ A0'd^ }_CA3ihj{[g:d$B/zs=oĻ)@KSy Z5dOZq!ja;F~]s;ns*=Iqsq|3k^5: ++ ͚=SL ֫uD]wiupffMGa؂uo1 5)0#vDտHSwzwKoFNO.G:.x|AτwN~@Ghz2 \kD-%?IQN ch;RS/,rZ"3aF=nom~˰=I]qh&v{ N{l]p + v(Ȼs%dڊ%h; tVd:CTaX 8v Ss+W9Z1WM ] {i]ii8x~~}&鷓i]Gz%NFP~%DErzEwc" +@!u;'\UE%4\3_2Uwr}vfqLj9e8܉|n akJCrm)6j!BwPm"rp gsW >B1o`hd' =?q[|]k&&7hu3q$ʳ`2&51c/ (z]7R?#2'}RI@)ېIA# WTzE/UtNi :P OHct(%-,iB)V3Uo H)}؜Ή8ݰԗZv;0ݑKe`k(Y/hIi;mt.Fs,N^yEXt\{~jXƍ7NBPGPEkn% v5Q4=7'6n)|{sxc7҈knHq̳9 L +) ;N0u}Pq0 . sl;=](Vd̽t-*NsI>IJ`zYUz$80_=IRu CL1<~}zϛR;S. OF촞]'j<Ѡt2Zlu+'i(_TΠAU:e(ꏞ*I\2>Jf69RnE1敤\2*;Q&`FFJiQ$hew +FlɆFoT4{֝|q}b#&xʥf}o%\`Gc tN˰ MH]jd-;=o1s 6x'ڊEh#ӷ+8$#eX +v6 +/ UkEn ">!; _エ +n>VwQV߯^|$vk3 땘>/A~9}><}m2$ro2 rGghvޥI ͼ)W;NṪ{\;AVi3*SRMFsE GQJuC^Қ&g!Xn)_<ւ`y풼܁s>_{nQ!y4Bp.whp_23g.˯zJH}NֺD(6Kvdg"vF/^+P:=j>j)e<;^6/W뾹"uE+{\f;.^1n~ %"YjT$wb{/S_Ncߣ=v.ZJq:(Umd$|c;)Hno71t{'BO,m{c<,GA&]M9}Bu u]W8B{}DO l8kD6M)`ZW=5jifd^: -g>mA?ՒlKjowex 7 / +̬^%w`6 `:*!1%#4 +5N("H-NU[h84r_i!!gȲ_V:Ousnt_Zh!JW<xJ'AO GzI2;{{]ٗ>;_XD Gݦ>V:iloHwۚ'1šjDbcK ~}FBF_ц)u w5-4Qєe7| 75ޚ]GMl?ܦ&craK \|fѢJ#%.ԍpHQaoXZHҰS՗HCpy o:@N& +vU}G18Bl1SB=W^}nZB@C4#n*p&K0Q8c%M1j`u`~3Gm:*ibm 4l:#| t6;jEPVϭT&9LG6!/q^-%ٿp]RQXC*IRlh"^|*+\_sZ/ZCgMSTpuF}yꉭCQ0e"Vnln扺\/)NA0eFtFPV/K!^[ Cna =bo1~+@Jkxtb'(:i卵i@'PqT;bU*̒ WRݱP5keзVQ) + vs5Y I˳c9*)s8C=P(WAazTlC]@vlДXsnw߅jo~BpH<03we.)0,Ɍ&J^8>QFq-_$'NVE + +b\O=]vvuQQE,$~@)MCxJRS=v,k/Tƽ}Cc$O lMB8{JA=Ҏ2')2 X_GZGB{υ~D"z0RfJ#&;W.Cf~iY’34H3 #D7:-4mݚ~"( ^"VJ SgN4㥇"Q" xϥ/nTJz]4Z/*8B:Eg;G~:jAeIumRvLnYREV9Gg(I_5lJyA+o_?26Ĺ&G7wߜ1#Ȅy3E,R^dhJ^_N0 eDRx<`=h3:BOs 2FH5! \o&rsz +tfHLy-^ZJX*nXMJu˜T6%RJ>?5F˱T*Q\&HL$j&B'po!P7wB5/NHU*A'Kc3A$ n"C?Jn&ߜ7)O_ADk܏τ+ FnKlனA7,da;)OcjQjZ{[wU#=?xpa|R(8*q_e  +Ϗ.ҧ'"J = + 5)vWҹ*=>sYfTТJꅦ! zTQ8Ij!*y +gg{Zx?RӁBT'^jH~S( Bu 0! Ⴈ$n 'lFLֿt%hUexܬZX-r=WD"Cc .[;{{]ٗ>;_XD aK5d:_h&AmT[01Ja!ox]ՆVTcK }FBF֕HzA{G]@C6CEMi6 +BSwp˦m\Tv z6yg y5,ʜ&7kqHi![~kZH ;MqK-.& yN& +U}A18Rl1juc>}ŭT]ˡRF(R;M#Y QJm?tNmݲΥheQ/VPfb} YB+QK/\W;~-!$^NƩѨ*gJӔR(Ce1ihliΨ/O=5wHrfg^^جXtk]0OS6 \FVFDV +KJbcXZu;`.,89yTT~k䮭C HT}UO(ۧwzJ:U%xX?õ2'c[;+4-̉t&Nu<Ti24ensg(ʏe#hk} Q1*: {NG=VhJ9ٻnBjl/UlǩDwڀ^#? PP Rp)H2OcÇp֊-O:CKӇje. !&EYM_Y+x?swpk?|8lxc0yn#BuQE k&v7Tyc\#_sw ^ǿS!}A%c$gf`ۆ}^:t6 i{fHa/|;*JiG3 9<#-suP at8TiJYK%3^>3kA9t8c-&=[B!CqQ&<-P`y|x饧Z@K Iӕ1Bu"Kb EUw+0]?h AiF-dZRKP* 9y=4P>$9֩1 !kvey_B2#$ +}Uv< X+ |.)+^~} +'~;+D8V]$ $ @d +ERDUVU )P%#K1_m\i?H-zCNf [֑xo&ˉId'LT &`Xt"6/%L +.sRԫ@|sO H>#5F4MErEe$TDcO +Rc$p$QuCBReTOuf(+e3 ~<'>3E'm7(O>6.]8Dsl ?#"8H&䘒*'ڿ>s4`\bY_{!(xrܴcjUuYrC@[cl2g'ދaz7}دq?>#0`»1%/=D}`3URcAugbH8FڧV-mEYzkbW8*;@%Ug\bzp_26հgQ{XA4 +C')*A = B*R247,(hL?3NM3 MBQ=[%'XGQV8#^g~!RDMvԁpV}4L2Q}nLg[Z^WaTܹe ~My!3a zA/uheQ4єb#P>075ޚ#yt!T۸UFLG96Dž*!;8 ׊2qCf0$ A +y s=:_зi$CҘup*bP\ +\o6||aC4\Gs#PRj]ʃS^yk-j<rm{%t,^&EGLȒHe(5.)RdwQ_LAvAYCe1 B4'"2/pHM>KܠK7*U88:@H-$"lHQw~WB3Qڕ*ar3 `4)45݋kdU j=d Ȝ[|fqݢ..jHbܒy &l݊ªϓ,>$h0xķVYdw#;JUGРf-~E=?' A_-o c]_Qa w7T(7g\wͼ_^ +$>,+ gٵ9+2(Y3\mKDu4B~}҆ +vyOX#4I󄆹,(+UvmBL}/e7D*{dZ6YLU^8KhYi*Et3ҫ1yXG@@~{]:STJZdxwzg\*G4kQOg&5L3M :]Y"}w~ٍ߮,Ӕ͒yJB?SQ%N+K*Qte~ʻ:c 9v#E^tef,oWI_w\kxU}s/ +_C Ocy|y.gx;vT^$rR/?NVxw>ֶ]89ž=~!Jz:{OKو E9dn]}gƸTVc.;TI:PUErtQ1.13+1BhBgGݖـSȁsJ9즊Pcj!_jZQg鵉^ ,2oItg]Yg\bzЌ?Ѷ{]MgU\nCdMvHƌ$ڊPB#;)-YHBA`als,PI!h€akd_Cڐ9SR]$ڻZϧ&3UiNlE*NafW8pYZ'4H ϐ1nPZPt4hf8[U&Gف\ +YuȈ5w%;>κ].ə8L>^/&3hg _Z\ПQ\ >aS^A*SZ±?'37,1G]LpsFZҷ4cT//+v ՊIDIfQy`LߺKT>S( + W{ƂݪMp k*(27eB _.(P7) +w2U0MJ FtP +[l_EN,N5:%Fur ]k11SclE MΩ%S2litܯLOJtR4t#t'0:ZV/eJ[imj6E3xJ <[ް3/J%k+7cC׊q繵kD-w{L/+78I50ǵ=sMCŞq00 Q+P.㯏?6W?Qq%!4-G[CT1pˡƪup:cI9[W׳/75Iyߑw#@㍻~PRF9t-#Y _ŏݻV nj.X墪lhxվ#+7eW!H AʲL7pw|׿2WM +f$uCGVGn.9t^3w}ZKs͹DV+q4;G=74>YT媯ڧ_~C.0w&Mc;Ma0óᑷ8! O]_'nqf٥Un-\A/k_~*h5d?iIsdۘӦOjސXIֱZ5ysrDx7h6}*:THgKMdYEk; 0hHkizy cQ_6 &rOݹE[.KK;x$Bxab-!ayB7J>%avFZK9(-@g#\o"!j3{`L[(nV@8`"%_B6zqIe7-aFu՘'8֨$9wy&yI}S#yTg-PCcr54DBh#nVId8(9W4Qve +uQijvDێBj Hzg0+%Z":"6MnY3wÖ[З`kżlc @ahJC7v6uto*:=c+!_;w;j+CR6W+LJ=5qmvRETc;;yQD| +S"ő.Q\Ip[%sYf Ɇ]ܠ oDH8!e6(PdVY,`0c2deC;7͒4I7d\Dw֓l&g0 IuKK3GM=13~XoJIAy-<PeLo|\QlV56^.Ρ5pXgYQTA7K*TH RJRlaG*xCZzL,lI-+&c5seSCdEўr@|S;i)KşAlԤO,EؐUF"ݪ,ʋ?L=D)b,oez|`yb݆~-Λk4',H)ְzfAFsƇ3lDG^;&N K Ϗ?/Ub,Z76f$ +Q7ĜT":3r+Jw{ȍkHZTk-;E׸Z ԬCU@5xG-SH2~@Ad6eh + + y6(8"mƅd:z"*yTt`'-(IsY5o cd ++R 1[WpZ,8Ld)-B~B1ͥ;fb8T`\kH/Oݲ)Kj=BAŗxoouBϏj)~4Q-1~HƷ5i/iD[ʥQH6T)w&>Z59y6kώ99a.+֕*>I&zw?$ߟ|M_*rf<]I]pkl+ +-8Wqg )s +h^.g RlYi' +6g膞!uT[9zLRAEuQjnpZ֓wمX5=62k6m:uݚV͜V .3D{몃O ;Rmn#:5FeU.""&MTMlyrWĻ<.F/w.ͥۡ*R^h6.::iE{ysxܙR\e N/&U,dp* :Գ +ky,E[WdG* SHgs!AFKp0{S~Aj >?-^3%)[͸YĎ0C-? <KMNdzjq>E9`p\=MW厤B3ٕ=UM46C`]Tt-El.0Oa H.FUXRR@Q_3o(體J*3UrWh~~`|~(J&Tчܫ+Q=t< WEHT ]fzOV ވUzr79QqA +|n}#Fy#DFuoE;#q~޺yHU uQ-i TʶG|-Z >f->IS+u9㭔]:fRȌJ/$冘7s@jUXe1YǾ2쏳wAXŸ.gQDF _&EUj;^bݴwzv|r$Ff(;&@ )bŢJU"O(x` +$ǮV!FZPiR9R@rzX;0&uicz%HmZw|sVg*GcI{|1tI=ӷ(ݻ( Z%>ִmi2eR}u@ф2OJK&Ŭn{NmA1X$]|Aߐ[uzBrUZ {DӕrayTwSf=7WC~)8ޜ9&hn=&^q +`M4*Ө\gmqĊ7$oY'ˉZ)4CYR99@ًMzuu} +eN/YyD +Y2xQF![ o~Ÿ0JwD$Uӥj0L_̌; sF2 -eoj6eΒ wbIަ&eQR:LtkAuIK0ٚm8$cfӊЪ-3% +ₔu\f=i`gBbO []V*M'R3Q6:qYER9[sw#8WۚJu,,rͽvI=xVKؑxoOYxpVQ>in&FZ]Ř6TC Vpí6TM*1@LrfW [DrڋцKPâĹ_Ë=BЄnypP*֨*/;_$˴Jm.+XȽ;×,Rr"۴ JN|{D~=Ar:6_5<-v3*BxٲGLSq`1OmYUPSOal:s~EjK, +3̂Tz1!]MVKRE,:tRTndYPQiq[8_ c9 +f4`:谞`8̘޶Y0bU2yA%Xø]45$ 8pP<7]Ai*-*>830A^@KE}g W6af + 5}'`K`g ǜk}QBahI>YjJ>tQE^- ٌF iJ)lD:BbM |,ItFwDAͼΪp/]Jmr1d7 JƬ?6,abq9r/en)~C6s:X},$;encbO9 ,@ZԜBfWxPwVLg2{t +4:$AܱJY;ё$MTǫtuyͯ9Om% < c"*l/aDIw $[%@Ң-'&t*YUw_YG#h?>낛=O<@گo_.9q)At8U +gQK(j LP t_`Q̝$iϽ&m/7o7wr~m\~BwwaχI/O"P"@\x[.%b2^ gi10zs.+fYkeiۺIoTxvWd`s޻$#MexPkJp~8D_6 vr=ieʛjONe=i;uj?1ӈ;(TBiyi?^SRуt|;^/I9)pFA'!.?(K=+lmH\;,kml?m[wXs'%)WQzez\ݭ98ˁM>WgaˇoT]>zt;CoF~_@W^^iզ==Tݕg+3౸{O嵝?#*$U,n lG!̤$U$59LOHjqԢ8նP厡y7oP'&Qz{PXu隓}Вu9;)jCg֤9E/I0Ύ}>tQC>:qǫ66N/yALӋS("ZFR邎R!N \i֜8ڹfYkeiۺYvi`Y8$"D)SK5"^?ûWRMN/xR5ĻT5~'{me\ܽ9kq - `E^L!~%\dM3n=eӶ7Ew4Z[>di2"t$a9|lnwI Lƚ\H]JnGN92 phݝ?lwk ӶwO`75r"aĬ2T8_糈VAQ~Ǝ} Ѓm|Ba tz39$*ɉY4Pu c/C5'fl9(%R<];HJ!Al{w-%eeQ4<yv +i߱\!KIMy\ ~[T<&3^:{v1N态d fK?X{:poj ?GT9XJ~}R`Z8#;Oڳc]Iv3P HaKseT P<%{_c6g9e184escEЃpSHfЄs?E~w:1͍m26sgjǫ3@{n&葦Q)ȃ<|yhK+ 7nX4wV=`)FRec8-m9>bpj´E!G9v; x{ |6=CN.:I +209[eV36H}3ZG3cI 4|GiQ U`巽W I[f/!pɏ$a˗% ofڴDm[:RPYB|,KdErUGA/Oh7fߌ6 %^r+CRˢ.U͊K 4/)E~5Z]GF C% ++-:F}-w#y\7VUV|wA7sPW %E)k:bgYX"^d]^__H'TI`d ض(uѵ:h2RըY紈蚚Y(.)ctm'֘E|uD31m,j +)IIjeG[[#Rn@(P gMZ 7ʒ-Tva1%ucIZp(+]hq/:- +ji"O%քit}WۓX|.cA*IMu9!U(BD#!P5 +DZW|~~ a$?-f O7Z˃tK Ȼf8G/RV>FAotÂ`gҪeBQ ⪜Fi]v~e6 ±$:*n4íyj*a {D5nXHX{ĊT,^?yQ?aL7jtmy! dL5ޡduþԬ!JPELe ~+Qo$k dאַbX:H9hRcYT=.E}O˞sTlUըuQ=O7=Or~lZ=OmW!$d^ :X=ngK|PsHzWs~uR47ޫy|lj'5/AӤiﺱj'5IռQi:yP<9 jNӤ4 +xwLKI;kq85 NAe:i:hz4MAײvMOYyQ,eIIy4I%%L^iWyV2zU]~dzu=κ>ʺ (/롍>:ϲYG˨2z2z2]/̺^dſ);.5h<ߩ orhk~( C6^r5A&nfuW׆ܭ+חo/ʓnwr ח?o7۽H1cm%_/ .[A)2dz Ksl*̖R w[~s{o)`گWR'ͷ +c:enf6nk=9iJ!!lCt!*בȁ~E !w뮩O827"쪮HXTɪzyIF"$btIװ>{vvkc|rLDc聜!Q%2DWL>"*qrH0#tH.yx`MPd>#<'>QoЁVѿ- ׍+_:Թ^ rif|GZ.7'v8OUOZ!>}Aq^WJAQGi//TljNv.r;Nzq'^S~7_ׂ[CMo|/U((XX74d(-\Ǜ#l6Gҵiepcc ΞdB`Jm'S}(ZYx#nۊjR:XnGM ,:ҘH{?F +T9ٞ9R .Y8d>qZ{둪IB}$nO`=@8l{ qp`dDlW+Y?M,)7rD:Qm+eP̧޸i<0Jp  >62!k _ϔI5X<ޗ7&06qgNxv)t&Sjnrp>rpVn9|{ou&̕|pxnw/߳Us:eR$t#rRbZtl& G \AUɱǰGP( UE-;Ȫ W9;[WnsuZ9Z "q6|t!XI!h֓o(;x>*:4^йŢ@쪮ZҲHDQA`=-/̪ce_Y: SF_Go"Fr{ +& x5ؤ617꾡¯ ?(a#Kƶ,SUq7vC5s^rIm1y]Bu#^d#d#!@ g1!c3`SgӚi.EElZh ;2S?uqֱˆTP,xWQHZ= :n:|#=H:9 s]c"q-i[*фR芸SCbpb[=&O/#wsOvbb!JPeΐE?zDȸM%r"aC"+$?,޼FANL?` +Ѷq9*5pi_dcePOn\gC~('b@7Pmp' Y?PGc$ƭ~;i0c-&51Kf x d"+Xg YCTCr,G܀WE a2'xk) d-I3TtQ}Kzж6kI\R2KI,%z[1 ʋIՏ,&تż$6^Ln`-&"oƍvd׎0Qd./:H-`z:d2E7W8*Zc@G0_=I}VGlqH +qiWn\,^N$[:96 /( v 5شQC*fs TӰxҡGgQթa]; DF=unJ,>[LH-yϪǟnɘ>_pJ PӧZZbc+LtdU$z|ʹ`wzt + B˙{`*غpoÇ*zǿɥ5`ܑGĆCc͓_}*,d֣ bRbD'5nkM1PN}׌͕{h #ʠE +t kF /3 +s|-T77D$%`l][$8'ӯ;;S$B!7Rc:1ve5߽ Rs Gd'/v'$ǟlAտ¿hiBvOcxzD_脊wɨ9<;:2G܉վ`pZ ?S`3jQ/;b['rZ;aP<&M2̖gsC?~#tE~k\#9&#ٲWecGܥL=g^~l:"C "_B+%ˑlڷ̀$,EͳosW~ y0R]1'GÙP3 4:w,ߋ }\~B,xs,7teVSd|ڏDYC#D{y?AN'"ۚUhQ *f82GΠ{.ZCEd1/;k~%s]F>uN>l<]8PW':tU梥(Wvݢߚ}[6DM11VZ%6mf#|YByk3@YhIk3ưo'6 e$nqHdzU$^TԆqV o5++7K!+ ?~RoU¼%LV $rr{v̵b2fH81;zns:ҔbM.~Xញ}E].6?TW!&@eń&]/r_HƑ^V Wh?ƂMu|5zx0 zGc*HRBEIn2Ǵ:eW!W:ԩ8ݤ&8Mp + \ 6s]!TJbbo>QG&Vjm[3BuE( 6h/Z[cܑ=|wLsEhq +Ox0RWE.ϩhjC2l {x2MT#|"Ѫ|*6 vya얨&䚋7>5Ms*?E=s#\%P%#^-ژ 1Lf`jq`ULU]:ӈ`ı\RzaEQͽbāW0[q=䊉?ēp}Q(*1* [d=mWb?|>P D/%k2n|Ak/Jarej];_@1N?y@ &B6.*ʯ,n>LS 1? +r%/qSz6_q3*C\0zz;/E5H'܂m۠a TzBzl,8-6yu=c%F.f,/YUm` AfNՒ\Iu +_`*R?J:Ɯ#&zvT xx ƄXJn0h +axqxx^Ceœ&:,fe CV {J޽ +[pļVQL <& %&Zl#Hg{k)#"TL_;;l7F">yGݾyG_d%=vž'ɇ0ihʂ`*ĻRcǖnT-A;>a۳<݇а3tBIv4Ẁ`=Ֆ4(1 +pwK!W!ƍiy83`4jE^xD?Ueׯ!ą09{+ ;/=gK636Xk5lqC]܊a7y8%|QE[_G mg,槅uji6;NHH GNo?j ٱDnɤ`ĝFBA/6};pS &!Zsb*^'PIg6^QxDqUƵ#bH s*Gxdxʫ4˜ ǁm ^›ߪ"v׫zeZ:'yF|A RPMx1؇)v.yDBk!Z7B WFdG"mG#㶌 +Y祋5F$/f(' `~OZ[%U243Y26u)Vv@p3n\5#o"2aRA֥a,j %sg^))4.r΅"=)ȮH!:,_ɻmC i=]= 15^BeJe`l썭im[,M֝0=Y؆@gj$!Lz1rXD( 'P&DCm=ߥWld;.هqG옟R =cAѽ{K<3~SS|V+V|؍aKjiǩSvJM];̸V&: jG)|u 'j<=cN_x_^lA+K-/0UbTBVo6]&,C[e <)%+T3?/ 劮Dٰi3_15*9O9&TZi̥G5\]pT%>u(D{II)4JES:Qt+2EvvP{(xV1xhn埗 +5Rx*tBL_dȇ?"7q8$ס4IJߜMxSRx?8f?eזzEeb;{1#}JZy% Z8ݐs>9AaѺ0wR7iih$UR3M/A`ߏ,FJFvݯﯔG?v+)??)y æQ\UO!_g1Dh݄Jr6TD$F'eSDŽ84 ʥK},דF{Ñ hM}!5V47ç+wM/ededH5Ԡ.YVd:etW$S4}ٮrS!Q>ika |Y3( KӇ +3R~GZJ[^玘a G~:W-f>;oHƋ[(0;ek%Xb,5Ӯg`-ߏ @vB ; sƝLK&K{R3/,;hgɎjOATNIr\{r?V +#^8*-wxrQiQ=oiDǑ׏1Kn +QnH)Rqfq޲P#7DKbG]Ī}=(̝Ft3qKk^?&7!D) _ec (rÑ6rsfRU4;.A,Me ]bEO/.+-9P;Z%ô6(zQop0(kM.; =1N8T8vN HpMfa#g~x3Ȗ 3TaFYӶ*?lMaɦ9g?p;?b>]g|Syř 1KtcmwQ[Th\o%Ɋ!JwVq}ǁhqxъߙs܉l:avt@=}l:6arl:0xݮc=z +LG_i̡rb= P=>>{ȞXf!"KBVquܙ7i8s2k6qS|(v7 j,̄>QjV:RIy +}*UA@ +'!KIZ%ߚ- &'̰^//5QAB5)/X& Rn;#pn9_^܍1fZ}\E#ߨj*U+7ӘDr$ڏg38nF+Za Ud\e<9B3WS mUʲSGyƻ{<@ƶ@Px7 #Zs;ҟ q7xNHco<p8+ݓbXO+ +F:@.t*}_Q_d̠㎏CdKgm*Z3  )T2_T_h!H;YH b29Ag4/+LbЏZL8^-cUb $@}KU8hFM"8MBZUk(:GRB%?$❿,`aqe~'=?xfQ{znܰ67H0m$]lzK9C.q}pUӪnꎣHbW~8{ +O\՝E9<嘚)uf@}ȱv)&EMD0(6MdDp2_+;M83- +OA=J-I*$/po1_eRr g#=}Ǯg(cY+rޕaA#U@'%)Nx +iݵ)L]@ #Pu!U0=V̺|SR۱GD@XkV"Enn`!6ִjݶXDbƌ(5I![)JH9/J.T8sXVlpTE~?_[Ot7KX@"8k7Hd}LE|2 (-2ɍHT'.7qU7~a.Na1:U!Mvv#,_&ê +Eg&\(=ndwI23i?Cop䳎rlNq,㭌F,[V$ƃBqRqz\qCx HBZc҂^tC}xlYDkDJqoZBKȍ8J|wS8~p*Ūp2 -=AQ{%}^~Q), Gh1H(N>+"Ga ]9{\P"vQ=/^ +#ՃGo JeG͵=I["rEeî.clO\ߪRHKfKPl*?Ą,؄ެɯpTXf^QaEQx%~% 6"Ib_.U=-tC~]Uy GnWD1Y&!Lb;m_xsh0@R٨"-6`7D ]]&qHdcd iQuE~&Y@bh? ˯\Eb6Q8wssU8SnW3|7AToZZ<0?xEߜ0Y? 5Z$Ui%9FXӳZɺvL ;P N.hpk#ly&4I;~2$<^](e+ϊ_yl`߳t+PDSaz|[qW[\sZkpSs$+F FʞUG>5'z5'4>5'sܳ{kN 6j7Ԝ`o\q\wԜnJdǓSsBRSn <%gPQrv[4|J3*m;> گ~X\V +f9d:ނ[~m?~QS3]D=pK s2}Eyk^K%27p+l<Wv@G;;ϖ;ԿjEվd y{ptM(7P哫i?BpaRʌ5wb;hr==n s#tf鯏~O@6H ןD;Ir74chC5XeqZdpP̣J,}LUN9kh"b':ݳ .̐1໴TxnpEMOh {PbIeS'eo0#s`9(:7 v`P8Sl(BI}(]E_~?ްIo̻tۊnvz*t<< +3/93M ɀjSgV]JunʊƇ5v~9*fgOR5)u<(ݱdy RiX]6Όkg Wh:1i-Dm[T*qqu_P…_8eΖ bx\oqqg/TJzܵ(6bTbl*Zf_r;bnkzg*N><' wt!܈&_2תH.h-U?t1%SS](5&mR]cDB~3:x%Œ<[\GDx*S[f%C{6Q8Si36ZvxF]4w~< W /B)6s߄YΈqn)Ʀ]!S ZOi*~dmknFJrtgm ~wʪb!kk :OP{R4/;zoMz<_mj8>릑?ʩc4xM5+^aYnZRwOs w (NϺ80{4{mN%TFEoSN׶ϏcZCluԻDrpY>C0Ho+[,"|r5ؐ~( e7wٶ#PunxQf'+{9O,Iq'0r 8G]N>|R[דsu :-ֈ#g] thã"V:KlaR5'WzPZ"HېzF4^bhG 2zfF8gs=](i췑; +>{~]G% >gqwt?'Q7:K w'AbaR6Cdl;BoԾ`)Wv={unF؝| +Y8g[W6sh2{0G4.[NUP_Е¤9v7PAf)Gs/R$V>>=<\گںjl9?`r\'aufzs1*bawvᢳ`ûhme ];/ Xg@o3GX`T0%SUc(i-F[RpY>WoۿpTRԋ;dc#dsmg=*^:?:S'N<'e&鞆-T}Z!;!tLQf=,ڶ:rare(`Նz +D@-:#Ӂ~?_ PPK+_~~H$Fza} y䳐EVU}R +"[b1 *xu^},ms9kT)䱳ڿn\딺; pYq.FT#Rro8?k1&-X~t:hQI,1aKBKTYGcT :#"7KHRΨݐ?e ԏ n9d/T :f$NUlͬ[m7O@$}&&Yx'M>Ոxx)" +C OHa^U_/dG_rS} #D'D!~WIA5AΤo*O 'Jxqo2*MT%ޏw[$pJ}+ێ%/bYߔ _ ]_1RSfA]sbh8[ +C!¡pLv|M*Tfb;%_TGw4NåeVR[~`ˍ1ic%2uw1$M&WeM NrUOU'Hx hFdRGvm,Z)R߆O&}vp`E9m|[/Y *7}n:*77opH+#MB_dhybtKZN&6<6yڽ=@o7}М!ʩcjrE0"Z"y†ჩ!\gkj;(G}!>&R*Ojq0ԉ adZ+⾆֋W){hZ#ETƳzF64On.TR$3ڴŹV zE} 3F7G՞&.EYDa\;_(dT%⦊ěqa ZJL#и [y9+Z!Pqjͯ_\=o}0Y{휕|h!9hgXPcqޖ;߸pݎSV"6|/t:G̒i𫆌(`9<< +zR#ֱ٫ 4xoq0+ ?}]0ӘIb6np8JZ<< D:+4ƅYϏ/WaCgF;ww%rvaUct324KQoHAWg\in;d8^o xܻ%^]qUwXTު{CeRdz͔^БAg47ڔ w==VsL?Vg59),K^gvҪ>1V./p)GVsu{Tmuuw/3wZeTkA2/) p~y~uD~ԶuȋJ|&˒(/|Rڨ!XmjPQU*)xnYekNq +4y$\딹ފLwl +7b5t7px@PbMEh=cY+ThPzBG3H @qŏ#ڪGX1, n9b7Éb,*W+f ߑ_ѧ +q߲3Ov$Y@t_ 8Cg(VU ?0"6i$>p2jlqi?E )qDP#AUI\gH:RLW"_H]HeTw~R䚗T鲆2$ɝyJ;{18\,Ov;J2_ާ̌K[c Y nd `wuJE s@m&WX4l;m̲u +lXVrWf[7G{w;}7nI׃6|32Fv{XpǤԴqVv=sӪ[)J?aҡYꀈ * SdΑ_ΓHAh+?rle| ]jrAm?zgV-F0lиUWqX#WWy 6-sP}C}]%X\^rq/UuY'Yx`ʣլ_}{0qu?O"Mqif+[%\ҕN%}^c8 '˜#=7m'?8'^U{'(H{һW(]>7§%s1_9lSPkfFjqɉIKd'z4tDݓvਕFoGbc L.E/k3QHYdzvn7ve#U=-쟏;<}:J /GwjNݻk¤6[UQ᛫շژ>vǾ;6HK @-YKur\f|FMDjE}C{`W_x#ѽ/N65v2v (0[EUsItsAJ䪧V8QBe S@"t N] ڽUQ AkHr/`(5D[4Qb/HZ~aPeO'AbaN6HCԑA l<:?"N߽v쟟a+W G|]*g|fU=/%5S[ +Q'U='^?VzP/{xwmc:-sM`|0(晜>#u)5`f:&|).u7h )b Ѽc|rZZ*M򦧝B +Tv {0d߶z aSEtS?/[<5/;MWσZh : [YvݺAdmQQgαǺ4ϹohbnuI }$UOJ/)zD +۴:ߵ-f ljy <[x%&veWyR%lθ׬i'RZ%eFM\o5፩{?A&m({13sPPi؛÷靰gSӝ(%~cAF'㦋qLkKp 2i^Y[ ,ƽ!)7ڊwi ;R. QcOE{_t3yן5U^W[ +*I>PJ6hsTs9/='q2$l+kֿ~(ME<x<߹peմQlwo{Ewwƪaq,y5JiF:b.X~2OQM' pu{Fzܠu1j6ֲ7VweFf0vF(=e%7ǤXbM{j26.fa"\W8{#% sz.)GVkk~Һ|v]et,5ߋPV2}֕zT_"`GtxiY1:__)a|Y}APZ CxIpW/XϤ. +s"Ggp;eBߢIrPCiq?!=ڻ^sgm_-bFL.zuUЬm],KwJ;Aemʽ[%5˄ Сs~kص`ԁn!W-*ŒN~JŘ5vqV6_hj׏qt˩~֋>h>[QFm_0B(rB T&CnJue|`dKٜc; Oz=iQqB0najͻnp5aFW~tX&Oe^bWmE]b5U8.R_2qQ +:ϑNo77t(ӗմA?Hz[֩:5["9e#>1f,pq+MNDzȵ"v`IefB2AeIֳӪzaHsI83 ہA"L!+BU2Ttw-Rfo2xyzVD[Zlb)M⾅]{PsV77Ζ9qG-ǵry"oy'rmHW 'F@"Ӻ坻\4ٷw(F|7L +iwc:C{Ɍ^'̯_9Pi_Srɱ+7V  +6z8$}jJG^2Yu|=ArN&t=0jB߭ g>1UuC Lķ#%%>ny ~Rohik^ء'u1 3 tPd.wJE`2۱\ЈANIK5;6ոUlA,A"X>'j*FS&V\jO/' + rWFTcn5lK j:T+C9`$lxv( +Л*>ȧ)qv6Ь"22IJ PI_-s.Fǭy!םV`WjRV\.4Yl،tcZ/fٰ荦u JZ? `* +[4UHIIK-ε :ީϠ|c, wI/fȱB;SgQ$utUx%̿>؞"x/;Hl2qpH~6ֱ vyt(ZJ@gYwFAd?s#9wؘ[f;m7"Ie +1I_*/my!:i\L67ke@keW=;bgغ>sUYg⣍"yf r@zY-_3Fkpw\=߭-_}]S1WF6BU eȔwТj_}$ZIә5*Pc|vJg%JtB ʺKT$| ?nsnA,1c x ++;<ȱficޣ+s+IE " MAUd) +5=e<ȴn/NtiPdZUg_62>6SI(3|.Mʣǃ8m~9\'[)3>8#,ڋP+);#q㨧N#fkZN: ݴ-;Ĥn|[$eXfLn*/.O v__1qFrbK,ܓ;mCCyCM{ &i[_A.[Y&?u0+w;{$3M]rk3"Ihʥu^FFdDsUN[OpִY:@2V6Π9&g\Π17 jluQ&S񚑇R>jԤʅV*ۖy[E*_?{}B01ծ?f}?d! __S<#~v,A2\aY&UұNep9,M6("8~iHl)gyVS2^R.z<Ѕ˒X1)4~T׃CvRD _8TD(1~3JD赑7f)#"Sag#n|6*z樮ilLxJ(/\T%bhpg嗁bML!B +ؽs>t2>uvld*uJ=2nlbxFtxh\Wxi<~7N-}#Zk;π8ʧ֓,A~uTFNSDhQI +.1곟}%Ý9c*-ٜXѼC B +ME1"NKu(cu ,=껶H0P~+tYֵ2(LrtМ\YS#X+lb +S,׼w}3ϺX$t& +dNXnMܖ7S#9g7d{{C$<|>vmZ:τ9x3R,>7#AuD7&{(pb;[KLl'C7KӭFНSLf|hXs'd'j2v<^ʓy)]2PObd=+i{!'A!t=7$34U'W"_~}*O;}ndՆ}r +i^m~xLh=OoM=O\xe1/2< z:u^cA?ߨډ2-7 k0( Ao 8V^ ֜юz|u$H!#  +:tDFj& ^IMdwqvhzPBSyq (O/F^ż~ __1 p;+\p|Ƅt=N# +v9  JbbJ?0ķE(eyæbKfI!۩9a[)15'Czpj$ـU"pU^B7PowL I9nP)y%-!hYT-T缱vZb[>_˔W[WJ_JHc8:j2Qi +~˶U_飞JqQO;*/V-`M09LQOcv::8MkTZ4na#Q'SwjC*pg9Pn>C>tdW՝IJUəo.Tu9߸j"`-j[9V%hh4'~V_c~YJYɼ"?{y{;࣓\o48f]`)ݻ IFŲkǜ^KFxPUB7'ܥJ +lOt\GR`qoow(1dB1[vu&FTJ&tks8W\S\Ψm'jT +dMaaDbjk*Ej wz4:|5Vet^mEyc+:#}MM-+oJ93cY +ֱIp^b`XLqyc/9r k03hLMJtDtQh +4rCڷ]8]l>zw+EavPZMtY( <3ymD.zڋJtm԰ ]Hдh_fz0TbPL𨣐>T*GꮥUUjAW* /N&55=؈ӴG}btn.GyzP/iƢ]QRj)D/&z%/fv<&ɪ.VJ̙-AV]tI f1 ja CQ_ &Eƫ_c&3WOby2&oz8^/gЦ@U}k}1߮_G5 ++uYA݃o!qd>DSN1:j2}>_O(r>[.MC\JsWs_ +{hOj*<(v_2 4TBY|R=2%(]j(zܟ?Ex(}f/?i6n쟜t3KY8zwjX1%gݿfhь[apk<@+nze=cVVQ݊&D8!6 :#l;:CtJgN'k36(lvN۝]rC&Z ~%Goh? "I+,UE \2#Ҥue6Ȇ7H!DM*KfS8p m)wQ5|*`QXYo'hq*.Bj.t;ܤԊL5=4K5]Vl&gjv;Ji \A&Ծc[.S]ob !O$oaÌ;s "x 3z fS,wkD΅J0 +JΧjViߖEhpZ -k"b4QOz?]Hs.g.%]qFn(V,*?t>8Dv'Pki>XeZ-R+hyFΘ3Șk"fG t%! 89ej7Ip."єMr!4LAsvZF. Ɋ.擝]_6w!plvt//VJV.fqY\D5ċ1+1b +H9\1aiW13EA|-h Z]=#xJr1I@8nӲ#dg.4XLvL>ʧ,#(x/1rEA4Y&+Ue15jMP<(s&P(`qɼiwT)Wn%WAj6K)E8>B-nW52 vU504IŠV :%Ԭ@By+xYtH).!\(!C!RMf$dp(@3<&*;ۥa4$e +>*;i¯rh(ĕ+ MO^wZ|"9$Erjm}EҐ"Yk!Uk2 'L{c@/lƀSYڨ7ShwUu>V1Q,-CZAbWU/,!OQ=2P*mWqkT 3hq4`)TeFtCv6P̌V-/2r%:AzyPtc24d)Pq=ZuO4xZS\' T+ȥd4j -שFQTciJcFRU ,ru5嘲i*g` @-D!mFP"j:2&C 9ЂrVۙLyX漩)\ _V TYѣdSLP \hX +ACȕT>טϴuE*PT|L +_qUq!Xָ_)Ake[tFҼME*5Ƅe +e +,Tz +-M pmJmE@+T?'ihMBhSti`ں+ ZDp\C:Qyjq-T8 s#T[(ꞆP J>jb4țTUdejAư!TQx$AH&!@}30l޹?++nσbӟGi{YzHY К9i/%H+Р0 4 zc۵|i(Vd5@_&ojZ`ھ:f@7,SAڟZ(Q:L6ۏ#Q6bju- =y̖xϔ9DVLr2zoDѶћ77籦l4@LRv[&C{܂5J+8 d4斜!keEGI +0H 5էg!?%"Q6MqA(O*-Z ؃~G +iKXci2ɶ6F J3K|7f8d?ДZR.Wm{q]ևEhֿr6=O^%ⶃJ2^Rvy)<5YPR-U6Do!{DA e&k99ǐV 7ڠl3 [f;88oQ3m+eWgjԚQ2^f ~Sf,=qlSkJKf & vK .mKYlA~Wy ޏ/#HE9•=,6ơ!kg+z.-oY PzW2, _B{Sg +Qs"+ 1XZP's$+^?WpԱr䦎ݷ 8YnY:Cf{!ou솜u솜u솜u~>kq`B'a'?&&0(k>ikQҐ'2+3,EGl`Bu0 +^/MIor#d0B - ¤{ B2XR@`-^&ruXE{2ؾ-&iyu%&w^FQ$2[j"vT)B8WU q`~ȫ`~lK2 l0-go e62̟8JWy o:`@rXCw ݂UW~`u&HXnaNrLpS"d_m[Ç_CiYYr]5-g%,[ai2(f d 98`BkbS) y%} kᾘH 5s׆~"Ͳ৸ZBUc,WMkh 7(!S/!Ee?d2Ee-SݷLQvv)ʿ KQ6=E6넔4d5*)u -%+}UCY5p乔PhHr_IZlyJ%dX#'Jmq #/8S$1(t % ԙGqKEjD["a2dL_6#Yk3rӎjvCbc[[r`dT1I v"H%Z)낁R&!/rXEMKjIDBo4b?!Fs#f}l.7bq,W VUU$²`ADR +# \'';L19IW,!W\B.+* *!+RWB26B^Y eA@UTB4+0IMrԀ:\$FaLo۱TRP eկ AIHX LJ'+E6yŏjyS_{qS-ݏ9jy ߊ=ܱ^@ yln&c캽~8K!_rec|ds8 f^YqU#TL@H˖8:&+&rP--BrHCMRC,HzK`n%T:kȓI7 . 2Pwj IkH2Ce")[q8NO8ǞH SdMH` Qǀ2L ؾefYy0Lk/!{ ac97>`-D30T[1'O3 3uXd92'"W+v,Awr, D+L9 '\w]crL[NlBa (,'6P ΁^XdOj&&X4ct|c൨|P\ AnܒL39~ +<>I[2>Nĩ{2G\3aiz;1Wdyˆv;'5K+ʝ 0w'WHe9Lsvz?/ )m[yyl vR0ІoPCuSc3>oě31cZrˑؾNQhC +~) 0C2$Lgho P iv6{aykׄuicI81{f^/`٥ 6?ǿK+Axfogv.ͽi3/^{HZyGo.ͽi3/^{ +ߙ?iq_濠y}-oy[0B0[*8ʷ^vWyg~/^o<>esoOwf^/`KKޱ8Ď9k_%xP; qQ?/oSseonߦ'CByly:3/lB}oi|C"ϖ')/Pny'4)-Us!bvN9'T/x~ -YBv18Z0UMvi'i7mAkQ1-]k&˥" ΔY8#+#v ♿7ăT+A y;9Uak^^D=̘s쳝1*Prn +ŷf~p90mC;l(XԌU<.Jݴ(S@qEa߈V^w;Z<^srǃkOXÄ,gA"mɭ򷴾(*'C{g:pF:Ӊ1+4$,KA&6(1p:;H}r6׺wE#KG4_V0UK*会52SsrRS +ǝ*͗y"Ib*r" +L&P*9F?P+,Y -8ITFC2 )9t93`?w?|tZ|}:AY- ~ ,a/^E$=YIk4)[E+iWӭmNտ.qmTu&\ B LMEr'B> +v!T/$P3P0h# b H6FS/fs(+3O=)VpGZ J0+֮-hxe(:_-Eǭ!))֋2i?Up߉*ٜw6ױ+1;>_l4Pi3WicU^5Q U\X ʮD^pJcw"Qy x`p<wpe*zM0׀E@}&5A^(EbQTU&Sz Ĉ6w|s(X]xt~M*yWC6P +Yh$ޝE Ԫ³h I_mM'$Pji=n/1~oFXAV/⸈olJ@SBԅq:D&6Ru;S%sN׸ZP?Z׋Ӧ>**c+LF.DRkx0פKwo;ť|Yi,IF2\{|5]6Rs,\GWJ`"}a.#~ea^abk_Ɖ.xN 9} +_;ǘ#8fߋ/&3%^& !R#uӅp!^ׇN#?yo[Fq3埓D$-/}Pt4xf|ykt_D[Ѩ\AMKB0Ug.4)#7fC+MPl@rhAlХ& =fA"^C)ZU5*<ֹs,9 +g':ABeu\f&b{dC5K5IJ4Dl=bNZm@aG*;[K:Æ$c&9o\RZzmj +S#^. +:z thjNmGBYkڮǣO* p0&Er &{+0"8!UdDw #z6k6"ai2meg߷y췱}mm1 ʂ9Ś`ߨ ꫮf0:=%Ќ~wo_smn}xFկ!N:nĎx_Dո&kJMV&xU6Қ=RJV]d՜dsTSAJBH$<01^`.S [ԆpZ'q/wMv92 .7YM*ˎX|_ Q;3vu"֓-%ݭ2Vsv+,SZfF.(AFI2wz*=)H[y:Q&{׬inHanSLRg^͋b0fP3s+{ۑA2!L=h3s7Ԕ#I'6qb)nSm4*CK3{q]Փڹ= uboXP@&b3rTBJD_" +baLCJLsݰ4fvƾM`CB`Zbnk98{WK)|+\ђ`ˮqdM_ڱIv\L.α<]:yd)俠%Rwpᷠ{mk*@k۸+Y^VބЦ&`T|\xP ykyx O> ;(j#7xZpo #v!=KS@՘7`S:PY` +X.<+zU$FkXk"CQ3KBܑ?^FՇ xxj1NG ͖ xɆ: u>/ -1^ǘuvVԢ2HA@l T_R"3yRȂ*9eeUhϾ +-R$Wt k2^` єBfåRz5\2cV}/o/9EAWz\T ÅXT4'W3E$Y OW뷺!f񗯨T'[=+?3̘ D\z.@hC8b-G$wC +ZCڿ3yQ: |%DM)6j;>`׳d폾3ѼO&ӝJf8_M>]^a 1Z@3y$٩,q~?\DMl>=S?.U*lANdF_NQuӞc5EW]d@Q|cǸV_ +Βz^{_!r$]/}7_C=Bfc!g:**VjUQkܥ/Kgˣ)X_vtR>6RqE +zGJSWFlH h-$ӑ*w9k*:$uws!| ̨ )'{I<-U Mv1ˀ3qׇ 8t~Ʊ 8! 6*<DԨdZ+8wOE~;Hp]qp!niF@&Rd m*)0R&ӖD;d]yY6ǨXaKkMigg (:P@ilesH5$WY塲5n#K@+/Ղx\ +`H .26j *TAe§0!CgM>MD@T.^X* BE~)Xm٧Su +Htޫ TB]Oq(ykhSl +ձziĩ ;A3Ơ`Lc-jXl5GFR<33(s``ɍ2T룡F=2-c`_[5`OWUBnn À@jgt~4jyNfU{`u]"Y$OkLEr߳_Fx\68 'l~L8k$7FpDripɮb| zUApU߿`uy 0}r鯂G'NƋgF?R#0(om{̵ʟF`L⧓F^7_r +'vF*<@m.gs~4p~߮RR]s_ԙrފvQ\}4N7KZY-%SpG<L`aɳF%t} +flp2\r0^:/dgvwCW<Иcȵ +ëBc +_uM&RZ3$wE҃AW9ȇ84`Me63&z@)>CU OUV4uRa_<&*~fR$6M"<بrߺ0gV^l z$h5Uў٨0zb%\zb^ȋê1FPA]vC3su[Z|R ]yz\m|kN!xn?aﹲ >Y+z1+=ߺz>hI԰Ou7H0vCM,lC2{[psaؕb8UAW8;R?TxJh=fC *k'ˁl&k~Wwg'!:鶀o@x;] 3^A Dz]5He +or74:k}QNnD=\?F5ZG1kp[VOL-1б,x텺JirK={Io L3VL;ɧ{wfV4mZW & { 7 a(ᅶDߍ4/ :Ly欔k%F|2ϴzN{%as'2kӕ5SlSدuX-XMuE؛ ++GdT5n%%tOI4U4%gt!Ay|Gme}up ~BU e2v.BɇeQ>m}8` {0|Y&1 Mye:=`~R0aKƀ j>7~ǻXa\Ⱦ]*HknSYƯ.OrU7 /WI*+Ԛ]k'|oK~{m9xKzRIhF~ٸRb߳⯴0qKO7D?3gtzf<^Թ A ,ԷY'~g\V9LUTI?'9s"2։zM8fTOq@KxT=ӌVzP=ZjyB4-3P:LwKJxT5z8>FKjfr6h+މ?Bn_x0 Tt,3-i١ef5\9s8ɞ [ZǑ +ncp ܒՄؑad-ӗzqVxv uҾ fv ՏG +G)ێsB۫m^RȬ2Y[n.js}ҶҢdxIh1Oa2gw3V}1E;p> 뜢Qe4i0pU'UaYqI[')<^1$8kDL|z77ׇ(YƜupg=[9 @ˢ^,Nq+`HP,@W萬bվbAY[R<=xWd+`uԕn7P\"b"}mu-Y{`ϩ5NYE*FicEq Xs/QfR+so`JYUOoҍzw3pj]t\YhbggoNqe)&ȬCpFv%85hB ƕ5QLuUӴA84; o9!  ,o87F<1)2Ĺ x?G .@&k)MwM-I!ΔxF[Nc΀l(mL+2ꓟWdW +'ճΜ]R +'ޯN6VZɰn6MW XԡQ %YN%J F۫<xy zS GJY8JBD,0K UBDJHh^JW +y$xR?prPk(+)=NSէ2rDoɈ_)R8SR +5qJ>pnq7cvW y6 C5R 'r\%d$TB齔[8|<8WZKL c(@~SWZh_qxA@죒GSV .A%sZk(3㕜Lǖ0I..ж0ˍK]P9m/j#`*\z_8M9FEhmVlC`dG2`8|g'=L'ߝecqzS3FpY6jcBĦ>^,nq cSIZB̓Wt3?Sie>0vR3Skw59GŎv3 Hc#NtsdtMm +UsVqVѪ˾Mp0YkÅ<ŷuMQ_QvA]ΧsS}ݤʭ%Tnr$RFVvTHOowM=4QTY:L/ 4Y%uy:lK)uG-WV2O[q7!^rhʼ/2i,5szYl5^UV,v`ct+T(xi/CvhҞ^!mqX2=*#S~x}W{(}Gn uku׾D6TӍ1'oN-)GuL Ck3:q?$h%p8La ϋC3Bcfs0j#a(5^'ĕrR=qn˚ Ϙ:SE<!3-Ԝ>[~8,J'oExd1*w-}4B3?:ŒU#=:Fs 2q +4;@^C71 45M#qms~di\_}D۝bemI]( ?Jwd0]3D4c9\t!).Uw;- 8F0uM#JM(hkot> +pMU$l@jq6~p5f)mLȪ"u`Z)=栁^0{OS9z5\]X:,#R3Yd P;+dHZH%'24;əAgd_7ͥ֯sڂCW h o\F 8"3?c>€ͣ]SN 3II7hzJϪ>>%^z U˗}9O< ¿k8xT 57Qׯ$>tU!JvAtiteU<ixbG f-ƣIm0I`L4mcmLx@=t溶*S"^[vfdcNܵUܝƱi17؊Șv%F{*o?`PcG" 0Z!4&/YE,&WNhOākmU]b.CʠG̃=PRp&!ho@Ӈ쁈 :̳"~e +07:3ϢPYzfcHK +M;G&jhS>ˊۑ~6NgѝހxwjrRd cnr& Ű;C*:]* /W ./¨W6 Mr׶ˠ) T3sHM-g?8 Ͻ#l y 'O^Gݙ\dyWN +i­T5TD<Y 0@kuݕS< +x͜XUKFxދcE +'P{[ڱo7}EtȠjrU =q%ǍieU:v`[A;wiV?mR<,ʡң mi3?oW'|2›";\n+_lP٤29^: jEi/^lWa5\\ 4šnܻ86r8/2޻V:t~V9dsj+$[20W Zɭu°3)̙9F^9Lwun|$E ƣ47x10e/g,*LܯU]xr+{IR u"w˭g+GA̳Dŝ[q1_^ NM-;â_J>KGgyv۴0 Q3f% ԡQU(e[F7U|]s@a{[mr:Е =:{++FV={OWr0 1=]X޻EayWvaX‘ηEV< 2Yq(4A?'i` E,)'Enpto<@?VB R8 T\>+p`"=IIZqIz;H{qiꊬԐ_#{7;ZR]4z*{"=ڶV+icb'ڧΓO[8}{#,Xo +_a`o④"PGSv.~&1—4sZ_&'mAzKz5ZpPгg/]9ioP +jԊ_)?pa]$!NϪ|;'k&rHgaP\mÞCSWK LguNs˙,8g& + ˙Ƞ~f@4v_9y> 3e +h_Ϝ"s"IyU??P_l Gk:ғ##_-'$tl#ng(?g5癒 4ΔNIF3%ٝԘD=$NJn=s;gZyY0 x蜘(F&V]Z^h^{jDPZE[#*H^ +{Mhí!wQ+gUDsҠfK>;YkG?jVM72! aQ7YOe{.:tܳ5bz"h=[[<e}=}}d֤Tso챫=Q@sb]3W<06/vueb)b:쉩6 '@J M{ku-M!yϽ*]7ٙS#|K-'kjt)PƳn +"TrQ)w>" ꮝ>,vVVT4:9t7Lw0עY\md]-ɑ:lSE}1gp̋p/f2܎P *e18DziŖumVoETtVQ2%?̙BIy`m}sÃ*^Ln>+#oN".+0xvK(t0Ϊ":^{^,Q65j1f5͢rBMDGR_d; 7%^Z]$QVM^[GׇKmE[) ,Smr=,u@֤ppBa@o[Q;֣˪EBnSb:&%z?!EL +y%l_R|$~D^Km +|MVgT2|R+Csm=.. 9j̀cnoߌB"v. &m}t)S5gIn9z^*5ae$Q4.TI;mH6})7mkVWŝPc8nNM @|_o%g`)w}@b޲`0%aTwi@%w;kswJhq0*9nFDAmY6&p/''"9Do}xA)*FOl[59-T+$3@85t)֠ߐS;OR4p~wHjG*f瘸݀OY{hK$ޱaST2PhƉ^Xw|CcRCDf+BIL?+4/1/@5"U# /\Nl`u\Fm(9[P>O X֐Ze%U;TtpWwiJG~PSa8qXEEƒ% +2Nh%k =KHwrWH5v{!&bV/  +|P9͜@و ocHvhX BSJ7ҷ5``.-5!%-^x =RŌ=h.*϶~mWX.eJDT71QtN` =4O.Rqwgh"x11k*5S+nDVoL'[­|;kbJ8)xƱIBua&jC䷥"G5%5)=W`#CɬJ%,(*B~f/Kq6Ϸq` p=ґӈaF"/FUF3+vn^`+,3;#KZB?c=|`kTyuvϨHH7.;)Z}0-/ٰub-YR[ X2dVxI#V ^x<vZ +F+C ̼YY4 wቾ׋wq!br-gesgvdI>H]զ0Gf"%wakG𠀣"c~tJ^1Q(# o{/^6ͺ k3mQhn|Uvc^ bpbCqzW)? 2#/Ws=_6]F9IXGބ9WՠI>NvHO[#Y&>HOd$S/m60[;T<Fvw4f^jGyXtacnQ筫aS'=DF06+,@'!`|H(Fs5ƒ-־Ȓyiݫ^óJaɒ-Dz,'IRXdC`*<؟+dTPN3já;;W(YiVZԄK쇱ȋvanM}O|ECWœR[?V4 RAb;tᣂp]B{Ȯx@"#K~Ȁ6bqt ` R 5/7k 6֎CF +41 < ZOԆE;ym0BE%fG+@7Bn*dl҉e=[_Bq}3=>VtU l6o2-n[V"kz+L"*Bţ=8qzcMihmrüx}>0qxVݶ,9EҴNxT{fuv!$>YZ|[r8꽷 ql}OOE9Th3m$ªve뇱w1PvO"xf2^0 ~8IrC Ulϼvؕ筃b$/(ȥǀ j8Z]}|rS~#Xk\a{bCqSH`;:i>oސkF<M`S`FU~Q7љm1wŸO(ZIdq 5֪pjKƝ @B0s:l8xd/9?Lϋyֽ7󮈸b: +[6ֈbF\qb3_䨜!LJ8s||0' _/#9e>w "HYj7+%Ta~-7Y|w6j WnӸkǁ̸\9tk.?8K.$ݠ&6:Hxadiu& f8&#Vv0 Axp-& 'vPccw9Gs9y"T=3i2x1_d\(8rwQ̊UVL$u3&V@a,]rQ,xhx;`6duWA~y[wtgmr7:üqPfL$S89U:x&㱓9qk' -Qr:d,4]gݣSiM&6Zq 4EwP +jNP Uʮ1 mނqlL8n#WONfY^b\6^]15jjq.5+0{ +-/ ${L +ZR]N8Y֐T̢6qx0se4Xp*$řR&ȚǠ);}H>͕:lm͗3lm;˺\X\d,'z +ˣL40W#:n"(MvyqmZf=OːO;MVbI%fV 4[{{wFjqku[D6GtE=F%r<;2#tL0SfӼkfzWI7XXY7"Ç&t*vXud xG9Ey$q!s{=zLTeͬE-v8}5s۩}|? 71j"P❿m |K Sb#wdV sLP5@R-26r'璛\cKCƓؔ:r2#r +GHZʏ>BocuXOy+} +>9xW#VϖG0IcF{FV) bitV]pRl y6WV)"$,&yr@qbu(Xo1/i66un:w96R8#zx,=pD8r֗mK8:H`| E0 w#zKK~6/7dn@~w=nLiŊNVEyBU!I|͇3/B]hD-q9d}~uȜ".i5-9@核px"okEBR^ ½g~fǻNե<(g^il!<ş_ Z f00 ]/E'iߪ+ClD:IȇT";eGe ^,2gl6s#N/y/^=F9Te02cLyW'L&m1*zgߕVQhF!jt-?fS<}@2j/ԗs̆H/hp<@z11|ׁg4$U V44@tA=ܡde ܣ!m9._8b4 qCq8:8`v;g4l pɀ;wP? %ۯ"!QؔC)Ziv . +SS>'&Iǜ# LmWn08[;@ zi0&ϕfcgμz1=87V!(nJT!T'MUK%wvf}35}[fWV_!7{^%_.fT t9ɧfyMރ*w"S0>7$y1^ws9wҀK7&UB!n=Q͛!]"!Bܜy<"sԽ!nf]2dG\\ +¯?:TRjq;P]JQm=2A$I Ȣ! 8@]h#ChG];I!p~dvU[B੡{Sv蹣]YBH!0G NiY}!p_:>Y>2)!Cp][Gf0!xH[ p¡!kGBeϷ`kE`tGKQEY[%bd`C{g݉q7q>~],F+5L.6;I_Oťkf0Kc# `SI蔅QWnwInȘϭQFԚBtm/\fl!!II2wל9$ѧU-W-#vMV +@7pFoD&]4K&UH>xcȈg"wUD(l)JO8;ib'I&ԖF&X?9aQ.%kTеg)۳`/骩rׇYQM%&޾ȡcf؂t۬ntw|!/2t*/^D7O zҸ81{Ijv HG: .#{VG>~ CEeqϽƸxtq#hM:brTV{ݳSw:zxU?W_ F|#V>4"lֶ Ll`fF2`[M-SQ7WkFY A da@B +q w =v0d0\p0Kg.8&K`=\z 7 VݨtCwgF#mCc}7 >9|[4,'k3*ƞL}jxuYu/H/J>=# K/0y pq MtQDFx<|>󛻌l*Zy FXJr;N9) o?8~UC MS`u3UZ `ՙpR`{ă$nz0@lHVMv6A--1҆A +wmUTE*@7 *@QhSLw޳hQ0ig`m *dtP6|:HMfLt3׋JЭ+lSҟ)b,"F +Ã!~/1+N0CP9ѼTMr.Ey؜e"^s% 00 +K:`m&u %e*I#C4ng$![WCVF)p{uTU} +3iFzu-BolxVQzP5~k:ޣKI: +ɼj.B*4w^'4RY&uS "GHPږO7O~veKQhY8XΝy:Z\HtKT.]]I(C:u=hGk}MK +&*YY UѴ{ǫu'NDz_ڰKa[7]u*!e%d ei+ɉ~gs)z|@׹.)d?)*HM^iPUa=} pw-T&{^&6JF5-E"Uţʖ8 +Xjԗ {dy7酭c;Q7bS6ml(y2::M$ϋM;]n6Mrl)14Xj,h8 @ngnQ*ڢġ`x3~.&i%ҥc{S'ba].r/.pwMh +\HOj5ʺ]e/diHj_{PTI jAN5`@|gӦ[`U Jy^:W`$UfNK5 %ӽwcuPGWDx"`uDx]%8":/TOull,B E8n^*`)\`@ +YY +U8eT ڱ#ƣT +hN󥠭<U8e/WczGi:#tp8*\8JV"L8GM9E" ~2ǝj,t9=2̝MS{9}Q*͞bI#,*QfP'̑lka= +y'ɪ-dɜDPoիUNF[sz6w֫P=V!ۣ[-GzWU^mEP^E~`(r*Њ`GR:%L}u5}O}S<>NqG?uU9.S*wW)܁cp-7} U*'rtT'Y[xlqU6ds2q謹UVHGN_6aK/`wcd&[/kGc3|dS>I +Xkm73`0 ڦwhyqDI])Gl{c}LL=5&Y 1M $s\.o'g̠6n$٦D;pvV:8ۍH: sB WU-`-讍di#3Q$؎䅟5ɦnxA9) +ݒW}` 7.ND~I9TT>{=w;d8'fy2`4e!޿o(BW8!gFE?Qn:flL\4J:t|mܤ9waAfKtC3ܒ%/ +΂  Nh=GϺL9 * ; @۬p<ƾ ?{ciZyzv(Gؓi4h ֎C\o1"kn m$|)3萮V7'H7wRjئä́ԁ#fspa;fsb^׾+u]|z.w(V>? ʱ(̡ S09X~sM'#&rj#w*ZCP_~.SYS͘" `c[ +jJowʌ]>DJe=Gm,vJ=)7+>2qz$dRhOS[9'8bS`kz>"Qwث'Ѣ9N&SKhݷgS] +$%5)׏G4挻*^15ֱZ;+*φKag>#*T3-3n}ĺ?Z_+?jWo υv]/p^)?1'&"tX4Dh/-Zjf,uYid2xX_@K#_`dM61>(GBPs~ 5e2aiO2h]`{kyPByH/Z19RXp>C`g~1RUFoo1xLܐ!Wc呜SśMR5(0| H.w5a9F$W@]iܺV lT:αq˙%waRҚ5N?T.͵GGl5Dx`V@gZ$D3} AC~n)AzM{2HYT( +R+^qU"H8 +hehOԙayP]-E=kz|rh=rLC.ʒ#k/(+_H +}Z1Av,z,0c"M̨Hq6Z:|ZӖ+^dI/,)6Ǥ#Rç+NN :7iIY.K2cʩ:WdLG^?5pd89q6ZZe¬U2G} +L")7axOkVWeYY9xk!h_Lǯ3t \`2#?Ւ[Iu +_}I<fQ/A)_()78y5N4fT{3`/~XDY " e e>Cm(JU/@VVXXF3 bR"d 6?+pKw7WmAPZPq!R(Y(X9[4u?x4M8 T.%Ghʮȟ /FF֢bOG <پ8>_;3Db`Bwyf:HA=)7 F`Ι+..W{K΂mRPnZFgJ ʱA0? +mPZ;^;b/(nE/tQŀM/DPl6L]ٜ,<X%H&8dª"4yf pe8 +j^|İM qUÕe09|TT-2U5Jx^#^Q 2cXaʳkt7z7hЧ3g%(ZӴHKIDڅX;2hx\.|=zQZE}L(XX"-IK[JuqΘ!`瀩g:uշV @M'1 1S;LqlB>ƃ{iy',oԚ +#$0GUoug~C';evW0=K"xcπ $U: `kmAN[p c aƗNXV<0FO-dJM<s]RGb1a\aPq2$ ВEWx o.ʧu>}$>+#qxյT1qzi}6THޞD )xtHr* Û;J 1>wXDTODD *i]3C}$dO/,PN+wX=ضZ[=B-`T`b09bY>QL+HξNZh_``6$lsByڒsDZQ}mw_`GCzZ=S~ \+ Ʈl.VF^  #/? u-*g\_~^8?yEO]n~J:>e_׾$mw4h7?i_~>>x+3}GO&vh.`!GgBzѷ}m>I#)P"ɹ!.XK?VDs@ΣBh{y868EW¿BǯoA5ƵWԉ:SZV H/䓙e+6jsS<%XqܶIV|yfKf$M DPE&XsP9t$CTV NU])PaڄYMYO- |DwCuPOB=(K 8'E/g51 qui>OYgthJ v~hf ˕)_,0L X k=n'͢t<Ƿm- Q[udF߇u/2$3t(Y5RZ'`ڇ*=qR*;@̟[ɘAu4}&gb/~\f 5++yUIDbGM,Y%mDm#Mi6H#dgO&G%mKg"=qS3"H"ڟ"lN/H.~_Ty:fFkv8 ytG B5v!@ ҉pRVxFԭ({`iT5}WgySJ=eJВ}v>gFfe5Kk?ۻqph+߆:&1^0O XS\S\:+.r??noԝ)Mtc)f+jApf赭|`xƦv +S+[-+YbAZ\(99jcZtjKU^^UiJ9DѬ" 5Ξ )'pcB[Z( jv`&Jmdrq;Wjd<-.c`[O@py{(`Oe`,6;\TP?-Cbe0+_Y }HJUe_ZlиYM lA+4 Kw +QQ+T +V4 wm[lM`il@~5?**W ++8BNa١Vj^PVR#X(ybx.ygݎZ)*RS—#uUlw4evٗ+zx fM +#j>2{`CH= eƕ&8uMwlÄ&ΨnHZ߫pȨW| $Z9 3qVo~VcP5iBߕ>c!k8TC3bɇ-؆}H0]0P +Lż+&QGGɨhG,6%4ZͮIl'{y$<^FulF*a3<܁Gplث^"GL?$zSbUsbǚY+2½WO0mLb԰TZwEřۥh{G= mmE@$cE-u~Q͸-,QL6NQڞ]%AKqLjX]-WJ@lq$*+,­c˜%?Υ%"'rXgI‚M+EX5v<ӕ%hʊNӘWڅh+0V8, Vt aɄjW<7`!<ϫpWl;{C®Ĥf?g邞NW|7o(rH䨜;.>|f?U-Gr'O od AU걣nj+ ڑ U\ 1:Qjb? ?_ܱ.Ch+w3d,~rTE 9W +rv1#~LsLO4?V(~\,>& z!9(v fd'ÊT-Q޸v,lKxrz炥>mr ?QunWu:erGeM8;3lx_UGLUzؕس&w 5FSa]#SmX47)3Vn`bo6ZC)%YC:֑10OmZw(me_Rc d}1s~K <=(;Z>W R8~j heZO=DSpvgH֧9*~M;Na@C 4b(*|D=V$$Oڈc\w7;](xjg/bvM6oz6cc\K'l=GqXdd#k6>]u' +D[~lnTקu)W_=)]kYfYftp0)%^N?K^%.@|D3MI7Q;Wy⭲*{ˣn=hᅐp4X[2Sq +qH$ܽcAjD2UyZ>d iL>T C zI[rbW3OSWpPzE*ڌk$pgCRp.-[q(L\$#dSjf!~ϸHp%QK.d#Vׇ䈵&W{̳ +PhyK:'$ιM;:|ZmYG]UgtuQ^ٺE)fyaE)f"/a1]Y[#cJGQ̢čR*)x?5Ӓ挬f; 3l'' $Аz008;(ݛ}=+ Q?"Vf5:;g2HstqzbUu +j1v|ÅdyW";M!)[kI 0MNB[c2b6hFGhu(/KV"xM40O8[9 )Lݯg=fM;cS(?9OF`hB;#5yf:k3$k PUMAe &(Ĺ{6xDi厃lWjݓ+Ya8ŵEn ~0 (#P*]6p/`ܩS(P `Ec8Ql5\052]óBX~sXٜVi}۱c|2%8djި*=̮7䨗lu6q*N^wJ)gkհ0BTh*8Q"pJ WkKENŇb7͢b~8MQ⚓?rKMoJttؖxqꈴ~9;́ebC}Ҿuy" (VK$<<5s)3t (vc퉿6-,؞1s?Va{a#*D`\u1QKD2&Z <@ +y@dU5N +,=U#GhPKz\AŃ>X$NA|y[ZX˷w|酤 'p]^c21QMt$x1c@nn(yHdh>U]z"+@MI gHhʆī'q<jv9,b+md:< 2q]H?$#߲8[LB6esBkk̨{r 䱑 Fl}ϠB,/=lnjơk.$TO 䎏^}1XNm\9EK O2exi"W* ~8{8~FRev/=_O!{=ePJ-j[!+}d-T掃<MME{# ZTH{i4$DD"66,r1YSzXxqf /$ B~5:n);SK@Csd*y ;רX^9!FUx*@f8( +Ohҿ3OrE-;wC/{o9:R `o9eVN3Zw,^-)fMyxZ)),vᗒ}9G#Ac7%V7"ӣӨa$2©ф## +H]z )3#irk4Y<.B.`/R

@6/iZ-ʲ@- -[QveZ+r0ﳏ`ܼ;˔T/uQ{խ48v֫b'q_/7hg_PԂ-sQT#\T~YY}0;ey%j.-"Ey|Z+ ?UPzU 7-z~KMз̋xDv1I M\f80چZY~/IMzǿeoq,;Y(9j%>*1dbO 62 Wr jr<|!fu(#~eVU]9XS&r[c2(gS&>N#-ʶDG6Qg +AɡAȷ;#z[PZ{Ҭ6ϤIkOm\!1jt (R½?E1ibRԣM\L|ͳ^|>j_245 w~VJ9!F=9BU[$XM=5kR"!:NZ?cpro{18yāçuZ`(zoD@%l[9zA#OU4Tͻ +qx~rH99uCu,+<|z&6p\«TPq'H7@XHO/}-{pnj>/6@HG\E&c(vsp{tG$}͞5mÆIs0oe•F 9cG)c x3Y /R:=x85`a.ܟm g0 1:Z?Cɀֶܯ_^KY;qM:H/G&mmB۴N]]bFƭmlgaDpH ƉTəw۽'{ yJ'ƻ?G_Tb[T)p!@dSIA$yjyCcΙ< gٲ!xZDFgwůrtC9.5Ee伡o%~5ot61ιv;G<9Nqj (=/c#fna^E)V;Y?9'dHi6ςIX`dAR\澐\Ε74 TPaUedJ[=OKd{=CCҘHcǿ>Xv[JEd3zH53j]:#_g A?>#xMD% ca%I )6~OXzH +ѦHk<-ѷ:R> R9G\ %3QzLaɳ#:}0ꗴȐ)py"SQs/4\1D[jI݁r2IwޫlDbF2ّCm'2.p$} |~M8r&ev}-ZV 8rL^l.AU +̒`/&Vp1on#y# &sZ?hK~#–jp" >LN Arp{y!rl$OlPP>s +# 4H; ֘'b(A(F!WiQs2:;:ܐ .κ<ٜ%U}ci?0!(7lCߪj%`T&pᷤ'T&ER0}c) +PNo87T~bs߹i +Lzq=@+E:Ee)^eO[u sMiQubxy0DX*O4gr $){`S'c>v&\ϰsڵj"1CO@\o@*H1 +Sbҡq!.å\s!T.\nB4CI4`,Fu$sX(ss-6dԊ&*FvΡF0M5#f+uL$) rU3Ҋ3#.4D]-4i +ιsQtP*.,EZ!8?&`\N%Bg2!vsZ17mp1PQ&@w/nEq2+ItDi%geƨ7<\ RKˎ\f$|k%T_&[%hڗT\WUnͯ:Os{fw۔+j6urYәdzN3'.#`[!>kfG y`Կ>ePV쳥ŇS_U5 +k;Jw Y]Le0Jv)2m#nH+-_pABeDӕ&ŏ~ga =gpgo.tZTzD" \$`]ߔܚֈwB>}ܑ;BQON>U1m\ksJy%0<3fjW'Ka"zO$KS/c,Ib Ѵ.=k~NK]Gxճ1UjBD|C88]Hv,:<*Jc$k0@ֽ4Ѫ x@c 4~\Hrƺf2DȰs=H; ڦÖz 1lj-SԨGqDNmۺr/\a?#Q0ƹkXa$Q=|_ћ ,p[ +RrENs' 5uho'pBνJzxOȞ37.˫aIkwKKpGj}WPe]%Ir@>}y_KR/):j4Ш _F'@I}[<"3٤N9v+#oT^h流ұQhMk27aZyɈ T/&s*5{EvʅF{2,85U(`5]D7jĂ(8NݵnQX!˩xy^dKQM4=P4NK,Iܝ d-' c= Qă:S7iU9յ{ZӮH5/\$:Jel'T踺ˎ0ޕn&,3/h% |ټxEo&GKdoͮ +Bd52>}W} Wss#e$W±W"Tr&n/c"(L2 V'Է9Ut=9G,ERC*gղ5e䎈Gt>AZ;|xZ gfN܅u_7ʶ~Z|:-G>?P +@vaM4RRC> fm"6G撉sCZSs!Vx8R +wT#)/ $Ih%hn{yiCG.8]>ٓFMwXnU9?/A ܖnHvv͊ZvRI++k5.zގLG&9F+)e˞%2͖0ddv]ۢURKlƥK | 0r |0Z%ϰ1_3Ã/;wq0_tÈaK[.v}n2'/Ú_{0ud5k{= 9;h@/ѧ*98$CF%vvH[v[/Y+V d󁟐'cWCWHz_y{_?ɷ~ܻu> +w$$/UB'/{&y WϩwSTN?~/OVQG#2G :I!Q;)%u]XGoTk֏ӂOSx~ p_bQK1kЯ+R\`Fl8L{U\o׆q[vSk]A,gJmU|L<~ٛj +EA9.IssJ[)vIۦzHpm'{6N׌a;!_L#v%UVqi_ھq-vCA4}!%"Y&{n.$&o))k8i͂ -%3뻨Q^(θ? ko,J׫sLznAKILw+{}F= +`ۘ_YZ>VQi'-c7"U JHd* !R$榚fA'hK.L7-zT8.x .f{IX;fImy8Y){>EghOBɫ\5oGZӥX~0)SDxnj*y3_Z +@vi;+cr K'㓍qE}>O{%d~L=:E}Vʶ&U/{~XlKҥ9KZAy_x>9=2)Opĸ-^":%pÿR ," 2˕ hX}[~u>nt 6Ǻcū0t6r;@++? n jGp8ᆚz x0> +8 *Gh9{Gg8dD)t$Er[PJ[$[6s[gR]8N@f|bUʕ35ˏ:a8ږR=\&% 90} &M>b'hOG0;(RYZ\?Y7kEF~sHΎMˈH{" +~6Kͭ>,HuʄwԘȁG!@׾L7/>:AISfRu&5z٭Z`-fnq47VIc@(`zav voGw&" vQhL^v°cy ފ oadF>xd+eВ{.wl5s%YWF@= cND棶X{2 +<8彀0Dt/а.Q|X7C&"+4c<҂!4z5ϬQ ַN #lL} Z@ALh!4|& K;"Nk`,2XU ɤxku<{tzw{ +ΘξTEfzBk!EW9.aQ/ !N%9s]KD]}o7Tf׼bu}Sgu=Qsu ?RGKk]_de7zhDZauu\:>}D?Dկy3 b}k{湂}%֒º a]r(EHY:[7zx_V+Z,|hbFY+] s7&ұqv)rR*RH:·ھInYyz)[NLIhrbxLLcPjZŲ&cC1g[\qഢ3Z5ul3)`Ij\mNp\G1w/cIK J&M9h-Z?;D;C"DCI +0IW,-e~8>{1["8,⸬[C"$_\$ A"XU$zHχDp[yHR?8<$EDQ HM"Cz8O{9X!w{O '8nV/G<,2tfeǖ"Ȍb4T)`2x[G<#8RoG.(b] 3CڂDG\lj#`:.]΢sh`,4'Yp]mcHڴ1 +pm%Z4 +/-q=d,IgJSF)x|KxaG^C4FNn!x¡E$ȏ$O[)Pۍ\{7>52 k8nq9K&҅$Syb[xn2%8G &~0x޼.iXN^@19`|&E40e;Jv=D/Mݤ$rhM2IG'ė[D{ +i2yʞ w |PsO wm%ǖkg6^CI'MD`gZ%Ii {aՑs\yՙ|)qӋo+ +^Gib +~™/&Hg+C䇊W7J +aUʞH:H0Kqr3g<l+ܭ?d^o2 |8տoX9xQy^iKcU]ԡ$a~pWv6^9ɡ`b-w|Dbw9p,鈛Rb" BC-< "'d*dtrth A'9f)ڏ?wQ40'hL<*ɨBT_$eZi;9PŽ߇ov 9V5$$4Ii},Z_+A:X!U{kGÞ +4]GOyA9R,0K,  \Ukh]uKI[g~Ƚ[E%ڨ^NgΒcoWDĞaV/'9>o~j]w YUl}8aHd a036AuoM%mMӧBPY8ldDN+G!9="MLt (I6429K>^ r:Qvc#sčT:; ME$128G΋k%>/8v5嚳lA~*LLB;$/{u\l׳^ 5ײS(#m[UY4g[{ˣLmal8R}n*hʲ -EpT_]@`=o_|cLNآȣMr˩goXQb#H;\wQ}&1 6ߒ\"GMl7OZ띃p>Tet{w7ɽ7}T5H2qIbWYK;' :)TyүEa@0^XFDnr<[RoB(8ge+BHE~vTMYnyD0k [Uv&p6m. +7a.Y5 +[n< sTE܎h3p l38\!xxN؀ =Jĉ7DC%VRKR+Ԫ +: qxc+/0;cJh)hvVWĸbgL4zFGbyT+N{!(#\M03:sv& [ a8-JBkBp%keLsF-I#G ]L`Kgf`8)"tpyd\=eO|P&k!.& 6vt /7Ѭm<Yn*kaFڸ*TQ{;q(;ڎox')̈/ahzgr{0KJDKϕ}Jq?<#`xƽ3e$=үf0/RlZG!'#(Fmlinja +7pp-0cH+DM8 pY.ZS0w>JeüƳDc`ٿ8Ѭ6 uSqc k܊g[Temoc(i4Yߒ|^6Us8Ml@BDYҮ*ɚ%JL}a+̆6IJ,r%_W %eerkKؚջ8kf{Yp)nyKpCJHpƴ +iIFҋX KC: +|"v}A'>t!j.~)%o\ :y̭jպy;-8~KɂT1KB9@A>(Z+th7|e1'̡rㇰTr g:jMg> TzFi7-ø)-5@ZހѢ=X| m ֎I`1 ĹE* *y6ܔ=( f]QL.Ä`{45DJ-WQ%/:^/煄?e3)4Ն +1x]:jxcAZ󥑞%gY{$ijmE%+qy [YmnmF"JڪekW6Rfuhڪyd+a)[ .R#[2o#[Z%e+As}ZfW2.TMZX9L%/@ppmYYbG&?` ۘE!,e +'C7J}3-F.2&t5`C(6X z#@. t5  +`ɝαP^$&'6sT .ɩqrYwtN~ n5U5+EڀH[%# Rz:Ob]w8})lW9ç#.5,<븟&ȅ!GzgHB|I#ZYI.1QKE\)y«ub0_M@u+weυ}]2Nb5XGѮmKzTm#Σjѭ@+X65KOY9ەn%RY%+e-$eIO#mfr-kyDڄw Y{!+yʈX,k B%# = "U٭YK͙x|`rd ߿gdAyaik-{e=(_pd<(GP,0bԢ$Jz 63~$8j7 -HcΏOBC<BqJ0B?&#XWQ!$x-HZGTimHZuN^FiGS{?/XYde 0-^g>l ==$UGF  +A 7n_j}9~Nj tfU"Ruij>ˆuF)p8HA&̥73A!W +.b$P3hA0IAK +{)+GQ<:vMP  {e D.e %ޠZ*IG& {(B0}צּwhW{$YNJTo0WXu9quVIM̱2WW6W'CR.\ 滨l'1%Y}{_)O<_ugf ݨ^vVϮ{ꏞ=z.o3{nO\"E`|T}sP__S p=kzV=%Uk1V6]Ev&j[Z.#>=xdϥwL#ƫgɓ1D8"v[qxݖ&(WZ"XIvKnp}n=DZc +`|Ɓm'Ȱ&f?vR&(hM "l>SHt$!'5vYWWot.SC~ůke"4 }3n) + P `]4oLcȝc_|pAvGPD7 y^}8]BI +{bᶂK!~2 Υ_h#&#(2H\~4f>LVan @S-lK>9s)v=f4ۆoaYH"-ē(G9H_WY[B /~yJמwQϧy'p|hxe#5/@4/UzP{< DԼ}ve~){y`{wV4}D/WN6h?$Ϭ_e-zSpreG&zdql){3WMy4n]wٯr}hKOŶ&]` ůW筧}y}iLyKge9Q^bez^b~y?v Hj$2}`PZ TxDNc|3n?ٴF(?}Q{>Xs>~8)uz +H,CKFW@|]9sDƣ[O"ZP"D D@+0S" 'aS;IčrU*d|OU2fHy޿58Z|k2f"ɥQD&L"6"&A(J˶+t:97 +(:8-Ǘv8ݎ5!W͍u0 VF`zdهtߵ.6~uF^ 0.ʼnyYθKsmh +H|v2!B#죦Gg dJB%Pd9ʠVD;qlĨ$; JG\;=W8Ć [I'~"sT5[5LCgQj U^i`J +K%$;soIH ,1wH&ICRDHB%Ly]7Ѯ~XY*wTED6*ޕ]}eȂfC])2Bq%F**W +7@\={f!:U(:mgi3ojs|QKGD]x#ӪKbP*ճSJe OOSo>cKoy3g{ĬoWkXa![4qB4v4gpT~PR +:퀿h]/r1d^i]nvmC*claV c&kEG0qyy ءŢZ<"e1G=sUao躣^:%LE]5rqU 6:)8v&Y8X=A)vcOTq pJ3 ypyos;MɞMiZs{I/UIlz>ЎN>o`\@VF1Nd@b+3&1_W|Go13^+&1cʙۼ*E]p|.{*)vOb#'1.ޮ5r <~ؐi"YJ >o\_;vuA +0Mija}|$&+ Jo +u +Ѐ!$wAn)几I9x{KXC0oeBUN :1}6"Glohf5묧=?UY_2BpQ7+h_] ! ғ`>p%}0샬e` O@ku6T[P_:q86L}PՔ 2s#^2|x  +䟢9¸ԑ&<I?P< ƚJ3\n|YISԃ $PζmB|@bge&(gyۥ=77OUI%DĎQ He Yk,A0Zy+ԇ +] H)D>e&Dȧ02{Eaa-8`{l!8/d>i -a_g)L@If0kQ(HzNa"Q()Lbm}< +^J>BToNŧ0mASuUm+t%*uW2ǣ@|ץJĸ*yIƯ*/Y#vͲP̈ټx{vgE-][޺Kye߾ww\^~^Z + \/ErCVwA}wVeՄxV̖/޶vo!˚fbpj`nKFWт`<#* y!dudl$qxlz$VVS/"P\C=%Y])NNHiܽq < ;k]Rgr ?u0ԻV-qR˂bDT_+Y|riҒ4CNz2jJ֬cuT?Eу}_s}GLW#M(r4.q3?: jٽh!=aWom<ǥꐠX!vw36VܜJWpaՐhtE4T-n%2(j0:KuVD7}.[.3/c>Qh7OЄWvv g/}VPfK9A|pE˒'Xs#5hn:T9_{8eu N9Z@ O{JBRm6NQc L%UҊ@'m?۬ ^ A#؍UQܚ?ZkMK4Aeٝt·Nx>'jI*޵ Ggaս'@hh~B +)#mTi J'%xNr2.rkPy'rKzGګj߿b^T;]5yr4zєG[2 ?PSSؼFaÌV <ۖrϚQ(9?w#W}mF,3;zZ7ݗ;lAw3Ӿ~r!:6a|g:7m,uZ}@2gM|6"&gOi]|6-}Ҽ.o#|P? <O=?^ߛf/l䗘~f+,fEɝ&myKW;?Æ݀pdee'r(R@i>F%L2؊ͅ`&`7s(ϳ<+V1vȬ~ZGB]SR|PĐ[& *uB\vu`{ 2G;E7\ 9$:Y6) 䳬613(LisN3 iy~1X<ܲ~bh%ߟ`$*dU{Ě9hYR,%kIg#K ?tEW8ߩ޸$˴1ťT'_ [ٌDB%2D4Ó#"zPNFPŞ:P2ђ8=MQyt]~DzHeg@R:!j:ȯ'hzeʥTOkN%恬PΖP)b{,s[e7ٟ.\ӌ[^Yqe{E:Ug[gT;*L4 rOH9.f.ߧ* t`Kk&_Bl%53usjl$*jN 1Wz*K/T66B]QF wD~;8/9l"[XeOM(jřKuz +&jV%~y(d3wc̷{na,}q["*HK 3%Ɏ};Ry1f2󹼪}3ɪM7' ޺W (lBbi+aCѹB(;j̹|qsN.?_ +|"%tNKڲH۹ H89m)ʑnC:FWގ띮{[J!m%/5>U[t&vo8d_vϟV1[QKqyV^lS*~ ;3ىf<3xz.̈gpo?.š7olȤ˻blJh18 Hs]KcϷ‹! +[4"ȩ0+OLz1b}/DQc%Hǡo60^2$ƒi Ͳ)79#M%?z^_fgj[S?4xwdcMM3l /53bYg`*8#ӷ$LG9{+EA0UtJոc__(*%Bpw ^nWkRv}ĶzC'jex ϺN:l*ibm'_SDoe%Y +Cij.~ZoBƨ\SbT'HP3uh03==NFdrtP42D +"^(ho֖khᶘA{WiU 5ջKp *ݰ=a%\vFA9g^z1<6I9[Z!f?.L>spCA4b/cR.v~?,c'܎QaLKXnŪC)$,?"lMl8AI,Mw[?qR,]qg.~Dxȝy:oz÷1yCQ#Ͻ?*#nu{r!t;N +Q'86g:oa,/p >WV650Imʷ_P1Czjh#efGI$v DfK!\CT^cD;ׇg?2,A΍`, M=t|v11^ +v2R-9_eBa/R)5Nyld?4.V;vCY"e|>1&ݻL)hZ8WA86j ~܇DvD0'.[5+,8I6^*T¥2fS#ey|F:څwEbc|Փ=#z5^r½{BdJuel% 4M?҇e^ɵXץڡ [[pM[YG [d %"'F|vi{DF_W]ZhWGj0?J;̩̀W_U drm͢p"AfRo*AS)O; 48NU?ja9Ϭ{xC?`yr4wZB/28x(q]g-͒Q or)3|ީX2v}Ҿc um3zo),NY^Cے|4Ax'2+Sb]g}Ԉ]WO]K ~u:¢{c8@ oPN.bpzf5BDS3S! |¡/^G7ͷӾ0^O?- g1fffo ,(O\@Tɠ4IXllqYm"DKmDRâ`~(C x ŝ`҄"RY|?J^ëZXa>ɨìxj]/!5ȕٰ^ɟ{Ygh6-p?0Bxy X$\l D=%[Lw\(8 URp\\+5cuԳt LydxYk37 n ʳS2`K6BcֽA.E0hҼJK<;&gt<ᫌ.]8VԺ2fGݵh5jk۷26{ <Tu&``Y}[qrgtAC*?(\5ѱ0n<:iwa +.IφMֹ 5W~'}=Ncv tWӐ8Wy%]3v,d"%j]^îVR8Ž^܌AFtmJPXܻ QfWQn'j# |m9S[!3 ޽[ -@~a7^*Pe+oυ^},JF,x;]N)FdHXa0:E-ZMJψ[U+8Ӏ&{*!:ni3cx~y Ӌ=^ǷOvcˑð|Ni_dRR%uD$8]usxFQؠ0g='$lN># +BJr@e@/jZ0 jTB*5ju"Zoăe;ĕ*6-ׅQJBJLmLH:5*BiǣS+tjq) +B0NG⾥ND:¯m**@TgHUC=*ZjGU(*㑫Y!xXFrّ^Ńs^eU+U#W'D%*k<\zjlZwX+V)YUyУ]moYMjT 6QUP+ƛTY:s0hy`T=%u*һ>1 |8 ԗ`k_Gkm("B /R:%^vO|bɆ&JNK)ְ`c&nښ06\r(3gy#1|:1Y/V?cmmì6n^ݺ +k5ka~7Ӗ Cٛ0z=l\8ސb'ָ8oAQ}C(}bi5wU:Ra< 4&VayכLd& }y {gFݥ:SI2'nJpUD EwyQw7~T\9d>ދjJe.jlNOGCisdv?h+imM.h-0C هC LowiA;ubj`^Zyiqr`Ȉmީ>8x*)a^V`a&mXq=Êmz{xXD: gcg-b ZT~栅(j`Z(T +#ƨb MWb\Qm4ʃu`i9l Q#jzf0z<`$Im{ B648F|!7$֯pxv{$5mCn\njs$>}6Km Zbh\WlAq=g`wEw <~@jfƆ9l秗ngfI*FVE*Kv,aigoMʯ7흥HL/={~>?N] iDVdKB&"ۼ9h( j&5l}}.esʶXP#WsVϵ!LF7 +[|XKiQtS(l5(ӭk1zק]ȏ?t6XE4s0`Z_lr)A/t~ ?=?>Ȼ -gH8v;Y[(m ÝP _+kۋm_goY.ؽ[`b7$9`:S mp?Lܼ"ǃOmadf)5,GIˏ3G~߇Wc=?xA`lƎNaQ4u@.(5#.fOַDxz¢&/igP/C{6*6*YXE"nI%2׮%`\ƩX4bKQޔ=0]lŭZ*`l-[66g I[ w+5KU#(#GU;B:*^~ZYMصZRx[7 笑[78Eվ+{5?M%0Gݭ6[ܘaCo֬Uk^ľ轵^D )ܲapzoVΌ7e̫rL7$A=I)oVR啠4<0F;UqJw=F[3)3[zlPco͞ȴXmW="kdmzѪ3LϒuB0`j#vJvi + +9CRq܇ q" +\<ݲp=]M`n;$ sO'Zƈ_K1l)Xj]7 dtV[Mƺ^b,:84D**G+_ۊGOFXz*?U:}DWθ\qQģzbТKu>}&owŃi'fB7^;A/-JzdZDѭ`s$9L;e8g hзfw%zI=Aq1•^( JKZAyD6"C=!~6fj򯠁Ug(TWD<W[j̚FE->q5|T0(V|ݻ[(4! pxG-t`A׫{\&QRLQ,7u&ˉL=*iJn'DH 0fi)ʑ<* Q-Yŗhºk᩿.ڭK [C][㚔A."vHy 1vvdG9\˥,ރ]y|~8vU NzSU<0}(0.Vܕhi?H|* !Or@0TZ$`8F 31>lƾ߯^8~T\ ¸dB /ـcG6´/ـ=ꓲ0& "Ke?/VqQ]z°qbٗzHȃŲ|SyG}׳/@?jq'ѥ{ˡ>E$_`W\{?`+~߀c%j 7Wnr+y7 /`[vV|8ߋ<Ꮿ3t:J@tg3^Ꮱ; b^gg1q?3<f^u;hFr3i:BGb$ YL10!R|Г.~]bOj/w$iOLk'F(A n}>\!_8F;O GU搔 Jp=HK]r-;W~}__ wܾK-S%E&6؏M_QقQy㟿2b3*3MQ#jOQREܑD~wjM*Izk>6so5yV@F4oH2 > yiaijp+"GG&G sk)%j* ْ>Hjo)KҦUY똘t`_,I(l}1dY u.XCh_Q"}!Xk9|O;@W?ϟCL.4K^2<|+2{4&J X"V aCBJRQ1J +u[KS[sS0:cS3r#KryZT~[~,7&UQU$l)6 +0úP%bn/(cېƟ+oboCe<94tS.+&Ek96␈P75\65O3*RҖ#`3DJVhxeᐪQ1g[sh'UW;%Ml@^L$yY,CvJ!yjPtol  + +gi~Q׻ ;ht]p5L:NB)oE$L*q\T]U{W7V@=z6S5B;acƏ?c䵾HUMԠsF9oʥPnZt"=cO|9NË#$V6XD" /*2dZhRZ A8ѯ_i)k'pj*`/nxu|qYH*T&su +oa['hxa*Q>bP?8埊{ƚzf(qYӟAv-%y?eOWVd=P눽E>qRpvZʴVmrr=EU"UkgKB +z y)LwV~e۲Bk9~zB`Oaz([?>?.,K\o83$$#yʲ X=9"cx֞*g920!20^pB2*Y|G~F$oxf$?Baʛ;rOse -^4#2s>5Y=#yYX{~zFn MILh`n[aX{Gݭ~HzhVq8tԴJ2%Z]XM͍M@a4~enʍ0XTE-p9+>AfP Ƽl ICv˴IJ9T<,$hmvb,T+]k͆8}6%u4\("cjMQ;˹ߚ4/ܘR{$_ίHqL'TVږwTgS&l[ we\%Z$:ۧ##Vîm< J}K7dyF=2VB)mEZVRu#vt1|Q*)ƶQ)[ԒxAx$ai*鹝Ow)74Iifm .v*HSˡxq)䬠[$sFvo4UrNoXubq0Dۺ{ UFh z$R`ilWIe9 +*bǽzjRps3sa_Dе8X.@;B O>e<t5h NAHEg;&i}Z,ؓLb^qx\ÜC@(r(Vq?y~T~޴QXƘ n֎"b,67"Ix7uh0q%#v10I,E bi{m]_^7)R0ߕw60v{El X(ug2SF{L=9'qbuC,4W' +&11w|HOb')J)\3bWϚ]V_ekKA|~U$6˫FEGTgS rOPq<i_qbeEM7DM0WkUlڱe>V,H w2|>{յ5?љg.Z/E,>,EGFE/Ԟn8|mzG^K2XQv;$Pmk~Bc Js_\6m?L..&jz-2^#`@˧>'8-qG>9Am~P[}P[j+פ fj{Ãj=E,=AmhjqC}P|QA-:L66n{QHAmSb' *Z/hbWk NiU}A+׺(Yh*.n ň}JcډdcKA*zGF>rT3_>s%R:AdQye'-Qqm>@}̜ iX",pI +0)eJ91:o!1zW4]_!O'ҊOf}!)n>r +Ȋ&_NkdM̻- T$I@He;:oW@$ ~ba=&7Bd=SoamLBy>k[K pT +/"cB$OO?^-hqEt4~k2AO8wX8MkD*hHbk"(XEvC ;)w#(s7љɉH7Ooǁ(/x4ˀ+ R+lUʼnQ<.D1͗f+0Dlq~@4DUGQ8L)S.8S VN,\*Ev4 7-*^QE (]=qat)O +|<Z‰}/ZP#ٶ97`KG[bO,?RDA O}ca]c3*.O.ϢY*[eJvRإ7 olU ]Zޕ<4SO|: +aQQH}WoA3)&l2+n)kO7gbBۛ@ Ey(u|{cI'`r_C>(,Z CwE\EQ;4?7k(b5kgZr,mNQ${ 0,fDP)e@uDQII*c`X+^٥HS!%^UԲIo[Mڻ +b΢@K8* +{A+:WP#w޾BE9$n?EXn/b瞷 9`瓺b^oMi dDk𩇝۸Yޟ,Z;z|w%Y7lc9P}@`2u-}d>m*[ʃBDϊHsɮ#͙;SQ\4:9 `{&vpq#G D(;?[!g$.U5ilXStWT6mج#Z'Eܪ_KUO$w >a”K-UV,ZpVh+-vw G?jȾ=-웺qq9bᑨ +͇OӼؾ~ūE\M8]WCEzf{9TyS}ȱsލE<$h}jмI-gA4劉wM]ͦyz>4;7cijTdH>$_š1y|%ingmgKnk>tlw|ayQX;Tc7q GR aywˡ{ͻV94Ff'˿EЦyw]<_U6e/7Or޾y~.d%rCbJb8,r(eOzۏDh4qqDy ÆWB6B 9dAT-s{YljYtf=0VR0R#~J\!lBHfǂш(=JBV~|SA%%2zNo}ꮝcJW|F²ᩈjB'kKIeBpn!$GG]μb~Έ=&&}˟,h^m[ylvu}5Ypo|vuaR'}kg&uViE_C)F L~шG9E+TYKĽOWl PyG;&WV; pX78DxRڞξm~c@:tMm Fu@5T{zEבRS Vj -Dq̰Z|4eqTي`]8%,&q +VH툓ΦA{հH[ 3K*4Ī%v+:I[< ]~cp`R#ՐfYnq/mnq-C4Qf!=@ +8뤓ն_ISK֬q +wأ#kC&kueۤQ j#5,Z K_~`F,Ey4' E_"5w-C;Y6)c/IDk +oj;:6@O w,!NjDp/=9nw1әU$֘m3 %jUBlA=cwt佦zHɩhNdC0kEE>VGD& ?3&UBpILa}@VDL]@g.A5YIU7 ;wE+ K^}3&?ɨ~l_o3٠wb&栅?1$sTLsu.{ \4A73 +ˀl|bV-ȭX{5A v0:p W\E^ۍ +mT28R)Cr`&9_*d (]ij$D-I7kSf8JhgP\b=+.uU_o!٦ѹ]K75,A?'w*TcҺ4>39U"ܬ7.$ޜWMZL݋")#]c׎ޙР|7[m>;+Y[Tc#F)QpX\u&s1 -͐%ez.!$c0jdǎQ_LQL3$goLf$oKlVdI;E#t^D̚ϝ]!~ЁLu|#)MTY*Dn=DZ/C 1[xai&^ܭ%4m.SN_̞PX Y}М(ݩ3u'um4B$E0fA%3Z; A?k5%3AyDF,ׯ`dz2W4D +3ws_jG L6>V4MIYI4f=e{mY8xo}}41 r1v&3';֌gYH!)G:EUb d_|7C[kV[IKkR+P;)^Ip寘|+&\q&j\&r~ù]k]مI4 +1h+%הUm!?v5e.]!ژ]!îPdsî`)ؕJ8z%UW;UfWP +\YWē^XEf?I m3ɶWid~l&qXW2)BDmO|Vlͨv{1eoڛ3𤘌:׏Ӌ 7 kW+StDPiJ +9*JS,nɏQL[͡Trm|`TidB!+Wu{H; 2D+L2M|n`MW)CDEj}[kMw`CsݴrЩsJ7VU k1*y8u3eNl8sgrѱHP;z?uӫ^˄..z<+hfWggvrrήWz%s?+5gWY +w|-gWڝ]y2rfWg]q3BtW,fW۝]i2Ml]ix9J]%v̮﹝m;gWt9qήxU"De 皷wUWq/SWiٕ]iy7J+5rt1{W]arPs~%"-UPU&9*vίL|j̯L}5+ί +Ox)Wʈ-Wp9R'D W,MOMF} ~Y>VwT+ 2]lJs͙+;Ҳee + (xKXl1CXmDlSP5@-X }52at[`U #҂˨ Q*]v6 +QDV-C&L2ޅ)\n%Cg7\v|(2(%AZ( &'rOjc_2;AG.Rk "'YhBB/G$鞆)spJ`"*` Li#Iu@?٪z]%I0W ڗL}B)z|. wUuׄ/*s6Ki`4 -L+ 1-ݻ⟳ҾX<,7LIz.g`J F$hLM`$N#$Uݪ6<$iPZa"yDh}=ѡ<Gg@}.*"8,erd.c=), +%iM?c_)"X6a6L@&+.Yr=cfT S Zɑj-kjRS5u_%ːuiDPo%prR%7Y|5}@)ct'g&hԖZ-Qih;4j(^z_;<1 @ӆ'-J;| 尘$K1.yƖpcey8*sF<27l20%)0\\e(U2>IP]"CDZmI-^z2Ud`ꣻ.?kU<|r4!) Ua'$y8/"lhJOz:u 5s/KAߜKCݬpDh:zt1Dc:Lںz-nDŃ3\d0bC(ilMc==b곶բ;ַ&mD{dҼ@8PR H:!gÎAsVW=lԳvfڴ4q\ʼnxxR݋(y"::JA>WjؘCt@ǽa4 u>dε.w=^$$[1ctVǴY.B!6lKܒIr-+_ٔU4\Z5ߎOss1"ƨc +V Ϡ<X M5Eރ| CQB%V7{x ]@dz( _z*G9ҥ7P/zv u%qKAg_^~`w;M'ipf7.yt Dbv3I`z갛o[Ao0;5^zGXL?#W5m@)zd|4HŕuiKqƑqVˑqjk'n8;l,*訚I߻U#f:?`EHurUJ%'J9KCrCWGj !`2pꕖ^ϐ0v~z[MZKKF@&6;v:| xCI;1[vAF q`,ʿ IJ _HC'ōy +o;utt3Wol.h^dC SҍEy'߫v [J065(4vJٱ1LyKuG2xłM[pKm[d=*&]Q#h$Nuj}ǘŵϳejk$"]A:P7}FCK#e 5@ dB#~@VeF7oIR8 |* h=AƺY0`.X2|JU:o13}[Y'ز"'vV^:|/ka˦1rLXQ6ĉw% + ^T;#gUIڪ2Wnj jGuu $3lZ$V|1mݲ̈,Y#@ +}U1yc;`7L)/7GגB (\r!4>Iف.H^~w^jwc@_B}N PҔ'wqwYN+1`Ojo>7U_S3Z[HSӘPZ*6Ѯp^VM` d/5,b[P'Fr?Z}LCc,u L3f߸ZCI/L*㊛wCrҾMrm*&Gk}Sxu}ˍuPw tMٲcJr+ɭ^ | +nBp /I&oVp!dFTz=D"+ew+d4Rygf\@ZPEpX7U}Qۺo{֎72Mچx[4̦k(YvgK3K9ݙW{s+^ +mj3dw,AFsAd3 N1A&Vʼ)I#H 6#bKw(bh[ \h8q-3tbSKR)bi$j)-8YsbѾgH|i: +Pa]3nE( aAGTf +:V]+i( ^4Ş +E[%耘 #]t R݈/s:0n=tauJ6oU&>t4v)V9)n`A奝N`^wjg]wWw M" +ŔQ҅:չR k5|Vt\#j;]$tt)xɵ*+s KP+'nAGw#ݷ cc>Eqc/`o{`6&_y.X;H+wː@71YShV Fh׬up-tIYҜ54]0^%eHoZQɅ=ݧG^iG"`Q2ŵ Аh*18qW9]WjZiYuȤ]Ӏr]J҅"'L?|c)IڊcXD\$4gW ,_8_ۗ.]D@`mf`\kw|o'>>7}| \ 4rAsf .CH>C p!DBZQrz=3%sZ3+=Cà3.ߺtX%bʌrô4YKV/)h2(~\ebܢ⊄ dh~0|d{Sn[ ?N<2ザ\TY?85E\HjwdG>y ]JG@Zƍ(rQ -*34VW>fTc:3qp*k5 =f=C\m]_NZOkVsw b+l4L6e~ul`M :!,BQ쫂\hY85XnȢ6^(GB_L +z)śXIp_fz1 9VY{屔u_ 흩x5?M1} _k~}??@W C%wQsA2G[D9/l11c+-Vw9v?PV!P@jUbj9y@2 +C:CRRHR-*5N +V]e'=I FZ!I,.3{r7ˎ'+j]3$_],9rX֙;]rӜ͞b!.:YϐYJ7k +s?:fo,p4L*[ TJ9tN/J*;gי:~pC2kk6%gژSfe+׺S +,s_];5@ּ:?OAkd͎+D0W`5,n!+{S$qD_z< 7 ѭL_UHh$W{)ɿcw]<(Lte'3݋93;yav"M`P8ʻhdUf2X#g=Ia*L#u>XU3f)&81i}+,ïT.e?KsUK%ºgt?c6uѳLvηgY~N&!QQ:boHD  YΗ*z5fm>ĽhX!ڀcT`ѥno0[WݩU@sGӡ0z=S!k}W7~Ϫ]i>ZqcHZ43]>jz|CZHUCՑ?:or"$+\|2Y9-g}T`XlsED 8Jb uv$RS=>otXU`>҃_/I,er:&`nϺlU;!4Lr.jZ): +=96c~SL=vB~\.˘a4] zdfA^53PugBu#ה ȫ.:yU +W~֜dK^ޮA^J9>rgIة Gk^bgD/P6O +$+g6h\\A_ܻs( j8,Ւ]+H#fh6 jWfHů;#]N0h`Z_ +|0RaX/xƨ N + +a\Bt̡,ZQ(wD'8+-Uw-BGWaAn LHމN˪Oӝי]ƧB}n!dzGLK5N[ؓwGKEL_??R_GcjuC+6hk^Yeqto+[ӟ̠ M|-1tz.?'dZ"$i-ĺ:P-bFIG4QwDh.K;OCtuM}}ÐܤCSܤ9y>4$Z]e~8C!<)ي_n!? ]/0Drѫ5ģ5D;f +mS -ǫlqGadu!B~!BW4D2CWp7z]7ǬjK˭Uh:C0CN/gZzWXM3(!7B^ȝ^楊Wl2iЌݾ)aԙN0s0F{K!eZbr12z Iմ8A&+LZ!rBuZ!7)l~?kt7,0[v\v+jգيrkZ abzv$C qRfZDWũʼnHcqaNtWTH)LM j֬5ػNF*+f=19 Bcy񮤌6A>[2HjB=!jBU+~Br + iK,Ig}%$Q;>K^y d(pvX#Py!P vBչ[Qhct]?3S' +) @- +M~T uCĬ2RA fb !!o=x|뤐N!Og`@R)rƦ6M +@XA!uK!z(z~N!W87p<s̄nAH7UL@HgB˾YxtB+CB:g1D+! NV#Y^nB˼&t| g$OB $򤙏 qI E.>Ej"~.83¼Iy"Iwk + y,»FFnht #T2Fˆ#Z^a%q]ܥ!Cc, ME6EG9&kdž1qxקJ"p їƘp(g:t)@#xp4:M߭ϣw `Tvжuo)BMM 8FmiKN--'q z|Ubl2Y0Ĕ0svi8mZJ&hئ ǟA[Ǜ\:-ԭz?3q,PΎ/zR,\ΎtGQ,}s('WEUַ `6ɈsPDdfJMS~&t]l:ƊI$H} +JLl2a~2r F!nX]*xՎ)Z2?9x;hN~I\A&mX ¶;khM 9k2T4uߐ5w' ;'}Ԝ' + +wNLq-NSi(fsL[`'ܢ;1_pؒا4w1:Q/fF?ocߐP:5ߚj‚}>% S{HG>h!ʕo]C2]4 +:*j@QynA^~? \#O5իHnmu] 8f(@yz#YŏdNR|\B%"ncܻő;t&kL[6^%j +- +3au-, Q9b~R}; 9|Rx`9i15WR\ N酺hrTl%Z,ZBϼaU (?G#F +JuI hx@< 3r#gP +04S5!B.Pk>~5H%YKyءĞ$VHdy{=jb}JjɵYiD5"jtkRmŮ1$#<B>}ޓXZIC~8US浑튘-Fx;-sN"ҡk!iB_#4#LJzg\Y[|gܫM$.}&`$8&Bj6Nx$ X7%Ho-6!<TƺVyb%s}pnWdZ ozWy\ E].'/cmϳ9ȳ>fa% 0 ̟(fƅls,h|͂,(-9]KܵdI/BdvLOZ)(݅2~ã#lu@dϬ.>W%'*v0|Ŝڠ^c8l(hY +k]V!~rβ @Y@=ts(l,+#0OtӏOR{ `k:h?k;PM1ycJ=~ޏMA{?ަ`9^`z?G# L8?qY_k{~"]ߋRUaLwИ"-ZU_|I(JQ.3k?NWA ^`>0cڨދ0Sٛ62 +hrdf 9h],-Bm8g.DL&KQ,2uj3S>(w&L=Ŧ/N5"8n' J0 +` Y及n+!;QL ϶=Q6wKLMKTO aޤPd[(p;HV%}/4QfD<̦((*BQd*1aU\JAJI^Uf˚?ڬY?{DrZsqTAȕ'5QzUsJAi[W@ȗ jHG~Z3^ Q.1WQhpT,|]Ⱦ4fPٺeXv֖B~C=n +}3K7:ķgVGU-U%"q(E~/`KJj 谦1ѣzi*PؽreU C)X At݀"yVC`R 3_hz={z3bif ~@c%jG4v=;O:\DlgvΏsdYPGn11H^?>H@DzmmT2&Y .hKy0-(_V: %L V Ա3@M? QR0D.?l/,3[^^,2[ { ]{4ݎP`ZSi5&pk3x]NL_NcqW1j0}]DMi2",5Ӌ}͈f!~ ʏ~Y+B3ylv<YӮח=^ST8Ni\1# Xɚ#e{C-5I踓Z,fa#K!Lm2PtEG.ʏ Gz4nXq~襽ׇd!uܼ!Hp1hƃXg~(ga0 +m5>[M ץ].`- #JLD89.wgoE.ԷfԹX*I6X*9ku j QzY^$̵<}@gJsiDFRlEe[MD>iNLˇ`+f`KmLD9RfgHߩi9t59s.I?8h;dad)SeOL0 +kɞ¯3S}&J,SLO)'ה4t)#8"8 al\nWa 9av?lWI]1 ++HH:OEG ^Շ=!I~I0frt-Z0C}BCJ5HJi- $21t*Ye +0ŠXIRj؏v9j.|3ynQ9)%NF%I;:XOdq`1"% Z^%'Ǽ;gVQ(s#NKa;"X7=6A[90PJnq# +_@ 燣̩b1;_\o=T?K-"0{lE jE6q[ڞ4lG>StH˲d%K-/-e#?ӌPޞ (ag|i%U߃ Ѣ'#mgn80DńWml]-OŹB$侹Н;Z536i&KѩP.iHBV4K<z0m sQM[elh`i8b/15m4vK:f{ Z9k KIBG%/b]$JI&.d]i]w"gR R%算d\G[#;fD<"b"Gr+J#l&YМS3@)F}Wбl2 ߢpnBωn)kl>C- %@7},) p@|$,g<PL"[18%$]+y-49+B%ߚOR,,OM$N9fcG,(ͷECDP2 Y- yfg!J͏\ L_ًvAhyG1#xڼ5T2Fs?䈔%;uoEiP*/IV1Yp_G3Yf^@R6e7,ny-܄,|qK92<h%4W顑apMTDjb)|cB~ĕ|q%[}uחz;< RW6^Њ<#=m2Rqe:>`w;YN+*ZCKJdyht3}J=rO𺭭z(mӫU "Er3>r\GicfDv#s4c";:IzZAm2^.`eQ??:xfQEF_f͂|b(*v$ d^]1wzUxQtܬ\|͛UʥZKyL6[\eOŨ;$x{xaSZtZeF̳=0Xpw+Fe,&̌}#kȼvߕ^$5/k0 +ŷ}z. *,WJP\z$ 9dD5gsرޜ.#8;+o?..ϼ\ Y=ٲN}O,#NcyHRb12 Xq*XR#aEX4ݚd=I4j{L!$(崍NC:6QyɹخM~ +ZuB{8$ccvž\ͻ.|Nx"Ξ [ 'd-V1Cc4h J-dJA)49!f}{_H:+QY̆]dR/4qWXkBqLtUqAle'=ddQ(r[⌂O3H)]9nWG+IN$l^Of/|6â E#LࣺV5e\-gN;%S呻FU/ 3C|Ej-3v WGCjBu]Kn[OIG!)i,WDd/RiuyjnjLu[ͷIĺ^ {URX^\_rk dѼF#ŐyiR4 Q5NƗM1mYmbdڏӮ 1 +r0T˂AK& _/>Q8Ae/TXϧ&W\8vVĚjC[/jYYGytvUIMU;̨#V"Yh ɲ'˩\ߞvD׏dJ_Kv٩2)k\Is[ +WBq\R)8B&++9Or2VrLB8zиr}!~˫AA+/'yÒ~4 lW8(ZWjޕ$ P<;LD" j ɀO$UeW^ѽ_{us)8~SMLVt(wP|2!z>; 8[ UF`71mRL!1[.!gQQQX7six4vm?_K9olۤ4A7, +Њ Jd,rDߕ$?(;ͩq0+ֹ5//\2V鮖$VZAgݓA&OuэQ h^qu=x|~j z ('DR2Zƚ}aCqܳ9,bbÑrs/V,p <0@`KbpC0nBYѓM ֹf`ӹ C)Csl<+kC o.˝8 WJs͏YqU.5T:b?I R/&"ƻ |VLsVkt8n1%%> &q--x(%*R-O%d+ޟ{*HN"-VBmAU|+PB9HCN) V≩tA)^9&Y;ڌ`+8 :9nq$6I G&5$}]<8cS]YvI9Z1KvxmD~9 Xii#kFvX ZLCW+eU}s }RZs&[NVc逸x,~*njCOmZ層FSp̮uVe5g9-e +OM[GGR6!8rA|`CvXr5|`&=`zBu"hAnbXaWtA~CRV[xRAW,IQ1#m?UaWTR(j]YƮ%ޞZD!3k}LëҺkB?pXﭢ"ІR+e 3%I %ųv0Lp>`su:p r{wd)](/Gg6nSRdc- ߝ+[/pm=Ա2R^4,w@\*EYj%WO̖JƳ$0* X}-ea.CR:Pxld: s\,wL'xoGU}֯zJ1%kaq)a{o7A8Gb,3-U60i~JUjdQTU9'~܍tdBp@k\mS)0O)q۾8'K.HnUI~U\P>T=- 6L-q +PEC][[#&n4cS@<)r +$ot;}eyR ^ҧ(˲cG37 AG&zĦt)ʔe33~ybkd hv ckfrz~Ŝt%+޿T}3Zp[V&vtUaŔk8@x>zS̀0%+;פkq*ϬxiF\SgZaJoeUl + c?|^'^_yŻG  ʘ0g^ ?LD!:⧋4b v;w9f)ۈ6lѢamW_6amXL% +?[)Q.yP67mWhm_vm״2/h<ʃ.}2 ts:]ڐW;}(g4P8"mmXq+rCF8/iӿeKf6vHb=۫ iC66"iîtIA/iΜ҆m҆k9 qK̇2$H!뗴]4݅ [ҁ4lf.jhksjop]EdQxڮHkfmcYK .ʓ#no8v m@ ~^FpCp;' LzE'QV7.p#UnVn$g ! W ྊ](U'pVyu;l44%S?'LDOFY7z¼ 1K܈DĽS/qiAuN}8^9SK~ޖfc-p#-OPf\L?o^ze^MVMX6fCa8ǯ.e}Ka ^ jAlqWtuQ"9>[vA16_\zrb7Nh6-~*Ç.oF>o,)Ԩq4hvGë=.֫$]+0Ûp +_+|V1P)JS*O+-U)&Nn8ҙO ň?U#'"={"zQVϢ)r6)ir$  +}iJ/1]d+{t&w{pyq&9nTl  +d)iHOBcۓ9ߺ"R}$T+2-eޒ"f6^GlM7䱿YȾ2ٕs{/W3Ic7[ҘntsGS{x hy'k5C"ی*fʘ6U ap-+*nYb'{oę_jTL72tαʎpv{~׹͓T'@tr162lA[.x qokxCohz93)L-,4^-t%d + tYQdܟGEOD<@;9'f磜: +98&&D%0e뢙-2Z**4j&]893Sq2QxCޞդI W!]ԆH=H70j&ElڥI='<`~QT:i t\7uYcWR>G-uVu@CB +?D31G,^ +l &,@*Mي'yP2 +FOѨ5:yϐJE}-E{Uzk*] Sn܎ 6t0 A<=p(9EF^OXj)xkMJ5ydMԽז*;CcAyJh!P\EI6  +!YaU.$syX>Y^28﮼J2`JU*H0`g#KQ P+K4Aj٢.ɨNt/Fܐ8BXgtĚo[mFrmli ūRS~YBޚαy:C 纕iQf{կ)Ұ56~sd4c{/jT>B7EXCX [#) z~Ai̷#ɴ ℁,.<eË8w\Ғ$xN=?$tOauIHWf.]".2>hQD ⭞\Z8!ۉPF9\6\do)nt![3>T I +}'.E Z`x88Q^kWcF|P="pj`pKWJk/$T'BpbԚn"1KRO-fؒ.PLq@E}mEAR-7'pM! åR"DdlJZsU>Ū1^sMKQsȻKB>X"1iqQͽ"nGUlUn̫1_./dR<ZVMVyWGae0~ +yToYy5xUZՕsQZ1}\vCeFFQfg +&nt`nH|/=_i MoY|^ӊžf|Okڕ<]n˪1G*jRGw([?hYXns_}vB-]VˡQo7)0TwBӡ'.A 4Y0ܧk%ݵV+glGt+zȐ;1!3*F"!yˈ@ʔ9?- 5\Sؾ4fggOAaE G:w+EKP`aeH 0|ڬ_/eپ1sT&g rZkIdMd-^͎̅sFҗdǎAVC4-9IQ~AV!>t%j\BClc@p>Nht4u7jz _^m+|Ng1LEv-$(C_I{K_O)i ~ Yu-:#PWZS=6ZTox%LoC ?l~LTW,{X˶ĥm}r(1VH'R,kCA<ˀe܌NW$Zl)]k $~Ri@my#o8be/\)ʺb Re֏X~Hb9QO俠2.yr;!& ^02#fG('F:2_ax6\10`>dGbQɟ3@$@ s]$k +d~ɡ9'OL=~7T|~- Q)as NE0pꚚy()s̓h()l`A-,Q[t>2x>čPHۡ y? hH@N\].7Ft&'E^ $_ R_կ|p~x#ጒð+|feF ʼn<@ ~y}ZXU2$O8rR%,^i1?"KEc˘KveFsZ.PgA;Cm8:uceSI96liŸ@=KK+Uu[ +biqkW5@_fqΧn>,)~fSZaŲx嬲,brSZAD3*yaeV lJ(TfL \T[ +Uqؕ,5&cV>5z9޳/ehkk<'*S bRt^uԑ r`jgC.6?fJ1+܄PQqMs1U؞ f +SC$UljXEKoJ06x+.Y(T + +_8O6[Tc7ivIzViE3zw4z(@K"탛dO푂زc6q N1cP>7ƣZ[m1"VXE.@17zm}q8Hlc<yD>󉘓5RCozJ{ZĘQWq Jh3&W2mc^K1uLˎx@8/AQѢ񲭞,&OQJL35`M3j[ۣr]V0nOOisPSVo"PuQLE,,3MӄdjS(Z;oJ߄÷&!.EG7zvYdyS3ݼkmkŧ}swJeֶ Ǧ[Z]pi޵X64pVOtx]Aj{צSvm2hUA\n9= ?N/Piv^h;c=(_DF/p/~^pՁR1}h7;>!dErmس]/Ɋ7x7ozz@z +=`vTV4>4 #Ecf;NMΗdO/uv^uk&Y@$nj;bFNjc{d+^ؽdf/=kGM {M$7L.tn{^@Q[Z=DU~QzIg#.߻~{]=<{ӕy^@mc_-^~A~i"n=D_̅3N~O\:aI~yϸ13Ȁ~[]Ivqv^f#=^GNEr{]񸽮`Kpޅr{ TN$A]!3{^(p=FqcRt{xݮCQ `fTn\n95vb] w!3Ps 1z=}}x=>^Bg}^׻wz< `dh]*Gvș*&$:Zj·U Jt¤R-2eA9j-n؝p<rloBu :J_'tAso& 2iNn`ddBhu':lU>{a̍Z w٨cȳ]5^4r"X +6Db%o.G|N]zX};t>տ# +pؚ[|= 3̔mGDsx!¦6Nz +ϐO6XrK5oYO*zIޞ:culqs3T~N'lj[ѓckI4@xE ~H]AP;on&o-o.E\q_3}Duq-cq + ϲP\ +D 5%xO`P5?3N3*q?{o̷$B~e ô?2](3PQyH'%<쟓ʑ,9? hy&!CrtCU%9? +x{濠)qφwEy0Q]G +ggc#,k86S749G1yӮ$!pVОs ]0#TjG8k/u&i*N0iD%Z}R +OZdES݌D9!գ"Z9 5՞ɽ1|0+1ȢY͖5;Q}Oք J}\csrߋf1s\R6I?IP-].+I0g$05Q$% PrY/7mo,QP~xRd >D$lkxk=L*BWӂ!j.MQIe^*I=biE%m:褓,f/^hiKf퀚9#/7-!N4;X0 `Oʤ/흍wD4f G ƾyzIPM$_vvkp:G%t7Wtz}fۛ9[V&Pl()S!(vr#k3UL#@)bx#B;9u graj%RaHhKꪡ̩0սq&k Mj`Jd4jm\ݾM]#ʕupb) QLl]eN7CHWM}"*bf Z7ꏖl)@|DC?I12˕S$NtT]^cY[6ĬQwh:]M`K3]KMsmYz/KK v-rO{6TY&P-UĐ c/\r KhFidzSw@k_j'@I6ӡi6o<4>eLyim2yTNRépLK\b,+M'Zn(&eF,\a)ewkc8J{_SyObџc" #Wԯqu;Cjjn@zAPn"|Db^_|0g.w\) JlNPX ̒dK`a(xV'\I!+<᪢l< +1 +G֦Mҗï wQ\U,|Ygy~4Ʀఠڗ%=fͅX#UL&R0+39k'kJ!G$wK4]x5}ɤk $EI -\jdNdk~Kx_µ'nC I@miC#@;Y̅Q5]@\#2#m[@Z)\M-%n9$s hZ>t1}2tlA ҄hw?hʠrFRa&ǭ$!o& +E1I#7eM} ҼMVy$'GVqaukY `_( YIWVYIRVSVnpd兖nYIŚ rױ$q%PK2%,YaITna)?naI#,ReɼjGY2,UZʒGYyRbmeI0|僩,U(Ӎk;-Z,ԞH^’>JWX0seIdieT<6 + Q<$|H".`j%Jx˟>_32Gv`N;駩OX#ݪ}=w oq*_!AC&Djd`WWNźг 9d qxHpkuV 7| +t]Uɻ(i \Crz!İe\MJi-lŠxx݂Ύ 'o\|:x.ac\=Kx2w}L'~C'}M vXgQVs +r%X|:F'4m6l/rS!6͜::cs:? V4UFR8ͦoĕ+elCy.#Q %)i Tu +y,VR]UYfWN@î̒I1H⩹pT4hMA1")3;:GŢA"ku-D@SF#I:'Ѭ Ni_t9!ӹS6+;FLJի!ܯֲoRsw Y4#f[L#hbVHZݘ;پC9szߨ2&5{`CYO(o62mV'c>+Ϫ(sxo~U5d[U=EATS~Wޥ]lR扢1!OBGH6Xȕl^ VУ]S +d,=(>ﻑVzf6kP~=Fɕ }Ԗ +=Cg̴eyAoMNZ=ԥ& {!N%2Dcعܸi'f +~CʋY*a: }u,Sim|K.CO4HHZP-ȅ:ig$A ֔!ՑڃrMS{X$Յb[a ieEϖNl-npĥNFW4K3}- mF[:fч䌰ZuQIHS?S^+OKO6q?ۛ+"ޅj8XuR$y|&)],J!,B19Y.A @( +{pljؐ .x.'^䫋{MSFEcnvOѴ?zj77 WD#x+Ev}b9/gA3uDC6E6#eE%Rq0(mG@cJVsdK~2FW6Ws+h-G2%Wy+o8poup;V^p:n)]C+A;ŀ\܉Ag Xt0SĴA-\4FVk[9Ss{?,KmcDυUYh詷ݘeC^֟ۤ 4!.,R3"BG“zw#G\M-d%w9TH[~0U":-8oQ 1~VWyx]R,VL9H]& d> g\0ieʸ}ieGIT?U-R:*(>=;y4DOfѽ †>+4;K/x7l(sϒYavU:NU䎹SsMxrGelX_k\ٚfk69Bl8׳SM*j׀@?5wY`t7WxGkΝ +֗NG\`if /E1Cxf+Ém9ep؉J@q-ˎ^RPd?1ss5~}=#QYf\TFoN?oL ;H=ξn )7:]C_o3dt:I„ L%s¶.)'VؑfttTqTml59b4NjR^B5F$,I*T8(R\G? (⠚}E(IpJ2ϱ{wBxʓ+Mu{$#JשD/DDϑ`. +؟P~Ŏ5Gs Ew"rL}@$fu͡|a*[̸ H/o#'9vxT_$_//GO3j<*1 [ +9`Sl p('S*#ac2>f='"\_RGTOD7':""SoDoD7?տտ58n77zQP鸟7P_ tl@u ?)b6AI@J/`P (~Bd r~9k<*7A`K8 NbKS庩m<*sep5ytd`ϐn. +S x4mpc[pdoOvtNYqdY[ʏH*Q`\ZxdN6KFPa*}MsK.qx;HMòVb SRBK]!O_JrDӚPw*AV܏;*r삺G垃@{L螏@cjOFS&V8Dq=򾏼 62\}3`< W}pچ.*掕uKnd#?ސ|uD2Z 3$a]wEt; ͂uݜc=])vzoE{^vIۑċ/vׇxׇ`Fi?ۃ0o<488k[y s9e0o\münb.9w9fv-x.kuq57@0oTbyye2/"ap&aky8$Ɓ:'9YX}7n.3۴uM!/yi1@:~% my7;tO4M#c +EScڳ/=LKuț6oط_q²lʃ:PUzFmiל*C59f%BJ/$k 5'WyWͺEOa:%qؼ Cn.wΑjȨ wk&:-hDj-"mY;L[E _2-T~IZ|vXQqKz >3 yqS~ ^Mn,0ȽHZ:aH(K,9;nWmp^? ^͋Ҝ`/6l@(2¦!fӸA|=)wЕ)%[,~mU9&4{8Ǐ1a&hg\/]E2Z(k5T Y̽y (C攵CJeSqVKLX2n4I@ jI䄁W 8I <Gbf/,e*z#t>TjzicKgc^!Lw}N YvBwt,O}PoL?aX&GRjyQ1S0tm]{>Aszv,@7>^Bf/\h~f?_9Y6!^AT7?N,3p^Ubez.8;" '4+ӵ^$AHo02 lg2R'/B!ga2T'-^@]xs\2Htv>Dr@8:*cKW?:i6Xw!O+HΗ[AA3flPޜ^fstْ1ˬc}0 F5EDg6,=؜d`>;!KBۘ¢;AAF~3~1!2/5CI[k`eMBO":לN?uuYL 3  8tRqjuC[yq '_V3 sC +/+C NM_QB*lu^WSl9YI!_P!_]. Ppj0வL3RѼpg~TH _ aQ"!(ݭZDM\,dԴ)=ˑ$qN)BAmܽn O HQ>3HMyFvTD\A1DODfYdWf_c.GcU^ ^*{J&w?)*9`x=7Fr7&C69{PNԙ4FaSvj$d\P^_WS~oSn6< 'DZRa|_3YRCgr~o Wね怒_|- +7/NWcFRAyzA|7jh2o:y,jn,\$lBMyNYyvwGncx ?QំGä HjBfF>ɰl,g9zrd?VtyOr ( 5[zĆK8*ICy^v L Tzm_ʢzm0?.-1Z3cOVZ@0iw,jÓBģZ', ."B ֵ$q%$'^+@z_BNngO5> Bum Na +6^ypNJ dN]w it3nW>71ev"dt/GWE('fU4]G2_♝0O,ܱ;٘vVOZ"_wHz6$Q5K?(wٴnk63DB eZmaB4u\jwǐU6?NoU:)lS;}:ٌs>Z4Vs{Ǥc]? k"v#i@gi׀Z*L) a8ImX?L9FǷN`cZ +FTy<ބ++;sΎiФۜveOvtO4"1nܻv7AhHpC>XH'QblNkJ/&B,zZL<(z5 ''o_2ҕx=ak]: j +l19O4Xg8|00r*QgqW,`!}[DȊ"Pgz#Iy~xSI!l h9 Ԗ2wo'd95,J N٬{#:dp>פXdLNԮF60U8Is]Y{PU['&E0RL)$$bSRgimL8.]C^΅xjs˚;w{ՄGv6>Hfyc-9CZ]\OXl,nS%)K|ϑy=MRnQQIo ,P%?j@j:'X粍GI&TӋ$ +KNƍ5S֌yͨy 4?rͨy:QkDͧ8Ft3&dK5 =7YÞgD=kD1RTx ߧB-7RyŐs0鐨~7Nnʗخ$9r#xWHC`3t9H?ȗ@V5VJՃnz2f˫)"X/\nocsNtUrm^svLw): ,U/ߧ%|3C$Ls)BR!4tjYV +rj?"hJZDb``Kۻ%ą ecr -xiqBNχ:R[jPkqKW Iv麟՛ε w/'pb>_<ַ.Z'nNQUO}$$;wy1[Gl{q†gxmvAQ婈pێ +W[ok1g!)`i.J+Cf!P#m5NҢwvDXverj ohv$;5CyV=&y^d>v-b+a~-oIp%Fo:bbXdE7}Z~)"L+Y2ċsX>OSBimdE/^[Ozjʹk^eM>~nP0L Xp?&pkOWqG5D-~4r = n5]Hx]Ck<cKTQH~}G{Bw=v-1*My]#mq39k 6w /[ VM}.ll^RCvΑ?u]d +pz)݅y!&m2Xybfu2Kk9sWH6 }0ˎT5 ƾvL E-!O5[ޱ6Oh),gf%8ڴg o=]b{;[/~ÕG +ǻT5:){(U}s{!/aҺSԨpm +e'IVı-Ke3!vvttn<(<)kHrTm:e*l܋ȪE|6j`; fkiXYYmul߅d\R&9TdOk\$%8,dhRؗGd*w +"~o#D\i*>ϴ1Vڊ3I|NS J/E`)mK yhtDh܄fׁ -1^S P]JcitZ5mWMO'sw"[(ʤqv7SJZ5/{0{ y^`Zyn9)L2GL8xlSVV템L9sN<)vcddW n?6SR1_\QE=R_'9jF1w\l4O",0 ʴ5X +hU!"lQeD GkT+l ]]f~C(f08  #Rq餴4:>,ū!կp`4S7Cga?h;q!N* tvk4#y]5ż ?\ɻiEd.k'\V,f8]5PWNt G[Itc5jy3u>#y|hYO;MaDow/MjTcGUz\d; +r+<̇Z 9 v4*g]V52ET yxTdZeɪp&zSQj*(MLvx 6"+tf:.'ZDͧRG2g:˦S a:ST!ߦSFN]D:ޓN{.=Nc`oYK^|jl·+cH GaĨ~OMKi65(j)F<\ВJ Q3)XSDjG!.OsdPC= 3C< +;Y 1\͞h3JKx"&qRo"$HP5kߗ4m|1GSV|#W\J ?+mV&;9B6UI \5Qe `rLE0-u{Ơ|NW"BMŰOha g95g=jes + C HC%ma:@ۿUVh*lE"Sc?sC/]XLz8}-.]m-n9 ?"C_!q;Gč",7IA ,dhH,bmo "oZӹ^[A8 3fr;<4I DbJ@ ?bYc5TKN(OH֊&!Y6(4R2GҮc[P +$AJ|uAizLE1kkZNf$ nȑ)&TvLl02GE홏'mm$I(eҺd5n ܉BLجt1ñ <`7^4 Vw#g n9bv]#Js`;/XZ]$(ԕ֐8!:"S { kvRv9U&泛h3TCwVA8P0ne&Xk]wҟdu`8NyQ}Wv_J̖Qٮ,Rl uXONғ-|^** ؖUTgºrg~f/|n pR]7|I _pɰl&8%`(0PUǽ,3Q~u[jKʄE]0*DQcJdZK~,3Ѽb2; "";0N$+fm(H^-o(WtrI/{EYbQjxF~y>G8,,Bmt升\9L˕\@oPZM\wBt@ũt̫R;o#è|aO]8 չ)yսHd.Ũ9]5EprӧvKO?M٢kzG5qU+nKUI,boa9O#/Uygsۙc]L4c<"[wȣ/{.K,7Z1<TQ9ԇ5>DlzQ:c黦 +/1WJ. gJSPb59ocPUNl3Dj99Η_gyoțhWaErS٫sdtQˇ0;T$Z'ch.o| hrR&HD5oq?_G}&l}HoH3S D>$~Hn`آ/G.lI׼u'vdcy5 joralts?d vi; V܋{tWqm0jV";5{teڂ`f=wD%A<~KicviJw@KCyw^r"Pw-=!h4guw8=>&5[/ y!Qwn^uQw7N>KQ|jGY@WRVot;=>l!q:IqՖ8LPwH|K㨻" +uGF~Ug06Qwqsczz +t.5t3v\;ov ۟QjZ9{虯Ŕ4i7rPl_S0}*hKQU>折5&-M>vF,mo>2yC+JC-CE [L^_y*MA悿iBXhwܐ9vip>du)n-b}F` I͇CKsȯ;_ayErSejwiaZ߻+#*SUjZr@#5V졾C_ӵప?<>03)V7&un2tתf/Yc^%qh`SӉZLGh]cyC˓LNXc\9aKr/ wdp_հ=Lc]PiM[oT{>BaSYnZv _nxܽ?vؿ>Ւl]9nPW~aGGɶ;@?L8&\6a y06:?F&dk~K|A㇑a4~3uEW.?y[abn֎#Ƨd01qi1.;-GgS+*Ga? 'sc[ ]%Y:l+L?AeIy1Gpto$)iľ1 d#3q\8`** .YDûjdatj|]4f]0_-?yDZ}wKX6}<*já|{FDH-aK-ˆCsZ:- F#I-ԣr$g@늙 +d8чxꄙLVKld~os?a%3ݕawݛȂ +w{ת7߼PA#>Lƞ&6;)NôbP^E<ny k#ٌhF%8}<2̾9He`BE&"<,[5J4>%<O +͗ +;LCgo3Ro:Ylz 6ƴK%x@SYF, |]糱ZN۪-ѦH.fs<7mBӦ$'m̉W~4q!ꪼ7yS75Gxw6M7Gqo#oNM[}'qc?&9M4SӽS;N(s>|GoA /u7s7qr&$yN#CFѿW de.;o!%**>S]xnbSg @KE-]OQdUq^8!l +j +\wiTQNb!XU0 H*]5]V^f_ق6t:֎QEoG`,Q+=b(ҀîWV^%E @x~;FyMD"jKA*JYWÝD.p]*VZc5/MѻsXG97v1Nڵ12BFvhy]P7I= OqĻ]NU"Nmuv;얝Fb߂'b`"0gn.WB;x  ,8%h4Et,DjA}K멐z- vX.lUgY=4&]-O[* "ǎƥjы~W+&borRҶ֑}KulC9v2qm(|;"~iy6Ijgg +)$-pCyru*(>8A "E]q{ۜ@uBFSv"W -8x~!Lڢ-^o0xzE?CJs/ȃ$rާ^ahPWJb/%x2lW07w*KVSggx<:³kȪAcC$D]WÆVؑ4V0` nƒ/ k\fdK}TUQuR>4BHB*ޅfh[dJ% XCCyT.c DkBkV f15Wbg9p ":dڣڤG/yu[鈭mȑ7$aMlk8*ŒnLۍRv^4 Uu6pa^۬|J=nMMUE] 4s\T1 Vͷ#{vw9d7UÌ_&C/? @1깽EuɆEOiD3"BibV7|!"zg؝ ',Y>B}5 l," . +mX" 䣼s8x͈j X oNzZ6dKnBךieM8TWm͍-go;HD$KNx 2k0!.ObJ;~ȣȟx(^\PzӯrFi?c!iphgBiӆi' +'+y-EÞ䍻=42 {by,«q}s4z鳊TArߧ' +ІLmbdRfLW5 +qBrTœhv™CH-h^xDb;eCr!XQrZͳEY܇I ֆ܏鄞д}>C -34dѽHHUŲ^ևURtm _n*yTeP!1xxN4)8&UJ!}M>i4&`gŋ&UŃ?.c+]RHPEd7Z9`l}=:{X_ + o])х*(_3LiܺtO,\\eb tOdͦB0s&hcqIZ!ym_:Dh?ͦ2UP86+3U*9 cNFS@xF7wyj؂UTRC'bVLA*#\\Z6_֋ԛTneNV.n#ӑp@HpSא%Bg\KōKȑ{wRTQVբIgo4^@|s9S`{qpy_ut ]Ɛ SH/R*ng!PVF"YKWk?eWj%̓MI -fKɊqL 2R/ }5l:guoh=/҆|w}=t/@zi?)O\_JzIҁbz 'X p~|\BJK*wTiM8SJ3ҝNѫp8Դd%nRڃඔt.G~1^mbg\]3Ds0K՗%/rGDa/Ŕχ"o +O%lBCR XY+ utqRK6"غRYՆ*=:.p~6_K#tQR_zɢ"vQ >]lS\E + 9{kh:АTI'J0A8DxKOu= +x%0_O5Hɷm (.[f +@6q+>ק{'n3Bo)Ag\P1ckh&<z{0ϰ#v$a>cEbke6:1"J'Bxe#7VZ0OPx3iբZdVST.u{/o0`tm^qK [<uhec6.jU߆Uexhu5|M58ucP]>m +RXGy kdFM0r|"Fa (m` +4۾tI@$ء,^.*I5IM"Q1҅-*ʰL*aqEܼU+ʣI%2RdGPhF>y;Y]@07(ߚ yQ@y&v``C-'^# 9Nt4 `^3Fׅ`vDZCKvhTģzV*{NI k9ϥnvнPGyxn W[w ;OaL@DD ԣT +qKPx{m<:KvU=wٙh9|lF{я>^O +d`EPDߴ!8# A/'J{ӌagg`J סkZA:BL;eUy ;@GT;Y?_4D6q^ȔwYZ\3Rn'p #mV)}x1 {| ws:7h N<7dzWt,;pǒÁ),(q\8uEBޥBRgvovo DjE.r@Sn6|Ps1qe9?9 .8#@@cZHa<:1Lzi[v䞫E? #1Im,qg +g5<4iݩ[P]ZE#W| Mh"RaW`MBa^(ҋ>Z$ޢŕY=|IouPj +節^Tc?*U&/E1)D r|@MS'NT/l1Z #v0hZ(L[V@cɌh,ZjK1bN TWCJ)o75bqv.c}aޤu@yMLh$ϔ.CuP*w[=,.g i+#A@Չᑝ)֢:A̠)¡4@ r TU'Ι*BcJͩش4pzJ[JEv/L;2Y&6??Nv#1.Νb9Ln葸~o؎2^m\H[rG?0Ai@^lqFYi&bQV_sMt87P]F{MBV w3au^upV ACΤSt@1) +MEG1lx-hNz0Z addhXɆ3Dͺl89›l]̻ꔾab.#HJXIҿߛqvx/9I]sv/'Zȉ HNpR aŀ^eCgF#2 Bb(_ȾË8'G.bZ\?_!E +Rb.%0?[g(BF];Ԕ?[Rb2aGo z%1Jǵȵtxb5`=z;Ӌ;Z-6j8h Ǿ:Ԣv'$8:FӢ Q oqCFhanބIOIo.lMMLuC +YQɀ!>p |2‰ǔܦhPBCM{3AW-彇nQmF`Tmtċ_u'lb42é[u yUDC ]Ǻ/* ABE/IcP&_X\ۄ CAT҂ +:0 7<9Fxuq}c|mb 6\ 0DQ!-QV"tfF{xT kuQoeUMa(Yysmt[f,gn0(cՓ]'U4u7c[9>ƖgU ya_"~ 1ykF#oC" 5..*{md_'/al|$h'%l?Ƞ2oqyi2ӀCA= LJl:]|T9]rq=uRݎy\njGh]FcmUC8lo+t_y;`\w𠋱߾QӠR H%xuo ~cD8aUOTJ]m51=8<2Kz 'rZZCi16Ŷ\.X6E'yɅC;ӷW?$]HFJ:O*;&_ᷳIS]6 5)/ +&ͼh7>/U"t@rcW,4ZHX +oc3%"}h>ͷ{ϘE2( 3#~30ϻyH{M d +*Š#3Rqj#tqwe\4c8L"bkH|o$ oxlgx ]2GU" ^Y{RAgݴ0]YSxe4 H.U75^%lOQcᥬ}z;J?]yކcuICA͢U+t5!Jَxjh +o9_WS3gaBX0f?C׎˃VDKʢb."%KRqUwm0.%ç]d)J+rᏭTA/jo.HkmrȀz6.zBlڽbǽjg8?guYHB[Y -75DhؕnŞY3eBa{ .XBΊ!grN<<%V"þ6̅?/8ĂzzXXxᄬ ?xaΚcSbj"tc"%2[$ކOzZ'z#A-S20L.׈0؀ND;!YP,M}{-W"|f"nߊmA{@5e.a\wEDY]! w7{0e.o`U!u0Ś׃:?^4yk9+yyLk _ȻboZ>$+[i"ȻXDbXiKx|f0ݺwšUAkc"0`<=÷Y8cr ~, ~G4 #> G@U`y*p(wG:zq+q- Hej0^h]Y<ݶW] +V:ϳVݲB4Ѵ<EXK5W6U bQ1I1UԞ0sН.VTZG[\OiMb[R)c_0o1 HLjYİQ~ 2/It<8!|a(G?T>>1; 8ǡM4 @iXM A0\jnmBĤ1U2 gaX}ײTV>%ɵy=7Mda orMs.ST=nB}8Xq+Nb|,sD&*Z{(u] ||I]61LS'0ibQzYPxBdZC,mW"X6~YЙ:~VX_oRT^<&1**uB8[iaԏA~-~ƑqP'.߲͸CR8_oBItxzRFP̹6dvzxUsSK,tI}~Ę[M:_j x wMv Nz0.L5΃] 21ԡ3Ѫ~[Xe]Fʓ +O,jA@{11"4fRYDj %]tS[桨ɽcJ$-p{sAMXT0O Bֳ|$)y4IWOViײאqTͶKReRx*O |[-m,L5Eyn~=v=,M_3kͻZL|.?^} +L?|_?H:ؕ =i& +\Lq Ub&ϛ6ןv TW׿3ʸ/vk~$rC/e(<]ۤC9OjZaǮ0^b*I}7ɽŢ&sE#a\]4Sec^hC +/t(D~L4`bBv%ʬ Nz=,93_0c)ӪLO|DO)W^AX .y'ښ!Ͳ+!ClhsOz=݉n{;V nE5{$46i[H0-6n@0HC1i{hgabRe%CWAK4Έc!A4nC$ۡX`{f..M;;ޖbtjcwOʼn⊗ǐ<́"KC~@t;.oAwx;,o/?Yd1Ŧ%oU!خ9xWyA2="U I7ʋaP̝/-:{gi/vm_vE`"^q8;.Bφh"~ w*v."w>Qniyl.ztZF]rg\U"w>!OOh kp]IܞAn9'ER7VmT˫xʊdQ3XA%BeTBM(qwXöՙ&p,My[*&NֽH웇x)݊.^l,PMn8n*ljRc3a P&E+ɪfV +5s $}Mr9?Y|К%>>8*dh3HcTA`) EDF59^ZXآMj.p{8;^RJ 6ن0Ae rI7ZqUZ#bN< wO8x')z;amhG W ×grGpRk`̳'Y^}?sMgEVy7 ;֤i(Q zH+$InpBp=")jCq{Mn"?yMOMiGY*{^VFlp o*0sb{_?U.Hdt*Z(,gޒC(fF=/o^3CxqUUPzoCE&U<|';5fX3NUGj}M&ٚNCPJRC521,䮤4UD +\|ܴ Skh%{\;J&Cxxox|cEr,3&f`x9D| ҡ< J=0՝_N*Ԯs4&f74EQyN 6S2 Ңy Ħ"*Rhj>"jǧdvpRZ6 pm'.k11ضcJGU(F_#R\n&>nL4 a)*AnjG6[k#ӝoݎF +>6Onh;߈ + +1'`Cln4d,_(搈B \Cѕ`|̟zuԩru_1W85O&As&Mo1r;B3΀H4* -5*ƼPU0gQ9 z8sq +`}Y +N}8+ui<5F*?\(|$r#eݵȬܼDnzּfvnCA0=_sB;Q|Ky8 <#˄th抲Fg>Da؉f7|Nƌ13|+gdpGKGDǗ]~c`_jR9rrrWfs{cMuQ@ EQ iڏ4ڈCK3I.aZ ,W?ĸ,C^E: $3B`%gn80j%zkO$%]q%,"3X*L,);Vߊd1JQg/(_$+I@>6f+L)a]'$qk)?:&S8i\|*Ȓx!8qe;fftd  VӕsyP|iH[d[dR` VE S{4ݩȭaz9E\a5 91$JJpxk{VSGȕ,z\aGUkUYӇ#vWHs.S9ZxpquTC1jvX; գ#pˣmMG':H wDRa[ScH|R[OJ>^Cq5B񀹮9EwSQ~tpջbHY1f= GNO |>X'Lhid <%"e:\X4iA:W9t?UTtGzՎ[9\M-F˦-go}Gr. '={EeWH~ +c9TXzZc2殀~$v/HkXChsIJ Hڔe&E?m(<*$㥯{R'@#[йVv7/z[!W[մI)@KA^J^^e^df~i#oD jma8(tj2m*GMe3L(fA[ +oi!Tɺܪ5o+BTG\)5"Ud@֙ [ZzGn]VR\r\uXyʌQgձ!ѭTm51M~zb4='C;Ĺ²㳙O2)@r3^AX]ڼ8w=w6'oS,z=5qUdy$m`eik!#[&R8,VTz` +Ǽ PuRصk7u<~R25Q`n%΁KW_jx ܍\O!k.gbr'Ltvϋ NH 1>pCnM[' mtIhtGIJ7Z.cjh:}of"[`jH}93ͬkpՊ*`u0]eJQ eDLʟ{oi]!ސ!XG8JiX:u٥W3jjQ5i!s=B/ 7 +W-T8H +y +U[+~@#JpIQ_` 0+t8]T't(y:2\uQ$X}ÍqQpwcTf0:|~7W껙}M dh zwPT9 \[!y?IDeqhz!E?TlRuGt7 iۮ~+Q~L#6n(]?h8y\~ t?H +.qx?QsL?`S Ϛv.\_kn8̇yBgID{j >Y?M5"'0Noh'*Q~ӐT_̩ԬFYi2NiL!`=]p% + +fC#G ngiX{6.CvΘp.oJە|z8ݳ0-wGDDq8+VR 8uY"/LNnmau&kP|kq1LW.'. >,اc}{vjnE< +X1{?QH)TR|LUp1ʷưӾ)C7%=_dŹDEa $]wW +P >8'ĤO͌RƩqQ Q[OhV +I56޸t@|\pErY"i)+F@RRcov0F"j"?;cmi _ZƷ4*_!Zqv^~K l*_2Ibŕ8ϕt]'ڸ!# >[B}cU&!0 `ʏ#h*Ok,>R>Ƚm}9C@Ca)d9KMd0TƝ`v +G] ɡzl'0ߛQ `0`杩}Cȴ01\!dHd1ײb1GP$< fZ?j~j;^>0]UJh91L=2 md54 +~Ȑ><$LkhwcB f VGډf;u105Uh1C W¾%3P%aMk.8e(C׻ 4]held.D ? ;axeL[!;*N2İE`d6HNBRbM´癶lao@RL[gںDa w.[x9lb;l4{KV[4dZwӇY V:CR~d LJC{h&H@4 bVDa"ԜE苇fPˇ[|8|0?I+8!mq#ޤBK1}zOl=!![=(ɥ2|$CZ_ 418Ҥ˄8MB5ʄ'w n8PZg \p1D: fkaB$aΡA;XtQVaH&n\$>:ݵє6L4+HCb`mh39ʤ*8W-WCq<{h͊,tVzTdE_ ݗ*>l'Oa poص4NmŷMU1𶊺F ƃ60,Qh ¢bЍ +s&&sĝ +t$MFӋ#X9qaP%7)Р!TuuѬFz!Vqϙr (DR|PZ6^'HYa0^ONKE@Pp*E9\)'k_0(}oNmk̇8KQo+YitAQvST"6VPk+.JUUH_f@CGІ*B3NW:uv R^_\f0oe'ЍůF;.+i[%_{lyBc5ꃞ.55[¶2 ʧ +35Ja2XԤqh^Q1!)b`ˍN3Ӆ9[XHq11¤FŲo(_)cDܗE B/\uK9^Pm[alD/UW|La21xzjId{/03(W}V$Dh:*2-guˀ4sP&Gl+y1;7Ja`膡FVv77E\s<2q W8ma~ ne gR׹%2a L=-UG.kjž^S^q+=Cԕ9L-8J5\%E:UcoYċxMDJ-ZeMDS>֥X5BޅdjSi0.D¤`ػV}Yrq%벏 +e@k +)ȭ`u=v{M[!oKD#.愒#. &/HFE žǽ<}q)!a(Z2xѷͣ9z4Y]쎞}Gϐ/ACn-hA3myR:Fx#܂/h:QGw)v=+!SNgyTMՖ,j(|ڣ )j\Q[-xCԖቡQ-#j:oZQSjTMy9tMеsv|q Y&9j@q*诽X,whKy?WhW>ckm3I;5&uˌCQD!p_ Oy Sh⌁8T,-)-!FM3u|* M^x0FI6짮ŘVPr+ѡ| us/}<*^hŚzƃUE^lX<VUOxC T. f 9l $B1#XNWzb=z'̵Uai|++Vgᡪ=W۸ۡi|c_ ltXn\>fiIN@poe]3֕ Xel_9t`luСdz?)-uT^u<ŧű)_Zuϖ]3{MҳZz9]$xV79!yؚ`9݊'I܏\sDԟ3XX}_LxѯVq3=f<>Կf?[HKu1`C9?_J^\E~ +ADJ[P?qAcPPb˿Šʇ Kf~BeB443kn6 $\Z7]ĿIcEVy}-_}qaT.%kc&Հ~WGJTXK6o\:K#H u̼Av- 0T%E+hRIN3<ԒC'@Rg|oOU_ HOtŻ + P_48|Anev/{>ZiL1u1=Qs/w|g,FQ k)[DU& hbe/翑Ags!ƃUL[U@s2i cc`ٟ%Odkzf%sCjr=mW"K֪gK +"{8I$:U]糮ߞKG0Rְ ͑]#Q+ JM.5ﻎ3ۦ0 %JNlb7@SW Dj2֕>W痫.Si?.Zx\TY.9F13]FT;kz*6q/l}]EQ5J/?[M1[`pdmJS~$3|層r5?;a?T k,2ibK)MgF0^U7T)dI0-Gٜ,hj0ձ, J ]yƾ}鋁Lyo{m;bl^|N=l܈YUhv +|e[8Š@$ ":bXUmmEg\9 fvXO,nEC +*R:;7kX+r٥ ȭpXԾh'hPLg1+.?]4CʹA 4*/6SılMwk^/y#9CY쯶#vIfS2Y"ݽ0t/v,k6!I]<6>f _ *c.؛ڗu" wH\ӬȳB4֪(>S3 9~SRsR/0d*ޘ]_dy֐ ^R9%RY\j",:Ylơ.{*Wl:ݎ٭(`6MyN% C v.&9 OAWWRbR?{4CòE[\ ^O qqGOmMEeGc}㯧Xy0=C{~REDX> gߠ(,ϩ, +UԿ{}b9V-#OŒpyMb%&Xa+q_V yԣ}_7m᠐ 5ZYun6~z!6=t|A1/`؇s% L1@V1{FY 1~'/7@AiMqȏXK,"7x.@G7b$c K4C齮x$[F(oa:+bhK1ASYD'g :|c<u L3h$ g} (plc]?'/|`;@Mg[o? o1axٛQNJcmC~DytLj?e-Ѝ=]eWjNVH$Svts#%e䍹| ('2u޻ zsた<G̀{D:tdzJK91%a'q_)UrodO1V˺+zUw "B>p?`\Fiz4FJ>܎$ly31UXO9oUXZ}bNK 7ӗ žUANxNWCE1<x7u.DPVN,Q `YTV7k0_KIvtr vd +sL?Yht"2>'t\uǯ:1O^\Rz{2 +_;jO/;<'\Y\7ɀd }2_ȝ᝗GqNS‘[*E+M%e.?O,c]e64}-N|tMMwbȿH|6l'GYZIx/9\d^/ZfCeјҤbnhgZfՆHlyo: lrM·@r)Q!_X_}'Vx3(E7ǃQոzmGfwG!`icuvߪ_U&oĹ$1S0-\ZX3Ѯ[*F`%&W꼐䍔9 n>'p]~|1t=7ItRFd_2"'FSbSRF켿+v굞 !I=u`lxVNCycJAL{a|/F- JDleE4)4b*1vA[xp!aN(ɔl[Hd ًsdw8z\ܕ0q5&?z%x5D㫨XP 9E3M#W Rjkr\.אk<ݜ_30WךSekNiؘ:<^UU +%Tsnhu3 *,L\e9su8&Wun=[!iA 2R:*j6(4s!?T8q!p,F:H@>./jɒka;| '7[Q dywr)&>sRRl€™\Seǭ8M&DY^8efFcBF^4g e&zlwy27\(JpO9leKny. +@^53sHU+5 +# +C5R|qЪ-G~q+WۧEQ?G7 p&oAC_G'm@m]k> 9.dn~9in9drYmʠpJJ7_ɏX6Nɵ9^o@~wrI.:6j4f)9˯  ތ__@:>JHWvqH P _6aX99w_l!{Ͽ1ƨ)ScTDjƨd1%5*SCޜzjbޯ(z5FAZq`:1 ]aj (_a’2r~La$0CCKQ9\Q-䊌hD)1SaTe* ;Zǫ0*8DUDRbxGbX}%%WBJ w]ƺWbX+1#1 ɼÊ`WcYjy55WdxŒ+2i8 :!E21E^WdHr5F 0 q"<]* 3P%Pb4C8]!թ1f*".s!7\L-7u|r`#:l|hgZ&ʉp X8.fY\[$KHЩus}cؾ3v#Nihp 2ߘ9Z" hu>=ݞ毺=a よ +9D:‹!Hc@udR4(z)f 1YL2?3MQj]Q`M%UV&C]DYt +Z0+s𡁝 zbp3ZmƯjr;}>>kJ +wY IN;1:!"!pYA z +EOOTc7R'[lr 5e^y\)teeip ӌOb{(A޻Ԥ* cPBwDc 2_g"  &thɺzH@soUtāuŧ!ꤋok.v)]PP/G.8G 6i[c➱ +fZjxd5,+ws]H ؕyhuaB:26HY |i +S^J?c%V$ų9pg/)˒ RhjlTZ>Pa+S|<0Z+X$Pցf~%9 bwGߴޠ/]%v8pS y8FP]_t 2m~(%g/ywꁼm4Fx$2ӥF淭pC-FlLqs0vv *HU"1p?~UJcx04Or~|Tmz ^8ۉ^ãFxOk>n]n*sPs܈{JĿy+Y vKY, +tpg}q[>]yT{3rtO/{ аn(P>ۤ dqGΎvor Z͐wZ<5 Fd$69[@Z7RsVKO<;@yGD Ud%dH>>0\N$=S G m$'ElXYdԊTz:ڐ}f515>\}"(G}3Q]u}ow`ЏW+{jn<]}   ձILi9iH+ +3^u9kso| āRٲ+` ы$Wi*-HGR<ھoB> m /DV=k tvhb֊ԸS\=r˴>9FwGWW7"7Ϋ$:t +ĵQ X)0sY@>Q]\7(3?S0 )9S٧RPaJvJUbR`V/iXR5T[-C/DJּh +40 4/:A<#?>~A#J*T k_/LE qrAm}NPU%:n] P0X//f¦UDzsv:~9-`TOSH}"¦Re.{ է(aC=TmfrgS*:M]uwH;uh& l4;d|7|1 'oȑ#nqm-^Jj[wG5.¨yJtCZ>/˯tk St=OEHjs{+ʡ&#iRIp29kv:;`ɕ^`i.#~x۵̾K,DxIlR&T} i'Ab/~c8 Urb?do=CR%꡸*Œf(f +yY6&oNcuc>/z *}NZCUR/ 2 Ua8a{ +V]#l|n&ʶX$-ma+)Kdcs\kx4o|}aUÿ;zDnr~~fzDcL +\etl\zD  |RL@0 x'ougDQU4bŽu=Ĝ!HV7 <ƂP«xpc}jK T@f!XUK2-<#* ۲!gi1-ɱT2ưJCPJšu߁52y9Kޞѭ6k.y/~5ZqhP=s&GQ 2oY +Ga֖+ D>:"[QxnAPbVQ{9xy$zLd7IQ8rO*]RҨV(eM!Yw,a_}o>ﺲ@)r'}¿o +3 +@\dT9ގ0,^oAIs󭬊ϔb#5T ?;.S蘍BSACf'ly8Ȏ-Hlc;ҙTVAZ8@f 6}1QwRgcT0 O|o8٥kc{Nշ}u;Ӥ[sv fQ|$ƑMQ ~} ʃ"pZy@ +I_#zi8x\3ɚ0Z}׺:/WVÚY YcݕS'ŵz}vu$tyG5C/| BER`|f9+nӮ72a*F%9 GohBns܀*lEXꍋWÉ+pdLv$t 4w ]jtghwMۛ66M;0N^ef#] MB`tm[IjF{dq Zy?3G=;޷kw&L73AB  EpHbN +B#sdWRK|s!&EUjuB zpKO[fKsk.~ -s[:tHU|-"oZ{Bh0ֿveK쓧[r}MIR)+JJ6V 6)7THc=UPNxAN 7yA/о1qc-:p퍭c]a85)D!MFl=k &WΞhl5;^Gݽ=4jF`Vг+fpkH4%mSf77ptjLC ^:Wyv4%Փ)'Kf>l: GSƪ޸di E[]ď6҈,U*" j6ksCEyގ +8Q:rv ݪUQU|\ZQB .QD0TQC\45Lqy&@-a) oZw+GXLA|"R'E;ORfJZv64~^-WNT }IɮE0G#pxKru`.q,;_s9Ӂ ~-2Mљ"H÷2HDUWyYzS!TߡCe:T{1xyyt 9umyCrb.(̡(sOi"v&ۅVSkJ*hRz2yaHVpvpHVZn3椦uswT 1J9K%cIYkj_?m鄖\)s+Ӏe:Fʼ&rG2 0ֺ: Jl=u: *|mtp*oVvt%_*6 ~?(}[MJѮS!WqTy+*}Lmm9soNzz4=UiRܐIٖXZʎɵ{ihH労>v'bX(E:{D:sa,Y?}m),W]> u^d;nB]%_p8y2TjzZz!pjhԂ"!8/ЂLv+IpIp#O-OrrZhPx8ZBbk@sܡy4 #RCԷ]}yfabڸ3 `xmN>JԎ3މHtoVO,ZaxEgpxd^F-ٺOz;0ޕY7 , r!z~LʁE :dϾ5U5ge +X OCS|=,F:x_"߄mMT,?*-ހO-g_0['69ϵ};f4[T̖(F\'FoG<{.I$k u~oB@gS:Vv&`6G|2t]%I8ޯk#Y.%Wd"@-. J,BO;"2D_ʳ_xw l1 0!/d&\VD+81dï"rLI+J%:wXoBq +ScnA08ŧ0.y҉~;N+]︡Yp7xqh>>f@;ƫˎ0Z}iӷPW6U"FP8:tHoh#7%Oyg)>gZ={GE'NV&vKl7xlNK>uMAO9=thS)F0р1WZΨ,)51ƙ]D4y(.]UhmћxܖƇqu_u;1tY9~r@1Y>5{L;`tG0GDx%kwI*Ţbck< \O +1wg0w U \}HgtS2Abu'y .A~ą~[D1+m5/1wߙҦS3m՟PuJ"hvS!m͛taV6n +!Sxdtd1UfyY-7"\r&f29jS;] Omh&N-M.n:׶g[G A#u=ؑ4%abG i)daR(hV߼SߞC3K/Q/STUU%uGl^}%kw<,楊&b//L]|#q1_!዆*GE @OKzMw0S.vTv c0p*'tv c8R|zQ +c|K#L).W](|~ c\=>:K}:<+Leӏueb8-OI"C)a5)g" +V_cuf +<ƍkNv' +Ps>}<[E聒V/R=?v::K@^OCuΒ g7ȇ랩GS B&bH]mC'f!4=*?TrqA=(_M:"^u6c>FKg4t} ys|]Fw8*e޲hv1Usnb05D0dv|"l?>ܵAD-lV)Fp;q(FK.\YVz1Ώ4?AspfZ|GĶ*aUwP=ϛ}'9>jB+jk2ɱ#>OBt>Q^؋/(+a 'Q>MF_+"Péwl?!y4r-ԭb0̅5 7n1[&k۾9劓p ({Rv;T os/+V[7XJh9bDˑ--j}D J_=hAKRWYWEpAg{|Ks}å"\ v|7_s14D_4rMXb Q7oЀ(U4!As>e 748Y:q>Q:]Q6y&laaqNMU*_ +DfNHpKx+2gP^T@sͽykT(tHM*[>@+6T%(z4!x=Q?KPRy|J=B1]+HbT(LTbRK"x.Ry T.ܶ; +8P ڦ(g8PME媴m,{Nig̞O*S4n#dv{CǴW.|eEbJP2O$K4mOuٟd T+mGL=Kh+޷G.[ELlȘ Vf#%m=qWp-c$WMFn {DD/A"Ji.zW!.vuq)xLjrlAXW*XeԫRhI\D{cxCEn( +bڇ"+D8Α˕N߬+.GD4dM &3h^YP$Yy>Pğ@ց>lb5Nh^(rOcvA +ڭ8-?薩_iDQL{)#m9+BQzхXLjt mѳaG(E[>wP>w+iSW 2㻩-8υqG1P 1 +\ݕ'H|UoBg(%Й +bk9й:p{!^>ʕ"3L3d8k1P/@2on_ĖiarZ.SCϯ?ҏҏywDvJ(!z__ׯ/0:f|J~Jj]}!UQ$~^@g/xRWR#iQ#앪f +(J5HH,w%<%FtB~~W4RhܵӉ,(-w,_J̙\1r'1WĘ / Fg 7>J.Ėʽe^YM#3gM oL; E:M2 ~Ԕp8ҁ Y+erQQTyKߝo^f ia"[oI{  +٬I\aB1z$y| b=C]&$2ӝ^GNE;_TkdrL%mr\EGߵdߪUYtF\988a! +E2 7F:W B{Ρe>}[urϚh :?> "C,C}7JtV.-%YéR!82OZT$ZGy[9uoeݲ?yvb`[(]>u$Irð Op`d-Zg. d"7'Kc#C&"ecji"z$\8wfB7)* qց* +?=V%…$wW,yER}VZ+ÖmcB!M5rq+h O_HCp=K{lxB; F̭V)"(m 8>37:G1x"cdѻ^# +v1iRP%#`qőJRiѼ+Wm+M, tz9sG`.:s7}c{ :l%+Ŝ9fc "%:1bܱVX0`FH;+)ɡkɼJgD.6ePaiXK',uRկ(-C7IWQJqBA!J5帩tT[i'1ZER`*o`|UT[޾sM4J~N=SbC dd QUxA/I5[{&ilx֑MǤDLm;߹HP8d/rJa[CCKYtZtg(l22؅s8K52ȆUY7fD>[՗|êM%b[%L⣊*<0Ds->APfQ4"(Cb9/ńH.))fbfL8 腔Jl؜92Eu< +b] "DLԐJ|Ѹ|Km+4T _CΑF,FJb@xDf|%%p(!0-٢v%WSȱ+"͙-}>hGڍ|uTw42h+HgH^6z1^(Z^֒ռdJIt޺=O1WF@QBN mA M;fR`#1+ +eVˑjVw!ɑ\Ct:Mbz`+q\bʹXl[ :<,sBpD˶+tA%\b1ڿ(IGԤ)׊Y碐U~g˶bue1Dg"5kBTU$IdRDlS+JۃKLy,k+y9f9HŒKtE +mݴ>eزmIU&~*7ȊLi^[Q]"Xi/jJWW }X])u^/g3F* DD']j?SW+)4%-L~'Nwlǝ06Pj..{˔Ee>6REn8^MG6di=_qwڰ: @  i`x}[;>[%1=zcY$O+[uѺnH%n~u +bB8x:|0+CrFYp&k\wWLSs78M1'Mr`yvOrSIj;TTiDiMJqfzq i\ʅ& ,,d"ٶ$ ɠdwEiKs>`wcGe +UNY,E0vP.Sѯwsnr;OG;#xᴉ?`R70𫩨d;qDqNeda9I$=;p ,E 4rg)BG d,p%Iy)M(ΡUG(7UH,P[9G O%,1TƎ6xَ(f)YWj 9f5-O)U`Bί{d^GdSD׏W2Aag|bˍH;5o^H +6$Wdeue"&MtLrHZ̸2q-if?1|F,q-|'QK@sN:5.IO3sSιW*ęY%1dnh27BZXi>v zVQ_䝖ލtM}V]mp#'Dfu`vLKacB, rگkjL*X9rAnIȐL]2AL!KN^R Ӛ|(|+%ȴsx;P_C<"<L4?y- SpxCp,s*2 ݦd|P%C/=d~/Opx:'ulS_!EM07G9'$  +e *G{ƳJON5"zBO7zϋh=Vt=ugaweP{ Z=PVz;sMVCC0;i6%L!ة|'t{$'\^` D2  D@\6`AO8?h5&;ֽjC=?<9(z#L'Ϡ`g%N.(z^.۶r'$ǨUi>jx X:-Z$h[S>S{/nNc "YkV z969qxg7GpUQ`ll|`h^31=vb,X܏RrNWMDj:FB..<_[ګkf`ۊᛀl4ɶlڊ=҅#^"/1ʭ3e ێB%1/M[M1煮#`0n>_~ DȆ!uL[t\=7 jqx ԋBD>h\eJ^fwdhz/:t^Z[w @qۧ9ϟ@_$/E'߿YE!rPuUv$iM1e7i 8W&+v위'O/ܮ7Wےm.,¡}pB{q X}/@>;^V ]7aTЋ]6r },B/n:urFW#s{R>$wdzV{ {{ -m7I֏i3sz/%&^exA +~[hXqg-K_/Ývj;ȫ.Z̰pLfD<"淲Qi3RgI.EA^ik(?B\_ݽ;?m,t"t,eJnr9w}co\#'2^I//|:|SK\g,5 {hfd3")ׄ^r K^070$~9Sm@_UCp 0Ra#< +rl"R2E𣬌"Aw qCeYC^WhONFpVnzL5 U(lX lA0블'If`g"<h 51^Ώ7a[w7a7R%܂-Xc{ZVs,iۘIMvQۍ-;9KV$Y#1 ap+D 0[m3fn<2{A\>/3ɮ>?6j9k&0 -v I_OHJnLK_uݤ /So-b'Z-aiw9$A2 ( 3T#%mjqy)5d.H?iꤖj#,q<셔eGCK[* dFc!3G +vX[%OUaX`++(V¤eux05HJs-EyU ?FRK*KJ9^35|O0n޶JY†,)[n zq[.bY%6,)5q}lSn57xu7ա_{ZjmFQQZx۱?v0ZIo+."~~kFT V =˛i 6)}]ԸZI:zY!>Rwh6 +o%"+E +\/*\I#goNvܟ>yV#}=,ylޕ/겣DcqN%Fe]~ɱcKjrpG-o3X8ms~GU V9˷/ؽsٍr2l + B7,BH/f+*Cb2Zcmaqmp<[ZBRNE2(*[mwdcXgPYrٟ3y.QtN'lZ'QFpR*ֵyyyw5 X)u;hFrώr{P+ +'0Rn[!O1)h=oE/"jxvOF@N43%8g~x'v1u$%TJ +[ȋ&<%q֑<'B' =SaM%= (BuWj`Q"@*Ч9-ZBӦZT\G,ޘJA?4W(3릓S\c@+[hC:ۡsMBW +Vq;%!`ë!$ع}|23p @uAKE8[Y;xsîĊ!կ +߲3E-g[]}ɿg[{~ոq#[,/S:K}:V*[``9Q1,jہQĩ-5o1(Y%}XޥUT@cD03؈k m`A!9= l@ϖy\/~SS@ݬ/e572 E =Xؠм ]3LAHKK,W6iمŻg]C.4Q0to Ǩu!suFvTzY cB{|cQ{{?O:S߼g>=.j䴱m]HEуEئdm쁲/n1{L ;h{1$67-ⴱj>/I}w~n lḃ~cR*oUfrY #$hd}CIcDžq"UM67,.|*[}5\\#.R8X"߫`wBڰiG'ha)>G,&@Yl}3|Y|+?=[!՛s +XXl~l&B!P`:~WعU5CR9׸@r:)T/L&MZQҤǒ8) jmyӃNg$8ʒiSgI49c2#J.w>9Yf8U}ϊԿx.J%vGYfG>w~*})db9H$),ƢZ>;NMz p*vfSwl4_5߆h]:<# +ŚqV͎@3IS'En {)GDŏ% i6O?oy(eΓ%k|'ƸKx<}{ _=G\ <.i Jtx1rI}|<pk(ٮ:r)/s,3UE_bԏ,̵=abZ2&byzLy7+ѳNr<﫟zgdt4O{zf:*ѣx%A`vΉ4AU>31 e`ه&_^Qd0|F!Q➱)V;RwGw~&ӽP)zhxEAxPd[s`i ?Y9.S1qXDS*yL` >MKI[f 8H{>Jsl)ucA%îUuf_ٞ*Ν& k)8,kcb%L||%?oDttfv<=È#f3C |bZ"!fd8b˴S$ lcu?S 5q};7`&j“ϣ3 +I$*2PΨlJ Bj '8U w[fRqƆgZ5~3<'hN%JBq(;0JvQ:j]W%MꩴkmE6%OY]62TPr\NptpLP,-Zjˑ*:rջuY{Ķ% zTv/Lx^-~DΉ(Ezw6%גѨYF3ov\=Z`2J,#eiᆴhM>,ud>z=#Iz@uffU@*߹2kvRRLį6_?۶-Ҳ H|I-ǮE Օ@Zq6BkpӺ@e}‘,)fQ 9=j slgӱӌ|n$/QB|_Jk{\^zlag>3<0RE2[=Zj[b77q $3BjRKl01(Hv-ޔKԊYFIp,hrZF;^JnCLH.\+-驾N#Op2e䛉ja3{3]>+19zdDv l+ۧig+# rr lK6ˮKc+:ƭmQk=eHٞɔ:j]LLvBu߹+Fh4X[r$\)Y# +lpH#RdVy#/գMI4(,WWP^V-or&[.ay{N93|Av|̌2<3yGWg $n_r橣5~w&\_X5wV< "KW Pyv _3!kX=lO.Kye檗VAS%L5iօlZbχq̴qh=s'&>Μ=f?(A==8ےF,? +QLjƖe:Ic;T/iGS8ng{cCj6t쮬y.(}La" +'&1Ί5>{WNr Bk'Wr8"aXS ~,eje`< J׆ }> fНҝBϳ;7Pu@CZ)&~c\TqPUkqcV@gq!MG@j7 Z )*2T֮Ɂ8fpHVûxVW% ;FWlkSkoj2HX3d*Z?쓺lO4̡5jߚxX~))q5(u䵩D:izN1S+N/%V8-oWS)-6]@ji5n*a^l=*p+z{s~Y!Ks9QH+M(uZۮy\ +ln0(Di❘+1@4]c]U'\az,g\JlD-SaHhcwg$B񠒭$AV&J>)qL"@Ls&,V DhQdGD;SYhؒuqZ|HN7?C#!*ƍvd!M310D6KZ/SYΪjƄ+k)# &a|+8kט$!RWt77)qS˔ \ חe +[2Dq !~ _SjrD G8J̲x1VtMJ<0_#$5H[D͘0޷l55Nxvi^g*g4js1AGJ0h +oܘ~Q1yEo bgYf|&鏯adDwAGrb +j"𥫖[(pUU␹B,O{tx'BZel;_.I4Nu* +І#Ò;m˞Vej,_7ŌaA]Y +R=[Ӷ\qRkv6}c/Ian]w}Qp^m **8ֶ-i:W bK" ۈPD{aM0U'm:śXWHr-_5W*`B6 M@T9&5%.m ^EpwW[(~ +E5f@,h0Oms10qw +xQD-K C+7MlO;+ok'4e5 $뉪ۈ_Z7k9"ŷrkt=ɛ$*>W~JzМ3beP8atWA ՜}0 蕕25(Oc븺sfT^'V_Ov_Pf"P .8lun938nśUO-eRW>tǰlΨ;a[Li<@\=٢-QCY65COqX +KnR+:`"Q[En{Z *&C6H*dmm=ff&8˥?Χ?FOǐqtG&c@-`:ǘ/: gNS4;D@ڝsy2K).<5?buV 6eh"ݠc!g?7vuVb }+E @-iu\bD*, TUu*xP1\- it ߂*2|;Xmg{\ǧF:DၞeZ Yy/ 7H{/k>Qe˼U*C1rR?gȟ` ̸[0qzP?)Sw98  P1=Zcb|q~K3{=\{aZg8?/KW苩QlW<J@"`!*A oYGw{KNh^)6jRc$R>eE"}FV.28sA\V1''QI\uU?9 :O3;눾GagRjطj2r(Б"ngs>MXep>O3\1 wQ0dr>`E?^'K!T#\ZtV[;m2s;/v]qc8ׄ_l\JmYGp䜯iؠ+rQ|U2_n)NYy՞._6,?sV 4Opv#4b0?&v1"YƒLB>Պy{m +ݛ:iH4n])c|ƢcnôYlLފ2uO l0 qh"@e_,Vv7x 2C==10uu߀:U-Sz6q.m~lz +1\3=+ C'^BXKpg>P +*p\  #f5@Ɛ Fq9[`#KTPdvdĞDE, ?2~A8^[ :<_Oƕ6Pi%ح^vbΠ;`-=!% &4*%o=gU0਑}&X< +ԣLs[S~^x[HAXf uL]̲O\*Z-͇61ytwsCUӠvCݺQzGA$ZGtV ձm|p5-Аjo4/<:u}9k^.hV AHZqU)B+:خR`@$E@O(2W v=h? + _i,U$I@!jTa{;TK\ qHr:֓Çw#qlߓiAܩ5Vzs 0WD5=ꈘGɭ #B)8j+W·I{;#rhD"3BťK:`VK"auoQ.T+t회˸bX|5Vq0Z=YYY/ux-g4Ujj}ܳF#d&Rйmu*G[sGs~63 kV~QvMB̉jƸ@KGR\3\}gsTP{@ z1tpo^8U9\4YfmTDKT%#ij\{([0)p@+6@ l]5$X5  XՓ` ̈(mPdXn'"v"t%"thC6̹W[`ڒ`_ p # 6u -,[ P_q_{r(ΝůgIJj"!JO~S_/P_\ +HJ6حd~aJ~5.6_:_."U8~/mKz JzMpB+˯ד^%WTCQΗ_Kp7;Ua,JiX,&`sj,<J.xx΂:tTK [Ӌ5gKIhD G+c˲JkT̔]yM02UqDkdS6[//@ۿg2 Љʨ/n$_`~ym# 0k:(=<<"d~E*AD qUiCl&ަBvi9'M8 pPM&D`b|vY%dL9$']_:9s+B"PmLݡu#JB;7$֫ ;d1M 3b,m7a;NyNat6uQtAu6YKG2 sNr@Q]Bd@c;ߚص٬|W3S&j'!#~wƌxZFs&o-Iwq;e"`۪N-Zex jr HVx,/ ɪo̸0zR="B$$zbV6&b "q(ȴP,OMh7bJd6j#< =y} 0_x+pP`mCeu*6d1;_4ͯ`' Hy +Y[Ơ:je4-̋EQd8ܞN3~t  w wN:#bíp+q-zS3:C$ +G.V&튭sT\#Rg lI,v*.kGz)̯4 (پѮ `4-㣖豑b"'FdC4 92 ;H58KȪ>A헁fx3I#;MBDHv{da pxLCne +L8,"K޿#Fzط/^񊌉:C>{bc?8ӏ}c}EwQFNhJIspcÀTjX6:> +4*=!H$W0] pݚFIDAȲrcPh<=qNPϦn:v{ U#-yAZti{X VLg +]%?g:h|a~>Q~e,URU2pЙ 4T7 ƳMwf,qHF&@q״8!a UdpD]Zt`aSkq*+^N-SsN|i2[c0j2(pvxt߬ /PwZ[ N벹4}4`oh]st.,uޝg+ӐHک}uÕU1"/Lq NZٍ<9Һѧ1%{4ΣyUWQ1;_92oWe>X}Rs11ō]tV%^uƇCwu=AiOey<p̺'{s1Eπk)\ +Us4,zͿuJd' ױLWFq˰5-N7W23 ng45SHwL&8*a^^*\*}>9歍~nZ$cZADxCBeف=Q$6z6Z&p4HݞQf{=kרOG7$w1Ud\qŁBTȈ@})K%+QcM5G(&ZMae?{ORȷÔd-nl㍺J1YG1`>L &oW.؆x1,gAhD&_K8!yFumSL>Ȁ㱰ckbNo{OlSY٭=)&@OJC&P 0j +Ren(,~E`F^@T +Py&Z|r +6~/U z^ p_7Isp,$= t&bn; 7}%BSKCߢ?V:n)qqqr[T]}"UQ~HM0p SZwQ&] +Pf4q՞&OS: blRJC/EOkJ-_O-ĪiYUC].fFnz֣-^ Qv851_Z;Ŭ(0k^a53a||h:+4~5 qjM׍|QkM<~58>.݊SX(TRTk"dGOvX5ڑ;n:YݚؽQR܆qPDĹ6-gu%ej|aSM1ieASv1O#?7WP\-E7([{@}*$ u^㼷%n~V*mDluL 3mDC*q!Vu6Dl ." "rgsDyq%pC D|= b>iDS("R2yUS45ŃlzBD(1o2WB7BO1{؈G K-cjqM"BQʃ0/ވvy݈ i|:\ADn ZZN_-?(e!]ƓbcӼs|͡"OFQ5vCZ^_fK}$ XP!o}>k?EQT{ t +( I%\ `9)M +to)G|ݧ(bU=K$ ] +ZP$#eCx~(vH7h&v֭-Ƭ#YCQWŪQb]۳Yy&UN8TO&X>U2g$HKX|5*M5!nP{gS׷{9UH,@{^CK[&(ۻp=R"wmHlu}+wy2CM,"V)N-MAtK92v2ךSi!H{#C7#I7X~O1BBs7#]M6qepVMa,b2\jKELmOUNU=NpaYj**UOqp~= qHHE_ 9r UT1'|tkOFS~+kYؙ[:(ءȌG먈|>YyG16b8K +ɵP@> +\j%:,2Xc%/+=`U :>ȝHVcvUoi/x?Ϳ5]u)m."_--zR$Erz`~яP~eD1K N^ + ?TcJoFSrgWz v:7W;ik߳vBN3 8-RSiFdPږ(s:7X(sOق?b8x f]fBoQt. ͑<4VOVe:"#{.t0ui(P@JchXSs+$u/&&='NaS=FҪ(j[&o6a{B/9eU,~mPkӼ.j05Ʃ3-i8!#Rwz[ˣw[#B`fNv#4,x'1Vz%]{cW=(#auۃgZM + -QZɋfXϏtb+=ʹَܬB9cᅘ;+j +6K V>+x؋Uv+}s@ g=C8f}npf$*bU7¬a`v3ӤH}dE +,0jFWP Po \#]n }+ gՎjlW4W<\f±B5=9T>QjK ` +2)@*KuLL9SQ33Fyjhu.r$3PW$rjؒ!qqW9 -d~/%s˲ ̍ O2UjH,ȨG@Kf_lt:iDW._H1(Z&:n`G>8jhڻ}V˼|2G|bY-Y ؋ǣJj򨲋^*:T 4M#l u !P8UL~,ùЁ·+8;̕++X ;GdJ2g8Z'4WWBrR/WD݇+ JG8\ 8 +daY`|]c*Z,g,W]zB,ԏ"8d٬.tR)ADdd@aD"˖ +g{[2 +BF$Qvhh:coeK7e;G[T(.{oB@抮XzG; +1?&rob[(Mde 1xczxUi EoLdDK~KW~M CBQ^[~2]&Y<.B8XGcIM)9~.xT.\;y(m>o) a> +mQjPbK# UKxobo/?D {/}层NgYl]->+MB |?9ÃP?}2Jxl.N2 il~Me> )Pxl uFŐ)YJٲ2-' +80ޭhOnϚ>sć2'[=Ae:p2 `s+Lc$|VΆTqoH!B%z{,/5XC9s3Ž# *j:+`ָ4j9p ;1eͲ` ָk"| +\bWp*`yܸYe0lKfI4@0?8ب*[ Cשf)Bsg͏ I,[+yJH0E3ɽ )!I#Cߞ/:Ohf;jNm.nIȆѱSOq%?pėW2 ja1 9&U>cuO3+DP&K̲=lBXyk%ge09|X}䙅X]_na稓n M4c] KgNj*!#dؗTܗPdzfa5=v!ZSp +D„k  |a"4 ]bOd rI܇?r5LVUР8C iVd (r4Ta7h<7Le9ep!;FCDl*wȰEgC4}E_e?L.q:V[XkSeJ֢!b=Dt4>twI.c&8!Wv!}|x]Z2`a2ڞAt#DǸe"\t kh P+!:( B +;P'C`{a"p!_!.w4uQSeUeJ`ێ>rmuk""+ CtdqTx@b `QBtA`\Em$l~NZηXv X,nCaf8}m6,_B fQҔyE_±MTeR).} k6BEgHE;P̔؋ZպD<2[d1idU&B%TAD@:>Xx &Bg0nN`?1B/-LlĠ`%:Ip4u};Y,.pd~܀|#yg +Ѱ3FKs;ޔBo3i͎;h8!: !J898dgrt!`B,R;kտ`!Q`x&3OYz^dTh![P(BD\cd~̎ ˢ}͉EL;pTY#ߛ|}6YoQV彳\K0玔 ]Y9~]%0pN @pH""5Pg0^j,L4!ڨt$Y z7{#krҖn|<_jAO9Rb"n"UAQ j)[:fHfYc4=&4lG|kZK~`=nޱ/i'j6pٯ +`UQ\YsxwmUC+ >Hk\ Cku'hf`. E(Iڦ'Ҳ>.7l^{jӆSTGfx/C'{Y8vC:>h1=06  cm֚×!N[PGޡ[w80 +ި$t 3W׳`S}Q5ipfOޛV7x E(;ɐQR=71,2Qhd B + /~%K*zԜHSecL΃OWM|o@iP6f[Bk4}?FjkH\NiEbw$߈bjу`qo 5R~S܈`G cge옽C6~Ѫ8cZS̆)vGu_!N˲nO CvKWC^Dt}4 |HQh< ;Ԝxi,X[Ѓv^ۮɋTW-AҞDE@RlhD1L3ѓl`L8i3\mUqvX.<ûFq[+muW4'Ʌ8ھm,]&Y1&f%%>h͖Y?5]ӊkO5aT1QubWA Ц5;ȷ;Fx3~|'&Tn'Q'J؃KX|IWR-h"zNl %N0Xn% `6.6}k:|ǁ;|c7ߗ&{5cS;BYtY^|?P|7w߰z}^>{YiuK 7{C7!(:|? n|k;7߷*}g)>w{qy_|whw,'PFƯVS|ROT_ S"nĽֲppQg3rN,|D8"FO%|sh@w|{1q=pF(G;z@IzK mΚ@ s֫;e]hxp-<9E6UƐXKGYU9w*:C:vZ*i-<Ь_ 磮CًQ%?0쑪' vJĈ#=r69{&>0vۦpV QVYt!Nml2ǝo}WLM~_|YwQ)D;Nŋ?_|ַQLg<Os|P9R{k(e1K0 v Bb aҵ+녨ؚ'A~9IeeJ3[2NF09MQeB[dDxI鸟 +oiaVҔTפel&y@9sUj)nY۬?iN"BD)cӲ/J$Ϫ?֓=kRJ6oU9 /OIvXfQYX50#rsR֜%rQ}0TzW10rX78\/=H{졼I ˜cYp6<.㭺{XzS_>LaJ VXҝe鉡8u~Sx| B})'gVUabQ^.{{k;'M!î??_k lDV)gAGc;_?2b9C6s- +?S@3N=6:O0G9~*VHFgx'ڨrw(BeC2[I>ddFAkl5䳒Y]ICNxA47$C1;$j y͢kŀ R)׭'[&V{`ߍG5tp%{"јּr^ +{)2S IJF'2߽fYwZd +;}SV7.m8X_dޠs*'!Zz;I.63Y+tH>4/T&G\,*rlwcꗤ7xVrʲ-r ?eeYy6Py@]OL#?MFJZ.0f +}@1qH'W/jY`='̳ …+0Q-ɻiR-gʓc^]sd]ĥr7 ?ofΰn[{sċs2=Q?ƧrQd!Q'7M'7˕]@puJ*S,r:1U;4|HiNS#<Hy`JOS'O|6;r>6FπUZ? iK/_ A'Aæ"-hP^{)C~uhI?Wd=Km]-@Q_5+G. RO +ڕs5)Gqe/.[ӫ2G'Mզү8xŶoӀ.'`3׶$Ң$@2"'_[qhbf`gMRy4Oe/X'ɉN1F϶LyթH[I mz(B~9i"Vtگo+۞:dv'9O7~R=Q#DEZݘYO1C.ѺgfL4.&g[F=$ub#V'~ȱ( p*$=iAO R:ro$4sa^1q*)6-Ja@#DcwýY(}'{YcMԨs'#  +y`dQ>P6Hqr,, PEW]#=T_^M5G3aT3䊘!Mϛ;U#u+"3`#K$2`٘Dg!{drhfG(T,7 +q}=UTo{`B C=&@8\GTL&iN?m%m3%Ĕ & :ɸ"* Z'LVFz29djh=/ګ#7&6WKRYe~Hkw2i3ڞ-g=aw^doUG}FnM9*3N_dGv|FtÈ6`g4gk,`v1Z52$noP 9``2`P7+r0;03g03&zaȕ6!@O`D4LK])Ipg g3o0YUd\g8 ko8s,d?LlXa6S<4f?%wd&XLf:HGdaMfC@R +5K` erY=3_):% +TozanӀ*Z2[r/F81cF a}ZR[*g0P֚7JܘPI"@W;̍Ì!H!}QF H;k]vȂnH,Op}-cUOcꅓ9cg 6}^4ALFUmxjIS3jQ;Ô "i/>BDF9@ e[5]z yy?KRC^y1M_GDc`{'nRRz z_S<6Lx9% )>Q[#_ + de +$k,] a1r}]_1N̡FZ8j`d` ygY(#J$q^6\@uιɓ p̐sJ5UDӺۑ՞˷^y7*rę`fh6F_G'Ď]JYVu,I V$2^ȑFjHD%!9cUqޗɳ!J/V_JæWR[!R޿׃̵)ɚۆE +`HX +WڡOW1*~LFyyLG)r 4 i:7N P Q{Ewo P+cra2l[ sFU +V#{:ܮֈ'd%Vaaqu Ӿ¡'@QNa1_О7@[L |E0KU[ Pn+?/ݿD<9%KqNgw&ڑ(iz+вD G h7jĈCEMx")dfd'n1QԬNÚlDzIs +0]>E 0@C~ 0#?>2? + }مꕗ{ 8ۉvEw3?Su 8Q<0Ut|)7 rmR nٮX:|Z:R.$OV{$cp UNWHJL ReDIA:bǽmc!cW"k]-atDhj?QnKk_ 0!G?м0s.OŒ`7gxG:>?/t'uW c' 1?00Wk̅TZH1EgepWyq\4]= 4sP,+#=׿W)x$؁x|$؅`eľPK* ) + Y+0Af= ++9pX)?^3N:1.?)^tE>\2%&씡vv@+z9{q]~C D -Ҏd +'hzCӢ̝Uv'K.+Q򛰱4 +J-3A1#?Z,Si_rL@DZ:൳ڬ(` L/Cԯ tJPs.Thl lUkVWM sKx=Kv~3n3`ڷ#o-?ITu3I{`uQ?s㱆KgR23 ﰒ9u|Xv5&&g_\h7Q>;04L kO"Aw<3j7* VxL]%@m߿%dfZ$'b)Ff;պbiSZU#tP̪i/bʼn. K¥XQ/ڰH(!S~T҆s 99P\T]ST^Z Ԧ ^2E [Y#Aa`tXu6E0MgI[Gx[4$jn5ƻ1".Xt +%ɶceEʄ:P qqEcp!| +5)-7d{G1▌/574<!x0;cۓutWpϾF<=ȭ4gm: |IuTAGONqc8{a^92}\ +ѠBftP\ HK iq4Lk$ל,nzn3M_@Nh\pxjqSU J?f%q8<>T}vTj.bJ$!ttG8{8u%.<$ǧ9^mؗAWeM΁SS8iՉ;m?4!R-cĦW0;ᇾKK)W%p<Ɨy*WL\cBE*g4)vXdM j?!& ϯy$G[色Ц̀h.X.з~f:䷸P^ XSsfA5v?q:,Suݗj I/)zC A$ >Ѫw\z<`\ej颠(:G.0ζQs6 !dDW*QhǑQYD( = 8VSd2)}eGVd\<zE"ݮ982mgg-F +04 +x/~֍Q֫7Ef pu(Qhz"PH@N k~hw2drN@t;`sIp!@*`&V\sY w4jDl~f-1#{hR|PMk8+dnM1˯6"&ηAu<ÙenҭuX8ۘƳ"e겺mP^WURPLsw{sNHç"/>Lo~gu0fZk=' X$`?aׄt|jH_-Pȑ;k4(!%*K3(ƐaYaH*z5*aP[Eff!hXhu^Y;N[g /]7a+g DB 6!-˓-p4<#iv Ϧx Ϯ_i$=_!^GS~4~"Zr="9?O9;=_0utPΔggbjDMXE-.oT:oI"c1ȵiG;ʼ(J!? A"r\@ty=sڕg+p U`4aUv59&eYH@-nz(9Ks]ѬvI2Z'F?IEw} fL t4Z{j:s+8p f>ӓzX2Η ,T>@s]yQˋ/(F4GA`$)a| tq:_!,ha"LD{c0e0$2s@L`"`@r fwc(jy3MEצC*k0ܣ%§/ݏN|nN<Ҷ_ز_b_ߗ_K>õ8əxk/ȴ6N{xe`_]veK@vSl"./*wY.u@ "t@] {R^wXuoxRKFKfu +]Wu+\Ku2q̏.q<0.]9qUe fr('z3D=s^,qjK<t]wÎAl|ǝ>@'^7 дo#3-JmȭPNMِlbU_ Ն"&8"E E| H + +qgI} KFL"ȱFmƴ^c|Z3 Y۸hb@\'$;X>f.9:٭l/#ȣ)TR iJYS2)$QȼQMdj9-ޑ<ڡP~>|:0+gR8S1^C*9$ ѽN Q$=»_Zu,Rߟ(}c34g[*95d]Ө\?!n7裤Ip΅R8LY~H l()rHTn #oT%9ϫm17ٻ$(+wDdgٸVBrPƬ9G7%Uĕ/Xk̟kq̢> + ҍ  =N^񈠸]2x$|dPFt$!G} ]E3~^%'$ 9u^8[nii@9,co?ι* G'O[^1$Flk)Z/uF븍#Ii8H sbZkN&QtY,~N +u '-,6b[_Tt%}n\B "W' Q`x$օO'Ԕn~d?;tJZ.wUٍXkN37Jum%[ <:PJAaxI3Ot>c-;}3b Qx{O(I~4 %"%GA~半~SlԤa;:5Λ:O$D=T#|c +X ƾZ#gą֨ݣ)ƾϭޯ|1;_:9-_CcgOkwsH'@.w?h'?P."Hl(=ϡ=% (`vi==ُ t^1\6V@za/_%ݠN +BkҗrB lo'TʺPi +rt;|y6*UbrS谥%]K`D9c N8 +vAMa u<P0 t'ԥָL~6}Kab U]S(Ҝ;i /oxH{uX8DEDn#T/"*ETG<5-rүCTZ +HZ:D!*!:صCLpbW,HbufB!kD~^zAdt˜agqpj֯ET%j&I7Z/n~|Ĵ=ȗoP%6ut>M+>#}Rxx4x&|f,y (ciQVңb*YS}Q|A?@EF<&:=ZZ+P +7zrHѯRvV"xw[BtSs̨_fM4v!:v1x-pxBrmԑvTIdc[ V-\|An+$0cZY1Ðʈߣ1tbaT11XmjFHc#ki$mqݛs\QmJkڡJ!ToCBU;PoU#5T'S J=*O\d.?t^Jj6W BW' ]ͿjjZOY͝j_t޶,e +гRV+x-Uu>"q0ZwAHz XAGeow z(ijp~Zg HSQ/qᶮj'4`2☚:l;wߎmkaJ&+En)Q;}v)Qp"7o/`vTAPPDŽVG;k3mrZ'=*AX4]{ d.1R\TR&Wk?PhTq!Kh ة9MzL^AvM=J, >N ߿߳귞RĿxdy΀lAwd9 }^4 jm>fJԿJU&_(}=ZI`χ V%ҁ=rv02kCYi3ÖW0gXL9.nl36fS܈pB7x)7:@6E8[I GĆ<,rt>nD .čZFAq^$+-#2[!?(rD@bUd @\ȏmb65xoqGӈ*u"["J]ƿef}HKq<ߗ +< V<}?X?Y@ٶ_ Sn-.K;?푊< - gqN'R~21a"zEb#Vx&:2Fwܳ(~T? ;* +;e99}AGer[b0V6|Cܽ>TǛ&p)4\aa3#7M䁹 g{Y^ .Wa^yQ|\_f>v}<Nو-" j57K rgGZ(uz`%XʶY`Ss&|˚Ft󔷍|9 iꔇNOy@S\O<ʾeH+ ~$(RzǢR1 KJ`3O@ttɵĥCX+`g`,UC]/\-Rsf Ug,i=B/4,>IH󰢇 뾣Mwa*]+"O2T: ƍ#n$ j$WJH#ӸcB6Nz<*ޫ0}mi$rbH cpǾi-vD%^{)jKg6~2cpK/3΍S8^}`~i}yZ`֌^Av".%vcŨ\QXm)?JcYiqcɈ?mVEݟUhK+ Tb+fy_T-bov6U4Ԯ#IέD8hΗLEq ኍ-aXrS+Ŀ$iKp`:YB+nQiotW!~gf[osKo1UlTK{0wûx)[j3*YW^2Rj-:ܶ\8Q`Y9⮫o}h-.d**=xՏ (A(#? NVY{nt +GDžzp0 ,V^8^+jd5-'쩙@ӑ[{/X)a *CTceu2lse{8"s:A22+E]'뛫}si{!07ZmpFe,D;Aq)q7&fx rzڱh2ڱm149ƇTNilw56O2W"S +X3[FLl1b$ 5#qc"nINyGL + b\v!6jIz _2ymDYUn;M=z$X |1иble&,:Ɩnu=2qӾzL̹.Bt<2ӱ[f%4 %GfKo閺ˣ̍-s ⏖MXϪO#3 1{d\"?Ygէ PkԐ(VB̄GgLC +LXԽ3iFE}>B]e\E|rH?eA¿{#yRS U]%XSs4HIc<&M(^6ǭ*5▿1: Vr= 7N ,TBċxd T i?EdH1?Cjm==c=]0̈́tuVn7SYM uMaJN\7{erN] +56Tfd&^\9ϗٵ>LwҤI|p1jmt7TxN")vS+۾q?1E$FG(66~}.3f~=\4_p"KKk[ȖKZa'<_k"L@y:U/~2n#WĘB3{GjlFن-|q60nvQ}Ud^q,??aRcǦuXBq;S5|8eJTJǣÿIh$c{-hMZiŮUR0p4 Q.T%:|pg +_V[򶺞(2p8[m~H1m^Fn8k0 fI]ly)3(ϖ&! gCRF".kx`G~Vؼ}R7wYjxSyi8XoTmwoI9Z颃}:eROӷ!e5yǷvرO. c 4T*+8I,K5pg2e[=w ~+?8f[0sHJpFXV:K{ܚ$5@z5դtaq;o%]TSSzV4:AאFr슀Fq¹j\@sL|b>I9Nl{*PyUb:Mِ82:+pzKChÉ(Zkc=eoxyaN <ăhZ:tKm܄Oȑ9@n+62jYW1H΄n932kxq>4"ڦZ7>$$qQ <ܨ 3Dy!rkt63π2WHM +4 Yv:f,ޥt *1h3 +m܋gcc;r]ם_\bx!ssEoMbҐW +TH̙ס}hcb-NP7D<z$cjr'54)yEq93hҝ^J,ȇ@vsSExW7 .z 'Yc8f-B= +VDۣ,6(te?.YU+}{0/T؃p( f4I<;tL`EX|O,fF1̼Ė_ %>K@ǝ#-Y?UiuT[+%AiAbKuHL|BNGI?Ci%^F Q= +Yt65q34AFahP=u'$zn6d> ƭ]jPSjKŦ)?:gsѷ'pM{x΂_YCjCB. f~L;t<3&Ly}ssQ1xr~Υ0'7d"w. BN!}H2n'In%q 3 + $ߴ"H+EjMԹp@m)[xzi"9Y`u}ϋ\4ufzWC`&=HtK63dU 2=5+s)2 1KrKbf T֌MU-bŀ2$N Py䬹bCIUXAaB*C$-B,|_f d3VHdݙMy ލZ!txx`2d=U@Tzۤ=u +^Yr#ʴA/u[C;\PX-sE-T)=* :Btp~߹fκ=w lډ2)1;B@3!*f i'4dƕzE*<@*TU4@O&Q82_s3Y5D 9>)^ صOR] ʐܴhτD·v妪|lXHxZq1ٜp\8{!(LsAՋXo^ t6TWqE\L9kžwA+;HGAU[rG*uɂ<>X3:@P.o\NiwٗI,jvMWf\w~H^2Y1khrY v8ⶲa7ur/8eܾٜO;nS>?E75#l\|ƉAWԕNJt) eF Kf$APl84X1hΘ#e&w&/[$?Ǡ$i1YQF)ru5W֤hSb"̘?Уr=*(smRcf@9s͌=҉q6ZKEg$p.,8F*7@776?czINd8|?Qmb~ >Ϛ;,1kݹ戰/c(1Thk]z׎3yO]//>!+R+kzޢQ8h/Treٚ|@rirk:ks8@sQۋ}9v*en¨{ =Ux'r|-k%~#S%:uP[g/*jpJŁٙdn뿿I/ O _b I~^gg]=3̼Y/-([0?RVIh?I,$uk$~jGmܔs_Ѷ>tW9ne7srrd^dNDu Ȃm|PA\xgY߫ⴓ&8՘-|t4 $T4o Flӯdҹ^ -ɟD'|+WvKs9 +ȝ؇H=_j+s<(:Y@,ȫtHJq< 7!O58x8:~Wfw/V&j)Lj%mkEMm߉-Κz,6~QS#O6AfTn}|OiRf`#EKSYw!ؘy՝ V52~5Ru˴݆@hB+]8zk#!F?_j{5dJ +7_&t4}3o|jgIתCҠpgi*(l%*ۼBFW}k;;k֤E2=mV=kp &D*R։ +KMW'$7)|@6 Smvd%XN,Cwt +ݠ )6E,IS8غ(wyTGmAQD:IH:>#Sudw:2=[=ۍJ'+2\UG4d 9ȎR:[v";ƒU1S=ʃȅEt,Qc??CyDhPQQ +慶_g1qnv)5w˥,xBF7ǣ,NzCA*ILC*pD\c5)|uk\s=mX+KC֣a`hYÔeVx3n +rmZj/9qLZB>e]zгn@H%TL~bYPΰ=Hmq꜇N;O:gZg+1u[|>ѰL*mыQ#>8FR#GdWe8[ +V;2,O,XbZ?Ȱ7`n!S +VN`-K:[-K- (zj]uK!ie`'j1O;j^r&T?B=h*'e?0h^+} ڝ|,ͤ/Ԉeʞ`3S`[˔ +ƴ4F`\[v[8EI!>| ACvOndkeHpp>z9KNj#E*0G|c_4ĕ!qo[18޽wKAE؊ɬqkٲ;o*i3uzW\Zġ<ǯ;0*gڗi +!x~N챁Y,m < "!E5 +|}|Nc)GtR2gzs֤,w0̜90S9yMtM?VaMҬI߬zhM> <6k> k~|0Yh[G4γ |PJ1h&~=cYa5(ZarWs[h S̉ØXnlMg5w"q1!̊m>8s ' KlK6*m=F|~`q 2ST*" Pȵs; MkƔqQD6'\ d]95{{j\x +_a<0LM1(S/1 ~&?#C,pЏ!HN_"HL*yʮ5xSoVFNd@YMg$}4n4l6?*["۽n<5 J}]#-,]Kڐs#Q*:MRNUlz~eCH(CIǩe]|*OH.w-3=i?+a2u5XI8%EAlł)P56Q~k#E&l8A6R%,M`AB5aB4޺ƂC5ma"f(&W`x YKt@JЦ^Q\B|TV )hmkYo<n~WxzS!|YVle]CuOhm c>5rWdJ"O=?ϲ(UY߫FԪ&SNQ^4ayPVi(‡| ۂJq'ВG,[R=ǬvG\e)UɹV:.XO,:O44i/.Upm0r¾1W9 pW,/˻-LL4z=u~v'åÇ-uЃYFlr{7W}の(Gz),zُWt}(>݇m&R7r Y,#uzZ,.k,2=="i4X~ay*}Sd}I̕&\6NEIUrwd8B};pح.C4iL2[u8\z0jz\㓉>R9ڟ߅JT+ƨ)nR\Th^6{c LmOw>pd3 w2Ɲ TOhJҬrng5`u>Es͝5GW,gBs#JQpu.3uA&Ci5D"Z<3lE![5$Hz֚#;F:F.*3!:7+jlyuxF5 O-07b_(Kfm<dϒCs/7,OS)vwww~A3w~;J14˦tvЄ wNf[P0< _S72by4Ԭw(6ۀ2#pN//ǔzT[Yny^1כ3jC%+#b& IvwzHF3SUX'go/Ê?"cn_E#¡РwQτ\ܿ\W\,-h_ё S,v6̒ +V^Kì-Q0WU.8 +l җK4aeS Nn59Y^ -X*M6SBZҼgvR6B uSFjfjpX.M'Q3gI`F0KQ7 J}R4z_ktӨQvo4țǪMb|Oe΅ +s6p*õwA$> ӝʩfz[3XSVZϾ&QXXԹ-KY}"mQZW+:x+v~%}:]5&}s| (gO <'S 2VF,l:ZweV[=N#2܌  $\+1T4 . S?D[Anv$ۑ?Ny8ϠdՓ_Ȫ"D"nVFX%@dQ{a\{uD)kgeDc!@2IaC8ev̓H'.=J>a)n{$00`ܴs- ;=%f3w,т6AEF(Re=mqB(m W5$ո:ud~4Qz]cl8~0sꮊӚ{n 8W)^<.4qd;|mh~ negjOJw Ut-ȋ<2 9[Z]J7Lʗ^2рx4'^&_̋eeRc)NFֻ"V<}F{X swe 4|;~_6|9|M[8x;/ T\DSKY&L/"M k/"VKAG<Q  5foу2;r){`76lo{,E{g3j+TZ3.c 9IHlXc$+r^K썞좆ΐ잳Gɟ8ZPwf GGBEB:U$+\~3/ sca$y ѹXNEbmKN} ےax 1d81Ċ)^RD'뱒Y2{cH Uqn(#){&;MUZr!)c/:{+”9w~ +9tMxozMu+Ózp*$ \v@EE3$,RރI{" +vti}of#=ax,וֹ۰/by5›hAüj{E=k䭳'wÿHpH]"YPPT*$mHu5LO:z.jȫEaTXÅek\m6rc/8kQSX5H6lXHK9^\?o^E(4z֜ oƧ-ֹ+:ֽu@(-+PWØ'&(*,ioZ.rˑA&b W? sqֵ_7rđsB;yC8}j ~!{5dV0D>/h2NrBr{-T[>^9)K 6>y{Z|7po6Ë +_w-T% A^UXyR :Sz/?f1ߨh:9XZU +z_#3Toc滒ګ=<[{m䃏~lßx9Hʝ a(JIV+`\b^FphYi1KR*"FAe0cvpB VJGȒK)zyIhcL${Mbhe9U8.U>ȅ3kup`Ϭ0ٳ9J95ռ7ɜn>TSE[y +c t|^.QJO* +%^띭/ljԗ?câ)[+R?<)lJbHbRKQXZ5~ۻ`ɎhY"|JVB\hXg<Z)E?_ Rqx<[џ|q/8 nÖob( lD]&u0@+y腴/:3FQ)s18,o'8I'Yv܎Z4Dm7_g!7&AFz<.ww8`oM:'RT[r"eEYϥrp9v +B)\2yoĎ vQN+/6bO.8vNZPK߭i,rYMh'RE'dX.w%Zmۉ"S *{1ÞS״77s);0H p%c`싪rc@QnU(>v`U'VD?1iHq Wgqb[ʌ]s$831\˦VaGx(n6wfӱ|1G</#1j<^zࢄ6O]v?WP8ZpQct|DxSZ NCXgmzh= +?.UQZ3M-# nDAqT+6A֟L| :C31̷ (^ k[ܩj(+FjcŹn;CsvxQěi>%ΐc;D99iw^VvT/ zR\}I.U3ر,;ϻ?cϩ2L,3t? ]wGT?ɛȼ/0/Ԉ KjеüPeܜkɨʑGy!Z@^]{x"4'z.EB C$^{/.3^|xy1%=. `ZXEcɻlR]}[#Ww).̓< +/V]!xGx}iWHإix!.zú{ɺ{]],x\p]n|Q,ÜFQ:w#䬠]ץ]x O`Kϒy! R̄^~+-{MyJ/DDa_X/nF_jfp4l.qlLZLˋ9-Ϙ*;n,ty0rL[rW$LnA"ǩv2tg0Œ0񵥟mdlt,Q,|aapqIr +58l5 )ڰ7{Ђ9 ,b#ô;*O8AN72_*R|C_>R޹adr&&\I P!k\ +t58 +o!P}K4c༁E?*ZNEU$20`cQTp P|UcQs j"#(H>j# \G T +x.5(HM[G"eXv ^O$tBT^_;1kd=+,+qXHw-!0cG/I%2%jJ}xt$],J|)A+X҅?5y֏31m\j؀OJ]huSNj*d<1bٷ9քu.ܰ(+Rs 8+:u@:zB9؀YĩfFG`@/w}b2DĬY@RT5r ů&{w=;`*E#/!4fc}?SYʵF/)qAs˽9$!@lu9.v4 UZlKC<؆aG>RqUw{S,؃GTow%^x` +wpjBó& G2N?SRIM.oFAɔ/De! )K$__g/(_T-R7pZѺi ݭFT|"&0ƥ=WK9sиGY,bz \κ@mcѨNoΫ>,=͙7k[oW< +zG3wYKQ<8 }Cp^3yA QB NsipW= Gc"pN| {Ow$I q6RBDI) GWH io,"tϝ.֏cZ{cF*/ktYp:Na+.˗"Ą~,jSy!5nZZEIinu,P!; a\u,\4k|ЩC0N % ''_@ԱlEǛj kx,7oo%)WHDL!sx+zgT>KRuսu7v{}s:~ s3lb^f pɕwֿ.RopnMuPh=GKt!搜$;F?7t}ԟ_q hQvnU #].[| f;h $ MM$?Ę!4.1_Nv9 T.ӑisZrYFΈӿw^>Kˍ!(e@srLO2p9}rY_;\E冹.3:g.匈7v04KӀuwLB9g7Ϙnb GS`>}Ni>S8De唾S:k@k )MW3^c/~ƿ2X)M0hSr)M0ڝL3rJ̖䓉)^nb \ M{ hٿmW mϵh?@Tb IWmq+mSN.%Ε`n Za|B]MVuK^;09Be쳘CE\†B4j|:?;y%,izdu15PɪA=oB(GC Vnxִ!ma5uR0 \qHxoG4^Z^_ GT;ږ2µ{NeV H|7_qh}Eq񨇚lb¶<~gr[>4bZoR-"Z+x(ۆ$_ мBqfCCwNM}Tgp3Wc6D|H.6ҁY0$r\Ifv>c_Qꪣvϥt_yy;̝?oܧ` vPɴJ~OVtl8L˗N)&Q5 {t 1T>=]۹"j-)wޣHoum7v}_! Dm1Fq +CӁiđ@Q>Z:=\G8 mX^HEIq +0r)Lmʯ`v)fjS\HKq^{)`ڔ*)v)9$,NW ++ Ժ/8%vCq+N8SieʤK=UX5wT%3fSFg6W*}hS>ZjSƾ4e轚L3)G;}zw:Jp T)*%i)N{GԦx8:%P;,*Y*(TX&%%֧&c'*g}ʋ@YkzO)ySNy{)W14 #OcFq;sx1טʦ/09b! ~#W'z,yFۄp0.K_Ud 9/.AS|$] I_O@0Q/Tek4iZ6d`/?NY$j߫؛!?p1]H{8n3J4&j6aكi5ƅ-xcuP~fO{Q"r)$rj35ah;;]8s1I\j;W$?"hBV'Z wfly~"aUȚ6+'']$$WSHzNF U KlO6iJ;\hֹ?fn[|?j-lgr@Y=2ŭY[3Yx\%d[nTp7'sX>C0p&&83N1j'f%w,q)"׎U7&|0/o|vqh뱙ϮW]'͇A͓E|QNB8jy''"($ /4Nχtؐ 1(%"!SD*2A;#Ez2PsE܍tg^gQxS9zǁPj<hTpR1 + G 2-+#$r ;V(<Jy/= B^!<Bxm y3fl[Z)Gx8jܝzWx8."(Jx|8 (xbKw%|tUAAYGKw$#?\p:cAp`;ƒ7#<ܓ%Ra&(0|+DÏcnsǞ=flɀcf) +, .Zmq4/cu: c xqB/:u7,d;t~u" X:,O9[/A9D ͳr(dW{c~2e6᷂r +Ak2@ڎ(w?Hf%i*}oc!g]ie#6~A&6׵+n:<[Lt:#ge&ѽ̔C.:-}; pq? iz7Vg ƒ4%F\Ub8y֧/dǯ HR j#s4NjXUkq:51c-*˝n2O Њn0B'xM ʻ_ a#QiLhw("@Scy Lrʤm ,ʫ-]Ǐ7J <˄g$|c~6~caP~,TVUKPQC=՛Mt@8v +bD!ĪQP}H!Go(NԴ爐`wCEvﻮ15rwUY` +1<](?gɚwp˗y;qR~>}p? AEs۵엾ҝo׊햶rS|y;"v;Ow?Y +7Q}d샾C]^ƔзW=H.{C?썘t)!uf1{a\'i]Y%7Le@!(.OF' kV nO!9\]hʸ΀ `8mU".1vC: 0af5aq:<\3AN5^L%EpQy4K5Ψ)rW±qH0CfRuQd:lCץD:kG>)Me}wDb T;#3nn742]ډ5pT~&j┽ݕ^c42{=~]HJ9:T75# ʐr>_ 4fλ_8Ba[BЗ!4Ļ[9!t!yih?,A£?G#bAI*`S Hv4`-B֑@@(7h2ƒrm;8HD~L#@oqJu}0mwgVkS>{h!ЌKD׹ū648]أ霅Rt\oTui>iL,0k4ʹ+)&odvمvvSRQ1et,$!Qc8޶,"{~.(߿y0U圬f\ aDTSՏ(w{/i!f5{MiVx,GVɸF[qzYE,# #SyI-4k@C9Wup#t/dGl]";0GGGc`|ƏE.pzjϐȱ"*󇣡 L?>c3P\3?2;O{ YZ@|x@.Y8 @egF4*ZM }!&3-(M f젝ZzMM#:ΰUb| ?QpLDR#jIl7ZEm;ZPa |>f#]$!8\05<0xavz96~߿MIyw|_͆Nq/xo( 9LڍGj@)}8J ~`Bdu؋76ڤ*7JKR Lz]:"X[Q@,x(.BMJ<#5-<|-{.ZhU(g]b՟*vN`x +?bY)/z.)(gd` a!<,v +~]QlQj7'Wz~J;FU:ٿ +.<"-o 0q}NHi;~rN?gՎ^jqT#;qxKڟҢY"cD2*抱'St?JAUB Mk-S栃}6=xGuGPfE-KeF3qFl iߓV/4\_ +4ثM1|b.녨}mX?mv&ŏ㩔c( ai,:TVywGUy?h&Tt: TQdG]zM߈` +Q$B*Sĩ)~U5"Lj{̡O0iA CP58T7 +ɥ%{ź8PlC#!']'2ڿY#%%vS~ ixTMKIu|g*q \bAWJo{N1rud_L+Z\Iܵ(5oh7<8:b23|z`泟N}Dpżpg&P5VTBwB"D5xku'uI~cE_]Z!F$s?UMbQAUk6gʲ(qr܄8yQ>wn--j<67qUklʚ/o(&10%qanZdw4W%6&3<5uZՙ$[QGc%S5%R۾xi4SHhHH%8LeOX̧#.wxj-3$6ҳL1^>:tᓮH(WHMκ;w+: đ^2Zn?cwkz.9h^-yI'ˣ{9 +N˵b…o^|=umq49:?Ĉu!~dJc5(! +yBSŕ}ۖ$ɫJfɽwqgʳ#ObI:'9y;Joc&9o ss^) ܚ3pVc^v+{x$O1rth㶯 L9;>r.q008Q)ڇ\xvx#d",~ehDD5 ,:s|H2+wbFu5պu;ze9!vێ?IC{IܾӉ;J&՞hlEnepDGxS@8mbN~zCĚ @Ud^i̤|{H1WM* C&Lb} ޹u!촊QL$L6;ٿ8Wp{rT_ rm[vu3L: 6j6~ +YY*.vYGhhNM<<6&J["VٻP|׈d|xQ͠^% u|DYY?(o|ȥmbW^%56՝`] LcXH,`g&8gv􏫁+ox/iVi/lEzf)K;SCEw=Uy΄gCYvaHr}cLp\ wZh߷;uӇcѮ7Všz$ߦ^Dnc>ҧΕ0J:dꭇWJ! h'[栛V;yϜiTUKTQk>-dSu_?YqVg80(Bg v<#a>Onn8L[WKWmxUҭwsmMR3S͓CQ?լkG"~ +ORemiy$wh$^Nɖ]%_K/8{H;8u˸/E8IkncM=eIH+Rj) `{β|XnqrYbN9TQ}zxTW=ID$&ưBdETS$O=|9bLoVA- +{=Bx[1,qG :"D$A4[Y@r0ȑesˌ=*~)^kt #, isS_keQ~VVJIkEITQn)L~ ԳD;{B))䅐=>8m,銨[AcJr"YwQ]fM-Qed&(i1 3ȓdN#͇zA|`3)z! + {(g6/cP~"l٦8OovzD¦f#̚d^O=I2_U. ?jK<eq.U_׾䔛@0Wn9g~l) +kLm;Y^ /0w +lyMbxz|淏h?~Ȧ?HSoQtMg _e myjb.gc?ԢPY#(/}3}{s^u9gElP:(Ѐ&A"<}ҀS~4μOg[^7ެw5?~[j~iـSB{6]2a_Yz~>o4>un^L捻2޼j.Qˎ8SL8-EfpCާ3Le.U|~Ғ}+8sܶyii9Jk^lf5CD fOy=&s ]? + x}KqFEʎo{6>b \(dFۗ/S?|)pLrlnJΐ/s_wۋI|m|qP3@ztkpM=w~|J@/{Z/|*/aay00P!"޼ me/BlίqD3#2m' C0'9 WIJapE }+?|$ɔMQ҇}{k! C<\d-LfFEBKEF+N1! H Cp)>o-  6ƁO|!xq}ymy[uJ'u6B5ܹxԈUARQs.' l:)b#N_ԑK ~!5O]wDt]yi~6=صqm4``O0T̙>?W Ӷ*+c @kCŽ+UYr* u}*["Ī7mR00PdUz셶ܥx6)+_.wSڢnKʫr3L̬4<šo-;6X*c%0£FDvѳN%V"{zJ+fgxݳ֖Bp*Ԗ/ꢤ"k]vl:nUT`jɱJ>!n.ey + +(GpNLCi;`WP@RiA0(p%=,q)b4<E$[됛U4OLC8 \ + a<9SqttssōVqyh.@GݓP|%VIs +,RU.^he}s7G)1:gm %Wo}plwnN + PBybֵν|Qb([cz?ZVWv?Hox0ٹP1oa@C,SK?$} +_h7uEJ;Wbf90T@4)izA@P{ SrE;(Z'߲kKs"H",[RYQ]m|ꪊ7Ϧ䏝elfQ֫G᨞u#bc^)a]ow^Xɯ L13r}P.VQSxL٘ + +]Ņ?Sfa8};eV$h4|Ni1pq f_~rPt qx&U؂e]縜 wN#Q{(fmgkAU늨7;FCWadw8vK;z,3IQA@+m|vFDB}dz7RnbYk{wE5]W̱f"p{B!,UhX l8?Yo[q CDAn̺̫yJrIgYOfќQamg~z/F9dF1&O=T2rv)q%ιl~Fs$5rrw7cy7U";[w՘bϰX%qwhuU#nfy%QQꎂ^<2S)wL2zUU̴b U5^Ux!NuYr6pǙ#֒w`AÛtBU@cN7^Fq:㺹<52 4E $ D{qlV4!Y/?tyv>nl^6ËWMnȫ=ɃGuN8R:ȐO;@wG2b_ۧ;FzGИӼ"R1U~?gZL- 3t +~X9+bE#[s`KQv/EÍ)H9 =|<^,Piy-n.QNuA|Q= +g>砘f3-ϬRIoes6=R1Mi%Sp'St*~<7emC։ŗB\e g&- wˣmOXo}qtL_.Rz_ާ> +E@Q2my7l+2hum2c֡6/f&G`JD\;G̛$\ \UIiUUW;9h*ipOXe&=HzIf8}^#U%U٫:@1wUncB7nɏx3{Ō;lU;QQY@k|!&y} Q|}J.́X9|]T>*̷($2(r4c#]tഫ<& To-*5(xٕ ;^vWȖ9!Ӌ30FM缻^˨Oɐu9_#eJL+\ +ܓt< \6jpV>3GH(O:]!1A VeW~f8#0? +xڈϷ{=]ŻT0M3LTG +N˘ju2F!¿ŷV̔v!\[wSƚܫ ߩ +w ~ Dia^>\XyĠ0_f\u)V/>_fm, Ӗ_7=k (/ę)FgGOrALSA?Wºg+:2X:sPp +{hL٣+E tK(f>UQ@J@6/DїzFF2*n4xJ +5.7*T;ZVo[?C`zW?=`ܥW( [ћ,48 -B9Gkt4\ycYBؓ^8Ρ^h x:O )Dfvlժݔ-:٣c"U7PE9m>ZrEy63y%F8fuc,wnO~ݬW*j0_5ku+i `.Yfo݇o$%<㽌;X +Jf'ecii c)1ܞ^ +V׍ Q~qk^W(TY`-ۨ·<ŘCtY6{܌(5)HEcA* (uwYzHwgˏ+>iNŽEIU͝R.ZL{qm5,{jŐD{-B% 0q`=P]1ǖb>>'6D"I[]yjb6.z;q }*-b'74y;>Nl!j)O2tՏocƦocPQMR=X:Ks?R +Poi}XPP`湒/G e?eZhÎ-y2fi]CT=C`e4i>.tX(3 ]~[dlDpu=b-X٥V]F92Zʾ[Q>g;.'ka!4GƯ(u'3wȜU:[i 5f=UU|T@f&+J}gMTN¬ n䴙7Yu +d$lvIp{O`3iAoZ=)};}7jRcIc*۔7 N}Oidgv=stX{ͱPXt% U9i{|K-|³_ދ'~DR +,_ -*[qC!L>3DӠ$AQM DU<[^#@MRICJ9ti ^T&1?!T[GXI&jz-m>#6xaf7eMjcgmreə/]k"xgMu=jXM¢mF8l*hoS1BtQ< t;n! +duU3`Zv.27Zeh}g.,:ϓ!Lc[x"AKZΨREu7IZpHE >&y@6[t\XJ ++ D}haF\9:(%[v|CYԡez[m;R7k$[HKtN'"R'7bqJ߫Ɠry,WQf'u$RS,Wq-x? +PVՉaړBB)%A_D*lhE;T4Hx7$z0׀Dx+jF`JGǨxNa7ߌ鉾Xl:"Bt1N^#4\}mVbwB%*kUaM]( \_TOO!1@H;!s2OU(-R qRP(VE/g! Jߩ) DY-34ۚ'~P<709J:%:С; ~:Tθ2]׮C$Pڑ,V bʡ;5 +FlDq'֬mZ!z-bn%2!f7܆8 +`][W4 Nژ]Rk?щ4K:6 ޴x^? zb*kZ e,} QtnQg5 Qhg^cf883ֳ aL4q(.=JYMuSYHIYwY 8fֹʶˆh('g6DP`qH>i +[8Dc0L]n;W|flZ/\.`;Jvfaax?\d[ÆHAƶ'tM{OR5&jt11}bdP%@ U7y OB=?]++A NHO(du.)TCr +UЈT[Ѩ-(_l?ɚ9En)s( WGw@ g.`[z5*U?ۦxC-,L@1cnQu jdkSBJ-6c5czYLEj Nhhk0HuI4kML0(|98 NwM8~11dXFs ٙXni:l=~o1QKmлpQ :})[}DT_JK{#5~~k(gVDi?Eo;Vd ;= 7Wq,[ (=V9 G莟Ma;'TmSmOg;G%t~ ${twNg5A}j<+{mfRJcq#HT\C%%[:h(ykc@6'#Ci @}{¤AjVa( }AuybBpJ@c3j$ b/ +^)'p. +~Odh=5BFZĶt)qmo bDfh))jPsk~0\=,J5{&*+lyZ̃6z+fbKbTClaPr(`Gk FG^YۍZ38!b=E eoiǶxP?Ւ$ˍ>O0)<½/(ee7 OJ}(i(t..H^v1  M 9 +!-ꤡ +hQq.W{P)J:]uT.,`3hjFdoA=߰Px9yadujN{hWt3nȰc02eY[7Y+6! 32p_$?+̀Ւ!uPD^8HY4ǚ7kDvzI;GUq7J."FP]5ZfXZhs)|O!84Q|#+Hm1W!dUSfn-m5>(ڣ#{mE{fp"P((wR0m8)?A[Z6ԥ[=kJîUg>`7C0>४ȝ0]F57`Ā@94y.,R =xla/Rk `[]}RwN5H_t696t>;YAw#M" #Z /6o)kzT4 s" |2C@t,ʹ$,\Q3^lb/vb9=0ִ“;{þaov؛faovfov_/vb_Ov`ޱv;v6N AeѬT66J3<{JXȮFb^s7iUr@@:drj۶Ѿ!eTUm5L^o]uq9al1%Q{#|jBȦ r9PD}ZE1TJZ=FKf5ݫKGW4+ [ gt$U[,aXDBR~GGw*`t'p򁖾DhHjVNt3|};ܰ|¯:A\R +̀S8(~>2`l)x +?S_ ;s6?)k]7SM {YY5i-WD6-}l7weqh:=sH~?+ ,'ّ$l?NG}/;M +ӕՏr-P:H_a3?h].kQ;hHܥlGlHJJe*6Ƴ$3mHWj[6iqj$R]mtmj[˸&`i T$m +\@7?FwG8GיPi{G'JGו6@ ҆̕|]qJKqIH+n#5+l)nq]7m7Z1qECy5X 5;L^/pi7Z}$B`Cnw:>3}eY4ZAŷ&nӄb6*`%[do&90+QyFg=7,O X66`cQ kL@r MtSN c&N&&z0=' +Sr8]|6]uS㐝aK6DQ\ `N"JG(KJ?dzpZ;'B}XމdoKZLfY\KRi6AY|$ⷋ(`vZ,w kTIGL/*u\Yӣ/5Dz^tfо݆s' 4 ز)zˠ"/^܋4l#c\h0Kd (V'f_լZXi&TĎ,^Pxs7!vGyc!SUӈXZwYFY +8#f8{@c\mϋ\%0۪!qyһ#rz1^6-꒾O[eq?m/[0Gؕ"&BEox|C7ʺ R=0j?9>c\e +!(/!Z8d̛tÏAǣy3Z͛qTm:-ռ?F^G5yi^eqJhyM#yٮÁ{$/VRi/(e"G +i`֏y{txգ{]-Iv8lStt1@)7'fDB X%X>,mW>@zl=`8ɢ|9">ؤ~i_(2+wO ., _^ +wpAyBaR0Pe`_8£/g> 05p8(؁QҰ"@iڢ2lb'/H(&t|اyk_HQX-4J}X= Ц8x+ Vs뵊U±U0֎Tz\~1!ԛϗ5KKb~3Вcq\eQH2#r ](QwLxflw!gCښEգ 5q t罿j@*E`ٻYV3XL4ݢ,cHf?NcO:` LR@=_*V^֫ 5,0 (arΗ-$>h`ߠQt [s9S~i3Wbl tA(pW.0T*|2'o@Fc05)Hh]/ѽFw6UYn8rh}VkEܡ[@*[\뉔J|3-^s!fl_"v{ cXW?.ZU#JO4zzz\|*2+j}ZGzl 81"1^3xdۺESht 3y0e؆IŃفڸFyޘ(U|L iV=cmL %mPA`-C:-".Vz|#pNE +cgJ|CPB!n@1vICs +Wei6=A+oܻAg АW_e(j>|e}gD83Tb[~:׊rf3L΢ٞ!X[bu* +ܧ"kunX7Ntbq%_0V&[`Ls}5#WjVO+H% +0>56k @NJ,}s$N,v=)aBBvcjꕻZ#\RS_,R"+B6nr|[֜(Rʚkkf~պZrd!c8q%u =Ga*M @~\&]D) ߩ.,Z]iJA%@U^TlghՂi 1u4ڑ o ).-vMIhwiq /EA< ږ"B7˥^~??o~8?ŏUȏ@^~\P_0=nddc?ÍI PÏE&5IuQ 3g adо4eL,æHrXJy@~gcw?1 muu1(|}1.v2D:]^ѬMzbFhyx;:~ncmuMyV6g4Kҟɜwzi}Bf*61lEv*Vy%ǎǔ{ k)EGbL/O)48ԜͲdp\Z^wI-,aQMzӠdgMc`gAS]CT#`O |m&4J;{Ȃy;6HmW& UPzU \D=L?J}h> `.Ur)tgLxվ7ܹw3Ɂ aq"ժ I`PJ< h`40l/ 0Z#7 ؛`=ѣcjAS/->4 8f,wFCCVgYݳ*xFa Ρl'+GVÝڕ}e0lkA _gֆζ +2+ۘJ*, +` = + +aK|WK~ A%QV?Z8_/dt~4B%?\S 05HlJAjڶFj@™7hpm4S)k)ܹ:.w}d:sam}u= Nn&5}Pؖr晀X TM`sd\@BY[{@FTeHȶߋpj#*6j9ʃb / It %)/hɬr Wz{futfq'^8f_ᇩ)PoEKy`B4Hh@nHU:B̰'am9 UL'%0b6P1laFwn*  Ψ_F?ĝ`qlDOᆊS^ (: SOɣ*suGzZ(o=f%?eInD}h:CY˴ .+3V8iʘޠ-tHKZqmAtF{E_Q!D}Qk]CqP]03 g3R3W<󊷹$0@U]L 47O0R|Ƭl>?HjPA~fR"n0wM/:CwGfݒUģ}rvC8"p:G 4j] V5ҕ]2zgL~7O#f`s.DsaS$PE S_W,ap6qöIx_6ĵ@70AӳA.Ԭv$͒u)˰zm:1g+_5Poed?dBԞIsji/Po?併t4,OEc ׾}L)QZ;;oDn&|?1׿~ x $߿JR>n%Wabǩǧ0R)VɿB6{sݰߎҮ̑WؗXڕIppH#XF;n7f]/bj+«ÈkC5Om;6326+MMlfq7+ "&."`qIZvj(VEU86';N8rqgRmg0T]$ CىWx{M啁6%fgv*ijFr(gO+;u%Tγc{4X3}QR]Fz~(I6 WʣG?ăm2 桻l:۲!0u!rPX!9my>ܕ7&o[>Kv FAu wkz۲[6^M!|ը4Y Ź1ꙏ&JÙImL-AbݘͲ:ŏ#pXM񻥩wjd}Rx2qⱗyyU uHBo1i?G1B]ei}^.ŵu +2ŕiY?e8 m:{)25 AiQ1Q@Tf2Ǣ#Kx* ȨR) +s$U%F#R샪L3V9ךVrA]^bK?ڕL{_dEqZ A+.r/Fj%l8~uVb\]!U^y򃯘c;bYfՏ8Ӹ O^a0KaIc_9^ZNC!0jAZ)aڮTHX<.r/F V[_,lmRlIzm[~/Pk6 pCXqӿkX(ՋY +|=˻󃹺Qp.oިto^D7`%>g܍Ƨv?:cvvcO_[Cd'M E=t8Dj' P£}7 ^XG4,ͺ}vmTQa ha\R*EYb~Dd$$ҵ^Hld 7c` +=ʦ"AyyRWa-0W]Be}dfn >Ĉ+)F#UC^H'YvƢ䥶i^0#VՔ\Wzmm>mRy_* NN}n$%U>0Xszbz(koՁv."d*r_ecU FȲ-8X]2@nXЗA§P6y1RHP%@7$Kkj.H4շ-`
S#%Ա2eLoeckH1>l|]uy0{^M;{i !~#9) OCb.͂Y`a;ϐ_2kL:r\Aʜ}_\|v PN89Kfw%(vs7Δ[gk_gJ$Ia}^Q(3"=ms#Kݷt%WNXg X0D&0- E;p篶FLD7G׉W}_Bq{V0m PVCiK1Js[HޘQUuŸsԔ~4aՙQה `E~TH믙h܀8C`)කBbG:Ea9좧 +Tq2* 4 +OȼaK%^EfA|@> pYҚf1T=O(ZapN?XXab`BMd[yPjL.'9c?Ⱦ3e9w qS1 -j9AJ_1Nl\pⰀ^Áw,n PZT2./&ẅP$d+ǧ$U>gtEf\BF劮xhVsGXz3knv%;ﰴ)+vG[r8(Q tO gTƙ̡]$SvRxiF_|cw?4O| jXUVe_c?SPò^+z +/Uv\\qvaV>:20PY NG4lJ˵kZ İ/7fn$Ȑpqg(F12ϭGкM4->GcQs>f%o[ă2,=1hf!JZ N׹;ۓeKE#,Siy^.0bC{M\^R5qBJ 6sc̲:],YƆG:%/rB♹b^9*7 I4ޜx<Y7vgo\CpdyT7KC@ ^ +g\cB[w6) 伿Ix‡[=RWOz},ݵB U,e[+}W+|w%h>l ΔU);;c.+hQN;]>C-͘Kر]+W1k`2ub^/"1waB\/-7Wޚ5FPÜ + ^f@Jnl87ߎX6qnoJ;)mpn;qIkE%ٔUJ¯\ak5N3G=!cV,k}U-58,hF_2&ٕ|,FS8WJT1K`vX㈦WU3<2&WMQHM1ĥݪ!G2>BHnpEy9ș]b3&ae4W/Bֹ'=1J8;Ԟ;Fhm_RlmbZn18?$Ll,BYd I3d3ܨf0Av:_+J{@L4 C=U?^RgAAXc  V0dFIK͜I[Ip-|UUgĤPs.edtʌxG8O ± k<K=,g ہ1E,Ni9Q1oKGUEL5@;NQ / o $:sC -S +56_x:;hY; 2VfPt5#i_3x:2Gp܃jT)xVTpFݬf^u ?]cn|CYH/ʮ],_|ؐ`[󘥱٦EcRhy]a3ukd\ԣpr޺,nS\[؛7#oE߲&߿},^lh`_ djEDݿcͤA7aG.Vu%F8*Z<7+,X=w`v1Km\>0ooܼE0K~3'Xaeoah֑vZ{!n| |Xv|cP%6A?oA˦յDtx~k-k׊<c{QxI u/_ht `VT+ʬs=m 4KɾXy*lWK- +J&,mƓz7̿@9g[,iB+Eg>rNzRn3;Vvѣ9R JJ +kŒEqn}V£]LEK0nm̒ +i6䴧2?f;9H?0S{xK"^|&=n9Lk)f\ÿɮw.68Z{wMcI|2QbkM2 +Z9¼?hhn{U> /zQ}X[!rӝָߏ?\yn +6w+*@SJT=+=L\ Ok9,cx?ʧ[FgP @%]2rHز{e9-F LVp%+sYquzm{N/JBQF?XglYG$M^7WAwdw>* +Vʞ-66lv|Tw8A֜-{ۦwoP >\iX۠=yml3gQi,>DIsp,!#Gj>G7@KA17{hnq[?爇y0\iX_73Laň }ۂ}ϋws/!p>x@EWd+pmЁaiGC>,&em,#B|"!/!b}-q 1,aĸ:Glwn2ŇX_ֺ"? +ʧּ$D v_E }.bX7_|#Iwqrt`kéc?|>5GNhv^t 9}h;Cv^t@ᎲP/!NjE.!{{"|.!fDE"{>oIಊo2,/=cWPƒ)Nk.J_jyMD0"̚7%޵O|g,$* jJAN fa\푠PYe +s"wR v`lG@quۍ="up9>FcRyH[|{Аmkah8 D@[aC0@b=_hbY.thvW-2V5 hhS!0VxWZS/:ѻ㍴#Zz^8x _{9-2št9u*O@]p$RD@h>ÚӔdme&q[Nnw ΞJ=(RSa[|~MiES ދ#5婪0q[{tA Y0D<^xځ +1YEK0CwƼ4Ң9D/q\pᜑzP^Vl''V7)̎y՚6/Ds=L87i +d0df.Zûnw( "=ܔ??YwōǿfoQu ?x-:.2\H1F}XF\QEAJ>1P8g*2G72u+'/TNEK&e:_$_/HQ`!Buw+8l=}ڭǐŘHmJ5́H3"٪5՛f.YG̖<<,.kAzJe@db UX"FNy 󂁪69)3F +y(=/E5I:ʲdH`DBd֑q^эDbƔqm3}ִK^R\Ss)O^Xcma$[o3 ;LOPxuŠWo&*b-r+Bz\6u863H5rus|vFk1N1UC].]߅n#jf"ort19q_ǔDi $6rgfn:MY( ^t7-Zf +@:u{flSl7UNS>Kb͠B%_;q7"T:Pk%vc?JENיɉr\)\8@ds8g1i:g\!|Tҙ'T3+/F`hWl;i=<&Vj 1rZ@L\JJ*?=g.JhU>Jѕ=C^hY]cI9]rwTrxu%TG4e_d;‡[=3"Tvmsۡv+ⴕX[Xy]%9v8p_ |Cp>_؋#RYeÀ둩TJ"#^rmN ˠ][C׊Z}| x%iORNEZu>8}* R랔ڽIKy8l@eJ;lLFm(2ˆB9yתT6+BK꾄ZjYü.ۥF^IRƷ뛒oJn,7%N7#%C8&a^g 勑I Hp6/JIKM.JgR{Q]NFIp̾oJ^)7%"!e$T7+;'拔2ݤ̦z8Kn)ۭݤt5;FRJkNq$&z7MKHhy4?"dsda=2yx3g5y@/8\D/Z}]c\v`xI/}W_o9|2a{.'Z+=a/Ai}?-굴xgekS(ȜWӳ% +wFw?pB~˓gwT¥`˙jU,8?-U|0vo7ñQ2frM&YϕA d|ߵ +R9TifW1V.VDxwޕ = caM\^ +0=Q..v\:-@i}1ǩ3ł<ӄ7vzԅ<_ظˠZOERc#@D 1Z8'lu_43qj^7,'Un +&TʔWy20mXE&M{HpH+@ W}?Ƙ.f/(%~*?]na;MḦONR伣?]ZZFBk?O/%K\S',Ϭΐű]BZ =fVV ;k3|܋0 4>1.x?>PvPa%G9)O>uV2Y#qYysu݌m]:h:Eަ۱pm:\k`}F..̀^%bDEZ2 #˳v :\1sg>#?,;WNpw3l5&YD dxdc.a6C*EB;I3#쌴=j2{Q5m!P{U(Ba)J­Kq]âEIE*&v5AXk*mc>Z|W-})@F?yG!DnΙ07q(Z>;=߲s=gs>$H& b&IbPo&'k,VROvT;~ni s,6`9BmRwQt8 +m=Qr`f ;) ޙChP#Rhu?BZ.4hB&4̱ncA\g{ y{:aqrdcU R7(KuPg0y k=JvLՔ +*R4`JPtJ#5xTRFMH debRU :ZFʮ5aZ4z -ǑH_JB> Cb(GRjZ'\f,\.`Z` ֦8eCiTfZeyjb"sj NC39=iMm^T K7@u!ƽL۬",7mqMg +qAg;,j~Y 2Kbl"γ%Q0L'l->CoH>^vRm]ڃdO1Z9VPn0pjZʏ1M=yTK[#Ôl$!g'2QLwI֎L͘ $on\l ZOi,c~ӖMi-+3VKlGceB#߳)s60u%5ju&$rq~Opf(َMG%"QESR;[g _?MX~59<Ŝ'u_kbeעux ^:ٍ%}Ѱ0X\3鋵xd&/'0#bw}8ѰZz]Q)n^tV'3gw*OX]nኦMOP#lwl:%k_T_(L)I{ۿ|.0y𗸔(ho3 +ik4w9X~r *aSlrE䙲n@ٍCb&+OF?+]t2nL!6v} vg)њѴ+|oN7q6ǥN]eؽ]K IJE/DנIPdJugE",(B:^5"\\1\u=x)R)_8mHEQ}(RdE=( k> Am +pX/APbOs"ȇ"r!0I fQ2,':cv\zWm )i~`3Tw&- ]kuߋKŽt=P]-v-`PoW0WyҺ*IJBgwgV?Y uN<ڎDÞl'' +7)*ճu.6cw V`#,QFtCzd1w[@i1)^bMC|~LepZ?\"M+-&HYT>nY|F @zְ֓U{2W֤KǎWbwO &s5n|oO1Rs`4a_J_hL4l kLOM=S'61Kή8. @Ä|>f=ZeYMUFT3~#(RXΣpo[0 *&Y}{׀fgiF)(9,(#?g'XP!1n-@w]3 2nGT˕NmG6|)F7j߳:VϗgOCbJ1jɠeWL\j!&= `I-Ą^XLʝ } pe-AaW A' ~x^Aayoh IڎCvzƕn}w֓ۂ(#aYdž#,74Ag +ObhZ,K1MIHnzk/mCKjv[/ؚ4̉KȆbBb4YDwMyVƉFEtӮ4hh DaGIdꝢC׻!ʽ+(. 8kp(rje}W^f{u@R~`^Su.`_u_e ,y܃^U;)̧?JyR_ХVi:+{SA_iMMiVQ E\e"]$lXv9"POAqB"GIr;sO V:Xn_0\W|pC<)lFweہXe~ +Qzb X۸I?X@+'S٣.nhñgi;&x2 ۃ,&dX6ꗄۋS6`&dS< 'l+"Gu74D#c*j}hݩnMufV> &WVm%bɗ8 pj|hKݩZjkT~I:mBy4o]T0Ϡ.N|wq"wQm׬4T0h:#2I.wQɟ9wM*KbHZ p58Rw!nj{-K1ku0 7ŨqGᕕr;Rp %#HŪp +fjMkkr}~ +ˌ=65:̠ao[/?l"jxiثAp.ɫ9OKe8x]CGIp@2طfx 4c;꼣 `(QpQzFA ާCK^rY,4̂aLYP7w$:x yU<8)܀ۜg4w=XUF"Gw XP3^@`_@c컬HkM}Qß023' CUU[a.I@) 5TdH2w 8BM6.7휹wEWÿ %Eռ?#bI + j?Eۺl,j+=\jɲ$aZEn`0֓gՓ$7zyOr|pijrЅI+X]Fۜ9Brz;A_0>Vll@h^rkɫ#>K&e rҦAw_1De] +!Q4{fl>6hoy~F`HrV=}ϼ@msULZ.Atx>q=Y|Y6B3KbXhP%A[Cs.Idf'0X_vv +pٽE-eq i A|qrn6o|#cV 51L̡C5޽6މsCrEVAd3I(d~hdDAܻ{ ljlRK+X-OPNu=cfoHJ~1=,e!C8Ϛ5~88UkF{s}^ qi { DNd*"JZ* h͋ђR1eI}],mѷt͹ӟӜ +Cd#@; F@OL<י_'Y)r .TN6rv :]IhYz'i4=ac'K¼"8#@16;/'V8x޸qp}#8?T KD?aauV(mIA%>CNF6Ie$_T+gqHl +Zd 9]77l \"x@;.7%T*9MavYGn^?ce!U\vת}aǺdT΄K+߲$<0 ȖHnRBe^46 +j#Q?-`Klآnx^q@>,w~?>E,'IKZCB@Hi; J -8y.,|-]']u tFxb,Q+Gڦ)Cn༩KO\ҹd5n @{Tp2~+ SFˑURuMAtl(I&aB$zFfVr%5yƉDb̞>_(8~0śVlSI,6GY4LjEB:DBfVRTs.sm^V'S1'p'wy֟>d}=ɮWKV7v +=dz0=IQ{!kX+쵵,mݠ@5*l;7!@UOY`=dƏLD+nPJGT&#]mˢtU~4DY`IU4 3۳g'E9Ah|г*~惉Y{1t*67lQcC۲{釚`v{/QUz4}|medrNP~ƷL8zjܝ:˔ՃȪa^H;yC6Ch ܊)G1-H;ZUP鲸!{ypo+a 36epmՠCHtʐN8>4➽*̛6{ʧgӪe8̂ndj1\ v,X OV+{pE77Lz|hm߿e;!fgYH@HjO\:_)6ϓBv|uBC~#a^hxcN &x;?7.1k`f^<85z!&;} to%l =({caeBg:Xh0zpQ9`iD{"f$Q 7 +VJ5S3B?xQzQb蝦 1kպƍPrLFFSl.xt@ȳN{r*҅dz2`=O\E +&&'Bb\.ubPYVnw 'Xb"j ᝡ=ě]|5*69yy!2W'B0)j"U.:H!!kKp ƛ+O'͔U^L3<|hH.FT{ӧ2fvB2B*KPo8gaTXX\)rbɅ7Pf6Ki.SR>~c?]s ז¥w}e9uivZjn[σNm[ߣ2ӈYI_!PۼCNfu$QWW'bR-uAה%K>xeq*W:"ZUtY<`_>7&1ZyR [9zap7u]^~ W|DNMb0RɄT:F'YesS7M@lQ񮲏P_w5ڪY:L~7u(Qؔ*{U0ʚ  )`5~UĢid]Bp)PQ Bmŕ3*1=6 phM޻S1Gf< +fVļ T :"+6pE)WF57*|u(tLonH1ZBheܞz#5n L5-}@ QRŻ(@m9F`gBa܇򲾋zG ʅ|"RD1QȰԺTDNd.WA>}u'PX:JH F{E_38v9O<.?<jRz}w1mzre 0:Dq 8`TSG! +F#S<>_yBF+oB~'NE(I̚>^oejj{qSq&dzFOX5MY>0*~X?GL%܋f T$ {3 ^A!`:^; +qtzpVH@)IGd?K̋d@ ܭ<]VS%v d$oJ4U(wi0R=%aʙV`, +Z ǼX%py;gZUi)b."nAUU &!v44pe@*b׌TB_Y, ߯b)Y!j簨P\,c4# 3RPk_f׿W?{͠3 2W,›u= <-g@?p}4ƒ)?R8=q|D۟v<aA)(4` ܰɸ-b|| (B &%LJLN{)0BՅm~Z-6A.DmAיqL*^nrZMF9zT !eG"*s=$sزں~RYG93/'.*," g\/ߡ4P[zd1h|³o:c[4(Zqew`0!Ʃ,QHVDAE2Ֆj6PإMt/xߜ؁]ZvX?mqK I?HQtFc5 _c +RvroC+auԃbH!;B ̄1 +)Ar-s# [:D$:kXS/r!nvqPٍ21(z0IDo  +F9 Mⷡ8C;LQNF wM/w +0(A$%LL%.F&0޿WJ]7OeF4nye2z,άH!еdtpT8&tՀBY @&#Jil!p(up: <!h0Tu9)}N~"bl0[( *0=]f7XnRޮK>,_W 5rfb}"`7W} + P]v,SL6 6BmExȼm`U{4D_t.c3'=_U?! V 򔜡V +WZ{ (^~} v;ͥ/Q1la0qAQu +e= 9 K|YvvRI˙٭4= ۠kQв@GQjBn{?va_dz<[O-,g`J'Dl/~1y;$_oLa^Y Ν~ +D0ĐB,V`@- )w6w[ I/)h- +#V4خ$;v)|<#R+Q y@=nW5czʂm9;-T}2g ev:ju-(~ٗ 3OL<8V5d҅#c>m98ZWeу0##>.zu FAFHft iɤmxSFݏ.nK6`Uz@C0pj=F6 y?l + ?GGG,7Pטʩ:0Vn .J9)KKn_H+yа>_bcuW<Tu_rĨթG|2$v~UnC-VuY95z]¯_?Kkɺ]my9.g}kI"% Ad=tv^BiR!=o &@}Z~'ڗYXQ+Ѧ*qΪd u₇ L䞵 D1/;s# (E +x\w7d#@vJz}̦ ߯εeFrbbYZWɿہC^\ua7Zڄ@| \Ƥ'MI{(xhkm9skr v~,4l9{T0(_Q8gFpqv8f8$)k?:QL1L:7 fpN눈t/+viB)r=ρ'B +4i@ݰwYX'f +̣SNg=u-޵yA4فp_5WѺl`| f^Ra3/-jGH@P[JGa^ 8HqP8J `ZK|>qwn9c^2 !Ϗd"F/;4|>~~U}>CI!{1XЌ| np ~=G}|)͇Дv 7!!8B}<UCpx|H#}!Iκv-E^%8|Nz| 3C{ 8.Vkuz9ϾH~AAp[$>%k^ww^Lp',N׏*۔[o7 +w!lQs', ^CfqpѽϹisn{PZwϬ+5tdF@ЋО`w}:p(HC) [l?6zA&<k7D/Kl&9/͢ Y W$:KV}ӖgPRx!:#Dw{Zs_«6疝nscnZ'`iޓMOl57K<\}NE2>˖ԓ<֍z@.uO;7:}btzw(|RToU'V+`n +V|Nt]~V?+fn9 +Yk;tAV- $uH{VK]1*=y2[+ +KG>vck&K%\Σɩ!@ճDn!JDG;^L)bCOL +E1rgt.PƕqTWHEEA/|hf!'/ƂaLî9{Ͽȗ}0nΞJF$[N?h׳4jg(pCKUUMU;3ƶizvgL1'>w'896eAhL85NhX3mssgwEm'Y.G8b`ۋJ=%?kx} a >o^OTQ߃#顠U݊4nqt7;O^]vΈ>}$F$AE.j_N!ZPxPQ%Aės\& Z _@l!%葪57fB|3Ίݸ_7lJU@.<\pGvok Pm΋<:ղUוQi!W\+ or|ce)jmsAAȽs?ޫglt汈P(H4@b7Ivuk H^^!Wxֺq/E-+AjiW$a7nii&KxKpPnԻq SN1Au8qU&i=~ٌ3\\BZ-0Ǒ`xSIkmMB+ת.Ø8z1!6jZW~@KƦRζd "4 >O|Bޓķ ']1 (e}6J&l9xB}f/ìV\%v> +OaqLJ(ieW4). ]D-GڂH׹C^|lc;|Bs$~H W; oM)\*!0)R-dߡŅ +5>4ky 9h4!gW'Qk/#8m= +u!"V8yh̞@p03X3й!WdAʄ=^d[N+xth9ټfWꂃT +_vDoNלk1q7@cD*t$wm ,˒HMkLxRT:SV\tŋ)-|ą?KߍCW`T)a#1t^̊YiPD͋o3ƧGi=RkWrL@3uׇ{H|o[T-:^dqj#=C^+0W};]_q4A1M9&ǜA|kp]8- +/2C1VXK/Ohd~#"8_ + U'p[$qt H^wj|LfO:|@bdND[K `o5~ 5 y5^{[l'R:4QN,!z>Pym<!o#v*#sx; cm,-9T[*P6u p`[uV|ue[ -eq $y;˔PV.hag_0/L5S,M>J%E^N/$8ɶM[usWhoK I2tiɮqIt-lWK%7 ):-˟c Y,rMDR7籬rPTއLe[ݬJD/u`vհn {j!hnsТJ Y0E&si*Tup.t(_X e +]td2n]1eT9zgm +m-%sJk,Nݔ rN!~L,\MvŒP[d.-g%L0/dYrgÄ6o'3jXEsjl{*ֆ>{ȶzȉr|V]N2=#6N^-ls4©l$FImsN9~:;AZXypW&$p]|/vVބ a? 9\mf+q:D8 BuJ&qCa[ǍP$G>4kQJ1G9T3F&S'-AHf*p5vętCnfХf#&S=3+TMTĨauayd~ǐ m@˙*1KSP+BCT`0|#7HU!=ZekZ3|#6}UR:6iROOD&|V *tZBmUI@mb۾5RCO(v^B4HьI^ar|Q5.|b$OOoSÃ'w|4O^mB ɭ]~ +zXx{ nkthv4!'gMy'\]CU4@5r% ~} buEfM2`c""Y'C[7i!2TP"g=}3cr}מ+{MNhɼO/HAiۧȺO ..WD SB]eM +%ft Eҩ_ k- Zח7 c(Y*`}2?ڢ9rw˜2d^xqt|ߐ\Q/-sNj+qʉ{Yu. '.Bu3yu/q/蚊p]*ev݂$H wi.7zXC^=N?5]]&);Rq7-C_k3WJRP킼|YoDD uoWu17 nE~ʵ6^Ecz?(TW?axlr6uk#{R#rڼ;ͣF0Pu&T(Wڀ+Ltԕe)[- 2;%A'88ZĔ8Rh+&TFk]mHr_"BlۘZU8WKm yt$V99&Œ8hTWuH=9[F>-X{kvwmʳkY]Ipxm>%&<jΰ=?#ړ=(Q <ʡ /KJN'`'7V7-ۯRI'tPE +y腌*@!|x8{SvKƨg标jYQҮO:lˤ($t=Wom sغZUc{vy67[-Mq¸q;vtDMw7Jl;-(|Ⱥl1EӵolF!oІ K8@)uԆ}u3%Ǖ=^{5MOťzJ:WL8+Sz~Q-<R0쯤r*ЅN8!9]aLQ4)<ʴ=VFyu2y6fQ¥khꓶ-Sj6{VS]Ku$˲+<0euRޣ_$ΜsJ"DG>)JQo8"="t#&]˛"NVR9ss^w޻^5#g]{م=F-Yk'[Ͼ?:+zOfK9 ~?k"z]=+:m,oCleQuh@Q ( x4gmYΠy.VdI-.(ae0A6{g HcNhT|T,QT)ڞK*{Kz2=YXZ[kA)o;= ;3Hy9F`e?P2C'< ; O)e}ZwC(y v(~{#B;^/ZwmK$7"wRL} ouϤErK/m] eeS z1pεl+:ͼY^xwN.95pg%ȗ܈]vB/Phv SVͿI PS`'HkY..mIOz1RF~VָVOWD_[:6:=- \'=]@tA{Jz†~= (; 7;͒p/OF>g%OvP$[s\ ~%po Rn{_*ZhLvCg;1/;<|P$t!eL~j^<\$?lr> _3_=<-.HH'jKeIь.I|IR|F$)9$vzu#6nho@Gj/w9-z&\x-ܺ?JfizW(D5L]kL`;Ȗ/\>:m'ϼic3K#joʃؑiW/0. #Ӵ@$#\%OzX<[g䑕Q=M߼zzᅦn"ONr>ʑ4vz_J '<$SJ\٘-/sieƼXU@z65hUCʼn0SVuf +51|_D&&sz!M)6J*h~24=x Hq41)zBN|PCحFTg|\Iܭ7d+ u'rƖ8`Q'L(O̠1=W) +QO\+:>cm`<9D.E$=,JdrKx!V[MpD%B`D@TJv&(yRJ*/7+~ߍD{MBSjN۫890WAJ'cfsyV+BqOW㇡aI׉|p?k(l&;k%7*`F/ dJ$:CKs-5M7)@>n=tǯi |邿t!> 2 0@+/=mIg Qv%l?uz@Ws+]AMI%j炱wmQzeu7QXف`:aWv`Q'^JKKRXW-H45R^fmK2oeG+ wO鳗d}\$7)J8Dv$;ϺvvZFjCj`|J7@>QS}lt;]ftIRsbeGvK|KhڥwY:vyn1K= 6id;z CډےRfV9 -Q K+mrgUS8(]H4űD}j28%ԼJ p{*BYȫBu޾֩PahbܙV+i;ʘV(}eVmXN*b9% ++D ǨR+\%D䅚"gT5Fyمfw W9D M&8g)Il<VlsOKS|TNbX&|>~c3S3'] ^btN!Ą*1>W${%(ޙ~{φt`6KwCeЂwmB-Pm6`^-hԯqJ!l\15EJ}CR iAe\΍C+r,lּ6_mj}ȧߑ#%qdHC@Mi\:)aW,hߎ49gEn\>1DKݬj9dފ /Qu @H iyeIXnLGs?}} P dBNԒI`tK=x%doa*H1E4GJӑv6\2Q{fKB"MĄD kKgj[H*CSGlRS甁t*A߇14";ZuFljb|ڽ&B-M\ƴS@#eR%eԾ;R}e(͗ }\dưV݌)۬X$v=Iz3ٸMES6z[}*q)4&n:9ϾҳyspOۋI?!dhgW/.O?gTU: i=8Nr'Y\Cz>̚y6O8tt2;#,,c5I:ےh;*j2y^A;/t;,l屧5B4ޖI +n.\uO٫ǾV^rb^֎!% :RayݹS%] 2qވ/4綃z)q\9%tL,?%;BpW/QT}r+\ +$æLj%ifYzJ (ǡ0yÃCFUA{ލ-|8ÇɜIHTp}kdSwۄ{ 3t 0(_Hu,Dِ~[ u|C^! K:bvHA)rw|e2¶I +DWڼ@:rmHnDէ}@bo=s܎]REuRwlx.Cyy1{g̐j5d*ׯQ_R_p{cH>s@F_[i{ã+rŦL{;k[mz{eҷ\P.p;o=Wl=F^noߌM731[hϣPc\:jnj &= OeFt =$:y/ JCێ<8vkEloF1ف]J b"=ư\[\qb' `tIow^>8eiǚǎM6K,)X+z8Am!wӶP؛tF Plmw>ƌ}%⊕\ra-ֈurl.Tr. L_2l(2hzr;2`c2\1y<@+߆k[6]H& K♙H֢:ìP|ϻofl[v"|!d7 LdQn3r+ha4Vx̱(NN\jp> 5r+):1}I]}WB>_ +`-7[qb7ick֣So1hNOԶLJp1>\rP6TcK)թu?y 232 "K"ydi; u1/.4CY{Hhfe᢯>É:{|8iJ|x vP'RR-K1ü.#r!D)Ѿ%D%]"Tjx! !D.!.1>.!i^F\LEpqAq~h;.#Q{\FmcDe`D#Q0y(Q}(K*Ȥx"HqFu\/)#)*e>V4sAXrJ|_N\tljJ%E1ΗРQ!)CKjeZc`bgoGEQ 뫿,GER dm4 -N +q#ĺB{拴!dAj͐߹GB9ےZwAsōz6 [ @=Kk[,_CeEF1 c^L`Tdm4PȹqDp3`^ 9+/О=&OCsy 2-gɌnvdžZ>~3uu0H#&6*F➝0''8H$]-.%ޅ "09ҧ_or'&o&VN]$Y{F6@_sn4ӑN1,."!rYrDzXV12Y?&dDʏsٷJ(E/$n`wrz.F+[06ܜJ9CZwV OcܴW7jrA5jh'(QzAld8㐞N^nT޿c߈ӊ&w =;6}w?c\ڷ#h۠X gr6mbruߞg\Me_Q0Fjf0W3- +ϊ I)d,ָ/y0z"ǾLA/%IcϿv WՠZwfJH 鰔` +ՠfi$Yۈ)$:B/NIV C9ٖ(vhea SiiQ`/z@Ura-}kH\w'Wy "1KM*nQռbw+MH/XS®=hEjƕþuQ3XF|u!ЌMf~ySn~xP_ *"^Ü+^ըu);2)e#FyhX֐*:9eyexWw\u]eKGwUOqy̠a˦ .,+%uԤjx㒰@kXo|im仡:Z/W[; A]fu#alW- NoB@j]?Lb&/&߳B& +amZADdCD$H"ڙ$Ms^"ID;3DDNL%hi{}ÙL4W2QXKEJ.eGK2o% FZ,$o^NgMVⅎ(>Zi)mg藗j_Ka8.-]*JAR8%@CKzj\dd| sjLbk}Z*{G)]/~~ ("WM"IRJ("44X Șk'S8ӱf  psi|zy2aQoLXі3 @+c\9Kbؚߐ d +gP~BCVʇx[+j@sc;͘r5v =g7e(8|!X}NUʤ?ȊZa^j\ʁ7e҅].ʔER82oiwv +[l30+?:e03ψ ʂGl8?؀[u)lL:TkmEګ8w« vaHBX%܌d)˗qkx$xl/ܔ9*v0վ ;Bo/yŒ{|hK: ;zaSt.%=T<Z n]d(inoAҦQ?kw[_y# +tcL ʅ(,Y[ j!*W[s.~&IGE7EՂi3L6t;74V~ݠ'@ M.2%wQ2P>a~ϗTVaEuۭǤ&Dw^ʏv/>Fr?si槪olI<;2"oEXQy>V˕ΫbyJ@[*n΢%7fz3k9iX"@%;3:Z Zɾz +_("Ȕ:{SM){3GI :H(3sE=ʕ_lSlDİr̄:%!Vn]wi>AhLnO!$]j͕t-j07NYe ehʛlkXAȀ\_\Ш&K-xb:쵰_XшOJre2g_.B1WO(~c1T1-XN1)a.,E ;sgG8!XKf˱T0yJ- +Ų/߬{rほ@RW*س1҈Ɲ (:OÙ:6p8_nѹM2 +Yh3%J[[79eϠ"5T`ૅ *PR0`v4BX4ٜ1bl6qb61nO8H6j*XA~F.El_8 S_ =/4,N3 _t3Ƣ]ء8,S |?yL`? U*;++({W<ٶ4dfLi ArYaeMhJmw텘OWl3`HmL캙izp{K.RdhpҨwZBkV3[fZ,㐢Au*F% B VxlzW NaPmlN3uЖ-A8mڂUǺ )^on$l6~d1͹ &YZx M퉋XGY0iK^V%Ԫk=%v杵Sۋ*ڜ];qEJ +WX45=+Ь"[sk}7r%"$84\`4%hVS- pռt:2׏u\[W_$*Ѽw}_kRut pAC^t#Z oAr7ʮjN޸sKrVJXt sx/h1V*oZ4J_*S|.a +J02 + l +F-F֌mݧG_!,)\"h' ň +۷6s@v +|@fxsJs3٘o{1mWskFqcN.l=n$iKfbm̅=1TX}0^/yRy;;בyS|E]M>tO܅?.mY\wǫ+|K>5Yggun8%e/{`pT=Ф^5Ud"[P+g_h?_m9 xY9ސC۝;h1zy[,2M7ң۪5oIrQ*uVhE+5jtQª-7}7ؙgs*KWfHMZ\/ls/F?^=5Q4q5= ~7 V?}xR\%(HgKX_ʅJ‰B![7vaܒܞթBwgQgЛJ5,Rfsjg.sdʛGU +'&ܽٷآ*HK]R*ϣ @2^*z4$u1ZCn'mpp9Bz7sMydGKP#,8G<n2qLMC%}44. ;\r2 3 |gZ>VNNMW`&YLCnLCw;_v$i4δy2 7ߙ[W1DC-WD+U\DuhU#sWXQH]MbE Iʪ5Kr<<d/+֨s>C%{$oYtb6maKr0a::.^6E+M 󬡮ɚ3 +~̌R^/ZgCجaP-Θ%O:ؚh ̳-špxsZ!r}::jc_]Xh=0|*,p{Cx#v4N-kzp +!G\ƛIq:՘)rQ_3B~{=n9!% FcvFd4* &2() է &!--ӐL [Hqܷp?UlI'&|6EhpIQzryX`T&Pɳ]f.kԷ*':,8`XoK+*R$ L0|=F_~4ko+5ȆHϝ,1cȣv7mrAc ƊV"a>?4ެuh|zk[Vi6+#fLAw07'q[#-`}|-Kc(NX jC)5w$Eؘؓ?ӳw&¯9b'Ѓ'~c3Me&bZ UV6,$"nriBB9`!rȓץlY:G7ԼR}j<#xuέh ݙ %&>8wpg!Fɍ*w$p3 v^g Nv:s9PxYiqm;cǎʚG^x@3nO*PIrJGDbA% fH{%`K lvI짞8id@],e ol4 $j;f rmr!Q~DAJi˓+T\ҧT\%KKb$3Uf֏EY]c eyL.#r .z{dC9a3@'{?7bǛ<=zQu?/P\0{켦Y숦"z71Ev)h뢅_YI)4-FYBVHؤ8U=9u;dx); w.fr9Bn\F;J d[| N"A%_e1쬋 +ɽ7̀ಃqpYx@bc̯E#KP,1ܮ(4Xt%[vzґn_Iң!1c?'vnjM|O #ٵylԫE)KܨU5\`dVFb?`$HFLI;O8`d3Lw F6e<`䩸PdrD8a +?dbQrm WaB $@*^"y}Rg==Y +7{ +X{<#J==o + >5T̼,h>yNdv>,Ιi $~2qHȆk󝈈)FA7q&o"&ʜO!bmE8HeѼq;54xiE<•7ܹ͢N3qM_D;;w]XyZlԃyUe:ƨqu{TM-LDUd 9/dq(t W:ksaH!DxzaxN=)#R9;йxH}S3Eau@?['3w~R|ͥTF\06ˬcݧ:j9M KCFd(R2-i(h.X%L~4j]_@(2k|Яչ9gU7y7HKOzYQo6_KFƣ: T)#c *?~W]^iƐI jI }`2T~ПJ#׵x".pp-~͕ϛPwŶS]g6 K_ z/+ys=n j}x|u! _e 2P8Wc3NNX.*cZl;uT2dk$ƒ ʻ*F᝽}m$Sit]}K${yp%^5xnJ.OMI2bW#^c+`T g7 yx-$ :rBݓ5,`KzQ;'v{7Hv7kNX$d72Դ[oyRvtyWng=n  +LkV~"~õN@z=;-^w/O[-?cȴAc'_.i\͹@Oa|3rդ1YWaÉܞT9=޷OH/ Mm_10TF٤j I3oqx& .$ЌKP^%@.x?j>hπ%T&8ҍܽjW +P?FݼU&uݑ-D6ze~d|KNiIl1-=N"M{Y\tb޼k aw6KG$8b3:vU] $Y},7ƄMa5F^g+]@҄eqZo%D52-24g^& }6n#T6|TOPH%LO/Q{x HL Nuny6DwTqZkm:h5i \NZzxZi{ջhk7PRunFT~t [iEF*.1WmMJ(jR`Be8g$`틫ܙxsjd\ʅv4?^9@[X0Zl6~Rᡗ).fV J6;P^l}F{Y6_gyTdmc\[+|2PBKS^)5N[Kr;kmg@y[1ۗD_E"gV1“YE]ezlAR>nCqI?cWBi0D +4s2yfIr@xU/'GWitw'8M)Ҕ)֨Gg{IdPVMb!U[jSŲxm}ǩxwuE$.]+]*:OJtx Ppr NbjNޔW7Nκ=8 +Gـ\B +7?n{E\)3\AEa]2HSNQS.:~& w}$6r{UWkG{*`LBZ^j[ %0Wnb'qٮRea? FJN`zj1M3# k Ȋ[dLm_ Sd4uJhE;pZ_CAe3+mr]qѮ XSKU[[onN2f[ү îhۀ~-`Wй3[@iM&Fzm<≗}-~iɵWmdSsGԆُLj5OJZN(Uye-]K3W«~T-c2Eձ]+7THI?/}}81$ +Hm~8@&9@+[ҝxq EЏ$FCW$ P̒4Iuܭ WKf[ЀpD=N J>3ƟFҀ9zá/&?Cm xdHGp5owx=$y-iq]$fv`IL&p Ic rn*D[wп\K"pvb) %8.ql }ClͲgǒ`&.WpޞByqMW,ʮa*i#J:-wyR<KNkgIM4'e]5c_ޏ0o O'z9Fßs2v^#c諨[/"hZpt\?g"99NٮKrS ~c̢zaHoF]SHWm#;=Q4Jb>t\OUNkz{$;'k%YUΧ$KQmx$}Ʊ;)l+Qy㣓n<$;NL[#w՟4na}?:]ḱM'Mt}=<$0f̑,]_?] o}W- X_ | c(0c#ShQnnpIJA`Z.UI,3n8@Y!Ɓb|l͊"GqQۑZIrjQlՉ_ַŽK(uVl$|*\ac{Q1qQGש&.x`R:V, (^|RƇ*R͊޷3 ¹ft5KU(r2?n>{~<"4bDw W \'~"6I; +MB,Ɏxf&4qgpǔT/?c룩<#e75}!m +qEc +O8̮uZ*^6lYڭ'WJrDЂZ~RSt\u_T=pK=6RGiGcFJnЭ $I !@j$Ix0${W}DNrR:qc'uUsP$y\:.BSiXRMTw<8V. hKJHm ce^]y`/6qBH6R:p Rz"yxKi\*M#=Tȣڿ]؊x7Vn9~P)剥"5vڂV%%׫S'Z2>)>¦>4:ɘ|\C7}+_.)Ito_}O?,b(!t>UN8kż(k,YP$WBeuIؼTExVSZDV[aX^y4ֳӦJswUaζƷz{_ ,Jxop-ˆ,"|1PN~?E$D<| +|.L).4|^TPF?TC՝.S5!w +ߕp7X2˻Di0PQ2v0LxZM)dumxM*?ΧN+BXQQ " x F,?Ih"܉L=-꼫VyK=-RZ6^%F7T\DJ֓'އ#v-%b"7!%8-8Zy@Gb͔ +zp؆e4=Vؗ)U<O-8"GgX.|*HJJ0T.E;>:Fɽ߈=rjO_ə;'AU(ۙPmj[1fX?jDN )&NH ŖɏSGnCpEbM'nYn.MmqM& nY2Z5烷B-LzGEU7vRm +[IuybIV*6x|ip C1&0дM"_0[ `e<04'O>mѐXaq ZWZ|boRM{^5 +iE9q&CG*sMvҍ B׻#ݿ%iל_$Id$pyOJApKZ:+cg7919$c,)*tQ +T)o O|0fzxAmA$z#4 p|__W5ؠL|oAk$;vm1Mb*#~&|jg<=_۴"\N&6i,~QpzA$kMi{80Q!"Dόsw/҂Oa!&/v,| ZPWWxv h тWMa Afr>I5PA.C +@HOfOGwfy3vUlЈ,*7 (Q*rߚ9Kp'N)^RdR>}WruU#]Rшoy+mQXU +*hDʦ.y-|E`FL\QgޣW@~@5Znsm<Dq#FuZvgj?s%!/"hn-EΒ|ao)fHI+oOJW0i[ʌm}}dqD^l̸"FF~Ǵ[p=0pz̯5%Qpd53ou819Gx܉5.GG#a?R##eX)3R>竇g$gw|O`yGd_Z3F"kƦ"C&w3GyAsu)k.Y+"3;p ޴릅t*"G:3;KI8;^B8XC>(kZ@ +5J0U`)ńkKP "ch-:C 9 +{vMϑ,ߙ;8R 1p "G[yǩRWp 5vI+nxofc@+6Ɖ#oll>xɾ367 1cdXOQ9x-M5h):*fpf:@vb0No žr>yY. ʽ2 z?٫Dn?gS ΢JrP|3Z$EKeIygםSyU%fm}Y+dFf玂7uĈd#0++'K#gr$gOJ 3رHHIHtJs|^8EVGBTHnl[y=<gxqY {`kZ_;qaZt81F69"G%vK hfեN0R+:ޝ <4 +wV蓯s-Ӌ [k 4^bԍjу3FO4KN+8"sCXnBиhZ# +(ZqY1-[6xȧlhѯC=P*v_}X.h-. m]#}B/O0:yn%5 )DKb|Mp:Jث̖R3&hЪȟEd}H([D6GC6ȄhȆԷlя"͢>#2 ҏ͢ {cԡ=ЏءG?(}1# S;L![# a Hr!8_깮!gT.ৗll~K@>#! <ϷđvUpGB:֐!-hH<1[CLxxԐx +Ґ]-I(p+~GGȳ#FU t.d!BPq)SNYd$,arH^ Y}9$42pk!kDH$PDB>%Dc/D5C"\dz9䕓C^8$/K!g\ +|!dC2rH`_RHK!/Dz ^QLZ7сΓArK!SjD!!y9$t^YF=qHx.dK/ij˺mZ5_=rH4>~1%?g/tjh4qXPwjMcGt\@'[*toר/opY~ZZw^` s("@˒̚5 ʸ1l$Wt. [gKU&n{[3_a!i=P^RPH&J6&olrk~P4b Q>ͱ'1 mG4z2~η$9?]H(jEGs⓭ּZ-qvPOHgky#v)^G}{F̅|F˶Yl\GĶ%f-,"?L8GS8ych(R8n"_l]ZbigK&̗gAO|{ш`4\ljkCj".OmR9Phޭ&WN׬\gjF.2}Cd qؤ'ň uuVp3J օe(!q<=IR) +Գv ^ +U$zg:)cX!];/ +ubsa@+/; ֗Xxfd bX<ĺwϾՅV(JvF@2I/ \8.:bMYEx&uޮ- %ab -( ؀-DGko)IR{I!ؕ\j4 zwp"MP 0#'byE'|vǹ|Ez5ޱ#B ~R!*UBQQ| RfD|]y2#?361[LQZi5fũyx܃hJHsv TSKWM/)–1>Ă -HS C=LٷeqTp=; c-hz̃xW'IV yɃx._oA b;o;EOV`t|7xjAfxޙ|SB-yHr+멝 >s7t6L&z!صk|bzE}Q,nwi5X4쥉9mlGb~⡫tnTd|*ӱ4=KI\"EVlǏUI^تReGS9sKT +}QɇSՆƾl┋HӷCo}Z: խ$F@_,Lɷ\|{Hȯ<+G0HYgz zDe +CQ5~{7A,bZ7^isMHU+ՉG="-^SWvwqJf+GS) N[.N#-p9οRu^6N"ůGеg@,<'+bT` sΔYƮ[.w SC눣pdNr.?-C{u1X5Ў6bU$a-rF`.n\Z k@UȚz/6 LM=+" +32~ԴS J?2Hli?;yXmIޠE>yW=!()0c~Y6(k?#- w\DJE"7lRz#9)|P-I.636!\۳fyRs7bhAaLyZshއ=w ܪ&b#RXxo`xD5 ՕK9+]jK u2lq{|[iP\&gGyxy +]$;5XѸ%B<},D +vbyp]clQCAR :v|Q ׋^ZG\]sIKH'dC 1/d%: 2sZEU9u|E98s*n|ʯ[R{cpMxQuEmi tEBҹI56[ټXax-;KG6$},hg0P腽o:JUH㉒8 Z:>Qp; =Uf҇ʶOgRZ; }={K^BL[:zi=a4V̡j×?;aV1o6kD:h/`A^z{,vttc瀛Br:e_F.ٌL/_J^=z\ɍbQyqe|ky{btM>-=cN7K(d^5o&?8KP!_(}B|.bc8䣎?V73,]@Kd8nwW>AkNV_>n@ó%<ϵL<~|P?paO%Ł q7TS#㹍Z@?SBKG#9GPiW=9b <8{6D8O+b_w8X gZ-uP PM w X wPئD*&c;g "FuL}ZN:l]ju=l90tׯY:0#haGg;7"fj6QÐTWg?[ is`M$-6%geg9`ae)t3i;Vܩze4k~|çizx~+_~V"s#e\_=a7#aE#NHHCe,Ly'f$w9% + ,kj߮Fhجܠ\_/F%TdcQhպZ;!/ъ.dv@k܁\Z/fȤISv)M"Gw5[ +ngkDavr:LT(drk]%I9T2ȆPؘXh"ͷRr Uw}~tSlMd>yG2IS'@2dOj9̰h M3)Y$X%K~s(* *C5v;ޗ%Rd;,:#iFH#`zgrJ2=%:OQݞ`{E6?jPPꬉ~D_QS',>jSv +Qsr#mjlo aQb8 +})Uޖ)PߤK}]Eãڧg6uJ U[mM.LΖu@R * iV#3`&/ o6eX“ +fhmDXiT鷅P(F|@C*PmJ)g<)$G*J錪uTBUSfW6 f:TkW1$WZ}XXH"f.H-P,ڛ8d&-(F5Y.*ivM,+M׹"z +i[JQvvweg1d:7|Z.Z.ŮJˆ*vDv$W"Zj<p:K_lAxOz +Ez\Y N@IݑqiM]R÷˷wf3H*)N1#Lݝ\VȖ&'6\B{:]5yD۸ 'z ,5M~ȦDd` +隔6W* kzQF!si~VD0 [d1ADު}vԶfZ})`kY@JrdSS^*gB} +~(4 rT:bQE;<(5r\܄nKYy<~$?t$^f<#!ɖ-Zҏ+)ï]-כWM ZPĉg"6AFewYJ9[i;&Q!);&8#nv$;a֥4سDN~c+Vde.%yٰ)dO" qP.-7,8aHEk6 )4 tGM<||%Ut7 2DE*\!t<7+F{b>iUt41)ɥ\WLV0k! %7&Who¬J6ͦs&(*GRPq{ dϭ Oj4Kmh"]4YTi;B( h5 9*U u*ʼnE']MMFtjT wH +&@3RkeO͝?釪%'Sc-> m9g߆=7hDO]+b"#HTfaxF]]ݛc)^+PcvupLMrSr*ͦTZT=1eN}wP83:>v"&p"SV˅_(]GOƗ"g*@O?h{c朑Lnk([mIi3T]>"%ޫ.|u5 Z)bTJV+ʔQŬUŎIVn&ȾȲ#4'e:PC 8RHߩ0ЫJ>ݦU-~V~kY /N}TItqZ_uz}DtVMͦڌµf >N(ۯf:4PDǫ]CfU%OTɩ$gA!zQB.%tsAVK(w$b袇ղ7\ѕkSE[Y+FeWNj|K38MG9hDJ@ZSd8Hy:հ~߷lD9-n)ۮa6/['#9#c7BDЈ)!k0p@êjVYmz8 ~*kXPeâ^:NoXB]N5k|*5X+.\^Y^17s^(H +2s/_n$={|Gvg'y7Z?g0[EP`a,{uf{+I7-pZaN@~84*Bin0r=<̯vxrw6Cײ!Z4d Đ3psa8#qqSa!qO0a^IƁ{a>4!X9| 5CôaT/ryRHS<̗Ѝ| +UC1ԍIļe-rV^qom`b:npj֭Ec'm&lS21DB1e +Cq O(A1a՘HIP3GŲ@ʡbga~`1#,nnT,e.f@~beW?\L6b:\LOp+LbgDe´Wjs#!ba>ba@8Xk?> }8K\ɪIX1K9Y2(e*NM1.x@^B&ta:-Uly?(~Acb4+EvOccGCD|S 2]"|8ڪ S~hXZx :ޫ7tµI/s;?J_L\id̳dȷ1C״[65C<-m7APXS&&v<.]՛1W#E*re?F{kL익c^bGK b(znZs v.!AQ~Sy4]vC4G#/TW;\=AvZ)bZS&&v9uD#S*&U%}u]bWr{8ӛ8w:1M]@N[t;5z|szjT+*N@$_5$*Nښ[|,dT%ػI5SA!/nOe: ժV\q8afay+YE|#yx_`Ĭq-ߑ_q#ɿ2ZS2ކײ04S0^X=<-+}~WW&LG؊ƀI Y )5,N +gGu ,ؼ_I7m e%i$u%7B)E$4$1Hy}tKve{k jog@^%:mek0c59鴤n%&:]G&τt_]KIPk_ڰ,.[m#ֶƓ34B̵s-Po 1魖M oYc֊77*QODɴwWa7o- ytA{ӕs:K <AZӳkg/)[.9c7>ac2uХiIi_äY`|dtL [F<9X~ +2>׋o3Zl ?CGڢ75B.5sv 4L;cxȭZ5 ʎ}[XtJdlmKlj:9fҐ[㊤i(H锘Пxne2y7m^{?!~^=a_G2q30 C$W#kE1|4–p@[0:-grh3^ْv6g.83;R4g\K6ՂX'{&6)O?|׭2%|ٮ$Kn)ݡ/EYTm ̪=)K|a |ؤGPICҧJvxq1TUҤPS1H+zL7xfSA%c KTuڄuݫ z;kN}SPw_#i +Ąi1).8c 7iRpWEJ|*~I5F⮭"59ܸiwq XGvwS)˩tYMu;XZղ$D/4mֹ(t/U籛P19m 176iO 8_s%*:JΒ!`t;j1 DII_d׬ʟ +\wmJDX8M(̄P1zeo-)+, +2h#F{uY&(A\n0S6AMSlJyzZڑe aa!,g +;}4gcՐR旅;{uh$@2p2޳DTY"rT SddܗޓDqPi2Q?TD^T_TXIEKEEEzH\K [KEI!.zא&X2і!]D8e"[4zIn#{(Yk5h/E* +w=T7QSAѭjlk*r]#~3d(y@Z} ]{CHf޺:mH%P][[{UW;>,pxF=S_粳Adʨ2ګT3>-(MFnԔ 5$yp/؎|&,^ʚ&(I!ȮGrd,~t擣5r1;u'PUUd[;ʓGy~/G}%? (xqJNP0y3J޶j[2a&(R{Bn;v5{}PzS07cjt?BL6R:3D+kmWvm3t(wz ѯj|FMCj([V7d>ZD_3 "[8\{X[gq663EL^u H0z1dg$Dezgl|P k7%:mXE^=WmeRw=sX%xÒzR<]?4f̪k0=?n +Fr>)>wn +JSh>~:6נ!7>+t)X). M6ܻ>b .y +^Ό9H!|dDEj'~ -Z]fD0TNe"}!ZAJ{ʑ>pdZk°-V #* j2oY%3gU'2%ت TehA_|9z'tth0t]=:?X)fCd( +1\ZNA^!zc7Gٻh{\0&jE)_ܞZ_dqRHxX9 +&UE|SAbG`T2I:?ж|G')fCO>U~%1iU tAnOV3<`Nq7D μOLt͠bY +!y109*pGU\ iP8t؇feJrmާ y8FHHH khKEOYVPC@u5%PS#Rd-CE_aqXre|񮏲GKM"gӢCö;bu J`m{`9k ^KzoZP.+IpqoǨs`~l])hA,nSɽgŝ!r֌A 0T0ɗ巁H}-ҿ <Ɩ`\:w`C}L3A#$P/g]'9/OQGӥ(kqy{p5nyino-г9!`JSO,*~%<~2 + =Bp]`pEg_Hj s˺@VK0p/ý"Ǖ5*%}7k +W % nWRMԫp\\~^^3w2_ao&U\\?j?w[qOk}Sy6__W˭[WK՟r`uS4Z[-TGtkR0`:'ff#9 jhD3xukniK[0!8kje-14gpyqݓ5NϷ&lW,o,C-go4ɖHq񨨍%݊X^ŵTi5O5B>{ޚBޚ)yfʹ 4>p*]M +sT$VQqLԜ_tj`rw *+`˟~n<6 ؁x w@*6Z.1]ucn/y1GӋӰZ>ct wFqzV}s <ްS(/GHCf}> _~p&(ؖLXdjI 3SJhn/moS4/a-,f8;ТqFZorrz%!f9:>)uxfsqЖ t)Ю%^/)q 5:IdU`-?IRӅ9|3 +Svh$/Qg©[rz#]L|vg^w3N [\hjCH,foKΩIAv滏AL1%I무fUq~̹J٫~lQLD()X Zj*߰tY[+`Ϩ?j)𣔤 g fY0X™2yF-܊Q< V-F#FFjz]q*I.˒w{4V鬕c +H 3@UU +[D4U1H/Hz?@_*)Kqx;KXz!K%SK룷H프ɴjkuxR˓ѓΦRo.|9AVhqJy\p! Vn+ezm=)f ( O P=|,, k +V xZhkn%Ӛ [2I}J +VIM7[*}귾*õn/k,_yߒU )ԀӲ _v2e'<9"~AnM -A1jo^@45*O?'\ +ƋY'9`b gX&'Wxn V-oPJ;k;dGb55_*`sa +T]ܵӬp*P& 'ƧeȉW-.ѫC\^5{UnѪ:nEJK+4s'q92D5W*$bJج8F3U~VXU?2i +4zKyS~xf{anXnnٰ^QU4rVڧm'koʣk0kEx]+omiVL6tobfK'>6%&NGq\#iTӈȋf,QI^R?5kDBC! @R@R{-@]%IrHޯ٘A8 _F&:`igP#E)u! Abj)1)2U|vVG#S{]-:2wq ~qb\+ @_E8;O8[Zt0>9j:ZH}y3 +3nbV|mo4r̥ H:di]|8e$mA_8״x(ir)izs#]< :'y=4_9gsycz&NQxܚ/m3?1+K <%^Tx%gUD@AkCcCbǬ֞mnnI-9`x'S`Y8 +UO"osC`(=a/?~1afӲR6W'L[VUOh]m^`&t^&0o*#ׯÙf4C&#Djb޳ZCЫ~51޵d.-C?3VT+tҋɔ+R78< Uw/WGB\Өެ>t~C F=VRP_A=WxSf`xq$Zʛ,.ّ34AP:N]WpW'Jü(fuW\LqcEIib=26ӔvTW$+^<54F#>(3h2G(Ue+ 4T]qOUW'P>O"ʾm\!/ ?NCӖV &ͬ +R&Z8zl,keD¸AYv<$Pi.Js]lZ]?ٟ2abralBhWN X%t]&ea5ڡz:Wmda~:lOexG֌0xTqBvK{iAPT+ Dzjؕ725Ǜ [n#(tX:vPf]?LE %1#gѦ!uv\1KDAueK;4SHڳ8[FU`~lzpOqKϖF3ö$L=i t$|x]T-3W ?=P +Z䡅 ʽMTqtWp)TpO ϫ|oMm*11CcDv߷ _OK- r(%3&G3T JظC8lEFqˋbkUicClzd(J [7y+e+S@=LuVnu֧V$4$0웯i LW3POnBښxX/T;ov)x.j@Kezt[AؖS:(%#+>s=n?ӌ僪{Mhki.# d:v$kr°@c~3,f6"%y4ieY~ƨNQ>óTVzۅ{pϷxG,-4>!tYc5-)f[WA)ZC7)a[KF@Vkr=iY(ׂ5$0[X!`.F毀ҳ_;$k-0Sl|#/].Vaj6AdR-.|B vXy~gm`qvLW_~>f,OG!jjoUR\3$T1>mt=kL 4%ɜR xeʂ88H:=~{{0MͿ~z\[92,LiZ衵c'Ƌ,v8T}3.-mz:8F"dJj=4+SlR 4<֣mֶ-ۙIE0qv!UM6Njyq+pyKS?D4МA.NaٯʗR)Q՚ןO6WW7@2 t6 /(0~0V;Ԁ?pq'D#Κޯ"ēks9wdO(:j tL5{C? tquXdKjS#Λ1F 1t>Y'"JMo&3PMڋ\?Sc!Iw:80Svb~2_/:9&jV <"]nIa~:z#%0>kfL$OezIrQvGz{.œU) Lw9ٝ)txtiZX}+b>"`x +'O +s*j)LMpu^f@a[6po#v ܞ[ 5¸We޺M^ۛ8&o-5۔L+WD +CDE#n55ݹکRghO*r~@OjflӍBxqX9\.q=0$~&龛Z.1" Z+ U&:tn96B5so1!T:O(~uѧZ`709Yg7GwHye*UQFa2JM+1]hҿp9aS2)̮3O~.s2" ¤.ýL-2,+#F̛H)hƬCLēb!zU+2)˞eĐe8 (˰p&Ȗ!ᚢswٔxֆ\EtXb=?g'gG@]WkeaWڒaSMY䙣ˀ%%Do +b\+l] a} F @{S!$4GS/Hk f4 Lw?GeeWTf?N?G9t n.Cʩ1C] >OuqС.Z.Cƈ?cBH + wKΧ.Kb>$%(',;Hg +3Q*SMGVsQ ̾v;mS՚iW@RWtĂG JœGZ^&ђo hm3-:Q[ fROpFœD˜@On^d~`7GJv$kWt8¢fH0Dl_ΐwm_!%X"Cl ;Dc[khGFy^w߾y}>Sl<50A)Wz>vy#˔)1L1Ec7CZ|2}UJ|(!2d䰭$ALQ JWC aU%_bc0zƞ_"LՒkKww% 2z Ojՠ?鈰ɓt48IO8ܩ,DGeܨLSy!I㫇e : =s5u\c貅T"o>| +^3GK0uӰqdg/S,?%vJ}$ޙZkLɸjuY% 6 iޛg᷀ z\)PGn2+w0RBQ Uk+ +*2xV#鮷G/aɐH 'e+DR a!5_>=w +ZWdX(~e,0<7/)\M3Z^WP2xKlfr8.LdXX- !!Ii$g&^;%MXX `,s)vӱz U &慤>ޥ>e&ѫf'U<@@ F8\HV/ame{DmǠ:b*ٕ"|5d'm˙e:릛-VLgI13ƶ*_Bٰ+d/l=J3x9,Yf1>ITp,dM.摻K:)%c*[A?xDT}ƒ`<3$EhEόF  2$>{w<<-䄈`MqYo^+gk&CDP"A? 9l_U>o\Qv9wQ!4$#P]`Uw%.TC;B3 x7XiS)y<C{!C- y/􍯡נUI*tQ E+Ϣí{7㬾 qט2A/0O1QoiHqd`vD3VR? ~',wWS4Uhvn+&7_qdo'G~]\`,}& "w]ҶQɈT&U8[ݳ53x+7U~o~RF;NsoI}>S.+zM=RH q p|ƇΑ8"2nB'6G@3gRbhOdj)}lՔc~)5_x+7tKwex&ホJ rClZq1V0Uw*WI?`f/N :юB ApV'U DCoYh=LX4*AE"B5HoqVD,, <%^ oz B ,և~ O?L\d %^#%@( +QbnJ.IssUĴV9],\3Pb=$q V|ګ+At8wٞ'&o6NRi.%rIY3C٥OҁBX%C,NlN9_BChJ0@z%X ڝoLKO8{[RiNwTyeU]x=݌=00TM& ?˜{ȟ$[;^ƺDvihMʱE'3}N/QeᖳgvΑz*%1SxQDCEsP]HW)JW)4ǺqmeAOXSsJ z7A~+g5YΟF=&଍$Ovd n0tU:Kɴizf6E0'DUo*CP445h\U}Gu9SNðIfG#xޠS 0nA흘K$/'zX͍~*kA5]Z2r?g5ȨaS>zOZץE]A5N kgNO3?3oNkzQYZ Ƶ`"b@ Vk/i/ '^Eiw f7G]x 4ٸ%Zⷐl-h~ ѿl0n&bqcAlѷ60;쀀~퀍y;@o8 +lho8;gHp;};@pRejvI䞲ZȼcN[DV0};?X [徙vRR~tr3dF;x +OZՀ@ +pOVy-%~6fjn2jrӕkՆmz("˻ST%a눍l%fGm挍C=>bѩ 'DLn ÈBNJ*<{eiilU6_)dƿ +?I("uNp90ؿEl] +/y+̶ƨ|:rᾳN؇V G-7y3[==^/<ʷB96NZۉ_ n3LaP A3XlEE +.d]%<) v_4'PdDs剃uzȪ'iluBIU6>(]%wAnw}#ɺz5I7/ +#0٣JpǪ-Nj\ГZ{ TU3X%U,ф(8~V||"8PA2Ύ7Yyim_)}'$=H}i~>C9|\sAvXq_kF"w7@]X"Uk{lu!HK\j8[i;|6WkgC.us8X P ,"c#X{\fm{\~]yՂo9;؅FE#5D|9Y[^N={1}3h$.\ZEyz^=fz%GNG1FL+oCLέܾ`}cX$DD +.@8PGt!10No~8mXm.$>bCA5SOVM*e?`){~Г_Iu8;-k}3d ;kdD";}öQayKX<5?+R2L:ZY9 kta85rwZ4i<o9U "!+$FOa%10=~z VeQwsYjc+LϚn X)V?nc[*ݳg^{څ hq#-% !oed}ak0DYWv1?kwX9BO3?h0@`E%U{{T--).F` +u h%6ll(cدظO#D_YnQpvݽ)#JP?}P$‚7 GAH6x霚9-Ǯrlf$Y":B󲜼OՔZrߵC' ;!C3®=jWv$ٱ})< {_4P|vm(E;pco{j+X$yzf(!E W; DB0]xA0 zbŚ8N0*yuh֦Y +` ~ՄmZ2(!Q!9jpռ&Ywl,KE­Z󂯝^4Q?^XȧY)MAUSzA!XYս zt0ݙ]G+.$ܭ@p3BP& X5> Ж& +m|e`ܹM#+T4::x]eGQeG +~ԙGO=Ȣm*0T;J)OI:x( R"q(1Pb')1PbPu>>ʼnҒqb |MOq5Ā͓/P AHp"**ScFMjoD/Jd,wR"I޸햌׃yrbȞfC˅QsRLKɓ<@Zl^/- +ΥwUbvXsˊV잒#)i9rU>pT]du#b= +WS- r'YƑֵs:l_,x!c!a8a8һ<];#6z"azKO8{߄-R^_#KrsWF Cj~"~5;+] ;`R4u|&ԑ>jq4N&le\Q:@$Caց?9_K%Xn6GN/dE@N+0h0}BKӸZM4 Tڹ5ja$z:$竱WzAz|'oNSLTQ0i[ӧ @Na(~V}5Go^ܜRD[`݀[w5_,lNJE%bw4#2mSNrU5.LuLj䷋?: gh4]1a-F"xMZlHM Guq e&nnmRNjo # K U Rum%(|R: VPB.gk2TԀ] ~OoqPS;xn#xnŽc̢TL!qtp=UiO"wÄ\}~Dól\o '#g|z_Uҿ  sG֨{a {DZXbCTڹ+6"J}I֊fՕ0Qd-Sdt)XJV,Xaۏ(/Z;:qwgjWzu +~;^^a#9:GÍՋ3U7{`Cv/$QmjS2@Q,8) + ݯ`bp 7uGnRgk{XI +HzD~ +ng] +G> +JVMv>^I6(XP kU,9͙#X8{W>S=gp+oN8TgrDxo+݀'rgIUG0* 2-,/<9nί !ߙYjTK2]{{f׷ak{!]ID9=7ja8kkw߰Z}la44{ +#{qEp;BPZ/onқ jq@ypPwF͇ {(q۰官b; Qz4}R\'O(mXbY6ʏAh!eeؓAI}2 FߓA]^EhYڧ//Zפ>e0w8/C6I`m :{cb(k9&4 GAM?Ւ\[^@;kpTG ]2Sp #M%9ZE>+&8Y}aA$q,h6ԷJ{ZJݥܫ^ K⍵l^mv-h6 m(]-ؼy~kŰ/EcJl am{DR[ %Tx fUZ8ǔ)X4]"^6lym1XpmA:Rs ,>ƋǑ<⢪d&)Zm1, +$ %$gߘq"ZmiX;,D\9,|*bᾐUO7 A߻F釆ws"8ܭA "Dyv"_Zk^D֤H"U܃Qn%<y8 \~-E]ө "ɓa" 8^W|$`ڏJo&j &`Xv8 5aQ &Zb8}q~_D ͂;dL=]PsۓVD;Q&]ǡ^0@%0xjT  ^?YCv"I|lfi8!IdN!\rXgQ>^?\#l0W@ZֳgoԀS`gD+U?e}\S6TZ>- +َ^A6d2 +YYPg mQZ hOW~wG]?YpUz98@qӈOGEP"^C=Wo~jeU[ +) qqnCfPs_)@1 ݨ&StM4=V}7ae4Hqk5]U|j ՛XAR6mv.՞bٸv`bM$A=.Q_iYꒇJc(csA1 7^h]WDW?v\Z9}T(4%! )B.㶙4]vZGFYll񶿢lV4fycRW}+rrm-s>1ܰ +IE&FZceMrvGǦW4yՅVT%Z~j$TxejF&ʤlb`m,JDs0h5!XSXwLW!جq `V &!Fy "A)؈mYUQdk <[uCBp{wA O/%RtZ͋,Ę uݚΎT3۲Gcjъ<Ԫ\˯6=b=g%xu!|H6LE_!dC Hb`+e~uμ8A,}I>"NaP]7,eߵnF,e%h1õ0cJmq<!O*b(X.EE}B0 Fމ8w iQߊᬫѫU-͛iL#er]ƒPJ["UDR!!TiYG6bJAdgZ(z͆2E>w&'l̊v;$J4>..3pBgx~u#MkAYx?Ι?@^P[fwj_^(D \.|)T5`M% +N`xx +!]:J+sҹ0 ߩ8w%oӣu :>hqJLH?${YuS2ҐTPY0&-C(%tMHs]59"8}wc3N&RB"c2=eT }G>! %U ^)L,-[AXN[/jSWl#| +9# +^.1: l__dܞ(mݿ!/駓q#FEdM<%^}z/>F\t$!\&y[6e_ХӌQ%.`צNO)}5|ǣ;o=1řٱCA& Y .ږq3nSf_)Q<)->:]]J +X][V{h–^ +hиn @H[k<ʚ0\g{T]:>yе=] 1~1=']RjAWgM:J(NBah1qDP?ScyDRNb?͙+m|cT@3ז=S5٦n15 g 60Oቫ:wLCNDK0y ʉ I8b*;B!(m "(Bl"O ..L=qq,707T?w-պG$I/s86 +{/,G5By?6K(cARtl[^RY 8tH8Dq0mjPFX68Ʃ_i9N:9r<:fev:t:x͎4;G㰣67;db^P;+!Žz5;De7J 22Kڐ-Ԩpz_12[9($u!(`ӣ[;8P\7;N8#ڛ'*;ݕ7;*v2;ZÎ`G%ǵfEmv̐;LeGxv/a7YBg];ʠwa\v^`ς)a$caRV;S+ep,d3I{gV]$F͝ 73뿿eF~p0-%p਩R.dT>V&di)|Ͱg_;%'=ӜŹ>Z ,lV4 1D0&㜍Dnt\mQ/5WF}|/7Nz{peL X7>Ԏ1k ~;>;K9I׋%Udk=Pguc ڟj:1/͌'9Rfox}œ*T2"JvdIr0tSgpumtH6it8 \uhZOQH˦gS5w=nÒ"AWmyyO؇ 0x2:{"#@S:ԓEoܵϯ^Nn]nc@޵:kywF.*>sT:_ x>#NԼLw}-+p^=7 +\!}]1 PR^$e-i%4y!H YpHOh߫mݨ *sȃ0q8̳–\hM:e1- |v#4`漅+(w sN/Xuaӈ^8E|~#?ʩq +}߭Ȳ|x5k|0=-Qf`}Lv6X뢤2jOip$B霬0tohuӰEBlx #0Iynds>fѻ_!LWwɳl) ԰>{z A$hElcuP;%P[%,FS@64H\{v4a ֟&;5 `w!r͢_ݸ]G z. r4 C6\&w;Lsҋ"U2J@_hi 4 ohGWS,Y+#iMamvJ'h>fU&,^9ay:| hwGۈb;Mj6+^- +[t^5[S[DQj +I +3tQ1L-:ܫ 7rt2OdSG +._ðsȲ\gshL:vJo:XORvt(r_(/j~d F@tN`ovY1= gRš _`6rDRrG5P7@Y#&4K{yGhS=g(3EK mB_liI T^]gHvLe_edzɵ'r2 lc l߫wM?`.xo>ۀ*P,65zJmͮkm&g *wE5J[y5P"\K.鬗|[M52U?+|N&@I:Sǟ?|ZP@^#ftt64je +Y6z\;z ɚwyΰy=&x M|Ť䤶DtoYEZ?l]" ӯqyn~$Wo; ,8OO,{Y}yV y: +n+$!PWG75R7 R{;E :# ̲=eAl# +ٜC'[IװtsfaA:s[F)Bfƻ2Q[+i= "xkToLZ=U}|[BwZ@69i&dzȌ:ϚZo#s6gFh冭6XTt :]8WՋG%)H=*{IWh$via)Baz^ͻ':><|g0"qYt/YJja0l415va8ܬy߲|3ty+.ڔAd5}pMe}Ae&@MFuXh?]%ɕ6pSy8Ϡ ¾9||DH $f֕2y,=++|*:xzx*r٪-"1S0 4ay=L'FJ۰o_Eo%O1*6Nߘ>;E햙{vͯlLYź׉>5S.+I "!3Wb S_(t"aauQt8\l7foBR֫FK&n8xί߼?OBY.ND+%@Xge ,Ɗ*OYZ,~=(K,)%PjP7LmoeZiJK^EQZPZs^iI7tgKˀX"TAA8}!r @+i.:]X8ulM[喊k{DT-d8 WU_HO9:)pja7+vhEog9΋j>g݋jݒ49>.͕f6pXsI`i!b3&(˗/*EuDh(#VN3咡<;Nqcݮ)Es]aޕNMq:qr0^żlYx\&چ!6G9žɖyA/DzN>&Mmϳ8x}¯& )݁PFP6\2ƙz:)PNT| Re&nl%ZroDÖ_T/: %Af7݈7V* `Igp`z[%2Bq Xz}%/eȳ[*IΤ@5kr^| t-6pAHjO^BbCC=CX^K[/ /.2L[~]J%\LV7 +%YKK^Mv͋%}Nņ?Xq r +r\`E|B([B-% +6V($ +a9;I =^TbgU] +/~gcE&3(Ė+ [rkZm8 ~ׇcucgi$xv຾UҮL"&U~MR3 ]5CAG׵)r_ئAgVzSe\ !?]eWDzNv[hRi屘] 1_2 +?aU7%1H2ΆtDENLN d[twqEGV"SV1TTJ]hyEs"$ }8мyq54?aRKt4Uǻ{k`0mRqɿ`V˼?=(S/scIqn{PLXZ>` ]]cE%o88[ӡC4/,mlU˩^ALFӏh3:mY2Tx$I=G ]s!Y5u6QU D}?\j% ]:k{āIx17]Ŧ%^)¿ݭ +VL6=Ʀ赛 |"'dt;NЀCl3EZw8u^p[=|wQ휶FI=UFZPC9rHI^T)v(F0CaF9FN"tQT4Ͽ +zג*;>:l[Vc{;*%G_aQ3 F3}K ؓ`h!,J46IP +=߈!aϢT / K" fLV>Ja}0/`d ѵ4-zN,i -x t8G8a-9˕Oq`.F 6|qje*\v~ +l%%#{*+??H@mt G4eDS\|CkicN Uv,ӆ*nɔUFsOZ~ENF=E z{ZF׳׶*ZR󴝇X}J>I Gt9uS M ))U4[i1<2 iU֕6}&WP77`;Lr :kiFON"bY y$]TC†bc-;7|mՏOħM[G }t1FO:E'U^ _f=Z{nkxswfaK#@` :ŃMcpGNp,S 'M@@xQKs@vOEou2tSqɒVn3+kk~*t+]G뺻oQsN-ksԗ @"lH:ͦT* 5w0ݲ);颹vwwh>f{*Hz4% +2pmђb4 l婊_ XXȀ@hT S5 4Kl'rӁ' T\CӚ UyiƘ j[ʁRM|G eC7Y{kC OOUUfaJ0}vEB ݦbV4/4 +\\Mx*Ŗ$.w4ug1n¢݁nh;1=-gد3&J5lgL10oߜI-ФWP-Y8b13NnՉ3gC.V +d/z0{8њw/(BԒt|&\P׋B#D(\XM!95UtK짜x=x]M빻,fzsUh7Nj?vܱ0.H^Uj.š#t(o[4Ϊ]NW6ic3B$p[EbbCa'?doİm&0Q,ZТbaC!n~/ Mr&.ZS#!q:AiLRV,x Uoכ@bsŹ󷢗5MQ*'LCrέnkt|+5뼧ӥdK'Bƭ9;x lEcps&=.(D!C_M~ՆŁK_pB̥n0/i{=HޝG0܎cT%pC\mf\jQ-h&BtPs:5EE/n딬SZSx>ZWn޸Z9@c3(ˏ|2p䫕Dh vF P ,G+.hecl7:Z je^[+y2[+ܷV&G+t2Sq0d!gLާe\|3BqYHGhI9esM?)4 fY 8 W[H(myC9LUgfm>ϕ?! j:٫$:#?Ue 9/?yu ~w6G$`CQ1}Rw Q; h!b]"`X @T_{,U>r7'"L,9n(l @5! + ?(PtnmY0Bs]E)[f=^}JβEYP2Q@Fa_2\]1BG-M9|zݞ:f I5Ǔ#`J0xyV3VQSf[l3/_va@ܗ@J9ݳ/XDh¿ +?j؀m3Xӿ<pw\w~ҡ QY\VR*/Z}'u|k5lII!d&)mȬ&0Fw #æLgpYۺ??H@>lIvb-{tfuo |JR#]7 V)^^Sz! jR&.QjNRE*[a.{Zaل_¼eCgM%0sj݃kS}k1QB̺( ۣSMw;zR_4{q?aV{3:tΜE= +E̤m"%Q ^C&TB6 U@upخJ/SכjBFrdKw |=5GY0l72.W[8 yɶkVɥW,cC·xE R5u:EnwAN1@mi^ߨj ,qyM jS)zLhV Ӕ4^~|4O,Zl +Py=e&egLr_@B{{ {U.qP)h½qV},W 5o'׶ĵ ) I6};6BK3.#Ir2RJv}5].ovo'lUCoXD KʆCa]s.&oRCGN@81a+;nPwmՆB߮jcokm +MSq^̡0/p09AkwS״'fFR +eѺm(rm|U~%E,<*B(wŅ7_m1Rї~./J49cŒ: 䥮ڳتb*bwo)+b06[nDubU[wi+/!5< X@5UӋ2YYE/vJ`Y[$[^fWʊ^4f'ڥl*ؒQmd=]MXj+vRG[=`})w3Z食w+5Gyw(9眑nqˡwܠww@d5xxPcq$-s,WWWGW/j,_ h XqEp| sikTku]ϩC}/OW7?ߠf1v!z BNN[<`n+!Zf;ڼLQ{'!,mO1h9rLBܣvƸO{f0ftƠEj1!1".QOҬi5k32hrScU@6'ZkڬF\Z H0R" ANV础#͟NR@cl]Hْz`"%dk+!["\cҮP1^n,[Qؼ:uӫ ,0%#v4KBPL f'/% +>JTZoܜE_B3U5n3<]Ff/8q'նKDaUjb鳇arZ^WVRk-C~iSEЗ83{M=Ǖ"Dy?r. Y~`l0p`qȩO~pUzܙ?@&=>O_F4 Zc]EkK%W ssy܈7O+KXCƙ?jJjfͷقDSs}СUe{~s5T JqpmmCE?<_r2Ã_,s|y땕/щd/PܴG>~c+>!<xS^;8^E5Dz/zRjuJT׍Gz;tr_U;U.%د`i5HK9]z KMo-e +.jzf` +3=($af:9[!ITmE0VWpӷe^4g a-ҧi0jɵcs!>d`ªul[-E1lF-[%Oe.Z!Rj1אG)vyG Mg1c`_5nȻer7QIjXc.ac ~uvu|o ƒZP}e=lؗkcۉQl} 3!ՁzjҰZ`erpKҵ r8q(}Y5WZ]*mb[rqMї +W 7o7;NjRZY5'{[F^W) 5z!^Ө٤Ƕ=('}k r_㼱f;8ĤWVg58Z_?̧*i+~zQ~^xXN&*}ĉwsN.qz~E{@?d|D{Jyۅ)`a-4QQUqL?$O~\BЄF#yfJ.f^/Cmln1Bl|=.,]6a=~o)+vCЍಯIcw=<`};-'->l\D$0d9_!&7% %twXb/ PH4ǩ}.ۆbOGŦfC:BlC6]{vӢk= jT[A:84%)@>ʈbQ#͔s +m2!d uKSlJ'BSZlR9w2:QDE2>cJ!`;c +̹4z ++ Zq !}Tdi+y`Nx?;$D:LbRi N6Jw~BL{Rb@(1<G} 2DW,ϼ)2\Y)onl1w㪳E BX̝oKܩD8uii}kIO<3݄h*hJ!x>_]] uqȻRxЛ^olmz MG,,ëB09nLx'Aoq݀Z9]̙c6 _zL dgB6[C^؊_TxR5KPw]hBrjMU|\(Z +H:"Nj֞R-c״c$o `9*[iKAj$\gXhYsY놢5m@zKΌ*%0kFnuiYV+#Mq暳qt.X$*X"o,BϮOj!yZ2sg@;d:mGoLS c06c6N*-n+m"5h,VrL&9ȚFs +4]bkj4ixlǺq4Dȓ +3#81hYr мCi9)pee> +&PP8JU8y{P[>rT@׾~Ng3s=AZ L1wA % jaky +S k|Mh's#XRȬ1O#]($RyAL|p˾I[ @hغS.ߎf;s?\̍C==sڣblA}0 AE0.x l*PAߤ^4Ek }ѹ[38P{w=lN wk$%Qyevջ>賩>\!ռFKxF{;xn{]ۆ!PY ct#}d' ԡ5`W6*j[Eo7BԨuTo7H7j>(묅+IqjR8G72 }|>p$2hM{)VyPd7Ù1B[=ٌ_kT+$+sBG`:8I<2ƈL.³+ц8:2Tr0yYX@}Kz5V>r(m!JpfP@9q"?:Ə־KJ MPëy5PǮU3=mO )`_:iֱr ]~8vSo9FdA G,։ +] 'd@Qj>>Q9O`ZPQzB~,|}}9tVe + +P̅Am9@7j.4jO$ZV!!lms+ѢX[2RW3RFo2фo1=*JhOM>X,*Bhu:Ď mA}+Qc1Tă͑n5[/1]rvtبڍzǘ` g}?qZbJ _sw|>w?qaq:F9e} +P2/vy:*T5l~̡ <|hח}Ǘ|n`^|Vd9B>}Ȩ+vϯᖗLQ12*8V";s +YH*;5Q{`wܝPhVVay-ᡆF㌃-k!{C]%Iv8lS@M舿7}EG 87rhۦ*XV؎Fc+IBI^׷TT d۟:'a2[1|h ~ug# Z9N<:0n]-KfP1g]4inֆ\vUÜ}0~>X|3GF?PCA-hlBxt +A.,ͪrdz_ ~B0C wxu&FBSیPBO"sE4+q!לU/ {ƅUjkM(ʆ6j*)E=&v +,ሦf]: ~~h$F+.[E;o~2є<߶B n6c0*`48880 Ѳ앑D9g20vB~U6aX`6]#lykNg.%k_ޝ|ۿxK1iyk{}ak@N9I)O [tnekW ռO' xQdۇ qC^_'0kagD-@aJCrXDg~P*'=#}āa,Lxd +ezz]'& ѤRk}Z t/Lx#;/)=Xu".# 'Ze-nz7 ɶ8w'"zc(0PnףE^U2 SRh՝j~Gf㑟a#5<7ș\^;I\#RKD:6VL2tdyYh8+;fFvisO#6ݼl&4]o&jD 9Ǽ>e2seֈlgh5:e9|k<+gu' zɴ-Ƞ >nuTwk=D1;ݗ@IƗ{"<ԐiWg +:}} kԐW>(3omCƆacٲvΖq/d{pfzu<CUec@LVP V`eg p v*XX :@wUI`lnE6jcNKf&_IM>;O`^g縱Qo*Ρo0owTkP6A'}{tYzuԵ {%iVmGitX+RcUx ZHq.6#ovg؁ah?NtȆobbznXgG=^U`Dl9Jf_pdS9y)fXT،-@;'Ihἄ]b P$LY*yb[i1VC7v1 W6b԰Vmׄ3P?JM< |6ŁJzuuCպz0(]RTȊ }8'{0)akpMn^"|Q<3fڱ)GȘ~f /˯$%Xa+ 7OTuH i24 'Ĥwŵ(W] @Zӏ*Vh~uXA%H4pىR`Ty70a,9$""0(YNM'?l,_}mJ q]dCObêXa&6g0g:Zf+^Hz447P0P[]yN)-(+p@D`i6qUΘ+t͊` #ҏ2%d/xkìʒN80Bը|JNA.c9qzt~¶ȄG^J*:z Jå +n{qX8ArM⎌e^NZR:+0J✣IFFx} +-|-&FN@^ )nP0eUB$fֻbeN1r 4_<ث:.gv?9(, + +xo(n7`b @>*5{sV1§ dfLmw+rwt#ACɥ&c1y~gр~Y %JF>fxT(./5/) "W:ePBX0æˢ ء6ֱEs^Rr hUsO:|MO:tǥz9JHa=dŊ6_:CO&L֝@v~sZEb>JfZ`PGu9\Ly(7ƿ_Yɯ~7œmE%^+!ZYN3 ͍3/C+ t1hU^5ӂ#G + +ord7qcLΑ,jX X|`b/ 5FdGbqbK:iH#vV[CCp|vXv:O/i9,}MD§ڣ>~jw%J9}+2\ˣ3TO yϹpDY#p'  {{/o=n(w8V΍w6x7"CD'.hwR2=0*ɍ$GwB}(YRUd8}5UP|eV1 \pQl( Y0mw\Xm]`~]MhT\6u펫CE7wH #F);7`%յ>fj 1l`GB[ihCnWP,"UmuёyG hT +ro-Lд˧m301D mzM]wEq}]b1=(b 0 [BϺ%CzI >\Yg\}M~+ "CݿG{[wՆٔdq߉9$#t՜p] d譱a!1keOtg1U嗉b +,dfR,Edg=Bzp":b}=Z,HPzBuwBo焠] E)J nld$Iß˳ a{VrK3ɶ\|8JMmIZwkB>6<H4{wt&Mm= 8s끯Lۣm74[:A-5S3 T|.8O@U{aM^dDĮS69} +ggyw3M/!5Lh=k7o/!!1ѿNP +TIX6*fg6=??ټ#9&7O7b|4LUa0x,#*4e Ma / 7MͮG! 1 +7mKoDsZ^G 鬻s&֣ܮيO3}{w̺C;_eD'ǾJG}f.=깁h5M-n=aa v)aN{~>ÖMe{D'~ g!vδ#v -7mX6t5!ޚ~< Ma_y:jˡ&^'=Zh>mވH!Nql4&:γ w)"9Nlu:B\@v&z!seFtD9#Uwsݥ)VOpg-r1K($_|u|437zQ^p=C\x0f\Y +L"ǂ,YbxW` vB+`ZIC:>t<7պi<߿&MRwG?29P|nR {du2]]vWѧpǜpȫtI^b JfQͳȹا@;Ӽ!p,cŃlse?\౅ ZXOWXUB—AFzGIc%߅_JaBR]1MC\,`Qja 6U jDVOV`@BO?_0+KPF#bS%/dwtC%eT^ + zYB u(c%JH8q}:%M$Z ֢sX˶n|>O2 >t5kJN=ЂGvz;'0g*``m`TB' +<.~FZ7u_׎ߡ~߹/7h~6oEar#ŽoOZ̨ D7DG#EƛEx#a@M3IPoɵ-YfٟIy8HӀAx'4Y +XqU|;@癘/&3%o^T> Lnޢ㜶U&BHy:frq,W^Qv\b_]\hgtwˠ6_r7bkf+7Ɂ:3oz yۧf&Õ!ӡn +Ɩ ,;ǰL0ycVMcVi#b^t  +a;^7זyS8ijjtWuZ|~[-cs>=巉>GݘJ[Dl:L_bQΞ,$XO;(緜xWR"DμKiJؤ:^J$bh +\~ / m)xDdF@VeNV_]SJd?_8 >(_]K>Ff__yӅK0,f5Yk,6\o~)f)$$&q&!%22'*yA-g#4z~9AB[wZӈK<)F Fs+é J7G‚hX 4Cˈ^wt94tO0Qo˲"q͒NjPGhc^9LҢ-53ɻdoCkld< ~M18)^野fT +/h%KGA՘S 8RY,^]M<9Gnö]~iM.;e^ W6PXaT)#Z{PcVcB[o +d[_[R װ}n8 f +Tg 7Q)8c0{ICr*rfƼZsuK-nE5\!ZBg̙3d$,7鑇w;!9i4FW1O;P*}3m#u #- VDeXߐpy•/r]iҸ!fԏ]hmUW>vEY5*^CQi` _&5f6kpay8z׺tcw+CpX ԙWax^~ݱmͤOC $"8X()2qRbR8ǥA2|!~zk֬ ;:sm@'$^t U θuf~[mUgwH!JUZ ؿO<@~;qޣۥ:l-&YiCn~(pZrg*V:P]?.-B.ZK%PfdL{Sڌ$ƒ~֚za{uUj\SZq*^ȲSEMRLu&arBz@h(t08dG tنճdNƇlNeYl_6R`#T<(شbQl)6WYvߏfRlZw*68v* V2 mY?і~#^LR*#B>]|+VΖ{+*# +\V57V 5BpŖɥo};V̸c@VW5ViWUJ/f}>JǻT"msYٝ +}!Qj^UYQow睫xUzTEwYfUN-Sl2e &=\w";WquLuS<?od*%m׺Vz=.[>2D f'OkY h]!r@͘$ &Shk/7Rע୻!!ߌN-L%@lV@cy,I}/l"jB27F\$lZ]7Ru3ғ[WGyڜgSQଛ=&|zQ/MA~}_=]ۭ `M \oC[% I6Li { JQ7@g.uZ>kH]3 + ?s>Dm Bv&|X.b>㷏w73҇w87P/A40/ejj0LiZ9S]%bI_Ҽx?^eP1[6v^<hZ%ȣ:CA6CўypgSE4=ph' p]Ο&Lwщ +;m*SɇȬR $6ඃu%IP@)egK~һT1A5 :i~@o=G)+[_)L G>N,jSExa.xzC3kQ CܚY/㩥-[(zpBD.K'VUU ǀ{:"YuV +E ÑxH彗1yψDV}`Qw*;ډy ԡ&rqsc)}ÃSBrD\2P=H>9;.rnn@^6$4D4+yqNcg%/[' Û%ZXpHR-@Z 2k!^]^<ῌɊ![ R!Ac"j~um +n!Z|PۅQ9m! үj +mV dz+u'U]hš](Lxxu !"LJ͡ pJvns-}ԟj`rIk1#EZik*TVwJ㲰|+)ԆdM*k+^ϲmY+)NԢJf(_쓪i[X8 Hw;\ {.xkli.ʧsiQ#ͻBUPQmʻ0`rHwm=cRdIۮ4KٶYr{}[(fP~U9HZ,9oCь4RضI +Es tX4Ī;j - %+bNߦ!EP-i}U(ݫ-sѶ-9ӿJ`?/BI]%ZW^tWkn֐BnYWGwNmw,FbJq=򪺑9odn +䌹+.AC\. Cflەh +=`;KH\``CE󔐦eQm^RA)cɻkViԞ^Ulo@뜷ڳ()F(mORr6Cz]hRٹ-BzoBct5ǪЪPК0 +f1^ʻ^zMOurڡ~Mjv(L_;])ՀU;kQϾ`mGH5%AaT_C@wWсpܲ6xYS($W(?0nLf3_nQ9oNK;53{u\Daz-岩CQBao-zDK%yWيy+mE]JpO}P!J4[L{M+,D*f Pv"m3;: ?"-[Q*MS~ζ‡ +C@a#,YQS(%')MqFV\>#&U OMXǷIx7"존b36lNmby58mˉ5fdN˒;Ǭ(@ 3 'U)/WrteHX\i*0cj3zq\I^A +|WNJ/*‹M0bq4߃.svf›cRG`fM + V?Wf=)y=r0rhH !Fw!JnoKzCClc Q R*jHEs{w9{[W/;f Ac[׸U f4Ƀf.νD=dp Zo}FPk2k[]Vm]Lأ; _L1e m:{VA.P+ .&M03PxS8ꪉ٤dT7}`&N(Ja=iKkhqoB?-۷j{g+49n1P+5,ͽr1_*@:A E,*P@]Œ7Gb :7h;฾HU>G24XXa I :W* }{JӉhV!`3"P@T8Ip+@\>WF!>Ao", dνm`+KdH#Hw!zEv%g+1a,9˴k M6Y_gUIϫ",,≄NVi`5?gzIɑ ibkmoݽ2H2mVix;LJkF,p;6BOq_Ҵy/IjEOK {ksd:pFKtP٥0 0z%ͧV>mGDC6 T9h;5<#c)VǫALhmKVg6Zԙ"Hd cu։!: j3CL+Y;޶OA\i ޭYxrg'8O[bDQR-5峺uZmbϬ.ȞE>G ^݋?xq?3bqP.*Ộ_ +|kW:ȗ? P@A‡ 4w{=Fq DqXiWRݻtb=Ԟ!<ݳYgY*DjiJ9Q>&X!1G>KyC yz*NrqO霟5~ =|ͣ{S1-q/\D&9oe{Wt= OFU=py@= u7x5xua{3@D?C˰({2de졲z~4gzҖ ̧z8Qj54 ̽VUS=4#{h.%{{>C) 䀲hm%v:7 +q +E;OW[$W4~CC}jirNq\Na&LcD]w\]1Phky̎?>bE &Ep 4P I >k~:g:8A1E~O4Ȟ;OV(ojsVCkv5so061=vk#5ya!il\E+@ ;WН07*Ԫ)V^,vbı)“xplj@ۭ #31[ʽť)5 *8C(w2:5_5 +mϖA&nHhTN*m#Vd|r,߆dz6E+7KvVv#N <Pp}Eǐ{R@G2Ȍ|ץw8JQ)rH~Q%R[ xÚ,ł04QDFZmww}PtD뜍véZ~sq.%N1}+cyRëכ~wzrV{қ*RHL=`7?IsZ?顶7(yxR˜gӍ֯x .Q>۽~~Si7 ++ҋ + +#+o"فzQxKǥ(a +3]*vRbQ_K!eTPK>l*P!l{GP~9LZ_LGÄ}3 ~LXL <8˄xe{-qbpɗ taL(p//tny[/vϽLcΪ &.G?1wJʡ$Mb vRK%mpbWxjYfn1#v"_D G e0"m5N<iYI @wNΰ#,8|I㛰ͰLZT5®B=4g +ےJ +OY`snni$C7Ur{K7 i_Y-/ዬQ! 2SF]gN!d*@Eޠ[[,+Ӫ1}C$A}Tw<|^Eリ#(YUuֱ i% $Zi͢84١37JrgҌC&hIi]h n+C86]f?u̗?x.3bO/>ŒCo Ol5{ss؆IVbȉ421lPZ݌Awo#itӀ-iMe([{3VRHp߃ہY`1KYi Ls=Mlؖ-bvԢYY]q91o138}7|žZWD*Rviu6W?'j'˟ g,/z_85򀤘uV9K .ɛd(M2x8U]TݴſM7úUU+skΨ44m=Me9!gymKcUVvӬw25iz3 ~RQY,SMNB%MySxl)ztiJ7[HVb AE(#7(9:JeI$]ݳ/m5r(gR㗥&a]xbq~荥:s(nP0:_25m7[z{>{2@w5(Ϊ[3'+$ʹC/TvY(MuDw_C>6ü,J^0G64ke^4}% [JrEj~,X[8%IQ ZңR}N +>}T쌪?Фȱ` ݔ;[c҃r~ɯjm +P*[Dcv?|UޣmFή;͐6׀ %:AsI_ E8 *>ov"n#OXuؾ=CIJ8fmHs,XeD+oMf7ĩjAKQue_F[*z!J+wт![*ܖwm&kaCwj59o/pFkۢO!V9/r[ifdR'r0ͻMYėXe:+j -3 FQS<ca`☒Fݖs;{ĕh)PlEZH0Jlm1B\@_P׼F^0ZъZd#5j*<$pznG[H N5꺆uJrԬ/]CR.J)u,Kq%FײV^`*֐k?mG_WgpmWЭd&e6ҠXaj[Qko(fgSuy56ᄁfv(.DjDbnjZG c۞Rgc*H'CcreJ| - tK|TӁ.P03nI~՗m +PW.[%8B+7=9,ksBStfSS8jɒ$a9E^߳n4oI8"6ݩB4fU5:׿40h$m:HwF0/^opOSxh70:@I]8EM.%G`@OHI~xkEDB9歛<"ᩦjs}*kNm-Ȩck Wi!SY <\Bشq\,٦B?*(t +ˬAU MeGClTCWA0HgE~EM~*;5 -)_~F.X '@Ĩ h_qA %-G8]1QDx=Z|@;6=IU_N4)IyN6C[nץNڄym'l#暫zJv"->niXN] +aqUldԖX$D&NrEዺ&+C$N&=%sשּׁ^`IAGKT7}OqGYj.wfU0h~|F! Vw35@u(Xk20Ɏҩ8BlyV͋<v1!/qȎ!-/rQN&$:$B5AT"N4ij~cPXrP(HՆH\~4Nuf0t~su& @C{S()PJ"<&dpađɍq +zyN{ C"so}7tK^KZ)x2,(\-22.&%Byu#q,(AGoS Ż % i^e*dd >'`v^%K2QF'"$ $5 kdHё%h\[[ǝqN/)6)7LV`чTWHI'4OR1HuV-u VoB{@$ohN&tdjw\I9尣!fd[h~rZwX 9Uk*W87+@媄㸞v +/I՘J[hr3aWsNԙC^'xgl`}59)[aک{-YMͺx5oƎ|]W%G_וy]W =)o=-(1;WA]eޯzlWa.vT,$g.ѨY*@]4&خn|n0"kR]w!js +,\+庈\z6|\wN. $ıDy`m \.^ YC e@ (N.ަl"OnBQB*߅c{Ev .& s923" +يvE&M>A]+ieBW+Qeg!7y,G$ Z@\^@u^MƵ^@Obz9/y9z2^#>F($$P"uzvMzp^(J/=yzp[ /=N,t^䘬$ڂ"ZY/y6Y/wxY/}W՚8@xx[G/> fMT3QB'(M'A1mvNiMv\AcЍ-AtvN&!9HF1g+0y}O1;{N%mg)u5CekoX`{Ud ¯o] +2c*VB0DbBԮ(#l )$ Bެ@}DW N[r)ةFcg@|50NLFcBH4n%o"HʠÓL] tk-NJׄ5ȖiB6$Q,ܮFJm ڣaPPiօ |һY9:WH…Cb4uY$Lut ^R2D +Eo 3xRِ fT%2Ն$fB@bQz:UuUcź;?1??F~4D6tWb/-P?YF\kI,d%|-M'R*AaMTG/hdxAq_ R7v(.>>s_EA>w{"ՔStu`$ι޾pl(=w-Ĺ4y>ӟs.H +=/3t6YM`"!f '׻A , `r9-}6갤d %I"K/,2,Rh ң=$UrzzRJsEM$WN1-v.Q<{ѷ6*jb^z*%-Yf@~= _]kB0(I˨5mw#y.H5t"uf*kc)]E@HmRQG3:wcʦ2><\}&,!74~C,_Rd*@5Kרe_ p@T.T%.3Q;?~# lqtpެtk3nИ MOL%T/礟,SS]˻,Ro!1n)wiv" ZuװQ+iE7^<$T ˏ( +-{]T֡=Nu!i,,a_X^3WꠄF +%~* x+ƏT&Quk#MUQU֯͹ٜtޖw%II#8ia}Lݪu|`(TD jR>XyQ<4ȋ zJHn|`siua`5q ȃ#drQC~mܑvPae%=<=x#!0ᤞ^aWw2$ܝjw/tzcVsD{x3>V׌;v +'EIQbIs2s _y$Ҫo\K]p]#)!S&BD^>=Yv@js2-{&|-GxL:RoJrāGLEѻL\jmHk(r}u3S`ȶ>ȶ|eQ+gtnޮ[/W{d&{Pr*q:neGwsn( +F=gxd2K`p @\g$r) ޖc>4Ol`Z[LYst$nZE{sw Yͻ:цsK(pv\- Ӭ: +6>8UEoed❗zg\u;tDϗIU!g~ÁCv h6&sԱР4#EzoE!ՓW >7YK^xW Wۯ~y6OO>q+*8Zu!4V?MQ]# w#Qr7NS:nHbJu4z_"ԷD\%5ħNb]v0%_?Udj}9D@1| ;az=ʂGRYaUt6:( _%>mm^ +7h: 0#/ྏzQC."鄸' +E&8NP + gt,gz<`,ޞ/c6~=avSU1u-Z6uxQCMIW>k;hOk:#[ c7'l ^'ѽ_W): 1u;{"+-м;uk\X:-O.&06y,۽mb$e~<Dcٞrv2mt̥^OdtZӗz:n<=9LS]ЯA]yX:Ke[:HK ϑce?]d2TQ]P GKz|0TUMUAђ4MpX,TCT_OP7JGd2tE}!HEm?\yA4,yɊ!<: ~}i:8.#l*SVcYԱJ>SrR2J*Jj+]"Qt2xsFSMkRWBv2D>8iv&fO"]*M*%&%K4>S%mALbgagYAZwO^ϐ§fsIx`<.g>zt6x5oF#+wuKMbTh+0G\[KB(? JgX=9eb|s) 2][ +]<PA4@=B+gV(g]@&y7ƲB>Q<#]} +S-UoS2,Aj E TXzE2ebGDҷK=nzv!90⅓%>|z ޺&ɛ8\(hʐ5"Aj|%z-n šZgNȬ e8Lk2T7}Y=V[|XcUĈTlS RQS6 xPDk[F3+`*@ɫ,F<`8T[RCVܬqE9`s[>cvff uͣW-%:XGLC%9CA X( uP.6%(2}LNœfH b*G Pd| QHa잁9WYj-C-8tw (c"P{=R]J)ٙE ʮjecm<K9_ }115fݔ|fS;zkW4]N:1}:#.MzAjޒQC2\KTfU,5}W37qJ29Rk\Ɏ{2#ơ67_? +. ITjۤÀ9h/w. >",Z$aAb h*܇&Vz*lgFpȍXxDjC3Xu[YxT<*(Tk]XF~K\{L,unj&0u4$j7dd}*҈jd8zeIKCרj╇r!6tWIne7 nhF,ErE/> "hFQ,4F9JLؓ=IT +-^Th6lT3-$hJ ?Ѵ>z4mhZBp-J;)E ok .$:!aqJ~.vBT=?+'ԅ9׼wĠ3XYLC<8P5Wʆ$vs.0@Z'.{ܯ Np^֣8ߡV3̪l×VeGA [~Lwϭ,p_J՗ڥH)Z!WuAV feC^,kGlׂf +00}B%vew$9/UwP1u{("[7MSk~D$zyxe(peJP^swg ⥘wLPIKA%K&Y$(fb"ڕI O,;Pf̌J{Ed7I&aDPt ٞnK;ls]/ +ӎAqZ3Դ}.XE;pE^&\oEy +\s xf!aèy $GDgҫu{.$KR׆0,i9J9̠j4Հg?QN X|H0.2@)yL]sjpԜ5d)'l3zvNkyJpXKp¬JA^r\%.9<(K$vXɡC(wɁ$|NQvQgCS:U::O߭+WqW:Ʃ:%e5:U"W$s8~5{7F+:O9+::VGRoz?EG*SRNLzg 踝NqKz9w$Tf,@'1Zӕ2 =Y1XSs?V(~:Ti:^~DmTX}CUXW"|ǿ+ɵ+?hgIB{>o7@><$`%On!IF?I:t#1~P~+ԏF_e؍0T#Hc~x3?3?՞a~zc"7vfm?{?$~ܼxXngx߼O|x ;|xו%>hzҾmS)PZ^7O??~HWqkx-8NؕɑF+w+0xJѯ?A>2RR:E@}4ʒ7*8bz /!]U(3`HH!"V4"HQ|Q8o%@KIBء?Bo|# + +x\A6P/BC(9A;_]aCsGh\: +ɐ t wS+TQQG4uk[_f嗁$F{$4ʉ_*hP*oPlB mgQ (8 s9vM*@;抓∶ӖAkGe<)֨(ՔՃ̲;-\Q?qma<[Y&+RL0D :-Wj~Z%d <3g$R%uJOX_\UKL[(J.01h0 MV뱬☉߽I'DuXgJ +{4hiBsV#©o|+3"ǀlؼ -Lp\ޘ4=_ 5."؆В3/bχu..S"o{o_<#ndJ[O',@17hBQV:(0׉ ?%m?/ud"cؠؓ-j#l71+?IyAA{le/ FfR^d j#xFmZ{vB`wɹB8]6~"`pcd}Z5 VքTdUd*|⏕[vVM[x͆2]Sy땵J`]ݥyyP=xp/z]n8t.}K~i1+}I#QV|O=xxz!i[f7u|OÕ[Cј:c)]AҊ|R66iz }2ﲧY˅L&g+H&3y`6[`P:[x-{/̓B(zu* q+\3K7!~M.Ƕ2 +Hi}XkwJ44"Wɭg]?NEK${E[o h6EycW#43`?z=Kb*eSTvx˭.}k+oRxp^.o1>: nj~;*]Z&$LN{?lU)]5MK9~\"`v +^h#LD"4gV4GJ E8蕎я]hKCejWmC2wj!N SG!ƦWBMx Ա +Q`q?T3E౑) ; S G&#8+*dKH2ѐ"fvTCJRM7csJQKXڶiE +Ыdk~uz(sk`pUEw_2 +r҈0ZޮCcoG++ x0WP%|'rN>5ƈO,$ )8n> IȀ?i԰Wx{HR@j}0+KSLܻž0C{񂤡z&T/N ̖/蒞B N:X ur*,bNwQmeuܔ[j7|ZA_0l;OBu ,X"Gu)ݧ %[HFrCݺv +W0)\9pGjI! Z%xKf1raYuPgJ<,\ }6ɘPQ4Q>UQg~d4O6ZE 'kg>ɵdaЁ Pfrjl\*r bRK)]QSY2|Hz6l<6Z1i+mVopH!t2BtFXP|xү玲t?cPL(ĝjhQ15<輱P7oqAH(4_x:X?\O*TSodY &XUdi#!i5;3 O3OiejmbV2mJ!x@mѓAJ8IiTUmS]z_S<2cieR!MR#k(MwgDŽh 9Tѣ{Xh+}shZ 㘋4ªDӴ'<׼yH TǙN:u'Pai81!jg sXVe1VKJ[RnckWKKLRSDHQQ)g2V%4C5%Qiw|Zbh-w7v4 F +^P$n]0Ugk9h{cr~lRoxŌ|C%Km,̃ƨx^8wV{,KBz9j|θZRL^n>/F)qnqq,鷰Qv5~gaxI!v‹QFۈ@X$zP2W&a4tsnb6X=PGִh&! fxBj +k#~ܮ6-omLFQiiu:҈Z'Iːr6,i +YЦǑ4HJH?Ɛ8n/[Ѷ2${nsK#@MRHt*G%BS/4zH/QGK4Dwq-L-2+pҨ׭&H#og-vW{ڈu@x~om4^ad8ȃSUm9do_(##%t:*c|S0rGJwox5 UYi8R zaEÍ: Oh 60 h^^ɰkж+{nAV +J1r$mph؛6VF@ ;/cSHoJEB;)L8,e=ZyQYjh% (?껒+³ӁązSoOIe!>wK6V_"mVܽڶm:#??2ċ $za A"za za=Ȱ^dX/2֓ I$C/2ċ &Co2ē CO2ă "C?/2ċ &dgX]XH۪imw| Wb3x`V(:7/g)\l++IXtAcPTftY4(Y6"ZaˇVyqNLQw 0M +?ڔx;RMJww| +gsvm` áYc<^/ѽ^_Æ|sFhuUVk92",Utj"NPRŭ;'YPָBND6g䜨 bSF*ߧMՓ RguW{^bOǑq)>YT 8.o4ʧ4 yK5qׂSΡ:>d5~ +S5U̹dވwA_Cz@ 0ϴÄ+E%BZ_; WH?O^W `ڙ?{@ kss@ +nBJޅ,eP@$"qSQT"dעtLi83]:y亹;S#.$;6z;?X9ǐT96YgcuohRE9xYDok b}[4fK7 YuXϤ\$Xfh46'#h85xbA7g1ju7TAm 4DjɝgƘw屖7,`8య!DI4\P%6$8f7'^kUaiA)D +Dމs::PR}Q>Cf6"}@9!@Rr.nTW/J,SOս-W-,Key)rX$9Da]m]XdNc$,g euˎ{ilVA' }]t)Q`cbi%Z]eѴJ_zU/t&buohl"p M,֢]&Iv0 NQ(‹_ tt9*.WΫ{T5fFqDȂ +v%ru@+xmqO\r땤\`~d>`}6~t`UEo;S0/ӡ(V(Cbq<C hLD d҂ nKAL6tyROߜp1)TR[FF->X6Q Ia:t_'kdb^Ƀ|5_,_6PX "62e di(jPcmxw(lB=#,6&2I$ ,Wd9T83VB}R6f8 K$cHvYM nmH*wpvnu_Uu 84CTIs=ێlj%׷m~p 0}Dr䏣x֤{ C1%Ԟ5j@=ʙ,25 ${;"c-]ȩ95-wꇄ[Eǥgb#L{UxN,(Ν}F%rp>X@'gbב4FNldK;\+uci=#;/}:,|2BtN4iA@0ÀۙkP0*{^2UL`PPCYyx;|ju?CE#dt CC~(J"0@:6Ͷ&d q,@z M|t\ ځZNkWiC4t~0J`:D]|y0OFW 9)LbQZb:!L{0c'C]Lۍ鰼tXvb:,m$z&L{\L!wb:}p\͊ +g+x@=;(kzvACTRXzHd:$+D)_%CxHzCꡄuvC5?^&P {:}j&oTmx \^S^RgLY:x(=TPhPL؄5ƾI\C]l(x FTz;p/o^ +=NE7]sSOTineZnK8lf%3Z͛j˹"ϚڼQ ^3Jw\8벦y˹h":XΑ3w:}KQ`SGԧT+禦@5KE^yô(F/i0VR {ڳ7m, +T|~{ZhQ 3s ++-8@)}HO{>Wm6 [qYgM:jtzyiЅ }W&±#²!ֶB?] Z8.hsj%Zd/YaiY7u0%sCsDŽQNpphX*J#Ai]uEw, 2Sv0iˢFL-u0ɛ$'|`̤2.G AiלרM9ޠx <N:15u-7~s\G;0<ïtu9h'NG7}u?}-̣Wv[]Q(uECy1ޥjCI$DWߑ 0-Pjkw~X~f }8\5?4kR1xbwKKVsczsF\өj5ztRyj(k‡9?wLE`㾋cu3$Cά"_*dvzL'Cy8IӐ_!PKDf|(`E;CF۹S)fh4!6"`AT5fe]!MhÒ8nYi^rh򗲋W4M%"I64Hμ a`&|03tНPs3q5$13bs]|^&r|=?3ⴆ(|J>g},=ۖKERC.+dQ' b q35)LU +A*,f{.6fO+!8d#[,_2&4!vӰ&" ITlAfl:tsOC+4U<rsޘfPJj om֫G5\Zp5CUi3":*%˞vݍ$TԬXPw/H +isdo 'H/޴[sԁdLkhuܽrw86)?jgڳXSqm@S獏ɜ{ 7NtPl}$x- ȸ\2f=kY/l<]L_a0On-g^hqlixEh`Im$/dumI"4± M/D<_~dsF) xO1dIlx /EgGr`}=44JQYujV܈rlEl56O[5 +xq}/on3t?>W(:TVհDiȶ٨GF\GhIBK'CYγљ/1MP:SڤPË+>OIP!m_r_eTI3ze2ұҾ:ʹl )qޡ[vAꠡj"V$tBMJgOz|$) ;Zx. nLɋ#tG^W s~h >uB7 f}nPdlt`Y>X|짐(s%qd.C/K:X̢% ]tYQ=$t7%JJ`qJW`mt JߺB[GҌu,6=s:(#u餅."l|(L@=bb/W2en xV.el*Mag\Y|e,ucum jVE&M]9*C]\ܽrw+w<`w Ӭw]KԻ@-ѻD6`y+kw\Ի‹qֻa.C|.~e8jԪ]rգvU]]i]KT"8rhđy ʹޥ=G)-iIG|Rg`ܬG2{!6"xuC+s^Ze=X@Pl!n3g|wj?eWA;碥HhW0P ;E5Ք&(JGv)3jβniRWJ:unyƽԎ4WEeQ=<@eNu[~~Q(?Ͱ=xjۘ| .:ScS$r휆:)ZDA Mͺ&ViP`]c&hPA-c56T<ĉ 4#zp~ } ֧F>aMI+JW4swV2HSOuRIxdLc,ŷ<W? ;"0a(g".^#^9/aDf0s?K;GH`DfY՜*D]94fecz_V1_jV~Wٹ! nw (jƼ +k(L~NV aު.${U/O:zDW̧M4t¡щ+SB;Ө^%԰eyp+N .44~y%(q"5Ė/eJLo=_RB\s^U^$&kȢW0s`TʶܦK]!nx7U Mp D|8ɰ7Ro/]$ձӂZKZir|xS5bՙoDFX-P됧* ˋQ֖_Qu93M乘+V2;Y-)rK*>xU˷pڜԓޗTRZy4lK9Ec[RI' Ƕdp.)FUκK!9H6,5qSr%3q֥SOuu)$.,LM_LYg2w.L I|aaJd'#Hb_'I.yxBrwƂI5RcA/1S IK,_sAqdK<ޞUa\88BHb. {ż^yiՊ#fƂ6Z g,>cH8<F:cA%B&6rR`vk*gLY6`9#T0q›*'I1S3L2L3֖`dOT0Kj;=?vyCn3?Sj3̒>Tx-ÉO㙻V*l*M`k1<ɭTWaYgu[|ܩa?*yTX0Mc2(Y>:yu1އc/HQe@϶\j.Y)isJ}Nˤ~`|1$;LxroW:,K=*ȡF? yE^|3=/G{zeU֓Ʊ53tu^;l!xEc #,fK`2@džwLΖOO]q55?/(„SG \Ar__|J 'rQ2CadIӼYf t܌L%NFad_L%T8dG9air0<~\O}J'3sM\pq'JyCp l1Ì1d9t,e6ct$Y`Ө{ÍYϯ;T})7d -n}C0HF٩δlzY)k:Xx#ð5Q!?6:ɫ`J!dWJ0}jwnh̑hWTwM@ hU+Zn3:n1U$kJW?WA4=ﰉ$2OfXav"woy_Bo9wpSj+Yܚ1=i|*m0cVfZZ?37UvFs o*ĝ0-@E]}UFS ʩiǓ<<ɣ'z_vAݽ`)oa^\襨3oeꢥ\ۏw`wH8}97Ӳ|]k>zq*F߾yyN(l3_ +*5TNu:XP@8;6oo:fjT;1vE?c9\0-9Ǘ/7@廍 |iu7LX)MATؾOw$v۰WWK}W2xd(q% 7+ܖ3и[Yp߽3|SRt:;]NH%\`2iY,0O&gr[* i4* -+iprtEf߰[68wjԪg[ P{ 3kqng5P;|i 60sfyM&gV{Ը=1&ͼ3V9Þ)Iάg@zfR80<U'r^6=Oo3\i:Y0FALe/0mFIlMJ)w,yW~n@oO4r߽2PNs3,,+ky&_+<,.݊b_)h~ԍ̮8Jl60 ?eQsoezF7gw-ЎKQm؈6Љ':̧˸k(Ɇ^34]l.=rb=2RaLbv^sV]{,g'Rnr,3DCKw-&A #LP#4&?9F%=WCƿ,]mO8g̹irD[h@ PApc^|nwk"y+6x ԞN,]SؽmO7LnC+rs3iBJgjSu믚=6A-xOt°&PWlAaxA:S a8v@]RI'cƞ V}ͯ]#(ǹh- >llb7c=ix,Ne1ѯ]1W6ʠ)tt욼\|ڽhP)(\ K~)c zAF|/<\vsߡ1T~&e;!Y$,YǹzP3`vx!h$)T +R+k䠾]χp7.*_d5Ϥ.AyUEq|JHTordJ@^e G?3Vlm5"FIi!uiG7>S$v=IJKoكn~kg; `Tgn:v)p1/ m_Q"?B +T-7-*р 8XP. R[Q~Sn̚]o\,QR RdO69!:LQʐʌGw1bE'L9nwm7A47M X|Q{TGPS`1^߹XoAYLi@J 䐲6]S%RJw)3Z HhУ.Xi~ +M/"uhIbp3 YlܓU7s8cKØg}״MOe,BzTB򪴧+m=H֛W`]$N-RY3RF0yA pO7figŔ`hjžG%#g߭ Al&+H׀9bSU2ZIb5^J'7QCrPٮYrSa}Z7MRrDTem(*M,[%,nfSYxzlU>yߢi^LЈ?$QO:]W&̬z8-vyH+!|dEznESh){F-apG90Pj|럯U^u'V;~"A&b, +oZ4OA/*,w¢{cZ{D>0GVpʠg|4k]Akh`͹F evdo[]M{pFe0xֻ49?6vCSƅyYƷE% #L Mn(t^+C6VռITIBꮠEF)fδREC6hؑ XE3ciky` Q$`Cѣfʼn].]sH\_)b;%נvȀqʂ_$yfi2m4ap1CKF!_ N@S@}~*bxODOPvv3\boħ4T=f +{.ۢ[#<]':ť+:Pׯ3kFOD%JsQK@`*b+GW;ovQBUp.o"A 4OxQQE qm?s%EN/ \qq|]QGI!!|kJF +ʚMX}P.cV~aj*vDMhAF ;j4]kSt iU*ʅoNUzui2a(v6٢ Guv`svM}eh'?iQBT Jh7..lj B bWvڲ߂j-ᆚ1i~WR^N Y(C c/x>&>3(^?_ut-%I_RHf^1^f,*иLN0=%Nɺ8q/~׆_F֢VP" Rd<,kz*+31Ę"(S8-zn!|*)I|PBX:܇~APw85xqiJ#'#4NL0`zqfDrQALEsƂgpgf0&4buv.NfpHIM\HuO9ʵ.s<7S1*Cy^ڨ-\ jl+1)p*x^]=(PX̿A{hV|]>L^瑕*XAu\rkJ#}GEǸ:aY }ap a +Xw}}u1<,aPWssOXUSon}\ZA1ka|;x\jŻQ+XAT+@3 ~jrś$cP78sNd9H8rM&j9XRx4=i}UJ┼G))Ut#iwsDZa1M-bX X y- &M-ݗtIz'Z'q"Rڪ)9^2. +W,xDɼ,-ܖޔ1o7p^ߐ1oȦ#(2C'0] +T^+#Pg4l̆p08H E@u ;hZJ Q ٿ<3G9ܿՙ/>LAAT]inʾ̟!X1QC%aX C&H_".'|ѽ}D7xBBTԺr?ץx./5w"'˙McL>ŭАb]*vTZJz> t~~Gn:A|j|FT?ꭍ6o^ ?ԗQpf8 eG |bv2|,UUB^mW +T!Si2] $ A&H.@$Н8rg:h_M 5(tWKM3୆o;Kθz!3G#h8(G~+ւ+ K8ghFL0=Bٗ`FYT!hJ Ʃř_nJM,rf ~o''8@-2 a0V0Ϭ]e~00ra,C1DŽ'1I;<9#$c!-˴ 8&1+8&aQ^W87]Le@t|~A_Ńz4N[O|rP1¢$KdOΞ=P*֞uK&bnwYqbPӝzPawx/&ϧ7IhdYw_91'ٖYdhh{u H-w,`DhhD) Lo:vGPVej'{\p~ @<1dzU'V{o +ldkTAɢX +FzٴXTL3kh@;rɾ ٵX 7i _)χ5J1t{?*JIO j1Ϫ}7K.zL=9$!HeX@3{m{NzFO 'G~]ӈe1ʂU 2ҬUR3Xih9 p^ dבٸp`, M?Yv$ְn< xj/3`p-TCyan~ɢ܇4¼Mrd}k.Xv|7]ӝ5T2%VP`N Wf  ~33v /u.n;v#=Ԥ},|HKD}@3%=?X@_e볶yHM~,ޢS{kts"{,C[ P:k a2srM= 0J? aNO-c& ֟]!_!7?2U.x!wAc7 > XF).z9jwNK=îldޱ>;~4/w+wc+M%Vc !{a%>巛&1Cj^¬- LVl(i 8R{JbR߶$Yކs +_)!mo^=|;lq&T {[?B,*{m De߀ U1݆kѡNpÃ6Ny9})eaI4ΎHR)T +`JtA۞!`V"rhȂדxuƷYtn]ȱD$}w6*qs@5⋭J8%MAaØ +L0ID8Xe10eD( +?Bgy񬦿:\3)aޗhMV!ځܘ6m-(׀F,HlIE_Kp=[tч$k*4;[Y8uUSޜ,(7팳V00oq^DCVI]2Kd a +???&aႥ^`k\xy݈3~dX`,ݟHBS/򞟘 ʅ~AYхC5vշO. ]v$.=AE²|Ei~@NB:BSݱŝɩYKjO-\AwܟG}, wΌ&?ӏjy R -&#hf%Nwv&p6 da+Ghh7L$Y]8f3[0i/v7c%3kx2iPn1!0_fAKዣ ЬIwpٯGe?f5=Sd|=~d8$==YT:]m҂A 5INYtYm{ֆ "?U=dgweswڶv|\oWg$D§ת¨ +DC|áj2:dqlEm +Ɛ + ;>羡<{6ۘ'joO QN>c6Tt]73^<ةؙh=iLx|[w5jINvE:∿Xh,GQ7N%8Q a +p["U@+Yj_}OEY+*pXoRx8FYMwc$8W'fJE/ ?eETT?pK` b; +Jdf#WLgi;\} [;{\] M6[u%SS.$!?e`2S7P4Y <̠;قfe w`E"B QyW1Ó[cjuR(wo *AMKҷ&oPCebz%yի1,vzgZɁ]Z#ZW8{b]>{bt` +!T^y#pgA1UPn+۔tz6\^\J 6y]VP Ϫ<70TZpӄ!4 h6$ܫ +atblGR?-0}:?{լNpZv`:o7ԃn&2jx +Nl'D9O8U`!aڃI+f=zɂ/aVɡ %WnE +ǚ`P׵Vc kAm=m}.c~ѓ142[)8\DM2JTq7ٷ@΁>Q8=}^n4[*q"Lfc@'R& +qݽпPL1x}p&pa瘷 8{`=&{*nOG݋ ^ Y'C:K/&T/Io5 њm7!Y:z=316 猊ifMqeaHR-fh=d)/"[p*lalR(x-ޘ(m'ar(7wҼ`Vn9~ASZN4-9,m90le2= })ds)38bi\c4%aRC;6->vxiq!Hɑ]6ɑw\?@ҐrYwBM>B- PU͈ p6Z5/Пg跶32:Gߎsy;af׷v$ٱN33(𢵰p`ՓPe1$#Ӫ2g hy=z|uR3I:]B}Ɨym +wSm|8 *d)1̦x:`eTO2"ȜC]M3+kYT.I˚Vyk[km[r31}?T<䔝2I2zSs~i}.So?p*l(Lc0)dzs7]7xzkmÎzՇXͫUS7'G]pLи"?xMycv~MXޝ]4ʰFX~fG;U4>{.? +A6]2&aǯ\fu2㉂Ǔ(ck;EcsM)T%v*t$/$WtM(T)EZE(aUQU;§XiKc;ז숡,F?kEeU5yߺ6{] o a!sm,Ք~RSs,t!&-z;#v%췜g[xsS45\P^eheVǯAsm>ۊ^{Qq0Ǻ8-} D2M=S!N=LPI=ob*c@*]H싎|Z'r¯Iԡ)6C[ᏸ1ǬhҡxpԙSiJ+:eŶ1}soTv.wlᾲݞ" +a(BڜTĢ^zmDC6rѮ.'EB|Z{ӂ%ӜjVY%;if-,+[.#JdTWa,&Bwڑ;X*0߶7Mw@_g=K׏-zl@N"tpPSBs TRfN]63yLKo<-7_&/6LH%"JF!A)ӛI\-8ۿ3}a1=|B׋%O3R*)}WE+^+OhOa7whř(:FM ׏œ+LAPLD#DэtB =K[Pߥ&>ŹT*[TQuhc?njG弿{4oZC5%6:v-PB1J͕$ؖ{4 +# RJt\W4hUaGЫmF ?jߛzPA"V(tˑ1r2C3-Mr[Ͷu'탍mz+'Oء[EѬQ|}.J~]N%}'Ir̪Jʹ*o⯣>j-O$O/ݡrtHrӓLnffy^$I.Gp3 Uv L!\#bH +Ao|ZO;Ց 8!ej0sc[)tlbt +k>6h:EihEAjbsCYDoqNn{\c2.*P:j);͑\'ŵq\5_""ޕdž8uvϠ(7yCKL&4]zTEo&jj%^_+͈dȑo,Xces*Wo^lœwnqנ/K-7j"v-$`O)\MSTm¬q60#O->CFQOKmkz.JB:瑞'띹Z64FFA9V](:dnϦ̲Qxq;:tpݱzϸmsl);tVMfiJg̥mFu]'uKšI(<_ǻ[SWV$֡کFbQ ђ :AU4,d2gXiw)zӴ{bёc?8sdoD rD%|,HKfYATWvжUM@,=!ox<kkxp=i6XEmjdCA=<?L pbw~ݜ~Rost{j" h13š%{.o:GtaHخn:n q*[4'ov~#~5T@21\C-VfPe{МYd}J0)PUʘco`W`Ԛ'%@j#ſgCS: OunCqМ';oVSvNJGvʵnV 83ˍ/rF0f+W`Ie0iR|i\gxDX]VL <'+jZvtfJ\6!p|"֞}= C-(O)):09K0̤}tӥA$%e[CFS=ꍆWPT [Q [ZԻQ^fu=*qZeXD"4="6UM< ;T=K;{_ Ĕ(3U}OH#8}Q+Y)NZawϨʾ:+ITUkŻr#' j:8;×i! i\Z;>qLJ4 ׇ$ #'Ly~-zΡ Wo vFzf ׏ tlȅ!M׵! £cqУmrk iI&)!`aC. aCڵ!8G*B,鼬^>Cd5pD%! +Vi5f9}'|x}!caVO\ؠ'ŕtuEZ$5c榬kC8i>m9kؐӘh8Sqw ǿ.:Udr0})"f ];Qo^ jN1_B*'#V"1t(p"_~u~@ `JWSu=ri ϜWTQ/.aNtFaU/Fk>O d`#(}Ln^uO^ζ!6h9f]fyy? @|qAzhՊk5UgM_M.")gϗ +k\"]ER jeZI*.`W6Z*]cv09} ZW'_J#;JƓ~9@ )i%ۏNe/),MxFNW *5P8NYc _swn9Gv2F-ȦHҋr|!//8<(&*ԑz8Psڜdxu?Iv8Σ75W?}ly82/uS"8]i/L|+p9>_(88.Vkuz_gxO7/0/$qn9{<帏y9;Ě/ǝɘ8&NXtމ~{g˧wvu:8PĹWXecWxw`w"wiD#  8}> v~ ?}h>$F?}>O@=^4}E{hM{]6OB b|Ƥt5Ctrlѩ5\B zҭ[``UA6#E/YCkY&jĽg@| y&LSA:֊.їnD]5-o!A&QS;Y:dA%^ ˑd6.qyϯ=Ui[ZHɓ:G?!l8PxQ;>ww)Su,|]~hmq^ʺtP %emjwl'c FsA 9GJ9ȡc]qŔDĉ 39uC~_.1Ae^]ϳFsH?[~E@+R@I6t>Kz>Ƌ:2,^r2>ғpiˤBnKL2հDB&XU3d wv_@ÉWJ[j,R ~O>M\,#n/ݼCj{\_4z+KcރZ\BOs.1"Z UAfr,|=K +\#snλUs=PokqWp YU^M3ۑ@){NyF*i`IŘ}DŽœLBiT%#GBz%UCr%j?23ӡlDq]-I{0lSHGD=ΓLɦ_($-K|xzӇ<#SuhN#p!fNa _iZB;,voC$O;M\Ꚃ*gZe~k2dY.e(6WYoD$ҲpjR ]f/SxdݦiBJ0/&<@j3{0eU?`.2qgxe0xV[Q=D)2㹷 3,G]l4ɛJ[ +GH2ks£F37 gማӤ:(5PT)3Fg< +F Fls`ܝ $zg|L->Ɖ@E_qK +h.`%,SdNaYJQI + q,:="MaLվ+/VHO$o񻂶O6 ?O +6 +PVМlBⴌ^PbIupJJIAxyLM9KHfBmYl2[z/ \7skv\JI"a[g +yZBU`h 1Bi`|ZQ]b2?PʞeIɦrAR! b4'ҙS,DLk5'i_H Jx m*WV^2b{P]^G-Ջ $~!hTAV/K-VmJI!.vk?DI.o2]E^у6'`rG8]tM`&m'8`"!T +t +76JěuJ Ee%Y=%BL0¤`8hr$mn1 ҫ|$y~t\9/'=N +#8`}B⹩/=WfC(6u>5vM>F^J\k!vgI2͵ס9 Tp9wDQVuO6 ,u9~*~ XM@\㟁-,œϽ5-1ޯS CyَGKZhűa:3 uܵ~}2 Jdo=j`8nDZ'҃"iOQk_ =(KSC-;+k>݂g*5?&}ףc ʦyASÞ_mˮnHy\w*UF0%9%oV +X7dž9#N/XK&V{܊ʈ߀.CNY8ZX4gZn~bĜ>FS(oP_}um^71ϭ;']kek܌#o/ 5m,F gcW ޥRC,X>-[ +*J"aj=M{EIʩe[LGASQ Э[o~=f^ a~vBDL\9]W@88dص K5Z5h5ɱR1b7jA7V>__~A xfBuY|M=t+U"WЧOw oytɅԑ=Jp+F6$rozDl'tpMO򠞥q2'X;SBFΡT9wzVqay㈕)eĀ9#b{u)g`O}'|g6_3z7ʊe~5_ߍ"_Z٠r8z?4?^`ȸ3p8iq>g./w^9 SO\9sQUE"C^~7Y2Pk3lYEI1 ktC +npMDUrxY1(ky܁/}h^ ciq?c?8-BVObl p@6$pj]Ո$w5^l/jO_O ζ: - DJ g _/gJ 6ܱ}Cats:$.4aD-f_Ȳ* TN3):'ha;=V L6jo$*m)6Kٗ)2B4?gxV]_bXµAN=1)%m)E<W'+O?Hx 2Wfg +:'.n Ki|"M.8a#)|9ttk{'_Q+l/Ʊ/l[Pww[R7O +B @|Ky11%ζ{1` ?Z&_5eD0s;&cu} Ln.qD6n,VuU"TT023˒r]&й>Sq0t~Tacm<둔BP#.PŅ?-k"{>7ըFh`YWccuZ)^Li% +4jNH)$뷨m8)sk`zcJr$ɍ^QI !zUH dp|Yb.9ߔvk<γЉ/Ejuh2sN[Sj[LC44 +V`HAR=a{CađP[vѨڃn+5n>Ctx{*X"bkSZz Fzj*IqCAt^cv]ҳgh4Dk]B{ڣCNjuUc} %jbq׍u,LU+'QVP|hKgI[}ˣJ5ʭ|ɭΉWuVgz9⪲Cܬç Tٝx.kQ0A%F0oin5$]ǥg˗`y/Cjq<3L(18nN]tg It'zlpeShsR7cc 7%s9xi|aJqw%z?i&T$땛*^M1ZnDKL=Ö\J\qY!TthD7'xSܴDS`915OѦ1(/Yl;5:(f 35ɄƝ`&rީmJ/D/.J PVuwwB|2zۋv29v5d E͗ 5${sD6ZxAp!G,5[ZI\L cdp|ʉd h8y `7MF/R NeC% LėVM +Ee$;H2CT4 )!c.85nB/SFΓNHz(2wl? "u;\Koj5hUӊ&~r9L&3|s;NU[0lh4^vP?h@9rqz-fB~;x2)(K}/U+y!;dծڌܯ~ -EG,i {}Cq>y>V=Yn[RZqMۦL8; Z5c0x 8YLMs3McR,P$ `nW>5%x}B8?YayiZMN& + Gﯶ-O>QFk}#L߲ZoZ /c ]…mɬc;MЉ +ˮne=<gQ\I]HI +mx$v 88al24l㎘tl²% Ϧ_l#F+5f󑃺#ss;Mô1AkkcGtm k{hfF63Ӹ)[Uax)Lfp^n@ֶtnE׹FLXOU2 +#nZf؏n|S dM6/M{ddޘ2omuhQ?ox#>˾!6l_7ʰ͡-69'Q $$Qox~A þMIF:'@ `70׾%͑X?4pJ#{:8\~6,akzճضmʹ#cEllH+p^6t)G!E.w>^60K!Xz:;Ixn,2D{\2SdX 0a~u ]d%C[)Iґʋ Y_.DzdŅ=\ `].$Ņ~pYr!_-d<^1_.֢D..u}!r%ϊ^2 STd_u@FHd\qߋx5BfwBfDT\l\ H.? '<vxgq8!T!{rM3ԇkX ЦUU֣~9nmhR[n +ƙ<7VCǾLrC,0xJkVi):m@­_|j<Owi<.DE+5`jC WK%r>YڶjDq .Zu{Y7pY1IMUCA8Y۟/-z'Ùnwzv A,.̼>޵bw::nw"؝٢$|bͲ{Ƕ_+BMb +Y=qJGƷq,&A{se u?ٕZ+b'N-G V.^KnKd?FyB 6*'x3ߵ"+MY0/"c#ŶGq%H1Ņ<U|(xΥv|Į9Q1\U [UdJtnQ#Lrdm )<^To|)e)d(`Pd^-J#vP$ }_Pc%Jʓ;i ,avX[M$(<Аޒ?dt LՏ%sUT4mع30-Mgo~;"߱ E87"\|^&o}b}7?oUHP|h2󷠺˱ *]V0bS֕y75F\F܂16W#/ -/G|ug)^rO!F#O>f?:~H[ K=6Ż +!㔪qٛ;4[Q5ó{?KLx~~$eDph#U[0~]>Y3u +N7n5PD;V+,D)p6tF=MGR0&̻`Mݲ+ ~nlgq5uQCKvpNtkaJ{¥p.^SMbSӤoC KPjl OA2ry 䁐+N@Z@%֮^4>cet1K%/RMeC4|3|x +qJ߷PDsX=MyfP'r +}wI~̸K!ygvziA;S$l1@Fxv\&q'^0mr#sHF&Á -j*ьktDiTC7Xw>69)K-gRL> +bЩ0\+ǀ2Or%49T p(,W+`kl-#r6lV7lUWY6 KkTW篃6^݄zu#Yu޾Z%TJ0*R(٧}-\}Qq"^DʏHiAߋơ)~i1mC_:5ξ?ɍ_C;۱N_vv$bCWZ +v=SզB;v30+1FN hIRꄁ8ʻgxJ=1v]^".TiUyC*(b|O,FPgSމ4F\ "Ak9hyc1}t').P褿HYŻmqШ$qwsE3 &]$S(Gnw,t_~ZPH=Zo&/j+3O`ȑ'GB2gx"K=ٍN%gA+q)[Az6"Vl_ZʙOr'z\s";2n\*o}odxFO:|f¦پY g8 >L'zDg~+{G(+v᚛`X8*}yȼN .ׂ|S#dGUzΓ1d١I^lS Ϋ6/jCʵc~7;1"xXx{z,eg#݀3MϘ #.)NkTQhwF1m@ cu1= iԇBSSL풩|Ϥl7D!wͮʌMDYA V4aȌW(\7ڠNS׼Nu7Y_ghC.g1;VjQ ,bkYgꤩeVVc6l-!O H>G ?/ETfp7/JJf2Tf!FjvYJՄ c+lK#~DO\}7)\?Gy9* R\NE,A ذA:`i,Fč͐?'$lJAO @v REE/ RsF7:(Nc]^ָ. vD<=,xw 7rKwd#ӞMl^M]c'ص&udhrL<)y٩s۔ ve< vԆ7'PI`|:Y4U c$ v*3'!m:/٦U Mn^f܁B5ъ%lϽSϲRQA+(CS?CN{KHxB)(11K\Dh!R sjaw5),Ox8ifM#{%d<9&qS=㺹:f b|c?)\R$*?7όocWbq-:w_鮖,Ibx ?OoJeu{WAAJg!CM'@ nN?-*8%,cNbFc{k/E\yf]lYMS40(>pQxp|cGpktC{|B#0,wx:Kv NͧFƺNI{S??3s|2&SܾHzaEieEb=5?>P\Rc*5T='C}R>q/jЙkȧQYrĀ@c +^ZyW5y)F 5 JC f +25v !`0y1k 0|aHY'ϻ +j!p~N0Y桰38C1-Dڤ:./4{#-LhE4iZ,v0GDꩇ;?ζKh+s%lO‚ |ٚoۢ oFkĒ:>f!>lSwm8r\R+!` +c)Ѫg9q:$_Xs== Jꡩ_} Eh7n1ɘT|Fqx)1WqJ$-`3q" Q\Ґ96J.<9g8 ;wN'3G[xe^N П@} 6Pˠd0-/̍qFsfr#sȠޏ;+ld +M(Z#\X [Z[.Do/cAI~ \?'sh)L vM7WǙUB_d_+)~b$;8! 8W41M$ (dH/{yK!t0v[/9^C!-(]cN?$;/EmllTRId% S#&c'&pIVrtY!UJld&ۍ'꘲2>F(Wq sEw"2He%E<L𮍤iPH}  !뇤=eFh/G);=!?Ivh |\Y_ ܢR֔YSX%,|0_Hk?߾Ȯ!_a[? +Jᱍ> ?ʊJ%&qmITL^[zǾ:5kacp 6/ҕ +NV }cHGAVVgZ`qW<^={]ˉ0Qi8CWZS[YC[c_ޡlDZi I>F֑^TV O ]ND3H(X1 +сI 4"/!Gƫ) "c;}zuhMNpGqgax0!X" 4d dm}xQ}gdc̲}-)ON' ^]Y{GlG6b甭blcmJx' 3.*laurԴp9rrDHUz唭eZͧ-,K>|Tɒ$!dA􉔓[*rZ`I̵FPZâGB~Vdrm.  zYDbu61$µJQD7 +Cg/s=ۇH};fh +fjt8%N7ɶzr +uϞ*>^ +9!̏Ry_reVi2'x-^F~i,~ 6e~GO;6|7 p{pWX;Sj!99N_feݥf=ϫlLyZM6hWٌrPOJH4w(5RнK.JB6Ui6r:7?u}Z焿 +ܾUh'PQh B;(4.4\}ݲNՋ;=:ufK|屯"3c6SQg: ɩ3|3^uKu;uvY<\n,x,| 0nCOTd**Ο N<#+:w[lKCaß +\'ԭ2pg'C#e@%~TJb=$1< +(:9l>%z$$Pq`$u(;fsJAs :o +XP(`CطyS( nGc^_fuzNr;#T_p&`.Xq&}P_jWW,)%Isxl͡2j㠓eJʾᔼ oCА~p-lrw6xCp4Lxk/OI9&bgWaC#%&URYBBRbɱ9 ggpDŽE4Pܿ|SC0,e~&bSkKyڠ;(|! 3CHD1_q[,qZXHT!N MH)g;N!DlF 2%[Rs{l91igb} 5D>#Bϻ."'KIQ@ V(ȃZ\-0&$(Sq%^zҗlpSGP&q2ԬTXkVY"],x(%h\{a Gu`>+8?_qO @\h[b~ éaE>}#OlI8LnEd,{7!^L +Oڝwa%G!,qmJ/gF`k,('D,mOWUp14Μ!*5в"s .<"?o8On8M%~\`}Km+f[G{[)_ٲn>9.^IF4k^ӼCZ?bʳLl A )[˭VwݳK!M +GWz'Rݥ)ːK#%3$lG[lid̕NOłOmPTN.Zҝ FFV9  @?,<1][-z2&4 Bf/B~ uTIV;l?Ջq3&'pr$q@E!|bg^h{nHҌ*Ae xQ%ܤ8#dB,F]Ɖut^aߜDZ-/, Aj4^7Y/y սY?Ci#YyKD" 2QBN(˳k`=  EB2> %P$"D)ׇ!y0 kaH^lÐ4kzCUK? X/ oC.DbH/BHF !p#1]~/~|S a&(W"#JtKC2|/%< i,8l)+$K +Ed?0#F7YOKnO͉g?] x#TV 0՞g^q1Do>CDVJRi*MBsu=x }N&u>%)5i9 ,Sq RceXEykI`TRD"~Gw%Pr•M7C"6GY~ ɽUaqIq!PC=:|[Ƿ~>ADҟѓqF)[?vNKOfH +H?\ K"$Qy2g k\&F/KN 9Ǭ<"尣=Y!X*ɱ\ǁ>zo:Jv +rX|2ERջw`K8;Ձ%m>\x}EτFuUFWJo}.)<* NX阖+o>;DBkVtBv_;J#U82KhJR+733}k3',G0$j'.ޫsڵZ_=~ ڋ ~c~Q,%+0|E[/Dp׍Fz۳ V9(]>{TLwNJ$Ҟ/AڜYL  +sh:`TBm#j_w=m땳UꊓXM:²$Y_|rkJOm܀̂ ׃8FH-\!Ns]ػinezk?\ J`Ccٳ0z1jMݳK>*Mξ2Ǝf}=W}wuGN tkoT+4ϠkhX"L^vc.9հ۲Xaը]6֭˷n3v?Xpܶ܃J8/Ձ%ŌeeNokw󷿬e@M[q,rL NT'NJ>P+.a[Isg%$:xϗFx6t kSKXK +}3p0=6~Ƕr,{qj˝uܬ[ehK +vbWߗu.]Hmj9sI0t`p BK~֏7+0ՒU@;A3^ɾD2=A8%E_%iayX;Nab{ז]RU+6Y0q4IQgcuН37;˳S[hdU,Ul:^lwD2t"rNVIHY +ݒ=9^U!6T +>f9}Ee uԋt;.x`|Xݺ' 'mn3̓)i3X+g%g=|LXb|2-t/~فl% A`<ŏ_ ; g`E/O9<(FBq:ɤ@cF9) T:_'IOf clcr^VM6t7l +QrW!b} :4[+YUt)I>_~;_%=6d{e∃M +|I "( w*E[VDmNm[2wijwmހ"ы`޻ E7ć  1V䃂28KY@k?b™zC]sC<7=W(L8lb _Wd#zBXQ"˂!:08MCkK$au4z$RY|@ZYsa.Q,g"pB~ֱdm:@3e3OL1)\g GM:n=dw,< v~h!Flhjx@ϤWwQWF5PSӭF PU^d堊`Mp.ĉE kʊWP:؜(uF")J"} + +|U\:\JY{Up*@QIKS$>_j69OէʐQ>c'&[yB"۔Ew|9Ku69}`U+*0(:fQˈB$RԽ{β+ [|qUm^v3R%\x|4f Tpr|E_H[EEiݫPzMxNn>tZ>s\vx9y7qwn}]ăyJRȻɿ|UurLo{5k󎕩3/g, +&t1 '.0=]Țgg_0#,^4@}6,ii!̖7kOhu:u{v;[~%sO4 κbfZga/ŋ~ x|Lڿ +Il4*d7 +Q +A8S`Oi +-#N(?|(>ъ֋njJ!{ʑUoQJ*\@=Fg!`+ԟ;+HP3{-o~xoC|LQ\g*z3ZEE9?&I(sBR?:+3:H/X€{c:B̌ca)4yt-3naaG s>usD iW5?[n%Sj"UH P +h2V%PȨj<9ЙQմlXdp;57Q|7m|b]h|b Dʕ8uǔ`^aȦL[6&2=ݲ XtJ.ar^e]p1|͘S sbֈ 4v0+w3IY}t"ǓB0U.N# L~fyN\} 9 +cL+u*_65-qX^huHt2ZVr=D= ɵT8pۊ7Ӻ,)GfUB10) rX}q;+]M,޻qfs ~jCN}PHik͛;9 +!WGEg.ZA 8,(Z=xvCj;3еfl9.LjC\t B`2-0IxpN8CX%ѭ/Rʛ< m&B'/c;arTiջTlM+;ä7"u}F+U=puKL@ >|e*gnjD!5}1n_'lbmr֤ZE%'(=f8qhviDTĻ}] &1t_K6Szq.qf%.{P69=b|%1X?4Ĕָ?ll2(Ʉr7o&[0C Iwc6;/:f ]cV<0 c/?\Sne+ "BUw2e*7F?#Z۰cZR&ar66YVmCeSO[ q]m|&|ρ)Kk<.(v"&_nr'zjZ98C[HE6|$S(0Ù lUsӾ5v,ͯr热;jؐ3֕UZmM O~c}Nz>&e|M򽛅4lFp5M*1pF{ge ş#JӭA%2^޾kD3kHDҹSP +u(z}^NxXW7däĢnː15D=4lv}=hrn?g2Qµe"ۯ =ۈuEa-xf±\?6΂.j!/kXZ2Kp~CC04 :Uڎ)&%yX jhEkb}4׉'|8伫jy%ƺԢ.;g_!T14o+]F;7g&3Z nU=$"w4cQ(֌|KYǫn:oX0@q5za`yۛ+3v`QZ XRy-lw[UnV>K Qαw:T"q\_Xe_Qȃo5r* j{('Z:0W:>Qv<. G)Hz8J:2K8=|!ԏz0}4hPKxn=j@BS>|u]wJZ;0FZRbEm)}ҎX{=K+{Ǖ~^@^ԏAOw#K@_ l(BX';AV $uy\1QJ?|%Wg#%a1R5%FJ}ޙ_wt@KbȻy4dLHϘ)yJM6w#a: דm$@N&~`-9j;@K7zWL&D8 0OXԛZ> s?n'I:h=?ga }p_/4ԗhƋK]խ_>/7a4aYs=)^쇳e?/b?.y~M//]+؏SU6:H',9"XkOO5`kig" 3Z1v8r rCIΑ&R> kckJ,<5]X4*k'(nˊ jWcS2?؎7%(~wgT0 ho9=eĿ]1>}7 \ 9lDHp !porhJ>K} !@MM9%z`짥C;67YpZO@B|s>S-*LxpHKAԍ-z}@⃲h%Lrs.ϭ2T uvr?];5SGH@r_i>쑪&((O]0ٷu o}-1r! ^?A*wݦ-} <8n1K6/ϛQE2>ǽYޫwt}0c~C;!nd2{YmURSy<`owX6Lo'u0.]e:џj,JiИJE3qI P/A]fn&BcP{?v! 6EC ΔR/ZRԷW1~NZf=q2YbxVD鲜lMbspDUΧ43VCד}YӴҝS$颵hԽX5> +ˊ؛}޻6WlLB䗧$% ] Tg,-vz5l@o|YwɥךcVuȂrVnFwo oE 蓫=Z<1R8q9ѳd4܊_Ygor?@F G`1tǥj ˰]3T֗<< 1d4t>|A_.Xm^20#8r@N6<@.je]pܗ9<%k}9h7K=nhkI zqxtd=.9RDֻť@t~Z&k-Kڏ;NGzgs{$1\X~£0E|YoY~&jzd ezO^g֦c{^ѣe|$ᦁQP|>9Pٮ+v)f*g[pae2u堰Rk%SddgֹXz82} V|T::ҝo[goylCsL}پ_ +OrU!h4V8Cю20[)&<` +1-euJFBgLX\g :oJ#,ҘGXfzaB0e*~2oO;v32'¡305(}?=1;c ,r PoRJ%`EecY/3p;;c Ʉ^Rsa蚪l2B,FMH/a^oՅ^NAe.rdiDvɽ^ 1U1 J k\? WlZ/CR-t qfsuG-wPj嚟u8rdQݽkTDG/_,RHP9:{7껝ݻ}Kj&γ|'cy<P6uh1n*ip&Eܑo7`<0 ̢smg,/0RU?k'KDGCw涭 P5io[UO=빩gE>1B4h'i\A\"\ZmFO01'cov2*M*q]Ksl7#=&HC:\p +Wۻ¤ffWLn3vI%H0DQ>i2TG9@q @ wSh=)t1Mf,SY jgwoO{]z72PxchRvK\z~{-b%kىXYYy (ni$OvNt&c^V!Tnw[:\ZXCֹv:GHBy=BsPyJKY~^"ѱ](w<^'n 6I[G {W[rŴ^~ VCOڸs¹ާE,u5k`@ܗvKG +*%?i:[www)@u; M7 ^n}Y\3I )t.q>fZ7+3@#W(*ZXz"KczA3_*ZWujOꊉ3o )SeHX +'v|xB\i+;\rz8aG#^qEoG`FCX)IgY +}&kqӷb\<XZgI)sLsf'pg+JIS;b$mB7,[z9 liX] jgA>\/P0p]i_ഋiNGj/w nb _Lsq1ӞbDTiؼ 0h"H.ILYAQ1b цtƋ|"k]%5_6@jzH푒΀H_KjxZV5Uqh9h[oՠs&^VE[5'jyoTH γ jP XIj!N_ 6a@0ERe.!LֹPz=(uD5e?B?~W?D"PR~`&%ZZ }L.h*'m ů;jr1#Zt(U|_"jRžQwT 86%Ʀ kTJnJѦGe +KJʝ+QaŽuU '5.<w-|s}wML_)~S4T +9HKtL$X4$E`ܝE/bѢ7\q)eKTB,E8:& +\tזK<RAg|Hu]Y]q?J?0נ +qaZlqHp~["Pաq miֺ^8TYx`u;o%͡8^iQ04xhޓ4ۛb>CsR05~~#)m{LtC45RVt]H9H(xŌl y<Oq}MbYXwkH;-6fͪ#{o:x̀u9дIgW>:mϷվ>XԹ0B~R8e56O&vWJ:CzVQ=$eͷ-B{Tvd0j"*_aq2UvjC(4G@B(rɸ#1N^37fOYvp25Z8̳ +]ɣA!;KR -ڱm#TQ\)ЩiJԧ~T'-uJt,Xó΄HG/V/؉8Hװ4wS p(Y=hƴRŬR5#-@@,$) L %M(bi2q8;P $1eJ q$A4ez +_iI<(Je0n2G)"wz=$5J4*G,N'ྜG1Ԏo(|UH H81SrZ"G7raE R#ΰhctNg9z'zM NqEn̲bRPl&M}2b@Cף%chs e`n? eC 9"ўG%NOBQ])V;bڵC*Cgf]]POx +(cuhڨ$}3I31b*ϦY;/#I%՝3N_1Kj.O s9IomGQ(tvBxU*:dG[{wh>$HRuya=,Gwƾ#mwuyZǗ8?ǿYמu8.tʗ8QTE+q^,α??$7.ǑA3s/0/$y8=8?8Ƿ<9/}X3?9ǿZ}z`-z=Шtʞ^e~Vs]E(Y)kv;=α5O>5{ O@7{^8> vCf0N'jwrV]Zhv~::koWHUQ0 :TP y$B{zXWyk}iX(L:W: 9K}C%'•S@+9ۏV N#Sp•.زJ}$L#ZS)0lC-ꔆd/|rX/;G`XyLSj[-!rr`n}EF~oAzղ6~<#dO2Aph9-KfjMjn7&`[yHV1JO8͔OC +Skx$B _vPu "$ŇC߰-k/wYT9'c1pc1w-j2]aC8qll3wܴkg;DPȍV*Ûv4gO+?Yo~֎Z*8,L}՜ʣeN{45{NzLʋkT1m65Ӽk~0jU5yЬʿ#ZĄEY *Z%[@9%(BHk/?@"LA`F+ "M@oٱDZ =u0Юm kyr;7of^f|T\i2I"0H R7N)[b| 3mI~/PͮL08r#׹Z2?IPpiZ>m|GrsIDЦn7wtsiom.xi t@8+<}Rg֝@J>.fr\\*/$':_KgtK(LIyf)i[͚VqZW :OQUun +GGyVv>MUJ"T|4Uz?RG TtmՅ4{DR,yLKR\Rx Us8S¼}J `7YG;TALu2W@yǼ~¥=/## RhȤT:'i>v%2Ep/Zl60력]bFW}(K _7Չw)s3'WZ-_# sӞ2<`Y2uB]jչr o5&VbaٕMTKFhGlOyqrk?eS3[˸ *[<u/6$c}Ke/I&5}?e# DѽN iF߱cA!_xx \9UiJ9hbaD82jhU!*nO-cp̐CyW\/sMKuЗ˃V}g@^ }`woF(qRC{$MBT +RF76>S8&rDL;XJ8°v*fh.)6]%['U+~%(T=A=7gŅ!UQՓ*5o|gQzT0A'GNzzܨCυ<=Uٯym=O&r(^R9tP9Tq&CQ*SF`9|ǖo,X~%\r)رr$r3CBmj7quBL&c9`9%A崻/SyC`XNBWƺQ &>L]J8\yù$r~= 'Vc_$=qɡ.$@~y\qW/3]駸YDpA}ԶYAq1/(i_s?LǂIډN3Х_'UC@f爭zYTؤٙbz:PVN[ nCj ceڬ{SW!T [mVD¥f—c#ꅖJ\UPiH՞L3(!r@Y6evYMv/е空*l4U[h>S4]C' յr e'Xf28qN^ʩاrR!qS/a9 'tVZ&I{f0FO-yg\>j5Y&m݂VumƬz"{L8\IqNxL!bV4eCvwjvb܊bj^[6k)JPa^]JG#SXv,]…-o>:m4u !8G*BjMt, qngitipzqm\MxHBrW9KҬюh->;;_;`·YȪw7 T,}3vPF,ic?ʵ){Y.WOIx׉H^{>4|6 evLFq_!b2Z[ug5|N\?wX2<}-ǶX_yj' +طfYʿlWIn;@Qg0W~/UE F_O Z'/j@m=,(`^<C+"_ܬ`ӃUQ ιQXVny:Ɂ,:փ@C8u~8UK;<f^U1 %/6.P>l8S\9T=rig`,㪫E=BI)YeZۃZ۲KN}&u34WFrUeˢ@ݤF]KFKV\]| SX1p4@\(w0@Mۦ)\ܭ hGp] ozp`R&q/  A$9*I{e(ɉ>7؉k"R'Qgi\h +c 2-GZ a#DK 6DmlYjdp:vg[q+nlD=ˆEp0r9Ua1g?B<5 1m@yP h[ r̯YԣP8 51aW|„(/RNjnn/EYk/U Mцϗdܹwa! J q;ZIO>>zä>ad&Wvs4WqVlcёV:}b2;6Y M>1iX^X D9~ӣ4KecUapc>&uu2!a{[ ưJ/d嶶nu4=D>8wW.hHֵw ֊` /TS;QWrogЂ+sI]^Tk%]onT+N Ѧ-6#ħh݊1.4c(:SdA 7!>b\zW7=X ikJE<Mк9}jp ^SG4. ;bb[T(=?w';b ^?#ߪOoa SQ[9x8y +KM*掷ȗN7yE %>jl*2< \"xuR^e7ǘא=h֞ %~j\xÞ{ Υ۝LSt(NHqɚ<%'g4B*Y*BE!Nޅ4XR14BY5b MGqX& zcI5nÄ[fP}{zVfe!]M NNj/#zinܼ&玳p6TXxTt"iKم9XW7OG4؟>zt.lPiʡuAk p/Tѥ_T ثj~h4΋_FW|x2>7ãр]ti4nFYڥр F7 +|ȫ 4h484v|h/4,$k^}lجkZGBSGW0~xGh·Gh|]=H^Žͅ55IN~8e\WNFXLZcO\]:W"sJ|).½+I3*H[o9<uK9o-̴XpeQ"[,>t%a3do+7x#-dKSGoBDgr* CUOsRFcݪ4igX0 ?eچ.kn/ʙ-S )&״̖PsC '(ncO|@J b)-R/t-Ƽ{MS)qܪxdY4Qg{FQ)ӒBuU塻>E ^YݥN! h!iŤ'm`|f_?s''$4GQ&G*TI=Ț6p5!|6-*` :.:凲6!ȬhgZt׿1.3RW(/QR^SqԐSIp*x]Yk ^TU 0~SKG͞EuЂEq-."MyG﨩_$ňb yY;u٢` +j0 Z7!3SۢJhzs +HɝUǰ0?i-W pӭUxAgvjL&u_d'9+z?`-r"IaA'_(dr ṇ̰j8oUQUj0TWRjF鴥R Izع ?10;L S/0%5jsBB錉I1B]7ycTZbZHA+1D 9y [pj2$w߿kJnI1:b@֮*)1k1b;HGx̟,#Z* +.ƃCx@,`U & +0qୗ ?؉H >8bi$ +Kpч_&$\I~Y![aC^&jQKh$Y]rj`ҽ/'ZȂ Eơdinovy$Fv2FEϳ/;~F6 ~dXƺ 8'C2~lTpH2h37(lp Tg6XtN;P4hZ w i~ך 0@k\؂E$0H?om "ϾمᶜgJˌia%ٹ0aHA*DTĥhMMmGU{Y-Ijx݊qd4c!G%']Џa}Pȟk6]]6Jx#`<. )b5و@ElDM`*~F +={=6vr8&: +_!&&u*G'd#[NMYЫYBo1I!RRx4ƒ/k +Sw|FNRТ_RHwp>Rwp6+ +:utKmWwz5=))6KAkM;ft֕ } #p4&SMr2x|@5x <^14 vU%POQxd停rGeZGx:ҥi*Ѝp-eA~~m#`0dTZ yGSF0%CXۼߕV2ZQ-ue;F%m(5d&&7l5 n; :;>k/Q_|Sx;R<Qji?y>n 篎fu`HJX] 4bda{}E d6.pZZHA$swIH[EEBM(đ܃=nE'4kx  +ug8C2/U螇F~lgx=]؉(vRhNg +TqI"ދP5Ř?\=2.tH:\ힼ(+ZdA)\* AXb*Et3S)?)|Cn'v/&K0x %*nP\fb ).b?/1-lQ!a8SI;Ii[{Q<t̀-ƮP Cf\azŒh hS F`ka7:nR$DX$M7 >vaAq֜ q%iWj[w2VwOm4llȼWn=\S7[ +v?ZQ-6hahPΫ8V8BZkҘbrfńgm'b#i^ aވ ־#.a9е=?\`rd6_;y]q 7;#]-rJlU ̞վ*UtvGܮ #Aq_gčc~N wp|Ra M~=\.ࣙ +  M- Oڙp!=8T>eO3b^.pʸ=q8BtR..0G܏ GCE:3.aqÚ3n\pF\Z}VϸTst3pei|`w-Ei2 y\ڬtTVO1d>4<|Ur??H<%aɈE,A6NBݯ8A ?GYߘzT2%9m(IHxxtRK +` +Z*uLKW RҔ$ZKlᘖQ;ɞfC6ɰoiҭKFI|V=cGfJW6[07vx|2r~aƮlҁ(}'=v cƈa(츥NE8KXXdt",wv,.[mJ +ҟ-%_v$ M瘪ϷH쐋"řH^<$? ҆E@yQr RtAr^shs> 7- .:!6i]귒M-w{[<1SV&Z$ک*Jjl$aOb.#{ǜB/!֚N&TgޥjP5 wv'Xo0AWRs@wY8#&Fʁ녿@wT j΄}@QG:ti5j<:P~Z~A8g{ HE +-O}'!uz<m=޾O-1|JjTV3vicڲ-hhD"2eo:ѲԳ}2$kȻ~wvwD~Fi!lD҉cc} ɮs}.O7V@6N8[?ۗ^_`3PNRy\:lwbĖ.Cw*uFW'XoY'.3k=ô417~]:cy5l T]_P3YlUَw/r+\)iƬi n eަC=;h7n))f80'. G!w""pڝcعNwѩ>zחWM妶w=nu47?_5 ~ WC1#!A Hɒ׬%0Z @6n̢nǸzwcĜ{g4c`}zqAuwnOVݘ7MႀV +@n . MB-8 =4+8|#p>nm6_s>4+[rv4w]v9a\vudo;F: nȽy.uo?"cڂ3)e$KnƶV^\ބ tӌ?e"G#2JJL%0NUgBŃ7Y627 "1¨T)e7PJukjԇ(>[:tGZo.OWu΅/oU`W3BurtrMw]TXL -ŲwKfڼ +.ҮR\=tUs!MIɸPuFm]Lœ +_uq U=*Ya!yI , HVI]?E6 b*OtX +m)_]7avG<nURc.ȼtUV٫=vUݣA5F +ջ`S\_Lq>\ +].T +&"[[rtZ?uYsB|ɠҘoQi]+3?4H  yu&*A0D +Ј l=#=MrJ[;LZBb6,; +u/AO|m,/)]F 썗 їѯ>V-A8@?HӮvBAi)xoh]SI +CY^(%{1%X%gVʦ'5/"b2d~е0m89Dȇ ߻C;NZSs%xLpP9{ KaZ' B"ĺoEٌ +W%R^_T]p7Lh1һesބwnkD;g\T&UkvXs+nzLӰ"9]y޹) DžT-\H?,4ZX|x-Ύ%8GlUWr5O]p lXL 3=˧,'ouciٜF5a5-6#a9Üns,8;;iR Sc\,v0_؇[Gh]dPlgu&9C@ؿOmNp<=Ux,]ֶsRY\uhw͞wQxUSH{=z+gm f8:'vMF=-Xr +rY)BBaݢ{*kGǜAÉO]m0lW{6{ Ov$˕??p Aю^F,Rr($HĐHDǎ\{^!FyExhy^%zg8[1ݎ㚲5C}O~SFa6plbM8LGw3bY'bvF׶ˉ)5WB޴<'^nl7P%(fJtf/ǻƢß I hI{Zܞ +eyݫ+Lba[i|"; + ges,H57'Cm-,Ze2S,/oGDV],o̳+i=Eg|l۷w:iL=sn߷a[|F&[ƇgČ +Q1R^1c<.VNoh:mpKxyuP.+wyqvr0y>M7iIS +}S7Clzx Uu"=W#ΈriAQ젱qŁf.oK7}\:K9]eXܧE2BYvS1 :]Qa* 6g/V$zlfeq fJ9^pmTM^ↈiSYl$Nk Ӈfz:EUSe-Ƨ6#0ﯺS1hp{f,,S@jBn)` .1%Blά!qEtB N׎zXš8BtmIyT bS B{^Ҟu$s~rigSb[K.r<Mg@tjgN Ćhpe^UFh +"aK/lCWM&2 |ORwbt+c!qq+_FHdV(K'[+uƯ?({LC@;Lw +0zcyW7å,զN-1iDK^?X4eVc3#MKG8CoAz6=5 +zfg:Oh]9f: U3 n3|7¹]!ʅ\OS|<ZW +hMʖApF[UaĩFQSԳlK2dNt-WËVNSڽU$~=3/vh`sԙ~a!ANɯn|_Oà FlJ4WAdC-(->SVW9< yӒ +"V7.kz&bXVT%PT? 3^N7("skAcI7+ƂD-lpx@_ [U.Ѯ^w*mDy>];2kǓo됖M&Mƃ'u/.@W΅۹ @bqpd*ufN#;)[O;RcFsӏǭIi=ԃa3(VI nha ;]4J~~J ek', EvZ[߇:Q\{"'q>jBqrKB<m֞5U-ѵXPNi)R{]!Oo̭d](l9=IZ0 WеG]{@x5\^mt{[K)t0=MŸkZ`BRw=!@,|`klM(bqA~/qQ_!dF}{Yf1kXp?@Erl?l5_cͰjGHokhPl_Oϻk:ɀ"cA>al(N1;/cC6`IaHjεÝ/8p p> ^ʆul쭯?|=ٖ~}EBHcf%F&4Nf?"ΰ1<-kDH5,Ĺ>w2laK5)T46(D+J njejpQd.#;aEթYlp W.=v~C 柝q>ɛ;g"@󬚴 ZY SGvcW08U +)]O't?|TK|D 4`{v]nD:qx3s8.=cY +i#~6u6UÁcHr /wb762*m[`HFRyxa{ilT:+Qi sAzS e{5UV3N~ w=?6bF*;Vv" DǃUu+::b5>c@k7| +ܚZg (״"? 75tİ o%AKXջ믋( T{W/gϝNRkF F8YT_Jb^-n7)ٌ +jI= Z]3#J)`FÄuUh^MD鄪-)~ c2& ަ"Z !S8&M 6>:Xizs[84.{p7LH|zBA9'߂ڠ@k)#<üIQ~-~Uv?/7pye腎wQ]RP+񲗬bVy[ՕV\WcUK!& JvԶE_T-M セ_I_ccr1=aimִQݼyYr,И/-rpk3kSlNkm Ye{1[$Zn%Cٻ֊j1>Kw(-GL7bNGjYr +E9T9<&5]Xc3(m~KaeAkV{Z)xϖvԓ#+{|5 T`Nbnh q7v0)N!V@Vh~U K|GRK/ TtEډ/9Fʡ+Q4L0z6CT?~p_ ۹Z~\X/{ V/RٝyRhIsz"҂匄7&8vW=n%I<1i{bݙe@MW]^ qO7/͙/X͈gU޺>7~-t6\u ̓\40JnPz|nn+Uy=k{IS0x5'AjÅ0= C䛷2dp?-5y=ժn˧5Q_i1y;HY֠viY),6VNgzPi7=+8jVǷqn&6ag[ich%P}V8>oCϷ+u}4LyIoʗ<-xm fՄ2tXSذK %ʏl,ӺCVm%Toq+h#)zү|o^,* N+Ѹ%SY* Rel'Rh07Bh ƶD}sf>Z@dl-~e$)ʾs޹g_TFQ\OGuM,0[axSҩ/6gsb?o*/d{>3NtL4YHv6G]ӭzWo=ϷUsc< N:__C(;u /+]Q2"%u%%'?)Xaa*nKC$.| +'{4Y>L;:vՎ]M@n2J xE? +6`^W\3l$J+СwG +8 +VqU2#Mm+G +f@ +Z"L]=0,?GzW#a +KAnڻ%0*XB֮=*5(X98!G8 +Ur)+XQ09{1|Bz9~%`WRϕߪ$ JX8Q`9`JX_}%, WΙ3u떔߼O 1jpV+`M`[ZQY*-'ܘVqCߊ{l'h{N'WՓ( +&a/WH91[=*p)`) {iXJV]~1DMTT +v!_phGY5#*:u~@D]- n8'͋Fn}~ӢI<,8ݡ>PUḿϔ6,yGv$[y 4ӬOK̚@Fe0U.dkq(6Υ{Fn@W[ 4Kk '>o.?E[WQݗ̱> TQ-_ݹ$3Xx CyVMJΛl/Fs 7rL b3LĬ^8SSKwްEKƯCZ>) S*0νZfުTFfzNǼ-ʾSAVz]յ_܅a!DɠSkd] sAJox(6pPCAsU -L =~ ƪ_?:d:%U^(éCtJpE~/y +o3=b#h)~-Sh zFp*o{UG9ݡbvo5^J+˭^qPdU}yLǪS?ДZhRȏӹ\8v3 fIyGǐ_nfEIs/gR>"cʲ%ƒ 8 U¾d|pr?9(Q[~ȊjaHXT6jc(OUUs;v H$qCeL-AΏ@nWCSMi\Em5FEj."Ȉvۈa;6 ,^j#ގ +;5#Փ=9=LN҃o6"G[mP2| =. x+$I8`dԩr Zd"Y9K*DtD`M[~M`UvIh2;_} +!1=ieG|دi EҲz ASxQck %zm(g6cG ףPcC^6)ϮR^GBӉ|ݿ<㸔X8[[b o ]QTA!e:.Y}%vov碩j'n;*XDBP[rէ^*ZKg-S֎ʜ +1X21rQ{9-t.675E\u&ېynPVh'p o1d1:W̌DYwdr)r),5-K1.ń2,{,JbӳPyUإ8#.εoxK,źZL 1s-EWZ[39Ubʦۢ 0ԁJ1RLڔ_5^8cÃdlݘ,m+01恴zvL t!{4ڕwqPʬ鱪@I@.é*g!#9{!4uvg +rbG^_JO`xtS!.tG`)HB [EE#^yjKNPjI%;•11&,cjEW"b>߂UFqHkWk%gRHj%fdL ;^:YuzVOmo,.+%ML*k Om"6[1WJ0t)n@Һc< iWGSEqZE*~U1r͢йQ)+@nC-Px5]Pߝ?Շrd%M8]ӻ ffhVj?Z2~ 7{cv^p ~cڶW ޼YQS*dOEvNUS +)COZUP䆴nsCW=t2hж}dK=DW\BxJNkR? UڎzS!R-Eװy7ppbc8穪5ɦ6ώY #fcPCG)Ď=|:K'>R1tpCSΥÉ:}pt8_Д![C!镃*z[T3{pPA^^ڥCԝ=cz׭; +e!zp+k/ny| [M(;j=q"@UaDF:F?:)>RKA"pQh(JQH0"PD%JdTJdĩaqmJēhohڂ%ɇq J[lF< d`}0F`b#3 Fm# D6uO{ q-Èx nhZ>וa7Km.lh(?\~?db6p0)[/c ϶_ g_? F $)Ĩwˊ}(Djy 4a>'H2R>=ɯNr"4+?ي`3M >s{L4u#54;kclv4\X ltj+ Lp,[{`t|o_GeIk󽖦TWZ 4:VxrxQq5mGצ<-},l<]Nd2p Uѕ~Sђ\jLyh@Vql’V:BW.1 K{oIp NoVᾖ/ +cw\~%q? gcÐXV m8QҖ;*k.O_UG>AhAh@O +%_=S<+9* KK4}ܛe ֩;o(̈ +\٘r6T!P/w^8ݕnvj|ڗ4ўÐJIT5k׾Ct~vL M+I)U rbv탫P1~,J6( lQ şcpq 2ת(x: 8µ=13t_wcUAq>9p>E SuYY +B_$h9o{43nX0,"PQn9G +3>WWCVcB"#_t>*À#'m`sAie"X "\D}r(hϨ= F\1%9֧i8Hǰ^>]^qEz~[;OcqmG>tnWL9{3뭱۫aNv$]=@( ϮI-O{I; Fvգ? *^q> { !,Ԙwj/8ԫm%(TWkI0=v +X#B:ulW:2-*&'* RUgK`h"Y+3B=)V_MxE0hcھ8u]> +WC^zI >'+PS~ +GNȮ@t3^ܬ@Z@StsW̾88/}"S:[NT u,TU hLJiSċ;F? c1:voG6NɕN m5Ya>6CPx=なQQ)8yeMawMP֘G0)iqcηsJ ņ(:Zɝ :;gP{3 }8%hZ<=I?Gmk(ۻHP>A?un^(ò¡I[/r-ûs + 1eY9?U#G.D7>L0̨-A:}5hkuOen +VD~Ͽ}6v# DX=6ҳZpϷ^FH&r5S#DrwS=?l dJX?Xژ,]> eHG2` L! _WJPE5_:;Cۘ40=ZhR#E,wbFY<*CTyp.L*ղT~~_IoeJa8>28)ID" 1Y>*c}6UUGƴRlρsӈ% a|I2$/;*]KL?C@JErŀ`aLvM'G>"JxZ=܈!?rوd5g\GQqR&ͅ(~ g $Tp (+&>E2{X?x# h +dń+eڬ w-^,h( _Qҗmx֘Cj^`[;Y_,˒P'XkVne)ZCKVrh p6wmPG `)ڢ?v@^ @(Mfl'7AlEM j¬"4mxx"⩹APL& CyDQ`XѱQz^/gV#~'|*i +y< <4CfX<}NnJ?r*2hn們Ŀd$}`.&CE}B{:kp8&8Ѳ+iE ;]vtaER:OBlǦl9â=yú!h,N$4@I1L DtI+&?l‚UM=tD 6,O}myX:!H_ c GEZe@t.͇jInZMSAoki]5US f2VF~0O]n,|}:Q%)"l16t#-(cNJW(O J)P>LQ99 +JJXHǩ@%Sg<SU.QWd*5/WNFɊ]} +{1c!a#HݜMZ6SMtij8)W7VNZ2W*:QԺ2M=P Z=& Bʂ@ U)*5d.~V]@Q.A@2U(Iaԉ5*UgriY[]Lr=M$0HG%}O>KEN@=^HJkx`Lv>uYmaݖ}3!@Sΐ]}0Y2WQ 4fMCzqϜ{PNhJM1KQZ6+F_lS ͽKߵ)3N*!0Bjv˱]&VHmN Wa.,Xd^e +R傔!;̪&ђ̷:wXǺXsmli +3pr,7pYV)xYXj}M ٞNQyp5W +Wj1) z: !ihH< wWUǮX]>>n ]FF*r &3uiwZT\[ZcX?{\Sp -XTEyN /i+,šC@ +6WC 5#Tۮ0!:~Ie_gSAϢ/HDIE,2BJ^21Hpߗ\ab8H +ఫbulOyX +Z$Or#t`+2Qުǐ)O[șEIIT4TSb*bB17Pm/ź?]Iܫ)L2g;rӇAo`hs"HRA-NLF|C6zaH82\ ^+368v7r%Y/D+BV!K.$U-jj +:Ǒ4FɯWZ*N-G9*״~~yׅ :z0ŠF^*.qn V pO Yu~߉?݄?:rdC{g@J”*IA$&&~ +KY0c7g|3)G W;q$; %lJԝ7-]OX[j̝@7fbS"R=6rVu$3q#`u$sy2qc>& j0hBN_ΫsdoՂ/yͅ86cbF[' ޫb +T(+~ejagP؎FSG9q0 paU ' +ąe6):qr/\`ssAmi9lk/n5/h\qLeqƍ0Ch/b=  c_p Ǡ:q +>ȷD&gL&P@017^o˄U dwX#ތl=E@O#]k;V!D5oҡ +)`čYm\vhz=>6_>zi]K?IKʡwKFEB6 '"AvUnލv/nӻ'HKo]4;hCmz+ryz=SV|1yսhӻ°[Un"܋ir7eO{̆v7n^yUwrv8Anە|rSL`oc:@n]yݶ/A+Ioj^H.h㴳,@gVE*u+>ya_r/Gl v-"c^.h%e϶]j3S8Wy"%uf'`TA\uvq[9c{^an@97sXsVg/&!arY (Ţ@H-AYlׯOy]027vT&Y]էsbD7UIs |?cy@"3] +ʬq^eeZTqw푾afwCNiKYŏBp/X|z' JO-³D7!s{^ fH>uWiBq};\d8cK0Ē +pDGeZX_\C* Ϻ'*Z׊y wG}KxfVjxr!+cĺu֨݁0ƻ-eǠC2Ng9X=YЎWw;l"L.ʨ.`7JFe5(_3?O`[Ze%ҷbEPUD9_ElA[hЀ)UD"ʽX"vu3P[$qP$t$QAe񣶇$# 8D=뫈mU$i>%T 2W{ DLU #ro3XtHyAe,d[ހ: +9pgپ}"TߠF^6C`ΘN+LSyiq'ظ+iw=kM:1}'쒀M0ͷ5z,w0ƹzo,co$OL˿ߏTrs>O/mԿ*9Ѷ4KU!(QF@Zqu]jBCo3Ji,;PΟS(tMO`YyjUP"Uj$Q`>NE)/9Q!x:3c} R:gn #i9-ci޼ + ^n=9웾 akDҠ-Wċ-#UJD:%4x(.~}m|lL3.u9xKVx|2{YoCL=͘dSR?Sl&q69P]BhU?a/ꁚ<7;B/X-)0w*}}.G_ұ(]Eg65?+n/mKu'U`|t(N7[~qYN~ɷ}z* ˢXyn!t]@߲NƺR(A3H%([dk+}d>F(JPTvs!2v4Xl%c[x1K-#T,t4'Tt`:c2|i8pi9 }jE.TBwi7jZm׫wdȡҳ]Ɔ&,qafЅ8ܹq|rE]$P"wv_>4;o凑yiCeS`- LjYO)wUya73['爌Hgm4_JBiԳo*|j嬌f[4F; c,YaqӢŎzncljT\,?].6}.'d2όY%vM4pCK{Kl7YPwenͬhTʾFݴ)Ǚ 7! G\I'*YEϙ:Q9q̡I;i)=9x&::LTӦ eF-f!'T>5^3e 5DTQdj|l?&QG6LJD.PCϕmU3\`D* xO[#/{[=.fZ=kyhWxxE_4&" %AgR9NEס _z^͗vanCe] mu.J\56l X^!R4l*a.Mo#'n鹕2/({+QY,hMpQ7 ^6]s ȝ ;c.]xsT +\fѐ$W7Qm=q1 jwU}}Ǚ''a)/fktjy~Jxjj#~gg>_cL9/10y{­ }ekKN[/F +Ozm<zmO$ƀdWZ;M/1޵]8}l|y6z6|~Z7g|7s-kshB}-7]5lWKn$;}nRǘ3o0 {1s0"̬jÀQAQJ e!O\E |u'1Xnbn8umؽ:X}[[&|פ /ſd|8$iǼ3:9NOJ2ۧΝ,@ &|LOf7;,|_"P1>5F<͢zebV@>ƘM6D ,T'4uk5~(Uzf{7KzkZ$x&nU처e0mR1U{]9X.Hgi;8`o%kQo:1ͽn;@ ( BhVYWS[G~%|~\ۢ4~'C*B7b%de'ܱɹ +CSaO~Q4eҶ>]`{[37uFO.A .E?n|Ʌj +3yIii5<aemNsH Z&*N5"Fi[.dǪ< +}_U[vZ`(•OO!Y` h%7#>[aL8 z2nyK^|W<'cӐ58֘ZNQMd B`) b@z益@#I!iSw&F&B8p6\Z\Q^[sS~a\ fl0ifhN8e24702H%-"d}C~|EiwꜤpYY-/V*ViX?I^y32xhX<~Qq`¯4(9ue~+}\9Ou>~{I :8ܙ.X)Ub=ٝ@΋ҐaDPA.;ӻ7&Eۖ sAȕ4)g1aFo;'pkϪE<kU!2_Ls #W罼6GM w͋ _*\|9ӈh&Y99F8G&$ٛ@WqQ[3.YP:2ADQݎP!EJ|2f<1^7zKY6{ׇQ| J4ZN4341FegCM~cW9_1hX݊x];iDC%Û"3.#Hͧcr7X>(ao:%`8pp`'nۮ̸[>l#?IHr׻dhdD)yJC$s׻]M0JK/97CA;5f[|*ˈ@?\܁`(b^^so~Hdw}G#*X?I0x?JfM  }G샸&<7oVgN%]UPͅ'حxUj(>1Ά^yd&&zI<9QHUNp#˥_`~Ej(AaCF[8\g%ї$`u?HBKY>F%x7|T*E\e2JNf +{m=? +S= +X0Ogr r0eNXEQz/x>5*Pd沣M\{Ioyoږ>K3і_xQ0eGi=ca°9B.(]xQ`:l lMX&&#/yN_' + hƘjx>{] +82g$ۿ#󼉥G|2wI2m+)9yf뜴\ךUeiJc*qI ξK:@jP:pbc3PQ2gqCU1eY8Gɣ]QdSCˠ!*Aw0 +3 ﹫hY-B3}-:GB{>#Uoҥz'Vs P7ADtذ߁/)e}7d|anv޶΁ =oX9`#oۜ~~vz<.?K#yJ+$s׻b@윆7;3OD@r$z|jG>n~y7px_4Ow K#cSOQ3%!̱g~<>XNI05R4G3癫!Kߜ2cN$, +2B^&q2Ӿa Cur%seƠ +Z +U'Q'&Sf:_Ѽ(EGR h~ؿ(ŀg|\(:j4U +B"Gx9O::_jN SdSPD? 2"if~Ռwg:<<ѣ:?'n[˃i'yiExkX7Y߹ofX#*ƼS8,!`D}8G1n-sQ0W;WHrx߽O+EEA{] G?8W`ED\y-$1RY֑~Xl,63'|L ML&rEKAJcӰ6ۑyx6)/36M'X @7蜽a_j& ;` +,- +~'R# AYhr7$ubGjXHJʍQ]aqZ -`?Sv,T"O$`qs';hV?5όFQ#Cl1 f=UTa/`إ*6ܴ$uf!Ⱥfcdx:>ӂhub,27Ae _*HVĿW| ic~TK! M^鄍&kV@JrlMMr?9Pc16B\U}? +p59QG|o ?#TԒB@V`N*BN8Z3ϑ<4EĚF{lbkvĕ̆𨡁lM^u"m1*3q2=q%WL^P3-zTǙu&3JUءI;> %|5 7 ZD`I9PF$\J' F*J΢JbL.,(Oفm-$zXVLT Hݘ{Tg;PEn85Lۄr&9,5gkLG#-$HE\X)4Lu``@ ~Zr + +w|twNӊ? O,pf!Scc@s# qH4Rg E%QcK_@dK]Օqxu?LOg$bC%t23D if)j@*Hg/=`мáQa92w%O\%kL]f<B2li?k+;H1 / ͙bBy4 WCwRx_ye;"fb +4rsq1ڜLR9b#Me+@6QO YGU(ggU@-јA( Q +NJ(/,FUqo Dh6hdg.X ¼&wPFX7QN! ɁǮ4ϕ”/9ÀYA)feYsf;FrKL+ (5ƣyl1蜠.6@==.bR{BY*@kp^{0h*{ {$c~{pBG5c;X_P9b X-cXv!(W]Y)/VrJiuH! +St@z77""q/^q q$54wP&f8;^q5JsB:nHQ[?"ׂ8;m`l`I'vg? - 600|0iL9od|Fd4։o`=]#nltx7&ع?MvL!ǓÝ~' ٪ou_mho2OEh`a-QۖQ! 4 D[~ZodlDne@j?WqaH47Ŝ#15xƅ,i+؍ o;9{Mmʭ~.:ct1FMai>Aqz 0q@<@=}Qi.8[lkkb\Z G v8(sL]-8l+o %@j( YL9'n]H#JRďEuӛ`vܢt {D1nrY}SݬzHNugLc9iZٟR$zKشn^4gpQ!7M7x wU!+o4sptR.&j%ml.M㖇\nE99尜MrtnqǍa98IIs;9&ŽSFm^h( X+gBO`gA=#35F-ƒ.2e6-f_ct5R5ێaeLnqCKZ9|s ^C|D6}Hce&^ҐϒP4&^%Ts"%n/ETu#BKy310W:f,@/uT70{; `=r_l0wY+Y)%/ӿ0ǿ>@ˠd8t [nc9> rGE8` +(]H6%V>ϸ)nң48e.R«1W8..> +a d,`Iy}q,֛< }R4uӽt*@lOKMU_ˋLhM.Bcr^bL&ګhkN='fK. ci {swd˟}04LpjF +q3?+vh`zbܨT{xL/{j,`Hp vץOaMb<ʓۦpCr8\k GفGay{0#3 c)/gm2m-Fx9@:nf1;gdnDlv@|%Cر'=cմ~˘,nvc x}|@<#X9eY~ >] 7 +fTRU""T)9fX}Sg1QNl iv+[l%3FiK{n\ w9K*.nJMIN쌧R7s" v GbC~+{\ٱ8խ{[`\ymeںͭ-QB[ T?㖧5\}eGU*@L3UАS 6)+/0Lzc:Y=^Y Cy:R[Oe:>.Y˖uljqsG^G\wꯔjUln+W,l;{$@n]v"qI OPn@ݭli^n:T*w.7[f pV녺GFb]nNgRI7{K?Q9%jkqq{<: 8rlƖck=B|6@"{G`1N.9v|xFNVqcW]&aZZYφ}`Iym7g~kaڮfVЋƻN|K6b[xIV_4&Ll <H?0oqcZ vwOղl5dpnq՜ܪIOWL%nj8TG̣(OcW_z1* +U3VrLZ:YW?t_7/ZoeI}fb}Ծ|\ڷSuͯK<Ъ=ݻ7+fMA|5׮;c{K.g/B> =/!qt j'ʋoUy]P=&HVcG֋7[ట7WoހvgCOI7Emfw*ANqy/o< ;?Ӛ; nڀd|4l@75/k<Ь#t!Kۦ9;@egCq*Cd{i#-l=gF!M \ 69ߖřWY6p^6k&wMoֈ=t^ECl8ۥ >i$4*e@PcKb#ɂ#,{ Y㙞h +&3B3yH3N\"]7Ptj!aNZc/k'Del^G٬30)t;dE'֝a1qt^[390)9~oZr;yPv-pg ";SpK[FH.~rFM>QO6RvLߨ~7'RǷrn2Rf@M kpQeF + mkPv?H. +ȊΫ,w`ԦJԚ&W%v݇.V/@F{pݖeOiDY% [e8SFjmg:uf +͆(m;*ZF!|ف07"-?P"ʓ1$;mf/l:P;Xwa}.ޝkq{yuj5+ip{ަx&<(\u {y&moZ>׿O|SFcܷO0?~}qJ]< - ;]35"ie?|(+dpz3!Hdy:IJ4#F[0zGp90@-~b ȄEsW?nR@!LI#rȤhORk]TbH'3`HYv$X1t/CZ9 . + ܽq1?qC0 +=Uӱ6'=َ5|e#&_%!^áWDoBK8TBvnC6Ap5G$-tpkh12k.Ԯ)$uu Tg`>`.OlʔɅ +!cj|qYB4e;ۄTKK0OTWiZ*}*T) yJYlU?tMu'^S ZA8lSgtuBc!2/+HRFaÖoM^ kS CA6ǣ а4;Xk5*"ֹ'bjYkJYUpY-$] 45:75Ǖ06!nJ=YWa + +\2][E+9S? }w)xUQiķToWFX4]8*WVm3)fh[u0&X?3CMUn̶_O,_Jݻ o;Zqb)L-zdfa] +㿌f5NP&pry>`d32 +um-;T"" n|Sv 7Qv4CM%5j,ΠY:M!bݿPrkc)J)5>**P"*t)3T&fbk*sG=BWpA&J%/UUJҩ#vI34zeU.4'wbf⋙DM.ΐ|'_\" ? -_I(œdFTUj%R HtyCH\C%3ä +ijelqU'y P Q%Ӯ-ؚ o;ۖ 熼PUf텄,G"'d܊ŕM\R ]hu\[!4>b߰LZ9ʢZh8 +j00<1a(|HDX5Mq$( \&lkf V'|9FZ]e,cJ'^ɡ/t1s95e)tjM괭NJg0ZҽDQPwJӐ6bߏlNj?^h6?+ +{E!0J?0^,mH+4!@QQ-q$V*+*V p}1%*_`..S_Z +M6xh7bV@vv,8)67CP_er$Ghc+J0K@q.c'\qlX Y|F#>٩/cK U7,ڀ8UR~Ep1'ƴtsND|ƻN,㼑!!kaO`U:ny'zc)dftT}Aߜ);S+n^鎔Hz8?C'#vf7^ֺtD;y~X_@"\kp弎,ve +Bb;22E#9O1eyp>|X |8jRgqWw17C+VbY,͡Q>SY}+$k<ϙzY@}!-Vĺ,&oGS/~ kbϦ/ (ˆu6ng,;-Zg ~MJP=X9 \VJPRc8Q~(Ulk$!,ٞ]> {1Ρ>N)<1q 9/{(F>hRjcOgh%Tí%G@(Z8ZYdXL7X_ɦ6i$1ZOR;ta-[رݲ| +Q~v I:l/.p܇AUQ<ٻ'kp[riT'kjP~"/QJbU/۸vh4D"veo)J+A\;5?ݳ*=k/0*wxEg}xD?>/3ӊW}k|-s@K. +TYq%f}?@r>Y%iA|Եa~.cl [=x}rfy)~YkQY XlxutjC_,ੜDEdapߍ!ّ+2 {fYʕ.Ջ*-A1 ϶+8j\OpEhyTeio"2:H jԒ&|gys{ϓS)N:w*;$OdDPWKޜz4cp8ew l;;y%DCt}?/s>~]yRt9#t_Zr5~W*7?_m\U]#ɣHG\AWe" 3 B&N [{ʠngb: +:d%E +Jb}`WvģHy2oVX仦T6޺ c#01Q٥#K8{FTc-0kgrG|nӽci) bbbo^8}ّ+BSa Z9{PI®oo/MW3[MqՀPu~!DޒMxȳT[UlɕD]I~΃=l %Z(@)SOQ5(;5a:}N3n[^ߧs!ߧRڐkQ'IT!% ligG.r\ड़1Eu‹aJ,v $|]YZy}JZz!{;#.3YYOWKOmfm=S9/;Ѿe&HXdlpŒV̸Ǩ\X\@])s1%A=QR݄cg_WL{i9jlC8r0`4$1k,Ge':! +$]_;nS I?w n<;^s;n4QͼrrW5՘Nۆc@Fp_<֌:<>I͚|b΁#0+eJh;1P ~3ZTMaiGg4'ڃ4hOجkZ2b_EVĔy4p:Rtۧv/h:NĊXyܷǖ-#VAk+"YRNУ?sh!uM@OgڃȶF5dޮd?W5Wvǰ9a y]'ڍ2Ԟ1Ue4#Mcκ[;H1i욪4:c%Up?͌8G=E8|޼jwmoZ΁5Eᆰ0cGXߥ-3g مbbԞ^2 p񂢫r|>ؤa6Axn|jʧ w{FOm*Vmrzq'V|8dE 1b m`2ࠈDhkي{N $!UfgYԩFG9IEe~`q-zl$f;_bc%ؾ\j+]V?s6vIP/HHխs*;e/2y=eBWuf:s)8K֦k"_|jy U7hdh@Q4"#BdC}榲3ut6(XXe\T{mbK`yI<$c. / woZ\} "46պDR FYb3J!x|CDFZYdxISGyi@vcNZF,}X9U^ͺ5gr]?gZsro[ Ĝed跨mG/GĝV> ng#GZԖȴ\Eh,p#x]ҩR#$N.H$+TlvQ- RߙѣL+0¨ [k~ +*0]8 نvTuoEjEE%֟۞DӬ:{͆݉4JhZsȦ=o΁ls<Wf]79X3Ik38{T*+l$kA-/5: Pћ"FyWXʸ;Z9ܗGfyCV}_o[ޟnI[Q}zaHaE%)nNV.="|d4,cWnݘcΏc/ 9şl޽;\l&+Ldɠ<|8Io2v 0CnTӗUiG[ǵZ:g=I˾$,P<2l_#sxrdr덿EF܁5v%%N0{T1 }Br5#?Pg~܄;9=?42^ wuޱ x &.}\Ѳ W՚z`@ZAXS|QrF̴u\WD.]Bj |2+ۣ/7IQ8Vcj5D}7i +R}OOo\dc[qv̧ 8A^ogRS^+%-ۉ@ /"}L[nn\"7n@NiBӟAǦרzFRT\/FOSnI^z +zee_j'M9// bzB[T/^״{~]׾'W̲M@ ?=1.&915'2-_^O1.rnxV|u$jܶWx=)}7=)żgXhv/Mkb{DFodU6pK<|䀵;ع˖[y4F˓$ Mp;/c]HoC4VoJ^- +͍l}jޠ8utPR%#=I/7} =Dȕ&dzNM0gR}xw͘L ukf쐄=| Jl?zwH7(\)`~sЃAvrN%X6/w{DJԻQyx2 ]*ڌsZ[k݅[Pg@u@8</T'1Ylj{&:zO6LꥹQ,OT[P TAMF)ǖp)yOT.|ǡOI 툿8Bln +qmo_Yj˗<zJ]p })N%ء̢ tx ԧ;]:x6sER0JJ<[@c[cexaQZs^;p`eNx)Lm0%kb}i ~rq G\EN_ڛh3Fm],Ypc0̀R=ZfjZje|6=Õá~zfWa`XSZ4sҎxU2&8z'$ȠgA--3oGl3sY()5SK+7^G;[s@/I43a[6+Y*o8SK3 [I }DzśG:\t?\g, 8c W%UNؚD+=;#9f߫UA_F$8V(7H'T+gJLDQ #z"  l+MC~q76V%?[-f*y\R^d{_tjo~P4 H*fIOr͎f"{cS/^a'*jU#_+]Lb*B(t<si.tMc0<$9P[־Q&2;IP}S::`xVNQCyca5nAw9z_}\B+x_b!:O9K .rܹ?26\XDdo\؛ iMx?o a^a 7B cWX=kZbXtX ( KKtchT%p (ಸ~ႂ!@pH"`-ʼnc7-d4=-X;K&SHBx-z)5qwG_TK C:4uIOqt8O,fxR:&+::g;P(6\]+]廎dAbR)&sUfu@:۹Bڄqx6Տh1ruD9"Vm+/7dvóY(ق|ǻ?+skFڦ9}8s5 sS_._U },MXQ#k!\6t\ﲢlE׉]ύk2|Q\#u ZXqVۇNzږ%t2/@Fmu~ckrpðxR_S8iC4|Qt7noj̉l[aNI(u<Гc+ OJ3oG~hOGX.M: ¥MmL7r-\]:9k$ MyBb}0i҃*n?PչS}%r䓤oʑ‡S&1UyxX~2iyxdV Qbց^/t*z*py +HٸfʔzK.һ<ճt̝u?f㡯tWWr~tEA|yKef5h;YmL?Ъ~{v 7X+I{cpn3Ahr>5W8;p-І[尫}Êf=]D3Q޿z$Cg>;wUGt'XF΁ŏ2pKuV7klƥfϤ{+ئ(.Vn]?*aNmD9?5 Ϯ/QC8'\K!uC15ɢfDb8-.HxO?e;ekǻ +Rc/t &ZiìBo~{ٱOjEd #ޔh& Kl%ςp''|^q9ԕdl׊^m8-/I[P,r I m+-#֧ANOexXܺ_aE"~$a~ +++JI˾UE )+_&+LQWJ`=n+ԓXQ)`y|5ŒV5xqpKFy~Gal&QLxCo sgIZOZb 1^V;;dGrx|A3I;s1.Xb!JnhISn"qrMRH-ݑt'F.(GɌL@,^T`.EN3?i֩dmL$48%'nWt>~>ݵzvZP-91,dN=piO*Ш9 }]ɫjhAF8z0F7B7F.z +|"fb  ' kh. ahj hHO{J 4~xilMD.-ڥv2l<2 źHჲ!4dnF@>PfnOYVaaNװ0A/Ɨl"qV}M<v4 wˆVN\5A*l<x9gN\9:2_<<$K8ke}-zhKR/j_ +znZN @Hv|^K/j_І-&(r <ϓ_)JJc$'izÆ303n}W_KW:yznW v(u WWjKmEaS txm A]uN?UKª#2n-~ 5xh_pnDj@ +7dyP$=kr&ڥ=Q#2:A4/ó:K0ܗlcO;M) О w#[\?w^ly!{J3C36DLtz&j'-|_=_Ϯߞbu85q=g/A:VETcҌCFS$(Q4)LC&H'~q;j*+oaʹ՞5j@o6ۣyvy!$:T`X0Z.ֱ݈a&ex[X?p;k@J( +1 4vNKfC *?+n&lmA$ƆP!|a&;ӱUS;- (S//j'*%ۡ&FȇvR:A69r_TOCdsq|yBgdM`!ٸ$V2K2fϊFY,P?GBlAB=?P~ĻCCz4yy6WGVE\\PDy,Si,M!}T fnCQw CGH:: ɦ7@W8zVz5ElzfG]2XJ̴;:;G(ލ5bmMbj9qYNk³Jĩψ@Dy{WVZ׾NY`0;8v(yNJ>@صKOw|+H(&xεF. 6H(t(wؒ%?eC`R{܏ DSZ ri,HL$)#%sA +oHѽDk(쒣뿜ȳPYQI>6yxȳ~y֏Y2K=/%׃#THEFDkp!zd۾D/chDLU<{ʇ4I5{?}Kf7}h&oRb[A".M/Xq}掲AU5ޚ~TۂʓLItnKxͻqxOFk⟞yǣ@('gўuFsv]!%mEer`ӛlcb'=3+A--B j2q7옻}VlNLR9KK%5R;N7YšģZ9AڙѮ*<;nᚽQ|ö^>9*ܰh.l[?l;J23'aqGo!f ;:g";ThX v~ bQiKY:gHFR^6kKQ^A:OH6㓬qv?^nt`J7)PkX{w6W 0-nkް~7/b=ug,kes z3nTn uVݳ ++lIne;D^@eGj} #磤,g;_*{00vRA}ȻF<Cq!E4*Y3 -GPcbs ygXo؞y<'։6r\k>gSل6Re?6Lo:H$ ݱG;xUcE:vHR4klZQ[K>2>2Qvn#a+dTmОn+CU*L7<,IO]s=VTfHνՕW@}vLw3rIgFl(^yhE>;ϯ[eDbVH!AOtc#k,}g9׃qg _E5qƘ&Yhe]}u +Yl^uWi>wYڜC:H]9Aߐ[17 h1yz4 +fq J.g?)mI2 [ëYXY'k췆ܧݴ":p:}zKwo0bv퓃dԒ>sbV#<5O*Mi>N^4;y +%mm_xkGՇĢ&?O |u"foB@_ߔk phbw_)vݡ~L7$N_h3Zǝ.QffvcX +I2 [b.5kp;nneh 15' RukxuJ_vjϨn3TN7B^Qx/Uͥ4 6J׆E x`xnKg'陣;׼GԮ߼|8 nߤ2LH, +ic|SާZ@k2f=9` ~5p&` 6k==|E fcE0򸻣~oێ0`` #% dTol7K#Q0rS:qy F(F0;x7yz놖}Cwp"+5K+J4:xC{ $X;>Y<,%J ݣ-ދ\|t$9g_EBVx4 ,}4B΢O ImOG wz*x$&z,J#ˍ5Ԥh<"{,[;V9F W1EԪ\N:3,bU9 ѱi p.SQWɇo5խh +9Cwի~+ؗqE$&Jz,FzeI=>]idF[f5_ݺ$o %l<E [c%=VM^%[T{o1jӖdC{$O(!dˀp d]fs7fuSƢU{#w%ʌhe{g}#ɷxVl[EpweOzRm*J%6eέ>{ArOakT +E.~qqkwqF!=O{H}ơڥw%jb^/2u PH9\HUh^ͦ]}.u3bإqUT)W_[$Vd]H< EHOTY +MJe&8"v:ˤ"+ rc^`u"LNo.mh[ː'2^Zˍ <ܹmxD!eSVDryZ ynh,e4g9fWk9w]7H#F9ȌЄI(/6oEŲzkyb9a)w5[ ?B[Rp OTp5Ȏ(qZ6^Z5WǠN[UR2-͋؇h2Hh ܵȄ 4JZ8iv @5 _eѫ BWp99`I,r3wf9w8^x>Sa[# W@īUjuQ\ -ɛ:1ˆ ;Ր8cĕ)R{TT I5REf||Y[}S7RW^1IwnhZ&Cύ|}/z)*i.<>(.Zn z6R潵& + +PdSiI|ɇ? wyOCmZj%˦\|-`crOlhpu +)9F0#2ij֧c"ծt5txA 0Gz%y(<>CTTקGCK.N 6U|I{$%) -7i[HJvVJ߂ʮ 9 +gxooW=0&E" gx<-_\{Hk)!{1`?}@ _JFSgs>}wk4۬rnc&| F #+ac !Q_u~| h6mxn~olG_FՅX16elTdl[R1$c?H0&26`,ƶ%VJP]iPR6lnILPDJ]_-)R&eyol9.f*fU?o,/{MH!S-k8xzp?! Ȥ3,T`NdĵjT^zng/ȩ/dLC(iM;C\-we=!z@=E2Fʹ - 'FUs:5tn,mqdS"O uR,)](8 Lhֳ6oe(ۥЫ8X22~gKlFרQ5(y 0ޔY 4;R^,{qԺS^h,peA!I}LڒQrD3mmFIK\pg<Y i 8Ekmc|RY}%@Xm~ʒNWyզUtz0xpJIRV$1Śem=m +ͮW]QmJN7̌O?/8L..Ym(QDdMq#4(\3OxSGv ݠVxlWW(jol7>e>l\mWFuME߭qL K;_lH _@&s#ϋ&,bOäFoX\a@VU$Wiikt?` XdΓrŀܛ[F/rYcQɩy,5Ɓ +2j3s3}$ s'4Jv';t03;=k7x斋$/IZ"I A89@ZK\ܺ7K56fjN2bƔ0/I(>L.hc p84 +Xce,^}qq[,;xVL+v.S}q>@'y1iP:^K&J庂O4ys/dFIzcwflN+wZ#oZ tߔ#5!XcsAZ: $hx|AJ++[r\O7F~zZd˒h`xh RIDb +n Jc%Y2< e閍>Quv$KlwO4~6bǛr!RƺwD<;\n%q*=vN*z01ɀ Nt9ͳ.Z(ʇb.V[ɎB qzêo]뵘`,Hdbu|\<k&FLT!Χ<|!3O~:<NS )vHD9ٛir/֝z1d!`4id)L+GʺmLSp(*WOP}b55'/JlVmhw6~Hl&jAe8We!Z`3|ϸalPAiV@wƻ[Duopl.gHjp)\qq<\ +q.gسTP1LIMnrT"C9~6hz3m2]gm"o_xt`A0^ ^KgM( [2#6,qTa'bDB۵-:r6\ĂXlʰPqk3> oW{s-X\$0q;:++ `OvSo=s{X oiw{3`&nRqP{/}?ˁmaVA]PkNbșCqwjzu]kz7zͦ[Vr7]]%I6l_ 8C5gȰW] 5|WGEd}HzI %݂cp`.W4uh!/66"edh73ഋ/rVfUW_|v3,/ىbVCnڽN.[v1 .څcc^ڭH.<VH+]-ӝݢʥ.ݏ _r˺V/brYgwaM0gq,ë.\|ݼ*ޭ4/ŵw?P>0$xpD:l:Cw@!`*8=t8`\\E<$;aÁӸ>6Qt8x:t8"r:" Dn BҰ|(4|7X|*C0T8|xPdCGyz 6( ]6ZbC/:lt +Mȥ2dD&W WzyȐ+|ؐ~С6:=QlMY>}C^gdڡC:CM4^oO_Cdz0%`U* b00^ +dӼǢ byIЅ]$`7Ɓ4BqȂS7^>Y(<:y/\u3,IMl>?3 +.nw`Ht/'p T>r_ܕC5VGZjE&9a'P)} QN!Rq(X)03KMV%dvڛ_zB%yJc)N(gm2IU]V[ݑmت*">' ?n= Ǵ#UO>dg&u57mwܪ؊fֆ?7l$.uE 84WߒNu 8:AOŌKSfYptbGkl'Jm_[-'%XMߺ{-i*MZV Հ]ޤ>G}W}@:Qf4>UַraѼC}lxpѯ´U='ԇuA8 A$7|v>a`A>. AKxBi|#>|'k]7vNy`lh twE{ƒu+!??yU@umŅ&ļCI55I{hYY +;o>ir Z=gsc}g\'֭j럿寚:x2_~~6OB:}!M+ '%MK:Z+@nŭW!&v_ G[Ԛ/r= +2N\ϫ/\]!d67/onD"s HMQd>i@!9)A'G`Lq`Ѫ:4qrVߠ^%cS~wc<~~^YHpfO(_ܿސB-(@*\\ iW !Ӯ]U` f4O :9f}K.`[d4~u 4dZ=鳏33zX=nW0sfioq-T,%9xo|,%8~@MOJXٟ;irSii٬FfKdKsP$g{Azܿ<TlQa,+1=G\0A rWnuB eLJXBW<'hw.dMwP˃wo1k/<;MFSRf+Y7EtN5m"Մ*gM}`n/j;)rV 4tW(f>h]NT#lH H-v@;e Z1p'MwqƸA7(q1 FLL0f@H݋2fif]zpuvvCy3A(X*D|}W +Y&@Tаy#+&(uSu VF)15o繢j(͖]cH/P>M`OX׾l~923t>pFXh"Wpr;'3fЩ."*j8Ϲϛa |Csf>0`Ao'zYwhvP·|hI;XC$R>@W>4uF>lж#ɇ-*|ȯ|ȇq&C܄ Rz[s)7 I1ƖbM@gCw:Li +C^iݶה + Dm7B$@VR=t7vmsNh[q[q9p$lR+qsׁX-@ E;!cR};z-VP׶MMDInלOoXGyty[>la~lK^Fq@?GITlp +DA\ :I[\T#BZaQ }?է\;=Ǻv:m*in;U"C~&UHr@#˃"6bA~$,T%CAl?i(4BC\3a^ \]c,pK~b‰(c!7C 12@t\5Yݚa'.j;D%c J-"BG +uWVlZ?R@˾x60_P>uBO )v};*eDK12.ZSGjn{5y3Zt4TI/"6.&lɒr*SCǫF1);d4ޜ̃tў3ەgCƇ)?43j._&86'ss(qຽ* LĩrZ_tlSCVlɎ[P6'OCdАPuTZIҹ>P42s\Id@ʩ=|TTKBOW# ħLjLzɔ0͘'2`a.Z?4NA*s9cБ +@T gcR& *=V13KAF GTAW% zk):5\[r Eqgx~ySRщ,0#k~^UsP 91"ޤG(IL/e=1 %mb^Sr?]YK[UEU|ǯ朽ļcA ?ӺX#jq?P/Tc= ?y ?PECf@xuUf: dܜ=CnV>!d뇌dlE}.PCF#":f%R7!M]m_g&(3s߭BT&r@םRh]cs +,}+C7V>^0nr4F ζ^ p|[se'P]GU,՚KCJ/ 9|ݕ֛Focڱ%uXޫ }Ii=tw#Jvͼp%9 9GL3s'؎| AQ|2u0M&"vb0,pbt]_9Ep"8He{?q0Y7cVUo1@-"liMok!--W!d3ձ/el\_!0oASbFӃ:i}-TMϧ 7Z{᧝ ev6͆ڝt4CN;(v6Nj^mAqlnBsܷ~lglxigu?STÚqJZ9Vd)9ۜgCψgDq f"? M4_ K5:n1m-pzNClt*@t3 Jx$d83`9 d? s@ G$uylqzoC ECC +okZõɰ-e1؊(@.C/a]=xՓB.Ġ'׫2 +~ƟRQY +cb»#p6={oMuI}. 6JnDXVC@^5եc[lb;@{[v,C u].LɀEiqj1kJD&4lZ*˄ODBw pn8q{DLSШA6W=GQpsdDX.KJŤaF}'2raؐ&gEIP2f=LȊ[fb8 +8vOhDѨB ^XM>u$@ZBK#u/7J|2z<` 81ZԐf/*c9B'Ofl1r/hFy*Le ]du؉,/]9N$/V85tO{1nmSr묌%Ġ3\-;OSύ;o*ya~W">N5%ʦ5n#1y+|2P\xvPŅBZ[ +Xv|'A`Br.M<x:u'c#и9I(e1h=>#@^5vr(@~],bĭ0Fr^>y:qㅜJs,( H6N^n%Y` 6$`ui +N߶j-!u/(-äl2QAr'w0+b=0q,Qj" q."4'fr}zj +;)ujH <-2 bVeH3X<"kݰ!Eќ/T, a/9DaӜޓV.*S6[zQ +G[+ Hҝm)ݹ]4 >ݕ ~ +ڗlJ3w=Nϋntz.#)9>؇.hGZWzLJ62M6&~{zhUrp"h; % 2+%Kf5TסU!xצ.v +SUKBƲXv:TDksNM4][NRmU{ICSsa U_ywGaYB`R0o/9ypmMF `v'xI#sK¼jB6o2>9rʧMFmgFOˮB1zІSͳ{_!x{y(?ZgWdJ +\%k +7P}6MLIH̰=rɑx)75.>C]pzu-/.NxYs*E'MD8Y@wk3c,p3#£0oQ4^?`n+W@"^5{.zrȞd7'!EAIN\WM%`/Z9q]hK„5Lʞ@8)E򞝳v粖QCx$o$@༩)"H3+QT@u*q:H:vj| HQCb~ŵl bPxxཬ/=o~#6B{Mb) r4)c)Khl m%yڋB|UCܵ!1 +P3V0RaM 0BY$<ɀ ┇\b^sCڊ%1ZJ2.*KFQJAʼwTWIb-] U8kq!zT^Ă%X je*P`f?_H6>76w?; cm dKw, EqkNY6' _>_{dL}}&#G2/s2$n}MWib1H+aC +Ż ck}E@e+ +pH[%fy`9Q@ӮϾzX`9c3ˁQo>^(ned&6 y=oߘ3},iUgOnӯx9yvy}Y!3f9pm㰜ѫ#5]As|r I)ٺaB;5ߊ^2œf\)~Q8I3g:Jּ)+ּf2!b]v"!q{IRV( + 6gNý6 <ׯIjmdM+&koK =aފtv}S90U0(0]pc̫fgKlwHO +ܚn|б~YK9YjDKf +r8A0~5Sb@, U0I*I 3|fMi)O5`a-t恻qGwz Df^i<S*Z흗Ygt$̎iutt;l@2+V?Ȕπi0r.@WKf6D72]٤ٰ }5 2^pv̧nȬS~'Fg{ؓtEۗm}"!Lv b‘6 }uU3ekFY0Sm򑕒X{-< kkgE@_.Zvg sgҡ +[>҈-$.uӱ>)L^]jLg$uj.XKX CYck5]]40qŘhCs<*ٺ瓴E_RTIrF9MRy (D'*r4}=T0YqMZ0֓\ω¾#gmp|Gi&wm,f=R7Uc=I4?͚oZ-{ZKZ))p1l6EmL_Qoж&E]fϪ^V!7}[obr< BwִJ5{%pAB߳>4_ +7rh0. w%O~ t=|擅_'o=WGN,欈 ,k5q;mQ"ux4:^>Zu m.|8> D` }G[?}_ C1h'îHΏX{ ?$Y?q8N/:'] hUT Vߥabc?l ;I>qΩE@&~Ϫ%^j :uƛv]N}!(jxT=]HKtaH[~Lo8ska}cŢM $V=J#xʙz~VuK38?0x _^m r}_!sQ[)r>0 +xetuçxrrT9]QE%SnDJΚUަ +ޛqhJ́S&d$7\/u~U+`Ҙ|&-mUg[e{z¤ʩ8{^H[`)(Xmd% .tu23l$<8u*i $Lrw8ʛ6LVIea$:gz*T+*jԦ{-8Iۿf IKk3>(X4I#m*.vp ?m0iy|M snG8/=o/ gU*LJ1U kkӥO_jfQE% +q3{'?Žj,4 S7=oL$:aϾyE#ICw _WصA>knl荹hRT_dВ̳4[LBԂ(7f bQQU@*"XґB zK'Y4Ff.F@aθ*5K>h\`V]4/ߥfh'mPr LޖcYL ޒKJAQcXQ_苪 kVuI(TΡk 0*{ PG3y=_MSyΒb{ؚ̡Y] +S@285dZ KrO8Ȅ;wQX{jSqgg! \{<#\WrPj 1թeW_Eq+cںȾ)gQhTxAkxBN|g_iRISPt2mPdykM<]Upn9{\&&FPyJoE5ґLS)2+rimw_St%^}޳|$o&y5#rZӎp#AIs!WTj + +9 1ӾmBnj͛g>> 77HxZ-/A&v Tll} k{f%hxXz_Gǫ @|]4I'boMQoR=ܡ.H!c\+,(\3d7w뚟 8ϓfF} Kv)|Sym AgR="6uStؗd4W].VRLG@gؒb%ʗoqH$KS2tXz0C샟-e3cnZʟH(a~-V@]6TrW鳞8ճXS;`MQ4\;@P_eHBYTgn\Rkg|W^Pow'P-"t3'ai+/ߙ\)[l^-0`,(T-f՞-xv@:qv@6pw@1Fd+ZjLrogP~wB[w$,=z ?aEX%A:ZAp]Ddּ Vj,4X셧he{r1gLS٭nJe=^ }j pw5-fWS.(yTE_K~{a3xW}o7-Cs҂lI, aj8g{ +5 /tQu`^}g:׆zm̚&/[&jg ;Hs~o~B={Lz_=EWSEZ֗m%׶j@rn1_O-Wj"Ҷ׼LB ]8 +-0j1/k~B{ .;.v4/Фkycjө:~c+z?Hc2$_v a?F,i".+nF&qԜ!,!"f/:Sj笎zOG K AaVoL$HrXBeK%S6LN^S"AP JDO. +S`9L[|ps;Ք'p{+ϦW%e/OzxQȚ#8Z#8.﵏`/(,Rj Ș:M8֣ZRVTX +lK(,uɑCaњhj}@?.9⮡n ++C`WFaOZ]5:|ϩ耵%P1.G_Nm?p2o+3+`WkEꫯJ}]}]"̧{j+}^}E R_--+ilz{I\Rg+8g/kqje=%Gݼ6, Cg*֐ßٜںˁzٿlWK]gހ }2' E$=~tu샍jlMo>W}m,M˺5r/y|Ẍbľx|fbc-Tā0;`*EC6od,FƦN@Ck0pdlY?ԦUujՀe[mX}+H}ۙ'Z;nWn}kcԱ7[MÈf [l!?NV3~ZP|X{j^#ŸCѪ3.uHFBe 5אA! ׻J;_ clxw܆bP?gN^8n\Cbq1^o `c,:Ŵv$Y[qzlL!,F!mL"alLb٘-M +M QEs3*@ib8 m@&&aޘȏq=񺘸&u1"_dݴPL> +s+ŏK첯A(tp.(i߻m|7ȽocGG戁l`U܇o*W2xA1gOwfΝ)"`S&!g\P@.B 85 HU&C1И֔KxB}sWQVvR_>2IrƱ'+h,$l(6n\T$<*jPE| >k3>2Yd7FogkFkUSP.nهmñc]2̀z5 .g3!C{=7u©F!qUQ^&M\ˢ=t0$v6Xe +n$ORWCF{0B)ܩ8ԩU6c:%=/hkU A5bjʝ3* I3)oN}ƍ`VYq{5S\1R/yR+^-Z~pnL<(%[>m*,qf%bhk)=3CmI`oK̶c#=y>.~p=|nsTv .X]cI eFiwEB̵ va%Hm{(6l&.`%4ZJBy#ss&]절Qt94&=[ ]A,6\1?ՒdGn}&gP iYjDh:|U  3l,Vb"LN{u׸KH5CȄj~J; mwRvmSǫ6Ygi_&˚3ic͊??.Xu-Bj8pQ`E~Z~\) R?(8 oz2W U WD^.aJqG) +Fc-+јoy]`01d4moku7 ץTY-VR]$X?^n?Ώ0MG?.5֏tc-#6p~gxHϾ$@tn|cRhՆb +mi!~U<>ImFD 8wOE\QByί 0/f#44rT7F SOG]8mnK.rZ4#bG/yZAnj.GS4?أJW˧Q2==4aݧ6]QJˠg =vXLi'KB%YEۡ] $ۭAr(@+n0O$[&ŸI& tS|Z,!(EIgQکr[%E߫4fUjDY F¥OF9" G}.o8K"Y Ya\\T :ԩ;apE,ipy_C[ ܸ^`#r?+&zƚK"H@NZ-)dfmz"+5uI]k90T}q ] |ːQ49~ W2}jH +oQMe^#D㵊 +4E) d]bNfBK"d=4Pڞ8Dz,K{N)IAV{U$JdWmŨVR*6^2Ս%[opi0÷:Ue:鈺%_`X@ùS7qsS^KZY1^/1ᅱϏ5 փeh_z0L4@BHk)$#󕐑1e#D Ya-æ +#xٛfy).jޜ3EyBXk8%4nSLWR& 355|Md&X:`.F,^k=Vː %Ϊ/ kʭy +J[5Y;Mq>Z=6p[/hrVN1|TZ+Z:>fn]aZ:luj>_azw|z/5_ʞQ"m#׃ c2)z0=hœcˆvssb۱Z&赾K>^%EӎR9~kQzZ2Ó){)ӗwS$P2ƹ<:v\<ΌZ٠7Sl0<1j3;KconZπ} ɶL+o6K(7:dW/iDFJ\NS<-Bă3zc}-x8Z6OH2qC SfrbIN،@.$@Q1v,=}*qhZSL2Ds3թ4kϗ`-CYR}(Wû6e1j E,Xr;}q( h )TPõNk` -O?AoxrYmmԈ ^_h%[UF` QA"C.;RU +1LӕVy*)q<]EA1k I>Wӕ,Հ6b# * +ę +K{U˪߁8~u-H/?9)pjGЭ6`ݾOB(C ;\5ŏ&Rz-sM)j 724Lap&B3r1:0S\4ZT#v@uN3I,^Qt* ؃$Z _R(-kbV9[JAtMf׼J(O}a+U㬪+@ C=z qtKyDgKT/)xCEDt74|-[aQuJڅ͈_0ptU]̎:EȊx8|IizbR̍'k`;CCv + ?uK_}xT3Z zU3bȓ4wq-w~b(0R>*>@b /Օ2twӺ' M'3wҍj՗X 9cmN+\]mxՆ.y˜wn=O/DuqюE 2#G{)X9ZܩVdYd_E r >XHU$:s;`/IdeZS@lgDuNNDHM1UՎl+XeSIj^~|D5hA lrKRIamS+SzM ܚ--vT][J^44cq]t\iZZd,=e&]\>au5$*K7p+lWXc|~ qnB0OUy!:;B;8W!OnM+滘0Igx3絢nH+^zg%s{GHbm('=)WT%ߖos\O9:@?qz%\??\DO$?+Cq+Z]QwWgT؎Yւ_mI,M7+%1ClwJ:#@`l` +ʳ~I89߿Ld32 `nQIH:BmcEN}(`-wțe >k;rt0K׵#$0~8$M?N]Yt-y cOIϧd83 } {rߣ-ȆeMKI7سxW*ǎ$:+6< .$zd +Z7~zl횘U3K-O,N+!pY?,eo(ҙ8JSp@rwtgt:i*Gl&9 +{2Lz\^gnIiVN fmq!q}Xa;Oհ=fA!9Ii}y7Pj1ic'0cB)'ypCBJJI7a=г".Cc>C3L_g +mѥYJe*'0;˚'e(xN"e5H,@˾ 9$CwdhͱNYp2 pv_/ܿvΦ_^^=eu;`_4Oί펝_W~p s->%_ jί_Ut]P57T/~][wr\N.jx' Ó\k+t%x=@:ogMD'ޤ/WMrF#׊}Q&>($u] X>ؔ {ڔ6^D݌)ڽKG`DXfKDfK,P'|Wĺu͡6љ/&)7 +LՒe ¶+z2ɠixv]Lpξݜ z8w X1~+$7"#0{qqL.=Ng.pE e lwteiJ-pk#p'{>fwrBqsu3}SGxkuBe.4. ϽDt +ZPZ\dko85H_Z PLkE#ϊ1Dl1<>x,$|~˾\yEv.=os}xKON#]U" fM;w;>o<mڀHhѝ ޫ5}^x{ /[孺 szYA/+Q#騘ׇo8> MmRu!'ц{Dq.M6( +{z˨wD#1$(hQ5XWR%+q& +%bZY!?V#;sr݀H;VrFa[ 9wmq㧜&փ+;Ǡ#5 +;8iZ߻7Nt -Jǹb/]rCDHiv*1p^c(ߔ~brXtߑi<᰼._ᄀ%>p ѵ$g,ݯHȤRYSEgYr%m+i5*ZᢂcৢrumԳQ<Y;]jٌ-?5\9{#гC1^/E5<=VLFBrد5JEkb\E-)Ѵ`P#EB0"8BWUhux Ems9o_ #VTXT`;F,, xlTf|76!cf8yd|{$$_\5cF2ķy&vvz'={Swٱyw`qw<e*:$a&r%a\zM,3'ϔw f*!Ќ xhkD1p)s*k7p)OV:s|+, _֬BzNDfZ "Z nF!al75N #)+3ԑfvXvʀ$F7Ok"|Ѷ'uJ360lAJ]_V"6#lpwA0 LQR=\G;e_EP@zIux`r939(_ޭIckE+;U)1(jg\QsZ`ZҚ=<p#;?cQ@F +0͌ :2v.ޠZybH 'xWT6-Z]| `ZñWRNmMrXƱb|pꬌ<;#~#spqǠ_N=svGfvwA{xB}+\-a9Xbx@RqH?ĥ&0'D$SjS|@}:SDTk"HqhI@PLNEjY;b4O>I#űa>w +=ZFn3円qƮp86ؔwXѝ x=g>+Bp4p#>:bz70.*i#zP["#J^_49O;pi513 o~5b89}V,E649\o3m聎:x,#ce m:+7x Ou +MÆ < %%,pNH_?B7F\OZ1IG#6G1`Iw,=\ A fFn\^ƠHy ܀!¨p2}#@p1ccMƈƙ.!$5NƲ0E`i`ĕe{qe^p~z|25.($-7_"gSH +&,'IҲXAX | ?<Ş Js]v 7 bERnEa׬YUʪ eZxjbwbI)©V4SkRzuG@(Bp(AP,FVO.=I\ +(obgb&v]/"xfR\KB G≖r$Vːi׏X5)?bL׎X}/YfJ"?VKˇ녧X>WI2ʇ@ht $9(7纊\QsQmvqpΒZ2R\!]F/}MlÀ_1̔b$mDnmˉi;C#mʆa&60(: mMI67""|ÅAdY8l&Aam|M-!:am;5j; q63Có76v6q9D46~5hr6\.F#NqOFYz 5r 8A L^`͇i|6c_xi#^1r0mlôdi,=Tχj3FI:L?M0+L^. mX8DQEZ0IѶCoRP;HlCɾP${C$, Y6 *V63$@dňd\Yg`tr;,ə˶3eR=,Y$]Ar6{X6KxtLl>tjfuh..b]6ÍrX`ѨֻLŃeaVLaوYGA/<<ۜ[зmؼqox:'!,O-}2ls/JшsJ˲ci:T)V݂Z£ۃfY-m {o/ym/aef _0qs&ÑYL5Sҵ6ϞUSw&ԌWQ]pPpe8w!sInP&p7ޞ֑G~5 .x߬KVW@/w$ 6ܒbL>$ ܐ4c p. 8x%n% 'ݷD~usw}dXMj$DD?` M GLn?nYIYW<]2W/de$Õap¯x_6q/UhsWN]kݿ!12i-$gmԊa8Cby]-NZ0ɏj̔&3(zfH8LsܲjI 򻞗c;'}=#LeHHTj;x+.4 Kıv6y}Qyfz &^z?#Vvva0O-1I k~fonrb:Ƚ,{wVƳ&S1#W<^C)XT`,5PYcKio>hOy/MIT +I۽wc,S䍐/nj5vv3ί3b{ /ӍDYCdKoRiq2B)7^p3z 6Xр3Gc#Ǖ>X0SrvZK˪X<ݘwJyR|3ن~򱷇cO͜x{=;ts,ξ]v ^~ܚĊM! /7_9ݷ8p_װ4 ~yb [.;,"7U%Pi1$Sdnhyɩėݠwh1-wըPoK/osC[a tEpi; G ه0L&ȇ0dM~!}|z>MsȫJ8BSàNzm(qeB &#liL|!&nlGߤ6 Yu=nxsDUg51LX1ssQ'Wmɬu1'b\IP׈pܓܠ^_T! :lSQnϲ}aH}>񔂥pqRo5 mO:ƫ:ȬèU&)#߂ |;'ʤ -D,k}QU^Dyf.1uYy.Gs[1C[giZ9݆# PW=0ȇ#"+@❳(z9ktϔ$9C3NY{"=#1+91rfaPObB^vc^Ck* Sy; uQ>MSgƭWȼEwI߶|=>n.ݛeRGp~-3#*#CI09UHn֗P$^5v<p (AO̡:wiMD}%zCCEkcSW \c'*}rmؚ6wz 5?^]&5_+,EiM8.",DtܵyZ`ϢLdzr &SiI)R._Uh];cINeoW/S5<4{k=J$hRrQdƱ~HG/E8"ʲZb^6Ls\.7tqr +J"1R9=h;'N6ndLg}WNAwN(~`Wd' kѶ ?isϦM$xMfد+cax s+̢Zq:]/x.5 )=]EY/-S|g /9՝ɞ^0tOG=U{y.'/2sͶ.?Ae@'o"\ ↻E"H{I>! ^] *!*@E@{>Jn}f7*.O48ЍѴG-* ~,% \tun5!^ HG*w?k#Qu=To3m#3|gks3tF+$IYVe"ҩ9&{czS"@'MnQT 4\ݥU +F_1]e ~B'Yhe DT5^%,8QTXzI + لZGc{Ch o$l(̾Py3{{L6ZeČ?Ȋ*]*=ZiVw̫d'ME79 }{q]i 1&/kJŘE@VȢ)Cکg*"XGՂŔB+{AZoIxaBD)Y[Ct&m_d +ܯyfRg`Z#Z"*FT2Lΰ`)&vUE,% Y_xѡJ)~E Ŷ5o7nEiYt0|C@`R_PHQ/}!ҡ͇T-\,{ ӵ_$xDOD`/Pk ʣ\;LvfEJb|ճ9-u!%gMu6In VU]TIܿZkٮiלEMi+㺚;FWLuun 4#()}/ll#z&o[`S]1$<'3=I q<R1V"@S>>vV}6'J-MVjQxAO R*2B@Y^ٜP0Vw&{ܚ0]( 44U8-Ф4"Fh1p.pũ i;w.~)z5OW^ڲ"ߡAĝ:"u19bg ɏ3O#{y2VW[C焼˹S:j-(3W*B{Jk5B(' #|d1>G%ÄR fX +ءQwU <ꕬ_&Gw]̼UBb /@fsIm^*;Vr(:P:r,6Z[BMGDM8.xJ3me7T26񪢭B/.ӒfYrAz! )%+y߄[s73Mv33$s-lGԫl3p\oȶ;Z%Jq5^CEfˏ 0c +y@M;ZðkN pEځdH~KH_5nP\Sh'vujWmQR=Pnِ%Xw4u{>rαjmj'e;Nvl˕ET;B&Rom:[zaݒsX'l׿Zv[)#O޴6<xƈ+7IxZ<5gKo|l"^R‹p蚭dLϟp+P*:͆#s+U]Ѥo BhX 56+Tl="4r+Əa2-Ikdi0{)SEj_F֨vFyRQ9qGzh%ҳK4n265Ka-K4-XBSk@\ (Pg(@W@~NV`V'`+O':5˓?;D񪓋{$Y:Kdur:y8 pOO_m}]w&M=ڤdmvE-2%[!5ɧEI FH5|' ! +4LlP ى>LtT$*m]|JB]:^-^Y\st}4{; >Ccis6kAϭ][ͩn~O_R7JQ&63)cyߎMĖȥ`&g2y~aa^[B%"UC +nEi8DKLm;Q?74!FSB ^t$ 66N4+BwP!T +٤B>RK]<8pd#ңaCB?Є,&,n"u7un,qRY ə3H}4QՍGnWQU>Ԙ  ~/ZF?ɐIm9³ /9rq{sDQ 8]Zۻ%NqrSB>jx$_{;W,iv8ݐri^rN_?#I7P@% wՔd!EQ.N QoOJʕ5ԁT4htLf;VdHg`5B퓏lYu- lmv$+&_e/}I|E ;³(:q_/1-ý oX:-d-5rC[lܰ7u +U`&& PBΞQ7?8Lc"DVXκkp3n,nFpzr@ϙE}IZ)T#zjǛ_$[S}4=|FnO*HNn X/ez{e뛅OƎ='M@m۠G푝=Ch X{0x +L'?#o`:p/vs4xw؆һlG^X'#!6)i"%U_j}VI ]%TxtDvE'C}Moڲnq`R(fo֑M`tl*ї}FULq':m\5ʱ& lTn]0\k-=?o\)H]}'sw [Sۦ6;>{jK|uQIuQe$>Za&aD:Gwl m޿P+L4za'o<ĉ7yKqU) WBjr.57ZKs6p=Iұ_MgysAe?[B Q3Tۓd:mklj̔=tN+\Wq_Vx" _zI(GGs~VWe-ǐ+-(sObkCݤ- +8:i {u~LAKT|Ҷ5bVmgH07' VqMZi:ܥ) y;}YJ`5:WP8+3K"q(#ޢJAmE;j +W7zx~`: 6;)q C/y˺tK摍ne%n/\CxFJw.#!T)6&tDL^7Ԛn%Og +ƀ䞓n]ʠ9fio\ۖ@1aƾ<@+lZG]noLE_cn]&5V1y{m[kk!:2H=JFjϳi<}) +I\(FƮ8=sȩ?ӎפ ;9 S"Vҍ y!)4EEUTp[#* w"Ka)շ\uVBhк$/.aUH=9ǐjh ."C?!<2CPz!Y>#Q^[7H3P'T%٧9Zwt"Bh6. NkߟɍEy($ gj8v|"$.MXjk"-w5Rzj"0_I\u.}ɦ@Vom0}V$jm_/ՂXU>ROY;7ʽ6VwQx=Iyb?+b*I+:E]@@@2}ͫQM7(+pJWIHh惂4/.o,\Ɵ)/3|Vu4Kwr@sZ)AJŢ"LiItρb+f 7֐uc +]›zC.~*7~AfK3xOR+Ӽvnc8mWwCଡ Զ{#{ +*]eE54go.WWoF51s`?M|z[3tmkM6Emw`3+Fp仗T[^R*9GŅcdLW`NA[(V1ۣ" +P8H9No}hBh{T.Ȥ.1wQmin7ѕ栗кV:GpE*N5L084$]ɠgep>H}ZLhJJ<F撁#L XdhɮW?RXƁ_Ѕq /57fûzKaˏ#]}w{w܀|U25\g?{ANvutr΃A9Ot3h@18\\'W?~iuFQYpzaAN<[baZ% )3ѯMY.0坆Xr&iQk$Tg~ô5B2Uýڽ*[2Eo%L/ft8긫l.G%.w)C\&q_d$=V14?/VPlwr&<} 3HN63}T7 zP+̩i"JDGkZ*srHqNG}05U&*Oq+N\G:IX<뭷S`t^a}@?!0HxzX8hkdHPly_a/ãU kY'zrkx^x{yW_&^0T~^5hH?p`iĺHu8ux ۓn tXF=]"v5(1,k8a uWOVm ? +uv!qH6VO 7(kj!j JrLL3M~wkkEAVo*XG(,x=eTЧ(2{e`rTG"xtQ +M2ėRf(}꺈y2%8<[gmЀϷ<<2n zL,.)4~o2ӖʇAȇ1fj1A=PC#wK.l]_[)gw[hYC X0Ay2ErypO^jEݘ. -L=CᛍɩWEP]*IMN!zj40vrϪh⺐S]v#`L Fci,=LjjqV!uZ&!M4: 1;tuD;y!$Tqy(lLT?OF#U05jUX\lef)H]xGl <5>MZ.w,TBΡI %FNWnbtTx +S7>lWKe +*j dMOܬdXi/,Zoje +?uIdTVE}`"_⛜ڢZ { + +Uկ<"6ŨٟYAI|d0U:,|'ΗEg#SBIAER$#z.zpc'o~׻Pڌlk;)HdzJSꯔS7[?RLYGJn$')O)gJc}6$[nT%_ITvLkd1dYud%4V={TwWXFJ(6XN0;\ %"YOUEBh-e%K `#j=j?<֐u#=mޝn)M4guM@$]2Lv)v 5O[tc^=GN\)R0գ+Z!kXX= ʌRi^]*|Ӥ,~ yFôMi*+G+/ʽb +@D$؆%g&]G 1 FX݇l[@E8rԪm1 +sȆ 0^61[o.1ʲ$AŽ{]sۥd]'R51 @GprZW.@0lΨ?A}j\.VLm(x/gL 4EM$M +tX56c˴9pW~P|B򂩏.*VsGK>D^u{懽 + ^8p]%{y ,6\?[ ,$Fx&& jUuܕp.k͉]<w{^r.uyR mӸZ$'RCps)Cꡔ]];<]e{RNI]:.u"A]1'%G|XqZ2uǾuV^|G}~?5P}@6+0M`Iftݭ(kS1jgou `C!OdqIB7ʵLJn#tYd! 5=bb[`Vve,8#B #Îd#-(ڶ0 hdX1Wqejx]g8ț~kv 9Ed(ed:FϠ.2?;юy;.P,hKK@vp|9u_0ݣd,Xbyʱ~{<^ 釋5f"Ge<5"zqK&hS'ԩɌGÕ[k$qg^QAA5Ɗ=! Z*aM/[\M mG6򩷀-T KxnE7꼟z ʕfst2ȔcW;;'N Cr{ (^_GS!jtx R͸}L,C_'ץdSqYg=ެDzEoyB@tDP' OQFIE]YR(#wV] >!'A!Mq!*N AYLMz-ν.G0UHH7zz_)l)OVͬTq/4ޟ Ż.c784`x^eO}8TjD_Ծ8CyX7= iRTpAzhd,665uO昔#gƃ~xE]:*4y DKh: @? p)p=zJD NFp}kC C+>&z`c=xa7%((Q ak1I`CliitU{g 6ݜ sE9SdZqt +ß -] sK9fF/P_yF#O8u4I哿 V[dM`5 Ps) qT rWӦr&ڦNɜKhSXY߇StK;N?T/{|4/8]@JzI24s9=!'?6iH MB>|jSq4fvʐMo!o(I;F{** M@ \@ g8KSW}x8RíJ>V*cSe%/3$ѓlL '^m4lE&! = 9aw-J͌t>p휠>ʰ6g:Cq i9q7<  =i% rT'c;jfGjkڀCU5TUu]]%~).uo[r'o/RB` L2gBxMj}f.;nԲ~cŀ^PP$e*\Gnwᗳ'RN6;ix=S**-K+ Ъ95J$ط4N56\GJ-ݕ2hDG7-K\y+"M3{RspJ7N`U "Rg1U 7ex%<\F=^pM3Z?LMq̤M@)U/^2WmkEQLe\y3ɞԓN oZ5@N&LI1?^MQdB|gBwB^L0aL0@PLHxp3|gB ~&3!3!>Xzb5b+lo#5i >ƢU _V|~#}&Ҥo*Ĝ3h +Ng^hؚDKQNdJA>xh|Y-'?C+ee \'TbGBhȁFd. ȞQvwMEg*n*>:dYl6i͐hV'&+KMd#?/8fgs=Gmez`_B2`HF{Nq[h9i]_C}kQliq`6cIh:Gaf;(۔cY Hg{Ik~0 /&Fa},R>8)5fy`oX]ꆾ-;lAe58apwZ}ƁkF@G܆pp,eH8)NCȜgRnQOCL[-aF% >A-Fy*O-1x);xFs:F#D0XE6<9nGI`AG;CGy̚(e˛3֙Oa6oGk +fGhU0`> 9ʽufG +Gvz!! ,@ҍ:{B1KFQ1uPpQGfDc*R3q+i-z񘻯=X tiuK omE ²iu!q2f;A +H P>N`e +&?2=O%0s6|vYY<ʹ{3:l$9~r3KѶW]W-Lߨ\yCM4\;]LE4'F~#1>}9~"d3.I'I/KSj:4hd=/eEA +DfjMh,HE>l\F$.{nCw&oPek8x\2ވ P +3Sy&cvFӔ\U+ 3'Q12Ozkj;!qF=rb;5lL8 _Cd\ŀl,)[lo)x&+,Z37LE4 *U U$ 7,z0lwR7is2iT'}h[]% }`ul{:v3WPҎG#ʼ +՗}{MθTD]5]mJܪ07di9L! s̛U!* + :c`=c o>wx[*ߕz]-v:p+wv7'oVf +εgG:@T +'1]_%0x_qNcABT݌w gL8bVwTM +n o"l*/\{#6cW ~7V*'H Q +)lp{pU,Xu )~*/ׂ8drx\}&y,LA5fSϪvVHK;i~-Hg =wN;'@US)d:4t`@/Eʈ$)}w"h.)YĹ|Š߲h(&osbe-: 7#ŵg֕3m/y!sbjV: O6]({kpv>eO1ft~}m:$v ^e[]'3Zp*KmUs7Aj'vlv`.эg[rT9Q]vO%rhSzٚo`pu Yj;>r~+}~^FEDMfqsbcp$FZ b&q +{TVQe=^yF|o{ MIޡ]ǂߌ:!uBcc䲮og MuBSgױv~'FzԄ*f! _NuB33Okšc~׿`U.ϨOh2Ryyouh#kETaW񍔫Fڪ4Mun M{FCC!4#ףqb/iy{NMSN6B̄TOKzqUZYr^9)+Ha& k.azpt:>DTVʥ]cYVrX}7{b\e Fƭ(^mM$#SQ͔-2ush{fϓNy~rj#q+dWunCG5za/ aY))NA:^Um^H#و{bDZ̞tO1p&Wۣ0K2<Ʌ~ +)\R!_v1!_V9>\VoV{ . +ja21?o3(ܷ/XOD7Cd]pPnʣ!<*=N66܍g L8@QXx}N?\/pL:/ĔsAՂ1] ^|"gY\a@OkS7LCL+ ]-5+A,vj<` t sL[ ?-荘cWŝT^2@~_8Vf.BM%;xo r41RAt,齅?֖2B!Uyäuƭv%"\EwU?IpD :nc6rԢp^#i*Ȕ M{#s{R&xϸy>fGz֦ $Mj#\5JI=hy>dFozz-lƦIǃ F:tl )of\6t5VN@|#U8H?DE_$v|ٴ틉L ::䵃>tih@د4 +עcupyZWOI]I!4 +,ˌ$J ߄m]rL +Dc~VBS@fa!FBSguFV۪,dqhyxrtphUKҕ6GGK;XU J*uR˴JkDYt}!G{2_& l&<"7*͎'X!L;@ +bQ=#tfJA(\>j4Gb5\װ 56axRdO ]*aWE"ad%{ܘֱxQ&Uf:evPpv0 eEX їt}eFvWT}2Nkhh-6SUHE؈f:+x.In\P^F}Dsb, +#TqJL #y*Ȓ$a9E_%Y*T8 llKaø4 o`7Ԫs&bKշ;I#hV<'q-'s$d,:g*bяw 1j=J:uΘ`w/3ždGȺS5R/#X_bJd EV*QGx㿄6j$;I؃ \Ru5|!jI'`6'!FI_j/N=?^\C@#9UBf>tv=C#tfACR`A0HjP]lxZ7I?N(sQjq;gS  @45^H&sQ\wJ CDȨ7Sςݔ%sl} Prd.zy=Q^)!8sK*7}>C&Fafky%ֺ;A|xj&a.fgs U<4WA-24-eD._s&ܝ ;fFc#qd`tq }֖kɑv%YևgHbccR^Ҩ7ݪIƧ'OF˶aӣ"Aࠛл!h]jO,U@C bBpMDzr4.|bB}4G *i8WA:D0 9]{;dHx7 a? }=@vyM&E̵fS=ChpEmFo @rwϛPXki&eoHIqTlq2'|TcKb˘3 cn1hGX9(uz>)q"ӝ {=>v&2|45cfӡ/Ȝ 6-1[ơ{!NHmW͆ U2uG? p\q1phcp >{׀șsV?Ve]Su66sz@M +U ÖݓMϦY_%< 5q9TUKdmZT.MuZJm@}d&UqV+;e =r+ VcO.ݯ QTG: +ˮ*xZU:-(`!Uk89^hqmd9TxYB0NX*5h&$ @W{fUPg= #S:\gxmݒیf({q q;5 œmA @~Sվ{"x~îQ-d3(߽);|ǛHN%3<8Z|Ej1l^[lsS}gO {^2 Nz O1gȆPy-r[홳O4 glꏧ6 z# +Ax| +9q!'|w)|w%MI,G?KrֽA.X&n߰=_yFv1[\v+Fv4?н `]`|$dwʽ u{H97z Na [Z1d.cטWo7:pَb7c"b!U**n5n?^Ff.$sxh +#%8߭{FXXo$L~_%ԉkTyᡷ}ɭMz|hԷ2No?b Qdn3(CSSt0agXI!M%%}:|_vk [czDBq&!qd@Ac[:ba($ /+ +[T0[\g2 >m_-/OAi1- K 4iglC3f(DoI@c=U +eM2 4EZq 9P!M2־%C?X$!':^4c\Hmi,{ =~W*!S{M #_0 Ric؟LUK's)F[1rx]IH~.sd n\ހb=rݏ<`U/9/,C"aYc-X[pRtn@oJd`Q?MT:#KSNiЁM{λ. 2kooͯHW~T=2əXU;W׺igT)9/TН7cIFGץhm^rZuɤ,JVW{CJ@YEi }@ jFկLuf]CFW71(sn4wjUk +LzxhI!Es[6/QO6X3NCOP,%bltr9e23[}HpK w N9H1SYUWYmڦ2La ng_L WMVpV:~wYjɇu&)M%4jN@BSO뼪ZH=t%:$غ"4t J0Q1 5A)/,%/a?}r`Ɨ+K"34 4 +ʹC{lkYmc\sHi& r/OK",J_/MieCi@I4:/L`{nLΡ(<]RPiB1ҳ'MN(( Ɯ,>:Bؤø*+J5eF%XYg72ε& +,*? cX08$g4'\~5z#KEY;\F6gX~_IQ.^7|X{{yCh$nJ^H6%ϔ Ʈ6J; 76' q eWW?>NsPluF-+DFim`51/@pe[02Sb$T3.NtT2Nk/ +2 |?;4U2eCjBǑ[|.* NvõbD. +ƩI& ,ˑ>-q陜=VɌUf(V3mZ2cR PGj9>dFUEgY#b?X,B4 a PxF ݉ڰL#"um{:tD_J"=W[HixJ-*qy+rL-B!7_Q<`4O{KJeӢ?<,}][9H,fB[_ӲKw=)eqdMKʈ׾'w8 uRZ7@̛hbC5]YTti{8ix +c 5l3VQExmU|++ZSIJŷ^IĽyrZ㇢k+,=/Eaș&ao桛DgS?\;\J9n(:”4\FQl=oAzdWIn$7+ /0$J;,2[D.e(Is>5E0yPMQESnjidŪl;{e:\9K{e Lƣq j +KĠIrEE큒I*pR +Mͼӷ$Gh.34aTNerERXN(NT8'"sw|\GB?ˑ%z[4Ou>7mѪp1E 2|dwX] +9$wR֖t +6sсP4 g +}YjܚΤTE=Tӣ,cX f"/4<*Y+Dݙ~pSwYkTzqR>g#_Սwj:s=dpZ9{鱨A84KZzkK)|~B=Yݩ~>JݹL6ҔЉu +("NCWR_nrYgG*Wlw⬩MJh*+p#J_$C +-o%ϥxaDM,@,p1  +RUaA):ʅ滮뚁SD"cB MvrLLT_3t:S{z0[kt*#n)zx{ejUѕ8֦6[ylS6+(׷-*"zFmk_5w;Hx%"ť1mL-PX+R([sgү ǻTYuĉ#$W uXz=*Mtrlռ5cC;xӫ[S;–⠢]-Nw1Xљf~/AKTc{V|ّs Rk%w5o⽟sX Į݄.a7;֜?y5&=5n+r49L.F/Kmeit+1ӣI~gpv8Q ñ3%Px: +_* lŞvB @Z̳wmWB &#0kw* F|׽r+:KMÞ_BXehx8ˎ-P +pNj03E3F崔x8 +~!>p8}jt)VRU7Um0ki55M_{$^6I-mޖå$,IشָRFfW<87nɽy2BcEM!z7VAmlzsMf  i(]LDݛ8Rz`m#ݻ5uʺ~׹B7}$ $=XX.$+A>W'_9\C'ߴGS%Bg b-!bZLƳ,5g͎~>`&Z-D8S L~amTth29g Q\ʎS/͒pv=i~nOˊcql)rE +g3R `vG  /롢HYM5\ W @py2wE982F!]07͠XqEJ=l8MK㑤/٨lIyCy7,w`0sJqtOM@zN]FZ~TY%t^HNDPT&n!ZVV;]{hu`GT"6!3^y{VFAlB_ ukBu;$,6ܴ5i'}lÊz'E7^٩B}4GNhVKKGMܢcBIgsGŘ#֬7]T yhp& 9Vt2"y jz䲚(X(Z[M׏в,mKbYTكܪ4#yjkGI=teyhx9?3ytљBzPkm^"V7Xr | OvhKE%jަI n/*>٨ nwe4&] ܪ{-#ߝnw[Xf`LZ9"Ov~T&sl+> *j%7GsyO|c5<1VL|F3^K f}K_xE?#/JQ/xcEu"Ѹ"a74EͥMI@KB}vZwIzH"2}Y.'ZrqN !Qc zZ7*OJDRkk<fr>hVE1] fc^HTc]$r KN3@Id-&~YOx_N^ɽn1M +֧/dOH#ԅeNJc@rPE;;N] +_ݛ"])s98 |H3s +~g ZćSa'伈 X,Yϵl]'Ggg&Hlɐ`'pw6qr1 ` ȞnY(2f#ʣf00K44ܤ<pw'VF[XD½Y2Asa ``zC4d6oaMk6~4^@?sx=m[I@-xE7leZ(>\`";sh;71bC4lDY'.?nj SA]h[:Ozu i4eGQ;ψ_:RzB`2jfiBB-$pC:7C:7={%pdp,YˉtEz[!`SR̴flodrڭmՋl&4NJ4 18=b(辠DÅj"[盽g>`jʲ<dHcG:6=aGhMh7{*Feʠg+eQ;ݮQKmĭc,h>  $o^E}qDUf kwOT6Z4/B$BYN1B,%bS`U:PuH')B.*fzWSJu蠆lF9Ǵ}c^nTAh]͡[KKY6i}\KA{,;Ѧc4>wqZ5U_.>D.궪{ϳ +FpoQb,qK٢X-mho^$s&+]%+G ]>}|ɢy;Ć4xf_+MVHiׇr&_Pmq{?c̓HLJ%n18njf1uhPoa69a[NJR)~^gV([82ɑEybFvUlY 91w,6}Ljv +$Sd\y-9iITlq#tW; ~ڑ~[Tx4iSlEϠxve xmr^ig`ИϞiG.n +eGE#fg%@۴ >qR!T^SZU[Z6-]769 'o"$7]N*-MPd!cfz,x{ZU K(S)X;g+Ⳏd}&t&:tϪzCE?b~==Y3mUR1x׎ZՑ5Q7_@lm1= +ѷJ2ێ( ?[ +ER ,f:NSY49@e0ԒjS:Yv "j#kN}1UOcNX)Ǹq57rRĆE ґKp!29R ] ͩv\#i5; ==mm1l旼#x&bǢ ~o'6ǧCjm?;|!Y4ڊMѲ'Kqʊgg3bg7svlc)D6PQ4u彶]pdm.a-'ZQ9;2X' +FA*G^TF Tù4W, #?O^T2c3qp':M¢ZS9d=gb9 )"*U3* mfK&Ƭ ,'ΥE1לxP:["A=. ?L&|xk[`bmz nV_̓Ou9!edϨ7q.zh!mE*L{9-r@<ǺYnEQfWlI%|ee P~MDorܿ_ѹ?QNXx*Ǯ#O ؏rIXOݒ"$ѹڼ'͆q5،dEA6"-Li-XM͠paيM -.B;'0底72j0,Lظ5V**Ւ 6H/Liխ/ QQԫ*d.0[c!ǹUY5UjHz4ГTZ]_ɿx}Pnc.7F<0$kB+ng+_[4Ӝ߰g*d~],\b7ѫ }#9L0u*lτL񹒵|.nЀ*a3~ރuix:|gYsEU laj+s3eFЂ>|MK|N?n0͇Ѐɜ!o4>_B@;Chܞ븄1&- Mڀ&4N[D>g>7OgXs~F&3Q y&ʹ`lyجZ"pANI+2llzȐ9Oh͟06׿j8obv/}i>ޜ} >"9m( 0)`~mj1黢Tx%b %7C?Dl~(mC?CD?~Џ' +%yh~|N;ﮗ~xGǮKKl˱]IЯMC?ˡ[anr٨C?hA%|GzA?59(x[9/\T~.+աg0^ *^qTΚn.|0[:GG_w ?`lFWG)@}7QSwb~)*F/l#Bk X45l0Koڶ%QGVj{b}iv:8fn駋XJeYUCm$$ W3%bYM3_ڛOp̽YK~/oNJu|Ipj>A #*| ĘpZV>.J +#.L?xXؒU4Pm\c-KW8ѮێX8_80RNon4 |r=i>^45d4rk۱Vǒ6APJ-{#g¤Z寒&@Uӻ=6pA#6~qH& $?'Gq=@>Л 5gVNMak{cG UVcҤS +AGiH#>[]5Qr:I㙉TTZfL$уV}~Mqdyљ4Yl:A?1 ךщUpjzN+CUtuʥwm<+SOvih(0aF f(֖>궣i#΍5@}VeGe&* Ӏ;d/4{W9ZGP|[SՀhkE~\zNz ?ZF5Bm> > R|B"f;*l {B8ծ}Ky+$2 Tp%5lGC]7 "&fj%Kc% Jjm$|<"ZBשgth"tc`7v(% =Pt=]8iz4}jE>keCSR>@ 7-j~n@(I{T 5(9xY˲n6_^/u҅<[W=M&_C[NAZ +V9#3p~꫅dң;ûVwhl񊤜B p(P+)?RIPl"=_+suͯTDXm%Ғ?饬C)ENf.H1$8uPR_Rc8,uƤ5o:&{C1KKM.cF Zn71{k_X**[L49 i)RyBY2[Đt5W4Ɯ^Li\vErr9 pTQv4ve7/MӘ*:b:﷟0\HNi q( +;}Oـ.L*¤Ҩ2 cFԈ&Uƌ&C1FP#7.5?z3k"_J#E|H&y9 Hn?ՙDjn?5GV%jsFT_]ݒV"M~_+VnV=CS[zhCBMؤ zM71u鲊ϭ+kW?j1uZG:_tY=߫Ɠ[o욯IW IPT'A6 +Ry +NMžU&ي>G3!Z( IP설B'$i鼐H[#:Ox5ksiJ|A&> +#_> z*?F01nu`p9RFb+ ?M^sM Y="J$U#t&7*y*X>f-6!ਫ਼)bYIDa*8Qj-[a Tڅj3+MEH6GpU df:iq1e$BMuA[#j:G jރ3/ ֔V +Zf`!Fc[e +V>rnp+N(pjɭ)[]~ W*.^-j]Z)k[f[[Fm$Bu%qa+S=T23)jh,zse<1-j(/vCBQG6o>LvȘoUX˓bnsUPL-bsP ?\ +^cxRCK/J`L11&l0& 1&'1:0j +4qk-5Mn;t.wzDpuo'c&6Gh +{Tp^k+ȕzѤh)^'{#(oۚ_zW] tf^U׊+~NKb;{o9>gfd373ڊKib^o;#}* _;a, +}ϩ-GwX}" Tò-1g(d-Io*,"y1pq-#Ϊ~EhL NV(^  `l+*)& +- ĮFٸ,m~~G?L곬/ڳTDXzf=9u_ƪ zEý??q:V\rIG.z@K/5:%&NָwEh Xy%c\RGD}?cz`%I^Rg^.gΓ HG-q,ӱM;%(8q,WӱZ=5Xd?D7dwt6{hJu3fΓe 4"iY.J˒XZe2iYaײ0?W7yGc׳ {< 35g4ϒY~i]y8fzE?}ͣZz`*P;鬢Ѻ7uSG%k/%ͩ\&tfk}͡FlDC%DAH}QORQ 1k]N]߲}69ehM&SvƔp3.էJs))(̶ +s0li.5 ߃$dwMvtW{bVs|˹ 3{+gё߾~bg_Xbp~̄ʱ̎LSL`?d + }glGɷ֠ d + +vfdLx;<_D$W~J" ~Y.D5@YxɛħepW6ndҪ,Z)#e~q. b\hMu>Sh\7+;Z,3йDz ܷPUsd\K5- zzUW_Ìtu=n)8&] ٥{uvů +9Ƣِ]̜=-2zUSWSsl$?R+tH#;Qee[ݢ@Dk;M-Nq):Ti ZR{iM}?Dkul4Ѷsd#G'gcl`> S.6 ə8Oe@-NYs)pw!_[5HޢZCQ jiM}H Va{avf\l0:<#fY[C 4@Nqٜ\{ k,S-ډ[")e]m+cU9 p1vy + +C&iWLZ9OEˊwlR`13fgU" uwlcL1nbΈ܅ ĜuV}[mi!U! bVm<ƲOO600 +i)Nsq=c2o lg n߹-DmMZHg6RLByRR%_~9"A*/$lȅHxo+EÔeqJtz&&&󘪦Hogc m>y/N'rd"l!K +D5EQ`kBèԯdMZv;H2q\h#I O߬j ۊP{y~7 +JW6w?3eߵ^$%`TIWV}$ݫ6ǥ\ZhpI4R$,px &%$ eL/}#C̎>h`M1&Uc8NM(@3jNvžC]ѾInd\#DcT8=+ :tX3N@th:Rϒ=0,XduC{@C~!;Ǹh .ZL|ڱtT>:{#zyTy/jLbr_6ʥNT+t:b?*NQ[rfV[ 7߶v?S0!K@ñ($)qL~e L")]>4G\l-AΈ`CA/!1k@%4vh?јa֔)Y,wE=!r>I"'nbP5HA|*7-P˺dgGՄ?ɝK >ճ]5_8;lKjuG^x`Z +&#HeFe8kE{Wbl)֍o[ũ+,RQtQq^FqY~UXP|1ҦcB)/UI3g:A&˹ {YM59wTQi͟gl>t1w_֊R_b!v{zwВg391C)SϊgQдS ,tWGF-^oKW$ZX4:TBbfu@BG'yj!:[eJr,;nNQP!23x% ¾Œ{]BÒm*3_$#Pu4[GWJŠ3$v{ab D(5- ]Юʖ(Y]k}wF2c̸3ny"yPxўyIQd +{v d~uoRĦ#υL(uc\#*3kw6HamLT)Bݥ1dV`3b|Zc瘉]iQ(τc5a1N'hO3t0btZ媒'UPQRTXdNve'5}n s%u&h"BB^!6m K>Z)5orCO*v naаa-|?d$0EYg"X?Ju7eN K:(X(?5zl,eODrN@<;ꗖ*վ49Umr OgbFo ="O"r}ZLrc[OP_qcSI~dRSܯ'=y?F6u%_-)yqvZOC)CH%R/cevRC[ڎT&^Ǐ;ڞ>}gu^)^q^^^x\%?m^KVߞ;-BA +r#lK20cQos667)r~Aꟽ`oyα#ט{_?yd0AsǓ'ё^ U^3__I8}hXgn! [9(/mFvDjm8FO{bk@lcmDZO5pQD$݋6:XJ3]e FSkQD{|ɍƪJBn,קq3c!aIXYlJRAYwGfƍJ`_c63&$-(, +eS veuT~kr* : +Q7[M7v +x`V e *+"Ȥ* ":4|',@Jx{?œzCxQbmxAWg<;{HzL{@a\_N`^G/ZG˹pcK}T]MfXLF'W=Ǹ0"fsz4bp mLջ:1L]{)9ٸ9{s]lp[;a |/ֺݓ1;LK(dDp⅃ sIU1]ޔHgԁ-EFOxHl_+b%AEq:3\kJ`hE"+D,BV_#`^+cY7:hkH&8,IbHb] SE.⺦>ǝO6kS|𴦮w#ڍk{%*ȀGa߅"9`43xlEFBrḑrEab"9.J@PM5.xv AM؊a-D0q0R'D..d;Xn; L'mQtNEI2俢. +Q1Ni%Qi9j]p/Xa0;53xS#UC8O}I6gEi8lX=Iꏸ"0n@.nrj +3d5+c]\&9B +7Luw"``,F_UXYŮrh6aYIunbO&veFw u3/|᣹?YeɈ>)(&i^%)%aP[\BW0Ƹkaȶ댪{2C w' ,kOJd(*#C7qD۪c=/d$|2p΃Wq2P^.$Q8xe0SlcQ7ngjˈa̅qΡ[.sSDRϝUm,r$-ݎ0Q?: ௠!}caT$trdNA&a +#k꾯C}\78ɩufg,\Б6UBZi̛¢pp߼>،ʼx1cfrS6-+_N4WRΤ&˽ۏl#T0s\j2RFG &N4n(q B"zԺ*}fT{\yxKaW_{kh(wq/\hdkw]V +LPRܜHeCZ+1|yp,ґ`5XOXLvR2ltOd +UY>E3G؈1z)ཪ:UI)y(z k3Vl Gy%?2iGJ-PT5J*7da%&.А2ch +2Q<;Rs*ԕ ,sLUgeGjcjW_d>7Brl*źk/e $⡬etlJ3-2"Y<)J"Ŋ0TZMi^*R)LЬ^? 4").YLv]EղDøݨh-{U1"[S:kAk#eNJ$y53>.MtlφMoEE?-kƝ~{_8 YX֝t 'BZTlLRSE! +kܡ!dN<dDnҙO/۶4MGC>RHt0z$ł=\2V^ Zny.1e1ǵKMIKG^s9y'z=NInxzPU^_¬U]/_KX*b,88=7p:3udSL +=MSdK}q{ɔ+|)G3GwcNi3T4,k^R h^b#.X%U|dRʫP)^H[[;P''@S'NϤ<\w5ui<_W$H̍<~ȅCK|)#&A{DŽee9BSL-w6K vSCBt3cB,ơgg3WjKI5_yZiQ$. g1b^0\\DrDT1huïX +!DNس3Q(XR 75x&UhUtM%ュ팻J"1TpԑJ%f/Tף.˝$ Tu,Ԁos]HHT&Q2:ZZɀYۀK1& XoЂ"JT|QF +K_kC˗[as $oqƙX7уx"%Lp-vhH;c>ECE)#걌٧jSLATμT@k(թ_#p  M{@[P[h(9F=PI9}YϕQUS8/Lc&|j*\VKlJ.c*O/4t_+"4Z4IdھQ#&wT`/:j>{-BP.9ikʚZ42u4Z*A>8Pƽ5HɕWUt, m"Mg<3◂EQ([yUo + 3 @ +aQ+ AOTΊR}1hL͌h'XT!Rm!Ԁs(mp%Jo{AHx쌗D 8KvY< i:)')ʉw>2\mޙHÊ:)+Ӥd|ԅZ=ӽmi쬸nL9)" ~1Cđkp1*SW! U?ʺd hҭ44@72K#U-J 1⹍\8U ǙcJGa{X h:T ^UmLt"JT&Sn&RZU$׎}B.vx=n}Yc@H 5KtDR\24"-qU0f5 6aHcr0jBM&LG<% 5۰%9$4)`ccg>OjT6p*/ e1Hi%(1ҏ#EeArXp4 +@JwoyʶfSH|e|=/%gWҦ^%)S*,C_m\Nn R`Jo%eVC囗TJCq@n$EWPZ!H: 孾b./Sm6Z Bګ%YL^vĦ:RMqF{Lo~s3~tqv6`oEN*LNS&{!Qjoϝ^69к9 FRU"}G\;3 B4 qTM ro: ش3aQElM!g_جzǺ7+بH6DL.iQOݾͩg;+z:{e?&e ]+jN!EWoRDwQ+:Q*tM50&9 +mlL47I_RUtZ1;oUtƧ_ok3lT jF)^z~IR"1[do]wpSmpPKݥD_&&J}vuYw+CZҝ]J@~"]M=">;$rEASC#pphexci1M9rkɪ0yKw}X]s[dm*m) .([~)YVmoKA>聅Ɲ;+#y-@[.uLR;EH~ 0/)Dn40T=I12^C-BIcY!z*E@i'[TD bk3^l3X-URSU%ApnYXӵ?eU*X綥`SZrBJ/y*9'UVtLea໷unՃ&殧hHi) ˧4J+'݃ה +'VVtVX\=Hu"o+[3TxU2˫|Wɫ)w/#|ЌQ̀h-Rhk HF;hM(1*>F8mJ ,3G Iƙ? uxT^.HqE8 &X'UotJ܁ouJxgP=]E <.2| kH% -oBQJ ʝ\J! 4SU>D-*PlkNrF@1v9H8O$v9 43w =XuN|r,ҧ~ _odocjA's}spogncx] Pp-tj9v!z'2g ["8 Ƅ hg Le%ʦ]EpM^:j؎Œ7q?]^ +Fz~8ٕէByiK׺uzVT$tY~ N1=1Ax!Nh@ԩD~x-FF`+\J.1C3תݣLj+)dוћ++c}|qj]x8kZY1+I'uG3=$6]JIGjuwI9 PgmSIyA)I]8k] Fp'e9) JZH#Y3% 7mf;@|3yP&iǝMS:olZ~uߋU,iK\I\PfF_W,(-)%Di +򬺍O/x>hDI=4{j]ƣRi>ee:Ăugv5IIy[U(I1䫎ݎ>Ł]7I>Fc2tMb6N6>Q7%}X3Nv9%!IӄY[|YW,ИB-o|*Uߠ`V{b(PDXS4Wl!?XUƩhcj +WP n+*k 0J6\{\P]q&mgTx Iq.Iͅzj3C.b`%CU;{*Xs#knݹE'Vz6B^6T!*pYmzJZWCXq8eߣk2jzL]N?\@9-C) 5- d2t16N^kۚ郈PmP:?G0!Ƈ-cy½ JC@<<E='@6yhF2yzryh0y?䉢 Mk }ѠMG Gҥ T1fŦ*IlT VPOR]S_" "9ffl>aUt$(UOO$4%f]oOn]mURI]3}TrCP_?^PU\M&'%_‡RY,4_Vr)ke+C&8g%#ڮ(ƄQ kQkx| JW\72P>sގyY 8O'% kw;\+uw4rӂIHFVV4qVRR~g+ vG0!!IT]493([of>,}?0$eo 3@Hu0J'IBGB͎0^uj +Wlҽ8bvܦ "鎨LU @Xe3R0YG14Ѳ=d2k0#d*2yjCZU:>Ywl +&C,]lI%5&v“\s>SZZ_a}tSGenhG˕{HjzxM|N סy>ک16cC'%>K̥")ML +ۍ|fD4+ԀkvA]Sd.u0u*9"yhO +5m*ߨ3mٮil\hIA ]S7d2-|u \6O)Pu'( >2Z-C f~JUOgtSpeWҜWΚWjZ{.q(ί,96,H9%rz̈vVkj;MmMQOV4i)YWkHmCJ[,Hw>[E**ݻ.4j-k53-Ng{>[̆3]lCgWL;;4TQgrdgtSyWpy泥|8M68@3-g<#lgk#i}={t2d2ċ,au5忲; nHØ fx~rPqIxk +:" 20=(Sֶ5VRF} +h͊fri"[y^i Pb=NjgR .~ŷF1ڗ>/r4\ di9tt(/KXfګjq8PgȚҹ Qb nܬl:ߔOZWbۆbN$4}Ze{ڤr9Q˨=&j6)1pTzۤCe8`%њ'h4w*@M=c7Frm51NIREeEbߢگ厺6ʛ[HjqRoڷe͒ozorAiG>{/:ެ `>!HٷdDX><:U&▞}‚Fσd 8S 5?g I6*K_ߺzWYNL>}l%=9ʆ];*h$l;鞢#>GCyd +c?̀쳅d:'|~Sސ˕Tmc3a1SRzg^hjfAe u]ˢG[g{|VفUOHKl[c\sAXhk(]Bs43TO!ШTNu;?2 2ݛ|mjĹȶB_=~2 EtSS ބK'y-G=}?/y#"U*"#Lby0Ez8D?@a6r9B傡ھ(l3j:;ָV 1۝ iydyU: \ (Q'0ɡIBPkqu h͆t*%(/,t\IJ5}]11ۉ:.k1$j_{ !-̩1ώHeͧ;W;;2ld͗p۱0pOܰ5 +'~'υ _A|q:NvLڞ8p$2E±":v&y6=n_c{=9d5 K[$51fT5&6o.\$-%(ˬIQxK6I>$%Cݑg߃c=74ejnlB 館'(gӼA\1;@zܡBu3R߭q# ?N@GդetR '>}Hg(P$%>S56F(-7CTa;}3r$qht<[jBZM:sTE%ިML۟n\q+aG:ZEZ0DÁFo>`&wti vJ J:8lP>Gձe*c?u%# M6'V\dW9v%7 } ljS4^OX +> -'NĈYZbo1i cOSƈ[_gt2쭍7˹o0@'ӚMJ,Znd:X}y tϟx׶zjEp߬'eNzT0VsV+7oT!gQ\ңS/qS>E\ᨦXd@[CYd$c(xx4K\Vә;D0f:e}a8;NYÖZ[ִ7ƇP`^Zշ.Tsh:&D/T25b[׵jRY 4j8#<j`[|l)BFVu6UMSlq".o]'> ++@ubċ?݄T{1oT~~CFY`aoxVmQHsflS+W6A& ճl#w}51r. r8U*ߜBU9/]$5IyIu˥5~s֐t*H|O"-8>KIs8ڶ0~_E=$Vp$olgsԺҢ\\ʓ*:7qWaiekz tc_& +R&v͟rQU<"{'Z`ڐ9 ;94m5|]+"qOvlzVݩ -W[i/&?煯 b)X~,ZPAU5jPVzIKC-`FL@|j=7 G28d4w2wͳJPPT HXOxcGTJD&%nGZLe0u"t 6 +R!I.w] cJ 1 ,zꦊR%p,g44c[ p}vg@uv2gcf1lNb_T&,RCO;U[jXMd|6]`GR^,$5Hp%z#50 nON "9 ׳4͹98x"lN4lf-jQ`G ^gDItzk"kz3: +>O*MV9<[O84IYؤ,! SK8|ՙ҄]jj7o5ygCq|Yw|uZ@r~ˍ)0ȂyH%61E/jd`(0*3WKar*943ʛJ×9 ={ٴޅF6Ю0xmJR/BUȭ͋=[ 2 L4"ڢ!C>{â^t2 v 2!^q@v ik-g;z %Pub@w +˥_w)qdJ%ܜ)x>aGd ȣ.ecRcna>氱n?g)˚ o;b{?RM8ݿJ ́-Q";!ivKD2t@3?Ҫ39_6zӱ}*s_b?1dZ,j=Y6g?+ր}wNml]Vv{ph4+K]7>" ~cGG,3yFMolԭQW" tj\p{{Vj{'AurE WOajTjYƒƖʯm+R<;ԂJXf%=/Xs_ ؘD=Q(RM5N0Iij۪'MNM\y'-etj1Z +mnʤl|YܝA:=v +V'snjԝP%cY"z?@oe@n`oμU#gpOմM+*%+ ?@qInT݀3r*g +5@%Y`]-ulPٌ86cx6C?\3t>cӓ#bwv]3r]$A Xle2p2s.-aFm2]FE_S;8M Y34|.In5\aS}O- +$uZ_g>GFQm4a4xYk^3]C`r\yO#Kc'<PLH< 7H;j 13eqw&ڙKG_Lu?d8^2?[qP 1iJI8r$|äH[eҒu5ڲA!֥:U;Wc[K^Š)sKuXEΕ=" uJoS@ū +AY*5EC@6jڋ(7_̝6/ٷ9.b!e*1v?D02ES)hkу޸ǖ6ՒI(1-^@ٻTdߢ Z&r)\-5ϗV4t>!o5TӡPu^ 4hXE@R2+ThR5{k`r͐ݧVS^:zDv"/^c*ےGKV0>s-~G}W$~k9C:o GpTZw^MBrE3n$'Q8Vd6L"y~^V{?zjݽ7 2iU/X[̧H]ov-'Qٮl9V6*z=Ys3k]e6JywKZϬ'}|$D,a1{AW0:25W{^ xyq:"$,P{G W'n2soTF|^.rF?æ :fV r]ͅC-p/ǁ<=SsIpLw_Ǒ7zŦV<[:E)o!U v"z~FbŎ2]:tat%G/]혳SrSݖX-rFQV6>Eaf7$4U=rNWĿڹaF%_ն~F5NU+T$>P 7DcFg=iWUsvūKt{kbPlದu"m8!ʸ(`-5;3@ދ- ,d/l1⑾X`ik^Kγv\>44Ջ>R="?̹a w +`J7 B2j\Y_WϘ & w0EҠ2OŦϮʿ)yN@sH,HP"?IP K [/_p$OC^$ +-IAeW%}. ufl#HKK,H< ~FKӘ 6= `6/NKbA\;w?]zɂ}I0K% ^L?;1Dj,-͓yLG=,sX0X0#|<_4xTqxhĨS)\6F3RЊEQ nd ,gv hH%\q/bXc@^.L=_eC~ZuZߺjD%]8T7|އ_F.6^qg8[V%?E\E醁.YTx[?PK t/c7 DXzt SRf``lQ+`܏[?pؘmwSUJ#&509uk¿}zMFޔw}>V,x_Tg]4f@ϑeHMHW_QBe,ז [Wj;g NmirA+^yjX\PI?SE$Ck5_)?}U,SԲ*04P@Z~N:_.xEso +}&'ᠣ^xkQ5C9_ 1=zt^^g3y*>bqիh#H.&Mkq$`:Dde֞|ʋ.ߦYXQӴx;C:dENBU$OGq(5r |n}'idj<*]-b ur5B3ג&DJ1kCJ9N +©ӄX>mgami-KK&|=䴔 \^|ކ{*e(BJU9\=fȨO0v#KĠ윎qɣ3w&Ѷw*kLpj;F^;߼)='{k?KU\;êyǘ7XiWTpa+|^(@ a*H40~8^@s(x8#;̽뺳$Yw>m`An_3r 7#(>[DokAY!-un)qJ+ikGF~ +3JYvAM,c.8+{kp\;Y +5)jR^15[ۢ +UFYQ^hJy: GtfBgG?CҜ9F4hAtsBLup9iii 1 3Jp/oa= 12M:K 8u/g$_P[1JZP(S6o %<$(r^LiyI,7X6Z4Dڌwe6g\7;6qOuֲW IwY A?I}0ՒHi"zcksƾXM;_n db~ ӔFێ[ܸ9lVw3T7M9ʲv!ӗq.#95$Ï<}Z>:ײIfw}6ǼLsXP;Erai}iq7I3=y7A<5(uQ(D[ [Xwo{iRoMA"˸"sVɼ}[Ń-QdMMK7(O=cvd6rrݿo\ bc0_~=%$'M/*#JEwބӥ[ZX[$߷khXޑsZMWjO BZhs̨LԨd5ll>'*iwaBMSXVɮX +Ƒ%CD6([\83)7ԩerG "yaz<@mLF]c&n$?GKc<" ܱl(:5гuS:xI+ &9X4rp3ʁyvg3(VB^ǫ@j8G-l,umh­grxY6߻65PyFUkE!ڢ]?O6[Ejzi*iΪxלȼz";K9v0KujuxO]. hWD+AœfėP\ q4/T-;Γ gVhι\<~9)p;je1 +=<4,e\w{Hةx~J/bwN%T=X$<;Hg-m; j\}xC#rRbUP 6)Їn Nhh3Ez9jAч!"ZFE,&RP1W݋JըW)on )V;RNZd5ocxj*ahsa~T#^'7BǕH=u3}=o =ܧois]ZT m ϩԚVʝ(q[怔3;c8:1B$R$͐\dE]B$j#JtE)f 3aʱc\([@?^v;S?_^HJvӡ6NqȬSσqI38QL׺?o)ut$Nڌi2qY>8@uJ 8{<>ƃSz'N}NmCm8 !Mg2fqi?HnHHT.*&RH@'X=T,yLŞ%(;X0Meިz$nr}v_5VJ_ܝ>c3q*[srypNvfU[$/`\<'i΄D2ƙbLPX}(Q=>&'  ][SD8y(>Epǘ K1 eY=ИX*Gn?fۂW9{6F'&—}XG)t&*C*-JQ7cYd'GǞ5pKQq>X/5%ʼnq׼vLBk EؑQ2GSa@{Rq;b|[\' Sدї4}#!jXDG CzomX5ӝf:Ԧ7]ݽViv7"E#Ʒ2| +bga{g¡: /V;wVA>EmsVĊBchTSB|!ַ3د`r +=Ha$ +R|/F&OLpU ;BSɏU#KC +(=Akw ,Q"Jse<෼{ a)(d F2Z\ő #Y_񴹷bܢM tU-Q5n<Ƥ)eUۑϜ am[EՅp싆2-A{Cr-ZQZB*P~@ +a{:sWXPMյ]bh1, %R@i\to:sJ;#[RD,ABH&AM*IC2 t~0$dٗ!r9e|0$oQRF j"JFR5V\ĆIX/ER4]{rJ&Cmi/CB]$HfH ,C ɐH TbH> IeY/CBZ’!qcI Z)\eH8]eHPsD}rjf}Ő3s2$r2eHy͍%>h_t.E "WvHrLHb*^d%{pc%ᴆXr?,0NXÒKT)bI%IsqYy%=C=,$˒S5,9v%An| TI.5Ї$ &O$y+$?K $:IIIˆzIrI?$|Hr)$Y$p$dՇˑSo$JDI$JO$?h 7AeHA``u + ƨ4|jOYxM0Sx*e +Z[5JDO#ƺ3dȧU[sj~\4 ۙ7G}[3D?d{w7p)aZ[R WMbms:d-zA[wr\ۍN~mDCl4W./b?ҸlWMr&)|"f޼/FR(&?DRV!xĉ# +`RZ\8uq?qbľ'vC+eLŕKo +.l9t,[X/-w]]b ZEb~e +*(;Y[1Tӈaw*qaC"ZoR46n݃,vB +l/[H$Pi/xX"Ÿ%bdehXN'՜x}nekkX\>sǷί#+UǮ y +,A,7B"D%`:3 +L=} 7QHa7HŸj@.Yl7X1Y>g)>-Aϩ\^.As8;*.ywRSyEi1@|kI!_ Fu Nco9Y]8~c2_VKy$k@>IH$ o0 K|=cCI;00 QG=8ECִ)nCEpx/=+>E>ƕ z# N j HY#8WrOvnmbDH@;BYފUpS*L5Y _Χm: 3" ܶ`!^; +8caKu1D}sA; qœ +bt} 0ܣVU'5T/ך ]OjAC ķ(`#ʒB۩Dr*ܳ]X$!PSZ={F8e0Gpy"a… >E|i7a$QMa+ɿ7Z (^ZDb8`eoyS8xuRQc~w ;df\u& `ok@3/W+<81!3&V4g"pg#G2à^SKˤM6)Rϗ:3 Ll-T=lսʹ`l2ףP70(z)Tf7)A\4Jt"9RBJ4kut ]מ;<9`/c="߲P+bbm~Xc ](#Sk5z9|F4=ܡ,(@#<i"2 + p1W?y@ǧ MhQLp22C;F<8Eu?HnvqXSF}ڌF Ib}4Pp띆dzsp5_6%0yVE +E%?0B3?{Zi8^@l2 +Ĩ@a[ 2Ejt2hGfj*q'%?COiPOoEdžQ_Oy+=ꬸ5W;!sA; clV=IUNS!ȯ#</ɯ>eZDfT[Lwi@J DShA-n=hq_}|z4P=85ڕh/i +#W{A45`o_HB}qAmz=*~xh +~Џٚ#p(c"ʤ5㷇+]8SN Eg mFwDUn `9@g@6I_Qsuw^'a7@{=| !+ S{ ׉jjO'jeG0#QI!)&uj5ѸVkFK󖉻6ogQcE:,:*أK 5%[A +qO)iK3#DS6Re/S:2heDoKC#hUH,T8]H3JTgȯ2&'JNf^xxuW0VLd'2b6ɖYza'\\OU1Cx8HFH6Y@YΧ'{U'x}4Z1ZV96x ӷp +o˵lM2g)E lRTI*DŽST@ɭ,u]]R7 ;mKeӞ`yϤ-ŁN)֪],Vd:MOV #ԐW>$MDܦZ[x>&@(9l wIxH\{Ǘ-;)rQ`g׏Kf.%[YgMc䳇n] 4=T9A r =bYY0_N} A2majMk  1AeG63 fN,Y׎OxWjk֍!-n@F5nJyv=Z$KqUe|hfQ/F.@t@?w6l~og˪X7㡄n`i-q_LYܝt)&253&;?']m'2-(̓kԂ~+T{AWcUSAW ?Ng)ؕ k)8㪅x_lT"q?C"<8\؛Dd*;Ox5H>%F7i͸M{0;HL̝f`Ѵ1lDIހ{}+o|{}mϷ_MH)s^+Կ]eerHlJo mw*֙`nS%BCJV$fFuZ25WRK]ԕo)RӪRChjq?? cgg=[#a).$+ڱVŻr8Mal s/OJ<:~W[ ys}j#n:"kZt92A(l3gQ>4FWB*#DqcO+O?,-*=NB +dwNGla|S[v|ouR X0oBz*gwG[|-(,|ޤ4d+(}]U+f?@҇,nX3:W8_9Y|r%tֵPf)nd SYG%,5HԠ}V4 +̰{uru}J I[8i JV1tibꔫ^fF:(J+PSYSuV4S*8e&uBwPT)>˥5BqcJ-*#SS'ϤR(ԙ fݮ*eWJsPWQj)!Yx7JSQvK">D{O":ꪧJjN_S+2?;{Xt\&B`h,> oF݃Yg' ]b|,xTxڊIƱա}rAd5ļ+=Q=.Ied Maܽ?VuhuKPTAjY 옚h5xA/'4cS9ܶ*iCV4PҢխCcU ';2J9յJHuV}/mw-ČZb +bMѹ#,&Ȗ`d{Qx@oo;vwYΔp*vMA6=ǡ'۠SOZwii^6[{6YEŀ-Hu {, lNӱ uȒŏ04nM>35Lߵ q;jcb[,|ǑmP?V x:D2WgvSi| f:^WKԗ:å]\t@3 +˨)Wל% +nTh TL'M*7ʀ ?WH2ɋ'Yf -cYehقDrCwHppw-1gC`F=7ꩥsioI4ep [|rq}[{=QJ>Xzh/9jfoD\a™[1ٝZb6|^7[{qjiۊ5HrXpdKX<XV!6^6(&@ˢ9ҍAUpýUűU_+' D̎;bV&}(C^L`,n[C֥TYĊbk!V _B+|p쀂NrFNc_xG+jk<0լ'V.b%YL Do`j.x.{\ugԊ.V`MݸCd\!WYW`VԿܙ2^~zwaIyh\2 nL󆮯x2حDVǓ"m}?D]qwT Q1xp(o[ +aq39.H` w6k=!'u1<冨=jDKhρ.e_(AjRjӵPQ<{#6I*FXHCՌbq\x-TRǦů{ +(1.\ŨPVء-ʩ|Hfpͯ+ⰺ2x8>3uD/޸* \[Ij `[ l+%p \o #p>D]Nj{+5 %p_~@WܥfW`po |ඤ&|{cp {R +ln-ji Oy w%َ݆* +\M-#kp%~d >vRn[?* ȤI.&92;UcLc re}Lz}lgV̙eh#bqfqKsp5wT/G?1n/?뷷;}M^Q5Vjz/^LczJ KXk;#gg GJljd/І|fKcо0p3\g wuʖE=^y׃?,ȌY6bAʄdnfuCI8WS }2ٗ} ]a:bհ8M̸]"(3o佷>坩 *IW*O\dk7nΔ/Cͭ)xP 4]MM80a3=]}[χqd|촋q) "4gmJ̩yw=] зog +y4X| +Z]5o B (Cżx Ő>XAOn@l/7?cC'bkY+LA% /ɫRHR`)SėlxOntm>T[p+XsUKuPgc7+HS?p] 3/B(|,1 f/.XVH4IY*nv^\B,|>D /I#: +m\>%2~2hL!kU=p1bR5kr9~Pv[5SFnX}A/&"襨A0zE\"Q3avGwm+ýMԱ[)y2u⨒~u %T +'O-yy ajydoہǪ ']v)PGPM\u@iT~d +_OVB%C*3C|yrˀ\Ųb:B6ZY0)W +zU ɃX|d,"]EE&T-Vڽحt `>7A7yxze kv1CU[zqq_J( 1T #]4?>3~w%T1CB(R2>Ri/MJvZ?|p2 %X[dL(hrB@%͹kKg =kc_а=lvpV>MY~qg,\D}XH,e&?53R'FvO^m}hzmd/C ]7%Ø앂8ka +q8396.xV8T]d/|[ZY5sbҙ"ФQ"z{ݸT5O}0[0on1COfW-2v3},S2o1-rǨ3f3S^ޜ{nŮΔM|YNjt܌T?g|i$jg*˕K4t}I5b ۮZ(Ȩ}Ϊ +N4Q|+'gDeZ{X7S7۲tzWKگ.eYVLN[XV՟-uHOǦ<`'p83Dn@gϞ7M&bgގ.`FzY1Gu1MYgg&f+FLtΓF3Yl!9`AbחsڲCNIm sHB."tIc{?:jZF{z"1쒿wy$``"?_ʡ5'Rۋi![+tFgqDGnh*o +a +)jmʅ9(&>8cW,g Ky76/gF0 5Ǿ+ZB70$K(C@,\I 0Rm^*!PdvxѮ~+fr:J2@'F+ {\p!5oi(Mjht'1禥(FQzGiC($@ +z)\H(PdU%4iz J~_`0W? W;\ѧ# + d+KXp āXxd%Q}B,HggAz>״5)g, 4=scNMGjEqF>Pd?wD<~>r[&'HnS\mws|Ԗ.L nYQu1GҿRhoKxAĮ[~Wk!5ՍQxa x CPs:F^J PPl^ xStQ%d}:A zܘ÷-*oNs_%HIz|2' *mw&> z~"Eo9]Wwh{5B1q :ƦMQ׳g"a8 N+qPo+<=7eS{^<M-f:)zT!. +M8߱61o=eN) (N8pu^dG5O.Ъ.ւXĞ^=kj k7Ss֧@$\9Gh,Ǟ}4tH1L;tZlF ~$|W61[HgnJ7<)8˦z%hy !G V'̂A䭡U H1mjcghR4" +USه$yET 8 =[c6HYpy |×חqT14-C 4ɘ#Z & U$Nܬh`PAUw2CDuPtt]ۖ#P -=!PZkMu +cUښ"K&c(U=Hlu1eR8$=4> ?Qu30h +JH9 F pBO$֢mrƙWϬP)BcX=4FuI?)7^A>#cP->%5ɝ1WhA¬΄VMİQmݞJ+MΙ_GN85&D# 9Ȯ8pi=,1XodVPOMKoLVϬpT"L6]<7:Q^)(X4]­2L Y9e&=< +}+~YW_,F~2V/fy ^R_wvw7,8Ydx,?9; pѼ\F6ʂ?bFMw*ڸv0F , o٥chƽ(HOƨv84N<57Qe$6t:[e4mXE0w.P;4*JX3!x2nEC6O  `¬h..6:Ќ؈MY/mK>08^dc%#ZlyFyp3[`:ivz0 +.,׆-y[ȊΡco>}Cp>ed6 i\OkDRmrC٤bmv0hlmss4y3C)K`LͥBrNoLdZc?0=(r +f.R@jlWyU j[mPHno($t +p(ژ5>nttIJlE4:v "p0'/)5E2&U+|H/tu@S<~vP@ Y*L.6fOꅡHgHk3bYv*Jplvˍ/?UVًP\ ߐѝs-~7p(6k1uxk +3f*DA|b +q]/Z`v:ü3îtoA筮;W5]X'#{W"# Dfv00?$DE?x-Ə!k9-A9ژؗjjsXI& 0| &n(n\s3_N\Lx׷[\?=Ǩ?TUиW@$B E8.9P`K0STvdFMQNe(CVGm)oSs`˵S@&3K`ȐU27 u ȃEp'xbK_#OeF bP :[*~tJ$o*Rs~R1-WD +q%% $x/8p' U"J@N  AtaOKS AgJ$4_%.A;JaWUm_+A K zzWzzcs7#~wJIrɁ%t6GאH8-*w Lz 4$=p: `Bf+E@o tII6KS1U TH<QDAph KiX!:WZ"D4CtJijɑNie>ki8 $AJAT ^[' a2${I|;"@,uOL)¤P?E%Pcm Jmk8~ؔgБew5h +eS̙(VY%v'z58QpB~(`w? {l +Ɖ'Qi`~OC|˃Lv']Pm@;`7Ŷ_u  > +U;\E4,)?<6 +ʓf1{8_lQjβL Q_;=͝ie ~ȝ{~M )l(gnGvǥ\<[V m.%sIH&xPO'WLe+Ӫ Ҧ97) o@E}OGE^jx4;LyDlgC`'s ^vqnv!.NI/B$C{`;Η/g#czcİF+gSYp7Wlz^+"$љDJ /T!+A'*mB۳S>0߽ڗFl/ՒY>E^ +:Ɯ`bHziwF }MAn_zd%0ÿn' qqK;'T&$Y}pUĬƝ$W)4H[.%L(2P:%IRF!N#ß`ثE*g2śeH*`nw, U8UfϤC`ںb,Cun j)ZZRҍOAVlncN ]V<Ǝhs/"^u>8eyhޡ0c߸Y}sEN!,bo,<苻Sp + oKsl͟Hi;^?Io}+ͱW0w4ǢѪ4p ?.[\GL ݝ毦ϢB<$F+S|X Gp0/ˮ,zBp.Ey1:^8O!k[Fst,)z/zG|IbhǻPIQʊ,Xr~s6Ѫe/|3g;%PLlb=^jgg:,dG; +f!789ԝsX~ֲVsV +/P5mij +j@᤟DnoιUp[F_04Xr(_;@RW13)2K0}x>(!l.u uܔy7)T)Vf$7/L]g ]㴚ilq+w@*ɢ$΃X1^p0( +/KUJR`;>?E{xREF-BVv;4zVU7l8Gdiꎌ4PIUS[xFC$aS [iխ J/aم]-9CR2ZwtgnucX;Ss-{H~`P(DcGd@){I WljSUt>wGd9A(ܓ8.8maHuu)}")Y"%:_HnC,C ޜqRLMYʂlс]a9twzx1$R^ų&A.*3˶jYsyeq*:fvH2K-$Iڂl4MpNր0)QtpnX*99Уi*ĩkE4Rs4ϔv94 /T!aA u᪴Ocjԗ:CjO]r%fBgWl WQ[Kc}B1=(plE\{6ك#u߅:/OԞ}250wSG.,q}q8oAQY~JE@ /Ů(GE~֑ Dn7:}9q걶)LK0e-6CuI' +<*L̝0Um0 LMsIp +kj>%Ga+e쌂YNMJ0.h񪼳%3hfRwjrn4ݒ+`hPaWcMl/xʍQ)yD3}aCpBSGj0Fx@n|{$W0]ؙpZ1EtFyGyAyV.ן 0>8&et@M-Fyiȉ5FO+MB1 kxRJe.Q{=g 7"DEw+"b'dKH=T!X5] ϝrё}cDLYOv@>K=y"⮻;NV휒@p_\*RCYM7ҠkIȘ C~DBtlja2Kn`RjvC a[ZG3QCy6Fj4+9%/E )ѓĕZLI&sgh0\/-D]͟1E$ŝ^[O L,٠3li[in:Ox}g-ꌬBؿ:"YPgWvViHxh"jfG&l(HōZ*5MK'2$϶8)EODl \by++h]\As f>:ᮕXsJ39Fk_c7ʪ(*:z('.xX@$Y>,@fM1{fINs^Uӯx xyZi{ F^ټ'#PV+ʃ +",tqcmFr6(K=b13dZYY`a&d7JvZD =jgv:c4mԄjR1 +vz[ښ2imkuɵUqa}IQq 1{1,i̚ 357MKB*tgS?N~.D[z<7`z#4s×~=oVĭoL:y8S4Ʊr9( C8lm> ᠩG@VN4cCHiPal0v!@2ωFc UL/$<Ψzn9؉vfrYay^k? 3ݟP[1CLS :\مQ'Ta_H8 ⤦ ~afitΦ㧸R4 Gv$]֣y"7Pt=Ț$l/svun^~Lz:$^cwq Z%9%L{֩j{U2YtAuLͮIwsk-Rʰɑ+<njqd0XTr]ٚ7V˸"vA8+APuJ{.pWE_bv[&/}B$/(TЅEL"tN<[Q_G#4#^ 487Oc_ v,;mUʮQٖCd%)U{\=2!%PIw hdQ 3h>/x"O/iLQ. +yD/( A%=Yu͊~mlvdn +8V{k[kq63& ūb84A5e uMNu; .: cY4ڞ25SB^Um`Ȫzʪ/]62pqTWTFӇhR@;aVbH;ma\QBx+PuTl msJ39vΙ4FVֹ/#tQ9:sQ:sEmJ ]C*1)x7R==)~}V'$K~ˤ|2 2 -äfHb<1}4OԚD1l}1)fLDITH$ҁ$R{Dl%"Hq4N"#dw8T/><"AiA"ELڗH?DR"x<"!_"A?"5*4$R<fGKH2K)aL8&~$|[6R<>\pstʰFB^;X+vLVh(c)%f:F3~yX̀T3M" %LRӣ[|uYJLE +W d}-@4fTW#sR/_x$^bY3*F;&lȡnbO E Y jMȎP"}q}I׊֫Ŭtx> )up(>*B&Yk؞Fd_ )qGvs3V߄UP gl0u:fv2+>Sit_|2a&bqTUVeлo7Ǖ`Za's-1QEL*F3Ȁ!n^?OE?* +C%G9UŶ}]PihD:u^C`\9v,e%(ǵp;-:E Цi!@֋{GTx_Ё<13lJyci ( 9QtH5hQ\gj TW'|kkP,:wJIA9OX[Vz +Ywq=(\p7d0es^K7B A6~#){5Y~`/*/_U`}>`l#]ءQm>BѰvV4Tk?a8/% |Ez9lށA0)c(Trfn7wbWl 'yЌ̎rR=ۨ]vkWևdkքչ$}$*.fG8SmkzkIqVy W<Q 2+ +VU¨td @)tTC-Qpzn {O-|q?] P3JL8!p J݀ ڰ_R925iϫ[6>>G8}X܇?5!j)! bCB!oeof?"Я.?!9 }#'$o)wd<#12+OZKLpMjXZٹH=SXH$( ߩ(xm +-{|g ~,@EB)+x[aۣ)E5]qt*":"ml|VK9ܨ8xlw¥gN)4 Xk7n ^~d'Fąb,`G:Z2O-=hmȚAh~h|(G<ډ9e[chlx>Da֟E0/z]|xoٝl=Z0YԒCw \Y{< P&G;m1Q@i':85*Ζ\E]:j(*b +~GdEn8^ĄÉ+c8e~x<4V^L;e).+YS\=k]&HS4J!9@#jqNyihLprޠL ;qerIqGU#orT _b)Ǩ](xίPu1 +0# +P, :'L־jb<ATkc1OWe +%-BkڷmƪwrTpè>$J)j@@A"W|pAcWps,P6Gm[ +JD bXB `zTʁ)ԵLZABd +cm(cx{ =>jsckWeuG6Jyz%klnC8s Auy0z~ 4mi7Ά'~D~{9"<Hs{y@̥tWK]*,g(OZ$eS4|Dn)GSL[R9߇[ʿJLEv).j4͡L#xEtU [®Q+}졈9wa⊅MSP,q~263rr&84Ʒmj(n*a^_("}{NmX@$35G( ⋞_"R%80Juh4*uY) +`@Ra﯏Jp,# *7jb#*p\|%DG$"p4bUdTp.Π+3c2&_p xDGNG+qo,ʛ'I 's!xWSL㼲t "ndFc9Jy X +KGm VW5y z>fZZIEk/B4@fN*"9V|)v|.qg+4 iXV vZPԲ\aB;!Fh-~}8É@n3Pf>B_+ec6h'`_ +hNF)\F :Q FT}|CEV +oiQhWϷAf49!u?,Rd$Cu1De^݇"LѴ/G5Y-)d S]cr%kaMs!aC+i^fg0d05smjH巶F ./,H0X" e+ _D\+y;=i]ˤ +{]zwr0X?7] ?h 鐑 і%! (1~8:Pm._j(/D'=>]hv:ZuZ jjl9jhqe#@kbƮփp  lԙButp/C÷vQquVO,t`}MW<X]t`q`W2/jkeNXw"Dz\5:k2kH.=a]rH3TvG#PQ ~Q +fE57لcTm>P sϧש.23x}6̋:Y;6oTe_w3xvDV ^yO,Ӽ7Cw+~oE?~U{9x!.8F;CdLvT.#s(>3ϖxw&H&̉֫+Pӗ +iJTv`@YtVɓr%,G +oZ95-kFҵz\Nzq.2Wl UdfFmdb'jQ){`HC]<Ku. =in\v͔1f}kbRs;g]f:b\e߈ڛ.a[G:"O{謦(4#U:Ý,F{hK͌~ɪ:YI*c9Y@OE,\ԍ9߇<0֥PWK"9G@`N8kDky sP43=,Jg j%M^elvSֺCyg#vÐ xC&v^s'i=PL=K."P&|ϡ1YʆeIcH +wR5Pttݞ%m۹E/뚗fKWSV67g T5wNMWţ֌C/ +ZUMKwXrðWȢDJzf̶_fN4&bsS#D ``6"*m7IsX} +A`G#}S_6Gyk`8Ag=H]̚?ÀBC +=nۊ8Lm3cPIAYnJd$bK-ЩÞi<2`"}֘(%t8ݜA`*״NՀk4}zqԒAiC$4=V)"[[XՙUkd‘+-5l'uwcxD}~"~u*f,9Ê;Z!'ş揦2<-h &4ب'0#a 2aTG^h(XTs@D or[|eޣx f܂{l &ò~(Z$&ٮwJpC 20:v~{8hD#ۏ@qNy(tFn#:qW1FN DȈ 5L36$MHrN:$ۣD:u(h!(0$c](;D:2T"PQ"|Q( !" ϦdC(D#&٠F8HRaDSC@=i/%鼨D7H R$ZiÐ"VAshRVgM!ztHMқlf ʡY1qZAX@l%tHkHehQ1Z;=(gT@&u>ա Ί)C\Q^4!@g0)S/!CzBZQrR6v Đ3i/؝_ -%C$ʐKr" A@ JNRɏ*d e̔!+:zh%M]oLpРdHsO{Z&dnRϰ f:dC 0th$}ΨCZS3;)%Ce;4V0D7Y!CF-c"ܚㇺ{3j ˷|窥zR$RM59BlpoȘ/g4N'FQ.7MySUqV`n`'AѶyД5CoҼlCh8DklgXl ]k2W)d82.ocq*Jcuƽ*W#pO"Yj4|ZA+́(ckX6ꕏV#7#a +S 0f5-M7V!`>L ̆8xHHԹg˵;rʙg_gvgAk5w)M ]v{URdWn4^y*qh(el;4x$6>zK3,V 3IfDv<ĕU+~ )֍iM38+YZLx{*kɌX*jKZHs?4ٿCWoHtLY6Eߠ3>}q`gj4pUJ ?TYB@4!"&\fCxaƁ[wW<g4䀉kIĺțnzՓi &% /7CL4xAE:"ؓBwlVTcTBm{EDҘ(#hLx(0JCҘ(T`L@}SƻGml1&,[lq'*VKhlK=[X~G +!ؒ -N`K,#[)Ig y=qdXbG ɟ(|ݶD9X7$%+p!%%_[' #ؒ|sɨ-=$$"OaT]h$\ @)@eUuݦmJ=S?=⪩HօT9,޷ЇTmi - YW$h CbUUKwEt܀Ryf.S[м.v}8xfK3>O^gQ=}q/>Rh]= +|Ρh4LRǞ' 鼁6fdd$>ɔ=dw1<7j:Cj_rCeggGPv</ԛ1,K@@{@"lC*gL}$:LWmWƞ)׸xt7/[u:O Ri.:(|J󏮥>D)Z:}%jƥv6tڲߛ*< u)1v.7u 3O^WQIS_;6cC٢J1YuƁ@cDRgdav?I^r1?s? }v0uhr&^ ڑ:T1-y*-!CNכjE31%5Ll@giʳ*95awGLi_p S`%?'%} +̫O?RālB:wmђ+DRKAdpG{}GZQ(HH,t{}.1{f>)`Na.z.&Ts`""v띮{8 ;7鍳) [+Jy&TKqH2>P1*d'[aY:Qђ5wNg^1pjl#H<9(ghDIbB q ؃+p!}0۱3Y;YlT_)vwFŁU9.W{x`aP<2lFCmn_Cxu+G, }`ټk_e6fT- 8pq?rRX\<]g/ewEi,r{8`U@QrqQ$n  ̳V8w}q`u88G]'JgS#=:6 T<"GJA8#@pKҗK=[-\a.8 8`Gx?ޅW>}ᶿ'dd2T" qjV +YS2cc@dud8EW]ڑ +3ݬ=6GrcQcĭ +o=gP]_2/YKEv{/⟄ D z_#Ȱf8wD66¬g]Yii16U;O*fǢ<& ʊbCv ×v_>3;Q2{7ν7#Mh'0'W@E$T,B)aRN`ؑ3Qv`XN&g"A&m|`h6id$/9/h˱Sc[$%NFqeNCK, ehN E3r D[W"[.=л$U(17błn LDľ1ڊvjZ%H*UI|)AR"Ke %G)JBy9XHs8vgѤ.0dWxa4E=>v'"1_Y{w!Ul{KJh[U1g 3]MТEklU#Ŝ %]@}dY7ZAE#y9)eyY3I߬',Jls~{NtH!Y`=HNJgHPe]l|% 5'0Z@]6x0L<4k?ڭv9f W':ogaM@DВX7\jCU@+o=29ouѭ4,:EWVq`hCjpA1~ 1Kokm0ö=2Y,.WQI+2Q{KLzﷵ$ggwbFgИ,V_Q{ \D&R9zjɢv J\C-EDCŢRH#[zƳ# KSBL>똻nVpzq2FDM[97lZ\a e.yz#4y@.ճ''&#yb›xv]j,/czaKKYOhTv>-ӮʹLeZI?+S'4L}Ѹ灱,삗z;aSQԌKb]K%2ި+:pqz3r^XP4+%|"k]Rbxs3Ij{'Z(.hSbZIۍ*tbrI2?t ڧvL\zU"o +P(Q~h{ƠA9Aw=/_Mk3=$FEr^9.:Ord: .Ug>5~g Ti3jn8#OOuqy-s)մBWD"םzw-wehs/14gTxVc {ߢS8ɷM:OqOMHp(b/r*-y3/AJչ3ڹ+ϲ2x1nMn$;+B%St:KWʳ4<)TO'Oad I[U}(tv;M%dZjJqnT&.q50>5Bwݒ]r yNUv Qtv˶u \ܢhT1d#[髐c. JNʟKBH.N2Ex3a FzgRK8,y +J KYG}OZtCV"?%:d#q0 +jהbBIX͛>bMG~CڿqFk8!_SnVh1#bQ`j1.f$+SÈNUtrjz({GJ:oUu*sl}K=-^Gg pfi| +̴) Q7N ]PIܣ׶x@_0T߂hD1<Tq'=py[9Atɾa>+3ԒN:H1iܠǩyH+zOgf3V;LFԀHkR9"ڬ4]@oG@df?iC%tUձP8hnfg}|R׊r-!59i=fMgsrۤ/.4j5^w:nSk7SdmaEgϲlhejfب٘Haz&kK˾"Xg C¥6ǞRFF#v(0NQKN}֑7c_֖!rSivR΢/%b+N1='Cޢh˹Cڔhe71gj&tI?}F[Y4$BQE^b* աE`sc7hRBC2N) fl4. :a@r#u:o4>89:+7V D6YL1-BM00Xj>9!"*X;hp9b깉c7 B_uF7S <,:o^|s-1UCL ^g i "ɝ-Mʱz[my)[2 "l8[P#'tb]/=uroS-iR(䓊h (V"~*5/ PtdE-Tlflws"*I#{pG1 8#z]Аɵ$ U_mзC}lƉBROW _,9_k$<'$@$ȅgq yH/>ₜRtѵ%TtAu4Gj}>P82=L{:- }r}y2Cm7|Έ߼B!U?{LT*Y7k*mՕJ&ݎddjMsJj! 9Mk\5Dk"MnG7q&@fuZHnxLh[7Zr8Z#u&HDj* .r"аrNDstO)+H'd-W;!-98X-% "K!NzW;v+h;@W;[;ֳ̣vNS;<%ZxCO_+쳽҉ d ҉tAPqwt"G:YJt +ިvN*vCЏvbI<#eOxKO +~pE']WW=W=^ z +P]wHCu5W(LZ9Nby +(bt#@9s҆x|n Rʅ39mqJ6@-SF %h|ȗ+Aw ~5J@b#d4orCJ؂c/-^לKcfG_}Ȇ  ΅y[Csz>u".h(p&`T[3>9)e"ޫ8a;Y)T"R3Kʠz4\]sSm{iУ1pls?XVY,^@:C Jwd5k7ǛӚOPg { hMhДCG7Oɢ-Xʣe[HP ҈ 6^γlC +!Wq0VS )M +4Ӻ%MreHCxKOU._b6Hv*mɖXԀ2f8D{[X +EG bxˤ=60ڤI-<zWjHǰ Z9C~9r]-YV`7`ۍ!4ƨΦ)Z{JSqe6MPuPُ!PgfS֥LkԦNPF.ۜ\ixnHL|Bu }in2nLӀjkCY ߥ}*Ί)E鎪}.c"|Z=~1Jxd7ږ+#9s#N;h-ܫ+tzZ; ^H/2yCK^.2j%|jRnRMieKlv:JPwSU<]#ntb[^ww)@5οɗ食˵n[x&vҫ,ZTI3%oW)DէXh:18Ҕ=r69%[Z5Ý'" `:RIj,sꃽ]̿A4~s(HҀZgi-۳,u9ݻ/6&DҜ_GöSݗlhiwCރ }}٫敵>u5Gi,! +;m1GNc>-[Jk> ϹXN xca \4MߵG,ᅨrT(*]dc7[vVfLd%&/+xXãME*FQBCA~F8xU?L."֭EO,^'aLuyiw`އ'ӒUy]OQʵ :{ޜ~g%]ݰt9\q8m#0vMA zJ"q5=I1Y⹒g%iY!zХO'dl/$<`Ko*\^GX~12旈#k#DZDm٘Lt  +;HQ{b/ՙTsGSuh1W_HG5zs4jSC~1(йT:~KT_k<E p=]y;jkl!+uxw]O])arqh+ k[:yL%HgXB)E֒cn0{$z <@}* Dsa_:UVhnGfy>z#2WM@okk +k23fF2T/ba*b{qRgV_ #X090C @]5C.$s 0a=`v*"99_:z/AR?ԠQղ.u`qq&qtO\pau9]LtR@# #w6qU>d9𥬙$ǎ}@5P:F^؋/Ae^(5r 0Orಞ{~x /zCָ=C( CLÜ1oūS.^Q隦G{tb Sw6CW(gfFuD&dԦxBȖ%gmh3#6=5+0,>A~* ᶂÖ}u),VZC{ +>X7R@?e7aSDTJ2T(i\-=ZpC@=4H:ʫ +JŷB=`2'Y~]gGNM*h1 v=TrUP`8e cm͇fE *k$hƒ21W$CK+`q+_)v'aq3X`|vʆQ7,?5޺Q:G!$DA%ӊ.XS+{iaPu"s>숀0:'.iKp n0h'cfR`$D:@Q2/ +SxP9?g`5FXo9ueh[s^̩.nB~lMvw!wsZq"GXѓ:(4i+Ub6:{45`vTE)#dGxlU-^PFNvMM=K,|W)Ǵ[ӪEC[:bY#e !Bk~`gE\[rPl=W:ĮdC6>f+LCC ֫ûW<2>jKka)_غS|/vȮ&|y?gЄQ{9Ű + ŨM` LƮQ9qc.+ 6E"Jv!+DHmRV#rdΌ=CtWSdPZkdf1a9Wmݙ~pQ1+B?xpԴutr} qHnx[=@[kx@QU~5BV}aBpQ[ R>6_x`Jh5 R<({F[4t{Ń#xxÍ7VF,¸!(+ +C>xOUvf{Cv|~% І@D~`p[@_\lO- W@ |H{>)(d +1*[A8jg{CA<[@8D{DR@y  + + ,( [ 0rBWA} +VxlW +/V#QlgY@|5"`[(FqT9 +lUY7SA _ +© N-OA[AYͮ|L}|=>fdgGR$_K*J*TWA<ܖ +p)>8ÏQbGɠ`#ȷ}Lp 63q\05@`Uຈ;rZч]Q`QZ,GL+Ztn"G@TJ[QF3',;m&Ψ(UWt_` NӪ/4C{Un4s˥@y ٶcMoK);Idz˶#=VPZ=#jŤ_?_?UJկYAń??%*Dz#GbNQ~dp?1Hڑ>L2Vp٬] ^R`{KP'/v"}8l&߿( YH$RwS!uyq3AqC pfm +@xBh`׹j'&}rzIk=k޴, ++7GH wҞ[*^pESétxi&M"5CVe#&2FٲX"іLԡ1<uh?3Ti&FO$úc+[T N'SqXGŕl.ܢxj"뫪i ;>(+s=Xd'߲1Zh*.U +OïS_Z_/cBg6תk +.kVUޖ6Rf U3a,A*Lײ;J-6=|cHt+:񜙾&pIe ZݽR}XK.;hydhtm@}~-Xl jهƠN=`3cڄ7Y[fGlq#̅&قS2#L'KuxtT /DF iaH/)Ki4#C&lЮDjĺ~+=i}^+K +&Ϛrr;Ϧh)p &FOՙ:qA0> C.xJ3*n(` L)ky(]|by>՞֚<8JJ "zk"[GiZVB"lcqx?BC8*ΪOx4j+a3A_jE۽ǵgkJiB+J+Sʕm !$^@L/DF]|L$I?-TpyE}9s +63YYq/f`B{9⻔k+}noQqlMN<Ш ~$&^-8 )HtUQ]F\Ojǐ,V$@Z=k^&Ȅ TlPɽN2qL(ղ8Z>iau $1>gwNq8d~&_Y^@1C銥">S]kFI6I5ܴѦ=:c92ꎡ_O>Cm 9wHV*R1 Nsxn9(!^[7 3,됲 Nqr\^yS_՜>1>GwH;zt&D:Z<64$tm8 c[k`'Ҹjx||`nTWx$/ț?0uoE8֎cV(dH"uo = +䐳8ggצƟ ޜ.|d +d>yU Lu> +L]jM*pEZVM@.,U +Y oqM*8oV ͨ7fqE|b#6oqQ(nq+^3Jhu߱-޵-?A1.y O)($Nᘝqm\[3Tq$p1+Ǐ_iEM” `ꍆYc,GuG#Tu=8]o,aպ˴sypz\M޼CTj3]EhuDdaoW,>Kvrխ []җ{ho={mʭNFH y.2˂z;#A~ +d +/0_HayFg-tf uʵ8$BX0l?ĠqivX,:69'Dw c@kE7_.@u7kpL Qm2M2 џ5cx \HQT)p*u6BhJjyQ,%y'$qٴo'7-0A#/g*rPNA.j9s'a5w)hCGnMq4 \3K-;7bv#גA_L(Bʥ/j,f2V2bίOolgSTiᛰ!/۳ +KѐTI܋V[rcXIx.k\-Ӳ^` +yJv?h.W;V$T[v$Zr-qzV/KdN<ƶ.Z<*%#5*z@jfÄ_E* +NoAȹë[*d˸Ł}cWuŞe–GjVY/s:׏'zPK]'YPLsVzf*qe5l9zh oK8UdbTg{߿58Ϋ=\x'0j)7jLled=H0U\C;nDZ /I0 IA f45~4y1G_ DNj*`8j{ѕ۫>~ھg:ts)X w>P6@= PsݼWRҁ1QƒPb2iTЇ \64<`h qJ'  㧲d(oR2ٚUn9]58>i|8ţVPd}!DޜZ 3/yQW*27MaNHW縲/TK= f']ZP,V$)UǾ<\Sl?1J7QwܵY"~׊5dJUUHtobg0 Ԡ\Q +g:?B:#(LQcDwr[L꓌HgX7$pP3 # . +Q_b -@p ڌl:\oߐ.P>X<|F%ʋdk؝y@v-kq?!YT<6;N_D̾=ɪpW-]Eq75>9_Y2MWC4)7[oRVx̀JOM$oͪVу5YgVC.hwvjٶ 1J 6g$S@,‹W)*1Yә %( 4 _;4d#%|*T s"2GtӓaO;V?LtS}CߔW2ZV/SG[0d+zZ%bSw[;2 &n9ÿaZ̀d#S-xy ;C0}ɴ'ӹ.«X29J)*Mx$@].hMi5!*7Ԭ1[n oBf&t+v&|n?"#v>n:g;v0Ee*wJh+J_'T52!"hkn"=UUv"n¢cƶ8Va40R$D `:Qr o#&]I|2U#[`r |HU]j?߀/Bv3؝V=/ a:/xj_c|st P/,arjnvue%7R`YȢ. X$/CXl.kL62D4SB,kɢ/XڒM[z, g>/+WPc4T 5Rj\b6&,k^@Հ~ nYf\yXjwA#r %j!lGC L M}6}~Lxf-׉E6B ;Hg4\+b.ѕ*G\hjqUɱx1얔׭ 9Eu1dۿx|fDb\4Cn;[…#7 YbMQ <gztHLL\~Sx6uDzmmcx4[v/jR]rε<{VUord˔'C$ +3X!OI\^Pؚۇ2)m?oᎥx[ K-xTjbL*y^pu=UuzOUG>Ue[O^@d9dV7 +qcjܑS3=kÛ05τ@M堯UKJt]G[uZ F ކ3C=/3D4qovZ}sʊHUqʂ8U~bhƜt)zaZgiE@Տ81'g5np?q8@]m5&  k0y!^vxkѮ>R{J $]/D>RO$|JW\C>_꼀+LI}0S|\ *Ǝ~I6vSWK$*ׄG:fiޣ\/h̅r Jm#]S-)fϪK0M%G"t@5%]qWVRs qu-++5}&LAqa詁|{40?#:K-ڠPT} L28f3<&_yo Pï[ D&&Ԝ* ,]UZ?!`oqML;s cӊJ릁sd]z->I1@H-3FQMW:?`䁄i:#'Hqu >d`}rʅU'EDoW2߯L~h@Ӄe+pQ~pxdpnpqU?GM@ʊMWӴq.UnmР}%mwɼ U0̈ mXk񿳼ջvIs!\l0ٙPZ`r;ɨk416gmdZ&h~R,69y\~?Le'pR槗*џ2Z`Ց P0mAޭDT) ʃKzBKl-@?q*Lgree'ݴwta?]gPG=m7VH/TƼgx*?7B2n~zc|D(O2xMښ 1E¯S![] r 'HrVZCRό?/H1KkN5 W;]Ov +@/3ijE#@Z-bog;L4}PzJ2{0:?6TI6&oօ}:aI(vFǠẸR͑vck{2T wskg"vk:u#`ۆ?7zY+]O$?, +^c0P*^#.Un0O moc{ <| 8BS >Z?O0$8I.l}sm1lnj8mM+@4D<Y٢X2*(3o$_lmi3kMo1dd[7Y&*g,dv,4X Xŋ+Dҝ&g8B"\pyf1aTϊHklDh3hCAԀvJVJs^߇y4rgwԘ-tcM2K][A/@hr}kv?   `U^3hG=2y>x *LjH=7?Z9I݃CǓUoc!QLق vW'X& \ $UU&`wY0oPօ̦L#ڕePv4kol\`!Pf^9ls^nva)TZmZBƉ'}q.झ[F9ʡ'FhAֵ<[ 8n]VbJ3TH `8 tdwk7]L宁㔭3gouMJ M<9p WP͝WL &Ddx|QNRX"S`Ȍ)` RV"FajEhux[#jM)/ZjM4 +xG}]Qf!G=zyWW֐|R$n, E}aVYO=N6'Q8V TAqVsobꔁ=Zy5ԏ6L̶`Ucaͷøvc$ +Edj?e;%KJ]1H}dYCUR S#t\/1vRUZ_9C0||A{YN{*kl IЭ8=A{=NAk)I];ѢЯ8Kf!Aš[h\l/uha?]lj% hs7@dt.[Ehjo%Ňg>ϙqK' >jAÑܻrA8ƇRf#~bT':fg5")͋Z\iD^dܥS, tdWvm**uE]|TA牀TK8pmYb &e!-m d&MnU4 HWQeQ$p*@7팓u$ժ8d lH:!:R1pN(D)Bvn|CWϘl~fɱɵ(GI/j%z-#KP9n#0zM\\,8—.>'p2Ž}FJsw5 C J&6i__R b.7\4i‰u[(5*,]liP䞩HGS=:tj&|Mj/`5vM=hjl!2,`WثӍwQa\5𴽢3h72{o7&#"'Yː7nyWQ.K"L[|lp.~&Ԋa7ZSaU= +1߄]sGa,T_qbFլ B,d5Mn/MPNθk9Bo45^h\O !3@ Ug1F̸эNc: t6Fnm1vH-yWC"Ԓ#+Ģ6Zzhº`oiBzm8lD"])pNu3@7?\&0t,7RCjryFtbϲw@b)DR*]R{@.`Z.֔VVVTyXxKX8;`58p^if!jc˹W}mި& B)R ;0D3`;viۜ>h=34xjv=ˇ 6;ML +鈀pH~a:o]E73!px]4MDږ90kiqD]>Hss%^o[nT~?ؑ-/]l)]MxҚNLAvNO272ml11Go~/S.S=Fh[5/h=<c4Ib^ C1inw&L%& }d{ݕMf=H}= ž7C1w*@o[q%bC)_Đfd.RJVjg'VXE%Aa+d̼z+"o R.)1z0Uz鍉rZدVĈ7;Y@$;'TYPdz-}ZpyĖZ>@$ꐫyVjДZ@8hfКmf`=v",ر `:K$f@ղ'p\ ly5_ o[KXK{ʹ%|Z)ceqY+խX`PXGLyq+SV՗`?/S9wq*"鰾h'\ߢ@kEr.+0f/UޙϨEsCHqRM>;:E`VYYCj:蕕Z_юv >Uy_j3($׬ +j:# QXDV&eeDP(ϷsG?+܏^xBñTaEW.ebϨ+~^C88sQy͏G6=DTpKSp!0ڑ"h[)2rs$WBtŌNa-_uW452Z&ņX޲ǎ"nHyt%e\i7'WPZ6#{&ZD&h7G욗SZvw 9VL1"wfG<ܺlQuGۣ(˻> >[̳FtŇ?Z<g1зFDc[Dc*<݉)P:©D0K!W$]ގ$&WA^ u= I* O;++`гX.|8|% K=Yk2<'b0OQ˻2Hd>zP}+Ovw4| hQewNJϳ#bjɶkaS`>yzNͿQ%>`lYroV=:" ^&j%3Pc齅yӬdiϚ7,AX w*{f f!·,]G"EKߍǯQ+kE\9$/  +dJGy6ljBtL_)vdosJ8>+SmNN}:2޳wD3ǚI"xKpS0@ء;D zR4}%EJ$}A/iJs@ϼDW^` V>B@rG4&#[ٷ8 +1=yDCi0?)c +@{- .㱐 ) xF__fϊzn lЩlD5OIF_vGBcNxR@‡C1GT>ea6@޶0);z!D;ra +ahưعc[ +76g +r=1:aO!❲Ȍ +Um 3ֈhhM'!@# 4\_)zWgI]8GV6W;Aj{!T7B=i~ghVY/0LBg#gz7@k0jz5;VyTV"4ĕ)q iP&8F&Q7tFUí~u~bvs)KI1FdFMW^pN?([i +%Nq;L+x''9wvfJ *2+'O +=CemW`*XH@d)Qgy9M__Ng`8$v,HıpgsټI?k6)I88Ωې|bB'i lGV=HN#i;LAuCJ1WL84 ҂!BiX\zpUNߢi%Hr1X*!}":ɞ㜩ۯ +>jOw!dҒhnl(_'fmrM3 hb 2/SAzPywLE1`9Lϸ֫s׼%g=8<^ z2f޼~ 0GnR+d|=DB[,iyanNSG6GC>9Ya=P\Kzt24>‹b[7CUsOD?Ӡyw˨S$aE!eC`$$[[hpdn XkxʻFpˣ P9An3ԀE +0/;C .p*`Cҫ-Z(`0EJ`C +سJ`Oc_ˣ9Ž.xv?7N |j`=+4+uEǺyyP_RxV,|$W{D +O@{<_vEG* .U1f TȾ"Oq "8p)(LEJ.ꡧ]]2;ٸA] dp`/!K;OMwe0y ;ȣhζ+v(?΁\p /VNU~Nxrcw"3 !vn\y#i*OJ'. +?6,7١_Uw'-vvBWb߿ ݈-K<CU.7UTvH3" ,:k4;&@1^N>Av̦6!iٙ*qƎ)5}Rx[;1Otd{] ZhO|?kxpQƂ.'2 n7vdѱⰭd׆ii[,$6bxe |r_l1䞄ؿdZeoS`vy fZ!IaL`iDMRRAhμFkLR +hק9"Rr|x‡駹gR1'"3IUtz9ȓ扡nTvfR D7n$"=YCcXNe`yי8 e*-A犦$d8B8橼ʡj09Y4eN\tֳɎkwgq,XݛCL&Yڏ9eGNˬ䄆`">PJfDكy`{|4f: -TNjR]IfF{QRtXf Iu` +['5TUGddF2*x8|zuh)d˃gwHbށ㮄.^k'\Uox$N>"#3tW|ZKWT/w!\^R\Ժ|*șSL/6kqe5~_,LiP걨j}tù\zC`XP0LޣjWr)E !`N1tqtf/Y=~pfNNSS>i89򗡌~?3?sMd +TS.dqL=k5 )?]lͅrYMbp6hu|t0 -`sz#y^Op=]J~ Z WN|??˦ Z<(Kԇ$[|r6pjWežG%yŸe%9}$/ hx@ѹS0o~_jw]n[xph%IċiTQ/(E=λ (Ƥy e] }3>Ŧ.a$4e<K !0OǮHȠxZQ8"M$Hᵮ3M>-NJ߃;}s{u&zeD2'G@0cg_kh(7#y:l9:m63a.8Kޡ3#TYOZx1"o~V]^oqçO!'$KdGA^MzܴRدdqRv><]C+Θ7c\ źzv?q!G%C2XQgY^Yl5}Q#Bm?&[*j8S>gȶ j )),5?d +_+ӽ y`s?o(^ ȧK :Jz20dҋqzVnVMum ˨bZOi%Dt^%-!3x-JMU$ jf8w9nhlUa9~Nֺa9 DnԽu> :0-k|% 5,U2gr>P.Crk!N9ٌU wET^X M={J&r4s5}UUv"g0{"˔ T$F6‘ODSf3V/K5t X}uY|6I_'yc5SwJ>DM70ܰ;l75,YuPĻIF1rR!.0a^x +d|CJHBR +}T}^Į sWu52+ zvGy>8]`SN}/)j^$Hqp(e]cxUA|e%>Nl>6HW1W9/}lM}ܟvFI];3qVۭ%~/>l}vV=[[/oS B?K Nppq%iyrj8콘}K  w`{ȸ߯2? a*d;m0$kMQhpfxY 1/G 9/ua@P!cj`Y-/1棐)v)dT}?#pKDa? +nQ/\gs[!d` N93k'-~)/헷m0gz:{̥myAY#G×(d޶rGsPBOI}zktm$}g>3U/u1β:9^HF:4S).K W||EuJb:g ̣wxݺ;^W=:v*S6)P>/\](K):r :?~'UHˮ7yE/r)>C= 'b7˷/#o~V܂.sK~wos<>D%Z>-gߏ33G5X!(#eB_/ݤx-grm i9~ vNR7cR%G(1*r\%C_Gi~γKYixz.ƕDr!5.K)pq4>nŞ_o2'7UWWrȿ|ᔼ2}fw79*VC~qf<آeڛxOo c*Bǘ)Rn1z#TYTa|zե%hKN,|^}\9Aق$<8.B]rB-P3ecV^<+%*'w/AE._d@>&HȠEj +7xJsrX MUjOBfvf8I^Mysf_bIϘp+XW%6/JpCGUՃЩp\d;>qxIw z!'v)`aKjɟbnJT_/WMYeICi"ʠ ܔ$irFHfvDkLQkk2E3/A$†*)H(Zēk*%>y憍q:e1cP”p.S",2In`]S'CtRgܔv@~ +nLW0!gN`".7 +'х((MǺ8=t9WE:,0[8Ҏ#5y< OY 6:eh+=⧹޷liC+`/,21*1**ɽ IuMi)t|r=nU-[:5GYR NI|/ +4Ʀ/L,LR1[-E%,X˺(Lslsd +JVt}o`¦L!0 Dyp!u0CbG4 H_AjŔBvYje1mp*h fL@ҵ$)\wjxM=lisOP +kET*,Mع @LP00^##m.-4i c WE Yx zmJ ?`|=^@$ ށK cU1KqɁ*蔆l-(Du`0 DLE"EBL +ߍ ¢=kj8@搾㲁TDX//#,a CK~ q0G \,X.}ƇMI,waSJNa +ǣȉ*c BTȜ +(ũ#es RnAg +޶,I!=7_jx`4?(6Pcc>;e}`nî>M= $ʻn]3MiY >hV1M\]Bdm`L <~+R]黱Rz:rv]9&'PI3 lv2ص91 rZVc4J 4VziȀ1Ǿi:= Spz. v[Cfg@g>)Nna'ORKEv҆Ge;a}ŃY$eN&NzTUt;9 kI +KwMpT nҧ (!'&.׃[ +rZdvl W݂ܱN6jv aTDɒO^9OC$??|u1Ie]6zD9:a_`_'@YuSqu~)Ė .զmu``ԧu tv~ Vy59'*~Ѧ#g>ص;9u׾9UxyjkuF<;Ǯ}uNjȃׁk1vL_4e;]C2)~v8 p]/y~+K_`TRIK6mihȞzTmw/+nq w/sH 8?|B¯a'G'oK%;Zx[Lr4.f=A`|\=8X_掛ÐYi|UCzݬ)YJ1#n}#dEQե?-ïYyk"c#wGO&p$aC~E )ʔ֔]ÇH8Jcrojm|0^ Rb.U#e${ MkQ$Q-JV(dk3jl\3\#%D"z|VצV;OBomv`Qh*ci g^ dkRu1kvo^cR D^d_d1UƠFޥPGu6Q(5Wdݗ6-s $I{/mpҖ`| ~!fgVZ MFEbcן^E YF +;7W-d$Ii>9iT򋱳ha/blMwMCY0 ׁg2,1rD㶢0r]59bٞD޵`ےMB;F>-&; 3xIDi`Omc \q'oa(U_l\Vѭ|%H]Ѯ5#&"hdrcK˚ Ňq:󛛘с8+pSWau@zria<079=}lK 2I#vƊFRis;eDԨ'X*;H|3 +g(ELJͩG5d8حl\aCu~Jjy`ԝ Ԙ ꮾ:ƥT X47evk'uAHSB0RʤKa5MMJT=*9m*J2Z:{P q6ؕⰽp:{kYSt"7]l>1)N* +KF+-~Dh,!fAϞ'q$`pGM]k +~e""%45 ^,+nN1AYs0,fJ "g\tBk3%S[ۨy4h^P=Wh(M*Hd;4*kDrx>uklQYT[0-Ah56.\GtMll*j%\WN55-vI vR:Ʃ"LK vSy%YkZg.C=OSUI'8ZJ&PqFđ!hU68P+]yȶLF;*É*ZUװ IX -r(h!hh)*;D+$zBEԛ7mfz-KP !xLbt]}*Cz-Ɔi(ۢ-bvѷeq )vi* +S=VYVOM b#EI,B7Ṽtx[bf#遖])fcx.Y"JH~q/Tz"iEѠfEM(CSN!K94)07sj~ +<{*{GlT8G!Eߊ.@(eY"Tقb`hrk1t3:@omHR=7C*c( {֜ګ3 }a 3j+'J(cDXk 1+K[nc&p^PQrUIVϔDj.D4qN3Wm笤cjKiU>|(*1mޚg&#"14ْ)h`Cv% ꫫH g/ Jů.gd5Q g E*( °C8p#1`c^*35X6Vy`10y ) K~kRp.5ǽHi4FH!$!!qsmJvAJJ>0) _v\8bgYOp()JRY>HA eL{CD|_R&J)q('A +h>R s?6-]+{=JK ƒ 'Y_΁5Jl}4_9Ղ%@ QNoL 8A#{"XؼGqF&R1$@ѫ1 J:T7,ts=vBUV(ҢE5_s._Qe}W;EVӾ ڷP/PUxVS?vaT}1f/JOkT +Ƅl+Ofl**#@3ٮn$S<g腴p1$ʆ!̈HQb?wgch68xk_'4U~͕_; s'N5iméFq1.Wa٦(IDxxOr+YwnLuI T+ϱycyHz"#ѶZ,ܲ=)agkeu&-\S#Qqder@ +65 +/a;MPRjHv|o s o8[B[a1֪, yg4acd;SJLezK 'e/< ^|k5@ɱ{Igh]qde}\NkFQkSP 9Sp@K6<!1TL> +`d`"t`+ΜE\ Vhth5ЀG.pZ,Z]G)ffU*ł@rⵥ b6^#ʔAJx 'gJ^ pxBږ^*5h9RrAl[ta.5\<7Ae">vxG\ GݗawxmJ.ATEsb(أ\>e5ܥk(ڢ`5jrXնJ>X8^RL +{s{(͡J;Ao JCn0lcxvd4C:i8gE8eOsK-]F0N(^)9Zͫp- ;ȷu 5)k~ ՞,]z.K6)3z)j7rlܠ1Ӹ7ڎ^t Ne=ܽ* ?FCR/$ TFH͸$sAIRϷdfpQXFjr` i~zl1hVJtZ/:_А_І~֖ׅw@~oM4o N?XgTjUkL Pyz&Ʈe5SKɬP*'Aj: d-NNeuU|VfDeCC!y"=3:A#"cCd uyBx.elZ;{]U|ʕX1GgV/yr +,R9@$sS1b>sn>Gpsbs!YqD^9h<ɛgRi>'hzة7"9?j +5g]JI a>>۔̭VϯY9#{$P Rͧyj?ޑʡ:Eю9ǜe=G0'J5OP,1F#N}I ,f}c)d0Q}v$펄|=֘M*@F]yê7xA'&[Xp{+ @Jqπ׉̩T6P.ඩN,($]džTIB|"&S#WoI"=sN`Da\ÅKTK+L*:`qڙ:aIbNFFlM +ZGE ] ~@f-@/4R&I; rK1uK_ET Ҕ*ƹZJ?`HdB{|''R@d_1K}$J6ITސ*A-Mw@uAXR_">hj*xgnvmn n?6J)$IOWS6[tP z^5ξPd![Ӫ* +rleK^(*p@3ⱃ)7KpQ_\Ckvux"TE(EL~J.P632JJm1f~_dwͳ.bw5hEFѪ*‹0z +˯4oHf=O6]KުWEkRI>7sHgd-O';qNm#%P0,%E3uɩΙ\C`{VJo=YC[^MgթԣiCE?g^St@V' Q)=7lg*Һړ5*'u9mb4L[ +[{lE>ag2p\Iܽ W]M], Ϗ`%wȍ~~N즐nVּ7sYGlhh˓o4nz/1|U|%d,!`I0EdɆdOf7roD^E&R–c6yL-^/?ǩo]TGKAZ> x;SUܰ{a@ukb--K[(X~"|lor>)ʕCzPZ\tܜI:'E8QӿzM;_.6Jo /Ȁ`sa0`& +D xyXY )su| +l$ObmCp(c5&ETz@&2 åv=e@.WUFqgfW;(N( 3]Q힎7{{8dAA*F* ` G1qp0ՉE75{ޯw'71D|Q]5m/'=Uu7rC{mbVf7wQ3?>i|nl5T8DLE+Rbǭ6O2ܺGZV4 Ԝj08U.$o~fvw× !60:MҦuW7/c'4ƊKZеH|W75\3дO:jXzm|=LwRՓr4}#F-Fe[DfD*e%M|.:9NĮ;li%HXBX񌓂~LSpO9w|xW +pBJ"JXΟ?A՛k 8 cGxv0⦭l `{.i*j~k͞m~1l)ﮱbgf29( Qiq5j&̦`}ՋCwj [ U/y+!o!'FH@v֢~SZeS}bz "@)9.*|ml#=RPL ++ JBpr5t`u3Gɾ{HѲjhٷ)Xbzf֕z[a/S;{{ɍSuK JIG'> Kۭ~2fYP}J1և1 crs`gǢ2o%"0 7 d<:իmHlEXmp\O/rLo'G +2'*8: x{V6BXTެ6|Lv{ ¶WVyX+|$CL$CѴi=6-6P&KGMIӈE%j\;%;z\J:)~3PS 6x`|NdID0ro뢛jCL O:ɨ Op xLƥ‘ +Ck3?!,֝/vnr95 S+s#:xv< PチgKT uN+ʰ,~X1N"!܄'yمvgy]ϓgMK9CYIjZ8 *8dzř]bLI {#a' eߢ+^|OGiuNvQl kons;͹;8xY5yWd$vI{KS7 wd/C5zf/#/>򹐛݈B8-y[U]40LXw]n:}{e0ڻ~V|wEBhP()g?S.+T7VpY("Db"@ݓ@=B{" "P] S3b _ZuϢD)JUśX ;x76HPm')AjXϘ45rnz؟|6Ds~`s{eUe)> 08 +#ET4J"NAJDo2_SD::{[Z[k>e(JG؅4[헧rʨO[KT$VW#*_?pZIJu +JcQ.Ǭ:k4ׯ=-gBϔH78^tW5` r[Y_WYk^}ׯyRz_„0Bye{9.L&c}]]U]˩gdgu:^Y}~3#'uc UdO q(LKc9])&ڡҐK \U ytZ?I ڌ<XnQGݼ?}7gm=f!k2V/jC+ tR7w}L˵CϷRȞ#(G*q9 leOSb4M~ivE4O<ߚ K4069Pl}5[,9LUڥ,:WB3-j+)0bc%)YGOINH YG:{] ]tR$ 27.Q 7-R_88khUtK^0+ aza J"3doǕ4Ay|qOl)-`,CYbWDM٧>5`o+EIrmn ε~Ƕg#~ӊ `@}8"~c7lM + QGm+j&Dnla~5QnHMW$i^Emw8vg`OcXAe`Z/yy>k}cQhz[oq9c Mf7n-͵sgՖbu YA$!ۀ54N^$w2]]WQ'K! ^*wg~tΏޔ3D@FĞFE,*A>$gG[GQahʟXOV}OMl8.5Yi!?cD·(JʱpYT>2j&cůi,*_ +c//wͫyAE}>F8ZƝ;Cd92pq> ȯȹX@YƖ`@s47c>~s4W܌4& EO&&Рj\_eYȉoÍK}qN9Ms@8>EFC3[56nZgh2jfF!ؕʼnDpZqDm]GjQ .Ylp44!%'ymLWsevuP"Jp~Ey9E{ +ށ~.v/#S𗳒@*tQf6m ړk(KfSE1CZjEqi1IWwayvzX^,7WNon޾]ݜ?'^~{psuu:-on^>,RKߗ[-\ ~Ea<$nu%^9r nխH} 5Ye4FAQI̱u9]MBQo+(a̩ݓȘj&ϳظ,&jJSvϥ0PB)6[p^&qi V<+?\uOg+&J}՜zYTiVnE_F]/٫VEt/n\i.Ì5޼!=&m}i!O%le-U 1miq8W~v?mpڗy`M<'ӫQ cZHA|+4q-c᠈}kYq]l,b^<`d%MXx\àX#4tuxõrR ]ƬE2Q%%.lWWU=d) ʘ(zYK$C}qAѿ#uh$dqӊ/w+OLIhGAзAt ޼6 *LH*G%a89(]U^uk 瘓K^4}&")s1YMǣDd}[a1)!sz(`yPb(O Y-H +d^KZEOYS1Ju@hj'$7$-:Qd UeL~әuG DZlwBt2[\Q+y6tTxGz!$#k <4$ ^J֡@pԋC`de V:g۱'N: ;5c Słrk:MlsֽT+B00`aPΒ54SݠÄ5 3"{i VImV"L1C/YCBN||'?d}ǢUˆ? .X8\8ڮp_ji+U8hpXaVc#/o>QDj5"wwgǗʆ` ePrCuu_׋x(cEXdmVW͹s3))wv #.Й>!!!g:11l_:f1jOnN=9Z̴S2RKE[5OeYA/Wcʱ,qߧ)j;O9ctm"~eq2/n[#˚r[l ȼP^ ~(]>6O .v_DžJӍg|qePxd5v'[>t$ݳ)tA |CihC|z,.|F!͈C2rJO-r9q!1yn:CuGU. +昁at;Zmo])֠B/`ًXAN獗.v8JIe;Jb Q"8lpC L' CH/ Mjw: Giz}Q pX텯:r!k'Uh t 5Jz8.aflW5gIVg" Omgsa*pBty ^%<7VlzfD,Wd'6bT~)ΉL3%n^cԲBIi48̡ @u8jW iG{oTmfD`)A͛S۩!;&j|1R>]9!}XmЃe`0Jzʦ[6@b:-j)ֶ5tO#kR>ޙP1ND{\i'xScȑuqz?QOr,,o"=}lSPxP^:̈AfĈvri|"bCZlCV/9כd#SL0zQ` EHb^6"%~YR2X)=gɤ_:|为|I)臭A gD5KK8Kx7*dgPЈz1'CMn&o9qN!%H=֢;\J"pShb5=zjNOhUBЛ/Yw~J0qvOl93y3Hcr|t Lg Z/A[&(\ju;\'ɴ0\u8|d9pnR7><&'19Ʀe\ +Y@k +3:0nx~$4oqP fχݨwJN ̵~:; Sj̭uFs`Z-\HtgZl!s5d>S˂%t7!)ڼCBέ+D#tlZoUuiHCVSs221 l^a pt.1,9`H> H H2/(WUr@epR3M6O>5۫!47RF^,ݵp,YMFQV.tL^8w!"ѣh k!NRiM/ +^IՖv;3WmfW$[#k.B&Tyo7&̳Y+@Lh4) eӓHs*N䶥jP&]6 i1v 7YDžRZ,քLJ #5} =tudjzGg9d z8sq;PyL۬=)MSs8v&&/p >H{ i ?U/p+XH]t򔏝ɔKaH8UQV. r:!x+JH%SHyNKnz`~HP763G]8S$~JACYU:4 掆 掮󙃺`NG&{4aPKgPVFcy'aS뺳es.,nZ ,bܦuA|AvbE+`d,.%G24f.HE<J w H'lg[Gܹ}iXr:e+Ns_Ye4xJ;q|,prԬ$ $ٴ2kl_QXJ(grMH~XM^AY,tpnYJu[2>Fth %ב"cZ,[13U${lO0~Bod<"ٞ}j8;ܯZ{٤~yGD>G%p\dB4˂9A8+/Qxoc|CN)rtws1.eʘMkb 8RϷjǣ/?.%nFI{z/""EWz~H P϶i*:]Z +.mzI 5KYo2נDBoxg~A6~LzB]-]x7U߶01x']&Y4"̳)-_}?} 0T +endstream endobj 283 0 obj <> endobj 375 0 obj <>stream +8;Xu\fl?=3$tM*h$=5lpd]!K=4A!0cEYkN6*n-lD(568'`$*\"/P0=EgkPHd5>o4b +#L'ST^I@4t7YuFe`,&^,2s\3`$I5P3u=X.!XbJTL<>c?b/L*2Ln/`%AQ`_[VBJ"m&),DP +ATq-Bo6l6^$Yu$TL'TAi-nRIMhV(n`mGq2IU-Em1Ei<8HhgF=Q\f=`=?-c1)_0o4' +bd)EWAr6@S?-X9T#=*C0A&dd!\MG0KO^fA:;0'_YU/p0QT`fB[jhoVuX't>l)VAc] +-GN"\MEZ-n2A"CdRflkL7bm)/aeN1eA7WVAj&t2)F?a>3UU4A1)dt>G1#b@e$L_3= +VLF3OY(kfpW$$\i%WE\d\(nN)qGGLu_M#?],Nf!"3p+#9]kis+Gf2ZfSbs#!j-W=P +YDojW4;&kNjq1a#Gn`gY*$*8M3fbaXNBQ0s\uK6B;71/+(ca@:@:I8"U5$,JW:d8X +Q*.038)u1Q\XV.j9R2El_fOY)@m5sd^'G=VoEm[]X*&8(7[2i_bMG+&.R^IidqPe/@X#qFiHk`sl#V +T^!^O,@aFcQfs5X7c6*4oEhLWLs-\/c_]tMu+ZKthOFpVru ++1:sYcD04+Y)C[VK<3>.DN&c^&KtV)SZmRMW4&p[m;`@%+dj&h1,6+a'X=*%qnFn6 +[O5&l+X1p.4(>2N66.2jqb]j +pADc=GuG.rNo*fWXht7kjIb,%*j/Tk&om;dA8!3K!rqD1M%Bh3m*Bf;O.IO&q[oe9 +OhM@NFZ]GsB:YaalN@/@U4dY\-l,>NO[O$L*CQ"m4-BFM&oT$rlftkV2+4OoIIpti +I")#9!!jQ@$i~> +endstream endobj 376 0 obj [/Indexed/DeviceRGB 255 377 0 R] endobj 377 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 370 0 obj <> endobj 378 0 obj [/View/Design] endobj 379 0 obj <>>> endobj 369 0 obj <> endobj 380 0 obj <> endobj 381 0 obj <>stream +H| XTG_*x›7EqlDSD<@xߊ7jPXfOܚUU;v*&fBGEVqrgIX qyư lG5*/5&F`ĄYiw;ݺ5QA8@@{EMkPȠY ?5%vJV +`1!уLg%b +p})FvBɮ'KD5"\Ҩ(l7M5aPI1RmLCU7jjmcޡyZ;:i"5ng{O:t٧ߺ~ڭg~bc`l&N + 3%vjܴ3f~ìsΛ`IK.[bթikfZ!;gc[m +vU{}{?SszA/?^ލw@F&E@FwHqνg܏ۓ}x?%CpX'HX;>|O y"O|OLs8|jMKB  UQNkK5L(IlH$"ckyjw{=t¬4pwufdl|cߙ&ϜpsƜ7L)SbnylDzuY_[ɾlښ| +umm`Clmdpsiٖ ~[Pl]lW}vma{^66ޠkv on؇}A PbG:Y/dl ,[KP+ɔm#KvH| "{d|+drH9*1^!HI9%匜䜜/E$??K\B)R,פD )䶔+gܗPH/<녪l:Ei64=78N>ܗq<'CU CTjU;Z:j9Xg5viTݑT'd<<T,sdL2QI@"Y,e, dy'X}J2d|*T; .E.t j7 &받8| 5àӞJ$ v3M,v1G&BA P3֤r}n 9jjP9 z^}Ps87܄#8r3yhAMVRx fGB;|o ]v2;A)zBPchQ 7>?0G-%׽uuﯾ AT#@}tP#oޅjRgZ44gPoɢƠ A߾n \>bHxDuw& ʗ|hw9Ktg9ȗjoSsD#h,_ .+ȷ:Rp fP,:姦q't׫44] 24W]t:9TϘQ3h;ԟ쥋s?x,Ul K oA =Ɣ7rbAFyM=338"ܛm:"1z> 1 F!@Wa`R6xYڞ"H0} rǫ*ҝpWv%߂kXB*SU +VfiyR]tp*x/@݋uO?a)c'ҙGq,#x,$L*P%t=xBrRCT%/Az֭ykr SsBY|Ej:s$)xT\Q}첇<=+==YoX YCQ}2b$J(+:/ڪ5Rxes +Ϋs;F{"t*x9]`B%!܅0~Jn!?H ` .ؙN@u+3w: m>hYߌW{L[?\}m ^.&" +\w14$,N+ʣZvԬ]隈 RGKd['E&$1ZQj"TMi[ TYɤŰ\ 4s99#b6d 2٠SWꌫR'Cjo_"dFaIUGP3A1͍jF͍pM +-E ?*KPpo>]Z{ d&t_adڀ$Еt)bnɄOK81)$ÂjK305ތv N{ʗ2 IT>)9Y-E]/P-Y,6kwabl]Q9zvGIU' 3xj0#Ts,&gU}JBki퍒U!@Fd8Yh5T7of1`N4>԰TBlF( )hu/Q4FJc04̯if|M^=-gP_-/NJRO`BgҫUJ-%8YӴ}ƌI.AhhTj,t4X&-Vʭe7VF 3tLƲAP+:^QBb*72OU d1f+V U: CgÖ.HtL$teҙ$P)3K+pr+'}jTbuG`Q=+}Y= 'Xz{2[ ĬI 2!cƠ &&7 4iu@Gri2Ӛ )蚌LW)=,G[Dvt#83?PHƎT^KYkr }NdɆƍE-܊,9ɨ8c|b"sO0$]J;i.BFw­݉z˜TN&J8gB-eo[93/=t +OCCdd!Sp1xӠ*8R<曛Z ź2yǟG))1 r+K3NFQ̔/s+PgZ4~f2GXn:aNWV.|(+F˒uE4$k~T2/e244 gV2mn_E̛Li۪jd3~b'~~~uǮ]77}wGK%G+iSf}]Pp[ZY5Uz9LLJ9rؙÞ lH D[Vxf9:]|waR8p*AǕ)羅}Gc>rA,iwډd!DRG,Hb(W[AE?ep-/,K{5p0eZ {1X4N7 +A o#H82ډ#'/o8Nyf+_#F&ڼ$պl o/_ޓ^$Y3~=^HrQ l-)vvu?~^uUu!o[8IV}GS;g38PXճ'<ǽSUD]AcSJ9PE8K5h/E<$x'D.6u1G^Uwuv6ѣ#D;6ׂI,p"wʣ%w)_*,|>G4M-BëKl`j .H-6WHGJ]?LW p^ItzKgaK?0 v0 !L)JLbJ6ei3IxдBN&ДifJ)C'$~*y M`\ZBK$8B%쎙xn-ܟ.*V.hvD>#{oXt|X?}x3ϾD~OaFUw̔9-Zv%[;Í%}#-?u׻nuoY3rە7~{b#3:_dKvt֫gVmݟbx10 +RWH2!Pv9dLJ<"-DǘbXFjfGB`X n/ưfF2iK=33c I6i7q&^Cb!dn;~FQCc +mT*ER$l[Iܮ)np9i.ec<ɽ#4qK{>٦G{ޞT;'?Nu+ɓjydzq`|~E7'2S>q0(::FW-#JHb/OĢБDA ߁qsK p?O'H"Y 7rs%j,1cb` +[c _`xK\) ckPܶ|;#ypTTN; !AU(7?hJoqȱʘHHG̭MР13u]zgoi8IG}zQA=[n̠k~EFjx,l]=j}QB<.p$O|6┝N:EE>(? jzE- FrBNMZW<qq@h2&5ck-i7X4:Fذdd2rBfLQ +ZIg#z߇g?"/o|A&OoWw{WT +pACrtehʔsOo쉰'}>B0`#Fƣ7L4S^7|BM}oңG.g"@9[uv:_~uߨy`$Lei@G_8 +5 5QAml 8[lJK$EXMy <#-x脞gx:8 J4Mxpi&Cۤ~ڣ inA7ӬhP.hpRrӥ&γ9Ҙ iOU:6vNN6AY6vWO`Wԁ)euLi`CanzMžE1 ڔ6eTtVUF\'jCH:f>{捻=#{<96Π*m`^/YlM&ɮ*Ɍ5\k]l.}Z|\7&_^.h{O E$hGp98r+qiD+= Ɋ4;gSSjiM;R +rIB)S@T#:Q딎Jɂ01fQ#Km6ӇØs5TXQ>IZgWTbY5]IDωD5YlٜF :QzaDM9KV_ r]h$!Mn6LjH=a8ZE & +1+{aR7ޚx [o—z6,0F!=PgۨZh/j.*,/U}{on]dC?anE +ɐP^ltJ`mFgJRTKRX 2deZCPd6mQ スݽ}{YǍ%zzۺtwK٦ofJؿ_ݯ_?Ѧ* !W [=ˆDh$H=@6h*XcZgk!!&^;ĩBcS9;-!N-*Xo,h_^d1$BH_'}E^6[!P m]G"OFDmMZ:H'@ 'H~zy8 $̯%&%1?Mm2" g' -mgS|rh8 |)C^9{f/57Ddh&iW8ljYev>ۿ ˁ]A~Ϗvxg~ !  a>gP;x'<io&O]\OsI2$g!g)*UU:YgφDX cKMֺ1nL_e6)+q Y]W V͂!5SP]aUvNc7b&9˟œ/O]r2.oӧ:|#c0&)]%"59 mT'J y82H\)zJJnUmJ + Ԩhf*?6+??3zED,"LRU x ji̘趢@ĄA+ItvF<0fpTZ"\t-E*?GR;`*7EHTt!DR2>,]eHuoVm4/ˏ[ۄ?-gkv(dfPh:r8v_ + {($kx8)TOhKk+4:FL8Bԉ\YDo4F/ہ_8]#=x?4' +^*J˨;2=7{w5q1)/) +yYfvvtEPV#JZ٥WX=9lP* +K_IŪ+(MjޮjӪ`' 553!d(cQ$);)RGWf[7ZVҗ?! 8<[4.d2R"%e0YelFJ%5%L}n+x"vXbU +GIZ8NԒ + obO9IA^HҤx\q0سqIIa%!B>7Ń"h' P PԌEPFd:)nc?>{>??a}G~uD(=}Ϻ_X9k[l =l\V3vm/;dhKJ1L +k|ar@1q`SɆوѻW i\:Ua dR-*[ DBT!}K-)Hπ S(U *Q:J\$DREimvL8TZ\o (+'xTA +Sx( n xD.f &!Dǰ1r30Uݪ c=E:ZdKv-f쾒}%(Qh)D!H%"~E]mc^lZb~To6'pB-f*G:KK"qdEZIXMJG%ωKF**Q>ptmɍ]<x<|^7p(3@:Ѐ6PA $&1 0?x Obk6heK1j0*U8E. v2=DGֳܰ)ը_?S^On| tD D;s15e0B\{^Z9`7 f c0## + (Ha?~zJG55Lj"7ĚFA~c 8s$RCV |ݺ$eFC^x(^\3ss*Gg'Z7 ɡ,L0S3X!*˶!僢,ۣ'T}6zY QKBZLKi9mi07 b 7X#Q[4N +׊Kp[x ywaG opD#Ah$pB#Ax$#sE\k" D\T9I"H~5dW>Y,EUp:F)bK=jQ٭B<69 XD;T`R +|%J`x(;]=C*=+ਫKfvYz77b-" ) :bUcD]IO ,i+aK0B(h9ޥ[.2v Hpޞ-0SVj 25`XONJlL^Z)"QL<@XNJ4;&M6Ĵ A;GO8,ر+` +Z^F(8<kC_?ĄS1U"D|.\#3$"IwO"pF8><^Gqh$Fv~;䘙3bD28L˔RT`J=g 5y˽q:r#ɮm9Q5]lƑ{qaHusany*:B^<3k׾;ZwOk[{]ہ+vCu냈#XeH2R>^юo5~^1-/Gffu^tqÈ?tNyy'HeQ1к<^EI(sJJClZpiNI6hCO=wCҋa!:JNc]tVaCe1[l>EQ1KJZPYT%ۣS8?:)rkdHP. 'Y.K4jg@6@# ijW)”2$IcE ƨRcbBxP&I+4+E':xF.tg^LlЋ:q]8!T/r,U|<4]|~ӎuaP!,h/Nkyf**ZLKqHQ%ٮx׼@'adB6Ua*7(Dfxû<:##rfk08!AN?7р:l _UBq#z& +J[(ƙ>tMOiN0$ )k05h^X eulxc2"T"܂?ocaIQai +eL\ܫ rA'soSֺJIr>? +9(PAG¯L,N_j(om>Svђӽ>6xKh|w"2 O ^HEwOOUGB!zLޅVjh҆npk')}sa#@jjV}a k m-? {M:ނ1:6h ]jc`k( }o_@}#̫-ԩ +*HyKVIB ]YaU@*P3 _ԍ/by?в_`\hz wʁʑ0:s]Dg9쿰g|]H.t>K?C+_.YTW; ARD,J )& +~ k$%q,- @  +2E:&vZ+m5Ue~t}޻={眛2(F&G0_}),v''r*s}R'4j *~lwpS6LJi_#8rsCC &Yyꄶd`dmX_}-yd}lYswa~>S_᷋MϹvQb;s]LḢ<]Cz,rl'Qr3yZ^i3b>h[\Cmz?3͟KyQ^2Y?[ΔL }G;ŦbgOa m+rLWSVЎW͊~u[k3ˡqߚ%۵d19;8tg&}J%Fm3gaG6~3PRĮq]~z9:#d?'eWcSJx9RT^N*7?< l/WrQ5Fc>2kB)&6@}I1Xdg}Ƴh>ۆ2,ul)X|Gq#ω enڱ#(5>c٧~Ov]vwse\Olw'XL6Kv ⨝?ۡEZV^gZJ r=wTMW`d *  \gEXa}|A档0m"͞NrU!}m5Jd;D%hN?Q9IGkk.dlB6{||C\DKւs:(D 1gh[ ۜBtr\q]uހ;yV7ρbUy~Xe\*\eq G(Ǯcub#a'6`#(VnO򽏳Q5װ]\Fr9/|trt)|1Ycv<l`Tm8\μ|=$ +N):G՗Ws\nwy:މIq 9z =1+.q2C.KK[J)=B{=NYd"ZB$g)X`c? +/zx_1-b +jp=0n7u3Ÿުh}ם+kW3GbcD0Jgx_#0R[e] +y?嫣Cyʉco_4 KN4ƳqLbͅgmy~*I%q!ØG0?+%&Qm I;;nʧ,/d{kn.l?6şo7o3'^o^?'GW)7'(kX_J~zL@:4(H> ?.V2gE={R|8|hgkH]ցw틿|'φxT{9e[ɣ%Y現7@@RVJ,+̿ʱ>kˏ8$te}N}ݣMSktlFhZCyAL |k3 }K>B|Y{M>cp,n)b%/qrAH6z8hq=0͞m/gkйk)rPs@9Pcv҃0Fٶ,rS q_%0OY!;ŧ$=Vsrʹ|RG66I#`wo2=W( <@ށQrXC=Ԗ~HmMEsڎnNOj m+Yp\z#]]ztg3 w^a>1a0= 9Vo\$I$I$I$I$I$I$I$I$I?8k3 KTdPZXZ+^g̀Wj-7ex|_`eOC9#~@wlwl/+tWr_>j &!܏٘E$qXbWncm!;lY,3.sf'$gPz!A_խ9beMQoYȽZ3hR')"ʶ\K /u9IJlX1 7 N uBI- +' 86AFZE]CIٖI#Ôz'fH%2u+j2)v{X:>˓;:Ieٴ^~;;;s5?YWTh M8~\!'?' ۃڇ$RUV5ž::IXwO ;'&c|jb5xČcHk0&}OeҼ/NMOvOBɉZ.tr4Q>yөәtjHd<{nc<5N͎A; q$S[6AKl)T|erX9BuJ Q``+q#I`Hf4KeG> endobj 373 0 obj <> endobj 382 0 obj <> endobj 383 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 16.0 +%%AI8_CreatorVersion: 16.0.0 +%%For: (Administrator) () +%%Title: (013.01.001,X5键盘说明书OMOTON-1.pdf) +%%CreationDate: 1/12/2018 2:07 PM +%%Canvassize: 16383 +%%BoundingBox: 0 0 842 596 +%%HiResBoundingBox: 0 0 841.8896 595.2754 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 12.0 +%AI12_BuildNumber: 682 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%CMYKProcessColor: 1 1 1 1 ([套版色]) +%AI3_Cropmarks: 0 0 841.8896 595.2754 +%AI3_TemplateBox: 420.5 296.5 420.5 296.5 +%AI3_TileBox: -0.015137 -0.022461 841.9053 595.2979 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 6 +%AI9_ColorModel: 2 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: -124.3325 634.0005 1.5 1789 915 18 1 0 82 116 0 0 0 1 1 0 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:0 -1 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 384 0 obj <>stream +%%BoundingBox: 0 0 842 596 +%%HiResBoundingBox: 0 0 841.8896 595.2754 +%AI7_Thumbnail: 128 92 8 +%%BeginData: 14542 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45FD88FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFF +%A8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FF +%FFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFF +%A8FFFFFFA8FFFFFFA8FFFFFFA8FD07FFA8FFFFFFA8FFFFFFA8FFFFFFA8FD +%08FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFFFFA8FFFFFF +%A8FFA8FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FF +%FFFFA8FFFFFFA8FFFFFFA8FD0BFFA8FFFFFFA8FFFFFFA8FFA8FFA8FFFFFF +%A8FFFFFFA8FFFFFFA8FFFFFFA8FFA8FFA8FFFFFFA8FFFFFFA8FD06FFA8FF +%A8A87DA87DA87DA87DFFA8FFA8FD0FFFA8FD1DFFA8A8FD08FFFD04A87DA8 +%A8A87DA8A8FD09FFA8FD0AFF7DFFFF7DFD05FFA8A8FD0AFFA8FD06FFA8A8 +%525252A87D7D52A8FD14FFA8FF52A87D7DA8FD17FFA8FD09FF7DA87DFD05 +%A87DA8A8FD09FFA8FD04FFA9A8FFA8FFA8FFA8A8A8FFFD04A87DA8A8FFA8 +%FD05FFA8FD06FFA8FFFD057DA87D7DA8FD13FFA8FFFD07A8FFA8FFA8FFFD +%05A8FFA8A8A8FFA8FD06FFA8FD04FFA8AFA8A8A8FFA8FFA8FFA8FFA8FFA8 +%FF7DFD04A8FFFFFFA8FD05FFA8A8A8FFA87D7DA87D7DA8FFFD0AA8FD04FF +%A8FD06FFA8FD05FFA8FD17FFA8FFA8A8A8FFFD06A87DA8A8A87DA8A8A87D +%FD04A8FD05FFA8FFFFFFA8A87DA8A8A87DFD07A884A8A8A87DFD04A8FFFF +%FFA8FFFFFFA8FFA8FFA8FFA8FFA8A8A8FFA8FFA8FFA8FFA8FFA8FFA8FD0A +%FFA8FFA8FFA8FFA8FFA8FD15FFA8AFA8A87D7D7DA87DA87DFD13FFA8A9FF +%FFFD04A87DA87DA87DA8A8A87DA8A8A87DA87EA87DA9A8FFFFFFA8A8FFFF +%FFA8A8FFFFFFA8FFFFFFA8FFA8FFA8FFFFFFA8FFFFFFA8A9FD0BFFAFFD1D +%FFA8FFA8A87DFD06A8FFA8FFFFFFA8FD05FFA8FFA8FD05FFA8FFFFFFA8FF +%FD04A87DFD10A8FFFFFFA8FFFFFFA8FD15FFA8FD0AFFA8FFA8FFA8FD1AFF +%FD05A87DA8A8A87DA87D7D7DA8A8A87DA87DA87DA87D7D7DFFFFFFA8FD04 +%FFA8A8A87DFD07A884FD07A87DFFA8FD04FFA8FFFFFFA8A8FD07FFA8FFA8 +%FFA8FD07FFA8FD0CFFA8FD1DFFA8FF7DA8A8A884A8A8A87DA87DA87DFD07 +%A87DA87DA8A8FFFFFFA8FFFFFFA8FFFFFD10A8FFA8A8A8FFFFFFA8FFFFFF +%A8FD15FFA8FFFFFFA8FD06FFA8FFA8FD1BFFA8FFA8A87DFD08A8A9A8A8A8 +%FFA8A8A8FFFD05A8FFFFFFA8A8FFFFFFA8A8FFA8A87DFD0EA8FFA8FFFFFF +%A8A9FD04FFA8FD11FFA8FFA8A8FD04FFA8FD06FFA8FD1DFFA8FF7DA8A8A8 +%7DA8A8FD15FFA8FFFFFFFD09A8FFA8FFFD06A87D7D7DFD05FFA8FFFFFFA8 +%FFA8FD11FFA8FFA8FFFFFFA8FD06FFA8FFA8FD0BFFA8FFFFFFA8FFFFFFA8 +%FD07FFA8FD1EFFA8FD04FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFFFFFA8FD05FFA8FFA8FFA8FFA8FFA8A8A8FFA8A8A8FFA8A8A8FFA8 +%FD05FFA8FD0EFF527D7DA87DA85252527D7D7D527DFD08FFA8FD1DFFA8FF +%FD05A8FFA8A9FD04A8FD10FFA8FFFFFFFD07A8FD1AFFA8FD07FFA8A87DA8 +%7DA8A8A87DA8A8FFA8A8A8FD07FFA8FFA852FD067DA8FFFFFFA8FFFFFFA8 +%FFFFFFA8FFFFFFA8FFFFFFA8FFFFA8527D52FD067DA8A8FD0FFFA8A8FFFF +%A87D7DA87DA87DA8A8FFA8FFA8FD34FFA8FFA8FFA8FFA8FFA8FFFFFFA8FD +%07FFAFFFFFFFAFFFFFFFA8FFA8FFA8A87DA87DA87DA87DFD13FFA8FFFFFF +%7DA8A8A87DA87DA87DA87D7DA8FD14FFA8FFA87D7DA87DA87DA8A8847DA8 +%7D7D7DA8FD057DA87D7D7DA8FD04FFA8A87D7D7DA8FD057DA8FFFFFD067D +%A8FD07FFA8FFFFFFA8FFA8A8A8FFA8FFFFA87DAFFD04A8FFFFA8A8A8FFA8 +%FFA8FFFFA8FFFFFF7D527D7DA8FD047D52FD1CFFA8FFFFFFA8FFFFFFA8FF +%FFFFA8FFFFFFA8FFA8FFA8FD05FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFD05 +%A8FFA8FFA8FFFFFFA8FFA8FFA8FFFD09A8FF7DFD05A87DFFA8A884FD06A8 +%FFA8FD1DFFA8FD06FFA8FFA8A87D7D7DA8A87D7DA87DA87DA87D7D7DA87D +%7D7DA87DA87D7D7DFFA8FFA8A8FD057DA87D7DA8FFFFFF7D7D7DA87DA87D +%7DA8FFFFFFA8FFA8A8FFFF7DFD06A8FFFFA87DA8A8A87DA8A8FFA8FD07FF +%A8FFFFA87DA8A8A87DFD06A8FD11FFA8FD08FFA8FFA8FFA8FFA8FFFFFFA8 +%FFA8FD05FFA8FFFFFFA8FFA8FFFFFFA8FFA8FFA8FFA8A8A8FFA8FFFFFFA8 +%A8A8FFA8A8A8FFA8FD05FFAFFFA8FFAFA8A8FFA8FFA8FFFFFFA8A8A8FFA8 +%FFA8FD0BFFA8FFA8A8A8A9FD07A8FD11FFA8FD06FFA8FFA8FD047DA87D7D +%7DA8A8847DA87D7D7DA87DA87DA87DFD04A8FFA8FFA8A87D847D7D527DFD +%06FF7D7D7DA87D7DA8FD05FFA8FFFFA8FFFF7DA884847DA8FFFFFFA87DA8 +%7D7D7DA8FD0AFFA8FFFFFF7DA8A8A87DA8A8A87DA8A8A87DA87DA87DA87D +%A87D7DA8FD05FFA8FD08FFA8FD05FFA8FFA8FD05FFA8FFAFFFA8FFA8FFFF +%FFAFFFFFFFA8FFA8FFFD05A8FFA8FFA8FFFFA8A8FFA8FFA8FFA8A8A8FFA8 +%FFA8FFA8FFA8A8A8FFA8FFA8FFFFFF7DA8A8FFA8A87DFD0BFFA8FFFFFFA8 +%FFFFFFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FD0CFFA8FFA8847DA8A8 +%A87DA87DA87DA8A8A87DA8A8A87D7D7DA8A8A884A8A8FFFD04A87D7D7DA8 +%7D7D52A9FFFFA8FF7D7D7DA87D7D7DA87D7D52FFFFFFA8A9FFFFA8FFA8A8 +%7DFD04FFA87DA87DA8FD0CFFA8A8FFFFA87D7DA87DA8FD047DA8FD1AFFA8 +%FFFFFFA8FFFFFFA8FFA8A8A8FFA8FFA8FFA8FFA8FFA8FD05FFA8FFA8A8A8 +%FFA8FFA8FFA8FFA8FFFFFFA8FFFFA8A8FD07FFA8FFA8FFA8A8A8FFA8FD05 +%FFA8A8A8FD0FFFA8FFFFFFA8FFA8FFA8FFA8FFA8FFFFFFA8FD14FFA8FFA8 +%A87DA87D7D7DA87DA87DA87DA87DA87DA87DA8A87D7DA87DFD04FFAFA8A8 +%7D7D7DA87DA87D7D7DFFFFFFA8A87DFF7D7DA8FD07FFA8FFFFFF52A8A87D +%A8FD04FFA87DA87DA87D7D7DFD0AFFA8FFFF7D7D7DA87DA87D7DA87D7DA8 +%7DA87D7DA8A87DA852A87DA8FD0DFFA8FFA8FFA8FFA8FFA8A8A8FFA8FD0F +%FFA8FFA8FFFD07A8FFFFFFA8A8A8FFA8FFA8FFA8FFFFFFA8FFA8FFA8FFA8 +%7D7DFFA8FFA8FFA8FFA8A8A8FFA8FFA8FD0BFFA8FFFFFFA8A8A8FFA8FFA8 +%FFA8FD11FFA8FD06FFA8FFFD04A87DA87DA87DA87DA87DA8A8FD0DFFA8FF +%A8A87DA87DA87D7D7DA8FD04FF7DA8A8A87DA8FFFFA8FFFFFFA8FFA8A8FF +%A952FD05A87DA8A8A87DA8FD047DA8FD09FFA8A9FFFFA8A87DA87DFF7DA8 +%7DA8A8FD11FFA8FD24FFA8FFA8FFA8FFA8FFFFFFA8FFFFFFA8FFA8FFA8FF +%A8FFA8FD05FFAFFFA8FFFFFFA8FFA8A8A8FFFFFFA8FD11FFA8FFFFFD04A8 +%FFA8A8A8FFFD05A8FD0DFFA8FD06FFA8FD1DFFA8FFFFA87DA87DA87DA87D +%FD05FFA8A87D847DA87DFD05FFA8FFFFA8FFA8527D7DA87DA87DA87D7D7D +%FD07FFA8FD07FFA8FFFFFFFD09A8A9A8A9A8A8A8FD0DFFA8FD24FFA8FFA8 +%A8A8FD07FFA8FFFFA87DA8A8FD05FFA8FFFFFFA8FFA8FFA8A8A8FFA8FD05 +%FFA8A884FD04A8FFFFA87DA87D7D7DA8FFFFA8FFAFA87DA8A87DA8A87DA8 +%7DA8A8A87DA87DA8FD11FFA8FD1DFFFD04A87D7DA8FFFFFFA8FFFFFFA8FF +%A8A87DA8FD06FFA8FFA8FFA8A9FFA87DFD04A8FFFFFFA8A87DFD04A8FFFF +%FF7DA8A8A87DA8A8FFA8A8FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FD30FFA8FFA8A8A8FFA8FFAFFFFFFFAFFFFFFD04A8FFA8FFFFFFAFFFFFFF +%A8FFA8FFFD04A87DA87DFFFFFFA87DA8A884A87DFFFFA87DA87D7D7DA8A8 +%FFA8FD24FFA8FD1DFFA8FFA8A87DA87DA8FD08FF7DA87DA8A8A87DFD07FF +%A8FFFFA87DA8A8FFA8FD04FFA87DA8A8FFA8FFFFFFA8A8A8FFFFFFA8FFFF +%A8FD25FFA8FD1DFFA8FD09FFA8FFFFFFA8FD07FFA8FFFFFFA8FFFFFFA8FF +%A87DA8A87DFD05FF7DA8A8A87DA8A8FD0BFFA8FD1DFFA8FD06FFA8FFA8FD +%1BFFA8A9FD1CFFA8A8FFFF84A8A8FFA8FD04FFA87DFD04A8FD0BFFA8A9FD +%1EFFA8FD06FFA8FD1DFFA8FFFD08A8FD14FFA8FFA8A8A8FFA8A8FD04FF7D +%FD06A8FD0BFFA8FD1DFFA8FD06FFA8FFA8FFA8FD15FFA8FFA8FFA8FFA8A8 +%7D7D7EA87DA87DFD14FFA8FFA852FD05A87DFD05A87DA8FD0CFFA8FD1FFF +%A8FD06FFA8FFA8FD17FFA8FFFFFFA8FFA8A884A87DA8A8A87D84A8FD11FF +%A8FFA87DA8FFA8FFA8A8A8FFA8A8A8FD0FFFA8FD24FFA8FFA8FFA8FFA8FF +%A8FD0FFFA8FFA8FFA8FFA8FFFD04A87DFD04A8FFA8FD11FFA8A9FFFF7DA8 +%7D7D7DA87DFFFF7D52A8FD057DFD09FFA8A8FD25FFAFFD1DFFA8FFA8FFFD +%05A8FD15FFA8FD05FFA8FD05FFA8FFAFFFA8FD0DFFA8FD24FFA8FFA8FFA8 +%FFA8FFA8FFA8FFA8FFA8FFA8A87DA8A8FFFD07A8FFA8A8FD1CFFA8FD1DFF +%AFA8FD25FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA87D7DA8A8A884A87D +%7DA87DA8FFA8FD1DFFA8FD1DFFA8FD1DFFA8FD06FFA8FFA8FFA8FFA8FFA8 +%7D527D7D84FD057DA87D7D7DA87DA87DA87DA8A8FD1BFF7DFFFFA8FD1AFF +%A87DA8FD1BFF7DA8FFFFA8FD06FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8A8A8FFA8A8A8FFA8FFA8FFA8FD1DFFA8FD1BFFA8FFA8FD1BFFA8FF +%A8FD06FFA8A8A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFFD05A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8 +%A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FF +%A8FFFD05A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FF +%A8A8FD25FFA8FD1DFFA8FD1DFFA8FD24FFA8FF7D7D7DA87D7DFD16FFA8FF +%A8FD067DA852A8FD077DA85252A8FD07FFA8FFA87D7DA8FD047D527D7DFD +%11FFA8A8FD43FFA8FD1DFFA8FFFD05A8A97DFD06A8FFA8FFA8FFFD05A8FF +%A8FFFFFFA8FD24FFA8FFA87DA8A87DA87DA8A8A87DA8A87D7D7DA87D7D84 +%7D7D7DA87D7DA8FFFFAFA8A87D7D7DA87DA87D7D7DA8A8A87DA87DA8FD0A +%FFA8FFFFFD07A87DA8A8A87DFD07A87DA87DFD04A8FFFFA8FD26FF7D52A8 +%FD057DA87D7D7DA87D7D52A87D7D527D7D7D52A8FD04FFA8FF7DA87DA87D +%A87DA87DA8A8A87DA87D7D7DA8A8A87DFF7DA87DFFFFFFA8FFA8A87DFFA8 +%A87DA87DA87DA884A87DA8A87D7DA8A8A87DA87D7DA8FFA8FD1DFFA8FD06 +%FFA8FFFD087D52FD097D52FD077D52FFA8FFA87D7DA8FD057DA852FFA8A9 +%A8FFFD0BA8FFA8A8FD10FFA8FFFFFFA8FFFFFFA8FFFFFFA8A9FFFF7DA87D +%A852FD047DA87DA87DA87EA87DA8A87D7DA87DA87DFFFFFFA8FD07FFA87D +%A852A8A8FD05FFA8FD0BFFA8FD05FFA8FFFD05A8FFA8FFA8A8FD12FFA8FF +%A8FFA8FFFFFFA8FD15FFA8FFFFFFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFFFFFA8FD06FFA8FFA8FFFFFFA8FD17FFA8FFFFFFA8FF +%A8FFA8AFFD05A8FFFD09A8A9A8A8A8FFFFA8FFA87DA87DA87DA87DA8FD12 +%FFA8FD0BFFA8FD13FFA8FD07FFA87DA8A8A884A87DA87DA8A8A8FD10FFA8 +%FFA8A87DA87DA87DA8A8A87DA8A8A87DA8847D7DA8A8A87DA87DA8FFFFA8 +%FFFD0CA884A8A8A87DA8A8A87DFD06A8FFA8FFFFA7527D7DFFA8FFA8FFA8 +%FFA8FFA8FFFFFFA8FFFFFF7D5252A8FD09FFA8FF7D7D527D7D7D5276527D +%52FD047DA87D7D7DA87DA87DA87D7DA8FFA8FFA8A87DA87DA87D7DA8FD13 +%FFA8FFFFA87DA8A8A87DA87D7D7DA87DA87DA87D7D7DA87D847DA8A87D7D +%FFFFA8FFFF524B4B7DA8A87DA8A87D52847D7DA8FFFFFFA8FFFFA827527D +%FD0BFFA8FD047D52A8A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFF +%FFA8FD1DFFA8FFFD08A87DA8A8A87DA8A8A87DA8A8A87DA8A8A87DA8FFFF +%A8FFFFA87DA8A8FFA8FFA8FFA8FFFFFFA8FFFFFFA8FFFFFFA8A87DFD0AFF +%A8FFA8A8A8FFA8FFA8FD16FFAFFD08A87DA8A8AF7DFD05A87DFD06A8FFFF +%FFA8FFFFA8A8A87D7D7DA8FD14FFAFA8FD27FFA8A8A8FFFD0BA8FD0DFFA8 +%FF7DA87DA87DA87D7D7DA8A8FD047DA87DA8FD047DA8FD05FFA8FFFD06A8 +%7DFD0CA8FFA8A8A8A9A8FFFFFFA8FFA8CAA8FFA8A8FFCAA1FD13FFA8FD06 +%FFA8FFFD087D52FD087DA87DA87DA8A87D7DA8A8FFA8FFA8A87DA87DA87D +%A87DA8A8A87DA87DA87DFD05A87DFD05FFA8A8FFFF7DA87DA8A8A87DA87D +%A87DA87DA87D7D7DA87DA87DA87DA87DFFA8A9FFFF7D7DFFA87DFF7D7DA8 +%FD13FFA8FD07FFA852A87D7D7DA852A852A8A8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFFFFFA8FD1DFFA8FFFFA8A8FFA8A87DA8A8FF7DFD0DA87DA8FFFF +%A8FFFFA8A8FFCAFFFFFFA8FD13FFA8FD06FFA8FFA8FFA8FFA8FD17FFA8FF +%A8A87DA97D7D7DA8A8A87DA87DA87DA87DA87D7DA8A87DA8A8A87DFFFFA8 +%A8FF7DA87DA87DA87DA8A8A87DA8A8A87DA8A87D7DA87D7DA8FFFFFFA8FF +%FFA820524B5227514B76FD14FFA8FD08FFFD09A8FFA8A8A8FFA8A87DFD04 +%A8FFA8FD05FFA8FFA8A87DA87DA8A8A87DA8A8A87DA87DA8A8A87DA87DA8 +%7DA8A8FFFFFFA8FFA8A87DFD05A884A87DA87DFD04A8FFA8A8A8A9A8FD05 +%FFA8FFCAA1FD07A8FD1AFFA8FF7D7D527D527DFD04527D7D5252527D7D52 +%527D52527DFD05FFA8FFA87D7DA87D7D7DA87DA8FD077DA87DA87DA87DA8 +%7D7DA8FFA8A8FFFF7DA87DFD07A87DA87DFD0DFFA8A8FD27FFA8FFA8FFA8 +%FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FD07FFA8FF7D7DA8A87DA87DA87D +%A87DA87DA87DA87DA8A8A87DA8A8A87DA8FFFFA8FFA8A87DFD05A87DA87D +%A8A8A87DA87DA8A8FD09FFA8FD24FFA8FFA8A8A8A9A8AF84A8A8A9A8FFFD +%07A8FFA8A8A8FF7DA8A8FFFFFFA8A87D7D7DA87DA87DA87D7D7DA8FD0EFF +%A8FFFFA87DA87DA87DA87DA87DA87D7D7DA87D7D7DA8FD09FFA8FD26FFA8 +%FD077D84FD057DA87DA87D7D7DA8FD067DFFFFA8FD1DFFA8FFA8A87DFF84 +%A87DA8A8A87DA87DA8A8A87D7DA8FD09FFA8FD1DFFA8FD06FFA8FFFD047D +%527D527D7D84527D52FD0B7D527D7DA8A8FFFD06A87EFD14A8FFFD04A87D +%FD04A87DFD04A87DA87DA87DA87DA87DA87D7D7DFFFFFFA8A9FD1EFFA8FD +%07FF7D52FD047DA852FD08A8FFA8A8A8FFA8FFA8A8A8FFA8FFA8FF7DA87D +%FD05A87DA87DA87DA87DA87DA87DA87DA87DA87DFFFFFFA8FFFFA87DFD07 +%A8FD12FFA8FD1DFFA8FD06FFA8FFFD09A8FD13FFA8FFFFA87DA87DA87DA8 +%7E847D7D7DA87DA87D7D7DA87DA87DA87DA8A8FFFFA8FFFF7D7D7DA87DA8 +%7DA87DFD05FFA8FFFFFFA8FFFFFFA8FFFFFFA8FD1FFFA8FD08FFA8FFA8FF +%A8FFA8A8A8FFA8A8A8FFA8FFA8FFA8A9A8FFA8A8A8FFFFFFA8FF7DA87DA9 +%FD04A87DA87DA8A8A87DA87DA87D7D7DA87D7D7DA8FFFFA8FF7DFD09A87D +%A87D7D7DA87DA87DA87DA8A8A87DFFFFFFA8FD24FFA8FF7D7D527D7D7D52 +%7D527D7D7D527D7D7D527D527D527D7D7D52527DFFA8FFA87DA8A8FD0B7D +%A87DA8FFFFA8FFFFFFA8FFFFFFA8A9FFFF7DA8A8A87DA87DFD04A87D7DA8 +%7DA87D7D7DA87DA87DA8A8FFA8A8FD26FFA87D7D847D7DA8A8A87DA87DA8 +%FD087D52FD057DA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFAFFD0B +%FFA8FFFFA87DA87DA8A8A87DFF7DA87DA8A8FFA8FFA8A8A8FFA8A8A8FFFF +%FFA8FD24FFA8FFFD097DA8527D7D7DA8FFFD06A87DA8A8FFA8FFFFA8FD1C +%FFA8FFFFFF7D7D7DA87DA87DA87D7D7DA87DFD0EFFA8FD04FFA8FD21FFA8 +%A8A87DA8A8A87DA8A8A87DA8A8FD0FFFA8FD1DFFA8FD1DFFA8FFA8A87DA8 +%7DA8FD16FFA8FD06FFA8FD1DFFA8FFA87D7DFD19FFA8A8FD1CFFA8A9FFAF +%A8FFA8A8A8FFA8A8A8FFA8FFA8FFA8A8A8FFA8A8A8FFA8FD05FFA8FD24FF +%A8FF7DFD04A8FFA8A9A8FFA8A8A8FFA8FFA8FFA8FFFD05A8FFFFFFA8FD1D +%FFA8FFFD047D527D527D527D527D527D7D7D525252A87DA87DA8FD04FFA8 +%FD06FFA8FF7D7DA8FD19FFA8FFA8A8FD057DA8FD057DA8FD057DA8FD057D +%A8A8FFFFA8FD1CFFA8FFFFA8527D527D5252527D527DFD05527D5252A8FF +%A8FD07FFA8FD08FF7DFD06A8FFFD09A8FFFD07A8FFFFFFA8FF7D7DFD04A8 +%7DA8A87D7DA87DA87D7D7DA8A87D7DA87DA87D7DA8FFA8FD1DFFA8FFA8A8 +%7DA8A8A87DFD05A87DA8A8A87DA97D847D7DA8FD0CFFA8FFA8A87DA87DA8 +%7DA87DA87D7D7DA87DA87DA8FD077DA8A8FFA8FFA8A87DA87DA87D7D7DA8 +%7D7DA8A87DA8A8A87DA87DA87D7DA8FFFFFFA8A9FD1CFFA8A8FD26FFFD04 +%A87D7DA8A8A87DA87DA87DA87DA8A8A884A87DFD04A87DA8FFA8FF7DA87D +%A884A87DA87DA87DA8847D7DA8FD0CFFA8FD1DFFA8FFFFFFA8FFA8FFA8FD +%1CFFA8FFFFA87D7D7DA87DA9FD14FFAFA8A8FFA8A8A8FFA8A8A8FFA8FFA8 +%FFA8FFA8FD0BFFA8FD1EFFA8FFA8FD077D84A8FD38FFA8FD1DFFA8FD1DFF +%A8FD1DFFA8FD06FFA8FD1DFFA8A9FD1CFFA8A8FD1CFFA8A9FD1EFFA8FD24 +%FFA8FD1DFFA8FD1DFFA8FD1DFFA8FD06FFA8FD1BFF7DA8A8FD1BFF7DFFFF +%A8FD1AFF7DA8FD1CFF7DA8FFFFA8FD24FFA8FD1DFFA8FD1DFFA8FD24FFA8 +%FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFA8 +%A8FD04FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8A8 +%FFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFA8A8 +%FD04FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FD0A +%FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8 +%FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFA8 +%FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8 +%FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FDFC +%FFFD87FFFF +%%EndData + +endstream endobj 385 0 obj <>stream +HiEpDpg&E&DIwv< +ePpcCd»,$ߝ|Ԍ!]%uۛhS?/3mU#/Tw`}eCG~9{vMG@py.uߚ߷l+d jx2W~CY5vL"/2cQvwa`<_؝Djϐ gfQzgv؋;;hyk'kN{al.&+g4_@eF54%1>ˀ*+Z;Sb$*ߍSHTT߬Z^o39F m# O-PWhFκEOy7T#sTd,@H/~#V珢l6Y'~pʞu'x-Th.A æz K"!YC`EHW@mC'PWwG4On-:jg^W)nn|h%^iC-2smT*'}]g~A=*%Wb3i9'uXE5݋>Ч7p[HgѪʨѰ噷wS*Jz-(Ɏ,=Rͷ{Ԥ||,D/2"z%"Z96nN&v4&J7ty&g:YgiNڝ,s~$?K_@W!SN 9I4mSҨ=7TeugA|~u |7Jkڬ)Ǧ-v*wFGG:!|e`8^~Kēuy&H>kugN3QzQ'7 βL\ʦBeiJl9+v'#&E| ?G<[nEEns k]b+OTyYhPɞVYkr]4}A#0Vqx._@?s@ugeL~7A!~75 q%#>@]6 1rk%AFQ:,:by.lT|jt{Bx`Ӱd?fc{'ZeÎuQd#PuB |gcψG(Ay +d~O Өt+?7 +-pL=szFwv3BesY.L,W`xj80_D5IK@UdyS 7d:{N C:v)@} y?DG];TTi_G5vXzX1ar8=R ZgM!%i6wtֺ%xFe&*'8`7yMRa-{<\=9֕™鎶E9kHIf^Vѯ ggw.3$b?V>ŶGIz]t~RCfji5.m%/wW/RW?*FU#5^[I>a`l'oxr[&qSױ1;J Rpc|Lj[[HR7]t3ztH%N~c qSM8WZooS$ hZ1#}ǀ6io# +Ўuva7[&t0BWN d}Z+w8qt*53>u IFМgk +Q[Θʮhe֤\"刂"3 do8}v2HN>c~<|nQtq ~ U1,3JRm*Pi eʣyldL՜6Dڃ&c2r:C泀1$\E}={ٽ%~?`'KUK^Բh9q';3*KXcDl6M2%NQa fUS 7[ n樍jՙw]la̞]qY=m?p6[_oæuИOͰ;ٽI+sJ{_VVm $ij7eL4~'01B;w1v9F +G#|B1=D&XJXK1?@UȀ2!@D P5@t4\גsa?q =/.n(Zl\`طRVcl1JM Ier?hل%T’#]e4"ڻA)ӯNKq3bBκgEcʽ5F;OtO7O,O;F !U{Ru|MzJPqŹ@&{%\\k|;.צ@ĖbfwΞnEg3IElLu@mY9%!k1Dݯ㛪axV@ #UM/j| hJݮp m{`\V@s2FFuuES9.֧I.m_aA{Uk{:X[_ Ȳk!Kihrf98R1?:-Ɂ%ϚNq34 ~ԁ]=JzQr,GE`V}r #ޒ͖ffUK}Gm 3,Q/zR13mhҟ?ۣh/jbɰcPI%4n;Hla{̍CnuxK5W.2TrQ{%U $2R= "څNPC߇k0XAjyEI .6v/Pq6|V$ug|f.G܃q!mJ 6xo!a=7ܷ'/T#^=ϭ,4+z:scS犂Vq:<5q=s6_>}{;aw˹ M \#T=I(46*\җ aL7q+rOkRɓ q.B%;2*oh/U( VQqkwAQT)SvnfIɩ8R@COc2T=l) ACէ(%=ڻX6|F][a0i-]݈8PVt9_%te4*={f罎#WK`bɧZ"YtJnYȷd`I(瓅S{3H}H]jX#VhG;]~fbՌ1-&=KOIݔڌQ+7OY:J2Rzo|]jD4ρr߅]h>#X88Zϳiz{jKo֧ ==gs<6Nqə#_1ڗl'JNY'\/nr>VST#=k߼gj9 +kS5P#xmlpNZ`OOx,Tm:uZYՂhXA&ؔkr==YcJs7Q|z-aa@W6фSZu>srhQ-wdlwn+ +4(U_ͭ 3CXx**6ĵP;vD dZසSiѭqL +''o}3QYȗ;v?W'TDMGt{$i69Me%2Ph7a 7xtW8pan,F-}:w<)?10]Ӱ~{#0&)VDV&<$,۞!k\a,_Pc<~0R{2k" c&7Qu>2߹xaz7۬As}VӒ`OӮ^dOϏDZ&9LKJh4K*2r[h7oc}gbvS_zs77@< 9a5cD7**.x36]dC1?F,8Jb 3{r]Þ@2,ADeNٮrͦHҢ +١fa8%"zd#~DpK!!zWAo|q J~VaLS} + ST/%E`?5PPA>DGBDx\jNx|gp9^ "=,-^[T0OokxoUMR'g%_'f_Jdy* rTXE MHS0\>>9 =*6zgw}PHʩ~uszީxSJ|^5fAJ.nǕ;5:ӕ̈́xơ{o8x6|-hd復tռRaO^]7 ?WC7RPՐ*ϱSe VA9Л>ny ΄Dbּϋ3!xV34h_FOT]l܆qK_6]/g^);XyozJ8VsJTize$+A~]ltoTdcB^99q(/&U0 +sBPs3WA@%ܻU[wvyl{ rZTK^Wܿ>ƉB5>@<hvxИ5`å&8_K +hUiUjaW^Kq.^8L"&,g&Lh_6__AYwb `X\]=)boRYJg4 2dԂ~ROnS( +qVGжp/m{w +>4.J3PR09fX|U'Rw~}rs}#;ŕdX#KÖisۖ2VS +0L O&:m;5!,}DWx9h.1He|Rx^4M\s/uCыΣ\(Z\]eeo׿_5QfWJ{Wnt15t=p'gO١EȂ~ +e$ ӎxE&MGRh8Ry,[RWkַ4ROA9*?VaUi9kP@.tz}P)܇)Zuec5}\[9o®gqCJuX-}m~DE@6zXyf47,G0)c&!g'vu~76[&~`^{n沪yजÕmBMxKD=U\s%["xBԨ Q[?a&~ L&$UV޴Ni܊ 3,5Ɲz=U}[UUed0$^\Ejb$" &l/- t+Z$*ڶ9uw .C]=ͺdV/]ʄjt&N$kI '!=s̎٧,ʈ3Bw~K +Q7;`rR[;|vKޢ'n1gaVSCF)2zǁpe!qԯNvghz*h"@Fp9/~ w 8fDx&3^CMi1OXV)sv2vUfUҏ %,g8!!y7gmqk?ritW5o+ۤ3j) Sr( m>|Vo?t=7f}bo @< 9i/#xsӠ5a@́m_u-e=ZRXF?8̯JeN2V#t- ~6K 5fo=+i9x +I'+]ڜ1_p_%>"BV/w90Uwіz.EǖQP@d46FHHEd-+A}ήgӖU6Tdd, +AOCV$"_Gا Gϙc=?v_ R#(#6tATo D=A-jXyy];/hp-aNf(˗rӌ?q?L̶4߉{ut>ru{_ٝnK9z_H@3pu渷 ƦnA^%W4_2kn@$ WIɓao-Z} 79C[R&~4?h/fE-E@EAQq8LI"l gb+x0Ŕ}6!q<^Gl~5pP_OxMkPaMTg2O_<,\aWP},=MTgڸK9ת۪) -Iz#zmB,_z3)kpfNXP͑c)`Eb`1{0/'dR 0槑_LK=J.?/V2SED$̰ +Эa.)sBX ~S3w9FC 1 ln$`ًvSzm79!G>=ёfcR@MJ=W[Bd) %<܇y&ZIY:N>f|AkD`+ j5 +.M}rƋU\BiåNԢyUCS~»[70겕yAxNpC7։% !rv\čyzOZ$Vd$j +0)awY4t8&ݲC} +۷w dv:b҉bP88Z.j1EǰaQR>'׆6=o.xdyc +}ݢ(y*?.ҥ=U=j! Z'Q +.nE"g#F:~0C쟥&TdQqGs$Jէ62N_UW1jpZ%PJ^}gRk&y D3cϩ S}#_瞰M]c鼷}G/)ێ4HL|!+ G!c]X*)PNcZ +_k4 Oi۹3P;[!l]ɭcYszt~4nO]zi̮JQ./15έ@5.t\=2L̓Oj|Ҷ[X4Q@y QF:NdVm;8w;FC + j51K+WF>{9!;|d>f&!?{~Qi^܉0π10^fMh-+*==U]=U s*y?`Y +zS""@F[.^bN +zd^1lWDnnl3p-v=;i`n17ba^DѬ&D0SMcVynυdtc\((-]Oy@q{n%w%rA- 42L bٰgz΁_,eX.Z{ەEHkrۇ%KdsӒX@xЦbI5P rUŠړ?V%@׀in" ܹyQ %b.Rڬ#b]([6eq AiNlFr?P_~{NF1xn +=:ٕ\uƽV:uޥ>vU? tYS4y5m# d::g*h T' +zgyPU[?6iqH@StF/+e~m5x/*D~щ[S=[`ۣKsZO1?GNEDiIw87uij7UeR>M Hg򰖸 $n%r?E +Mx ջĜ9d8nCDfŒlF|Y>:/#5Ch +ؠ[UPPwfNH.pIHL_HQcD 2,^9@?lw~X;Fl& +n\) BnJBrr̅xY<9Gxy0 OE^9 gx6Q7X^:]*]X) O[o޴|Gͨq(UEI>%)i<72 İgSxt"EC.T,|endT^i +]pN;55F"G­۵H-f* ;"/ޡ Knΐ&Ό"5C:b5 )F9p}=|-RGO +<%[J-.FL@ 契՜JעC)jF}Z'*.;i0.]830TO^Iþ6CAnU$Ż=eSgctkA)+Rke牶//L=]38ʥ> @~M[3-8-#-~poP +qN]]ߞ,;e- l +ҩ@ + &SˬKY$ ŝDET\PK}+qaAd?igtx_.|0AFFFx}e0VT4I +:ݯnFuž~*ST >nk9i=AcBN-F+~NO0L 39*Jz6Eˀwn!D|tz:z3m"(Ϝt# 3*^nY9ND>8\Q\Nր]5s@ G*,I[SZ-B7:O[lȆ\&l4g1N׫ۘ3$j<"KB=-׭?/%T4(b* A -X=| 4E+ʶݮ +]bz'Z=&aj]@\>|Iӭ!~6zRmM.N@Dc]@63z1Gbrɳq] "W<- y9DG0Lnk@X?4g WgEڇp` =F z~^[v Y:cqŲhX|l{AB6eOAB?0' + 65G,@mN^=.B +p B(Ij%Mb/}xvnaOplp&HFC^wgWiGEF9oäƶ\ ;o4T~gRJQj|"πPUGb +HGXO)X*GBѹj'iFX.Үad]֯3o"@Ϩx{T?lɟ 2 +]?w҆KH +ᎸAݹ˼CJG}bj0k0ht,ndqYWEt%kDIKg&[YGPur2UmGq-a.~Yj +c^c^I[u=c3KIۍ՛|\]4>gk.S逫KS+}6} [?K6,U{T Ja}⁘tbٺ4jIz. h0r)׻HGϕe<gG+anW+~aOTiL Bh H\ +5xb~xD[WVǽ}cٛ//2S>|ӵW֌q^}Z˖IJ8TJKr;?/IpzH70V~rTK nQ@OٶcݵІ; \ୁ>պ^Lo-%I}9Tn4AzꡨKUR*PxtOԂBML +Tm2DH5T$1(%P՞2jmJJ((<#gT[XXhj0(?X_*&DJ8EV K-/ ٜc8o*)T%| +[ďRZRj.NUQϖL/TG^ǛIijfyx):X-2l}Y$|Drg1x]9DdzNUO)3:@/AV A"ZQ~y2 #G:UaQH ߕԉ3WM+f1i\e*Eoq(,)YDeApTq;nDGwG|X%9xXwU$,@Cֻ%SѨxٖ6I6 [ A;g?)rNu91cv`}$8g]D$kl;o_>Օ(RڹhjJENmkPEUI-/ +?N~GE"j6*-긔cA|gACb`Of֫=6x6W|?$8CIM647@JicQ\=^}+4H<~nQ񫷼ў77OS!ÎZ2봍GGOCv&SV<RK#o ӮR&$))7EέInYGQ"j,O\r,RlvMN,yW:oK|Whl)`=S+uME%M|QT W&+O?"NNi歮Onj`.?q99ID0{h6?l,XJs]7NMqЈ/a|t"G._+*)Ͳ[ }n}O5$0p܂6YmQm(WEڬ?|ڜC+P*4> ㋟v7 Q=9j;>ԃxE-1T幬Vt**[!iGwr8BW@b|+@UW}? +ͯ5_7+of:TZ':ϜE4E}:'ؒ)Ŗ{_lp۷^Sа2`ӶİȴUH A8_ykOamdw!@B |8VYUG +HaRR\Ĵx[}nn+̸^cYKŹӽ'Q7b{ƠJ !~Kh4|^&2 ۸gDlEOe#cAfCMOHLD"q8u0Wj{`>d.mt'-zSKQzǜ1e(_OlyXp<\˻ԭ}_}F" URE4^OYysZ;ΞǶ'њMJy@I%^&!A !" p g\BCP7" %a8h wD_>\xQOrY?9 U 8EکEhX*=UcjӻzT"Ǥ1f 0$!D^փD[0͛h\N}Pi \^^M[!t1:W{gT*T@~T3}Ɇ@F{h biGo*4B !pp0?Xaf<%}V ;h<?(48n.P- Ҫ<:vaQ}MT,I_ck_zS>嵣g! 04d08aliFK>K#eԧۂ=wvuyA~/{To#hMKW  2Ek|4-Q%fiL=qݩsuQg!o+_*:W -cDq=P2@MM kyDPhvT9 NRsĶ5N*M%w׶x\|tT]VY{Ȍ[Fae/8(̿V;;*?U )fdÀ- )Y{OɾnJ8sTJTfE[7h]!:Wxc QTBgr)F 8\ b#rdBRi<6 n\I|E\Vr-Ƅ<~|>~j>ێwK?{5]u0ٍIɻE5wlZ_{N;PNZ(QOCO+eXrBNj5久8/P"7P|١mW_3mVYuybpzp8TQgp_9/aHK[vܒ2LIpY7֜GiSs]'?LlzvO҆>^6Cs{v7jlޕ +˖Sc$eٙ[FAMT=O=ߨxC'>owG%W}x CSDqڰ̛E)s5dȼq0|A]T +| + ++ Ø,t=N?鑫S2AMaF-Pq<90~}G7n0^f˪jIqc/ +" +(`Fݨ:_A̙R' I =-}N 0i ur[/21O3ř,zʦkIiQ6Ygm}VGjM70ѬGԊˬfPZn夳KE V/%Hk!DTٳ4WH-'qw?B +[6HE*k9ƐA::,5 +8Ksj~@U&6yTq\_BYOuq)m[yG.\yyGC.V!ҿ}1Fm2 k%*?Tejq +/< ٿo~Jx)@ +'v`!dpPR8 G_h^?'S(Ρ 6' KQ7j2m^k(R?m.ck - Nbtՠr^3)y|jfZAB(-js@qΙgg>|aPIhQ1J +K˸!ָk H~S n|?b/~ZjV5m4Z?_t<}͵ ֦C;7t!jV[m,?-)+0+_T-`ǪˊǶ]b8_fߑGZ!zVҭMaZ D. MS~>*|R^Č>c\3J.`O>ⱗ:uga 5A QHkΎ9~c n{Yf}W]%}G{eoR +3XFx pb㬰Wa;`qEdž +ZѣfCU-/;2 `"njc?:<%}}TVG?ZvfsZYe,wZO1v?Iޖ{ F:6=961U*xxE.ϥ>Nݸ3Lc #5ˎTaBME:r|вPX8,rgBFO:1F?Z6W}>1% coN7:):#v7 ٯCS'e|9W˂7 RaNnH#Q3|Y0GED!]bhspMxsHEDPfHgY1DJcpyk8Awﴏ:;;5X)Oez{XDteӉf=)Vϩ\}"0 (W{1ݵǐ$V/ ЧLmכ}L@ne|3@-IIPhJl^-f?6Iss fOF\1h-$v0$XAK ,ߠR;ѭ;pɰDH#A+aܚ9߱i#ݞU4Gvߢ4AM_ESWEqP_& 9DFլߢkI6+h@@Si9Uj׽e^ѓtw%R+FMzVi߳b|5ܢ0rp^N,|=", Z/< 핍i>a (2 " (B1ѽA \\7烲hp~RHJā /g4 @UUZ*7/!<@rK3c] +x F 7 ܽ +ʎί'f+s@{T>U3*rP yCjp7 +T?UQL.(AX(Oҡ!7Yx>zM{tvk\ivywmQ"#b< hR&P3{9QJ+шӎ9kNI5tj{9 ,'_GWM6`kЯ2OњUbS gxu'ynW*yަvZ@ G5BǟRv <'pv C*kX`=؍j!'CwmNo?@1}Z Fwt2g@,PAUh-v% +x K~T?x{0Wg%t&p~/L*Ͼt;TM^p|,Իl9*^mm{3T|zGϲDӿ~M.=d93E=A ŭz\oT{$?hbw(gX²X&} |lm;b,wHma .-}`r6%lC1~3ϱ˫wn6Gxd7IV3{᫁r/kU]Wv~}8#jtrtP_o~,*Pl7Y+U<٪MCr3H=|dZ˻zFnPG1z.; Um8-htA\3`qY%?\gC:7u{4ndp֠u/Q ~cqƇPRBm/FJl2~*%ﵛ.r5󜺗x}^Þ5R\S4FƠZ [tr4m i8 +yy)y+T ZX[aJ{U滣az]eӹذNբǙR^TVi']Yӄ:/eUY|J뿀bzF R 4hRK=֙w.rR҉\2r⤩i[nUXP4},̻/[Y0:*5A>r0OK5Ev Df`)l8L5zg6 ķ xa7[g8is׾@]Kۭ Fa藡a=X ~{ L*G4>#%(su9y5st|ٱO0M뚗IVio^p:>͕RWH6-&ڻU?P@qx Qn}~h㟅T9jt>4l%X3g#iƸz8h⪆tXQka T6AxPZR/rT,/ji_ oT}9E*r~醫u&Kg԰PWHcjiF<+-/q w.3@V(ҕmy')cx8j$6-暃30?} jSV1V袻tzi@ՑXF Cu6dD58uZ(;l"$}9~1*bIon"[zhPT|h[2c.tX8}I}yYceI-k-ʿ5| ik'}"[᳙ ~GKgKU+R$ \cl5{a`VdzУE!U&&a=13'xx2meo#EJp~F'^1;,N o/8+Oo;]$(4v>%ش%uq$, ~vZnZ=F1ȂeP$"bJ ^0N1X/wATE=H?c }&*``[č{o|KD݃P:h+.Z-?侨 DɇؚhXc׃;f\vF[H ,oi?YmAl1{4$$]ǖF߻@iY-N-^f[c.u)W%ѧv? ]{dmPhDʙhUO&[^XJ/@=9WoRs{֞`3#31ŧϦenUJ,Zu ;9cs-Yҫv~yeh#U$j?2ͶeU:9RFPD$*ӶhT^ɫB'nyϣRţ϶PlGTenp7> +pC,m 񆛙:C |{uGʼn\Of/mІ2qu=lE^CG+Ud[T$d1`;Ҁxӂ>#e 1&!~5!6JPP*$ɰT^fvhvƳ y x @FV5@5Fؾ@'Cf˿Y 3!{ޕ!qLZ(4j%al+j;p?op!" ђNЭ=[@>߫5*Kq'hJ D_1]qJc.jE<;jqD2dv˺u'R؈:\W +(MTV/W֗w ldŁ3,/jԹБx)Tgf7iS #+Z?8|L8 YXgOGN ĘMzrSWJ)L< +[{\uO XWc!Q +aw5>'qGO1X𚺣o w1FU3j9VԲVcن7z~o./:v~/_\\Æ2[ᘿz^[0 HW|j$@ &>W"Y>z \#O{b<9k͉ouV1Ncvg䋵/|9I7.$/ƜvpzZ2DltN:Z5027-P0Koѯ[Mx\&kRiNnߗ?|ٲJ( +"*8"3( +8oܨ7}O}"(n֪$3+/WӚEbŹnoms? + (n_jYAcwF?)d|Cuϛ. +`~Cid̵'NgnhLQQafg{{T-A]Q|晩O @N5}@*ST94"֩E!`BOO]b/ +8\ j ^0"(Eں<6+%Yok1Qu麏F_2-B^ETwUaUV y+*zAs\*qsa;T/Fg60x[T:Y`Xv;O*dظ^*|*%ۘNFSN-.QkscoG"sO +@[=LpJ{. 3InP_{X|S56%+k!4v 5&f:5Hh̻7[y+Ԣs1r0>%yeYvo&*FGQ+pԸٔ-B$vQ;\.׵ia$h5)i*7ɬ^A4Ut?u*| +ϏѩRBEwEv ܁4^T/-@]illX΍|mb{!Xt]B5kFX(ҭ/0v~E@ޓk0Q6R;zzdr`Z*> ӡOg֍^I lx]ko!^ r]hNa43jkID5dzr~B} @NÏ녌̮8(Tӑs}tQ6UQqsz4x+y_Eo2Ne+?|YKL}9UQe"-YgdᯱU^x-LA(H&j,d$?yɊ"G+ qXczyMc3lH *i ]6Wɭ{峊tL "]FKҋ*zf.#HLJTt,#n79Pk.im-3Η&5o=r%sgvۺ5q&ik&{nYrqRׄn74\wh(`]V2cPJFQ!ՏY2ĐV=\n`?Y@mb!mN6 +>m3B3֝|eȳ&wg8>~T&;1 +W.1ePyx^j㸷 Wp]~v{#uJWpMb}хEXk>z]"Q^ _a_Qh4g4Mzm&09`<ݹKv=s;;}+׈Ǒ27RH S#"ڄO&8 rD3Y!6 (6cC T:h S$v;-@OTM軩5zlb6bN_;k1 N +EO wE^󗅡Ec+p{Mo9j@q5#tk[.e3-O5Gy{@0 f"y `YLVMrCˌ{(0߁K:b<[6G羡5uwە֥W8ܶYy3DdCPB>?dQR:)@Cs^i`' M(mPHWLw}h$}ri[z5j;H߇*V#HJs)yp)yR_מ؟@{sPr5 +10un>lI7:xX7~KTj:FfiܓHK&U Oh=qͣMl>k^/n/C5ꞵ܌L0_TfZ3ޕ1X3I:I˭Ij @InGek;9Hd<>@CS$-OM%4g[I 2m̬x3QrbَABb.K1P*e2Pr,kOQ܈mB?ژkwI|bTSK1uvhΫSgR9W&c.Eط8N!*I3;7)Y$%wPެt?A{M|* +҈δNjЗ]jb`չx nϚ6V‘ZBF>H +*8y_E7a-͡l';eK(WSH3׬w1ǤP kPEٝj~y'}sZVPz2ԥ$Hub- xТ>Wݸegg`ihőΰa(?d2}+{O-^Dye/f($5&tU~671ؐ˓3&UrY ri.*KL=hA/!Ѥ?P݊凞7rT ifT `e"]ۊ,On̹= +lHFC`u( }V2F;ROœV܅vBsBSJk~(*_(AD: +:i  T>`2$)Ry$Bnɿ,=70l1Zwy-AYD%0DsΊ E@0g7^nffQ?ھ_9ⷖTA ?RGotȿ2$nmUqTff֕\aHT.Է^>LwWΌn?G]Qݯi+˟u$^45]W[rV[U['+eĝ:wZ2{oaI`"Fw,I6d#{ќEprt,kHќ'jJ +̱4(#AW̷Jq~YkU֨U1MH3YG-|jc?h$T>]VEAI6J4kV񼑜^j ['3ĭU]&nu,̿jFqg*‰+uI=r?]yΥɣuh浱{O"qW;scu +ݳv\Bj3+4kԩYTmoMi_Y4x2}/yW;RD"MEnxYܩLn̠vU{.tn+}Qu.׭,d^ni^PPlP,VFJ^+t(Jiv<_e9*җ4r{hb* nL-묨3QiCJY\8i8#%so: SĜ#-d;"Q.uzءrU̟u0cjJAH/_vaRA+؁~yWUoGAXQO|6TSQSD"jOZ[\v725Sy%\0hvi0~u+@# *@%}t :~ T 6lr`IJu/ #bC<2;Q;-Yzw)iGׄz<^dx2xHKHVe@6 FU&u`,(7nkŹ~};8[Fc % pV4j>K!~qzՋDKGxʵ7|eB Q=Z 9`S\x$jo.G7f|bG? 2= }.5Pf-Nvx+;W^/~ꗝ _yHP(F`Ogpt>|?{*pfÌ}.08' 2a5;W_($cp[3h๋h)wy +8zrK="><@T J['5"IE+ay K".F!\% _ `l\' 7 \tp lιi()CC"B9.B((3/լEߢbc#䜣(H003u|]V^]7<>Kwkx29]43u:xp8mbq'Ì7!m-e.|wίWʏaAtN8kAtQr~O_dow',x#'@:t&ڲ{X\X#2F?j, Au@mʙvH6{Pyq #nqV1OTCCj/] +_ƴo&]~&^1$)Ƭ7w뺿 %7s2@VT:vfU0PsO*E +bvkP+),KZ#jrsBXѭ?JBS׻9JECyT~x0 xZu^ULTou( +DnTxj +G$~n&T"7釿ne;nmMd[|P0 |됻xk¦z+`֕_*Qrq>(^Ԕ) `ֹͱ_%jEKYD+e|f*uXF;m)УF^e 0 !Sx(͝ "LHd +r .3,-P/u?zZ&&d1urDp7ijf)d,p[(XZm\iL}Ub--(GY#$L?gR&Ùu⋧C9K# Re2r6NfߕD=?FwNwWdI1^m?Z@FrR(iD Avm]zv\/걎HX'X\e@\e!T|T4>-w+f=Euh;oN+Lͩύ(=\x c~6w9ʎ;i0~@+rL;ƢMR uwdV +Wy!6L d}ivh33*:!ݾ=a/J`dV)wເ^NbukR6FZvH<,e2WDF¼/{rvN^+1gh2e9ӒjOf?`$'~$'BVJCB|Ւ5'|8ZikBhP0.\۟EZmɩ%'J$ jA0W>E;vjqirpsmJ Ru#i]9BI_Ձp1p6: ~ڥ\šN91+5U[.I"NK(ʗLE1sYVLK.bt2B +9  ݣq6Q(|q_U0/8/FQ Yْa($򀽡{ۗ}xk9!hДS\PvEIA4)BfX/fq]7?s6ZtXN yg1l7!X׳7a8d}[@Y%v}Ea~ił +"("JQް+[gN2ěϐKe[䄣Uxj%3J0M=9mghhZ0A_I 8{'He^=Uk^_N tR̷2Շ |do؜E+ﵖ"K!k#QE6x>Su1y=uT"okrU*10zY4v*ل8CQ +̸S}f"\a荣2:u~z]Φ״i|!|/ dʜǸ/PuX&п;hOjmF{e7`]I܁rGěu)w9^æL|[n)gy +//B6Kߣm_-ݬK?3xn#zTJFv{"um˷u\~1sQG32EXY;LYN.3CʗmD`DV>fIh+QZ󃴪LNfw j:xb J X8@#,?4(>6OMJ&6Rl](o$jf5i`\ľVse"TY>Rx&+EprR:WcײѾ6rv+2%Ti찼V8uA"f~1yYxڇ08kIh0bV_.ϠH9\W +}n$*>ԛp"qHtДgdMKJquNL^(HMiԈ@, .TG%ĻtF} zt^$}{4bA<@L&g +9^yBF-7tq"ۿTt"a<@>NO'v,5@ !JS !/:Yj$=qfl^yǚHO,?qSi,p/`+  +5pB+=M0ul]!K 1.wֵd, @w(U-Yw Q8g͟uaww 60[XꜢ5# +MR:sp`P +ҫB."lXIt2m ΧI|;W!Q󋑡Fhks;,=#w[O&U/;b3vyl)DQY *VV0L A1rpuD=yF)48t>@  +/+/tsr \I4ߟlFI׋AXcpףLzN2c,j{SGqg/߶ָtoE.C$o4N^S  ̟ b7 +٢MOIw (L;0M/=vE7*{8ϰ4d ;:},v^pᳺz>C#M{4a\ߛ'}>J\$: :t" IB{'MhBobgvc\Wvυw'k cٙ]p\|C$Jk +`U b+2@ϾdI֯%P*_[ qo`~q۽k gYkJ]ʾVufti]UWTOݘ3+z\b@<'y +6}zƴAKO C}<.7?~p3=8ֹ9L]kixyw6+a5J~dXDOLiLI:=ߨt5 H:o~ÿfS`xl f8Qjء^3ʷ3 X5gcumoe L[ Mܚ,$ FUjAOpzdD:yԒMf4%E!S5,z5j +m))c͵Q\&X%q88WFkgN#dS?U;@㨂yj:hdxkSV3Z*נP:_A|ϣ/'.k{o䇪SA g%vv,kmzrSZj/TJkk;7Xa%>7c@@=> 2\LF+Vkυtj '4@4`)8^ls, ,u3ɷ;KisHmg⁉~HL{(('9f @ aDF]ΰblX{b9dq9rY)sUN[OScVRv:}0B +ē"B?)yo0Fnu2\T{k;v.}[4:ٶnyfyAҳ[ Lf'h+u Yk.>)! ߷.OQ0fl{,$PEq|O +`7HO)T:̨L~ޟ.}A{[So|4SaSA `RxyDP?~c&g\äMq_}_/SEEmC@/'Uҩ]ʱ7/K> υ(j4E>S-NH~K"Oj49v˅hۀ&KwmU8?x\?˭FP?<]=# I8"H] lTݭxG>2,N۲Pa ?I7n)ы=}vLd&sdmǹYqQEEdO@LP^K4(D "* n/(~Ins:XM{bní3hn F4y7.4wP~!+N>%uOuv;?T~q sa=LR47Tlg&xK{N֚7v̐$`:F݅Q1ƲR[ +ZCV`4PmPz8U)}aY+Ťq;]&F u/ӓȤi$f\1l-ncu>>Gc+<5:ꌏb-k~Uԗ&s9ݓS۫ IQ"OOF*=ogs%P>fV;{Opw>Jf/{gm,5>y[q6/zrG4qlZ@*Kx*dn}I;r3xzzj2Խ +˅ I1-o + 5W]1m{PҺBM1&z$BXZ/8jvp+O0n1x\&"Ba䩩ڄagEtefL/G*~Ţ͔lmc`juc4)GewELצ~$PEa]P.H5ϥm* +-8\9Ȝp6E } ѸW/EEs4|92ve}:*otNZN` +ǻMYQ_ +N諨Xo\{>ˑ`g;tǻ'ÆA ޞg+<=$5Z c7vjOLO@ +2sU =Ty7. mZ sC.nwnr#R6mJ,2S6UB3k00/ + c`FL7]H*PI/TGuYzpTgc|SVˢl u.) QruxP=mZG,]uH*p&W<dëzQzz C0lt{ɎY*>wwUi\mvy'INph~topD+1@_: ά&8^i,j7.9Yxni h=3,T<[ݹ^ӂ1߻nG7fKZa6E?(Xa9h yaD y \0A+q<][@gz>ylEc}BjTz׫W^T}l.ʝ1 R*,5Lʀ> z H.:4o_oT 4+۸ܠc/δGW1 +֤bs*~d;EcfZ/Ne"᥃1-/N Xnk Ԫ.8fN{>Uv:ڪ7EbV~Rk^grtcV͉k1[zk"ZҺ4a֗2tA\i_R5&dndfANxI؄}Phg6Oo0GtQj!n4IEE℡6(sf3[-gݧQ!S4>ޔ=wX?N42@PbȵIw LB φ)]YC2b](H0 z>ea댱w.12>R(I-khcXeu:wڢ"՛Hك~ab#Da 6@EJS|Xו_7띐ۋpX:k8}fqGK-8.<ԹJғ)bu-E HpBģQ"=":"R[s"_1~Gp8=F|"wĉz`sBoʺX5Cܝrŷv=VRU6RBdʕIB6MPg)ҧ$"Î% 5);7 _ȼynP)ڟ=v]s&ʭjAgKCΑ[$cga`鳅uж73jRYA))ǝ*P]GdXLh$ZI'q|kga{@~c'E9/re޼U]-9}ϓTq[fq\^$t1~0c7JeO<4:c*W]MHd>Jqkԑz2f(2B-Tޕxg:tEIoz~J'! pr_N.Y׆,8\zѴSGD'EHQV`Wty-C-zE\MH>UQKAp}8{_fyEV>MD53Ae߿Zmu8]l! m#0ܑjNDE*LY>>/R^'${3 +;_6 +c4TCt, =}K|Y8)HI(9w%h}l[O*39Hs4VcQS1E%4xD%SlD7ouN=0^G[hDp5_Gg՜rŜȉψ<2J2W jQXI$z]g5n>,$YoL~N~( 7 ;mv۬ۊ`+ |9n K]'6LsL[h 淡Dλ?48S%4BVfBD 8dRH +iNHFtNG(_9,YSՓlӪ sue!}"˘,Rorˆ0#bƘ͚ztg>=1a֭T^?L-tzA_w]%H}!;".g+ח9GTB}DxôYjcN*;*OE|Lm&PB +#>gg4N( )*U,=`m0Q}a][Q#Ѻ)賈>_xz屮/+n/|i?ғ.#tn; oO}XXj޵z=xs"}#+UEz&¸`FTw*lOH( mYG  l[`߂" g"]T&M7O2Wsd&fYc`d̵)ttwC+;Ȧtlt쓼)`TyN՚SkϪ3V=>4 M4fg[ig&,\w%%QJj0 O*MoMR'fBY"vŽT{۩p#$2:i^R' ;\r4+9*/BCZdqhf1^;V -/-6@[|V|b+gI;3Ɵ M cRL2';'sa7 Gpޒny,o_@jZl~f)b)HiEKyw +endstream endobj 386 0 obj <>stream +Hlɒ8`MٖeSH+(H#)}7bOt,Vد ^r0K +GMs8&O;hOc]~q48X> uO}GGS}dC;p+iyȀ3B:ȠSC-$W`̟35ˑ={ES/˫>rrg9!d9p!R zliȥ,Z,dy=A8/;^l_dU=R@DLhsL {K%/Yi=naUH(yUw=T&bjlѣa72 AnN@㹷V$A7~.pQ;~@>&lics`T:l:>>kJ6Nƭm>Q` ,W +`bge/_&oY[. dvo!r2T;qUkB\tgҼ:}6E\)߀ +Ǒ3#/?@KPdtB̌WC'fX׷ +Lܯ>z&uSݦIG͇ǽ1v!-vs6!n Ë-^IIq)G_ˆ+Zt=пa+jΟdNWvF]%v>m‚KIHֶ]2ęn72Zbrף.֔]RSVcAT;|矰}+Jt:xrWw%8DGh&0bm.[-r.F0< `|P4viT3Z28y{j;.VsMS\xzĤT>JT.RmpwW-`UHv {D~sM'"}[ͻtt!tDѹBem8Z'Kup,Rƨm ڸ*=nUWBKw VTwsv(0P2 'q5-ʗfC$]Wvd' +Ǣ9!jEp+ƴR;zu1uWK޺z} "B,uAbx_wAyR2q]ntXu۫ zh'Z~QUVRCc<`bsBjeVK׉߂mOBwBږ̀zCGG`n@aL[˵N14{씫}a*7RSSǖ f?mi۞Dd0rV6<(Dpߎ+kʏB=J rSL%sK!~ V'(%R0ۏ]=;9^*w48QE~o\Dz@gQ1#JQ$/ͭrM~j:6j_!yDѯbVujY5$|zQ݆R8]2֫r9YK;HՏS +`}>rJ[1wM ՝[-LpqiJ{"A%]5U[ +^ivyT1S&bDn {4 ?|0?%-nXuh'9+)5 usJ}}ѧjzBEY!oV.PEw"2rUvW +rK 继R>e]W;݌ca=|x;[ gx(,>m Y$U4$?3VĔQ6Dti)]E_dh(ıK#SW>/Q];H(~FO~Kѓ$tmK63W{aYPYRfy\]BZFO+Fl(Cm#W؁|u3Mx3eX'̦ةFFZzr sl!pnעN碼> Mjt&(du =?]2#4( Z2r{sƮїIe8h M;SE[3ZACەBy91 +2sw9t&iWݚLMKG]^٬8.7'`MrZJ9̀2j?ҩ6yق={vRf껐"%x>敳~F6;{cƮ|PɋtYVߵ%}odydKm/QtIc[.\vg~+rr;wY>eHGs+2Yvel-g4>_Sc}斌kd7d{dIgl+BثϰU11x62ظ?dSF\/Fvu3Pv+rE Ѕ^!ؤ01芵9P^z@#P1HאQgb^!zSd޶*ޤOh@E˖+v"Œ'5> x}Kcm"uFH@gWg<(vwcOϷ03D 0w +'grٜa^bmC0:A5T*|U)|̧צٛht X2~=x2oG`0!G + g[2,utpLhL4*~ldğI'_ D%v0Q-|&̂Nmؓm!Aϻ5&W\yBZb,E(A4ӜJID؛9}]b<6a'w+ 8'yr(-/E*]nR0VUiy-8#/˴ՌWq[̤(eD5rg r3/ۗ/sRwzԚ$Z._ + 6V8uj=AK#4{F1;04Up_ab=Mźua]sZ8~&m{A3#hBX_NkߍB=K+JZ4 +z%Y~JL0 ]]>Jv.:ANVS00 %X5KqVFD&;w%'zpn}輠?gUQYgԂ`FW]"?jZS;>Zղ3鋿e|O+0ht:zv]l2ɛbgi!0vDN|Go`&c +a +OOގ0E1EF(SZ^Лֿjav[nZPkiwT䗊J2، t#f8.QBָbkaE)RkS1R )rwR9RюNVϧullLv3>9ĥ07 N' +rSRgFxvOO/;^C(ZwtQnaK|2b^ikYsU:vna![SOQJ%#TIIJ}ۤ-);H;dIr~zb'St\Rqv+aEkO XVⲕh;܎l{Lo +40ԫ! (;ξN.D*kGd&gM&]s=`3jkbs+cms.4Orlr&}ȧ͑|5#>ګH%k ~lz25yΛ:LEƄ1c%V {+_횑D}uDEUFO킶_8(A~'AMaWàKz&8fbv~g=gj:w||TRƭp4+ ;FC ߹ZoRBTGBscr0_|? q/G{aEKH5~($U`Ah+JJ"9s0/vcwn֧_RA%A- V le1@$$gIKvDݖou sBU/ ^ <#CyB0«3[˛/'z2˭*}) b%Y*CH^ѧ~Yb./z]ZU8W] +8! 8(* R=C@j{R?x s%`i0d\4M-Touɍ=Ӧԟ(gMې,>ݓNk1]/;_s+HݓS22}fzSHJYDX{8Ţ8 'h{m_:ۥj2ca] v^3>'Uwi@[ծ kh0JNIۅGhdj+f^ +FiJc('WU#?u.J#q C\xpPpL-*OH yu8mUS>-6Q2'EiOƙ!HUb uP/y9wniǀ\[Vs5gmUY5 &L86=-4avi3>oFy=ųvhZ*@%xWsX'D뻦no:6`͇d38Ʒk{͡ R"a}3zO2V2JeȫMV l4g?DeoU|e Bn*Wkgp^blԞ!ꚦۮR^e×U[V bQeh"f *oeA@_]-g3ܩgN0,1lvX_I=FboQ+u-D=IXx9*%`),DqvwQ͎FNajC7;sKݟTN BnP\^susk;[뤹ik܁k$6 {G%le"⧤lKxnOW3 %#^lxv|"bB{NZd8$Ӫ:#z!E3 j93Ed\c=l?>V0: :J +"h%Z9">+PU4+OQY8P(2FyPSPl&BDR{Bl!zbF<vprpa2PL(W~]],aCؽvtTиFGvOO4#ꩽP M{\BYT fKBpA>awSٯE&/># U(${}otyA1{?Q)pSQG؀qQ¨:F=sBN&D[0E\q6M\Bc_K/a`oXOyT(Om]YwV o]^_1_;lq~g?M;iAhYx$azL +L؉0>e+zS}JfvOp\ +ɿ{VY߫,ȝۘ[[/-hWxb\yJyN[^&9;Uεz;ƉI~x41/`EVw|{.\ךFEE6bji10IfT7Enp5Wu$]^Hc'߂~C 1|4*ɸ4rHkBJ`_-kkK76psgN6!l{`%CdT"ԽJQGAP X'[K11;N^Iϗ`'' aY{ +9+|:NZ2PV棥%\1^D2S7DۭI*M ào՘m*jHEJ/?؝E:P+^q=O;*4'p`_ clqxXiΚ1ԿK|ֽx@݊s+koZk{ TV;NQd$gLӕ'A3xH-y +ymY9DrW.T0 +:6#M=kŒ_?K3k-l5ܹҵr4OSuANVn4Sbx?)6|I(EY"&"j@pv!/ wUs4E.K9iy<Ӣ\V* +~ Ud1f7Fr10K45s.Br,54pD\Dn_m@MIᤜT+] !uAGO=/Ϥr-gܓ +k!xP8V&[:4&{MoT3sH:ħEa(j.sbhV1,vd=NG Kٔ"5.-Js=c'Uoڈ efeǜ`e >oFлyEs+ܽfبSPWgK҅F{! +LYCnd\ZE '~of^'\-b=l帛QdGp=鬳>P&RZ|)-Ԍj|7@p7B!H p_fszɨ-h*y\Y{Ǎ٥4.Cd+2'56j˓##=N!*(] +e1i.?EABL!`o/A6jOmT%-aq]^\-(PB(P)رm/~ߞyNLNrM2Y+wxld+;+nVSou("L?S[7shy0wXYyo\4 Hو02\m+vRQ3jipDnTG*s' +H9S*YŇ*{8ah`|WrScѡqN=ጉEՌ\M{hq}/lmikMg*(suSTڢu/X$e_[]ˤ;XK/5@oN;zہ)֙|z4r\@׈KܨXFգ᳻֘_~Ut}Zk+.֍|}<>v?cb Q(rm_l"{hNF Bt6 Yd +T7l}jMU˄ϕʗ ^CK6 1lgG(BX0M̎䀹?8}$*N6Kls +P2^͝lxS{i]նȥZ ֕Ƚj-͸Hdu2>!ޑ(Z=J9nr0mYc pȵ]uPhb2nΖu#M>l LhF_(8ׂ_̥i"s(yB8R~5\Fܼ9r𦁰g0b_0D]G<-ң}J'4u=%L-W8fatLR 8nrQ@^hS r Y{,d!|N)ӎJG"W>ukԴaVGi$])";oz+\c! &qDi 1PK..L]8pG)YFȃjSøv{:Q%R<">z!Ί( !ރjA}It!z[o'Bѵ̳=t=k=?Yb %XkK)aF,Ml!{EzgDtN:(`u>Ggun!L3@U7@)`o`c%X 0,Ab;t-謧%;MF-˧eivKL+ZAPhw{O V:CV<ڭ*zQBmj S${F dC?އg$kf5:WAWitot_h˔ ٌ2O2cݐϚLN"7n냺Jqo7 |f81΁:sǛL^-| LFu5f[U4qweŰ|OecJ `~Gf}04CDG"I>=Sd"聕} &Dg%iߵƻ@ b5-4TPx5$a  7,'D87j +n0i:~,ALxz:'hV;3/RS;Z *Ž:FDa1+ $ "J{l?Va~]m b;)VD˱iX`P,0h\?1 $;0$V Ŏ H-ғLFK@’T!/\k*Nl!vq1RݕLk-g! A37,6U?~렟`  $hry8C@:@[PfH@u74G88z3t>Xj A*ᰮ!J#>4>E6(]oA ꑼgB !MKkWۯҎZǘP9QUYzRw.ڿ? +~UE=jYDÍH3Ԓ@\1CdhK֫oلO3D6^5` +WB/0Jy*Z[!VI_[ lwRv?64C'F-;%DtAe_4[gYnUŲҲ~۷``pZ M9z\.w)̻̉ibܕ: ;M[ަoQIsllK=UW8pZR0s,4lQs|UG|Sm|;ssN3! "9r|l8J52 w-l% +IA)-/@OZOs-ǫz-ōUM{-<{AhGA}.%N7q5/RtiLpOm_}آu۹KV+E.1dk$˅R=v*k8vX磈iyblټQ gn:~Y;vv :Is6ߧ'E<^]XoV6^'3,QtuO-9q#p څy0` nw^[nRwwkomǃۅinni4:}nG ݾޭ/gfSZ¼5ϵ/`huk'jUc00$(On _{Q~r8uy-[0TsΨ" }vyY{z+9nZd Y 5]2[..3ry_MH ԑti^jk 4V"ħ7%/RsӴؕҊe?%`魉-)ݯUo4.$ d$,Xc`nD0h@ѯ5G{0- Hjd2ʻ~2+$}(<]Ap!~F,F7[vi~*ro 8 ulK8]T?e%^wE mh"ל:- ;ʡBBRpۤ f̜\6^~\֢EpQH~ ^6, EvG Bqvg6\\DN}UgKyG@ 5V +*G30Y?K狐^Ҥ{ĭxyp3ׇj- 3%p0msI-][(`gaOGw,|5UpTB*y)ŲTդ!fKk@%aqqEEPU-Iܑ>oDvu^:y}jLl3-|+ =U܌JlVJql^D =$|KtO7Po5 p̾{b׆yk5k"}4 !|JvyNl|%ekrȒ͘2sqPNjұcE5f(S' #zaͧ t-I[ȿ f}q r1bq2ᾫ16YJ\R$QvLEʫN$Jt2_[kYÒEbIc XRXe֍Mǚ T;>n8G*bGz~awVQkCľTFGЍʌn[Ǒ|ÒGܸX=khmhlPbQB -~ұԹ^X<(g/$ͩ?BpMSwQfJ{% J Upt4H1ll#C;hvDK$K.}X A]q!7G߄ghY~32謿/87~ + 3Gf;J4;l,wÆSfO&4X9@h;rVOO^D48 ]LtoSh9?SiQЃў!hsuu l)!3)J +EyryKU[[m3ZSgu_'cv(vr8>\ن8:NTfQ koY۝^3)Rd,-z͙l3,y.Py6Qe)`0g^dFO(0=9x?wX +v4S1Jje~s>HE忒[#<1Oیˤ+D%z1l1,2+.`&O7H3w52j"BSp ]\X:b&tZsq$rQUAX5;/0Y#kEen\{kUC)؎yy\S7Ge +xs|p@иBID]Ç>kAxu~[9a ^bB_/L'f׀Ք)k$&eJa Ķ8ʯ/赮߻=?~LKx{?l\ewxlj"WR xY{q`tovlMoj{#ҫ7F 7 D% 5;ΈRofVڈHxBָBgX3&bz>p&?@X3ԡ;(cnl~\w'ڒ/>Eu#\93;]p' &=[YmpW{֢[P,6&(tx3s̋jΫ +njB n˃ jCҘgu +Oo}}_7S..CmR4)nT}ا +OVl~Q[k't%ǒzGЭs8¡q` "vXzY݇z\fuh>Ywޜ{(^!kU1Cj2&}ޓpP#{- wx?2[eFK;Ճ϶gr-}CC{7@8?26Q|mb@51Q__ ~İxUGMNz Jƺ5 73~6mq8m_E|4Vl u?t,.go?5*&FMw ri 2:sZ2q_ z9 ̒Gtv H{1CTM `sdupQ{eYzz)lֵgsu042M &^g: +-ϝ&P lpRUIhtMuLD9&eþ Y*'JŻTji7KʈcQ-[0!vX&Oy32e#z=kV +Gn]ն7̕tƔ7w9ژɃ(JI㾜lhٗ5KA'_FNS9m7:\i+p)l-ٜ{erh}0Hzj|WFUF~"vI[NUj[|wOcq"@ڶA Ңί8Ӈ(eC^7r6šEײ$&iVo׹=@vuNN5 IA 9p> $9YKcى>, "`FHS +P t&_+n;.*SkL¿xhOk'Kc=trl:J*\>GzF#"?Wt'ᥧyVI9_>" +V P;NC_:_;=_:$$*:{h1+2a apy툧cʫa+}I X=~^s'Szru{h"Ϥ 1"FˢƚӴj\l<&+.7/\`\ȑrRm"nN.|קMMs:q*3\AD ,IX#)kjH+*}U 0 +WƀT7ugK?9FuI;3z2ǜBK"י {l@=Ln mk/&_(&$7;6 oBu8`C +Jw |Z%-}FGz]T6k(` +_KDC9}Q+Kʥ$e_',k'6׼<\TsH<+b?-ApW:q䍙t^ Mԏ#ʴGB aOv;yAI9$Ja|i@ZMq18x_Fƫ(Vɝh?]Ɩ& !3b/"1_D粲cTދŎm-fy\aL$Ä +2va̛ʉOzI?]Z4i_{T2^Q袝 GQ?ևՉ+mE +RQ2Gy`x5$9i&V"oՆ 8p`;[`[/EњRQcGU#{҄ļp:K _2毾"5"FMIkXҝ0Ν3Z#?exQrI{*pǾڮFu.nt4a RgCƟ'?5f!{}!\кL[k!fd [I^4(^?b?!z}?-R6ŧ(y3k3Uajbi^#/u:Kv(J- +DvH/Ud^AL C^܇7b̀ؤq1{<,?㡛Vݞq6xj6-2B=i 9.RoQUTҮ.JWߋrTe Woe𚁒*Т]v4}z +V rYrr"Ӣ^St*FIOĪ>sOӟFׅ8 UtVܸdm%?;>$m` l;$On*@E5 PA Pjo( .|7?`laڟF6 +@QO#) +YY`gU@>-iex~:(W.ϼ}> (}̚wt.f( C['E(%"gQ|G{?oau[ee!ͣ)=;Lq`e+%fԻhQy *GxSI咍&>M&HD?q>Pn_pĵiyC82Eϑ?X)/O L)vqm bR)g<^?r |:3YV0&_7ט6[Ww9\+pX_l!idT3ig7h*C +ԘWW;7JFyt,)x8MyY.|m"_E= ,D-]`hXxWX3[#ܗj:3L +V3 $Tan/'s|?_iU7o+Tg\iLm10yO Cq}l5RmDG@mm@w%\CC)0 +c#cKdD _+G +\H1e84G2&O(KH %,U} -36-D:?G8p0r0LhN?/5mY5Ğmm#ۗ˿Rm=_ s_Rd.TbZGTx>aS%=+^!\ C"< +N}eɔpEB8|~4\J@H M35 g-qB8ræ-Oq-{'ys-D=۳G D"@󓎇%G%.x9lv^[ +cXcYrn6km&2! ?V߃SkCw-Z=.M +ΡS$ e.j[d.W3ntI~l]>>l{lKF`src{օjic#cp3{\}lq}S'zPv + iaDi 4RH4EH/@7 SP~P<;$K/[Wgl{7'~Kp]Aے寸l68oZ``c6}VzkF z_4"%Ґ wkn=ik,*jCenI^|<}?(-Id8šjq|:)XjuT9׬7GI@5wRXgW^_Iou2jz KɻK\Vz$IP7&bi-皣Rq05[r-^\50.OаFc1Q)lMWz2VYwW/ +,!8אdEZ8/}Mo w1pߪدl-LKË'睺ae͐M7mF0 R F DǶ@EEEEsE0+Mdpu*Oxr\%;ni:T9Mm]lR*X[0̴(`oLX3HFgƳjIfhԢw +.'%A^{]kBeQI$Pe](am{$j,ot~ kl̔Nfp]& F3]!ett&Y.㍥šC +kפh(kveplG_yˑS$t @PV`L3[`5|l{UԠ?oY9_6HtQ`M;)ZP0 ++IlaZZ֎az8c qOpL8l~Qj*[L1cT4\7=5(oRg02k zٰׄGj;KY@-X-hQDӌP7wSXvBǏ=<#rO ]MI^ͿcM?>P6ځ/x%q=-iT\*K2ys2y%XbZp_W߄hy==#ɳSbb"eWf bA`vvYJu,9{C[],+;CKmZJ;J#X΅TCl6= fߥb.1[Z&:LFJiДԮ- ơ6?c1"1&{T7f,G91wۨe>[SKo.|W=V6?`FG^S>5x" |rqjsP 7(Ap[ ~(oK`2K?,/e̚-ՌS5p%!Gn~d΋LMr/l6Y|m=]M&2q/2!8:l C+K gJ [}]5:ewk| **cD%Od29S{rFݍ鄳Qfuzc8W >ړwSVmdu-C+3H*ofƷjrWZi¤~76Oa 0Uk;ez[/]@Kug1諙q&)l}Dx?"A^qcI(ۦ3.;*UK)v񇑆NPeQL`>W::~ahX߄dHx\*$`G=*gf>9suuװCлM;ԊNGԍ3וl6nl;=hΉXGYE뽐Zlf]u,m1A* R@ +IJ)>R^ӰZ+c> MLOLzbsQaBcWvh|ۄ`kwH׬/k}vmSg>ߙ62O+Z^ʔ+WoQJiM:KHN^!V;ۻftBH|y˯f Qժ X;Y@G_||_uTU0ceRSyc5|$,"!ʏ%Ar|5zU=a(.Dˏ:>)@t.iwQU+ιה{!2|Y}&!)U'W酑8r?J&G?~&E8 6ݘIRD(#5-݄5%A\1~'7j{?9wE{XpZk81j1d`UeOvhYP [ʿJh_"&xR% W +@d:6ZvoCDk·khڹ<څcf +c$tAߤٍZ;>k~933dߝۥ0(>Zf]@0٧u'm$*3Íc~ͨw)<&yZ-^%ZwzY>'?[ P˖ө1o0̙r3<ӬonY@|5dڤ$+c3Ѐ́sp]d4ah/>Ѥ}!}h|k?*vgjnM>&q'C AOy7ƜŃ5=lg"u`pOc#son; O_ Tu#00ðpK_ĵa pd`ogF/^"£.;V$ju%r[7Į{nqyP}7ef9C9;lX{l]?lR?P=Ky)Ea7.It5;4+w׊tm78>m7?e/3)>GP !t *[L+.h>"PmR6U7DU N{O1goyuT} u-tjZЫ|rnms;ӚkSl Stכh}) +2ZIcV4c(|qQX!?B'.~Ϗuչ9J(Rs}numnSl%v㋞5/Elيt\(. 1%)C)'p 1qKa1_Le:]k`Y@Nf s'}ȝJy` t ͎_PS(bV^Jq!Lr}}%=cIY^.)#BdHPhSh7yr>~-G61qM z虗2`a-bЌ:b\:ܝỎ^[Dd^>ֵZ-)#[ :3@LSO$%Tx={y&\:zkd8?!IZ{߽PiL_cik3gƴ~rŤշkj ']ޱɰ* 3+*9sXu l h,!笁u#E n@!@0GlR~@b1k `ٷ +%h;&,\e/Ǒn-ۯ5Cp97$Za{/ L|& lnRG=`YepAc9,2@4ALk7Y`l.27D;cfznj'=:i2oogC=pcf iMNtW2%d'BqMJ>&5tSnxϤ?˝65nW^梾&By ߼>Ң%p=mN]k2P1Bh._@P3+ RZ~@7-"E#ay=)W6id4=Z;JZ 6wZ`S\LTәU׍T*A$([ĥ7)C}w#V;Ljr ,c典~tG<\ӘmUQ턗7=wوɹ[\@2A 3T=4 j[dry=7L;e}&Ck;P4 v(B]@0E +q#eEjFQT\&"-Dt]Hy}+w/:3 +*L]Wտ_X/y{4~zq]QE;Tӛvm ;шz}itt9Ԓ6 ̯*5V5.4iT.i՞m<h>$·A;帺pW;qWh.RrZ\ M#[N'~ +"oE"SbREۈpFvVv\ ٠ȓ!Uն(-9'  $GAAp{kWxl2T*uoqF 1 3 @⩢Ƒ. ܐ-/RwZ xԂsg_5[{P-+} t}gJ8JPhy<?#|ʕ Ɗ`Yj=M>spzhB V~iBwdbmź7*{>')%ب/3|BmO0-_mîqA4'Lvxsug8thl;Fx-A\a,i̻LMu + c^)G-nAHA{>(yCHs1Ys'>zIu\;tAVښ 2z]#szK1+|9iHQi.`5jGfJ4?~F ſbᜨ9wvv߉$x[>peapˎi/Ńƃ9NQØx1'L6s:`â"UvN%jҀ z$= };tq?zhIhbΠ˘K-Mı= KّL{#)مOg#s@RK#JUI 6vu6@}Kiv+|ste$?N|3χ۷`$dC5a>~Whsڪ + +#[r `}ya$&*"E2] F76o`Lci#犥]fNRʰi|j2S]W) +vqƍwP]SՀ}Wcy8 +Ҙl7h 0 qa)9zh!BrdcPvV":-gX!| |=)<ϿޠWY 9h컖vb*[bp7jR՚'k^Mb=>* eBnCasY5Ƿ֞/ΊgWXK4,G#*lQU,*HUXQ{ vQ|!a&krլ$YYn̛(Xfwy]N:sv:nu:Fwɬ^T\*3wPqtz̦8ycX6ܪ{`n*Y V-5})a4vQ6 C^ ~h?bVƧev+?n6]GUɮԣYJ#"둓c$i cMltR̘GUaěxn 9[MgYRE{O5M4?44.,iØɗg @7^~ft#hqKb)ɪ+MvZµыY/&__F fr^?%Vo_ 0-k>!u5!ۀr:زfGJ$-D8Ha~ |XLi^)_2gWbA-d bv*DϬؑhX?zDoˋvo9Mh5dIU䰨2߶WԱ' +(]Fgcٹf*#zDCPqi&"$r>nhThZf-;5$3hoz{rh6cac;b^]M!Iy¾ٺȖ4䢏H\Xp)"ס"lv lNغ?:p46QMQC-7.ojyXU˙wHASJMUbWH1\ v߉4߀دkXMRo h|(|I\3י>? y'3`Ǖ sArFzNaVnwv-Wi|c +i<)Fzbb>.hj'a8{lrI@+"+p8IBmz-z40N/?sYNQ̙l,i^98^fLv+ +P_eܹlx7}V* y-3}Y&L^Bcթ[p'|R%kBoBn|@njxʈP(嵐c)ô7{YIui_r^MA(Rh=)0/ INhkمV ۮM$*9jFn7)A,<:EW=mϡJa6-vl#8=crJ5ݧ0w8 L3bN0mrA|U<5F.u]LCa.]TWu̢)1Y%B*DC!ԵVE<0U#at>FͶte/}}:mJ( ݢOze{)s7J "*Z_)5gw(94<{7m xӹ@p᭴ x{8c_ +W:d=wCe/@Ozӏ׊UG"[j5B~Rl|}E |pb2]'  80@h :D/A @7 <*A'e +< Gi7.eU-~K7[QENAPTEp:Qq bV9-z/eKceeg%Ztay %HB`P3P*C:@Z z z4OުMO{]] j\3BMjEߒ>20Dϛ7ub9 mw:}u1 +5^EAp% ` +`YG9/0#t@sw@t ie`B[8shG nʺdr;_gd[ݢp}np N]N ^vl#l{4C>&\3)BNaep$ U ߘ  z}&dԙ$ա_|UJ| gDi6+Vlm/ LM<)Ănd1a4PVc/ff]O.sj: gYO+>SKhzeT [WƳqUjGs6̽|rP. sOފ{W גڐ=gx<92.+l&V_kxui^XnaXD| ;!څ0:1m(PfOhIOCnFKoyQ\wl.Ujt)9G xF !Q.e&a +nwQO1!b9v("L,&qfʹ`6~}jF$r|䈥3Iiy;ȲMF5|;GXM{x걲=0mY ٷ]z۹/_*WK#f2m_x-CSYI~Mc7z'n}`΁h K|7|H Tt.;SZ8u:⿄yZNULr,F5Ȧ= % tW\[wCD_oyv8`:aneg ÇeڸwbL|߷l=#Vb+i5ϖa j^${yY7mpsk/A ?EcIu&f{/jR7 ͋ 9;NmLSfhCmv174 AmtB7bj7Vr5lWe_6dq6"V(g!`$|V"Pnh?ɐ/W2wrS՛/Ҹ|G}+XX&AqC$.暸9[&eچ-n5qZ<>%~6L3n\޺5yoWP4Nƺv~IovnU<؞> ryIq%nh<1nV-^ ϯY|zʧ'h\"n C<|]9\U[[Ma>(m|&;Hp);1\-4*ѭ +E5y vr"_Sq8^brQyDFaڨ|j,?Gu5gθèry؈݊BwN.U>2wMI/glKakguJ.56C--{ Bf 5F[ vʷH8mЯBçޛ݋ 3~dcɺh#dK!pMPg/LՋ?bR9>z B\. g%m1fu In,hUT]z.Lъ4R쥣$ۗ8@uQ-Mx(4> Zt7`k*\׫q0A30sqqw0]<4.nu6 |@q_;-Uwh q`r8ڣ㳟|` +rS?2_ gtP+iBctCwxJ΋6Њ0jaw1!/?*3?ސ=pɬ!&&`}]a: 9e$XYGk*[Xf̶qtrοCkΖeaRvCMFMIy` #!2g@~H$xCHMʮIҗễ&gbkLHD)0瘠ׇ gud4qeC!fBѢaڀ 9jFF·O2@~ @Ci?[hpɧnCƘĆ^ʑnl^rǒ$gB4M蕒iuLGidFa/I2Wh]5j׮I #2"̢On-$@+53ZEZŶZ2kZPhC}lVC62]`&UHGq$u5ha ÓeQdz$ *qW8)!F/?XQh9!{("!#]ʀl'0h2?c A@򭯕|^Z z*L*iY;KǴ4A<Ŵ[J9mƭ۹:+-=V;^Զۈ( +;QLa S* +yR˕twpӲ}SkADTH\,'Dqۮ;xy"TWA]QE+܍ܭblن&#Ml0.֦ВES +;&E LtX_Y|}'TT{U{d7""pEvBtSDDerZ 8q> +fK6\mxZs< Y7{d57׳͂%({ZfJI^'1.wl'x"4b&$ ]A9br-`|{޹([5q/|FY2s Q`+.6eF( 0MtR I}qJq t3]/F4SEALash>G*"-@X 446Nz0*RUDQ;IH3;RvDI~nW%u,4v=b5ܽA< (YD %C8}{?A$CQ$Ip W$@cڷrn5ZNnI +S5NJxu3T5s%V<4H*4 v72y@X r[}@o@b,uBiPo@a+{evBϩӒQSܵn̵Όu)Z{pLM+w++B$P5Ÿ} J唰Ayv:)V \PGN@c45@+ke3J;hamdXPgu!PL.{dB4pW^fDρ܎<IJO_&cmPw2 +t@+_6dF:.4wzفP0O,[r $A#֪xxf (O?ބ \cbTG*De*d#g +z>)@w7ȭi0Xít+, "yu8k8.Dێfd{ƍy-b^DK>7Biܛ^N_>BNWӒqAݕ]{uicq˞5n ޯ'rzbLWG1@xW϶cW?uc+G3ha *}UnAcλ۳ǮqO\#̸LwR̙^,J9pyn/nŝGSXw^X- %mE,xeS(USXԳr +$we~poٻ̘q]^ +jI-&@䜣(&Ķ-#zX+s8~=S̶-Oo>ZT\A^L[u։ P;ԟ|iOZOi ~+`-ƭDQ Վfa3_NrzpO_84}v[woDD;%p+mxğMPiqϛq'}@ىk³Rns:dJoc _CuO.u{0]>STtPK - eO< +nJney9d9k1fZ) NSfs両~1vs~L٨C%`R8Sd|b1YL҃|}JC.d5'B׎qae4Xݙ{2N2u1gOn\Y!PK"e5U#&w%~WTşNu.o HQ +}Ppw9+*HMK/.?w n {gʹuJ\4s"YɁǔ>.Uq$ڼd`*:9\i+VR؋ŝ ^'pyݱ_ֈۜutZ`p#iQTK0/1͔u!{2TGc,]x'%ͶCcBڵߢH]:ψRSuŏ8['ԿXoYkޙ`?*1UhѠuo=suX) {]9ya.A{G'ԨP5JLWcp(v|\gZZ2=^۬'QƹJpNg_6 2LȒ>SݑMLBןbB[{.!$'sq7‚bc{1wQuŁ8Wf5륿./bwQj2x[|,YT QLBna;ݬ&n}K #~v>i ^&س&\44(1j"a$ky"Dvv漡xN{ET;1l>o`8 U2zWF1Nm EeGBq}ً9`Oa^tF?rs.S[Ms 35O :L"(;HM۷@@5sxm /W6 DU9*Q\=<&'+e{5+yXWG?. o*DrȜ#t;?bS0W a;G"IGwxb^K;.gqe.I&TkӉ_@8żohN'*sRG:ܲ<#\:d ĂǐL(f33>7]翘5-jTː9:–MYa4YFYCAYȧo1-#dML ʆ?4箲JkAA:Cl t+*{3KΟgB2Yk`>mtGR;.JB~D[{Zӽ{?dsUhiMd_ru2:5!>dJ wikXTlqcע3NV>Wp٨d5ԯ' *4(BN窱|-̉n]}G^l^#im#n?.yNvᔑЏ ; iN P @L{ocI3pW&y|~'CZyUK5ccݎJ|aN1%/gWA&h\J/Y(g6d]_s'xG[(5̤cgvgwL5X%PI]]!{lbvDQ.t7V?_m~]^Ґ̝W6;6Uh8۹A.!Tm"dk]BqX[ZkeE)B냟=ّ)'mJ$H-G)vy3St !0ߏ| |] [ tn0 +n`ϕ=8k:tupwzX'ݓ]fiύ;yT R•jXԛkZ. 5„1w]ߡ$ΝJ׍:UtCQ&QT8}u0bٓ{^; t}9|3~AlM@w\@u]@Zpī2:w@J3f U}$RΎ8,)rϨO4tns+Ck?_΃n{'3D6:+@<. т k^'=T!t _G̎(7u) (MBR.Cd ~$)Cb),&MNR=}?3 8{ɱ2S'lt3ܣQ%Ɠ~%BdY&> 8J ^%=/2>v9=8bϠ3<Ÿhܕϸ{ Ӄk<+KBd|[\'$ܱIh}.s+Y84phz>I>LDvhϑntwG֑Ü|%<YU/ #@> 8v҅˕T}kbTm =1trY=w*q툕uˁ IߒRv~)5#VsF/*5B 5-?]8jdy0Kƣnhwe4>~*OgM\C<R'hkd1k" ~mކG%|P䰹(HHFD@{R2Ep#3ґwNO&#ܣkIwT7n0!T*3h?/nFgVsۗ +pkst&ԗGJuK^X'k!ܨɥR +) 3=2?my I{MDn`1LZ:6rUu4*?;晃[̞!+6w^>^jZZI^) +V}NL&ɓcx^*t(rң Ws+Yb +pUp7ۓNK^wc,Nn㉹HKjԽܶ軍.=cT~2Vow:PWr|(ysRs!ێ29ΣXeBa@s1nOέ,6A;Kuǘko'IZZ붝轝.kgxTSe"Tr?Gu ;}W4{<'-pj+rն3j]I:sף5NL Fc!6 ME~u%@vaUxSo$a,&UI.eT ,Q4eA"؄Tl7u)4Mks_/ֻdBGP<~].YBss3\ ܽ#jW ktGrS^:"+™l d.f +cӹq,R,G1g$t3M9r:mqF92TJa|̗b +ݟY#+4oV| 8d:oc攔׾S12hIb]W>c߫w/O*`붌>_yR?gyaX^tXH Wl7A=B#FdhH +oI?ӓʙϣkU^rlT$B#@Q9?{wf.Guhl,lpԘ`RLfttQ]$QfC /@H&*~w^wL[&;٘@Um4^䥳KuvW@M;?xސQ-U6y _C6c;fr#kY5'M0(hvw$NAͦ1H'tr ze#jߝniwuGj(w 4Dp2>7(6[0&,3#UfI ]kה1(H7$( p&L`')P;{jwx^~0`×^9jmg|[Q]|{RA#r1 R']lI\r7|$T[4A/-$ ΄'6v:p TZ::l=?fx 2d"wR;Q)yoSr`Om6c_3̰"晟@E0fh9<`m.ކ*YQ*ށ4 2Kf\hI q0-})X4-h߇l\}wLJdek^82<44 rm&<-pV:\} + wtvF T)8LQ oCgAc+iFc9]q ^Vj> yCnY]OEg/=wN}މiZa%vhKpe>W&2xگTk1ׂNUF-4 +ZȮuU( +ϲEAsˆ +&錄*^Wn!flX+XVСֈӴ`~]@>lA*̇rnEQc,7rmp_;h/]o2ΜeFdt aIO k/I$-]f@W:6%vSׯ|j8x]S}\: 01mgS,ǝQ6xnSWW:}n2UgD|V/+,uVsG؟0-Fӈ5kuD<FrX4QM`O!01j]8A#ɄTE[w"xwק"Ʈ81pRL^'i Ǎm:/'O>bA'`UꎨbVDxWXc6E\B`:)'rh3}?OB7"Vd/oӵ\gzet[zK;@$!SmAti )&F!!>.f "3ztF1eV4vQj-TB<̮V +; +?C_&_sDҌĔWG }HKDH^ `i.+H: R5@zF^=H9 )o ݃#qnTb d_v&BF\yn:jo|Y3 +dH\/ }c Ã|TPҷ*(F(=A9 (I~Pg@E= [XgVfU:SV0pW)CŚ!Z[ODAPJ-{U<س* 63JF*hbvZ|᠗[w5i hA`RU/ɦ(ҥXFƿ,ؿ.ط7F¿= Z⁶Ih/#0>-.B  އo&ġE0}̎rB,$) +YDl$Ura1"~I3OEc^LQ|nQ}h|?#[G{Mb.K5R +v_4󧳟[tu$vKDŭdDb1$<}=xQ=nw7'`7ncqvDӑpWۗnI7OCNV̅F1's^r_ +CjY{TCþQٽ5n[2y۰{s>֧#=C/m/OYۇWƒmWٍt*,뮲iI3̂ ez0j.^ٙrOe{kC\}M?nUKWE=$8SvV?jyl47PWzaNOѪt]8e=lQ?DYl?Y1,SiРL9xOut"uyF1zWָ4v?,x9>L|V}þ>PpO}~/^޻I+mSoijo-7ԡ3z,A|~LAp]XWY$\;;+d 8KH%B%ψXգ^=/.&&y-9dM?XfUr-=^V I\^NQM_g]~w; Ou._+W G6ZO7Ĕ҈Ռ1GcxjHITycUjJ rpҮ>$ tsOE5Jev#:iY\ ^-,],+2b 'z/JLKYYCY}O!@-o*[\)ԕd:z*/)5xgq(xQL׃zuHvwBFiJo΅3y-?WfE5WTE+rK %f.xwmǚ,Ce!).`(_Em#=9Y_:,LUth.*47V}s؀/d!=;]o%#~W͂XX*Q>o_UZb a>=L>gU0{~3LN56U243Dd9B礪e)eιښXդOLAn/ 2PYLwoIlCp.8PA*m55e=o&UvoeҼ^d=wJݏl; YĆilD鰚U(C6'iNPY@HB!3:mN +3퉵=aDQ2!|/5°vpX۠X8yث&gm4iGzƛߋxQnr2Ӎ/IVVUbMzwqff`,#ӭŢҊRa#3-젽;71R轝b/a@^kL[2 s5T|r1vv3n&4ȕW:۬M󨊞{&un!\ ~ N + j4~}c )WKd\+{X ݶ,GwR;[jm*=J![fsiHw`nuB\ C'0^a}Cݡ?8@ rgop z ?]p&\)Q0km(?ZˏD4-4*uߠ58xt#6B 0q ߲BT!E(Qʡv#:5\`\!ĵR]Tz`&ě^ދ4qYX6Xr^BA̢i4'f{3Ak9iWBZA-߽nЈ:8qJ𙮛7-oe!NҘW4T405G{YK~bU_>stream +HlײZS1P$HsDTa)*&TD{sT™>l2d4Gn3"Vs纛M`š_@^/xaCbL]v0cLMavM?_8}Q-\5)I[vNT(l?֣iݤn:S#&,s`UJؐO{n13DCp_Nj@eP8В81x#>G!Pz7m^弦5eD[M 6)J=3:j J]XLJ8|h.KIeWQˣRac@ysqx`ܩ>r_1O.Kt0}x +[Iu +n%IOOdDj;}ʝa-&chə1r~bX.?he +-{!g !4z BSAw]@̊;5/1g2 |by[m22s-fujp4w4LgmPgm&VOA6 +1\/$8.U1R i?S$ʀ$(>@UAZ DdS 6=_S G ɻh#WͻF.oas4znյ/`SuwNxԒ(DžM\IT;Vؼv=yv,ᏓօM;p oA1ͤQuSYijӥwB4_L J-K.Mn+GQ"ń{=m[Sܚ/M̺0YLe,4'" * $%2gu׺콞}/$1bNL47@ +Fp^h>ӴNüSG lpJzhOS'Q5GGU}zVD ̪xg8e*k6 +['1 >̓و{>4{n;o`ўO#o-푑g[h7p.V.7 HDZkīY L@Y'pȮ?&<'u),RP<7k쯥9oZk8udϔ-ܖ6ݓWvW[ɦz~oE<7Y>klSvɉ8+%3ފ e@a[JYGjZѻMi:ԑwI=+~9c4ܞgR> K#nkI +o V=XU8[Q~E(RЄȮB7;WfF0MH#'K=LڊHZfT[J w~OMp딒m#)y2dɟUIeC w/$\bC_uztuI)J^q8 Zo'+ed=_DӧRagRV6ԭ(s@P!Ag*2o%PXRȿwv I'gs>I),cݹX+rٿ؉{i1K4gUѽ6FֶⱟQ< +FN2ivǔK[7`cj̹Ƿ]Ֆۗss,d%Np߇؃2Kw+*qe ij`Sm 4hU#C7dN/ @Ncߋqjǣz{mr2;X wQ_L?Hy1#m%ʨ+Hp ,#W69;?:cI&FTN/ԏC;#,fw{%;ZA%1$;Jnc#C?:Lah14xn`ڟf!ԉs^Ri(m_}BzwwS3. ݪ}(e9Gno-@vƾMdߘFJ.NF +6|<\q ijX  6#OVF<=ﭾnEq-;S2q(xN4ZNíSb8jCˁp? Z$>ж +v]FvwZ!CtFfqSj5z* 6'̶>2I_NzQ~6uFVf5V>Vd?Jܥ g>|*-_Ŕn> ն23Jja;,O@)  Ylsӹ秱t渐 r{ +iz&eΰkTZDuXYQMb6Z.e\^TKH d2@v5DPʝLgSrv{eeC^S@u#X3ᘣe3H|Swj?WI❚2ضk8yWQKSRNTy爼3ž0E L4Z {5@}: }4oHZbjJ4,jhB&&߃<:>vt:X3HG5?|WQkADj58 p 8 Ջu5HY>PSAR p>vvuqLp;CR)JDD;?c=\5ki':~L\zTOjij 32Sl5^!|C5 thӢ 7*TS^CM'Vz ۗoDc /->0h`U 93d( .ŃV'oqZkan3wY gCt` n/mKN6W-JEiUW.sN.:7r@JȮCrUx>z^u\vio//D/JzD3)I7H>6rr+J Dc^_SHۇSkn2*mS_\{Kenb{FťTkog#׉fq/~kyCExl6&mV7 FkMcV#qe6z5GZlb}vm*~op{ΨjM&Wکaebd~ulfG# ǯ1Z-Wv5{P En;S߇U{MX^sI1P_\ΦDgP} b"]nZ{̉-P>}c ;#iiIĶp/l6e/6bjRl;OV<Ԕ_񽆚o#<9o0h9oM6v\}Jnk1uB}w"% SՄz۝~~:>߲-`s#Mwa$bZŀc6#c]7Vf;ZqQ=g1!Y]P{=ku:6|)wEvDOY\ߡRHv@iYҦ]tHDbx> +TnQ/8]=lZj8Tq1Y0Lڗ8Dz^?)!)`I+l6Qo?E|AbA)3XS`dKݤ#Ȍpo|7 (.*9ɦB0b>peM/ɛk2C5oNPD΁NjqэmcӁ=?,sgT.ȗ+*bVRx)K/ BG3i_uƩ(-kLcݡrF¤| SHZ " k'.5w:*Юv`LUo\B+hk<"l8R.~d,oSCP9*3U4bcFovuuO3JC>#ಢט4ό!i W`6gϤ2^hHx8n_q\ +SܟDs Ds:49WU&륆QCcܡܣ?&jNiM4D 1xNB11$se\F6? RK}~k3`t/k.GI4 v471Xj-fLjIh(m.p`੃zkRġ"&n1%xϽ6mHվJc_3b+ͷ5PkUk'j򀺔DvQѫ+*ҶWe^˿?]Zc^%3yM$|ơ+s kI=3Mjt3ݾѶRL놰ެ1h+50 @Ur:0KQ;d-zKALvk_f]e1 =7ޝUIxPl, ukb͙ھ{l&BN F +څ}\)C@?;+*VZg/ ȵjKy [>98"-]y:Ƽ; [fh|'/A7 !1,O` Jk3*+%t^SI0=訯 kXPMt5?mK6%zTIk.mXo8B #+"pՀKG6TE`_."a2, 1 !cVst\JRw6ʍK#sq#3./ϑo~}iy mg!>33M1 , NCUnWԦyP6>?R <ľC1EikdybI7@ t CZdZd %'q2!=,@;T ) +q M ȝ'lA{Fp˝2NT꫊p!2ӝƮX&izW7->l$akG odW YD +AvTb!{/A$CDʴAoD,(m3tIX/1b3d6:W6du]â%pKoU&\#J(W7S +D@l^ "ߘH@"HyMĩLSۓ32-||A~C \'#5S{GD$}K~^k,rM-V O~w\ Ӡ\f* TmϨ׆!_@QE 6(}D3 :WBuN!b5qDgZ7;I߮ߍMf|NV;їg_ "q7Pr5;zxvH$n)ыkN&Uv֧ZASUNjO k^lۍ=luo[\ 3Eqk;3>\p'Ut&sL[W:ӟBpے%f_m+Y]o{\<6-__Vr=΍wU t9m3uٕ\O/^Uac㷶v_=xpwb#`kg g&VJkf +Y?jnU&93^mN[T|9+w˰T:QθѲ<3q9SrٱQw"LT ,J٠ +((H4 +b +?~"dKkPu5Zn"4|!CrPł7A rD7ϛ4= \]Y$恃>{;;ۡ7߈%枴s~+mڝA4] -^ 2_b u&47ZgWQͧ!,κ|k?ވKH:\s-1 Xߵ6n o.[doQFDz!vS&2䗘Hآ&S*a:P&sdN;f5ۭOc +VȓW%Yz35|; 8# }T= eYh]0~2LI5K .j %<mœ/tTY N&Y/1dSȉpn$gm>4q{e@XޖŹFﺏZK9M:, \=m? +cn=ڪtH݌5G^Xp4[f {rP?cQ3 QBKc&Dyoo=p:;qANBEu;~Ž`ǠR3" i40rw̦w鰰n4yK+Ӊg45} _{ +"Z}=0!.\Y?,kV:CsPc+_O{lz"h/庽/廕a's,1:}Nɺؽ4vO KvV5j֍s,R *` қ8=lGTGCbAt.?{.&/DךҘJ~x]lbfl)(\UTQǽZPW,қ"͊J޻ξ7,&29Ԣ'AB޾Ui;r4/,&'KOlvWF +.'Ilp ˂Q|߱B>]n[LM%`W1SuaӍOңvnH,C'ަ|2vj8͞dKAzVYVBXv/t!۪|6Ȭϟ %tzGnpʍv_%Z|Y1Ckz487+%[w[ RFX/6b6;#F¤l^Ez3ZL5ʭ/JSoXoPA[3[uz$7.$;%NYYD0iP}Njyb9)w#.IǼYݱrxIݞc6ya&2Iyc߉1fn&\1<̨ySW/ߠٻJʷT/yI/t=*hHAF%,t떥VTTKI)4R=lK{˧ 6#oX*TYkU&(;SNxUnM{ wT|d$WgޓˆWj+ +zy6U0U"fP&TU}}@]q s|C= Qg^NFjv:VJ^s~U4W60PE>Dœ #G +΅dDWrc +M9:o' zHV@fƳpG4[=O9*)Iemq*]-Lk\N>$#:9ġ*xπ0 LE߄j ↚e 9֝[qlz)c[Rj,&ր\>h2 ً-&Y, +UlfM$6CD\2 +1 , ۈ[X>:+.TM^{e}xugyYC%3tz:ňj},Ѓ,pd Q/'jt~G/Āt.c;=6pwS6:hmٷPv?=nsbNڲ?o!pwBfdy?I*Jih:g漈ZgyaňZg E1-/hMuˆ'oɲ\D]pۻƆ7*ƪ%ڹ5*yCO"pN +23%0+ B\_/T: ~^}9{A$/ BAC Eg p Bjez&H0^Q>Ls3 ݁*m!#;ї8@G1xbOV\s1 Kpe#7bˀ鈓o Ag5LkrbW#> Y(~T>dYhHR)}G<^( :k==V!HL@zeW6yĵR }ד~٤At' =pIDU Svj'U~O!D\Y^8:,HWĮj3HҞ6]Bl%K˶@n eFDzU%RHmyϙ3r[Y L1  +Ŧ9{m `sW,z.0}_ǟN[d\GȼސE4X.mp6ZWe.@?n?No gYi8 f `ý–ʀ]@\ = +AJ _έMN.9BޣfOR`W +s'P" \pk9 Hx]$ɝ@  +ȸ姁6(qB#z@[@@7GC G[J=!bʿqRg0CM-,nߔ&"__ԌBjburF@|=?xMYZGI:L;&m[/%`Gs7S 6W\?ry |xF=;7_5.YRP̝Vg/ENϙ>ޱkB|,DZfezm MtfK}9^偤AK^sa~s&X0Ss̙?RGOcŇto =k4Kv(-r[^1/5K|-Jʇxho;,8F+Ӥpw؉zujo~E5Ґxx׉֞kob=qYt;l mB*Wu;P£-ײdL#2Z/qY +.HM%'{&}^J| pċkTdm5*թϊIؒ=hx-Xdht r!Acб,՚5E`kHw~өd.BHWw;)5 +$zS6pna/l8L_9[xka_`Αpʹ( LI-f~ԞM潬N'l{k8j#G 4p5#=E嚑Jʋ[uץG +o8"D͑}S[xf:'&L™BwpC`k&^?&Cj2_ơ FEjl0Y! 3Ŗ.Vn +IAv/w K:Oq,5P o3<nB&P*zntЉT&I-PHFOdg04RQ6RVK&˖5BF~픃ܩ,nwxr:7} [R`Ѷ?.TT]5T2Uƍ%w^G m:m@Jj4Q *(ab! +[1r^k!7З@ +d,Ifj Yx!='ɦ=}"[l\(_\&iNdT\j(]n F /D}Oҹ󔂉I*+HrMOcTW#R +fĕصݨ:>JkwN Fۛ^wY/]z%'v|GkجCʎ`jЛKQCȽiLe'N […j]{Pyt^a-tNx-Q:/3>5Lj{[PІmhc!V8.-qy, +[ HX`/x킊os'I2>멋encmjm۶už؆q_jƿ*XgxeT3B {XNs0=_+t䎅R'RSrymbсӣN4DW&>٪cv\3{zL jit.1sĪYs׶bOh d-;^T 6UO,ŕ&N0 oߓHjknSQ?wa2@u7U^U'G =uSqgKt{8. s]|FPxϺ邒&#sPzv\ǾZ) GO=~E<&<aϨ^?ysd"V*/Ѯk%]_2Uv3 MӽR)xhߡN +ŧruߚꀀ/ ʉOg[5B9U-K%jpq.bO3dKЙmS.-z,i5)G>KIc%ydV=cD=k[Y{i)/jEr2so)X",4TI7L.˶Xvc6s3_{)g|fAf +J] aw4pW8%u |Lf˩bzyB{܎4ar>3*5Z +7B}_]SP{3HS^c=F2]u^m\ Q뵯a%̋6zʁrZc6|Y ]g$ʆJO\SCVj)P]_8ҎA1>tz4Sy !,H[HDJ*ĦXHMܽ{\kU/ege5zd%`Ն +d tug nzߞ=Z+!cLd5lV*zrYӛ>?Ww# BrxQdS aR,i-N&IQ ,-6 ?Hh w9>!@!|Vzi@#t=*aW#ݎ[MS;,+5gL@oΊ ;i|b@>dqQ1xBd2nofDl:BY0r= whV?fxLTϱf )#zGb4#?;tz}seQloCOr\ktQpX\{4Q]0,&]xzqJ$XQSKDؔmwph <GK@̫= FvVE ںD_ymt"}^ĢH`̴ݳLY*oa&|1Fd"]JdțE L) wpd&cSEL i쁜P/' 䔫9X@5*j::>!is}m8v|/-c:SOaΜq;'r ?V]GPчpRPM#t^5"FN +(&P <P TuJ>_uhz8}2IoKsVyYx?j gKV@Ƿ +d*dY;Ձ~-E8bSu^38W + /@k 6=mt荭i|o ʪ֪P{ *¦R̴ 7b) !{vI8&VuՁ1B?@8V{t_0}L& l q?Y@D?o;cf˟:nWlE6FĶuNW ̙3yr-%!\M!;Z[8}5GfO2k6ew-?ڮ;va~t+llr_cR.r^s񋍺.okGp4&"8OF 8&c5\mW|sRE[nBzEw{ JG Vv[xr|~PRh7x{/e%FA@% ruBKPTDNcNԛҾdGОv0XFE',7=p'#-$7u6 *%rW@ CR 곛|yKC^Dv\b4dj2"Ĉt7uq q>kezuIK'0?AD䏷[?>,xoFD_gA%/($(Բu( K2^fc$ƙ$[1Hڷ𑑂R\ހ`3H`xTR_I?$厩rһH󳙰'GG"cy9\y|L Kx^Q>QgK͋_IG,_E-A))g>8OQS!%x--FP[V@m 6q)e%r3{ j:6Q]Ævm;p.Heg&oӛ 75T01hww4RY/w)WfynJ!/HfQS- ÔOz\M*k*Ǯd5Zmifry&aI9[~}Edwafvukgd,M,Rͯ`9k-dy9Nl+5z+ɋSiogǶ;Yӟڜv?)ߌ㖌j1U5-&bZEQwu(ƹ@zkkc&23֓w[[y׹9/QOz3_[rPHQ QRNi?}'}]3fejΝ?߻A$$4vdn|}- +}t7qڹdks6ki9nngl+kk|9% ):_}$r>JQI!<0kfCfF̚kk k(+2zLyBU!XEAݒlr K٣@1k"T?:;(&-? +GG/kZ[ln먶Rۙq=5<<9x_SGIV#J x +Zpya^T~f#⨛E_Y9'}W2H7w bT`|&{93~܎5;"tz#!H)?Mv~꥓#_]:ʝ|vxtCyj + j:B9#aJdy ;[!xS-Fz(U% ^E'W߆4FR+$L; ;iI*Եdv:&,P%P>GeautT8i4ec$AzY/b6|nbƸ u!B؀VRYwbΛz|\g Zz{"-0vlI kNr%.%'X}\[R ^6ֹ5te.Z~ DS.c8y$Lje<ed(s|- ͻǀu 2Yc$'Ԝ6#8+8v=~wԸ)+8tj~B + +W?E%Wچ.G .BP +q\Gێ--g-_u3o>J +&߆H\uzcZJ^̒!?,$ط[[rl& Yeѯ k&8lF]E }}~,1  Q hVU,Sr*%(R9aN9b75N-I<0\M;kT~*6:m´-qe,w{$8 -JꊖeqĄniq}I')WD2 O3Y)zmOu`4^ڈ=y{A1?"2r!Rk֫Xr4^+.OshId ,0v vcX78YY@!/8 +=p~C\c1.@p gï.Ŝ'쾉,_LӔQAcOJ~U4EčMh,UNn3 +*[@ {Xׁ$1H]>{כ2֯@9v϶/Dgf[5Oq jK4mG~?r}ӫqp}Rd$MGp@ؗѾ!) ;*3@<*@N>d-rDi $HwQgV:68]  5;1ɘXܕn1_oG *n@H!b q@6b.Oj-%оi@ub(<(*N?8ܪuɇ v>NkP濲آ~P6,k]]1o,ޫ*YoTX?' \IDd2ZO1@K[ ]͵۱4qFLo=lFJl:u{1kV+YOwfXDvxl۲1D0Ô~?+e84^ 0E/`DID*H^ %~ӧ:ic3 {~_ iYmTwߞ}X [@s N.C6 ΅G9㸯T@N+[l|`>D1AlqL $I;F  6!>.HH} Wd'\xyjR+;)%,׽+n$ԟA]@=m_tkɂ# ir^<>m<@ni9cn2%9 (Fɮ  +[sOĚ|;PFKJ"%ʞEgPe~jgo5jpK{PGjh:^ A]OnZ醕]m,ɎR s暹f7o:cp;ٿ]E:渱}w%"g<?9g W4\FU• @ܦuvDvN@:u  i7|sPµ/_hUƕ'lWbI_Topk|i fI2'q8HkNG@hAA>h.~`x!9s +%喍)ErG-bބqāϫg[qH w'DIo}:;[=̧wP+wxxvZڰE$KvwxJ UUAXǭ(׈: \b}鵖|O..׳^ZnM?7k&#+]l,Rn$4ըӫ}|'OW dvgQ"} sGWbO};dwȶh]6SPΦϚY '7]<]alp +>-YK dL&s +ö"L| ҅X/{-++07Ux֙~7|vbyؠ߭>žYu"zdrMO=I5-G:VG]5-Jz{J'D.\?o +mՇ_9!Ev$·c]TB㹉mCa1L]+k}c̃BólXfkgVܦ߁FRհ,;ZBB#6.6OAFcZ[o\ە0[VAI:9vRwd{ֲg$ :~EEHMTu)iSC5Y㺵 +okYPaeėJpgqXqa뱓g+V, NѬ ;.#}BY.rTawz'r(Ӣm٣dmV̜JFL ;+y\vWR:31~[׳0v4΋iZ)nϷsXS zDl[ݓp-k#ǿ$sP_=m+ +r6'%rEmf>|K +j7ŰA[ڸcD(3\Im kqw?}X`nSv^Ub~dCA:tw=% e5p୉8) SR²8/k@Of?x< cs ݈_,Kؗk U郗mk{kZf7{RS=WYD˹wbHc g^·5Mn<"&w/8$&z~ 9xM#WH[9"l.t783UGJ'6HJ믔ѧѱ&rh:oA[5.5k49,[2Q3aM4 + (9g-Y@~ P&*}⠀" Pd_ Ơ:"![W`%z)&U_#ΕH%@;3oJ&%#`'*lz\b*^RP9@m jf́OPJ@ T^\|udp*%Ix0UwrbGBJ~N<Ϛ"c8ĭRgܑ8"%`|Ӏ.U8J#AMR@P>wfxMS$7 +{~4,΁.eT]9]$VdTt"fBhh_wj1 #,IZ@ft@uh=@MVw=:zn.醘]\ڬ9/O7Ӄ jJXv|= +~%@oFox{\Xtާg1"ǶY=St+i96r^4澱m9h@J| ٸH&) ٮj8؂6Gu`1_Y`VXW060tfb,ɿj$aWumo>6 $[Dx. xV\v5}},^m~eϵ9MCJ{3oX'ʭ(1XekPk[i d'!'0MDh%w KZ}; +' +~@+cQtQӵc֩8sE&rZJ!/ Ta PJ_(/|{TBO):P(6 P)9gšMp~\_@Ev +3r})Ҹ況c8aͭ<9jBHN 1eY-tؠ#EPj!?~B7=n}KAv<tڦ tތET!.E-]e7LN }4u-0Bctc`cOAǦ ,!,+$<WI`|XYK2\m҃jzy %{p yD`nWL/ç[;r~a=}{XDpy>'Tjk&eH%(lӺzv0V"۹=3}rWj&_{'BXSZs E4 zab8 ZRbLf~lNIcr/U& L-<~YD l}AGh"SA* & +HW?i)BzS1 }' 6āL0FV.4 >tiΥ?J4)=[ O_=],C9n[t>=W.?ngmM]2?.a8,W15Rwƴ73hݒ>gY[zɾg]f0R=-)iv6?KZw> 2oVW+7nmqwc˓fjVފWczSS(F~,.b~b~OšiDݢq(w7Z**r:b'a6NЁHc톺R +VfB]}H*MvCd=8+lu Sŭ- OH$ +jX ^qkT&:#uY68v~Yzry8Rpt4K6cbBӝyɯ6_KC't}6ue:IrTߓ:C쵪a`w]?rVg! nS k]rjd:n}?4nR1ܪ Cțhw9vp^NMNWh|Ŕ=}XQӯ+£щ; S4fu]FŬA&oX&ߚi:Ë+}j_xUo|+8a{1F^ա5ao6ӯ&AV87LW !UvK8e/S22ɏtEY 谮\mk˳Řd5gP #Vܽ|%ԙK:I%SKNc7egA *[( v7NY`7 +ÃcܦϋW ٦قs%.s&a64oo;_s>uWo@؟4r6HgɎ^_\q1?l\'7\kx1(˩E3ՁE]=Ii%eZQ@ߴɏn}0uUQ?9;^ܮ=<1wٶ!_'2IL_'{!ӀttC-~.s1.Oip&!R_nG'{*k#2g;QBK|k@WJ~Crui7BA) 97BؠvPOKjҘ=T]gmaT3J)b9W!$w,KQ `~e9Ϯ_y1I>ÇUkb\̿${Sm ҽU5nqHiE ți9}+Z쳻Ogv!bJ6djKB.^]=3\*1xfd +W.]c^Fy6dz3w۩*рvcMM=xo!=fʪlD`!Em:$,r +";>&Xɺv?"e~;81t LYAhvzaJBx0r3`8<N@Ƀw*!8 >N'27=k߯v: 1Rp4}w +% t%oNJٸ['h +獙vuV8ĭaL54ŋ 5Frywu݈ԹpAb$oq67~ÒB-0% Jt Ⓥ鴼r~>uh>bHZ ?M,7V=VhZQ6 rCǐ9^cE㮂%NՇffBk;MH+4̓J(W' syt|km{YDLmzxTW}ԳN6=yq]pUlmWBy>! OZEnT3>?WY{:-(:ֹ,UZ8?[z.=I.9|lluxlM >y"[Bݓ51D7.-3uQIw_Ma:;kʗq>TɁdy1XԾ-\HHHR_D[WSQbpQdQj`\U%aoK\] -۝w=c~hrF0o5WP Oe⵨dpӖn2%YBX6P'zLCr! oxJ2~LYQde'7U[1gCX*KY9ltƀwp>&`l$KTOv/ウlmb~)Nf̪]l34J~z߁9fH^&@4]IJV@;u&jvn ,VAVlqU4͝f;xPvƞ|1L8-[[Ŷ;bXc_q{F_WA&n?Ơ +/ݜ+31" 9!g7Gגfw8!za+<¡'DȭȑkRtn>N֦׌Iw;^9* qO+z/ߦ/G.m _C Phav5Mp|ODD/iSض%>XxL7"z*U΀x)(/pvqr Ip.!uwD0*Gx0ٔɺ]lFvc"Y(`}f+t 4볖`HyגC!k2yW\)HM:j?JtÞ/T|2wC{7g䤿J9J~C|姺XVn +Om܏T48xpqɇ˶)wA\# ͍y!7{+Xӈ\cQe漚tN»*Ez!./j?nςߤd^( +dN[[~Ѥeh@\DSKphaW>.WF3 +. Af b!R%yg 2u@}x +i(m\9 nIR/.VPV3cs)H7R OWb|_4SQ91Quahj|V|_o߆6svYx%?%.pR _5MW]mo=R_@iދQkbrꐟZ>;=c&'GfSq3Q]P$XGl9/&r,Y4B y鵗w2Imd򰐟(Q2KAH_6S- *ag?]#vM8ш!ltr5p +-f^cy]R?Tm|荱r{FU*}>V_ƴ]̋fg);y:HDr0&^n У]Oj`yB5"hșaVcC2xLȜ~3p4Se$|Ruܦo'۸~胞)غkm$-[axiLJp:Zܭco͍$du܉-z=yzJV\wQͅGVe&&Sϗkp؃#d8p!țj@Rc~>nnƔqt+8,u}T])d Rl9\-cu 0c +endstream endobj 388 0 obj <>stream +HٖJ{~FQp@Y)GYƧ?8^ +ZẗHjB)oMM"C??˰?c5"NY Rqá|QV.A3AuF^,z=7=`cذucACZ|IcPW͠7v.Jb,3kz I xD3KcƎ,M*+SlqbNھ`ҷA"."2ޘ3ƹeNE]C>x,I$!$ArC+?D(NXv~Ob S~[7%x v7y?nz4I{˰yuK88 +`]lnʫS fMoxm9veXN.?Cr]*r jm9QMk +E/NҲ^(83XsҟlWc1<|NJ( j DnϼK ҢX[l(giB"98sCL[RfS8_izi]O ZbnȢNme@ww}TXZQ쨅4Pl:1# fNs?!Sŭl܌*Òm#O7y%w# m~cxb#"x& R +lo3Oni,LBƳ<4m|8k{׷ b); +qmkBo>\Z͎>˧[ *+螈SϺUFvkʕCars9ry4^R-2L(߉ꢛVD)8R|7i< +_N1o׉z#58Z)doI˿Y_8x)PtJ l5oz`S|"ꔅen+qd7o4ԋb?*_S7R5W%1iN7L"!KJK)O]u8e5jy;026w0GZyvnmW+TGMk`H|< nk0>Оn}>}=f7`Vs10=ؿěm"{bY/7uzZJh|'x<>;EnƬP>yPo7W{ZgHmud;5ѵ 0MawNnjuFs$yۖtl? #xTCvւ57fgxuc:V8јH;7YGeu~cޘw.vX\ϱ`˼fҘV4IeY1NIPzʇgK8z{ڼyQ4=~,w8ok{ٞs4(DžtyS^W`٪ v8\AX ET35{]mE2*322ڶa" ׭^K׏\GIJC5ԐeCLg^Eb4~O{#O,|k\-O1EȮ3{\B3둉i 31SX3XɐO +pR/N.KDce1Ǡyjk}dycޘCժjډͺ:jiaE!c99ޡŒ0a&S&]~utk\A`b?6%B~¹n-u&JbS$??E3P>C⾱`Uq*07nzꋔ^D)Ȗa_tO>7~TjQ 7ycq;6âM:D_,pr7>%cA6pob@ْEdRp ðF7(';PMHGNDbp@ؔʆ2ߎD]d[ۅSRaZ"Ztֿ1obf2Rܔuzބr %w6;zNh׬Uu + (*"}s70bTm- n8T Gl@c _̗YwND1EEPIKPϙSŢIw8ioUV/ő7 | +Fǧ|ώ3քc[~) y>JbR.p+L +mW_!(0t ;J% Y=9Lc\Cz0[/K᳠uK¹'uwZʞl ӎ.1bc+/4F t(#*&w`uri6\At3H.)]EEfyjߨ><&Ԡ6I@0?l2D)FϮ d7zk4/݀뗔U?꘰F* +Qq ƃq,v{V¨"OgrTqIuk \)<#q;4Ѓu̾ g}?"_z}=Iӹi'ěM^ס>`s`ؤlcTo6MQ/ۨFGZᵂNױ,:RfenKxkgjڅnDUN{oi/TiΥԨO'4nhK'̥EWGy *j1;FBduAO +.ټdNF BMN"ů#u6XC\1C2pp>˲F9QDVb$QEgs9Ip_1O{60 *5^DQgHMT/&9Qaln*vyr .3ԨZPi 2꿢qecͭfj[/1u(D |X) +S;)">ʝ0΄tTB},ˍs/$lMNT>ZgBRuT`n~=z4F#G꨹8QaY't6VM3;R'V͵TG>6UGjta` P_*Ĩ4MtsavyAmW,?T噬p: g'ogt1يǟԨX"zY R/T+T%h[<ڨTը91hԞkLUjؓ,>ZEͷ\%/4bjY% :5ٺyص]g刔{x-H飤KГyOH)7<#8c+9fFۥۥŸN bO=St96iQwo6Ǣ{ +)m^F,rwФW¶Ht)Jڥjg{vÂ>0Ow!ؤM}u|ڗu-R٬(JE*e_ҷS4+C?j;^2RZes.!2] M9]ZOۤӝ2s9W CAK6SiW Aݽ童09i'i _7gs9iaro:&gjXMh_\B%%$x8"0hq̅AFh[]V:Z pX:S=B@)H*lR GNkb~.^uZM-,KG>̫nԷjx `TЇ;I]>ٽo õ +ݓHzF6QT% ElVF-yU6uqX-e{m8tzbDsVsҽ'_v' m"ЋɇOccvt5IuIO:9;Ժxvt*<uuv)0$X~aǥ.QGat-#-=qv u ;D/Qfka%|wOr>l87>k?>s.U- ? +*0@qF4(fX܈k}u}t1[w=adq\[#6B>{\O/LS!de_LJKLI(;33Qoў5F&2Fz&QnfRi nf5w.rM7C +~^jKWʤx;HD a=4ӚZ&݀5GQ8?̄-{FXaY=D!JulnFl.AzR9"I^7U Z)p<7:檈׳չ hj{s~ÄCD7&K ޟи77%>m:#GSQi|t{M}&?4ku}6UڕFE NL  "Ha`OUVU]K~unkB>֚s-!NkL!M/><uTy֤{%,PпU W8pi{ۛ>$X4v iW p6 +sHHd@E}=kfoEb\k32Y_8_v8x[DRP^ ˇ$bT6BQܰe7Be{<|$)k0? a!}6"NJx@̵&aB?89lWY1Hz;vSyN#u1P6aXdtϵhm<23jU +í5im;C +/$:q +DsxdlƉz}NfpPՑw8^4x5꣯qX(>)J..}B;@]WJ!vR +꣣/{R'\갛h5=?&Q& +'R l܉O \7V_:d17 +EP`04O>31{^yA'VK2/CEV%0\E\~wZo 9k&kBPi VbxG)hПgB<ƚ=׏֊YďYTS7Qa7}tLSgT yD9]{Wn}'؏P sUji#q-t5}@ci`8\o8k +8W!qR4%P,(ӱFM7Msjc*XAƯf6CnS5pоθbc#yl\4?ێrFydmif4Fg/:ZyM>`e}ǴV^ +Faٯn>*wa<&SrA 0ܨ㌘4,ڗ0p#7MQ|\=C+YzezVn~vH;6s-]TvKc\MAp̤O^8k-R޳9[.V+so|dl~ca5pk{",@ rxM(G"݊95zͷ~]^c |~ոbǫP*\[G\8NNA'Ek]E1=kaix%ags׋8FB`|2l}}gӇqdКܜ>1ãIS%=ۻ mD"x²L&Z]X{{Ɗۢ{ V +uCge qKj-ZS KKRŻh?εuzE Z}n pⴱTITD8urRիWR9u"EK=-=Oآ +<l|}ߤԆ{& z+ɐD,<ㆥvZ,$9N涅]wwf2FD,ԫYqR W8H.%LNu'DZ4Qe6ݨ̝w./vJ1#/hs($:'YçӏQzժE6+hPi.i§KYײqW!69<_AH&zL1 +_Ja0&t񸰵ANV]; V\+=qZdY4]4$6X~DxtBmh Ÿ(PohkEŌE川lq<ݞF^B1U< a6H<tjOCnTѿ{ ǽ{heCX $r7P>N=RE;Hyξ4 x`Eci7hXv&^dI[QlLZT340Uo~HnU=g!nd9V D3 +EqYRDŽZgמOq5QntmY98> 4;넥ӄָST< +rB/Ⴓ~ѤKS'6TH5 +tSѐ"+T'@2 9奔crKo"4&GKS M!^֚[9yXZ[akŬef1K9&~ʂX%6972- ?t=>dh /]ȔA6U)e ,8FM6~O҂Jjb%Ya'͇!%V}#6c -C*}b͒JEZ#aܓANH)7;Mz(q#T8Gi[;)Ku\z3 L?9K I`dP.H8%򁅿mA '=OIVF6zvC"E +2|@)2("z֎t-n GΏQ2/2#,EYqQ?m pwfwA{8AEzQ_PsfX\ #4ޯb^$ťWFVK2ni&G+&1!fDQbT<(iٵ6^H1R(D9;:TѢbJlkP2-&A3CQ)h͠ lW=F<NYe!Vh +`XK%Dc-[#w$P FÞ P_4k)BX;ھBu +VeBIV' Ȧ{_6钷7#4ElW6ΤC.i'bQxnuie6§rO>t +vpc.اHi!VhE#dY +GS@{տ J Cx0QȗY*6Q2(иŒُuh*ra4YهU TO;GBEkb&Q +r:{d0R'hGsP@=eâ){8+xHFfFh::m6 rcQ⛧=N ++z^0ʝΖ%5F~-/Vh@J[u"s6ESh_k>;@qEZ00 4 o}QA,8[n-ÀOY` +;STx&d{xl:͡42Wb(P:;\,[uDah~L7qD|bѓw۠Du-[|ɵք-cJky>F(ç_$ Wcҵn{R$ۀD:Jl3]L5svG"CzN\y~_/|QC5Kqo߸Biȋ)U?:"nDA.vG:Mt*eߍh+vow/&JΨ3F=Q}X~I'75yY߻r؋iMrKP}ulsl fZX7['0WS$g *_Uf؜]/d%s2GsJ ooV={dB-qFA{k-hyO +.wq/lnZ,jG$++tF53{tҩ-v + ݼ9rtRwTP(hz{R;~NBNNWЬ>iorG΀=r>oӓt\̾Y˥f$|(rA6#oM+ 4RlBŖ`Y>< $ثml4|M=S)6/_Ρ5><!} AXԚxs9^K+Bi}AJWzZ$i!ZZ6#TnA̶VCSpJ^eTi,f7 +܁ѩoE +)8@ҼB%@h-[W"C'A?^} ǃB~^g%(F<6E7=U\`J4uQYΒЛm{J PjuDCxtv +\3&H1,y@ٯ$5ByѿTW; DcsҪ@^ %Ӎ8m@IR@s1MlEJʷT,H }G'2( _?%U3DIKfG-bӕ)uI+c1Xbh~$}N[(3)iy47ӬAVV]M0 潸$h$ E#9ј\p KC淖2^0%=]aoCA!љ\W:UfWlc#u4CSQ6er3 +\Ҟ\(7M.Xtv}x@PelW}7DU'2&LҠ>3jtW]{n]+As/i[5^EԜh:a4ص1pyO!`~// Kf#BZr:obn)=3ph|lu^N<jR4Ff+`-h~:q11` Y<'fk+A4FHUpo?K>E"a @PAƢ7>k> f 5S a씘`"npjE/z>T5zjbLT)轊fp)0q/7Q\&V}-,JQS4^>wkV>dr 4\][ѭp\^^[|䦶">&|@0ґP |$镅?='\\+o{SnZ3E:>p!ԩIFh=6H"4xlh7"Oz#3FaK #Oj>stream +HWgWN}|$@i?Pҫ-AQT.fܙs(d +Fu3!Rwr,]o)&hGgC,7QO6ЌD?{#i:-#8ŕ& M0#fm:Ue^Dt^ KxJ ̬vv1yy3l?AAEZJ4θ"hxJ 2$R@®d1a`4h[4,0#햽?^M|#5~8i# MKC2AVq VK'!jkLu,Lv/-"+d]K `yfcᙇmhWmr8Ǥ9 `gJ"qUYTއfߓF@}Fuz ?fięz:0*QB x]$J#MO:F[ sb fpC3b_ĸ5㲔>3>Uz2*Mr{@6eN+b(g+h.-b-өXrC@j Tn=~PFXQm(SX[Ti3nr0>c 9Ukŏ-U1ch! 0IKćWVѡ˂&U}m WuPA? ˞*U FfD"׷S-CcJ}58 +1Ƨw|sh.6 T #OxU7!U4h (VC$⛈U5b i1wknj&vt 2կ%A66/J=m R5/f yQ^e3:Q)b]Ui\Rw[g*fB:>M\v*YӹC^M^uq'YK3 ntC;WTri aD>.n4Sëӳi2^$5RYuOn.VZ89w[vJQ@u@f> +p *_K:mMѷ~bfb|PjO3PYx?i?uw_ Zvg ͏y&67Y-.:CVofM~礽~w + +^ +(uFVX4vK񗫁R +u@(\NrG{Z*#GwX5*ԀŊN*ZrnP(d(MY7vod߸Ih<$M`}zPbtӰ8XcUrpt#cȡ\]qПHk=K\E.F#4 + z :?7AҮ&gpr?8]quFl66z=ˮt \G;&SvIh0>{\@H75g5p\!5ӸQ eg j|awo h*Fـ9l=Vmt35&s/ۆI"(}n(! sh wkTEV!]ti=xSp5< q6D5h$_T܈:f^hLJl?l]E|Cܪ fci~aEf*C2m:6ώ" +DkH/R}<ƃ|c+1. + D0xzs˽?isyePEw ؉~+tNCgYfV[TS}5GJNxP&u"/p ,REaıߚyhc@ <1?]}tzϨfggNN T[Uow T x Q݀1vVg(jc[˺\ʜ*k܉v0n'Cz1N;hQ l5|֍<,+b;&7PDxtIbrU_r͇x[_&\(݊2[RxJ>젟r>BLxu҇A}w`bD'qG_ X*$(")-q  + _6qmnzˉc#%6Z ׳nD_NcTinaꖳ8l0ٜi)-iD1ǦJN͹Մ_^8 `Vo ND]oߛ}zG0៨r+92+zc󫴩rs@"iUjkHY_(P{Fy= WyRV/d6؈IWYzeNF[JoRp=k^zs4fӉvсQDS{UR_mJ?&%jX + bsKl oV +R\w|8#>Yؖ\`.km9 66Āp+Ý{NZ4>qȎtگ9;qȠӖ! f[uˡ/B8{ p U*W+#~]"|_x -|UlT%AP'~*p ,REbݎEg2znZ#Gͩ^246 $nichDFC{<}ԊL9^s:95/ܳ.-`7k8vS~G6 {P+.X9vl h1 M9v.)QW*k#\7{w] DcuuoG|$:#'eenM;MxUIC0kßЋ|ʒH?Nm;7ve֨#/O_˞E11yO#f|ߡ9JW\ȇ8E>0Kp2E>0lk)ဍ\wVؐݹY?Dfٲb$c~mrWKZ׳sfp$Sik1C?Yo +K}ciX& +ϔ5X' })34M{3+e[܆zG&7=S9w(ɚ0o0~l|xӐzzp%+?(ԔeChY*2[F[]dMq)vXC}<4Ֆ8,Z-3tZ+W!́CViv='sA0`PFUqm~@%:mJԛJ?bn5Փ&786|3[h>:[ާAZ<jǜvuf|f9UC"hCqd<#QhNyz$U˃A/$poF0$qf9LFO^4{a{'%dѾgtjݐUe[Ƒ 5YA_ĜHj%VU|5Yhʂz`P((45vvrJ Ŕ&Bo֝/2:IFB%35.F6sarM_!&MF_mIMA Z'7NMpijLj9 +^r2kԜ|in?c:yKPl5IF|~4ZWT,D6 23 7.@n$ ^+Rv_X~(jWF `rX Y6He*̧ ZHJCYy">=F $'}]چM / vg5C>PdJ1bPB$"Jdڛ8"ڽC+<3S:mȰ!ÚL!%:^/p!H|?ʅ_- kT 虽4~*h)za&ΞAT>%PQ휵!`))炻IDRM4rcpVX{L]6DŬ*ouT r3u; @x* =c* 29Ve Cꇑ8"/~ĕ$ C.=ylN.ᮉ&,0vt{On>)m6@4EKT!0Jna'|݄E @T4ιUbO"nc.s?gP|A]Ԑ¶a*xtJ?x&+(k<~y2a PC(In 9@1D?6^T+Ap`m 77IN< 4 +^і"YY {,ܓ ty$NxJ_qwnlnӻM}R2vY3J!@/J)%R> +8@84>Ŧn!bltǨ<J6uHgȤ^;MW E>=H6A:q2 +uzAgV:3 +VnhD&>YdQDUMl-befZ[Yyoޓ'OB0 +Nm "

S;O@FAksRil)>,fԊ/ ƅHZ$hd,qGrZ6H~{ٰ.4 bzAWAr+G-qj)qllɡ &ZG{ĴPu8\U#1pI ف|#RO?_ W!#S "Jg3./_JHT ͇qJ h fwq +$Aј>O("ʪx˖5˜h8^TE 94{[룕j_qo<aL=:KEDs&17aT2QFцq?$JLC9?9TP1=mڋ?a>>讬7oI>ŋԼ`o30ȦIOtQws@WT(G;2ab%y58#l˸\iƾ]tYddѳeT{k MuylG@A0/4w.hxyS83͝!}`'g)'i sFP2n}Sicm]}IR]\3s},+/Aۓf<\~y?:-9f@ {o4OI-:@z +m. LI5Ԍ)۝SҪN|KZ}^69Jֵx-}_mF֠AA`<쑈T/+N:ȫP0 :ĵ +y + +ٵ1䨿0O/5=3Zlay?:=mX';7 4ɛoe+Qp6jjL6XdkrHY¢r6T>.^I)yum1F hNMw;x^UE e arS@=KevUXQvm/tm`tT<$"r?-mʑ=:Cܡv ]#WljGDo0EbY2I@x"nV(!(\~ '7&d$Aa}B% L϶Ծ"qj\6cD.ܑoR`nf5 /k6#/VL;~CFU-5@p]4۹8g~FP[K/iyAԫ2O0#0vwM-~+\\Fmו?s(Tm"`f!W%E5@*OnoYŽ'RsYRlύ$Af%=랻Xϊ.̋匔z,T&]UFeqn!{,,{ztFR@/gZۣT[D9gzIE-eDNm]Ǯ zKI%U^k7[ґ"<LP ҝS4\P9^/v7oB COջ֨\KQ38^nګ<[ߏz|29pZПP2VA1r*¿B"Y{.!JJ{>Zuaj~{,jLӛ[ +_,jjf=xzX+*:(`k(VXvֲ4/ަT@R#2SAŜwC]Vx>5?M^HQ ٶ4/~Y'|~nt&:h80*JU#M֠.Ep>X;.;ѾrLmDx&esݑ>VˍbӽԌ6 *x `(ʍw [nf$qdN(N6i9 + qAȇ#IۑIo;G;cAdz v +2 q9޿Ǟ>x&]>>Wp7γu EO3c+5{?S`?]u`NyM]2־:R/V#HMSbLhlr/Tg^|a6XC+NR G *v>e֑GR.kx-cV[4"ESbMf!mi*e4~?ꅦ4 e.__VknZ\- LZS] Y7W3uzRL^j<[Ms&1~,l{lWӃoiow,:*=;u8{Ljƍ쬨P@)7RDtɍlm{W_&j$rB[q[/Zbjlξe#?v@l*W9b1((^7yڧc;7h WS.inlϡlX $6>sZw\ScRPd'XHG`w4qćH0Mq@:Gmc`t7 Pbѝ; ~_Q[wSE{:"ZB ̊>9OQz?#?xԢ~Qn|/]?YisthLx`U/R7G\cP8[]d9cEYU_VgD=E\XU@mP h'buѓ({G_΃*BH\kO%H~f>]YG~e\05 `B)G9JxdC^%nz1D\8RzcA%pH XvdjV GMŠBvB ՂZf zGN%߷3OcJ6ωL-.jem/&R 8ӏթv=wc)ÆKGkե+Zf IeD=Ԉb`emZx i} ]:i-<>Y$Fli訶BO57}D]{L8ѫl[ V.X_.>\A=rd=?l5¦/fR-lWp.rxK6'b+4:p]_-+>ڣ +!RO)SW7p#I2jn)hL2l$e:y+O}K0n<*]߶/ⅼEST{sQ֮7_=`fm7~m>(!:3? =LڊgGF ]˘fڸʡwuddT~qא-ȅrw*BQFp2Kc^?7nBgt_=8>+٤^=*rڡ+%uF;]C,9!ljB|:ꋦد} fhb:7V`-}c}D,E!y:cUkR6\qLImlϞWZ#7%@="uGO}q[xFn¢X +YT 8#wY@GGJs]SțdixVK/ѼF1*0HS(`dpzGf)37^ж"73?hysզ8B7LKmNz;U,GZU T +nnn,s?]ڕ0WnA~pKRM_3 QURQ4"꼱/bXʜlz+8K2ǔ;z+z@b/By kVH`W`tu +z\!. t b<q+ƏI]|qz37`oJvo{1Q_7蹃LP@gW\)݇Sdтrn32?CS-@%@UOR[iGQrX<=3*ב&27bh]7y;0v qژX xmJNqI ׊Qa;K6U7ç_Sw[ss^wB v!Y%.EȲʈx&Er$Unα̭u`f-p \{y񚎫owAt}ͯdw5uo+dݪ֚X}qcg0Hk."9u^I"ƒx"z7Hy0~ x}o8Нw桏7W_{Y4ȵĩ“Ⓟl$ɌX b LHY{;y2o.bEqULJd V3< > v^h ϻHzI%g.4#Ȑ秲9e:j)*0(v_ fxȃ*Mx6Gt56N*aŲqK &o$6BE粆 |bmTKC0x=k" Z/MD;b6ZzeupK>2Oihl'E9BC|]>dnmq ̢x sY `KvЪGCS}VldZ0 m=.BeJc_ +aRh i\i*srU9457`9WÌn0>#m =[` \DBs:b-Q'#/>;u+tm-a{=Vlq>p^%=-O}hl_5zancŽ1͜~ѷ[US&id 5 +' Ш#f]t>+9AG.^vVIͽqԕ gbUOV dN+׉kAk֣ +jZ[/|B=]&GZa)3Š/n u L <Uw b +YqJ٧ I٪be^ANY-H6|qH!M|.fESEuRd>qE''H9Cn?A0\xڊmZZYإ3>#>JR칬/%ҝɼ)>'м>lΧ]bHy"nоnL!>].*n4hVAKm @ L䈭ײ5(<K @AK}m-M+y Z>t8"5}ӮkAWFY6f}M I'lA=0Fz^uN*\%C +g$/֢9dΪectni{eiO Fn\sƱ0F + +leY$`}O.Cw6?=dm782832x\#r֋iᑀnR8]Bu}2^~nA/YV\ݽ M8+?dIսqu+eYW޻s*acY*>py#鎺kΌ\=GV.A͙h* %+afuUY٥e0z僝䪊[ OhC(^Y&Gt}*Zo+f80j a΍{Zt,XHʱjt-Z&X%U /jJ| ٙiE_s!"J]I0ґWL>k%LjFiTϨwrD⣐gƎZERdP A>-8z~7vw:ÿƿ{(AXBO꤃jWca2FIbWmtM$VWq \-q8rg6̃x|彬g@π14$[(Q Ž.4T20QbU4#`Q'kuw?΍6C!qH1+ ?y.`/c'G +l}D3<^Ξ`pW3kF+@ܲ.+@ Қ? #Ҧh].yu;?[Ზ}jz0A>(+= @i˫|m9RmqnAع{ Y. +ؠZdo}OhH=jt(6hYFVg;`Q}fn5ȴPQ^W2Z3Ȃ +fj;մÁ"h.Q{/[%} 5,ox`ta6վ;,x%DVޚ,E<"ܘAV1NyNVJ~vn~r{QӼMQ9Jb o\f+sĎ8rMKmTfD*i$ҋ+ˍr#AbUJvFiQ`[5VFSˆZuTD-3c|" ,s'ɯ#榯;8#iu%I'u_WTvX7#Q{ɣ0y6whh>_Tqx8F-7rB4c()Ȱ\dq:7gZHdv6Hxv6&,Jc1^ƶ[US潲u!e14h yłpz:<̄ (azr '_`q%vāud<`ܽeхhO#md=fVsU8`b|k M#H3skT]F=4W-7ԏ5twߙB{s龧{0@hb\]8 +~􎎨ΒuYВT̎-PXfRqn@l^&&R s۩_2.io:5@*)QX 8(Ӑo\G`p n vU*2ush~;{c3]NK^[2ΉQ5u&S]}n(t D:(s%sVYQ ]+R =$/1+\YEurikp/Fy]p}G%^:Hn߁7~eO{YMOS⭨:EMN`6hEL.WK x˒Vn,g = WH,^']E˗_m구5%ҜǼ#^:-s2jd:^ 6s-^# Z-Þkmp76wi Vz}d1K>FT,5f\C3[&KthZP>j6!.ݲk[où.]-MXm4O2-r(Eεpe9OqcՕc 9Ǖt鏆;ghO x7G7&9n^c&H>x2 z FW5GH,N'&?Q{UAhdlm({vhMt|WR<&k&E<.nzOzGkYsTz}'~)zej_?wRI]Iio(=Q1B=LQ+*hehhnuJ mXZmEhҐX&8`=#.K\![)i4F +E}f223adɌPKһ`N6Xo9@HGۮ 'dNpICr@#6 ii?ѹW`~ *wc zb636̒ 94# 'Ryz>è? ؗEPY)?ΑюqHh&0ioc v\%MP8jmG|˴;*̓yk4[DkϺ܈+1ߛh8¶*W+ׁx7pU5ruM^k9P"AlHwlYQ/\^M&ޖS|rsT\HOugAd15c{y!z4 6"Aud(d7vJ/D 2 :V[0*T.kd[aeG:1lEFvqF[qa%]UҌxZuryTɛNEAn;@(ŘӚ#.K>U-I)2q1 +Lq("r|c8)"kx2dØ\[5@n|p30Y$s 'fbjciM60U(US[!i2R C U*,/J>F2xP?e~cyܠ-Ǟ|h[,NS¼"XRh )Z \0R Q&sjc +?%r0*bNUWnmА4.+@Oav&@%t1pW5 +6ۑc +Q7ܬaDFqv=9FmCMPy|^}{ 1g1; "+zC"}cV) *ۦ$&")" |ёiyx)VxokD(̔RaZ) [WzE?&i۴vË}Y'$]_pXun枕= S=aKXPT薢Kj")c.C$գ g lo%*dRطPX_W7ONAu/Ҏ"*6VVn[@lM7 {^|J} CmÅײO0ht +i s,oqN`zy#hq!X^3@4b#zK}K]# Yynw#  U3&(!2<=:ᕵ\A(3 ?p($7`e 77<)jH__rإjw%#f*iRx3(N Nx?:yҌ:AD)$dJqfҴ6yHojgPP_uHܷa/cid毶fOSKef]eh*gʥLBZ%%3f4KHsV?LvmZT#os)PX)E uyȲX̏Eȫy#nݷ7,$1 ?DI{7>Ԍ!2ATQ"~% 4zSX~lg\p ]fwզB7v./#9x35{Nפqbe\_srZǧ[MݿC{)psp86g""}_Qk<ƨDvc<ٕ5ՓcqyPm`U!5X~B^Sy"X^dGGNr"j 8j}s&"X#Ž9d}^R'Vkqu3Eο*>䩩@q7SU)]\>UE˘N= L/c?`sj|@Z3i5GJ+.`n߀RXYՎ(<GGhA HɉNɋ* Z CP0K|!'1ˉrM^틲Z:4D, 0C"8bMT_;>0Aij Icvrr)tҺlCAMp{W,ŭӕQÃnO#u/^{jOu6/ Q=X;uk(W*"hsX4f?lH)/jԔXx_X׸+n6,#e漐1[]~lHZ(c~)1(Ed/P))^XvʚmH]̹8gyM2ۗ=Vjh>F/$/zc v˧t=83\f?3{'$.m_H+sGsX-G$ccW1dރ}O䀯uPo[o^$V]aoP00˺P{'[^gE$IVZ=T7 U} ?x.bM-\"~ygnwR$H~-5rHIB .v/ ^Ω_"/2J>]Cwџq>^2Ņ%'8u$əEOk5CXI/6n~uwX"0Hr^T $: 'k~lCK/ϑt ʧtۅ]Ծk._? @p?XЪf̅@Ae,w:܎DVh ;wh}REypNJ"߹AMH4|$<$ԙPV(Ĉ2jE}c qG 0_^~! qzOWu -QV:}_.yIb0ްu/2#/崅k`چ䚧Z,7+)nJCFtN8|!t2QZ ɀƦqS1'R2dF +U3*-jtN_tEL%h F-p/^;;a:-#-c,cu*Aѱ/x5+cZ)T;yMYD3,Gоayt+maud^{SYmfsl$CjAD#s"ƾ7K p]%Grﻏ Aa_mA R:b!P>v#"{>@Q~?E͑-ʰ4 +NA'x*uWvaIb7ekMLD u,ŧdQ _]R3;ip 4^?b?a"7tr#b-d^Nns1nVOjŒo`bcr܈8 AA_+xEx@F1@u6X*}Ea^9v"s; >V\gq\9A]]漛L+y=Qtp~:-|vu>eƗ7Sfn=[%j?RǷP-kW7ڳ +ztW!.c%^mO5\`*;YΪUKQcokI}5q]ׄ%F} W^ttH5ydJ`)56=0X{ݎ6=TW1wk_snM0Fm#r;'*`w{1 t}I~*30D3~e!:(EOiXm뭕6FqĜUi-C) + +$6o@>| !d[t& "MఋHqވ_/$P[.fч}Ed" JV`b/J WB<^Ӵi~ο3l%EwjsfDy +Um}߸XQR_@25uj b5&e틋䖆;5\)@26+h:::x<@9[woo~) 1N`>ud5Z*DT~1 `(;&. +r7@_2vi &3HBŚchja116MAm  F +endstream endobj 403 0 obj <>stream +HtW9?tD؃ب/_RU >K/C{)|؅Wcj/($:8 \4!ADZy n|M!fR^m3Rv^0 D@"r#x7 +<v!mhk1-[~j<,qҮ*H ,?@D> `"@.d0R"/~U :Hcљ?O=@,Ȯ?&BL=m;#/` ֕Y#Lp,Kթ-钇y!m؁3e8 bjR,x6 ifefw<_+;+O{bD<~ dg8O^*nsX~i>痔?~#CVD).Dg~*n*7Y)NߩK<'6҆sD+>|j;z f%d+ţRLN&\a!9C pX&8C]}̼K+Z\"܂x@cؠ1-K2X +uMnC_=4SA#>". :ʾyײvm8vkeW*/ٯ (A;s_I[й~UQjJ +>Wz~S뙵S >'ayy,]:^=k$H+n]nI߱hR_Ro\v6Zp :ςÔ'y}^vJ2tq"6RlZ {Y1PK`!2ċ̯jKɜcy=|<WYd"%c Y\VYp,c"۰!1)]86^61|PIj:B6iMr)3ZD.7vti6MM-#uuUM#7m|g(8zW{7CPWY ^Y}KO`x^yWBж{\|[LGmEV\Eђq"E9nZFckuK;BA*#jJuaIʲX.ga&u/RQR"?)w.m0'oVg䬹)yt,&sUL\lƚ 7 ,}MI Y1aꩥ{LD25ZsGy`{ >z]Tϻn]{-}sf^dSh >V> 5ɆWN !js beܽ2ջ˞ǼP!C Q'̤#ûkhRjGbWQV9R(} +}&gm.iJM  |Qb39'!D).@-z*5s3n1X,Np? K'6Ûz]`CY49"âOd!q#qCМ&/eDv{;TVX _Z' `RFDpu raa߃ُse+ͧEEyxJ.\ޭ PQ&ߕn| >h'ژ~4$i=#?C`|RV@B)$AѱhkL2:VbM]) +j!<؏]H,.Df$˳6g v"gbwLJ{儰{Pr9:H>׏8 }o'Y%Ɛ׀gդgUl-%ۻݕ?f+6qQy(F_8y`e[Ao?ӑ:ٝS +:jhg7'I3nc:B/ٜWMX +6bHD%f;?=sٮ\~S})Y\osi+<18ExT~>ciԂJ;1߹qTSFOMeGFILYZS1ȵ4fnX*U&bqDFֲ2mJ^<%Djd"yԕSp5>ؓɖ ԅ\ȵ i1")cY4^Ͳw3K%N#%߅#P}ǟm4mX~ ] vX+\| K>ԃN9IgT7'*tUxĈ5Avk<b#|r2y߆oJ©9 w>ۤl 󝠫("},D; 䖥wx%yR_-Қ[OGaS%f/kc힐fK42(:rA[i!p+l,v%P#EX k# RV=ԉ?'2b|Kdc+!E8lMREJBLTOhys:yAuG{[fп`n8ӣDr>*,.rqiV.d[`]!Ho7=$xJhMrHMMfz@VRL|!<ԩ|yJD+4e[Pa)!7Bkl~6u;(=UjRwv7;-*$O ĵYWt҆^MqˠɗM#J'#*3{5(>P_RFp4|;-qR ]w1KE$^}CzC` hWs!N9ۨcכ`=M,#K;O/w6':aN&(4Iݼ`${ٻ 䫓{҇687E_~D`nė4Dp- *̀Yc:29.;*tr+:l  n ^߀<ރp@p̮trC%^Pt,eH.$ջ SAȸ~8"ƸIzp/siY@Yq.gd ;/3̾~<.\;\ŊK$쾗ذ#D[3*jآR5۞tp͸MI_gy~j4.#éhq>ݺB +M y{HX#+ +y'|SC8QMpheńv(2cؕ\݈w~ؕ܀abB?چ5 8wQ~Eq0PdMVg;<\MWg->7f\i[5_SA94Gy H[f Hg}an?5Dz>;+>yan*i)vh=0= +bp s HlH.UR-oKGeTv@$GچIׯa/ s' F%l긗djYNV$ 3umd;g4TGu2$!d!iYd$b[V&}YyIHuv#>ذfS\yq +sëfHV8* ui&ZTQ|m96~ P8 +$zf/Y&S%.!9x +cxri+7jjZ㑮 j\/$Ч\ccǴFnbªP ͥ? = z~k$f!)HOQJ\ X4ҽQ/-8HsZit.!h$raj7&CzʷRd|chL^LC Gn5_Elk}nU @1'di̦YU\]쬩>m _1#Eƥ[Sept %`̝KQ4?&]4~L%y?xquƳ"n<FB@3GiK Li`^sCrA&I1B .bF=>Z34*9:Y}~N3_#N>Ztԃ,. t-a :B+|xVOBZ>jm tzMRYҩ8S+sGiX?pp)M YI"c x|5˪Jt8BV5ݠJcfBSR92>L#TRk_ T+;)p537:§e<(+`uj_kk +?in)!XcUdF|mi>5~c>_<1ڸNvZ7;[i.̀]iA9 L60>X ~E0;KP+>'t~m~q}k Jp~ ~EgS?0ZR_rBWu7dmTCV?TiJNsw Tq\̈`'i'id-f\ +[6x ƶHRʹi)p&89f:@-%pl]"uN?n׭9AS3/mmmp }/)7cKysN9`ZY~`#[%P0nqjzca L^T^(D׎C2ұI?QMK&Yɸr4{,.zn <_%I:*>8[z 1]Q~m+We@"EbpkU~(M9Z8ø,';fgp6HƳyT V +;3{xSKK6I0l\dрw 2$Pn (Y|ٛGk4yV8F]j|Bdxs녽 VXkEN55{̬5G +tŔ KX@֧dNvw_j3ȄHyIn wn #Hqiso=N3 [A=EtCQW[ ja!Y?p +*+ ǹZeG5ZhQsгSMf:{[aBv[@Ik{+>_CYB3U7ye=f$kJYJ+PZܣ:9B+ kMK-Yք m%+u?HQ2@wW5РƳKe=6dD- E%{J v8K8+El )u4Y;38p$E<\xa[KUT]ŴtvGbɖ ֱv9U7W),Ϫ٪;Mts +*m%'uy;>o9\thWA[qԲCJ3rJik`o{+'Xb+N[)Jw[@JkOsV}n&/A48a6Y$ cx))4yu!TN.bytײ+.2Besu-)`ūm] 0]f0ۅ vl.TWnAbY/^hVs{wz]>r]B[`N (USk]Km +bvhCfqb: ABeP[] DƲTeм©5;}$20iTM jj.w|t! +*؈*XzգB+!H/w >zmEbp*oD+7 +iJ#2h`T+ElD21u9~ƀ"(YhE&8r9@ +HC SSW^O4ij1{JURD:XZ4|bٌҴBju\Xse8y(dĭe{e|"k8~YMbVQ] + 82Uc+.#*J>0+9 c.ځDlHG2v+fPrBqI BBQo|}YC 3:$f̞ď/~0croޒ}p2"7nfi{&"|(x )i-y>DSHY j}_X>TUC! DSz MeD$B ZS.Vj\b0<{">!3  +ֆdɤ*0gGC,gB5Dy<"ţX, +5X +D&AWI_H +.dtql!ILR-fov?uWȘ%j-r5R$j~"˚ FM+DkM6e`$j}"9@OB9ghHDzyՐTG:BC؞xG^Cx7)FxQ]|ߋ:̫F𢡄4>֮HkogTLFGuqcY҈he +I }^F B2:J'{׬݋=z50|ߍ7 AZBpBaA|v:ٙaR%߫C Va +р0(tHY|/ ퟿Gk4?vfa6v.o8?A7'^7Z3bMYXkx KX@֧dNvG'4Xm𶀒ܟt^sK$@3wg[A:%1OV;ӘͿk]C#Y{(%P} +F +Rt)ZhLx6;\R귧/wƳ@e7:Pb쳄/u3`ׄն745 CV*=>ȩiJ$b'Ϸ%RRSUӞ5W*O>H{dq{*bIŘO*{oKwÞ'c>}/:RȚ%BW)a_;:W>߮?Dq"oEW*T*\<|cW;+9JcTc'x_vJ^LL FSAe +TO?/!uF w*QżSAe +T"RT^#ܬ*x% :Ril rliTG"/;BLCHηi@DS6(]ct(vu"u2g 4$ܜ dVѽloWfM.$S$i;u5kA9'h.2L26Ǣ8ƃŜ6r#~ +F皉+rJd3>3s>LԙfT^jv*pw*U·:7qb-J3>Ss>1̼}Έ+w*ʞa}^DX,q_H`1Wr+ϋHd]uRNeTr>\?/"uHQ#0T&pP=KHч;`; TAe>/"uJ f"oVaj^ܮE+v~depn/ +F1A}E\4#tE2" 0CHZd=.1H4sb۰IO4%3H|jeۮz]{'h~}JT2ԐhRsˢ+B޴6Cke! dfZk~A:|΄hD/ s S[Fm_=nJ!ܩ?\픃HG@*w#Hxò;jރX̮B,t~+&1IOjp/72>atF除?ZyH9x4lт[Ƈygi ѧz;8YoǺ} ayc-`Zn t4#vlsdӽ,`Uwkd{?xklY3’ahl3к}8:̶vGlΑF*v;}}W=ug\ '9}QHrʘx(*fW?Xki^t䐶E'^US6Ygd&]~!f{^lXfJϰF$aFG&#- T +itl{wŞ4Q@zzvi#)m)m< 2 (z_,2Eg}mg͢ӽ,`UOw~ړ&V?`$MY a6Ib=Y0u΋KϺަjK.]aӄxFupDZ=Kp'!pװe}@ٮ]f1;}=ioſ6- dBqek4#)m) ]ELiM/,zu Y.C]Y?#25Lw5fƶ+՞UMS.@5EUl<y}LJ̸(` ?'[ϯ3[ᪿ-m7-?R1s\z$Omhr]XЏ8| +Tt .|8sX(#;He<3Q$yc,\H9h6BT@E\o[_E?m1ZX7M4IU=PA$ Ma} XMT%yP@Lg"l,U&Y $* aEbla +Lh"7W.rc2dEl9f\2`LbLhQXQY99Pc)8mmq+[ Q7CYL;JQJ ժd2TI;"b8O TRe%dAH+6 ΄Z idjG0G/gCV8>Bg,:#l_!~FDI9G֖GhOi̤%$@,g>2u3.g ,D 8湼@S,Tp8L4&K)Be$bPۖVf6b$Yg/w ]QnىԻ(ou@${E.WijdS +na`:0*Q $e~N).a^I@N: Xl(,E&Tޓ3цDI^VI\)jhmiE'.$kR&pFK»d?OWJc [VN.2?[%5f5I"siKB +) h^$\]=(RYĽexI!Y+21K#T[s69z-I熪*1u[㼋uvXw?R'ĕc#3q@VJvƉrյ8%KO-N49̞_nDq+{OP~ +endstream endobj 404 0 obj <>stream +H엻d *@qO6 k`?EQN\{(LHxHȹK +񨡴KTJ' e_.>rеRLB/s. ܣBR!%Ծ jr^ 09ʱЗ8NKkHTX E_+$cDVC"g蓊ΣBQVIH E낋{EaL,p5skee%W G$]w-4whL"5|d Ǝ0ȵV5l3ہĐҨaf5җQBMO?7m  2?HɩMqNR/BzrIqN +(F4\$$Fu[ȣB> *iu8tNzOYJ`U.K0Yi&V F1֢vJb= {=!Qm;Gl7hQח'V(\ܿ+;9hNDeWWH[8}$$B8~9D*ƲZ/ E$L g /K*~ZGO]Uor@Llc2UB|Ϊ Q9As07 ׇ"_Ç!Ȏ@+.aEI_t+!Ɖ|q\Cٯ v@ve䁛h!rxMbmeg|iU6<_zh662SӷFsW}L(cBiF\'D)rK>fĎ eۘO 1^OPZcH3?k|]f4,%n#Biz|zni-iF!u?KCl C6"n#b6?/2FAnow߭s׹!]@;s4‚q,$G^-42OYbbh^2w?5Xq7&i +mwTf:ejI8{w%3zK~uGr1Qʶ8MgAlQݺju_[']wiԣ% uFE:c[Z D-LֵV !% %[gTui;cg^j7u"ux(j֟?En Vc\iN3Oח|>G1=[m+EyK%c=j+Q)M^uky-hZ&W8z 9 m^uykKnyMh6ϧmÄ%a%2+zImD߇ +oa+`BdH. *RE qABܠ5ot{%.O^9OOFdX,P|-Է!델y#8p˿V捸6oDYɒE_ q2@\Lsg7t'i*&<'Mpΰ[DaL\c6o +eQWMG2 d|n™tizm뾋>c[(vT*~q*~c|iyߖ )`_CdZ`f Jm +2%F_D2 p'(YMYW?Ap??}͊4_7z*p /AM#:M&a蕺ȟIsv8Xt8@@=W @'DG4; J>emoe}A +QeA&8Iu`'9DLkĒ@W^ x0[4 5(YW?XD8?MնdzC.tb[A.R@xP"O+A X[{1TiXa,Jfd@c7vH#iϝș5@)v; vgtMWC)cXИS-,TN[ %3Ž)9c" :Y=*|l R& $x-L8eCF rfzsyZ#RQk;62)LM"LL}#@UjC2*șqc.sF5{#B2l\,Xܺ!2$eW;7Gi p7n>;H:":B=ckxYFtvz)4-$LSS( Q%=_b'׶7[st`:Tk,T"%63.EuZgldOH;{v<60"2J6 8-9)ZL8hbc&ǎp>Cʍ.Cn5Nh'h$J]fxB>ՏKqAlX,d1J7˹+C'SEK.uR7#DOD]NO^@gt玑 ˑA + HǮ H]h7^ev^GN>S]sjZ0WD>zͥt"23Hd5DZ]I&f$AU'g nD؊zn o1J#E.F%D,a}pi=gj S_Nؑg`?>rj*V} +zɊs#{F%}S lW:m_J)F-y)uYړ8"H"_\ L0Q\4$K)|̆1[J4sb4ͻh<6[9Z[ t9_?j{g7~h33&$}{ ȍ0ьKzV>K-Xz"Toˌqˀt$ 9W; <̊~uX܀eFI A$Ԏ_歧HC\x7C8i(yq%rzV@=gICSK. pd:t<T^<&jɁg7b3Σ8e*?v62d^ +_| B6b?CӐ09vo xyЖx?R9YbVt K?Ϻz<_ߺl]9e~&U8ʷs~X@l fQx3ty{yoc_%2^rIm]6ޫF[B7W}({yM񢭥G yh-s=ƽ /A[olu2 N7h7( Xh;úI9E99wʀ.[EZF8;h,|y>FsT +Ql6۽ W>*lVFH"o pxRW${/ssl%y#m(00lbbƍ[o Lj*Q|ILO=EcS> + QPs kle.9|9ȿ\=0f*Ke鄪B +qGdAELʢ6>Zo}$\Ϛ/v$ Qpry- g8KUCUKq 5Me6Uկ+fbo4vq~#ieHW3x|0א/K o~M\%)rP}3 3 1)Ue6M:8Zm +WFknQba\֡Z\&/k60y'D8 + ?9<dhȇeR`%6_K +awڧCKFB;ێ;Cǩ{of΀átvʍx4uF#Ddg&e4iSAPF]hK1y"] h}6P g/{$Xuq =y-ebʤu}$H J ʞ^g1" +[:󟋨oAؽzr}%xȘuy,@t(AГ,/42)S k s)GRULWLȞ"|p-;̄|J)G= H=^3{*'ȬhfOXu{*ߢGSm+'P?BF{ϾWJoMcjwR0؊DǧGc1gԝy?gp{(e>2~'L}& +gI)#=<-$Y6an=' 43d8J77z=Hm]$Ȕ)0H@ރPDp>cog)bsb VB{B X;vSǶOμ):(6b5gc&kKMqLx6jfS]~,kSS+eCAs=JnO6BBod t\j%EDvOHKȢZLw_ilxA}suNxh^ ?}PZnX1A;N~0JЪ"A? Ι>y]Fל̪]׽ +odu>C7rQO6k?@)kDCɝ@|Jd:7I@JS<سmSxD_;fGh/̌whVF!pc;fl"@vJbwX.GJМ<=ZPXPTVrl񿇬^2by:#j ? 'uyD>#s 2}QHy +H(W LPȑbTIhIcv\@y-6"[QzqIYͫ.wdʢU$Fw]qEôa˵AәJ@N6dK5vMӦ%0tYA~FIk +R0s7LsW-զYYnٛť>e!gSl{hv aMel;l\7#]K,Ԛ!%vBy-/0GkWPE̕R|rNb7eWWDsd$ztYH.a-{HClTv¥"~d[`m*$y:@x<6Ueq 8l?8 @ZѼjhlwsUV86&i/f .ix);]pX|_^JOSxj%㑨c6,"?}cgho v4z+g@qh'Ƚu@>_,@/\!rL[cgl HO(c%'^s޻Sa*:u]J"ҍUm8|k4,# +r|2jbkZyK|"}vgF~OoBDq?!ɸfX=1S G1i[ca8-Xe 6\KwH^ٙ28G$MFi)=APc=t_`f۰M.vwL$l>#o`1r}dt!ęvaeV dR2N0b;49H R,yCsnUߵ(%Ǽkk5 |ﮍƱ:l8>iЇ1 ^7xh4E3ޝ#ݳeC̕vx頒VO"yKXReRj +|xv<)󾵙Ʉ878c7eݺ +%/5*猒H 9C!GQnheyNDm3#xFy౛}k.].Æs" -@pMs~i.$8~/}?? _82{_ 7w 0DQbl,5X1'0(&Gz)uDBUmsx,ȅ, OtrW!hdeAǰ%7FfZ.5HU+Z|ռJU5bkgdJJMcN}ı@ZD_\T 3w9E7o)) v%FaٙH6Xfɸt<`@0y[`e֒͑qQ +pZ!3 2vŇ>OU}M"R]9l,ky KxA(xUx bzZVSpu%LPylҒ ce(]αiVY&F~6t-f=%VMd5fv/z.? jt.0>z029d48bo!D<] AnMXV[> KkrgzSW sfV4f#- b{L32JyiNJASӟ43:-l</9VI}0kd.84e#H0q s5Z>Wե#fBa&eM/q/PHW Q,."+ߥ^3'ҜkT.׿78,of_/bcOltըaaeVjRWQh#:ϛVYA]^aI\UYۮVS]MF޹߳5:J|``pbv@_n]qK-ܒmύߙ 3V'n(5z;vNu$I]2h!$PV в-Euuqt֗Tcޟɾi@OwcIð /* ?]PeκТL 1',b*VD?q6)[g!|+(:!ZJ}aUyT5w62xw^-{<#.絬p,3kHrSf(M +-0Nጯ ?ow~~k[k%o7P^wMֺpWu%Uk hة-%#rHîd:'0~c QBhX2k8mr1bo*3>Y Sqš(ÐޜsN +;^x Gzzrt#64x,ՎNsKtCvƋK# Nr;!,,^2\f](NZފM4auqm-ˇ`ưiZ&@uzq#BSɈI7نZ1$sϖ>jO-"pYbeG$e%=hqH"<_gB[aeэft}hJR;Uc@=fXPVVWj'Ib8#>S+.A{`wd`.\-:T+; dЎ?Fn*>w(k_n3*#j +1o't,QJʣT ֆ[rG x|x +!׻ըww:׻.ORsE ;5! Ax]<~q $oE.T$]]( M*b5:~㜇yj xaG1)h>5vt 8Aw .֋W mv}zh5]Ef~]-ꔢ޽nD_vU%c a+KF[9O O@F԰*cp"2kj3* _Y<-wIF4f!"p}lw }*{*:iBӭ5-^\br(89E kF")rnxdJ>\LycuOZٗVB=dr$lL^o{^vև5RnR(F/,DW;M&6em v.rtMz5%Ja-k]+ W}UQݖI$C}kEDRVDtNU3_6܌vݏtY?=uSfC PJ܈=PmXHЋuLIˈ% !)ܧeK6"TK1~_߮Jx)9:ľ*5] @!IAZNi_vZhY76h +a,N,SP3#вRs˶D޺nv}=^4ȨjKnhLFs~iY] J@ASw6]*#4ᘏ݈;f2i,RW'/n:kԭonMnj`(R!y5'f Ԍifb&mFO_v1X 2Vf?kc+҄R:\JB%V*@XYvM3Ca8R=(MZJ[ #TlAFsZ?7>?ۚحWՏ#ې,㏿ptU!r΄ܮf0Hd:Ah8c+Y6f+VsIkՔ=["([7ZCX["P4Xͣᚡ0v% x7 2 G&<h5[nV9>6`wˍ$ M@r)EYiN7$1=x(u/j}2lWRZ]Q{Pg38l+c$'jبt^?o9t6C-0^' +,iӦ[WP6עuZI~JBP/ V+O`_/gDm{<cC1]&M}NmjpB%[ɰ],HI.y?Fٝ4}|Ք8g" #yӷ!w +sacd@s# ŷE&)jǰoFLJh6PdxƄE=٪N*tR +",]u8p+'x2vDN +@:P"x@| "-n#Щjc#XK*=m>VJoBo4TMm5 Ȣ<^F؎s$"ϟ hPu)՞V|2#r8nyl"*A<ۤzȱz'he=5ÖMj?(^le9M-0a 4l}+Lٛ4BI,lEHm J7=8 XE\k0|"647AE=.`#O6WtphcҐK zd+4#ϷZ$ +IuFQtXgOOe!Ue Qs.5FJk%c:Ou+iL,'܇3y#ܩqȠny}X +uކKq5s +H/B{Tq44JF8v?/}}x<0T}lk+r[k_\aY@mʞ,;|9Q%d@"R=)Ӛ%*0yoXgfáWˡ |70tf zJ0b4G@v*;S=O}zamrCQuGS݆= Lcq r502Nۆ*En:W⋧J=m@s2]l祈DA' 񓆓u`JϠv$;Nĭ'UZR:8\[%G\G; Z1bԹ#;hў=@I X;ǃӢ덎ikyee۰dta~pxL<'C{; 2  lGz?wdiCTNLɢͧci+-@"9~3e`C1ݒxEMM %۹xp5聟$uI2bZ?o`nPsK2'jHz1-㍶}3>c*$E*Xi UGK܂ ){L~#ڹ D$H"G&FU݆{jX8~DDF:hUi&g +pwiP5` {C"P>[Cd?u'[NyiɆNg4΄U4Λ$e渴#q%b;lx c6N7qV6<`[uZ$sk6R[h/dJ*e8BzQ,^|0{5!֤w%cTI +*uǼ T$?jevO^\Yݏ:A땓xcF]c|wxQ෗ o0)HC𠭇@?֋(!Ggpͼ?r $x! ^ I=Tk}SI͋]$:Pm|"z1̈́wn fBs:^ɄdغnvTh.*ҧEro.Dz4m\? BS/I. rs T#BXf‚! |3{#OTɆQ՛?l1܅}jz!W6Q]W l=<-'v_gȤ1@ =d&`ٽ 1#"h>̹k +phcc=r>4l7xζl +\lc\`귑嗟\s(_PPObȯ hu՟~Tp'&gIzR3p /0G[gw0č<í0've7=I +q4uj{G >ABx1b{vr*)^FDp*uFmy}F+й\^3i#D#LCN%k`㢕1h@uCsxOTB+ũˍ>.uQޠ_Fƫꐵ% {$)ZJn7..R4ED?-{uaL%1עT'0W02B82{Νnw!e)cc&AZmSsL6ǚH͘]Atb +V㈴gN +| H +oe1b( K;kN5)LOpD/6ƹ`uX9;2ֆu0Cc;s6bH&(2ir q(l>(Y;o}s_-̔ΝW)(sf/;;Ž>l^Hxmt̥4r@G}9v_D]Me?`B0CR]*p,4{V GbQ[aP5'xy69ԝKAD=ABsQ\`BR{tdM:M{zzS"Z@>^IS&Ȱ Ѯ`q 7r 'ާiW:> D _[hZw˨>h%N7b6sΜZ٥'<~ cQo)K>[6fd@NnjIQTj;S~K( 0_=wDid_DE 3Ў0'4S5,e$q{ 's LJeb7m`tYc81C8c#̺ZK[s)Ԝ z[68|w(EJ].`J_KW`LG^kn*b:(5;p7ir%°XгL^G @(TYkߵ WQMH׶8%ij+tPT͸<kԐp%\vOyn,ݽV ѲV#g5{mr# Ռ*tu=KUv}?@1Ã$1#ř;c<V(f}WjG1fGپ*S +4qgKWj\AOi6}DzRI+*RN'Oy]U7pX{f%F\d6ā$]LytXDwu!ϦDw{/Kdzs/l­F.RrD\M +lFS&ߢcԶt~ Km +endstream endobj 405 0 obj <>stream +HtW9rd;;P$S@cUߢ.!>H$zz SCh_TXSdj"(CA9`κ~Y|Ǒkd=>~_߸ G]?7S)*;$$y)wtowpMs>.kJ"ҌȬC2)sϊ{ҎK;4+dҸ!@B %2<5&D.I8AD#jp-S2ˣ8F:,coZT7Xa0&*Y:=0к^5)7[cVtular+3҉6ǃ+Y %D?ps, +XPJ HNL;,Xry>n*.s=19k(*p;p鶥p^!]-dzu Njw#%" +w8 x Jy =nL l Xkf95RXAﭢE'O{A uU/,NV%!iUl]krHi*njuRTdwA!.0vp_V߉фU좏zg?Mš1frnyF/C)xQO[B8G|9wfY]bؠ?`aLM $j?iRj-FpI,> 14C,!DF4#Cp +Y $F&#{ F}o3Sq quDz&cKdvڃYwTmҏ3RnW+CtNG N$ mTL0=7 N:Ru|vy_:puG؆m#)Suժ#T]h1a/bDױr`S1ǃ j +형a'(7w{e96Ͼ5Yj0I)m ! GgRTG(d'~&cqܛWe(cTyB!H:\):Pywec#8.ICQ{:T4#3~hn=93OXg v,2ٳ^h,Ӷƺʬ.Iնq T5<` =~T*DTCC)=dBlIJj-N+Hs,d0/ DTD6ͶE,͚iu\9=!$]]*lT7[bI-CF!.fkgRk$OS<`X]2ln%߂g~f6.Sy[_rC%?sK0}y(ZtǮjK".ױȈ2>k1,U9K[j%Qv=אW'yfx.3f#cc=dT C{Z*OphjK{I[wU5A3c V?/4ƁV&4U #QZme롌fY|2]w_S͝^^j'mlknvPM@;m?=c}#;yQ6$x6iKEТ}e@7ɎU]>>'Ynصz_%d@ͰػoGJL|8 QAfHc̵ sxNCρ AQgIjPg7 +bvRO6g]0a7*6+1)ѽ@sr {1[`իvƥCO[kO/c(XJϮ%C[elGs)ur_ohZ?ggsѧƦ :Og*y3S}[IPfH#L[N*Cn{=m!9‘T\b ,_:F !f @]mIz <&ND|gB,az69R9 r͸"ԙyx}Օ"ZD<p(I;T + +V_c u'48rTViT.e%v/Վ]Gx@I +^oS@쫫R` +k=O;^Ւ׉AN;H=@buat"Ro0CT9h W.,p77Pײprk7G P`PI̖H`B9ey'0e64>t<Þy7u Ixy(@8С9Vz[jNkq5R5,tiUu.3蒢|,XTptɍ!.=ZU `vʶtBA޹'$.LL:hb7»mCo:oM>X%q(2R~MZswJ+/:CO :G+\%OˮKX^z'8Žأ|5_T@bш<}ĦF2HC&M , Õ;]#,? DebxX:3 t̔^1Z ]ꏪOd +8,g@qŞ̞.ߦ^L)[[Ζ/WaEi5`_#GO Xtbb9yՐOk1nUFye8\1\T^%B`|6vN D-6Lo.dKV*zbnX=)T?V յ<62 \HLDYK):XE9oy՜D cuǢ™bmfv0t2s}JD]39y*ђUfE|02SAAG 78Ǡ[F{4(66 5Yu¯d]6@\bKf`ŖsҐzR6s V2ܾH=,F|*wm]#%t(`cFw6xDBKn C4[ի,,!K`쁀" +PVّW~OL,JC߰%N ~B#"L^O4|nwO ar$fXM^9m0Dj'GMIiJۺWRG$Qp%ʨHvodž\WrI;Q}\ D$4μFEk>@W;8ge(&t xlR[VVc+q̗pՃ"EM!8u%FFl ֟3H6Y^07ZL'8@X:mb,HkegmrDQtI໮5 +s^!cy yFAds R|1wt,Zu~e^@2Ec!Ng̙-cʡ\pf9a]5}8:ADv#Gゥ+#vDv=OAO^y\Bmwu0 DZfo=?v D}`>ݹ5eo `K{F;ԗD_.\ vܺm-{y mV':CFMr_!hA´ĩJ/j@ҋZ86Tx^\C6ٯV#sL  Dr%1' 6@$WY5pʠAhJbT(g^oEz1-|3sKn k Do.ߡr4cܽP6 q5q Çmj51fZUc2)|urK>]oS=/":G|}J GBzd#ǽʛTF<;⹒Za|+;$r١Ӥ>]fbЧacm2S5xu э푳b0`FJ)%k>szDԋ`P/DPv=hR$\̍e nLgoAL8n DvU(M谫@cxk `a()!hK +hܡRZt%T]}" U+e&MR$]ܚб-eyc zaX\phIQnRu5bm\Vz +@|*VV?ÿA{bD}5O((v3e" @dA.6WrSHers}Bg^;-7*ᕯ-0#؜jvʘhڈn}_2`S.PLAm;fp}8e4D]Aasؼ3m#UBCod<= O0_h]g=4X0xd{x4,Z޼P] +o= Vޞ+$|lDB.Y,┕=fOmkY<"ߑ/Y aX}sQ2# + >6zXi@3hk[h-#5,qF2_4L>.e+΀.-a60WAI]M^TXQ&j ;tylnJn_"/wP)m@20>1=+@D 8W8L5@Lz"9Z/ Y}XtґRs(c#t1f-#RLvgQL1q.#_id9}Zp 6Aa @pv=$#xs? Mn#.DP)'# MHC-<5\ +k 4#?238C^KICX9^%ǟJ>#EvcSvx_7(C|ٺ@l{&T +.v +_ b"vDwb 5+Wp Pgf;brba0 mǘ#zGl'"( O'@K0B2A-2dkk]r45SCv!x88v۱ѠwmRV*inhdlx:=a9xΐ”jZhx"10\TvnE@D6#ȩCb,!9X\*|9{|gDiH! ˋocvCƱ wH* O|g*#9b  ;l&io U>}^Hfr.cHͫC_ 3ΏxI>@݉{6J=w~S(Tj"FC8m]~Y@sл{lYC/$z|)*D|ܸXcRX_BoEDdøAX=pFrm^\BF׺UqdɟvLd+)#P̮_8Kq]c% X{9b;{ZلKKx\7=QeIW]g-s=˨?JN1t}Vȴnڥ;%LNxp R'T.Քk64pSQEyhSwa xRgb dהcڅq)2%EN&B`)r-gkkz~wl~G>w|^[J:~ӝ-^O8ֿw|eC @( 6=#x$؅@ rVQ' Y ŧ c}YҔ+N&q4Oٙv.{z5Ϯ9Ɩ{`j6dH.lSb4};#߼VA)v ]8 5G%Hɹ ߈T8ؘؓj}l6@Z*Be[٘8qf9ҁ}hK:&ʱ3| zmʏ^ŶȜ^s4_|ł"rEQN^o"Q/둲?qw0xM0gM0.B<ȥT5 okUeV&<6r5,Ix#Th o!WY56qUtiHoa,V1ALUߟRR-m}kx f⧼x"H:6$ +Xa20sY.)49 +7̷ h;H+M1d)Ư>(M=IQ+ wmXs9ik m-v +UkۮfMyҽWDz5f&#'XUW;4uU HŔD6̕ +*F(0V1ʅU?ſӖ/W?@iF>_!@+Ȱ#8?Ddž1;lp:gV!/"ncxe˫@#?~C*(vp#CYB%^dFAbXX>`_cJݩ/,dd[y`Y/eb|C\ m YHd*1;͢ț>G7p2pm5Np*Xktk/d]Xv:l+9مS{ P4THm ֧v%֖ravtF2Ӹ'޸HEÐ4;_%Ŭo$%eƁ)&ѷ ѸRTC]xsl߳Nv|`ԅQp8h:k)pRx +2^L, s:)ǯHQ&upyˀ8w -5d"'}n4(Ռ>wDbm+2cHeK6m"K.ղ7m:oy`H7+? 36sIևPu~ V/e"ZTc`$\\9g)JAqq%`Ș.J:||Icc~mr3e7t =y`lh{5@P# yv.EG3EFGgn5"d^P2Ce170˧TҴ_$ɰ[,z( b'=\48Xz%h`iEt"WKԭȳ&ʒGYU'*Wc]LS+3dw}<1c`ן602ZOS ܃Plb +G#P(j>cޢ]c@ z7H.ðR hʍaPyuddj?OQ?pke{/ڏ^k߱ Ip7 ]/o2o$^ ?2aء@՛ T׊J.C@lr}g˨LA&mbP@~uڷeg=$m—W $3"%}R$Gfف@ҠvX;16pzqh9(ڷ9tЌew=m=Mr} nw܁ kA AmYrdT^/`C3ned` 7Obԕ;D/܏nļ}ska6 k9Z'pyG͍ S]Ckbtr;MJé|\&YkjzeN1^2m:ERāH>R|rbyd M Z8ǎz40oV!TCT;#6pP +~5~\{qov _۪i5j^q-{+\{>$.x\ȗ}^dp7mG*o^>$؆؆VtLFJѵZsXDbj >őĵE#Tl,3Rzڽ0((`ůenfb$k kA1f2ij6NJ/1O,/Ple|@mhQyx}/2L)I]w"ݫmK3\z,|I'^I! w\:ʜ[Dfrfe=s b)9:"CA0S| e&5u(HCB{q[Qs ++Zs3QpT"']!-Y}ލUѩ9TL{+:+@+(u`o~:_)c܋+FtONhM2S밝ЁC2 LaUv"F7QV3-es֡ 6Co;5rӉW_XtiabkftvEr8zcF *]%^إp]YYr\qF7*,=m +Tô#epƪ?x8t?IO~ +O·mI4є0)#)Sa*䲹 Yz,ob&*X]J4Z%J2{FB\~bUUU xLhg,n)JӯGkX[U#m>V2~ $XEq5V'?#cϩ|D}һ^uziKp`w.61>AK7Fҳ\ X䉑C]YvE*bt'MDC>ӌ9vE>}AXơ4~(]CȾ+(Y鍌# ;|hXAAꊷfhP~KZ[n'a[Mi" 6k^8KiS_J'`j=D&-&27] ߤ F? &E ގsd{JZ luqJR[R@I.W-MIdV Й@8=u e²s[y]JfeZK+hyߔBmC}eL%?,q_bR=piǦT?cCn<&rY&&Weq3K- $^9al\] l^FTZMSh"'Uɪ o!&"[>GRqF~12+`vH|Q.erG5 1~;8^x75> ןRNP)" ZNDKjF~~ 8>7JcUR=YO;yWiQd5BbN<1.;.z^%b#mEreWVhDz?~T4mL-™DjLqYcS.3 ;c  ޭRq^cÛ_4%d)N ј׼Fkn#s~^ocaGt2 +~͚1V Έ_lۿtVXĤ߂ k#:! G}׋:G}#o4fQNd + W <{' +g#,$ +3regwPz$A;s-'@lPq(M}PR1hm'r&D:4G>=}6e.I>shߢM'\+ ԺKe +3O*a3}9eX[i)3 +f9>]ȕH߈ó r+s*֓dtD?dAgԦ=10JU/{ +!I.{ +n! +밧 5eP`FGfܠI3mPŽAmP÷A_;T"?nwz#vo7\A,2@.& uÜ"p)2ݜAaN־/Y]999_sJp(ygRxL`wlsj^7s'ySM!7eL7e1AnZȁ+Ǣ,aẈnk +dHokz#5=AYS"naMy6-Ú\WoxukCY k'k9$.eMWFmMURۜG T}mtA>Ų7BXiMW-e/l#~sj3;3F* t sRe~ى}[zF-)0J)pnQ `t3D5 .Y|]9d?PO{P=G rTkyf_~T[-&kbd9 ;xK=2zʎ|=@M{& "!uI6( sayl6g/t+U5oa|cOV\P5H Nh-Ń;V1u@ 00QD +G#s5PDZ@YV}O +$\zD@~"%d{K5^1-ĞK'LՊ;H(##*'+qSUyޢ:08zc #q!o$q ē.??W}etIՖ'\DY*n RU$hz6OTڠ[IH,oKvuᷨQ+M-s?nBP0 J@Bф+V|H/= )?f({ٔ=4-)/Ɗ 2r|7 !< uoJFmJZ]xar.KX +qijk;/U=V1=K?]wVqd"eQaB7fg% X69~ Z688͔R[ŠW87B#C{AWɻWͬ9{uMūX'}DoS}ڭU5@y#qU=☄!Em3n){W!{U=>BL|']ڳhk` =WAjd㸖H-#K)E@Z<2%/jV5ܜi9lG-{` JDB/oA׻6>C-E7y>J=UgW$c-J;x鈣lӕ`2`NV' z:UDzrSteMl"+W_Ŭ"i`~tϟj{< /#7q0lb6 Skg$ȐQXgw$eo8s iN>LoO>nHv˜9Z7Aשs1)@zU+Lb&( xHDy gj9D"M|^"<(+ ި~g(oDd4uZΊYk8~]_^poHi7li + j7Kߵ#&U։Dlm:"HC+*AOnϟB2xSS f U+Ⱦs[9aI Td>^'3p7|A#*yNZL'x3EtN+t86}<`/XBNDւ32 +#zSAA!b=j8V:uSqZg_V[P⯌"JЏJ#FZ6gp{i ^?>}T-`˗Y]v(.u=ĉW۹ )QC%7U1 iٴ^Lj/DHi +0f:ć#{H%gr}abݵ0e{Fۊz@o$,յB$'%1m P'"Ae N%Ѥ9`,9?az|0{rWf-_>z^_X*A,a5CQUDX]?`O{¯7M6@ߦq!W4I:c@04,50 |?@H\9NZ+ +>Q61b[)X\<rD'z{c:*tvŤjqia4b޽h.c'#ײ+ZϿ;\s+hg30x3'O{{MKcE -Y,KVPYݢҊMu(:{|~Ps2r׽rd e7W32LS\@ +\([Zdz*$T9xDs(s 1)%OчN4q[ÚL6jCN,|iV C?َF?khh?.x!)!M$v=,eVBw.7_.4^3_BG#5yJ,E[O_Y#?~Re=G.i]ڏ+\ezj6ҥ?<[0l9(yv*PbS N .ŠePzh{bZ +~Uy_SH0PI쿟lFOF_fpF8u]l,Y(V` W`KY(y*WgsK`%!Z tڮ,Fo@򡔩}bɲfٕ\vBGL2DX`8AD&nѱZRT\^:D|IA d'{u7jHif?Vg:+$3ґN1s {u lNB/٦ZF4C}tvemg;#>4! 7(n.*s=EW׶spϒuԌ$cu7؆m6=YxY;Co?'hVhjă'x0{sFZOg6qfmmgי=F5w=P6{ڮ,FM/LiﵰqfԈ;Yt XՕOwo5ބ5*G JXzѪ˪w[9[E]®l~ullSj&w·Z·nS[t#·:}+o;?ki~OvtlAw]WKz5Gup{\qWpt'<.6׊D=ۃ:ݾ]?taҺplm^ZE֑Bb?cq}wz߲zI9U^^fuO׀oP kJJޢOϩ zNŠOŦT1'%eҳрp79DZ҈g(PZx󽖅g+D`υuo I`%ݫ+ o;Q޷CmgPce+QIM/ aವ,<|!57+qHLr:f;ж|<q?/sctۥd~i|)'c VE$OO]#u?Vh}%펿ʹ i$6*Y6718X(anrDҬ|K/#-4 !xr90x>L h5^KvhTsqK1Qn(xUF̎q}sΐ^9##7-c@kS!ȸf#,%>YW85%Y@Xj]ŸoQqk5pǹJO"Rm +$k犜*LZʧׂm.Z̷=1-&^4޴b8tŮDSz0R̎oZ{I9<(m'?e<# WUNFȈx"Y) +}yS\x<=2%rRQh_ļ +R% (?Yذ$SnwD'4JIp٦Z5JJfщ5xSD+ve/ZjkTOGOFƦdAװ639V&)_/!&h Q9L  a)!s)T3-DvH1 JI\r։b}LD¡fC2Y1 !uaH2X'k$[҆}TTq:DDGz$ -ՃE.FJ 939 9):V^/--w֖χwUzׇZp%ӄza +I)2DV4 ^m #jD*-8(+tKtȕУO[|SIDE3! 8nJ׭;OOW_<^pl`WUN:Mv-o/M[tR1 $3#V[Y`~2Uٶ~t" +-җrocߞЭ*8rsPtԏu]}VYc>PP LY9 |GQ붲MCZdR xv?m5F[v Fm!h*?OJ@O"=uހ*oދL cJ+o`` y O +0! +endstream endobj 406 0 obj <>stream +H4疪g $ FMY{{`~Uk*WvW4ұ!Rx:Wn= e +mg2286\K^ +ݐI:M:jK.+h:F|od%EsS\{+$b~7!Wj>fyqЁ Ψ8$'~ ]郁~ي95'C'# e$Pe79Y,RFNѸRvind懸EL{N=!+ W|F* ^@ +wPk;WĽeN<􊡟DeJ.{?< +Zl>A!TLp3n,`c}nxV4 Zc41אw#tb_]zԠx鎋97޼>O]pdGVxaG%[4CbechN B2SILqwbew'hCّHgeۢعvKϺZLz iJXJ :C6~t첊q=|Z>F<:rJ^.v^n#dLU2Qkw +b}5wAO>rCysO5{[b-͛IOjt +mJZrO]o[$iPԚy*s_EC.ڢCx:vS]ӎxQC}!o {-)QbS_xkg3P[T⠹H#um EiyYq f<`eXN:4DleܔK7v+>qrёh1Hpghw)^4&o1%Y^a?Nj^StٸfӍmlGv&Ɋ(M}*Qteee;GY6D. 7 %z ]cS)Dߩw5)';ZOh:3!3~n#DV^@/O5 hMF쵙,UMN nttL(`MT_2g6R!]ggy~ XqqŌ< +3b0q*n`dž7ƙBx1앭LOĉjO#$mWyӓ4з#i.V5gۂHQňn-X6mhשtWPxXbكd>XYh-Pdi!FJ:^h<նr6}iڀϘ_ 5~2-ɨWbr)3*hajKI뭩Lx~m|+91DшH>"49urK.Gu A/--uQg.P +n,ױRXŠNOZϱcNeN%TŽݹЪau'Q%,A`=5tӜ'c^E]bz8Tovsj{-'}b_r/5'~IjAmc\lZLUh%ܲpnZuEH=YJS6?43uh~iSV4qY[ +bK~($g1v3U{z5Zk-S?[T-JȬOYy,c3R.6hgG7 ixHFv i7%66Dj!`'ɖKB_w5x@b*ibWGCjH>^yM>kXC2WO h'H3p;eJ\a#CWc/]DLֵH6rö^yw1VO +|(XO"qⶩ|n\9cZ*@޹4CI9it0vۻ)M yX7?$oCOiU8KnV{iQa&LpFzrـ&Tf<Iwo!)Ut6޹b;bFZNJ5|<.HgsQW EBmdjUBWNtxWi 7RSB/1HH"i{ۓ!V@˕X2ZUWuE 6f1iI"G~wW{.0Zå=$^o[?$v5F%*G]W _ݖeb Lj4h81t?0h[عK;X\ߗqTX;ugD G3&>mzV+ڝDL:~G V[| +3b8{۳R@ۦː%:;1ճf0awS:lXlm( c]뿳^0aϋ"LЮivyNwjJfjS0Z $=? [}0fThɋ +6z"#7 L"@M~/;,pA xz#BM{Kth[Oų~sn p?i:8CӎyfR'ۤ43fq>-a,PZ4&jbCFgf.wUZ +$ҩ T}m j Owj:̟׶;?PAς=+YIҋ|%٨V7J/Ltq+6I!oTf*"[=x>+r=|vrوɠo\I:vde8UkAB|#Uhy)E(̖[j?k/l'?ȉn#4ը z63*|{[\8"fAð^Uq>kdu3ec@Y'A_,@C.dզU'Y ARдTSza\h +CR/ދ\EOhHXUm<l}Sp -l! Xd]ޢYg0) 4}mftzsyʊp-uUQzk@FBͼm&f>XLPf3WcT-bGzd1iD5ڂcoeLfʫMhG˿`^mW58GieMLwhQZUjh ne&-:s}=1BZn2,hAfUb^9c +Uzr}фi~w;rc!XTGfDfC/S'>2Zwuߗym`An!RUDlݾ$S8}[e{t+< +lbKVfF•s>>L%`,_3]_xBD 4ˆs0Bgy0Qo%FevPۺ΁cml]uOA4Ag+Dsp<,A9N~m u;n=.{m7@h F=.}XkH Mm.#Ux.7gGS₩PLЇ2r_R#,JTVLd+Vjp>`AUi7Ts<"hm;Lw ~Ze}:崕W4Jv"~[sA%ٗg +K ">f7>Dً(a( 3bï +љ+%)uI>1PCHx ķ<7c%p~rF̔;ځgB B_yjգF19Mɜɕ?J# ʆzIlV kfw QCU?`|mG1@^(#r !Ӭ̬~Ppa u>}XW(v˺_[ϸ~.{Zu8%wPކ?᧴5}4zo-$ 43 TSf1_]aJ_ĵҡ(fyHRML)YL"xzRXŠ;t0w*:$챈ؙBI|,:ZП')YO)8NxԚپa;j!앞DV+z\DN*zZٙ"p"@Wj8Ͻ\ _+#MC\n &ٹ}Y\֝Q_] +** s=qҗyyBVSh[-ZR? 7DsEvn0]X2Kp-H0Y?=US]0etZ~o\X]KӒI=$Onhh%eSچ2 +@[FnI[lI[Ȩ;`k*yk^cܐ- %j|! R&awذ, +(-/p4׮O0Elj<,sy +`3&c + ^r[^3J_CkԘ/+W_:7V%_I%ޏIiVMk#? +$aP#tmiz".ʌ\>sʨwIN-l +2{aq*4L1D頄GƯi>zTg= + +u 9&sW}rIH(fX $*75;`j43H8 +>!ԈCs +$iU?&=ѱ':J/g®CuTe0Wf\$x5L<*s~2):6gJhN]yM5&<|i\%{ջ»O(42R{%xЎ2:7s#[%:yIbH UEoAv顥C\c +DkOET:pZi{ a&(%w/#+tf TQYΕ:r/}]8]:i>DUjuېbul^ƺP7k&3"o%9hR7"X<ȭ_fnC% u_ NkĴʕ8A)q +W#?H+WŦJe`ax! (sA?dEqbGy+ A?b`bknO@&4ay)j*Rt&[ b0E>"RPbr(?&q .(R^xQ5P0*gVI[ c,;Vv (,Cn?D(%84wb,ty:/s v䶳aNt,htŖonkHHZ5*%t5&1>',ZFcKK_odA0.so2=YI4Wbl%Se]7c@iЀriM'VcC >Ј lYWJ>]3':nBS|jY6ovN7ITȣy$f}[ڢ7~dE +D + sK<nR.۠{0k=سQ(i*mI],Rε* H,9)U()˜6C6oPMO +7⊇Wfœ,*R[߂("2N8 rO$3#cG.nz*KGUI soWu9x1mqQY1q~zS:١9Torv61-omtgj-"Vƭ3lX9jcpn)lN fN6MCT;dC+;As|׾1Ѐg7;Kٕyr^kmesƄ/MgNm >~kS7,ͲJE!%ڦs)cԐ+-SI\@Ijw˨ + o[2ǠZzA_jLQ^őw QVq/[w+]a\g̦/| +z(ͧFVөxY!U# M\hrҶA “ Z=_#T2xUD*sC;~ <$Q0ZHc(i$4Koe?i\G8*PIAi@ +ߕ >|7~SBIU=Kꡁbk s_C@e,+܈ecv)zƶ8ߵ#]܎J s vAv.[j:iZ uƭ=869߷i Ks״/(d˙A\P4rkJ{I{?0 +Nkilӗfs30UȊo;Fb8p/3<š.0_{,r/E{3Kw\3y/8ݼW4:sI;ۥ:% +V7[$cqpda[|5q1 +l̮"c ywDtj#;~hU:̴7Jq/] b[j5kU +>4D6yеi)U\]4++v~QbV՚Uja2Pcvdibm饌ަ},ISdZn@1_o.K ȸ@ d'肙 j&C;кe9إ&tMt$kz^DZM1 (¸'YO^cXy0uJW+S`4G؍M߰ȷb7.lrYJ#9EktŨh 7QDx;'J{*w~;mqe:s9:p!;%퓒b Mlio* Z )-|<ƺ<*zqG ٠kk;K.g!#3GC8ͩ28dIQl$3|$~djиȪqilv}jKr6\SRᝅKV%?\-ZhSF&. m/L}q.ٻES9&?kPDWݔ[(RK)Jqgfg޵^sg&8}xqOYէ?"иqjmEQۗCdnQf&r]6n۹+zohN xS9 "իKrY|/ϙuo˛LN6ӎdv +"#hjӒ2<N;ǎj7Fr2jJ]fb1cp?9RBMiE<@'n +W*6aK00"|Ry}Gu\n 4+/^0 ڡBҥ r/D>hV#bgO5"'%ȬQƶm;ڭ]F'/:kʳB}u,w R1ی?ՔZ`w^Xh[w!6n4t:zbN5$ıٽt dx3: <=>Vvh\T3Fqo V􏾊jOꯞNVOeE'pcg*q.fpsT-+&{WcRkzczVFҒu6 s +q{a?^j;1 VKmɫV+ɧii|B-,}K&K+f`\zt'eY,n[z1VVV%snMF nM mA{n"4DTЪ~V2/XJ~ m8Oz5'iczd#%vTouhyҿi?Lmau ;u,&tǮ -+aETkV ԘbƫBLǹ-~IiײOMn==.r?|kuWti"(%}d *6'Þ@Y +[>R#ֆ=@tl"k }}T195TvPq\~:_Mʼ^̇_-oR۟琜;7\|DNm\"ܺ Yn5uJ.u<%&n[յ:ʅCR +CWN!;{br]\_6{TL2 k1\(q4s7:瘔XّY"Kj|ڮ~ds0{_T~iwtR.jw?#HU5G7^ + + Xn`GEÖq?E%L r:Y^ڂb w/"13 tADA鵑ߵ~W)>3_t,fҷ;(,VgG~Hc>L9uYc 95}gU=1iz*1y ~hͶ{``^9d_gDo l׷K8UC +2UID/&?馱e.|?̤?%} fzH,cU6XZJ΄LE"ʩMS\lԏxP GˮnYHq0h +q&f'fImjHPOVТ|Qz?̩,3 kwM~m*Y#n7X5rH7xԏD P˧"4ЖTOܴl-;OʮEz1jRQ2a + jV6RtfQtP-h^/~+w({*5ުT@j^+ 9$bW6їun\p'oǻ2NˤBYn^K74EJ90jbai?2gSд`MPv{+Bl`֒YA=ZSWiAX vdEmF[D"@HQ.aֱ 6_qT7tʦD֩E$ id / :{sCZ& m HѼOnm}kDdþ9y*Մy7#tifRL0ì/W>_E.؅T;DepDl鹣^4*49I{}B$w5ʱ}^ cKUL7^C|r!NFbt2Ul_.b3[K;z*~p+r7v/!V$U+tW3ޞ8TH75aѡEsţD)B'/7jʶGS=aj{;s3-8Ae)aG{vtNB=6ub;"? +#Qy[-)Ѽyx% ^b7}CqL<#^7Pn\ҡg54[,Tq'͵ ELŕ~h~Ǡb8Ŗ`WCBElb&q5,ݭ s]80Z}ۘ- EүRKR "ukse.Ɖz*XSyVgZW(#ţRc|'Ȏ{T*Vs ougQ#š&\m\UE|*|s Π2P +L>ʑU _I:_\_ʮ}m0A<Nc=X26~(Uk,r2d"8v`1s)V;ŕ>]!C}Z~{:Ju~`|,N~hR ;1onLy=w[ømPd_IwL?a,_cYnmFIcǕ2GiTн|4 ᅍsHOsZy` +9V$F ,%5`1J@^XB* yhZNWo?ہq6]yl>5`ff'+)Ƌlpzxٱ 86mt ‡z2HyEŷ7cNB9d1~o)9JqkrQ.5фM)? F]Gl.Ƃ)-è_'к% Ǿ@)%hl@ُwitI²kn^>Lzqskfna6b,Au(VSz?L$癹{v2EUp1DTp +mv~鏢Ʈ"xy.XoQ\&Z@~"k"iW"~?Op̹3A!aj *|{}I1q;pzFuf/Is?KA& +I=SgQβ}Sķ9RQ̂wҒTޝu9xXo&#3lQ{gU5KԣA5rn@eaKq6_Jue`M{.% 'V*:dbY8>>bĜb?Th4-:!LOŏtQj\<;RIQuJʠz ܮAvn'",|{7C˒M[l g\@@L}q84J~U j%'0 :IΐA--!vޥvۀۤ=ƠJܡIl#cѷq2:?t\ZYA4dXq]軸~s8TOjG_t#kbp%K6E3y9sTyЁtR1s_xߌJZkc(Lwqĩ70mBCK7NlM>](M?\C$ZsX#r~F[y7cSDW緲"Q׉d3^g]tRӊ6H;WZTO 2: q84%>c}nH㔞cAC؞iE,:,!:[Y +RęKF4Թ*s}IV|:xww'/7/|1=xY,\5QŒwځ0LKnl Zy VvV0+Dz4~l{Pڴy鈴Dž~I?<컹W׃4>?ގ7Ux;f|eƝ Y%R'gkG'dRt%@ +N"ύ1ad/<6Vժ3g~u]iܟwD3;u-d{M5an T4엄S_юBYHĻ~ &Ȩq+ˬ4ӵEP7JNND E)jAz+>RWLJQ9SawVWOhpeKz=QۏhP7:E`om8랪?sN{5Zݥ@ W*]&e}m}\W٫uڻpAR1ORH̔q += O-x ڥlf +GΊJl訅%. 0%U`Z{iY+BQ敁l:O%HtX+`,c聊@$kʠ+̠-J\}dρ4֖K*(m](+|nޱ~*Y)dhmf$sKҁWb;6# 8+.+&־h4}Ee SjoԨ:VҢiұ8{k؍hقkP&s0J4dNclVΜrY7V3l 8=(]|B.*d +`#7bG=[5'Ua fuڟt:ݍZ8 +Cc$P#E_=],(HV~#q> j@c3( =.<ꌽFбZyd8ZMO_טPo!hh)oT1֬3TidR`JJR^ P`Bs=fj3\Z*O x*tv #c.guh5p}jF?%ILGfAUϏ{%Z6x_HޙT߄{Y׵H۞YI6E07-d^-_Tępzuʶ{ Q2j[NeUqr3flM(j~ qQ Ь@ IW#77{{ +^6mwijNU^+UB]>zyh %HCfrBgHA ` @4 +hjZ]ҟOE]q>rۙCgLQ":Mn6֘[cp>؏9`?T0z*S _XQF> h^"8㥰7ofW)S=Xb7/9IJ6Ýzr$[uK}dlޛ̘_D+0Xe\=Ic|y ag$]ƮTL'm{pJ|(V9]ݍ6IpimNgŐ!Dɠ-s.Y%Fhᨊ9-q^G5crٯ.[i5sCWK)8|3T:Yol$g(s&T +$Fi::d`Rvo9 zڛ ZMߨI>lAőly6jo}r']w3yגkk< +-SHFhwf/LC7jZ-sx\fcrG3!tʦ0n"a_՞dBC/KoF!-VޒX(ԯm^`;UnqdNJWVB{YٷÅZu${0vŎUPzi]-!?"Àos6|+-_LEN ,\8XW]!fmz%NFTx "Nn3JD8q2|nO}:2XF}~5EFQ/X9;!Fm5)(ySn9$J2o{bTY:/t,dz^]܇8%/ZL>:WYi_ v -MN`Zn<(hf"QY+dsh›=FJ|Ǻ5vެWug vQG@Pւ8tK(7Z =bzh,F抬8ݫ!&~gGcaAJ~pB6(۫VIYNIlY_u7(ܥN7X_CX٤u|&֎ebk}1>PA{e)4r>WCAϚ{mLC (F L쟠 nIp.;UgOV6 NѲev2rIy`Bs*#uycMM Rs; iz~Ne8rC<'4/|UOCHue{?4v{ +IݚE=oE 3;Ԝp4XC ve5^, ڻ:H'C`Fb8-"9`7x׫/BKN0%O5?1@)KGb=]T{TQNCRw^p*Rp,>#DF$7./xim谔 ;b$~ 6aSj'$rB$kLPUEĽ}nU +[1춥W"F5g?E"\``&"Y|}I9yDWTXꑓm_g1r#Mia5ɫ^me>Hd Ta:|^-'Yu>8CYaU{Uw"&^d?!N8iun b&Jgau61B{A^+^_f1wa~sU7IiVM6[-ϣ׀UCw=:]wz®Zi72 Z[u!Uߌpo$?@oUIxxs-%qXω2;psO8oz< +< +"i(Ӳ*+EրѢ67Ф>H{)@hU]b =UDKy|sʮylL˓({&7J +"Fh߁{[9@+QlAf?+Ygf +~0ܟ!o1ngV2"vOQLņ/f6)Ղ aL5 {NKzTF ExZ@wXCӎUCao3vXL}:\XւWA&{ +Ou19ˉp]g4-ft&`j5WPw+äluFCE!rt̀T!T.|61,W!Q7X}fL2L^^~#n»=v1FfM^1..jze1ndW+i #Ggb-pe%OO?$8J<fz.VieZQ|H(mYNee&5$?p.uô|qzO@tG} *!̻ +`^6Lѵ)o5 ez}P0$Q sA*\:G^ xr 8^UÏ>zT^B:xF͎'5pyMH<}!3]7neG~[dn:J /7Rz7n(_=Ef?Tb?Xc8F3[ R: 3G~LGwAr1ڬ^*3+'/+\3n\{Mnڏ*u~XJoܳ j;W]D!$qe^\k; ]j$P{GB~hoU<}$rO&(-_XGj(Y4GNGZ P[Z6ow,[yP[Y=-{."̛|9$>XF=kZgM K[U7n2Lݒr6{Sgc#$~ku}LƨF#ݺ[)_[k{(WٷsDRu" +[o94ķV̱]kDe:CKgaf‰jnFo3i#6"C4kK፸8UDD j/ilZtAPv-?g·m,ۈwM42:jXo %)}50yHmξ'Xy44UY]#maj^)ʭoȹ捅 |7;$ͳ s=KfI c`y]M6>*˵:U>H_qh +@QYLQ)E$y2d{s>f \[7EtG\r}=4rbgۭa08Oh4*0pN36.lEoa--烞]icxy!ڬ/H?vÐ/malbN +rMTX0m}cEwE`kXpȷ9ǣ%I6lVuqrE^cGxl]A1Z7zQke.73)nܬN:>lw 5Y/D;if!~#)|ќ!Ye%JyC4X,Juș XxiMOW]kSR9x=?/{}AKzv%FaR AWJ ?#Ӫe)hvm5oXkgQÚxײ07/n|S!ܣʔ޴51il=l* +ϻF:&vͯk' RK VU 's&!nJ۠0z9J-G7ӕi[19U6kģj62c/FtF_b^:?Ĭo +aqq7v[md{;FbB{bX~97ꍪej\.9(ɓIn_tPmd^v*@㵹&+|4"z{ui?ZS5;wП-[tKKȤ=pdLU&D= Vcƻ:HRJQ-:Jg +ܴR߃X*ʈzj,_Ҭ@@KҴPXė|1V}4hm׉f}ZoRgޣtf[ @t";Ce-졓o:qsSHӉI\>',%[naZܐzʡ.]\4Xy@{"iS`sݳ|=k{w7Tu9b|vV %`Lژ6KqM +ߗ!jw:A!bJts: +[];]e_ӇS5W'A#>\PzJoj2>?`&muMK(-mu?e= >Q?K+E1DGs|Däb#Q~?>6D؁گ;P)"䟇F,(.jʀ'F^ߴZ\S(*RWM`M[6h+IH D@l;A6Zg!A p$QszYVQ7^1-[FJ;B/GW}Ommcw!ujL~1N5 :ߧJubWWCK+&Lu歉uiѢK bdb-sB[d]IWh[pge(ԩZ51]Fo;^<[3g˿iJ-:lAhLS㸼U ^Q@1 T9avOf꣫~m }>~1e19)-3Ѷdmk5s{X +ı3{3gf6_a=Y+rY{d ƓQfoWXpk[ td$վ֕`0V y*9$\oԱZ-P3B^jsS7z{EՀUQҿBV umH8:I$uځT1&F;Xl/=F p3ikFy>4̺P?[%*Pv蜸o@p`j*,_i9|Zpw89ovwǾϟ l i-ֱG5b$q:xPqk @,AGh*qM+Li{sWY@p3hѰ!:]*/u6n"\}kE`FShE3\kKU2EucY3'S`zK2 _{$~uVX ZqSbVXk|5ix2ClNՏ +r~˛ ۿgE}h57CER3!(r &Xf9%3ȺXb0 Cۃ_SLGpa#XXGt81 n1Bn-D%?rZUq!7P&OcбsZN}W kRE|q^OQ$69`lzuc~ ߗ 5.%쎃ry+X"5RH *o@i({>{Ɓ' +LiHkY+c$uwjN%t3}4N +kAGoxM8缢/ڹ4 ]~5?EOٶ +QqF!b1h6oԖc?kѤ쌪GL{CfXןӺqXU]lDlK6j:k3KbqX%z(:3 ]$V [yn0"Xa61JazvEw]TfۜP򬺟On 7|5ڦp j*q^s5 z;~+*4Dž%"&VPYW~ꙇ5. g# * sxJkODK0tK/qy"~m5zM+7`vjq/?-uv1\\ 2ghYX!Y; VfZmLXHN +`CR=blyh sYh؎Xy?jp{j6_f0{ufMbt/<͋MkDq` +GŘړ\ӌ06%ת݄ ɒEϒ謲$e\z7bOںGKM^ԹPJAci +(ߥA# #PA ~NꬣDsT2RADYUWk|(SXzp^HW1WTPiB=S%^{|%Xx 4C=n4yBق{zFc,--?H^ A =ID(nd! z4kWK{clt  So3+a;n5(eWao 5٭l=VgfZ*M |8&uhRqSB{a. QLfܪMg`(&AţK+ +놊n&GJWQ렊+q +oj,)~+(sEuBXe{w9Qav|mL?a@ʭL͏K-b0IǺP zCq&:k^pwnͳE ۙ :rA: Vٖi'4/ 89SB 5y"3“("I FQ_7_NUsw,:"xXJ-Le\=Db]9\ySbJ,_ɫ.8hi8􂔻qs~a+Uf qdYpG0tތ4ᅘܬF3:RD*VJ$`t ӛ߀HV-g +?0|䇉t 3u-h ZIgʜֆZ- M?ZXoFM'!hYYA2$vYnxSYQa%@bnn1=\'rwynH_]`^6l=Ct~eK1i/Z=&.FLIęokoWr{k7^{l =Y*F]QzNp^|12u!bz^4TƜYοBpSK1910W+VF7z_K ,άܪm~}J9ʛY |--]V\uYsiP^s*3ݿ$Ѳ~nж/e@~2ޕi8Ǧ Xv%kCv[C['E2aqt>/UCBAh|1Z0A= Gaj{_Ia1+l w@z5B9.lUa@T=$`òs̝CK4niX{kmv1E+#%6~R =7:LNWfv)+yn,-:Ӟ;'_[늅 0m%^(3H!]T^6cgF^Kls j`CȠ$l|f4զȍ8thq cFrе#H5X ږЏF?1nǝ?Y _b=~oCZ<*`,)* ]5>6Fi5ݦCi^Cu(B#Oj$IYCSe}4_3g3f)],`Ƌ8+ѹ];? /bnZ^c`H/U'^tY=ðiM ͝>4,mq{f'bӼd%ȋw)7ImeѕnqCkd2Aኮsgh2b95&g;<ܖS+HPOu~Dq㯮ï!oxՖA_yRG6}I^BwwE묐EZk43CA1qIdgwcqUjS&6/i%նip6[9SMKT˂Lʚ_y,ծtkd̕(G[LR,[ bTA1OzC"=+ +*@AuetIJbi +-Ea$i֠% P-j=/.L7֣O5Ba21pw*+xnG* +@s* KsI|/aJ/`эQz 9rߋ os }e(A +aWSx^ڱP$HJtGd(~ҶfYabbiȴ&8䍙vcEyldڌ rQ8" i{d㣅eK C=ԼY[P3uU^fԻQQ,=Nm]*t* ktL'hs=Q]+U#祏YӽQJZ2.rx'z Nô1fW2<%KRR2ݠ.淗FTMMJۜpQZoԽYTOa-Fg7V,V)(*vz{;ztM60l6O43Jɏpr0Sgx R.+>4j-3a'7Ƿ5{8x"U3C~n!aǪG;ssqosɑ?{|=4&3wP#*VIsl}Jgce7ζ( E[C>_L}cE|?ATɫsޫqsô:sӐ)ǔ`O?dWZVIaQٟ /n$ $p ['?yxٺ,yrkS &ٮ:& sjK1lKP\$?}z-\b)PvM3՘a+˭*;}yQ5m-+n *GF[f t<j.o}2Mt90}xOlu76θ˹+]Yt9.>A2x$Nrp謁>jm^WA&e5}Yl6v;mWNX] &- M*n11mm 8 NtN4[v:YA[4vb{QGLPݿF|9dL)KbgnC5s3}tsAo{-ꐴZa7i +۲ w,FkyXN wI?Jnց?.9Ucc_q 9-1>ÛBH (XvM +NYaP;ƹgIʦ4[:Xmi K$krk3%jl;_5Ie6OR-u- 8n,Zsba +$Dw%j0ϞG2-yA +aEhL.LN(|-EDyT$L!OظŜS9٤m`'K~6ITy?a^c'+X}]L1]9T|7| A~_NCuJ ;WT/ :7q5ٞ-(Z͟=deP.$vz%`6!9}$V- |Z-}okL-?lv M4"|~yU\ҿlӵ:2ٵ_]='EV:^i8NJ){z R Sc)\Q@s[6TOw30Eg 4y8NæQigѤ¨ +5lb,m^';-(ي*vѼ$'3QŐ>o[f#$k-`[V@qZ sf`~>.Aʄp#L4u +=u)v(ejOd03'XMu>>{<۾Xd/!cF_.GxדVS\\no;GHTɆ \ +tȻ!qzA% ss7ZG. ai|ۛj,6ui-2!um(,+3_Z/Ћdqn/qp_9ʄb)%9PS-"KJ/B!Zؐn"Z%Dmx [<僦Z7B \QWIq.ԟFCy@Ia^xb9}xpÕWZ+ M(ŁD + ?[ͦ^_mNinz.fܗ)DRCgV5zcȄ~l \=46WKߠxK[XߔPocFÄܰQߙˋ9q3&{fH`t[g,*7Ϋ>,5,v,!i΄71|ض۬r.w&Wbh&$Q_9P~kLLBkhDI>ڎE|UvͼzcbI/wVc̓vᑢj+zՔ =7vPDҐ0= +GK7bBs;2yKg'nK;9G=W>pg(bDER_/}(0dnrj/$ivy*7c^K柇QBA /BiŠHpt ,W +!4RW`vvf#Ze,Ifzam$pv"GkGe}m i2V]yP{r3Pא~vxR~wc=*5ttmRMJMF@[/k.$WA>+Wij£MoA? XdWwTRf6OF2;yV.N]}1s59*72q`L8s)KNzM:N&s} ;=ג#tmpA_\i/ιJvKZVO`:{CI="/!m w ]fVy`/{]'h|7ŗP˿Q=1q"[ E)M7B)詏FAO[0Ė:KRܙ "YB0 IZƑeT;T/I<^t݉ [}Ivz#B)78Wp5#V8y z=2&6慄ID)3ZGHh13dǍZx7jk8-[l(l|ɼ֞?x7 +"*9"}o頓zQBs6`$R7b KE J h&U|OG'aH7}Cd ϽӘ)Fg-&KbEN2g/ 7|q56F|>i3+KC2ڰWcY1EB!Ą~Az'.3 Co޼ +~];> >j'Cqr@;f2ųɎxrbq[(gӨ[CkE65EN|s8R2d'SW у<f::5q 7O&=Ӟ-ؙPog*Elc8CF0.;x~b' to$Sk@qAeVc8(*{4~_WmGFtCp]to7Cvpi +!f>2L{/#;M ^ZW21Qu;=E|)|:UmI#JhĖa25!l~xB \i\2ZN.kW +5뻯Ϳ-*j4lzR8K-w'Y`jVn-Bᦛzu`JQk`LƃIQxEeUW4c#&`,Ȟ zMa-k6DZ FBAqb~eG$>M]oTI +ͥ +>ï# 6KrF,Y̾L)-M=wjmO#Ķv +MlPpSR2Xr(/,ղ{rnvnΝ{څK`-?b;KƘ77~9&k Lޖ2$AqG$77d/AUenBόaf;Zo3YeJaмb*2Hɠ;wyysl.&,9(W$^Tp+Z$QT )|Є Kbi;׻jW}lty? C9v?9.$]9DZe;qh/;JٌgeԥKgxܩ7,^Ӆ8)! įb)Q)" +Q$wsI=9jWkXQ_:zEBõZ;+nhB&-lar{<^?|.E栈ܽ̎sRnt&-<>5gBE'}S&O6UyG|1ChRt ٫  6:Y4qi`QnN +^o&W=wĵį 9ukW OI{R3qaݶϘjI1&s7i.q]h>R}p $1s'hq%jeŜXQZ҇ 1^Jh\sB)y7 +Hxb"s#?ʯ:ycT3b0t {x;ulw /Hs`\@CPZLΪ/6!|T9at1%T6*LiV`ԫlB.ue&Ąllh~FrSyMDص}jSjuhiBVՆIQ/XmMzC7qbU \')r_Oj=wgڡkڵl^WWop"܎5^ ^+&PChŶ [9Grj/}խzB3{II5f +uD]-H*mkֹ4snP*ßiZݘe}F>7L`6oH0vpjρCr:G :M2D>N4d14e-WY sXlx s\z0 &Y&En5*<tHmcA~Ik6g57F/@xA^ + +(0"}Ës}KU+r;Y2{q}dQw c4"".}!o jĬ{ymsPw/BcXUP?-{XEk'kCCq*>!Qk}\hBe+3v9M3}K1Vb~˱ 1R-  u~!"#w'lYc'F YŚMx;kx+,U,@}ˈK.qem g1dB7Lu`u^IKƈMuTi\?Cl]1{ØvuBKZ<7Z=XO&Fǽ]^NŲ``1 S[2}Vz5-^cU (z/R{zVKO;;(LEa!j*3Hm5Ow8D0Jz+|NQz Ic>YQQ)UMy&qrS^8{Kǔ9!@4٠NQ^~?ZB2׸!e+T`A^cRi_2WO.\嗃.d{aժ)P[VE€Hr[p:ȅ_,!dty]EF#-Kmsp4jƈymBޔ!;QnEѹ}+\F/7gPǰRySyp7@^м$4ݶTmsZӂT{r}%eʨI~FlsQJwohCي @Q=3ɰ0f;Pp$RG'fe⪍Q[u_<Md ;#SxuQʹQz}Hu>=5<ͩƑ__[yPҷyp"w1;b4YkM{uy5?}bY$"5;(+t:u> =`p=vɵBHQgBJgD_ %|:LyD۾X):@lf7H\[ hII;RoC֧%]!5E! J1:$~B+$]'eAq6"}u"Bd) IrG>QPcr)q`~"x`ei =Z$MK'f<1^Gp>VIGn[% :ڥ:Ϸ9۳~hO4Ulr9ܞ>vuLѲ']7}zw*޹xtCj^G>J|JtԙS:fSO$;Ͳ?k.D־II`eDKLBKI + h Q=WGxe9VVVm˙J*HӝMa{se?4$̾oܾow>iN//=^zkmE.^wĂ-!sx<1 $$ 8>Eﯘ@:O + qs&`(Z>wk@|n}ѯÿp }B4%4oqbiY^cp9NLGXp:;I$+:?"oKH19+wuى͍{>Jo^/,,ߧ +}' +}z[2:&sRrCIjp=aJ+mn<"sFxER3ϗ?裏f +V pVAţ\+3 rqYh V z٦dTRZl}N 5ͼjIE]5X,Gi_RU\n-I|r_}ʐ+ھ/~gkcjXR̵?{ЫRWr8D@DbB("aP"gD1+誩<wD&&G3EJvZ#vmzۭ@zQUTo w6z疤Sv[~2'GM"hHCAf\uɅS`t?'vk5$Q%C06ASig ЮɮXh@?3)l5jl`3M {md[DշgiԉַC*5oMS+סbjlΒYyYd͢IA-G6C[,PadA&5!\^uЦ +R0,u`_ˬ5f|#Jg1Ñ1`#a\: ?9ӳu`YS/ua?G) bB7[3_s. +RyWh+NdI?[k<2&Z ͬ8r?֐uܘM*'JbxB\&:t.J.wD_mPaxޮ;*3撂TUgSG%/CaM:ޜ\tk?7*OWkNTBV&zw3jyki3b94jV;#=^xx]=Ӯb-qooQНJ@%;y= &-|MfNtֶp!YPk;fDsq/ዢ|Dz5׿1fT.}b{wcs'Rš@C Toj-Hr]bG? DG7471,`iz Qq Vs[QORzMsŏho,:?voYF=f_S4LP4?K'6 Fe&o}{ڬVEY 7t;v<ԩ>uwN~pU4[W ^R`/N{>nLrSy睗7bN-~tl{ƕP?fr]6oH$ppFGUm뤙QIqvLOvK~=vӓ*8y _Ezϭryi<h8!-D]4r:pIh.h́+O*P;=ͱ2^ǰN:v YJMبmZ?6O9[}^a~f +p QфF+Q# Jȁl;)װԏf?N&fE$NeY#;e? sٵGJ搰4'Yj 2-x9{pbxۮͅoKSa}Ŀ2 ʈui){Dr oXO+{mMم; E7y\ vlWFxx]8 ܨ{Cu +q{Vq@ +8#ܽ|F܌/hn9IS<>ۚw/N.N𮄽, ;. )=kݿ20W+ 3l(LC dOyhDq {  .g&$Nn +c\Whdk=h/2(z` fq7ٲz BXtPr|<}֚fYڨ_ǘ'?u m@r  q$kcƗi0.L+Hn^Ĭy|ޘa (^ĀJlck)i7 }FnF4g̙p`jzKeX}aŋ7;zo~s~93 bY"&l̿c[Y Wf]MZ yBk YP4oE.-FNy0+h|FE=ޱo1‚XUZk@lcLiq1͎u  $(G\`{$ +kl48O99M/[{;lWkPyoo\)-W@EzrbljsWy߈ N +`X[6Uхx<(1L=V,uUlB#68ҕbgJ8;ZxzK.⣗\ ,4ӺJic;nJ}ѤSLnWtkvf$n0Zu76_/Pf5ihlu:'N};6OLyۋt,ldtl_1q5A]9'`i?]b=Dr#-9&?Ck pϢ\4o \̉S&j#ɠtQ;U/HME>YOW/%DZC{63mޛb:\gt(}kF=tMs1 >nz3ɨv8.}c(?+Q4NA"J2\|Oou][kOb-wYsA:_Gw_۽UoKg~2-17iXgm,'IaЮKgbOVF֬״_RK<’]%[kh_? 9nv-`HF-w{G̜5BC=/Q=yNT>o*~Ӏ%"jòKC7,if1?Tdז[)@[F{BkΛWkb>%f477NG~̎u1oBcZb7[!3P*~nCL"|ZU˧^oޱǥŏ=j][ V=P8hҏ,9l*/ >m& Wuj X^{3QpY?߳cjo;OVٔYa9ݶY@+l=ZoG򛫑ISK/ViQr[Xp: E0;۳ j&r؇|-9s.I!˷6]/VGg `"`Blt5 max4Xk߰iz\3ZG߿v gH nIi`35WS9jmV4I\Oh`p,R`Hm5Y;L#iԝ ybXI>36iwT}g-%3flTpg_Thٻql8q+T,!⯵CJ)ԖFeͫtܭ{K̈́vunFnZ#{fKhtM)߱{EW_OWή?=4N0za_Kҍ`uؠЁQr8n5]9"Gtwtc6\2>yʜ=xLE9pŢA}j[ 5׋صE&N@kB6VeV镓c-o̺GNX_?[C]X{,'IИ,8yOXǶ݈$^sa1}oCX3է[A)VJP%H>E5:WCRcbԛ#ʊa=eAY rwi(BT&p{ya.Y#j2 x8Yk?{Tom1io!e%8G +S|hl.ye?2jn6~WI}p*|}>1~>NEhE)d{xڿ4S qk[ai>VX}~}Wdc>gg%D=亰= BuՌ~ɇhDNqyͩcE><ڥ|kI=UR+ <6ſUJ#ozXc'\Ł&ئ"at3_:sk2Ggnw2%͖8.U ?$ &T² f +jebȏ}3a V|ceq,~I&^,'5: ȑ3 `8NaSkS7At:k>2DjᬇEE4WQgqoy +LgUE5v`SNu*RB,]Js +(j o߭rB s۠{c W60-eDl&pa }U1St42Tz# ¯L葍NZA1n u!_h0+e#OKff ) Mz8q>"= i$yNKoÙ]il.nh yA;:+˵=2s=f;>miQQxar !:ɣ/.紡6k ~Q*MG[|p8|"y~rT"Ge^KΔ1F f'ty /.A"Ȣiat&"l i]eUr&K<`P*؎E}ZQp"GI7bN' {!d:s39-x (PIW- +ёԌw(`H ?e{hfQ˶1iW!mb&>vM֨V/s0y62ՎqeΛҤ$W4EOۀqqrruJc!/eGB@H ٍB1-@|;"G"U lܹZ3QJe##Q#{LJ5%$aֿ 6فI`j/S- }a(-֢Kf˗}v;{d >>kݎ/}?f Un[)4[VqTSe LQ4dT ݗ ~ceJe뻵b\Ov+(nXl7zhz#~'~aӟ~XBo O亥j-BFEբL9ia{˥?,UL0NƦd/VTpcstr3eOYQ*=Ay"y.LbۏZQ n pʀ0zխr=Y'#0Ą Y&db8 LVS-mVp8έ*&X*3OJ +o`27e-bKU;*^j?\dZ_~eܗɾ`{0 nL#V%7hk_J'0*O)'z`1\CrxoY NǯoHxacz錳 jJ/ .:m[\K5f峥Nݝj_:?>;4yb$|%ήmJ.e,"r^Gja)$\N7 (A4| }Նmc@B2u15i 0!'^,· Dl<G3"cQb`>:v4fRݮjN偬(@`@QPQ2PL_)ayA6%7[&d2ӿ`eٌJo9KVP+5\ &w {9޷%,g"s]ƀѹn.x{`֦q OxV?p%kau|%žOYh-J\6%GH.fKƯ 鹥 bXh8|+ N~gxԏэCS!2Q +V˂pp=|lqEOB.;(CrpG$2%Y *iCJ"}ƥ猾 Uvt{cܿ^W0cfw u̱J'2KjqlSfW!f+R`h4{65|34 utI$ -$l:}VlXIsILV,w]N76Zgw?]|Nkn K\~&CX<=] :}Mvwi[AGQ, +ClXgWSagi,3is>DJ_ 9^p[Df2U 4L\HMB2OKuӮCJ)GmlFLՌUSNvpWϡcF =%Q]^KUvVJ}K|9IT<̂"v}%3٥2m)^@ɾd%/ՎD^270lJɬX?|OfzOv%晗OiY |ɿ4~PĀiY Lx*Vf,?au]KwJ0sMqK Vù+զ6H4f!nO67ͧ(z[8KiJR3iؼEp!fcIjl$"QL`'I1 WжzÑ\$yfh>LBeF (Ǚw}\]y c۞f06/}jGUX r=Ѩ+fUi`r17:‹dCZ5hŞ8%;>.]k۹2FO6]\aSi87rW>?'/8KvJR*b 0FWs>g뇃1RΣ+͇WkM~HY4ƻ0˚S'wKX4su',v?l*w UaT|zTcyoCN3Ѩ򦘩v֜Gnʥ"M̫\軬)LtHLVZC'M*=HgOgm~ct_xח[F('*heT vd@#zѲƔsqUVcn[t̼3dO(=|fH)8ݪ)pshX['>nJ8,*L8 +2Mߒ5p^;:H??&#mT(=SREwW +IAOu\]kPͪxxUN2+EWG 5-JP*;NHg'k}_6f!g-h˯ xO{W]~Osҫ+; ٽ=Qkӧw}:zRq(;A #诪+i62fK$Bk X bMVI:gǛ7/q +םt%I vl +n;^DtWYC_ ߄w zRuFT+.AZk&~ؚyS2ג<.h"F ZNtGڡݥSRQ + 7?A튘=! FORpB1G{k)sm4X|.ԶY]%rOm48c6?fij ع9ы) +Oiq+ϛ\W6ѹԦxZFjlX;5wDVYm82!͂if'z,;{dZt}(xA|QCE:{)/1ʬ z&v ´N]wzpF̨PK(17Cx|?`Ҋt>|{^W`S71%=^ͽf-dT2Ij;&҂G%*,3hn-،ԟ[RK盧%[HvKTƓ BNATH.`vսQCFA>FޭU͍wli'n #ܥNN|x?jUZֻ9΀Pᅪ1VM[2̨դ+ڛuZ 1X'-I 3:a#[1jo)x`Ȼ FyG68p$2ߘfhf['K +xZEh7x/Ql5 $cV nZ JuuIű͊m"7;s1,y`n=AiPL:^~oU:ƒW[ +iZs7glV+Ge8')ޢN#l!miYV,gr5/G^3s'o YBJ2@X݁^>po=ns&t,]cobF<_,9 7*.a3 11:#/t* omVGQ#HVQ+W9syfNF_~ _S@ؑWWNu_뫩.MK*6V?Y,$#!Ž)(dO^]k3ϯJIX}H|H]gw_H*aO +ueV@2nxmՊTߢQ%i5-Lt b8tt'+qPܣfJQ{/Y!M,pMOv5o5Bjr֭ r4<#ِQ zjl(IJOGLU%溥 BC1# ](k%bvfxžlk< ՠbso\yn>|݄0(#_aC+W[A(zaN)'U\|'w*Lu`И{$Te',TC:]KѪ9H[E,/"q1[d hq-8N-օ[UmU|Q.y:3wNFk,ymT6x7{nNǝمP :,;rU#Cyv V֚\;ϸ -Umwukz|ECճӠ5f'TVQ7ܧ^%]; IlI]\GUaenOC=|' &TzIHյ]W8I26.Mدp(9֝ +_ؙf6fe.Rơ#%ݭm)/q~;4 H5_(皦-U?!ؖ0i6_X9 9tu&֦K+fkeO%xG9qk=̛^O#d`m=A LTERuy93/dn*rSMLKm5@_cQhXXUVDI@v@.,aIՀ0 +endstream endobj 407 0 obj <>stream +Hlɱ6 Dr?'pX;qFCc7qKZ_rU*?J&C7d2Vu{G\)DW*E؁[r/|=D&Ef+?9L@xeF@i׸T7,J0)+^&<%UƠs]4ǵGnB}e&5 ^WFERY [n&C3M<< +23I~K:']nB!闻>raW>v\"V@Q c\eE40Hw@L9A"\}8&e2D=wN ztEIJ=,J>r`)nu%(D%,5<0_$NjhC9鼵.vޭ6"WBo9hrnvg5`l?G! pr|:ͬ[WXAJΟ钹8gj\wiCS+3SP*~3A!$z3I]{{z뺴r/8/^\ ׯM΁_\ɘpӬoDWtXNG>!ƕ|{ 8{9.nv|t y6o!>MnpO">)硒WxB "L`| W׍G:O!>(-S"!z{lFֹ]0 +BmB(P,>C+dNAs?D$ÑNd8f}!uxBBHv_:0oWn2zBۆ$zI32'<],%%282(4@12Z'x O+ЧZżtZZܛFkY%2k't4k>J0g!^fK$faNGB&WnouOsC$`)0|Wʽ'jɵ~rsr$B p63G{5 <$햝*?}U>f {$a+ɓ^Z?cǰj|k;)$rޒTES`pPیDQg^=!iBdI)^{ +f8>W|WɫziV|r> G䫯b1ungs P;bJT'M%5cr,Iܧ{ۭi'}G!fvqާ! Z7$1m1 'Syo8<ܓBx"<ԗc2|JfY ][n2pNaQ^4)+X_gC9'tMI>*YBdŕ[}T2ll!BT>GgA4()K>I>~ΦK3I>yE1d ꇤgc'e1"4{% ]HO ڭ -0맻&D'IV8 5 +zZ +\ 2kP^OUgc]v6 ~H8-p@JA3 Bl>Pi>vN?I?.!cpd Z!9suRbH#*_CpPD3OKӕ\'t9.q˧&EYdK\$0F(fN!@er]Sj?IV8|c| ~gj'k'-5*!J*CPidz͜zz:N80I?"| 귚RoxxFLjZba>:ǝ't9.q`]~ nEDկA\ pwa9njWWeXb~S)]pҮRK3ڝ=r +miD[£f0+_, %LjjWƓpX;vXkAZ.JPb25BX71)]/F4rwXZejaӅㇼ7 TUK% vT(32\C+to|n @OP +C2юlu+?6p_nM|#Wڎqv^r6|_&q6AXǡ'qDVY:8UWg8"YGdu!Lduq#:ΒqDaͯ5]K+ {O$OÕ ,> W'{BQv|@;3/}ytu\֋ȮR3sAuWs\܆9l#E qa{c&I}e>GwyDKw&ܜf7u1 C"̚E b V'C.s|dq#,wj oX6x,[%7$WOdܻY̭=etqkz]鞹+001狜q1{w3gD^ENx; /W3V%#y/&FfC&.V1K,7)d;ls[.o6vFYMGpӭ+Rr&e8wZm&Q3BK6(h3I[AM,+>f)nWζ~ h[__@8"'G@4d!Bd:B=PA{@O',.zZ&+v@_ޘ,͑1Yʛ'd)o,RY97&Kysd)oLRޘ|ʛJyc7Kfȧ97"kysd)oLRޘ,iGGk ^3+ ەG`6[uWus~ h!|Gȇ!D9&}}Oͅe}X>*#*VyZ$}ϴU2\{BiiV^7hV*\o(n^ͼimue> +\ ǴZyߑav]v?:łj\A>) !K7 K(E *YŖLrYXLTCF^XL1½W.rBq{#LΩ_dR2аɀ5Jaȡ_x_HA9ȹ5.ŝG$-JL8"O?%705NzK@fgҚ\qM. ʕj` cRRKR RQO+*r+*r+*J URO+&er+*3 L ,˞1 (:؍C;ΑQ*X28qsΘÐmКy!jrŨ8B"cES[ C,vI=jPB`T&{ +9i;1I@۟ӽ&.]ˠ?Gv~A:)j| oފB31>s?8P[Zdä98Bev@9 eoԢ~s0AOƷHNѺ!uc>\z. ]fyM^55#55v\@ȑi2w G܁,Y:"Krd@ȓOd@Hq}j0tqpS  pMtut=`=^ΩYAGk-ԬZ-;o +>Þk4јc2hbNTǠ Aqz2 ^yp0=td,^kCZ%6RB@DapE̘ +M@策A'Y(O +AObEcЋ+)K>'=*> սM4a4;S(P G=k&h6Axk&Pv4!Ver m`ffGM[f<|j:UScp =r[NƵZ;2hZ%&7q=C>;;\0v0젧RɑC;*ϛmAsV8pyV-*q-V8`(e6vn[ذ +vD1NcZcGRs1ktӼ7['a:ԁ?h8B0`\K! !Mh6;J MlADl hkuj6qp mCAW eDV *D ! >p{k'Z1f m8d;nу +ye?ضv-,FNudz##7|iJxPD9!z9θʊW?lFxW] +SU> 48ƠX|@J^!/m^G/:ˬ;"Yr q΄ ң@C}:KVzccAps ˥✬ fsK1Rh?uyY򞨤o/#f2*bB f\īZ#kzTBt +ܥRpjht l9udz##m?7J$KOM炫x?iD;4jwd|@U5|'X6RY:.yh?yYk/ jkp%P}WY-ӈvT5'>6J^'x^r(F(N;~GxJ5E{C5*'^N +^W8-Z'\PnsͰvKr } ^ב6N/ۛ~G$GnÚ4[jgU.@٣CjJU{%<ԴaxwfbI|@Vvs@V,fǠ)lkX4)r&>89S`ǐ6}UD)e됡:ioIXZݛ˴9ssZ2&bAX i PzHDJڱiuV܃@zN?%gƙ]#ѠG5$^G!6Wz ^O5y|xY;,e7+3(( ",`Qtޙ'KU{b[b$  )h%A5t$eփe9;&Ac +PQRb{-JZXQRő3Wc;fFN!ɓ(U\ZtYi&t""Ad:jq˅%ti4a,3gƊO2O3h&ʣj8H_י~0HD&e߬oeY(Oo\䪕*-X4_VZF#,IiLstbr{Rzk[} ؠrSaٸEʼn@Ep&;J$K4byuV\1R-O(>:&b(JIdj(<|NڠZnaVx('jY];ʉlzȊ ̸yկBQѴReY6V߃L&W16JDD\] i_duJJ͋JIp>X+A#X85G9DoDZt3D/bUMϑT)DOC;&W.fQ?!m>¬V#2 )BgmYo-Z5fVأ{hs(\#BIEՉ_bw +=\`5E3LqC¯W% Z+L jƇծls6NFd>mS6jtǸôRrcXn@][xT[UjsTjFB>z9*Q @B-f-; Z C(Buԛȹn٭Hu]MWb)tvlQܽ}ޥ-_lI&P`4:!b`PLz$ZĎih@΄mS]} |]nl}vqx#Jd@wd(჌NU](+9>@nR4 (оtT[u`_N{W(Q}SrouGe{=w|BÑqdr)Iuzd>(  ޵#Wȼ$ +`Yv%DK<(XQ"F}#ǾPi g R{7ZX`}-.z!b4u.6ESZ8`Omqe]ӐxX+x +R\}KM4I4~;>t2[H"OW)> G\9Xx@\#7~@ڡ~/ynUuO` EɌ[n'ݿt0螴nfo;ޕJokjWf;~n'ݓcKI#r~c"7@dy= @kDz>bu}PU1¦H&h|,݁Aw`i|N2 - ˛aէaucXDSmiXG,3* +*̦]P[]R.Yv Ǯ옾݂"ˮj4$]=ཝyo;.zM1 ͨ{"ahTk=k˖ZQM/ +0Ge(pb J|Lr3/nL.@blSCD R +$oT^lN㱨2K# (~EkLi9xYP`aH0t5[ф^Rwas1"s-:V!uw*юYG.O`r/*db7~LVR:Sj9&*c,"6dV,sK%2VkȣIvL_@8H"Tx[p31*x+&Emyw +- ߕgz| 2¡t򲍖Q=آ}Y.8ߐRf +tB8AϷ`j47P0}ՠAy?fX$hJLJh:]%,>!۳X[6$i*M~$4TC\b㾖b$dqV {1p >e~Wf:|Ov/Ć/g o0X&Э/b'Ko'~V3ɲρ /b +(2SYlmA_(Q g%\Sv2sv>Ej޸rlv׽̋ ]!$(FU ⃯]r5bxoSffIU +qxLiI|:nVr"-rG|èkh_֖=8%wHI@hfu;.,93+&6ؖƍdX7H 7nƴN#yr^1wY8/ ]o.&U}!nȍ+O[-/[Ffd T)%_7c z]'6e"zlo`ӦL"}/b=ƫ|v6hoq,T0<ã{5уO 6JE9@ӖN]>:9Z"s!eSLjOM]PmPWYT0*bvEn-%\ êd)iנp(9O71.PA~T,[_|/CV,"*os!Ҫ݁._?ϥ9.jM0Ɣ&2a I@,"ujLa:@Ѷc_g#EFibHM~z]*Gi3WC]+i .?T χݳ+~YSEeP:jF6@H~cKÐ*#oG..4eܗڜ골ImАsz^st%f* yGm4EJ"6(rbUhOGeВ0K&AOɲK/T'lBIb3c{O޲X Lɬ'4vK5FRfi̟,2Rdaҧ Hyu^<gge [T6+>wޖ*ˡ ݺVg<9$Ɍg\A'>CFuu`3cyej《9vyR35 +3xZ +|eB +6:[Ɋa5 )6fW?'6JE Z' Tj4~2 "u ~0:U58C(Vz&rbj2A)>ݿNff%CbqFoi׊\k8`* +>kXeY}+'"=bFF'ޘ)_l XfE>nhuU-EGe.M\;2$yuS:(o ,jHuVw0`=]OmMz(@G c)Riw" d‘mf +ŵc{qY 3jljB! z z*>)JH;H,6.f(K F;0KE63kUv 3&{}?2Q,G݈)VKSzJDJ]72>Xfuj` +H[8t Ubw /ܶ3TgW\9jwL0/I͎v&ǟl1%S"Ύ deձѭ-5pW`39P](#"mX~+iCx]=A[ ls* +4s+ㆶY}#dcG7wX8B~{Ʌ73a@\.JƱ9{.?U%+9] +}/ z.0'>Q˼@fǨd:Pn([ޯz*ਜ਼7 ,`Lq<D g~ +Aw"8՜:._C~l"mqja/eh[Q bF,Z)̇@Ad# "cZq&d\ 9>SV|(~<c^5#į'**Mֱ]n0(c/PYT`ڀԪbQ=VYr/gSX5 zsH C5k/Y(:[&eԝ0 ΉY BhzcsI,5_ pw){i^G \@o:b%‚Rn!ĠOc:G@D6>Sld6xf=# o鿌 M}ƾ :)}n1c9>ǯ-z\4<AVKc}~rF~xZCaJ}ֶy17} QL9Yuq'jbcۮ嗅! ~4 N +u e1K:Υecv~(٪g_Ǹc򩁱GWy-a-G-n" (~pKy0C2.u@+\S~J+;)lh7#0fhlq +;j%}γ1mz!5GF7'ӊLz a ){Xz^:n}p׊yQi>=v"P*-ݡQ aVmKDSZPpJqe~dt?k k߷ \_bin[vSkP nlV*ptbOUvH&S[ iH\HǓ#ShZ%AֹU[1/oS&el]A،6B#'řx@jlW9 C=G߿d~8FF9@,a5!{Mb3!w/^A9hgPmwX福bz}ff\(m@4(*㻷/EM(km+k@>9 ?5(XύD(*_Sta} 6[?׫] L_őLZXA/sOB3f|J3+tQo#p9XI #/d% sCA!&*Am#KeNS[*+Y*:SRto@x D4Jb-w :*8؈3u2oYb,/B8b}9fP`c[Nzlagd+ +endstream endobj 408 0 obj <>stream +Ht9r$;;=FA m)wN$a#Y^OOƠG& 'i4LF#TLJ[(ρ|yfr":EP e湕<'1kmTW|'r+%k> +fgPbEPn .dEMZz e5)vl^)NH0sY#P'a23*Չ{V|5XX o0| +O1τ?[B.ufF:WDj#IuE/´}[iHD[s;du^KCz]oN:wY "sAmmg-YөU{W4A;`=/Ao̭WOh3hê"nYd(%޷0=tzz c.6"~XdR`ou'v@N7b$Mnc/X>)3R%7G57Rv- P||Mazv-O]G/7,8#6]}EMݢaFrtyvqzDgX!H e'p_R(ʷ.`V܉U˕:x;ۆ9ҚدOX# >P1h)㎙f.B·@,/=2p+,'62ʐbː'%aqᦺ Pv i@CGLdM|YYDY5=Oex,(`sI3ErsyFGh mf*|M)Ly=ʥ]|2'\i"Uˊ ҉%Q +6z d3 ݭzdfc9 ^ UBT "+z#VлIܿa4yU|YFꪴMp M=GG}0.Ǹ/ș[b?eK gѕ['@6LK`zu>GƗTmDg} +dXԗT3UNl8'xKC,1}~&>A+_\zA]>;fgmsoh~7Nn4Ӑ`Um,CrZsT&o%k j!BBώ"RFY7Jz|HjD萜B-?vBRԘ@ҐJG TO&;;MsC.[prOEC +< +mH# ):eVYfoe|M(]س5n?g>oо },(DI2/lœ F=8֭bY KL "ޡLʴSOߛ|d#2j+_%ɎUm+xSN>eJPi{PNq0p|Kҏ=@lSZ%وp-ucHy1=1\^ա_J\bOqY^2G2pR{ CȐ,ԫiF"0VN!i.3iC&2|A 0DOUrTC>XbZ-y yOozm0W'~GFLzPil-#J H_chYrru@)t8"HVאQr%<}Y /?\R<7ϲeU=g; ӲePWQz1u!wҢe%NH/iiO2k6%‘ae7@FL‚b!n:So*!ʼn F ˍ.;#GqA8ÓʶSRxzq~0Xd9WĽ$\fW6wolW/d) iq^:k^ԳuyI>5L0RS'Z -pe~zDtx: b;&kdV*dWCqZ5H|b*Etܒwpv/q{z +)dݮyjߪcSB e]-p˚@zb^HX/m$ԊtO6Q˽J <)% fYf1/ka 0m1#?:R>,=~ dv{h:|r$1S3{ʏp4zsiW}23 }%2 T:l8E+wA +y]xj AAS+A0({o[Mj*'vL*  +]Ta@?"0% βf/66$>X}d.\RZ^7DqRu4L\bHd$ =C'u3BAX2t`Po|_֗C w I9QMd)օ4"sd2nn6lԆcT U;/S۶1 >-DS2/&W|)%I1O.WQKKI#w/C6I1xoya.]82/t?n&̖c4U_h콬i^m)6+)975HyR"BN6ĄJaL`{Ѳ-JJVuDH)+klJ9+cuGÛ]#]V`R͹ב xsٽ^%@JAͱ(+2RbqF +56xRȹ.v^蛬3"EbI/YWYɝ:Y! %iStn7p w#9ʞƋ`GECvlbHY/~܂,fD|@'`K*)B A? cf,eSa |?ȥD_b"\G! iT}ź[ʑBOpm<`1GIgA..[/vuy.'g yB +rVS~fE/:?69΀bq&X@0>5G5b[.#^T7\o%q[pLFm#N&;ㆆTwƲIetNq6/׽'^ݥW]Y˱K (l_aal!꽊!hHpeͨj\yU@";+܌= /g$']3絬k TmfL\܀}5~/Id 9&QhI&K9OJRwh@Jn;fJʬ֦.qkڶgH+xlYui5eUw{$ 1ȀyRz!4x4RَsGT+܉q~yWkPK) ~}Z[(y/OSސ!#qmTJyt;5EY/ +Jk'/nbN|`!bhNf9gsmE%Flwie~gY[,@FW*)*Zj7R>PxT!&mɃ_Hrie ++Sz5JhADu~ShI\G +obiq~hQO;1]YMF T<5d8{C%ƌ\ ROele-) +s9W]3G5jf僬>TXIug#Auz9QCWSs*b ll6O>i@rtRJy+^ّyuɰ%깞_l-Yj83> +V73I!OBϾ𛠭͖> ߣGE0lXN(i[ .@j h[e)Ous?[@Mp#F\*1*W;'()(cpb2XQFHI4k.$1LM5HU͘L1Ń2Wyה4?Ւ]K +öoiymMf.P,PZݑXǠ:V I)Ƭ,Y=6tIH5 dO mw,uPADPJ)S(ᅤ$6䣁d"f 7ALeUgpAgdmy5{ܔ:Kb`纂 1m'HE%XRfʆ, `w'9A\ԑ9GK>CUrp"nCoꤨC]Ikut=Fjʦ +utME"rR'uKMYKlM +Ђ:tju("iaqeYrRBR`C K)3Gmh1{6st7s 氓Xd`8yÒds h>ԡL:,٠EJD6u<`P#aɇ28C HPd|rƇ;)V"$ji̓;jjV=;e +蓞ɵSN0{τq3 x3!qTkR)V +j: NZ3*zNZ'KPT=t_EA|2תΆmU$ A۪`YA *CA.-mPEpX,*,fly"^QW}6#"410g1e.g {[4%cȰ"} +B^yGrUbc ZU,7 fo}C8w"=` n5Km&Ǥ:6p ՟2+4:/! u}2KTxԵ&xRh{D`\Em<yS  jP^{I"IP'f4îMP2 +/;*XW7|gKf>LFC]( `bWJV#x#:.޸A:,+ss c0wޛ&Ż/.e(0?:M<) oNZGZܼCFLP,6%;GF2sԼ`DE뭭<\8ɹYZcUDQAb +X2/@)sjke);y3G@D8ORNE}98~SG#]rlҦfLsx?Sp] a4lkgqjvɦ^;dփ0TNESaNNz°v'I!wk/kgkwdz.mrQjR6I:0#vuKM3p4(fx;-]ruY;MYK7%݅cX6[>%6c,Ѕk +1[Jl\z" +(XɧJ*Ns6V1TK rbܨ9sʷPB=5 Y䨁 6#%׶Tg_HFSsN/`QM!?=udV)k]~nD,Hmt+vÚ bͰ2~6ֻxQN!OtO8 \\GN: +d$@ X}*粉T@< i3Yl e!Zp~2NBPsD8Q3> }JʹsBk$M*7Pſ_`=1h#JBxhFLCtB:RV:Jܡxq?d.XZ2-fGLOr挚y4_j(/l$Ϛ>_[fj:şr F)l 7CHgȠj gH 8! gwU62!=lj?k3&>_}h?>S#3~Aj?CRͷ8-z:ߪCx>me©ϐdt?3d '] w8Yw5ş߫l!{BR4t-՟5WPEϓIw!҈R By%,ԟB#C<yA93D_N ) eU*Is/Bfxh(kME(}m灞E8'*{igoUZݕSgoax~kDZ +0Ə-ʶ^1Qm-g~|KY[3[Z&wF rj>Ԧ:֢ \hͫ +QXV!Toy] j)]`[Ԧ#rP94wUtW^R[DR}J$"㝘s8tK I{0 u2ѤDH+ O:rKل"A2U]X{Y^ڐ&g1pKBٰlN Y}>bQ#DLA{Fϔ7]1 =`IL^ZLs{F PbR"WOa/me7N8Y3De;H(C1'"Ĕ҄jfM*ʧeGfT?UeǮö+G5N $ _֍+$ H#jؘYgkvi9s+vK'O[,u'.&>z&aːd)4[j@\lIsWjڄOEXb/B}̃!;LX~OAR&sg96)hq!JBՐ"sƵw%2U=5)SisJÀ~j+|c]" [գ RhxnDz$;1JLc4b}L=Zի +'ȦRT[h)T +%Tsz5jQ7g55hU[i6"fBBFr`Ӻ8jkL"5.-u|>#׼d۷,)_9O&u>4>O-ǮE2IF%99CWbem){Y먅f]fNlq4kvvnA/oӗU@Ftr[ʸ{ċVHe'Z~O'&FAUb'Y3pjؔ l38#%32kٷ9KLM% x)9q泵Q\)u9c)}Qk]QnN5# Qߋ +aQʰ.iaQQs-??"`28ڨIb8RP9ic,l+U?~Ty3p/k:/NGX+L3n7jng?Wݶ8=mo >tJ7ս1[_5]<$621=' J[~>XL5-'[ +E\Rn}y|']3h9|8A rN~} +'j~GlX7-w/Z0"ƳpNdr`{AdzlfU.<嶬 +bb-5m(t-<(hk6H#6YoQʛNL!GBo+h_HRT- Z=Ig1{kz24)yQ#&Jo;܊{I9QMĄ8A7aa8&yi\:C3?dKʫ"lcJ<)fDt ' +c] kCN?;m[dxɬ#rO4Zps"D~^SL}`FeD*7zK >Zԉx̛K]sdDmvإ]T +u uGs{xjnr *"_yQ)0OJdJExhAiJ]zS)/*Ť3TeMjR)^;$'I҃_q)$ +*4I$"u."Hxԑn"u}oHͤXIf6&NbR/vȻ z31ײᷩ;pJ~Qib.*Qy*҇}5 d/+F,HWA>J&.@Wl H\)s,ZFSٸNA$jo$/qO@N>1x4٬zg^g쑲.!O?a*`o3oJ~Eh?Tj) § d@tG}Z"}r36βs_NG$*7k[` dxF;qDg=A iNC}[ޫIHOR";o[sy|g ʍ: )VQ)jr6>fNb\ j36Zt.Zo"gl-`}v6d'K|lZD#?5]Ug 8?g\dip=U)*m-)]laK!Xeȟ#?s'149~ox=r|#,(ﶹDiIϲhQ.[.\) "\3D쫾nŽ4w^w?Tgp,vңܸ&}jS'5l~X;l2ys#QeW:J޹mN .4} х8f @Y~tI\֗!Ll,DʚsF~{fh؋Rfh>9bUou[XP8w@"e^;=Va&iCZxչpĤCPL=˾ޓ #(n+&am@*N:koDh}ݳm˖*Fr`8lVG/,R2ʾy&]!Y(m>ޫymtHK䲥є|(}8̍lSm|, L B.k']F=rJ-"sKnqjEVI3*RӭuDl#wdCfoXg*xa &o@x/,'S-Aɜ0J;Z{zpYv*g=hRa4~^q* }ݗB(mEG8%fW-P D-3ɱr;#"x9#@DϤY^XZ0qFm3:ng4$GO:g錆Gi3)zю~9_hrF+쳿EogjXmз3b!Hg3^`8ȡ1XCB38vogϡP\$orF3ZMht+vF .gĝ2og4]H@\>d!<. +~ǐp9"j7+׺Ѿg7~9yK}#@972?1G/skqhusA!Zns4d91ڸљns4dK=%qUf9ZvH^^hD9!eԪ9~9ў4m2Gs +6^/kt/k4NJ]m^]SYY3d2Gis0Ƿ Pc6wD~8]u$è YnΏgCfNw +{Bh%Z9$%;BɊk3EC˗|)e{Yb' ލbvʷO1iճ+z@yeJD{#ڷGI!8nw|hYl,t@4Î1}@\Em>*R}<_jK~|TAٝ۠PQ ڛ=UҘ셰(R ~c:҃{ VC)ƿH WLr9* nP"dvQ/a9ÇOzYkդ,9sHɾ,kGdF|o10Mi`E1 6w6JwF.HwKͻ<~qty=hntiwLb47AOn.h #:O4lQ2⳹dľRX[ug:!zɊx[buDw`OWl$; ~^nR``vNȐ-HDam~q#U3@[tz mgo5\4=IJW36m+0QVMGuC9꫆z(#:`^B"mDtmA+{̹q] .~ ?NAsͿ,5\Xԏ2LZ8 [(CBjIaw[rv.69 P\1!,3eZip1xcgtvdL=~"scf~_%I]e`f۹ld[&ɞMEH0Bf<rĀ`XIC;f@F/UFj>QVR/y 0Ìq~ұ].G4mG7./r)@R?C`#ܭƦ tYֿ{X3-1Ր爋ΟbVzek4'\5N)BUZ՜'B?fZ.f~b0X[u$\f81wdG3 ^hXlDk?zs;{KgGǜа 4n+b KcHaͮ;^nLJ°D>bXRA8q7{iwE%mw쮥`} 4NJ~Yc_(#{я Z2iHip$K=/GYY8+0ix ip"6htF1ag5~*a<"Sa +"3veHW״gHdtUiCtکO­i^K`wg}=l< +Ȑ+t;'5AMUXy!tn& oBqB$2Y);nmMߙ 1ھXA+U>K֬,J9}}8V}tP +k,Et6-5* +lղTיd8 +k dahb\dL9k3-o2sUØPE_}1f_7էgvuZnMNℾۺnuHXFOWw$ݭw]ƈ;/ս"kWW{z^'Й|z T,"[cuΧ;2~Z#phdt'YlvNiBJеqM +m X:eŗv`gX2|T?p$Kw\!5vFQ;&%;'","PX/[D.k~yp~q7F皰fWGJmBt ͔3l)#!j,܅tz"Tdb>'Q)cA(k uB?(60udxR+Fo=5EIwIh$%t&j4i}^ÀUnD麢u>-bX-8;dxDnH˞AVȽVt,>kö0c&Cܼyk? 5Ked([2wqS(Uj5JXVՂ)u{kBX)/K -Uhi.; nvQMĭ&V&9j+]8 vn vFģ.q]1$IILl_dǮx^zAT#dBʯ5d,bi Df3ν~ <M ]$I"'SIё"Վd[: c{5}c{sJ[b 2B9^e83 &j + Z`B_T7,tZoӤJz&j!=UEwAWďFq[kiI8B܁BӸiZLݑ6UuVT-A "mYͲ1NYv/ :^_B%ދ~9Ӛ-Ơ{ )l'-ݫMO\WF`hALz"QsTD*G lpI =I W$ҵi"ȉ$xzOxDK"|-+`CF#81D*1$?BďPb4IiØ$ %xk/Ys!APa0Na(}sExX҉!e2M +lsp ~R]sY*vq K 3@P9 xIKRԽ-}#LOh붵nF`91w;Z96-Q|p4P(5L5xeW}/d) ;|^b)T/ LH^]tCgȍӆeD-VJcpWfJ jl;KLsUexƉu|}ؾlJ|s^jʗ6/29쌲67}P\fmL1}Ӎjyh/1׊x_%7c,k{;0q02|T-F11o2o1=!Hg+KvN\{2)nNk5&WA殉 F+U~)/mSy=eLG)zİ*+=]P!Us{yz!4b'9~wWf|"D IZ4 ؒN=1{{Z!YkqbWFŽ2t6sÛ#䒛{*_!7CM[&\C58TR5otWKz +JVp?=@BcOL}?@?vYxטqs.ŏOe4t{lu߬ѓx\=ض7)qU.Eh]; +fڥF/7CN(괆@Cp=9v"/JEe!)0x*5F0Ѭ>6ʉirekeiZ5bRg13<6PLf ~+;5bdCHQob/{_57z}p.{!K :2h:د.aϬ킆y3kjmBI;\O=%#W9Y|\KBʺĊ#>i\},7 N7]P6en;Q9ߢPH/v0Wױ5 qn0۩0L$)9 VET8p(c8qSC?+Uٸƛ B zjpU6P CL֮jBg9VcU @kryuSQI%0$ 9pnQ=&z% rWqnJer-KO[M6˩e$\27B;`C# +1:e'r(`r/UM6&:|E,%s%"1S8Y9$Y vX=L :b,'" 1\ ~ +T ^ dYДu*F%0#:% f'2tⴋO"mL\4T]VU"7D~㤭,UNR\~Vn:,NԉŮٶK?\R'/ʳKC; A,E7H˿ Cғ?7x.APyw}7nd4bjrYm\0qs] 6N.o?SXdiה +5\#ѶBa Zpq!mE?.>\E;~1*)YY,\ĞOXk۵ʇPq&$D2ƃPؑs*4]2MFhėJt5/1i Ө,  ޱﻓ~Q?to4𲢯FyNJMR8K+ 1 ^=+a 6Cr-Ka6!)!-jW{nC +{/Kiӝ[.ynl +4d~b~DHΖ> +c؅Z蘖zy c)ځ4,wthIa7t|bu%;exm$܊EVkPW ꝱkH<^HW >$غ-͛&U)k'F?K!:EARDs%IqUG2͎=Sc={5"Di7GeqQJ_bQ̨~)K=µ$}T EW)նk$U3Wdfr1W9"Z|+^COiWߕ2oIR?:qɫDsWseֲ3EWnw^;(sEI2An<YV"DVR,Y2c1_t+*YCkj.ث1ǻ8e^P5F,Yf)׋onJ1PjZ(FE$sĶ44Ѿ"4=8恬-gvKXhC:!N!ϱ#*8#>JK0V)Ci +r戌#a`c1*/"g.ebx!oj} HWkoE.\u^Kޣi(fjM^=87y`[R8 !A$fqibc,YCHX,s %݀9Ȟ *4-baKX_G M-5}lNX)ص1H +P JId "tiH(v@y`z<5v_:I3cQ=Gz=Y(5Tt>YzyQY!=PHa yt̠amܔd߰H"Bj XbB&׏<{s#*eU%(T0ENlrVbM^+:vS<Cy@p. +X6A+9wrk+ڸ4PP?7O}~橱`QtJRs Hp'xteDп;IL +endstream endobj 409 0 obj <>stream +HtW$9((g_0Z%'TRd0HဧC=u}!)һNCf?9 @TN ;2E_a$ +řHAND'(P^8 fK&2.Ľƴ-̐$3w{\Lˏ]f!€pGZ_Uz"e T&} !@q#m8B*ġpȏE`IDt4H9GYs`D;RG}U|v~"6H`ϓIH\$Y,q!*BՓhP:H4hT6R'͌zKDib1-.,cj#ƀ"aN3rb x|:h ,B~bgPPx".",]'Rҷ}I_o>g,_?_h+dI 4 +xTL%qU?FʅyT8m~S#n.$Lxu2S QcQW>'guW¾g!v?1FaC-\L ^q*7ጳS?oy`8ifCȌ.r?g^Qf`%뚈R0jmhBe&&߅@w tORG7:% +5?z!ה#P2rfE. F5 !'swۢHo9;lQ;h(5h;ֈqo*:|!9>TfrN3d/+Wi8*UA{WWXgqِ^C+k4#O`k|?öx!J_dC{1\18#']0Ecn22Q@'oRԶ1 )9\|ˡ#I8d}o!c縈d3eCm"8͠NrTѵGƻp'*?Y6Ʀf+w|\[.Xb!N^]iHqk^gS,Fj!700LџEQ޿8wfK\ȖL}xluR뎇`\f;];;@%ul&6b⠘H̼?R)6l1꺩y!gǒmVO=? !YDqPcMp^7í?g3e܆L+e}^ RɑXlgU7?̖Su.RWsl"Y~Z|ğ}z3üf\PP 1 ȄMcƕ&Im0dzkg ls˥ԕ:1F\F0^*bfBK?ɐ +cmU @%OwգY ?vbӞ+r1!0"z'oPЁu=Sz(kS|zZv -k.Ԗϊ!dfꞑUZQF9ӿ_k؇(5Ӊ.c<"`.XcNdcImuIsKz@e6)-RQ|_ڛU -R1}1313aM9K/ | zE@\8(Գő\7g툴!43/u%Zy3jpOsG|Ӧp ,wxz˥}(e#d,k`we>R4ryBQO>]@ _>[=rΰM~?^TYK##1v!*iRVʸ/>4ck6SKV=[` +aսj DˆqȒ>,H#m噵Ǭ NڊJ/ٔhLʴEdH %mnj9h5SKՌCޑ7\6Ъ4m群>cw(a'Hj$/?\ucQ=bmi͚)(:Z;v5BJŔw(u:Yx{'Gg"f\Ѽ B_yj{_MLWHG.!1]9#$WNk '/Boy)rz( ϗт4J„[{:'WtXx.et>:!ju7eQ c}/(D<㓇?׫ ./1LUp? +E=vm/F=R#Y[(364`P fNBΪn_W泽kk 3lBǰ} +`xzFѷj<]=C_6ƃ{o3;GU]Oyն_,XA@or^ilǖ*7Mʕ KЎOE 0iqM4f=szl/KBsKpc0^n>6HuNZcu$V_cKIk h0\ +MFL{Z@0K 6}.GY= J;6>QL7 @@kE0N+ʅ,e\0W:ډ-:k3NTZm [کE+KHHAG3};q^[B1ܕ w!JuE[jVRXTZi5٩ VOe k*-y8u<g'7=V4G|,Bڝ;qDXS fQbx%&Xwaz0 32g(:@ +! b dT"٢[mlz]ňrWda}<l{7~;i;c8<GlNM,{ ocsgwCVN9s$h(mVMXaD$y p=".3.̣2^4s8BwͰ*7}@ԹkO52̬/p4;N3ӏ1AfHW:7&DYuIc9y{47-W᭲fKLk\%c+P3'խ5O}c}JYo!Π津L[{5x~ȫ :;\.xT0( +5]e!fsW*fjG,S$]]̮h }iBJF>^?0[kI$Gh[/ZGnVȣ*yBȢgeC[M%ٷXGO)ƝFm[8[HHy|Fd>75)P?WpyܽFIf&YjApׅv_!z B,* O^k+sJu8 mp-E^sMj*Y=Ln7w2%L7ȗh4\Gu?l +m:YJlpFFƱ?!1@ٛ*zL^a]K. \Jw2'ě!lĦ{(CxcJӀtK/I4kdJHژ"kmQ-:yzQ:}ܜUȰ۳Y%M@\\6NjW-d{ÄHuh=t@QedyǤ+gDj96D L䗭~,оM[OߍsU=ib_况狸g2H+oH8 ]^U9w@ʘOYXƲ= SK2xl[ +m',q>fg~?k īeHoJ P."qA&;^O-'%57ȋTrL?8տG5CR αdFFm78Jy3-IAp._00{t qټkS!emp1\H@7,p?bh$-`btTV5ΓRE8dp|-KO/ JZ]{6Z@`M4X?E ^eC\qp+q9s#]5}6s,8p==iEɻZ)=np^ٞ>j.;jodw pe+x##Xĵ.ojCA<Oܾͩ\Ol0DUDφ%{ pv"I ulե}y9A!iFte/4B=I6 M O:%DfM)=.1];?I,S& Gޔč1@V-K/尫fs 5*_*x}d +B3Vwof¼b[ VMc$8yZ14䚜rca㴁:E@G1;&3J6rjFO0U-|"Ҵ̳qW"/ Kl[?aj":^uvئ-Ȗ# t=.h Eh3r D[18 4g 3 SGP|䱬 q[k;y9t%wMwxZ[т! `pIQZ/Am^ e<^t[HPa8#QQu5l|+VF0 Ɵn0,1x9ә@|k%y(~q :{TC"(λ6}6K16^x6'Qxpu*xfPԸ+QfwϿ:gݧ}tn>߾x1=iYc4 #a]0c @5]]QI[Xq9q ?cHG\w)E/J k 7@D\.Z Q nbcmhxq07V2S"XzH|JL8:b\ˣ\# sxϭ@%[$jV=mΧoh)9إKܜJdjɒfؚJG6U0(E7X>͑Z~m#+m4 󼉵?\ 4'$ Grv}.ӁC ~Ov1Sus5kb82pyVA2u=*@@{Rqc\YQ2+KS~._l9løTqOxauä?u%ԜnS,! ,PwY~VuxXRKH]D`v4OOoScxZ-緦p +VhJ}F5(j涨e|tݲqş; +(D#W QD njMN,f3Oۚ[uzi^&uf@] ]l+6ve:c=mۅ\X C_ 1qkj$31;]H]Ԋnщ \x5RxJVu㬱}Qx4'@޲k>o'&*JMx9(zm+Yq&,ET ~dU*%[e#c^Ϝ/W?Qy#5@܁qY{,L×5t +lH)-!,^ݴalև< Dz^HMNÇg:k\ew>˟Hq*~tYwF$5matJ5Hd7?w+xo?m_lR^iM^dʑҾ]tڒE;9'%h-#.*ȘEkXLBخ~pu /Edcxg7 , +t1 +B]j8">8 .bK#NY) [>"94O8es>&uN_K|Rd?I-tjQUO͎KXiԼxnq{rv x~JgMڶtJ3|HHs[ ?ɎmE7H&K@n|bF,2?2:>@6o|vVh7V^ZU;%^#1Gdgºj}KrF[0ǃhj= &$5# +*\~ yT9*y$zaT0 ?GɓY/rF]0_F5NʈǩT_>Tzwtj` ǯǿoS@6#E߂F\i^t%l¢Խ86ZolpXi}7>STi%,? P./ar Q^^~>I}% iJ#_^‘əm{ VAh%ԗz[ !\Op-Uf0*݄#t M8".7_݄9O7aP>@{ +ζq/;/hxhWi''JLs TK {]f"<_f=և슐>3οJF-k{;RktLt #1qԃ1 hK7G\`F)3m!•)Kcb2(@=@2vsST*kbnĩsW~i`y}@TA5x@4V Bv՟Y'O ̥e|ٲ(32b82;VJE +\csoLY fb[k*| +|V6e?.'X.N +%J kxoS0aŻ ~kEH԰b^D}ɦU ( (7mf]bU7{]Nh{$rdy'DqQeTskމ;5\1ZA/e1Q -G5ƩpzDZ"F1IHz!;hkq `> =+>7ʍfXSd!;`]Nǣbp@,ym,B:c(Sf5+ W| W*435%Ke<+ɥϴK뚫j}i%fތ9ήgu6R>/ncעdYikGeK\ K21,M@ZH2"U{O",v'R9' dR_0/Rlc-88T@ GE%ko>gwƌl<92A,Ѣ _ 檤&+q#H QoA?zJapY԰ +>htc,ְGɓ)oՀ̤]/n8tm\Va`8q6]kUF 8Σ)Ǵ\>!#~! f<`KII-Nj0դ6. +\b+2"28=mXj ϑ<=`)FL*Zz"sgUF ER[bRX k)X>0|,`ReJdGꅌ4R8S쒤T@i^kr74XUf5ql7%#4Ȅ82KLJiW;xBAa/6{|qNd«iD{ZƩ3 P3%gcYr_]\5rЅL^37I5_x#k{[T mKch +g%8@ Esɲ-ʦ=0)(,/_X̜Ō#! H2} 3O eN7or.߃]OiͿZ-n,4m[$T`U#G~q =Gn,Ƙg"{%1XZguJ>pRcK6dL_1*TjhUa{_imTK&ň4!!mш/sEz9}\|V.D~Dž`v}Gb?2 `;r ,kWݶMgo!BdO?a~}# iK;WՌ-xx>6aC^#ے;wȎ3tW;v%ۊW0GOtviJu]ÒJ"Aq<<GrS`^U9?q'D1h @} ?ci Y9N!{?*}xf(4x˅aIN]k"*\t*3jK t*@o1!^}LBQ,7\}U ^0q&o9 [_'H/^u}BQa8N:3{EԾkDTōglRm|vP}NYCM94J*S`4ã\Rsi$QsE4 ^+@vVjFixO^= +irR ^l',)hhӢXZV@t"G cnF\lNJQ#-/16;i^ɐQ?9T 3I衂-6ά$E'0^@7TƂtP"pь#M{lnE渊ExԤ*4ͰT 澵nE5i /ke,<IYwPglk8X>Ʋe&*ݔiiA5+iB2ݴKޫc,d?~_(RłHZ!,FZdBrB kQ}Xhб|"YFmm +G96X341luom+68V~/ݿa%cds~_B5~Q1kH@47Nfv8|[1FCJ IdpcDa7);:tumt B,P"z]%6<9tlu8RFfuӒbWY;{ K{nvV`̳%Vk@ c}!(Zܘx("ϨuJWѴi-"h| 뵱16Qc&y׺!sek.3Q*z PG+|!w9"43~͍yȖv{5o57i5|tu-={EѬ`m1}գhf `atVCUJrot)8JӀg; snSs=_C6.e.-!ݕK735޳> 2t' t~|*W ]dUYi*%I1AJp<=YH{|$ײ:4ϘDFCk'{2Jk_9^CRX1Ŝ [ef_3,P[.q{I{U*R D޸0mG{Ⅰ(o;Ƙ'y,vҚ7ݱ,-%3I +*3eńa_ҷx\xb{l|@{6AD]̐&le{mhg[#]dvg5|VTȬZ46*uH#Ϊf OzׁCl`Yvٯ~Ǭ3`}eRab >9g2LȪJY̱qs\/(eKW(-edž2DoAd5jYΗTn' 6|kA_g1tٟnј[r1qZ+^9 <沴&I6Y8!`5A92/[Vd9VEP{8T`m$1Ŀ0_m~ɪ&7_!(?_2-Zz2oʃcە^ҨAy !}ԋ򆰥]8 ɒ]rF7OpNye?naUyԇm$o'ȖN+ۅUY"C?|o|?b{9Ôp\s )MV([)XA>/N1eR. sK|#e}Rz:K}#'4ƸE/`T>HtؠP샡dśc Q`kBe'93ND]<{+7v~vtQ<ՃSSf +?X,4GO aK^ӗc[UTc=0*oծw!Ӧ6%7y:!|2뢻p#['{}k2I]hook ߚr=L߾zgT~h\cgڎ:-Yh؀dH%QV|4PL+]jewA'5Y6$դBfyP1D.|n=[&y!U3نԩ>j#,Zcfc3Ur/ۜkIgjWdr{( %:XyIhwܨ鴿"E_)<6HMwX8krq]ed\%/j"L\D Yy +3z%ҧqLm=t^+m"3I ]B\bBt"jGm5Q_?H`k/@kGwdg° 3lfے)*,>In;B KwKbcL[FkؖR:_vٿ#mZ/:8#]l넏_8X*UPΏp 5b#}MS-Hi6"Ca=) ww06}ߑ]xoa: wDcqLwpd0 ];Ph-dF-*.?ng,5L:ngBzXմ^~ڿe[oKW *JoOj"jݘqցRd fɱq *&v^2>EH[#fYcn X܁q-mt --g@kn~-=]h4m*[xF$'̋pF^I0N* 0TJLj+O>3L 8ONACZ@IEs2w ߫#hػNovIj#Kڼ!ٕ` 5Ԋa|!b c6U=ca#U3y[tjŒhg'T=$Q#p`Db` m N-Q4/ʷmHzl?RP;RJ$L7Hp#ӤK2XnoЀYu<7 -n_#ܷ9J*wF5rO 霸e.B\ȉґ"w(^~ iydD-\HEl<[?Iƻ'x3,n8VL8kkFUE*r_PFH5N?ocs>uIv&|d:zfOskM6 l]wM@z2*j@Vo{N۵bS%=~_i-.iq૜L3)uV{oͽ YOwGi4ym/r97I{y/W}iYh tȦ~:/C#|l*/[n{PJhpF"5t7Yb9m 3w9o@եґ3sb]^aYӴV#f]E_2ݾ<0u46y=V|”["zCA6;!=w``тіL2;mBߋi.u.WfF6a9qOHD&q cPNKef,~Ƶ({cDq*Eű$Æu)e9bIONTeY-.ܧnrNɫm4-\v0 -덄*Ɖ؏ob \lHٲMWvH6R(C:{7^Ͷ.;Ƹ$Lmqut 2F30F;ؗi漵ʸ+f+ Dc+@*&z/uX0J`Y'bzyCrdZ~v6zjDP—WGEOtѢu42sMޭ +4Wav@MW>w?8O7 -va ^PzQPp`GdS]e-L np?? Ʋ`rƬ'B(HVŎl2'U "Eec:9ݰ.Ɖ#,MD!NL6FBI]1wbԞԅ-#{al)I% +51{&~!UJvC덙KKH#J$ikEu qQq6*3qi[fibw "VgX7R`7NJ"H uo>0nJ%ͽϸa!_H[ +m('=L,\!@5l=iluPbKR+Ӡ'ո3ULh@g`y۳} 50J®lsnE-hMaSʼ1(cEɛ`5JvMy885QU([%0LΚ}ՎǪĶ(ޅӹOT%h;U T@k^ cTܦ%R{jڎca r}PySg*{| #lj +4H&(O \1kq1Ƒ}%v8w[:L =q^ +kYHr{ek[7=NG na,[X$8K8k`/I`֖:Yo0;ͶAcHKΙ{cM)6ւ%"m@Sv5ιL sq*-L^ļ+q$lL`}{;HF$O*!"V(4 b;]66q rsQ|hkL[,3 qo\~lrTvt_omqmJ(ˡe}) Om̌^LW"~7Lˆe}1RҒ3k;'sd( +BkVҊ ~3)^m e>JҴM볫Oר9R:y^4NxbiS,-4Uօc8U4j h ~ȢkoPSLj7ٺtm>MИ!tS+ 19 ZkYZf/:tyݢz^xk ^G1OLG'ueWgHeW5>>檂ua8*H3w'eϋ#Rd dNgv/Xbr?>;@w0^Dr2S  \–,"3ij,k<>6R³} FBy/P <ѥ4$҄ԏgȄ+x.@VT}9s[3fkѴSECL3YZ,I/dMF|#-Qe0u@Sq1^U&d݅"gxE}icB֊žR Pl^uuN:A} |ǥgocZ*Wv +A%*``!#9R- W:;be+UHfNM{/mGYdlkr~FVd{@DOXІn՜.`vaA +endstream endobj 410 0 obj <>stream +HtWd; +'b5*x}cq &bnsDN7VA$y2{%BbZȚcś4ta*T3qxˆ| +@ , +K쎄{^uL1ccԽE#cYmS!X7BCvHd ì5a`|nz``_C}E@!GOZXmhb^G &8Hv)2ݹ%^]Ei&n82QE+ʍt2X>CNriڈ8_K/Pf|' DӖ~#g_0+GE2UTĦ`, Rc:wH+k-ݰv(}?>(RK]Hh.zLEPHY}Q2ΕI{B9xn # Q6Tow~-yοQU; OR_ Zc̛D⯒y[/t.H͍DQ`eY P:۳)r;,AI@[Xr#쭒$aMps=Ҵtޑ\/% yK x= l[$ ֊/~w7/E,l||SbuC +i>h:2@AFKt" WuC%B02iCM=I1 `6l!C{ˇff(QQ-{z45͢a"ܣG!\c6c:jh9?|5 bq + OJ5wfӄaBeѣ~,r1qU+`w{0.tC&HU߭矊)|M `Z-،f+N'aZUKQҠ@f 9'Dv4.ڣr bF籍ٷ$A-]P`[vl,,t?vC\Ǥzfywd҄%ޛa)%9~4F>=ǗD4Lv9ТB$RϩwҼ6VOKѯWbzZ+L6R7z/LTn!^9.+rE@j`e1eU D: +cVbWl T2qHVg`MW˶Bd,H -XWYh7b,j rk/~U}}E?쉄}hx4'|~}y7@ZԺحZ*څ_>,[Rߐ\G(aƌ3:O^i:V/-Qz%!7<}h;)7;bЬȋM3]ە&-%0c"2gIj&w ewU;?8nBڞJ~Y2%"SHX˄r=XoTc9$/F(>VHxΨEdɁ|sce>uG+uiˬ(r +,ˬ7FeK;< Y\3KK˦h|m~>3{V>ڞ?`/JVPr爤ރ饠}Ug* 1?Ir]aŠĻ5y1 I}\=ˍQ^ѰyHRcLuʙJ8D(Z uhօ)ȑOxx3&3+Bn@-׽HfO%w?ۦB8^EQ1+Z)Ϯ<WwivJ+|hW8"GJMĽʟg)1>VHNhGLF[`ݧ,\'Y5rjPlcΐM݉2$Iʘ,-j@J#`=ձ䏰̗!XY[pV)ې# +'tH0]$v{:Xvi1]StvQau=d|| "y%L}ת{j2-NʂWJpM`#t +iٝWMkם}El ^$JH)[kTȷH%8Y]m[Dt)V[cմ6is6lR:W}=OET$=VP<܍dkPIOkHPP C@q"8f]V멠>4^_jljC@.~@˳C?MC?Uu'e."Χ|<ҖO̪M>QX2N,PݜR=/PK)џJ:ު&AeԚz'69}NOLJ \y!h?3)[:vt" +!U7֨v:k6ś vc5!ӫx l ū#>"SMNR-x9Nk]oзc@}=Ds:vIK3.>s); Z6~!Lҵ]* +Ao]>!j@+ZpYFu;uY+ @|=(ut4Ev[jznUw[DxgX㹃=7mhv#HV{,S@lJe:#U}uᲱQp["=MO$('|C[a8HcNAAd'x쾒;v_ 'J lQ66%";ު{@V^{257l <"HI7=N*rW{E`;-hf|gcOlT l+ +TjGP\fxoӥm9c:{V GM$RUt9FkECL$y+k빬>696ȓ;/^[f=쀄$DںD1[*\3V,=DmǯcUVZM" oGq:>A7)`O%Ko XvU7x` #&̾Ey͑ 2amX0/Ozk 8iAKJ3>(A)E8)i B^H^.e0xTm~޻enWщhv@+dT a&=H/5f(9@-׿'ckM-@*Bdz-Kg/%>ٖhR!!ǢU9Ȝ5?7h\Jy)ss͞HKrb3ZUbNZ-W45q-8-#]AsHfNbΔpxkaN}+2 :"{b}"Zgs?\ bm= +]b,̄NOlstݼDƹøRsa5I2}9rU+JwB9ApTj#hs36JXv-+u5,Zyd scАq^؈?Hu|`ZHS"t$/% ,KIN^L ѽ<R&oŔ-vyO(+Ìcџ|%ȳAjӢ#FNfA7PИP0 (q#1̛J]Iƨ<Ʋ>ÅøSs,IaU0{I{et@-[r~<i'(SsQHzma_EpWI'ʐXw!$|ԡdsƖѳgbee9cιN)78{f+#YkjjOݖt%61Jsk1•U(K;I!a*L}p#{l-$H!<ڬlvT&5apL6d0VVײŊheOe|xH;Ϣ97#7kMlg 춌_X(%MAX+M>or;aun3 +iǁaOs}Qqy;m`cm +FڈjyĨyhE9icӴ=vW(_V!҂k#Ǘ{1dN?תvqL"HC)}eQ]=N4;\pU{Ywstc-b=XbqYn|˚%3Y(M\'ea76nv ߛ̊P.3w|ŸZ;-Y+zFY3dT|P" ;.+Ɏߛf{]ս§)zK9M_ˊ8/$ +ZZqo9 eWt+ Xd_M3 +%aze\rćP}>Aehe}/'+?^Bu}Bke Bi-t6BGFjB=ήYG\`egm +ULc4xM!:6)Tϖ%MSfV{BPqtzLvz-iIos,YoU_: 4/S7/)TyLI-a +#̠)D^5_>0-Y6R_!(-!ҵm8u[@&c Aje Jae'a' ^5xʈ1~ضa(^y̱Q.(,aqjVXneb"b֊dû"VQOCg߬&}lu!]%2ȑg )ϖi>>ıd/ x[OђxrXഃ| ¥bE?Iocŧ/ 1:Ъã}αA, 1q#O{7'"f4 z [|&8>袆2Q_+}Yxf)Gtp [Ur-bÈl{GӍ\&0s܈kt>`LOl^Fr +z(u"'; #RqF#Hlu/k&J𻸄h, 냎Qd4 Gq,ink, hŸd +e@kR"KƟT TZPZّ\wDSp7 +2" ,}?22.<* &Ѻ9t}Z_bŖ2{o&bA噹1͈ dfNH?:uZkeOk#3Jexd[adld꒵V,[5)=2|k>H%Q}Ҍ9-l_>"{M6,~it}Mh0quGD2.Yu'`/Îkו\D˦s֮|<_{L1@5:oJa(Hb owTy%v[^"n2@F&)?dbrDre \s#M[ಈqtN +&PwS\Dܜ?JKIm8ʂnw+1{g9ϼ^PGd&]GF5fH@zX n\5xtGY'dU yzTƓFN &Wv^˺i ZS zhӖ )m!Qڎ7_H\Bfb{eoiFoX &}^Z=o1i*^'a4'kV;!b?nvETZm OW`%WNwDl"!6% jʹ)288hI 띈7Ƞ/3K_`ȠYj j2^2Pz/ }hnky~/[LurbQ[G̞(q Bx8hDz})!U~yҸ녏^eR +>RxRxe'g/)?IJ#P(!"G hdz#Q?MVBF 8C+bx[ /0:{P!칵Z)ZWA@e|#HWWC f#^tlWKb%) JY6m۱-AQKd)$ĸ phA?\2 ^s*%eP K?]a;8@月N[٥5dokHm"/Í`5v/3;L $ 0 On~+^lgfs;IjS~gHA_= +6wb$}zlpv_̍z/kAgF(fu2 BUF[;r6{M2ϰI&@'"7 +)CZ +V.Phy4!JF +tp`4e{{Q$@lx5'ctS8̮#| 2ѧb?)t)bY7D?Xѻ%Q< *eݭ|F,톣[J5礑*.Y&XEy#s AuS[.9s{cN5$jhwUpENS TDݕ=^^mn2DD:Y 백X˺BVcNS- X]Jl_A/qiSwvѴWC}84>D'@\9l"HJ4&ײsncS<`Ou|5x]: 6n"AO,V`a@k] Q'FQ )&ǣJX;7l"J(hXR_@62*p4~H[k$gLb"ܘǾI!Ac{ncLTssY/ʶ&]fZloԦ 2ݰE;/z6 +d(ꜻZP5üux\F]eņALf}y (̬OQm}z$abY[8gXKQըpr8!~/]ʘu%2Y/Yx1B^^>}L}!u_ +)S윗rTP2ErO(pEUۮA$ ibʼ]۰~B^e +slB>=/r,cu4jsM0ZAu0H6\n!HW0K0c;]ttw_4|0鍤z]vgx r.`rHp_;UAx-a_U'3=?gh0`/lcHޯ =w p\l[^LN4hm #ZnDt*98w /*hljUYB#rnz'?P7afZJZ$?LNbٽ`4\,8Vټm LRy;EZ3O@R8"bkYk)2阙>?ܘ&) GD#ޘZ/e|}-:-@Y~" ! 8p`|4x[ VW7X2 G ;fnp_爲G](.qDB:~}3RM evUDSBB/eliTՏLlHd+1A:CcQIײ'@4kxW`.!hLE{m7@ƒN0": U?l%2)£ eS@Ͷz\B T9jGtVI]ȑHl@y1{VT=bQh3k ףxF&'ek[[Y92ykI,g-nv1v bUM\-'hA;|WճAƨR = _ob4{cD5qPH.MC84=i +L'Y + qLD@"T+,2K+n7j٫D3Ti97;܂j9`,$13G!fݺU7|e  $6&+* +2Wg9G0"mݴFI(|ֽ81&{U;9CNzpaMI%g0,zzmK>Bͧ3ZgJ@ZiBԺዼ%>.-P$K{@U"1~{Tp6y@C('@PHcT&AaHDԒd$\YCԁe+'FE7Cca6wQ_~bj~ӊ2Xakk\)f,RZ-7[$*{n?V|V*y=۶5ȊWA'~ݮbJ=.Kr_ݟ Qctc2}SE1)d}$nAԣɓamqKd'æU5DdMb[fE:pYoTS _ptۛwNa+2`y[dJJ ( m +=v=4  YoTr'KY$6 Aƽ{&r9usFHvĹan:O倶O= +*F+s$hM ~-5)Gs䁹SPe> N(3$Kpb )#W*n*$6FC.U'Da){UVZ={毘Li\HA:D1 Cb%~U5=RC# Hv+2;%lk]`׮3ۮ8aIzã} ɧB'ZA]jR}QIq욍Qɠ8_31 ƌB>X4*ClBPsDѦ,/D0[tR#7Zini/g&>6ݕ ,*pO su`ar"](Af~6I|ca݉oq`~.5Zt؍j .N2GxCX'v) C(8I=JSiHkj2(@dvf~ŦTiUɑ1$\}[Ǿ#1VƬ"kD~"=g03},nCbj E|z s;m^b4G"-wά$yf'sz&ж5^ %tR`8FjunWo+!ۼs5Fx;QScO`>0)ydN4^$6BsB6">DAO׋?Bt msWZG݆؈Wǰ_|% (/ U#~Gb'Q< fR$zr!8"1H&պ1ǵ+kD#=BǖxuȇAf2*!0<TERI:A0 !Zf1]O%8SzQr@3@K +f +cYBՇHJ2[j @ps^Zd8ȭ.x}<+gx˱ <Lz7<_W:8=֩H 9@2)@9@)(%rdE\rG~.[|`}@)YS2h6 +8>{KAnuD+b>i f˗/hȍMomL~빬ϋPʓdI](|ӚɿWεn7JQ29ȏӉ2 pN `/LVMd  ԶMP H5dNl26 TcK 6XqjXל$e Űt$ױ1iFG[̝-kz XSo M!wokA?^⁈5 ,2>xXHT x:F1+аDV "JȌiĠL8hfY}/0hBGzo1:L+> 'դPIXe)NZ&4# gQJ+`Yt?{kRD~ kj6h ]>:5i89uy,LǺ1'rj+O%w!QȋX `*-anEG$zB VbMlLˤ*VM9$cu_*>KboU-c`K5&K&?1 ] kc*k1,*@B0dx@./a/$>/u%Vfg=hU eLu:O:uﱬO\7ujio%q֡x" vSq:534UfgǦL$}r鞟CW*,2ik.`B~硘ˈ"~\Ͱ },k0`9 c~Mg/nuvi/%^Z-btK4H]?!('k\"OwR/ֱg{G|>ZWCipt!{HP +IwZ.w_/!o0]lvE!%h͜M::5du._P\4Y-9_i%c~Ԡ˦Kd^]ZVβUYUMTΛ-;j-HfWY, Y_9jWX/edZ܍/$d?J).Vr%p}m_Pn+lÖ҈ ?&ŞCRb̢؝'XX4CM*+my{IRւ*q!`EVam. PһB21􉞦!t֐t Kh=V:\5+?$|/reI{?X8U!F?޶$@>d1-)Τؖx>U+{kQd V;Π5+t&;7SɂAdٖ))4;9!'JD+x럙_ɭ.;X#s#ƩGLǾ5?cb8iN#Bb7$u+[ͲMł@t5%J]K&8לvp9[#+wuè}S+/u'ΖJ O+ [O(7^j[.7&&9 WO{ 0Md6pe-1i a +Ed1+r \<L_д$F06~/ѫÀ]ӪL*kBcob3do_mDi)?cD\ 31C0QYC2O8 +lBr'$%G'bW/yd\?mrb|ndԥ@@:LmI kt*7 lmW½smTh}?o6wHn+_2]w[[хϡh\xqRkY\{ܚdPrxP IEF|#8aV7߾x:/EG >3brZB]~$$+ԙ1G1jEΌ $֭?Fq>6}j2]X;)PpSc[)vĶyq6 F=_?U{YN?UYîR'觕c}IO$Wٙ$n!]L9j]D~Y"H}k.X؆Ok| ioF)GS^jR\4} $1C{[*k_& PV +r!Jw9Je9Y3򊧎GitQB.^U%mG h9HWp@D;>5F¸+ +:-}.cdڎ֗`hkɵױvSm|Es5wn2&YFxii^0 KkHeg?jzP 5J0*m-V20$XP@<,ՉޮΦECKV7 +Ր"P]lP1B_Gz ;'VsY^z4T +#*szg,DGo^P<~3uk{--Bq:ULOT -Kӟ#evgRBgU "4T ƭT%Uh:RFT)Q!UDT^QmKi~Rr>'. +0U$KZ"f"FV1ЮeXRhU$NתV88R]ȥS?J/I&"MZ3( fU򃿬 wA +ӣ}t#Q=E}OYЍ 6ܐ.΁R~sdr<tUnj-Echm0zH5XG}W ljHb7F96כA:(",!ɮ velA6,/2/2IMZވW#v{U4ѳӍn]Y7!O+똃='-9{!X øM Zh7hJRX ׵͕re7̿D WRmWfڲ]_<vQZs۲e-Mn0lÖW`X 7L-挨kYe0G2 z2I ul"L\زٶÖ$x/lcp N*Ec[2z2 +SfU1sDA+=ƅ=21:a{2mhd'd'}Ӌx[-hFd1|dwGۿ9+Q)`@< U- +׿S3=䦌&GoKx/6R]˪-`0f K,^bY +È?p@*eZk$b.%kRelb= gο;O X0Ú7{`ޝs>j4&A$U֝CU vArʍuQFnfHk&R(W93b)vxV|RA\V5Mt xXK,ՙVN,dZ 1'HpQ;)K 4Q2emY唽{ڔvM9 @,&Ʀ. |g8^%P=h*PepqLLS FIɆ+ +͜j I[JqDr0^wYvf_RgB 50XןjFm $OonLµ%["-[js)#G/6p+-r^ ZH >ϣq̶Ǫ^\-p6~o;ov_e'>@tjjiRq\6 3'G϶MOj~ɵױ@ +2M]dHjDz#a[ +H"O]8:$TݙuU +UiɎ lIz-Yiz8+/3l 45=Rwi E>6Ğ.\e{l82-KA J[cOv$Y m"8v ۑIZ[I9`.DzL#GN++/>@  1#\Xw7ReL.20@F-dHF s +C %ZC ]/c+d`)N)OE)9Aܚ4ɷѺ\e -U OBy`c,srao˟@ڤfW50:)muC'KJf^Y[?bV7KVקn!Bq.x:lpٱMe.k|v pd́z"G: ;pl0V[ :UkN)&҅q:n16xV_V`G>!ϭ5G5 iNbz]~q +.#S¢s|?hJ4#O!lmV-O8ވ ;d`jڔm׍S/Z>28:䌝!}6!h4bn&6H|WpR>TBˎ[/mvϰ73%׵QvW1QV*EވupfcU" {706QJeJ7˘aW:a^:ۼ^FAtaNρRk?Ƴd֣&`^92JDZV['p.Dxl 0 gXDmZY: aސ R +.N}m,h͡TŪ`6cIHW^qCd2czzKv!$kDjCov++0kF:ɡȑ aT][_I<Dz,2v24b@F\dtd7.p/vјUx,Neav룉3:'IbIKVg#?OpA.aYU1N-Hh@ZݝDL㍝(B\<xx0n]N'!Z2[^ m#k +*"%uqT6ᖂ{Rzt}5V`z&4%G +L9}ί5wrj_K4-j,@j&w %O愜Oҟ%P !xB~VGX֘@zY'_}{}8f d zO9 4ONՙLb{lK~2 +endstream endobj 411 0 obj <>stream +HtW9p({/U݁ӢJ$I<0 ?W 3Ju@@8/0^e7_@k%Bk n SM7k \6dڏzt@I vP>c`J4|plS44Qé$v>< = X~CջUo JOU0l( چIQt.:+Au;xS1;vbG{&yG>!2\z ,"ۇ!Y!3#SKT,;`x-jl]TQ KrĨ\"Dn t]<:O2 Oj>]5]pRa)a^JKo C<(yM[+;RHY+:I i{GSյ3D,%/]yL>*ޱ)\/,Esl;EvI.{Vnx7Mwmz{<>J/8kЀ=* ^ux^0{g%~rʘ>`x +~,IX Ok(,`.Cy )xW[VO :Zlqe<D`^AbA.sYchh?d>resJ+:ʝQ$y.}/}<@zRI-:Ygcmإ} VrUfb&Dꣃѯ%+Kd\8j;!,X]:74ڙ@BS.#@Ȃ΁Ĝֆ בJ1aۭU@:d `%A;P]^߀fN鸎_ƞi~eK|e%* +V S,ٌ]٨AǢ%0+Fu3GVӪEBAV<:MKylF g߻aӛ=?Rr=l^H%I2@\0Q*GXP̭/ ZFp /oq]񻜒|1^mß&]PՎ@9F`ƀ< LJNJ8fpNu'rf2ʈQ܁qr`'AA.A2 Y| . כD*$5"Sdxe4YQOǮ^=ŭMa84þ:#DB玧66*$fB@id5,(ػ)'h,Qg1%cr#WJsѺK֩_tB"m!>6ho@~fu*5WEPV6n,]-o4uGp_O[ul K=yd#"cE0!kg'\ ېv Y!C#ROd <נl`SVȢ4;0{$K1+ը]:՞.)ݯYd]aǜIQ7B~/JUyjmmTe7 c(J:lOGYZ3ӛMK"~)b'pJ#G4-+c.qw[R=$}V;D! }ڍ=΄6 TR?a Y݅\ +s.7h>zHA6l=ZSEB#7<xM2RXê#\%AwPlp>sMNk:^W{bW_vTq ucGԷ8ݲj:?U^ +OQǙ.2M)<ޛ,()wtg`6GLD9vl<˔*qw6!b K1>Ry̱uYZ6?no켩'!i9#76p1eNsȈ9cϵJQ; ʙmUbqcd +`97dՐ(͚0~F 7D3AnAǰiMt͑hr۩xL`cul$ K i"2}Y FR^|{G7}վ7'jH[Dt+o oQ)IYfڠ S{S΋7Xi |- ':*3zP=KpSnE}TA|w:.' Y >0 r C_O\+!Eñf4ZDe@C[N1=H`puȫ={9/ +LN ¨2m x2lz`# h>{9a "Fgt]Ą} ^=³K/3;o EnӐ>Qev7q6pND1kfgy}}ˍzGNeQkcUq}wrm cm\"o(ia-m!k|e8ۄ*NUO_Gp|U%XI$Xuԫ$A> hpARWIf]NGPsU uE#q鐙dBg1NNopqR MYz5L ?k)| NghΞtc~ZoHBEXc=awA@"ǔjnٰ[1ՆmX{ *di̟gN4RE;gmR !V~':lf[_ouӬogQ`1Djkkh<#Ǭ7kk+#P-t{ Y=;NS?H9FQ cB&DLSCbFĉI[s!:7hƑp(QmE ]6R W֤6kx +1+<хM?s@Wb ǒ1}+_mE1EM'9+MLUPX )WQ%R)~l#ʙ;22spP%AT,+Ϝ3;{CdŧҡCt7ʳpNukPq{oGf6R>@qww UgDrJmc㜜cIJm(7NZZLIelM'hu,{oe{l^w$x/5|nzYLzp|>G<ֆ0p͙Dp+ݳ"OT3bozw# Dd 4wdʑ=4kPAk!=4OL\zCg`֘ 1^#Tyu/3=-b~F% Qߎؕ"j%U*fFk|SΆ[6ld9"N?o\kG_x_ _Ǖ iUmq?>]fVjt,;|j4xٺvD" E{l,vDV^^R46rQX^ 6Qߵ34j$y7];̙u7(iLd^!z2d;fHK=(fݰ>؊\ji=C:Z|ץ21w~0#!Gh?"65X֦0*+RbG\F .*عkc z;- ;=kU ܩtcԬO3>3)sF֝a f3"%QcYk%4"~iN ˛10" F@|κ=$,1j] J-O,j{t#y* \BNi Q D0Cw52$Hj-bv<$::t!h +l%PgF` ۢYâ1[&wOw$XTTt=۹Eo{@eVKZ>ݰLh!c4 !O HHT (m7SJ[rPZk 9$xe٦T;ݨ Y!UeTʊ}ODX2O~Z)@d6W:U}eXZF7#FbP$Am7thӉ ixxWk4'F8 H0t,S8|U3-wx7RVDž}w"!4_Ӯv:Sc#xX%hRrrc)[u_Hn;##ieqvM +%B\-f_tq֮9H"WzќomE>RzX%bS@npXxZ dHݚ[=aA2ft,6݀ۼ py6 +ٍkfE)@7? P,=3 /t6}Zd.᪓3LzlT<9z#wnrߞ,ġDU}Lר"B"_xfRZGY#V5}q>(1p> Rg744wa4 U %6=ʌ< {aZ:5f,[KqÑiu8tk_/$02K9=1zѕE'HpK;3}ptdmYZh:SI>L4#FVkQ+ZSQ4dT!>>~A?*܈d f/72<qEh:)E6:N'ե +MyF[V6.kuq`#[Q\&Є|A}%vn|E|[~H|"wF"]lu`vO&˺O2v̑y=ma̫/y# $qEb"ćW$β yxoe%xxort%P{eD;m5Yh}$g^wKч{!_`?r߁coQ( gďX7_ď"Zf7n<75䚲B]Ŀ2M= +F#2ikzXݚ:b% ǯe]u5tֺ,<#w6ښ}mX#l^c8k`#s`M{}#xI1"C$:hz =l^(QrW&s`W'3kq9XO+kV50~ljko 1Q!Ƙ/6_W),D0:?<ԾޫJTK2_x\<>|rS~KXЌ ꡾F/kEy!"o}u tRCYdK,(K~jŠmGL#?QRd㙲Lr#Ab_E`{>4v?iPRt9?-\sm[CܵdUss͉;ե/d  +%"Cj~9 +P̺3;T-ufU$5,9k|+U ki3&I_Ԩ"O';ZGj5B՗"c_.Ɨ^\_S9R}fW}MZq我3͚"<ow04Ul|Q{*B~#HX&@VۈBqRϑ:6;ul6Z+#H[w[9bN>=):X]x;/NWQ")}7}5%K8_gٶ.d:ޑmҤ ?y⽙!m-&^qU |Ox@}|IhddԝPo5gv4 y8[eRD)M}.J%~Ys7 +!,Wp SD *$ p`H ̆Qg6T7yUy{SS ZXe9Háh y=K*錆jů#GCa! mn +sFCLkuOU~fh^ThLb24D7PS* <1ș aTqLA) ڊЯmӦJ%ʡ#}S,N,(K[n + +_`LLPkgTGw갫-z}mAQVI K:8)bzfKW:8-&-~.c w;ǎ]+F{R&!OC }ƜA=mTӃU1.;z +T9g}W hJa5J:'Xj#Ybl I= fG=/nhi|~ N4SBUwQKHp -8n"Xs\SkԀ?;ܧIe#^*=3ۻ6l3g:)CFU=e>(<|m0x5/nYbNHɍYϲVi{*Q bP[`E\W))|RgA[ Ǣ:r@6Yz*OonN Q|V"o$[zҳ[f'p%@䋾k ;kܶF-md}TE(0Swݚy%yg^(T/^6ԻG)~fv6Ƕ8O$|#܀ذ#ԧ`1 ٥EɗM {Ǵ֏G׮&rtm&Wߺ-K KUsU#܀4ppRp@EҷKIAir65l.ñuB)C8׷rӎO DGkr-Ny*P4rwz[gFx fzk<[!|S ҋJA.CҲSRlμUoʬ?kv -<4>{֫HmvQoIkYGI)vI8?mH8Q@p)<3{סҞ| !"l͇eJ=ve'"ՉsM1h VhQuVzX5~$_LdmRĘl~>,k["+B.7nagbRF" no &8jyAB ^KP$i``#Z&䵱~M,0][]UC +usF)$H'fHbCGbw;tJb#De>I^O H vd-ʂX3 7U2xP{=MG9^ȟLm\OЅr˭#5VQ+ӱF{cx +rT_Z jm&%8ENJ%6K7rf25fzO~ſ3'8`6ƶER\m1H9# jp9AG@v.JF&.LfFgEr< u<^{c`=@(EB8ʉ]Z4G'22O]Է Kj\v^)XC:Ҍ .!n-;7p4eP v"bbxAqqb\k10ќm /:?^]nkqZMѲi7?Ze- ug&s5_2Q6=hK |++RhhnD8; <ngs;Xr|*Ҡ%Rd: +~ +id.1=*㽌 +DJƯ4ۊp4$VA;/(DITh5~}M~I֠ zѰ>j6WeLQV:;l>d3E,p \ÒZj +)`<>LHPTYolg_hYZ[ةEnrvsuCf+-y"nchJC9-#O'u -}VmՒnq&! iZ/9˾>ZQUd)= t&8@#r'%s{-Ҿ, _(Lsg([",R7zi`QH~6,?eݒ9p͍ >$>Y -G,lFuXFi"k9^U9 iJsHFO%2I@F{cOϏ"Vs ^KaSvg_SzdYpk?`'Elh޹yx6 +o@zc:gwx*اx\U0 ˆ~ >w}>s߅Dɦֻ74=%9 UU ?(@DP9N,"#:6W:t~'&4##,0YY^HeAN^g1U@x8 +FyV#jXsWD9qr}<?ޞaTE=ȣٙF1ϜY+Qd.Gɬb.4i\ʴBg!־ ĩ _ڟ'N2a'{x@t< DdhՖ3sTЉ7"'Z`OIAygu-}qB`$+=k5$e<4b7m|aXtvIBT=k!QpTvA{M|,ͪ(lH 6W4sJLbi>akXH 922C!M,C@:_?UJG6xC0Ѥ/)Re;0`u5Y,Mc^J=ZgCp??.[>c:V5ILe/O'&dM`x]Wrlk&~PA,(Gmܹڤ<J3{?*jd[rL iGs|GdJ4 pKw;`!j94B־] 4(r6 89\w| +';=#VJFHZ9#iiD8|/*S$ + >/Z)B6i2=i0X_K5<=b_!8o" ˆF8enj8HE)&hjU'N }ƾ&E>v#k߯,GFrb0/tknUPym:3'i! + Vkc#Nsk@(Dޞ  Pյ(Vɉ: wDkkHHd Êyq %렗sIJHfDj,d Ծ9KlT@DHi>1mޘ#XT}[%ґ`,ܮ32=urF'X7tĦ5?MF sī"?[ +FbB2Z{xК$" f352m.rO.IǍm=`e8JzOZL>)R$*nSe14$˩1Iz5|M*L5EyHsfYlhU5uxAoo6vMA??7O/D׽q4FД G K|F hWe< +)P3r11MdS{/(@dxs_ʦSJ*mDo%q>߶>x|&7 j|Fv(ajy!CLKg~]mKI1ڒzJIP×dMX"RymLb D991vPSBL>9ҳO-$m C?@̂I1Rj]_JS~?uk@쌴̮cJIt2lO@z6h5R:Fbf[SL%^ؘ`;nǥvs~'+BW>]Ew5cΌʟ]@>LbZ[t9ўȟML + #Ns)o}sdS-bcd\д|!ØeG-`Xx]5u)%ָQmm!*۝7)r;Gdѱ©e5$ʸ6R;ME>{l:BP- +efFM{ՆZ}G[R(SqGvZ, | Rў=HnveM%n~ga`<z仰 U y] J}}溫ja6OiRl3$9!-Ǐ,|l_ 䏂jGsHg* RٸgH1Rg!j6zo!,ŋ:(cO aM?^ H Kmey|V>}'h=u(3fӮ+6𺤕Qtյ&Bxg-]Ԭc))I=þk*wm!SjY(WAd (\6zW<Ϧ|>8Kkh KwcYrz=nnߞ$Sj$XZ$AOXsC,U B#$ن!,ƅG $rgm9Յ;Kκ J885/Gk"eYI<Pz +F+UbBVnH] Xuԍj;U.'8|N ֔"kGhS7}o )ݲ4k5m"J$S>ft\Q쭨X f1Ҫq.Y@fIψjܨNt^ƞ O*%LtxE\0d‘qbC{3UzDt>k"s. |>J粖gR i&+ȱe 懬Jdմi:S1pA +;2+~Ê|D&O-LL5y#0$>kJ]1~J[a.]VkgVeL2_4+߳ϐ}},'/vbQi3Ay8M#}-Q okk,eIDyX,DdL{-Q!]D6.KAhm,P,@^K<@Zj]kAY%↵|-)z{TZ"JD|-3JZ"~DL'V/K38-ANK<@Z">D|c8C%4$%b*|,Q܇%0\^k(DZ%*ZJD<Я%i=$-Dø_K4<-r;,ђ}%%kZ"@qV/Kt%E.K=>^rDT{rY"u\X⁼xie{k8ePDu[ƶDouYKx"鉶!^ry"կ''zy⁼xD^h)'#D'.}<>]uw߽\(;ziV/S_?|`x)]fOyv$׉t`DK}ZLQ: 6c_0= Wu &>T2R|K\}鬈dEpOV+:1 +1$Xj G,̽|i *Glg/LNŨM.ec Tsq$<qUxҁ#;;ip (Θeo|)QJ0]L[Opxs®͛7gM;,Ɉe +5D0՜`;!&HcA"s;`b,')uElAYzRy=Tx*x -jf82!kBRkG3Ү]!`.;"&{#E Zf0 +Đ}Ua~ MWi,UYVY䇴aV eԹGq#HOjI636bR'E> ' +١ܷ-(T)=L1E酪j+{ź3޴G=“/=A{ۏS޶%G̽KaDn}lzjsDgV'XN d|ۡ߸A  e +99w9@(v]e +_Yu+n}~_Ho{MJy.)RV,+|ybj]ل~ːZ T_ܘY|jP?lb?dԌ.ϖ#4|8I uG Ď:2bSg1t^oE)Se;q`Z#oUcx^m6 2I;3?\e` ύ&Ä#EJ +q. 41mM8ՕN%v+QQjtbzl^6c/#֜9Gu}1!?hbJΛJg*lq]ٖ*p+QA] D׼=s^US!]␌y7CZg7D|q8٤q8>Dx[Ve*}i)y>1)9&͋fBfoD_duSi+0)_XL\ޫ(G2!YxJt.2%zv)p[i_z##vX#6pwqi:QP S~t*bY`Mōj  Da@㳓t"F5\|xfxY X4:tIK6J,;饨->h;gX=rI ?8V* )aCgY}&8j'u1 }%l%hՏr^^=VOpTA'r}⑜@_@#0:k{3 0ωmm~p{-;8٧5k{Ɨm d}ϼdH8)73|,I=Iw/0 .:_1)6&]\){E+#^_{Sdd-êp/Xe]#׈pc-emՀ4٧ Ԕ s9i?e`7,1X46P?Ǭ$颷Od`ʁRs`r43(H^zrʟe4@k;[mio6X꡾_>Jx\yd(6puI!1vivi] VqXQ*/ːl@3%48'Y $S Ɖv_7M'Ss>H4l|@o Taț I+g*-.m͒h]GЗBZ"3YfI[NΖht7XB!_4գ N?F;³#M#PNl'a)]00[Ї׼ #,K6&ֱCnNzgAU¯[;$,vdmK;:2yiG?ڼ~KGrt% =j|_7nQ2 ɞV 'D~>!Oxr|?5VqZ(GIIvIG?.U K9"K*+rHaף-rD?r<ȭH刣[9gy#/VH?lYeP)%0t ԍ֍ R7Y~F"[7$$F0t#޺]5|޺9Η7% +a-Z5}[6z8&_‘f] p(}~^AN+wz pOsrtMNOǩewyT6e +p +L6EF/t7d籮 ePb B9Kdq+X1T &gSoaeR\:NHN>8)[U$qg +P2-yڊ+ҍG|*܅߷ +@bd7e=[R-IǹN M4kp_ +\XfVWNO2Bdf|!C!ГpH!RӌW}Ⱄ +TYEKS+ ++*ARZV&N&KwyscʋYw`]H9Ű(Ml!} 1Hj @#Q )낫o'kW6:BfE( 9uCȌmP @9%ԩtQG{:zgWf eqG u>Ӄ%f<875pr! +_ Loz! +zB2WsO@#F(0 `"'MJɼIONq;3=0hKqhLWn4tB|w)']Jse{=Eг='m97耺=. l\TmQiTl' fTIml m(e +n'xsT`>;xtZDSpkSx]eN%Q?*v[L$ov$5 8:adVƠ]SOby[HC[ݐ^(.>T9/Tzcd%S\ѢW}Z5!JCWv9ѓÏ1 Ղ!d +^H #6*p/,*`o J!~EV@H>E Kͺi0p,`)A^K*-(ce?myVx1.zҞj$0i,jSs˻qodT&u;#C'w^UEI25]eƐ}^o6-ƒ~j(6+|G->&6 {q=8D5YH\P,KmTRfH+kD̬͢IoHU&e|8 kίW5Yߏ{E|^P!>Ŧ#[x`)Mͅ>SG՗#k1D͝ceC\/|].ٸuf(ߎ= hr]6@pb ]N9HWYB2(RβhʬKEt}̿\t#>[_LrcjA~/]mE]<_=e2|p)' mq`Z(>sIPH;,q]:3>&!P hP[&^yg {PcE1 Q}e:͝fa{L MŸE:FhOeKS74maU[4x=b\(gMj4bpD2ͼl`KHZT;36ƴR[J=0=X9WHjJQ7ۣ6I9h]#%9Rg ~5m=ԝ 6,PʿZȋsp5E|Y<(O0gV4c";Rɽvt)h` +6La`c͊/+c{NP̽C߳Cc뾏Yeəm xlH^lDeLT ROia|I62lž_ v!fni”>zE0@w`I;ֿ{RVƻMC4:*M|\sͿ.oܪG<_/?&.IR#Zv0h>BmJaQ]3;g;2 qZҐaR\: `X޾p9 x "?Gnq %ud].T! 9 Pӥ m Y=y){Yh,ceRBW6YBW.(# p,ԑ풅㮰C7"АsBTt9KD!vNGgiigP's,# 0^# C]SObZ$*3 딦?(# +V +QmL( .Xۊu| g@6Dz+ƈTdžԮ!jnOI 묏B:&*q8ӽF"yɄ" @X_,J3/V jg#c UZVDoC'+Af|3/Ky-%hKoe|ԜSw)"@bVc9w!-"-/Rl2Mr}!ɻ&NȺAuv rp' +endstream endobj 412 0 obj <>stream +HtW; +o}/A j#;gft| !hhfk9EK,{ Ƌ S+bJ@0Xڀfy^}4x0}+pw>> ~TQ5lG%T^aDxo)d4TP坴2sƅH7okpQ? 9g|JP1Dw,tl|bDl0'(|8/kѐBhR"LzU\FT*,5AMږ7d: + CDA*~ +~B~ 1Ի=#đi`x<"퐇 MES5 +w)E v߳^("3 1Wa/0vU# ,E dI*QN9岑(RtA ,y$ێXrP.Q`-ת[jٮv?4(߫+4*JhKpEA5h8c:F*b ~ChTka15]c4?uԽKl+<$3D R%UɟVl=ꮱjw%.Gʛv%2:(ITbygы7ԺWpqwJ:J%O"E LIDs &[߈TB+ၵХ8?O%Sc>ENƇϵ1f|F܇ + TDn|K) +9&蒮lT~㩂w*S\x,~M1'_x 8@Ӗ@:?Þr + ]~#1":^+CH C}( .Z) +xzѩ_'CjV  +/w8 +&;Dބw]bՏ`bTҭ/b?bˉ Y2ʮ@G {AZ@j&eUIY D#I7#3 aA/rOjb 4XGVo' 'Ú磎·zG)^`~ w*a#.pk_+Һ-77+r냴gҎQGW,d<[;e.6$AwdجXg?ѳ>X#è0#ieٲU'tIkLN@crlVX_%OlI$يu0C"L$%%9w} PfLc#wKt7gr[Q!J1ϗ?bIdرunTHy\?igٷECi@>.^>êCi.55[h*2? D{c35r@q%_oS3E YҎcY͵ֱ+"16.$RmЛRPKG͗@| u>d咁DM8zק,yt0IdᏀ7€J6܎ CVEZ gsҺW̷N"\`t]% _g{ףe'b^^"q덽iLfp?4;;|ðnNDZڄ`$휫?FnҶY1\^y b̧7G݌7k "Eu82k!)E56E8죈%zaiBنEӹVE/ğ:onZEl'u9lR|ԕb)6&\͆],04*|y!u0D(&2А_,$"9)2q'O~9LIm3o);X$h e5 k n􊛞:W|t= +>zԏ;%[G&3E|AUbo"\Z{"R{hos13 +װuT:ȪH0s1` +a\VED*l6\׽ݭ8[踾R8 ( .Br/sa)(~&A}=eoJyW}_4ډUsw;Ԙ9<dF0&%2goթb)nW ]-_:# q@C7޺O0e+j`mC(FmyF.Qs碅cʣUu\`F+!݃ +DB;`籎`hRJJvC#qH ]gEOd/Kִs`ܕf(DDa lIy{OPSCG)5d(ˇ;4Nsj;SboQ:od‹le±3"LUp6akd /W|:n"#YF-g+VtUFI*E\Z̈́+]H ;OGe +DPOoC 6v^c=V$Ķ]}pƳ*h3"mmD4WM$+ اM d4 .\>\zJ2`x^@P82v @ nW{%pm3f=m} sy6VΞ0jp({O9@TNad;ے$:*{z@ӊ9ӏ,t,^Bg;YHtc+\:9LW30T^pdDPt(6mst4Ha:)97ܜٍP +7)Z }[xIf!k\t50i  e2Quz\dS%'QՑ0XHL/mk$Yإ+v;NxG@_y?Z. en]-VADYzg2R]4kK_lTgy dF|Co^tgDαhxCR1Op/V"\ M¥69L2|qwPH|D{ k\؈fRgO:d"dNx^}!S73ߋЅe*Uتö+_~EM>ےFؖ,8fm!nyvm"=a Ϡ+Y۩њ `Ѡ ;-6[/Û:)bV-^6rwf 3SU%S?əRb.ʊN,ȍ ŕW#2 +UI9枴d^;5*Ow']H)pnE/"Hn,y͘ NMchI-![%+zWM8|Uvب4_\26[ODqUv\PZxԘѧd?qu E_fWilA +Kz{%MdYҁ/5 +ud ҃lmyv9z͒Y|/o-[%iyqGpo}:zWc w)(&aD}t!I ћ"qoibtKR+R3{쫳ŐĐc,69DdT;Hjcqs78kHh`Ut!ܘ=ZB*oFayf~"m2&AֱHZK= mm׉ic/*Sԫ,ܜ /Moh dI<6r J8qdӶU>Bb +`=ȓyݬqk KGm=`_4DMvvHqiO|u@}bumLUu cɁ-!)eed,=7KՋ(pN{قUl_tP;z23 ͑Ym9WJ_&F(E}NDzڼޙw3%mes;m00DUZ\ܞN`E *{x_R4(% +J{_-A. GUo_9Ջ~w-x}xDZhOP/更x_}v7+svjX]1~_Q@75Z ϋLiIUi!W$u_EEkAQZH{_AV'6,eƛ|_AsZѬfq0 Y.Um<+!I&g?z>?1k  `LT78h'Ύ!`jѧ4P6ۼmdG4{Yr2Kr4 )[nn-V+L5,q7 6G)wd@ FeZ>) ԩI[Z ?vw> hl{1NW3_AW3MhLn9Hc͐"/RiHoI8QUWm5xu-{Qa T<!FaGj!<]#犍ctXW ^D^= vߛ{n`Gn&9AeȑŤꂹ /7]DC?'B|(ݘD{>r?o:@N~Oޘ0ϝGfQCS}>HםUs@Rˤ$*U_6B*{i8K/NJq㜅TlNF^9R/^a-s0105&,˧Q5 '#ؼII34WA>RF!QO6-R'4D^*Wϲ0D~֚PS%M'II9o$$eԄFo'UcJ2ʍ 4;]2)fIv*NmcИ a$Lק||_>RtZ 2.I3A qlќ iZnQUz8Cܙ"Rݣ- "L.˭ȍdopXQ1qa/ũDOы5V'FjBZWOhqP5mZ.D$>vpg%*HZrC]]L8Kp!fme ~(8y`6AeS{B~>h茖\l3?+}ikNVŊbQ +kIuV + L{oۖLQ%\S`lYB,pU<0`;IQxʼn`%!*BWحh +ň؛82nM M8t.5p`~pZn̊0P5y_HW u!3y]@,lyIA8)\FԜf#_ZjZpuz.]-ڌ6A6pjRN!)01q5Y(ds^;J,m 瞥Q̱bF)n@ ]3 ؈ĸpAScejT:=ւ% g7dK:8w2\O/&,U6VnNCL-rW+=Yn )iȤF4A!,=+W֫t2kT^֪<1Z.ʳ鍁q~GkM|93ԍQwZܻ\" \[~V8rd1/fSBBVJ=61$32z8E )o2"I*1vr#iMp؍MeM`'Ri_"4C+- =XL +GrZ|{O|Y L`U ;Aqw儴m=4~LT2/c8mBI +ny_j_K +/ܬK*WA=9Ċ/)@Sٝ:Х%O!< OA>1 RF[R1FOryhkQ_[ dc.oGfRT:jr1˼,DjW$a2";q ڭ1 ^c"1Rba Q\x7&s-h @&JSL8uTUS#o"vpLAlʍ+j;\#=x a X<%Es +p@k~Y[Y &o1iV(Q@gv!;އ侧|(XX\>*U\VƙNw +}`c+ײ [Gܮ#`ыx}9Hn%W1<~ܖH|kĤ͓Pw:]XjfD`L.+1wf5Uᇀ m!NGÀ~]+&Ü2A 8Rn%묍cnh:5S-,NOy!'h泉T!՚ // ͮ$RC|Br- +`C}]ˉ%=& T1"nCchƣF|%dk>xgַz<>%yl4E>|pbҶd=;;_1?˜6Zsq5׾﬒0+Wz6\i45&{*W] g&cpqu'kh3eST/ӲL +0ms.[ԕM,dS+H{4a 3fnWDZcGNܽn)jD~='̾@y eJVmu58@$΁6jU##7gg}NpQjJ$B WoOd}}JHqTU"S~>ؕ4*1ymQINQo]{QlO>>%Kx9,y-rDd4>D-%ェxr'OH$S6GM.&vF9cPJ;E'PӦ?|3qKck^GiGi'Bm;3^Wog70+"[Na"r*n4Ρae$Vz?O`jWt"5߈v5mHuzqt5B<^0kE%/3`FS #p O 68 kZ~Bߗy|ZA< Ȭ2OA⦆R7/| 9 Bg (d511e "Oicb+w.",lL~N"jlS9Yrof5|sZs<cƗ$`uHėӯ䈓rwrEA>>(TCkl,GW,ͯ2MgB<_́ Vݯ֌MEriUT5-@)0E5˘u]FSceқG=<3PЕ8I {2;O]r Bk +VYoAZb$ _7gc/>rū2<]hP`AOĿu&2 4CAYM֊b{YwƋ~c׽-!^XU(1 iйOd[Q 񨦘vs]Kd\5i7 \޼0v(B'/LxUyꭞ}o&"I^Jy6M953H#jl_iJu㌇gFEH"^Dܤŷy-df2 S&`bP {} ionlH3.(evb>*fYR=d<@sH6F9\LWf8c]BT}[vdM^ FiL RosɘRW} bɋ6h)e/>O2NQV{i0R:-v@{5Ht*,}N[z@7}Xg"6ɸilRVl:Q[d/v3vaVlXduaTuitXj5"_z6R:qDzB4+Y^S A)2wdHXOru64AX#JJmxrle!4ӎ2ےyCOXow7BO_lzx&@8ϙ@g6;(Pl)Z 6Ұ ހzJtWK]9Qƌ3]dZy#^HIMcStS-|H!"/)Izvӥ`$84'ZSvqߘd6)MٞfR!=Sꢚ@U\mW­~aL@\V}Y&~&%;K+u,ļhE䯩Y9{EmUCI[CAx[/!s 9*Qu#H;܈־#fk@b90h5AW| (I +pEr,ɮ̥@\ +Ƶ?Sf(Tmq.~wk;O9K+/ O+a`a/hmqvN@XnWwcU7 +߼U- }p[;c4e}NU4'uT"C@ K1f@oz"NgugH_ cvEr:x RUww5~,$FGMC&H>M鲫10HM/Zy|mzK}O4g_e'{(;T]ٽ^ܶާ&֗#(ho+@݇ #ge؈R٣{r(㍈[T/!?]7gů4vRt=^9u`.u 0vP㍋B|ΊR}KufD˵m S\BWw| 3`ⰔKSEBBW<~k"{xn-BA΄>B,$z5K ߃h6Ȼ^(9x^ʽ+ 2"i<8ydWWVR7zJ`<9rYgnj%RNMlh\%MpPt0ha&tGk8ϻJ6Ǒ] +;@dYQSֆq;<=7<\o[oj$1ca;bSd@;!< t]F@G>V,w vnb)b ++hiE șbp'[fSW>Eީ";cR|y2W0^ywZDBB? R^P̥Pb|O-Z1F7 sSح[}it(7uf e2xp `υSCi +@ؚ@T!S9 Ry]9}LGCBN.}d> 9O-$=%m%)yqCm sjV8R95O-hL uEg%VRRW(oe={61Srd[.|e4V0`l<P1&˒FwQi .Pzǰ;p`f};Q!Plk7y} n@yUUBj$\:˄6rH8w!u0Vɨs0b9 ^01C[+453˭FF)Nx^DnLj] lMi v sq~ho$OL#*U>je3~躖9z\쌋4\ꢭB-S۸ Eŝr1j wI ы/bFZ8cV#Iy}T6`jDt\` Gñ=pyg%CTܿ*Y|ljx,'%O +ڛӅsAS𙗰mN.6_Ĵ+wd471L>Ba(DV"r"B5` +7k<7T` \'"AI];z[o|+5{r\Q*o>=R3|b]sʊ5 +2]ENq;*Z ۦo^:&k1P\RLߦ,Gr. d(=jxRl^!B;.0) R_M *[a<=+?YK[0KC6Lql+i8,OX=)P#e)=[G$%XyQЅ=r#eW#ooJ¼MڑaU|b Z=#%Aoʉ5nǪa+@c:X*oH-vMm־Lf\S3 g5 ]Y^4n^M ^m򆥢S5Tfp}֭OP"<=u=$hc'L=Bv{27?/d_M\ V6cT31)mF0(q(伊qQIugY|xE5QY/%+DmugcwoYrh@ +d^{GU` +!UO>nٗԳ,󅔦.~;r:*{\lVƊF24U5Rd1UB{BSR +nK:dXCT){V؄h@".F<"SǍ+W+mfvd7J +d2#/˫aXRMLg񴺢CSbGx<0Vϑ(u ˼5d m1FmVAKIX&cdwk o0[aڲم֚X= HDzUqNs{\Ė +F<$u^:Id_RLeUIMa[}YQoYFT:li`[Kđ$<7޽tMBA  6;ZcJ1ܪ⑳Rx1/hۏz`z,YQUؙ%Ah 5fr{y'Ql 0(GMf/Зsh-VB'b+K&cڵj'w3Qײ2ee[JDP_N3;2ڒϮnXf5 .F3_3rmn2ԝ53f#sx|6; \bXI]2.-X.iJYH{0ar_@6GW2|4WZtB}0E0r);,mL}{IBPiW=cJ_|ntm hK\E0(m>U 9y5pYWL{ $zYnq4U>Cf#жU~- +?|V/9Z +h6 4Q5r멩oܳ;VpXctH1} LTU_$o@ꬺ 8`9Qpʽr.><[ڥĚ#;f;#ChԼ~]Y;ycRz@SJdqF/jg pjJb.@ޫ̞{M 驫Fu{HsϬ\tdwhM1 @nyN5rMҪ07CQsIȧJh}U}YJzB8ܺrA{b9:%"$1W{og (oߠ5~^WFl;\VӋ٦$Evا6EvE)MvMm,!Q y~D/t_P .~.f8iq3>znƣN7SMyV.ʣ*6w[ZuQ,;f+Q26.߉vd^Vz/˚m-oY]Pݳi#ix=Ť{>|ntm xygg5$xV:=+'#MjHWju4г2cQ* X>ZȎ$o_ $Xej_Z卸Fz +9v*Ok?9f.B5,c|[IinCˍENEʚz^o<fO&@ T@TOVؒ( ^uҶ*V]J_] aF]i,YoEd*A7ˡ}1lc;U; GBRhy(` fwOQl/3TنJ̸,mܼ?אmLV.dvKeRq?eFC@ Bl}:FE]ʀHk\*2`mdJҪѨ.ټ}ʘmY6h&*i(Ӟa%aJydpZ>1VÐ0N.xx1ůPX"Ƹm`B`%Z}W&}J362}Z2ӾNϸCY(-PA迈Ho5Z)Tke3qyן~Yb/w= 3Hrєs]0P)R8jרҥҙD#j-2M*"S.>)68%2x-Q}8/aHDLjrT"< R{ӽSzF|4ԨNz~E1j'<3G3qi1*k$6dQ4\v8g%yNR10ZU0h3ܸNx5&2 cܻLSq8C!8syłUct1@zx5up&ѝ1`(]-WC<q3EsOm"n'1 *B.nM8)?4j_UH*C;ih"x/] ڧC#!:UI!CTapW8DJJmvzhZ\#MQ +h"rєRZdw7%!!Ƨnc7~q-,mX8{DpaGWJ/1=U\/-!!Z"^oDWzKDW +\PsKtqj%; DI)M$zgL%LixkP&LI=.hL5mGDtQ'w뢜}=vՔW&Hsə,7gdі+t)uDxʓhȭZ[Bh?K+Zs4[~g|M{|m&Be$owA`SJ>F-@6CLYt_6SJ<{d4+x"}Vlԟ!l;Q!h])هv9}"u`%zGF7m] mM+R~b5c6*vHa2fQ+ &É*VpOl 7E{ol]C|4jj{O;O)|\?5]^i8qdBB.QB¾JG=/ *6B..s"YD$[ʸ-9 ؊M<^\],USbo7)Hd{rm0q2}s!ߔZ(a3UJCB[3 +s|ZR1n*GdWq 1.0"?}0_L˙$wdD\4k#^zJ2lƿd|TCvKD"|z T"xz/l F5eC .ڵCY82$[5׆|A'C t@-a|z4v( 9E+M3ƏJeBjޫ Joc n6'HXPP(: VH]HeE|Q]DtWau2׿iٹ2J Sx׺mE +endstream endobj 413 0 obj <>stream +Ht= +sW}0ltUýO3v*gZ/?4^>LI`MBhPǯ~Dˡde&! ֔!rc4ð2DE.83Y2fچ$dPe ?>ۮh +/po)|1b`XGyeKd+ !M~!аc{']DTA;&3U!*1Y;Ix|hC>q.cu!S}}a z@Z8) i^ߡHp Df:EOvZ Nt-f&ʰ% Rd{\@nOA:B+"v{9ːhUJ,J%w[MX I  22U_vF]٩u|q *yl]6ZF*U s1 7UZx.ܝV]ܖ/vD\ˉ*Cyr(EyVɆCܴYp7@Oӷb)+Gr2 ;vhe#bI_KȹL"2Y 6ԸVKW{1J2>6l7%\mَF}BJ䎷̛[Frݜ,tsb>[z6[݉It!ݾhk7q;&}tS("+'ܐh+!VV:Ȯ TJN937$Diī-~3wW6$}f͖pRpw-6/<2$ qRAՖm)Lob[dveAk G<}~LP+Ό']WDFY[ "% iO^X!>U*cBZ )3xr= mÆ_649je,ن3J(<4R7j-Hkc=T``)몝( +z۩ifSyHCUŠlK2k,\mICB xtI+6^#tqOECw(=w~vsJЗk}<4FpïơfBiNrByiyI%]iNH8pG~FLJ݃ǠIIaOjqK:jMj}Njq&5^[ Zh=q59вQK[MkNF6۳&ħ>Þ85D͏{Z;5pOkUܵ#{^,8缫Eܩ59$H?CAljظ1|ï~P;MkHZmk^Ʈ9eQΌu#=u ֥k`,'oȿ\I $$V_sָ`9$F i.AL=ALbegmCRy+u"$$2b/S@N +Nሦ2#8"Ãf Rkχ~ɵ?5D-"o~зJ ev0Vd}|-R7H Gr$@/A"NR +]i{ ۣfZ[0:HB/^ЇZL+fEUs|1:i5Ԡ&uTȣ:i,+DH讐ԅIxz'w w ,lpIeHhHUF\9ͨ;?k7Fnv(mY^⻣P8j!GFgk){ quW\ s4$f\"P3թhG(ml=_z=8x z~*]5K.=$#7Q"BC)gK$>N,f>iDǺAsz_uR5H] f 8N>H&A +-IH.Y)5f6[H,S r|q?w_籆Ck&Q-uϲأM`Ԇ|]_4o҇nA*c(;14.GPFS*p1%Ka"CmS'g aw)p$qQU6Lz-hTڊijIWa[ +ɸwv?u[ǯ2# : ?(21GOX\9*1쳻,r8:KD)2 VI:>M9>HNz/4ƚq}rZAdV)Ck#Iޔ ȇ-qnyoj"?J-R.Ǭ8 +9F`+C'<'q#}Xƪ"@ +ةeֳlW9I +tlXV0/f''|z]g~e'jxBΦ3qWuOhƪH,~Xgv[&B Z#.F/3.m<ёm%Y +sFuCu11'72l`Tl ٖX 8'!C b k]ڏ5PJL=kK~-X#CpPםaW>ePKŋ V+ Kx"ǡX_HΟQ<)ɓxvA1]bBnR0'^XF_'"& "7"R< rV!biy:bD@ <k ,,ClK}#2ڙ) 5ύZY c}io7 +n NrZ/I3"]eLfs(Y(*Ke#g>cjzɁTq*ӷlP`omƔ=X3f>=gNI`l<C+Y). >d6՚B%D?z_اhՋx{Y9fSK*}%oCu̷TM$kr?) 3#mo; +a]2k 49`)eFY`V%2"YcYm)fSE'Um' -XM]Ntu4n: ҋܫ,…1d. +ȾȌ,k,卌7!Cv&z SЇ:> +zdNe-@,om8)5+JL6Ҿ.újKwGxIr:7.Jh]}c=qrlU2m]fjrؙWc7>ƮHcgH2: yR٫ +j*e۪gtw`eb}4$<;İGyz6L;,OyftwAKF%nw?*qw T8pw2A2 G{Wfr!ɗ%gY(QgZln\l4x smYah +5g"UICg_4A]Ϋ?Fαw5 m,CYZ {wػàw/並 s"c j)e1x.tcA&r)T.;ūO&< L< >ҳi\+/|1pxh+pR/WHOm왡K^^V| ^6])=A4yLDR9o9&@YY gFo6]{AUWz.^v.51xacGǥ0p\..s\*ݎ2 -3:6ϐ6/IaNPȠ  ul_' ?\>.i<.ei.ytxNqx7Gg.Iϟ&J:I`Ԑlj%Q(wv_No|ԇ+\vش$gnHfu_Exְ& UvZ#GLN,f >:QR4үÊ/^]|K.]o^ϻ\xϧojz'8BLtLmM ʉVctqz̓H+ QǦNԁ8YooNd;?GGȑhӻ~m5JweMRFExӣ/e@'R()b2p,7p"?w`:ã7v+_e)IWQ 3][_w}oenZtENqaxQB@ZK"rp|J+"`e >ԐIe`3  n $۰ C@QB9\\`A5IҤO"Kf/I_ĴlFc|Ӏ"T$ڑ:IJ`fe FN ܄Qv耇^Vn9K TT+)]Ut"K0jBBߒ#in2 T*V *l rmi0+i.o{w}1O9#yNs9Ը(YS|fv04(+םDܓSlZਸ਼"glaM +fS|F + +=.x)fm!x<[;:V]mZ0>CYN0j;>$R8rOb +>& B@D@zBOO՘Kv^@V ;8ǬƦ9i`Vc_U :@b$+Y~Y @}YDNd%"( c}ㅭ{5"y/WP!eC7ĭsTnOxR0B99!HdТ#g o#*=7oU5`KFDzJ6tfxgΕHK)(f1O1 a9_cθP7f[4o7);,#3:'YHOD*S!Gwzw bvс$]M{ΤhqylK:ZvCmuCs,Yf[qd=4F*ڗ2XHAeE~Nih!ʩMmRPDꑅzO \tOJ:Ҋ16죴MQ, !!! C+g䙆2a^o΅xd%^>:d?Њ ls wV>wgpp am6AX¥ٕBC*H7-7I0@r2R'z7 J :QۨMSn_Uj L.MD1_XxPv?MP$965ˑY?vkn F_G9Pĥ VN0ҚetJ8(V'2F@Ѥ5ILv7d'l %7RSg[Zfe#'vmyj7a::b{!-ږtdMi TD.- +顚\bAl F6>9rLr𛔛"gߑH.iəTGr~I綜yWi{Pq :ckcx SU(Q{ez!0 wIs'6 ճqK_ZO{_\"MZ>y5-L2ĕf1EZydgUvqy 2w 7WI>dňP8= +u rcɝz#No2{ -䃜XzN7_G*h*Kfp|HN;"z2:V|5Xl3QG"S%ğ#pFU&m%M7v!d'2'XDn`@`CSqA_O-HZU$=6o*a y!c7 ` ʴԴE!5"TE=7 pW\(:Dh ̼>duH=t1o9{Rg~_Ձ6^&*t "ٛ6>ºu7Y~u u^([B>g}~vKB& )jHy +~B|Lavꑖio"[e . LIIp@4Np/h\3*yPx´ DžYӟOdr/ڥ<_T:St. +9U^Y go25u-5Ù1ޘSZWW=8 l Y|q~* x@lo=]JXV__|;ILJ'ڒ;2mL܄Ij т8Z/prl ,E}>JvKzFgM2hO!&'9/Iq +{1L sg [ dO$ Wʷq){De- Eȑ̎T~׭;hOpԐVm~FOlr~!֩m""_KdeU^؊@$Jg^;"c$U_؟?ގ1×`À$SgW,6V?:ΓZ M Aⲱ3'#q&վA0!WǘիnlR)ŘU{eEkq獀qdl^ Mdy@& k¼I Dgi)!v^iC9!g)1πQo˧*(T2^\+%5MFʔ5Vk!2vGonle'O +$QZߪ[ ҁT0WY1kb$Z| !%^toA| RDڢ{וA6#Mb!X(:zqu֢u2+~#> zNX&[{G +C4jNyug~ݳ;#8bp8 }HR+ @)Jy h4QY(:/f{_hK6;@ZSzogGF~I6q$^9y-DjO 4b}u7)KRzJն͙6sw6T'CGRQ% 9=N͏txb9|̼+SGu,֛ Z(@L!\?7|ׯX|sp0W* Rdv,zש$zՓmV0]f>f/|?+ yeD1e,z[h>Dv)a- -Og g1ٸIYWQ 7^`BGmiD5?89_. >wƢD26\Vo .bQ#JU~D/DŗIe>~?#ȋC EzZ3L>+WG{I{VQmK5l^h'"%Swˈ[V 2"mC:a5^jbМzK aI)V~mgc\3&C7{РV&%p|'kbgf[*G{c]|u[>M;΁#$0 'VJܷ ly `T>iw{9=׳.^Vf8vvǘ5ؙE[65q6/p13Uqֺ`\l)!_+Gn Y|?c[RbX7{.or' kɦ1®'`ϻ|•A+LYS앏e6{p eɤ5,!L WW +ߠ`*|~|oJU;Cc؊&X[s 7cajZ&$'AXdod|lNB6!{1B.MD]0,"jƬFf d +dz_[oV96|HY?I? Ҷt\";'{ -Y㻛(. p\V"uIp"2*S6j/Uu_^t;WF'mL𮤏S0#c+Q`% 䙖`({>HZ\Hns(gmN,Y>A'oiW??% +d(=Z;/l{"DBĄ@nXKXA%=a뉄qlLv?1h$r |wR%{ 7??r@0B^d}n2lI .'vT {>7Br*a,D $ zvKAVE dd~떒[82@,}Nb(oY.;*|FK,ݒЃQ_caSܡ7π"Dػ@϶\W5r +UO\:%dU]sУ[ efzcP%qpnA1 鮠>@W,Bx [a>{FgX]^uu_&IZBT 7I?98z Ĭv+k"+乿 eU5|7^K_3!HX׸8DC%>Ȓ%gAĴ d5$&mP䨯5hRĮˬ4ݴSS?[P>IlqbQG`ĝ&D7„ IR }w Eg Bo$6-Q.VƽHs\Ny[Ksh jlLY$luϘUBO.Mcֿ +oVҺqT'z\) +vξ䫅a%b%b=)Sv&K+X٥U  +f/!ʥ-T{4UG>0ɆtO*nB`{Wd Pn ` }D|!DDʢBr޽ d?Q߀u!6.] +x}ٓX=9]i)T?B>@Oюq! \mtZ[I9L0>[!8茩/=r>$ؚ5nfHyG؅z\܃o)*n4D( 8qkUP$1p"7E4 Ny:#ֶ^kULW¢DW9ocoͽRӕLUuNM$,)nIy}]NO3$ "!{kh,AlSD°GDhEHs6rjI؜S 5E&3!ye#dqL6%x> +gv2Gp諧ltg_s>ȍ%,@a)2~ , \qO2U%_E/) $ UBCA،+sȝTLhpzOF޴/G"6@솃}mf͎-"==IlK,(4{ i{ٹ< +ϋ)[(6WVۜ=rOI+3i V5gTӘ6qT:l2\>lkFܥ("I|]jK[w# 3b)N&-s c9S8̨WUyN-.dgcG҉P314/$wI4GUmڡUEDs͛a +| (}dz:@)Uy7CF*IM,%KzvaŠOPSAzohQL9 0;ڱ$IU- ɚmT@|Rl W"kO/ RX\2 +5z}-aYnxhwa.H=n5;̨3Cɺ8:R[]^™CKi^vAXF\nd?6g"\\ Xu ؙyAT qAR2iSD0Inj2g98ԌEGgdBz/|i#A4jXME>'|I^~d%b $<}E~U)$^ vFyx,=^͑Zc쩐h_>],6fz/ږ+m*#]# 5ض| q/eF Ɣ"2nL/*l 0 1 +KDR?|vL3_ܕ}6jmF6GJp&QzlT5m9qdeGy/<Б(t0nH[sPZ܀QJ7W*=͛#V-Ҟ՟;4[hd}!"~&-wC¡CMX7$~,!*yc<;+m.> lъw)?7xǶ`7F/5V zkU̝B ]Glؒ0F@.q26r{\Ͻs s2UQO8Ҕ-V D_Si2~@7Aۜ?ࢄi&ҔXPKz9Zm/3rRrW-\ַ, n@J޳Q,SWFY]0U@/Ų K{@i^ +鯲roWmznp +6+2IHkOCiTJ)$ 5ƃ(4lj(2}Q_3`zUVzh]]mO#S$Lwcűp(1w5IH:^oH[##?M] jh,; jR?mWXqA_ @ȧĈ9ȿ8k#>rtN:mYB`,29Ju|6,wbYNW讦ٮ`oZ;sԔSzŬc&7fm[{7vk_6Q/j,6_9X#5[<}|wsc%_`n}rRvɞnKAs$qT\9X=bI|DJ(lu+w+p^%_i*?MЍ8 xgY[R6whAES \s>YGM(08Q/^oR*UCUᕳE_$k)~Ǎ]hPtK5H!ǜKՕhy^ǃ00q-LmгL蠐By/x5 +H\k{0&So*?8H-ۖy9׸u!2:lz帅V F;{ Qrj8Xcz'r3odxP ,`w %G U jj*vն>;ݾ=<)M2ߋFuԏy/*4 k8CVLr+4@J6GK* a +Tbs>=88÷ "ԜxqQkY:n9baYӆe-/#K2)""J\Tt&9Mq2+_STtŭN>GN֓/^aAfKlgn^ j +H_c:@BIF҅ +;"lvni@٨B_'z}P C@~ 2qi$ϨlVl5*88ҌjNsra1.wO Hgχjsn+ՅBbœ7 wi!.oq<|x%٪F wX¡#c8}ށeK +/J[ծc[&RI0BWY &RJɢ <_\$آ]d-z!#Lzqj|`nE J!Jj+bY&W#Z.4#26h ijntsoRwcCO<]jDAA\* +ĬC=YK9;PfN({LP*{k~q|4__ 2tH>^c?7x|~^qŦlbhٌ;A4KŤ8щhkG\JI=5kYYۋ_'uuv9,7@Xh5t Gbl4D㟞 ӷAH RqRr x=3fǽj =ᔮxk8~cNC_-㥻M&Ŷ>yC`!di"ѰI1 z|W&%[e2uH b;cbUcynLۘW}S^k+Xx W-TF?ErB" :"9K]<@wu2IW㍈w35&2@!(,l mAGC93<5'ԇ{70[Hg aԑY֎s3m4#5?^p5{ut`Vwj}KTq&H4Ͽ/GP3rl{ 6QիO*s@ ,IyɵM^3^x{@x8F82cVz`||OR%`KIkVJKU*BJ]Ӕ,/f!*ـF|GkS(z 5ВVop4I}â0ʾeJcU=uA$K= z˴0myCR9FMwz86_`\\H߈%ms}䨐DV_cf`|t&w sZv/ybt'On0K<)f9 Bߵ!CKVث@' Di;.;R>xiI"Tռ)DPjaR6Q74(X4Q˶snXt\50~X+d*7Oe2jUi7$_bHa+˞@TE }²ݬ75[}ǽ})L?מ67Ƃs[ +^>I4 O="/8M P¬߫j9 C87S$W%3U9vcM&J7!IUN;!az7S,ڦPRޯ)% Fd"U%[ƬkW5 .E+I,t|e&քbbO&vrv=Aj{%hFl:?XH@_E OOixF4}VP%cke]wJ ݩ1-[gWRs\1Ӽ_P$a7/k[?+IsX7F35}()c +`J{jkFU.ft22 +[ ?'ɀxWHyh }40U& +/7|`M)t>I=@~`/ֆi!] uAޗ*cɎe1:ܬ#m͸Z6>̅<Ýl!,C1׍RZJdWZĆnE9^FoƵsY 2iHѠw.;N2+r`HSs -mkc9"e7I` ^'O4 'xG:qڮD^`&yvQVx۰ {}Ȓ[tF3N2cԒ"M˱jƁ,ңzwQGstXb 1Ž92+b ˒i iق+_y~ k0uW3>A`@CE\cRGK8 w0S\Wq?L˼yL܋m̛Y=siuЩ/e9?+zTӇٽAU`z*[@%`ײ$l]g # !90]IG|S,6( 7%C !eYͨ@]VȬ^|/NgNP m̍ (=mY̶1#kJ%-gF&PmmE\R=E͜ců҄ߪM{YS$&Z]qB8 kK&痷YLM~$öYZ` +l2=lWTߌiN8"2pz*[ k粛cU~])fƾdv `'>J{H1G7;uc\q/ۏLHgɎɭ>LiDW1ƣ@s_پsXrENU\!MMf  )Tmb= +D/3!%RP\>#jP'dx/?IBZY&?2/o<갿Jw^ D`/x633>eP +6͸YeU82 Puv/2Oɝ Afζ;.Eut'95ĝzВk9A8E<LD}2C_zt!'ropa‘xQ}ֱTn*V9#pdyE9+[8' Ӳ/K2>R'+1u89oMίiuSoUUir&%%<jo]=` QYU vʕ,}@]3abl@Z]ycP7G^+?(2G@.v`+b $fWXƒ D]@{)?X5FSz?3z'/>oJpL60@ JW2.%SR@8.&OD^ؚ3n-I\:Hr#߼(ﱬ찐,9F0?xN'Xn[.M <U]Dm"XX6a +A*OahTU4be2d Zn72s:Q֍3~qo>stream +HtWI>di, 52쪞1d bOĥ|.dX dq! H@e$mpGHF"?~ߟ?;( +OSmV  vOM ₲#6xO] V-sx^2z/%iHU37~GH :}hQh=`ɭb!qyw#Hqο8;6Qԇo< u*`J BO%?Uk"̬T4;J_AFJnQ- gL @M LЀo)d{t@/Ȍ>~7b +Nt߀mߤ$/`f1Z-80_m+7o#j]C8XxXמ ks\ MBs8=ow2yTP sn.H1 ZJ27D0V +Dx]Zwiq( A#Q0iZ%_9Ej/lM8`:l ϥR<Ә%_"y&l[eC$'x uF-͋;Q)X8]_D.9!IyCX̏q,0;Cd~%B 4xs}#h}oxts57u%64jk'V WE W3k&«S]*vńܩ+ؤ/d(fl7lce%N6ML&d +XH h$Ϙ1%,3]؎ T +qBr8n1_ʟ٦WVOdekYIK1|CfwfKϢMi6zWurP>烑UVsepY)05lmx4Fl/zni쵄.b#ԙHWŗ{WQ+ϝ8VfmN!3q4=T(̀ud{> +̢U@j+HJӦVny[4/)"Q^z|(ȶu!+C3"Cuvw~R`1r0nJ\=慨O)8zK{ǧ?ꈊUAgB9kPdܡu6a䡐IfnF:KO}#Sg#ʬB#έ.$o{a{ěFцx[}IXk !ϊ|i ٘nXWgTsVHLKGj-pcŞC.IIqFFe>RV{`1z2!s-R|_2z,9 $5z ҬQڱk33z^WNo,y֊7RK|oNB__ExHA9 2ԯblYi P-wb{gBA4`o }דxHoメyRֺyFi,7 .`ɉElI=\*׵­go*$EH8S~"+l_X5,-aaxq+DC0uj|ޑ|ǔ 5لS&=Ϭ/F]2@A"ˡ}PY=2+P<^ߵY5]ZR*7,57[v؈/P*0~wU6~ZڻO'Kn?5K!}uܓݻlrK;xP ͭcy!(НrO(PZCH3|RjkOn~&bk*{;*qn1PQ YBp%oXG)`+L\u3b/ՂGQ#;(Zx!ק}c_h +!si s77- 1s 2Bt'x|/cߣG5/OC۳~1I>fBk+b @|]$W*Bi&BYDDr1?){,XsIĎKT N=?g4?5U@{AFi>D +gIs8}]0r2CsژEjJ@ lSǤz>8xXo7$Fe5K 1B(&w!ӉHlǑvͤ"kFAqP7 +^nȨ¯R4^U/dhNH1YU0 +>+\j, ̼x3bB^JO8n΃,e'P{SoC[xq1i C{^V>`S+\}P#FG8۬*_IF L*Bb;z $<6i>☈;2!;?`w31ǖ˳j$gDFc4.@I^KWOlսd5^qPva\WV1d[[ڟtW_?ՒK +î6ȶsؒ:=#K@GR"2OIc=j?Ԧx^n + "k~kol> ofnYp٨*OOGͧDNY|?˿+yVAПG}´::]Ԟը>bL"Fv_Nqَ|LGH[I5eu+gw2]+ 9F׋`mf_˰xزoD!Iv-1)G?&t zP#s!50#Kn$>LRv} ٓG}lQ !w޿~Y7xW}'?_It4y,\5/K-afֽ8.EDgˊu.KBb7S羞²T\×4NnS9V8ɕ@${ָ$ +X7O2?3fݾh@]C{:N HojS +!6tVx OMW\=!w>P-%ZPs!ID00~>k%]2jם=ZV{Zp ]cGt%}"_bEAv .\$%*Q$ M,~HݺЁ(F(xW4ic/q24RgY2mwyﳺB%> 9"SH(N&^PwHBlo XHȨ $5& GZ4j `zC \э$(l7= uMK!kcQdtc7>e]Wb[$EbȗК&{zEA cZ!Y jz*5oĜK"l 5 +EצOC +X}@#wMk=] +?-9:/ +K4mPZ<F6V[*J3wvp!GڃBS' Rd(.,ves*Kyt b! ,X+Y1k=vޥAcW@M7ͽL5)(a$iT +!GU!- 2{LW#@PZ\7ZŸl 7I?ӾOebI@풜eJe4QGHLZ%,>$L|5X[,}_TJUO)sSZa[ڶ2Ir/A@:i97v>X&UHOFH\C!<xO <)I-XsP85!rL@fK]liFט~%p&'5pH]v[f3|HF +;qAyo4>?oEГo@ޕ.yxPldM!stf䥉\섖B3wbFE_6b?A&k_{( TՃawCZ3K380cwPCݎ)i+0l`vG\VG(v,֤wf_&+!W\6nD<.7C}÷TbPBCK5BnCk#8S`4/$[/eTU!)A2mWHڲ0t->^$iUG%-Mpc[<<|BH;BH +ܓCO$WCƘ^@`Βv@`!<,odZ؜ w~>Ǖxf(diCB.\}\qưʄ[ tdiCkp "c, m\l(=JḏUɂla )N#xSUu'KG;oSG3?\WDtx?<eCxH!rQtZQR=+sK97Tf) | g*\3=hPuxGD]`4koueRǩrUB +h\ց}-+-46R< *z P#V9q&)ῨV$EV}yF6cP3dVgGKRLDt,sYl`/3o)"s,sŧ ™- ^j +h#mY}y~ i&RG[V75^ryc;$qPU` jTǗ_֩+[Q)Ջ讖,IRv>A?|̺uf;-Y+%DF3V @VBD|pzں;4`H+@]ZOVx!q>3\NmTk-~kcv毸ʰBfgX]7[ эKi'悫(j +b6zOKݝ(~5eOpX}e'^:W  .a}okcYz+ k͛E"pA +f"1 :{W*l M36$}6N${M20r} C o@"9M47.%:BڨttD[)i8dPN3{Y4/rZZi[-]̥σTse6Dgo~C `,PpPV!^n̆1>Q,/r &Әu;_!MkM}1)n6zZ>u8f,j(Sc^b Nս£)Fhc^]da1N<0Jؿb?Ikˡ⻪'sYKWH ^8K(FQk\W`D18t86jbc"&%>E=,"CC0E`vE̽/IGjlP;#R(ŭ%bZs,B5@q#'.‰E<3־=ϤLT\iy} f9PO0B">]i<#Z[T[$uțp9sYvN ov (1=!U"y`Dt}-}b!# C8S5̤vոf^Cf4sВ҈Ӌ(=ƮK-g*.ys!}{~㜋ɓm+D +èHѽk\.)!IZ>tL Hrʬ|"Iã&l=Z35kl"I2-Cg9%0 #Rr nn\ݷP5R#BQ.ʞ;#^]HsYi]k QuGydT3$ҞNk&}Zߟgʭ*궍&9HW&&!YO3 "MuF-(~Ȱ~\^r>:ueKvu5xc|~s2lZװ&ZBfMFNGMzgqDF|Sͫr6nW&ZIL2 1C^DI7ە_p5*)ILWM.Dֵ1@bLQʢמ˥>08UH_57Reƴԣ>^s]Tw{2t!ºtG) 1pg!a&$,HRZx?0<(Y攨8#7- ē|.SWg4>41?bU]9uwByrOL>0̒fkʁy0.e +ANsXu&sPfd̄r+Nm>3CQ$_<92ŸFYF`Qt%Zm˨Q18|2_LLSl{9ot¤|I$OU~'W2@ڛOw~ۤA_6i(mHbtqz|pz|?I Q2nRoܬ_8y/ B崓@> piڀ7B+o"2x-B > &4wp6%gڝ_\VG0DZZNt=߇?Nw 0E>8GHn9, ŶZ2;o+uyj,Zc\Ͻ\mi!U׉}#;Lэ]118"5^Q(")|s/k_^aKFk8jC4tƀ9z\ͨ(ˍsW+Bkot92/~3wT=NҌz'1Ha30j]O}Oj/A4,g$];omͮ>ۋkډ ͺmrn3.YI4/}oT7Ǫ1D-Xڸ ~`0Dr zGw璫gŒ3=8rI(ƅyщLA}ClVjHM !RCmّ +ì)9R܃TR)BN} ]bzI.QgӃUBn-bH{Ư%1co5@#_n؆ژr&V_z-Mфn \Ͷv# l0dm· e LxMtaNW<Ԭ'b{\T=́|:,Մٹ@))y )E'F +#ӽ!q"G%M0 \lEa`4/I(ӿ-e#):,'wea(`gt+9j\Yi[ԈfNfYN8S`XpFrDeQ #q#b.KeSƖ,Os~*S;oꈩD{8`3=xULLkdy?7ylO~âKɴ~^M  f_}̄S"\KېyR?U߂+RTH f&8zM\* O Zc-Gu&KHdHOmTܮuQ^

]Ĵ!U]#-.*ųHpAkܝmo0fvO7΍MX]Ct*/ȄEݫ=7B]ӃomXrdB:驵ce77NE&56?5ڗ߳cUJ35Tكx~f|}~.XjX2P/*>Ȁp8¨r5a^aثSZ"g ^SܕgԷTMcn +TWes{ca&rθ-TᔱY#ljKσQ$'@PdDiŦ웘_zC2;ePjݬUNZ+{#;箇DcQH«J^gW/zoV:=p[7M_`40h)Uӑ5=I?☳xc5Y qHCjbOC# '⠰͸QY@hrl*74l-ǜG7"]$EyJ sƓpH"Xi@8[IHL 2E +>(j Q6ogߪv6$!vgr\I7H%+=/$ѮLk?X@E}R$M( *8N՟+@n8kɡ: jj},'2'"4Բ9zU i(>FkU8\w2l{1?TNaSq%fM9 (=f)fmdҺqg6k{qWAB9gX"kRO}bн_vr9 "*(l^6`ʹ%b3 JgW* #bxk*/EM@# %YPǬ1XE!т43l⚧,gԐ0nROzOD&2Q}!u˩E(LRyYV!ƭn69^(x4ҝ3|v.6߳06y0PPZz;`l% 71x2N{>$&DP*W + iR68ק(irduL Qw4eZyXav#fxo<൛<濂."z|wv3粝љʵ]KN*=0O !~ %ClYKeY5>6XXtT/Bh9ʍ> o R+h笥Rk5Ò=\XwuڈHU]giWXY + XgF|# X ]mt۱lw[fT!DW*e+L%;yVv .qf ;(Vvk->.cdꅅc>]B܈p*&@ ƅ vbz=]uO +mMlH2Dq +5;(;?6hX:zqfy`LYU[/=$%׹$6hvM?M SY v#LXY锠xc}T3ǁ2NvOglR*Şin.\1}]lX{>hZ.?Ue׎ö#I?8 +{MPρ)㉺$۶kk(jf`KdLes+0-^{&$7 M5SHS&-E-6Ro1E\[LjQzcwDy\!_zWVj7M?[nԬ0XV7ޏQ9 *S۲L%c,LDz 'Q*M PfyR[fkWb:d7YSa`A &CE!+`0J)(ᐈjUjrGuD  m׋픴@F3)Q"5>W"ЇNxjqY1DA1hdZz/xUK8J,˓E +MzpD*[V|H}QdE3Ϳ>ݹڞgy}K;G0̿>ߪc ҽHߟ'm"/pN0_B`/e`\v't#jeG&4'^ކ[#W+V>60i]FDV ;i݃6Vz=dw|>&z&LR|w:21 {yHNY$Dؠ\-tr829:ȟaȋB8ȸzH-UeLBqaRd4NLŸɠ*7RS:96^ @,np1ߓ.4P)ȣ}*֖8ðp.@$H-VGe}1bՊ@K ̈zYǪXY)ްbe#m1È#%?Uzع#G+YcBYa XCJ*4NM#Phr⃋H a}vlI\yV.ŧYRhp* +a9Xͩ2}ꁵA y|NA&!$ +hr5j5Gvl^ۋNEO&FŽbqX7Duaq[ "hZplBi]!6qInӆ DLѠk&VHzUVT8DUj왶_kAk:#v~6ڪ :uw$!YcS?N+EUGN')oxv)sͨуcE ;g.pee%rH-R{aHIG1be#bpBT@:`@^ :J߱ej<ހLOyr79=1,e^t&rKgUIt=&f殮`vrϮX]|MGAge~6*!&#FI9/uVeO95)kmw1pS !iF6$vg3F땧* ؒm?Ufɪ+x{ڻi4OםU WY + +CaYO}l +Q[+r-n1 cnfrZ^|"Eg5tfJ~^7KI s+* *2z2DzF!%QnRw5G2l.n>WY%Z05-tW6Whj7k /^H1sNu']adzS,v^ѫEtnAlm9K~ Kj3SdcaCW<;W|k +Z/[^ l".@J@ #Jf2]mA ~ahh# w?&) +"agp]â諠HE]JDeo~-%סh7_m 9 iYcqb_,MM5Aj }s:L%dsn!w z}^hV!U!%1fIu>q|z!K:Sv.܄Yx<|r!),[v5iz#َ7Cuz T;lcf{2 xm4WgYD6 ɩ֬2NP eeڕLQgz9L2éeFR-UyQaw*, L:V|zGY*YAeNIUg'&d4&y+!$GESQ_ pzM9=.+uJГlOd`־<U3ھgEkK1)zΩ&-իW"EYIBѳ=XԶ#=@ZJ84"aVYejI4AS]!ѐ>sji9Ǽ$j O-闳)Uwȏ{4<#*-HOw,}xs,W@Șw͠F&^-Gtu2LЪ +cHf]w\Dꭇ݄h\浽f?Ͽ81cѬQPӐ +}] X[kDI-LHƒ*2fQgPgVm:;6kqFHASJz=F_lDzf +U0~=y4|Ƶ,B Cx@V0goPf-i~l"lUX"S$Rah~|7"5 \y,?ЋX/h[8v:e3.uAmD"hSoZ@admgjxksCbCsO Sʉg b=-l9̀ vQvd,5%r_f#K;N>hSX[RsGR{~.%Z6 5{pş^'F9lO*ܰ6:ene焥1 V:ӌ# lR*3!3^7<3_1_kةHmbbሯI5:u|,ig0@[ں +%s $7:[:?^B(mw"$&}M= eH{="z%8P#ZdӮќ{ULa<E.ŧ*fH5?Mw c`oZtCd}  #)I_i9HMc|w}QTK+z"s\"qdNU2B*Wy vF7 īDl "!>&YM­Hv+ٕGQ.9r%:K +&Y.V+T`-3nzoRd +@.?56[MR u҄PjJ/w<:@'7hޟY^`rffDxoVoA3eb }PCf@.}bHQ#Xi*~b vlӜA,y;=VwGKJ'?rL#s0p 1 ^ǚtBlUjrR'SKw5Mħkż:Hذ#Q.J4K]b +rX7³366u"Ѩ1Gr,@^lѲ)0A[BMCddU-8rV&OY5ݜ|.o;C.I`5Dcs AJv[31?b?ri@0=Y^=k ]&?lG+VҲOw#H+0qs?wY^d*/$W̥ܝdqMcX7Bl\G] IB }ގ}<<.{ա_3Bb'قfFQU5~Hb$]C<ݝan X 2 4)T?hF{b;IΏ| Sq%W O-}a+1udyJ +U5(&kxs/I}^UW!MГdot}:-ʩueL֬ΦMVn-\U/2*' r?9Dh]PsO,պ2=9 5F,Z^Co|4?0izCN9iQ[n~`4j{Ǝd'c>tp>mʜt{AP{ߎrGo_} v>15]޿}z?{?%z3O^M%%?z`L6QMϿqS1GR,i~˺Du?!ͯ@hh~m:#hkʷ6O_ aѳ 6HW?{3VINHY,.$eueMJ6Ѩ@HYgYWv) 5XW)DYE\4[Ww!eĥŒ 8'j̴`HY lw0l&1*f-;DE5לY-敲 +LUhe ) [2ޛrq& p=)%[Y7) mk:t1{(BNH AAWȉdVnJ:d VwUGۄi:۵ ]q7L_ -xnD +Ӳ̲썠,&SMf"ŘR?&OB׽'herɖ2qߠJ݃.+\oGvD/.DLay6ҠSrD4O˚ aLb1Cn"B^|[lNKޫ7^6d8"sđn#O?5Z[0_[xu#?m;'x`D+|>y4YlB AukM +4(F&JWu8Iy)"enkoh(n֫tˇ.K:ϾװiL2lT?ح [eGA_~*{HmeBDQ$OWߒt(8Er႞ܶ]+D%R2sTжPZvԮ.V` i"2R;]ڃȕG;I:`ݘNQcHk[ /p8̀L6 B޶q:<6/su+'T6Ȝ"o?IK24z zXuw}EibR{Rǚ:or]laM 9{0z:P N^2"tb_Ghըz+aCF[0wW{.&nKKהnL}(NmP|0jEV1/gIiRe@! x]evoL 5jTGMj%F8YCC\2{! gDU`Q cƘ e6Z%*kϲSP! +hZ8DyeĿ "^HU,a3eqS3B6z1F˖S&9ۋ0tI@\^-싧/zSyke8Lv^:Cݮ#3ϲ^᪍_D,VYj0s@ +4Rt]ϭQ3E- `|VhP văЋ'7t^6 wvծ JbvA{d+)<1#Ho4]t*9h|oBoԐ=˄8ImQ.xvSt XsnMM5M '&H_I]neC5I7Anޒru%׫tj)3o{v7wm j7ĉwMM +  ;W#(*zslo$~oE(>@e"ie~^^J2Qodj(7"-?5oOxy,^govu|69ҥ>@/lJ +!8]ʨC/Cp1 .6/K'EjUYE$ҋ"m7 R> +7*` ^9q\TS^W \8v(.P|Q\y!Aq*މm'Nc9,zb_ ^8s<[՞'qZBhFUs>_ +Tƙ//9;pl'dˈSk*kgYۃ搂O{.BApj'el+]YqR:!޼-LMW_M)"qc%i?5] 8O9.гdCP&Le7Za $j):睸ͤ~3-p_+{QFiīrFob盈v )ڨe6Oz i5ON,CȡM]Vв&$@mtG,ؠr 7ѽrX |g|DHzeOS+!*?Sio/p` o&z/V:ν?)_Oua,F=x@X1ѤBi0\ZNÍ>4{L뻯Jb!F ]ą(p+kĜP(dnOOiun$Q%KD + ]O*1EH`%*>˒*:ٰZr:Y1t8Szሥ +ҡ4\X k?㥋٣Y@i󚋲-k %eq.d5K7gnK6ڏ} (+SK Q"o#>>șFOx?m_r$ۗΧ24SԽo"KOFŶ}uV2Ve͚v/V$sk^Y}aUpGz+^0 J>⿈;!gOh2?aY'ՕzCg.*v)vjX}ɽL"HdڌJSCrr55NWT:[Sׇ|۶}r(M+$``ۈT8E sQ3凴#J2*DUkF>B?x(6yGp˒t1-a)ʞ~=FIJz 5|rR\_z{K*;"^*59hqc'ܭxb\CHÀ%sgI0QDy#U1v3ǩp;fj>gp"4f{Tm[2M9m ax ~*7" A3_2 +endstream endobj 415 0 obj <>stream +HtWn%9$֚4߼Xz#3bDKIzU  ^؍9Aຖ^L~)z1ڴQ~!m+k0Ri"`BPNCy y!^Lq熘t&) hLvT>ƈ0??>0#"y $AB|S08U fFvBPl.SsTi;bz;2Q#FjL;Z5<6ZDoP^Y+wō1'>z-Ŗc|;^?=&xg?V/S'!.Hd$mQF:[沺May8u, @&v,Rvuc\`ːUvǁd| ]aC\r"K=oj92`v~~)$4Ujwt;&$gVSlkLAeS+9smEVnjՇeD]AT]-ge2.\%2@sQ:6HЁjA1椧'Wq]= ONbq@Qbfc'F^kR)UO*S BȈ`F*V,Zsn΃Z#y`w[& +4}8rm4(G2WDw )"˅mꆉcw7,;j0*_p#:7ZR3Wxqf^0r{c4\؉]l7ĤꂠzWEdKo>U\VtE>)f:RZ dR0V1%yL!$/4 +-%2< Hh6DjC52"0k*7ȴJxQM62<L5D`;RIy<8NVO鹪 gMAY5ğNڬ9Sl#)5HIN\F{i 81FL5Fй1>-^e0E7P/qvl1']F%_]1㨦˛ۃ¿ni;*9Ld;TMsﴄ3*<071!ٗbgyɌ[ ڿ5|@b #I]<"$8N9yS^4˯ +\>+9$JDsbJUuE +%n$5gkT&˄&Mv:gjY]<{)FE)2hePڰ݅[$=h&Pv!1C'7eKOe7P]tSykՌTrna4`ΓPvnϭNK5좮ޔwoTCI:HW==UjTܬGRI L+I="\dPG8w$XtYx7nH+i +p@ha'sE1ωּ)WqUf蛃ɪb\'9;ՉR ^矺Pјy2>V*?K'^8 fZ ߵk]A>CIAkm ~lQOh̆d[HIic&!U}0pSVGm]{fYF5^8gH"M,$,%_ͺ=%>}Xl;`E'A}S7XcD'[\9(gJ +%Ѭ驿pO05tdԒeIIpF= sa4FɦzC h?)LS?uScڥ1FZb-C}=Hp~,sSaJ6ƕFTQae`|ĢK 9/_`cLܻ8jV3(9yrY9^@ZyyHecĉD-9!>b$WUO*~7 .3_/&dxe>Riry -P䛋h^3ǐ˒^k N댛18ɒ"m3jo̽"aIQc(6ޔEpY.Rk;WȴQD0!#U`QuwPQwP؟;[`ͯEaopGr2SvRҁJW.t 6( pzPO=Ǘ*$p6K?cӉK@joa>Y7ː;RJw682g!M(x$:Qm'~5/RR2z,-?"̣<H@<^#Hj#gpqZV.D]G ##5*c{DdKi<"x<".wqG2}I$K#zM"ڣsW$=Qc#4\#^_ ="Oi<Q<Iȫq5.Ž_p'Y`PmLȐyO~L &BF=&Q@/gϰT#[=}<"s6Db[DR:-"EC򳬄a;mU>Q:Tc=1yb}eKbV^61T\y{ +ML%iL6aN%-}cR]lx)loә?e^I + cӺ}޺) / $hH2"P:i9BQ(k }Q9º쉥2$BE/4""&`-l}[i|GW׈RTUBJgi &Çm7cc}$nPᦇ+&+g4КYhhq=yy XS.qdz, }ݻƫДf׫a9wgߢR,*^aB`A,sw7?<"i9[ڿ#C| +HѐA JkOv{Wnf!Cd`}%i*{"C*}g({iTZ\5V4 +j^2Pʭ,{w t6WCiEHr߆MS3m¢]z/}~K%|LJ{ m:-D }苈ed/s6˯ʚ7γѢ6uf_tt2 [}̤S}+]>ƙ ڀ^"v=+ cRfben; 4; 6L@sBk!M[E`g-qm\q+xaY%6gk?J?(^+sUn^k;ﻁYvL(?;oErҚ|wMliu7{z4n۠RbG):ŧAī8}+P[=fInO'OqwJn}mѼp=rŕ +QYDXƈ`zsS$ +6.WCBOe02Zn_<;^ ^ii#\6TkDfzh|z?B`g 4i }_Kt +^ %q"2b#zz/[5'!x m0/(rR񴥲G*âP'F6$rv _h dNνԼ PS"F5y(m_嗖n94&!GfܥRK $s]zJl'%c ˯jO=j5]unb ]KXRU㻧r#Jܶm5Z ?2afLřC+:}㈗G#@Ř.噓,C^k_oϚSMLW6pQT,)>췂G 9Me5 \Gq@5ː->{cYqe$WbVNTG$)V2궖ضd=44(ۗͬe/GL[|/i,>%FoF zC) BG܈b!Xk߈ !gp[v#4 DCh,J]7A0hb4(aG.F/4Wy oJ;Z^~So^) (ҠX!Th& HhR0-kAqzAZ'"|Q'nP#nJ|Sr( P4B*̋vG4Z2|qQeuwrǭҾysY AG +EdtòºuقJ&)c=oDXw2Qq3Ve 3X/6*sY?^5fѢ4au kN7>le괳Hf0*a"Å0[! +ϵ7H8ryRm9c g'1n]y5D+;[S6"iZUj B_ǜO8jm JִA¾>1b5"g?668Ј׶KCRn(;rWf4V(f_%kB ,P-l[.GuQhn|hIۍPzn#8 p^{/W& -8NN +ϙQ(WfeijU Ou͊GΡN_F^JiA3][l{#:jNX6c-(Ӵ+y`1IJOaG퍩8tIJ]Xjs?UfǪö3fv?}d(3 +#mE{oijqGN|I+-/_jU*K$אѶ-Qk#ZAJ1{vaaͲ?&ρ&HTE;biV/z|$ƍ:L%mFPj+_r%INJ]1M6q`FiTj* +#䗝#`Y9,`/HCs.0iķi3{7FNJcF}dz4;z&r^sc7Ε.g6%ʞo`nT4 ̷.%ӣ1)cJuD)F z.0 +`jSA_%4t&0I(2)ZpFmz)SPe~ #eZvdDm/{*FI#S\д܄OsЋEE4cXV6U|raPK"sE4=ym '.G ח]FՋq]e! ۻAўS:l.)̫IYL + de iYL\'3z!n_;N>R0/%m9\hʥj AG`YPeNlc30y՟LfV+ln=fquoA "xҧ z.#f!zNM弪=%# ) ޕ1I|Ǎ |+~3  E +.]yX[ie%^HB4 5ǣFghkᲡlƲ/"077?X2DxK}BwU 򾺏db4EQw1.Ay"lI)\U~:6Fk?B +/ slڹ7@(p5ֶ#k0Zғr !+\~;B +V[ d- !Ae?"k7CX7 s9:lS<+@ Sui!<(h6>1Fk8 q3n BP}jRCԅšKm[O ڌZC ׾hoϲ)\Va|S; 9%#^En`J(2~=S bs3QxE{ ƫƤӴA<gН0 jekk0H<)o경Hal;YH+?ز5,#fmAbV%kXZ]"PvX-˦e\lh9&UAe}=(lho"m|i1,JZ Amop<&eY]\s(uq3RGјmnZHɠ9rEs#; (gY}pjȤV]/p$GJP޲3qB A8^1}:؞Ja[IC"_Ԣ)K鸨+[-g[ Ȫ5y˓Fve1P/û ꜟ5 k6{YІz=:]άe6HY$"{{ p({DCNo[%])1ɏ#_ +>f9ԉsJb hm gAs_󹇱UQu{4!g{ؔ'5Ze֖%W$j_f6ժrJOs}ϠeΕ1(VڥhYom 鉽e3^"MAU>$\r4v( N&{H 1i9i?He{X:'GldеjLYx|QCl5 +Cj}1cR_Io~ aZպ-7^)pmPf紌  W Eٚe;\m jRY]L B<ˢJՌjc$T,ue1ed ]AnEuIд&͌-uѳH/@q377,iߺgˬAy\6G 3M udA@`8\>'"sxIb0x u;9߃xd}}#:6&:RwQ͝SɁl:ĠFQ21BrfmĪ>Hn "~>^W~շZo= dH殮~#Vs;^h1VwDL?/G :%/}ďwzg:̽A[&la쭇(حsSK#:^O)֪ȇ"6͓"n䥈G 1A a!a#6{DX#"FL/A79.5 !^?#ȡV%L9{r nZduKde8h@WFoY+~{.H58p:ugHҒs: Aii}p ZEE`Gs@ˎ*f*sS"UelQm:rkK6jê&Wc# O3?C@{tD'c]W3!XuUR=nJf)xfځbzŅ7*Ա@w/lKݪ +PUZ3gu5@+>LT J +(}vb"@`h?T˨ql jǒ#GbW=1۝-{,fd5d(rRg7HrsǑ ӂDpv% }} )ۺ/k#4cQg{i[ڬ]?|Q>@YH㚯 w_>~ $s!+mߨAVHIa!|eFe^?eV8F|c,_叅[*m1͸X>ʊŮ r>\R2+ZuPOg&%$q!s }W%(l>35\@a( Լ);jޞhbe/@I2괘U0%dp[|ΧVL'b] wǣhz5i.@J 8#)ٳ}lH7= ͪK;s/=bC)+!5Ѱ _"y船b~hƟ\)XJz㌂® y\U\h3t0ڣ ݗ2mL:bs3z&2«ϖh BJe)^-t=8.Seh= ӣ_ +ᱬnjGp6lol+W" +嗇/;z{߬Cן%;B +sb5nua(Qk+5 2ʧmѮ/a2)sFʡA<)ٵBS|[&5*d8cdYUu':#K{Ceko d'6-Jd椖qshIF/(QYYJ@)֮6j}hSn!ɰ¢4$Mgxrkle7Ĭ<,K)19zeM8u alm>UGāMZȦat1*LcT9.F2b/EEԸq`:,qtа:f0DͿ-BuuV^w$YH`yH㾬2'̛HQHO +RoJ*8RJ(wl,n`tL@D #3{k}TNe +K`@N`3N+*X&`N]ZHUi:׎W9Wl_u舵j~ S)Z $>{ lч +Ar+"ľ(۲k-u΢Y#(zنiT.c E:U{=*eUK h* =LnN79Y`$C~QVNM0#dʡ o4FMV/$M,;Fꆾ]tӹ})'Um=޵۱P ' tB4:ZקgF`Rc(v<7ʃ)y%ߏ:g֮x)+h~#t#य{Xз'qW>$h+K O.qC 8t v_V&hJ1XVM-jUȚ}0*JU&D;Vd7xg5bܙ8Iben:VHsB&b؅k$ [!5h^i%0FtD}{[o]oEȖ "EAj$k_H7MޱsNgѵG&r'(!dE;4nSҪbKq 5Y1e&G@ԷA;v_J?ጱ~D'NL"01e螓mF~[6 +g"p:xp 9 ɭbM?xޗ?F ŸXAZU [h`nHN#zt+k\m]c:;/`q+[A=s +d$J+!si:B PZ]k Mf.+O;i*^GW- DK>u?8zo=[oUhôDZt+U~?@KH6/eO@G;6`wd45:&4(z| VM0-(!G80R.׶EgT)kӇw&a*8֧{$՛&5>ϭFyu|_Ⱥ hM^Gqj__]AuRzaQf{Gw R8ٔ\e"~Xk,a@RIBbťg.k'4+ߣ8Ybc-Y4_GoG$6HY3еt8UBjEX pt= ӹ&RcU* }q4r.C&9ȥkg4Jz$IPȣJ0J$ >a0$Ow#ر@t+^zNg>IUƑ=u}.N0>ޜ0)m^]Wb9D-^}­Pxċ M(@!!r@}[q +]N!e oK3'Lc5E֍/T0e)xܔzbQ +r#wCXg|8ҽGXiVb ؅%i7}0W!p_{W>Hk"61u_8Q^Qmx6O,iDhU؇&JԵ  +H hZ% 6Ykͧ5IOrB:6bam]&VUXh4t[~)Hǣ~I K;#>5q,rqRts1bU(Gwf92E"}_̧7/22o%bDl p1(YyaQOJ(, +ei!._@!4s5Fr=bg Uiho`.+}hA4=s}_\o7TLѸyhyLv,+PfVZdgǑh2M4K Sf-q&ΑuL*Y8cuw6e[pƝ0Ǎ3f8cniK>pq6}%-}-bzpSMtƙ4q`L1H6 3*ĦqO#lD{^fO>|.$GYsnOtkl<}YUz1ѕlY ڹSI̼5;2ʢp6Ok煢mXz3en<־pO7voӅw gVa ^jO.w(WuNTY]kCX Lu0URΞ>k(g^E47tp)GCz)a(4\4e726~ve3._Np.޹庁X6Q㠛kM9]$͊KtF!|EoDe ="(XXKV@?K)VNWbSzeS| ݤ&D}*^aMbJ +E.4/a3Ҵσ(;P̾`YQ\YW_kɹp5qx:gjmȇ}1"t&n%7U|ʴRk8E sW/$ +ɬRQo}є^FY7X=PjQl%k*ظb*W'0bgDK)\D"Q$Q'؜V%X  <'"׍$?x0le&DuãX4ӁE^=h'.7Ba,ܦFeUˀ[9a J"I>2.bl9Go(>VD͊ fpƺ&[/XSw~Y1 @qbfwl!ו+{#f yKGB=Krp{DPnд yYH5q+"V-C{<*ymnI͛+MpeXŦ9Sس `2oaƞY.Z;w")4, *ұf\ ļ;gjoM` +V0dq|КfQh%o%Pnh!*Ըty`HmTkЫ$8 ά3 ^7Y˜)ք+^Y>fWOsl O_LŋnwkTӁ o a-x>{QL P`.&zQղ18SĻا3 (n:OB `JPp2CYˇR곑՚%ҏRҜ瞹/Jq6C z:0TM<%#gnQ'?`5dQlF_'Ԧ46y'\<̦>y+q6ޑ췪Ry'JT9ly+ + ?uvعՏ=6Izxb m&s̬#dX8ۙDŽVd"M"(]XroEwDZˏnU>:f@S} (isR&{9J ^dJ?BbS:@"1a ʱ +6=6y>(a$͌Me]J+D[0)\sZX>yfg)j}Ū=J֢YTOۿ$s.wZrN#xKOgqSJu;q$DQ<$*vF*F55L皺3ފl ġ<>&R!x₮U;:i3yht/y vKN%"k{-A՝Qmx_|7cfVMiQu0zF +6F*K{Oت +3[χ ϕ4b2CbR)#>lLVu#\u}gWQ-m|7`+X8sA*ZȷbRhJ a@2"í8I5gV#{ϬQ$:`LhµX*xCt)ھ(`_*O6gL0F=-! N}tX(%hG\i^ʠ#7C뢬z)?iApC9nWU!Q0L<6ef {*-=)62-fN{eG,۬V>M3vDMIY)zX9u?Gts*uoGN9;E/zlJ -ױwfyF-bkY1a"FRfQ{DKAP2Z<2uk u&cQ`=/E*m^hsqޢbZwL|ՔB $:7اİ=] ud^ٲyxG>y +5 C\кD*"ZJ°Ȟz?[=-/YAm̯]S5Ww` *|1#_Jz"/u(*3'3eMMLySٽ, |RW@sLOG|*_MG['9è*ثw +Ԧvx!hW +ȦКjcZM6Yֺ[/ߣYZrWw<9TbVN\k< +ZMW+C_ ^@X Li%_⋱8qi \ڭAwç]FR Ne݊ +jteBb숗 +W'o3K5AǰBDr?F<1ˮ#{=2ГC'WD6Ak#*':/?D=*r.);[Ek헅xpX؉~'ٚT.t֊2AECRjdqX僟ujp$y~:1WlI/ 𔍍F.* ~ILV%J<>╛U +ך}M?*jokƷ9Ϲxa2Z|4PXCO(c&>cqWArJ[dLߚzm: +χ؊=b;jي>_NSt+M#z?z c +'VSy1lM{3pΩ`--&O2+QKAM}r4r08v+l}g:l 0ܬ@Mݕ?{f Kjv +p>byfvA_A Sr+(=vߚHѶNRܿWy*~`{z\oڽ3W`ElR5prq ffŽR=ѕ>KvXTO4N?$dR]Q4>a} +M {b;f+2,&N\N6};#ĬB?u~껮k%(sh%G6˘%簡&oeǭfF)E6$:n_*@ *oI9EFqaO + +}Offw.O UF?nmVkTǔ651u ¡:O;/#)F@A? ĺc6hv`G Y\imӏQX<12>stream +HtW;/pEJ\ltFpugHWNo6t?ĸCmJMy  fSmj CWDgODl\Ǩc}r.$SvKc~޶@q#~q8븾I :muLO EM#,,+b2 '/t]/GhuIĘRe =<@XyE5Za4Czskit#e!w{2iL7j 6;";h,iu}<zO?*i V?CdЊc%G䭝 ~H-Ofnx4ӕWj2 B-E9vfG o$.v<<ԉƔ@hX}5G\y%g:;iY͂ '$N[5 j#[DODvDDeVW*`j5ch^8M:TT#3 wgXN H@^TՁ;Ms)Ț[bdܢ~bG(֣8Ti#c4c$v:8oN%}&AC@!Y 3W)& 5KΎřJmO Hf,$rFR@_HyH[<$(VNJuģJY#F(, =wG\z;6j^8yY4Nsg2}k%2ZgԴD*Y"=|h%£:ͺu>@L|"\u?~qL?M+Jn3vvN݋1XpxGP7PJ!3ޱLkIr*kESF?9"( +X'ŬzЄ:2u unǃMǬRBf{:{ 抷m=5kO6ԍ6HEѱ?"Ecvep}G7$ьSמ# m15MAIa 6P~.3(,ME Ѕ߂HJWz~ &] 1v" li[` ߤdwdftD3[Kt]VR:$Pf={Bǜ8FߏCYO3̵/`Y>A4`qe3/.PwCSHt,Sjϧ0mQ}qzm 8JSQDX D)1>Xj^ڶJx[KB^:v+^ZgL%.{Gzfз,A~L~cɟb/TS \QV/Y +=7cqneLJȆc»]Xt@*&X8f"iWHR%y!fC1u0r'+PGGj.v28U IiOmUCL3s\u$KI7Yf>jY~n +%Ƶ`Ze32 mH]ֱeeހL޵7ݴ\߾9(,7TФTwo@P l7r}X] \bh9CpU HbLɥ"׷ַ[!Di ϮҖ:T@[l=NLO=pa9Db3d_rq bŕKNCNj.1ua[K;SLt +db* C:Zu +Rw<-uE}K;1[J}9U5JꂶDʗsJʭ +J +>9]r{~6I'r) | +)WR@lu:TQP̴by(5vr,r$rB̺53hY[C奡T[CM %ɅW?vS9g)?$F,šzA D]k1AQ2Ox!qtYc=? bba}1'HрHoU(1[J`,~u)e#1%;(:1 ĴYyk8֚ + R%|C8m"1L8C.Yֵ $n^~n$_k$3{*֮ɴ>AC##:ʧyA/S<-i{8N9+0}V15$jY+s2FXATBȳx=DFQm +`51V=%='#+#ĵ$P/*|Q>@7ʫ,戃1>㫟WA;4.čKt3hW]-v0l+w)̟f&>4}n2HU`˶&eR68V[t*C8R:ZƼ +p5 UquBF.D,M˥Ϗ+ z';+]'o,>{Ibx&Eyʼn5f/F`W4$'R3CY@ Jk#֫GbR? [^S:>gdjrK?/Y,jiͫVQ׫+2\ꨮRJyIbY_:<`(7887]>{e,|=Q"H\6?rT(~^>JcЌ?6:4 +s G.+(=.]frϮׄqe eS#33HξpUf('|:HL*Û:uՅ/zhM-u yu,R%\\cXk>;etF:lgoܧѾw5BSE=KV71}6NP,l 0%UrdH4dOO.{l//1) xl}V C'|HC`(n> 4WkN4m䌀*d2;e +fe|LnS]3': GSp|-ަ@2jlYE.'/uLA>n 6r)Y)/vr㘂Iʇ)\OL YuBM<ߦMަ(z671@j|1~Ŭ(]y)8{C}ש +9Ȝ-KplJK#GMX:f"ArN "s9eHyAVن3~Cto!!_.>#X [ {~ "pipGJ499WRT su!3ۅxz/T~ˎZ~dEEu!(mYՆO !V_kږ(gDTKB(jnkFѦj_ܗmNu < B^W.&%;PhQy̯6eƙO>*vaDLxJ Uu荻jp_̴µO!$-?/o?uk "Ñ4qI]3jU͌į< +c$3{z؄W5d{MEz ^+gJ8\nm ]y R1|zB.D%R#e-pMȶz\r$ +߬q(2zTrizijU AܢC|g"Ϫ1'`]YՅ>FEᄥys:bN`1}R՛M Ess`B7m79 ZA?/?aX/++%]N!bYmt_ZAL_thC9Tz$Wv/Az4ZGra4k 疰Lcڎ ) ËyiBVMd~(~ԉOyd2eZ;YJGJ8!,w$U$i)9(vقHHFE ͍!hk忁>|a 3¬N~jg$Z)ϯ~&Ʌ2c6vϝ9 +ډ$ʀ7ȓƚE׳,c7[R`5[<j{#(J/f7)$Zv&{έޘq.3x!ns05mR-Qd: .tʈ}%p)`\M/0>yR6D$zAms#D !{e=:#nI]ȂÉyc?r#lb~!@ŭ@?ܺ?Zo8; +.F_ }5)}!RU?[a=!nzCi;RCG>/xX?TbtD~ѹe6tDgZL6N5rh;? c{YqDRc@l'ZD jEX-#+]@":!9\#^?1J~,b41Qo襙":o-~jZD!NEtz]v:,7wtT# z~Z"³}܈x+b "1oD%#r򍈸{/DX J5݈UkcC,66_8ٰGDDHZDDD'"5!/@⥑q*7%><)܅ĨE? )WMs^7vSb8ߔbhEG=ߔw-1#؀7oEh&ET okG-.1* +QTs7v/mTTe"<($aM~A~(*{b§X ˳BsSUFnTAȍu;!B.7oCIy~*lKiu~i#Eoc8%g`|D^ӿ)rXN+Lwhk^6(մf:,Ec^J h(ϸ>n_hTuPƈ (U%R_GJ< mQЏ>+Pdq(c ,m5FBˀ/z,&KBYp~(7c mZ.ۮ&G ~Qb/UfSWGv -^ŪF0]JSG>)#^#),KN(!gfJ\l.dш0y#+kpfe+򤾁ֿkJkd:\x~dŢej[M4z]/2YvlGp1)(|XptDDcj8%.T{?"a9ɟP٠X<ֿ&yFp-s{DDxBi0Ѵu:Ye8:Z`C n#ṷ bk.sY۱!%AO4JĔjn8wO04浓&bJMK٘j-f˭^bz{{_ek{ӻE;y ,Xi2&S({` J͏0u9X"̸6Fh\]i2F)B-ʵ*ipT1C1F%k@+}QY;_4L/(.ˉ`sA+ +d0ΘJK0lt~v5iǜAJNszR6}Jf?zޙbL|uTr1U~xfBxyF%>3Rx% +ZhXq Zc{Q;+._C<@`$sK-Z\0ldxv)U!n({QmPFKdۗ)]R!be⻊Oy\R L1 ) M +H/7+a"4cM쥳G-&$I2M=)fZ(NIlH#p3;k:4(gݦz/Bv[J'RU_t'uV5ؒ~>nxk'M0X (}Ow6P&Ywʃ`ms%0t` ؝Nu})P"so,ŀ鿭(^{F +(J:Jߍ(ڛ9iBvfe:mh皆Bp2PT~x7EzͩSI|Zbb{lՏ-$ Ύa- r@H/ǫzQ #zY/~TL-Ӓ [C\V[N<&cj Y$)5}sbB{( Dr:T-n^A6K{U2O;q{F{eBk/wmx}{MzB}Sl{Fh&.:`pDfu_KW+x=~3#Yklݶk9㮳-{9 PzJ˸ͨ ]aiе> ]8YJ<ڽ̍ŽȖX'⑵Zx]5^7]X1u%|`+$`CWG[i |H<6 ]I1\Q>na[$⠙ׇ {kzG.A#>`!]fW%EsjBת ʑ +oPC * eHJgJn*찭=1bL,R1<ikwdj#Z6 +]" +:WFSnjWV{ +zx/6Mv fYؽ!qX33&ZXͣHb_,b2RW<qm7]hc^nkdҢZ2iuH8y~=\ÇwK-0#2+ Kmnޣ+5aBAp>X~a2_Ž^-mBm/3dlf VbnD[H2ȼzN\);Z9-9xlXިBm`a+$ U䪜.:2op@qq|5ЋUHMRv+}%څ$?BK@tW;v܊ |HLtVT +_ +f_]2)&7I +tY𽋀nb\}w3c"v">/\[ Euq*lQ" R_Ds샵ǪY8:糏iM[eG+$l,g~" P {z&i`:n|w Ļi6Zބd׼~"#ܤf'`fdpgr]5#-⬑y]:"sEFPrrn#V8fZxEhra)NJ1+zn&J>x|.6r`#Ddݔ<(zx""%뺝ٰVCQثxG"` +aզ./š.9ZkQEzQGCZ17ot*"6 sbjt&i8Χ_= +ZB"YӪRD}zi+`I5:7lY*n5FH%?L; +} +,bЙs'i͸vVNd*@y7j۷}3d(B} y$1=A tHDڦh|JƮp"t"_o*3 +o5-&n`'4TvFceaKMek>a,WC#-!C6,\BO~}>Hpjh 3 ǯOoA%P) +SU1BJ#ȆwFޜUHgjH[5l+e ĹRxZ~7!s6׃FͲG[kEs9\14LlN+CqH̱{k#hD`AݓDZJ ;A' 5JNrNAoGj.샾 +B3̤@Au'r7N=$X7 +s3XYsF03k6-G^gTLlK+dM.f'?1Oi`kr\igC[?(`! t$ }nĒf UDEo"b^j8/g5BKb>'Hq mnb}s7Ҋqx^ a*?j9;/kVȝ@ -h8EX&-t?d1nSvPGZKUdV1l Fl ۊpxܲ}jC>";:.1'k;܄ _ܜy*6cwDp 7GKij\^XL!0UQ kg} BQ}>]7%S)ge k%[^\GԍAlC)VMJ7%dYf:+v}dJ`\8ҹ_uY] }xv42CO 3^4uQn9Ob8SD?shwW><:8/#YvR'Aܺ{mU 氪c猈otV / 5*AoH̚:س|3Od6}ٚWDx5~FĄ`չWJʔqOOϹQ ;2iȷXVy9:ւ. +"w];G ƹ!zRz$M@+Qwm(ƮB22{zXC̍/TFȔ!ΔO3yШ%GBd'-+:Mђ9p=$"$& S!'1: +0LsEHR^Moy `ע_eʶUp+):ByMw(04>;;U3 q}@Rvd@j@~G8l+/mP&!.;u9N+keE7MvWq,|XC"i˫gjW镹dKPm/hc'-7$yl`g](z k,< ]=*eQqr$8j*S G!UZ󖮙d EY} AD} 4TvS1DZR㥑WUȓN`_]O3G zFtR"@O1lY4U*} ^`]~kއ;>zJ,$Sa8'/] ^Ulb[F VoO3<2i[e 3_s% ;F'vWBكCcSekAmмg˚{ ̤]u)+Ϙ"9iXfr%U?^xn@$扇C V3ܻ* Ԧ@T?Re-''M+ $5U~Zmr7`8]q0JCԻP*?@uK-H>EMii X.:rNl\Ұ2oU[TV"ۺT&ăIloT0ҵ)Sѧ *z,ZhP(^gmYuˬ "LDLqpuL IG()4ZC0xoGoQȀoߛx9Qt}+5bxBڬeI"xAJ6V4*z7Α4Ѭtٚ0 =Citg FB`f,`Ȣҭ ")^Z[kQ\buKfytX$̥Z6R6(p\rk-sjIf?9_H؉Sg^=4sWXсΜ"И>sBY0BBecQ:qwsJ:!eNyⶦʩѱMgꄸzPvNsؤƩ3ua[)fj^Ep(&Y j/q^JKkܙ1Ux()'ΝQ9s}ԣ,68wܩ^(tzGULv1ij$ Ȳ]mS: [c9/(+.K<>{vr Y22xqu08 ~/Q3S/Qk)~h?0Bοx NQwd'˲<&&B9({D8} PqقŪ1(@X GoDdZVd(;[Z}(_܈2EVeӍ x~Щf⪁m6Aj:wb ǛR?wqR+;c"lҺm)ƣyq/Ou1^t pL +? *$%-CmV,2xQXƇ?b*je}JmgWf~<-6eTpZ4$^mY^DY)9nĩ精rG}߮6)m0@~ĖyvnQk}n_MJ4<0XX==%*x }AeCont'vSj{4*]5%7F4CŻc֯+z֋d=9sd=f 7!j$=D2a$J>fY +!飨Iu^?h9/WC=P5BULט!=壳)|8#״CCi=yxC._Bëe0wZ7{[?39$Iy&kC=S^ܕZ)])y5~"`\!>)B`D:|Zeʝp\!YSpRGg%R:B1лݳ5*NW @c_qbW\^1n?E<7 +?@Q$w^*b5UxU~z>^qF@UUV_=*{^ Gm5j\Wi&2QDz2i.a C@}7(,ړˬ!Ka@*Y;ǢZE![ ے}+«Mb[5͏/o/|x?Fr6lp"큚2NDqO*]e&Ȇe&˨R}y{e5(Tcyܺ]f5Sٻg"̖x$vMk>i6̰_ѡ[jf˻]}>fPp+J*FT)WS*qp-dKM\[5$VKj} :>ewLqҋp2 "> 4Qzچlu~ks*ߛ5()f"w.3ųC[eQ5=eWR|l424MQfbi9Y i2(k.RCTi>tY{AAhZ2I;W ]J$M7Wn1[(6=[SqhkϻK+W7e֮8=ɖIăB`Okשkcae +%1l$ +Gצ,)FnHh3??%_Kɻ%/1?[]ؑ8l+^;S=aT.g6Jԥs,qk0l z;|{%Td,ݩ 61jW rVj/99XUuZQdz!)+Ҡ +jknÅ{#F !RR*",PJcZ89:4ț*jVi6kS&Ð>%2\ LᲑt.mFΘQ,apSM˚|#+y+ Պ*l.I}YTEĤBBz*,_j`ʾdZU |uѹ;W>A#E,o3`g#F,Nl'O&nH]3\IyHmDA#ae˰dD[HOݳfOQUeWM'bo6P3чuے٭MTۺVY4y{x3*:9;92s2 +Kzbdz$*OιBA>:$"ʿ)s>sLlUE{cvnDZl)$SeK j DCA 2"TCهo\C<_ V8$QS}BD;$z1DS"C{KJJ҈ +&1D"K#k|]Q4'dzܰbOz)o$Z Nފ-D 9"q(P@H\x"[%>`D^U<䔉Ş)sC[A]j1$J%W"LD<1ļrB'"^uԉ@:Jщ'uNT$M% '1]׹VБ)ZtjMm!CӨ_L)RZbX-8b}^Or/4:׿ ?9n>m"PaY0Op{tXt՝ UڗeQMHrh\1%w]G~Q(*қ#V7+vze?*~bqCYo.k9K덳<*G'{K ,6w-W\0 M>%=|}^h.1Gg}AN°ϐR2QZ2$5nהpSTR TnhYJ+?bucP5A7ckk +yȰeiFDP:1&N gwd8ӝ5ĩu[k¯jS1K +O,֋YGDlS&@F^}ҁ~g^y ՝R^yZep~ +S%`Ēo,Y ,݋#HU0: ↲ƺ\x\.o( +"ZRn Ne(ҚsôD@eo=5EX;O缏CLO&:WLFKMbra_gd[6/sT=l2JkV]DFnPd,d4xͨjtnE#_LvE87KARtHJTdܘΗlzT:t"cxPm_juc:ݢ:WmlG XcVn R,1܆H84aaVZXR0 {gL;Vu]H3>GdFHԢ!XZIyŔ*Ҡ| I@e>_lXj~ynRD R29J~LD^"u{gxlt:'/҉Nd;BŜucgn4| i5pa`.0DGBB!),ZU E +4VMպBֲvF7쵗,Uժo/)g NR = WS/QΠY 9{u̽fʑH HH<|UEE:vUN$HJp,q-JJvQltL^VզB@)(2TQPa)Os%mxV Lj<_t3b}qY'@d'NG38Ʋ:Ljz}[`$QYfhڦi5N=.ByfK3Xoݝ^qZ2-9gGǃ .e`ҭK$Gל7Re[h8Td VDcl=ɞUl]cJaxF'WG6fV {X6_ 2 ?Ւe¶+oڻOpٕdj\D:bZsP]o?Em2MvfC*o5EY ϰ6ń]~;v;i;̶S:li+8>݂|ᰶXډ a-A*0[Qu'/ɛ/z;g]^ <:@dFpBgK칓V[iX{Wr#U:>`ֈx+SǺ)$F1v L&{UgAfPe׈`l%8a%u+[!mAY)oaͱO>~.q_h jt係4}ksOVdu+a9+̼EϽ (_+ +&M"1wE CW(R; AEKsj}!gv!dBǿ.FFt[BONoL% VZl.pK{ +\.g3yzoaԗd*{Yޜ[0%Rզ~AoUNJȣ ҆ޕm[3 zߴRNQ;ARb!b,Ѻx'r+5z^o#{1(yԾSoY?:geE֠4'VS8 "Jh + \45a%1QWQC2>a=@B]'ypSݓԄ/r)\=5,,y=!GR1qr^/]9?gZRR +6hMsgEWL%nv?;alfhN|)R:gB7@jmVE,[;f[L-4ku-beFI10X}wTB/b5ZGBU2GJF3Oo7u2mOqltnsa|eSٮHuhJR%ɗ[8{|/WD&vX9W0ggg&[:thR̈CCLjx[R)A:ҭkC4Չ)w^U=J_6I0P#kBEpNVfv,e;,: +>! Tjtbkv֠oVo! A- Aw`e;rƃuM]e=㡬;l +W=4JHe +endstream endobj 417 0 obj <>stream +HYsoj4Aط(I+J+ ,֮sc +XUߘsEձL6o,djPw=(M'^ !.NlqLȫ%KGS\P){ +y0[s耵mf؝MyX1d(JGxeijoehVU#Hx/N<9V([`zO >jڭVoyU5LMVK|RyrT7j)<9UÖ̓{5OI #Oyr߭枖̓jcGkQ-'jFe(Ә$!cxfL9UX.GB5[kyW˞䳸t¼zO Ȝ9ogJX:Iv''d03%hl8?*Av˺ڱՔF٘:qEZ%)KT$}" Úk0X*Dd;Jhn'.O r[5w`z[5ONa#pOyrU^u#F֖^>V 0ON:OMVKUͣ>/#>~.ئo\TzOpH܋ضhf(~Ʃ]IG$jU5TMCrb}ǷCWf^Ui¡v"#.c5iLO_Vɣl9,jUUʮʋ8r\icJxiN~>=Bk_nD-ɤՏOG( cFu(P۩V݀Qw{(ā)`# >+u"*:eu@g#L`q>lʻQ̔:K}.6xjL\'@Mf(̟4E-b#~Rsh/%ő% +? #\C B-ܝޞsqʬj,&\-kSǙ6('OڈttH7#a2M\7RVp|h|58 ,|*v PiϋroTc^{{<6ocM\^\-x`YoPU{4s˚gݧM5QQ=d8?K)^XY*yIX +ow 6˷O y>jc4tePSfwE]5iekP/ف69Fss>&#})IF\0'_S oi^8`$]1 ][;D rNRc{50mpg#HwyǬQت?zQWbq;֣nHhq|W9nJ`07OƆ/p}',;d[zCa A9zkMWQ8+7М[YxpT!s)RR+'E ;4k8#՛̛?|;B.ƥwjޙ*{)5Tn,h*VZ$X<[2Ҷʤ5r Hǃntj jWbrzGy+׎`J'{/=]c|Fs&+)|d.R&jTM$MIm +M\'7=Q ~`wH@|[#>|^̡Lo(ك^rQ; *,(E}ѡ<=#M {k2K#6$T 9l~5?9K Fv3H߶i3N;m&MpyYaQ]cXnR" l A +Qe`8Wt{A&Wm~v2OE\CadSDסrXDXj_og N]Ihxn3ެdSO0g+6߿zƣV)`=A +OB܂/t}Kbv[Aݭecxڍ/ZZDV tOQ>θ,;83n6\͗d3_7η^K-foq/'` |:_ @FG@X,/RtL< vHY5-r <.\Z pKF⬥o " t܂ &i}m .eXW[[f } VR/Pwv:5/JgD{x JSa̮rωEHu<"OV9O/]G⒴y㌷z"n>h8LMN3|d:W̼2d.S}[$5E8OPH.ź6y YH[H 𠖠 HbGl+{M< ljmyKsB{mnVuirjs4zFQۢ-*BձÑIap̛՚0YP"qA wUVd5=zVff&wy(Ϗ}nh'>^cuL,so7:@>>O6݋5`z|Z3 Pa ^b~E>8)T5HGHxLߢʽ<) 2cׁe +ZWF&z3È㽍6$ƺG`$Mx;hgT{dhu/V%V{4zf l5b'1Tk*W +HE eGhð |*'5bkZ:KlAm1ryo5;E"?W㪪 +y+`S|4P1;*ZK &Q8ch/-n9M_ FH2uZBʳOW5)%j!}nJE :AydOFALRnO.G/@z׊$|%XFSsz*zNhx[r$ʛ\L`ʷRSB3KnfvesͺIC|C/:U\eqymȼ*jX)1$  <6 X3Bۗ^oOu=ޅǽY DŽZp uoՑZ "tŬ⏄y5 2N͏*ߠ>L p*m˿3h8M +u ޞz^宆K])Z!!_J;3α |H0C @B,l@Adb8ԃ`Z`E.n&R,;DgF5 ޮYƅx9W"W$m#ގqBr(rlNdv8_mt052#SN6Hs0(o+3 +l+r4?`{IQ:bd2Jf*R*KrI%}6)p;ogx!n0sdX!ց6P㚔#}ʌl9djl#\oE]U"Ƚnb~3xyE܌e.*ԹѪf˽aNF+rԸ)vsi og,]6k}liY*ru..2z[,:r| +UN$H[4ō6s8 )3\H0xdW$)U=@=Oף̼b擲I)P_f?̜Ez:ަ1S7bA%W2;[:"vQbs>JM7(Op~WoYOp/~5iM/GWoFy +n-O7?yDw٬`>}uPH1տtD=\ؒ;x"kJl6mu(\8aa4|v .Z|jsb?le _j݅v eRHE3AmQ8/eE5^"ls$ +iS3wffEX,}b`yz)/Y~!on?Y#)OF[|(F6^xBZC^"Fݲgbm`^kr3s]w֦ + ûcΥ_T +R|'&HĨZçBCAn8(X_z} ra6c챱ͥԏ5 ⿈=(np/2mRrӴS6ޱzە)y͇=Q5nF,4Wxϥ +OL1B ԧ*FmXBYC4H|^m(`c _ +EZw^ gt'iP6824.j@g TQ_U2P'ܑнM.⶜LPU3ӁY{3_5j3-zS}8UVdSJ?z Z-9xN-3nFnyq`sA#]2.Q*wef0=5mJ Z|3V~o?$j[N@U-P,iWsDt2Q͆SfAq!OOCHdi%XݻG}YD4 z0 +VC¬sٔ3د1;t؝gN]CmZn@m R 9. 77H'=0LY5WM3CQ1zWugK;mzR:"ZʁH0!3f!ѱ=L=5H,j +5 +EŰXz#|g.v +Ȍ"QB&KyJYK_A񗞼Td1mXmEza>R;AXD[/d4<"~z6xWBn'T :K5b\:e:L_gp cNd%2E2)慞sBl'JsEn4tӱeu4лb+'O,b%`^ FLi ׋ Q~]:(Xvv +_ħF{.#i|nS<~I>;"l,X#|-]ᡀzن㔈P'mW6[Q+)S7p `b=I9֡l`6ytחblvP2W)iWv^<>a&RƠİ*QenRl8ʉ4`yu' O`dr+wr}R]יgA_\[o^ [s0p-Dz6%NQ5B1&6ɩӰ'GE'I$%B*4r SIЃ''yT7p;orjO;X3T,/T̟$'| YQ?=h fsw_Jj QNlJy>8XIж8Dno'cYP#?]#Qe$JM5wPuI}r 7OPl2OP ۊP`.J5-._=/u+e7A_ x<^z~wbi]6['=;.Il-jٷ vv&;1ۊo RJ'i ʓ$A|pgk] *kj}PgwW6]R$Wb@rN0Pa/]Rf)6X@7,P]ɪsg,c6M.l,?:ū|pv|Q +@ j"? Ƹ p xzfDz^>]s꥝ՉۛFj ~:24/{&4Sxu~ +$^vcz +퇇Gzb|6нWp]kT N`^p/oХ]M2dW5$rr2#HJ nl~7;")5*wq\qmܹ}ói@vkɷ _ k三<+m%e52rbyk*ϜqX=aݯFaQzy˪w +BuW˴wkBZA&밼NiVP1Zb'?0%55jkpfM+uPςv6eX`#rh3+rMbg +Q其_ +'8jLǮrcpj8YxwpotREŢ+ש/ BϠ <x:k8&ꔽ'{v$wpGj +ƭtu:89jC]9KL+pr s.}&\5ouғ}?e9~`c ˢxqu#+opӛU=&L7g# +WCR#l_~[<1Yw5 trN2r*vđɴG>+")jS/'5~)a=LԮ9[e81?̎;-W&+$CҔ(I23*-X'`φ(=h˼ Ғs7Eg!4ʪJEܢn.<ƨq)˳d, &<#wm<*ZSU[lHv +<) +zyz%BI{9ZG?v[JE=^ZZE>LUx]h$HhrO n)d>2: R駚=C +uHu{Bd搠=uZcTgk-(%S9.ĺ7\ +Ï"q&x|AbvA arQg1|(sU`΁hMm̩nq.Xhb_i'-ݥ'xcp+ܳjOvs}P _*2nYs{ Zʶ~z6s+eJaI "Usڪq *}0/wŋND /s-eXTΘPݸ% *6}g)h~7D LP*Fy|D'ZslS =; Li~MuUk~(t;O lGe`K‚՚**ׅR=]"c;&5}bWd*5*?kzI]c݄-]+k˔XTb$lFJcw=~L3mLZޜ!KT +333fM"Y%;a؀1h~K^w/z󚓎UD3cQ67 r^6Mܖ3%,Z#N*jhٸLW9 1LWÿA)lVl]wN2L!Y-(r)Պnc0?s85F*egboDỪAx6cz${/>@} Cu<ʠ0 6'RD>hD:@=b4v Q3e$/tHM垫G3S,$c65?{6P7:Uc|b߻|u6p FZ 1P>uY`fV@хؒA9"$Q; y:4_Kԫ +_ 78cᾶimq1i#SA,Eج 0uPM]9,s%hjmѨŖ.^|M8:7N"5v?PeTK+` ıy + *^prz-Z*^BM>ϹR\uNu,8~8Ww M ,.` 1ݘ6lSC4aJ(DV|-dH;{q[XyI#w3t&Af})vK6%0[XLb@ѭ(hĞٙ:2 +=4(&l| *ֻb~@񧄟HجZN{,,5[s,ӈfOYm +…5FKsc%x̏ n1嵤,k1 I*%HTvjݭ=x4Xnf^_@WgU +ϓ"Q~rѦ|RCd~s/C'z<⒪e:_s؂x4pxqwQRs;Q U0:Q*3sɛY;qIJɔ,FiGƯ\.[e}ng'a\tMo14Y?Pρ*>;< ViyM,⺯@XHn ]ȄORd^G[NWqǘ8jS9;.X){_|v (??{sd֔P4d9{`q2֦H8jn sW3@:Lc]ouhcϔn; c\RunGFk{ːb'^eYOYęh*_K\t?;O;:|+T+ }=Vt( '-FGu5T]]?0SuZ\NLlJ Ź}@$NFO)Keij}0j; efrCjHn`Ǔ|:=CRАBNR+p~Ȩ[_g[~~+P*/ZO|]A')|V"​P#&&n[4#k|8j:i+_Gnq+)csXT7GaUX9ykUD]'ȓS,Tr~F 柆T .k\9ZߥɊݝ!B$g[˂vGI.0.Z%jSr0ﵑz_/A(&u<^Q\;?O=3 ~ kVw\*7)X7vYXB>+5AX,쳽'`fPI~TC no LV/ySq^ ,ɝY4F%!k%)/4j'e]_{kyeAZ"IWʞLvc/ش\FD5Lp 忑hYc?؎픵Y_F&Z= +ީȵH  GdaN eA\QeaCS bԈDw$/@3kE{*ʥQ8\GGPQqFQ=ޮ++Htɉ%Gal2O.P'k<¿T-ߞ? )MK@ jbn_& ]C{) ㍽/0TT#$tZXݒT ]߄{*^8 mr)@1?i$> +{X)"Æu {(Ld\t$JYDD!\zXH/C*B>hurj˚YWb`AX>AQ]O_V!:F`0^!g|gC4rM3 o`}p6~\/tWzt +~ۂ-)lOIS}JaX8"fC_A>^y?A g5Q ͗Htqs!ͱUuX'ye662&5@=^i-ۄ +F<{0hj`{$+@:o0wXEDGâS Lk˞䖁/PkwWƉz+P@hJ ` pmD^&&XQSi5aD*o[D?ȒlyIp5U{n7^mNYg<դ΅Sa?(2뇧RV+i[ק8)/[L!Hq.`fve^WEϦ.t 5u3b֪*=Ƥ0tSKɘpKwq/ӓ6(ePy>~a>wIZM2AݺvizeR@ZŌvؾ"< ړ)jGV7`/86@Q "T]IPQZ ~xh4p2DrT3@pVl{vYwd}PZӑw9ZYc 9ȴ==cL@f\Sȶ"_7SKB&wBݞZu7: kʱ?,\J2crv[1[+ f܁O`U 9edOaJ^H O]e/s^Tpv~(x7Vžov{4;k}>BU#Cu$(vmzrUToQQA_.EEDAAEtIg2o@朅l>{+յr /؋ {N*nNXCo(Jnu蚼Q&nCfs7K=gwZaP{")íONd\a,12s[[9P%6Z5V[+e%[1L "VCET`'WA#eoE ~j$N^Mw)}_EJ%plN? α +/lDޏt+ ֊AHB;"-O՞kvDV]^9%rM!)Cza<%k[o5xpmvzY7"R[qP׉Tg5zդDA\Y:R bL1^BOR)o4]~G3C.a_]|D0Wwۤr2oVg5Ԁ0Ӑ 鯢[\TKuE7hx<߶!O|&Y^In#w2c hzb85hcpd| 2v"hԯjppBPػ[@M/MFj6#́{t~~UV] Ͻ7G]SqI> Se¥YȐeЋOeX0%6*>󅋱_Lm.o>/d[#'R\^N@BQZ'Q}|.gHI_zyO9PZX]!|/H'N%`-gY5^Y4YtS<%<OEauxX%H9mPMHӼA{] 5[4 8 @źrNWQX>#\T,<ƕzc|8^vXpwvЯ${eo~k`O+X? +'OK~e!#$e:5(/mʚFIb'?\tU#myY6LI _}<&sxa^TVb+  0?"U,v,@U}^řͲ3~J*rgZAb)BOdw[AR0:<=x4VFEj/UMPBoTlzH7衟ys,?k]YC(qMM^w276LK2cp|s[zaxc:y&r9]uy[ \v*.L58[7JSX*{ F +hdVQ>4YVUeB t )! Y\"*IO_g舐FS׏+b>p +{cP˻މ>}%FwA" TAHz&eIZ$Q^ܼQ~bG'UdxCLj;_yqNA}{馷sf[=t 2uo3%WN>(//XGO~˗/}E_bGhe>m 4uPsrb4oo!@lnpik{%RxONOYeb<ѻ6ޏ `q'`Cؼ/c5gZ'|`s"8\`UߗWi{P%z +N槭zJ{vJCy8)/l!s;.ƃqnypU*UB#E5ɚZO7F#v'Ҿu@7xp3t5Ѐ|_pIU;<冘 U71''^ٳ'``O> v!XN/I`6yqb/nQ Dv` !*H~q&_}u7F[$lX>,jŕhB\@'h#ǃ^_<G'vU?KύL  "Գ/Vw<WF {fU}Y cL?9L}3}B!P ' +&N5/_y=jг<^~b 0 b<7-j-g2Bv[stsb;%P-etJ$rQ#|3e +/M@㤐 *!zw]Y2TG}aOKp/d㖔J@i"@mƳ,5jWX]:5=ι;eOO*]}N/_sI/&;wp,Yˉ=n^goT<~:w1׆r~U#AA - ~ qa<}N@*NAߵH<3ʈP:?:Z깂bl[QX^$VUI)M, '@`uыFoz=~6֨slmE"9!tA~z՚괓xdμ_`?¾)y|2:9V.r5t+{'2XqFiԴ<4ά'iqJ0k !|EwTEO{٭ś"o~*$uj_`hs$&o+4l+dfA8*ispԞS@o{lzٴqux/]q1IeɚXjeMqqF$?P㬾.E l`-6|o4_I+E{P:ϳ ZP̓^ ꌸOBC퍝,LBx&hT_RgXi {E(H';d//lTZxxpO g/7ϟS0Q\Z <>]b..ByˬYkR69\*66L1!kG\.X~8 86o,8]N;fPxê:UG$`%Q~ܠo߆'YMxBFl-~ |~30&r*8 J c.ҡWURUeV/=XRZh ljĎfDJdˊm1@#'V#f<XOC9mCW)Jp Q# .*X1=1l~PHްmtK^󦰚KKXIJ-q[C?dC [~*Kh/7?^^"n&}}LHGb??ʠ&PU> + I.IB$|yJþY~˞Oov}uVϲfݷqq`Ὂ*frZܝ^/2tB2,դo &[KEn&<;9;ſպ/OQLޝ~1:V~=UYCbjA͏)Ʒh:>~vP8pkPZ\!eA̬-}"RٻQjjY +GL{=U1w `Q\2ڣFԊSù;p$G+@YSwRYuSA_Ǟ輙py=k _:}!槂f Qkp}7)эNC+@ݏrJ&@uzf +Σz ȣbou{o.jˮt'e ~iC;tUj*I}8SQ9LgRG J0&cmdU+:u4+"a"_zaIϦS +Pڀ"q#ya!u>,bhcQך.,ʑX[q).۸;ůqݸօx/\G7C|Ø@ ,lAU~V$؂b7ң_ޘKwĖn\=켃o{]@"ҵydu!@0ӟ@R +9T{W^]"^j|{/a&m U6TFZ4}:W+cNR +aCUTit=d"b[W_֏ȅAxo pgz +&?si\#c`l?|HWy"jt eIr-J}Xm8`q~0#V8SaDm|ڍSS?_pOB7O&"wxGA +۰[Ǔa%3S*®*76MlÐ.Nx9sI:> ~*wBdE#*t/~ԙ;D[n lFf""uw{^'h*taZP#d"Zz?vN}P' +1/VϊXf (JUї˘,dLc w?}qJNL5p [|b {] `9禭ΩtSi p-Scל}X/LS]9>kYi]F?v'?cd=alf.0bڹgCŪUC:H5vVßfEKؽ8ۦW߮t.LlI/YX #pޖpmu56( '_;MXfXi j^q ߖ*)ڜ@`ߚՕyfk:ע_LlH wu$#I`506nC +M K5O tXWGX1Xmvb02?iNތYy|MnK ɲ./ڪSy5]+O3.&l]ݹ 1ʈ ۪D ',E!<YXV^ +p/EӦÎAm,;~އ͖ +u=–,մLuM NoNx{QHUitI!^.4+^;/N#ZG?`%/#}[Gsp_üWDn1,؅2U/ƬfP` +n?kSw:^K7 ŎWGM +50P/`~Y|FYŕ6߿\ތp2Zp9D;] +?; Q*ZiPiB*?{$7$Y-x*0>P7{v#b=בC> >L+  ۢp^~"FM(:g]Sj[[!`fBJH;y;/j:D2 +C䰄K)Y 6ZjzB{ reє:)a8XXh ;X=UԃiiM򰬡MX# n]Q$S J4CQ_d!xh0-J.',|wBS_^^۪*KE sPPr {u^fx&ES-Zjc Dr%eܯgP?AY_E?o"=wUnf2NpTGdQ^bGsˆ8=c ni9} >T89h> : +}; ;W7Nj4ݐ|ZwĞ]8ٽ.q0LDrC5dGN'Np/UB?Kt2*&OPKȰhD@pd]r`;tju9-B;wE$BRrF|q=]atrAzͪA?/aYq_c?4TòV( p^CU GVc:PVzJUٌ)č$5,jݯ0J ݐ9V'aq=ϒ/ ƾbRj Zu]|`Uc xȠTD>x84#*nNG\tYz>br0diץO^t7ɺtSӱ[ہ3Pma +jX.u#+>4aGQ' o[/%b%G$ln=PYӣfݧaemJ2 LU`CXhzg-+P55$4nZn{/}u=|l{+w]+?[)yM>Oؾكj 1V4'0saz #2(v_ HPB{PztϊaLۋlr0^hW9{&nG1#SSWIGRRM|#pV([iai̧Y5atO4m -H⽦ Hag@ry$ ~F]PKI.zkVTچG{'Q^-[4f^ULt#f}3ɹ;(0ɬJs7K\%a +or%F\HfwLRsMICK`Ks-j%-Fu5{V +SNW]H~vz'S!Y/;~KyO lJyMzR۪=6bblQ3D,,:ߔ;@ ~@ķ#|2g"X{K務3<@Ub|3½A18[]Jv{`KS=k>?cuBz١1F +Y{RHp)]9Upkb +cngWўpʂRA2PQ.A^޽Pӌ +|`Ȭ^hUDt28vg~N~H!W:]ݫ{~qs{ AIJyvX:v'}ۭO/,AhwܾO8G6fRCQ\ a{~hΌ;+n&þ2K7)KB)סDrS7PS69:s^CK=rZ㻱!v<*|}J@zs\M +ͮ08S\Y_ގV=8 s44Qz׳?*O@(]_u^zU~٘ze=SUzG=Kq +yUFsڅm90]\N^^M2e,./bƑCxy- +&QY0`ŰUT$GAA@L`dྙS5G߬^#<·g{=x_#,L|dt$U'@ +[3jM ++AkYqj.fdoqe',\U[+>ͱ^$MgʖY5 r(.̄BI +Ͷ xخ~vj8j[W- 5!M4cաŦѷtUᣞ̡! hsҤ9hSWʝC5v _xz;+;#4">*i6d9tN@bw q܂3?IӦHvRcP^aYdg zH|ʇ]FU38 W( .9fҭDe >= |v9jٵg=)UमA;垽Yo| +?Kum*v"ṭ +<t(%@}uv}Wk(cCVŴEjr jY[ڊ#ٞo2Ԧ 1He9jtv턕 88I]*pQo|2YCu:Pt2W( d.]uTk3]|v/&"g{g)2SvJJ>%&#;q^oYc"XVfC.WmT?qQE˴i\+=8GfIC6XuM)S/$hQB/PRD> ?2m7֟(ٕڹu k!kR0*-shg(Y[F&PTi +:XF 2%B/I $% Lwd)བྷ2E+e.Dj.Q|[XilŔX|{獖wxkj2y5I|(OD kGJ>2!rTw9lȠ-MN-mzS_*+~9t!C=5W>zfS^!]c0]cAAip=žÙq0X`M}.T$_2ߌ*xЭTSN°Kb׃U2UZ:re? Վ%?b_`Kƍ=uJc ec̔А)4)ECzaޤL_ځ-1k7#t^< (y2ڏL1ڮpYW#~*'<.bl1Rt eej> %#k+<) E2 ,l xQn}5joI9]8w9ӴtnY6X[X;8N'%6ya]B?pۑk}`?/9`u#EXԁQnɬX%$ǂӨnX ]hN +>2) -! gjvI{x/9QS $22 熍꾫. iCb*HT_/eÚslԱzlj߾3^3+t\,l [jkE2E.jp4p Gw8=0u4eIד]^dk!H+G /&E% +KeK-K- APe{#:>/K.V&/{?Ez-EM4)-$ۙ:a#jw%M;JzSe~?2/u8P, UFQ| +0x_^NO6] 0iBKreQٽ5jҀN7`%`,$-'{kǓ _]bO(@ /pRM&  VsphEW%_~~c{YN7RNJ5cL]9J|l&SSHYeb2{ +Qv (ވnT8c!n) \: +v4nbW%{1,ɘeՉ9r}; (iWeL\6:r4b u1TVܟ'45d plx?BputE'P*h`6lγǤeL>^Ex}}%!7DZ^1PTİݙɬ/lJƋ[3  *˴NsW/~6SHȬQ ')?O$T]"E-iU+Nm?ySX_b?,_`jV8]2? +N,t;`T Q +|ϬIX튭D|8dug6ʟ)귥KzNai!0j+}qZ}0=iװ4B3JQ -QW*j|N4ٹR|PivR;`3oƨ/Y^x݇wQtnOcmYmW2-U" gtl|ن +kt3 k֏VͣqhX}7lku潃P{ç#G:)ܻ^ -FzpE*"- @|iݘ_/X$]GJ^2c7ټ5k}V:R]l!ڳrhk?>꟬y(i2pXOplN"Y,ez_{ZGq3avQɬKKs9kMkS_}Ÿ38yxAk˧r2 CӭsbNt;y[\_ߛ;("Hi?<}N<ǼΝeB{ӷ]xi6 zh/Wg:&m'z/U|3~a?WZrLEQQ bΊryvjv^hq qq(n7F[@ ecx(d`MstڌԳUc6\ +N[Q,I83з0>:lxA'iEPHox ;*&9.&݈cG!N ֪}^Ϗ.^KÃm/,\/LMPB\A`=٠ 3j=gts_"7i&Q +f#mڹpeZյZ[#ձW~}C%e{Zck3͂!uBC@cb⮁쳡?qerdG}-3:<뜶KߕS'}%)GHDx5bI.l,ϹaLբWsՃՃzz:ljy8݊5&CtnH=,}fQ GO^k~tE58̅SgATV;*(>(=HeT_e}tQ2fs0>\񻧙jiTF&i./W9@Va?Lt Bk>UR~펅 +п"Lkk{'y`)[RunWɗ<)n)Լ fZHcr_7ƘvqA_ Q +UCu +sqB9{9 NTo#%mFi>7GOf[YYй\UcV:5AXy&RnҤ~]T+ +l7~g~݃?bS:O1ȪVkW3=e$F0cg9 &S]nsߑh2`Ciu1 5!0ߥ;Oos ~-@!|HmK2p)`(wׯSYqPyS 5 ;z!v#^l=P_@>w}3wk?R?:"~Bs L49\3Y+' ?tAyd`NU(+j*9u5YY5j\ /MvԞ)P!& } +qoJk3mE屮 y>77 +[&J[^%h>TyvgZw8 5M6Bi>S5i]6)@ ?2 x@(3[{uH8+iz3m5o&^cIӘj׼eKQhh՛_T#ӔuP0&re rI1u@Xnt`]M(Z I69q]t߽6!oF9ZRR=6v)UÚFsV_妒 p@hOéN8g@' tJ]]pt`͔ɕ*ϒ.mBkxΉG)Bĩ-6am)[j!fkAcb;]A'0ޜ?5R@ 9L "0IlMO'%gY}7.ׂ_[Qeᑁxl"csG~ >4gj-=qJU:ܝ~Y|⩃JŶR"Ҋ EIοNKn}P?@4QyP']^=:U(?wa)-OY@v  +gܜϺk<ư6HzZpO[Bi[@Ke[~NRxC=f?>t._@- + \(ü H"j],1;Dqc{ |C b&@s%T2 7((sg2?ܼ{0g?sp|A'nc ?o6@ק0 i1\^G[M؄9Ƚ={FwG?^^Jr.1Ruyp ?s"S8pQeF0~ʛ;vr gT !d}C1/F}g~|b4yWk=~-u[ P,TɋvgWlTU(f_I4'|x"}ƨtf8D8/UWm80'n(e'w#,ijUcIz~pyqNhنLl٢R*vC2'c<뫉QYs(YثZaE?`!O^L-Yaqʴ3xY/QÄ@,d=+z6QnTuɡGݲureކ]1y4S|7i:sr'=cXѱEbuZEPG W%aK*_⭀C3ANי\>N,f!Gbr+:%*9Ռ2,gyV<&fXXa0"}C`?Ҳܯ"#i؀RqZPVh$~mX=} TrC8ܗο)9G䳾ެFئfF æ2dFP4ЀTJS\]O7oXKCUdLK^i 6vUTX"9r'?ЏMOf3P^z$wTAEo" '.b8KcۚiLS 麷t[oBH]HGUdwNA ,eiB#">Y'IKyϒ}=~Tͦ{YHS#gFis&%c[{0]}S5:|ꭕT2j*ߗ%9CH1$.VG'P5IΠe~;a)::(Zl3[z +lJi_kc="v >~Uj?dɀs$\W}lĩ w  #YJ.R2qK+%`ʏKݲ8crekVo]zAZYN]g45Xb-jJ',-'dd@ +z6?t~ Z'QMOı*6,e| ^֞5د*Wӭ<+y)' +JztJV*VB* BEU_rb RP# $R-ּc¹tMkg~$61w#zm'+>W3'P$-@ 7Ag\x5zGy͗DpgoA5' +W2jU6Xcb雔ՍIVa2wRŌC `_YmqSfA?2ncH@#>x1ܨKi npGGI25 E\[FӶڏ{c>$/&V-Y:{|;U'+#?m:prn/@NoZ_m{ :T^K=k(WX GӾ?ٗTܯw2{ 9T0ibER;n͆83l>aB=ݮXkX:EL"fe LYq:Ͽs=O|L?  +endstream endobj 418 0 obj <>stream +HtW9r$'b' stcc5%yP$$`]OSDŽD}U01`Kd &`"~x?yCJDT8e^6`q4q K߲NgFbY| +cF z'%¢6Ҏ +iu3Dǵ|aJs^'暖ǠR! Dd}$)ءC|~#|fHU0<7ʠgPh 4SQXO`UsYcX + +qxv%uf +YPBHdXgdiǁ2OeT-s1ҵS1:^GnW]'rk? ( > dpVK -odl3GIJMS~DXKm7Q ):%,dN0u2t;{x`!8@/:r>mt}  *p$4YA+;?+߀Ay<1UQTsQ]4J㪨⢴ѱGzۣHkM"!ZBUH=;WUHO(~s-z2ez/~3AΩsO Dt+G>ʍ9E}9x1ePVĔkѢ.~٭3qY9`/0ٹ>2ıg}UB0OcL0H:YĊz + +isdb+ po ձ$?w;U*;<;ʗ0bU'[ҳA;rHjkm92PqF>NUY_b~2K5F&H +myх#̽ +Ͱ|AU^~µWV[D:Fq8kBЃLamC(E-4Ƒg:ap7P3íۯG{%,mfB0 #w$WA`X'6MAs.fUMz{ |d&+{DP}EnF+3+9vХ\͝ՠPM7 ꁓCt%% qߝZ49卜<C]*xPC/fCO< zܗB?mOYmje%f~x,x_]ෳ^[a$6)W NGB + c-|˫]^32d|5ۉ.J͒ssUKEi4s"5J{M{%oGeiqc+*q8\?wh/sU]zjO;tMFkj9wphOLhFGҌYvw#[:<@ v,቗w{GK~iS7OiW(eycCTyh˓dg _9t+ ‡p8J93ÛNm1^U}(?yNs L^ kvz+Z8㲚 >ibŜ#g0p qGWF $cqRk}g֘9!Yad{"k8֤tǽ"r)3Xi~z;Ƌ9ic[ۇzv[!sTkŷ|HCH=HνX CA&f Bg ذt/ֱZpv1VQ޶b(dHCՆ(Z7|RcǗqi{U@st4Zj2%7Vx~ʚL0yquzLTDJ7$gx!x̢>N9 -$џ`6:'Jbv(EI(0P5xY sZVEyLh*vejJP!4薏ݒeWIݻ +Ux!avaH F!Vf;4 W|lH5nl Tþ6*{4+y +iƿ|F\@^m's+%೚A0jd@Mο;AbOX;wK"3͘S?Rc|(h9żesp>Y#=rjNwb)$IW !݆V +(n"}qM!e߆}I)$-ߜRhR@I"0-" 53$+ʹmJSoJ䐥<6#.2RvZ*OݚU_`Զҩuy>M|q>VVZA寀v=@M&g2%SY=< 1QP) 吒K76 kuPj UϿqQ8މ2xb5A cύm?S#HoIRHu̧S7lnjU7hzʡy5  ~:vT"W +  }cqa$",(= +Ts~b[S StsG^ zF?ϵyq.s6:bf @r@Q0\xq,%,X%+OC4)=ڂ88`ޕ-Cw3oq̈hʘO y +u2Ei"dmSU*iX|~ hw5UGtHB +F<e2WsHA|Yߢ/2a2e 0}n6i+c\o-)I=*RwbLԄ[FQ߇4y rhk_ N3C1UQR}P.v/T QtC bu49=s4@5Ω!G'q>O㯏E_`%N ~{<* c|zs;}v濜}BN78c.b 88-b'R*`mJ@ XlGS$8 9PfB&Eu#2Ge8F( T@ZA=ڰ<OcIvo:#f`qyÐjswԻW\^>hW4Swa!($!`vZ d<}gPTq RxqIw߿Oy%*ȉh"B$%FW#R 7l@ʥBi?ABsrSw6b˷YkD1JC:>ltljUVR2 quQ:Rp68WPF^:R*?)HMj P"'.ZA +yY +IA |w}-Ӱ_uRT\hB۝ip}rzMǮzpf2ak$jY!_ #ơ_Z6E6ky=SYU!.IT|"=Q`@n~)֜Hw]$Sz/,lDjx?uhݭ5U韪=>ѴdBF AŁDGkx 5f{c \3~+w\{ +ȣ/x2`ؖD?ڨPL_*rˍ6d4+ajc{Ywy0޺)> o]`zh +Q־F D&M(rQU2;]nlAdb""4Ul]4/}@ ) UH; SeLeJ>6x/$6b{7)y%3) TCtnz^B Q/A> r~u>RHnDDq ,uT%6,>r:2_pF]]=ae^H/nLgE%qKHn]Us-]"$0,H`QQ1I>$e&q5. AdUc͙tFka^ڷƼgA6eRS>z&neaZޏKy$Ţ]“-GJdyXϛL୅EGW|1W0g,+>n:ׄP#(5MLke.r)L5%@/y~G˃I] 63@M\@CT2vgʌm^[,gMȷ8Q܋4T1xظ5cB hK,9ļȨʽƦ*I1Mgm0RF{ hcƓ>ɳI@Vqq?(BǼ(0#2V-zu4^, +vÍ+__!俅S) {]A7 [f2諶+n-ڐt_+l @ ;F`YX# đKeK1*!pnۤF2<,`&cT,a"GSGPi>"дsT&\j͓tM)j$σXq D]: y3ͺZqӠG`Rwj|;@=^6 +EOV#& œ2HKe/60 #n1Iy#3|qckTx)ܽ6,ݤ8ޑk8e! `CWu9ƽlׯVűF׽bƸ\tkyT6/'g#ihƗTR}>o$02+J^=Q/n?+?6~[[u :<|ViGLRiVۯQsp7'Br6pmrL! h]A9:1=FF|r; \^m׹Q }uUߴ2h-R Z3fU_G:{Yo$(U2wqŲue6m'(a1UBb(ͮr0tOHq|Ox:_ߵ\е$ysv~+.~G J,ۼ蔐@|NR#o: g y|ڣewu2#LyńE1OU9'*5&q@ڴ{FPJ_C'BPYN/ Kԙr^3A..~=ḋW0ѸQtO,``ҧg0W %X&mA8Tx$"݉l/$GE@/dh&OT7jXʄWꥩU%~que ,4yT*A p|dQ)Yϵ2Nz L-Xb?$ڱ$qU>[[swPTטB K dgз"P]^24mm̎`-ԩ`}7rvRE9bj$7Z f1bFxۓ1m,6F(T1ǣZYmoGb<4 V8" pmjR XOEZ1X1Kt''}m _灘փIQ Vuu(m>P{PgxQ29Evgd?V̲^4Ƣ @ +ƥ^kOkb.l-.y`U_庆@k1 +dwMvHPא8k#0qk;omg'J9IP?ud[߿Jܭ翥 I71 vޤޅL̢xHהyU8ثZ":y l!FJ/0 Kվykg'kKhJOrTɠ2GRњmO˂ +K66C0k9\f'HӵqV$6|˸p-mpA6n'0I14Y[d\$oJ2؄!HPhlFt,?Ԙ^ +mxj^2rOċo9l}/);␖qF>[.g,^R,@vG~uJY1Ao D&,D2%sK.aiX󾗍2pTY3lt#ά6xx} mh@ M'ώEVŗ5V[gD C hlbƊ Hqu4TJx܈ G7祌c٭.eb=fUwOՍF(ͳ,=bWwb켚|^WLNʞ=WbUU +L +Fk`3pA d|x~%xEJ4KEPL4!E̐9˚dP%z]|2 `Z Ca@]=z0/0v頕^4>4pJzh!c_|/,x)5)B2cյ郹9%@8J! yq*uܘ=봐Ho(~I}':9L/.mfLJF9 0uƷ` t/~%"Ye)}%Jvu F {?S)'LD4,3쟠̵q + 1̛~>nk. W.'8exsK y o,6Uc~´]sC}Gyc`bJЅȉg#{Y +l\/µ"Xģn8,:jPrZM)L+ڽɮ4RJ]Yui6}β1t/Zu> XEj#gBumd铇Wův'7OXK糼1U Wd^kgc5 1fxEm.)0prB@tR}4(msI7u5wj,G0D}04 _ɣ?F]N9Y]4cGa%^Ҳh?OqgכwG>_P1<*=_TlW=G=hUPW.^73rQG3WrV\>ئzW1t(uX˸FyܥE'6 +'~<*=cǐ+ =Oww4~0!\վv+[dcTzUwY#JߋV(tbJȬS^hۨ7Ҵ@UD[H/lcQ[3wUbuҌW>~/B@ڪrՠ[ua*Ϟ V\)y(BW*mZ,ǒ0rZyfU#+=0hOZm7dD!q XSRYE#!c z?l/Ը&`Ռď6jzۛY0K|uV3R7yLk[HaD|.}nGV~\;>7qaht-NvMKE=CS!pM12ZdO:+(:Y^M®p ͦ|;vJhи{4ny,}"@|-ZU&ʪ&H.9+jyTֵ yۨ}̸6\;w% )utkp~{\=o:pqag|}<<%RSHV0 +9v͙j,6N˹S + +_% %XvybB5MNlÄl~tW=ܼAzXNɖH4)F6Wu癟Xc'를KM^yxF\Z_gZ@>8s9  W@HǙ IL{ ®'?a10ˆצ}ȑϙM<]Q`7KJLHSQ&"/bUJ`nM0Tȓ8H8h Mr5߉DTH FbiXb#g Ay{HufVDSjEsjTWTx\YA\ CLexd0h;S_!v*LceSTdtio9[BkeS*]!ҏkCEŁmMO!SآyT'%"^_ʌw&Hoܻ#zar1쨩'}]ƫg +,a~'2{ $E.Y[EwI}$g)28 +FQqR%3drQ5B0|5oz'i꾄Ÿ Y+'hܕu?o S=0mٺbhx_FEmpWd}E:bW},&Hp ]Fǁ; xtҫb桳F{*M#T&:NkW}ִ!ܩ!YiƭŠb;UWf{S~0عhG¢LoqIevwig櫫0Zm^&,Ks8bg']&SK. +EH!.[VM)6}_Ґ2-irz= #JfxF}:>Nr44&vpL1HZӆQvoED|2O-kқ w74ـv6 nQh u2ho"ȯE#LV]Z6E&8=l:JK' sW;[rk\TKf{v oǫs׭Py^?.mYS#O2 "jSͳ0UjJŎsNuXr5!TITtf<>$O:4Bg pm/!=@~!5j9N3T{Sҹ\BBkZv|ʉ,ATN@cΙ)@^܊A5,pNɘʪY䓺z~=ңLj:.eK#" # + 2uQ`P;4Z5N%?>"*OˇBr %pb86BV ʳL1[(oI ʐ \XՀ"O>p7' L +z$| %G{8Рx(3.o٫=@J{e@-Ӷ3?I\('dHdγ=9V~DR2\a?ica?y18~V>3XUͧ.2f=2GysxOz>֓Д6R{>=oxKNʬTM=ْ$ 9Ԥ<#6nLaI~>>󟾙֡Ow#ɕ@"ט $mEݖL,K,/½k]Sa=vGQ{̋N$u+،}ѱmѢ8`2-' %Ɩ;,$+b@,-MQ40Cj`j/Z(W!ve:xjB:zY@(>߼XoƆoO9Ƅ9 .MK@~j6Ata60gO$D:n+a$9Hf161Pt/?<˧DžVbt^w-3 *,k͞ 6wYGnOG;c]:x\:2F+ `EdB["`_ nmopB :>3!cܬ݋)Ӄg *%53,:90+>-I(|F, hY2R|9mL?00ϹQzy>@CGKS67Cr fH29LAr:^Grg^;$[ eYBؘBJ2ul:-SH k5?O>v*pDK Z^J/̸#VGrȝ)B8jޘ7MӍ>B٬pL`OD3 +P3 H}]䈲*\tJ`*!\ +8vi, 6S_|svC-C3w= ]}}});Cv skiuך674g yDRvS"|`1w&A_Gۨ+9|Uf>)́#{jB$O۾30#%7 )ZsU0hv ͎oFJ&`٢3tI[ $O;321/s (E@FeYks9Z{hE jk#B@1,؍[ħ&'+['PDD`̤rPd1fHR4852ʇ9i]l-GQgq{}]ۇ\]ob7QvmG,} #i|zVϡ3D2{6!f|1{c7Xijqǹ-S)Ei`C"dݽ,N*ϡ?"}{ח^.\AXǷH2T&/HH_ dd|P6Ʋ;%\B"7<6~mktNaymk,nhFQ{͖nj5v:#uhE-Tl,zŚ/^HUL{6JʰV02Kln> +P$ .j 2(&9l:g(vPǭ(,H'ZX~}O`Bynd㻜[ 2Za!r)8졈5=:bbָ5 c% +:0v޼"x=:1'xEdT]@XbfȷyFxEI!0u_ǧPMeʭa I;u1I$CG(]y۠+ +Bu1<~+{_ cj@7 C&=v Ɛ`Zp -%;4v&ZӾ$5rjC{f7鮖JR@Ho5uڡv} ) +ъUaɌ_aˈE3TF@웞9Y 逆[Fd$Ǹ-B0+#.9E:E0P4v` @|,3%btd^ ^֨1$H ,gԪulwkX,^: QVJ )2{l +A$iKHҌ[ZBV(cb\yC>x瞋KXb? 4񧿊ž8zbC;ŧ[34.OڼH7X;ɠE>gZ|fϦ-ɧՖDYr`k̇!-@Hu.@ঈ/:jk; Fܲ9.iËO6z;zY3b6_0MpYCs1ǑKʧߝ5Y+ȱ?td\\WB|:lנӮ\<V` +!zc]$?0oٓ],ģy + ZC\ZXj>[ `E_oO^t{hE +{/;6E0Q0+c">+wiõGo0%q= #Ba%ػ]Ţ(Eݾ`h;O\;p`==3s> ,ɴ}}ﻰϒkB+ k"d_ +PzZa]}cdln5wv@go4ޓd6.q"8! b=8,❛tF:l+(/@RJlXt[VYFT~#--=ym{H"J9 EL2Jt7: =Y3T'j뭈7˽ߡ졣A@3q5}M . +l +G̜&EК3k&#x"i9@ +8rWX5ϣ8SsӒ"uV.9S4RGi7 ˔^~ze H}gYNp<F:,q+$J1̬F=h@cW~j.9k{zȐۜK6F< +՞B3|ī.#n I0F<Φ0Zn -%bTz-ZasCG xn){jʟ>9OLgAZ!D=8=;3Mnt^jM=dڅGq09 ;hu9ӨѨ mfOɳZC& HgaA>i5y=k;ZX+akJe"YL.8{:TVϳLc2(`iܬg߾CMAYRfp?`;HGbp,_KHD,Y[0X-G[8L٤+˓RlQscŇ~mA_Òy$f['3 emVs<1jtmU81c J&tK+ݢ5*B3β1$6ͽs\ՠ>&  @j̹`,zq<%g빠urmWE(ИϤӈOCa3o-EkV}-Vn#Wc^apai]8vE GwĕT7.˳ej==ogр&.`} qpeY1+ 9aGKUsL/0F69 +ts+0FZHiD) B|ԉ.!9Z闟WM&SNOR?t4\Yc'ڋa͊liޑr],HD!#l]ڎO]-Y(l+:qvӨO[ ~^F"/Q1C'u#agps֖W+ 1eӊ-[§X؂_oM8\]q~.kr.A@Ghl4H4YUC5⇳MF7 )<\(#0`Bqq1m?ߡOf ikݟ>}fr̅=\331]c7vP=>ygmוa>}bg]ĖK?4uaљw_ĖIkFCP +|_#-x-((vl0̵҅lZ, +fcoGۜ e1ԩO)!,k BLZLӊNhG8{2bqR=?)' \=@*kۭe R!?O s yWHq{ i4{{0uUZ9=@&Z)t,s ݎ][ht0Ә5!cH6q Ǟgh@xk6-1D~@9́0r/ I =̛>."ŶBP*#G(*Ey>V7:'xK[$o|/:X nدAYgM^}9[jde1Y{ $ױ>TI(TˀZa<(:֝,Zݎ])M$>Mb jnO$ow}_ed5wuf¤^E0g"Q+)HrذSv2- 2D5y KQrs ab$fmWޖlOU')z7yaPG;"ˠlKA:1+Q+XrJ`4+cQTm&ū1*û[[Deq#z3g}'RJ‡x\B\F˨ 옍;X۪ +G&J\*} , q_qu_+YE"ވԡMV'm 4t0䝗yʚ k܆X+8*=Z_f(̕qSZvZF i^9$3zn c,gj)kZ=9U6ȶmkޞi囬u; 2GRɋ2–IT̶} +U5q +l.mn ֪OY)ƴ>v-qx|v\<}tg1ID^ڇ]f jYST +i|d&7UL.X* 7K0(4Y͛4%ъy);7%#+>1X7!ѽr9\l Rj2Wu0H +I^Jշ" 6cwtW+pcVlcZnsgkK'O_S"_9.:zN\ǚkzyhI.܀l@v`핾TTɯ@Z2o'bMJ"hqlPsLpkjMz?^3VK Λ4.t3#޼70%Pauv ܚ:Al<,U/HDx-6ʐb\c ' Vރd>stream +HtMr%; +F/Wk>75+| +I$S%4郂0*'4w i1mAy˛A yO1}"< 郓v+{E'}d<@ƚ8/w#(&AP>tIsA O|+XK23VY5/.W2`y؀ A6˯טUn$/+Ou@eED!W&ZiebTd!֒ ;,1z1Ȕyj-'WD}_ +FBLg+/"KU`?/~7~펯ՠ?بILEx RKi3*ʜ3R"5L7 2*"˨ݙA%ClQ~oD6_/!jO>٥k Ba sk12~UUm^׋f`t-X{~<M?ExxÙU.(\e pSD;PMĈZCl2HޤIj@,˲nWe$}{1+ox瞈vh$e9-b-@M:%8BӨQ8} NLorlJ{(*THydΚvwstN:} ֋aτq +7=`8%#o6.BJ' Erlk85E'5ed VO^^Al3ө* 9%ob@aqECQڝg5y ;֘@sw\ً~GьP\X *_wd1K1bxBPJL}z^vev%*vs^L!ƢsXN{1E@ΨJ$NË턋D~A' AJ~}11rni}zl7X&eΪ;"/67oj\Ed I*=RAbm'0\{TBOwoXmBC3ַ?1Em |oY[Qw~{Gǿ~5OJ#{*:bjqU$lVkO-M~`L rF^JD"ӞPI8܋zPzV2aW}w]б|F%Aʟm$6<'?>T:1ڼxA578A!r#Jf_FG탓6n:aJ61g]%_{_^,BO.Dj Lِ#bɲ +. YaH„Ŗ9W H|>ƴ{g\Si{X۞u?Z8ZT0 WhW&1تIdD}Q]ed#DK&n^ҧB(>e"I%)>oKPgPy|Q]Hpcs78y,`ju6FND=js!Mc냑ʦVaF' =!G7Ɇ%uLr_J0ˬ"WLzoĬ6<# &t!\CY SkIwf=%**PxVX)#\YGo$-rA0rjH=dj{Kޣbg]z>ɻjIۥĮw_@clwB(\g-!6Ls0uňV ٪#a]L{C5,; +^v2:"lwKăVP\S*BϵCB1/6$nvsi 'U2#s}E /̫͞۾pO5) +GWsvJ< 6*X`L9}poTA}UUkrSfܴ_6jLq!^6y/x3,sLb[Lc7]VcN:4{xGcxV+r9y3P5l/Li3H;EhAh޸LL5b&Xb_ddS1Ģ t"]L*La->RY +w_DC>̹KD:Up2WCDC3mњs/g`DP R=>eVd { }ߥbZu)=l8DR|~8ߵ 9K`z\Y66f}uQ6_$^وa>KD|qb$w\g̸X!"z68szds5r[ Dϛ]aܮxq ͌Lz{3VVKO8=QrLK,^v]IdQAHD۰kkP%tW% elAaOu^V.<<@pkZh +ģ ) :CF̈D]k3(`Vaxqf9O]JBCl94o`jHf'{8xu#4 z~ܘe; +9ΖGAfBcQkE&ۅ&ijנIJqK~׏bQ%[vi-謐GAaǏ\DX-cJRWR2$!e)PwsW"" nxs4|/2愢Zs o'ȡ)ζS}mϬ]FH-ͭIk̩ٔX$tW,2}麜 er6qFaN(8-Пw98UAF ӾPjX~dV&ZT|`$OtGf?g&Ydvr _D@dq{SVʼ}Q0pFJa1 "^yϴ+"r+YFp>0FN7rcmd^s" SDkYGyS||ì^Y̺%8)H_hqjdQBŇro8x 1Zc.#=óͿEEUbh@x|}bVQs)~5V|}_</+4y*yc,UR֞ V&i6N H/g2ͦq (tz)`9o=RJ=jF'A9ƕi͗K6e^X$WO.%-K5wvA:SP_ʲT8Oֿ|HIaVyUn:eR\J{}ky 3" !+f̤Bi,]+ayi>80mlZ6s)6 V#x:Q 31jU64ѵ^CC'%sOHFOpe,;ZJ ?k{:5u[MVRLDȍW,kO)&X/7kK[+-.ct0{*f="ء!\ݺyF۴ s +ƨKL6޵<@<15>4Ig*Ś +Mt#]n_]LӟpiGe) SU;Vos2b^%ΝVR\H` `f5W9ԥ } 0R%}־$,[惡wE`(kΐ}O4byqTT0_a@fɆQj]qխ}g($6 +yfjxުߔ/!OxYs(z8%Y$dE\*u |؀lFfbfh0H ܞ7PrK +Ed;VTLszPz \c?R_S0c#M;m]W`koa1 bl_)sg넊Ģ8@`Sj)ǵW6G5޵Hhj >t &i}(T|pRo h'`1SXqAJ١:KݏKA\d)h))kSVi;IExy]O>YXQ_zZvX +H54gG Ġ ` 3{-B=@/˚(|"zj&^sWb4}ÁΫk40ce-FЕ/]kEUӀ(ʅ%yi[t[O;6)뭋(<덅 +6$LMC|U +OEQN}t2.VBۯ6ԞOs@EͱZ(yC|b_j +ro°: FP\쬸 XG@`~C HSOAxn˜5 %qTmY(CF61Pvd V]t/pƂy) +u='CiyAmancl؇Kdå.fZ Ȳ2U;!>"U׈f ",kG-Tϟt qkL/cĥRFlmb _T6a_9~2O:C |crvda},Z!MZ|]l0Ro@! H6fonI[X.Ɯhh';|f3 W^ނB[o-'mPEj՜26Ys+]J Z# i-O {JxiH uOb!vͰ(KRmmuC +"KvNF#uJh`+=5y$Ĵ-8kGdḬ̄)kXy{]kVmzq!L(G/ aIU836Heve_j9v^_' ]k;7팞_ LRb:Ǭ^-2NZ8Z2!lUgyⳫ?$dRs>I' ݲ5Vӑgܘ7`sj~ +T5w-kX(<;e1[r ŹX(W;Vw6CQɭ^=Da 8w3N}>{ kdگr0Ai@YtHDh x͠fԶMHğDm<3rS&T8bA|w(EAM̿Bg0O3A<_fKԷ5}&ҘrYŲPq>띂9D?U0^3c=.*f0E-,="c(!x8n7d +Xg݁Ȏy擜i/UW<|Fk)dkQ&Cۉנ%_)RE*PNYG\~f.%Wg/=ip _7/ DŽ*:P2:5pT0*4 PZ6[谛f5DuAGԞO:c:s]qlhVh7C,Em =pk"aOƕ/\F`ؐZyl(uDxE qހ違9FZbS_h|卍fH7 @O xސ}ڦ m`HVq U; ȫO:5L )zNj0. Mů~ |+i ~ W+ u)(rj-*9@d7ez }Bw\_Hqo#:Ø]SfSq?C"hѾ8L*imb"- +5Ø\$ղ׆αaԯӝe~f9ܾuT- ,n|Kvs2MEAǗLlƾՒi5qFQܨqg~jS}h >%Xc w4bJ]T#@Z/Ak'|Nƫ ezbMzn7$ZYDjUs[l 56$ uWx%{Џc+i+\4t3+ˊG6/]~+bkC̻kEsZ(7f;hvr۪pYm:lhƯ@*Tc0FX뜬"'R7F5>RN5^c- Ogf1"tDlOewd(zodTT;+"@jέDxqq ZvbƏ|2Fz_%uLQJa%Yt7"Y(0jI[qd I#?4 ]1 'ټ=Vv,2]La/s02~į[O0o {Ns̏ noׄqѿ:p'z̏ o }|VLmcgf8-zN'Ms|oHS*G Vq6^gQdaEW&;َB:UW4mLDUآ#q.rbs5_]h <+Ii[baU|ٹDb}q4r3VD-՛KB4 iB+ɫdGuy8D_eHLT u~uo}҃3,>iEPWLVUm:28fEtr:|-Kv.ف.`P2'Rwql66LsP+Pj9Ue bl(I%m(~nL6Mʚ6/_x!٤F1K#s[_oug>Ħ0ZB0m\cO*DF(qōehRs2?8^w la| I???Ḭ̌Ø]:h)؝aoɡtz8mn׼u`rS~.:2a^OU#̓ >j@eٳWt/ŷjd^8iז]I@;tkhߠ%QlTQhA\ "4xI$k>>tiE}-Ӈ@@/dYPHKm):<dgԦ[ bDjU#0y: L%T ۧ:lsoE5(tOSQE2f[R1 {ǬFKq>LcDĖeEBl59c3i,l|qcpYgU{#=ϋMv:5IشF2p9/ul|i`r0fQgt6 +8FL@ )ALFGPwDmTZ +Yt3"oܧT +$;HJׅF\j״53O-FØr6zwe:B=4٢~r@kݫw7HE:9A Bb 'x~G~k 9 oq1D/*'`-d8RBE4Hp}a*&Aqe 3>Ȧ'k&fɀZ,SπDw04n# +qɮ]N&讖Voiwim頧\Oҭ6G`{KOt-uϛpr!BHPq Dj VVu+n|=-qPa7͋cɺ z{M|&`-d`1Nftty~)8aQ!t)P0~FGP;Ay\ +^Pa粥1YApV ~@s[E1Cpa,.ګȍPsU ku?dQNdvjeNcfɒAeԏqTZ0A%5zكZ yWSKSW}:˔*ޚP#|w)2(΃%k|D%OoqƺGm)gb-dJ؁LYKhKuxHߕ΃*yOwz-;+,ɀBnfY:FlOCc72*e[$N9 K gfϜvL{yԹFP?~10gVb#k{A d +@ +zYoխ__j9`T;5Ens>"i/A5™<kiGvܚ ::G̓ޥ A#$Qn pdl o>ܨm2)k0#byC G;T橀B*^"R]/K{H,Rxo?t^ +Yx#.vp+T@0})C;YG{]F0#Qf*QMƱY̻+rn^VAyxka?̥Id CZyzÈ{Yu ^oYp_ul7}^"@yoҨoTWD 7u뗕ھ1 V/ns-,.ʔ{Y}F )CxR +DYWNLr[ ՛/FI?ط&,{5rYnzӛ'}7aA7UIwKğZNEUIȘ!J&tMP?TB?T@z%!*{vUYOJ!Cف )rlsq!BȬ$CT}7{#C@֒02155wxds`MA4CL0ssuS<6f}?,e˕R;埶HB)@kio$!7DvP~sHZ)H#XFni<˪a2ݶ*UFtxRy+sOjp PMEq +hsJH ++MF˼Z%;sL,}/+ +\FͨMv%'[,(үAYbmPsz[ij;xa|/1"M$kyM4Ky^rL%mB8]ŔaI!e\5˓)j w~bJo˶˟,-Wsd%}P]ëUfA9V.0$#{00gUT[!>m+X˪Je*P(g홟[|x@zɋP7.y6v2]5:w~ v{ԣt7%UAA!$T, TI1Ise)o>\G:0K8r$SmGfeFWA5pE*'`5.-B)8L1tW;e9 +c$+h7˜O@UQԋ:HZqtkň\*v#  ) 7XEi ,*eHu]oˎB +S2SOROnF f h*eR.A"Ez?<[B>ZNX`G ]S YIFM^q_^^TM= j8q.'jHmv"EVm2PFAVoZFm<_)uMb#HOs7WǺj'nMyyzI\l=e/Σp*yR 1 XsbpnIj1wCjXkw!FFjl_zE|tVϊl9?䦜fqsUShz&5_'TjR?!%f갬F,WbѠ TTےG\l(O[\T9!R+欀YM~%:Fg#law^hVA+_rA@`Ki2e. jV.ߕ8[m;F2rBR"xvk8WvTb -WvXbĺ&dET +qU0*+2fL`y1!OYdr3F Ege#T_2$DM |&2&@ZlPIOӡG U@ݔfs~eqJvl6 R.6"K@4I2-5p6d Eӄ'Mz g/??5Rl/Z%sb$d +*cS|'D*N!u>RY<X kh#oNEXySoMLF&;&D^@1(S s%-vFޛ6urɄُ !ggdzn,3(ꉡ,}b} !ԧhВت͔'HcxRZ!^gu6?^8shu VܸI4lj +P1{\*Uξ~][#@E9A<ÈooKAW<@qLO,`ychT0j-h~#Nm=$ wrʉ;iJR[+.,'W\;ɦS,Fy\]`1ȴY mPnDgS5buqhFO$Ђ`=MDǧhkabcm瀭uѲnMlhw|Qo$NΦA +Z)ۖ=WM:F+AK@246 M +т>i?pkl_C4Adka`b7z#-ފֲa ]h&PF}wX>Sd@Q (.#o,Od-Ʌ | E8&cZ%Cý^ˤ{ju(Qq\#gMɇ6Q(phT)yȏ*_ 85iM%r-:$a?XL`wXic3h;4M3J^]H;Z9AȒEЕ 8?Av`txwWVD08|-ݔU$R6*睚,}Qc-& +t!Y~}9p%P/޳sTG*h ++*lBE 4*ՙ֬S#bH'zq1~.~em߿ رlg'Ξx-^8\Y?R#XŽ9?/Mv #?@80ݩAb}P\@>}MJڗgҵ;Rz/HGw>It9Dnom  +X_Oq:gKmcAUIG7+BLYZ%z 81 s?%jEf{ǝZ\oԙ(%6HAw3!{]bt3~vRx?08fz2 = .ߋUEJ]nסߢNwuE8i-&N5*p8~\3x?{tbWS#t +EƋr-@Um}Qh]*l+wtG&$_ |o}/cZcx{L'Z}frWn2߀M ^|VBN#1mw .E<qzdwzR'?X_|G*fF#Kzݍ|wa Ew<,bx%1|#W3;b"4(C;O, +e(WWSz#g8ݍ|#|g-;ܶ-cݤ8~ _|-m 搯;r@yb}f;H]hߩޖFNAwVF㧻;3w *@44 Sbl4 +VqeX,iRdsȰq5U,yuhbw+X @3Nat)t.ھ9“)#uFnc9)Cdj45c&T߱Q_`T1L=~ԣI5۝Rű/RTֺʶ neh$w)20ZZirДG=1TVDŽ.G*Buz5_f_/TkZj{m:(*J*AQȱ{}U(&q3@dvsz8Ta[zpRwJӛ]ts-1xkt7j_{΁4D+.쟡9gi+g);) ӯeGܪn1l"Qi5 (8G +?$Nդ\gd"j|=PZ\FN V*k,E+#4$Fi+qFqr4 =>n u çG uͅa>ܠ;VGFx>gVS.|~.Wyd&2ErIk+$>D[Qѧl߱QA,T7N3#xӘ+^Z9j>ʃ6]<7PxV.F^4B#u&58ڋ*?}G97smT5TUH}8Hy&p =k{ kM4dŢUTIJOQrs_/42+^2&a]]6ޟ?P\S :>o@^p= vZ(m6HuzSճ-:j&(AC[+M ֯㶼+|Tv=Cc:LCTHS o b8`@\Ʌ 3߉vDnKږ2SjXnqI~e MXCib]֐[5c ˱joӇK  L4XRfYL]1@Jt7r sOtCv~,[Cq)\Mp UXE谈{|XC>D;,?cxc Q$ 0 +u +w|!龐 ٫Mk/NCv} aqH*Y;CZA@ n }%Ya ?f j6l_ @@bC89GjnXmޙUn?/a2>[{ X:%G0ߒ85teFi@XZFR +~s}Eg/S{TkMtDTx/CST4{Ev}"f?^*?$]''ԴQumTde&UG`p!{Ms!KmzO1ZRkD̖qͮYAs$,q%6Vq= E+\ B<( 4yS|z:g+LlM$AH_S<>RQ89dm+"kv&u< "0+xEFF"woX`9'xhsj 0od0RX^QVDy嘰o#Li/1f+0ͨJ%ʋ]^H\k%Kp\fƓ,< +v;5`8QZfVq2-;r8j+7OKa}J1|8>`Gqj9.0w ЯZۺ&fz!|'\:R6AI:sP)=> wv&Y[D{#z5-KP G60 ˽qQ,E#j˿;6z2Z1dk-7Q~@YV}-<RV~s_I%]َ6p=6܅,EhSgnφu]݂oAp'l{9KEi;v- )u()7i ;e@Nێ;-5~yF6 ҕ'q[A[ue{ċ=8f;X~RLR|Pc^f_wGnVIyzAzs8<}Ōy@,U'` \O}5 F7x|t3]}R[FqL# d'Hfǣ|]א"nl`L%x" f.Kx^edwP,‚?HQoo3hC-u zˑ¥qV+^z+$[o9XWH-GL[7rޕ{S|֍rHCp9z).VjrdiXu$gQSE~$^A/#ܚ I +{9Ibj)u5e>rph;<14׍wAGx7gWd,z'X1έ2+) +Y&T +upc5b= +H/#?۬: ,VQn4#@`U9 3̼9_2Z Xe*7o( !y$2'J +*E`Jbv-[r6)дi^6fi k{lFfڣ 7+j!NUĺL`CgȎFVVIi=MzO}%hfC QnpmڔU3lN %Y%ru +2>f,WU~+Ly l6/@3٥lQ^fP3JjC$H L5Y#ksiJ5 ш⵾kuMKXY.5kfsS@5P-$4>xA͹) 2'kBӞccoY7]TC*!{CK:R%aDbqdYHQ+]{24rLEK֙tR<oiH +endstream endobj 420 0 obj <>stream +HtW9d9;$DqeטuuR(2hg,⯩1>SkMr#+9np21smεl0Y4s^kq38ٮ\n37DjRVڥ_{!4˟[LF}1>F[<"]_f$vO+Y{(km0EǞm 9QڰG427A.;< ken(YP.ܩx mEqN@:[uy6,7! +=k+3EbU}" poO6RT62cKZ>_kruY&o:d/DT͏.] +`I-5?{JqsSFquaB\C^pz|U쭳:m򙼼wJyhnD(7M_uVsϽAw[ZVew?gB,&vadL#!\y|@Fi4-p~-km+G5 ?cuq#E؂LVtJU/mn]N/2ۨ19N`4\2Oդ&M컘X?]˶RdSjs !pHPx̳Tw M^1mG,]Wbʡ&Q4_Ъ\}Cd0CF7;18c9IF$̅DŽBgS;uWG0:bVn㧛G1Ք{kuq:Ȁ5ڭ8hadm@|X! XBPKeP!;z#6ƻZ!VoeuaPk0įyn%`hFSKyֿ2zcҽ1$`ڨ/lSo%xq c@d +}ͲUW#;1<Äz(#c֩$qu 쎑h%fuDqTMM>Za~²N:48\0k֎Cv +_+;} l-O"7BɚoRMyoʧeN-~ +Z=hZuhy?+&9-_.fjO)?* aɴ-_]ɒ݆?l%J n:7nu0UsPRK8e0F<QxA5ݰs1C%8Pi!8GIcv +pJ<9$zx95N޾]WZՎ }2a@[dq J"U27߶,%!0ՙ1ډ42 mb %mӞ&,KZLi ruoTyTlF T4ž,Y@:d;1hv´)Tʚt@b"Ξ-I `1,f/dZ[/ +͎NZooyM>Ss? 'o%G]!nf=wZe/*3C L`) :A%Ϋ?ʶ!]pUG0=Bl,Js2xCD.A!1]D'W!; +g{N͝^b\I%P (Jhb_Ć^<%ZmtLZKa]GTCWDx2"+?S3[ʆS2eDT}G(F䡄 KOD}K4;#Μ V_ Ђ[0C,CXs/mZI:q +,$SVԩOMsF`*sٌTXV=#:]jSp8L~J[.!BSAkAJ3]"IKJYM6QơҦƮ!ĻRbX+$A >~[xNkSgO='Fzjm!x7&žlZk:aA}7Ű:gǮ OUΕ7gU ǠUE(290J<_qɚ(e艞agTR"^oJf_ VEpKJSzKbT[{~@A=RJ={NQ7n-%|jJi9bqO9[sUXF4.im$~tCev^ٽ֨VI^kYjϱp?}dedǒ3"T2:ۘ>;.3)Lu>|yo@<14 ј21˒8n"42ۗDf։ } G]ȗGHvW&XkNJ;Gkcql=R+emf?WʐZ\ڎ'o[0CBlcwըͮ`|K+t +QZy*Q̠mwWOCб,(3 ߽)xLZRŅ4-Ǯ-ܲj6bĪfAv@WpU% 0faWr)vMOYB4H\|fP`lHm _L`Qf: Rn%S4ί;,8:`6.1\fJ+;r^BB ʋq_GyŲQ,wflfrgug>1p=Zo}1^/IPNBHBbQ Gf Wo_ [D: ~v u' t?c11dߟ%v8P]f8(psΩ<{:z"b/(IW|˷[K#?:BCMrkm9WʲU o""19ruv-`a@]Y"ÝA2]:.׺r6 QnhmzvWe$؃ _Ɗ똮;댏W4 ¼we:&~Î܄ڒءolX}HcZ:rGlMT +';ukM Whe4ˣ:P 8`5>=XH9x^"2Ue(ow{)o^<`=o 53EiD dp,B"^H)cEhhh,~0@ϮMXDPv{SXQJq-^5M՞)Վێ=x ; @RGj27,J:vÅ"w(%f +0ĸ5Sn2dԃ**fߓ$~ (^|^hop S/2 >یJJ,N7ytqksc 845L@4/A vӣ.yTi3p{rT.FP\BI -r뉜hH[ǻ(3VH?,71,J,L2R*97ƒDK?C[uXa ŷLPY=K@a37s)4O.=OXgԏ_p=+&8;14ٵ& &(BDoP.D%$jܠE-/EӪm;3ȲL/*ҡ<ʲRFF(%六ݕҵnͮE;3܅{ǣgPT)K?SLo(+GX;-6xH[,ӌ( z'm9ӝvdEjy scZ%v.Ξ6;ʚjDWxEqO"M sh`QqL'by"PCN]~\o"[QًVj }Gag 17AH :Q:yl75D'QОQdC-q$b2챨t=@d_M絨Ork+E߅KKEƛ 66V n@x@8rBw"rw1N.@tY,4=$x<|{,`)X6 +4[)mQͭIcQTW==+Qq-pucvISA+ ?D4M 5mzǻ'1X LQib$uۊ 79MWYM"&Wn`#^ȕX + {՗$>xOXgmBW9X@WPA̢̡QAnؖ?kk0! A׺@K|зmъbP(=׏ǣfDIt471y\@u[ `H }L.Η鋊 6@M ~YO;JQ`76 E?twp6 Pn_8d>a'~<2 <:=ͱ.1gow0 ~f$\{':ruh5΂6EebAodԃ=30%.mśYG(r,zBӼ'nIv$u4[nʜ7a>L%0'9,ho\2{F.Ǯe +̡ H;bhĊ ʲʏ|u6 6Y> k'qMmi] ~:KԭMn=@OV[>yR_bSkR'`mjy8V,txg"|?&gRG2@e.F.~+(1N23'}r%H[+Y"@ @)U`}Gq +}$_RxtlIlf[k [FbqOFmQ{5eobVҦP*ʖSb4I$ԕRbK'Rk;%</bIB$mQ C hk~j蜵z-t9Fo{ϔ42P"QŠD:VzT LYCZ9ކnA1ca\rª:2`Ϙ@#\\+Bemma啯- Â5Le _ZiI(XzbR+?G,c_?T +Kp5ŖY[h!ֲMc/RJ&. (2!:`_[6iWjaD-WaՈ};嗀߆D +`:E QlOi O`}?TslA"hU(苼WGH>sO)޸Ԟfarht¨"E]fYǛķjIa[ g"d+٢NFQ*?$9L¯Ć%V7ѭ̮0xbmMQa=/ úkb1 RJ{'c*P(e~j˚{j9XS tN;{wo',+'X+OGmL dՐ})u0ŢVJ +VO]'kѣgSb6̢⋚GN*AK#O.wHM1δCtuK5Fv(Q`4,B +|Y7Jr sO-[WjӒ6e |UVG~?R!l3gH|qaǮCk3 *mѻ--[mw;0nB,X.!'ETQMsffp* +20Df/ b`p('^d)7`Xf˹ +$; 笑~MqNO9,ULDB)p"F`R'cv1wMZu;H7r[WbMso_>$H& Sf8էǢ(~Ykⵏ]] wP9Z/[אusm򮺆IԬebܗ w_ aE| g]<Қ${C9ֵjfL.A}c&v$fkmNj]:rjei(d@qFG@f[cԩ)>@!"`lT]~ۑڧCzX=(_73 V+VQ܅rJ ީP|ƞUSv$,l?#ϽϢět_CKIyi0^=pULkZt ZlD/i JoOVĆNe—H}(e)L6qʣE$ٻ"SOtmvD+JpCvI9]fg§E6xܲN ELڇ2]Ⱥ8l}~>^7nN{ag]fzw<{ `jDc cw}8pbW1\RNɁsY!q:o"0a̕*wp"(w-,q^@ a3VbcZ(gV۵# EʀD ̮6-]g&J* wi_]]ꩩQdsϤL绬<`<6`V m()Wa-O(}h&oNvEUe~vh㘚yI~:@V[ i!B9z Qb#o$ 4 1z&,k&AwicfM|AĊ ejOܔo]b3 棎<=#6E5RvmIJ֢v"]z4,v5,Ӻ X]$PJPI. +b3Pmmza|Qݻwo,\8vFm,ũ/C^BK8I-yH*={XޔƬ7>.ҎaQVܠڑu;nWPi*E5Dyj"||Sa6̢ IOm kQrUJPRò{ݛͱ6s)qu99⡹ӢIxOR} yHocQ+KrBPIt/vy XUs\X-3'Ic[ +"av OQPOQs>Ǎ =:vz5֬-CJ8)[/bZLh'E2w5pɹҽta<zS!cJclj2(11Ww W>#yr#Kq*J[%4iksetycWg1_Q3v,(GisIO>ixͼ*X_hmjxjf^Z6Kz#eۆ +@+ 6`dR_K 臞ϕYw"K^ss!x'DWbj z@XƒS^282=zy/%]F3+F|WO]7BÚuj^F&6'Jʤňt$#G'O;y"\,J#2$B}FZ-bf=>FfbcP=&QŢ;N|<Z.b󣑚]y~~i_W֟8\>ߟoUxz6浂]cDWf; +H4VX  zlalwFIJyk\biHpw#+ k[玩`m3G/&h"Fi34}mTfJP~pwJuĿ;͇w=:+Hw.t`v9Qw7JŅz )=Se@ʋ-Sz $"(^T@SnK"(SȡN0#NERגS_{ƥ"p|)&T@fF#SDF{"LRHv*OjLK82pیr*"p/H~֩{W"8u"LFdžS=HqI0kC=#e Al4,N;b2U)#JA A*Xl7`=iRc1Iz;L55*^&4$t=0<@L#",ؒEv6Z憫f%-#:K:%[!;mJW+z`gd f`OOH=s=@bw.߆B-hD-qNCh 2b䞆4$~݆i:8Flj!Qlƥ-beJr +#9lpj`HL YKaWt%rs];4l}hƱ$X%KM S1Aa_,sĞ(b}Lz{2cnh{ax!{ED5V\"2"{V Uy! JM+TU86:,}K/(BtnTm-^2yGg&R|ˈ<֮ U+d!dTF.;I7% A`p/Ye8wRdޮt=F3>Ss*uuXyfk3=:#Y10&7AuX3m䱢~4}"tdl/Ro " ZA|cmB%`Uvk#G8ᮾjv~.{f+?R8:\̜lHp!q,EOv~Z|t+_fR#Za<[`ojfjFz̮dyU^h1_5X:.bwIl;(8iAI9w"oix1>L|W;r b[7o'T} r* "9d!ar;Y nq:){4zY2+{JʈnKeGrK-V!D }^&2BFKVt//::cz)DX _j3KH]x#lntFbA,ܮ0W"ns< 3;V3X^,@}E+u:lghO>@D X݋ 9V>ϾUA)'I6dȱƳA E J 9ߒ X+ate d֪8,aҖc| +CkH)*|Nh,~1`@qjz^k$Z 0.M= P9y}xbNTE 4kH9}xz, @ƥ#ɪ(Fʊ;P +i~]uEf^Jgfbz!9UmULe~=P':蠴ɬ(:<qZy^#꼉˪OQ|J;AsrQ|o1 AoyɝYPzqƶ$C+@ԚŇCe/@b-^$$'-+n/\u-XV}_H!RQ&s`*'IÂwe(D7iՉD4wUkt"[o63h1zASKO0lWf<,.&^~:D1]ΏoHqA7!j@g#MU;8LR90ʕ0bYV*$ n g 5D/LzlH]$Y]ƶoYFfbs` cn=3-[&3Q|7jH˕){At%Q@l| ^)F I&Nw*ְX5V{Jvx#Xv6n6.p Ap +K}m$ WW m WgͤDH?&&V_o(ow'(fyG(0ze3<*r#΍=7<Ց=A]h\} +?m(DUؚT?6$7^lۄGb+]'؆|Z2ci̙~REGf7X1HD)hYclҸuLmwp-Ls(Gr.ÜT~ /ѣ ?Hʎ֩[S *킑jxmAvږB{*G͛gq)i0xA}zɑ!:I5s&i_>S$*IeSGQvZVQi1?1BڔFVSHZ< e]C-hJiΊ/ƂHٷ~$nCJBZJmo< ʚ?"3w碛U1x$n[;k-ۢRAwyCO+ZU^ + ܛxZF<1ky~yAI%b?`)U}Ǜ_lP?4NjKTϢ&P_.l.2<Y`0p $BϴQeM'5y{f0]ƒb2~qirۍ dwkcQ5 TժmkdT>kdEzY#`m#Dɕa pf@55{!"'DG R*=7IZ>BI}ji|.tz hvuwS f(׿_;O4B|@ $hɥAm*_UAkG -KJd/MGJ]Y/u"ÑYJGZyX n1 }Gȶ@ x42<B$;;>6ZƳl=^HSAA_v!>xE 7S?x jy#KlWA=\mDKsp/# {qlO{9􍗉Y)XdmjE%Ψk K)8gJ=K9NXJ^ZFeQA?6ٴ:j.Ti1% #98]|Y(xUn\97y>,^Oc]I 2fL`kU]S/i⿎`s72t['0 +-WSAY=̋j\,>U\t +?*7rpFh(23rBZIEz6QgſFz8w&!Yv t2n>D7&v$[R迈 @9I}RUv:d@RJZMzpϱx_iy܈m ҅+ː>o Dpq Ym-8H i ;l(b5jg0Éj3:KGG1S)h\Ɯ )&l[)WPy +dK)Q0\ތ{HO6L=zޱHح?` Zޙ- ".!h)@d؈`Xg8Gt?D8 +`-b#mm>Fd_dfP9;/и(Ђ ;`5}/:ij,nƐ|}xЇg}קog3J-Gq =|#LliK#È \ܒm͐b7Rgͤ EChV8P[-X")>ő=>L r#q W7߈j>[3tm伕|69+OMu?17tЙ|YhZmjetA.F_Xjewhd+=}`etbB+lZ-hTcSbYe/Ӛ SYaL\sZ| ~/e#Cc/ǖ3]Ǭ`6:#,8BXD6(X W]{ȨNȨl9h6j& venQ?it~2$'Zmۦ$Gڌ0& J4˹N61lw`Tc\~6^A53U4LG4Gb.L̶Ebm(_-*!@W2aҏ%653+kg ڻ@B4s|s+q]2v(KwHo5dyrת;NG&ɋ"E"1#]a(K +FZ +C3$]TfDY Q(ocY ؁jNJ7^ wh19y1({uNC0ǮE#v ' @|oAhΞM/ ̈́[AIR017.dqf:;o}t{ Ȍ_c +<! 2j7F>=f<^l8v犹r~3F!ԲsomGiKM-~­8_aiBF}[E}FvKElY9xsu1y*kc7qA3_SF[nڍn `U1 +Mr53JEMVW5 `Гfu-Ia-V/ @-⏹k>sׅLps8*[Hs4c7w8et3"!|fGhբY~k598e+h,<( +^*N>xd{c> +^D`6Vfe2JHO)gUQ]#f8V,8~sZЋo ?&0hp83vXw%aDE$(sܛbǮ5L'ΜB"B>Y4N$,]QWv>X;rA>l|-'j1wS82T115SGjI>0&Eh).[|]A[ڸ[ 8%\2Y>}O +5ݛF þ$eK­T~?%=Um%+`aiL^t2J2d8DQ%Jw#me]׬Y’"Y]Vx 3s(;/ pQV-S(d9sbGlJ/MWF6p"܍W5hOT^sqo>gMjp~RC0-ž Z.ʦqТ#wj5g]z I+0[űȚ0ڞao#RdX$}5Z՚ s1~mÐ_oO{! Cp!vJ/w&Ʉq$m⨺&pJ3O1 +QokE$%> N^IEa3&٨Woi3Bj 갽H;\U˓Di2mֆ]w|}ʔE -RՇC,҃ ڨq`|rHh8ruiL9g+QtY&RBV; "pt,,*Dy6XIƸʾeA|-{h.:]^ +$j# xmgMEE;7 +oEY+i.EKuy#5DͶRdIOvK&l<+/iDs>< zxGVo +2r_":G0:jVT4LĘc_ȻThz;gNOc~LysMϖ`ax bڿ7v%e¿0@19 5TrE|7:Gs=-l>Ls)-.`i}P?}ۘ qvA^HY$E χnעrRSMZx9gԗ˱jٞ ?eCR?\m&IA~Xm@u^Ã_m>=&N25's:mQm'ӜhL2X= 5HˁVOCx1]g_ L*CV'n-iP蒀4x}䚈y'Zfoζdd2bp/ S(6h k"O+P f-hd$Jsў)ՠ]nPGB>mRfj7 +3𒜩Ԩ.Z:, ceϽaL +ybb +jxVJmƢVu˳XkJ\H2P5\qP# U)EH%LdHwfg«wF{ _1\5AX3 o֢5U#a5uP 헤O#Ay(`Q4u! FE] .+}:[)s*HB{8濩ChN ʹXA=IKފ,y#Lpl~fꫯ76;0r:`hhq[Ok +@'M0Ъ= 1[@]ٯEŕеڞ01fjhbCrn[y^_0w|Ր/HǠDb;ANQ8 +kjd|>$ZOP,B+`$uWprVc +G/}T9?z +>*ͥx~0^Y{c&3&B)EHcV{ƹڵ [=>Q0B= یRB=Q;o>c^gH ]|uyYDP<|L|eP]aܿ D;zgZዃkȋ?MnR53ȹ. E'tP! +|[:,Wtq錣vnir딒$/lr\?<9u](g QP{2w+g`B9Q90DZDCC b0؅^\FʃI=4(H+#fwjb7ꋠ׽N txQ_.T@pc{kP,YwsO`|A|:_+&N:>kє7`z/HY2dzXz9ݵUI +/wXva{ǃ+B2'PNlzA?;J!&!$v"kIY Oy'?J^` 7oU֖s_gM?2fmMD*O &]|gKj2l8뒬 s硦W{l +endstream endobj 421 0 obj <>stream +Ht;#1>!"%ƽab6,I(2<3)ƸP(l'ӂd+f@!&(;}+'MGE3pKx +Ǎ`\Cg5iI$M9*ClXuLwDFc+0hT|ɭƯqowyJo:/GWԒ +Dt{i3 S젮7ٟXb jeV/,&!sFf-5{'R,(X]<%Y(o\ߨX֭8Bh@W0MoVS ߺFgB#zyނb~|'1p^zf'4Wsi6fY}=W<-I5 KЃ0J>F/GYRĐa(EτwL@-Х >G*9[84{\#)z#ƴ{=;Ll̖?qz8s}TDxJ>4SC8j>,>%IM'}Wmc % ~z{#,sd͔eCY>`oUiF=Bf͊C6RCۨypWWjv勽a'Ȣtci̳˕0JCʈ5'i+͚]:tL΄\HQ%j!!roqNYonv)sXHU94IVd KѐZ'A v][l)7QLהQyjfo껾F$iQ:2nsXj{r?3g2j3-0|>`5o J9/+S_#π]lT6r Ӱi3IE8"zoz_ Dhg: gd++`8 +XfsD-՝AZ8~O4|3AlUjb3[{͆EH&6Nm鄉lԼ,r9Dj?YY!T K[hc6o/ݿwכ[(z>/_͋± mV”P (Qp̽k4RfKٌ2(w6*bQo**Бw!f"9)-RdNhq]ߖs@= ˤ-\,& *+ZL΃`۫u2B8ဪSHGs& j(PqV5^(_nyL J:XR+hAFw))w+H2^=-#e\$[Ll#t:b\H\Yy֑n{㜽vui/$26$ib^,s5`$G- 5^4IHXEaTT.M^V//+#Ӏd{^XϦgH;h7[+R/"[>C\S }{ou^(r.Wg`)Q[C#k'Rf'VGJiK^D%XNVQ+n 4Pwl?OLC玟7r+~!@ia5"5+Zˁ%$$-Gj -4~r~VKEclrГLbS]m-t]_Uӕq wk]VHu82QFduN%+K<qQ-F˾MQWSKH@Q{7yWXNЩcF>{7Ld YKdox@j2.NɇXm[GV%ȑ IEhE5,@1|>w[Ȫ5IUE c@S|%@^LV8^ZEkw]m]. ro}"^{p< + ,[{tj(BH`H4|ϻ3raXPQc<&6wg"ehsW⩑ W5=EkjL & `OJrx X9/Y ;) %ϚQJ&} 0߸_j]W&YfHS׸P +f6_SYP@_Py 3uT/=c_~*nUI 1ٶÊYo ԃϼ>fYI ݤK4TXREOųs,|s+(W.qr#(P!{̨(xT:xCErX 'Sa]t|(X pFYd'¨%9m|w )f vOY$8FfI>I+| iJ v)MiGx:G+b-lQB%hb:l)}^}HcP-Y1@dI"\9'X7M`n#}g_HUlUa.'5 oZ4/7ہ{PŌ%،%tkίGzb-BKU+ v-bg7lw7 vwͭM͗|u8׷EeIGsXhjv uf[{4&sM".=搭 }[{$1ﰣO^}ds$a~ܟ&[Ad|aco˞6:ߙI#6OuC>H6b +x&viE%1Muj۸Pne ӪֿHaJ7 j*mhwo34V#gjV;ga+~verQOsj&/ ETQMa*]ytun˓)„v"kbWPBUܔ[v-4Tdrٍ El8;S5B)4H}+Z!>MKGu9N",*[;oJkwA_M ߳"L O` O +} ++\EpW̃WYCL"DB<@7t |]؆@xQ 症n[Fq2T(itywN'QMKLE gmQ3藔 l3z!L,G+쓗0:Q2G]M H8*G͈{FyEkv,]Mmbѩ_ cR}n6M{Op&&)oр|٥7IqZKI /s\,Gغike]˔6Ru,7_ؽ2#%E: *7^K" ٵgET0ֵF]MY R L-@6|r;GPhIMT&08aT51I vVԔL52o`̃Vr#t&Cn̓;?@=Ο5oĦe"h:Pl+KlK∉MU6I`.+byb@:@9$}r֠JөoļUΰ* Kǰ1 8[ f~$fZlVy\Pܪ?:ZT!>Z= +@iq> +,Kz :#!fHKw5]L +Oj`ln٭ڵ1m$Erf/y.y)] 7a`3wZcӒ0,F%BXww}ݴ;[1h dD4W^`7 ~7r7 <`4/$_ыrR7)M+ޢ0c"McqH:$f#ط."Tk8kXơ+QJX7J Y-)]j9rzhr5<&g,`vh\RQš.X?̶B+H%ZoZ2-GOHc6`;5 |@ yM@%CP%>13 B~,#6gQfHAم>7׶{֭x= e7(?X[V qN}D!T_r"^$Z,(2õLI(?8FJDwƬ}KCR>Sϩ^@;LGi?nvŬ޺'o{}ejm>gWrj +J5sT`N?H<.L-Xv8 bf2c +\,=8ؚ(%$( +mcYl:L +Aqi\?$Ϣ1Ms- ;"oB) +%8A6C}wYl6 *Т#=E)5l0,H~hPlk&YrHj}MޢbNV<vޙ7ep%X 19YıOwkpz%!C!.kJDSm,- dHZQ$ +!2!d8O3o Dmw1Ht}:śЁ<߄q$4קv3Q|?LFO70B67vò2zc>{tF-8&2{{9φnNq58MdNiޜ&2fJ"8 0e 'y 4C1!Ywʿ@mә?+0~K8c??)9 ]q䭼}0J1kh)-20+ٙÐ[/`V{^'P1ZQ* 2+hh*Dq$@owo +%uwLb6<ssy8+hCx W RJv>W b_W ֕1SqAgm@$5e<ڻ5$Q3/e$jk_+0Ӻ9*BWx@ +~RVԹUk5bZzs> ^xlJV + +We5+ ^V!S@ +DfL7SzŲ +k*3x=Y[(2Nb @sRAU ҳRgVAU@ +Z`Vή*o@ג2W'!+yFR;6+XKJIYFp\^V*^JRw$oG|k6Ӵ9?X>6l`l< ??Uݽ֥]M6C8dž؄/?*2e%( #Rc,am䕒+1ٲQwL3JdX8=ѳ|VAT+}7@ĞRK%o7sddW3]Ȣ ,ͨeԥDbPKFrN9$6AzEIS7R o2I@$CfVOwcѫ +|'.^:W@V_N|nP#"Xm'3 +D^|Y>Y +C0!ZD_lRÐR7+/"LG[-n";|pUé~qBc cFl,5l+ C Dy;NYq]-.ء~:,%jކĆ[|фuFl8h?awfHڤJr<-h*\痶[, "`ňEuShcKXz"el|Ѧ?3+/d a?L$eQ=ԴGRkѴz*D*iؕ1-S㋐MQ[Lz@[b˟E=wtKHԨ}R?&73yq}MGGtpk:ssxȐ#9v@u!nؐcp5\o49XG4w7@"\+\,-Abh~p +0v]D3 D1DռjGL"EY.z]1 5WF,[=5ޥ"'Dw=ᙶP{XoGMY;F3iwco\ݕ_8 +I6[\4N^q7BW]"k`xj}!hW] K8F/'6$o~L/[&,*RY+5+ܠ]ܽ1{ adƀJew3иFLDΏ-,HQjC!*3VcR{6 >b7Uc=brB7}tq`C, J" 9SVgZ0p%1ݳ$:Ò77{R \!~>9%I9dsuj>S:ytez**t~B\1UheӫPOdy} o8&DMxd?*5Z':Sg-0Fb!V>(+`S,ǜ> )iOe;-SpO, iV&w(xC(r&bٮ/"+"/Fh֥J6l &V#x|%>f7TPvi6Q?֡|ߡPz +4BLW9f4XX+"Ow̹X=qdxyg+."nT趬 VO3 3Q#nj `'4<~Ҁ lc7 zEƊ+"h3iCvfK#uáfR9meΥk *S,lJ2X>H@r =4F׵ 6pt<.:Ptv juAzz;tڣqtط6fc/cyHws6z B[ZO>;ݕh4.^b(@Mשoz>%DE*օܧlQ 6c =AzAPh~I'k936ņ#ivFS[}#coHsVܦ} q1hGC+)ms-tlTP[&x m 7au-գكc6WdSBWչ6SeRQ5Gvr?&% {7z M|YwqmKt@n?І@J#!ivB*H}J|nfMDYasUF״S-әS0:wQ&bX#ey*SgU>}2 1lA}LZ#bΐRW\wDF1 _ DF {ٜҰ1H.CWT@ho, x\3p/e}2 GvxVl9jÑE&ƪ#4ӌc}TŊ@KV-O/RbE:2 9/?ZFѼlbh M`^g&#{4xa.겛0{k*cʴ>$qp~NL\VAluI]NKizJo[Wn_73|(u o$l[u`kFo:; ak=Z_?vؾ#>fY$2PKe$ALToLaFeF'УYѐvM +W[u?+7P"sg-> ͵$Zcr)c +}* 3]+3/ ֛jᒛPjQ>T>4^ԯH:cklYy>2nP)_Pћ1R9u:Ÿ8ބh_Y +whPN"q'CvȹrO[o?OȎNUȁ:Z"ԣR =otD huDzD#G:8`ւU +IFDh()ZA]/5 +*M#Z.2xmg2{lGE#AnEE6J]u-4ߦ.43d1{ioN*Jg"чN ? F㍦l}k22$0ah@eHϸW<#9wcښF*ɽE0(j/βG<٫V59llLeQR0M_zD5Rbe7: W'2 "%bbTF&oQڒitRDVmI5y<*mG{qI>/Yq93`[{r!ng5+o' C P @hO"0N[s]h^-2f,] E3kp Pqd2aXU\g3\*fw["c^Wۨ-:'aEF  "݋&4~P.fFڑ-L dfW;.x W(i1,AM%DںO\̙Ʊ OWGpG Z^G,#o/nL*ᅰoJI. B ~'h?o} 8׋P v;AN&!W"sxw#1:. W9@г*I4g X8ލ}>/"83%dhr; 7/13?h}& 7UDQֹYt&LhQFZ,nő#:B~Fod@w&Y1c\XdX{`b4n&=Kxը`8&+|rIEev%EBv$>`/.0s[- Xr[U[:b.d̍2e%$>* A.uMi"Kqqg.xdud}X+GSգܩ$R -foIљtL[E2ΥYJVـe !K/L'JˑpFi. q lT6!䉋9nˊl$NZ2r`Ume_΁ƮIE vA_e#@P  +}1G_ ;-I} Wjk)!zC`'gD?}C;QqoLj1!)J=Җ|O#$d4͡?֬=H+vJ<,9|`8JYa㋲IsCa/Y (J?ݡ'6Sfio0Ʀ0BHc45GBS3;tvJۻ;t +^!@ZF͡WzC'a圷JoGbucEhc0*"F5/j cz@+^KL8ҕy4A kؽ3Dbi3e{c ` FS}-XkwE*g،{ F14g v~r3{vbۜ/gR?]#6 ' E'v d֦,׹mx%b.s +6uI_4^ &-aw+~\VO 3JnrYIfssbnf'A#5Zs?`sKVr\B9GyT&/_A} +WM0$6ՊBoAѺlF0bҞ(y1ڃ%t4F،W暹 ,ZĭOħǔ=gi>}id|]Y'$4z\[[)u!.2ƽLIqYo R +>LBeQ4(7"Jف(.$Ĩ_\TpI436fZm06P.n!ZZS35c ӂDA{%p/:6ȡZU RvDOE2nV`Db9ݡ{w=`AU!y~ /#J&N1rd:$ +Mn9aZMoa+|sa"rɘRBcx,όʁ(eD+7 + .=}ԩGurV{}$}&hh *_ƑxMkKA|%>aG2g/IUK{#7v5|?@ |+) /f"ckLɞj1wyT%Dح% 'D]yzςy7dN޶Ѩ>](SW+i7.G)D<*1)==5igPlb,?n&BoF%|G%߱K'~~1yv"ŲSs,.E.e 28Rei>PؽjfTU5 -J$nn[B:.KڑcۍX*A#oTWR7rxF4(6٧R(_qk/P(7 X~Qm_'x+G9'5o/Pn +%ғYT(8 +>9ĨPbrQ6c?H#QGe(=x~ ūOM kzlobX W&~5@r9pS$\ YOa[D;tWTLrmъO q2uzc#S ~ Okj:F9(,f֥b6ob7ؘ +PcP]>{Wƞu'_\INXV +PjN"dl"j^X-0ЇQyCn*.t1h!U#9pڦN. b-+S*x_{$9mmTN+뽔TKyJ@;0gbdMŜ _`E +MCdͱd].ݩNHXj]%)ĵy7@0}E|I3pFl'ىut@I iev@J(p*1 ["Pjfc(2RRH f('DDr,iwô?a4]DDS)F1yOڅb{U+bs5~ªK\ג#]+``̡%` bqωXUI!+$EXO.ҚpE_Pť}l̉w4>xht-NibZDpmBNBSV[*:="aZ}^>Qts +ܵQxU wBT>qy^ekEd"YKs +b +I5N4L];[/`zvEÜx}^]3h܏ҙ|sW[bg| l>g 43QxY{]D1K5 M,?G-gQtL#(F/Tx1_Q%kⰩbPe`_ZB`uk|1 Gݝ4+r&E!M}fŰD&t@t*~7uilKsuܘ+*Sj,:rCp1u]?dy(go>逯PW_fHw`ӹckuF6'X<'tGm(&9y& %R<y@D@maSVyG@L-Qz(>ic&B9@UˈNTC_mn(xO +pÏ+F!XSf+Y\C#rYk3QG7mʅgjS݇6G8 (:D@osqƪ JQ1('xףP\|6=^y .D)1EpŪowꭊ==zBgAb؆/# #Kjtu O~GI5m}kVWjf$zYB:"tce;'DWwfglnv- 8}d{_;By;n&>`jZkXCg(,籼.I}Ӌزrnzj" :R>QPc'\~ո{#i?yB05}yVm\\ ̃I{ ۼggyx;FW%cPĊQنڴvӮ8*s]juqK4ƢJ6PѪUx*M+ka5Y5d]q:B$#Rs*"*vԲho^/KJ|VF$̑9*/l}@mo%yu-w`i#oZj"-NsGE9&تѵܣqnBV 'TR_j%WBɫ 9q`߻uY3J\V"j( Sy#x/Z pˈ_mxs\(tkfCkƼEl,'bHF_}2Z&%u2i1JNtO|RWa;`D<|WVqCFKlqQMSd2l;{zewn"T"2#7tMWPW8wO֙{ QM*ӺZ)J(]Uy+1גVeE_qm5ծu˛bTW s\!8lI+m>+I >3S˷ l \oT +KoV7FxZ +U%xĪωB+h]-Y:l+>`woڽm gy@?%'V;B}gNn~d,42Er {l 6k,t]pXײ]%LjE2z {gЃ\Q_$US87vyeP'\4OP/> 0?x-A}hE@̯n># ݱaʉ|>ejHO}x S<h:/Fx4# ^~:y]" @$r,(5^\|5V +[e@* rDKZp$6M00 ɟRXXn5`ٵHC\w? jB|#r*1f/rا}P͟F@tp{&䜏>k6UR8t};дA}j_([2ޫ<~ؘ6 +utƕ2M?s~Z'f&G1'iUz5`pi䉞oAƕAEo{eP" 2>TnĪ2)hu m-Jd DO^ӳq*6QҍE ]ܵ !,q)0[(ltEDMAu|,sxXGW4uRXFyɖGcf2ڤ4fЕYngƜJerѲ /CQefAT:(X\Na0uF|2Z3@F ̅*߫FfOUvCQ +CVߊ + FtRޔD)#KHUL:l=M̔ȂB8^ۡ$XҒFaG/у^QF5dўOD .?IZ!['O/Өh&Q"dPn47KQ߃mUJG_~%geݞ0p5SH l(n=l]ceh\_ʌYWΙe-kw09ˎ<9JynYY7ο棫A{6 ق0£V#"9q Ufݴ)\{N6Dzu W\ƴI6(ӱ(, +֑ؿzXӘS. +_^Qs[oɊov-OolMyy]bbݎ=h5Yi{~*SWUJYf$Hy=ns)}{֬kiNup!|tP]~?*TQ{ d@e ;oՂn}:_o3HfjaU+'!j*m/PGxgliq5z^< +fV +ƪ&YNS{o3ъ1AB#GA'7b}n on쁘Mmeu/ RdxBH։;z~ɊemAqﲽ=Xf6j!j2 z=ku/óףhl 5_Fh3 + +$M-.;XIDG40^v FM%7<ݫnJܴq}s$sjo+eGx* 7o^$C:F>Ҫ5 YH\_}XdpROd"/77xg-OW\w E; J!Rp»PXkl\I[ UwD )duy9GiBEj06w{Tk1QN)Yj NMWw̫ՔWĺMY6izȬ9{۹qMWb͢wE[_6 ʒEV+0!yv7[g] q#Y)0[3VxX|'ɐ}cowbn GAkY-9!8f<*!<}sr^Ǔq5ume=_h^fJ('Iv.MB(m侮"u,L8G22i rF +RI-Cqϳ~[-A@R$8b4E6#s%[k4jѨbt#7hw,E3v=qw2c:IzJ4gRKEDʌ"r>Xr߀WTWДF 9<G䷒ute&ܸrA`|@][(b4dV%53}/-Ki5Gqy @-$W[ f[ʛI^L"}@^u(㵩e!uFYk$ey(' 2*z +}p'v6^ un?Pwo~Fk=]k )*l3JUfAS{zCpsi ڡ"^F__8H?UXî'mݢo[@*DArFy_Iz ć,^b_pT/* w?%\441G9dw*YDu@Q +r]J~ٽȝV-e߷]ʝY`ǀ|oh?0lM!E}KeRVCV5&Jd̿(t(6@yaA0rq_AQ=$eC4\EWmɪ*a41AikjO05$a)a&qk, "tj]XJ8l{=&`j%o(+1p.'^WQqbl𙌦rǫneQ1wB5ҴY߂|J1 ,/:dP&5?pE8i?+fLh'M!OTw<ƆEQ)6xFn; Y>( 17S? oir,JK6k'J}oPMudoX+bPR;0!E/q͉2 uթsfI|V k̘.ˌOA%s0T{nY?rlUq}.nO#+0 nD4{qW/&qMh: +9=ޙ%%5UtsDuXgK=!y&yկ +z3'dJK%`ϝWkb/yOcm# u.#ʅׇ~oSlHvO%Nt\\&=`MzjFTǵa=ĩSiJl{zk7.9"oþY73ڳ,.MS3t0@^g/2ElORI{lS.{ؼwb{~L_0I|vd3D$Hr>7@gAo46Ybb$$@R Cρ"Y0 +Ls7Y16+?(3TVP",fRӢ$@+/" }9f_pieiפ;NaZ1OTX``5 =(5E 8YEEWHF:&q4As15ɓJ)#vՙ^X8`<E`9NKl/>aC`2D4mOum%wXfRu( 2f~<B^( L"Lȩn;{%#RCFjM@ESW T҂ 2Jhu:f,n͉M|؍5U%@*f+cW +D&?cq994?{%]IH-n4c>aKW[!-p`  ;|Mν1;?@Bz}"ƺ:.4L8|yն|z!qO:ic'KS>A3s' '祄\e|'w:f֭1O48>'R\:RƖNtndqAKtI{Z{xB4^xJdiQ-N ӂ 06GU +endstream endobj 422 0 obj <>stream +HtWK;0wDi- ̪}?lmhQ) cǞ4} +2o^\\<_xm3Kb{ounoFxbom Z<×yIۤN 7C%/Wc}cm[&b<g{^G]}Z<ӭn⌬=Lo@8n^Men!Z'MNi~ؚC2q S*u@.*ό2m !'@ׅ@ Me%4E ~%?!Ho<0 [7%PꫭUbYJ;(B>^% +tUxw%P:DЊ<~uf.*}_e9?Xfs Jg)MXZ{ m1|r86?[.5nU=WݟNݏfz>I(. +`#ȊXEk/0ңPL[#Kw49J#9T>oW^ He͇ ۫ kw-RYTr٬՟Ag#?6(5JEX"3͎N,`٦=2[<"uYB YK|CUDdi+_aS);@N++_ݬ0wO[dĘ %{xV7;b<*tPi܏C[|]6;i=Zrpl6 )H|$+Tv HڪߠK2˙;?CŅE o`%U +܉ wQ5&C9~'Ф\fCо:^tztF51(:Viph %}b.;6.".m#T׆[ wh̡6%dkASH 1NryPҺQנ([N=wnއ[[<֨3$8sTm c&)|wv5弯eu,u2cdgפ< ms(S֢mFuUO{K6Lْjd?0FS B[qf낾 +a F_4F~ #F|)ßUDx~bBQhm:.*NyQw's$vu&0y+\j/ st:>S|!X0Q.B`mŬуqwڔ.gu'䎔o/ײ0P_y ul3n;].5Qq1{K8 l.)9}1VS1ȋUkF&D {xoJ0:gٲBwAE3wd@Z9{2~ hT!=G8l'kzfƥtE#Ǧ1Xuq^Cmk}(Ldò2ˊ;E Ȓ'Q04<h̞Rc-^+ #}zf'+_C)W^*AF;O  >F6t3z0͗NР,]v7X;WL@dZ0>VXyVz2thtΆi%YJ "RSޚ&Z`6(T喭'5y_x\~jm L:.#*&U?_q4YUtkO+}>u]ZƽNGw1@Ņ-ԟUO>EʖՅ XTz'/bPGXd͑nV* ag$-4H-*Q(>? óAȧS4r@'fn"0wطÎtWgnHu^Ц__re42?Fs6 +#Sjߑ̭˨^&)"iS1K.Po/C&fY:DuD?PRDn^vo=mmA&Y9h(i!GX򬤵#YZۢ^!$ <#kW2_Mʆ|L ׊1.f Bs"`q".BaPxLX㭺ZTȉ1}%z,_J+پzfHK&˦dc$@߬[jdDCzYvDfk>Z@Kbٗ +-PNjC,L*gXfOc{ը߅aЊE 5x*Z;c"dpt+rB':8ra1u6C .3e1\ȗeo Gi +ecQZU_RHaHbS YPtP +h)QL,Jb`2+8cׅƿx$& TX==)a^ i|[/Eu`@=Wqjd#V^]2?!_R(]CcXߣmKmϻBXD#G cFn]h) X"M }ظK=Vl!ˋ6kΙcyh+}9WBFn xg][/rCKSճ:2+鸐D$ =8^`(THVN>^HڌDP\Fw8d2"VhAyI- )c'DxPkA0)=|*d{jiE-hBפ -M`z.ǰ2~Nr,z_mƇn^h'Æ=gE8]C3#=9e{T :XUUwIKwJҕۊ2 Si1/%i-'~4wGo^Yp!(xw/(?!kys;1\,IaU lʖ=Y.Ȳ`y_EK,dGW;[AO`7b%< qrYExV\Р $Sp8Nm:h'ROz4\̉MCiwRHO#@;bƯ^ *-n?,iY bH#I[3`\ @ru_쑫|xVkИh5H݉0!Gsg4ۛon@z!o~F"sJNdTS)LuBGⲴdUڴÎ9M+B^1oyD^:o'[^O%!it"C5[dV!rr jTYbuOxФ8&+%}k)bW**/)W,b>q$zkٙ*10BQ]4Gi=N) +MU}lTOrWI:Ƶ5.s}P8j儔̷ʤ *)3"CޒzK.\-HBv>& np)?dQÖ[$/9Saы2 sNUהL& 2#h;ae|F$Uj#L@&ALq,9h; +FP)[Vj#ӏx@\/#Rj=EIe#Z2W݁ nj+GfNZ2* ìɸR\H\6էqߢ5v8 WK|͏<0\{Aͽמv',B)*#mRp=B0j5_Σ:4Zь_訖f XUW~9^w>>윋m(h| MqV /-8eHA A&d%DC>}b"kqPqQwuSQlvv[7Op7nfs;77>8*^|Nv99*Hj/F_CH?q:+fuv^V?9xjnO^ @7v["ŜՐa0N0kȠ]l9[ف O`j H*ENWvZa`)J8*y7?lhξT;m,&ES +sEM$r=a83Hkf @U ڶs0*^rYx 76|#ĿmfpeJ>2]ؓ΍`,'_U$p n<\~XAwEq}X4w15f`K lLQu7T*S0q-ezcy4*^owPr K5U;lX'^dT{œq+招9+l d+> }͹ 廕z,gdSlAYkyJWM"Y{vu]cI+d0Mj8BƮ%45grqƳ^pG< :b5hMsr r/kJ<6_m-cXWrfDho3jZTȇ+l MWMD_5 -ɴg jɲ$a[1 k9ɶ *U,ɟ//Ƕ ay^St +WO/ ?|VI*T5蓐PѢKΫ ~$o}98UL LͫڊlxJ6<FxS׹/ 9Ca iSgML4먰v`.Hy܈YWJ(#q0p͑: +c"g1֨5޶x@1>1y*FP\᧑.5vG-|2=q},)u\,`#hk@j[Rg3JIiG*=FG/PL0gɒ =wS u,Y[-ʡtWZR'unX[n{l#hlc iF j_a@ʍ=SkVeA3;ҾwA#2A^ srRךIƶhIA\+> %S 8VwCbUTAlJwdZGo~_x"ImSӞ+K4H㍋dˍEZrz>>ny7r/;*`Bd`2yHނ$CTOĵx8s!U;E<'EHwƅ![<HNƚIP=Kr r18\lE+]r$$"奛/3VAZc+Ccc 76LK_K6}D]lӕ5z>>4Q +:^49%wTD0m6+ty ˃Qn"3yhrЏy|0ˠr8[U(^fK^o~w]WhY25c{&=M،*k;MK%SQ(ߴn,S04_0e(>TވBW_9iq&w/AWgh}Q5Ha@H e LE 7!XBcF.؄@mޭ2ͩ -1?wxšGp?(GG;7xo?o kv9x@]eC[&Еrqp%W-0[>~vrq7tHI eQ,L\xv;W#]5Rp:_0x-> +d <r2ĥH_"1U U7u{Sܫ>uQvdXR4oc>8JKNOL 0ǹG= 4,ĵO0>5_d{Jy˾' #)'>S9o;,ZoKy Y4'=W?.\:[e48Re,kр0xh>Hhuxkn'84H8}r}2S|H(_ɥ"\z½z82ke +<+{LZ +ŨE7ޝM_Ġַ*mdqO +H6 R> l~ ȴ2NІ˪g Bz#2Eʪ]1]V>\cy1.0ρ?`XʸCZ#=z[CX4eo@]_7ZsfSН{/~%篪ڗ_rJbxO7ER7 k +4-߫vFVUcb+.]x'oy桝JNm*{r@Zx༃~dy~#mn< S+~p,ƛ"{_a WM{O{r_QQ_ mH"S3MSwT +/|>- +˚$R Є ֘X]5c yi{c;{/+ӦM4?>ވ2mm76ʶqHeMO&?cw9"D|钗bY)tADX!r&\ƐD: : +jM DY6K"P\ ;J(HZ&}{.F`I9 /]ƃڦ% +3J ]H=]ӶiveQiP)yh7Ih{\寴 : kc*paM;fiyQ9SKU._mŠeo*52!Q='Av Z],gxf,[󶷻<=eDb2LoQccˤd뚚z;R(jY9нQ, + +,9aU lvd*{yӥU`˖F.i̅H3Oψ4\,F"j".Вub?R4HgY1-aؚ'Z\eG̻%Ŵ'R-@[n3N'y.ZjpG編Ddn.vy /HQQWb z;wY KKLb@ͩIcdIstA0ʎJ- \#7ZzOTҳ5K aW ^柯6~;o]# ֯1xg|kQkm2̼ ](o݀?zK h_ ׅؔixt]kG*Y4j@śI,B׬gS|Ϩ|^t~YGŌcךk[PˈA EJ@22{G>KDuK߯W +<ƸW׈>X_ + DN_P-qP?*?`lXH7(}A<>^m㮇~szDؔ)+yʝ`U=W?-b+EE jί-JDWٴC^Usפv㷬!dc^U9fjGxf*oa#0O1%Myr ly;1$YY][H|d]xkQyt,v Sk$CvOYcy{캾Ɗu^gJW~;e[uCX'Dөu/m@Vi\ T&]Hx33% :KFҁh35ޫ2d2溙:kLźVX Qsy-$ N"X Nv`*/Tt+,`ypmͳ +~-¥AOv} 9áM + R`lv$WQR80Y5TWE-H y@-whsҐg X86R4]0f]^c9[7?6QPJ'ORvVxjIߖ˶*]=c+\.p5Es!l)J Y<6iؖbjK$g<g`d뵚#]y[,柨Rvk]Y9,51NTkI1)=zxJhFB~v.`i=Rlm]4YRdғIgOš^<+%<%j^Het.A:QnH(:Usaf<[ +X-iu9CXѽ xMPTIdǢYM! XXXX}M !W-aܮ-gVywQV`BFf}dPf<>4Y؛ 4ERJT1Hl_)K7 v@ǏYGW~-?#Ii^$]|$ ZcD# _!gԞGAqrєsVi ߍz_1z>20X^x䮵2t-:T jt|/مe PGplj1.g?fxf{CR$_]g<51- rgWZoiMatRH$U)t5ـAڵkZ#0k ,]h[0%$v=-Ѥ"uODe`qaڧj/p._Jc׬2GnL%BS3KIO 3JI^W|s 5]8nY 첧ٹT=4s~vWvfJ #ulM'ko9 d\.jdzZuQ]jGBtJhG+w%buZK'}Xlό; j`H.BKNj'ThKYvjLz%"UUx?EowǠZ$9 kGN33m3noΘ(NjY^F1 Stm]dd}5<͗l7<˺GfW?iwA}KBOqsWe{.#`d]F5zSg񵻯bN#(?0؋7'~E@j+;?tI +>h6^>Ϣv63#G M;J_<{Bϑ3̴hT_C(?1[wq"C>^<^|e܄*9rpru+c&$C2&M!rXEV9▆*8KFop +Ky4`;3TLSgw]I_h Kb$Ƣ">; ,p4О> Υ}EW>b@Lfjp%&6{|pu^Ұ.9UcNHpFIK݇`V,%h- ' zDsӨ7-ϋGMO'53r$zO'PRܩ9zڸQ9\u ob ֩Yn _2>6%|asА@4$5VrnD&xϠj$u6l5R#[5'78Fоf`98#Vt!T?LX-*miNǾH֮*Fsi" $l5򠃓c+9Vٚ sm'[r_A@fi*WE5=.g֞㌟_x!bL[5:W`טz?Pg}8 27 XeeSE7f\ն:F|]P+n4e&̄j +#[ j00Cfe?~Pɽ٣Y'X{uӦf%X##vF(u +1E00/Y9Utyd;;s%4|A, -{?~hOə-/|^|`43rȎB^9u ['m4/?{r|LObTqv2Ds~9+A%U P䘝UA2~Fi~)E~CsZ{0ѐt XkClB%@Ylol?9p2 *5c/;*9}F3^ҌF|ZooxrN?]Rk?`PQc,2$0g36mD 8/Dhò9UVu1Qh1o'-WŶ[5)#"5O%+u= Aq * V1r)=DD;ꑻkz1#(-~Ń$}D^"HLC^'vȆ_$uf>> Hy͵e4sRYOY%rr-gwf~wNGof|gfpzw5uAFQ5i"5Fz=mW}!F{-Jh=]3׌j 떞[xL3QY87U41nSP]&ǹCğyC~GӜ qRC\'n3h-yN 02r6ʴg#@Y\euoPmnQLjdْ۔hI㺘= }G`>WbLmKnDEljp`s* _at `yၨM~7n%;~lXhR ހ]¯d؛'BL Aφ^ +no è_YW$-<%a8Q_Ъ W`|t2!nPBD 3Ñ\7{8o򚎰wĉghRQ%(<>,(3Qzoy9 +[U=uKgGavuW.]}?4V1tNNQF0YSC]` +Ϊ/[m1H+W&/c #ĕ(VnKeFl.# yB Cٮ([@Kɑ4j +VUR[qA2CIĨ;,Uv=9[YV5YI`܁U@47z}5#n!2:^|) ^$9`werxe Uc +FcZo\Ɩ#;FfVB{@<+ ƮKA%B"T;Qo=kL+!r ֫0k:F Wuuc3lE߲R +yf:(5`[X1Wa?1)4 FruQClE0S?:&4CFЄM1s F%]s3_Z/ߴf+a4VQ:QsxNe44;^9>QFH>5j*w[O,L X͝&;*9겳Y ,0KO Xϭo&@KX>VLv({ƚ`o{j&n֓ XkIFѪ9VՒJvfBnz-+1un#I̪i& 15LӃӹcMV"gIBpMEd{ ȴKip}SK9J8SInBܽwm--:C 2rmqVb.[<+[^W*#[l![-[0{e r.[- 1zWNEWQKx%nJoɋ[xO-d1B[kuӥ#[Nzo9kVԊ*jörgHWjpWbb6y[*+WX(Xװ̝ %Ib(jb?u ؼnh9Bs/q8P#3Y\Cʠ/Pɔ̝!3Y[ي\[H< RXq-kP1 r=AxQ퓊hoA`~l{ak, vji)*E]klҠID"4'1M,c!3T.m|b>kfԣzL# ,h^?{ӲeR,T<F(brR'6zJIPRJ`$N3z O%e17 D_NJ@^xںBJv@A " LaC%`| "]@0ujhXIÅj1 +ypWwp4fQF200rD [DV5i0))gV~WȘ ͔x >[]Dw˕by7Udp dsf c"P;djR +MDjbsJdC3ͱ6cwڷwD5 PCr3<%+;O1k vU=4{;v=;.!t'ތ,6:JB4t>qtlK4hjb"quD}h7~[BkmkG@n@-4Ėڛ/TholPn G|Op{-A8gւ+c &fK0t[{ (v t)Lq}<^p{IC.f7n`W\bg_е@ssInc02P։L1ɱ  F>\AFYG!}{+b눁o@2 +ZG^CHG&=f`"҆CԆ4 +MC>N' )AH7h ܪbz7xK ذ/|x`5}YkĢ1kb^pԑacD ,ƽrAPƥ# ByZ?q(5fV<(1={Xe% L]Z]>NjS~\a ѨUZJD~.* Qԣ4[7lŷ5|6&]ڠ Ţ m@^ʓr]C,G^wQ-rЋE0-+3ܻM=fBo3*bLѩ0q fpՁ*Jj|KZTdsfXEg2`^Y81uIZ358`nf-Nva' vM n"^`wyH{&гh4}eQiEjY`A' .YX )Y2,*5p]^ s7hpYJ8:^؀2E5kN6~xIJ23]#/yzLrSg*7s]cЊJu0tŕ̂VkA"ޕ;ۊc ,'5A48W$ LZIE-?>[߱)0 67BX_0V#aД0f] ˽?5t^7;=F +3ؐ 1O[ٗ:9e[8ң*Db4C_v_DK3躔?FO:ǥ&'#; Kj!$=A,'~g"b]Yfg*UE)̥Xv +@ T`וv?}-zi[yJbx̢v#5ebl|cw?]%0EtQ¶2E;Q6o~T#NFl "Vp{)1t(+K[:JE@tOڳ1 + 1WA3w"|wٞKXYԵսiZP&:!zߍ&ի$I@l( QB/IQ5: +X}knOf L\o,[Y N !8% *Q&_qREW-i leMPfJ  hu_/*+=Ҳϥf_Ky_%Ϝ'zg0EnJ&02"i|QnozSF +\}=k 1<1 +zJ.ze,7Z9 @,[\E䓁5`{~⤑x2FdGX~HlGdyxfJ}fa Ձ#m_Q eZj|E(ýcW`Ǯ:J1m lG6 lÈ_r XWTVobSm".GcQPg4k(A|z3Oa<0X`b_dUvp>vF]Uet^'[OHP>(vfkC;"o}.ȳ/?":hKرni- qפ 2jE,o_H@H<~%+OTHMus6E[ gi%k 5wַ_gyD\Ϣҟ+ qɰJ=ܗ#Пsv4z'k9O%=O8?U0® +E.@:qD.$; eBӮMK$ut\EՋ} N|utrpSzI$m lcB'+ֽ}4Ȏ(Ʀ}*K`j=7"4U(n-Pk5`x;;4L0p +cFq_:ݑ&7iS- +J@?q*b]cX瀇swX_d +Sv!:)D)q84EHgH+5xɸEhbŭ{ȹTw@,Y&YY%uf #ˀ1 +endstream endobj 423 0 obj <>stream +HtWK +;P+?w7S|n1I_o/p`0??C+11IA211A5ߢS! +#B)`a /yOH8g^K aoE HW\`u +~׽=J\L?OO0۸/ocͼ?MKlr#|+`:ad373 >H/ZXG"$$|J7[  y&$f7e*hƙ Tg ~W,ZeԕggrU)G0 5?'6ߨC^~I]+1u݀Q, ,e֑fFNʈ7P^WUJe5,2u6 ogy!V)1@gn\;HTSQiJMV{ uΠ7I<;DE +) NNOW~&ZdxoLP%)wP&NZAZMt]S;W+4p;!d Lcv>tφf?1V"CfLR`͕-O^z{-7s]RkP86sMd{O|˄!-RkG;tMw";pXQ$\"-"RKNN L_~+Rc.&.,(čh3Rb[UpIWܒ1%-_Dʠğp@ACA-_.e\a6O]"ӌg%P7l23Duc]JH߭w~J3-Z瀎n3N+(K{=o7>/T4ScI<#!/0-KUw#kOـ! #sj +(3c "L)D-:\Y`?,oŹٺrv@l p*Fy B${!쭷RˇWթչ +ZL~SSB@>wS}Km_-s. 5Ϸǣ V<)Ou7J]Qv]Lo~:Iϳo0?ۨg߀ϸ>g:IdZ#"92l!>]XT|)1|45iQr?d}bk?ǖ. ێF\8'h}jAV(gj}.ٞ@fjuQ"$>0,GSmm]POasJ{ԺXs@GLuJmw#NNѸ`lbM"sܢq1-39DX\VMY x +̵A͗, 7i7TObR8:)XLA`P) Y6TemRVj}i4Ux9(CC+zQ2eˢ.vhxjyQZQ<̞ a dKکz̃pL5ef_vn<9 53zvŒO'A _Y>LwDxU-exZE&zYI^vNۃh*H_+%(?" w2WWB0,BeA.;!2ػ|5vpŪq?._y]I/P]^N}Uǂh"qhQ$-^>/Yd3^q| 364vtVi3ފD:gXHthg jDmQpI [x!z7vzk8W5 53J_AR4Gȹ'!ӽYNTGXdJ<[Wltx<$le6O2߃/ĹmR;߻̡yKU݉\AdFeB1 +zXV折? {R0h(t@&H>Ԫ>./>Fnp5x(Y|ԃhÁܪ\E+,.?2=8}t7֘ <쥭"NK+<d{ԒgM\FZnx2T4Di5սoĢq܈ֹƣge0MmʈdF@_ Y$у̑Gm+:uu}CjP*o=:!)k_5Ɩ+{!Һr[bCLes %XUj zTy==Anlъ-Q*wۺ x@W썔DcR&A +}uK%=l53aZ]",@'Jk9Cv _şk` Ukҟ7V\ $G`57zF쏢 `S~w/@"Xj 7d%e G7&,SIW@K0isdp?"iVPs!NNu$\ǗL!O℡ ͍5tQ`d:|>D7t/k=W^EPm_~{s7c.y&58R#S6Bh<6MƉdn,821CA|˔)4]+ǟsd'cnZ2ztd$ANPt1e@QQiL2b Soh 6|ow3"XV{q:g`=Ew3w`\ b?,}lT_0J6}MHקV5u6f| tF(w6g ~/ciBҲtq>՛Zsm/|?pE/;#C*Eelbq;-:O͕!HsǾ5b[8^DEHF[F+&۷޴G +0a%2k͢nifR/M'* ?oa= zou&h~=xhm"L){-%~ke;A,Ej0cڪ{&6R ޣ<ϤOS;:yHcr{BjtADǗd$%D/Nm'R)?A1NQ_rzd?/T q⸷!kJ莑ݙ,zڇ5NT$3PXE#70MV:)MJ7]jD &Z + m: WopuT}EMdIF\{SZ`(nޛDc]܎1[ѤȻۨ]*j2M%M,j@sYOPq>"1ޅjעNi=[U'CAo5.WdR $wSSy3m,:BKLʱ,U7-oINwdWN k0'~%MKע-s\xn +혎}?.yXʃENJ\ +vI? +[y T0O$C" $#a:BW*hSHG#rL^|a 78CF +HHaJ2V=M:EטĦEMŃú9\ rfI`&V#x=f|:tje󆒑'ʼnfٶ|2hfZHgkAd5ď_g^[P~w>18eFC۾TV.cUQ&skCohGA֋/r"нCfožݗ T_o kt]r,Yˇ& $(m˂`?؏0Va'TYpvt.I#$AShiC68e(]s/Reg|W*? +ʆ,xYӉMf ĿȎ0a0[/]H}Z~>J`m15DŽNsѹDp(%e oHF |X>]MEF] zKyo!+T O¸yf}+x'5k2d8@yppc͢i_N֬Sڢ?@4:IfO']1S9'2{*+^6`Qy+y>STݴpjs&N(z`z_nͪGxC(37=V`T]wl͋Y^/HAby+&ZS_SHh7 \)ϣ&_*ψH?9?N!R/GN*GP᧪Nɜ>W9#ؗv۴xFl3X8ꃘDŨOJ n`%JXZx"(e/rlMoZ&n#͹%?XY]+l:g͎ތ|IĕlI*i/,mgNCK.rl }#=9XYʾHt&[ijj)#Xetho4 `<{!܂lB`J{*?(Ғ6Zcof4*-dܾ͏ߋmRPm.훭k}>}s^Qڣ=òvENS JD4К4\yNM ~+aZ%ZLhz_LVo0+jg"}ɘιM T.Z{Iժ#u~G4-f&uhob^\eII$dfkQE݆ }+4C!1vMA~P' I~ATҁk}i-*twJHv +gƾ~؀AY9/m>tu%G~0"oLqďU4tdN&7 +XjYuX 857f!2N piZ:[lĊNa*HJШzQ|KFepx/2{kQ>~"Mې%9 WnN~#e؝孆BTF@̱Q1!j4QjNQJ`62K-ӏ[\/-[Ir3. 'lWv?ՒeGĶ+x_0wiOyɲ{sʺ@B$VoglB|2Xnя}$ jvT'!:#'tP<]F?~Hiil+K3jo4gpAGx)7is#s8ѺO6>WpSԷKdDoL5gڥ.[/ ^pȣCOJ1N'$ +W/aQX{gKb\}1ei}_$fB{OD+uFޕUHi(e՘#۪DR@9պ>5n2:R|Д=wVF*{Ce$pc;o6/QEP)/7^fTꊫEd0rlT5qhuIi6N $puoQdbp^`79{o/473-^c&WLNkЏ`%O'jlkQQ >1o:6?j:Y]eh1ݝ%yYm*3l5&:1e X,1J)WOR+ѡ muXTfvNdLn+50!R~pʍ[ju4DžOlV5sT2O,OxֈR~!^ шl-oi*p9E]Tb$ϲgۧ:',~os & W ַ̢:[hڥo+SQߥ0'}H:E Ӹ&<t~IFŷj +o:ik>iҵfn$::BCyȪ|c9/㬂w г"QMf+&$Vs/npƨ.*&rt-"WuĨ.!Py#b<^XcmS"eU5UEY+KШU(ЈDr +p@2ˠ3_Ύєغӛ77NEyR> }/.}HP:F$upce6 ե֐ζEc`w"ܦ<|U Jc7@! :o>sv(e]{!6Y/wbOS:Lvo4n &'sj&'ᅰoJHz+#b.\9`YD]M$[?+l QxP +~:y6~7vF>@/ǵ&Mŕ"ZU?Z p.|;q%PzĿM7QV**pIYj$eIqJ$N|zM+g`L >)ʐ򍰥 +ۨ{.lXLT, %>ע lV5_ϼ)3gAnWG)R +5й }őx^S|QcAMr8Y<>_Jlʍ*yd"E d[[ +MLlk;K)o +`?눹j㽟9˱I=;G{4WSd-%Ff~\. ily{D))s)!V8fOΎLd"%SUw&]F:JeΘo!ѷ:#6k6٨C\v%{\t dӪ&Z;j(oJN/ވVvXm\.6m/b~~2C쭵0Ê+uqEFٶ^7O0Զ?lC{B .JUB*hsmn1@@QWE& >i}_|UGr{ 3-a-my yq4m48an)6ZMh7kn E*^Qs%4UxrJ٘ɳCXaE> N. ԙi_ڛv h֖dp2+"9G*:ey;YB*_qywUz߹dyxvcux i'|c;0 'y';Pfac_mo(&)S߄f=$P9xQ@u\OV.>uzpi,{dO=toS+bvqЧ.3:]mP{Nz#:)!4mfa+vC|H[m Ⱶxlk(Pf-PQ¯jܰ>lmK6_pXڒ}x8Ё +S%B 0(q֬V=pLkm/d7[tY ɬRmSYv+'ͷF2qJg#md{xd>4ķh5f* Ne6db/'X]%;Ž V^[{Q ֵS〼XGʉTKA_Ctp‡EhtmerWAP7G頞ML MEy+" -@QCKu 2Tݸׯb`˃oaMJ|)j̊xÚj߄>y֙.$m]ORpZxy>&R-ق5 BXlW!]q;mq({ h\g0ENẉ!q7c˲؟+BCVm5l#uy3Wnp,"Sl9 mx[NYtn|o@!+!Zە1uLl!_\l^g= >YU&lJBˊ,#>VKxu:Y>?tb2`lszQavbi;b-`]W@qݣU|OߕUϮ=ߴgOksO`v\&!w<mϾR8 2Fe'kcmFPO +rî1lւ8 E =95c?OlsΣJAsm<(!YF5͊6,Ml8As(s#{a7;+7="Rʴڗ3; -[0^#"ɘ5QkzA'3&+xQZt}R꿁^c'Ni1 xӫZ? 1} ;֡ s>`1:=!fN\:=lUχ]aql}<ߵǪo(L;xj#^?*3C9x*dk-H[EvUErW%<{/Yjp)n?=.jYkA0tŴm\kW`FTU<7YFiPn2&@,t \%jrhtKSM|P=RsU= +=R:{ ^="5J$tXBk0s޳p${}[=CQEk(Rh ŴQ6ќP5TIeѺ/H̗Ԃz̗cׅ5?&JRWRw`RT{p7I,9ƒٻZ4}6& ps25 + F;cUސ1l9Bw&MW;h_g洎rxK%=^"pg̭|vv|wޢkܚBC(.O1x%\4iqq5ou` +}u~_B7JBݓ$鴵CUiONUC4:-x Kk>yp܎E(v V1,t,UT]fH ucjo1S٨uWɱ 堻0jpWb_H8a8׋ȹHٛц pCA:` + >y7jq4cy|؅fC9#k ]ʝ",|>Rda*% +eN}= JhJUry55kέf;kGZQ1< =ҰD&+eLayLȊwuEgx~ -3r$ڃ ,7p +,ʸ#6CmUA?4l@h#i7[|Zq9?L5zT?eEgh={C%6㲤4B]]š5vʘ2ȼDzg3 B +-%{qrFc,K.Q~R0L i)Pz"j-xiLihĻWfڕt|1pmMH9w71%&Nݎj1u{OSl4vu(ɁADSoLKq#I%0FYΫT7-S L/Z0=^!&=lUr(zS?mN[֞i i +SE]u01TfpX3I7n:ů.p/O{YUBy(ps,#Zړmu4/1[,oH'F笼nw`gqY3vy/O̵Ϙpޟ+ V"2J&ұj+d ?"=3=_f!<}Z*'ۗ3nҥ?Yɛj`tLn=[cXnp҃j;2^@S6= 3斥삠^4CZ;84t~Xe<*Ϯ?e~Oo/ZqT$`SdܼbӅa_~Jg-5zМKSiK<nD{#_Nr/3LPxtClBY]$ڳ!7?#]bfՅS[8($(C)]5-eP&~<Ǡ5̒ sH5VOyJ ']' (/C$*3F2^&t 碕B{ \C'!WfG3P{,j}9pM.rRHLGhA|[ZTfh|/'6fS.JfHom;v@UC*)ދrJ3J1 )wQ\cy @,]6bPtgߵ%ovtT[j {wm]ٚ߰k/X_7ӟVl<жz2;nq?iU7]8Σ~>sjP<Tf ݧb[[h`^ԙFO}:ʱ+AVz}4 bOOA!vf( Ĕ -H$`t~ ]/oprLŵ38t@n t`? CG0=az7L N@Vu)w/N3,@++2 +\>ӪN,@z5iG +u~\ѹ1)Ko@4tv+c]}ZȬ X冤a0+||QGz"ҹDZ\/+}I)svD*?tw}w|M? +7mL¿ޗ!єۇon@s`Gf楺@u!ih?qٜj;QJ86Vc]' DVv ÛG W UHy2PGEkpdoT_D /g1wmwsDێoA⧤,V2 ++HCRf/wv6%Jӟ+jVӶᘅ ?zCA_)W5 +,m%Ƣ(ڤѰxXg:lTt y*~ثmˇ]4He d28F4HFLhRڑ^ƨɜ(M}_ #魕M;FNJ90LX]s%A23:)H`5VK}}8vlNH#B8b#B9kF + F^u_teY֒䱒 ɟ][OfȣGvLs$sIo 'Efm.K˒(Db=袡6sҮO)7!7:>y}#]H 7.,+cѮIcM}!ߗ[@TF.e m8@7ƴ a#cH~ + +|1aJ^yN1%{ + "sՙcSB1{Kn{mNJ|gvE3~9맭mvzL:4Wu R?UyM\ueNg6{_nt 1y&ҩpVrP@QbM}S#94D%p n3[$YՑ9 ̖v訔DZO' dc1:y=t_,fˡpBDNZk,ǂ&+@uM;nʪvTڟ`t.џT[ +ҥljk+O!5үB%6"D-`~K"0@Ama1eI;R04({_mk}C蘴Ҷ(LqHO͠4CZ5ckZE6{oՒ$~sqNuOu]NOyo99멍 jObue [K'6 KIMq⮕ +2q,$ʋ鳗5tyfᡳ}ЯM&VXsq[k1 冡:/ |&b+WcGf $hDW*|Zx1׫櫎|>> .gbVAW<`;|ywL^@ŐYa;Nt b ||lSȖ~äA!$ئ.;d*o)^-A3Z Iv P{.TTK@@I{ճ>  CTs[~s׋h/\$qV4z"Jx@UӲ/S|qm3ڝ+ENho +9v5rG/1c+A|=5@d8\Ox|]q&/hQJIf)3ƢB +lq}Mx${Cq:JU1u*7El |p|MI6Խnv]cyzK[UZ "SʑySmGJ8q+&m{s]i9:A4lx?:uQ\Be&yWQUu{ea,}kz@$ƀYf j4ǬCn1qĄ(z9})ڤ;υxݿ _З2DP'lAķnQ5-ڷdt/әM f=y;cZ>R17]eN0O"NzH*owfk+. PF%ŤP줞~ki/z)hٮj&Ś Hzn?QFܿcc 5< I z!Zh툔E +ImMǥs4"e1πeD9x<DĂ*,l'VlP<XRSKka;֞ȰF(t4M_y D mJ'Np/|#l]K0hkgyxiowq#s<`z?C;<8):sÄ#=^ Yve9ЧxCGoLi90-]ִ[;U9'ZY~572!3,Q,@dXf=VRxC7À[i9\KR=J c8;pXa,`:'2hn^fBGR:l`;a8> GÈًɢ(`D BA Qo )A~ƶ~/SAB#Q 1scUQiP5?¡yawEz4>a4@<ǯ{p+J#2|N/7v9O8,P]'jzc`9œe +Ʊ=VFk-DEPQDnfu"F4mء@ 4Yɜ!\Iڜ`F>I<ô/V@Bxѕ.!:QݱFֺ6&8 ;. +lۧN=_%O\58#e>!4nqT8$Tz $ 2zz [pUgDbO5뚍D!ė,嘀g aBq6VO;tn55ƅdг sT?,\@dgcٚɚ [YbI]E0 ) ug#f1CAڹ'e +eL>gOv*tH/0ӨX,ZZ"N"r"SzS0 w K>* bzlM2?LeZqe5ccZ+.!Zs-=YhrS-5 q#Pl.*+ S>/Մq +w8Lf=m6PcL%aJhGCǺBOX-Jt|/K8@TWfVk\RGYCz84n"FDZ$j)Bi FR%b7r+6@htWw JO =L7F:}Zj'&DFkNrinbR UnG|הN'd^6[{F`;Wn< fc[J `݋oOiFB2-^D&-:lEO+YlՃ jkcل|[6eg>~BYL]HY݄٤Dud"F̑Gw{rN#9 7orPs!0^k4ї:R|6GBF .U1|cA(huqVJUqp:"W5cO-RP6 # Y[7Nuv#lTj=mEv{zq/2Ki?@7#}󞁼?@y."];ӉrbRQ*U0ʠ/Qx(8$6 d pkpn/R̄ +|fEf%ǖ{1n#v۸u*}:v>stream +HtWKn$0w +HQԺgٷ0Vo_Le1*â$~ \:h?8_,l1w{YO/1Ł^~%s :bkcdhX*tm5D m8&w$wd<&ݢ'2^ 헺d%uu G9ĝg +oZFqvZJigFt sʍzrLsʺ@;tq94==-[;4ELR!r1e֔ i'TU|3GZ$Ń̫RAB}E +tE2V攚>p6 LPҽuJD ̬Ѯ[v,,ZK떔EPū̕FQ|T˄)OGY/6eN$SSJe]&Za6RӣY8"CǍ|~W8>Z VPx"oG*8@\x0w}l| HTqi ^-/+&88vanOy|~~ʐ[{{]/ D>9 QjܒLx%Jߖ&Iaޫ%;  2$}wJp1cMss" Fs;q~ 9Zts<退=v2*ImA sdX&L4A9&#=/ +9 Ʒrxa18K'o(zNr%?-m^OFC}RnrEF𠲚AVi}|oR_PQ:v3:*vosoOn6_^k*.W6_l)(2%2|^A m&CjCdqLI't׃.k׹FvP9 ``}^A^3A7uLf_Xą+SZX]'w "XmkOI @ؓHKvNe-Jb{T lR -4VJיJ~ |E+V5"L-1+XJ,ҡ!0}tJg.O س~nw~m]AO˒7t^;r~݊BPF24zҁ-'\jդV1 Q@iI#Cpj%tYȿ*3КQ!YWbpM}"`P:0ƴLjN"=f |Ɲlφz*02üPVNVO_v>s`yNYٽqWTGtlox: +JQqpƩ:Uϣ{דE ѽTS*+GO& +V!VfUI-ϪV# Fwكdz[LXtN2ƬɦYP?hqo,\ @ `L$. + +UJPYpEMrPF߅󚜣U)RAE/m=֝C}Hl-4q+M"C2-Z7uXLCZ[Z>eZh 뤴*l84?>?>`> s_Yס/<k^Nؚ6#B8rN4gyj+Sx!_5cwLtvH%sYx+0VS~]G?AԃYv{ LeutFz v/ͣfǢ%IV1QfJ}7Ըl@.f3梁fMhkym \khukzf,vu vHOS{tC8wum䷺-yL#3]]V7ڷ1y]Ϛo<I|ZȚmB)GmuMk^5c]nu}bTFDً́$յK:hkb+1ץy5Ѳ][=w& +)E}JFwS[3zyi^W6jGGOL+>S=QR`'Wό ՎEJMd @TeMj-aDF瘯N.66(5wVLkn50@X[E)'Gtɦ9J1T;k܄$ݕ_sԕ +mǸWD.t'n薡?2/D#C۱6F(;֕Xs \C^5M E*+* y30>H=oZΪ=TϞޯ0J8'vg KE mzGv\e584wԮI;lP EK%UfQqQ-J9SbdldYi9U ,lX͍'Ig\oUwn]؄|:Bk\K"U}Mq/1 LWInͽ D[&`:3*:ⷲ7FQC Y-Ł9N!6Yr +dT[H vO*6g4J1.!qb^PJ=,Y~uOo }Vq=J '\gtO{\H(weObA2¡fCs;T,@i'7"' &Wlւ1xNhŚ,RA\ᠱ߶4Zs>F +fJP lx}l; K|'~yEqHKRaaG(_Үp]5?j6dj@j D|4&l5v_J'<ݯ0L +6jB~xWo 'Fڑ}:DpC-wmע{ ~C\; TalyGB64R9DK+CG_{" > + P^3ؚ %hxk:3/b Lʹ!PیU0$HM*Dze!s#~+ Y-mm bYka fK^L׾B\FB(cx<14LOY܉]-)3^c1NdF rW}+>9 vGz,3T5 o-~b Fj "kDܦ nl% D.sZ$f]f3/ ś jܳD&mXjϢk܁Y1#.Ϝ9VB's b'Vw,0g;f@@~7r{;JôbȐ1n/ܽˠznE&2'iاDhhyٚp*=e˪4^C" N9ѹ~O;K M-)aS~.f8ZhkcE1 z0,ӷpe[qLP׈vL3 ,p }#Njڽ9pdP|AMzxYyJ;e1Jcł#(:]r:QHb$GGd/ML>(sQC.Q$9 ?W't䶨1e1za*9Y%ȎX4* bxTvH(ԯ.^B{~ +o{`Kxdj+ 닾I>bW3w{YJfYkeeɣ"è}:G])t̎^aVkl|,;oYn,gPM}!t΂_B؄s-j:`E9@ýLJA/n=YtCL*T1MЌg*ªERSrJKZ:-$T^}`>gL].0 /eR).]6H?nniZ7c +ߟ,IH2e#&=)Og@ 2i\U@7E7±HA"Eu7E2E&ch[[1d-VI2o*[vCY\?Gd_en&I,C.,l,>PQ ?,wSu^H^nBr!op#Yٰx"Y|~^@*1Ӛy,,5:Ѱ/Ҋv)'R&ܳ e_@}>ȢT7EMt|V Edqx" qId"l%~!Y"unۜEp:Ǩba]YXy~Y:* [L;X ` ) {)}29&eTʇ"fN"6OjAQ󣜺 nO>DqurU:E,N4Q{r!D(J4֦R2j_AY?.(?5fa oX#=h$>Bd&hdb2(cz09m,y J?(.AGUb BN:@q/" %FCj\L[%m,K +[mEy@AGmZ:t[bļpBgmn#)ㅝ<ڵQjR="HOU@Y>sJZaZFw* wOZ\&6HmLÍ)^4Uy_Ktςz9xXڹ/JN}h -O4}\23,y+g3=#ʶ2܆cd-LMe~Eⷲ+dc~~O*;'~ b率 K +̽L*  +%p3yc{FEƨd+Qp^CeF p-DZ!ƋZkK|6zWOdMpg#Fx +Ɠs# +ArWi8(ю\mKDQC.~z$bU#_h*ؓWtVT{?0XO2Q}og)GUn5$aO[J+@hֵۯM3 Hˑ(p22w7V"l=T1a|[6&#wF M7rՋZ"ky9ɏ(ErY0!;Ã3Ìy6@;GU8'lA]#kF{ˑRhG_58]v! kHp[Ur dwFz0xR7Zfn{ݏL}Zr7^R/pAsn[b2d0̎ƐTiGX{16Oж.q,{0I铣 kl~dB0FTɈ67kb*\K9,Bȣ 4?źҋr3x<5%̨8m-ALL1K/E9}Ǭ1[ +An2Hː~tb 5ьD)6;MK HM_iN%!=*8 #!Y "gzY ;}o4pҐJȌX4@QHBbQHːȬY!}-Dوf4!+!Y! X +/C,UY!QHeoVjVH<|(O( +鑠^!`}|q-:TZVHBZ&x)%{B^$+dC! ٚ($@'NU++$G!d%jZI!Y!Y!Q7a BP~ҫdgDM|V B%+dFBf0쬐^㣐xUHԽjVH'PNW!Qczwbl,CK \V +X3 K Bx&4U-PӪk`n)E?sÏ9T !"W)$4v~ϯ94pO[ }gշ +OB:H+}®gI̺A} К%g:{G%cčGbҳF]2> p%5~a։(U#U^ IjK)_(Ui?D$'@3leIJ\U~ +6g^W:a<: ~ =:6=JE}l",V@@^wAuV!̆}F,a! .Yt&L"=T^~,D2LB1u&r-Li]6`[S +R[}[d ,[wks +6~{8ɲB&}Δ?צaIX<SOUw1J!#!:kduUu]s(ֆZ3Rg/Sq +]mfѣ.pNQa<>g{)U a/Io `Guw }0|T( ƅ+kq b2 }G JQsk4ʛgAQߟyboSiN6.Le1(b]87eV>mXG9`cx\Ĩzlˌ8yɱ;\q_#e*anppnЕegw8٠J+ZwF:g>8M#i$(}*wccH@:848.K[ o[Qj4ECsc*Ucabuث:n]IG!kF]9ZV8ڣT@)w4L`'Ws۷albd( "nWCv ^oJ ௙vpZ,IuVz}|fd[A:W #K>HwPW2 T+B=' ύ]6}~3ȊOj 1ыf狵[mwU2M?'<4~Pb%%I_jɉsԗ{vLᏠi9o45i<:xMT M\Q. +ą'2ZA:;krd!>@Nn#5"8C)[}#ԧ} )?O?%KsE(JT:$ jNl#xfO^J?gJDx3@R_iqCT !(vYAf㽪퍓\勻*7[S:V4's i+Բވ4|%% :Xdo3Zk!u^g}kOK=cLxP\ x! W-=`"Мt . {߽jJTmW* ") J(F00}u͜Dm2+''0Iu75wYiAQsJ/|=D+iqvCGc`G/J tZ= O7Vm NViݪ'EDij4)q}d[ziWV")I/'_,̖ƁѯoT!'*~z i +N["u_Mm Ynǭ_DɘH/2]R ȬhFغ*"h|$L}IƐ.a$,MpޘOI58Y9`f1Y xL[7pxw"3t=*(DYok䵮db=rD.,;gWq^hhT2%);[6GZ7&i hp\|AxGiF1ǍJhwo"s7VqXÛIK/&9Ťd.&2Lo]5jCZVQA r űn.|ͥTͥi1..^\ +oT +tC=\ju$9S2˸UpiM{q ^rn.@rDE.WCosߋS5gLh?Qś{JgEN.А7z.ťfzq.@z4:ՇKp7:#JPl;T]R~K/wDikV}OdDNWT:ԟuF!O+7"9aH*LlkUoRx7%M죨gQ7^)jjNՏ9.eC֫A"QHPU\q5R竨4}pr^EYu&/3&rJ/b|Aqd~| EN!sĐ?PʲaEYw쭭5#uvߛFƆHl +IVbV~Q 7Mk.: QGz!^^"۳i6("_懋?R~Ϙ1_*:jD*=jн"ȐJr/%͕JC@<^ZRf ؖAF(f7)vH}pW zӒ5^k/@Jv\@B·Xݹ5ت22GW~e/">hd~驂)lM6Δ}kL O_ʼضÃPI 3GUe S`ЂOMP<BMw`z:`#9- +g^ڶo!Xf-XF+̶l ofsUO"4-Xhr /HX|͹nBhz&<1 ;~ 7)^ gm2;+ }~\|'+C<)OWزfBLWTS쐫^PnB؛!WV#V/ZoBMw#[X"*bv⽽/Krܭjj+KR#C;V=u"ЪU5ūU|zHgm:F*:3}bB)ۥPIx>s2l[ *ơz+{aYtȕ@Ah@ L$ih, &9.`1ֲ-wk5_ 1נm/cCh%FxK@~ə;[znf r Lx59y QנN: ^Vi^lg1h4V٭*LTv[RgN -@7 {Fg!<3MfxT~rѬ9indf81B*; +[t^ji]wL +Ƀe-x+t3l_خW0Wqm\Z;\/ߵm6v.$KsbM$̖ر_}lSv:'_?۰kVOT l%^ +Lz(J'' ynիJ{C4_MiqA1=VyaHb)YȴC$ŬYE-vcx:5Y## Z+ ^U>Pj7%[bV_*ʁ/q`g\RX@sB.e; +ž+,+]a}QOqJ+,\n Zj:W4 2lekZ@W#13#z%W^ԴC]I+!MuR>4ڥsأixR]:UWijM_i>~CZyI5PVl(M竬dmh2xu "\J;0\UVxYg6TV&βqeej.ng䇏 zߤ o4$Qzq$-;F;[6HxW }-Xͩ:F,FFv4z3vU#8!nNUc"m~dY-JUS@l=+ ~?F1ҐDPOj(̆_(A?Jem#u'}18'D#k'Z)0[#,x @8i) k<p|\gtУA)eug@"ⷑ; OKeL+cz%KM{}^^G{1^x^^ЎȬ"]hs`%^dq?B#Tf ڪ}šY_z;H͋_>ECZ݋{'c:u0I\ t @OJ^˞ F[.Dkx? aHS;^bVA\FM,n11hX =`{E߳hsWmumX:͐8,H-hꀄ矒9~@ec`HP;8g˺svz=kkD8">ZP=;;+A @atILR@]sb>lư.q8*3}&*C-C=ÐvqRk @VA+Uӫ3QuIB;`AzՍX,=?bUpxץbͼ6Ii=]H!HXl()u#(U*ge@x/&2Y2<_*{ C[IAӔI |kFI,4(=o^i&}8:Nwόn's_;ଇ\B^ pJ*t +8R'N= 88RLHќW\._U|eoycm@`Ə p?9|[m)&Wޮ9yllU@Z|8@`v?HnŅFxtqT?^Vy]g+7{N_9_k9^ C4|?st=b4r2b? =C+KĢ]JF.wt@4F{c?}QkNZ_H sjj?F[k\#2Tl]199ckm%fǵw5W}k#(z)Ao>72M[Mi؋ḽى,5"Ǟ2X b!{܉(x\6Ih6"9OU74.xFж(' !__*  P|m(*[CΎTeAxš^_HrڏݦdsmMqMF d3^$>XrΔS{~ש(7ߵ*w0ϋb [6+ ł3$X'mDDj}0Zg2޵[i[:2 uHxXw%+:EwOu'Uy561aîrԨg86j63 tƳgpPb"ztWYB@ H/>p_ +; Fݳ+>X*bwHvٗW3RbeK.1.kɵ,S`DMkIe +R[=]H!lMv"5d{`4E~qco>j/5qNVM.V< 2ޠrۦ71v +nb gAd}x[7o[:T&dɑLv4y&f}$fH;]v%2NJ66u>6`D&2̍h wJ4$Fz^<2gn ekkG283n%"oM=AOɈ19nC\ PTLϪpB<kmgR6վ2߀5U7et4]#[ɓ K~/T^1a,aq&15]@kRTA83Tq!f.jsv OClb'6mOi}Cz +&TC[SMSȬza׫lxk +vd#t k:ҥbr +58"f Jn<0mWcj\e6+ Qռ7@3➒C\jmLij݉@!Goy jE˶- zͦkBkM"^FP1?@MMx\[9ˌ *E^d2^&^NJ,uTKʚֵY*Fn}?buc{'x3^|( 9tߵ=@i;\|g^e.SF i?v*YLˁOd=jGq#R,aȊ:IXKW2c@ ̦5hoBrz!J3cUDiWF?MzmbL3{;UDtK[uXx S˺#`zi-ײԱC"$,s iVn +` 1.ʢcꌓ!ttǜ +ū!i]>$^*ܓWD/#RVe)(tMWM1Rfow-b\v鑽h(&F\/390_T⥔1IM`Ϊ1Kąi+mƥLë8K0V60K~r3<1my%.(Z\eg{>PȏydPj']a*Gpt/B=veΪxʚoFGwmZP ˲w۬Zݏ9b3{n!'YdjsGwգٵ°d:mvv's)TcqԲ6]! ٢*atz3ɴ1iaTG + K調,jmckqFЁFn>QGa0^iGK QuKVʫ]c +ii@N~غŬlZ\EHOvd= |V"d } ͉uH.l2^ v/ 6d[ƦIH%v +3W8 Ϙeٜ? x5qţɗ[XeilmŸZQbrNYaioKQ{Una%Fn +L2e!ɴ? V9\ 2ʹ[A)ɤKR9Lr؍IU1 CnG_Vv2h\wZ緆9+Re r׿ּsFL`~" --[Lˠ wɝ 9z!= %l-~H)$;WW'QyQp''>f|5&ӏZwr\O誎tǰy[,&?GXqQ[SG_I"f<[bdUޣ$]*\y23@2qɨiK_o +u[䙘*f|l<A)﷯ms/[\Y2,RtFk^e]fB8q+]5|g:kfH~0<" a=9vK@o[>ZV3j8S̒Ю~$7{n9}=w,ٕYn@K{O=}~Rߘ06b\te?N?" 3oj^M`@mLK|Gz{O3 7{c +Ik]ΰPkhE-3=~܊s3Éɭz}|%v!{8F=: @Du[˖RY;4uYZ[dmΣ +2motI\_{vR]oV)$VێV5/ɥo[Y]T7֥!cr2_J]Ue`rE56GP~e$BPvTbo$;ؑӿ8qj7[PcS/(EV *d v/Z`]1QW0ʻ\ p07o677Eջ&E)ٺ ϔvu~T0sqV˕^%n9Jhi)fIjn^-nU_@4>/%_0@A8O'fn[1gDp 2]D uc+̖ւ7+Ix𡂻,B֘s9Ӈ'nu34GnDRQ=vqdә8KIZ1jc"JLy&GLc.v- 4S?_[!֎Q}6m(2G9ؽJ > tip45H%ʹ (]N*l(9s hKTu(rL]/{ :`G 'Q'5}U@|x AF(K0Bzϝl`r5 = 7k9]ZY +X$)顇PJp{9/N +endstream endobj 425 0 obj <>stream +HtW9r%;N$ٷPķ?XzHk=eګdV+/`2be8jМL贷ix}Z4xe5ʜTG`F0oMEZn[<L0#Ψoo!) {uK6Ci6%mftH.y00i fpYjZj͸̣;~yq8q0)O`!7 +:K/FB GȡE?ptay Rslթf8ل_e-_-/|϶$߶%fo՟b{a Bx: <R-cǬw_s5mhZ7bYaqop ِ- M3ҦIR(arSk +Go3UJ-<{2blKv [%v:|.yxS$. Q]TϽku +}pʫ8KkU[BVk1oĐV/ uZ"bCBdɎ; B2J ucʰK9Ua*\G&i!>]ft9|}Y^*ϑ`_OłXL@5FfUcRt%B,^n!3ˬE7;xYEojahU/\z.hj%5jgM-WQ֌ם,ֹ5@:cU>buU0~u|^-#Y>?'!?y3E%q>?C&\K2Gu Bxo75*^w $<18dsuҙpj9UPg `|͈<̟htt`@і5~\cFZh2ݹU n gGųs~ܺBԪNÏ}"]꼥1"X_bx!Fm\܊I 7 -0Lz͖F">-IiwRJiedOTCV;m!s%kvl2'2ڞl4Qdse^D +џ^?!")g`cm'?ÍpL7MNgҋP5a&0߭]E"V*['i|p؈To>'#]$/Ks\ae˺wb[F>H R7!,7ĺV$3)7g( ?=)RB u* l_| MS^{t7%oy9aЇFf^MdVz/%jhlf-F|EF|L?'$ΙN LNl`L(u1A``ઝ2my8NTQ)?_$% CW2+ [0ԖJ>RrBǚQDK?5ճ'dpm~C >W(2>+ ј3T. 9/ ` &$#>[cdVmA<"_HcGvZ)_.A"K>kӕ!LW*l#xt$~tOgcv[AG69-d = O L v͖`)t?xoxu 7v?̤ k`﨨};`PGh-`B5&!#F6h:Qu2ikBǬe%Fol7vD9>OW^m<`S}b{=\ + TQ7` X(!\. YwyUCW4I C9Dl@b*߂؝`e&GN\+!+B|}F*0M 0ܻPnzCHuC~eHr NN&JJB!}bE|p"urTY0ÒK' /C+`mi XUA>B2l2:V̀8n=xa$TXB!~@j tY }Wy'1v:>c?(dkS]{&Fw݅|?=Yܕq +cq>Xkr[f`29MI< L휙|z3wlo7L6ZYgh}7Q?Xӷ46ۖL5_TƝٷ{Ou0wS5lp4̖kbM@<W:xp !>ZG+rWGYla +Þ'>Uٷb8ZfI*/97 #YWۡ<["ngLgBFdE},9/gm}Nl͛d?ɔX ѳإh]} ~QDErW_*MMGPF&3w]d4Hv7P߱i Rz5V .-GӐ(`KCfO]c e`H؅j] 9Rh֛JqNГ I4`w~B .v >4-}Ǯ`]'~}e\ȃ ُ: {}@!|n1xXS@9{9z3e,:@7/`P>SEfn +,} +jFΨqˠgI\#)NPg{fC^%y"yx{؅B2=+dCJ` YW)x0:|J Ô*9(9 gbi+=$;AY$@JײM2J-g\i@yҚ9XR5]r~#{-ZHhjea +=o=!{_;8m6GE/cxRoEMٺ +3/v$7Y8 eXOrpj ,JmJx?4Ud'\"XO!i{+ǛjKW-KCf&GG"2GMrtE?.t"oƼ0 YϿ +2%pP@TiiQ 3Cxw,WBo*J5ޖ&֔TZ jzT?;Kw?ɯABG$v16kf%s#^eرW3^pu~d5*gثu=TԎUreƐ4yAt'DW X9)LĨ2 ie752(B>tDSkJ9K3޵O{ݩ) dN(8@Dd5g2s_.Np4J=WXK#$ѱ@xU .O'~_*ЅOHίyܗTME֛Re:<`QH5JLIj_m1"ZӇ/R9ԬE]64_ +0fqוc +?p:D8NifG U0nD k35H& +k6dsB ȘIYq~ĪYJh5e.錔)I?ק"e#.msW0IJ>cVHZ֛Iks(y9ݣ$Cvk\Ѥ]- 6-=c"ɨ-ÈU؉pz8:;eѢd1>Y /P_o +>̑g+W~_4}ߪ< +s +-vQHOs~m^C +.}ԁџ0LOO3N((BkXZA$?O&7<=w,gwoj3|:7x}_o}> z]zL F#{l[ &7񅎂@ ݉D!ZHl9ZɯCc2.->5GsB@kGݪ6*Z0cҐQI6nGYz,seDFŌB{iGfBZEOfx%;cqKkAрTʘ{( r@T'=͚nK¶_AzeƧ栒70w/,5_lƱ,$ĄH%P('8qf#6B-_@'xc4NXзUy1?C'FS7K7.N6f 6. .B @ICY ح@N7Z"yf:3, u^+{-̤G*o2 p/bfo d8Fj3j%#YD$yă4(tW99 O0O+%N} O .OօpLT~r?t *^"[2a*4S ?nk`q'},._V9U&m@k~8rWvBﭝ@H_#rC܄7%١؟2)a~+˷6nohgYwcaM>axX]'P$ R:0j.GDNv@2Z#gɤ|EH69d4עЩKy4Ox(){s +[%,z;yd8|Mn\WbĢI&Li;~-p`Y^P-V0.[ZuhU~7b-`2W9~fLA}嬇V;/}UNO8)n<ߋh_~I[juUOJ+ 吮1J,iyMA꬙!i dv?m(ݪֽE=cZӧBrXe$l-fK=@Om]@`s{3Gjz'/aהk, +ci mF/O·C]!?:!>k .`2C;'irϐOrOn.=6=T?o,(+b{\֕m^EZTz鶈=fY7O=-+xU?Tk@[ 447&#Y +!Yh-qV1j.Lj12ܐH(8PFmhBbysƾҨlKq&7>2y^RPjM%[X%Jh!9*\+Գp,T׬:w}Uk_OEj'`jmҶ_^! +;H sd$*[C<)jѲbg.l +Lv{0$4-5.X> c@53~ A;l&W$Yvcխ'+3I|w&h5*rלS,C/{liϰHE +k)\^#5PG0 +iLlX`''!q8˫Zb됕. +m06wԢ +"*E$) 4Ɖ>ztܞѯ>USB}03捽vF6ruUPpj5hWVYnoXs}yAmhEIm3+.o3g&\*"4sBk*1fܭ>Rm`w4J]Ozq nN=P[Mqxd5<(/chEu)6RibvXՊ0Uk~u`yud`FG7ͤU3M3utvf_ߜitx_{˃G{z?NoԫCdpu?oc jL`ƈXR Bts:=4t"q ?"gmܩBqj)oj,rxG=ئ6,f8*2[{0p"Ԭ +CԌgѺEs VDeZ&vT9muƂ߾JS\3V],Bz/c5n)Ik2*͂渨1RS!"$G#0"[ +@G&ykky0頡ئ*&`t?[dDLY( [G◙_ngꀨsL`TC>SQ[e|zd9he}ܭG/_8q[4vZdq_)tĀiX< lj sPג!<1i50$"۽#2+ N:N)؀! 3[9ӋjXzW\5)62a%uܰZmBP%jh9RqA/9T0Kk_v,c [3Yꐌ9onP[+5]g}q\PԢqO90*OoFrb:kKV\2oʼ +p4z0>3UgUM<ʡs )wlA!,Ϧ y^^!y,GAkT]paݙ˅V⯂!Oa l*cz`'%盗в^<8{y:Pyp]bҍWih֢0NJe΂kq&,ɖL̿bÅqL=ߙRNF9T[cjf.1fiVP)~~oAm1خ[‱^֭2еwF("w-e˴M]r&fa +%"Av|Hu1$hU9k%-R?VgPd2[ +F. f)jk40_ ce$kG\XɁ r6[i5GY+f4+aP`,!H+7[ZsC2uq,#;L3o,\2[!U8tlf&E`.wt9voW0ޢbw?Ǟқ- )DmDUԨWգɿczҺP;b`D`3=!!C5^nK1茕>=FWŏǺ#`5B]χFAs)!T ,zbik@BW[T(jSG<c%46/48tJ!EP=ZldU*q0 7x'W/]|&{ͩҫyhJfD3׿vFk>~w!אX3Ak!tb3@lQEu+ ( +MUkGGsV;j;64ȶ5g.Q[s4L4iHjâUHiڃS︊S4L2eF㌍ћT*\isfކK,Lwld=/^~ף#E~L|Wۊ:JݰDU#Y5-Co IַXFV}4 c8gʐy9*5wg5Nt5+"3H!̲=̬+V-i); 6Q.x#n`&c&jlf4-H]tXq,/տ"$ZF?.le".BH;p,$+C<>i+@mEȻa=$aZ<'FYQXLaS(bZ#nݺ!!YG iTj(vzEZk*KO FOƩZBD/!b!O=Xl +lK@k+(, k0>x;jܙ*i5hv6(_p=2*YԸ sڱW ++KڋޯBܯS|XS2?SM˱2Eg[ΆM9r候 MTɬQTHħ\uKOh^b7PϕA8Z(ԯ\O;I}aTO ߥyQx(@8c=d?;h2BҧQ1˵,+ZlAjxw'EiB -Ѿ֠y yͲjń6GЗ Cd<< 3_ێ~PkP_#dgA"v^a)nx5i($vyoǀ0.c.1H'5KCd~R~bpw9KNa߸Tعu;˞vhLqr+?#H`g,9?;i:SSB6N0[H`5/>MعV%S;+N8gb]uw]0uLOTH^I?ϝn{ޟ8w= ~y}Pt؟')4'm\ש`S(zP&㬘:yp(u?JP\ VZjB⺟j?u߆q_qqŸ>7SKv\xUvqԖZK/_*-Km0-[zh>lFƒ &3B:XI)D>~ I&Z46Nu*?sYIUuNSH]僴|1ST.1h*ieI|/Z[lBx*=VǟK)]o?g:sasˑ@">'?^ LG0}ѡ >P] XYr.dپO_@&2ײ+(ӹqpGgjl-|0wJov޿L3ݾ}\,!,% +7/ɇhV7a4#a<0E=<~L# UGZv*z/+Sg=-3YR-"&I28l.{_jq"nl4@\.$7"v.CF9cGw!#%>`nPA *>urZeQp[rtwjṭ{ t>p/\Fƶ կ0goc,Oт0<_ςl(Y^} wLY7v~pܵU?bܻ8[g+݃ +udNR&e"~z'R4ͬ#qP\.r5 +(lm,_fŭFhPS)=jh_oK q@o8l͐j&{[&fX6Ebq9^[Awhpag2,cGsFG2 n`Ic~_Ovy82&wrcȮSkfEn=8KW~mdq͹_Ǫ[DpP(]#3$Zv+e{Ⱦi˫aq~o"gȝGOIND1@M5?̹ll<,Qw;+kQwjylSG5b*H-{#&#M%8I]Tz:v ^m,/O DåZY7x Ԑi2dxy ?oDp98f̬ n{ƵA/2]s=J>IM_n+刦qBىSiI;Vo 7HvU`4J< C8ld3`3xh T i< +83:0(u#:27~d*m_A4U)]F::$ b'??KCl޻NldzPJ1s¥DXkN#~Q`03+𵬮9"MvZ +^sd|L?bS")`.IS[XפDHP}^.;99Wl\)d1GBNlE@/o7Olo@AOe1E$GX?INA-p@zE6;!VkKڜRt3kfdN'XdEp7E^:,+P%}W[Fr:q^u.#t7rp!9zan6s8p֚(S3 swM}f+NjQq^[3صG=4fو}Ź AZk2LS20x-%.򐌗'?$O~J=jJ2(Sى4ڋb J"v*±f} a7sv0C<<49ګ֋ 9S/OyU/zapX gsNSM_&\ [WHK.$\Zf\n8L@fZH8Z2N[sES-򱬇=Ղ%jz8!R-&HaopW![e&,TO#2 N˶|f4^1`31NQ>Y6ie>aI>L<*n^.6DzIYZ:DCײ9ohN3rl\m ?Un\Jj6OSy} XQGA䓺sԈ(gӱZh}Gxy? ~r:3'C/$*?]0[t;@̧ 4[޲x5<;t.{Et)j26\No~kIc@iuaxu#?2zq/U(~cIdE𦞶I2Ex4|Mm޹{"P՚HZWuLwj1'.RuCFfe>vv(hRbm)rDcmVr?¹QzC :d_`Zi`̋nE0tNL'XfR22r2DZOEeH +e3Y%Jnh2o˳pk@?%ol&ho= s]TX#VVTS{X5 +`=JgشUQ3#+&M^fq#[ӏi'+YZL2Mr#0$H?U30zb}KT{w/ERNΡ[CJEp)"<%siB*HȲ.ٚyH;h)𔜣=%%\y!ʰpyRϪJUzV~jnen,Gx|8{\lײV 2 ~ ".Yާo3/{Gc]f8-u:el1 + H4UwW fl;@Ӈ>޵t dU 0~"׏ܧM%=qᷢޗ^G%.8|_Fg/:4eVcܝ}= MKI9 bW`MA8^=j\$q5z JDI*>-=)Fa8MCڑ,xʭ'@ŭwvk'WLeOً4R7K](|rwnl5+dV9'_Y<'ճ`2OQFG"!)m(B}IJ_p@A9Vu7f9 +[8 ,g9ٝ*[PE;ʸDK ;qr?% q8;_"Od\'еɿwy^/DUϪt^.ɶ[RFg&bfP܏w&, ADӐLiqE;~bEcҥ\- O- ëFU}>湘ZSQ`SKpbr2ńH釵KoM9e5<!Z)6u5EZ_E A*4h]fp#aVsK1Sd̍3+cf{tE߃ ˒> `! ET= c#EQ/&Md1>}o=}w;|=e;^\x٩y<˒.x- +|iYx/~z S%:Zג{#~zDx~c(j3e RYB<Z:r-ڭh YzK# u 3 <Xg;B77ڍ@OB҃Ynzyv]rj-T/n]/nT"-|>Ba'M&a# zTc rXtʷoKиZIPb,PҎLQàvB +tz2 +9WZEGa׀M[2\4ZلT\^"`声T_[j|(d5FopF3&q'mxKV1#M]uX0vy<,г< +잆P`֣sf;FG2]%cMgcOSH= F7FPK\Mv{^ ^P^m9xAV/) ^`b/F̓uI{="Fܩ?ˍ""xe5X(mE-~`ļ h)rbΓh"\VM/>i/"/C[^oI]/7wN lgB@zè+g!^q': ^O6t\d/%y ^>0m$xyqST7x1CܮǢ1a7/OO7xE]Jh?^C{BRQa1ѽu}x0cbGTהt|+Q=ҲR+PeM<ɮU+SD<(NCΕb#fRTR1,q/uڔ6y/\pfģ<%%|iF/v៬Λ/F.6V*Q]Aoa۬dElދόn18hȨTdࡇU0B.%6ڜrE!fU# 8MJ +Ckz(yiCT}2*޼S|1H?ĎHɈQl zD>?_J"/DhS[a%4fSm0{zеڶ0ZEƅ2{`2x'P.+iG?3YC*Q+ usx٨:7eT1UfHDT9v}2Z{B_RȴL.$*tvVTF'hle@)FeL+mnU=v93ӇnFd)-|ATK Y|W%fv{>Vع,ov䡣bz*uZR'x[Ί({,& ,#KD?XbZ:V='93\,YZ\; B&h:&Ln[Ȳr*&8E<vTe¸>jpWџ1);^߂Qb@c8sLzW}>B{ yNa>QRHJh!UI,t8Mdn7Ydx-IJn->5ƣL!gNQ>~%_VҪ0Ty$%:Eg?DT-GsJ*4UfQ8w2S}&4mv,sͪ(EdK:J**B?aF3/:-èe0Slw_4D |{ܑM•axeTuϑЭk\ +1r)ѡlP"%[CMr%$fjqȣ6n +Wz5 &q4e0`ݝ{p^{]Wl]k襛Lx#6cuAZEd_!bBr8SJ&k515I>ltgHGkȚm?+c5M +gad!PE +$ ڛ+BnŞRw;Nkz/҇gYeĺ\]q|..|.(8k״+(.CUFϳ޿qUD㬳)^*ogǂW:E=zB,빬مڪ.\־<<6$͆)b#B\b#ª2OS-}Ug(b֥Y 曂TzS?D"r+IvD0O({CnIP%UϘQ79T#K{r}Ad3S ޣiEԡ"ANX^}\gc髠#ɂ=0Ex*[:9rOzȣ̈ۢY_1\g_ +՞>;͊hrzSjՔvʤG*9s:K880|T08u/z?&l~5fbT{KG1*5v.+hFHT ǩ`+8sUq^H8ؙ:N)ZLz˼s5¹6)\~:NX6y AkYC#N8Nh4+qZbeqZ{l< +aתQsX&-8HaMD}NN78?;$/H xN$7;'H%Td\ͼ25M(S#_F<ʪn_vSsMܐFĂYܫR ;{.EEENk󛆴NiA&-lx >3'HF0iތ`%E;'(@h:ĤDTn>ZYգsN#N u:xI'n:UtIn:7ሌNNtCtqZGNd{xBBDC ZzQg c $K2mjisl*X0Lb7Gq8=mK_Q(cӜ"Jȭ Q{6TeȃЈ1됑R=Gs;!thᖄɣE{Hu}9[T*{d$o+A04h@3ffb8#lӰsڣaƐ>@XǻDVmP1J[쐚 l$pQx(VUdcU-V(aXۘ64i Sz&ir(vXi=eVvjQ&=HZi{5'"IЏ:]cHݬߞB}Z=O$0{َeAˈ~Xo_G~"#FٍW<j,L~:Ɖi,gƧ9uTC.*2Fy` +y#C9ERtEogWLϬ$2]ra-k5T`n:]a +-E:3ha9ռNxV#%l^|-5;F$Ĺ~3epz:ӎB*&w"@T%iZb*CfuCASx^&bYZ`B +۷ԑa]!bBYQ-3kPn#ie~IED>stream +HtWK$_ZAHqa+2"OS(8/R{`_Nx~ `}i3"Ok{岽2^v)D-\Ն*dh'*W}f e6HyOD_ۀRZ؜DBw0)Ca@^?({r} U^x:Dt=WM)ye8a:fDL!@Q%&nq3Amd۱z4rT'Ҳ7'Fy|)g&mÌh7-9x3 wNDej'.޻i]ܵ ,m@h)8>jR>F/aLi`m`֠ ex':!PD7C4Mlҩۻ؞F,aHkUv#nթ*@l"hC\S/f6E6M=!'/]8EJP,CA D4gçDh<B<"3VSr=gD)yLEDZPciM|s2}S+Bm8A7y-r0(>›c mp.c2&7I[oH0|v/z sF׻a] s0"D6eA#{hٻ9_*N6ë: P:E_Uf3\^ȰN]E ݀$]|n-5c:Zy&}\9Rʌ?ؚEhӌQ~g_Zk2&j'̴fQ*Ͼ>tZi1j!01m؄D!-@"!jn aEJݨn40kȊw@`K჻ݯnʏ3gQ&uIpmGAUD@R':ܺsINPg1R +Q ]/ސÔ<ʘH<{v +94d%`aȼY7Kh -a`Z>W&. glׂb8 )W7LqG0t5!NlreX:p &f. %xZF`wycVscuOhDv_oӛscϑ9m.o2 Uo lh=<񄟛FanesRHE{fj;1"ži"929ȁlt!HrK_뀬Ff22G]Y[;rX"d9^nk h7RB݂-zO^`엉jZtݜs/"*ְ.Ƽ1!D: ~}6Ж_u3gNl8[>}AGrpYUthbB0DI$9`L FȠE@qgz;5sQ 20᎕.36DZ] dBұ"`o|w'csjHx~|_/7 0p{ K(A&=ԁ[f@* &#[kTAɸ^F{_9/XavSV>b;.7<,]JAI9Bo[O19)n&)!ԲIM`6>@v!sQ,VwmO}l&ņI|m4CQkIû{f<-gj~fՁg v ,PiMc +| Er&fH" ? R$fF͍1s-02Nu ڱn/c?91R:ӓ@Lmwte8rtE9.DOeK'r3nD/- +ȡh(\N1fc wf `2/ֻ5 8El|옰uiѩ]l^E`(qwd͐Ϯ*+M 9<;Ea EC 6Uӯ^5ٖiMڇ)]Ryz=حeRd? ~ ?6?`XC%?1:@a&y±1t_c*XP0RBcY}`u8Nfuaֱeĥ5/eǓh ;)bZn A+TW(1ݲ'6z٩*Y}yd*Fd$vnBf޼T|Eo;EBp1dsۧr;8W.gTmY=jM6UmU3yz:: D$|43c.TsOqpx7K@5ֺ˪[n'}%bcKs٩ P*1T_jW nK]îVvsZW%*X^6‚%G-R]U]Ói[_3e9K8#@T*h:Foр#(@|?MmGm7:Ve8f`#\eSFL.GF܍FS(hH9V9KM> +=793t2y!z`Y*ٳ,/)I蕼_\ [_g2l\l)ܕ >A}Uܫr?/1D?S~!nD1̩HrԴŝhIfsZLNLpWЀ~ L&ڜb'* L^ǥGɰf, LXǛj" A51 +ڀO}eXhȩJ#S9j5u#z: U럩< u>}GFM DqRHhж\u^R}'YWC+9b'h:=R@(C%[\^byAj~*o%n fF H:/,>1m%L |@ ^Vˊ )EZb}Z|<ɚu{!T]|BLom pR/E%v5?vL6X%ѩ :,~z.9i+ӷTP#sGdԨUE׌},aU YM]UV]o{ d.q\SuX[qhZaڽ ̴LGQc.]lƺ'\`}#3;7 +;Q#TPu _4it:mI}\o7*2GOTEUOyV] b4")2 '@p␔6R7aA/}^,07_i[tNTg$'A"Ax0yī EaZ.cR\^ː.[Ҍ;MqQ4[W(7wp@6BƜdȡuZp Z`m ؑa'#7 +>-2%Н|U$75 lHQ"\FP)H;B4/[S ("VcYWFxyNAt (DE=dbwDReFkEY7?\$gYmԉڊHlD`SDEA.dv!o>>˜p$U&[¶V01M{?}Suw@C [|gF9]/`g˲b]±OUtvr~ᐫ1EDPx u/RqdArx0Dߘfl{}!y=ԇMOE]́a~ AG1ϷaɬRc>Bmt*jy%kLdb̵!!7wCb(CztyܨL1΍195T41yRIS;) wO p,es>mH:.igJ0"ZSprq]ޘ6L~I/$&1be1|]b +9h?f#j&ɌՄIq#hٟ?j Bez +iHHDU&p rvf2SדXUc?*C<"Ļ1ۉ,Gœ>@*y.a);* '}}I :IJ_zS)LR)j֖~.MQ@|7ҥ0zk8z)O ð&ܗ!~XƵ1*ꉣe4)DEYN (bOd<#~@Pگ(\Bsu1Xp8@{zV\~{`|먺@jfv{>LK$@ ϒR/WN2B2d h뽬/>V=bhi [=a.Kw$oɿB2 'sYYTN"gwZ1p"Cpg&8IN| ō,?3j􀃞48uí7- +f!ܸsǙ.* ^2ۄTzfFML{>l+M/d!:\#Zm>\9<&!6ݱ9*{R=$,_ +Yb GOTn jZlǚt {ƈvn' ՛7Ӥg +ÚEc0=c՛\s=l v(,qO! 5r$+)a_3́Piz <|)C4/D~-0*pBMIMq2F"嬜6#j<ҏ2Wg7NQ KՉ7A]ϷFpjMa r )Ɲ͡|ױedjOa܉КVQL3+=U_61(VO(.Ͼ|e Hⷳ1_lְd +_Y>OTB$'.^`?g1kW8uYEAMq'4k{Qd}P<e5(ofCφ߻>wciIbcN*e+'HIfu䁡83;v!vGzT/j^y9ز3w6Ocf@YlsjLZv$CTwۢ77B|u6T!&}ۛpxT\:q8>Tm}#ܧd'є-L2Dd2q .x7z@0fw|`?@3H'fdUFR7NǨʮH7ɵ2Ӂs!5٦ +`o{JCeg-$_˔0:TdM">R4"hDzJ5Vm;jV<87ʘ8ke3rB{;;$:~BȈ03iw~N/ҼP-a)vHh+SrF_r| '5Js:͙AtA=ʷVwê  rf_>U@K;"4h;; ekt0ѲV qJ)Zԝ ρ8MWUE;fZ-x"88l>vmdz$/̂+Hz(Ih7 4*gѹhRs%U wYuzDZY"!;[x];Ũʇ;8=b'DzzPC!1IM'&TP+g n M ԘВrcmdz2Rl LK^x6uJ%8 p5Ӭ/]4k٨|7ƙhKK(c?^ٸ/W|Gj?J@YCC6Qm:[8࿚]-@_>{x<_yfN'j^\xSvdW°d}ƙR>uf +,%>Ÿjc >(kCs6?D&"G1QD8uWZRR'HL(/m}.n}=j,HS]zVǏ;+M텵XL+Q$DmV +ly&(l@ܻi{|?O]4 -tM|R־rngF4shvEv1d +窸Dq3PpVq^&]wF0sh!5ehS%8$6"1a%^1N'!cIq {wz.]0j)HLC@VM2ƀ݆(}cS%wq|w0貃nXܞv{ޢUx`r68q ߺ=çegqzi2$P<@FϠ53H4\ p<Åe Mr,ۖ`=DvX ؎YݖL",#$c`<Ԁwϻ;!wr2 + ,1 ` 12zͱ !Ayde ?5c s:p 61xFr0x-(-%20v (_c^;sH8"yn[d=&W|X^}EtzD=ؗ^U3m40٢UK/ۮoA~R$ঁ0z 4$)Nt 7}Ãm/5)0Qr8^8=K>eIأ&ic:DK0\d 9?rp$Ɓnlo43hҷ@l`6޵ym]۬z-pZ̏(3+Z5>z|*"Ifpz[#Nlڇu6a18d&ˉc6mLmQ5\ڱia7bƾ5)Y2c?[^>P$5Molk ksg-:L<~ +eո kc\aڑpJzX +yYP.mnJa0U4FC=ӫGg Mğ:ՠ\GW`ͅx{"K0g CpkJoyiNpEϋUmlˢƋUco6L4m,Q:4L\J0[D4zcQk*=KyxJkj,JeFNh6Š +R04,5=8U5[ h.Rʱ1|+mоsS:]K9PNX.iOp0=y}j҅_QA$&>~R,5™NfppLAaRαU +\?, >m斈`jl5ټ!IA&i)yB"7TIanو϶(x "- +2aW iq )7˸"ADs@P%GAcH>ŪiH,Vڴ(J͝#7O>A ,0AxM1G:E}Y\I'JHP ^s`4ڕO0=sڞ6dLT^}O+

kV$PeiWI\L.GYzXC#-y8 =/՝YVyEQ2zj^C?!C=4wJaU4Q:" -wV8j} "t$OjIIk`ɛJQ\D->ʣPH;yJ*7O^=Fc קti\`']Z&7c|kt,{}Vd]~瀞zn-@:noR?_bbiۧ ,wڡ\TH\1q0>18h]}hg:V`[y +c t0A0MzA9@8uփO4_Ws@.d; .RH~:KߢW:#a)cI:4[|δGЍ]=h27Z1#?M^75><=0,;_i ,5 d]yƧ\Lӿ[\}z[sm+9T3~$l 5$1yn;ѹA#MSGwAx Au;1$!o Lmkjzi҇3p4><;%M,cRD >ݖAQwmrm} @kOiDCC:Ł hҚ[1ݶ:H8n!Yn 8g(ܯ{:TeOђ+O*?;k!vq"~C36f0`XTTfpN~`tԁ.Up-h%Ijƾ+c4"Jb ?(?lMRQ gҷ`gDPCmZ#fW{8 k2Oig#` :(Z#c^. ;Ki޹J$z¿{ 0W `fMZk{c-i HX]O -_aEZ%$ϷFPs=SpI<D,|5,7k +jmeJQoh SFr:p⿁,k,E lvHRcxa$<.Dx3~)b +u6<QkKlT u @eeT^XAU@dhaU`HCO{]IC@WR㉝Wް) -fL%KV랜6_@p "VKH5uyILZXA(şm@X!q򲋻?RIЕDu_. +Lo`i2rV# +Yhr)&F;2LaOiN\g a;şLYTDdqkg}{mBBO*k} @3e6,rVh-nC:Tnܱ0v&ֵ3mk5@8/df8unZW[1{u.+9]bHlvhsWъRt.?j:.C[C\*>10c#(8\3ǧ#'FS}blI'F% 10έOn}rc׸7_|}'v^ix\/ЭOnL>P|bݥOh>!U[סOtVJO},$85=R劑yO䆎qg:þ7qNUSiXCDRGZ⋪Ʋl}p+̎jAoXLV ٝyl,eA# 4ֈiVf4-j 詽Cx #ioY96qնJfG3oIՖҐ`DhrK"MT ZL +VT%oHzI|6]Vh%~lȬ602ɇpgTpcqVrJ~X~b{0mis &z*Qm&|7nHlEt+VkD +@]ɼPQ9U3̝VHW:]d}hIWR 1ƍdHgtH{Ë x ObA,=}6p5< p9 Xh<E=d&0,q*[ "@8V6KKzƇhxa7A,|f:$ [4J/392\q(F ]W^R DX +wp\2ɒ]eb[Qxm8}@[f&ت}%  +X Fex_0F"JwB_frF*o'&nOP'd!Z/$KZDL˱WɡɑU}A1hl"?tۮ+Xﱩi,wCnGy8F x,I[C+)ZXzE8:o+BdBds>(kD´׆(JqzD^vw:<BC s.#!)c*C|sF@΁DT!:2mY-'< + 7Fk`?Eog[ƒϯ E @SKHNl6a1EڲX/dE/LŸd.Q-":kn3,n8in[?`딉k>:bȳE #мHO> fmz.9WKh%ʡB}E5^XcDS/m^%2JhiR); s=cS` (-JL/p@{Hm"#q-tpoۇ'r.YE V z,'e" n-`FECYXV أM` - Bn|Lc@Rm]sGrt&|  $%xWE`-a)_Pz {ҕ x|fc# Or* 92+Flt +,ygԘuVQPj~̻ɞM?<ǻPII}[r"% U~\/b"9ޅ~@{aCiUر.j;| Zo΅T*;vv^~:yan: ZZ3G=u_47dU-׸`^4iᒜe@8 #n;ވ 'Zr&3~<B[QAYԇNsT?nV2NA'Πش1V,5KRGf oY)i*ľ4֓+O`ΞcW`w:mQf4.6zuP|{rǬv$-v8G?2_08-V.6R1翹x (RbN 5*@< GKkxk"ڏ!SQE AI)B$R-ZYZmYHHiHTvE踞h]1 .2$lGR?fZv;MPN~ ozbwA)é?mrj +}R7{UG!1f=Wԡ%`f#q=V&032&+PkWͭS +|G3Q5RWY Ł:XC¹ ޱAתy`tDŽSw<1lFЯ Y +7 .*57.; 8w]Uϛf7VYl!/@^cJIf4.W/=S '!M_qAz| |aYъ;P໲,J2<xkb{M$svxe0R[L0 sD'p!!Me~jl&ZL51~>I;/_Y=2&DZoVjh=1?'X=}rۤ:t(=џ"vpPefq( Ӡoۡ3 pxXF3N?8WL̀/{Pע&#vx_lqvUx3V3pEe4siM +,wj:}֍|#tˈ<9jt|XiHr1P9!7b6.JQcW`2, +vȫ$`as>N5-fIfZdC>)$y濙*~jdD!9e.<0i80I CЂV8 s HIfw ƊԓIi:ȐbcggiKE̔ M@+˚G!t8R;zED1lOxշa}Eg]3*/U$˵(f&ri`@2 ƌŏ,\!^+A4]j?Z `V'PBaX-a;cӝ@C+`S{[{ 7Z6tPDz*f(_%.Ș>d@n+{KYfd,\'d~:߰W? 4OA^tqTܙ>Mzpl6`R:7e0ӄrUӷO13j, o)vL__11xQ}ǚi|#7ĉLsз;v:ȑjHPLLӱl`Z|UU;{tH[*F3[ɠ]Z8(+0/4آ\HT~$呟(H*Q"5 MRq9X71 G Jٺbbmo+ +,x0-;Q{yyq C8S\}{. |K}ls"1"0;#6q Dhd40ٰ݆1;dKk1Mn-s\g/L3Ѫ<|XJ|;6c }RvT)1;%# (fR33q*- 6=RC o.D2أgı'ZB Hd4?ך#A#rw(xK0v/8zy$zj[uƲMu)=`s(=pLB٥s!DlVIjSzovJ5KuR/Gl J=|6lJ[eoj 8hCѼIǭYziPz'B1-oǻRztH~L/*LKC6e2hyK[c$w<B6c&jX2ҵ2 [^*eQJաe<b ~<&|'AvLz`?}Dzgbw6$ݶ74LY#X-f[F&Oİ9c̿c#cdCs EX:~I;ƾ{9B9;`Mk|ov JǦdMEՐ5>F4 ]y-jE3j$s7S@ Hg"%A7m Cgdń@=0b48NCRJo$jer5w`Măda>j>m*r_"N:`iYHX w:[d^·C0Hcs I2ה(7~Z#!ѽHBqVL!xcQ!|!vVM\?FKE5ޏTIM3)nI>8[]E[V=FJOZ#7Z`ܱ7f-USYm5VR`2#( =`C ?;l߱+'RRMm';&T/غ2reV6VF}.% @|ʌ3#c'78tŮ.ɘ1wg-kgsFg<)@(75/4*3HR ' Md̥fX)QM}0˺P lrb_dACIu g &y >) H>Z?9U :=N#x`܋+;ٹuk֏52@4XxbKΧ$k#k`%L4]8aZ2%O4Mon气 vwPM~@=mp$1V)0P48JQ|Y囗CRѾvޯ X=#9PGA@iﱽ7KA&5(+:G?a^Ңuu`ͧ /@>)Lt] !B^ ꅏ|d 8@UtNQ SՁ1JiS@(V]b`t #:݈F,+^l8qDDQlc7; +fa=UBfSl<3Y)N#^|3B r$p=\ÙC^,p$d:(Ot$H˱ /q6I~:MFL'>b#ld>]Hg\]Q4NFqYVƜ( Ց)0)r"}(,r"!ZOQ9S/EGyDCkV-fH#+T94C#GC *x奆%TCCjx"K l!C cjhZ-:Ј:O5~CӤz!s;UnBNRUԃBX-DZO-DLrj[B 5k΁ vL?l=jceu׃XNrYA#z͑V5i40Z5 +Tt}l!%V ~}}rģD(NUxk#BoBRu13$(0[amkc#ʌNAMqfWפfw +sF(A0.3uXaȡY(aFz e=2):Ӷ <2\~f)l%%3=':cQ Յ8ۏ7\0?c;]l+Ǫ=Yс4mq[ՠm%pAviI?Vfr&pDHLaˆW ɞ:ߏHu%>7kMDmQO 5 .E +endstream endobj 427 0 obj <>stream +HtW;9'b'xAć-VŏUK| "FxYk&=A#8%Yځ$&+1MLd.DZCb'Ilh-YDq}ů&_߁t _u?&"y(y;!4%ГJ=wtĤ2T:dÝS !_Gyw[J ya(A^2_0t@#u뒳 3܃_yX-╵-?E~.iѳ;ã8Bܸ鎃*a/*/FD5Po8&$ٖ2'KqRըqPn&ih@-avܜRq48ygЛ@*;uU`8pH #p~ -騸 p1c(m4|6J"f/R:l2J}ցL +FEܔtWuT+@c3-^TI +HO}fۄ,/3_ vSKh9H| 1@\nч;灡vUG-nq"`<2VC3_& _&"x\X?2.dAxJ̋R~|2dZ{sA +l4ejT"BۃNXbLK(4ՊCVB\s_Tz$-PT ^_ڦ`4|4Ҧ[ r7ɹˮv[89MJFn.Q7ݤ>? RTcGgSSbT#4Pj +:mGfxXW%~mNP[}n+WJv@fq#0Ύ@H(EZ +.<wjtޣ>F2-WBUr:QXYt-;k.RGp*q$[ +4_Ϩ;1?`k׹#;I Aq\|YGl]~v'xjUrx[pjgǶ:uda5k5$; t̺U@;*x;Fyiژ6bT5zeh+, q9Z 3ϸr5hop7xśڧU +L{`t?Tj6!)ZQyPeW-!o\w h~hх8QdGd_c;dN_ʆUulB8ilFO[bZglAʜ9wYuY&0ģLvW #= {akүڧk1Q!QeC~$rǦSȢ؈//S1˞=G* I {n0u"XfNF D2Ӛ|AfBbߍYa8*S]aMUnfum9u?_7Nت$XD֡oK) ,J1bC)7~(]/pR + +cmRċnz}ѭ"S;}{xh'뫵?@NmR?Ɏ[wpKxp=q_u?aQ-Q |pTG[w!/K-ZZwj4sݗ_Z4wjY}W L ]-nOzXU^ E,aWozAZM.{Wېҋv|-rorMQMr.% E/'nw\־Wg]&ijk<ŤJVy,5&mڸ;p Q \6ύ?2lהsFk +dtCU_9㹬3f4Hq`٠eE539hxjT'Ra(V";)đ&_v@sΟi8AƔg\ 4+w2X$\1S &jUTsiTs>އT7iBM1&LWzed{ke*37.8W3#g-mi^̭z,|6FsS !b| ++8T|=5BĨș){>9Ƨvs&Q]m=,>2}~j]H8׌?fUsqf;t}`}Ng֝gwM^`.N~jc/DM&A{PZ\Y'f#5Rt6S_Ŵ̳ @v? +4z[NpCmvzUuq5i DcZ?d[i[.֞U +@#tfރt8P؞LGĭiY-JZ84 !X{^Y@<9^!ĸ`T1g'$룼WhtXӚ;8RQTQ- ?0D/+gRضO͙#8#3gMXQ֘˪m&Pj/u63樽++~ ̆LgFE3F{ts #QU-%ofǢ^2bR8J[*a_g:Jh+#!6XRi 99} k2sLe5<>|rĢǾ-+Uɾm<vRXH;tZ;2MZ$GF{vx4u͇C:1'u2ݵUNP[HuL̩ V=-J /HRH$Ȳ R!S P4B1DXA|vrvl> md v_E@b}vo^Uٵ6XX$mF߱^ 'A-`f3ο` Fz=A/ +$8uWC, d?*H( eh{ )B`b3GKo%yXS^V)1qG-yzۍ(K#,ǃOïo)⢑dYD'K [ScqSQB_4suX]uu˨2T2Dʔ 'mrdEds!C gA]_02Sp`Oz*a\aXׯmS/^V=Y>-1s"*P^M@d$D7@qת]]*ƚ}a,yTu,S5k9}6NzKs 7\VVO3$ʠ4X-gHoiPLjhD뙕uuzT[=T.μcZ9,RUGLNf@R>=}Ԩ9d}ǔe}>>\x:*A<S#l$qo -^bN6#<~ ; +1߃' l)LX"4K9vn7J F`Z:.Q{sV-),H"1Vj 8 +{.k+K.'AG9ud9r͏ԇ1A{@ WMh3_2h9ihe2SD 83?a +$RJjf]qayTyw5K^<*/++;rC`v ֨UROR>/iL+|%X6g 5'vc [bx"( Vلgb/Βsv,!z>׃ņ+4!0,20m֕;͚IlI6R鮚4tHK{x'qUtduƳ.Wi,yk"2sU/ BBVy٦]v/E᦯ObvZoʹdt`}1Nkdvs|>@9|ΡlvLl1f彩>uA ETP/6our`E澜?S:Fr +\X8|H8b5MlaK_-CB #jν޻j mXb\;@cU1oc$6~xlqcX@ss똰;Ea7P*tVu5Ҹ>WXv3T\ZffBΈJy +54,-XzKb϶ژOD!\6oIXe賻}&]YZvij^S*J בT QJ\ˉ#bؽ`T|(N +ltKXzw`Be ͒bё!}e+U:g)&ӷvF +j5C_;xO&ZrodQT,'ctHh&65p9pkqȎb|ڷh$բHY@BZ ?0Lp'.9v7w>/@f>i7]wmP}vΟ`ʶHK#SB5)?ɢ.H +ƿe:PSYp̸l~R +$,Ŕ鱙k#.|;)C7dR 6< +ҮODkO]>wo*b?,An<Ѩ7UNUh! A&e$&nP-hl8fk`@DЮѴs4'w{>Nˈ1|#"" sV;G +mB5JuҋO"5rijrPcc23LTUMվ: :YO)fA7\(PPu$30rNq," KRpRf%M[&b0yʶxs! ׄBNt?@8իk_r$%:qIWZ@߅MCetH DDP;_"縉6zVdU_Y`E',Rd>[S_⻔Ghbp;=ӣbz=mfpH]+k讖$KRv9úoG̚Zt+5$3pHUMQxjpFGL<6D镵v7zťTYGmn>#BYL^`KlˌCTGw-q;D~2o2S{Kqi2tZ07UXW݀| )+U!;z U^:AvG- Pe|jNDl:xT-㼻d9fQ~C~xSߏZݽ7X}b}:;5˧+1;iyJʝi2 }e=} + g8ʐ]bwce\Fx$MT:aMM՗I\}SyŅa88N|4|%'1䝼{Hco)s5@kTmnI]4 biݧ"wFak +lNP7NM/ $jA?(,pmj so'/rU^vW<~y#<-Eq+mp0dgz/<6֑5F;;&%r6©,>&9ҚJC)1c:G]Q4sUVQOƬ[ V{ &rdQ2]9Jm,ׇy9q IfVL]e܆w.n|"|FĖ2Q ex|oMv}[S9uE$}ѻB7$[NE"ˇyLyEP +Y!lI$˵Ƀص~֟j#RqČ57K +2kSJS`rN[ K޳cGM;RJ1vT +cTTB%zK,ֳw؍:?7]iSdJk֟\=׎f55zs%#{!nlaڒ,g~D5#Y;L:ӥ˝yן(6|vix+bRGU|L$0j%N2\O8j X\nwD4Ai8VX*jsY &}6 +OK%WLۯ˽ +&ك#ELEhipBJe"A~scy:\>J}ޮGlYQ Mo|˽e -"wCncWkg1AhSJi4/ËW.CyTґZrUZ7hq1օ{_˻6Kt~m *#s*YiEX~򷡽Qx'=ڈ6NK^a窣h(9UJ: eL!ʃ3]MNXR["eW'i@0ԗ_d +Ep*s$Rl@M;:\N{H϶.ȿm"Z&:^^4TL"(~Qԥy5 +,DbD QM|Vm#]N{,bZ&Df/јdG8 ++Y~8 0w<^KZ թ^6txS#>XRG7我hWn/W t!h@bDs68CCrɥg` ?Lyߛ]ߒ,?GO)x|njgZ +c~밻%zcѪH8d<ǴfmL{䆽=Z߱]J +t}]gfLjo908N6U%hvWc0RT;'粹2ɒo)|i*#[,k+#UR7b@f.UAR*Əe44^x sպj )vVx]kmP:*#y,y/ycɂy<Ī2DܝKʆJSsFq_U rjx.1wEY"Nn" hjVZHuV``?}?UIzpW(G Yy b,AZ 7)HtmRlMG47X@S$)7$գmM"Vv(؇!o'0 uF eX&N Kj~/3✴kCA+6_Fq́V !df"[}7fs};_W|{"{KJcAP3 +skA_/P2L2Hb6>D@b0 !6Ƶ>r!v \skf/~Yޠ;ҧ#([˷2d"/#0Gx'jADbX=q |9 ?8}_Wu~0z>CV4ˮ1rTL5tvCμPOlaӖ-qsK(7y/-'K/Js,j1J_o셏Qwб*/zGcTDf:@~b3 uΥ 60Ϗö՞X&3އشPTI?,lKluUfˀs#ɲ[7Z *{eV|`=&L kD + +RkLc"ysIܨ28(b?z?8-{ iG,"2#4'?[ywmݻȉº!ߋ&of.1^VՐv"hA.B0z) 6`,c<gpM#2&UaT3PYD+$8%n$F#_6!-D,UW4m-DlWƩ!igtX.Dd=kC jCԜeMzֵIh*E1X&=>>25 +Yh d0-'WʆhLf\%ʼnKbRxW󿎅S>BHhu , +m!@#@+R8 SGe?sO,pgRػʎ*>B([o 2/!~`B}HB'.!cʔBJL..#mT;B !ꥃZޘTnךeؚ3$ƅXGd_Bh6R:[1˜:xG_%oSRqKݗ\̷Z exjuШje!AG) }}dEu]2jr =odyA^ȣ:ȣ&K4uЋ,dgA޻yqXod1JuuW-̃A$un2Hʑ.mKz2ߑeicbee:zeWtf uV(jʼn nՀDž,58uJ4mpI. :u-'E=*ţh߈ +R4"mDBܴD3KH}庑=RHmQcjf_`SS2FŀüWFt VpAm$};!qӵoT~S0gG,͹O4 G2W(VDIqR[ӶzGcx|@˴8Jქള{ +WtB[⪦[VH̕0]4%#TW> +CN\XCwJ36,&zyq)Do=-:^Itn8;$ɛ^6. +Sl"3L.d#1 d0c#fbS.͍S4qDZ2V| V󉲋1䞖ad|:E=˰n02gCEjnT=d_\:+ԤbhD[o_ yqR _DfI#GF]+ +#^fΟ +8k52ӻ!SɎ*t(/quJ;:cD,ݽmaFBƽ +"{UOՏ1\"Pќ{6ԭn:^}|( 4u<sr(ON(Imo M~Xn +hpOdчo +RonP&)Fs&7QI4XA<یl +[4`ᲜZZX$k?Ld{f5\$ۚ~ƥ݃~x?OsjGka[yDd쩉 +^I𴺯U-H[wo0?vuq5v$u߻qvT= ݽһx>ƙFb̏Zܺ{jH\m1VxDJ&t!7-y} +xm@zJ8`TV +d?D= +ͨd#џy}Y lb+#{ُEﶊf_1veǺBfChĊ"Ǩ3Fqy \.un'olʼ5jD3{EuS*C[.a!rK?6hzR⮵>Ӵ=Nvv +d?Y6IΠhd]Ms#o5>D M--G2hg,,7QT`B~=2Ov<(Z6Qbpܖ_ljnY:yv^Ƴ=ym_;uJvX: +OeXt~YTݎH u˚3) "dP|p* z:-+i[@F0E(NRA5NjGu@ Y]YԄsj~k짉 Yk3RX:J4*уRS+M[bѽúʖ(U~w_ȋu&6&7з=ӽ5l"NHWQngp^XmY.AGmӆRMh5Mg*%THZΧ)da(4ϸsL56K.w2mvzJËe@&qVZS_GiOrE8e`p{. y"12{!336N.n/OD(SPM4Y?GƦ.47JZ0"@> D(`2j.~`QB38@ .$ށPe>"kdE!jeƩUg8AhXbkDVC@}4HZ x] 3J9ݕh_z2`h%|w6VHcȪ )9cR`QA2y2f}شD| ua#y{*eeodHVP ן6VPo CfL]@NB3&F ^,[#eeհn1 #V ˺eU2u:#z6œp04{ +\vHqjJr!o5Z=4#;.țyRǙcPtp!12XQ(-_DrlRNe@NS3xJ73YJ(l8͢<_?Վ& î%Qms߿ HBt;XSUB2[na.֭7җOC@]3r#s}=$&ofɂŒz>7)/ S!B/Ȭ+ }.5vM~vlJ! Ś{F +BRKiʱI7k]JF.$&w$Ca,ҺX5>J8ܨ2pcly㦻"nx}W In6^ JO ZjڅnqkKpvvP0(δhĜ 5+ w3iJ 䳍Xlb @KzzÆs3jMk˨;XC?Ӗ&c̫yP@0 M1R%6`K +$=(nrT3Jd.wFxB.S, y"J|Xe1=7F|A!޾8Ɓ:gbYLӗF &0sxRGB-MoBpfR@V1ֺaUs\V] +a]pn۞ie+@XL0ϔ#- x뾍1c6 HLK̟f%)n7" :g\b tB2wowvYV> rɳu;4V~X t/$'gzFU:y(u&R)KSn1ѧ۝xM6_Dy,U{ "|\ +Ba w lvZQIJJE>@/r˗d2,!vΙnxn nyEQJ}dfѫ~Pe|V0s1jyvHc2ֳ>we*ZR]Vkt1 FOrFXqy䗘MI/o۟./Y(Ub&Wָ +&IZ6 +GDR^qolso\}5֖3.m6Bp c}]{bm=N8ew(;~} ?srfl>rxN9L&r2fpT^l+ި@09‡Z6O(zWeKwzcxi#ŕopj2LнRp%<@hYP{_s٪9>xv>`UxM7BOʍ8uH)B}d:Gφu Xab. (DW:d3i4>6o`%l1E2_A|̬)ܐ7dTNײT8/R;sl[,0f'm&-\@ӎ] V,9!(sf $I)iG| !JF +&GbQz quE csuoI޹x݄d7 dDW.'Z=q1pDH]5 {lc,-`Vzn]jyf7u)d3ye3>qIOŒT$ڸHyj:^QׁSO] ?nfZܯ؈ yux]9 Zݾ}`0|Y??y'c>a̤F$(hUaT=Ṛ%& %eOYDQ9ᣢ*j(VQ=M$VGQQ nؙ}8BVRȪ&x)Bemh*6ΝYE5{؈2Ħ"We3|2)l4'-sm`k"wO C-{&\1~_E[FkzԐȪpj/qFekO"4Dz݃U%^6 ;1>[ADlhaL{H&24߱!wOl;7i&mq!~yn·<2^W"ӉP&oM<967!N`uqXiyj{R*9DЭ8^ POX=,aԉwnDj$٩2c<ۍw9u{Z7уS8tID1PbG +?OCŀ>3ECCU,[ٍ^>stream +H|ٮZF+EAQ;D@FA@@E;lQX>uQu3d||kBP9Q9J\oByouٷN@ x>Sr:=u|XO81 &]UTB븤Vsj(ÃƮ66սkh-q8{t 3m`(4i'z? dG( ʸ?mM:Fް!Y/?qYD\'&sa{BìvT h +gr9Ym=3^hDkJX_OIWcc<׌+e=Pż(AN))YZ:HIϳ .C 5oe} Az\|zdN5`U3 }M +c\cċf& +LF%uUEg "|, +HThr_djsx u >xTm\OѲП{?t#6;|Z1 =>StOFF ?;?v?;,.fxFZM_tNgO Sƈe}2BUt+j34N]B} + IiBѽX[[mN`o.oZKI>5'g#NTr7=yՙb,JRPRHq8<;6޷'՜2U8ywfg6;mP`=fs xbo.E̤z-\c% %NHjRWOÈ*"hjwGNsxLPR-(lAS♤j ŖL7xS~_i&բ7q2[]{ gO\D~vC"dN:xM+tVȜ0/Lz CgϹIOmk#T߄^9'\@P#bFRGu;GMN#P @`<\EfzA(,3Y4i58go9@W gOlai$$~͓S'׮b2,Eb)F i;tK:Hu]^K +*A~s5qH?7Ԟoj{2=P26@0v$fNƼXG}=:5R(,0n[!3 `{??9(I#@5+ۙ [sy5u'~׿Jq[ד<=g2 u󴛘c@E~VO[}?y(Q;оA[.=ּS[x9Nm-\g؟?2*~r͑??"DPbۯL/:X=ryY]~s p }M6, }8s186brCS@R9M +B~(ST GAEڭ6SnE4Ob&ydwK{:C m.V6լ[Xdl.n˜{1oo_sxL;XFe~@W4 +p*>}ߖ {@yk2\Jo +֍.]pf+bT0ýqrjYA}ҳS|uYNVӳvOgJ_>3N?Hja?C{Tz.\f2U3Q-*W->S:`)u&^1_+{3r'3{_ڿ8qIQS P RN?!] ~xRꝱgβrW3J.({EZXK Q~ˆa@g(%}MG 'D(k;N#ȶ\ '۶~>H|&I#a-6Lp8a6ue42jE>:Bk|f b{x=OVEOOge)ZyG>=z|Rx9]*Q}AP#* *3HĜswГӓU_VQEؕc4o^!+ &C03_VہZQẹ7Wznj``QfF0d >3FTfF;}4/~cpJvclԱՉՙp5X/힥d[i*cY!K݂gZoƛ(ki4 =} 2{vziCtk!`8 ڄ5z͍³a 6gLD`ǽYHZI^Er3ʷt?4.*O4v`V^4{q܌Z mtF!hMao{tێXNyz & {G#gf@WUa˥PVޕGїVOdf"֎C7'*-Z +.0o{Q$^!G"_{ہ:N}kUnSEgir=W?|jZD4({.5œ/!/PfGr +8xxRpP +^X ɃJpq{MAP@9l8cA U4 ]ؑMQSxS.'8wR*~Ty8ȐɟW ZP`B90 D2X{zqZ~jO=sS_kJ +}`{P.m~CO$Olm"BȊXeE˙g|ɊAePͣ3 |7-Ŷ0,9TLۜ$#$/y}3Vq5GMMn-p3DRZIñX^qiRṒ +Af$2uOo%%(g]QAp>Ʉ%9]h׽Ldݜ66ԟUC`A; C"#)ak $gGVFOaNe<ǣbcr;5+ ׆lo]6SM`,,),ӱ z2i-b}1UgCdecąAe Ya;v ~nRC0;ݿy|wtq3QgM֟@{&(K)д Bs.'֋؜n)ބ=| vvKD@xLt +=b7of\xf-̕ +Ykޅz _v)jޓbyyf ]5{-{*v$"15i3 ̏T[8R M Q)dqŮELFd<['}4"em>:Z᳐~C6(LUDIfK +S챮~wJrrzv}JֻfP sMBzyctt}46,)*+#$f#cI4H&3ՠݖp APT-?x{HSsueÉu5&17[W|Q7]łʋNQ(Zh+]dx}wd1oXxuTSj\m+ZS;iv髈=t;:tBi-ՕP+:\*[Nfzʍ~ːhI)}?vٛ")BRT)ad>M9Kz'@aog@ATݺMMGuv0[T~8fz!gª"uElQIVFn+;q{#,b&g "FXP>ݬzԴ7s"8ɬ2u~ P0w?(@Bӱk]ՙJF#O1ƃ|E) Ĩ: +4HT~o{dK~o7"mɱρ\k5*/h +W(Nz^ )iIeODTu3{ϞCw]K8͖GkqM>J*\3Cg[phI9+ Tj͖oPTYV5IfDjKmڞhJWk=>#;" nBYVsS5oGIir&QGBGdt|I$5Z ̳?~Gxe Eeő&Yvҭ +=ĘE$[ /K1' cV6SF)78.v=˕} ʂFR{oP}b\0DB/ &o;~rx6*R0TOeeN~unvraJpӤޛɀꊃDk2ƻ'rDt)oݻ:7)~moR22m(߯_HsT>nJ?kIYf 8&E@$J$I0"F7U횓Z4kuO: 8d3`.r[)2'r |o׹бR?KOyIƻ]eQ-M18/ +)%XA8BҾVLmb:؝,ivр?%b=}uAzibpRZq*zwlɝҘ9r< rf/av2oO +ѱJ)=+l& @ƫ{.rW2wZŧ^3oI`$}.U/GjMeEB-wbEDҭƥkҭVZ紂//y̿_Jfb=[q6tiuI#[y w _6K}U[lSe(a92:@q!!S F]4@4;LX Q!EV ECcfWJNvן]$$Lk0^{wZ`~OBTp(rⳜjiBV/yPP+eo_dr2RZ1PΫonS[mA hs):kHk]M KjDt@i\O0.A"Y(:'dyCQ[!ek?/h^ˌ;;r +$fsu*p6f#rz[96wmS[C~ FRpR;s} 2`Ηe @xZgxB=-_-[o- h+Ƿ%є+X#W"Z | n' .N.61vi)Ǖnab{̑<&_d@Bh;*Iq1/dNӉ-|F6). M+WDNp7U(Ôv!* sF|OkУZI#E4xWGL9e_A{빚U'^ |an(wr"y3y[D\>X^~槽 0@l61.A={5̮tNtA:8qHq~j:\ae/H)1z5NIւ^<VB W {چQ90fC\B@O %IU"*EHTZD{<(ǺHÓt' C;]AKjCmyqPL4-s P]0*ͿrCՑF~.twCΦIL'Ae8h#><3﫳`'~cѧ ZŢ]"ŷu$'6vH).KX-+(cJ]"K+&țP·k`쌧*v=Lҫszm?yTIGCprj(Ԝ&+8 *(hayci{M|܁kNoO UzLdpC~ @!q"V!Ȯv.pѿG_&GLHҞTk1 /0]US ;G7 +F6ZuL$kF%NFAy U|@Ch^Aߘ@ōۅXI:^UE(-ń"FT `HU/~;2k4sx.@D=i {L@;!So5*Pom *eLOH8PV*Ne:?RJzO찬j]s4!OYO7]ڭ'tP +R]X)F*Zd{T-`秼R +{b%qKt (q9_M F'3U0wL\Pqhѣ˿Ԡ@j\ū{+dr#]+NRlQM^UZFM1q ;o/D[j X}?uۙFFqWB'% H(\+ +nRUk==_tL$qLtԈO$ IV|Jl 0ݢў|r]dUn?wj? {ɭg7dmYklTbl"@yKF3̷]nW ȵ']<dķ'Q-ABvß\VRZuwLzlV&kKtE,n- b-d6ڏ' e`lZ|ǫ务V/CxSmd0 [x2G޽Lí#.Mgse[WqWh/.b+˅eܚm/dmFYs(4dw `c{sˌ 4]. :R++ʭ.ֽ.[Ss6 61ڹIh\iy9ՏuLsv\rJiUOU17#M8(wGm|e#9SpntcinLvS"O2(K}{"1j? blgQ%^O\ߙ}ApCJ=,꼢Du p|sWDz$bOW: 2~E5w|sو'NCe3saPM4GOIwn#RS[׮3 uK*{ek\خĂgϴGR`f+mӛ2.uηNޓNOT?^x>5ZzLYKq+m; SKuHE?6q )p$P~r@R"Q5ղ6YT;sԳ?]Hڟlei-e>T?z׽`VsJ̟($qhp$|~̓9h͝O:,w[7 &~CeN,ZTsf *"*dW}N)j杦ҹ-]'?W+#agȳU=PMtwTwh>~R;wfjPaJVJ|9E}&VMCPVg6}M~H8x) M5xM9Y`9L ;,J_IR=]G5}תd%~UEյ%lXrW"+#:09_偙m`!IO'>ٍN-TM HXZLwe:geziX,h@rk+ )r9ɒjþ$ڢӷf3R"20uk1K^v$K]xhkPך#t1wsޠ]gQ{VW5*#:BE&c'\VFtN7ݧ-SACGL'2B IZvR +.C]DެIOB;[mF}y5ڇ2܆A^Rv].ޱ dqd~\e6ca џ^v~D驀&_H+1Uzvco*{$h$l(KE]* +ZờXņ^ˎ`Q`[q GH*W~aM*CCAGkax¡z`G$YF$r)ƙeVsN2~+X73d$~)LL>) )鮳2$*X .q\|"*${oG'^?Aq;ۏrUwiȀ# 5, 4z2;MFۇ%M=^%jhHG=UI0} J渧(%o}!v/M&xr%2|Wh@DrOoĆgQJNr#67'CViKFpgx\ NVZW+y\dնS.Z,X`w'X[ȶwa@2V=?txG=ao&F=} jՅk)`'Un=:)JU6-ܞ"Y=A w2S(nɮh<$P=A[LODG5:>q[&0n|pUgHO ul{IsjhAgw/{ ~fHY>6}\Aשb4`v `ni h5P:܏]zv7v/!wﳂI/;Gt a;Ȇh6{yY 嵬g!T +&*y3U͢Z]jٽk4J%jA;ҵVł&+ߔIdEˤpd)᎟)"\^Cw;q.bO-4ҼLa>}mx6~8NSu(eybi=Sk]D¦#+ ?W%+P#-.-kSOZTAZz]Z_z?Zf@`6}:-Ƀ 7nygz6ϚǥaYω1mF"Ea;4@2BxhH!c7*O&!/¨~?/Q|j;bnp-ұfntN3Y#*q +eM0c{*ycoʓ~Íu'[,0-fI"ÆḬ'dXA_0W&pqs~͠|6]qIܾ2ȱ9fg5u +Yꥸ% R3.BM+UwU:"*Ҧ5WiGUAwAL”zқ:YY!/#1P,ݞ;iwM\\mm(^ש;(u(&TJ_(~?r9&i[S.d@aQHrPx} Nf)e\X^e+3z&a$[&=F%wtbu!Ѱ%‰J\iE?X_%?5!ub9u(_@-WRa1@N},T?]{Gߨ8Vhj"ZwN^v(ֽMXʠGg^N0_3QZ ?n}\n{>\1>ұ ì9H|}H.nXw]JGm-9)tհuo-/yߛZqZr0fFT|y{̫0=hlÅvD׀JW&9E ~ j.|W %`mxHSBSZŌFe,_6dn}?B{sU#wPPqMzcD̊ˑXlKaβ)4)0% Df=D_ȘzdպPPxQ9a9B>yxn1ٰgK6A5r{n${VD‚jbs"kni4٦jyL[Y41EJ=x vJ6&vCbٌټ^ˑ_?״O?Oq"mXw"{eƫmJ8I{E*Cޓ\jw9@6T_< o|۽g7*5ظ97Ma'a?O{98,ՊW Ĩi;U29߱h!%˛ 3k\}#-{ﺥWF\jԸp ?0G@wX!zIXFlq)oq{"-(j{߃meŭaT@VnҕFHG0Q5aF̰ah]$s rֺ>Dzm-\ XM P!EAzTv}*ma~ R + l"/vo?394E~cT~{{;y;7{ e'?Ϝ&7\H3z}}`!2(1LwPz!Q']:έf9};AiQz0)8^$9L1Ix!˱p}E%ٴB܍A1؀FutwvtR#5<7PTިqRLf^*c.CKQvJqavO'cԥhhXA݉Nwg7-`Mk|' v4v?Sb>(6ƭn_>:ExY,rt?P4\^d Dž>4m Icz5b>v/c(,$#[9M ũ7kMܠ;jdobgeQv@Т!f{? tEr>W5>X:>1_ҎGP1k|+{ @u?P*\vpwxq@ُnYLiտGjtg3N3h1n/Bhdlb˞{ -e6oju3g˄J~-frW )6eucʤrcpJIԗfK!?OП + .&? ̎/mq8z6/+1Kw5e/:MO$x rՇpvC[G]ޝ`)Kb'+ܪ 'spsTٔӪ3r/98y:kABЉLfhLOt^hδ4;KIsϫg.<{.1tR}NR}L5<7ZjmB70:8i;ӻ3α@?@P5[> .QH `)N ~֤.+"8; +&.ƏӘ={XÔtpq4Wg hE*A: .Un9Q{S]a8i[| d`dXp,׫fS; +U*Ln.u} ;Ex,wbPrω~`^tO͋ku}-+,a (9 AŜjXz-SmڹNK1?1K'ԝJx|jS,hdL{jN*urOΩ,KQVYsV]'6socm\Z9h,WJanP}]<{ a^kϸknzR'QS,Ha:$W%SCVsJG`k9K53Cɾ>+S(phm~y6:4\4֝ݚ^H@O-KeJye~!Q2Ȗ}*(Ar\;=[+ |u%x@ _ߔ#qDtGGs7T1uΎv #H,L0e`EMzKt"D0^pR.?,kB'?ۯAxe5 O./ϓէų8"֤I,1ZxHtfw1Ϸ`ZѶOأU1>72Zk*]志q?Ί&3o}euUex$(daӆbXz?va o:Jϕ-ѳuVij|yL#PņD!"zRR7F۠nm@*H|IkbNsVn.2ѕ7E:#=T%y#ps߯K^ i + (Do 6Q] cE%٤,φP bC:ҤHf<_$39 roEP܌v~nkp8j}h3 W8[c7C-4c1m`4KA^N1`LIMJt7.woG;M5PCV܅#6DX!݄C<@ 3-Im<7ޠ |8Yzz<ƣ;jh&K2^O-BqQߨ> o+.+谳k"`Eȅ:k[GeDHFC +yѐHAHf#RgC8tvw포\t5Vhu{BljRASw^{p%A&PH!Vt)@8PmC2yN\f2,J 9{0=|` WOC 7gΓJ=13aL3;6k?W{r[ѱwQSKɦ|]ҋ56RoF9}r$ XSr 0;J.d@= ȘdrXNTV+1iB:&FIדdW]bwn5`L|$+T(8_*^-Xd2:=O}ug9 G |^:D-w.- ndd L%|f͜Q@⍍lgWscfm_GScLc<~.pq\ ψ BA8m m12-"EW._Qt`Q/䥐pN@fNKS'x*]-yŠnˁcݤJu|OȮk1x_XO'c2% Ix &dު~Bz~qtVhIt Ud8?c ٭tr뺛ڂ1u't9-hje3[Br5b?j rybVζI#PxH%w5U+xef:sAweu>uf*Xv%tȮ}bȩ^٘f u2ZGm]aJsCN;WL7G'k%Cn.fXL3lĢ{GY]3vX|" itme|t|֪w1K֤G=TKm +?Jg-׫'ZW(|e8}OD+ț+,G| b^i(l* Ģ#_Cb /7$S6T"Jmrɋ'#XyPJ]x|vc&t"Z0F!|Tg|<\n8!C~އ a4z1O槃jl +S_m]!Warz|!~VfZD,(>'P\5h,+%߀aTfݽEdhmtΰUC$.h<(3;/nMAoz5ջK@% 5&nq[P@6Sdʯ+斳 G0ݙ0Fǽ=i4R -_kh/ [*\n^kڛy+c_L%%O1G93"_E~й;ix”]_ +j[EE{GPAT.vN}q s2HWrN>s\gNV{=߭ܨXA ǢIK :P_|ʵ_bw+5/+K9cT<.s)t_6o"]FtF ˆT1k#[<4,|- %fD%#Z C9=َ=i7_?OYmpu?df`WH*Am[7[Oz0( +j^HA DZumӞ!a?33{Rfr}rw`,Dj-IxM"yu}cK?G n&,}~ۭ\-л[t[|$>պPO5jEW(f1>GwԔsSIK߯'(:ut#l@9 {'u;Ҳ:mjVܨûޚoJ+Z6T-ztՁV+>W̯AO:Ի4j& q5u7 z vځ>rAAl ks^f)7‹:jGּgQ*+Vߪ)˪%?@v2"#|$8rDbE\TͶ|"#h4>y"'=rمTԽ(VB{;SYD.rul^vXϢOYlHIr%E¬"SDO sH=l2 W?2=˥tCmBHg{ks鞾gG x[~z[*ЕXʉ5=Ig)5HĘcFo|G;E;ޡeG H +?cșf8j+~{r7cF"j` a {GXgFȑv+i scn]fL&mT"CM-n-|}}e82o >?dg ~NTa.3M%me4!.u~'b8$<D኱$fWDڝ>?@gr%dIL Y~rAgMx +(;Me5,2xL3nElDw^Ěl;SǠ#_r=0s}0hg {k +@,Ȑ =YG ı=1E{Xxޣ:Q~PP-QU2Zmg'F "Nj\efL#r 1]]z]_ ɔJ#d&SdIugwwVNzmjS_!xo=eHdD qYAw&,e"Wɯo/}Sc +O.˩nV;Ms icRG mk[rސ܃-IYY'^Zbppg-Ֆ +\mE{5sĉ;^Q&N$&SǞ4VKBvִ[v^ tk-uksMsH.Evo2-9Fn,fo1y9u^3V!hbV&HY +35'qb/}15 5?;Qngmew +"gZʷmjJCWS}зUqBHN.WOcrKR8&nmbCLO٬WFz%1ztpq,$y8vuql_Y.?69M/C/^in3l^ǩՕ&ey2(j?!ޗ鵌TKEu .hTL/T2>[B{9w{!f& &MflV._c).lsZߍtzVZԴhq5 +U#uOŽ㹠z-hkU=$(2١d +̋N_3|-ݷnP[?(Ȕ8m:EWtǒ9le|[':#Q}P}iUu:|^ 3h g٦=:w/qW{rqƏ   E"m%, HNU(<)bs_Ot iQ7:LC-²3 ;Y,sU{z> Ϣw]njFLL:Mڵߢ5(}$'_Y_/]'mdIRZŵ7[~]} A쉬DHV!'! +`5sa;b] )WN~j'pm5iI_;ie—Lۭ{=w÷>.Ǣ>ÑWq\u%i'\LF#w7 al}^ؾ^HZ$άWD?Jb3zhaj_tPÏ0(xƨ:|Efi)qOTEU9)qظ;e|-vSMI\;> +Kle3~ѾwN0&uXa] ?q]^KjQAm(9fz^nU ǮŮ~4eZ;_!r>i&r8D~TB5v`PWQ\lX$E,ZM2 >k{9Ĩi0?8tOef\ۦa?a#UWr]<ߡB%ujpNvw8/kxILtK[yC3i9?#6Q<Q)DsjYj?Z7ۈP dOvGcViD3%@6 @f#gR ۤӍ>h1˒U.MOوPhLX]R;7UUK"A >}ު m! d;S|<݃o9HP>>Š{\N]Qlb`rm #<>hD4Շ?(u>bsnd2cm6&rwٕ?8O5;mTeڻ$^ o02?b yFt驐̭4.eӈGiJ҉nJ7"漮uǂg ǩNٰSag(^OM67=i.tɐ11֦bzp:AqC@=WsTe +w\EN<<ȆLjܗ#rQhC.n-?_0MR6{*6cvbl0Z?>n:9c3F45:7[ ~:P7U⪌[[LVGXCqӭ lߛ7&>ɡ2!<['ʨ lW>-^W + zIn3tv6a} z[kg*-!命Qe6hE.r#Eq RKwOpcUJFjkJi!;$X0e8{w Q3v{/G.X>Rf{L h)vߊ ad > BjRy^2wRySrT*?/'|'_ً#FU&f ,˽ۍ{~TVemD_i5P]Ң%[Nfy .EGvr{j1}2*g5o4t َvvIio=-Z, C.xvV8cxZʇC8EgA_Vx,; 4Ƽ +CJ1ӓ ݫdB\?$y y9i K)ܺGi(0uƏ_.ā-8ʤc0s,F31* +Yc~E~3E<ӝԭY߇F=% +jӚeWtDc)#Ztnj"/mK +CĦԹ7alg'3她jn4룅ź}:#JC-uI2y>t}Q̢vc: +9Ud{1AjTTb4LnB_4VLY5{snoAovaLOy6qTw:o.#Œ_&>KLG^im>n>.ddGn1_z}wХd+zTέʝwNv*d\!Nx^Čiִ +!JW]`PEP*=v/μ1EtJ Zh hi?=rp6њl犊%_HTᣔ0>R&-D/s6QX ±>\~>hyB +fh%ɟ;yLq^ąJl DzQЁy0r{\Uc|g,#>`%޵h[:#) 1.c8T,qkH'J"UssA ڹM=3``00ٸ{),<>9h`Z,e93G(xrIY9ܻZ[4Յǁ/1|QY inq"V +O-=`G`9Y~;2( 9vs4OsЗ / &#䖭CoV&BT*pYKl}(y`7ѽste,=+\Xԁ&5SZ^au yĥc"NgƛtZ^&u8vW=?^GȬL=@ oݹJku|K԰m[K F|Q_u~P2oJZ5!Pm/ QW7uJ%WDJ5Hg8Dv}c. lD&նfH?f v@X5QU%aajg< Un+ &4Ț̲="-D +.y)8ni>,rvImYcF>:5iF"t&==uc]8:\[n}Z+ccI];PZ-*c!#o-@< 6AV܌@f5S.9#Qe8>ņbieb-j+)jeVTD%l)=!#n8QTCi-poۨG\I:KejG^+imq +j{q7cex6fPs Kder:%yIɭ9'f6O卐&I&4Kfd"ըÙ`!Ip qGb2ϭL6ꢏlM!^)"8|JL>}^/ \eWy mW{ [,G=eS ׋=s vLlO=K'ƢCFzRbE, V2O>.5bHE7}Dm8kCG)sp6Xrv^=}:v' #azIuY(L3JwT?)@5/g FfTJC +|k%,m+;s>Em3E/׵6caPvI.{]ne jͯ'Tmf''\vR%bY[{9bmcUQuHa.eBuj15-εZvPaT^k}L;g,qzƸRk:4wCj+KVFrx~H0$]m&OHW)dx! -uBRġ.W_Us1Ѫ^>\2 {s13U{ FTf'yVN07N/Fz!O1|#QtZ9mw{!!-֟3fdr[8S%9 ?)ݘ_}WJ$Pm$;1Ny<݄xDBg9*YuV%I%0"jj)vAo4IW 8*[]6CjGML_5l'x̀F<#SJ% 0lU잻/q=o.Aڴf4&-nVQY2{>iE]JF?,#N:Y&\sc{#S8os7Q'z  ^k.l㮃y-[ʾܙz1Lju74Y%Z6g"R~_,zt(?NseޥfqcplV£_ɕjݶi&oSxų*䇆GFZ:P_zj[;hƺV|8y&V=O'i#._n^.bӁ 8Ƶ`h#'7U Y̤tt-q-!~QGފ(rv(Vʕ^N"Fvg&aE#~k{\CuJ G.+m(~SqzGSQʹXQ'O!?eCO5RU4tf^[yISsiyF'sgJsӫRS%LJBgb 3LmBmC{Epl1\ yv:K!(@SYI(IX_ޙ4?ּv>ώ [i\(ĻIo@|}7 ?',I._x8V4]ξ=q s֫6I?}3FY4.Ϩ$yP@jSkR.1Vg#!SʅyQsL}ʱ?\*wg>v6ƣJCt[Ӕ]QO^R'?NNK)dN,ZJȹ6$䢝r|'ɂP#AlјZ, *\ޞk? oJTR/xsV 9[]3Q[4]?v"̓J"ebVmn1ؾqu3  29Vlj $v.z͒6K;DA CҘ1)^!YΥxlށV`a)~xQjMp̧ͮp[LO0h.i܎?@fgTr|Q$)$׳׳߼i?S2. 0oY|WP(/?$O~4%>m2~8_ Ɔ뚜lJ>VqpE]lYUKb$lS: +Hr!YM}Hz Q !B5Mabbit^tЍh+} z8U % +F";v&5<"fZJrj^x +fUcOvx6^C;D s%7w 6w$H +O0X1$F +6&#Y>v 7T~[(lcCtV99l*ezYBb}jWgTTSzF$ {SQp:mŃ7Nmh#l}<@ ]T2ʦV7x$T| vyQU@ u c$`L9 w\#ș@fH NXu/S)3;R{Uk/ =k)̞X"Wabj2(|0pYL\5U-$7֚_K>l4K+nNIkz;pe{sɓ!!:WJ>G_ޣ7~omv:j2s+D`v2ਖn?Qxu1W5! % &Ľ@Yy] ﴏ -3!򧢻[qǂ0(:N p9n ;&<f@,F :) +W% D8ci@t .t#Mh+MMqq4m{2<3f&':c&e(!]J꽴lXquw8Djx=Ɲ߯0h(Xg7@Vo]mzQ]1s, . yزڃeX0cB^*ڛi-I\ w%PjIzvDW7 30r(XeSv+{2Au\.J@JV\ lYhCSHI;kY֬=}?1=Z4(9l[ׅ^-ƀ?|Sfvur=l[VW.ȌXx,>:10X_}KDڥwܲZHzYĠ(5Zy ig".ە>$˼&Nkէ:;2uC^Ae?s9S/?|3E~zc/O5Sn߮8?7vH[U͙ ; Fвm{E&U6%5:_ʷ fMS^J{f\`?|g(ZO&p~r{硍//ΣV폃Vv@773 .4[Ƒz\ɲluzLC9wKOyσƳstI=] @,2,P~xF9FFzup͗$6}'E)7|hm:AkSߦ;rNieteo=Xh6uI‰$*b-!4 oZtM{}}NM nާ z4Sq^;KX8<;'1ִbzJStw20p%HT1xdȩ Gߍ fWaHO^~h)?Vu.A#no~ʆ5]&9^|ά_^{47-R*_[[SbZ˫F~jr-=bMCŠB#oC +waoR,N.]\N_%niG)*L^O̚-HT 4vSFRg1?9EP/OX²WvnMM6z=*|7G[uy6$ &O&~ۣ⊮ ]]h+rcE{9@\KQ8ݓ*N x0yo?<` i8UHJwE M]OY1T^R!-rb/er-'♲iUҵ`P'P>V~nu!,Bm~6OBx? xYFMtCU<4]c"aS~^8t%]ka!R4N +8uح@7d"dH#NOC r9f˽3n^rI Ĵt&RiГxg˲?mל8;,aGW~i0NԤR1Eu 558K A&(4x<=ȩ>0oj&@l84ۢƨuF( }Z,1vҷNx ؂z li)4`,tC]O$Rk=<(9Qݯ0_LtX_S^R9sE+[<n6[ +/ +j%2j9KJ)7rM)Lx#9bAw(KYIiᅍ´!9w:Ə^e^!nDh6I}ǴQ*c=h&@|O5VҾ?OA٦d:,fCB -< Q&~U47 V17E$+'[BTZ?(ūxԏU^) +GU =Փ/^3eSA*zbЌ#]qW}e46jo4s\hWըkz8ӃYz&&;n{ޜpµ+}_Qfx׎ȍٔ1{$"'6UjaSԾbE2^n? }g|r gD8E3W:mfɳuAVrJ%OhGAc#V`7TcоInC.,@Ĵ" |^]y ^ftD`aCၵ/"^?Ӎ|AIh@NgRa4nk;"bƜн׹KuX'S.KRs^[eWlW;#WV֬Qr5בY"tbİn.0ʥJfbEW iT_%UM5$c֬A~yʋVr&i3Up'v (>XK(d,ʼn5mEz.嘻<˅0AKoKzT:Ncq \#>z6ǿE{qu׉YcL8Lyy^˂]ʳ.nZ#g-drlT5mh ր&W@c̿t  ЄU +'6*x1*Lm88+VZx6U;)ĕA%ɴ0tR c4Ms pw5r^L=r5`D + !<`<m.S(,M;69>Z`oݭ"[O6KQAJ Ffck'# s*y eb X&vbX5S7oW}0Ui1gkf +w6,5oަ^ǚ{Jc>.1@RVs]_K ?8 v[Z; ~Gm.}pvf.]IVFM~ +M8ZasAz =^Ze J´s l%u%ASX:Wi`1\OKJ9a%xN@b t3 DӐuڵ@(ǧ&DDT?z{~wnƩ{,Lij0iW2+L919rae*)Vs/'ze0<~pZ`(!MjruqgRpuojU2 &U>t]Q3 7(ȸ'Jj wwLΣ,#ogrʢ,arnߩ *'T,4 +1A[<7TG<.0SBÉ;V"xOG ‚9qƔk9=tig׵A9GdӼ%F/>,'d wYs\o} GC'}fyL`4" +t]ǟ;#?tcƥi:g1ALܾ-NÐ#{l4m,]Gkje]E/k%ɻu=RgEEC.uկV۴|XYpQNs? ŏ3e-` P=.>prȂ@uɢi5:2B-B(?bBLs}azwJ+oa>4܍WIs~,XiGkEj-gu(|7}OBTcӥ?_}&Y253/U;C2j||, *ıLBc[gp[P;ۦyy%C; u=/G}кO)ڏ0i>Z +-T-[>[9q]!碂|-i tٷ`yr%rq48(Z 阭gqh* o6lv[魭yЊUNuGf'`$}K9R[Rܗ?o˛Ҁ~{ToOG.w@F%ۜ-my-Cy/LF B{ڭn{mI93?;k-띷 /mjWz(|3:$q̾F,`"̋ +,lߖ wK'N=ia"h'kxY-3xchL_ |չ +TVTݮ6yrK Iʹ=|%` +ެQ^!jĶ0"-.-hJO5j4mŮ~~HI*)b),M֫\W]ܸ:oG %7>MF(0Is#'pPeiEr#j1j.wFqՂR' +JD=CX=㤛Ғ:/y ɠ\GW#N1-rS N ' EaGnW_]L+.=gTS"82YOe.8̉ NBx.WLCg+$C-C8I^"Y  }sJURmR5~lհdưoY4'KĂ?lb].]kxvbjXݒ9AU׭3~#AUXιPf>%>zj|U.36Pa۵E⺼30k_]2(w`2.$ґ}=: y|LB@%LbU`fQǨQD|MVhce4E[T *L-@C\\&x7䞾G~ڇ#k7(}3:[j2dqP_/?1kkΑ/%E5A94}8j¡4gm-ţjȕ BLR!4'R)9)?zV.lM^_@󦟨 X͸%4X^ كB2̝M]%߰ͨqQ8+zJ.M*Z"Abh[;|k5BjLOB m5׳Q-?9p0 &iWFONJ?;ew{K-5Etn'*Y-wPo*-K_M !K-4]cLbMyBe^I%S,U-fG*]81qj&_-Sq}dW=o +xŲLJw$+UXoy`BB%]_j-ECUmɈMS#Dkg [zHr#flJ%SNe|̰U)e ԣ~8ծIK!(J2n/KmuBEv@8;K38YVqF'$2lIEQIj] 6xL@eX&f4ŕK!Q_7Tuxi?~Nrߝ0-Aظ+ Bַ1`@HVc.uAz8,UBU7i^y: yࣰu]~m>zR7[t;y];e$ũ 6og)>VNmvQ/ +1hħZ6z/+.! cr=w]On}85k(Sø6C;BJ9Ij818*Z敇~gr%O~͍u]Rg؎.Dq8wn{!U3 7Kva'\r`!:S* ӭfpw_Qu%S ꖆ}/s[\6Q/N,3=;G151¸_섳V &}eO>fDSS+M%AqDXq舚ر~O&^'K6o/J`O 8C*l45jOo*mq:;;H_.}X(fΆ +N>vvyx_'3^β5m~lhNc01/W^99L*Gsσ(-x OWvv%=hY=)9lsjldv׉֬"Y34̏y牚rc]3krPZjyQiQ&읣Ww5JhoqAjvPnCNLo]y=oYsdAaLgfsV-j6鬲PAec9xŹ/;_Wyi3~4/SkMU%bLQIr$((YQA}s._wu_B+Β vң3됋 ͅU<;D.?Zv k6_G֞_N,it̓}LιB3nsina}װB~]#5>8V@nH J9}!Gu~G!XUlO6a柍̒sǖ(B ]ut"z X2T:*Z2;yd#-i"$)So-^Uv?m`w;o=C&).!xXfE:2HXb?O(5I_yQ;mj+1hӥձZ/`eJe]FrҩRoE=opkq|!޹+%C $kJW [o-v׸ս[HǢI#S2VbQZ א6ז;Og I + ~q`9UU0˻vUdUT|6 V0V̩6/{.UJ iDs_@;O7{NƱ:qjJw +ȼ4I$: `09 |.cy1t#U/kFOVNlt-&. + VFXo|v>6J^lߨfUgn&oBqTXU#>o B]&L1'Ex24 >Af@`ggF`՜K%Chojv6(.԰=~R*8+?[>jFշ4f*h9$;@dY> Ҁ{sZ^/y/a^D=nE6Ol٩jbڟI+b{cL?+iBDFZgId%Z'σpޭ{ *>&l|r"C_NJ*򭴔iGad/˴jtO)䝉i"%-tO}ZDZC)8-xy͒V yzrHWl\JҰTq<@7ٸ/=n8jݸ[jx^5]V %\c4&OJ,e1Ũ%VQ'47"UKLK@2(jkҵ'|Y!3sloPяID/w?6^C,%Qo?G# λc$FD#"B"NzaՕY.96)_L ClAj?x2ThX3xP(]N<"kn |xXH U 8A4Ң,`$ @$UGڿ%|xagXNUs +M&9k ?Dw~8b%s8jAtM`^@Z3̂ +8EK|/,88[<4:ohː]VF@ڢ܃| w54]:ؗT*sME3O8q%6\>a)\+T)ȟ_4N,2F&>t5"6N <|Lf{&hb +ﻙ\;nì@f yy{屰 Dg)%(9#{g17Ч*NU{j<&jk&+6:U a(hr,sb϶K<mM=>IJ#Ejt_ R|ƼJ=}'}dcCGGtۭǵZ+ b1Q>y ,K0Ɣ %qweQ\m7vRb_wB-i&|%Wi896V Jnb:969||fNS|Y왖 6LUݬs|lkCMS 6x%}bx4=5e^+, W83Hl|Zl!a?96GldH$ڭ"҉>*7RB8)F =G Kϩ1X"mEe_;ZS'3$37C^,y^Z1"Te^ڊ䦍 +!Kɸ ay˥_fjgJ /kuIHdzxNʗCL~ ^t1<]wL|-ݬ.Î9A×S}н/kfFK޷Lbhx\PI>|7lyFu˙)OLo̥ &.k 2k 饪ۊkU~ԀC<BG2h4>Aq8ynU~a\%DXB%=섿+vsdg] o/?\p ڇ/)e𸩌[ +^pVY7Ŕv%sarc +,#S_$rۺPaY1< ȗ2757Ǜ +~ E1ottv~ֳ5jX?hO2c!Z9.j nv'6 [z~JƇ]IzrΩ,[rU0( {AcEхݱEd˄a׈^ + {KE}Uqwtyakm9']-YwDc"pVSK,1%ߢBhV2&˅! NczG $lPRe.om\_#mnKx :j'w dV߻y +d&Չ s_?Kk@MʀC=fpw&&>OLƲK2ȕ (3(Ȉ;9I6dgCp|9a=;zY}+-Mt< ǚN+.R_[~TW㊽hux;!>dJMIb _)+Y8}),-}Kr}7?5G'{yrdـ}sNR[n*#o.rw>TCg"|q@q}uKlO+(U_j/Ϡ7Y"FJo sdBG4!wSx{7J{('Q'GRQJɬ^N)^GóᮐfNYn3/;oW706qcn72  ?6u.1Lf7Pj :盺-w#JT^GV%#[[ߓ V\W,l_ND&TD ӢFe'$lܽ += EC,3w X(n{2?80HY(]063GMȯQ:\ιn~~`z񮾠 ߜF*ˑz^۟&L9$Ո&+8?OUUB8Y&u +F聽̥M'+th䢞UHQ#dʷ }W2e xwp@8NT>stream +HtW;r$7b' m9gićU,IS$D}Wfc{PLWbr`DOdivaSYS{S>X.okf cv?@*f7=sXu@Z>oյ0xz"2^iZV9^&}C2|6f.S;Vk@*l;F +fIDeeYF{pwӕ [o8,OX #~mŲ.6G ޺oyS;䓴2m<$?@EFl +B*LX6pԪTq71KVR)boz1gܶ gGwzGe-JO\;C3vn{BE2dl?#s2(U a=йf߀و$]h?0BIHg(mrcU +ԈȂ<2>:7VkN.잜? jzC}3A-AXiںN  +:y0Tv0ʭo`ZiX=j@ՄԼqimL X+{" < _I1N*-82sb|+3?cu?b"H/ +G6!qPFMtlb,imKB2rFgQ|#q^ _nE+kf@cx-tw7es@^J^0j I/]u]fkoإdLk,ۡeF䰆NoX){#ҹ%ּA;Щ!{qcCl`؋D F3"P2 \621$#M#i#uʒ JrWb<1,[,@ 18+/(ceuHQ̀k!j9o]ZZŹM0FS{,jOdڟ 7 Ta_VJJZlhz!RMGR)GDʔ^q]ݳS?T@bϙ +cSkOE/y -QU$ $cոF[.^QEЛŗ(!Nm<һP!Tޮʘl Eo 0Ug[~b~zCs% ՟ +_dXڂ/FظH6[U5I(&;t.Bf1bNUSEvlJ?[v3amȣ]v` H*i" `IJ<$F6nhe]o=ҐESjC1)F ]55p8Wp X뱨ȷ# e%gӪZOLuĹ9J͘>3"{~mЃV^CUnm1bn7mXVF1ꅾL%=BƷǗJvJ6mk/ŗ.w|?VMIK ~!榃!'hn!t2=$ܯƍo GUOU ܍Cqw$諒"~[oD. D!Vq|V~!KPq'@*@3}GVqYX$[R{fܳ㴻_)$T3OZV6F nz nswcSm8*Nb64׭fȮvIdaWy'_$[TQ.,jkC &T Zú˓￱{T~v;A}_옓[w>!-}w. 03L,{w$%CA}tQ= eu8XvM25Թ 70T?,hcE.Aav2:nn=NI4 +\LH +sRKRρtu"Rl͵ _)i)n<;#:| Kņi>zѓֱ(sT82a@j8vR cNNNܧrFOz*`dMxo_烆=BYsG=UB1yxվb rTY`5))dCva> +E$56 苆M u”F~0#'SxuS9g]TZ1y0D]?v.bLʐ5[q +iט|SSYk]y ٬Qq2Ф1 $\RʳAt0tBtD|Ҩ'3SAPxw3ND[Ҟ +S \EOsLbת1\2aNe>[ʾ:[媠S߉]`sTA#9q',~uVnfpe9A*[/owܱ@R#-TdǚчNҪŴ18gq 9}pua!d޸ðYi[`ið :SUm`e"-I0&Xz_Smゲ\5ȮsiKetӚ:RRk)cnrGu4q Q3IDq4v?#Ѱ 9'&zCQ>~,r޶;F{?aw `hB^BVd7Oi-L'0u /)j߁Q(@բqW] cO`Gt"F7ׯ4S,5nl7&@1KˑA бchc@9fԆb0B+#>0Kc  tHyGx9EMk.itzd:ټbl8cф(Uq8.6Mj!AD(G?Gδ((Pv'Գ0ZcFp"C>N?4w'\@D!E$= W +ͭ"=-ͨ1f,kw@42?_0!\< s?[! LÎEd{Dm=q=Dt.rVLԆ:cYia 4)O4'` wyŮC3ytW;$;kL{c$LeU]2%?`>:|?6qiN}5c#`}@zso>ʼn*fDTjXlijhU{^‘fEK Y0nzz/V4rݮW>B=w *3/6e;UJ=1Eєuo6 xJ`8R#uZɡ8fKMXk`{PO`&μ jdAHX08:̕NmK}mwo04'% п3$|8b@d:#34C/B9>o`_w, 3VZFSx޿dHS{!4B|A灣H"T"I,&LL}q2fSkA2gw DQIGվUZi/5gX|#|S=@T>T$-i |ƾ4ë[ atZ7XsS1GluWcʋ;24yOd7VPw@6 ]JGjCG^^t7:ֺODp&CɘfU%kLD0wT[ GE]gqA],4sq"r ~ Lq#if%Zsw*28zl(ja8rԱH0'. t2GE~?@>CK:YZ⺰:V ҭĬk1햠^5l{}YI:Dv ??#v^Jَm5khY+;1PJNtgp7+$(QIˢi%vNC%R"O3N*=6a^5FYu-w|sc-jfM-p!#& *RjݠW180 U/4ia⡡1vJA5!`%44͚ܺ}`)kgz ֭99eOrX55SHڸZSXufՕ6Iv2pfh6u!_V{aW9xHN>+fC}'ѹce1Nj{ SAȹ‚%s!q#`[܀F)9H ]la*t?U%Yyo''hg}vH@QUVGZRsE%C㨲y v }HT9$wqRdXIY?}|~㏙eҋU +YǵeA𺕏#CNM`S5Yh\I +#{: rXkަ}VL c*wr $!n`5;(\ֻJ1uڊW D܌ɀ$![#M˂v׮ʟtD}M5yLADlq 0MdBTȠ1GxML׷%juzD"2_ DkYQV[UaW|Eꦥp*`8&MzҔl1+l;`6Q6Y0Ⱦ<ĵ%-97fUgz*> +wI Oܡde墑sR{;B4C|}.U^dj&ųS L9' +!1b-jIA'03uޔ&{@<7RFS㺐GXP-S󙤳?ՠ"[\xm5+='Wzg:jGc9.&`0hJhUmQ] 旄V݇ce?NaGhs{K!/^X ȈP}xG|&K!^sR(` ǮH?tt9rB"59A|-в/N㌳LVֹ@9({<tCauu3fK, +$R߫\4 +ʣ LX$W M!"_b"+P <,jO˱\wqatԼ+i!"lkhy/]VMd!r+U$"iԵO5ƺ[wϘnM2ŧtT<2)&GQܥJ ECm)Jߨ:dȁpfհfms-{ ȵē/@+?|/1x"eG.q҂NtjBew`Oc<&~cH/vl~A2wޱb{ٮ[Fl]Z|i\F!:)DVeڴr`ieiC̰FI40/VhU鮼M ቇ?@ΞZҨBOyRQrԭJi:uuQ Yz^ҏ !bk*BY:DۢY͙9h6 iަTNϮgm^)VcŒpё)Hr`쪱,gׯw7TC;Io VE}W9j^WXynk<.0@ܸ UV/,)|ncj!A)٧/Y 7X gE4fhpᙧ=f?xhWN;*3Zl%Ɏ#:WP3Cp8qZRtypC`w 7 PN,^x.T +:3 @d:`r}yM#$'Qű,t!XO&j5́p K}=)|AP Xĵ2ȹu蓠Nګ+vj󛌸>/w}bVeJBuK.*zEC@o摮Q +V=bSG"xv)>߀3 n,˞ۨꑜyRSbDШlU b+\盏״jeFzF9EG?cuU}1`$;;40q8 jXcE7ιUzn hzS9 +X +x_!ߴ"8F?`-,p@Sx`LuJy2Tu9i478o؊B\rF{ZƐq-ۖ-w3ij"k5HB`W>&5fvu7$Cxcƌ@v` @Q&E@Da7nݬX4{ZtVPCVa1hfXlh aH`8 Z7GؘQ7n ̈́hZ/HpK|v2C ҫt`>m!)ΏL~9_/tXg4|Ss,aKo4NE34#]Z{razUfdo +B!P' u!} BoJc鬚AjciMF}\S&Lh41q*4UVNb>2<+^dRfqr85w䈨ahUW&_I QZ?16 oA;Y: Kg&/Y?^WW6r{VlN)4P)sݸ]Xv0*>N}IO$*{&RIf}.kaM}\EE3z}K|Y&<^h @򑦦q8gr׃zVw]~Qm=K "_'kC1LSp6&U(FLƒE&"*#7p˅0{Q5:> )g`qcTWu[q]Nbf!_`mRȣJHmzK^ na>,#gFL #.v9Z$QԤ-^g͚&W\(lV ؋xaڊE #J6#ϒ-*JYqe+qלQp[.ffԋ +DžO;(gs,(ғMc <B3i9:' E5W3xRP\a$Q y  CQ=Jy( ) +pWoҏTa4++M+cY2ܳBRwv,z08juŚsj^]MG]@Eb!E:IYfTo7È>Qew}g8ƒ?coa,^4_UὺA]]MvaS<%&Ӵpz25"-q4]e^nlcRE b8K(MʹxBWiJ͔Jv>JƱ1CFl](hY#MeVlkwn9Ǥ^N)wM2HjGg˝'v? WjR^uX ؿ`JW + +åd;~nblP~~iHς{읻V c׊5L_1eZ= X 2^؄SEn]}b Es|XȺT`;ȦxṀtG< ]<)lEk|R\rwBȩi5gE߀JZ?*zLE ৪X}U)y_uR@k]X_q2a燎gαU_Pչ4خ j-y|kbS8v +W\;)3Xڶp9+uty.=Tyg89' +[^8K?(7 bol Y:Oڍo5*vxN⋕lϢ6M-VHX#4w_[mz0R2G/cI?Jf6θOt!q5$P2[gf駁" [E;)(Q<6H _yeImzүT~U𤪔F #B(FXCh@.N.1\Ozw$5(NJbΦ_ҭD~ ol;9%O֔5}eΌDx3.e@$1Jo#Mtb)uCrl +=eI}ufbp/øɹSv/ ^E)wgS+ pT!0 7뷢%خͯFN:\'ͽ"_y.n)d^K7Rxo[du#KVInOiD}&Ě_铽QlY6-*WU-N[l)‚a *0y5FY"Z~]UO4#pY¥]0#8G@Uu$NRSyqDR"n"3LtL7]9l@<QN9w=բ40a~Z*B.,}qw,DlGj\D1g|6ۮNs]AU1 E`]!)\l` )uO9 6n$ +6XZ}w;$%ψwpԡȫV c3iI6ZKm3C0bhL|33}<-ɴcgjc + `ASIw6̎ B053Y]Q~ߤgacl@WN6ڏtb "J DVni ȅYE|jkI-qOeV7^m 8Z bHO=سtgpLefVV)>C$e}Ec N+ak:pAT'iB@dV.8j-@o]x4H@lJzõpl&kDq qDu,J.(>A CuK~VH}Vz]p47$(r*&8]-8tVGWPD}""vtZ\n`d>ec]5N0bVRDڎ!"VpeV2U' yQlEzX^d֓Iuŧz_7rU]l|y $26&_eFb08Kr ~@"C +C-bK\٦TUbsaQV%qr򪤹ɗU51#7K9A} )5v{t?tl,MIbPͼ <nS`] <<-zvЂd'k;F,VlK]DvtW<+Q 6UW61h2iJv4gvQɀK@,Lȟ7w%ɇǷ]DݷHu <%P{uȮe4wd7 ŝn!y,("Fqsk]jVo (i"tHC7=N}kf00ղU@ףυ4ޱ8齑+dZe7GSLp!64)}qzIE9-ǂcM,oic$gɞAMخ *`D\" +ؼPlޟbW+S``ƍⰶ0 xdέ(T19 +CQ| \y~Dvn.$at?0L!⧢.BQ]--bc`׏ +!ߕsںŽ ,z^ףfW1gM1I'ӕ"R4.>iވQu8"bH X I|jgj n"(beg44HyV3[ε4:]TetN"l"F4@Nr`LdExQ3$xWr r}Ls}`΋7m MvNQWzs61Mrxd#HZDzw1I|iR=k巏R;Ju&+L9 onWΉIߓO#_ſ! +$cD/ mg oq CZ:CA$4ϯX2 ݏG>5Dp3Z΃niyH>0'i=A V8ϴ#L E^.چ74sV8WͻD+:/'*عva6OFL4%C3̖g!>kD pž+&^f:vQgVw "u*k<1<Ș%2:/  l􊰫b&h M ^!\ _oFyKyaq6sBuXMzs!Ry}Yb`0L ̝=%.Wnk;R8?1w0mH~Ya +z []ks9t)^3E +w\*qReO8nY T gA1&i9\#E|g(MVi2[(DW(c~vAZ$ 0p.YtK+@T`uR@Y|'kxQAg\Xd[;݆lEJCOC*7$ t2} Ξt2U[G|xqq1Co_瞃?Uljꆋ{,6_GѭD÷C4LĜٵƀb`qP_SIĒyu:+cн-5cpsZ# +c/km|A:Ύe] As;bDkt#]@8[v85wt/V$0b`\aۜ#U3" +Y g4IuVz-ǽgd]mU?BRH?hЋؖT` +xMK@.aC>z_,Iu4]ZUlH,xbC?owzܿL붿!:MLtƬ 7JG 5v⁈fD;"c&D۞bNRb,ԕ孵0\MR: +vԎ@:!hDZۢ 2j  pB, 5H&: bE'%CYHb>[h]]H:Bel-~MB|. yR;>)G5v @ʼn8^kרYm*CWZ`N;R;k7as'd!Iw9 z-æm(QnJҭQ]W^*_ll_e}ADzk-nsؽ.Q!)"1jޡ_ͤAז޼8,gaaDy[ÊƩy4& uA4GX36VU׆(]BdFfqr=UjH1wqú@?k O}W#/G(1l'0:2kZ?I(nOd"Ѹ.flH+!4ǵzzoѼ+HƩQUۨ6A_}Pu9P#́Rug||E wm)+҄kIǻ?ՅǷuIkM\LږˉN}4خ-H֥5GYR"G4JN-$)~ +ATS()L?*%6}082x3 +ˁ +p,w|0:򱥡y5ѕT-xք/Ev.@RPE +]ڄP x ~@0~6} hVouCJ h6Z]toL5y+Zn @k1H^ _l:%!S;nYr@#^׬+t'*J2(z(;0*fNg77 83> xMkvxAMWF]'*=g*h'T̤1| H1+MtzN\7q8kf|ޅɴ?ʀMF\C&S-^JZL♖Vi'~*?/QI|6pk K„h8~ 3kP"@Ĩ_*/*zpS6qM[2ʔYb2-7In"_{Mׁ/'oߑ17oL<&PxL좱,K+_4NȸQ.>~Y 'yYnosSڕl=,"1ՀY?>$*a0:0b퉛ѝ C`rE`bh) &~ VG0b]aD$T@YLקt,NTDxȸR^K@[ɽ8R>F_ާF^/qNlM`>ʑ(?ozړ:2a#{ҳwd{_*1^SMF~E_ܪ)A}K^14%9Aimx||,A_:؎GT%NPnll0izմEP^`\k),O:3Uj/Ԏ1uHT7t'ce,NJΫ.zSfZ_; :D"N3#_X)6'X!Gna5?NU@~Ԧq?E6A[>WHP>*gPs@}zKnIK/rBq%Л3wW *8K[}%сˋ>5{7ѣMoMu:n{3e&K@P -䋉]'b]Ҍ..[e=KٚM"qƈ@S M^;ꠏ;G1W=:#o?X;nB?liF9pm J`OajMxC<*.M[R\7@PDA1sn 'g 7) D +8tm8מ&0| /MvS]nFZ瀺"6e?]K@TЄm:K !]9 xqJ@|!hWBOуzO͔gӚ~T_ܒ!]^)> EQ]uYcS_W88i^b>3%SoKKm/gSm3nEcʗHSyUsDΣ$.n +o[a{&ޔc./)O$o MZvVS=ݦ'bsT)')')ULuH>r HOqOx%|W8bkUHQ|6t} Th )j4J@z?'uѿ1I%fI@=?e%Gѫ-mߝJ=^eIbM!1=, y q_aI.9! >T4]P Ż_aHC8= 6FOV+@]3MmÒtÊxD96KoJ+xU|bh601By#Q+p3ڞhA}DaxMjQ6Ϛ q̸H]iG3mG[|i(LP Ԧ@"R j$Spˢ)T#<%j 6@ Io2aj4XMK4/'nBAh }qhĘ#&hsy)T9.lhpͧoh$2C4Y!hq¥=bR>DC/\kH4jh|&=@s h.@cBg7@sJ4sĦ~Ky+>H3ifkf, c0x2E5q#A`K0[#_bQh6n)%-Yh°7(ZT0Խe)bKZlB(R|MW5WV TTcF%&Y|<W̪?j.mgejp(XcЂWǨ%.R.ĞkJ /kOʌRDHsv@ 㔵:haekV1_w\pwRP\<80,eZ jۂ>#8(ĢS9?k`NYeЬpOv噑 >/s+vPJG21hvv #(ݬ` mV_޻\vr5᳣5 /}q!_f֟i{=*j*4D{9/&$Be3b^Ex9鬎ߵsk@i̟+5>3h{>>coY/7J+ޡY)XGKZx +45UЌMS3 gc060xҺ  |Y@H3}\ +̓_@C+nn5(19iα{=93[ @sڄ/w㡭F%q$dcQ/+A.lXNM|}q)GŽQ .%@HG:..r-vU6ۥ0Ky4og3> remh\4)A7adCL\ެyxgDL)4b{?Qh!`\!莶)e73EU{E*Q>=w}tEi;m1 -[u0s"!Qo) v+y ZJ4KFĠ̑^[ŅLB} (IĩgBpbե5JL\N0rq0(ǞQ6V>a;}03؀cp*K-3܃vʙZO^ڭl#R.>n7 +;H* )>2v-{0_nl#%uTG=Û_im|Z |M } oeAdKݙbax ekkvS + U=JxHx$¤JPa1 +ஂ[LV6 +ZX{^~5LK7h`a,YpтEV)o4զ1"U4$rQSHxQ%ܔseZ5-\{K)K;⫭]3sM+ 0f<>Yb*M,bU͌=bkWtutEm}K}~D>I-O'CT9nBbco$mBp%hJTc-^]"9爾-O&54D EyPu8?i+vԬ{ }8iUN 0ِaǰhsg؃ر&.n\ne]lE +M g.4Ywsb>ic p7]}db+ˊ45M~4UeÓ #n&dwǦiO|uIm] ʹ*yBC3[mZڐT+ͽOn5a]}pcm +3|SNߵ)8yj'J״IВp$odq/ rl +>#!wE ~ 2c>iϹtd2^ːޫK$| g@3(Zr'1kܖ|w8vZX&h^=o -)e5p'syX\~>stream +HtWKre߈;=BA۷YrͿRJXӱD^޿T|IE$6+h4{}y&v^{9-1^?'f_M&gU +:(>eo/6)vFSG*چ /˃Q, S)b}쀅~ᇵ/iPZ_N̾a<3 *=mC=0=m#k8Z N8.nErnԑ4'Wi4^h!eFz7%mfGV*id>Qi})m԰"XSkg _Yn_XXG>W+s2k6Ufo ֭ɓyM1i4HtrG*³ƨj@*F|),&VƦƔfHgp:4X>)WlФ7wQ/e,:޷֞Y$$|#Hcŋ1Co'PIY +2=VhC45*xӆ_X=XGCnςgJ;/9=NlUb+|Θ58oNNXwwo1qj6!oEUHzC>T;0CŠWrx տ=Y]wu6Xo_1kqn\׋hq(!}Ex։~.#3ҍC@C 4kHW41MF~sO*kbmڬV``:ѓFPud0cV +.0 &K`z Je0iJk#ŖXQ^u@)T֞Q f֌h>ezsQd+lwA.xzW^~흊 $ ȋ`LGiKs{0LDQr+_[DxxbO9*UY[%.unIW$2ZaJMsI_G1qQo:*.hh-vZK^Lg zG9}c$h|jfiHwR4|yb}4 +Xpg*`qiD͠;8а۫]$/h˓UvsX=!ewc}']Z]Zw++=gqP,:.כGP40܎vi +`~4EmߚB"6ǻCg`V.QH':ОU8 +}b0OU,T7uIX*PLw!3)4:fuln)R@@ Zmy/g @YriZYVLL5Rlټe7Ca/B`^ÞZƆ~p…]Kw}?14hف6b:  +`?ڮ }xiggk Hp9M@Ę|/>z (|i3=K= dZG +B{K(FAoA{Q= d%Ay;C<6LΖOIQJZ% +鮖lU: f\śVڒEoؖ%8` +x&)0ZG+ެT^w8 +y18JЂ˱y=Ā4/\ y~?f9 ݲ-j0^^"̴0/8$:_>ܜ`'!N'l CVxlup(ӁjWRA;ekfC7]hk}˸yqLJIQO# XD`if*Us$e-U:5դ%8cra. eCO]лH~,0T+MuA,@S jʅ1܌CeW{mJi )Em M4iAmוcFJrLvNju` +8)}su. M{tu-dN OwMU??|Nz[k=aA_n:^@T?.k#EdQtU`K\w$Һ;+v0 +1Ύ)Xa,~'Us!֨_ #_X朶= +0r_Y/؏ra ?ؔfHnv]l"N4Xk R$}T42b FD%)ԅH "yH]0n +U6) +"HEۏQH}#BvKiAZA*fQ̪!|ԶJ]@86J'W2pX{^7obxNCOc7\/nC2fߪW\^E{ W\E!ֱ኿b xWvXIU<+ZշdUnԗrٮ70Gnajşt+ak&3kZUw)vj w_F:z-A~dW#vqK&p1-/`wUsEC-୪% +kגczDzL\Uj4 +:hc~}h-߿$L20~&0b֍}ML!R) +wv<bWj^.Hx^ 4A-w%h( l1W5#UP}hn5 56]ijĭ-$o f +>ǡT _poi 1< +ɝ:60s"JHV^N{9N]vTO`HHc+#\|+JQA_߇Y54 32746*Խ},g`e[6FKQuc,!5!6]8@ nT?>ltZ-X sWX4x 6($Ne= r3RIXS$3YGvjɽ} 2{b1F=ƾa4&ĺ$WB+>9REQKs1 jP*++R};mt_[QP!#%6;c3GTXjP +|aW<S{?|Ů5Z58sL@Slީ |䈡DwQѵ)5"/o]@N0י2g_RL4K[Ơ!i^@nZl20YziQΝ4;0CFjauTi($7P_+w`e1p:I`T$t\Q=[Au!zƿ$-#dRޑj ɱͿXߌw%+2OjIwB +)$ MM㦽H%"NɡF +/ms>bmC A;EKV%bѭg}&OJ,|0hj3(:n͡6R6>àyK-;?A'nhg39\>%`Fx kR4JhK>=Zh>O}2r΃Şsc{o'f.{rnHQ:=9Z4=Ҿ9$+{rfQ{Q.{rѩS'{rj&{r$ \ L],$`"6{n#`;NHnܻ"{=z@A*%R$oL%s(1tqe&y8s%q(1"%;3 +dHta;u5(%L5'/#?cy\`EʍD76J.wXLt W`H88d8b,s(qRJ1n 3~ԅf#5ZuaZ&9U4<hr- ,gZS lwlvD)'}޺fW=Hx3Տyyɏ &YQĜI+ba es Vx=+cw,-@&,Otȑut ]ncO>}9=%,ro\k y_OvzSF -{(iƝA4\)W!?]@k2b@; 0~~vVZ]f4z`Zm>Ġ17\=Qrv1ƸL4"wNrneΐ97P̈́(777t3Hr3bsCvN ;XamR)0ȹFps+S6,n]W77 = NFݤ5uS3T< @'5QiNͰU֥Z51X~pj EJjyS31Շ`S5)jb|DPևu=S-jڱ1U~O2q|%+ +È2 -wIƱЗGљJmOAUi,ow]缈E+ș(5vX@.GTIߞH~vj";vv)k_^~~!H4BC@kݭGX\Vt5і隑@D-cE1%Ȑb۱Ϧ4ncMo;P%/ 40]-Eb~>/mlE4|Kcޓ߱]>{Mbe^5E}PP +Ȋ3BDqc $k q1JJX% ked͔fZ!{tn@p#PVe$ZOq t_^٪T8˧M4w)/M@+\m0W$9bV*H `ڕX-`_d\n-N-=?|0/K,sjuV1S1M$4V&ψZ$#ҦJ O=I^&PIt39 X,C +b<;@Tp si{rEmB$ x`bc~<$OjSsv-&R{;Lb,D~WO BHgլ8t'2* m\zS}SGO"n e†^k)'OSP,%tU\5gɨO$tkp}f P 2c>FWzP R2qےLI! y6LYTXfWoz\24O!=Y>g&qWiS->iH3(76b`MJ$ekC ]6^-hx[[>|{[nI_=y^8tɑ<y."P|Oˢ# +ZM5F ]L$qYmCkh8>إ7oŀ 32٢_ ,Ej`^gr:٬h {j>jϳiĪ$me`ᔧpݪ %̳*-\)"7%X%?%Qv,x:\&s2B.>Xˑ.cԓ ]*X*K&q,񯬋ξ4P+hύGY u +&I1] |y[:4vs$GaZ +OٸSPһgzՖ_:X:~6yM_9[5*74#朿c+~c".YD upqz"$sun.!Pؚʣ8>ow2YԊ]fg̳ )PN-XD}<1CYUbNsͩzQeْSEP|fcN +Q5A{Eciȝx%D]+:37'ѽ$=?gUҸlkJjR??K$fN "@ } =)5_xe,1Ti[oEG7TǏ +]D6ǔa8/`4e`NȔgɀ-dV1ۓ\ +0sfe^SO2.>XW9F T&w@K2f@ -S)\=#Ṁhl;1g$:n x +׏Pz@IM{ lR_fZq>խgO+cq⳸8߬|k)SL>-vWÔaF +bU>$$g9(^dW6(cz ]_CR59*C#@'*FTܨ/`a36LI)ngʬX︮ojH@ Fd<E"BGw#F 8yuW@q~a7@CZY ~d@&}2vG i1R2PUwݡ\B1)+,6/z{PI䕴S.+Y5",/xQp"2QҬ +ڝlP>M)X%vgtdԼǒ{1~y*Q7(ٳ OxథL2 Yͦ:Hq%&i8=f HY]i뢱LJ:iXm"h7cY!TfVwUZHC_$K}ɢLy~ÕP~ a5,&y +;AJ pA<{٧HRV?$-+LqB7γ+9vդ<睗x0Cu2TbQ!mqDq{ÉEB!yE )IAe| boψ.@s +[ E.d`|ֽh*>AЊ0FZ715xş"U'`$d,v!w\z9TUh<}ocuϼg]Piޓ.bGm>Lbd @TMwX4)Irn̈́`Z:1ŌWx2HV!*HQ4 RK*k3~t'ȩ#1^s&^\-^o׈]gF @((V!?RǬz$r0P$Xɢ"`1hZ,MF3؉ {F<8X.+N±3Yk1NܑtA~'B2x +rȨ]f~aItNIN@}1=y^Danv?X.p2ՙco?_> J? ^EG`'-mߝX՝"W.FϛwCz9L7#n4|P}&>>xQkue"h)uol-DKBS9dEX{MOvo-7̑ <7q3{H +?a:^2wب9ܵ6!YXS"\ Xs+ײE7?\oY'N)y Bԗە~労XS~rS2@ 6ive$O{TB-O$/Xԯ.4 =sK-,µ"}4%`031E$hj>zFRӮ]?"וoF@oN#P螲HGYJf?,!ZU_\RBihkNCށh(SOV )kGp\E[ZA`i@.ݲ]a#i^{pKjIĠHlb^.Q +pm[Ӈ`Ϊ碄s\!_{ ^'g-3Wl˞}! {diԢk絇9NY/?/@)@C>BJ\*m u}/o s`i %ċnUçp|Ug (kȋo%d!B@X]sfN!M\o/CRaZ͝J).&f!߬O?dYr=U0 BΡL{DU)ഉ%z:? (=s,qgѩ{qJ ,23##w?X1f3̏ҞT"ؙZαyN[ӊk:^ðUE(J\֋UFpl d=p="5N99EK"YJОbSVߺ .&jAw&z]LlÈ2m6ޱDـ !y]h8$<* $Ҍ+X3`̟N{le׃b?;k~2aI|?/N\5q5/4¤)[ ~lS=[W+U?2Gj}90 `XB}K +^p'9!{n|9+`,90 hq88l OyOgN.3y~Nqaj%8#Jyߊ nd`[p~7sVy J/6YzO!Zn[kG,U;ײ\xUfq~D +zWRI &Ր9iH2U%|u~cc.#~D/[p-vB63JWKwi)(tBI=) u$= UWx\vuIFfųd5py?al۝3^Ĝ{)N!}d~Mp:kȱj,k?|7 sUH&y rj΂d8Z. hu'JayIk<չ>ܩ R,BeHlb>1;Ϸ^KliQr|v乳 tELr>׀c~׮{>p x*+Tu*zU9- ڂSM)[fݧ f@R: P/es6˧asI+[~;Kk]ў4e4U1[yH&eVƝ&kel]ٲX:$YJtL=KJVj,fW3vMzXD(쐪WPS +D4c{kInMs;u6Ag$MRζ[gTpw:XJ?u.+>T6J=x쪤j{\6@fG,}w/P2Lp~xQ"j(A44Y>#ZNW}bzNfjZ}C+ب@1%R +j "⭢ANS9deޢA&bv*!% *%딑8UX)Oxѳah*Ȑ$_EkW'M>j jъ[>k@&zEZ!S:MmaX"8(́2&X*=:԰ZBQc[V:4ZUBi.#(mjA4L!bh˰:3(MENU,ˊ9JO*y HX=&^=#h쇔RE՗o^K!̱2ԆCL1nA40FCډ DeHY)5f&uoԛY6Tu%5[d`vz\#kÒr۝=Gj%F eUZJ2A@}XP4s Ym +( =>`ZdPKZn/vvN2ujQ-̠S]X}o9J6D~GW RDܮ +82&URGP'GiЪl>EEH6q<Ye}tmۇ Bvm]aF0MOvpA6(Vf6׌VmH{Oyא0յ ,Mz# uC^Uqvä}oI9ԲĤ(QjŰ,?k'yedQX 2l[rO*al$rt QJ}(ct)i*^Nycc>bDj~?I8D3Kꥃk:ЦnǃxT/f`?~b|=`;uvW\<PHA䭴1.@NT/D4o_|vF |Yb|֜s*iqsE|-2>j\>Hcw+e|MU$|"28|nwz ROx8?>` +ҵcCnY#Ws74<̷MSݏǕyhz8X3.(TLD!3y^s!Oӣsmw窊U +"~qUᎇm~Tޏ f^6)$Yh'&CB6xGw,-0SA@-=aqJ=hѾbSi b6]>fRMr >Ns=m)p~N!E[ƻQ&jf/0qlFۨÎցWjӉQTȎki699×-I#>"ysEDq8ݥB/cKqs`v)p57mdo:";|&X1UH`Bi>LЭؿP"f 1-K^ȓN;cZ?m&F +7pK%a$0!lh;oitC7>cL0^FO{w4_ԅFIۅb{|T h5ȩ!cv|ibQI" U>@eD6U2Gd}S(N qc^^93Iu?_%ݲ| \.ϲTBpn@V a8ASV"9kJG g_Uޅ122W9a_6uݱuR+j1Wެt\CCQF:!☏j^C(h-¸[ cwv2 +Jw#D|iCN-ѿ&i]zG[6s +/CKMNvdE+ap,502V^}V5P6V]kҝn Zm. 8nW8:׀Afϗ$!sI&b5Wz_\EKB[`x_V2/ Ȋ O1GKjpI;Se Z<5 D_A94<~ƽꡌ$RśC(bCpSvkUM6*t',y7|0C0ƌ..#u|klڪ: d84lYAU߮f:5{׷H^+7vՆ V26i/A(dJϮ&ɰ0 \D_0+.aSU0wwo#JrX:}n C:ԪQ%lmag*7fFzf/.\>4cWڴ%,u2NQ9 U>3K n쐑9k,Cls(GW*Vm8M!(b<l$WUm[+3?o4D$=Nc2 K~[. 5~?h׍dd狩ZJp~B0 To`[.ԗO@X h7v_3qj_o C#1z Ii84 +N̉ZAdB0%ȴj!XOIb!0ćzU"H_dol9J -+nW U IH vp#½ʽEmg +<%"SP7"¥} +&ښhpo?~(9ilX +pppaZ +ոw)Lzƞ.ř<۰&1DDr)[PY`,J@L>\^5b AIJ#K)9zLچW$c*oZ&@"WD{mjRUNdFE 0̍~FѴGX6vnOQp!ޙSz $ؾ-5;ߗ.9Ȇ(nh%a@G81Iחg?RגE76c5EUtD? +6Ek?0 - K T6Ha>IcY fP5 2E+pf 3DQ6S +6}zB\w\kW.!Ιw>`0HwG8 ܃M+R870]9Y |s&I3N2pZc8s2nG{hg0\La b;+YR ׍ai@4`/^.0yۥ؀\yz1 +'wx&mլ5G:3eJ`6m%{T; 7Fu2aCƂiK؈,)7pr#X +endstream endobj 431 0 obj <>stream +H|WA; +C `ݳ-1$Wڞ#$Aɚf??H{Dh6ߐ=m Tu*i +ia:p8e}i#vcH$evq.dߟ{_P?,fko;~$70Q@Խmm㓮GX(01!cf`K4 0[0wqq' .mnan;2h ?t!c!#o1 Ժ9◓16YɎ0)jO@Ə3^v<;wbp6b +!xY,3f4 B[c~)ZF&ݺd`)鏷:$;!b%quGh: :Ousx@;W0W: Ht |y7 +-1x.D4gߧCs̘Nߺ#q|j (FŅTYQDiJk1^]Ѐnniɐ;# +x~̒J 4" a.#3[:@KRQֿT rΌ@v󜪢%>BE'=QMh:9Sk2q;x4;!6Y#iP',a@ΠJώT +P^o$oJ7_ow+Gm@1|Kp5fNYLn;$#tcL42Χ} .}":oB*&,d(7HkQؤG)̨ VjvwdOThw@;, *Y /ԃk>5/FFv(tL h>C Fq_f( +-$TB"kmcѫkqEB^Ď@kYOiU/@c'?E4!VOJ]&o7uҵguor n:TZ;7ե%Grdlh_܀Z<5^D^hʹvZc<~1/WʌdoZ4jwzuzȕ$}'!RK.ŶP@ܾYypQ'21q$6 U AP7DJ}) inǚף+"=v(~`uI0f7*8w>zo+lCb%* QmjWʟKl=Ady, +~of̙ZJFOdމ! mj! |+pm]K5XQ٥ cB0`F:eU!Me\p"8\CAi!O$@S!{Fs۰Um[վΆOTCGGk_J> y6+>aU +9982qUVKP#&e(lFYLRon%JTro@Zbc1cL=.($oJI;Z2@b$ Wn`)Zt]BDpTr%c= {~R)I,$6HY:.-haφ@VAL&Y@~Y0KLU4Kŵ1ZՊ+"'?O"j*&Z*%e5M 7<\.DgW>Cu@vldW^;n'lkL4a$[243} +ٴ qCԧV'bq-<$OoОok[󖞃T5`&XAC|H3GO{1_p> $훏0~Ǎu_#0T5. ifqlXg-ȑ:U2 LIdž%@d~C)0M$ppa\Ej@Ex֪Vg{*7 2$S6̤%W'3ے_,ļVO$Ly{?m>.A*ъi x"q>a)+~j ſ*!2nzրaGp}SR]KX郎տY,@}ƢbrESqy`F,a}8 Tk2ZWr)um陙U-64یW2X0¸t60fXJx(J3o'q#V۠bz;TzƝ+; e )5w:ud9zqWRREx?N% :6P@m7՛6$Y  :OպdDjM$Ae{vqq LWvdm1VTy:twӕjimI&1C8/y)dg;)9эˤZwAQ?q2k4=OTOo46eHq$J%AqO$j4Ҟ-z)m!ƥbbHʥ:2C[ P*S Wt{g%O1fC̎T bbC.C|nO1y!!x[ #.S1p.}0">CʲC =x!AH=}q l9<8C10 øxCѭɼaeCc[GF,^= 8БCq=+t:SAO9,uaa^rX!Q.92Skx^9%꬗קzC\Q!,[#{LHةCp'Ci@0.Q֡Wv b򨾂4K+ ( ";sz ɬ:CCgDÊfס֭ȫH=CgC\K"z,zu:/=֮>A}0=C0:U(0ج{[ Pvzgu®{A/jqڵNp +ՃukɽGέDˍ.*(`^N [|!h~%oֲs%T4^}M'0j7nJs}l Z@WHpRwz~+2D>`-7"C%K.ٴX'KNm'&)ɢ1-J:@戒tX}{ϲa}Tq7?JC rwp/M0M{%< +.zvbxJZjpS:ʇ54e8SOBum9ujW!Tp$%B٬?- wNeg[a|019X0L@YɢhɈ<3-"2|c0HmwlѶ7V] ~`_kz"t-GmW7 vp-n:˺ +ӎlwwǸsYT +*O,"kncg 2D~/EjaǢ}F8Dv"8cH^ +L, i享8Zs-C<$4H})Wη~'("=>5.ՑNpNTg͑ၸF(˩dޜGKv +Bt kۨH϶ moaNw/Pgp1?mneGfʑ^Ke1=zrL2 %> d +UmF3{ҭF]:{wfseXI_9܀sRi=C2jy>T#SΓQ{F .3OHMDdosAZޝ/C9Q}s%2 ܨc|d݃ d 7$JC=IeYH1&:s&hvd"IoQwhnTв׸.`N/ՙ}}'ss{[c070$-HLQ X `ySioUu9~7 m'oymUْae+.U:kI³K-*"FܗU*UFcPi>|ˊoVla:HBm+镁 1B[U+lH { ##H Vwv*< ?:8DL)kʸQ^@dUhunVffxV>KcȠUoy(;ӏ6zmϤULH=y4:G}R +evk:{cܼt;%D}ڃl&hL_Iv1hE$,`4xe*|8={]8[d_G1Oܴ;O qr]~oo%m D9l*]!Q>I%oӱ5`SYO?kgY*=־C)S*qvMNtQ[074BA-u$+TJ&R1K(5KF8)(~fTF{#jNנx>i*i\\4sL*?K lK쭀c2Jዕo0GevKwh+#!=ScXw}Q&-VNI^&݄Q}0 ?s Iu|a+Y!Fzv1Q_ǧ1ȲU-ZWUJWq8793z(JsJlTr(ze@$FChh"̆ZmӍx;͝Jqv"XUH o<" eU["SP?GyOa-b{co1 6_b3ik 32t̙;XƩe.ԥfDwg4='[iiLQì-U}!3jR{VIzGG Lfc2ۦs ݂7iutا^qB);\kăJʚ/6C.,-.>k\5Ǖӹi 2czsMݥ5? Ň5D<{)2׮)zW)4/.p~(\WgE "x(y tqcZoֽ42 4fz> b@&@y{C.u_z+qE{߫mfuqyϪmCǢ?vx\oxo17C6G  +;#4V ˭d{*}K[Y`y;Zy:I<8q29*#C%60y>ZRmM N_oN@@X DdʪX)a,P( u >*uyL&(`T(! oVnś޸Ÿ/0LM@=6*/Q*`bGs,?})wUS)SŢ2=M"wAm1? 氠v5zQI!1igozQj2عibT,_oA /|È/l~+u7d4,N=Zdkn5J}Z^xBpHB 6U)r<`wpR!LYːKm(}YhحiR8%21̛R'|Ē;f7!f3L1QA/JCY +ju%,]ˠTr@/<(żQYd^|+v6nz,ћ +ޱ!{bnC4pD+MgLq%~N-[ n zdCz6 Ű<* W]m.WJO +D ٴlԢㅦ mqYӪfJ?f{Uy,-Ì9]%843'SMBLwJOQĖl( SТG ZIZfJis Q5^CPfKbYKiX~Me6k9{v@49 +0zff3i$b'Rp Sb'؜(˲-33(;pgfiQ5M՟yT @n2\{`oc6Ym]Boܴ%Q9*[b J ;r3Gъ O(8"9[C%,:> )GX#%[}F(hM&4xDpS'>ʨEw{R{4nvH֮ڍF}9 ^GыHZǞP&CebwC 箠 [D$Ğ 4b8D5YlE:h|Q=Y=Z+;L{TF5f$WU=}Z+UVj}aO!LwJwdh"}bFnL8{oIcHJ͛srW9ӊDLDY9y,VD^U;$ډIԀVm?2Du3Jn";6SF? ' 7:ٛ{=T' ?xo `\P_,"HB¾-4^ b[b3ˌx?ZOqDoZPձeD +ĝ/]"dԼ,O 92?m"^+"_SX?Ɩx1tN d&hbhj5G#5ɺhODUqBv *(,}Meb" ~|vdpk5@X%>@50r=23’{gկQ#U~:OސԠPyA0%cZI" gt/>="%t [~tY sJ{loV"^ԭ(SbmO-D ^,VXC + WT:*BcO$U䞇&o|ywL]?qI걬9Q*Q}Td2.㿬P72]2_~:vp5pd:'75]qCX[=k#\ 68!\&6#D.D>Ӂ,u "9Rw;y=ѩ3<[2)>o.R+ ʂ>0S­DvOlq'apt½ p׾f̍adaB x/yw6DNeQzYO`:_=ɋx‰lHy6h4qmTgPVHlB`]*bA1&:6ehf-- IiUl@D` It7Y-B7m@D;[Pt?nrlz[1gŕ׬۱87Q2Bs*0Oև=BzXfe)>,YqSr(ަ6 c;/HMex5#Wj0vS_\ b F`455,@c +Ŧ6~`3} +%V=RbݣZ]{!iI mOD"0h]+EbOZ P H3Rbؒ($OI4$@$"0^m*o ($"Oe`b-ɔST;!l%ʅ;?xfX&8zlz{)bfOٜ!5Fľ-qjxԍ$HlU~X:۷(tH(G&Zf" 쬸Q]D^U@G?:-h]3;"H~},-l|ګh'f>ptI$^ګVr[qO1u ]d?שXVC遬!b9x+A7 Q#*blΕ-L`Fo*fmǍ?Z0ߧP2(kx\yM qŖ◆.GLdorZGiE~E9umd9V"׋4u +kaU3H7c~d Ϡ kHSHrekurږ HPχat!)Y䩂9VKB p҄7غ&@jJ+xCFrH!.o8WsoT76Ĝ}l|^>7/?k"rs89ΦaD.sxN7d7kh73(xV0@!"գ3d"nΐ5 0bJp: 'BR)N'f >Zj7.[hVVG*\aVp\Տs[Ⱥm!ձGɱ0ƾFT"QM&4 Rwe>WRtV iS$}έ@EWMǍz.Uy\^s\6fK# 1T{]S |#Lf=țe2ji)o ur7:AX7aMc@F=\2 ]s%ݖztDc"mmt]#7|_m\/L_`0{_k 9'?xk'rTl^ +U2D\f; pgo4ɦ"3-6dѾ4E]vQj{"檁WgNR|#MWwsw@Qwc]WphM3d(s`7h^sY:G}Z'O]G=$ ,%|_v^qeTrfNױ×odF* gvOsQ׌.26u3LWn.; A{yMΖޑHNi8*cҷr%u|v| M6h&&*$C i82G;YEʖS{2"3$2Ys}nI"EH2 +|mb-QdMFNmӓQ3ݯ/3rea R ֞\w.zO}c(ek}<袽["c;=Oq^>3|#/-K:|&)T.b HhԪݵIYbm5rIBdĥ2\A7Qaֿ(D8Tjҳ5Q `/aK>P\b7+|cc>g?A97]d( mwMknomYN?~BA&ū1<H݆+pfΦ{pn`_c!!&e[=KY1B|<]+O^€ˊ& .1ja(ĆQj5f65p.Yc߿ZG^7jV0F[M >m3$",h3 4uG.mq!I'x.]濁t Jj.)2s]Ah> Nṥ1 ZeKќiN& A&b-JPlP"W›ڮK%%G8 nW_!dL{ Tj/ JOQïF?ѴF"# /32 p`:jc D2 Y@- +p:Z#0_pn PYhƓghJV;;Н4\<CժUh c2&.)4D5H:0MS|4/f)}6:G:_B +xF y,Wo6Zd2?|o}L3_?ڕc'sUhE3Tr@uqk8 Z9Tds=-B 1ݿ%<>}$@$^`Lʈ@jV#`]TM +i>}ܻ@ZC}Y nZ)'zt흕m' 5XE2W"gXROƎ! Kh~B3NC{4VC&6\fG6TG~mQuH d)X0CdD;yD 9c ہmÖR3Ԏ#u0cR|G4szm,@$[l[PJ|r:һ=7Ԣ#3CE:j= P#?gc#u@LH44ef^ug0XMt [+/lr +#0@>+ +ߴ Y/MmYN_2َ1(awcõWfQNJ;@MgFMmA6\\:ϗ9(nɆ)KFY6ľI{-3k@dO=\|l, +]Fnjcu^:)vj,[H"hH;Z7EEDu ^Y.yLsu=+ *}Hw5 +ROX֏(xy>i#QLCؗ?Sslf}"]obdJ_dhؼ$?.fA}<}SɶѬn2$]Ǒ_"}^L_X({'Kd$8sl1%pޟǚ-aE54MVz6dgc:I46 FQv R+g62Fy,љ):2SjpM~ lRceu vQдr$Ӯ^1VTaO&"ne[XzCh?ϞkLjtH\ I <ϋ`\H3bsa<ӿ(pN(#:&Htys籥cR j~]lѕ wԆ%b= Rsh>:BPEՔB{sqdhlmJ!%2Z Iă毈z@l7XmQWA)P^Բ8۶h8M-%L ])W  S.icŻ@, +A,+Z IYy7#T Y +`9w[?-<%n#`+Jc!legȟ +pG@i0|Df[(5LJ}!:"y7H 6{Lǽ UǦz^j2w_ +˴¡>#>? [úRqoNцY]H0p+Ty^qùLT_-t΅OA%uLՑ;L]f GQ-+T곱7يz@qyᾐ60@Ċ3*eFJ&3ƣ6"ЩEEpUj>r& MjfNmڑ5RxhaxeT6ҎMOFw.#la0BC^d~ {ܼA[Z0MKns =p֬&ŸEIƇu{Y7e~luꑦ|iϳ1DBsN˃ڌ|.TejsJXӄJܢ8{ dUƵZ!?#w +m-{@PFZa{^k59w[Zʀ1> *C&4dt$XT3̴cH2Cl*JќtLLI_b"sPb9]VIfXzWUӶQlGUL f.wA +Dnc p_̀Vn+#`mǛA=Q JŠ) CʸVAT*yJ? PYzCgލ`, ͕H69n1 њ[iXTWje}D@شd2WӶ;\i}V:'De6K<VFP5z 8'i'AmeB ]jc@ LZ>õgX aHˣ̞2H*X ԽF΄Cg>x{/@t *p(n}J?S2y8.( nڼX'#K)'C@*Es'ZmcϺ(=)1WhRB!+$# arxbBl W4\d\9p퍣FΖƒ}u77 DIe:Q}rCS>V°yx'U3Q|*~/N#_`4ZZq%SVt>LWos_, +i\XB?'O2X:W9^rU4И>$om@ŀ'V $K fjz8(NR\"e9ެ?ODzIX{GXRęh[6PЧEz^ +o[]*%UR6G=@I]z;Sq[]B]?}ev>g{[牷O$e;ZGOP/?/?[f0S-}sS5%}?Wa7٥}nsG7֏ek\G +0+̷͇9/1 o0oe}GZodlB~w]{hdyKəB7kr)[D/NHro(70wrL#@"6Qy3;AK#pdρO;6ٽL!_( +62Zf$u=hDRM3^K)#}MMP&̿r i2aj*$X~D{ˣ=66R/-|᭢@ksbWM/LT72`1i$;SZy~rȶWH KA! s/ueSQDʍH➅oy4v>"t9P U 136 |FeBO^mr̴R#ΤR!D  JI$-SN Z=*ug1LS)x0\hoo=G|UWμmTи[] ~K {%3{w0p{1!q YhlYYkj Yçi{i˚(A;&)7 ϰ݋ DڹEб @+{H7Ǝ*p_$_u$Ƀ7J +e(br!2qaÍ;" +{ZU ĭJ@ Y1 #`KMneͩ]g]bps#Gp2TuI>,coǵ,Zw ==7F=i<_4ygXt@F(b +'\}n{yEuQ4 ]qfDbz5tһG *$_z8bɥELQu6'Gn +endstream endobj 432 0 obj <>stream +H|;r;sW=ĚлUYO/t>lkB'MJ׿@Ə6ppmE@1DZRܱ5"LJeAr2NT&#61r; +j/۶ȶ3j"HCf/Z:ǰ!G,Mzo,.V  ޟېOnEP:#,/!~h=ClݗEbEא|>dUCd2q^]{+࠾>~G&1G뫡HF(.ׯ93D,b65TZ 4$r*/wĚxl8[{Pи$ϟ*{ĥ7XIfye<0=!|me*|G64o[hfq۪.|Ηay*ZX mMp|V=~*:VoVL9`^*ߐL``?Qek66%uA5'6UoxϹNAy80~.kjNb\@l6I%]c4+ +̟ NE|`g?#<ZE +P4c'..Raʖ ہs[2#lcWMJⲖS@0w~zB@Vtmi vjdΕ];WoI>:PLUܲO(< Ď>4XPʾMpOSLŀh]ktTgpYoY3a78y {#D vL!{=Wͮ@3jZi\,Epێ6U :U* Uw)3~.h + 4oyC2$AM8ēl.>F /m}cqN[@g/rwJڧ o2麖i +ٳ!$'{OulTt^d4Fɓ7u'muАPIb浧|92٩+4jّܼ3`9w$ȉs zd,"OC X^&S!)kk kDLB9X-*u#O rHnM)qỒ$5 |2kB+B羕1w=䴖_wqM!kRdUdqzFdʃX''$%>:eM\|' "㉂MC?,0+&DhE5ZZ7༆$AZ~DTX&:f+& бsQ#f ٞٻEqZ 2̭&vQiϵ WPC&6kܪ1gI̪jsp&Pc 7cPMT̞m+oC粘AzeVebtz[=1jաT`keF!xɇ!5>Z'f/ )3Q#f C5琂`N\_\}:=4NWdMԆ{?fy5])h\ ?fK #nwx͌ZyDL6)OC趝[Zse[R4p6@% y1Dobp'c PC!`KAލ8ZƯ֊n-)'t>ʟE3x4\w&=갾[vQ> +"@uj}`g6N܊CMp7;,m;- cLO釯md6Z띢)"Vp>fB9;D}ojsh3C%"զORbfWe,1e~ FE#S!3(D䉮a `ʓAfvR#T HoC粨5I 6_ynyCVBlJtWK$J1qO{=mAYݳ24w;I_<*z:~TkeD\U *:;b_z>P(Ρ 4VahIQŽH g?(ۺA-'ZL1:9X,Y+6pSxlevWJJ jǦ^6^`Pa [/lG]Je)꼨v9)/z-cn~Az?{-`g2xRReJJMS@Y4P"Fm5:d* Zv Fn~Qp氋+<_ e6|J#埿K::5?11PYGE +"E?% i D߅YkUX~{> #߁5(U55y:i$rPpw"`4U=hL U>Bf Ҕ;1f(lWIȁՕu^9 +)O73t?y_hƆ{5|ؾ2>Rz+s7Cp ߭,-^=kfb,@^e$!JVTPA{&1-4$:𛠭oc\,m>rdģ#=v$ 뵬W#,-|=ϟ|YVWe_SO=뮭cnu  <]`tV7B'-ACP"Sni1AQE׽ +omک,0֦d[У3[i7ʈLi꓃`u0ykyN>1 J= ؆옎«DZɫy!H +d4)%DI3(" ˇwmyZ5[ϑ (˧&);ת)Ɠ +OZ9V@Rλ*;]e^mӷ.{Km+ؒXOQu,>i)Ե>T!V*e֏i +7{:%FZތVk^V35?D)ֻuAڌQ'烾Uyɣ@8"mF%43>P@UK4_sY٠>UI,/iE!\Yt[&T댇n c !;ս#99Bpj.Ӳ=Y ҇=ȟ,v/YDIO?#"6mXIz#ג=]RqZωP?V%@g .HCဴ2Bݮ2hz +Ƣ/[]ygR]]I {Cyeu-B (%VDz!c β,? ԿGjH+c!?sx+"`/041L]]š}].Y:J`VL@QѳL 8stOX-g5 _w@ $$5r*3)eФu;|aJ4P0<&hiW]>h7J-8 x:9Bb^vBu0q WUyD4}"}k'37zG"!&z^Bsx-P장G]aQlK(EG]%v(K鴿4_t\DzdzԂx_zT}5Hf8;ҾS7֗D)D792_++^RęHqh]= W:b4?MdHy q&z{ `Ӵ))h +[ҟøLE6hiZcti垦/WIE]Hc_ݘ6_33&Wqso5Ͻ>9j>qCHEC,QK8G%OB }p0㔲OfӋ>"m&T)3a,f]iC +'"O8?jI-n}r;5zl?j--X +:FzĂ/ 놟QMtLm LGvx(zހ{=bÔQ}U4C˅/S + +gzdfg1Їp*Ѵrn!fjG9 v3>aeeq!pFol\?1ۂԻxELn/Q1Y:iaididKdKCi4H)n>q skߤ]9l%NH\ZvY;Pդ,RcT}\-ڌGUsd#UxTZ{(pRWӪ +rp^>hD0:{ WL΃fĦ]F(F|W]P^>>6Azu"ڕw(^uP8BoBL>sjQ=c + r/%˔w1LAPz`0kmOuRA:;kգ-bNUQ&-DӍ>&lM^1M3/W[q"X7kW2V_ ؆5=z<+ĵy}d2hyVԩEO@3sg@I,zj'_Ҿ +mckL^EE6F/c.4#j/O[v~hH|XRM~1j +Diei6x 7X.7eERvOtwp5;:3?GN#F E(%.Y,X5BRW~=l1'Oxh#\íH)]hE<+oۇ +zmz +,u-;Z:M~QA7̮]{.h嫘*>nɅ$27 0XEU !P*S>]GQH8eѧbB H>D +MC *HQ@T$xTƞdW$mg RrrCd_܎=jnŢWU8ďH4 \ +﫱VN9ѧ~LiH`7ۛ攀.}[%`qM9hj1>˺BDa(SZj~Eu߅}sSG'T4Ckn&?2lS,*ӹM]0K}Y޳?5(&$.yq#u#GWi=/7nJEk~b>RæO1}8D%̅X%& MxMiч -36t#,a⧪GS+4GU Ԧ˒eVMmHo, 7.@ 7W%EK#Gn/ ީ*&S VCV )19n,Q02 +7dt` fF`Isܜݽ)2_S`pi/Qz",9"߯e\+Xc+XC{qqq9ȏ@[ *0[G0 x*4Ύ>Ώ0Plz]4$K*pr&=?Z|iBRy5ڧ@o/f Y(s7Je&H2ʣ(n|/%L/*9]CR$}ka/w doy+Q`MS7H`P)w\\I5&oA!%2t5)~MI9_8[hÐk͘B^Mqŕ"x#%dg51t1f;QDU_5 C<`RR2-GgǽH¾mL٫ /{k$`C'" Y8x! gk723ͪH2lUptj$_sހ8V!+jQt_6QGVzE lh3~%"VN^ %nqo cH4+KdQ̂57 5wEcH1Ċva'gb@E6uCp?[al_Ai@F[N K]0b4 438k[~Ť|м4*"CbһOr!DjV!C )"4o_k3`)C@vݹ^vDziBW\=/)5dDhbť" D\JxֵZ,io2;qW/]'4X\W^`WiMK7, ]FWpХ_{=gN! `#ޗ<_ുbVX ڞrGy +ʠ?)Ǧx + [)*J4 +JO̾qu8'XX#,mqV<{DR.MtK1*RFղ@6h +nEږru`͓KuJ$u1 T| +#zʊhjPB$l,h?q 徬L\0-eHK&$omybH(3m4:rEJtseXt#ǻjjpՓ@?aQKeiQ7ָޔ=r2a a liB͖iʼnA9I"Z5}tT=Ʊmjsjfqi)35Vv.B\Ap07@n$X_zԲܢZ4CNXH$~ߗ`6FUEom,ZVo)n ؙ. Jt xWftE@:NT8CerRv;"v4K)mS zqٓ+-a$u6$US1$0pPO D~NAyԗG +nJuu‰՗ [ fɾn^}>d U&6G~`N;Mu - b2->Y2;XgZB( Ȕ,cۊjW(x\ђvn0)Ŕ,-B) ^1ۤ6D?ܢw@ŅHAGl0[vB=M %c\İ3iJЉJPG7)FYi#2j nU/ՓRtD +e7e#xٻXdɦ=lZ*7d#) 7믍icx@e `Zyb;cV8}c;%|Q: V2Ao>6RJA +Nbez[n;,[FKT3 ;ϑ wޅ<~]+A}9 +H%⫏ߚ"ݱBU&ȿ:!W5tNuSܼo jw"_ 6c[FSΎ zWD&]k:6֩kB#EDVR9g\t@ZjUu]7[1Xty"?hAImT2_/0K)H#DQ=#• +}T^Ñ9ݤz!YZxîA ;ZnU*H]>։ C]*M!eGx۝-/.[x^ooݥT  d '^H븑:px.Dxخ#.q|MvuL\2Yt^IOqVse9p{ l֩+ 5IFzyæឡqmkp@0pqp9p$R88Dq~mM +? xc+h'tZ$qx}̕ Bd83 ǷʧX-rf6A.>`-&S`@1@uj[ V^O8JdLj<ަ}X2:3Y&j mx@r˸4_{CAl]I]B'GhX'S*Z H6yje Z=3od*{5[DT3m jͱ:Zɪ*)K~\{-lֺ9`A:kC#`Cۄ,\&uXkGvV"l0ȟeZUHN1'Lt3 viecRܞ2p}3 * {[ӷf$Y{:YkmqKIW2;څ.&°^;ԭǴ[\HBj>}${ܔĮZk{?鸐+53njZ*/}eUwdA R)AnPL2@ *ћa3niR1xmUSni3c|9TU8? 6}l-,<;ڦJ~QfeL7/պEK@tŕ3k7<ǨjTcWmc9@;sIH"bhꫡToؔp]~fKɖTx1 6D+4H~JQ)R-V ~[mȒ ^n5Jslq(dN68%רfK)]Y¾R6sfͽ,?Gf1/$ĀVp_ D{>MtKZaj2s1HuD'p;a3V[z/R%ApeԪgO҉hߕs_u(s![̡Jۤ;c)cj3R驵#{3^&*N_*XO6sYÇK1yDkaMhK\ +8qq a.ǎ[k8r7Ae<u 9ot |llbYB=y2&i+yY B{%k#Ns18W) $>VsVu}-I2p yFRd1@eɁ%׺iܖFK(26Դ#YG I:6xTwE& +>F^>57#xg.W-h&?3^Bڳﻱ6\O9֣ dGFI>TcQecPXz׫PZ:A+Ir,9DVZ θĜ'2ΜC;4 +wLXH&DdlA:9Zہ;v֟>>W?nO}/0'AHdb _mo+xrr䟱- +mhS+C- %l@@tK"u{\xNvd@#(\l K>kԬVRu.i8m, 3fBJ~Wk'a\9#F+wPFp9ʺjԲs=9JoB혓ѾY2Z= jzoM)o 5E`u[Z}[7y'B ]U3ljIHR PbIwP$1hR VrRh\V[78FoDQ|j_f 09j̆$b~s:慥)A5+6c]Oe]prl3h-sUaA2cv*&0UyEwAV_7 M2?5n^kT7~g+\mpy>:^(%菑RБ@ ,5!);2>cT]))mzqmg PfIbw˨F YM56ƈ"GZ2r?V7 @;ˌ-C=#lY?Vqqv^d]` bwvJCg-$^3Ci92L̃-]i ((IxeBj?JgG@;W)Awv_7t1w*9*1\*Cm^JoIJH4P@Cd"XK ZRuY$ս;s3->%JD-о,_4mnG*iZ~ $m * 4D)oᔒb!hU"{XC-ݔXWhA*H$s}w숐6x(Dgށ5Ax5/1ںUwiMyBMul3gRG$Ը9e fJg$tTZ\k.Л-+c3;TkYs5(*s#wH}/1<*.QsΏyG5="0 7"1M:9=0/珣Ri*FB@iOD-K=6۽* yd96N|l͗PN +iŠb${`;χF!8/Q)]La5Ik a4y-[ =ë́O酪ub)q#D™8=B)zxǁ 1;XSl$܎Nz췰 +psD5&=gU^(ݲ1y!S)Z 1%J TeД/$|^*J/*rzb1B +ߔ5vMt-QcEFU9p:eme=ѝ'N}~$OWgav^Oe-LYH}L?D}HC'YoJc>7S?Lmljg6;ov]xe>7+3ѯg(+9q2wL||x&~keI|f:Z1=Oyo'/OO|tJ|B_K|xx%E O%yfOұsW`Ǎʓ?<ze1fl3n?Xhu]({BYwNGeMYPĚ$eM2,&ai-dF3DR;(ʚB/jP{l̿-jb ?숦OI$ i:L [ @1+/:@S)ژj-PUGq1+9p~>K7۳']6ê/!m#@Nm D1IP#OP=mr x)~}v/cW1I( +m|l*`8[JQ3sJO>}=trزcwlitճ!^@0.FE]/eu֦oy{:)ݮTW GD# +g}OTrQ GW\ߖ5zk¶Ur\XM2WkW79}WF(MǍ)MrlJaT(cgGZEÆt5zUyb0bn(0d`3P6Ё>7_4}҃}O\F|| Xdվ\<Mwthf C)% +Okc"_F$Pp*`ixIm7QqC.aw#[ h22پϣXQKZ|ǽ2{QbdfAD؎1T{^:fS#Q'1kM la/WGDϨ֎恆Wէm812}y|/uz:92oy +rܗ'S"E͞L:MW8\iu&C#n[LmMf}c]^vKm a"慇OPJT?W`!-g(>?%FYTDPb^\M]jPyϕ_{Yۏ./t_/rL-{{ ?C~(b,PJQ"~jӯEGFd!>c8CAd.IiSg^>OeS#C}Uϖu^[eQO{YoW/g0Wՠ8FXkhnR~h#?eQDC~SMH eMf'~zgMն~&;z2@!у̀>lgx0৕$wԥ܏uG7t:.9.ݠ]8@9%#z9$B)z&L38ӥg1`=ACCuWjcQٲt!k5&6KϗV (].o볟Bn >EP3zz*ع_2Bk[^墀^5-c/3cG`h! \tpwc/`*l]-y0l+]A>6q0ʶpt_ز,seU8a\R5zP7+7"e^dXZLgTBܙLDLeo@p +z{_:ڞ mڿk90d0Bv7x~2-x]- Ԭيfw[)h.k(tȎT2ygD:#9GF"K#y625+y)#psA?\B| z@H:{eqJ\BB@PáWr#t}6 qs T'GY-W-yi\.9C_]z k<]0?y$A[w%iS;@۳5>TwwM:HL )vˬ?HVz R ֶ)͚+os݈{t'eP\4jfύM}4xTxu iW<`ҢaaSL<͹An):VegVoUl|FeVI&A61އޟ2%iܙ.kval3iBlQC6ɖ4^ >~,fރGee[tجi`biPdM(' +ч0GJG},NyBKNbS˜NPMUyz%""Fa*[Ƹq<)K<{][L ~^[u #{ Z+#,sĜt$1hZσj O@lԜW-KJ sa7I +P$A4ke*di`>Y'z7_Y7#ccUG3@ŧIZF Odip3HH'h^@J+ ZK-J ;d.H/%oݯsӑWhK֍^ݟw?m`gi vuN9bsOh 28( :wUW`{zvn{(e'M99Zե:҇{93nGq6!1I{ic;a_-z"qV`I>$<1a=V"k[qG]q*l<5#9BTf`#[8_b$wOCFwL7=@Z7.\\!нr!FHjTݓ {fo#Ye$T_HqqkY{fdJNxpּ1^IO@>讛}-甐0t3ks[-UZZ=~&tZ)3U:}{euD +endstream endobj 433 0 obj <>stream +HtW;ImLm)wO0Z-s?eMf1  {yX. ^kbPh>>$MT"RfiD-Nwܶw\Xb! Z_p^.2#:[Br/#MLN$*<[/$E(Zf$1>g]7GV]q{0Y.UCt7 ۰$iTtjHYX4 TYI?9>AW/- ԉ2!&n"խvlV擿=c'hJA79[TYEѩcgH"@ƫo̭V||4hSjxdlBލ0(╄!\0^5et~{W^9fV1%f=%#%eG 4kSm .9Ws l 2 ^mfȸkD[Gbe#Y5UT>MFt"˒?\ymEA|\лߑf"^PfNs]}>|x[ {I}/<)?,HZ3Kz6{MfvU&x#Q.>"U҅plꯣp7[Gem}eVp߬3ሟUϼx$c|9CϏPoNq\p6W_3&2!-%6{!5v%bdf9YH9Z>iqQ($F|qwM\kCV#\ҧް!;WЙ/B4iHuځp<MνOas3bh$z甴Sf. 6gnh'bNckǬFAi2=u9$uG)q_ϨCd1[˧N> HS |7}:&(Ǥӛ_'yy 9t.~>;Jh=Q>)^>]itBvZûſ>SMd&֦#½yJU讖t=V^Am0-ۢ +G%)vyF3'uʹ 虐cć$ʊ6U6#J`g"j]DtDƢa4\FV%HMͅ8J$B)_.Ξ VBI|s#eBÅP`E..Ņr SB͢.%@*rQ!K̘3EBЦ:u1[?P!d0R!/*< +7R!Y + +[D`B[$ⲛɄ6eBz1a8ĊqxP{Uz1!R3 >/&u'nd#q1!Y +'E^2B}/zEx*ByB +.*N*D 5IsSb4 +mCR +)FM;' '/jAvDO*^TP!N-]TxfBdX1C*׉a>`oxxnY=oY~TļhW{*:_ÿBD(FI>ڲn~/S]5jR]i#J]FTt6x'Um\ g8Af<,L@tY\{# ۑS}>tUKѴ}`gGyE?eoJ^'׈xoMP2pJ"9hD.l3VYW/qGXERIEfguh$Iк7>bك=Oߍ&V^yCՌZY6J8ZW@dlrAT9dA7&@[eyT9nK ˊ2x{c%Q2,A]{FLqK>9W}Qn +0%w=#W|-i7 dRDCO.+-J&1y4UK9V &h>ʡѦ0}8NזqN$DظAx߯r򥮤҇W*{'uqR߸KqDGEs0Ph +IʈpE D +)n-սşmL+#|!x=}rp߅"Uަ!+S{$b;oE8*z'q7yFf?Uo"1xojĽz3ELֶnDe7X%RR)D,ӾyZ@hcՊcZF7fl^iF< jJ`pkޖ|`R'Z OqVɊ hhbpȿv-F.qb3g*V_&{[JrYiVѬ@9~f% _00F?5S35@BDj}>6j@D9>Js/3_ WPd5U~d<pb[ r5,ZE#ZƠUl99({x@}VQ|2$^O`\8W@.Bd x|@=4IӕD\Rdo1R,8K"=)2뙽ҽP#>RwTnZx?y]}^\._8Po l6(x^K"Zbl`myK{-\@:@`'0X*{>6n=EB d.+ay |_hV#XxaFϲܰG.fa[C@ xwFpsML`R+_ QR[@`.$A (L/m)"c 7z+*8 7t7ReluVuvXf1>*ؚ -9Ѭ,fU&|f'DjE{ +l f gû 讖,Irv><=˹EorGU-SG 0r2c +H\`~(vo>FZvz:bk"+y XF-7/ޯF*݇;L n{hOJVTကE™!EZh5OEi׎-%_2}DyڷF~(K,}ӑ5띩L+,&N>5Ui[gj9)<)'DNTB"<&;}oV`uc:2%($|d¢6|)Z붢4oh@] $QbS +e\?4ʳVz* A@hLF]}41ABQ)2Hh]B +%JwlA@M+PMh+2m5>Fy*1hƨqHԭ~-Q<NJb>3cn>v>`I*Ul,oW@9S;*~zf-cp֖ jP0kwж E8|{SNO '\ESH]N4,׻ o@Κ_Rgw$x|K=/,B}װ}ǰ+Yūa|7;"\62/$7G +xN`ۈ$NyZE)Xj9=;9tPۦcyz?>tt)"`l9)iaų̓j}s#ž-ؗx2lpTݽΌD`< _Aׅy&OhT䃤1 Z9}V`I۬|?H|D<5^: y.-!m@hMsϑI{e 9sa•F"ъTY7L)2+2:"0` >Lǿ#"SX_$B=Z'IqX\V{%՘ؔ^N^ꤞ;KMj6kHf/YtO_DJO6>ѻrji#mVBRwFfpIKv>~ [ C6RS!}Fǝxsle|@K"rVGjV=Ãm wF2:p8D=RQ-4Y~yu_ !miuM_iWx k^v!c5d5O |淠Cq Os)rGrk>6Y[OrEҤkae-H#]:FLCm}ŝ9']К#V/ E*,-Fir?HezqD]M[UUϿ%ӭZWI;v? D.Ph"LеZ߫Ur}ՙwƞ:}yPV/a{8J=+ɳ$.YVov Jٗ]8WvBwV \F̺vjURI>,Ok'D}P2E\(mDPDwK~bYS@)Ty K:N&lmCF1dfy_ -C]i+J((^`\HM/vD+1E-r=++ybv$le5IIYn9l8QVNҝc>iLL5+6؎!C N#rvl0~ HOvjnw]0sm0ԺDW.^ih&v]uymx9 WsJ]sp+oZmd`E6  "#_T^W^UzH V6UWƆz6AYUf1#/ #CcPp18ej toX1 fŰA'Ǡ"c+ +vcО)s1a [˜T>x\cP6i5)Hejm +knj3OLAKAoWxĆ  +QP<$33rOqc-v/~wt,V}%u9!zvf0㚁V3fx`G<3ʾ7=%ukR>ЀǙ֑Z2OwEm]Lкð3#L@^KL@y;vZ -|[ڇAa$O1X1˪SNv`S}CDTS4 SaujDF̠}|KL%}.&@Yd'\'_ay&J`LSح|Ƚv]l_W͚g]K UTM[?^~bW-,{|T;"؟g bڵ 0;z{ +m +( DM-iDU(R?e,pH;[{?jc^9+NIWSﴬtqe"3_2ԯP1d-` Ԛ>&Y+XCXӀ +2x#I` Ή$"hXGҖe,E~Dy;Kc';$쉹uudRj5d$*TK:8H"]zԅ:2>/h%KtTےUrkXCêygK)aJа +Y4Vj\XU}LC֥Uۙml'3_{mE-aB݇YU[ u"ւ!btq:Jm~DZLP@zoVMGN +*A{bԔbEaX7l,j*֌7Ν+0ⱏ PJ2-cGXdj=-c/e#,vX+ĨlkP@EB>%m 㬕<1+!k XAc9εBKDBtM]6x2"e~ ;ʨ}c!&)CdeA4}l1cT0E vfj'biuq R}=Jf mOn9 +Kɯq1%ǰ&'FLbf}Ŵs4bxx's)voy130({K_.rE5,ts'@aIhVʅ!BN"m{M:fqXVcICT59hU][ؠ d:n+,H.G1εpm +_z,K)Dͯ݅}RTVկMrny P<>mv5R|ezo&R)y:CҜ7e]LX@z/Dۈ}ƅkEU"`\泬RqqNQlpuM3up1D*5l +׼CCKxM6%n9#,xLfTqG~WiccJQQ4QNO, +EsP˨eu[S´MtmD^UFƃ-2yDd#R_Q}Ƅ׊BI9ř"ewSxQ1+ KeUSɁ\Qe0V}pH/5}.LlV&e.e<= KcѰD#z'RJ\<˝gM +; +zIA iɻ_C3>ʯ5mzs&S( =20KkVZZK%Ǻ,:[[nζ͇U@ѨmĴ֌B9hneCBԾA\,k\_ڢg8xq6f#ޛm6nwGp41MNt5JyNY”촣s %b)Esҗ}?V8670̻}7)Ze_IOpȖ.ܕ?ڔ?~5+0S Z M{?b[3R32>E1RVyz,ݗ;ʷp:̸wдZzEY PdA虩+$ +L׊M:e13Xf'b?,N7@,q49wTꕄ߿%:$~IgIl#`m`aq䆙-@ > c}kVNxQ^5kR_&@+Q.)g j[) +@%iJi!g6?M̈N ʜxLX|$-Z]ˊXrE|IZő:+#O|}q*tT1-9=TqjD5,-e]B݌;Ҥ1^9^q[#3?j70 v+ۭI0Inkp~4O#ƽr8V+:)l i2$u] N;^#> BR7yHlGXd@W#}S IBuEH2S€7Vh;:6ɗF~һm󮢃.ę/`UT=h% v۞RQ6c5QcS5eA a`TYj:]>d%;4) A:Ieuv&(hHjiLCk}|Z5Xoڑ+aU&o$ [vUfI&A$d?-"L4чp + c638*bđR!^v y;|owd&'E}J(pT[!O p<3&m_Cx .j¤a"JʲX HKeG/N~bN2F'`Cjc'N/ 5ao +d],*`4*#51dUߡax!x]Xӱ]n#&TXyUb_{/YUU eA,{Zy0hN!Mtk|!ް*ư??;9;2*:r2%ΉD1+hd%A<A"ɏ~c1V›}k%桮c&@`=6³G#Oiil亗zQ?q58f= ŵmig +})&OPh lDrMBOD$v2ppAq|l1I`r-'Cj|f pT_B>^j0R h 3P'/N2[P&xWsQ$%LzU|ex )Q՘4S/np,'/ZRX}_'(h"@gT*K*U:V9-]Z%Kn7VQYT jMuX"y. F +W\昝OŌWtlg8;N &?s2v96-h+`TlK{yO 'ejIpF6}g1LC7#MMwܺԩ$SO՞H <62YMsq{{mhص2%9)sIMY +ſD"6hӫԶ6#z9rdm<ĈLwq-B]Ϸ= qޞH.2',Sse]l2+ԭ-/*QM7*Co\'1DxM[4*Ɽ,3} ㏓<ׂ<| >i +>ւb Q}{۸~?`\s7J +;Y 'H҈,3.j+s8!e^Z~ rd1d^֚X؍<}"VZ2_?hZ@ښ^Ll=ht/v|Ze܈2=Ƕ&36am պc7Qs-}={'owG,ie?9R# f5oѕO]X X.)&v\rIyDd}=Od B<6fCsŹGiFӳA*ɞ>onhDFk~-\{KZ鋝5!snx :9p*j0uс% <$fV] ܗX}>|1(~}Glmki׳x +>? +Y޿~qO1>A#3c U{/gb%kRJfgRyqb>)5FmTN52D5>[/p1./O<68V]r9Jq>Cl9QŇK{r:)Ǿrkrk#ao9Ft(i}1f=Hr]c9c ֭r|"xCF16]-Yv0l+>]dɶY.P,m:,cϧsqVɳb踤x,^I%AǕwӱWwm'ExR >]b$E{UNZ|_s*Ɨ"If$"ȁN붲Ü(=G.Hl>A&Kw$%FN\-M#Okq7◀ʽ^o֍ kCUHSbkn %H]y9@d&J U"xA:@\4ZRSYVSedYR|DwJ7m$ lb ]3uyk w0YLUցx7`E6`GCm:A69$ܨz ){m*;U30F ږ/ƻf}j#x ӳF\YwiDh{!mګ@Rf"Z9`JjHB漿Sy'q-nu$Ju]Qnde˒pԴmg Q(yZTv~g l%JL}TBKan}FS^ $Ue> '7q~ddUޫ[rҬګ͒4Bרk|uJJQ"._PwH{ٞct}"!M>h{$`핧-Rx^#k´8b(!poy I~36BxeMFKIv RtF˙&C/Y9H\e< +MT^H7A.vi{BxH +~n3X0^܈rI;(bA+; VX+{Wu$_6~I{nysd"2k'kKZx.HcG^}#8x~>21._]H+M]imLV1xmԁδUg֒"a$vIhݍZJϸQQc .WgVc䐉TQ. GcnmMd@k?/b? 4w;LLkɮJQؑު=Am{.ӱV5%u3oQsb:>bȋI aFxu<qsDOĩg]nŭ*fFa1쓾$ KMΩ'HHyhxG-~׻>Ҩa#NCyрķNoYG,]3Uħ; +. յ Vve&b_ IpXeAwW"{GYLDgxJªI`3F̼'"y2F!\ooY;i-=` 0Gt^#kY}]2>Y0nԖ;0_=+1_7M^dGy6 +(o x_hExU sf BՑDz:ktvƷ[|sY1B\ 4ǣcnQZaaA߹ . Ǭ5kaT@ʡ ,ZJd@MQmM=rr?J;J֭SW EQY9˘FbWVb2uo$QG24fQږEG#l9#h,{Vs £$ /j˔{"&ʍZj<@\>oH+<-'߈ ~\;kfdڝ,RY[CZUeK4-$key%rsr,WnM2Κ]ʀ= {O)v_P D]'/Ҫ4GےҟgZB",>%%{2ՅE+|i@@-稵'=. {s٘; @K"}XIFa>6JU0ATk6Ry'q{ +A FL@^frHW6qv,+p~KZ 7]cd΂Cd4sd>W*p* 1NTY?Z}$kջU5H?Ób p:un #y#s[Dz:, )g]f )K;%׵|r%6Xhb^C)pcLM?k%D\\q\ wr7:ĘY1U34lإDvQ`J+}%hdTUyjQIVb٨==#%m%+xg좧Ol +*Beɫ &sPSid%}bB9^i쉪:`]Z5^e kHih)˺p,e8ybGmtCbb'2k#Ajb|Jf?1E-uUFH;ąp_DYGDnqd'Sw%4]Pg'wa켆 l=NEfega1NA% )mjd~iRd:; +\IݘwCZ#WP^P0ty|YA(䃒Jl栩Dh2ޕ_6a0rF#k{-Yid-a; C$ G<#QZN.W^^}1eKKGƛ ro"Qyx֓vv+]͓}sUjѝؾCپӲclf6y iv8\q|vr-9>\N߃1cFM"zhL< +#LFb:Jd:J(Ф,ApH0݌s+|,p団fw&,j41e$㱬ɸqZQLv 59-{;V }˱63c5ñv +Ր;cX}wp[sY-&jxS&x8 pc5( > +˰5аZ7.E4*6Q:8ЯjTRtps+["sjbH0]kfА`˃1oܭ\e2GGj 7qg96?l A`e XڋYł2`z6k[%4U,$mҬ\VRL5ګ|lpaQ|̴^72`+ėzJJ瞹gUTPx:$Qq5;VԮTŒ#Wi̞<}h$G#DQ?EP ٘"i BF"yELf=Ikſm` wAlӇhDeaYs^u +[Kp#n$/P XKceH G0Z> q$qS~W +C#Z@/ n=_'Zi]sc-66܈ײ;3;C2}{ʴ-?(g7'͘Q9<J-Dد6 8U3֔aa 9d)6G::88##|/e1S)'% /s医I3d^)s3Hبnfwx^,]?c~b'|1d3-q}8_7?:3#Sd_n5rАg^qy9 X "subg.&L!`܈!҅sbc:nDMbF́M@f{#f(N.ojJ)وZ [{Á~J&\ WrsICГQ]>d蘈G̸&Qlry5$?wm-¹" ꘑS RV"(G9wy5ЇP@U`ɐ@>kìnkNfuhHHBd))T3 R> F&^ʍpP%i "CJ"3eaR4_K93'6֑"ޣĒÖϩy]ǮzUn l5;h|Aä5ӮNesݔG`?d޸ YXGvXZ6 +gkԌz^2MmkjԒT;M3ϔ~TC=?:нRg} hxm<* NT.|v)xoޗz~ + (_* P L7%TN7{*+e5Yb~@q<)_䧼 +LVYPU7 0gcJ4Codd~ė|r!q ءk_vՁ~/D$^]a񥼎$q$'Ϊ~$aw@\lckzN?y,gd4Ǣl{ $ _P#ǛSC*d<:[ .}O> \QQϵȾ4S ;ojϬ.:t2G߇r"C}7WFРd4aєG^6g#bHPRWMdߢ㦳3|S PV\wt6xN)Hh9Wvi2FBڬ+ r/qcӑQy77 8g~W+Y~|e8: s/o,Mgk^(~;=nAܪN y89R;Ҝzn1}ʱ,AU$5ܢ,TfU3>H5E2 q#8)fBe tEpTMɩܽkieG."RfRf.yzl"i^dZ!%fx5:Bx.|d;2Ӹ7h`mEB@DcV/V̺aVǨ>yh%y}|M\ Ɍ7A9#}.] OgP}=J|rO_O@|ccۆgb jiF _-#fD,pc.f[|\D9Ba.wghZGinѿW9(ıUc2YSgHUþB$]9M-HlnIBVmJ%eأ![WGj5|!C>[9FfPChÙSʩ-KVvݨ?t+n$֣5b2gwxgRXZ&Z$fr#]b,~ꀷ( 饆P,N9W?k#Fdc'j1 iuBk"1b_b,1Fa"1BoЃ[7k"͢}bqg֦[VTBfTF6x5b(/yM B]՘n9#R8*9'z]mja>˹~ ,^{6'XKv& {Wmu;YzWx^srP2sL9{ŭgAQ%-ąp p8#Hv<}ly#i~1 mjU2q>J2j/GPZse bu8wɁ"R[bTG H#u PI#"EqaȺ FIQ>qDn.g?cz?qrꨒj2y+62Lݮ#f39 +sJე %nְ=mC%&;}D*+ H̠o^9E}VLR8\9k>Nĝ}+^ +IVRTVjRC;Pȹ{kvZ.'{>} >WIx14bbVxlH MܟB~O-j_@X;8j۵&dQ:IL6$)n[s&f%f@Cʇ3A-%PTA=k"uxlE^EI*+;e>>S;(%uШԫV.0͊4կlӵ=~q5L|mEl R)RQHwR0Q(WX5i@FЈT@<>J+? jQ +B@(9zN]>bMJ//IЬ( 􎔔v@:/R/dOV5x>ᑇ# zkkbzg: +ӶW0`g +endstream endobj 434 0 obj <>stream +HtWK[w8L~{ֺ#_ ȯΝ1Zv_l֦k-j_?_knpW!oV4 J9S `DL8Ư}"c}d p}~9s!Tؾ4F|$A U,pT2B27w+'߲,^nFtJ}u,hEeTWV'q]&flHY2ك7t:[(@V050s& +!9xeP)в1+2SH1qG~LzQ eËu**/d8. )E7r6CǐlG!ܑ9gq sș>UUU\Zjo{,_ly,^:o7S= ?O1?=豨$ptׇBگ1&XxU# sl;MԱ{6+qt9}"'EyH֟oar-lp}6 ܕt1 rVȍBd4 = X/#[?~oMS/Z:v$IMI#9z W9$4ɧ&ӥ[HM UH9T;4QphR퍓R +m&e|p|}jƿSR3j֕joŪ(Km` +\4tY3D,?gc#zy$' !w0Nf˷ؚ Lwq2NJԯeB0Agn/n] Yͤ!aTPqV$=92vsq\iQq@f~صPd.0ӧ>!V#H6Œ:܂gq}1[>oMcyočN#Tc)C%8R6e ӧ{҆q2Pt`Lq|@a8_x&x4_,4(oN▐%9!$kɍ4$5 upTp*L;{ϕY(5I=;%[&蔐|SOo2 'K<9P]v$r]2K^9"$1ǾMfΤpvPd62Yv,ͨ;m>`7$|1IZɳBVc\Eo1z;j8/ANCcgMl_{s5Iv1c.ߧ#S_0]_էc6/h27;8?@5<`t}b}փ3S.?ڊ떇pZ^T4[&`@1J#gǿ&_OmihR$BocVfAgvK˲&.e=8UH=d?737j-[ L&t οǑ"(i22j֒0}|6W&ṳ # c۷؈H-"cz_}9z*IVO{ $hQ]tmπ:%XSkUbnϔyPp7 ]ilta>Y`RмDĆIrɼVcy&ls=P·,\(#v7[E-hƢҴY/Owcr+pbлp*?@;2 d5'.ɝbTG{Tg5Y1zl뫷gYS{ZNdsuGZ։noYTVn4_e5$5ξ軴SE.4KP.(ߙ'mQgs&)d/IcbY(^/'krȶ<- +G 3QFgu9Amۆ87B!5\CzM) s7} +{idn1)҅ƍ?<(TM|dL1nq1RR&thx1hGUn7zlU#z"'A/WѾ!Z1 9ԗ{\0eFԤ=|_͙ +Wmv^t3N9&@=<xPޗ UR`˗iz#v69iHzl=fPx ֓5T•.{̂KBNIб1P/a$gOڟYT1険|Vᄜ[m&gtYa3*>5vNRn'zwng'"#~bOS$ơh혔DaOB Fu 2gOXP5OL'x:| =B7Nm<~Y + 0O&'r9~us+>u vKG'39~O-t!HROدAVO c?1 ?_G-h+0*: +Fg EcږC +V2 C,d@PM|ebRu_憂Ȓ?U82 Q3ʾ 3dWR 5hcYCW8Ad#ōGQi2Q(_8 +K"Jqv9j)ۑFsGaUю`CeOGѽQL::ܖ4RXL?-Dvr,Ť2K[, Q]-XP[ +^G,gz[ +Ve+VGEyeL~%gY-,K,M :-E,ŷ'K0d5"{=0 ]ۉ|~2x sOe'/ Mx +p,rp%ü;u(EZ[P%r0"!h`,KjW#(=yW̻@nwzkCG4`QP-Bs*znNd[ +!XWXVZps1bN$"gZ azڎ}p4[*{j/{!Ϳ_%u5ʷ2N 82lI2ډ['D@A!Ҷ{ـpDIHS^`a1Liۑ"`C5"vd/gihJO0oÓm}ƑJ츄nbH$;ggzmF^Zoy#^G=}@*ʵ= |s7cZq8F8E1!i8ec,v/Au|29UlL%mHDwh!ߘ Q0qy2FI~?=~#z~: I Ƈ0&.1':VVV-䬻X@R;b7Dd2]Gm*4x +K^7bF*6NŽJ W&9m\)>! +p-Sxиɯ-}D|e׷:.>ɮ@Z팶 JOMտi&M 1j^adG箏щྦྷ㴮a"9 jDa0y/|$m]\1Jh+Hzg*5SV,׿Rؔާ-C;7H^߫RN󪌶C2xJ BPINU\7"CڇdeoT{-\f<b,@/Cɩ$ z-ö^g"ޏѾUT$!H&*k0C_J_t[̩@-pe#s ,.%(Y : ̫xA3Z4\I;V?ykj A!xY+/?i=XjiD/.n9F2=YHb݋^MLj8]4XM35~av ^E] D>9/v?j[>jOgƷ]_:}@ yf| DVous*rϏغa$!}v@4`4K>XVHk6oFs gGhۆ-6R$IPCݪ!6z}&gF=tF ݗ7PReݵ>?4Ջ* lJ;?n%MG/Q0F0͢0vm462Fgl( 鋴,ꈉ;eqlkOwcY +=aҚ6!B$TQ_h=Ź7TXNDF'2t4F.wݺ_%RXy=%k\'aEٝ%̇*݃BDpp\Ll,/D .ybWWL(Y{I*/DMGj +1]Ht ZqŴj+׳䰷,{)|/ea~%7gsk%moXnIAy=b{#TvϠ\i4ػ'ľP!E;fn}2d +@XI g="HrБ6.z6NuU&w@S.9:*dDa&`!@!?*PI".(*dhfC L-s+>TڤȊR^'9-캷\5ՙ.&o>/g4.OHc*0,`Kͽ!u;q | `JʨA }*'BskF=u4?Mi +ޟuɠ JÀt>:c‰5+_#Ȩ)*ô2bj;>xX]PKq<5 _D#|]#: /-T"g1gEqE00~B|χ usyKl>t͑5օ 1}2{7gD^H/ؐT J0+̐#1n>=\δeN毋EF^i}ʲ@b(+iq-<ڗ֌0'`a;o K3K3G3p>=j?Tw[;ֵCnNV_Pgh3S4~>! @ko@"ܢ1nʺeH C6p)sqf{E\z%nl|dx%T ^옖\eS^Fa^K8أ-!)'H2)@# nH‘袷p$er#S8[^80BeD +GVTj-#IJ'R9~kd| >,4.q /%,, +Q@d'72mŠpv7j[ ̖%A_ܷuR6S_{nZMg*Pa*2Vdԣc}g5{.krQ@1cU?qFRZ\)%?IOjF?\ɭ/.lv`TѪG(GŝΑh,$pJtV=E[QFR>"{ծ]y2_Q4Cm\e^mRX Heks^FHk-¦jsDL7na u:{.|b]QXl 4&yhp5ͼƐj2` ݆$9/_^Iv{UDp'=k0ٲNd$q*'use@}b:KI{KJ\NH$sYDڮiȡxy؇鰪YrD2,kfҸleOdvo<$rO`ɏRIP⓵M%Kb Nmz48kVøUbW粩l`:]E;UӥqKyipiy"y8CmZ)&ҡ &LL} UwdټnP|c]B }cuk:1T]gvj )`F49/pDpBL%$&ƵꨇɋXeR#K^N=NB^YtTI{LL̩,vMsiT:Wi7yiɶ1 G l#TRޠM55Qi ImJt 'ms\3 Z",u3d,. G}TNR~sN_aU}q +D{z~W^-7]m]\N=+GS]!sk8C(b-'B= ( l}2-?CT"~J&ܧY.KtD1ԎFmEtkI6]s[kU$|D%Xa7җWi“ƨQ^:8TtDHa; k';lmʷ#ص,\n萙wGƘ^x9#W~6c+1Ӹi$#:(1Cu)o.rԪzŷwd갭dƙ.zmAQK(2JA}Å\TCHF{=b׉vfIшJ%s7= s 6+d~=dύM '2iQ$O#ٻc7wcQyS׈-fZbF +*ݚ(ԕ'1< o_dSR)9Ɇi`_ĺgc +ɑI%}fVV^wtoW6k{&=S G0ߧVp Iù{ +YZyӻ֐f? 2/6ws&?7PU: K Ӻ̙ l${IX9"N#d +`_%ݨP361p46n9CcEH7p6]`9evrb_'j}L8Ad -V(m8%gQ.*Ux}NS׈uOkT)cκ"Su`p8 C9 ҬPն!EfVr>IՒ;֡ra Vnk7_nP0s}gYz[*x|ruVVӊ+c^D#ۏ^#!j&(|vBJS IMF9p v>hQ>h^BS-NAP +;݅#%}ҮjƩ*\B Dn bz[FF[Чg2jY@5N*AhqKa.胬fΞ '=:2s;d/߽Ë́'^c '>o׋dF>sTɎhN25+Nju1Tڷich{4&IZV/%lm))kj$Y;L.O}Tw<<PlH=9o_X^Kކ8PRF|2m%v* 4Ĉ$b CA>óGsj#S[ GƆ3oVcyJQQW݆HZ% RۆuTUIQ0 ]7Ħu.SGꚌ^7 %#\8vf#!g9"]aHC-dž U_c5:UZ;?G"U(4tN֜T2fn}iwJnOírwPMkopduc19{S[ԩ3`<+lUuPz9hk>Hac)mv4n3]bsV";/di0ApS6&);i(;@Gz^/xwX۴z+_{N!uWjg?/ɅJ;]b ݵN>)\F? 9eDָgו?#͐dg0ur2+x0iX#F,midDk Eb-%(#kIHliAN5k #wɷ)o2x:BΡDig@<| ;>~O;GNп QF uNEP ѨV"AMCmUF +%ˠ^q-䇃:hu{z0|Ew`:{W.֡Jb+,O".`T0 +jX3h(Mj.%Zɨ0?pw\ٙ63UbyI'^3`4c'D4(FhKYcmb1{ DurxW[ @roLU=/AS] [ce MH>/_dfbeUM~h u5n,oGF\NsͤkۆdСq̲0I܍_>0ģl/vхbc*u:Bp~"wNC +Kْʔ`eJŵC6FRya\/)A@[Eyp/'הw' ),7Q7~7_ܯC^Vҵ͔(Y32:SyG&y_#?jz?G&b97H6C\/ ץ]f']H8*e?cCgwZ 1 LeU1(I) (XVwrc׏u7x/`hT 1'!@Qc8qPz@ ) sz@ (] 7r Cl["*kIMG3; ^  +İX/WcKq<^ +ɲpxRgu_Gzpu,>/M͜(o +"|@כ.ڃ%yM~|߮~Vh|JӗAFͬgL".f{c]H72G`^QY)= 0 1CdPsIj~eKF[dJ +@sR; qΥUU>`_QD+nCemxIT?4\H}`4$(4;|pƼ&?ؠ7Q6hs{QK A\i͊nޠ;WG!ke4xxc嬽_`w~Ɩ:H6QM!IKD?{-hGƺCfRhy?'$ZӺIA{S~XaW=k|r5g.!Gy;񤧲|Y/)p&Q>4bc]YPݗ|t a7n,Zٕu`"JAs:}tQM_= D~;!E!u8k[2S +Dds(@Al^O]IEU-FKtH`Èq`]Ja" Hh<Hzr2{@ 5kݑlo]j2 ] '!Zlo sȗKx1M3(l4[zҬVΟlsAV5*q?L=pƱ +ECGi=9ni%pW~fR%X@wDodA5'kq;ȁT'ΖNZD:)9z\s,+S`aW +%P1\c,X7;ӹJ|v T39Aqu"[L3(檫VHmMCQA_A[ x 俁VG緎ix#ۿ:s Tn.2hJJq4r"hﭝ^WJrg 5Fq 4/cEWuƲe:T. q.J4Nnaܝ6vT3癣 4XC{xYe'b=AV?ɢ#NX~0314)"x]."뉔8OQsd(0Ӽz`)Q9sS)$T&pxb +cQ#4yhrӲwO:9-/ Sjy.Ko<ŽQr~"\|Yd`V{8.~HKEw>$^ *t"S5btD'ċE 68M+o:AAFFT>x6>[CvG0_E$mj,7XJ=W,(.Xs $%nI,iymGXGdɵpdqo(6wih "o/is9'RR) BȸPf; CXyg +o52\C'_P2VYE7h%2@܀e^ٮT![C%Re@Q6Dj8ʀe@oН\8ɞ=?92c0Ј#\M XW "!CF:f4o󡵑U^\ue(/N.1KJd-NKHz}$2[Tg`9c + uJg!{om]['5&tt"j=GlWa[?E3O@]7,9 @d< +:6Z:a \wQ!M>(6m *id$eeFRzi x5:ËiS8xpX\[F С,\^l&R{"KdF@qH֠eLTOL;U!f=yʘyYQeI ql1P ?SڼmE̯# ]G(m5Ke8=SؘV xDƒ>՛#hdx٧/L68˂H@ihs!2s QΡ [V; BdXυmjvV+̣HB^ǤY 㒓#bA@Ƕᓟ5i<{`#pLcWlsDa>@I5{RkgB*"B,~y+ElhIc~) #pl.P!6[fZܸ|Sls55{W] HkI%|n{u=X(ƸpH>W} +5)fRx zLᄛmx9c O-!UÛ/R|,w=dC;0}Y߯Q4+x}QJ{>FWwAVHgo] `w6'887XNޱ% + ́3 +ă#D@oV!k!0Z{9[[GOJ..>$]ֵUtkD4wMd}R&=씥rMa82 +BBe;cȏzSTk@i>כ[_.%kXz2##:+ [-`K46@H4 +w;\XT }HGrGqֺ18˜wvxlĵٳ'ȠڞL">Vk rNf[f4M,iB %4YRbFd`Nj`AtDj^ۇIUc(ܴ3NOu T<o7a(5it +>\}5Ss3V6YQȴ)Q"•˳[ٙ&XE@CT})}' М6n h%$2"2HP8^o )CۇYQ'ǽ2%%̟S/ +e0+ [3:#T<8X* H,3$q˼qc2TY 'NwOs@* e*=\\Dg@BFQ&cA)SU饑QDP_:xLEm"z#:~yͥIܮ'm5_}#ͥdH}Ae]1p#=B|]Y {ш ܳ.3/1c+GY{k*vlػ.gEZmYi9,e1o63e,GZ7#` cH^Ng'h'0]3SwXVɎ L!<}[E Tkz6/kkB!u.NL,K2"=uQ"`Xpcj; <__ ΝdeőO z QX]0srLD:A:L哗P("߁Lrdɐ}EoՀȢJ`MD:wy; &-@WNqK~Hn_)TԥQum4Qe`ᩝ:WUj"kNUZ/6K kq e:kiHY/`u3Թ̙ꭺ2a"~)¹)*6Uyroyi׽8 qvHOjzP}bi]Bb@LI‰L= 79$+:Z{5!{"'Qow4ON$׸OZ \*k?fΡx+.dYu*EOy.QMDd?1qm멬b>ețkYY龶"n+2|>e$@6UE:˰x:{ckm[sH.V6$JO#Q >Wk c91k71G"kpPej`,i Ru'Th귕 fJDaJc*67K]- **uumw G:rz]Ja elBi]$uibHIKJJ}=Yv)=#̥ÆW&@3jR;.\Y a0>i;7$j1s}k"sTK9K02Y! ~!>x ԲfYhʣ@Z7ue񕩕EMKxHOWxGOlFDhԬ?`9DJ}[3p +!c%S\D>aux#)lÌt SBkٳmh` 5w|AxceI6FaiU^-: O)NvX {M,4Џo-S^/^}n,cGu!Mk<4JEUꝁMuSZ]# d +|iJY-yt>KԒ6mW+f !ęi {2TLOi!No +&hhG2.ID:_>$MaMBHA=NgHڟӌ렅rg@Ȝ[,mV ڱL_k_kmz0ϗ=w[= 6"}}%cT&ijHKQFH,[@|;m}?_wbҎHq!l|@_!_\rMywGoUy",{';ZV1e54~^q(xc/g24T"2s:3 nw?Ubr߃d%i4裵o-z`:7lEF+[ UT}Ţ=dH(UNɍ (`ܯev_ Dldhq7?$x%W7dVw(⵸VcL=-0n츽ZZ Z.Suy}Wc˽LKToMD?`aLύC&V }x1E7ُ|K\RqpS)=υ2t^;P$]ݾLmy h۳ +F\/a-K=M~- 1q/NbL^Ƿ&0/uKdp3([AL ~5>:[S }4LϾ5Wr9,_>A#l+ڥH\r6nH:D&44[uluR8yu Ar@ٷ,Kr}U@v QtmU^*g󷢵ԫmvXxtAR󕆯̸bU`Bz/dyuTF[p2M +K;&Δqż7˿@ BU }}+;?7Y\&﯇HrCCPFx> ^`, d|ZxSbyڈt<bk6T SF MaL}xWX-;2e\mZ^q32J[ڋKa(~gﲐ6 #J$MWOHTiؚ%#Wۺ.bNMt- WKsk`M\%K]O0{;m#ۂpdW)!ےe]2Fһ̳ 2f\ws2Q랸U(}^+HOؔ]h8)[1*+%~6nѨ3 eΈ(KƺUf!QU oED;nRFt5xr SwHeVLSg[:fTahxx*JԪVMK'_T^Zw0,}t }qhmj-7IhJL_~Puok[0|^N<LأĿD3nXqZ޽YplsL~ ]+iElM4:|ʆM򶛷d@^ò?`>{Ks"'BO31OG@vMǍ,|*sp,*@X$ n7fRg8Zabnz#S0U_Uze1r_ng amm ͘'wX?ϲ^%( D0 Vc5 +&#J%*QYZ2Y@77d!',ӳp]`~-ot ⑅*h'B-S'ǎeS}(9HnU6 2F5ͻZ-l'^Z +ˀ,IW >WΠO,e-3qFm)0aL7$nn[^fl_BAO%߇;9>|Y3/ͳ;d*!^OQ0jk)xz!H,!臟Z^ى>Weǟu?0ANT!M8t Y mfu6AcLVf޹0޵u2i5fKl*dᚴl s q9%DUIaQg D8UρݝU]9^Q}$yB&+,CW;5-;y DoKw_M $WQH~4zZ}`(X Gvg׹تY!hBK GlosK͔\g0*-qOywL牉7icu~RCf|PכQ=h?Hb>>&J?Os e"_w?: 3t|f>Z&n} K}+.ԷʐV3ol-u|,Ck>o#e$RM&( .7Dwu7 d'=.@%!b_{cf[\3+fKafEnm!X +;k>l~ >`.'0g B^~o|[0  6>~@6,?,NYZY9T+2)jjv~uVmg^h14Y7#},3A8DW;m*ev/v^A z@.wrTF˴іUW7:Lj#)cDF4="3CK]7${Qp#M: :?%#{lȉi_u뎮iː}#u7Qxg#ѱX6*2 MB%QK#7\nOOCԲ2ylp~r$6ƉKg߃u9T8PD'ŀ9E6TQO> υ&U;9QV! +V_ݚfHejTV?z{gGTZG$T`G4q}`_V#5 ++Y[.u mH^'zҲe۬Fu WoQǣH5{7`9%53H)GqgCڠcΰ]}f-{ J &0ِ9Hw Q0וa 3RRJ7AxJROm/ؘxu^O!"HWq [-NP Ϝ%)! + \0!z%wAia.ءg'8Y7ujaꂄ9PTړ +CN~MaFlu>%S *@Ĉ94Y2dV);˚uU߈ؠx6PũiDix/>Wu "aX.U$ +`L)N;ciKW~}SViq0e`qsYZzr 6c,ndz4>5daIc?>2cY'NLo;pу +endstream endobj 435 0 obj <>stream +HtWˎ%9 +ݗTp׳k4ԫ[eGDvNI%e4 1_fHoDe%(4euLq!+j!ӑ6 >$EC`|!k۱?~x8lG{G(p^=z"DHg!M~ⲥod.ߗx)`e8CVP@81 +3>Bmw@~Yޤb2F}Ә6DUU@j0( Pن`} *ODA@锹SA15*O?|>x@Y *+L 1bJ*\}fe?Q!QB 9 bjgǂI8q!}"fֲ䱃:@B {|y>+h4\{\篮ӫ_/0 8ʵ< P3hDX'&"LS1U\, EBҐ#<&D2ZL6< +*$^9f yaES'3thԨE!'_gݔ=~C+?UB0:bBEUc.\q!#"Ou6B5iL p=B|yU\B)Sn_ b +VgϵDF~݄U*<%x!0/r]WI#/6f]|A )̌j2$vW_7u9WۤXvOc1Ǒ<>E+nʸ 7_Y3]Hw=ǀGy +ǏOh\ :ܢ1v0RMv0tqa19"i_Wu&ܬfVC~rU{£~ԘJWod 0fp=9Ia^CaDY]s?3i +=͘~)r/N=G +f"?<"ͺ5Ч:w_h.,^HcN3Dz~,v[fcHUvg:Y15TXNU:[1toL[ULC>&-IWh7aVP?c7`Ąiq1CW9*%9b;"JO{Q)L##lIHg5ˁ4>y@گp+0DwDc:fCr0q9#~ZD1_0O Ĭv,pư>8"~)uT>#uF(㬜Y"DQ e_ u̧BHC_tfȢ2!Cې|4]w9")3`,+M +c-#<\zdqFz(yb jd#jcWUWl:hjNJ}19z ])*Ckk mbRWF2Z궡O,urno-1D(~N +GR[tm~*0]}S#\'VtDp[s5+¡{ Ѫ3C\ JlA3֪4d +M1DjX!=dRrl{zC=8+mNu:6Zw h;Er#u(P mUA4U5uuMȖeyxڣ_SN.I+'hIK饓ȉa`%9lty40:3h,t:nգs)g +OƋcO~?QC`ݝ5?$[p ubLnp zZcE|( |޷c]/ݷ+<5i$##ca Jo, xmUX0 ׌R +~¹Ϥ +1:5^Ñ=,3fB:&F_Ciu!9zCE]ǟ% 103>) +@zlA 怱9PWpnW9C?%m S"SgӨJ 4G%)xt@jDspdJfe'sp]8`ic +Ɓ~aBe=5e%1UyB-sP<#>SL@{~rl ېB=mpd&!6%h^m󘆤2 QC*YN("~ *r^:23nπfيcf-Cc0c8qmÍ8Q8D=N16~M4%eOtYΨz̒tN[^SєýhfL hL1@ܦkY"Y*_[ߗ (e c1f,Q 0_ +G`{/C58x5R?(:#/?62fvhB@$$jJHK&'MAb#לLl 1.~=!]-AGƠr٥Z,Y]k߮y)Mt2dR5&< m m 7ݲ +J|7];h@/;Y8t@FsIr6/`{5ezOC.ns ,lN;˕{ةx1왓[BP.4Bġ(+&$K5fuk IOAh)gYc>le{#MOC,bZVXFxQͤW?Ո^SqQkZyń?\uy^VGHY'6Nikt.[Hq hQ['I[/"³.3a:nܗ@Y/-ĸeY : j[MMI?5an]A70xaB&~aPaѵFJ#7x^'ՍA;>K|>[U+CMu[|}[; i8?8\?_ Б@((9@Y0.ϱ`,6^Gr'tѸotW~V@0,~pbY+RݫBʃ|("} 7;S,L`ܼe†Ȭ(=)-ƍP{WkK3z ?:I`}Ftd%FBYv +1'6(@䝹8҉D&iry/xJ;}_JjW2WiM؏4=`TH퓓MKwdǶ@t*A-@H@; w߭ݨIwU6cZ*\%s pS؁16e`=XbFrJ^7a:#wj>b253I#g:>F\>k.o:Vڜhdu/{WƷ//c:D,\4 =h׉2^ĭ6SK\]©x'u#X<GלhLAoN_ L↾-#܊GTUvOVL)6:$n.FlRH/F᠌P:4FTQx2Sm>>U6$YDC1wk9%-v +ՉLގن9jS3GA!x,&HzM*s2 x>>{BU2Ux6M_m.1Z_({ZcWlm?49]q!}iI4;67]2<•-eUb2}EIvɑ{%O4Ar?IR´-S7qjd7x.P%RIkcӲ@|+y @n*,quv_5VS鍣/2BQ^}~RT' SD0&CT/ia0#_G{_JW&@]m|{EwZ`F6 7ƝEOI% GQvf*SKs H!gC>GF}8X+n|j~S4k4xd +ѻ+5([4QK>>V bbg"?s&xR@|iqb5ۜ!F;~&1|5i>[/o@x;VB6mC-̇WMBf!1?T(Ώ{y}̻eַ/OCk7#en 5rq-&Rg&@p~v!gf.p'V̓ʶo]=F6p@K)E"u*P$ Ro Q$u'A&/}",.dҍO"ސI='=)ȤgoȤdmdߣg!مL4 \/؜!2M=2a~x_*^72s78nLꕡv!+F>>iQLغɓ32AD%1l;72Vߐ%'Mtxju7W֣i?@$(XH>1HW6,w/ Od{B(6v+8HGOWtf( xjy*#^{?IWKa@wTH e,8߶HjggRyx*{K_FL莸p)_ !Nצ̕Nm痂Eʐ[~gxaN l4 T_Z2\1e%xF^`-2_Do\)Ox!^G.qt_oߪ^;lMVfLl$.BDaw˥dYCvPacw;?~&Xe*gUm +^JY5fhgúũM|je_ +hllÆ.X?})5*;;sѰmXDƈBgm"BV)/ G~!v8"2P[,c{6<͓vJYJPzq(O򎕌sx-">9T_ +a-ޓ<$o2*#O)yQrIPNyzϱ!!TJBRa-ڒON̐r3!{7>o.ofJl_~Goje6;ۻ[*ci]$h)S3 c Ne[k-zӄ']QS DGjH|p+RCtC!#}ZB)-\0UO` i8@-އ_ +_6='|5$li׳FGKAX80AC<-]YtƩZ) EԃB` c b,]8;XZC `}{(mmNeRuQT'&-~S ,+sVdh9<#≬G= WVg)Acjrqe#`x լjg%|m=nHK?ynu*u`PfTħW5o:1%n(y}SUJsf 9X\ƥ$jѰ~J*kDJF]30/alP$vw1xB%OUYbۧK^ 񈙥ߣ脞/#wj%ېwZhU-_h-qC*rlf?e^Ǯh#8R©WC2L!1~jyݬK)GIQeJNjcL4qԠ/K:{bئDY' c4nkSfrz >DasO%[7LNS`֑_ r--?eV eY`AdA MIГKph ܢDf޶o(yW %2p?`Aw Z^AZ~A1y aPn}L{*bi!J_z6SeF/iv>8\v?;i,l\rkeY#`w!uBP̄ZN>"覔ʛξ[Ћ4Ko`9_s~/; :9fv +/y+Xf9<&LG+2]vWzƴ|X=VU,Y~q9g}~kƔ~BQ12 i.SvrqKJFd]I#E vb`s. +0畝ޞ˴A'B@'gjn" ȍ.I3#^`QDg:{ϕ}HM<\bbKovs/-EGoڭj_zB#j%3MZPjf C(3P z( 6nqV<\I \ VKcbH AB>*;]{nd\YS-Yp8#?$ך;h1r Q߆\KfHR"RS˾^ Sw&(P64%~ÀsEz3qE9Sr2Ybg:Hλݛi`bx<+[.yw?RwS]][ +~ N;~LS{_$`umm#ʈUp:18֌iCLɔ81ڈG@-2DTǝ/AedaOx*Ӕ91 #-Id[+z@f~_?i/ýN+|jDTΰ=h)\ +*[ʍZp?8ԝ:'p2"uV*H)eSxT4-ҪL9^Ih*Ssm;:J8+24T7[12.P*l) +E*\SĝXJqFW8Xc"|-k-SFqu%˪[DV]-'#΅g +\&o"9'9Ӫh^~ ѿJ2Fns!ɔֹ]vÍ# ŕZ杷bX;s˗h՟e$e'6+vȎE`\ +7e9 ¦(>ȥj$[&O3>Rڻƨ"dvX/~әv5tDwXb F7.t)&lKE(~12pa)4̬QƪۘLӗ[R`"$Ĭ!ؓt77b{%4?A\/xj杛_sSe "55{s eC61Fl +kVXNqLL!THkLGU[b|.}%5&»ti:ev|+pߤ j.%g1);(i ڔ>! \1wf0l*aNN|< 5fʘNAbtԼV,t/{vt͚#j=(J43_fBnWS~^EdbՌUۛ!fUu ۡHOcڽ=3nMAZ8JC,J 0tMv5 ltNwp[Xf̾y*h47w%>?Pc:r {(:0Ok9R[ Oi5C g}l\:8L_>B*[..|KY|9 +!9໢P6[ѿobg-˕f l^vn]jM5sY5 + _I^ws5R[y(I_(&$(*Z/Q׃ Ϋ%Aɭ(Ir( JzQ CwSl$S䋒neS/ڀTwLk?ċp꙾8ik杝_sS8Ҝ,9I4*bsd`䃓tI&/Nys)9ɊYI˚с& +%nUq8Qcڽ/3 +QjvAOѴz:j}RW Rbcy?%ۊѩF/^;$eQ}" +x3!MY{oB> +WUaѝV.$bI Q=6'E+ǧ%م^S޶Ac$4isVY.>vE1" T^Jzuh/ In.uI!LlM VBL0IxGT7`*u@nSI`o7,hҔ %Mhգ>E;c B bzN~޼I;?}49 2Ԃ^g6A?]"uʨ?5\*GE%ݘg&i ~j 16g DϨ#M 7)Y(ݢx@߯~z9%OL<%% e/ -Q3V~ϔ `?Sx5I~*O QJ^1-udՠ4g 4{RQ~u,WigxULs AETG-(K:w* TʷVЯqtf˺2tE8M,3漏ʨ5FQPb+Ƒp+H(+=d ~qWˆ0g[:17RqzңlYgbl] e/gN-PTq!QһؕX*w_?E!GeY #->yڌ.) ( +{:+4^(J-+>!NZ `<<>:oNSu 'kIѶTzm'3e) gJhm޿/"tdQ t{ӕI(zy. bep,mȸ'?k}2*M~drٓt~*xVw+axOK'{"@c;}\E[TLDTbIMי`ܳɷ{г@1A9>c?ߦ⁃F Nzj]uh٭d@iRQYQԪf$l7^%@; WxJ p m:o k&t_qh[ukqJk%8=!h1kIa-d?D:jd5(ŔԫHZ=[tty-)_JA_^b)-ނ B Ʋ:f^]& ecmj9MQ j0YI2 /]c&ov(| oɦ,49SreG43 "M_6m8 +nm@R'U7xnj9uAv]])S!`Z]GX=v4Vy8q@1=0 I|0m$ǥ)-^HeRt! +?LD[5#FWĵ`\ٶO~k*5c(;Tn{Щ~ցåWX]1ŏ&ޡxÄYY{WQE+:9GyFؕe:j}v$CXWe]+G^Ml>]+R:I)^nR~sx:HqzFxܝA1B3B \bZ<BTCYn8zX J"]@)mpj5 f(!b[/Q%yAdzo6! O +G@i9p!&Q3ppbI!j9 )J7+hbB̀# +5=qa}p{Qo ѻHeiPƬoӴ;=xԪBǣN߇Wb29C5_`dl^3;{lh@Ӧs]蕤0*s$l-@CT%r1 ؓI- cRgN0Ph<}+ެj#Df"]tEDDj!6M>Z삦ef #I +oaeP_M6i:a4VccGs*`0,sn' F 93o?f˽{o#CH'+"&gD >ւQrnו|_ *^77x^ +}#}q%L8ɇ*X';NZ ӝBa MPHK!lc FrD> +\c8P7MM1 `KimF%U˹GyӃ&Xkh:c# dtSL8F F{ԅc $7BD2hfyoIe%@j-AđڡE• BW|#~#aύ*#@AC:fX?G<\%AE| a+;8G 4D3HnDV(?5IG\M^>̕q" Cn#C8LkF +Gd6_ Gl6}l4jf[pE.9 n149zɀ\rO%FscѼƿlq-7/Gz@!@ew 6'A9-Eh]5XKLuKԣƲ2KZ[R:^vn;Cg,c\\4J7O!8P@ڍ84L! GR5Eגl.}%j gq!>jYZI ҹ!MdyS&2$(:GuKPiq -;S" N{o{ts|2Ow+rGAKtG^lEXop;ʸݱ!inܮ 9Xw>8hs,ƫlmq5"/NoghqQ c(qQ/>X"~/+mQ-J --WZcxst"BmYc`)emj}LѹTe=Le]R0:SҴ昵LЍq!hlO4>V_O,{2YhmrzM:Dh'Z 剈7<2HP#zy"XHu O2u(C=p`mO4:=wg/OeKDD jOH#=mh6EI;=("txXNO4D'='Be|yk uxq^t/O4 +ry)6]fCzz.~e6NOkߞQ#BH:"O [l*뢙O5(6mJ$HZ`o z&Z =J}N9|MW{z gla0)hz}q Y{f%iٛز8䷄>I"U9h y:VHG[>;Yu=:fZ@-Qc4khYʰ7GLqt#|# u +ڧ=Y!hrؚ-m2:C_9oS, 07X֭)G1 +ܨd@3ܗ L/eĔrƦmi?W٦I[Ej?!H=sxXdbk?2\Y3߂#OսtF/d +ՙSr9jYcLuvq{1Z ]ZfUdoԵo٧q?CH76u 35nU 9- |QHq>Wk[JlskԂ}DžkVOCP;^e0ExH#5O2KB9s% WB0~'L +~JeͷAӵ0hnDd| {ږeUR%*hR%4u%V'ήL* QrcV٭2Ԡ6xĉը#_F4#Tx3{UcM[Mk[i%ljSG4.%8ے ל OVڟ&/akjOZ#Th?:J#f:0m#Y +#]<`N%6c1yԶB=OmӞ-G"xRRڊhV;} tKk6$tRz(Ɇ Z}Lbp4L D?۔-x5͋{ ۟acű'{\tfVxq7nt +N]|/@9 5 {q[y>.#귌κ ݎ`&e`H g $<_BouWgw,YV'Z(7Ds@.c"TO6ŊYKy` Dl/+#9, (5tK|]{mF$жZ> AYN* SǺ!~_,0.?2NYZO aɫȁۙ5fx.#+h'mہr5 HGNC s34tF*|+@>Of.~=ܬ!dݔqڔ7.`r M3[0%Վ-5lǾ +f2Y߀4l(+4}_6cf69٦fB3{۸W~x Pg-.Pٓg=;.? rYU Z=@>J]^ Ιq b~4~A.?WcFâyY 껥 &^[ `4. B# 51 ck&D>5b1H&.R Ge e6<Ի* Lhv-'9" IuWsvN$a<'k^FS0cyU殸N/6udwʎ86ZYJ7Gp[/ }u 0!#{nG~OSc_Z9g{{ZT{&U=%xxw"1l%#Q+_gVy0:9n4=̦i8#@ +asLtE]Ao"n\<_bթA[՞"u߽ޮ:Du|&>O\o&Qm~U)jp p8R%ʺ4'U R9RrpnuKo%ԄCG8NuͺQPI7*r|>[^.Vwo6:iJ &>aR0?b]d|q܋d?4[ۈ5cʪ/jbߜK\hbEWSc*U]ٽXfK pkl__JвYdf7K~V7bX(xhav>pw3!yb$y]dI[ ltu6v.s륊i}S}[a+ζﱪQ:+0;o#5WN{C`Ӎ$d%XEaQAm)_ҰD-Uj\VLJv4JشHNUKXX0fwD9W8u؀f"ꗨ  +{Eaȹn /o>3c\oΩ5bSwi}X?5]Q}hۻLB_'m,h~zS1vlrM}>h~]یqi- +:}l>3T"{{OyW{~ȠTvAy*ܸ.7uK>`/y{ԉ;]Gd*ѯ@K[):0w!xw'lc0]2ka <`tzRNu L-OflbL '%iD<#?&R[l@mނT%B }98@Iǣ `NfG>>#!:=voA ق\\O8,R0 We{9+;{MɍwgPJʼnmfdPȎFAR1JR@Uby FlhHGpjY)^VNp[Ć-6z chBu'0#:p_L䳕7BIfAgڗQS=r:xL[5*NlšBh +@ +9.T~@h|"OvT!ZͲ^ |'9nmk>UGCz3eUb[jJ$'TvEϓ(;fV0d6Gjg07n :26BHɗzV286rT*t1 T2ŊhFֱlR7  e|)W ʤXƻr$a" FȲ\֖i?OG}$g#wKdGيڒ=RwK1%(~T,_ E -͌n8sG0503׮e]f 8RFIg6X4DZ/GbqE~O\2xȃhwnLRETG@l)m r:a)m"qĝb/3Mj=WJL)*Lu¨vg aerLJ%2_bo丩 ,/Dzrsmf!5@YD&QWvbeN{Դf?9N,sfaI{R>NcYN|mORzt( _w>(rol 6LNTZi >e4]FW]4 Дed\#R"K۵ï"XbxѼ}J-uŷEw.k +'Iҡ6 5$$!lIK^S[C^y/o^m2e.)QH yng`8,; +endstream endobj 436 0 obj <>stream +HtW\K +h{* +uMQOf +eC +c\A˞z2IW"e'>=~?8`?kSn~|r?ULi"U?-fz"먡aB>r*kp9_e4=b+Klj/rTvkl~'yٞ3qlB̶HנŸD&W$u}!fQ^I%ERtL3q*3>u0 ̋2UeNub $v',-Od*dRw >صyUvrUM|/BF:(H}3,m[/:Z^ڑ|B5ʜ)H6rv CV!MڕܑLj\J} +@nG9윲 }QRYgU6ȪuFi?e^~ktǠ㶨[tjݟYUl) |.̂]P wbyfɦJ(I=ܼP^:Oie y̓FUqIΠc6=$JGiS_KDk4.* MvlI20 \z􃖨{DDhdSj Kc=[@2VUQ3di [ĺ4xȖtTq +5U9?7^sdA炬6+|!^ǞKqh>~EF2@tIUJV#Sf}seǍXq966'J{TiY%&g"Cr|#إ!cp4[Ь ׮n5Az]t<ȭrزz!(@ )S;"e-0FY)2#נFgj2 +{";dr4;VYFg#f"6bg]ܟ4FZ6ijg6]vhn$IWbL%鲀5Bn{@;M 5Ю^EA #{"2Cb' ,^șL>tR(L*a\ߓ]P:>^mO>m,~ `rϭ5jPؓ4fU +^ [sMɲ}3ӑGyjh Fv"P\7:Xjn4`bBoso?"m05kzCoK#PFo`]˩3q+|<'Pqi!>!)~p5>KHO>[ֲfyڽ [-gPC+w 2XPG߭Z&v-*Fg_2@MCʍ^m5۳fĐ j8+I-5t#/c˓EMRy&IϬauOY;Ȟ5%.DKL -h)6b H%K,*BR.Xy2:_H}f}<ճ+qd_gYqNuTI6 ?fM}`t>kTuS?d*$l:%'*7$npf6eQn2 z5$Q&cC̶BBEގZTf3$a" =B/oy]Vҡw_7uI *H^} @$D4:׃@zC1v3FLWlяSMPS`cDZFN qNuIw4-><܎4}i0c}N?f x/z%;߬֜ڀw[H=|;_KlaJS[Ыo_T`RXWۿUW 7׿TOJ<ɜXJ W v䆯2۫b$0R*M<2$'sv-sY{CHMV&!}_.[h? ` + mY&#.dt@r8t2en>Ơƻ.7c MUԘDК+Ty4a@#~ԱZɣQdīS<1FRD#yØ^?kp;jkZ% ;#Ɖwn눥8HfᝈX UzQOg#X!Sk">xPm6+K]MavZ?&KmǵO?HGvQR1Sѧ:Qj{[WN1yǺ^G>/=4%z/๎c!l3U*Y&X I!W Vyf' ֪÷T UM%U,fĩO dqvu@=r1u#+-:n̉C!* Jdi9ZdCߋ)^-[pb +|@a>/ Sn![ +-QA,CLj B8◘¯8Y̔anW7{WE$ej8aP4FdWBu?6'e } VcwCx۳*)ݞ^C_a~ה3:M-N.@d[jDZ,y&nPϲTmnfQJTw$$g7nlЪj=MU~ =GA >N-nؘ'3u=3Q#HS|UwR/8=5x nZ3袰^l<͎)lnUEj6 k1Q؎$-Mrk߆Aq@V%~td)#A*"'7:q,JnYc2C:hXcך>4 X;nǜmxDG*e<>ˮkתx}]%` +v|ԧmOጓxZLe*ڪj<+hI*XU%O%T"5ig R4.h72z\߳DNЪOCf4ԓUut&mui8۬MCVv"J=l>SuLhu}{;ZBM׫c[!:V5Gx!Gixq%/`&TCΒ &7>G=qեyixci\jƸu<5 &Z_cPj Q g˂j;p\rU'eVg2*FMޖY'N.ŔhHǹ@yxf&,"`d&evg2e!~NWhY}St +kKyݰh72FdlG?v!v:axSchlR>H‡eaչJveVLk:rU~!-:p4Ýd̳nA N "`m22#ǚa QjI+e{{ Ҧ"=ZV_O6W +]ǁxq&r%q$0AELhs3 3s}h1/q}ĥgZz[\5*ՃC&' +d>څlˉz#>Ez`/r` ,$34-mqTW}rӼX_AR{_Vkp+#zARFںOaz;D9SLX;!ٱNnS(h!Kء@ljEz eYM[YV-K98Uoܣk !%O<9RŶ2h, dsn!Khw?h*UtܺR=,/A81z%r&#u{4RX`ɧ[sA7Nkk=8ty( mKzNƇ\w߃=tb#kEG^DqeUѼDE.?7yt='O6xJ}ovyVE\_Jvz|!B2.FmUBH]XrqͽB@ n@*%WA Zy #3Oekڽεybxgdz^l5z3\B{#&Fr̮Q7"(<+ ]Db%Blpdz8fPM?lT1yt+/RJ m%@W;,@GKEyyjW{m0-P@)ˁe5d5WԜhȫjˋI?IuaU,">_0e+YmmWW~2 ʴ~}B\tBOCֹWG~;iȺՉ5~417/5X1HmBPfP,Q@l0& &-i?I<[G!Q"m!‹\aljȍ:HjJs=fMyzDKb5vyOa~.)h,sTev]yᥭU'k6͊OX/^F&>DZcD.޿XȜG +/ 1b%V!@rN*M%bΉXvXEiIsz +2nN#j9DI?X!."5w5Yh:O'Yne$ (i-,S}ԧ̑ >b_\*z<|d^L0zjN>7npPURLLӺ[kwK eoq^ߚ?ChiϲRH K!)tU3`i[sm MM@鍈2ވvNo4f4=8h{9ʋ )_otp ;@o<0z#r델Vo~#>厈е 2lv#⾯#\}Q.wT +뎚վQ!Nw⢧;nr;Nw/ڣqZ/{2eJ1pi(N)=߬QE~j=V#gc{(~/_hG4py=l ?:#|w ="b\V-Ѭk} 2zxxɺ};mry^!y9$NoCF3it/:$Z'CsDH2ak"ظ,@^<@Z$ΟhKZMDۛikhB_W},2_He`c&oEB^´]i$i] 03*]EyCI^ +,]E]QCГ)2KTB9@Z˕WAجtGgA*H|G+~Hx,|R[f,ضҾ8tXe%{6,K\.]+ėO [F8sFx0Ɯ+uj) !|7[/OX0)-41 _|u/(z z V`|oS5Ror&HOٝ;u}({^Ukml1>p2ˢiV +6u۴Q5 * M7bMYҲKiM'u0fQS.B|iO? MYІ2(ё0qF0X7mCO`1pBT<$"i {BV#czh> ie*ŃXZg5x޽w&5T II\L~*lեK<ē\6` \ҭ[⋗.2$A i$GM!5'mn7bf{#AOyd19]$ L#G,kn +GF6RfJm +|پU RG*#+`vz00(^O?YCfT>wD'V5KbH c#@$/q'υh0B*OE#rRP5ā`~77.pן =}K\$<UΣd Yʦ2r#j~fA{nDh%Qk6w*:P Nݺ?F"ca[ +EOl%tNG)0,aK:nj}/ i@R}?с?Q*(栲&)ÓGEl|!z[f".i |~/:9TuD63H1'-z[mrUv`2dYt>u#YD`mD^F&mݨO:k=Y/E{pvPk3.nVU[<  #l:0AcsgCΙq[8q8PCtFUJ +b-#;2gUV#R.E PFR/#(8߾tY)^:3GhtbV .)ӗ ӸONFȚqttrݔDǜRFFDDaҳz?1mΒWT0dXT8TWlanYj?YH4EF4-z!Q#MP֌BmB&uMy&fvKޤ +rZH~Tܿ1qRYk}}F?^>RsQ<6Hpu4]Oߡ W蟠HPI$},tQA b@,<Hc<*:5DTjtGSykqlPAAS6弒!MXIn֜Af1 ::Ѷ6&TAF}I{T^, F 0Hp --vQL#+ų `Z P +ʬ%J`8Dj#eN&);^4veե_XP@4]qE4\UxjAGjla)G|0Vx-{v/[o^}v",Wі= lVaɆ0Qr_];:]%8&WD EFrFI7e2նV Po,^&E̮N˔vJpvRhBrH׻8*Fx7O0@U׷oǞ?o#~e%jli#kEHfD^u.$5N +@}њ\$OZRS=عF6CoQ :]yX.;Hoܻm +MK|p`70ֻ"aZs 4q}lRƩf+LYoR&rܙV>Ŧ.뜷_*mc6lZd} 3:1,1mwӰtمvo&tzaYf1 Y`j@82"QkO dۈS88Q>'ـt-ZqUuߥ"÷Džy[Vĕ +##«ٓXUի3j+7H@`g |diАʆ7+ߖ`I]#Aաu&ScS9jUHB_d Ȏd9]^S~˳lZ3p;U|}vn%YT9YwA5$~޼? ^f7%|U3r +W'tIQ2VqRU^`O{G)Stt9l*xBWވQ,!JậmATdخ}SKY)FR&9yKh%i aG6mrZiJ Tة23aD {[#p gPyWT vce`&"2AN L̨"-fɻ臦ᙤ|><ت2>˲u I}ȉ[CJHBrWOQ按jjZ>V#RltayXF0{|~-a>se=]=yB^`eN uܩhe= GF we1fl@yXbXU*7|.YBx)b~‘a 4m(tXy'ɻD_R1F4Ο?їȰ/b= X EY6 TTl˻ivL@g!Zg "ⱓePwm'  @bq(F*ܧcH筅ߛyiifnd:ciCFl-=ْUwA_&(c^-wa|@MϺHl ;n/UQׇؚ2(7}ɘB!Qu֭6Y nV3L|wQku[tݛBf\JaeUC)O,5{t?2ҵ[R/rwLJ{Fn0(Sq0 xNk +.LMGjfc 9t)t'X#n zd֜_Iw0]_?.c|FC +dY>b(-=t ;:ScN6Z.E!鶡WqE{"X6ƊcW m׵ qjUl):R-auk3$18ߣ$b((j9i=sN!g213ci$N/l`ggl桫9vD ig,f hz#h81*NE ~AO 35R.R; Ġoj+d orfŻ@ 7E[曧tjxLj=kdZa`x۞v)بlŻB |=jyʚgͥد3(p+Nnyxc:oTMh3+W ;=iBC} iv1^A|ہn/oE8^H]z=܋#&l@ERl WsHZ{M+w{qj-75qِ{/;PCz#{Ǻ/WGi{DDebH};BPF@qJ$)&zU1_ )}zJyW+WDc, ^_P@Im0(zʖ..m\aX!; A<|<G XmQ}FHZLo$۸ȗaq1##brI U4*9t6G՟h44rTDVS]Ԯ{-s43GA|g;W\#^f^ˡH)Y9H*>ԦjkYnq(DHC6dT|e! (F<#a[ %;li/&q 7HN ")+b_*uO]գ4i@SXՃ`Y)aQ ŗ 9 0{icH@]Y+'qA5«]D$6*!N }a|НC*P0J C&QcGR*'x]9L?Akr)dU2zޑTm€Z zMؚa!Am{EICt&9}&(wnF ך-{>v;;"pņ`%ݩ[>`':T14DApI A_d{fu Zʉ"k|2xLA4#JM&Rii|a S-=']|D$_1Pcrı2Zh? c[5't}cw|mOq>>((`EG^N`H¿aQmĈ`FFCd W)#u`b +H]\gwJywGⳎg5F TY BD<Ѻ.pudz:nUS +yxB ˘֟ڜxVFzeb涹RJ3y`ߩ-GrDT{Wcfa:y6QW}A{,'Fkwp6'gxH0}P:Ҭɍas˛.lia $p z_eI;≎@"4\IhNGmĻ'R3>\.5b0xD`9[ڋFC{bU~Wvɾ쫃]L&>qHcFF5| iPc ۚX,!&_LRo lԵ}:`uҰ֊|;;Z2 +ÓyiLqUo#6R6l`%18k.Epl>|xsTz W[(X4{v p3)׈8TXvbQ9ӰnC'^]ȳw3(3ǡGF>aT'ycнrSE)>+dX} +׭1(UƳ'zBğNJ ~?mJIQ][::3&Md6DϨ0Dvji]٫OH{ᚢs@K1!6[Rcfڑ&h/KΫ='&dKC)$IJb7Je#a"$m3JKX c\^$|BL`Dc;m$n־>;Ǩdѝ2Oy5\v +B]-CJ + x0AԀ&͑w +U߽J;ZҎk3{vh=8ZiwUJbգvrɒ$ՁVzu 5]Gd &x$L&tqi$o)H))R@<-Kү{W)wzK)`NEJHR<.R +VJŎ(Rm^ZP&Z~-my#CYR +؛%h](C%,?U(~(j%:x?gb9}AgK%TыZƼ%*ifUR'Lo% +:*ANJ):jrQjQ+6\=$rY]7Rȯ;])ۡӍ,:RGgryU2'̄GfK}I%U*yRh'lEӉ67Gn\%l~̇I#&a^t|X,(Ftx;TUQW/w|@] H^ܲb= 2<ЮQ8V殒ǔЭc*6XX Ic42"0ۿr?+"4o]K!FUf"nuYNp49ϞH;# 5_l9U(g߁Heie8Tԃ0YFˊX:pK4 ']Fʴ#<lBP1~&gl~rf3R@*aB SeS?N Mzk/k&3[wk]ePj*tV%RṅE +ῷV8/LV![O<r7pow@%BPW]EM/Bn27\.+uu(?Y?녲R9/A5Xa(-re`̗+w_{zMQqsbHߙ6<&4Ƅ- 7_8ѩsPLiY⠋$x&[p.F(_;Z * 9œ~,]m88eJa;o[8]-l9~ ޮjɦ YWU[hj.jX*LoWepV雊 ?+e7+1ݙofg{,ުV[9qƋ+3oeD'Use@z̕=]U<x +dj6dW7{ ku F("QTW'P_ӗ)w`FdK_n`[&UdSCS 0P'ra|>"/жfB"xN9n!ce V.d_h2z| - +5l%Ea{l@ɏJYjm޾mlV{I.^W39ë1z|Qo]qYf|34y5'Z$Io ǭ1ܓ9u·6҃"iGXoJ/pw8aJc>2':"(YږET p{*_mngr2&Vv7BL7{OAMnj$!\6k*)C'זe2Gֆ"CZ2bG]FlH _] X(ي';N:p$(چAˉwl/ZZa +&{hgrL|-]du43oP(g%>^p :\Zs#A 4.ʎ;UhnIEO]=O%Ф;3Y jk\f)@+H%̞[c*'h=g2 $,0];4@ 2W03Gj Y}UN 봫Yl:>z [& ״q_cѻ{Yk(m|dWƈxPijw M1P8  Radn[) 0! HIFSղRJ*[r97 +KEK5X3J1ոSfKYJZL$+ 'rHo[ZEI׋SO8EȘ?pdK)HA&O=+zd}5a~څ,M +GP1&zbC RZ{/XMq^$#:Yby1$Jg`5Rd|<UfX*X +wC; ,etdJ/؈f:m "q:b $óx1Q +Ԙύ0r}$0j;WW\ۓ,FEo4I*2b@?0 DoA1B/Ӧ=v-?++]e/hie+4_J=iĩ{ ԺM^QO[2؅1Cv:0bl\GlXӔ\BPx/HlcIX % Gd0ȓ}r\óŢl-)DVRs8Q3]s.Bq~:`x"И=xX54{E|lJ{ʥxܜ=Жs}f2&[{=QdIGQI+/{|(GfNE0α!.UߍxQgJ,c_ 8~X26<]9G"e~qYl]SEfRdq.>O DJ_DZw2ZymCHŢX^Ye5-FȨ yƌFj^YkЎzC/D gYAPfhD}"FMGNld)gY|spNZh(i#zGXFwk#L{6c*l>[ +=嵓ú{;*"gv8|nfuŋpl0$)C3Ku$52F7K}ydnp[MeXNJ϶}ǭ\Djt}s D>f?Yiӻce2y~tġ؆)!c'Cw +\j|(et{Y7rQZxA[ hTP)emr /ކrvӴWyO]jPsΙʍ7,Pf\NIqU];ͪ-&[}BGYDz9ʍ-=Td`Ϫ٫EHU Ȓq[ýr4mK0xEFg9;:c(ԤGJW[ + *)/p}BfՑ)GrE;ӊe1݅FG֨.ĥ":ҙ6e`\}-BÕ6Ko +)o3CmlM' _<_"w紅b],yǗ0Y}`/hp1iDԦ +ɿ -MŲ Nu06iKmo֊F< lkц[yNBX>c:V,F>D@]C{n|@OG){@L=47FCݽv wdG@t+:$`iS! d{V@PZj>@!5V0b?Ƚ2&~3*ޅ{B~BjKj1H{ +ΨlWj{[sh +hݺ`_Zb5Hbӕ7׶Nq"4;F> C{?o~zr13YeYDB\vpFPjq=%cO;b-xZ[T'MfI27jWM_j}gv}_t| /cmɵN@J 5| IܝR•Uq=M;tNCCIVkvƲ6+: sqו?*xó,'\vj-3ի쁗*İef{~rqzY٨Dk$Ҳ/KITBz6vquA8V]7-07Kh( GkCoDa&g}G bJOӬhyWoWEtdo3'WtG O]06T>!W=żjI e<!A4T3?H/GLcy}/|}sEpE]DHpG +IWhE@KYGFU>2N6ؓki1ED $\P>gFcOkİGc]S7i~cBbQ"b_+a/a`ɠsV_f F`H?l0QCW0,BȣSN=kۆT$}Tk,MOMWz}b$ϴo %WW~~U3>V rms yؼWk]/8*VkF*ү /WQې[7;{a>K=M9}3Y$(mѾ6&/+6֑ r1rYabD;QhEN"_Ql_mypo #IHM#JSkT +R ߞפ@[TZ|#K]m+6b;Fv^Aְ4|sY!l?ęڤ<7B,l +EPgM ÖKt }ߋIX-s0By i +P<+?Rkr=w3tVt^ؽs=96ʶ!fdzc*NVI8ARǣP|AY+8p#˨$- ^%]JwIrIEB"t2/z>]Kؖk$'.8Sgn $r'fa6@![ qwྌ!5P??cf| + g +ycB,3 $ DᐅMFN+9Cl#PAp QχCd4[֖v!Yǔ!´{g\CECfFC6o]3jGM2`Ꮘ D.!M>ds6veD$ܪGlbi -ڳdlDm2HIK>&`qF?XM +endstream endobj 437 0 obj <>stream +Htnd wpa ɟZlDxئ߂{L/D'=ƠYDY'!sA.Pnt39/ca$O\؃ RlSbK&s7\u +>k9>ޡZb1/--<W98zI0 S> (5La&EiG}1SqI+Zu 2HM*&hyQq!=CuV=bV<~"DK-Z!0:M0w@qh~s w`O +jw^ pY`/qΑj+"}eljA6]%(Tw[X +Ez7^PV]d:Wϳa-˜H&Ed| bO3q U)IdNcKܧ`2GR'g_!کo!lW3c+&HLS|fzlxּLY;/2tVJ3f(@ Vu/W;AN%V+FleDJ_/Lϯ; ݥ +_@}a]dpRCpDÊ<8zv7kA WIjDA,jLq+߁6nn_a|iFBHU=t hBd*@ndlŧm_Lsg[+nIJ~C-i,um"ƪ,䱹+(wi-\Y'̇܃JT,Z +$=\uzE5'k+<-C2Ú.C<6fikȈ(E|1/2xܫd;,魮i>u75zd.2aKhs;V)OtXAkқVqyf^o([ب CdnDTM vB+VNV.m%$p m~'-xܥqME/}!z]&Vзޟ| ީ;{9ކ儼/_\|$.[ X8, ZY ̜.Ҏ1ҫY|]M:_'Ά\)Dg[1]^·6M"׎[tЎW$KhF~ )z_M_%iQcWO,>7? QAQ=2E_( -(=Q&nfg6KrJ|{,!zf}e qNh:5T@,6  8Mk#~R2Yx'"ӻUdVDrҸ:E^M)F7\q V vUhX<<;ɍHcv.;z @߯.=};q^G+$wB!^ M=LFͬ#UF_zL^xͯcT~JnWe'wv+AQCkTN%`֑e)MKRWhQ갹0hʢ+leBP2++g#"vE4ǼzhK<Ӿ$,JwL,$~H.vQ8Ffu3~AӰ]-r w6dkaʲ2e&RxR&4#q7S9}¼cX0H$: G2wXGl>[egXJra .FbTPgVxLdFI |yʣ(`7j/ ]wdKq wE'~Ge{?`M/~ +릆Z`~}+'⚣Vf6nl. Ro%8߇bZ0;.z5kw1244FIuhcrjH=~r+L+Fwdx \_fg֒^+6.yWU Dz|Co3cWБ5;}j9MZKWQAFҟBmGg5T#;23uWҪ]ɡ$/L2;>;\۴T>U.#XX+t.˥_<`Hm3)LHj#"dRHba1kn  2:G%_|n6}<րڱRJ <&onPnuUfPY*"VZM{Si*VpԿhA``,x\7Lwa&(z};LP +R&(b35k}/&6zYHHM(tl]ET3'@msy9Ҥ0U I/m[{H(9=.Heʨ1lUQ'@1D4Ց?3G20L7,)er];W`e臣 /pϱE3Xc *>:(Ol@7s_M%#Uv"6ܠk59eUն Y#S__nf}K}hJEHJܽHXN~yo)^]3`՟gjХ㚃`o~ 7_5L>_:(O}o|4N + ӷǭHrya"RdJBxa +Ftg;o:B7Q&m>1ŸuYM6%ʵ982 x$YtIe0_A/;=xERX^Z#d)ÃT1>} Q?W.{=%r[l bDKcH3h(MӮL8,n$ +u c*IUmG?%]%'җ-M®2,)~@^]r?SHf//Z܃ dB)R4{N#Ԛi*/R:EWG'4ZD~a<~Ywp4[fPC] +u DڧBaGH+R: +U}~bssL@ФǐR(C(u("4‹v 7kX:(} ^XOTV+QylH5 >bcC ^@)ۥsh0#`B@n/qȻD0Nq3s+d|STDd@1ru$I_ְJS/fTj2Қy:$kdĐcS}ued>1[ѹHqghK?s8^yIGj1e +}5u985X,#pN{K ]Rj5"*ڞ/ +II(H yj b> }Z gWɓe`ZeB&WgPBA$➍1uc,Rڍ▵S\\TO_gXFnǺ𡫶[CAe;~aH%Q۷Fwy,sH=\>泊 t^UdH,;#_E]4Hh. 4_H])' _NN Ĝl +b@Csѥjߑ;[U͖_IVx3=x,Љuf[Wv \I(eYS`w{&A&hkqDM asZ @Jܚ 2%vǢ!›#21b걠>\:/XPeEqŢ*!e_'4R_6.G; X{|#H jٚ6:VJdΫVU`s:VcK,?vrYclm㱾n$ +jkP*U'3hԂ"kYj"㦴UY5eBN4-1t_3=u6 1Rg('"J{ⱺ?::Z9bZd.aG}~ HMωa,<*ѱY Zg(\ L[B3=c[D/ ر`JD* iAVA <4 n:k~Poz4!ND)tHobTX4XM /.=[3,*hfhCk6啈34E[<?/`lrcZAo.}l4Ǣ' W!>}꟰,J @!$ +>h%|}!)dǪHPlm6j:MVPj3`Te.ˆ5"lmqֲ'0OU|*Mu\9e]Uv>ߟR1ZKa sG;Y,{ <5zg #b J/&GY +z1v;Ț1`ʩVt~ف} NߺIȚZ(X?V$VPbebtH)dNYS4F< ,W`}jCTglu61Ze+:rcN f{g8SY\fU=!9D74\YR{*YOҧyQ4zple(1l9~Y8MQk>Uy?BUnD ú&FL?ttΌ?uxTR," ,a_ʜ gkyA<؋&8)J$Фpn] }}B~ t:1R +[kWbgeSV Xq^o^_p [6C; +A.#aJR`C18ǯ.ٸXP{+JJ䊖 dAV)^ d +lf>nǮ\,> ԅ%+ >ǼkO6dGHY( 2˝*:JJRŨ;̙IUCJIqSGw$W"I֞eߢoGe{pAQJc@F+[U(t]BXmO$JgWT6>?a>E/o{щJ7TsØI( 6wwXU \%|ދaVp1Ye nϙkRޑmN (Tڶ2t؏zS蝳F\6 +͂>_YHU]ST@2JZTb~e?ҷNmgTsqkG?5Ġ0(:Z\jV=td1fvlȊ!-\/OkڐR $n7 ȒeQC8@8FR{)H#3끰7単 +mnqPMQꕄ4n]¥b#{2G{l[<ʍ,5e-%&FI c[}D{L`9k'o| ,uLqn?fy'U$nDU|6yƑɸ5n|8\LLbpdQAs|{ ~Q1*w)_UQIm3+iCѨG`{MJ(}Vk"vP:1`:W1fy[_H|f=6o{VЧfPN̛^TU'c;`HlH&2`@X}E$tBEvLnL_)$S(DOS/չ]A--qS7$“]'EF"Mg#6h3r +<ݻ])^3#|Ǣ#bN'oEcIe"ʑ^PLdz\F{^` ~y-gh[um&Ec$%mW\݆XseHr;FfxJOTMT]:WͶBzϾ +Z6;?y`Xx':ŋɭ09AJ)s0ObѳEB%ɵ m6:c )GQQsMZR[2FKM$}-GzXV' sG32ȄZ{n ,?ˌ;5_~gcerv)!-q +&0v 녌x^sP,ZĭWh*M SHRe,CJ|+TPak,F# B0ƕkŚ(VϪ%#ԸTCa'(/V&Ek[np&'[%8f23ٷu%1RվRCWDyXT!յᢧ/DX;%L^Sٚ9.p!u\`ICSzhA影%2e|:D͘)N^ͬZk]|ae/[O=vT.\xYjv6$)zlի=z53K~8oDG/I0?0[7Y^`#6pҮ^ġTPPf +Y3F%eeVis +HNRA(kfKܬӡRҽh;xQRgd[,q-ԟv|MoT; osI`hM69c"8bBfn5o-L +pmY s-U2ySIFb~ ȅj/c%mz:#:sSB.pt I=Վ#ydF2?1~Fˏ0 +d". ʀLv*\ xm+ }=\VbϙE} +*$rI*7rncCڇ$7{v4vT:'~ '~+3aTigIw˘ ^`qYU1w(Mɣۆ!C:ŁҔ]Z=HJ־7L p>!F\(;d[[|Y4(~~ru.cϼ*4|! +^3p*oyF+(?n+.^ + +m;-ZSfOYc6&1 [a+8昌sqv5Lx +GRKD b|q9ۜH'v4}T܁`/}1߇7^#4 29t'[iDZ'2xq;Ko9w3j㶆(:K3i'8apXx!u#ѱuէY%;RoPäӧwGCql@vuΐMsƜyԝ,-07G﷖V.єffcZ{u4$ fOt*ͯ셪3d(Ԓ/I1dd@ښ Z3/$qq)ґCCtL:VNG$lƭ,!0_4pz]ł֬b~gIe~*c &!߉ԽUj/-/g@zVx92  }f.yE״=HVƽ:JM^A]7jMYOjܿ&-Q%► InDG8~f6({8y}psxХƽv8sߒw +[G8!<Ÿ33Iy+ Սtd1iI{ <7VOO-4 gDGk6@T}7qȯuy-*5]nɠh=/}FUU-l(_ )YLe Y _Y4oؼ+dVzr"w;qRYآNVz24_ЯK,~m2. y#/#n+hX{gwPS4*ߧfI i}חyׇNt}м~81?TI >E1֏Q3-pߺؾQR/g淌f}+DZJg-$]Xsō=>,}^0ۉ|c=oo $\ We_t!#01N1dB5cߨ&` E8]Se `%MHD 4ȳ 쥯=R6ũ gm .i9(T+Pl B1KAli3a2p.1hLCtw~{WǞz#]F!g!B4 &2 4ExT%bgG 1)w@P*5"K2Cp[zHKRcZyG$JWS/k fbS>s˪z#5fxʖd)wH WC0SDMIgڅfbrgɪdtLpT4>f/YjG$/$2K֎ra1k?x h8i"CYT\уvXGh"q x̟eY 5<oޕ~2mX-%->3+ue\W-)ؐ8lS"1u 96` ;J-Z[n$hLxI?փRt]#Llt|sY{m N"3D5_kݫOZI+QɝC̪FYii?7+/n+k uYw_#}quDZۡ!>׮-J#!K]l8ޠ64_$F~()DZ[R)2CqVt 3@Z>de"< SY!5RFdkWVʹB"oe}eJbl.ECywAEN-Wwؠհ0&=-11f#23e"\s@!dvg+s%WR}:?8 Ŵ_3f}i8)0>D_Oc.C3W;$dtԐ)5doדV +>Z>zPdonmDs@/?挥& vړ =W%2: W=+,> \Mcx/,1%֦Z|[LZ&ѰATB(?U\KĮ"{.n}qDzPdSn9}(Roa7ו4> +Ñ6RT* ( +㭷7[H$698M#ơ:2Qf"M!Ϫ~R1YScv+2Dp,kԷBzQt S1 OAgjYxWfG43q}ޢTp r{9/c3fc8T0Z ~]0#f4i$1?@)WHޕ0jr + 24Z}r? _/ +"9>~&E}iX] @=ٲ0|!PCnV+˯I^ ]3`ZvV|a#|1Q 7Y b(a| L:E`6wY z글Le_H)k.؋0 `PcJQ`XПGwQHǒI8 >[P4EQFB0 ޵17?%39.ڃ/Y2Y.Z$,ǃkjq3 jWp:Gߒf  pJΜG$sIʽ<فZEdz e5sZOke&7@24P9b׊F!K EC0m±칆"l QnU :Q.;}{ >Ԭc3Tya!UW@jlzX?PKMg%^6@\śSf7Ze+T4  e^74,;|4a5и)kM4_myYL'CsX۾T> Fց!\}[==o-ybwa}Ec*%mddruV*S9LsOOg`169Yuw\V\ktW>&鐳.$Ut=ZEHlb#x\qau.aLXT_QiTE SnY:N%ik:W~-j:YNޏc{Ϣx4|QCSQA,jdу]|w!ՆvZ~$:(5ĝ9l:j=l߉ fr51VQ~^_dXR.|*Xɇv1уm&J0Q>L8]D."})$㲓9&"D TJ*5s +ieNCy(Ry(%0ex.zUW⡟ ~qЃmZ=C:׋VD:x3H|*}"`w-yzV88,X- +X(=y\,B6wbnxXa +n9g&p5$M8Hv=Nk5Lq/JN-!X6Y 邆_`frޥooL- ;O LZkȥ1kmB +/NO?Hk;V Bi;L-tj*I,Ĺb3=Wa-hݞo |QYERp϶Kx(pzb{p)$S1O |Q!S4=6E೷n؋ (Br?*e\:^u0>TBŷg8' +[w@SEu,wp+LP D&h[sx_4 {T%=J')P+lzG ft6Z*OzO`ǻ\-k&5<0)r4}nvXnL<ᨖp@ufP&Ui!oǃ_/!ACns}G5Kx-2W@19ZC^|s{`8Q?pGR?&ǘ=w%UQ?nX-Y9ϺyY%#g sa.=n1~KдQ?0շ'?-Z?9?zV.蟃m2sQ/Lp (  sjI$u[01EO?=d"寄wB9/e:Ѹ+BzH|3!=V3=;:SV5`JDITD3ט4xݝ\}m +7ZSErSuw Y2 KSB:/-1#ãLz*\'+M3.8=. rZr/ +ut5VwrYiaa+7g>KS`.)6,p".tꏰ<.ohl.YQ|Ϧwtq]8W|xWsJ +lLr [%Lj2,ےIL*]e|X7`şکBsp.U|vqϭod #`ۇ4E*V#Y0KM^?|?-0.c⚘=dTh{SEV Wڟu6MiT暢Эn '0+fFF۵GmU4e/9FY.4GԵ!s"LkYCw2pÓׁl0zτ7FֹqUzU\K3F={|]țnvVYMU`B~ ٮ5M|蛛:BE6Y17;0$-qW<{O(v;CL#s~t=E[G{}+κ "8?TMTד07Aj]KVg^t=V 1o׋>4B +BM].ύE֙_ڢVPsyg`"Z4W-أG [ə rS%$yT76vm0` +O=Q.K &E.4 Io3E>"ɩ. ěj`ψq~v"z8W!WݟEm55ictWf##SHT~4E–\VʴKjÃzݟEQY݀6zH`pQ@dzl zά`U-n~u0:l}ƒ>YEyγfyfc|9({vɥЗ,|k+`|3} @<;H~@Hh{߫()̤vLJq]Z+&EpRpX $/a=~n/q%E%REǺɟq!&nb ՛vb7>>܏Q/7FW‚up?3CڇE1L_˗#覑5 +/_7]6/'4`2?{?R473e>zN:& #z^g+MMPYZ?x/Ե*И~~]_H\>X7|$O.RΟ--'-M0Qg("u"Z~ps@x?/_G@XoF&Q@գjI 06w f;@h{Pur?%˒['9]m zW3}gP5 "IVisٽFvlHz +G"ʈ7/SvHb֏U-?8 -Ƴх&xXlZ2I*{{akע>8"l`@Q= 'hy,'NywڽaIy$3}'O;bӲN`W[*BKV,]+Uw &ߔvjM'X_|ڈ6YR#, ^&[*{GV\0*P.f}lYߛzV5B38-IG3VUgQֹٔҐzjhݐ[k/sgoϠiFN 4iֵv)y ZJj'-7nDu `"TΈ2/_U&\D׃? =Jh!<T0B5ꘁ{ȌV;#e5+h iAݦp3^V60YU_!bƲÂY0_h ǂ03o`C`{ `V[/` FxϓC55/#5T_ٖ> '[%`<F12_0|1P[0zcm$0FrQжƍKlExYxܷ񧏳(;1~UT>\ Za((з+!ykQ4QkJ +dJϳTYk0̽ wBq음x6\0ݓCtЎKL +yk E +h4-SΗ[[:sdE]賁X լS.KOz-K L50AƭXmv0rjWs%8YY/Q? v)ޢ15]g#uobͧA8h}(kh-w] :06Gx¥MF@i!>*<8g!@:2.ug5G슌.Lx!`(W/GcgGT;כ8k\(ji~ۚg5dkla(VbXƓzd]yLKw2F6 &{ ۀBߔSѭ>Y[- +_G #c|VkS׌QG܅NM`Fk>b4oS:!&Q4hu pZ;RCcJ,r +3,kF3S3fŸ3NN / 4"8h( cq*Qdp&IVuVӲ +Ό6VhkGghFoգv a02 +&M&-UH'_9tWRAZ1a:mҐ4!e8br}a~=K= ow"LawވV^a(H,`40ZPOA`+ +~#+^eE2.ZF-)*]VgiyN6)Omƾ5U[秙 Zy!(jĺ§U@nUÇ~6hfMIDŽpX$|21R ȋ~{u&*ɶol+iσm D`z[qTnjJcc0*%9MLBYR+3`ޱ.Qg^=8VO:$t1G†''\x"}l!GBBy.\(d]#".[[t#~]߲AU=fħЕBwPSЗ?.rcFtߩf7d*-.Pح0^uQha4& )ha`tW1E{c~)0YJb؏о/Cn[ %k6 aIY5:ݥl_k/VkR +endstream endobj 438 0 obj <>stream +HtW9]wP| AgCHO ;h3Rޛ}#CG9D $l ̠0^'Q ',69oo{yǜįSH>P'fỊ[0N@EM^oz|FR#߀[]5kۈ";ȁ7b,f@™,RH5N>-FB;S ,@tȭq@qxA:q +3f8292_}bW艊0Hn@R4YˣL([V0 4Hk%&ߟ{p <3_ȃ>?|ْ;$'OQ]ՠPZ^fX,4BPʸr[cLP@/q?3\9 fLպdH3cRQ!q!Z7}Q >Yì5L&*͑ȑ+ˢe֤Jʓ&D (z}B% wak񊼃>3Ub+2}~֊`eޫL`oMabNG#J_L\LGECgZv!ю4 0Jϣb ^4Gb$l㙁Q[h0->e%NB8'ۤTG}~]s|Yep˛~$^2ff0>r 񽑴)5\۝Qv-4+7˪x^Ӵ*Yͬ ބ{ҥ"eV[3 ݗߢR(@Y:J dVU~H7]TY^R=%ƥsZWjp&>Ʀ#9-d4tFHdM$ ミ^ӿWi@ל K _/vϹ)S?atG9P;$`=GrpV +?7y)(ִQLղOpl7gDM YBop$궪 % r apQ5= -ؤR)}IL#̏tϋY^(?t jٽ#.E+{"{?0 jP6ğ3Lqxs }M̋Ğ~yoھ]900Uq@@!$ MAaJ `.X(NX*L;|F i4;kWzu+zfR ci@h#m-0|Q3o-kVb`p$-_3 6*Y5:{ +~6D?7:b0+)X +lQXa H~Pv<)YkXn +s SdχbԦӓr'C#f|TGLT_,)/ <^g6ِd̲'DNmζ?#=1 u rH3S (gSC2#vOQOTy?L .lm<5-u-?'^%.[$"qE>}DHMc4U%jDg y)Ni,zL7]Q iC>ԻƇ +Rpް C;$Q`*K$lGpa=5hE"rή>]q{t8P6|C"ҸEGy`5B2׊;;q $l_)>WNbOz|OJ~~Iֶi-CuDpbLH0舋Mk}r-nf21L2=#]EK H_wKJsJ-75*pᤈPVl)YnBOF)޾TiNlѓZ[] ZHN]j/д9l^N*"Ug8xɊߏ]%gFZXeԆE]W\:tok\>8gr#oH<7UlP? +ή a: $?vůjOfM`MbGX @ۏD!?N`a] +n?`g[Wִw]WC77\c+NMl˥ǃ+ )W $c ln7Lk `6k$k;gZQZKNXjZeȵ=O99XKZ -=[KF۸&62 + -y?pjw沭\owc//ve2%/yre\8Fm=L>WZJH &"X`20-a29suHt3*aLF{YLb3 -&/6dEu,&Rd^F~<&05/*5V\ɸ2.σj1'|Hs2#udmlF +1|KE-+54[A8& "{1pYb55|94xe/"_Nj8/u\  =ƪh^`y8SRZG^e#F +'`!KcwM>H8wN "yn}䱭[pQ(ooޔߕQ"g&9wμZUj!6a 7{H z+`esb)d(`6QZ^[ZSa5&nևaV4Vz|und$_aC,J foXh-}Mczu~%س `&Bzu9]{;4<_d ; EeSQgLŞ022ٛrzO&{W)5~O9uv-S%1#1e X_%m)&lKͅ'FcK-59Qcer̫ycK֞U-ŀrٖy-R\˖:p[֔-Ed0:][qnGx YhYb mS}RLcK']ʵ `זiK% [ٖ2E[ s[E/uWcK>Ml)qm)|l-m)[T_e2q^[ +l=1[>RmǖQWRޯE׼ȦcK[+e>9J*| jvtce866QӰM*JBPU .mOcBl(UkQ" owҾl߳$ǵ)M>s֛A8&fw@PzdsP-KUYz5C K8dD:yU1>z}y?ϙ23F'ɞO@l&ۭ%G9|?і=쏪&%bOia͋!s31?R_}AfkaAf ;,n +:@oZ8if{3Jg0ǟ%-ʟj^ĹkXDT5‘.lƔ(`~4* fq4}kQq&P(S}oc>_O}?_%m+x?8"S{'np[.O>ЊNt/"[a~R PM?xJvI%w6ه>" A/Sz3 'A+P̓ToZ(89R?VH%WpQρz``Cz({<,qp)kܼ`tk,Rtm᳆5"i\,Þve+܅3YV=;1FAU6`w ejSvrXG\ܯ=Zq.XT@kWϥƃwY%B"o@40<6=5.6 P$e>1; {Gg +㠿 +ț+Qd +:ގ*z ˝UEU0PLV*\*Y +*!ZvmU+R%>t}Dͳ +}TQZVjgx +)KҬ6@_7eC>34Q +&nRkR'JO{5vl~*V_k'{ +hu% -Üfg$VB\PpK?4 +jdi?iGB>_ +_y:5.enB.(I4y^ǔ}\91ُsv,l{l"Y=yJ`/@uŚF@_ٟSSZ&́Т*SֽTF8 +`ڜTNa(~]O-Rm-G1lƘ16/QdLu܊6m>D`RYk]{c# =F 4j=͒=je'ة[k=|! 6a"z|#Zm]q$B )iA={6*5I1{tٸmI>iz l9;jɣTGw!6=׳0Ғ +Ja^t^EcU8ݑץdYm+wh8$Uc,Dm$bz[IJV/dH^)V֌(-zu )B4 ,]i)\>yL)dQegcT]ҡsLo{2گ+I<"݇u[ͼ1i)E-~X{e,e,hKN|i"M154*]a@ܩLf~c4XRGܕJOZkg`xy̬8za8 +n*\3wLhBdFEC.`RK. OaBTFx.cƐXZLfS^`F7nvڅO4❋a>D]0]6V؊F8<lM;e4Iww&n)kA?tWKv%+ J_L{=M$PT7@?"5*%Xw"]=tulu8j(YI(iQd[o +=R"œ4>)Q볇ej* .. UC*Psi/+g`uj@ɮ|ho}o]MղEye D[EBZ:5TBʬB69Y/`sήЁ]jPT:D TnlcH`-Pw*Q.#bDr]V;,p,_!@ԏAiJ.9z$>9'v.GO'T + !EZ-N~3! Qtt[wugxmeYuI-%#/]]T"$H״k%`IFrqURQu} !RʄgTT:ͩxvr*cLykѪcTxRyiI.K~6{U} 0^P̅e<J~WߟIlb̑O, +)G2~خ j`4 / pLFQg|2KkJ~9t24xoDU[)+Ia%8 dqc;?cU10$jjH|e YҒeNk%Gk!9'eL6.F2p1]N1ƏRIk#*٨v+(bp@C7Qy[IDOP(O4wTPH xMƼVx%\ #wkE_ 3~TmT +SGtP~KuUy^!l9Aʉ:Mj{#!yMڭȩIcS]|h@X1J?r]t|d2I1;1{Q#\%~]V  KVBLԕRwNP[ɑ?uNY>0bׯENm4OpVvs݇*7ˉSj &gaE$+5t<5@ &ᵭ!1[-ژWںj>E b8C\3$/C/cV.cH'ƐX#cHStcH }e=7_!Uf!5Lv虙<^zfcv6g7XBL$:cڏ/ŐE-ȗl뱄0- m + OzBɁ= lO`V?c'0yB ;5_ x ]䳨&ȼ =e22Oa)0A{'$|*CufE3iJY2yح,*ziGY25d)^ʒ_RP(yrPx=`8ڥ6 ,tWi&붕%JY^Qҏ)%Ky)K^'HYzz7Vy EUV 9ʒa=s,,8kOwcCF@ A%VOTCjH +K&] +T40W+"|?Tٿ# UܕdCHrxI vmԾq7D-E--C3<#E[*?N~qAcP4f`;Ua&&V=.^,sWC^P:`jLpTnfNSS D"[PڵO XyhQ0Dmr<6IltHlWjL%,_>AE`%2y),T oNLXd5xSmbncd,$,7I.B$60MvmF.rkJAĨ-EKV$FGbd +elS>LkV/GLSE5Ej|ENawvR'w<0j$+qfwR |=OEKŶzՏ+}?`YT@C#j' 妕{$R[ 0MaO@/ne8j}NZXA҅8<-Ivpu23<1F-fBK&V2uiG}PaGimJxúhn@UGSS+Kq,z.΀ j0<ʓC_ջEEHM=EE/cG: 5?ᘍ~ݳ#6 + +F3_@XGNS1E)'iw]C}b0iDZXLwƨj`N#c.*u帺 dXԥV³4Fhl F2g!v5*X2(SLңryeqǻZuy3%i+K` i٥VS0,4]]C$MrwQE= kcNBo4P7K%kBL|| +NùTjHY./c,L2Ffu~ӯvCbFv鵱Qn})+bKRB~R{h$҈ɣ~W>`hhE@n+ߕ#AS3}i\f +6Psx +:C>-Zݽ2~B32&u w}u&eO'4"-R;L]5Gg+9*^`xK)P- AO WgpCX *(sVrҎL}rvϾ ؑHcvSgwtG>Y7qeEOs.ηF_A<GslBA odA6J'!#gFbnƍ2tkP7w4 +`^qdTo.[F}^3v|wY}>"0.zc8`oy:z_5,01WϚ2@tG?(  + `Lƃjw;mms P;lH~w5>fIn<$y ?zNWMVE݂pJ h C<2YtȏʈRK1c +Z݌F/J;ONa!`FU,F.oV^LI|{_"EmޭU/T==>Yvs -c SU!UuzQ6IT^ډE4^>O~˘iI9W]=`.W>}\u7}.WR^ڽ(?ZU ۶(mFkX+Ҏޥ^ ;&Ϫ fr4?)QWC(Fh3U|;m b՗\H0"a|bXbT),^i^Fk*q ZQf@VKt]*@M!o6BFPozݥ-k[QjEaR]Ł5\<%oo&nCêײW?juG]uO;zˏ -L#C8a(4`l`b!Nxz2Ԭ,1Uz|kE lt_MeC1`T + eTWaO@D&{ xtzu좀^Z t%ZBlr^jށbL [L뙟OsdX|vD Pm\X*fottHϪUSznEh{VbY.fwqmt)~}Pyp6]p$/pĎ9O pkjkPU`QLa]Mykξ$a^N;bXo#.b~La{(k,g f.QVdmf&* }!;KO\5|LUR=F K2~,vp`ua=t`62zx-m6/fυRx5g89vN^)+F_ Y^ |+otiyVr.*Tv`K1)ԅ2'#0RKY]1zȜU=bHx7/51{,}'kzяTXkkT%f#;X Iw6௼ 0WҴ8{F):So,Fl sKF~Wg8% H'6AE眾5.v6(>h\.w}yHDZ5)P#K@?T&Zz}4UJs To=Ff?={a~^2*L!Th΁-bb,"qɁުk`?J + Lzۘ/#vEmNM3e$|_.<\5]o/bNwŏ"Fl,}_fU[nwëZfe'8"Qt_IWnceS`GɏJh`O=lU/)Z.t_;1߲{Q#UQM#&-E;SC/Mκ0USy1ر_ Ku +f|"ǧz(@γ +6yLYPX7y燜Y&MMv`yb)&clp~&0ThlW}ɥ`{`oȼ5x4"K BN_FX?*8ӻ!6:UuCu7Tc7N{C{h]'XƊ=oh/#=;/e*>_]kMVeyc ||yo~& UKi` /vw'bj=#ǟn9A:MF0KNT}|}D9g5tL_Fck3a.?[ʰsHV!)W +'O!I]u0l+^A>ԯN]u>f$ǯJ.,$<$uc^_-Um|aػ!V|ѢI!:'K8{3s˞ovHd:&Ş{c,$m2.Y,)t=6f~kcAk8KfU`yiMjj>WYvにƨN7.ru?z`4Åt,GTM L]!O ltr0ӢY(0T ]"l  3 H[P}GX$vlI|}ls}DkiKmqyŎ]DkE`$՚qcL޼Tԥ ejQ1Bڱ.HV݄ܿKg.\_4zuEfg7eM3ZA8ۻ#|X%(TQRq!>Ŋ)}ɊےޯE^2띀|*Ui\=lڵFؕi'0-z@ZjRٵ|J..kWJx,]p5n 8)uu 0T<لY'`VCh#@C2!FV^F;jJP7ߍ=iDD0_(%i(W~"[cQT"]4sAʋTu{<cO@7? J63˄Ǖ+琷+&kL,uk TZ1X)543+LJP.} Mַ) o#y),BRU($d+oXPh@vMfÓECIz-y.rw +]ܱ(|du&u,A] EŲ߰ /.Ǩ3>gjfMHAŮ;f~e=*?uC<\Pw ,Y;~!;~d!fs1ޱ;K MسgR=fM*6j~<>ʹMEfۆۆE^.s5;lṛ1ŚUUu~Ǯ>UQS lE8P;6L5I^G X>th!! `Ryc6P]3OZZyɨv߅mGhE8:Okޥ +>""y|)՛8\:}e븗ru10U`th>dfGUԴxkZw/).i=YUE j" (y*9n,ٳj腽N6o5D (X%f.fT)R 1XMf܆h90"ϻǼhXs9j!LUOg}8%4 Q(L"t0!0 +r\EQQgw怍-4Z'C`K1=r~eqj+:CuL`<+>àa_Q}Rᢁa;]RVH١ Rh:ZSA7pVr_0T8fJӬ^Ӻ*%[ֳ;G_jXz @yW f~MUf ].#%* }9>Bmjxe{2Ӈg2c @ YylmhDMTGk<ؗ0̰^-y˙iF_L=BKU`ƫfm5륷koXq,N !;FB= |G/W.mU5t0KX&*Q ںkU (#mɑS*5eҳ\fB_5z.fS7]UP0=Q;MuҌfmOҌZJzV~;x% WW ~P@/pNސagp3`|Y+L@M +Ҥ1}6ٜbLnYdiҾ 橉"ܑ#[O-Tܪ*SaUWgNȗ*D;.%cnduQfvYN!huq: _?ovJ! va1^)}J!C^){V[v+!ϲZ! +M5i n>^! B~!IG^B(v!r\.BhfhBF=B] Bxb.01BhRg",RB0i+$mnKeBBHKZwm!dM\/!ڛBNCiK:%A25~ -(7vPL֙/J4Zq/,+S07&eƽb);oB>Or>lb|͔|JKh„Pf=5v:v GW}H}Ai4St% ZZ\ur}4wkTp$PسZw̒?hJ{In.ዌ}9Z N/'QFV'Hbtބ9i B^īJ"tfX?Egg'Z57:a]ehM`-i xb-U"[Yy/bUHCd IA4JoD8.Q̠.nԧ*} M{3d`C7H֘BRvՔ"|.L6RE#ͦsZ2w fz]*}IYS8cQ,jR]!AK_t08)Ϩ{7|{7?6ܚi  ]sB6{;$ Ar/'@J ̇Ol($_\ 2ªjٛT8Pۈ=[*!(#*|c-[mkDgEfqF<ͻQk;FisR$x՗P8ע;E!&h=DBgSzحR6=LZ־~^|/@Af}!?BۇTU//#bL#uY%-# 1 uIcp\n(j{׸9e(1ɡ6Iޒ#,h,rs;WqעVJ6%ID5s:?hV*&68]Kbb{}zl;gd =-L$ݗ\N2+eTY(|OhSU~7#L%Vmz=TAH +L:mě;b0KetŠw齁aUI @j(,Suw7tIW&{">_0z(]`a^jWm $!H&6 'U=̩^γZ˫1ekGֿtŮ#vۤM\o+v`-ꀚ Q3Y<J\Ey(z(Lҟ~}*k_s~hB0k%>NL8Ď]%7v˞Z W-}G!EC*Q_\J$vX@` 䱀2 Hn Hh2^Qҭd=f$, 0 xM C=5N 165%MYo//E̥(XϞRA^h%+[v:H}xh_hbcJ.%)K[!HRKo8"Gm 6EЏ +ᶣo[nM|(o6C z5(|kn[t OCBr@tSآ/s}ͲNelr /Sb #_G$c_C.LtE~_t1nbSJK89ay GI3LivAϽu*S;fW~C["}ڤl| +6uCڲbp(3&ҡJ N +endstream endobj 439 0 obj <>stream +HײSuŒJ$g0 `#fnuUuVɹe9:h+Yv3jjxa[F }e50pi7u:ӛ} R>h̏f? ܆zR3PEn/\Ak.- [ ,R3|+cXy"x/'a{[wLڊ!dc3CW2"5MJFov@g-ULuEECLȪ:]TzWF +[@,T\kQiXBTB:X|(pAgGCMh-. cWRpfg*hgF:k·dw4bTݗfƅ{l ǝyW-2 fH80MU*}xX2\E!=a B(R^{<0O.M+۷X{ ,-JӞ3b0y%%Vܗ᳒IҼ/%HKɡOVm@)@kjBʬeBQ'KXғm~;Ӧ<2TG+l {=-"sG-s$&S@tFBOW֡](Sl)zJ7N/x &S_cAKF2ë-sNqzcpYjp0fR^2aѹ\i.>k Ԩ }u"p\Ke4NDq*c͉ȋ7e5,{`[O'U6gF@T)w/tiJ I;na&ݦsJt5^nD;aOD.K(pvBOФ>I`o|\|Ϯ |~50e]<[g3V;_z'Z+sR]apݮDq3m߾U #Nz532eQ$ On['odBzF:^]sìaջhJKgz7 :y9sҵd*I'_SZܺ|36gk*(2 #zt9F4gS%$V&3$r— f٠w9N{ QBdm_~V`niPTMUHPTSp;P H>3qKƹ7Y;<(gD|LAEZOD+S&A^DoR-Oh]7&UxvwI2=0Q3}IY3]SJeiB?'dbk]#,mD59 HVж^5DH(bk8xP_^@P]K*Y@eAQsΠ0ԽgAt5̦rTȥ +N) ZZ9Z |znL:% mX#_߼1_ߛ](%j] +s`UobdcI⹢02)Q$I[ZO=riy!O2H_ -߀N +]OG"ʾSONVOOtqtҏ3RxT +O imP8hD]'@%s7y2&^qri/RYмfTOI !ʯSoD;[ УzP%"UJ.YԗO#2`t6b*@2Pa?LJY[FKLh3W\lDRi^Ҡ2,3r7dTY<`n̏ -@$§sE!=0<@3\ I ?s6jM;3CKԷz.bV mo&lȞv>ir~0,)05fM;OEў0o +:$?]2G`˫7:p+*GZEkj~A3Sh;[q&1Qݑŭ]")<+F3MTVf"qlmc`i|k;/`#~8pI5 \~/'j&pnk~l\03=G^$p$zk ^;9obx?*v. /drobt4t-Op ;<hG:?'Y(Ֆ!6bFwy-n^w$,V2dqhgHL_J}93^[!q"WHé } >%W1cI-ݮ˭2x_H__M1 Icԕ]Ms\KOV3 \!;;̏jwM/novr};VGhgێXOHT[_3ۜb7̵bv!uq눿~DlSpW|b=ȿ^ m*sK#"Eٓ|UF{MUv_=n ņ{k+j[ ΞyCh +m=$XJyťe 6KlǞvCVbO~<_.v!HM< `tbmnr-rԵ4f5vU"szMU}ߡp]j &K/vu[{07c[Y.,K6E:] uDx`(|ˎ(.2;hRߠԧ AZ4O9p췲&m%:;ub9&G-2{j]KuV;Z;׌I4>R/iug!!8Y/\: +b +|moO:JtC1}Ϡ?}`zIxXpsߨe׍]o׎"+q7n8$/}~]y$)=c-on!/<1pn_peoaje,W:6oWs+hsxI.=?Jm+'$ eԌ 2 vY>{O5G(""$SYxJk#)iŐh˻fΑgVNd0peԯTS<I|C5˿E}smr6(\$+#S>dzH %@?"C7eytS8=ߛſ|TNjc /j}]Mv[7ckS\&MKNMF (E-$V. >-$A*4OXσ"m#.ptzR{W RNBchC- Mj&e+{z+ !T؎L4$YIohI鸓ʯ(.kX:_a ˭М+=>kݟ;;:̹>3Z!؞e*-64Gj?a`.ɴm4f]{V|fQ{+nIT%-qiMVEz%ONh|6Oܮ>z>f1bb0[Mo+{E{ϕzf_gB{oRӍP,,3k3=QC(BIrY2EVjxp D8$Xb96 =_T' ryi|z%~V`5ZP7Lǻ#N33#,.̧uG:^tP7)kf89L .=Yo<Ս ?rLO魷{SMNRݞmw>&`M ,nfoNrzwګsE}3WAcJUiie};;X -(年* k )#(`E7>`Æa&/&ͮ;smn6vzԙeI +7> rdJE +m}*r"zFဦ'|x |'eVnM\_;ο𚜳>a߻y+60xȖS aiehiU_ۯRg]/7J1q%4Lu-wpq^/VGq/Cٰ%3m3 2կ.nH_ze6taXR5RQRڨJO{4,z"٭oŏdSJ΀WˆTTe'fzWs#wD6r!?YXLtLAEoT\˦פ:-hA ٓb>!'%ja"X˛iQ0ξS~,=f6n3VfMORŞ1M}S-I{tzә{+B̬іn^BKKEbSB!cy &_k,#$Z'0X= y:+Ut^S]E@n##M%p9ٟw+M~㮮(ӧk,"#&5OdS{P6<'`!0au$k}Hi7©xzSFSu{:7krp=Mŝ/a,k)A$ &[#w i!x) aR/ I Ӧ]B$H@* + $"$w 4Y H]LQ-aLQQ{gO¾ELWo\O |+I $@ydBZ\8Y)rQQ\sͼjj|oF*!5vr r !]WɁN/@g@53Iw.<} ŏf +jttO&= +z<ڿsCΰ7^wqzJʲ?uk#B'äDӤ~Q-35l'S戺y&h5Wv{H[l3I2?OZ>}}NI8)U,uqUsib뺗X9\ 2OAw}>CrDw8Xûc;q2^~uots=YWuP;$PLڡ?QM#h՛q2Q߿/!XG.sm2 lx8=0L:uto{NV]0.;dm~H%u![ӛDCم2~Ds9锺}vGEsN3#_ZawDgFw%T +9b㩅bNMaSmK*zϝ)h, ])[ PXe x-J'хW5܊j$3nA|jMOOJ(T-/W[^mů`b"%Y}n +Eɽ;$VM<O* HVW`!fÕ%>.+?Zk4PszGg!BBMCO.PsT:${ofY_ek2:ܙ<|Ó"I< 4!#vQFφN_hmd߿1ؘv0 + 1lh)+Vٻiu_&ȷ%aGymX?{{y m;"Eqy,(A[T@ 9+suK]zLT["zKI/5ەvh)1Ёa1@;SϝqCz*?8MzcػӲ:"heRZԈaMM<<j|^TX +&mUUs|CUs(%n n#ڔpQa?g-N'׈z58ce! +oZ}ͼ'}dѾkduhB.ϥV^]B5?OXz8T/~&Un!"ߚX±KuY|8s.arSW0]]c{n >CvÌeJ!>'!UoE jk5+6Vz+WQC:OF +nvK"8v⠦6 ͘lw_]Sjf7 ?zG=]v4ӥuGɓ91u5}wZ;&4] u}X*/U-Mo|u.yk%u(zϧ8PPH#J SɈ}#.*P1χrY#r}h #`uO +t7ǡ]Jdӽajm B +uXq )d$J]LR%յ[L}7Gm!#%v9L'ܾ6SW $X?FX Jk}\ptG_@ տu%Z-Ty)Hmz J]/AJx{^H!Ma{K:f%U}^:\ޭm95\%]XLv[ƌ_V3`?aOuOM!BR(ܓ;js+ne>$j(aի +Ȅk"\ZNt qޏ$dȹҨqO9YT2c&g^@F6.}D֪SKӦu@d'Ke⭙ 2ЌnWv㸓,آ˜QQA +u'Ⱥ%?w\n\uY0N~(Vf&"}m&K]I/9]~RR,V`WC'nO$xV ~Yao>Ni.r/Lf?Gw”Yy#vD"BKX9d~%cILX+B'RW1}_ +!f?Vr!Y>oX-[X0Ij +>qnfx7/@W +ͼL<' ҬC#jZe152W$I,sO`fabE ^ArDgA>p G/)Qcv/Ip=\A=j +WLj3Q W{Ks!#Km$GEmń`QD9fxYt0cHd,}fS2VX ϕ1fo6r>%VaY|6Zam'(dţ >vknRv*~-./El;;▙7'U1n5d /2/5|؄%';V;V;),(e=Ԁ C o1 KYĽ i2\rKeuq4 ՊP*jV).V*{翭wyZ-ƛ|-2~1zKO7 (S\#w2ԬD f"Qڶp [w(ojh@a +8F[6BJ!U˓ +SqsHJ?zݔ|edzɉzc/Vv&ג2ZÆetwEGH4h~9ڮ@hz@f}6z2u{?+Zq*]4rʜ_b?9~<ۇ.d.Ql'O~~)#`)0%B$.0a>gfq"z G_BD+`tÜVrl W/-ww-/=gE/3-yv1́q9DVɭ|`STG6eV 8:8"W4l8iy{7z*-BjtÔjw~XGyPo6p G=\'%a &/~N#"CF&_շl8",*%[Z &+`<4C|{-ӟ>O <~@@3 lX\DXvAYĊU78Q n qMx}n!kƳWݠ.7́O7SA{̢@VP7rm'w: A>yc0\zB~%10LJc߁klb}IR#OH@ +U+@^U _-PܰPhޠ`s9(DA~'(yUl!OGY+w?1/#֝{7oCK^pDl/y]nٓ+;c5}FGڅEU9g`%en/Ǯ4FDemU\JV8]1& %5 A4{S+*YS9~ۭnI{k~ZC_%b"zJ8TWByW+^$T]55Z[ytz趫> ;k;nH6_fvc ) +`%]Ҕ Ţo+̭qW/Jս:~ {g^]2np;[/#!m-5;*gǂʬB]+$PZFC4z}hBiyk0ݟ]5sM]^Ŧ=j'L@`MB?[4/lw=O}{fH.5e-_ Y%'H(9|{o?j˹7*4{S2ĵ'u=0֝P~ʯUdOX-t0GH!N/3rgfeC}4=T/FALaUa@pdץ55lCqbQ/K݄>Vg1)L&qiq~r$+cXB +A3(}NGܨ`tqO4 nHDJOG*\q%uǤEbȂ!av숿%?->݉у#fڇ!K$ޙd ej#!Qg/S{_$Z؋Wj +7xeFCܼMSEѼl+.&:t}A9wW:{j +j=rBG*YyJͺ{r).Zx*۹-_;f$ +g!vN|/o~y\mwW0Y4:f.1­grmaEseϭG ?wH%"<w4}ߒV~}/K].q!q2F<z̚Gp·2CIs-!^qJ5,m$I:f@X2b_ +*L[ra'sѫ{s+93oJPĊ +vEl/{炁n,V9VlٸuƒCyV왚soe=6nl<$&^[{~ {k̓>֧sC z(-EZ`C/zꗵ4QK-k|[o)}ffޭܾOQr?qU'*C&ŪhYq} 2F?5HC7R㝺jj|(15iX5+_c2ȣ$p$SAO LJoXmU=nw`6<6NP7NGf5\BJ?̲.92Y?=M=3cU`Pn;A.+Z/QLIrӒ#y*R^v6,BMx5E?-PsZ*cJY`f<(mkحڊce{40ޡ|[9dT0J_1~vTH"͝҂5dJcZZ8R|-|7wmEqfNh{ i?M5y#*l̯fFT\\enXD́LܫZ#at87% P.E; guN#U>o)-\\4us\׌)Y f81aPg2!e.,m#ua2y؎KV>w* _P5TPCm#~`wN>x/Y랤ܙuo'>͌a*k%!]"Q| :(#D-qUe6eS*̳G:N97yB36LVUx:,аuI~ज़YguU֘?sWt YY9N=P⡱T a8nBWA0%Lfl6 2DblťrŚf[25 Vn9O__RoO&sv:jTͲЬz c2 ̙0 پ8ES1}h]51yEn>q"xgr*-Ѣ-4=V'(V3 |6~u#5yww[M˕fl-\mߗ !$;H@X ZoVXoF35$\AJtRWY7PnXR>7:u*NUeݜ;1`K/ q|/fAy EhCANH/B#A 'qd_dC>pm?e^?31 Zz 8+рsJ{޲ +{rOY$ַcTG(-Ek2/a[װs4<I']x=;VqǽSo/Uf®\Y. j o?"dwZ?{%k{^],[՘H٩'*X Uߑ}"=uFVU]]?NvC% [Am!|TlV1Tjۚg߫d1\'.iW-*^gX1 + +rzdlbJgxVAFo+0fp?F.a +%!*Hí2V1Y]ڧkULh-"o.},'bߕɽq +;^:j7ʥ^VE>mlfkSUDJuZ5v[%۠N_.Ya kT53TU[̢yz㼢o4Øj*U:438&d=p57xF>Z=y swQW񯲎GXm7| ўtQT)oCUABFuH\s^puemEt2ɺ1ՖT!<Fy54]MY ֌GOWSKU.Xrm։|o + ,Ȟ2{_1 -)ϯLP;RU砥v)r? Ns_h 5]heNa +eapɔ-)|'5kD1hQM SSY6dcs>=L\4yK,|<[S^$N36ߩlE +>,lBo9/riI/y [_T?b^oI@f|l09 -oq?md lK?3e,ԓ[F1G2u4>~kRQIk_ݦ9w=+"u!Ùx6 +rs*c˯k]o2/l>c:53%QԾ"n(֗!~g`LfVN9'CgDq@=(N7LjTގi ykGNF ?Fog[YA|v7=|^Gc(2z+@^e!oտ7cNGGBk`G1d\線;FAM$ealjV!񖿔Fc5Jű ĖP\3mCZU8.-e ? HF $HRQM J]M V͉lo(hY>d2y ?RSS11k%% F6P rn#IoދڹXN-2D&32zPs^.ܽoxV$WӲPv+NO -'/SHu`K % X9 %@ 2l{dm/6Md4u񙳾w$+Bs3#Z- Ӂ>sΎ0Ҿs[@vre6d +#k+KRAb01* ]Ea"FkAk> y W8#B"g O%,u|lqO;͛fbQ*ğîc]}z6>_-d*]mJļ='ͅ^".[AZmK"ymj8&nvoMzT2`=ztd=~Wb^G=] *3۵uG^3my"S_O%]H 9yDٴgfϗR/9%ggmvEn~l6=CM<3I`s[]_ I~@1<þgVVjPA'̖ebi$l 7|<+Ls?fͮ|UaI% f؆L+7r/{NZTkT1+b{Km<b?vfa#a󩂾P '-LcAЊ GYe72v6zevk0G|4_N;HPůPL.T#%TߚٖT/HH-Uf^RJET>Ӂ Mƹv"5cP79>j4PFl4 _9o7uq[#-Da {T;X} [ޅǦ4Zu:}Rt'¬<8zMYwϺ6de]̳Fb~}z8wS^Hۼ`I@ʪ-ֵ]c` P+ʹx?nt@s .+ c:wLbk/_JPIK#CgePT {1gj3Mn70g]pMy I$]I818o+9Ni8\UXOw \|@d +~RLLT(,)Ⱦk㾢"ؾ=7̟"s2%3,|[]8|I]rl~gb1OC] @PA&|ԑ`y}v<@&} 2 Qa2RUW?EXku܏e.M::XR,Om X4mD5sW˙֤S@M1E@7>PfP{"8P+!4 !e_@-9԰N]flUԦ UGtnA%03c1}Hy43V(@N h ]L&&Н$@O Lh,u..QyZQf@66Ծb^*romľ/?ǚ  xWQN*)&|Ui#`F00gd0:fati2*N3jIлp9l(u 㧔bjI0IǓj{&&]Fٴ[2U9s`lo:{ElDΑ{,\؟y X_EG`:44VKWX/ıpCzMY&]`!{jl"k3\ya:oH_?pkP"@v|) rS7A^{ /Py5]pNWQ ğNH;d@\yy$GAXpPY\YԕA7[G2^Lՠ]PDqJA9P]?2%1wR;Ơ PZjiZ@!rZqƳSκxuWskM/^r1$4:k0 +P domg4H'6=돊~'5/>!`eScc?Hn{@BKVLdWP㴝^zqyh ;#)4&>fVg[--ν*-<•֛>*ic[ ˅љs}k++ V\Ve|]>z\-s8A1c -"#vC{3*Ή=#MY\ctmKݷPƶ1'ͯ`a"`71\Sutl]z51auׯ'kv}6qe 2o=|ڎʅWf̢VmI;f f<2qdˈkf(~&tjUTͬS+o?Y<[X?$ׂJ5"Q%g Ê(EQ).zs̸Ua6ʆ^,kK%ڗ5oQa`ݕطz*\:{ Nr9QgBq<0JZN}l vZ{*, p[ry)'~:a>֒),|K^6q j_T:adL98+͜6ZرZۭe|1x-f:&ˈ͎}@K/w[†>lvR% %I)qWzs.}O5ԍ$wo>m;=uh~V~q`JfUi˜ &R }PLX$#xhکi,0j=T֕xÕnF_"৪:o[sZrS, oF9ޫ8|42Aﳼwh/ +l4CuVcۀLS.f4UdXV7.2H9%ttiEDK;#۷-fк G" NtQq5ݣ/ܲC-wB}֏":_~²p~b:GdѢSȖ/|֙q:@f&(h( + i_}OͭL-jUXѲZn ]:DusR{%^D:N 4HEB]*ಿ-=p;fdzEbL<fWx480Z݆n_Ոn:eY#~s+*!IViY$Q2pS?pE˄ ?]nf_.E,k_k*Lþд\vT` +CLBFBM!Ef1!Vr9cZzN=cחq}V13K;Hm DdU҂{Kt)ο9c>~ #Ήi?O_h  dPr X2Ct$Tq_y/pX/΀Mvs8>NR"A{mN FqyFq"+\X}zǃ!I5S꾥9Wp*[i{8pKxs 5 OSIms1o0;XF] tMMV ]e6 jWSAǥxU?D];>Do!S{Hs8\͇yүMmh!z~g$ }nei=ӡt;Y_ޭmڀ,05lANRx<n FQTaH+3Gx 憉sK1cDQn3-l%Ftj)4R9@:z$S`81TrC,Yw:߲d +(hUuV:7M;$d?4~aDr9usӬk깻Ul c66A9m\Z2,Q35l%u]:畭}5iK8FVY -z?U wz#L/gO4ɼ{K} >/S.`^{8<6k`07-. z\}ykiʅ&DgRϚ&qÌ-.U~4NTP|5*z:Hj­>OO:͙HxeȕO&K8c.fz'kY2f[]Q)&}gup$_e%gj}VqJJshVTiCg(-fxu"<TVF_=0<< G3xV0~0$e +3Q/H.f5^7<1ms1-grC0 Vc6;_!rncޜgUOvF:Az= <0&Pʧb#{oz|HjˆH?I`J};{j &zli3w7Vq%U΃uq`au@Hl"`>/ ,iI "As BG OD? ã,o6P^GX?MDhCiQ ŶNDvA{;fƀ26T\Jd3IA*2SIAwxufnSYsz;~,dcJ`S/]z@Т Rqm֨ Afg rPyrIK :f1ymnANxt [ w hpaF~-_qT)\po1'>& 5+Ze +H@)l[Ÿj۠VsP] jqƂZژWT%Yq]A{,k+])6)rWH覔RJ7hg3y9TJWffsj2Z":;3v_T>>>r}f {5M`ă=h*I[$qXCLt%ݗ$2p8 N1 x^8G291| ;dRwΡ s5;&@3_Jc͊:D'L #8|\% +ɨ ~~K <FM!=a6 +D~U?s* ߻$HW'.MIzտV]_To[mXt ī3~'7wp@C6t{i@%w>j[ {vB,-?,ւn=L*Px}_!uA02ZjM.I8LϏErs˜-Bl\,l횶 C-ްi^kt2"|Ybb.п/7_;<ԓ]K$n#[Г_QRakP[**?z^zڊkY+9!gz/pc|3OqN5'pPp|$x~24~% N~%|nn|Wd7]sBXpVJ { B<|QΩ$h..}ZY}y+v&Blb6L*Ve2ћhy&eيgB_<,n<.BRMW U#*4aQ iLٷ:4',K=٬&)ϕW\q:c~0#VYσL **deDpЁ9J]n'^a"J[ኗm- O׷UKгk쮸|VɾR?G\RBdh_IbJ𩆋D$!!I͂ ԑ 7l&k;Ś%k/8aY&OULJ~^Z+,\IΗmb"r>Bh^BvLhz}:Wn_u"aIXbbvl2VՏtv.@o$쐆Ӓ?G'cgx$]ڎ|Zy,2B x52xL9,[.mXu!o>z:ӌ4{!<4XdWF )Z^9Z~#z%Z2^~R>Sq!/Ƥ #wHta"&d#vƄ%>=kd縻emHQ5w`mbR%ڿjfn5U[luFC8+d<҆TIT"{McF 2;rC?Pa:G݊,Ii8߳٥ߩeF*¹/9q]~̀Ut)?Tʵ\6W$!ͅc _ +6pzqzg1XV13v:TYPۄiM̺^whPׇ-uV,iEr RX6TqvM܊uANZy%76wbZdg_e*r10hϛnh$jsլB $ֶi'WlݰZQ5NAhc,vG+!2DGG\ :=K$aMZl7a޿?{*hgDDHT(1h|%`'y(y0d?T1ZVZJZG^xĤaKjׅbj}UڥbK_¡ +>Vq.Xvm7M)~w1 %oM/eA_+exoçHO֜]Q_ N7AosAֆGqp~CH_A\ ,%R79@stY5zc tע=##~rr~FBKM B"3yzr`}2UT]ee؞Nx 4;emEd/y_ڧ/%/,P ª 4k :Gx9!@?r"@![e#QlΡƻGYp_tZ7P*%lH_^zHF6F3判Ɋn#%7 < +7  >-,'1BpfEKGlWp.TA^M(ĸiG+Kxj!b$T@d9>9@>CZVʢ,JŃ ń=>u(b7a,M8{e/,qaW ;]CG*fSZX{堘#'yՅvLqǡJ?oLV.{ fx%24x$j)OL@V j4ξӐ!>Y/$r6lIO Ո2)6J=yq[?=P)/ND7ɻ_{LIs{Lp +ɠyȰW +k@>P ~Z>2j¥Ozi6j +LY?a}?_ KZSC>kT3-ʾ7x/.b1)%k< 9} +9T-h5w 1@*JVfUCzπvk3O:B@cҧDLwvBA@m|eV\~+Vhht)D[cnWMl]2?Z@n&1#OLVi- l; *eW`iXzUib}lEʀ'UFkbeJ=ތ>*bߘ_?;=fMtx + =y[kj=p ^%Ax:a{׉pA ܅][ +^ђOo&7ŴR;?I6 ڼM;Y=4}L2jBc_ o%_qTRꩅy}_O7[x܎\Ε#s#uL/̸k +W 6.u/Wƽ2q :#d_"3wJ{iH99XhD, a=[TIdWh !2}?Зa4Lto>ZaVL$\skVwh;ʿ +bӆ6aPlFڟd\Y _,(+J}ØM6;R~=rm&kLg¸s]✣|KxS}6VxVgA$w5ʄ̅ꡦQmp?~!Z'V*4̇tGfq>(պ2eJʁF*@˰6*z(F"ph`PTUW:'C\LX?- 1= i/4膾lt1K|p9&R?TOH)?F|⳸(9/m&$3?]ytS>lǼN31AT2Tq2)^w)[/JWGױ6jW)2jZo1(\K*&Ĝ%؎ϧ3'MC /MdM35ߞoĮ_\E&BX#=K}[SHߌ]FN'ByfB^p9zI=Ok|:<\keOtFT%,vL~<L~Ҧa<nerhœmoTF`xPi Srqh8OFN|Mz\69uS%{I~l&߱9Z^IMz/Z/Jd=YZ%./:(b,Dh.(^3I_w ?0%mzgcC~DדL Jd)p03>t4զBĔ(g9)Ok{[ ߶!n=a-GZV]:{bLq!N\kp{ZϮxX0dX@(1wZD}AY|\a~&Je t3 xhmPyuV +[SWW|p*Ks=U)rF(A{Za/U~n;=z%m~pbXd=|{,U݃,@c[٣@_Gp´<}{@UR*^cmڎ2CY|2I%&Í-8_j9T-#oZ +bCo긅pa.ѝ6;(SмVX(Rmi{Aom kp1D(|,V:4-V'k?]M#+10V#a&RV\q;b*~t.`Vv@+2&D`F /E8xO+ ,d% 5çH vBӾ&:Wj3Dʩ2@$g&-<-%$0eUap;GǗNG4 R,mTpYNڰlpsк*PӠ-[p.{94їB5FgˬOڢԻ &ݼJf*mh!QfIQ7]|-08\D-ωٕm1 +!xA3VkNRf`H?]vI9n0ĽOGGO(Z;tl}:>{/w/];BAvocvҔSWi,O+e-"y3C xpdo~IvD]J>v{Eď"OL~| kSnS L3%aD0Zkd˺K~-BtsИWrR=n&t 雖O\-CG+*(N@ yGpOL f2 #Ū̎qs%n1dṢz&)u}ig~#'9VzV;)mĚ5Qo\-7HW +1s=p&@kwz}C s4 'F}q^O93\ptpg=oNY?ÅW<Ōku5Xq_qi]j>E\G 0zIBF6Ȍ Fe#P +y@{iSfbPoi̦^*HEoIqqTĩ[V%8R?1d$A=r@K&A6l +"t@'c bQ(4cJ R!1!_F<\?]k,_R=%PJ~K{fmjeXuJxX6Al-lWdħSI 5%HYG0<d#U,?\@2j"HV¸iOnצ1f%Kۅيl=G`F]ޠT%@Ol+i f)h8 d}-WEr(rg( 1ȅ ;q96;hoPʝ>(3r*9fjxT,Po~ ԃ<u AKS+/|4A%f_ύQ/]rD;h}ܒ>F2p*jYK{ nvGv-)dAYJZEAAT\p EQl#&f&ʏ<:ƋMOcŚxFl?6&|<0Ϡ|̝z*{7x jw:x?">;*u&-`4ϤWcxQW|{E<F +E@$vTJjW@|419@8Q䘸%{rN' qG +4/ ^$2/`HPl8`?Cu 諥GesEl +0nCZ,~ ?!TK7U@˃G;Iѽxőq~L$_Als-(st6ՏMfxZ ^:XFf8~J2K0xo>6/`:/o#L7W؏A)İ!tw7xU:T+q@BoVY]]mVNE$g3!&AbD矼>3c[OC821zأF!KxY3(9{gዳ~[oy%^`.*$ٰEOO s2S(qip82FȪ?' K=nـ}'7o#Ӂ\[MJM?].t9M/U Rӄ}PѩTID~/fϯ=Y1[#莆Nݞ@!iXkn#^-~P⊥`ʌlY1 Yl@6=_1n>6 뇫& эs:5"Χ^. uIPw|nOٞ%hʪWJێ>O\Kl{r8٦]/^".n᰿=N^뵷5-T[M_s _+񳤻+O?6a{Ⳡ[oö>6h7bDHglC1wLu=mjY`mM(]u-:ܦyϑO!98I6LF߿ 9" ݤsa[.sot=v)вEsh~[4hڻ2|P:(tGeEk* +r=ePwWxF$RFbFk.VTZ-7\-l\zũgyAdK`:5㰯RYTҌ]فhRWWKq,GZ $lǍubHAr&m$n=Dkkj"Ӻ T?^».CNa;g:P'k\:6`?v9"v|}recla=U)H (1 [P ;" Ez+ ~S2WȶXTPhοȊ.ؑSHwXF7a !m3:LT^v݄Cssߚ5>ғIRʑ}s:*[r˓E!pEAeUy#?-t5ZdNZSFy-]q +:ZDPzQ4( !{Dd@#Hcs߽w [(FԨƳ̛5"̢/c֔d%K; x)Rf9(w%"KdqϟjuOO +Fw"ܴF`M& +n2n[[&umVjȓ܆+ZPz" +Ad$Lf2 3~7NqlxDW]6S&:,%=xe__`Q֓%-/|m,ݕWoi]9Fyn..mcU뭚W-v:,|GMaz2ڈ7#i=BJz-v2':y}`'̈48 B4S).4l4~򁽙;;y:1)s:b:[x5K EnS{آENȿ+o]rUMJebBpͮBgI3N^d\R,} 7\;d5>MSM]t5$(t}4݈g։\Q*~>T8mq"O.tHMqqD kܦ`KJ=/ Wi/ٻgrָ[fYGn\h7zH2jd}IUI.{夞`R5'ov ړM *McYXV;R~]R)DvCsޱg{Ť?}NǗ%U\dH^R>o5k70ݶ~I8#,d7,X"2 +{ښv_kbj)!è6S亼Ďo)"XN*S|KT=tgDǨ-i;(T6ܬRL?0tX4dlJn<z z)G:]f΢`ܽ=N/ZTj ǙpJ>7^1;03o+efIջ)<anPO$) T C,Nje~ po<8{<b&"< F%5#Z *v͔Og$`5QY\؝3A '%Xރ x(gשּ%p߼:64}<s~W;I>%؂W$PPvCu"*sk>؉?+W@_ЙIYs,mY6qDЎX6tT7tP\Y +v6ZoI8ǩEIq#C>Q__ 7@ t~c#vK$  #r0W֙ฅ,w` +/ӂ020X#AF jKghZU:N>ܛH$?t-Mabec>xd"- lT8ˮpL‣#H|I{U6n_AXVƋ)bU2גY$USΌ!A$|濾n_''c?cUy/bDTnPw36fe |Dq-f*<>%WKQCVtx5.PHǽmWZF~k, +ΪO-7 v@HaȋK[7##@2]Z:<@D-Dbk)ՑY؇D2H.C./.)7M6T 0A8؄X _xuאg!hp)}tPhq1)hHT“FbY b.l'Dk$6E#V m[j}}ݾZ_̼xH;HßITs yStAքȳjҊtA/F1(% b@zCz]{\Ẉw'M2s*vroUĎ&W=^.e. 7LBMiH/6DPyeȘr2rAVwb^zYl 2SzI*ѣwdOۙ+I/QUv,o~ Z(-C}}7z5c鈍D?gg$MG3]/M]1~ymv\xhH#G/K^~|3{a1v LcȬ0&d"1s(9Z'zCvrwDBi<΄gGi kS Xgl'  IǦ+w\fFOG/3vj213&@an9c&5rorz^ʯc~3⇣Vˠ}sS;uܟbG};.gu{LX(ډҷm=۾Zc; 4k:?[f|xFC拃f2MܷYSdt{ٹvG}{n ,9L۾ӮQQhhCn5S"w %܃|KM;3)]Uំl?Pinޮ;/qNbFL~wb>ŶkIJF\юXMQlҳDyS^!'5<47^Vh>ku2\^ A1(bB1ab wvyiAyЄNisvIWHI+ ->ys\6 +|ͧ08yt=Uyuҥ2VQxKֶkx&Mݝ|m sRd?y7djcj2JK2jJmZ~SOZ +)fJfʻFxM  +{-"ɍ0dզEpSrZv[m$X%[v% +`|{:t6uʮ [U +d31ktzVݷ긖R҂ޡ +25A2զp URTM[V +˻c +8m)>1ɳeI%Wi.ΆXT +y n%Cg$_%ou;pL'%/4PaY!? "UTEƱ& )aґd"8U2Bvn]\yxR,p֓8tNY9չfw7A2OW'X!ŭBZ$uw +X +$M>IƝ!ԖA :J|0*)EYkVBjK3saon U::-] C;=D;}L;\ݖo9#݌P diD:Y Y$o|~gC>gw/S7w +?AXK_b,HƮX*>+tjcv\+J`Ȉ&eq(iuQN_Tm괶ז.` Zʫº$Hf>n{^ ޜ ƂΖ9c:Z'|SnQ@H2WЀLy6[D.l `4= 7JG(jZ ,ּѓ]Pm%M9Κ!W:, `ӿlGʣnŔS/5rz3SV$&&(AvqDخl8Xi^*EWک2(5(ZEVpfPk\= [s}_|u^8BȹNI뒕BcT~QY 19.Ƽ*HCXN-DiGEl NWW&YxWF0};h5$cSNw)VkA\ԁ_Wᇄ1tr;n>sןpYt]Lg7E-Hp9;l`:Ǭ#Yڐ#A &C +;ֈ\{D|RvG^D_3@I`S~ >MS|_ӊq6İ`Pb+< wDO7+8𫐩:R.Xj)قԃ]9ؽ!x_!؇~; GJ Z`&WmCR~D DKƅ3KB=$&r `e8Op9vK$ PFR8|p*2&.LA[( d~ B(C#!TnCp.!gES1]^۩+K3& aD9666c2_KRUw6XɲWQb'tax1nH9<23ɍCk]AM6Ygߥ +ݷw>5UL;c#30vq&d%)50m:IP}50Sv@0rAwgu|0FV2*9'Q]ys-Ņm{;#% VҴ,LE~>bLJdFʰ)YO{B6*rALc3R%`O`nE +4kFXD8Xj[sj`gεaӜq+dh+_t [FѠr<p(mǫ̣6:}6 +5x'Q`u6QN+~&A# vZJ. 6q1~"Jpq% TV] ^4+9=eĴ1}Ψ_;oP_mH6y[lTp#(,#-yHHd8ALtm,fODu!vQz *Pĥb.ip;dⱠC /뿡EF: 57o +GY/x`/+<]T9."wY +Ns;h7Ɩ=%cRZ9hl2>ڒ'SPԽb~\k:%A >܁_^~_s5e(\lT`惿I}QLaaL+-~*6}G*Rh Fo9wG|M ^08~#?ȓlν5yZ'"e$;Z'O v¿t ChJ@p3>J UtsI*kxzeNZŖ3 !2]<:w^ 6}\/cL%(ʒ` %!?b@KIkRsV]I; ڋ2u쟄rHfr'miX妮6Fۿ3 +k'N %5RDmۥYU~P{4 { xANn\߅tw;nn44t JĢ6rC#vf~ySW,e^܁۶1(d CC(73 ʤ@Hg_xo(@!ϒf9FkmIpiNSگ!PN2=^:SހHPbL2\$\N:B&P#h ݝC /hTyWxuτn`Ϙa:lb+Ӵ_ϲb_nϔ1GV%^& %羿La 5hk]ȶzws5F< c]aD ƂNK0!{o49$w35򆱭pNO?+Fy{, / [v*]AsM4,a03a-4![#պ̺;Uޞe,6`F<̭3+dAf'ܿ_^b|Lf+af{1eTam"{#ձ<)_ȡX<98 Vysr9ߴUz ~k_6h/.E. +Ls(`*s{zUݯե~8,\.rpLY- +,Y.i3.Bv SPoNJ$B؞egK#o dZ"T/T_Sx"DdbWQ7tي׹K0&.fXǧ +S ڕixkh ]x^.OkjD 8Z K?F> VJw7.3Z|\,&ڨj3xbޫX:HL[LRj}˭ad#Kɉ(NmRlh8D5:M1؎gAj/sw"Z^'O/}{68 g2{8׏+ģ +8 hܯ1CV + b0[[.l d^") SGMU:jaT5]CS:%b]FɄ4$-26dQu]GTelECC&şј +,2zAY*cՓٝo:VFƭ~rm6갮}»PGd.GC*hfF9,OD1 |omS7$ȼW֗ȿ+!/fp Yu_E&pRL='*mYa|JM3JC&D͖IZymv+8Goӥ'jCqm f03lwA*]r#&t*<)Œhy>=l4;Ԇ$ܵ|$qt9}r dK, +=& hѧ[a[s)?f.dձ CHOvhL屰!',US >^O@V #ƜlFn#k5tQ[y&2`)11Q\h,"O)3Z}d\~|g|xmu`|m?/#2/?yq|w<ivȼψH̋W Dݿ2/#)@j}>w`zm<6VV5c9>Iv5jYV=?s`֭8bq3\Od#S*0XUXwsJ*A7>rʗz1$C  d +vMs MtꄇmdԻ + +S?Yu +/2VRJB[ҎFu ia +@2^lT}:gPa*)mˬnMlYh$y=OTHa +00aĞS̿7r:ohJ ^#ϻ-D/YeLDkHds:W+͇c8 +K ŒV(Ͱ@]ͧCkF3=n7oba:.m]!lM^M.:[h2ԢjXuzpiwƅj!=WQ!Q^-r7/1;7eOؚ7M7c:ڃ坟Xj%Q|ʹF__Z\-`@#}!5Q ю'\%ljVrdx +m[Oa1Ue'NE`Y +7U@FQ+ULK73ʽ'Uʈ"c{wHުdXF e@I_58ہLU(,F'(IcDvݹII?{-j9Sf+m]ٴ*iD:4P^يInGZLUf\!~$ <\0 j^Mo96.pWV:.ÊsլIsFzLs(֤rf9ᱴwWlDY2lB;V&؆7fYG궖ko(2k)kszLd6#wE/nM5q6_`DžDꚹb~k'6OKj*+H\KњX=f6 N!4AT,V ~Dv5?Eo yw 142F+iORa.G8xȫ 9sKgu}';2a]ѱ +Eּ$$5xwt+VӘh`/^ +w]<`!ayX׽#v +!t#F-:4M2yF-c,Wˡ!p^,`˗Msoi3"2ٸzόifȰcuuϼ!$dL^+*$g38voQϐwv!38koQ\{$vVE!!ȇ'ZrXwY;֦="V5mBҬ VAV(BĻ d&C潹PѧbQߖ2吙xPr)42xw+oƩYz1k$huvr{~Ce w {q90Vt!=TN( L r/حE3tϼ`ۤ5Z +endstream endobj 440 0 obj <>stream +HtW,9({Dc_$LeOhd5/'y ۈX)w7IK&v lǝ{WbvM)goZǁdӯM:Z. +M ]lEGKw>5O‰9P|6U7s)$,l ~UkϮ (m7\3]d~+MgDTieaR?诿k,if]/, ImFN*HڥB&e]_؄c),< ՝\32FmzZahpˎg>5iDN͑@sJцb_QȈ% + 7iEc4ꔋق ^,]lD^OޓpۺV% y96ʭ<8*<5`Yh{aW^Pȍiu]P_(`&VC}f$4V*ZVArBn^$k>HEƃ)XzCYhջˆ+VsPg^?GT3_X,;0ƹk| +[ÎT",`z!- jWmb:\rHm QI jy,p̬]p"AuX$d5Y9ʳ2Z {.t.aZg/GiE&{[m nYL_Uӛs&qfn"Y7m M`;r9p7fJd(DEjXI~ns ŏbFWc׳#?Mj:ӳuU{״10GfܴՒqJDk9HU45Gj+b$M{R 9F{&ha}C9Q \2Uʆ3 жGѺe7:u\KE{3Ffꀊ}gɒrMȷ5'5"+4L[]Fq[2*lzHh +, cgڬ~Y!:Z0'+ #~~8<SXp41= [{??W×Hwk?9XeS-u..ȗHJ&0 Pz-3`5|9 +Or6-R*ɀx<}^6 +@wk`v@%8,T|41Gr4J{,\~ɩkЭ݋VGGc͓֞X$rB wيJ,:%C9D-hv`G##%Цw X}ǸI`<[X69˗# ^[MQj:$È1E^h|%JFO%3,£k "(0Ƶ6ͪ.wP޳p5s+ 6F> rOE }lsX­d5wX*"\QnjViZ.G=P5U}1[Rl5Y_g{rVd8ᡑƫFn/"z93дU˷ǡ Ucl1"&NW{X`:m)x|l=H+, +CM~,B{*_Eݥ.mqɁ bs oY)pHKiZnU 4-"⟱JcZqU|$=˾m`f*kjH%Kx<Ċq?͙X41Ģ>l*b-Ѵ%9\Z9aXIKUPYf.wlȠb8cKv]k{y0TOmo\_AuAϹlJXm h:l O;4HH ,M^98Gڵŕ!Hיt@+ذڵP/t)lu ؖlOZ񡭾!윲nkay.3MYfqֲ[vߚ*#+%zݬd-w7k98D2Vq۸@Qbgkgs0RD H,v'EAM"!:NbFv>[{-¿Jή-[r;uj߱3t98Tʷ[ +"lUݻRX4P%vE#zi7WF1"Yp# +cYcLp͓0]Oa z{ ZTfOW +Q4Ц^ν-o`Oֆ+v{k߱|2-MkΣW<{ .e1? ˽?_w={b+53Yq}/&5fqx pV5ս̜^9Z4G@\ܾԫh^_]C^8CѶ0<xCZT;1[)@E,"l4.W%SX;ͥU0) 2?7>-_:QtrGv(O2 )tWCl3>T릡Cl7 0ȯTmjXCZ~Q S#U YpC4բحA# s +T&z2_*Nޖ FJ;4^{nTe"lNPņUMg9;'8}~5`#G*5 _TEQ!rFcٿY(~,ji~YE,gWUM +Q% [yƄfkC̪vLw5 +yI|@ ~킣 OvdO0Wjɮ-aS̷nfqοdɰ٩VQ`,r&H޵ r+,T)T00϶#?<&Σ_(z!$Pn&-%*P+{0䤉.0(}Nuc58"&)΂)=8Z"0?0% +IMEP8|w[c{ˡhc(r4_kD#sc(KSjK[ghn h60%w(6Y2;XOiHU!n,@K#-*hPAcFZRI4\=24DȠZ>/ ^SsΏw螘ؕn2Z ygRb+1P+*]vnVVϠ"MU<G7SlEkŃж/`ͺ.r]N Z% <gFO(Gui[&Z]:xjO3ު,'YW/ЀŸc ࢘17.ͽ`EKR}(>A +E4hZG199TZWM"^l +;`j<j<HKÁ)g>} ,0kǃLv|egZĹ5.Jk<EӒ_xD,I퓆J#a֑qa]zHin09d7ԕ1qMh$..h.QvoϮ P](.+{SRQ}I Ouv d*)~5N"PkKQ ,.&[W#6k 5~2gpiN>a@b>y8кn-zĂ]e`X@:w`qgWVDT,&ܩK8w@ oI ؔ֞qkSrךRO%OMxp+h `jOk|cFu7$` [/QY](y(` /tŕN](ExDN1~54aPgnHQ +ϽK#0X= L\+|]JJ~LSCZ`34r$]U`mq T_Rvk;.x׵ijZj_֜ |ZtWslg#l\p:X , +^Pۗ0H^{ZA cy)Fd=iV!`ybK^OM?-L|6v ?5;3ÈKKiƻU6J4\!F7jpZ-YTM΂A xEl@+F {?|o/_ͬon°wqk U uǓwF˽;΁lpVj#lw>J\EMw_-!՗eK&{[6ǒJU-dLn˖|*˖4] lY},[ay. bDz7Zd{,ϧޖOeUm ö5.24eP?O.F0kd@O%DV>EVܖk$@3գ\_d~mI!G'CAJ6plp$RWhC)(dpP3^"Co""k>ɆVkl RyvBkmvKXH([8ql^(:1lxdݒRԬ[}P<0Z4R,Qy[GEI6k̡d/Z +_ գ&3kG ]ԁFɎ!Y}g0I[F:Yw_n\ Svn2G-VT>kxYl};ډaC[LRߚ%G9df7ge5ֿ~Eb8a|odB9U 7x&^R#xͷ7!mYt,HU>X̅M` |ts6%frZ ]l/%Ik g@3"U&Eh796B ^BlgFB(JjV +(M>SH->$V\+k0:D_xhȡi9t߼޺;lЃd\_b\AH_CX/3{1i nùy!7E1]9>bG oǯyQ~N]7}jb\] V74c=GG nnfhi3|aJfx&5HxY/vj;x6o9=^/GS}MzW{$ +LY{ac7tHU[ 䠠X0!eg5OyK!mð=c!ķƮ WADjD"Vua&^>Jvg-Rv*8D7ćA+$pqʺDguo\ ~یc 7 DD=gHMo ăK u Db6Q%"AtoPn +g1̨= dTD*X)6zƂ +椠 f'9rU.{89CbҢӅSPe$+&]͘Q3'["[:J*&o㮁(E֒Kt"C/?H~mƹg\pE0 Y}˕D3EzC(Hja ͌;@f&K]z}\ݙj<_,$\z6#-t> a1Yds +؁_ulOWV,hȮQ`w*A{L k0YZ`׹wfڠ;Q>kUשUM(B3S]PgGC{N|!URWםQz+{'RA~ yg\+{\ +@>)'w.%g@-p>XmD~c,8/M@s/Q[jj~ߞ♋b`Z7^izFZ>X5sLE8BOV ǵ|ⳊhPļ"4gl+SvnK2V.|PZk^S:{'ʩ\u?UI/ +/OY'p|pn.b*#|& cox[GK&̹*b jqaѪ4?K~f (8}I~f< Fma>ʌVǢHW9w1f(&kV72gX#ZThUsJL&iiVj`βYRԨWKo!whed`i be\GZ9B*C/7/)f |LIRX#EE +՝(-]L;@OEh,,+?7_St%C,(scZ("OϢc5{8N|)('FEJq V4lt4[zH AEB{DR.16dExCm<&hgnҷH?c-Z++#@@psd2D@6z5T2BՍ^"LTIo4p~-i."&LX1w&.;wbP+/[>cլNG_Pԏ1Ʉ.[diBdždn +@WR\^ȽgQ:;Es&9W,!NdIbs?[J{2ʢۉ%ѭ,ŠBZZRl *b\o/O شGA7QEibug,70{{1̸_Ob8=Sz]Q8x+Ji 6",鋀O [rBWO2gCDA.7pŸ31"((?!v}jUdrB[N] 92&b:_-( $3 "#by- xLMvL6dwN=jQLSRG P9[haJe6[L{}sϸfXuޝ&t$0o1z\H. 5K'cX=v 0F NGX+KlNc4-U-X,YĶK3~Z4`friT?hRƭTUG'6dx6 |fܗt ,*=yd0Ce!Z]_@I.zɀn;.-v?xC!߆ZqJ2V(Y5Y7M$.d]#hT'4`2?U=ogo۶ζ:0Oe-.,KjPݰab3 #hoܘ:*S8ZO$p{(&Ei\Guj<Ђ 'KӏffE)-L$#>96H_dhf29U+ŽF6Cb1,Uh6ZT*7TJTtMH)fФ:CҴB"}dXOcP8L3vύ*!mbG a`L+0'MKFO)*J ~oeYp^>#os0]MRq#޴}$5j> εKOgvw71Vjl,VIw X].V0&-:DP_/peObLp՗TD:E}dogIsKi[u]HP]Q)EW %i:^@iOD^:[]V.9=۾Sr&T(= 35RIê4=NJzc3}aSnld5yH!vUY=N,ߑx>42`eh/:К^+8+vFbv\᝸of S*HȮES1W|c +䪺utﯲyZeKX`eѷY;,;tsyW?|M=;{FSLl(cf%O+9%':]|uqfy)gL,՘W\<<75' tژ]y!dRu5IY>f>be5]E],ln@a Eks*\#SYG(*obXfwpZT^/lh/ާF8Mdr`Ji݌|v5 մhF'iO6`}_4Ҟon5V<(<r^g.cY9 U6IU)c((TٮX `P p>d##$EBX~Z hwO٦ѳ[e%eU)T^!tw jR ]@.J7yXQp6;lޗ3z8Xr3#Ǯ HV!Sl̐b@ dζޮҪT~7{.V4$~N&0UR$3ʣ2j- +z#;i&u>E{zQ3]5$6aڄ~:؄cL4f,3 + T4{yuM*EvOsH_wG +:sp?z~'=mOsڊ^A^햵ڒ9ޏ}޹%ﻼWWmw"r hNXUSZZiQ>VB\X:f;*?FB,~6 )MP&B䙪 .Rݹ" 3K30ދ4o1qo!xgu8^p1 +5P,2HYJuPMg=? +CZM:7K&g+M]0x_G֯ǣ,')Nк竍~',>EB#THk'ᚃYuvN1L:Hd'7T/H?˒[_]nݚ'<xR$R$Ԓ?ւaHvԆ5eZ/ \!5ǽj¨zs˩fV41Qμ~8UR{!)'BV= "@ptX>uMҘ9žǬ^˖_mFS;íȷb 9Xmc[Wˤ֨q9=6ܴ"Gbe4ҐVRhgMhc|{$Hl4kzS +-+`[e=kkikCߚ=_{VϹV%'˷r_̿r gиi?xS֙="&U-ٱH`uzah8Koi3W_W: %ҷøOɅGTSXOIhcЗMwF)`ӟ '4?ﲽϏP<0ZRK ^~ǝwu^榐,5c4+$*% ޕ/kpPTȼ'$iCR~&oTi2E,Pyn)~ep-VOIۉOcfLxMP&]l#- 䚡bh5y!`0Uz.Aͅx +IdY#L46 +]= Vx~5H6a455VmBIidD%zF֟՞!9 sxs~ s" rƛ%:S$n9&᪯Ϙެ甮w[Y`|N6SOm?|Q/f'5_hLSOJOM7*fkP(8+픚j[5,B)a({zJBcPP];R +-{V%m[>酞.0a2DV^]x_^J{JAWQ(ccf5QԢ\ĈuRcZPk~Q@i Imϲ!H J=݄F5^pݹ%i#n"2t;x.u7\J͟AoY%mN[~6Z[ISPpft}е B 6FGh* zYZ,/Q`0X/j lԂmR mԂMZ|2҇lʄlZp֕dҼϬ9?/fqGnf,Hsx,e61g`b, +M)&o"Y7dXXu،L8XPba֒]byO +ϣ+hmt|}>tu%ߑdT)LetOjQԚVL{\Z1j[nKLhexJA렕Dd%Irz3#ln9Zge)'+xJ+UK$^.RYg戸8~ʋI\̪3_8|Hbij,`jEn*ߺTOu!u|w%7";*͒wcݨ,P:a*:F*J"(XN &{ڢULUV箆ʵи?4pqTHu@*#HZU Gc#|J/-{ςL?E*cȇp̋T<RAGRk +m~]rB*xcn8Փ}@\;Ǔ׸L3}TOp>7Z/Sq3t >TvL.F6v[] Gwh/TFl,2PrL;TЦTFNbLILŷSSe*8-EILփ΢ZeboLW];L%La5*1Cg0~NƋ'-?&PyrXM[{ ڰ/R[oI1V& _zl]ru1=j,KSQ-Αdm*y}ǠQ}WO{bYzFu].?cYfj+jӘEsFTO jHr~eyR bEi|T[~آ_ʰe.@/1IFH$j8D`b[DX|,"Rs<@cH@C42DZB|boh)ng&~Yy{ $0Eg(oùE2rZ}ZE4 }KL.P,U9M9D&c\J{"f9DdJ"#~[<xF񈼌$v;hDr%kv(gvZ5t|"HD`\O$lxakJٍy$(1=?fאW$GVK!>VeBo"i7OU$ζUdEEtyXVlc+CN^ѨSc*eIhz5"o%?5ɼcq"ܚWdxݢ)-2*p**3z9El)ڥ1]x0g &EkӲkrLmA9ctλm;hU6k* v+*VPSjd Pe7pY [VqC*껲bV i3*|nQs=bȠtķO^ +r{R1U<.3bz`,*\c6 CmvEhgQ"㫃Cd\֖B mժϼM-Y(am%|!]cBV ix{J9Ȯ4ٱRӾ(3B3,7HYι蹋Gn )(~Ԍ`Ku]5,K}bl V5*U/vs1Du -]|ÀHIm]oYԫ2㶇7͡Yإڧk%ه*? mXoO]T^6ԉ%sȝD;suWw릾 J#Ҳ-*Ҫ[& .E0t,k|a.AgX)馥^LD|W!_(ڻjrjZdJK߉nUAmuN pܮ<;Ҕ$e"$.PY:.H,B1ubT%͎"e'G~l>W //hCQcrԢ܋)>LWo_5}s[;}Vg\Lj!mZz[5/e#6T[3h 8g똏':Qen=oK33Сa[_{cηIL{W~Yإ/L:r̷noodi Ed>OW|d>dBG*)T>1sעT=6S3*ti^AԘ[7Sg(PF:4^LRD+/nloGۮ߮Evy!-WrU=5[ԣ69p HJeE".Ԫ [GC)<|![+<̿xr,E]Ɗ/4kLSyPy< lAA!Y?Qh"@^(x]]Kʩer5fZUf&"kOʳ~YSb&Cց!Vh֝"Fdv.ܶ.3ONBwlF(_;wV ]ZeK@T4E`W-VsK>ݠ 0&$4# kR)HBr@=X%>5ݥ $քb.hB# {{$a05nIgCUey@[];c d%7v]I;G9{c 0 BVEiߊX(BU6EEH̼R#НaK>gь(/| ċYٌɑ( N7UZA9A_b+]au\DHuUut6?U,YîZInsr̾4*~%BzHo wSYTx, Û Kl/~Z\d7Ԯ[_U j8x 3KЗ`u~v ԥ΁)v6-K:[1% kw#MiW_'uΦ8oIt;\`ZGqظC^ +I7"< ˒;o)fLl|ܭ-n!zMOP^ Ei.cF$.e(4HH8bue,[_V^Py?-c~-Z3=V'q\ڱũh2"8Vt6SnX +GXsܭcD9.>#nE Pf#c,ؚ4lOzRmlHB/>47BӇQUkj%oe=%w!H2.t2%uE?]yjyi\TKklW} 6*Zh㱀l)eW]$b!M_:wݻ/n$f=xA,eK>pWRr S`jS .7y̿Y=MJ ;X3lj;]e%孶^X]%4ciO}KX#A@.8H]-*@>m̳` vUh+ͦ÷&ѣ gMqIMX*5OD~? %Chxe2ya'u/ɩnf%WDdfd4Cٽj4E}gy|IcXm8wkZ-g 3 FL` )RGrx[׮ڧ,!kMikh1Ks(ܳ( qx8ÊPTNQH,y  0e E,alw~I]E<8oD..JV"uPCأke򤲶K'H}E\›k˶yTUeչ.I 4>6lK?٭jEQ6a<6ki¢ _d90fW6 ҕ> ~5 @m-" 2eaiĜ[/̨hW*b6{Ti{(o"+P\omz>|K< 4!c#2;dj#,#g"Fj.5ѭ/E Am.³ljNZ 1rMKv Prl1ŭ"7HFw_F@\zxǣJкN@"GQF\Wxuv]H57{vvD'렯vaĦb9 ؽ)>ەRI:>~F``ssJSVp)FUi>stream +HtW$ @DhSG{ERz)W.uKʛWF_/Gd'z{I}+1koS lXʍ0h.XLЂ pUXTkkc)QeeUhF%J^v?7חc*0kMmbׅ1D_mW!>cw,O g+TW\2i[Ts ZܳqɠX$ZoHs +N#%4È2f90rkɬz Se&'.N?cc~se76-4{zv]3om3Vǹ˄4}3orz{%kA[ЗOؖܣAs;Xϝn,~#;hjq 4{6gVhĪ0Wi5ƱȐlzuj8\ϽrV%0I+yR0t80[V u\4T#-x`?|kic2zj L#ܲsQ^ddmh\.y=V~+on +ζ^溱g`:[A6W)y*=nBÙֶ`Ó *iv*$̋5$Č`dY%jzh&Gk S4UQ+ֳ/>N +01 PKwVٲ'#4Sf)}{ #2!uFqAڸ%꟣z6vr(!>a;a==)ka{_-6H !v*`n]먷dd*`t.)RVUYULȭܔ̒CUn +UbvRU)`4L y6U#W{ mW>cw,OlSUKQ-{UuUt*|7U[Ty#qk@T6EfCT=3ƿDok X'PwbWz9`I|f Ǫ r)b:NPƠp_EN7x8PI0inrsN`oP.Wb+me!HZ0lHFRpo<h9\{-ݨFTV- 0^^1c?x3 +kZ8.Z㻣r'r +:b^;Ls+r{\ +zM7>-|.SZ%Mաf[.KX((eaװs7#WIzQx"|_J5:-Nnt,1qodB `G+"(K裸7jYU}h%`gicX|P-%Lz[{@JV> 315 v@rhyDuHפ7#?|X_0=H vt |927|:;fXD &bq. 1J_.՝ O|\g1=1c3`- Kgv4ߣObd` g5p{G5z2j`D{5P886w5!ƺW3ڳpbJG1 yT5=TrfZI0> ոQzQG=,gD^k{=VVTTz^ӣ pSaGZ `)v"zzӈstq/-Y _PVѾrd< o){OQ 7ȟ]TB6Im t.:bYe'ާ>_ig<+(ͫShz`iQD#K,@%AB4G=y_@ jo[S #? 5N2;хR)ڼ’D\@KyJ A42,.h &ZZ$AU5˾Emk>rdDv.KemBlFX3bEYN36i6nj8Ȝw2`%|^pyAѠ[٘_6M,M2zG(d$@jOTO-2 sDE. (²TPkOv@VӸ"=z,)oM^htBT 9:,C[pn9)RZn6#i).pcC&(16 ܍iaz=DNq[;G]" ֤9Ve4eڸVצّJKx{S.͘:aJ%ߟr4 XԘeQߤAܱiV++U|F, !C3)Zg_iWW+f~PUCçRԬޡ ɂ! R7(I^-#6zp f_ |m>ݯrm&b<@L8U9R9+N29}J̃H$1 ƽˢM's0bo.oP_Pq*j+EAhA)K)u!Eyt2BzL +2_3u,E20 o.?p}ҧ BA'+#/א\kS 6`J$+ZL +aёGwdB +|XwgChHEPb}? aWh!x/{CP'IhN: ]\"D8zk36h}Vxl~mx xXJw\QMDӑuiaGg1B˕Tep5E:^^A[Оz,i +ItZi֕o¨0:7G Pջ'om1JrN0j) \fkq(Îz3a3Ull/pn°,k<5XK2ۉ2{wt1)5gD| +$= +&.FpuD*@c5/ +⪡w2@WJ#p]$bMoW'4Xˡ  ihD@[<4}N|**7vmuSu0X0Z׶2^l1ϛȌall`z4_hؘchlo+Vd *'a7X'xE>m +nj̒47ldnuò JM۸N5bvbNWUm{9>]$D,o(Ho:_|#%HT&ld\Ցw.і(^#D+4J}Hwt&Ɖî:;AH`U!i= Ȟr82މT̞˵7ؖ{> lÒbk)7K/Y`c6CWPJŽe~I#* +fė +㈷`NGQ߽ +Ou)K$PJJ-vEu'RoX9¾-svX}D&ƧFFV3*˒L :A(ʶ8mHq^c&`-jM7m*.~<@B%9R^-!Gt?wV+;CV/1^4|0 {Xe:s'RTU_Ȱ9 E6x%`o= ol?3* aE |>ӔZ~7#{5g r(_i_8cYGTIlL3pH'Z Q VΜ,w@[˼\RiaFs<|N^Zq5},bj\N\϶7.51a Q5 +`)|!J[nu_Y'i܏`l-$^`" uz躅~ ! j[`i*: F*o4prl\FzY̙񩠉R:bq0|0C DlF?ݤ_?W%DƬazc!RzqZtןSa";mׅԮ׼/==N|NB2sN0u\zn$&D46^`ў'n=ruZ<  +p[0|ߣ; z͞둘jm}8ɝ~s^]+X@Fl#YSyM,D,]#u*;j"6)]A_LBb;b9'+ײ:OQ/Y!ZM4.V;d .??hNY!\aPca]z;\P!l{jkԊ-C/4zm-,F,Vϴьoe#3 P0 +r-{#ea3-2Ӓ@/`&lԗЋu%:#rkϺ;lNDmvmmaܽBM:BRAɰaje&e>Ipawn C{Ꮃ"-+V{'2# oO`5b3!H+{:w`Pi¬ ̥[z(qY*;lV%bQ&1Vf\;doWV4Y ,[6YR-[wcj1N8NI`V8iN keRo$@rf߹.F NwT΍1ϣ 2nQ?&D,y白PctirKSH&Go0//@v_/*۹Y˼Y,MVV +CSGihM[=MH0Nɕ1{1 +Ӌ+ >6@8C\ߟ9}_^;\67(l i)3RuQ3UN݌n\=7u==6gޫE-Ɗv#-t[dkۄ>9WwE%9 g2\|.m0c95=&"RS'栟ozoR#Hn>g_Aq+=PCM*m q?Y,¡rXuY;G;K7+lfr8-R~dWF٢n$(]S^ty-ЯIKȭn'^'ް1B46VtE2Ue0! CJ4 J )_m-Q}Q:iZ-OU&4>3bV +Fd7᩿Iſ4ViQ$.:񊛹\GW΄rdU)NjucJ=lel^`ҋ]8 0}i' +IZo7!EƅCֿr<ˁο7Z\XVɄ(5  ` zGpuC2ljr3Bk/<`{n"ddE̅P(8|Bbnr׃03>߽ж=Q]Pm2=iqz@◬L ldb+UU_wTTn* d I9\} Ű.~uWBl \+(qlpviI{+_ 5=f!=yimiY?X .z Qxg,7h1ʉ~KA oK>ٵZ'mEj0 + nTm?duI+/{>ݯx[=e,*=~ZKQV-x[Bx= Yj__sq>}Bt##0czBJ*̧Q;ʾ8"Tx5wV&!%*Ú-n%[,* 0KD7w% Y]uUr]5]eD cUs]i#I֛{;P |';NXn^ 1ێ BCS) 'u1XLrm[z V_i!!:CnscWDG~!uoF~-t1V`w ﷝t.e))])|U‰^wxۓ::Hx +)`¹Kޟ24_p!A0x:'o`; g  ΓjǮda[ +;]t: X'J"AYD.nu euz0L8d5xT~}fD+{{UٖuU"{2tҢ.ޤ:VDJTm^3 +^~/ٍmW2m$9v+z9=Hh鵉FOmk-5tد~%PUfuaK5`ʭjٲ +:O`޾T{J:*ŏŸ'潳Kt6sF,nfgyF%J5zfD ( kP߫ =Kx]UZ]^卌xAJgUx$nVS{DZrp($G7`i'95+/8K&LhY B}Ʋ#rՁV4࿮icSGi`jgvHX'MhY +oڇDaPR +IƱaT\s+Ȧ?e {YyGDp9aߟ4ɭl\:vH7 h:VN]l}s)~Z5oe}QT %}Jp"0_!3@A:ލiR$ bAĪڐk7pu% z%DɍH0=<>c#`̳,5^g1,/Տj{9?9 s3Pqݞ"} U/К"wd,ٗv)WߞLȐ9- 33DƔUπh#eeD|>tDvxjw~I6"YEmlIYm.=nZ(,K2_Rd"2G>h7ärf~z9B2Eck%|4Uŋؗ#(P8t۝Esu 5NrF<¼8OgTؗYC,L⥠)qwTss*S]5 kL>^CVyP~ÊY2>1i͏;j 7? hpcfiooTt!PżNPхG/%X *x젢"ms ]$ +&HyE JneT#&@4S^4*EOlc(|g-5X]A2.@z߇! C- ӖudL{T* -+HB4W@/v)+'?7l%.dKݨ$Ji%.FA=4A=>dͅ.x{4d>Bk$TA:fN soi;h'Y?U.A:YB5: dN} +tʎ"v(i炄]`J`{;fDAsa䬽)@zk-rsc^U0ڵ E+C]ȧ82׉YuYi0iM2SFTT?Od$Om\rWc}~þFE"e'SUZٷvu]Mu*5$zغZE֦7+?Ɋꩻd,huY5=`v?XGEgLq3$!n"e",Y@5|nDSU-7Bj\ۧvSal*yNA&<7貼OBr>0T[ D|j(G?+2JP* '=+rqy9|CVIQar}:.ٛ>QL&2Upƒ$x xջw~ 7-:ɫo]GphVu7ukহvp,]MFlYnSjeF79HGS|ѿӃp,~e۴a'H'*Zpo@ri;%2<ݾH `37[D{c{+˾V}Ef7љYcV`t2R8 0ByȺ8.[̽Ѓy372(Pǽkܾ1u]v8WO0%QS9/:_tm;.H/<͛-۾9Wi,m>T@)߼(Oeߜ7ez4oEwZ@,3>KiC߫$7"pƯnmWu1˺@fL%ݗU9$/61] l~.DsY2R5Ѷ ѩi#84bդ"߆\kx~s{O{u^>wTCB;z"fTaOI<;څ;vH{;Q-rNR/m[.Ee,^1tY,e +Gw.uE`oPa10>7r";,ؔ]3Jⳏʍۥ:R{.jQp[Mő;+`9gud5Nu(SɍT:+`5f JGӑ*:="7A3>*6#jrL佬*hƶվz\tK4LFҘ:˂Ɨ,d(~꩛$ P[INA4&5?1KcRr֚PӶP +i7v#gNњYS^()l,U +(mHԓz샩N,%ʒv9=6$w<;Kv`p%=!h]єuIp.R)(}+-]u-X' <^ ;'*'MYh(xۼ= +jÄ3B\|AgiؚBA;9.]ڶ]|1Dv:jUiО;s'҃%R{+25ctrf$71R&kr)R]<b>/e-ml*iMJ~cvsI:=y?qSԱMx^* +!3tvP>O2y6%?Iu,IU"瞹f$1 rMa +1TUDF1'x?KEn(M͋QA<7\x$eÁ*۴1*bX@!SD˖^5닻N&lT v37^&mk/:UmOٍ:ؗj) ȏRQeЋNb,U.ȿUv(" 6^g!]1"57Aޠ%6& Pﰨ<+@^ij椕6Wm>3c~y_|*z8˻\*ڎ ;pnk"oyrDHqܗUyRP@P*`{#Pm22,36 +e5WǬVzp&p#s}-ԌɠA4=8׺7.V7L HchlBR4_etZ&Aϛ%=;9ՐĔ'k*jo=Jqj/ +ok!BW篝WD)m8fF4(R)]K^V]u~$=dc* ^|6偾3Ol< "e4Nr*z1N\f)ȍ7ϲЈSިxHPI%K\NOL4vIERS Vt[Cpҫ}購 X#,}kRa?ֺdȆ%}m<7ղS>*ΐ/Q5V4w?jV)O#fy&\k01`k&L/DmlIȎh,WF"|Lb2QTQFOlmu>Y.cRs>oK}fQeR*W-j٨%[%VTceX6ˬOsTj$L=9. NEHrd| `E5J>{w@?m0ak + oSrg@^ ]V&`0!rO/}/Y[|}Vr~0pq)g^õ Xƒ@{tG3/23kpe@2+nS,>`|1kkh1pɒHS'n]0. =4? @PG] + P喿\ђuԌ#j+ +ЮUΪ.v95y2IFWaXtkL]@e@`T`V6"SƦ] ]6= ]ž Ñ:!)M /sv%f`y ύGzu(D5,DGR%JcVdey6WF Ĕ>ZgO92 ۆM9XU,=>VC?UW +B渫n[b{]#:~?ϙ5砏`Fqţ 6E >gd'"yX]fm13SFt~]l%tѝ͢V"%E-|R )Ō+F nKIW⠪I'r[ٯwfe Zl<*Yk8\d=O\aݜ!.$ jYfO(Zv'ק̜=iTa7 ́x05U3w?d8-`)3甖5' 1a#hgJK"i%W\c E7ǗHm z1|:O,vK'Z'H_}ET ٥uiVXm0T|.J@S+*wǡ}jYFIt2hv>T鹬 jd0kݣ׿[AI@GAǸUJSe5& k,]g[&ifX$uE;s.tmԠPblJyn)P-}oV%eV# 9wUxbJ[C؏e'*.O 9Qwhd2~O?Єc{\3l3W<#x&:xPG$T`}e\K.yOI0o'otWv;Ax#5@VmmU2Wwvjl 5%>r ?Rp4nh"3PY:Q v4k{pԃ|e זy7KHEf ŷ#;+{.ImBw «%7 JR+G>eFFϿ'x?' R!_h 5dSs +w=^ Q ZqP2ļw>>(% ;,jVڻn2$%.=u+Н^_ ĤNm_2&Fs>%kٰ-c+ P8} ն0̹ys7hq2-n\jqu0/lgw|N>㼶èVap eK?6>];4tdˤ4r)YHrFElp6dX&/тcdE'xd5)t{+Y}[G;e1||UYqg#M)?J@\ ׫#e{SiL4_|R#S)mAcokOL4Z:{%(Έ;s°2V8'pUy 5y9 +)Sָ "f{UH',0%Ws5Yu ށ}Hf+'_]eP@;[;gf-jq7mV3n, +A`]j|M&:Fk# C +͙LmWgo=NG-\6Vr'h=܁f>&5e5˨Xd v+Ta %&0_[7 ;p Ld{ʭ.niPNeJ9ujO"p׵?SFRKHi^J(s $s8r*y۾ ybf>n w[H0s{U_c :=H)L@ U/GE)^Hﭟ 6e{māECEуyZn$ c@Fi>"½XewJ, 2SJYRC:ݲ]0Ы:- Bjvjk&'ǢZVdE]H@tbl>}+*IW!6k˚ZP,ٳRzQ e v5{Uwl/==}O*t E{1rk&U׽$/f@^.Hk?$0\*fRxc&QitD.+>-$c1#A.Ivx2UZ/ؼ'#t"$ ޏ rB?t.E.'.$TTgfIǖ\>Kc\Cԭo#QeIH*Y4\u[QItEg+Z$/G̪]S%5,v 1Sװ5w+~! DC2JRVK_m9,}.' Xk}/+[0h5 ӳL)P.g9̒nx-kY)Xm؛ʿ+_+ǓV&%G]BfGrr'^EgsFnC{`&PinO䉰gǘpo8&?<>qPlk0w",27W<ŋV{G]Y8*uz`0uoQۜo[AelKĂ< @9(㕁~9o-jw˳I9]&KZV.=]b|TmH,aV,J67V:^CӃZ} YȄCCЇm=CЭyhh#\0F>6恰!q!C.H|/, g:?QRh)}6E@ui7z<3't(m8^{_YeW +4+i iPpf$CF{:cK;0s#P'G'X%ɖ Uy6t\{u*I#Tjj/ȭu$[[4{c*_ÕVsyyK͋k"SdBENRS\UXY:{eGbb^sm skݫ{Ұax*3=Ycw 90GM E1EёK +{2PC]'k7,fh3hَl5J02A[YֶE2_r)5[A(s.OM( A>.Am$K!0IMnkt/>s*;V59MrUF/cI54^ q!0}exljP؇:cYϐ5u7Y׻#e2E<0oS5vTXe>s-zCg!iXSIUGr@eq-.d1(?egZ`zqd8c {P5Y50qR"BpX'&^^pZkEgap}Pys/wG3u~#h% Gɍ %ӡB{mLEkPp?@|K>>V~s'y[eh+Ŭn4v2(xipD]:$bpx/Sr\-f=Y9_}'<}Skiv9.}P ^ Ț<{O"#%ߞ8 :͒6!8Fw5=xwt^em唎GrZJD}:ɇ`EE5cӆ&W퍀#}#@6QPC @+A(Б*eJ Nmzq V@m2J9m0]3W]{gAó`JF/[#%[.ĨZpWǺkrr#fd +k^%Gy97vXIF\/ƾ}_yP=ׁBϳPR{L懷QECpOUT^U6(Я'* <{JĈzz_`uF$Y581Ddd%(`RTň@/|/ÜضQjRpT={=kU {t Uy1~%V%]_gD_cM$0.EːsjX54}Bj,;BD@ Lޢϖv8F< }SOA?gК7c4OzAƝ +ÇzAS,ND6 ƴs`G-#38 ` +mm%27a}WIȿ>iB|h,[o4b7]muh}ИA~-qƕ&{i71&oAy7/P><;crGp_D>yg'@, C6Bה H}1{X_vZ-&!$ ^B\dKյf2l~Qsp|!S^ +^^kML,WIaˎa)toDHrc7G̍>qۣE  dN%_Eo$Ъ+o }Gfʯ-#l)Ѻ#܈:heh9[F9\7!I촚lN/J*LPq.qBĿŏH_;/4VW&[ F5؜~`ޡɑpmSerKD&7B ֶ5rYn䍬:<"_)I.Job[:`B2K< cMƻ ;5nnFqrJ &!K)\R17:n\ Q0 JQXKEoD}P +(\å&{_&~BnFa,Q7Zɑݚ^{Y֭[o1ޮQ\{f3 M^cD8 [E¿4S+ӡ+nqe0#4_ +$5iƶ{媴Lއh_:|(vWR"J?8e썣)oQW.3@> fTʎiE\0 +#G <讖HRx9A?$>o'$dU +ϖ;i_'O_JzDF!o{Tڻ$e1LҬ҉̕HYx_^7KsE-z/ijW"~ahO7 Niރ:UǠ7浯БGN&@>AݶU.hCmO\~q{ft3 EF!5W'-ol(k*9$t!,槢_׶¢+!o*$7Uz?^)?kw"8i?}}s +[#r[s+O@ٜú-U"Q #1eD$H1y c%LUQR^b/Hm3ue[-/BcN+ P;݊lhchS^.(RG[+K(Ekv8[4m7KP*"JL #us<Β4խbܸRvR|! $>n9+bnn+<#e@ţ$.,L'*k"\yB-!ЦpڕjsV&[`HJlVb5At5OED6 ߘ{;PB~6wvCn&8m(osxy[Rɏai +bf܆=V?i[ qŘ'ޠG5v%#1s+QxKR_7ۙ {gN{<2t0"^=8qIntO=B,`7)>xJ +1Aou>2fJ̣ lcbw%B@vc%uC6V>Gf ] +>~1`L)'*eͭpGKV:!H,.@F fEkcij)c٦YhĨuL<ORѾ uzۘ`j4JkdU ,x0%o$Qlsب$ M{^SS#sd;wD;lsOT8D~kPqqP} +Re2MIBК&A P܎l +[~emjYPj<›Ӯ@E8,!8dAsFN a/iټLA/x C vdT6_'9~uo1 0L_~re/%+ MxKE~簹St}~P[5nl(>Ğsy=5I U,hKFq{dnA.n]P޻vI-q;mK<] +s/Н c#K&g7? "0N1# Fen'bpdv"8㲺'^`G W/y;ZϣZtdƭ lqU܋xs~f/s oZGjу,6Y7pT4qsCG*. ]T ,e.Eءan)tEOEEtéhnOE۝96O]ȩh7xt /{GR4/=lC}+sJvCc{C]p):HcS&[Ǽ ce@yyHOQ&Qpr[6Vܴgg.?2kb~Қ`8sk/BZc+oDGB㶛eGp;#?Ud@pG:] j /}O\м- :lL~ؚfY IkȇYjnCϢ∩@nyj{bl'#4kr s/FY9ɖeۗ_\BrXA̢Jv"BZ >stream +HtWKr- +wD+pOcлoSvT!(Ӧq"K>WCalD?ic]dm&2/bl?0-+3 +aߴt:If+*h9HW9"Sf*(ҍ~Jh+b9djp q3}1W"4}N fSȉРJ* 櫾hfXB 5*Z]]l֍vt%%"5Y D/Yxy= MDV~qט*.KfOhǫOy}0tf"jl 4qD< eT2%6yQI(86$f":,-PT,o,`i$sm?2'a1R!/۱@^>7JTuDVr #Lp +V`̐Pf";7NttB009ԍj$o(@LB1.G? Z{j1S8sm?M+o gn@daS~ys5?^Zp$O2]' +t0&oN3g_q! +~t%dTBXUFD+du-D IevPĵ7hv=v1)f~U1bbMqy2N세1gƁvķ-J̬9})le' 8%ʳ N3gذGS0{;Ht~;E)"]Jnz? +`Udpt*tst2cљ1?cim=f% +Uܮ+O:tIOWk`(Veo︿.|ggˇނPž ^=sh*d/q#>^5SVE, ^ ư2MʩVZAJ4~GnܛAG58BZt5 &( 5Eh=5il+Q>f5Pwd>_WN`ߊ)$}pM%1-51}-[| C%TlrocSnQ zbӇyMlT?d0!4Y p.[vb v8t13xb,).fd<[qЇ.r1[ЙKH ふ0IoeO|1cg9OiL xZtQo-Y +HSbX)7pKCmBH6&LmL3+T41-eAIwˌV@dzcuj-1̓oy_&.-œS~~!$[]IqDSոٵ`v5֚+YߛKGxbC'?ʱAw{: W%Yu2yUB&.k%;!%ѴgU¦n"w펗ɠ^y |\/||iR94&jxn>=p >3H^ ޭώ ^'] S/ƈxr~ |l /v|w|\߁>`9*#'! ']dΎ'fQ^ Agx/f+mΜE9US|wiSsWGی4?bc|>JT'$A;Бt^wDћ%ؔȵnDDA]r(W*E;x1G"RNG=i"HfnټZ +}G*lGׯ1P:-݋8@cZV1h +k;>)}lO=0hU7Q=4s^Kuu9@y ]18[G{S`,N5ocW+|{v^DtlDdzW( i͛صf۪_IEu7|OHdv8΍ruhYcU'MP`sBQܩ=.O@%`S{X't*v"8b1g3-!ozAؤ_' Իj3m :M4ҎWm6giU݈25+{2IDl N~zLӪЧ9[f Yj 2T5C?ڵoz6EHKiV)-A٥EUfWȠjDԾ6jbDjr&2; v:: _k4 l}ôǻY_j(\V<)4c.!R v=>R;beKvb.IHH/`ӻ f4s:U{zF@M\UnďW&`(11 +;q:S}btDc2'ty [n-E%@drppb6OG[O$ Vb*FsAWͥklk%74R>808Cu M|_|G /}Oَ1?a>'o)j96} dt09 dMUX?{/Jo1*vZ1C^;ULE8l3 5wn?+U% eϣ#1S)n<%F{R< BZ[ :f,~#tOlIQ6zO@0M8YJz}nX}ϓVB}m^yxQw'(ZOaJq3Jx8 ¿CZ/H7 EF®a㘀 +6| +/i'F5#!-{ׁAȅmxA@K I;+cێ{7UZ>HZ k_G#45& 1^f~\L"ZsREʼKCw݉^D揆'6+. _N;nt`. +ID6)"1Q..Nd\]Da«Z5Vh!5Y$zc}7#R`qnhԸ7S}'.ݾbD ۑbiN/gR )<T2D/R6|uj :WO0 i0O?N-<ͧ'xv/N ;-Anā wK\ po pqD_Q[ 9:3'7}H)Ab ~B|%#&##@{ӬGFć#XU1I~#B7g;\.YOCC{#ex`w;ƀmM=?"O\Rǖ:ad#SnG]yRUipP9C}ijn%cn1c?"NۆkStgBUoyZ?!zmp".!@C8XSPA1 +uwGȦI ;e晘# Ș@ +tà,#ii'Jdr2Z5mnuR]wkRJ4&G)d!6Jfr}NVɸBrʽP4 +w 6tD&7 m=XꉀLAP +2o-f K]/A ,R>0/mV:jVÝ}1YbBqd3pE$/PR}%ג?^_xdtj/𴾒<_tvܮ 10$R}SI>։xbi8T v&-'J?Mmyl 5Dћ$*[n&@ˏ1D*lx}zAs $klx@2JyAWh ^j{GKB(@F b}YݍMN TU* Oy;*VߡtS{J|}5'(ͲJ띭hsmdO&U[.eQfUcz fL@lQº1;!;¯4\U[x=f X8#`V $Lv9 J (%XƪxqcI\ͥ=wi7f]IT9<5&@tX"qvSv݊ޑ6- aN xeOq, re5&Xg#Z.pT5p-ۇ=dס1V~e"k3,ѵ7gG'HD1@5!R;BA xVO>P͹ +|XɇJ$m6)Es"k.Xo϶S#S/{"c{x{Dz4^v^ڑL G R)6]_,[UD<}/Dz5Az6bU(s4!'6 QW pwgL5poz-2" + }/ۺB( #7Ԙ\5'맬7O+Yg1sG6-M!cN*78@.a+\%ra\/ 7?tK_1Đ$e$Ewft=ܛV <45 +Ʊ楺1uVm`Y6Mo-4ѬˆhS\|_(/1 j +[⻶br)S4Q"[A^j\FS5Yfr#(:pr <5E :wIh`:ƫ~#[# +m׵uP._P~ vey&ƛˆiPXRuo0M I@*Mk?*h%%[wYt1d䬖VIkL٪Gg}a3STeaX'[31SEW*5 szXv{ 4yTO-Nq{Ń{w,?mzWQ0uqdoj5=mZ\?OuyOV}}R[z'R }㪒E|v$kTӰ Kn͡)L%@0bPsGܵ&AHEUxnԩ gԞ6!|WmcԱ'Z6j7yA%rr-=(Uӓq{->gWQz5.U;RND^!DT6=>&7eO UѺS}A?> +⚫u6\ 2;aFS1ߴyފc4\߄Yife,kk4U_銱lfs簳Q_Da"  nY6p`%W _6=N4'0&'R[RA\67A쇿58)adqVV45%O5:g0GXz ғBUn ҥ]1s2He»E9ߖ> + +ԡUj&[fs\V6?G +["Ƹdi%l(f M)J~9Zݛ3E ;# :;DH@bUǛ,0]]26~QaOA˲.pEl9Ih~+6.ÕjC k~ٿ!~FcT"~c7Y9,@fe/ 4YY1sU u78HCҞv7W1%y(qYRU,iCM~!]?` .``fH;rHq^5[6Fw yf.A߹~5B%K}|5˷8ͽ%K AaY[C#t LCle\˻@`J=y~Fh[?( . =8-z*g2H/ H/?Of~<[Cda} ĵPGz ÇdB1U 31h%A?'2&!Np^3׀%.19s7ho4Rs[߈씶 $m@6{YO/ D6e|8@op\ȪtV#]u} \Dj1p6c~YetC-L ?-\ pVtp:½y A*4q0k~3>/ƴjG$ׁW?A2_ VZ11)EDBј#0ƦmU6s!R)=fJq +ik۸mG}vi#6 Gl vr2粩uN.J0:9`Ky{I:o*?>n^Rz`hVxcCV GCӬlRs(;(XtHɣ8#QuoYq-(F\6gͣcwpehBzp-&?NdgwWL %z:aoY8 Ÿd &`@LXymg֓#vZ%22UKX\& <~7?+4jG$<Y 7~ Css\AVTF/p tZ[ٕbDz$P؈!25:/A<2A.Dfw-4k"Au- ӑbliu1T)k-oI"xc0|)(BsRP@te6wxr@Dl'nr_ʍR=&"w|1 -:2CH LOq"k +ͫ"si&?!R+KTcz ?Pf/FTg]F+&6lJ Tp&ݡ:¥fh֪G$;f$/ 4ra +q_~yxW"m یGݖ0YO-9X$=VU0z5!7P#v.CMm藺>rԪkkVt''z.kCXSoVjIS@~^GmɨGasYkQʣp=읫8aY'9uh;X^7ؙWX]h\\l>}DxZ2Pbn-g:$e+sH"G ,U҂y+Tnquw<6Ys&DYœN]\%Scg\&O{0sX(6xEP 3HqQWFBXa)aE+}>'fVC!)i*vH̹mES9w 4;e[t 5Լ#/ Ov -4kw?Ƣ)iMo0SegaP'`sEjSHT'Eo^T1F;]7(,׽*b%B# +hV&Ib>JG"`9] %BfϹ+`8(i׾+7y߭ZVJT/zX3RS +(NU}֥VdJioL9xvmAEmId\7$>)=tD|y@toԑ6!4ՑsП(K \hDM =1I f +GbaJ]?9VFu{ c,rp #.u6 $oz_e4/1vEJID1GMg@U"z gh]Y' +L{@>Olfo,x#]Y&M$wY-HhYD^KnWgazJrt1gcdiLSNRkj# Ǹzz½TݟH:df vTYu=Ph(aWw V +2)c[+.HAUSvdu:%9'ed#[LcDU!r˥8؍q ސwM\[F ,#+-cD9Yi4E CQClt>砘i#dKVDw)96KqO!ívy޴~&oJ;VGҞNFO̒4t]% 6*u5;7ɶsm@ϮJezx%Q|~"ZQ9EԵ~̐HKW(a<.u໶5F1 __drQ Wu {pf6R 䐀 GW{ZFꙟ=sxP+mg[ +0I@ QվZPܠi/&3o6h[Jm%꼍^o] k~ %Yț36*+sSW'ቖS6JМ|v \g^N;,X3Q&}.5$N1 +վ4xTi Hf>r$_iźԽ/0m Oa `!%M1=ǝ#F~hŢ^X '΀W=@Mqze\޷<k|v~j_VؼKwOg7v;|ji"*ԢZ{bJ|#~%c6GlSU1om :ak,sȎ {:hZ}c]Kv/9Û˧FR;`k`k=$;uϰ +~E> +ҿolإŀXkZ7*n2uVujb3'惣[Pҹj}X)+zEJ;NϢ6+ǽ^D˶[cd${KEIzn RUz@wdɇ?da/X9Zo0:FNC&j|G$FS 96Zll@|0ȁ uF˛Sz +9KIO*-ٱb{gzaW$>CFSm`Ht^ >(5'Y',S ,)|!^a*(+VϿq|z#_B«4(Rf2ޥɱOL<ӵt}o-G{ىkK&F$bj %7Np?En[(bd,\iu lc0[gK ΨH^ldSp<dYy*1֬p o7SZֽAju[,uhbұE!*r6UIM9j]d/jcxOD{O[UM[xz!dv6NO"5kBJX0 z#tC {N" ƪka\ nH3YG>2F ͝)tu_1bry!tJ([W؅vLbɈې(we+\kH噦VEWl)1'uŪM[ :BcQ KX=ꉻY); I ks8 8oZ?^vu`~.I[%]gEP7M~q/.7 S:&D0E WuWH.!cߔcm,L֗,T8W&,XTv+p`!${K*=P7;z6H(/Dq=.`av#p"@Л}~87zN-3>]{yc3=HmIH{ += 3Um^O |'¹GGqx~nX"w{A 1Y:i[ɹb l29%HM|[*M2~Zƚ5/Abѿmڇ'2%wȱ$!c`d`=%wxɵ"@LƔY,1j_rABqA> +`9R!ӛH5 AćJz5j跶]>c5ױMH~fV=|%b|֪Y)[F =d`==FYWW.y#5g0-o$T\z(Q)=CKr|;t~QT)]Q77ߋ +E. = m"AH:|O@C3 ް &]*gZmE`2-/H Pe5)0՞\ !OR?^HtƱk/ɚy uKfj; L2l-KJ0sȚ3 +eٮIS].ٵ:G @}5οBR@OwkȄAߛ_[bIڠNF0.;VR&: ԀjCIʪ<.% s:(, 2͖,Em֔7vm-D˦Z* Pu/%6{3vir1Zի2zH*GJzRU$_c((yG[iJ+1)$F^ +(GܽGCFkOݛOQ!aXuȋp&GC{|{vS+QBL ^Bi妺6HJNo%-RWQ'S +:O*id`rWX`%ARzs/蔋J_yWo v57ҽɍ/ӣ$ڒ_+~3I_%7S8Q8z~޴u/gHl݋:ǧ0#d5뜢DkLf˰+7:R9z$I({.'(g2\KyWa%҇\A!zA%͕%š!${a.cM)OqS5Y_^ʆw}Luu1uyR.Se\SǪb ښm}ݰ\P se&ž=?{qs68"|M Wy/Pl@,m +,/yEU{^+]{/1x/?#y/N울y/.V_^=*y\FFT|a! +J]|aV27{ͤ \C<h+3Pm9SSaXhy qK@2(rOgyoy9l'/˃HضK(LE|RǰGFAy/a +SB?bOYm{Mhd<_?vhs g*N&[I!oAؕᵼ, {j-'2iPMsJۤŸ*vԸ7u] s +z!>C?NXLV^?e)ޮ)FA3_`auqA PKĸHB/S@ײ2dj~[,E5Bnp&w6~r)8bBWV!HTs{W )zxe +'Nn n6eܦ# |RR/)~Q968yI͊;֦۶ \Ym|i⊒4pNpґukBNZ&kPو +f=Te ÍVE4WY)Kh0g&=!ًK/:M?/G!Jw Ζ^tnʡ*a?XjWU s3(qTv.K؍Q{f {qS@SZ Kj +O<_2Zwj7me\Fɰ9В kz`Hg㌱f2[Dy)~}w00vǹ_ڃhЪEG@(y+l \G3@Vwhc d҅ ?Mم%0~:My+Ìhh"Zd^S@֞i, PEJ< ,{&692\i5mpKӯg؟ v H$Ǥ?J` Tc-=(o(7T +pv'BD%s"LSp#Zlw8'xM1Y F&ƽV7Oܫs2aeX&jXve(#f08uUX#M`ҩ,lp?&a#^D c}^:jd[J  +*qJ*q@RQcJ;A.95N0K> Ʊ'@_DlnDHtTƸɎCG Nml.I%;2_p]fxalepDUKKjQv[keLhg,<8zJu,P56oSq#z/vAtGՕ9"Ey}829+%bp-5N+m <9={O:J-gi4}>wO( d)>WOr5z:=)GdfO'3L󣃺%ϩu*O f]38e%aU-Ħ̈ +UmPY5ScH 6%nt1(s:QU oNRV@xrl sMsp%q䈏™jWF _4IFQH"K +i{g'UʊTH݂e{]:⊭;΁dHkq| vTƬԐ1Hm|؊0b\QZ|h{cp&Kkf}^5VdZZ26 En>W/Gmԍ5JVV>:%-TBì.tD~ t-e0<&xYcOK 1fHwFy %}3HCeh0c vu]Dª@ 8gCg$UCϪOwB7ҘN 3d,0LʼAgw ~JUYZR Ҝ xإn@:D%8K0$I9}] ATUIKFie5n2-yxx$bx2C蜗ƻx,o跳=:s]_O8e]EP>j;lCvC} ۖfѢxXKb,I=Y_ƹfݢ,ԣT#/gOx[5=]ξ;vd 3Da%e/tq}%{x҉X1^/)H,ӹKHe@>xl tzMPJxA֬{D(K}EX18<7nq~(=N]a~썭P:fl Gi !Ѧ{j HZ.)| (4ȣX>mrR bX&}T?_v T4RY-νJY[+@&oV]'d( nHwr+uGc2S'FijL{e5 SC Ӣr2HidnND]uR5>7ZDTܻj<:mOC#fdo<D5>{v\-6c:$y_qBME"Oą'I)5jDvˬSǻf +&JH#NV.z/`Bϵi;whk_1*14R0Xgxo5Gt7=sPu$sJsl#ޜ{dZ8 +1 ֽfgE‡yڇfn"v 0t9j$eu=+)DG˻4^>dwQ߫W%r\xX&i0ջ}_(euQ d[Y{D0#U LIV`DZi&UT&LNIYa/Z;%lIJfYW5~Ph#-dcVkOjGDk\VJI2{-N2=/n#.TbI|_yմ[=RATjn)ca*<; +uv6+'7_{~y襺;EI| ^IwZ\'2m7;>;Ueee|>,3`%ydSYu`$G{ח~{};Bb8o3җ?TV=L3;"`"Xf; .(U{ǧyϿYY"0j$xZS]PxHl9%'_)_{iU T$.sB 1i/k uґ$]"0b^%26ZވG̨0_%,+9v@ RLtQ؉9"&cҺC$tpLHX6U,uRٵ+e_HEE͍.Cčkw,t?FaI~!渗=e{oۢ+1#/=Hsk|k鞕[laCva#,g氏 S̿z܈º˨⤮i?Ѽpm+/ipϴyiL{>(x~]"4Т PԓxC) +E#v LB[}M cdRcc/c+&2E}sۿs5w= 9hLhIQ]6m(9O z$ن7z$G'kffӓ.b)yܿbX+)₌^q/%D9 nA"amx3ۓE^ȋmDG=mC$nqp!Se[#Owd갭d̏ vdKv[UDj#6=호$h1y<]ɌQ1Ҡ2z/;.w.˗^=YgVQO\cuXS.KևőN%SIǷ&AVe d6VNKqFt.KR +drmJF:eftsD[*_>T7V;s#{v:linC~qپ/P@yug}(0:$ & W%WTSuTWuhe*&DXמ<ۡ6Y栮Űv%^ Q`Q0(.5IufR&蛫v"̚H;N+Y9;[aAUYU={*_!]PaB`H&k"\R&\R$#295/$KYֶNm'R'm+֛RRC= 5F [2%򇧁# Qh^Mfzo?q}*䪮 c|xF+̧4WȰͷA~LoX?3BilNF5bYSf:˻na-6!)J4m%$X rʂd(Yk<DסzsPܑrM񌻯{؝LzSH 02dk` 1 {V Fd}"a :(,maBH+(/NP:`P*U?pH(Y(@⭜X?>^\"Q,gggc s\#y. ɺu< NQ}2N_a N&-DH72E˄] PW +͵M9#;~԰Jk,BM|LTT8^'҆DϘO[[ZNʍ NjB";FxL<8-lBTWe(B]:(0t= bK '%e +t|kFA<.Dc \UbƠ] Ytu^d-lO4yO;u$f6VQqQ-vb +朊a7O KRˏalV 9c7=˚m!j^,"=2ѐ] SG <h`?^{%Vֽc2iUغO+ m҂21zXZ@#ـ3k&Y^"?dHY=P>αv֧@μx Dٍȹl"ug7bJ @Nk7b.&*kw{w`5~. +|ݫ.c/cW N5Jb eL$*Wsr1i|QTk.جnb},%l?jA1(v/Q^VYgIdN$N—)lيJ Z|#K™ހ*V>؝B뤞Op6> 13ym[ zkxhMB%~JФvPjo^9)icZ/|5l2 }'ҭz|?}Zrm]*x]+\ީzj[!R1;ndN +V}03@xu/< i'?hbq +n$((n9t> ,ҠGٺ/(DTeյ2zIsxza4;Ѥe7Uu wQ/6hFm/g(mmZ.rVՄ5i^DD$ I!wH>wb +ٷŭNE +=&yϹȺrꢬ ^E"!4K&Խ@\ tG0Uzlybg>zlLezHcWHfLL`* zRq^ A;BiIlyFk ˂+썊A2>W>Y+)o`weTCl$=Mx[uyGc;xHQ% iFSN^\ +[.T_LIRs%e]7OM-^=:QMcv`l^2i.|i=2[H3-jz%zTKt~0\ _`N{ +endstream endobj 443 0 obj <>stream +HtW;9;<{$5 VćUV1yddoeK}4y&﹖ +q)2HYz#D'Dz d{l}X5Gښ~{ lX{ Sn|:zFOߍ'v;G||t6 8!k6O-^qƊ`bw>ĭt3Mϴc=4; sM VG~PEt׵^sHS_y5mGlG gicj"{Fb#,e )@(? *#j.'إ;OٝwՈ剘WmLpoqķ 8_LK#!=qEEV0Idh{$@lfT3! WE,X5bvq"8(k\ ՁW 8qE[v=W a%vfv#5Ie5t`(}oqY*? euVpP!bcHNTMU̧ ۨ!ɋE_$]{^68D|d)kp%;aȹ"\Ģx:g.3 5Gi<8#z1T"Y4aQWfp0_~iy?^HGy gON袍y+T눵oi{RI-Ii\ ;sYǧl#[SSB.w}BSo٧@:s]BpZk%̽[,He.j;21yݴ搋L[d&y:/x(Y%X*O>0,f8.4;Y*Oo^7o$FSkpCι;ڹZMYOE"0ljW +|J [IE{,yg2h.[ޒ9޼KgUs]mp~"̔+9\AXwHXUYT)YohBep*1։ߘcg/rQ|J`^yxQ|[q +$d*]='`];UR/MQWȚv2txNudMqm ǕI5fF.j#;Tr$=ұ,;?wBcco,ǂ$B= +>U"O +Φva{(H(sWT&ף[RH1f zx҉ fOpVE&uKT<hS8V]@3Dxu +5 r ~h +'4[.А~y]/fU9n x2+e#t1]CjvC+<F-~=̣vƎw~ ^\ݥu":a_%#mزsY"'u< 3%#/WYn[GViNG?p{m&+t>_J'S.0 >,T= tes^.7r$>8̞M(={AhDDd""LgSIj*+,ML r?DB 64^(|MYgDH֗TPd!9Δ Oڭ|fDHEFm5SI";j V_Olo41O>E|`Q% bmW=f<0<(Ik#3X"#-(TGZ@+|%D[PNJ&cBO w_̱m0V]˚lOM#ȟ&:5N= +宯1Q_ŃrЪaCAn43щ/Hgn `@ohL1rjV\ƾK 3}f#V co  "}] Jb*,a+{CAYgƅ2Ky0DĽFj(ڋ!\6:>1Wma}IHޯ ;2{VߟF,;::')}d07vZ{FPfi8%7?[7,5S |䒄? +wM6``{> ql e%¡+}}ˁk[%xr[oN.zzзāW^5C3зF}e L}}kbߨCJ5!Gk-$Uql(è_775y(ⷌ5Tț*5=yhv]D6_WZeҴ5 tJ꤆ʏbo2YB;kO89)~ oi۸RUMvlwhqnՖn++HP!TbKw k_Ą*T[:J/wHp?kY<"!Lg!:\'hj `!zvPж>.|Cu\[ +B}fwe׋Ѿ^wxChǺnE3CRrsuUdfPz?FEl-C q'2(>̈椮2O;dFJJχ;%C`i0+^1QVML0=;EEĵ;w;!Z%CSi(kk=f? *B8͡>9gs(Jks͕͡(f+GcOŒ(PX/UL0;9^ky?I8°.韼=D[#$OJ@=,S vPq o 2 g>D+K#ng_Y2C!g`OR5?[/_QJ:¯#តUcP' +מ? _ri/z\F޼e׺IUSs-9Qofo<Zp…CP"VI2ܽ r<j<Դh +X[g +'mJ|݇1; >+yUa}\Cc ]EڽT]/!7MѱV)fXMJǥK(Rktp}?Wl-<}hm\ oϴ)*,16WC6\!l.Xl٨}?~G~6[vR +O)f;GX'Ezx1d많ͩ'6k@)?>\~#VMe+G{6߅gMO0))6)_|| YQ,O/l""IE|NILk&"s2;|Z)51-FP\"Fu\Vh;X +qv Y׍u+N]"ioeӛ; y(ꈾ}9he7RlGiM4l/Qu]PfR"{knr*1dtPJ"O5msj>ou߃¨-fŶA7eh&O"xxYW 2t8c/C"ŦPw1j9_q/RF@{@"{s`}؜/OϽR?r +g +jFp(Ny !A:√mOh a5$eU2)!8$u~]FA,b ~2r529s\; %m\3&y@Q1>8"!4IimR1-'*lKre34UaH#U~Ű'?/񌏶1Uiz~1Rudн2Eʷ+yz>ґx#78h%**Z˔bwv5K|(@F"29!RH62o+}ߟL]hv ʬ@ةwqS[Ԩ'k*o\*Y`42d~LPԩG[׈+*w?%!HΞ2}KtO(,l}G1/Q\}pa|Yڀ"2@ib/ =B nƯvN"Toj5 wQ6k$1*UcNQ Vѣk>hj2lT=i'>,(OJ_N\\YIKÓצc`kRm$YK(gSlqkOxY˕ޞ0:{ Ǭ[K[ C)DnWIGW~))hb|~LmZل Nf Jv},+ckKj(Rͬy&"뗁0݊ZxAmYw}WgbK +(J݉X{'zV_aAjL_bX- :2}u|dq-Q)+4V"RkDLsW-?җdu ºW1F4Q"^=U\χ@TF>_ 7f_] ]Ȅ]:+,>s3yzJ2$,1P<_YU-qy +^6OkYuK_D?OP~i,#Hl(<:1''`Rwm[6g& TQt#ŮeiWÐVe6n|QFQ))艋Tk-0n|"Wv$? SMT';oh!LJ&-_^ls+UgdoԲ".XZ_) +澩<Зg~=i)OLWZ㖖IDBS8Hno*'JĶeɐR}%Xx5D%h[;[gHQ>x^K! +5:nAAAd-/~beZQzppƱQ]﬑X'nɥfO{//mc$ +2Djq"sw>Ӱq,=!/ 67xHʖAt/ҡou`Q:Ý7:(&hNˆOh[&+2VV% ^ĝT%3T /Cul jUQ+SR +~Sa*>uSgT({ĄPJP $Xfes PZ/BkgBse>Jj UBܪJHגɴ_H*+3/+^TkϼJ@a@HXrNoU:d!?UInsW.vE Hě^'T ft{JRh<'o^: +KOxc&8 TRuόjX_++Ђ+L!ȠP^3kB]*bW8σ7EH=FL"&bUl` XBnFPGB4R|̻|ݚ*H8-=hw?mFGmHc QU`?Z^"mSFKj%׍)j`fHdv9{|l!xQrZ"29'm\ڌ $]>~*amO w]q%p1n!PԍV0 +%NHGh s +҉ uPrZfSW7sF7m_Af& $+@thӆ[4Ց/04\&f*Fuӽb*]2l&۬lQզURկ0TQ{>hY_u \6V +@b;zg/-AzaB3ݝNЌ4QkJ2D>5 +V]YjDsi\A_ *%g{9OhmX5?~! k~zcx7-eB: `mo]~srA|95v,Q肨ئODT(Щ˷p'[x;*+m&M'?)p?. To:^%,MקduÐ:HTh^fMH+'YsJbUya??_U6h/1n=jQqq\DAzjf 5Bt}9 (-|#s,9.烽 \ +qSwK/69Źܲ%-'lw='Oj|Ft22Ry4.n<K蕊V0񜬿~?hXrJzɺ(.jXQE;a) ixGf('Nj4ˀX⪫u0Dv3UUYrn "dS@4UFn F"#%kb]C%$Ƙ^r `i156b)[3t@4DQj]_ݴ㶕JM@3sA[,Jq!l%j\_FQJ-EbOnU OEK P=,h@;uYJk -Y\BxcH.湽O5Ot`}@Le ބ+ÜEz$Ho_p/m͸6~ݵoU5㦔W7)]w?t?OC4޴h/b€7RRf 'ntH= +a{x]wñ0mHUhRfE"]@}߯߼9x5S \mMrd94抻1'*dGWPX>f;Hqk(֎q]ʟF9 \TJv]r >^E~1c9&ќ幭 ă"c/ee3r$ȄvV]~Edn@߇bcpm_ aTd8Z=HSzZ!|fL]]$)e6tAL1+fD{"+s56c ߰2)ϪOYmaWk4[S #cLfTɧ1 9QMHs' nRؠ qp0La-&rq2S +'[d2 4e9{u {Q-d9Z/ytZYGU^Zj``Tq2-<eT:njޫbC@垈I@֔㩾Vg24sǪno3G{ 8ڍSYh>@XS U KD"Uz}z$5^k⼧)TU +pTJ.>Hu9xkd#;2'bz C :ī'G2K6=D_xE_'gSD nbsVJmȃ&hfӗ `K]etdus jQR!g^*Dz Jel3ܑ( Rr/g,xv\9s f`!q1kRsU:8h[L/Ҋ$ƙ&)z)!m gf*DBumG/ unEa| ܍^*3}xjM߈v;/8c~7( d +n3=py,f&tJ~z!ocC̢uJR0NiD2Ec!(V,fN 3+ʪC%]G;8HDI62` &Y%zfKf$6Gx! >;LUv $J; $'c\A㯂cٱpZE>RGnEMJnv*֊P +<ٚLyk,Pr胢4uq0U.~LIdBeMs93I;uD9q.WrM ">;Zwm>N> po@ם:آ0(qcd!q&]9 +r/+hupqT}X?S9>r~2mrq$WjBS^1z{[$rݻv*dA,P#Y̬j{r;(kojbuM]Dı62 +M\d]l!K]; k*EH< b>/rlZq]R +V&XP\%C= /wDeE ^b_ǝÙ8%e2d_xԶ3Z-Y[L~.0|%uPst\V;W#N7ގY:?k2{Q&#\'q+K\r p8/$ܳ*>cf!KwO@ q-e 40)cDq3pކ{]S5rH\T(m,N=nZk2߳Z, ֭(x?״İ {Ǹ|'hV*֬Ɉ20id(E" bO\w o’O?LhX([IEƲA*H(R1հ5RɐIwGz@~ 9SEC +/V +K?oxL kJm;ie Z(.NyU²V:UؑtecN6UG^&|>ދcTi]cg>3_M{-Z& +yFR5 ;ZX@3x}aQ?-edR{ 2*W4SFrdnu*HdE ֮~,z+KRuoNH!9hS^h9Y٠-,pc <X^―g/ǓvsP5m0{߆} ދ3Ze(IRȘ{#OAJ a*~ Ƭk \r@"I2eM'>xdJ8Ə9+'V 2R2rBߍ~+D(z|sʈ7ׯW|&cyeKo18ߙT-#hlC.Zqh])gd̚$||H(-E0kB3 "A󪆪rSCN_߇xHU/,R}rܐ{+RBaܒK㘓-k?rF9VSȄ}, + Hr=*X~[M_}7{#/e Y5<٧ڰ?^t_&rv|MzyqQCc-:O M I齇ڀm8f?Jtm@y=@SrFoHx]Tܓ,ntNt|8պy yV)Ot"-"I./hm0G 2= 9=vL9W)@2?x$NjG c ^k8q֎gh_ ֭& m ^\qoIM:QgR+{!jbBx2>f;gzHM/npuMz.@{g05gO3opC(inhh%'\K< CƑ\\X2/H]bz*+ #WAԮ!::r(܋Q@bS.Dnox[æT@)DŽ=7$i~вle&5/ˁVF11`_ugK Ƃ vl6|@k`M ߻y"@6 gzFf|/F]kUiE8l %v$WqoHkDn5ܜFz( H.=,?Iw*\ȹB#hoV\M%@hJ:m)ta JWlٵjTӬOiDֲ]ktE1!ݶ_"z@DHAKDV0(žձ+^U/$6O^m%7rD)@nk}?\D EQGη:Y2[D= +[:z=!/xl^uT0P5?H uvi(tPϪd8 +pͼ59HNRCYm7ߟQP1rA Y6~fs'Q#2MU\cYL_7Sz&yZ0'A[>>Tx K o@`DA\<2[G9:4l1Z, ,>}dC h΁,q CLq6{&rUs[B GBcD + n.z@ztt}"x\f3D%h=9rcܸz)uo+HHJG6ưT8ҦɍeRKՔ_b'L7=ҸlV"|\ ƍ~/`>0SzHn԰ϲG0܁Ja05VU JBʂԗXG5 !hO+Y!Lʕ14]L|쬅[W{قfR k1U .lYL`%ac(\/I՞K틢}xAֈ +Cnj:{U=&gS6a٘,$"H/uJ0QxUx@Ӣ~iLB|2|>*6f%_)%>rɗU8(7  I^ ?>HrR(;ҤSFX\yz'Wk_Ҧ<q'ǝRj@uI:^Tӳ^.cՏ끽Z#z6x@y?J 1,kSGLCc).nMy DVY2G,+>R"#Ҵ<7XV.=Y^s :#:)t@:ǃv-CFeDtL6JM"!zҹ!߯5F7{9CYi}`_]vq,l`6WyiつS:_)R]F\W4Q2@Sg mm_8Ҟ9QmLD>-0.X,ӞdT[Ɏ> Xwft^ g(DOV광 ĹD2"(jsgU7*AiQ,;R7tJ#RG({8 +O")U7[Dl^{ s.q \%'*#rVsv$FZ{4`GV"ò.;Xk@ͦ2kKߎ FK}ͽ\k4 +"O{6昕Yr_V|>A?&. ~ ӲSaDl埿s[/mgrH[|k[Ų'`ɐ[YG Z[.^X-Zpm7 r6Obev'g[lh΃ZޯwWwMgv^%.IhЫCfRըk:R_4eDe2VK&( c07Q͕c&Uřrc;;]c䛵vmw,H{u/@f:ޫD,J׽'2v /fٲԵMhnsC4YKzB})i[w&'evfv惦5 $粳*gGbedD铦CVgjz8G\Ý] +"F2\oD;2VW#̏Lh )N:#MBb{r7kz}w(2y X\H$7pMrDZ=$泓#4,B!mo pVް(B%rxǩ}1h[н1{[[Ip g/{Ru3BΠul,K#ou[q5g*FZhFF c0T2:Z!G{?n^-r@/;GTRzFUx 1dgm!ԕHը#!İiU.N2QRK݌yvj6%G`k/ +"E `{骕a=*3=4݈(>~n6܋)Yg}o,qa|d }D1*<|/քE]A|La#:bnt*њD$: YAl +wOw;lEZ{k^$Z{Pwڝk J!Ķ )}K:?&{?O4??ZM;R }:;p_zgӵ>ì߁[6?e-^ ƳOUUMZZΕM(0f 2ӊ*@@/R-}6K*UbGSr8!JG mAuhbqϣFUJ.j` c :P'Ol { d0NpzE*8ūY&TV(wY )Cǧ>*t0 +w&ؽc% M D}OǾQ1"C9~!UU0fD՟? @}v?\h1JViwӑ@H{Rjj#cxw[.GOPbQnE*O( QuP~di=u;~-mLCԱy D17 0eNuV7k@ +j}ξP5"! mr"<oP7eeC*4.C(~-^Fv1kɨƃd~I$1.O~V^rj T*b̏ 8!j- "~y/~fΣ(6m]G*YN£Mx4TFGӛ#Cάo.Lgݥ'#]q./$Q^7H:OӼww5߽L*|f"j'#ƧԔV1ړ{;,>{y*V9Ojag|_W͓U_Y`]F6& y%b-5&o3ZfQcLt+V:Mub-sD+IK +Dh IĚA'V35&V}$VU}c^U#Sq *RjP;X5։5'͙%Z ԚAVEtvjMNjH[@rĭ@'rM\ +db]MYOnVĭJΤNՖ!C.q̭@Hܚ[ops3qkѥ̓\կ3ʹiE%'jŎڽ5:j*j'jbĭw9WOiUٟ#W.ϯ0;T>Fe+Y`mܠ"̏3@I=AH!*2}/n ~__>@ &P@??4~_= ^}qԵ ߷h[pUU} +9ngڞ@{ M~{ӟ1THwE,qcԯHj[їnd=TdY-m[+6{nH#ĵUSqrc\Gsa*Î${~!:,3yZ`T{T4s*I@|1qfH&g>fvĜ1#EQ3%dHdwA^nm OROBju.1Mwjy/qK@ 2}c Ԍ顚x>.EЗk*K'|`B30U]Qp"Dg$r`?Wgo(ARn.TI@mW)CQ(mK&Q~Zyw3|}wWI!1QRqW 9X +߃b'Cic8℡H#zfP(Sܸ84$_DZ1.7WuK'z mCZAUybNl'F ͲA'(oL1˓2?]7ʏ[|g@#Cs2('Â!仐O _&'5ʏ@vceiVs0Hz S>PIh5)~=+ʬޥDl]%c@ dLdMQC&r{ pm_@))]~X dn}Ao$4 J 9*N?UFC!$ȌN6+S1* qIAE}:O1/JF>حxڜE*!JYc^ihCVflxݱ2 Jbĵ}Tv+l6y6إ9p}`:Y#s#21,\V2K+@ +Knpj{".䐳E/M Ó +0EK8}ujA7hKl%$J+R=v/pf$2 ù}07b% c[9M`G>*]|Σ5 jCDGV@/^@`FϨ瞥 ggMl|$pyUS'Qo7(j;{UUe4t[b8t蛽N1DwXƗM&7O_rM-!oynI(Tz!cX)nPonDx7˃ԬvUk`Yq6PO~Uk2Y1L5Еx /b|dKp[?}m X*;>'4 OȰ'*i9ya| t݈1ƫަ3ɜ%Dfƒr*nW.^sr1P_ |1P*&j:w2^͠`h;nY~DY`9CBRE!8lʠC#'DNd ڄhS£ȎQ[d27PH;>1jBV'> @${,GQ#dNGyP^|,`x84"Ldz7R KkBl&@n +eu(N>2> %괺d1g0s+/ +endstream endobj 444 0 obj <>stream +HtW;Kw42I&ɴn!`'=!2t}ͩcx /D +XĬ}8^'v!2 \!对G"LY(X$Ybn&DX؏"@{ Tn=$M/pl1})ӵ7{a -,wAv!|9c/:jw&!^{i;kVzpֿ~*lk^KB7@6dw^lT(Y\q7Rk +MK0 |/oh*)~ʘliGkSUF8[o] QrT 6Q}\jmDFEQo h,Rkώ(GR6W2mf2l~h60->M\?Zhb8j'lC)|_ȴ\iQ*+^_2ΈUz@D=Ո8vC8X],&J|p#ϢG3|7hYGo1%,o z6e" XDL}Z&|4ƾX٣jӢ],FA +_=X.)bLr萤"c5#F|Gx6fDԫV4J1Z>!1sK͢Lu g)fC#zdcLJ@}, ?jh{RN&>])f;uʷ9G\2e?\԰)*sE'L!aGwv# j$2W!SWp'J/4S22Ujv ܻܻ?4 +A tt\9RI!3IG `bX԰2^f5-fiX,jEXf6˵]dBUƔHVz_%2ʀA_`\blpMsW#yPcBqV<> 0:Y01؋`X"ʡش[kjX }juzpFgUEE%jto}`\)'CUe _cԌN*1VH B‚_7԰{MTMnhH1ފ0K#Z~+P\'dh?3DNEsLĄπ U@{}//\ b`qPe(?LPZlj1j ͓Jy>1O8޺\1Q8S0|\1)zDVx}.2sW,izrϼZB7!ȁjCJ6Rm1`ZV?[J1]6FDB?WBrÂ"/hmK!N]5@q;&iBΦDE ӱ ٓ-Rދ4cN?q|M~ +n??,L[ˋ17ԣ;uoXDd/Dwu6;g*z"?Zq<}$.0|) KcaΆI"{πRI=U>mzLMy12w%l]ZWRka=~hQ@ )Ō42EX'Df"'k٠v2! #P62[^RqܤJ*IP`0Id036 u|0H vUirz#ЛJ.!XY ض )-*UX#F( F?eC*tK+ӱS y,C=W0r!<Ԕjt=~?E4ּȶj}cߍ5aL플%\7,(DLJτDzubxNTDڝnEҿ R(wyi~d hk,bULk"D02D%y'0}E8M7/T1 ].! اlZRq΁\C q @ + ܺZ\l V7jϡaP Eu OT>3{0j)hYpxs#V9}"Kb:e3M:e}+{ |gU Q!'—:{#*2iUޤSV[У1't'y7Nc95?U.Ǫö60o]dzmASua[elPz+B!#%lx}:9Cnbڟ˰mF~jMب=L_i]|Yh#/PwEE +'5+ƾ{ueoͶ-P9:MT22xtH>[F_W#Q>%@߬: uMo~o`]i욋#rW ]Snf}J}UXm>.M%RJu~.Rн\̌,\CiuVIbKs.uG¶sxF))< U//^Ɩ#јYր1@mӗ µ9{jt{per8]&EMKSlM+F* : &7nh@ +L{>jF/XܫK p c}8\u g]/WaOy-zO8=l9C?uaR&HI¯1Kҥ]o%oǺ$y˒ޠ} \ϼ.Y S/%L*AnY@ʒf\Nsd ҧ,ƍ*FfUvI..]BDdI\t%NC`Jyo:%ሶUɩ "|2ۺGc2z>e Fl޲oY]tB)K1h% ]V} +@ %F0` +Jl^'%zɒ[6eY,!CKG'uK6vқQ*?Z0搴ppF+^5)dkF G ?psZѵu΍Ft{󺿮*Vt5:(}g9*:L tk\/zco J{2!? _KuY3R$]wcHc`exW=(ۮHF\5:@M"_|XYոj.>_v~ipSZ *m~o/y*م?߰C!j"uHLXMeĠ+'JÑ5iPƍ\Mpes63gс)A7vUvQ5aL3 Ӧmζv~mekh ̐Ҟ)B2{.7&bw~$l[&,-)82p6VMy6WF}9kl-W8GZaL\ rCO!ૼODhsiK)L:t#ݭ&PjYs"aSs̼i%Ofת^_Bo0p޿3k-ůX_{!3;7j$1`g,"M,̋JϢVJuJ#=y]F*3 qBN o0 Jft(]^ٯE2uv@G$VE +ZRtCTDH9;8JFDj/4ON7x6by;x,xyW|q!w}v6TAͲ8@lmhkF +$FAbepg2) ?y2P"gFk\Uf~Or=@G'W͋6kLN`.[͛ǯғ5DĶK{Mm;SGDK>ĝ[.+Ed]` jg-,!Z5e+);v&ݵ>Y4(s3mwjgC><0ڑp'WKe]&Vr_%EjfcZ"(<3YuP IcPȽ$x{Uv?2\P''@%erQ"6 +!ܽQݍ'mʡ|"ÿZ֋8$k5E.^p[x(T q#0#V.5'Wr^:t*$Սi-MkWǦCBSzҡ7u?lJQ^閝rTQ ~io;!giI2FŖz8+aԀʲt(-}F}yܮ%?̆;Dvֆ<ݨ$7׬FZRś`nV=0/2G Ϟx@DDexсu2X0ĭdz#|u+{UCZ-`c]*띭Dr $KQ`HKbRomxa1RбP?pW +ʗ~ fzN/g"s2{)fU~Ly鮚ZV?0û7Mڒ-(eeKU_L{c.-kBk bF(*/[JG(ڧXߟBea07Ohnڣ {Y"Gς%3;6mHfyt1x`op,{`@Ɍ69/&MU/k֯ag>{=7ŝng\`Tz#6V`߼/?q8N6럈^Y+H7OCo9o`zDg"Mz^v/>߯?X˦qV̬롽o7bOHО=@K/ޛ {b̨,>{FnB?x󦣂H5K=IU7Q!]cwsc,.ĨWShѵ>^g l*rj/Mȁ" ÑFXXfԹ< As |vT&Hjy|xxwBTw +Gq緶(7uj:BMv"fFo@dL :F֪y;f^~[UfͽpI(YojHڬlOI/u)xn(˵ H~Йm VVl\M"5r5.C)GRބEˀ#= +IG>HZ=Vcs `5@/~LdR*,Z +ZбD8 xpD{V`9)zǷƴs7ZJqS015{=>t tO\k8Mw^`k^`k%Iʱh[*3?Yw4^I5V)hg#կ ?.)^gAblI[b isKeF:p~R#tn @i jI/>յ1-˃(NײF ^,%I[{gT`&Ug$^$}@΄ܰ-)^'X73H-ɬ#L3w j*Bd&ly}9Ku!0@jTԬqXB5 Dx9ZD1pKy ZRQdKf dɺcp, c6ddl%uRb,^ބ7`,GղpOOp.Zw&v,f 똬'dHFYz@QSAgV[E,P}N4j,="+V@FVJ@e_ngn| [)]qBd5Gl2ڥ^Vӊ)Z0n%\gPϨ5-Tjͱ%LPj603 +"_slFzgƆVn!kVZ{-}}u B5˝\%m 3ؑ#ONN' jM獵tҙXVFR1Ͼ +mU jά佯#l)d3/:R4n36[7p8Lw^dnz)Dz' 3(l '箳,]{5c[9Rc.JFhӎVӻ>疄 M +A…Q +EܷmqVZjbMnp+|F/_JAwGNҒ-E +0.RVe1R9nl?w0X$`BT8ɨ~辬HX褜 TfH0$jhF㋮hqi*iY3]mJpUBNqAbx:P 5\o%tGE>YC 9,tjG;;eu&騺򥺞p^y>˘vsK t[[T"n͵i%gؠ(`+:;ǼJԙVb=$1C^F즅}_52v˂χ,X+\,H3 [Kѐu𦻜AFע껰=Mfh""].;{2L0OF{=!sp eѲtMu7[u/`Ox_0-(76j@Mwr{'6yzN˶xH +!ST@jgeP^豈уbAW,mJM,K, Tv?Qg=nra }5HH7(qt g5i)1iuWj #`]y\)qG~m>6*GGmr meofeA]J8߈EQ]wjsAz~H `U-v^w5(*w(}Iϻ:-Lb{WQF 8,jyF9 ElX4`@gmơRZ"OS q[re06/@a::Fq759BSXĥ"齠֮(^qLtT|n;=H*GVڤmJŦoXU=&$ҶA *v kg|Yp[Dm *H$m3OH$~|NN۬_!k4U&?yh5pzBEO',˾h-ƥZ5蚥3hTF`mkƻhxT۵L.g|].}1૞T SZ#]bRrvטK@0]}gŀM1e$]RatzRMi|ei`TƝWѾ-imd޿]:pt ~JY-<->Zbe +lܞ>λYꒊD20X23&p7̇B]c_hoS9H6=~Dx sYS%G!9e&0[RHI W{Tvsɻ T]^Ec_HY.jA\{tq#ص7eh_?Wk`O_lM6OʯgMhݺ!xGY.儼*ƥݗXoU1yàSHȦ@$U"5&L`mh3!=^6Z5u>$N~n6v%fA.ymm9ÃKi QP~E4ΡcKJ&A/Y }Trle4h_륻qZO^9Bc|+fiMc6nze{@Bohyz+ҳ +yھOn.3ڃޏ]NM",^:IQjE'""U+}MCQԹU8` ]=iIʱ3baබRyu#+}=଍<&%/j³$u$,y^V {to!\O* fI +)@HVP}` q.H7)rMV~0RT] 7Hmj>*YIy!2ep4oӈ@3']c|ZX%Zm("7lG~Џet3cp +#L˦Mn\,1%ucVku 9luiU1%-H:OmC5aQI6kBp!)i#.'Cc˲"/47x"H'jfm;aJ4 +Dc,#r ))ZĎ`Ў{Sd)6;=,2j2Ft^ F?7v~kot⧋ Fo9{?[3m u7eT>'23j${]u!zJ{gģD5%)򱊌iSnl.*=}$]3e4alE<dT cvI|FN[˺9zGxq nibY6zA|#X(Z܎Z5쉴9577*-[f"A}܃N5*%!YzpKH@Zޕ\=SAz6Tibx1΍dCR|gL*yZkOTIW0k+oimԳRcfmjoÄh1^6y<=Ul"/d qot)F* B<28eXQbqɣ 9,!oHaRjcq +C2'/Ыn,GkK.jە"N5+LVx2mu?g ␠덝j +mkLoN*89kCK%3w9 Nk? $ y" e].%|Ch]bb Z>2Yd~", m8HX4]U=gSZck4;h >Py:\ƃ AdCNm2#yRz1whtTDZZ[ UD)( cExmj$UUlwvȑe*!l#~#ۨ7$oٿ^_H]4 oV u@$t\.Cvo@O3t3dF Aq@,{ :[`!)uĹx"">Cf$Ȼcݿ?&:"[ #(>r2 @^QDpBlk!m~IqCMv>nj +Էf=LE0Y]9@5ꦛAcz%Bz,NM]Fw AOX^%sGWKPe؍Fy\Ohc+a!UgV!~_*'C~ٻ?'VVk={<=48/WaS[2*?K`Z"ѝt d,OHYw no-w07k ֢fBs$P5!?{`Fh> /c5Cӭљ," xL(!ȕFc ;۫v͆8n͘ፊwUBr&+x-v=ATYؘd{ 8p < DL(Hn+7֞܅:f/^ dȤ@)Zfàз hESDBuNVMW~ YCq|B}Y9p֑%Wόfz灣I5O\)[EϬ>'֨Wg`OԻx8I%$-@Ԝs,烀GhD29y6!ǵryHl!|R&̓a֙O +PLUH(${&cvgک2ß*"dTu+#ug6?O \mϏ$w^Uݬb*p9}WWe6hZY*@yJzSgYDsE$*ܞTU2f|wEۚ4oXu6ؔ>bAfbByGvUEIN \̒&NDsLefG:JYgNaSk}]5iV_)ѵ!M#J [{FQb\4O?@h-z8K¡qϔB^qQiK'#di[3II 3on3xq`Rֹƽ YL]-+||8|]KMb9;i8)UO4td9ۚJ WX3q{_@W=C&<&P^+z' +VJL-7=nt/{vR)M_FbJɄva".WV +]-i0*9A> 浞mno#ۂru,[ !Zh ^y.?P 1b f#h9H3 7}W fߩΘ;3]wٻT`am/BS_V.w\4T' xfόhx9]wˮK -0]y} 9/;җ}h?w)ɖd<Rx_tߩV̖+I@s$P% dU_S1;ARz|g̠NE^i^*>2`7jE[oi0Oz\F u=;j ҒBi`|NTiyy|W)Dmho!`fPR3[e4ޟdX:"ڂe|!m{1^K׺u3?W,ܾlUˮ֐KrE``r}&-im{1 iW%j 2uZc[j$M{OE&K_h7յq'jyl_Ey9/eˠ+$eҌ[_Y"Is./]kyYɧ((o﷮\GB ٦W2rt-ck~RIsu$ _>5/ CDPfY,kle0dՕd +k,Y5 f`=\"X^n~0d d}d-W2̏29`Q5@Wp݇$U;WGjV e!WO'UL|LYd5Ҥk'MYil,]X߀cAL>Ӎ} :Cf68L{lC=Ӣic>ݘQ錽Iesz<>3Y@M2.p;S}bf+̹ufО@ cе) ?+# +5jQc1|XO1X]Hm-rBbE!L^-ikj# (C_0[i)י$~b'1풙@ғ$d1P1e&zR9"{ibS=w HV)썵HbWgv=$f${pIUKf';mf'X:bV|2d(''ZDN@29]$SƜ?MB +nЎ\bTk35{75YDML%9/:/9Rƛp2%^2p3K5{%5@$y%kA,GK@z_%:% /YJ_OVP̨Ohx3~]G@v|2>;G[L 0hL0mCiU;, +|wYod\\!_&7o3z-:*нQ|y48Pa,'vxL(z.oU{k@;c})՝]Q5;)x"`}3gq~]=8?jZ"$C\t@ YޫfKhyԙҸŭz]Qg㮡earK=8fU ќQv?z JxDȦ^zg_j+qOnӧ!V->g{;36Go%/_Feҧ"F+ܔ.&hjHBV W#UoɮG IU6L*:M2Y56 +7j~au@,ud8E֙MٷV /ZccoJ)ڙζY!}-wQ438N?)Zu=~Y*?״žRa#ٮshVlsesB~Y }*ɮca[ +S/$R]獒 :6H6AdEVmЋ Eg˅/sy^YCPǼ,sCͱHZ2#e,*i=J{&En 42xigW3W3cѶ ]*2><3yfirGE+cPޟa 0ViM3zLA.|cac#]sG5sݜE,'6/i:>RsL3γ +PJ jNlei5≭,leUm'BD0.Iz}t +h2}Zy+~>g޿ +5jè<%k"MEk){vjwYt7@`Btt~f5bj]6HzmL3rd-"%:F10iJMz9ɋә#ҹ9s|Q&^2'4 ϲ켸Ȏ1s\aF-Bjw1¾ZJuW#"wcн_g}yB}u?OgzRPM;ݴ$ԭ.ZIN2Z{a4b[|*}M] P,}ՆnU6Ǟ*U׭ʗ_ $7gxfz_9~m`h<VZOTG UsK-b\50ؾ>B4nO9M!E;>N<]"%J?|C[ZX4`[J5[3kdZ;SyZQ0GZzH٣~ % +ڻ*T՞%>!ce @*WZu8e oFعB,w .ymh,Nh~=7z=ኼ" [V"J'rN')*xU,1w syg!ߵfQ#Rh SRF@ǜt[yI61(y6_5<F92EqEbܹ~2וbl?r#ǃ:J2a;RZ9* NA]5_epFZdL7B_G[ SZe׈j}ErkL>-N2=?z+!paHuaC޽j8XRvNiII+obxX'+E8P9VoOl Rs257Ӱ2|JH4m)TAQFCs߿5)9\xQ60F麉T + +}*qK|ԕbL*HIc>q,4 %`< ġ_*y͌ &]y1+߈yNsP߹ 0'=Px*WLlօ#lGR\$EB\ϛm8*P){&STkH[ܞv" +!V{%)4IҹiE4 ~E|` n87!ʌxU͏ \ +nbΜeQwW"SYΈ)Q0:3֞'Q FZhh4Neg), \SiOsu%2|:ٷHc>JZtKjŬ?e+-5uYe]}iGFh ^fKAcgnigc?O@ȱ=.R.,ܕ1Mǜc]ƇRX|4'Y#͡C'/%jLUxO.!>'*|!N G2X2V:5\r4]*lKcMxzڦl[, M//,OE~ګ`ZWaհٞx!~ww opLEf]D^o7l3,1WzO(0GD՞͸j|=m&e`~%8G#5wKNڃ8Zedf}蛴 ÐM0̲ @;nsWVÏ%J76Uf\g1CA{`릐9CaYJ '!6%gFgbaI Vv5a|@|T`;hAÖD9oI䚋+XW:ÎF[WWMӞF@4 HX^kw}ys뺵~wj]떒fC`?)z>+G51^_ &+v{Q۠|#kdOhYYͪBrAN +j<0=4ߘQ<T`<"YOw#㰫LW!))ު jƁI"!tw +-g ]G} #KrR{q}ǖٴK@\z۠!R`e@UjD=h<pgA6hDG,1ߒnЬEv +;{ӫ~`w^q|#ߡ;1I>9zS8.nA9>JvS=o0jfN"7;9\6.>zWvs9ǥ.szTӖO)<рFŸM6=brx1=[g{DA\SޞJmmxY+pM44;rgkqFgf·ɒhbd:Eb%e[qM H͢-Sht jX@Ev k;(myec1ޖlK̞/'j !\,AΏgŨ `?" +&{Z#1% (%`%z#4]7SlN>"1bƍ[q2L+1O"e|'UFYgFo3Vʔ^{sHήE+]ĽWAx_P̍8,G߰KY12ﱳ,v+]J0 uޘ+!>L%z }dNK +h&P'4K ',O吧ޏ%w=?R2b/9#ʧ{\ꘊU奇\">[v7 2ַ⮧v)û",$߽6$DO9@g{K?oES^I[GYw%Xv$ܱ$LpI"^XUGSdiGo`KRvi"EDTJ?ȐsD^7KMdvb& +i5RYbM$[DVn>O2ȒN˻PuE)&>إkOQBfk)jW9}7a(Ib ڙȚw +Bl%ثV;x\P^Va6R\l.}Nk/}1ww|Z'^;K߅6Knrd b[[wq 5wKpb>-0]o'ad{ICij*6x@xL="/D^* +̙wx*A ٿ4ݪQu"\,&@XVdC%Ҫ񜈠wA~r#xZY1\"nF緉|FHsn+PJ8b<셌ydčyms Dtkw(joiO߂zYTۺ#SW* +[M-g\4eR-BHM9bt/=kf>9R'hm 6 +dq%OWDO8PjȱcKݵ3"5 SbsR63b31}/فGuN_[̍-AmU}Yi}}^j/_N~Cz_%2 +ZI}vn>ˡ.%u1NArbeay:>o$vt "F`uoLCk1=fֻ qJJPkqVQ@pG%/ׅ +endstream endobj 445 0 obj <>stream +HtK;E r )q7.У}Q"<0P>))$~/ڛe׿O'zsk:Q6 B}rkkxhnwW*7l $ =GͤiU"Hkۦ~4р$u⧩AHcWzx^PY8AQAIY4) +2ښyQiUẪR5} ba8';ѹ 2+r7tB_xF3eЏeS6ҘA 5ݰ.h7G5LfD{K/,w2d@Έ4'e"O*+,O y6&,kgϿ0lm]oFc!l` k]Ehn.ɴiS18 ~J`2 +Φ3eBZ'dڹjL=aD*N}#2k<3U &,}տ+#/xF2}%j %|K+( 2>!T.o˔TW°GhsӬ66AX*%zʗUem辿Ypj~t z&I^I]C2i<(/p62LhNHt9 jûJj'˨i$g  +?cGW4FlW(*8F5'o-mс2LW"ʧ }"MͻsY .bƠBxtA6(.w)h 6D4&f$ hIxl,dZD[ew2ɨZ\erN:W]>$ )۾ȃ}=]Iu9*'nAcxtG:uLsez +6TS@ꢩaTU M5b*WeN+s]b!mr;d#b +DBSnU=ĔG&VIgD\bs)|,}!<Y)<'!ΫCL}K-ڻ:o $CU f%Xgudw=]vhs½D#q0ZM:s'A$?ZeM ;j[еʂ7橡oh*3 O{{'v^##-҈bR!1.gd$NrDJ"8{4vne^"jP7}UUy x'%nxzGx|DŽT<&reҿiCBʹ X[D'<H{Bpb%{ɱcGɔ#-e~%QݜNr'yC)9&AǠصZN +m_RoO +hC1)dxD{)kTd\Fm̘@෺՞?kVY̶Y&ǬpC~ҁoqG6cªϭ?Ǘ>O<C%8JQWA+V#] +NnlhKpJE[SXby2?4t%6Fح5͇RfYB6B^ !jkSiDJdf& HJ9.![r M04CCtLj* 2^hkf y1oV3)7oYY~ ̰|ۢ¯ܖ52%Z)/MJg}!/0䥓a%C_ [R[^:+s=ԥasC\z:eq  +G0*=R2r85$*E}+!9QrӢQUbrw gg>L^y|>'~pYS1

)}JSVMU \CzjW +>]AJ!TS >pcN?}umHfzk3Wq SШiEB"ރiYET9yXVۘoWg(\Rιi->d%@4h~U܁T'(Z6#8z-V^AHƻΈ lJQu7TXyy,B*:/q3 _ݩdП'cF.sgKn7XϿƪc^5Ԝy,r/B$ˉ vX>YcyQ`:.gXS:;,[TzWnr*֐2C밡+umhnv¯ב՛Y/~t^fd왹3=:# 蝹ahsU%VwU9JTYTRfyk׮1:/A!{=,,2#ڌ`xc+SBE=HFբ3wx.kJ{s檻jZ^S BUf%]!NX^KiT҅BM {AB=QN&PEz +wR\hu ^C0ޔ#|=?0硵,{ 4w^%3k{׼qڻm O2} g+}d, ^v4fs1M֭DCh3e1+B(%ro ˦atU8@Z{%cHb-㾾0\ *GxFV? +lZYeQBÊWqLYܩl` H +6/: A ܈Rt kGus~ŝj;w@ OyE'#HhwHQc${]˿YuFQKf,%4S[AR[4ﶣ /㚡3~嘳vm9vgy6"4+IrT~j'mT;z8~l⳨/eoVU^U|Qmf~BѦ% s6y{lv%3יaZ'ܑ!ODMEku{-[胰d9 o<;. +Cxֲ'ᱫy dA5ZV*/N`NDm򔃦6U<!']NM,7 gN_yH,aM9f0.! @ ,!-Vר6+^! ~cJ'X|˂EI/?<_j%(qJEX(Eh>~-ljXH0AY1E{ >/.jlv}&"o.@ Й7<Uo(2bW'1 HOk}3 ]^2GEdkD%V )$m;X-77;?VQPtCC< zʼG0%f=ߺkV.]b!GSbq ++J7qfP0c;֤\"cN,1 Jhhb,3t˂ܵcg ;n+BiޭLKqoX g,lI䞺5-n0JόWe v6αFh 0GM# ( Qp 6 Ίf! aTfQs7_kaIpB/K"Ԯ>#0Q9gў`ㆥ 7+%<`hg8PD,bAvip%BX?g>6..d!®lW,77kFaS2`MGڄP:<j4[l7$5 xu-HQ:N{ cSfDe$A(ǮGs6Pj5Zj1Ak,꘱DeF!͟>|5[}{$: v n1`a9bP2O#i ,)E$nb 헁dq;vB6B&R]:!xK^ Z+5>HDW&GW?L3DW`=GWJ.ޗE 2qO?Đ-7wlωDlk3 [q]-Y{:l+l0qzػ4-CQ|?$YS’>}lG).1}YɈoqEF]~\7XiZVsV`}UV/-uaZ60g1~)w=oLw["DT3aZγ-Ҵ;7L2 *z}nx`뿉l5}*]8Oz9< +^['lmkm`4M? g^f83U6Ǥ69 8-] f$h&a;6i]:EEMpGp(x tۄY%VxQʿ"S-;h,3'*^D㮃hlt|!4Ԑ$Z-nu뫰K +k+A|]>hF[%KUvE; 5޾Qg3IH@f1Wz;-1hD*)Dd@,ދL4 G`op @cĪODF^*+,9k/[yRg!(@Z f @eCOόޅǏΪ")0H)+zYm VPjfR>S4+mp_)se㠸P{IkY'Қ[v8/<3y7ιd(8$YiK?o렢GR+*Xa G+VAxq\P +ϚݗS?"Fo Yg-H6GqW1#6##ʖCϥ>Ly+"륀q+W ӗjK7p߃0[y˟nJK.Gɔ?ؐ[P`Ik']-~ZEnCuQ?yu(V{_LkXk> G(}@>ߺ1t|uϫr޲Ui1^^Uȭz۹6z@j}ޅR5V=\9 Uϯ>Gj£z^>PzNzrGKt}D%z^SoRз g`uo\lBb HP^V3VIAI0.kd|LBk!C&ԢwQx! fhTwEj x;DHA~4?؏33c)vmI +iK=ep â^I{֔UGrC!/=}urѥfy C +*\s;,|UOA? ]}Nj4yw5('>3C5`xuP_g!Ÿ pbcv6b3KpT.U2b K#K~端#[H0r=lIns(62}]RRk6ʭ5/Ui3h?JjiBɾտ^@|2V}UϺGLVci`@l+kC amJ1Sc!ʛ,1KV^ЯFZͬڇYexqC5ZC6jQ_wOibj+!!_do޻=%S潟.rxgaߴe1X{ܴǮf}Ѿ!fSo`]y /_hh7>iE{u{Ҟ;֣@ў7M{.nE{^_6*b9P٪9iLTOGzhMr:o?%Ϝ*ȥuUh3/rFdqώe&RZnƱc &QpEnZK;vEi$~!{:`EvGABbE{}g뤍FеU֒nrKz^uo7o{"M Lfy灣Ȼ?2=צĿi@ke|-O}G^㟝lZxM^YE^BU=(S~ǢG!H6nȖ 5.E͎ iE^ZM;^ .;Jvs4RImeL`}*2e2G=F^\b@=Yh%|6 9f}I<.V_a1&s{W"kޚ$995`@~|dtϔ5S]'r2Z\*dۯS`Z +0m90#Һڪ uqv]!>$NEP@~q9Q(އMY՚e9"aʫ5 +_eS!S$0D}>gk~Xn J A-"΍1r=f +͞ak/0hDwQrsݑ֨^&j$v4)ӾnuI }P=BtS5\ߩ/uh!|'nd)fk]heT.{}dӊhyPHXF؀w3\?-myF|coQ t&êwjf> /bT3@+[722hk5~In ܂݄)?[Vѷ5KNK`a6g=_>K鉞~k+(SQS8`ō{R\uϿ?;$Rњ񯾖?O@s@KO ̡8O|L( >Pݑ +SFPz8'yK^liI ( L& [7޵^MNGr<^"+'TbA&( k~_e[.c,76H`xmNM̌= :':# s%d OX4GPe?Ię;&\ /YC2M-djkrg5a#3)X zxeCo"ΞڛtZ&$H<+3K\`Zr[VUǴQ8g{/D>XY5'>e=ĢFӸj.#)LTL꟫AbȰHZ"Z AԒp=7 Z~JH/lkDfс64~jSn8Ki?jpLw(QIP|_ICr3٥2,r*6Hm0(A{</2kx@йY@V/Dr>NcMc$-ю ۡg"%=[Tl-#}fTۮ4}bj+b"b8!tQr[><:x$=DONb35# cF0![] I? nq,ʑ v^9z *}{]7GB0aon-8=ۓSAߛ+k>u(c\BxQXKC1F\bA\ hjyюW!uvM ' +cO}(9fTy BJ|!ev 3 Μ2$nQRO @6,="f! h_1pY\SHC(x[;9^;m)M;MICT##Gj9cQ&@AF~Ձ:.WV:)).~uuiӒoN;KUlyU>6{:*g_:JMyuTŨZ*$r5@P[YףUxu rQYgeX(pP7uTX4N$䫬TAC_Q}I˄>:f5:xMi YMQp24,Qutcy>+ +.}/YnVӲ5DZjyۭlGI{Ș0Ou׆><ـm{.=4<-gnh`6N9j_}EUC#ȠQ[hi^9~wschz r:|Kܭ*BLa:tR!#A9gMܵy4ou6UHև]"TfV$I#Q-5ږj yxMØta;*-X %&Bm|m*ݺX*uG4G7!J;*U(ih&mT_mLѸ&Hḑn#;:K!E}-=_q EQUss11HT] W$2 V +o0OҴoc7NJiUpcn"ȰwuJTW"]3ь8d/yBVߐzgݒ!ds1"-e<.07NVe&?U+b=Hj}=&R2Wvah_H},vcJ$R%'7Z,Y^ZXVW#;Z~MU, BdAF*.h xWg֘dW!ާb8]TU[\5Xw灒cmEcʌnҊ5H6"j] +{/.Vjڏ"N3uK*jдì=+{=Z2C5e7w(ej)u\?e4:,QcY +?@Uoc T(KIWR|RV"iAҹ 0R\⾆dzdm9:˘!wrǰW!IoV bT@4ߐH8lD7~: ~o|t\lѿX̑ژ?ul߄(ɄՒ( +¶r(.H<~JG!?SٓD?[dY_`]Ch,gFΨT I|"Tl$ǀ˨ԙ"Ux]5&GlLNH#aedQq yե`#kwT@49oWL=qj4"=/>oA!X6BљmIMD'n90b Jd+s su#`Yo^u z͜v3LgVrw29}HbX2Å_ɋڍm .٦{ %;`j8MD{t $y#ܸ/oWfޝ}g O]y7x۩ce8o$ψG)X}%yٰq.iH>VGg5\P: +|S]yc~$iYCPm>UieGOd1 e`ade +0q!PmMltt)ƭ g%%qIvi8~ 2L G[([O NuJ@#M( 6U<N .m\HMla.\JstGE!(6^{G8mZb*w n*vFkWqɓZg_C?KbVBT^`|YldW"qy ]7 #8&mhd/ ?NJFᢉŐv(u2>5>!@:#Pe:]Rh6 + 34lL&z.6Z jJ3 +QKqUW[8ddo/vF3fQ}L#UQsF#?lF:yΫm".^ØkPfkQSHzjOm"zg F'FǾd}8*i([P{t}ÔaR@" >e^o<WHF4뷗WF@Uչ,Ggܷ`L9#l.w +lvo$/:H5P4[Hŗf8r Z)p#]HO:'wTRbVƭ@:UrTf'K<5q.O$`:JOTCؠ 9 `MḿJ-|y‘Yv|0 aQ:鍶\^΂arq(\LDA"E(HZf)%Q;tQJDsliH>.G1e¤rI-z*0go{&ү+;bG8G6]V#Pt)3Gr@% \5r̂r6}y>uR1>"RlEe~I9 <ZLU V/eʗ +7H\y&ox`P|ΒJWđ>>e +Z\ #^ÝZ`FX܋vf4u?Ѕv ,mT08e +.˲W]cژ? #JNމabѥ 7KNፚP> + e) +&E|d|i&6ԩZ_48f8;%Jq +Kdp0cLXQ +ZsՆ{=Gt6&!>UFΖ~H{VY+Qo @ux?N̴sؐwIqT1W2 +=?mllA25bx!ضDz},'bfA&ǕRbͭik r.^$8FV\gʳ ql2ݶJb64WdlFXӽB6O|q>osNe:>2LL?` h|POq<|P ]+ٸv +˧O@=ˬc?^Gbw@ze-ms(Y52Bg#`'jQ0Ӗ!V$([0Sw_ˀ/^~ A},l4=>u#"^R022,XY<.wp t!4>]hTث*XaljXJ굴.FESD|`ZmVex +B?%]+Qf͏N* !|_&qkk-%,۞:̫DJ\50$c*$ 0V1|Zz}-C吵YSZ3|px*ܭ $'?\-ZMtG@} QJϢ '<}V CZ^6l >Blvǭ<O-"8g#I $\/k^C Q. l egY<<DҾ//Aofbim}O%% ګH'b`81iKhϟ4 +kY?3' +83 ~`H,PZg6|TCɨ$V'*k +oϊThm1NTD~Q2~x1ڸ\jBt"~WgTzҲ&_Jvpn%}/n23u:N/ϾiX\v/dC쳢jqx ^4ꭏ2lqRiYa+ZUJT5|l3)"N +eu~(̑ȑ z[¾3"9բ8oHi$GO~@ڲQUrEg(u]QZ(%_=6~a]Ԉ$SViCԃMk^6duT$385ΟotX [96pߟ\B# 5UP^Ohb'D +ɷ>&r& w&TY`b$9z8+&㈷~13;=r1X̖r$YN¥C鞢sB3A;e 9.uG>#_9}_>#FcFu%# j)A|lY]u`|h8FlpAZdyC _zT#H ӅRxjaίG5d.hGypH51ќNhhXFSexFtE hǤh|!/hшN516Thd 7mD#-<%-~K42&A;[X6Nhdl5#0yI3ZB{'f+Ω35aF  2%~Ϯ 撴SZ?2PGCO N[fT;~FV;%&m6:ltT))w IU7M/lR,W/#ѽ qF<|c/ oeoMn=#0UH'A/GӤӎrL4{Y^.#볽/+FKf=zju Nwғ|ζPTIcj{wF0Ω+pBz^7Kxx˶ݦpX `[0fU(LÀGde nH!*,rMNE(9ϔ(GpTWA@T<3fT|.%lb W|ml2̬\oveebgEm>5kֶ 3  1,-\!4NsJd cnԲld݋Av2fq8/ex'Hkt_u?-mQW 01H5h[Bz +l[d#YI,;(ϝQUez[B^;xèe1tEa1Kp!Ww ^ky급7:d:j6sWǙ3 (2zfˇF\˦JfJ_|Uv'ڇyK*(ߩ*ܞG= Й NFYb xDy8O m[Ӆqp +ΓlCBM)Hj-[p>ܾ<<9X'$)%8 ǒ NIYє$)\p|/u׃-94fuo/vҔj1mcX aUTK]}Ujߵn1qBN:1_/ e:nFIAW$A& +ćDCaLt oGsƮ&DM&!u$%狽̢Qa ㌖wa_% +endstream endobj 446 0 obj <>stream +HtWI\;7w$IK*ۜKIIA/@x% x-l_?PxZ)&bBȚy|!/Dr!~Aqf5JE/J /js@.*s} CDg)T̯1:z}7Q͘z z2ZCsHѡi5W^K*TF:TcDr;RHEt.(5^:? +z#_ n[.wK:lW0j=A3|e%0Lċ6}1jD'B"` i?n0y2iv,͍+GK]F;.ŎBF˶n6 y#MJ &I)mʆ&uek-xi-Lu6hn /:Iu:ifkmp~fa`kyŃ{jA2[7?I pκAʄ{x[AOi>;͙(̞ MwcjYY!.~8Aƍo*z5#hsh E#~y ~$ul!@ȚG=]||~CfC"D|-&l[gٞOwdyH@j5GQmٺ쐠($x>^J:!vp%sܚ6y~Mu҉Ud'QI-ں})CfAbw@`ys#4mc;-D?qsx -f8tTs^jBX;.Q͸1030*~bv4u*wb.($LWЭ{S ݜJi3S*X!$%va/ZXhm #ȹ\9J^N/j,Q3=yaFVU9cVM.!~tصV,K7=T.zr$MO ɑw1Ov":c OzrPXmUF.zy U^z+NjW,%G ؽ,f584>cK :;-ITV[Pb-;`Z`i#Ns0 WDn"OG!́z.вpu4ǜbuj#.Lf +ݓҺ]<$D. j|dx w\ĞS (ckR)^~̐{]8Z\;Byl^3T!UOTiwSq=j ‹J3ޛVP# w+ 2[:ipʃ0ѣuV%U;ev{AҜv6FԡE{y%5nBЧ=X&K3`u^8jxFZ!5Ҋ%|qlɘRz|UJ>x4CuѬv:a lB;'rvMfP|M6MoͱUH*փ9cg[c{1rJfAL~WU fHܱ;`[dž䟴2a=xWgTkj-P5;S Zf찛 [jyTp g +JT6xwWAN;qLpa_Kٞ/>yt^GhA MhfČ慞7P bB8 t];]-VZڞK N ycxO'Ngk`4gDXyf6)$)h ѫT}2U Rf+8/^*4Mޙ uA G>"ƒ1(C?!M5VQ͌m&ҕҮOAFW8/SAǔ i;O稅vfF$tDvG( wv +*MgUKV5x\'"' +fDvi/D!y;T;A!M)mp JQzW{3}R)XKcꚒ>]-1ԘuU@RB r9GE(RzD*@R/-lf?t--[T,qrj|^JF{C"2TD ?,/˵rD_5-֫Yʊ~q-)4E/}k,0bSGUP@6зă+Dh[#4> `.EVaZ+R!;ʲ=BͲׯVGo[@v>mHpwx?fgCK3Gu@`^MK۱:5j@X"exAʫT~/j:!Y7]/rя +Qd{* + +i0~ņ̘l~IYYϏq{k#NѿLz.&:ʉc;-c{MLL&:B9)wߐ@玓HUhnm uOOLLmCUWŸX2$)?rwcs;z;=F\M{չN׳1|G(ì ,6]7Y=+s[1 *rfcԕ) lOpnx4zv4ZjqhRӅC;\_ VX(2X2dVh|Oh )jc%@B@_4 3jv"@"`6W t_؁u$ўԈؓd:6,ifvg%9|GzLcw@[Y&͆զqoB$6`'P %_ oL :L\Z0$/=@ q,+WfpGWJO=LDz@F sa)zNјy)WɁKՏcF!%h'Am7},p-\ky} Ԉhz4Z_ c4U,"[3[A \K|o]KfhG9YG4|gsG;^ɨuwdu%kgGՉ;f-dW`4Qr,Jnv>6JTܩo}tR{_ڸke~}kkσup$=c|znޥ*+9 3fj,32]Cok:ˁx.[C3NqCK`_13&T ):8LԼRZQn|JN_9,{ #zUOX%(0]fDtl/1)D_j.=ڴY4T+tj2mƢ*2R]ژ/ۼ@xRbLBygdqF0 ^=zy +:5pZz hx ꬁ-!hjHH5IĮs + Pe9U7eitYΦY[D쮖ʹg INCkyϰmYT-a-Hٌe\GQrS٩jQ4m9F;z](!n{=Es'BM&V$B̤xPEN\yGf6Vˤ?c.G %-{f׮\:g*Elk JQ<{د<# Ǥ@Y%)#Ʋyuz388PFnu]Ħ89"yJ0)wir%B n7SY{s SmlTBhV͠ TSn͗x 69HDp-f + Ww;|] b X"H1l 鮬&#&s&eOo8[aSr_YՒšZD0]{jo\Jngvux]d[=f5e')}]=Ong/4Ç[ء6˨KG TfD>@jLҝ|*w^tgw VFL{TgGEN WJa! & }wXCa&-ʺqjC !%2hz aIʉyE!7RX̎}㙡u_ڟ?7JKHbfԪ_^zYtEL'<;dYO `6M@C̷8EIcti0:O&3]I&od )\@%Z Z+$ܺ2A!^55-uӦU8xz\FӋ!l4aj1t%2k?c;ޭA +k*Pi~)Lsذ(vo*hzOI%3~ds|jWkGx)M/pzO--}ujD/.ή7~k^P.ݩ+\w9Ēj`3;4嚛n^z硸%`_ +5Н\ .=1xTυƚI*+hVˮ Eyg0(<ϫd(}ƯA~S-dNṀoDomuu?X@M/t΁n'pK5'*{!ll{޵ X9" pbn58zeҊX@fAD@SjjYj=BnFl˹CAͻ5k {hdFmX9^8 r|%6]L}x +ca MK޴`_xRf>@~:FFI K㚺cV\;,Cϣ.?M'ƛWfƟi\2/_XF؃ƸzMc'}eSJ^O>4v:C5墱)?S䢱bΌzi 0Icu4VƸXrXq'pX bI*b۷琯lU.CZ7!⓽g gFNaO ѐd^K_AzyJsAPw;b4ע?^/l/ԏ(g^VG/:P'5z mŶmcfd<.۝?BǓ;vT l<]5e?.!Enht`6?FO\4/5B%5# U3֖o%ny5k- +$Dv iX50lw5ت:aM:Dinl=[ +CM(mF:rj9gM2t &KV/LaX63 +Lze֥"d +g rx`jƻ%)Ycu/@1z0iqAGjɟ }%[.$P,+6.5UN]ٛ^ϞN]ab5"Q7c풴 Ff(㳶/M@ԕ[*{_ tWh['@bXF.G~ļzH:tצCwu:0 >Ұ.W0`H)XAhQE-~! Ѽ@25&RʎFHM&yS@Gc= {)>6|4'i3e(jdަ% ޴mu0XFQ3r; |Y_Ϝ4;=́Q`J,Fျ;#ŻWs5Pƽ+DZ ,s]=Ԇ_4u>cAGo E`Q4m.MDygs/O]K}rK 3c-<> 맵#/cˣU2>zY9J3̗8a_>N +]\֩c! =]zFkeqc(<bӘ Ac잾x#+4e<+cy:uJɈ~'A$Mяf"#nz8IŶ$|W~|%E\_ =`pump,NChuYg\y,ٸE# ˒mxYGƩUhn4"X{Vaزg)k2O+{!1I5t$7"g/7]cJ~Q6$|IpBY3C  +TN4ѿ1* AC?%\XS.k4Ak"- q➠\>U0i\|Hߢ!dbwv78`#"iءa8e͡AȰFUAv9_EucvodDK'@ɱ~K!y@ze X"Ex+Wln#lmƤU;1# 4lcG-"RT ++21еK4 +U{ֽaPXNRԠ(_ S|S#a1(Zs~: }c戩y'1!Q`mh$7NL />ۢ޶_T[L| l]|pvS\rUszPm-EuZ4i݆\h ,_RyjϦ+{z~Tͧ%/+D%`u'5»:&yDFiʼǚNe|]-1A'v=N\^U9g݆!nYKR Kj^2Ӱ%Jjɒ$aWuov#{J? AQy +Ac8X>8G'GF#'ߖ@VCRmD\'pw| Yd,:Rc4t?UQٔ`6Xg}/,vMK!'.<|%΅Ti;14EDNnj1R< Fy,KEw F*PBѐǓ66 dlvo;sS̓[d2>=* I-QƋ~!BۚD0¦Pp 85jU2% b<0ʠwn&@ۆvӔ 2 ^U"{r\9%bzUKY!Natc Ma y +F{\ V};N}j~wgWؼ,X hcLA/^wŌ4waYf 16poLDIc1L,", +ӤVVkkޢ,MrM[b_W x} :{YYCZ$lvړW +MZ^ZDuL.C{ cOfqèeF3;]FO@{ $( +Xk%٪,0R]#S,G"u9q93Z"팂HwphfI-@ݹ@t<a58|/a4=ʻa{>\})O4s_j $~6lteqD|Fc~-{JYFY74LY(dV.pW+sZ Q_`" "l[ޱkV.MLJZ HwY.1]<ȿA^/)w;1_6@0jA$oA=V̯'Mo. +G4dG/CSp-rti`#7io'Z4]E؅ۘ,K(̼` wK]M$Z.-fCykGs?a)]1jE Xr'D r)jlD@ȶ\~-rwh)j GԀ5{ڨڏޢfo-jF'ta!j joQ+)4)jhmSٳ׺MoiO.M hڍ6M$;dFhߘ k )ע14ܒc5 QZдz(4M14ŤK/;$afO/lhFI/Ahd7qA3bS_V2g4sJG@ ꅠ!K>W0+>?gCCX)UW8 ڒOZYorkQFMYJ^ϭ(ѕGy~q T,ٝ&HbO7+-gpZvzQESĻ[v ܥ (k1ZjX̍J]R; Z~YcngbOX/=[ `%xlmGޯ5}p٫M~h\.pYZ3ޡqN1waL_ g%jO +|."f8 Qt!GƯfaU]=(Xe_=3(u: Co̺zv >Q_hN^u=M%Ӡa20f9`H`ԨW?0ۨsK$׊J((qh=98{XܥaP Ps<| :\A@ - :0;^db FfB@zX?MqE}SK)lDagt0#j4y~7~ŕm;A.?􉡟e _hŢ'=+_ ߌ"d.lx s~RUqL߾z4v/ +΢EoMfOfWv+wl@tufsXeH6ՠw}LwN{tuE֬H?@j#^Tg]]@ ޱ8za;YH>EO^ "G?2hbO#k? =Ǽ .on $M~i#`6{`َ=:G7l7(eBucR9:,bthN=:q=+]=Oэ=hĤrG~5a\Kݚ? ЮQƃ[9\70W/odGFF7ȁ7)/sdsF~irLe<3F{N,9xkE%Nse<51G'y4G_w78^tCD v8F]>0xw˒amEI lnfvFi6`28"Ǧz .l\m/z4D _V˳b&E+i06?vSByEhvhnUSς3h)ZgGj?ӣg!zQ:)HL0)Bc=gƤ\/GuJչ e(lzK)w!ܴNe0nxPԐ= 60*lΧ£v6uke4o>ۧ'h>qAWG0zo eӑ4=h WDQnK# +"&k e{^%1:ig62@^:P>[jsWs򎒀X/3؃A|sbXd3`S@2m;Y~_f3w(/gKoYr\´B:#9Y183fUVw,2ܲؗeޅAH7VUcqT%(|eX%#gwI: 5|ѵ4tr.eU-)jh4~(ӡ"dyn,#3FntSI,R8R+J2Ƈys\-bX=tK1pm+6SR7ڇ^uRz0E uIJO*؁Rt^_=~Z/jo{uuɗWiV/٭nCp٥^sҮtw_tCMun-]k.ڥ\~~+KWR.(EWz w^_k% u-`QR#.R`P[nuF[<.u_奠[n_u ˪]ƺsU bS[.Q KTߪ%GIzV%Z>vxsE]nrBBLoKb9[4U]^z`ه^W +k\ߏZ( +u^bl}b:wXybYob+ل݋<:/w-Vjp +!Yvէ*X_b7OfL'8vu+1:piV 0-q8qE;T;yWd4_Tfl¥& SX}+oX/< +sb八w 4 PrxԤ>}n}<<^ ~5[L+n6 :.PNU+> H{g B yE +L`?<&110Lk^!3V/REillh<sV: {=}׋XFuo Ӓ2c-]^->ttL$[1EWn +x1Q6}"޽ <{'Ȑ ڰ24Mՙ +͂uh- $SGϜ]ʖ60wab  nz&[k>cS5@ }6e#Hj))h:+%3Ki[ دH{;WE2%Z$iqM&ssY-`wnۥ,j'ypwTF/ EBAitgbJIU_d1`XN`v|+/Ŕ{+3]ѡ |ղ|fñ<F\0_'V'Ir27N +2|r +S9%Sa" 1Jq+71dCn;F9blzLctPR;;o5.!dv.S90^e^ǐ/Eъ]Z`>:%zqjc4Ftjmep/'?Lj Į":Eg̞PQou/fTJ¿GCd4dn&q`eR$n0{B XOdt>a'!sc[nfA5T$64.΍n3:y9 +Ҟ}\یQQ]W|3 y pp+6.`Qo['ɐ}>]l8:^mQF@½ڊɍe="mW[omߡ?eS-@iP|OgmDլ5Z4pA[H(dXhAi"0%z#l 0,Z3͂E2GWzUv#NNs%B[gsV:l`)y.NC}"H[F#݇ڳ\,`aF#gQb1Fd42{l R:m`$^&y^HGΟiXVjRzRC]b%^[!}d Pi";QDz-<_6kuT\VDT܏3?+w@0\ +:Q;>@tiBc:TYaY|LZt\gP~4AP|,&H5Vsןw@,'W`QTe#4JM5ZJ]6joךyoӏBd̟HF&\ An#6T5430rԛUH&jfC c&&q*X!TZۗi&q[0g|1sSA0||0fD2 +82i}Ph$>m-ꉰv0$ǏҔ(|q'XVe/DDl| >4i2oL_dWl\FX٣,jqД>NyūG`ZЋa`ܲ]zʪ~|+(CYvS2_Mޭ`ܱ/~A`j^3^r%0Nr%L3N:MADR7= Z]usRS, 5 ꜇XȨ-F3!@CPpK]Xbayvǒ[,, +C+Nъ 02NG+xp~``XV5 +ouj 00CkY>|mZ|Ohśԭrjrkŋm0zhϜ2"uaBZ؊Vo`6:/T-L9H98 ?:nf9tU`E:9N`:jRv, fj<:C&_ aC&X.2qgEC-]PEe(!.4_RjKB@]n5`)VK5 +caRu*&f3a wtW=.7ʞ%_6-Auv6FRS$BP + aY{hC + 1uRUhÎlwSCp̪߁ATk`pX+PϓX!]E1 +9~4*O~=.R1uHٳc{QM`cԃBgD +;"koik4,olF2s]7+tS]Xa]E{]&Oį -%a' ;Sg=B#w @g"<$$PJSlOSۓMÕ̆e62iV$sWG#b4;ơ^"DlgC. CxCl; /OOj:'HB^mB+^Bػ>(b +? H`}}hȋ;'QikR".ai͢r8aT @F$,=OHyc0xwu}MpN[GH-YEǍgsJ4_;ˎR)\RƱS5"'j=8*"Hd .@ydZrM4D -xФ!ʿ4wl(qW?FJ,/[5#ɒ Vy>Z|F5~j<$OR Z m%se~?o~܆mѣҟ +*k: +[o3ןخu״C}Td-l>8pzV5$񧬝l߯վ_ 7LUa(TTg rs65VAM@IZ2&dګp? +([Y3JC?O7hyh9ָS Why-Fixh8F9`F@:-r#Xi9MTziX8-sZ"?Ήq;-Fe8x#j9V"ZvY-ftAKAZ,;ZU-_EѰZQTZ7pߪ~Y-PX-r{jX-^p<2xPȜ4@P~6.lUAȴ~m< 0u#x0gyX\w֫|@EVtkL2Bl#X2T]("A|NFsllsiU`ta7ĵ3DO夫Av>IH4G ($ܞ ) +endstream endobj 447 0 obj <>stream +HtW9r%'b(`} E|K XOYY bM7h]uj 1d?V`KN۰@KOf@6JDpS @BmuO&3edNJ=zy!Fc [* ' MVO7 }2z}]HY负YV{~#xJwJy=A^0.l9 + ]\ַb8Q!nlbuĆ4j]x&ٍWp=V_yߘ\@9DwZϣg~"{O'p 5K0zґ+52q#aDO*$EOji"kzOHCQyJCFbCVy-NF)vC3 f?fPv_6-qxՊՊŵ}$bT \g[.}GX3JQSXzf jV .1hNo1/ Z)>cy@dtRDcl<[߹xXˏ߸Jrf)vEMVk {9`a}F=Cf^i3=ߓݒ%Ado=MF! fWDJ lHb[eJ=RbV Yucޙ$08mq"sȢINIih_lo +{?g2,;gI=d/e/+:o 1)sf5KT*!_H){BUc1JI^R9mxLhCrT:2չ8Sю(;?rT OK?)i+jKr%6:.VVD̨@U[h4SG#RmjaYD}}/$TT_!$홟k)%#B1ۈxZ.)1\d١ǨPBЪb*>2p oELZЬҜJ3e"ni Vq|LAIn=) Zި&*kME&A!֊ Ju%ȭ$ϙ1Ǎ"fW?VkHًkd-\Dc܊rVkR|[=p ljɶX66VK"wA=#YVu,T>y;pGW&B'ѾqHOڗ4; z0Hu0{&L|=`ٷ rfٷ7};}#VI_wrlm+}`ɰUOH->(o(6@rQRl k}R>[496O!ĻփadV)5@閹e%v;%ͺ[{>0J|`!vHyecVUfYB2+XF!0<(7)zz%.LcK3]>{Hu) ͗ ,#dy!u#8Vb׺ 8:7tR]m |,,v{=s뀨eђ' HZG' Q裛sl?> +CxX-^O+8EWo dʌQE1)ee`M1lc9*%^Rm1a%i[h\# %չiwZ>,|k ꒣Q5}sЎM%>8za8H 9+#=sc`c)4ZzhL9)&֣QbYe5ep"`5ZFSyc=;f!KqW$ӯBx`ľ?{b]mfpw}\[2,^)lDs.J7Pz'>z+FH$j"$YxL<v,)OZ W\h䜑`(S,8MܣƠaѫzxDYNbr1 ͞NA3'h2yf ]z '{j)6U/u/ͥ8b S%X$b"Xbb+ ϝȢCqFСqKoM9q^>z0nVv]weUG}|e [U"6ד蚱 `w 5';`pa;ʤY#*{R(H= "c?𷟤W0Pd)+]i`u/[d2d~jhG.jb-¶J٤t"%GW},SVv֠fV2 FbGG}ܿSF\P!DOH۩u䡰rN7FK(M27BP/1;\7C -X8^ލUYU 3r60rQD`ج/[`[JKxѲd~`8F 5 ;'HM:LiMc “֥6IHdAk'QRG8@ȜDFE8Gc̼#G9fK[/u/Wt'ֹ^/yLp]ɭ|ؕo{mHjƗF,s/?"O!RkOh ' d6yz4%Sz&Nqa_@mB@#q DENӽZg1<1< ʒe[/81Y d6M)I21VXP8) n kzfӉ)_3N &[ LKc `і[QGz65bj*kIJB䵬i@[*.WWjK1p! vrZNKŲT@z]gʿ-CkzYll` YLcM^>Pۭ oUY !7>>PNU#`Oա U#~zԚLMABؕP-x׏m +*o{9m,\Ûke)SicLȴc":_oQZ~c@_8 S\I* ^ݓÔK Þ|e,NhǺIK|4K2TI}p*\هOiS6s1Gd";dYYU_/UǎîxW;}8/H@j7v0U3Q-Q$$"յC VU'2ݥMlZRahHiX){T4{.̎7;vnQ@ Uܴ2;v@0PI-sN=THc*s:8(gFYRûtc/6$Kkauu>ZֺQR@bƩZ$(~6ZFܳ` +$ L`rQr3H5fI?L^r$ݽRGIQ-Jބd Z'4Sj+ayIu^8bF^o|dnx1{I俁ꓦ݈κEɖXT-< +}]58C~eJ>G$ M7ڊ$>u0,@Apd/xm 4,LU#ٗ|xfYv2xHɠTy u}z7e VsqPZ._ځ,T>{] *2wr?(kdϵ<o!ul=S䦨L~x4J?bmzy܊ +4"3wt7Y/)UUTsJz\H_,Ռ5`Y18%U\)!]GgOۥ֧Ӕ4TR>Ĭ'02<[M muuѐn+CooWIR|a:DM(`̍A"6ԑVWb@xퟕw 5H,uAہ}$ء2h^lAiRi͹[J|ᶛ)J>2~ϝӧT +Ew] \)qgU;<_HW@/v84[GGva EV1O1UE*y.TD2SAXFe'jL2g >R_̺Dx6-Z%p0z#x5"m#=kԧkgVB},k˴UHb$'! 'KYc`/ۈ\xIa\fndY0z_aP.CziY%W4f$hiY,df^9H0AbF9#`_nyp2-^Rih/`U}ѻc‹nNg]s.ˏYC;∗O:i ,텀 `2S -2'QN!I8|0d)2%-$vbZբ+:.@t2صc2JMGWu~Wwi08 U:&zFxcụ7$/SĹ|@3:ݔTJjQmtC0჋"м-{*&o1Ļ$|鮡/7 y+PٝɻzJQF,FZu9}{ }.Lv#d@,] `>x+3 HCYFDZ pqi8*]uИIm= (dF*nޟR2EPx[%Sa*$B jy8@ZûNZ{d\AG~4Idu!V GCX YVͨnYж%;ه\ ΨFS6t wлUY1}o4Ǭ]-= ϫl=ޯkjj (&ӞXeK2glHi6"D9ffiRڏI']cx)m}`Qi%Gj\L|zlN$v(Y==j'sу'A L_,MRAƒ>MqL+oF(D\&;Hw9b̬3EVRmv|3=ySWBu5@{ٕ;*ۘcY{9 B@)odlUmqVY"Xğ&_UG&%>/. +,v8_igM?2 p8իh]0<2)QwݫF^ +6|'wv3 f t>dtRYc욘u| 1F}g dslWH"Zb2!Wq ‡4o#4Ķc(j=0J ⍽)Wߏ~5K7/ +;-uj͡lf|_ X +`xI&Fua{ke /n(`sR[Tmd4Rqa?>X>ރcHcRڂ (mpM*ϬdIjbyXeQRZ?RHX&^ VIX3~cO I +{If"ei-=OclvS*}I `[ێ ތf1#6fv:[*q8n-Af~/$?H+m aO8c^:\ùf^Շ#c}!6`*@3PH[(3/䇳xT E]BbHWBN:ei@ZE"S.yEБ b':6E9/]&TsDAe}mjC9C:tzR;9EE LcNi Kٚݖ&-PaQ3-z~q4J犊;E]deZ裦a#Yf҉z >:P$X|Qd1ԙHŁZm,S4퍔~>H l]כpVxI2Tno +^-4NVnd'bQEg~NF:Npf,ӭ' rz$? cÎQCbew `1ǎcRǵbob,.ҁ\1Z1xtxKc+xb4.rE8B7;G0m6>Tz2=Z͠9KKqNZR[)l*$~LDqdD|̈\lSK6-z6XqQFCsVObv6/bCf)P&t*(+C'DT~Iʊ"q .b>!KUxd TS~x=֡tj"l=W<6*>#Oڈ2ċM?_>nXjkaPR> 48(J6[zjǮda[ї7aJumGƕT? Zii^9X +fAq>~}& WNJg|^& †q6l:x뱱7 7DKճhu^}oXR;w3 iȤAsIBz }{C4+WqJF[򕮸VԪ-2`nK>2t&dmf;@-Y6G )Z˴5ܸИ+p`в72`F|[X,'?jq`hljTE?F=)4)(5M7O@o `0ZtWXbHGDZ <%tI +^ >]gkEC=o4C&VY|:d(Ւ+CW~oWfm$0ZRV'^،Z@I +-e3+ݒmݪӀ-e\ØʲNc<8o\r8;k6Nٲp9'4⚳2m[4> +^zSGB85-F{ْIO!7jsMVo=D'%A @@nAnP\'f⺩/NRZnMnejQ:zqUZC-L;֭TI:8 +T;k`x~/@\ݭo@Ls_ޞU)Os Yhz_&WNe)?i_ﵞ"~+Ɍ{>x +~Ĥ[heʐcI4N-/N~C;<ۯ^ջ±t`:eorE"!k{:(kx5%7U7aXk?l0DzөDQJF^~ο\'7Oݡe>npZѶIڍ vRz, + CEtU͚$ЛzZ KcWx_4]<"{e(J"P|'l,Ep ±4RZCAe +r̹5= ^Ѵ6'C*By UqkL"9R=TIo7b0JzXֿM\9:h~ +l ,zكYxMM3SRsOcfmU Sa\VUPLϥS*iTYI ?PD #X΅Rn]Y +=>:'0i:.jPLMCYb~[9S;Ĝa4(E.E߈,ʢ؉ [L!)~Di=Mـ$bCNj= p6euCs(߂ D *RA(*/bΞ;bԹh3ovbR\%}kiJj䘒F28&y`.<%}5o h`H5 kU,5յE+:4;6B}wK"hչx2s>FƾºMƔ[o}!2M MQʆQjcLqOW3YكO3!}$Fs5@2[ct!S09^Ȉ܃6*D#|x/b#*v}[yR,zVHN}z3|/Z#s6UҡM(;Bn̞dJ xSlj>> +Η~3qu0(?=og/!V@n/{ wOCsǣ|Oґi ܈Z=˖WQ + +t){ z/Axu/CwI e{)\|-odv@Hx%xg-eA# mKU#FR}ojK?W|;3%m@^'B!5~NI>ۘD i0$%[+W-gjLe=T6iAfV9Vp [\<LpcieK5TP\( 1O/\A50g ML{TS[=IĊiv/ў~P]T؈Ǿa3b^ +b(uDrf=Ԟj9R"2J{#žUğKُcN^*i]h1R&c}i$,&c`!n,9nt<!i/!g +rX֋n^n`,""F74vvkR&;BV8{?UYö%g"d'قV9] +pey8,@"zN!fV q +X8bE-csș䲌^t!b;AwiRXoH3=Qn*GAű_{BT7yƾ5f/D97=ˢvHM)&yn@VX_l֥Nuƚ{-o`A /*~j 'OfQ?K$bcK y\s7} z/cg(px 1!9C͝P)i1֢o7.Mt=D|B,6 +쨖#x˅dNXwI9: E &lkL=g6י)[ ftfBPֶH!M5j]5Jd. &3D# +F~kk"X*O n|i[QDܯx5O,fHW!\ +7z$5YQ73UC:d8R$x 5'?m>?c{>ҲB1qBZj]@Gըj2w.>~тYM&B;/]@@d$]>Y~"/c>w!mSW{SY2gyЊe2 b@hLo,shN-Mhiumؘ̕LMM56.&hiƶ5صW_q z=zh8H{]r0{ٌ%DzwA['zMOLH$8U`mA>~, BXtCCh:?fpc'3gN"5 +yF]8@2qeFeDn0Kz FO#dk̞0xl?'M+7޾st̄n)wY"m-rƙťflʳ%GYМU>;w_hJֵB`Wܓ[1bD:W3Sz{ %`1~=fCג>SFNS`3pfH+ktA;)+KA H.2fR={҆]K^; +b\a2f"2O%(I܁(EEƨB% ']{mn:\-$_P4bp:Uana췮#86QT;z;v[zKB=/Xw@ +I˄z7`uaޜbFpc#''Kn;0MCX:F 1tD*p0 +A[Zt= WX7x\47U7$K&#-hj@fC@De +*eP( ̼ "S)Ԛa{yp M:5aXS[ه[RUNI' m'Ko]Ł, ̻4t fS{q"45 @doKʥ2LǚEv`Ħ%}#'1HyYb@ejW+XF_S"U:dca"jP<1kk^YEXt F"qj+8O, 5>ۨra#i\kd4^h[*IZ?c.vK46-h]V3QX\^Z΀r:f(]>8ov\sQ+!Bj]m3nۂ뽖/^tMdY19-15& {ijb!F!Zv5&}='Q5`@m:%V)XgӯK{s~ [33ؓZ'ߜjGtN^K[5?^aMܥhZӟp r +[MGQ:o4Oc\>ir)pM]n9%.kEKLDCɈ4' UJK{R# 8z9;$آtK +~c݇/A}3MX!HA\1wfmUv&o h ?`RXQgzk'/"9صwg#v]eC*+k<e}qE3H=0%=^ND8c 2^@{F}~RtEOfk4_Ź)O6⋮HU"~k{A-]-ݼ +A\t؝OEvQ{kG {0h7auJ KpLs߄`orod:E5a Lt^ZAئM,OtW;;ۊ |%NgN;@BuUmOw# `:z?hw6;xTC>ͅlƁzi. gQ"lvjcr!|[/7sWbeƢդǜe)xP3㓇ȼ#GUiR"1/ =g]qJ$b?._Dj@Xu32Ž &4^_?SFN> $:X]jjD NQg X$Qg X?O.'M10%+:jht*/jk ޚ/vZ4T3o\aH4] P8ed)Gω^B6+}%)xsޅJ-ԝ네E*'dgtPTq.V.N@5cwkm-Kw.2S^S"#;$Z3h";2>1'9ʙ<1Ĉ]"[Uѩiˮ{ﻖҰ|ouP"rWbR`F4oquRSl"vҤ=1kcҝjeɤ3E{\HTum@44"Շ@E2M KI!Hp򮢀[?UFH;'l{L63h5SQcӛwg)0ѽoo`*ph:9Oq lQg.NH}%cu:0TˬI{X䣧(s{V"8O."orSv +NlC&G|#lZ3MPeIn;oz?(#u_ |14<%`?g -YW꺖 *aZU52β d! g&8jm]qo&[7ٹPyn.jKr5o,v87>pE1._/ L#Sa<nT@=3(̮_:ק"rrY>:M}~8`}haoGc]^>:meQqfkuwrH+˨/?y8G +w>^"΢ G.F̌{dy8زir!!|d `G񈸸6F6 z$Nt*C}8k Ǡ&qq/fƮGę[őC9* G5L# (pDAuT. s.}&mq qhbRȸQq: o+{ehТYNbt5f>%`=VB}S? fE?Rtӏr{Cxosϐ{%Rp#R +`[j,bUA]{lJ 3 w'zmBqC%Yz2rfHt8Q+kDT08jǠyU%+ڵgˬ*m(]%FevJd +-I6C.! +vťCSֻ!N.'*~vȶf㌽VX]о>wf| +u l q':{ԧeٸ"px΁('{OX]& ?>JdVi! +sKSh"oD#+SG$ (jujO5-eR p$5WurfWjغ x5vaHn/*A^R_)UDkf +dz&'\( !,|jw:颺8qifIGFVF bPN"b*(VSѤ14%|QRɐ%el} !yMfHFrF:U 9e#c`ʘyix)8\=պRGvև`.ZRN Q9"lg˾g  {n&95XdŒYԛG vzE56w4M]ӳby-1 D-p@43Ί#ꛑ4@݈-S:ZU㡹T|/j[h95%8x /+p+g5RᤄcZ{!*j pE,P4Tڿx~Eל5=]Q2FMLӷ=}K[i&"B(1^ST^Wмz&^#A}F(YG8tQtiԈ#qfVF4 "ccޔ Fgӯ׷vvNg0sļmŤGw@Uã31 ]xZ盿{)b $]*mU[nSuݑtaݢI w/+j3tMUqd\%N1JYO3떾u|oYQ_rSj cs28 +t}B? sA +mmpϚY2P Ct5 (4idbuk<4/Sz6C#ÈB#t8Q5`ŷgл" +Q@ufZ҉Qh?ATPyI} g35a(=ȟQ(Da-}%& +۪((~INhc:?(3{Ȳ2Q5 +4!+nPDa|M~C*֕B\&as-Qhxi!G*4?lA=2D" +Kٕz%ocE{H@sGg*BY_g@'s',x(U_AB,ۤd3)z7FaJ⣲&tfż箱SiT^xDf +K|p͈sqbf1\b_9α1n܅$ W8#)K׈an߳wUҼQD2^Ds3^+L IG\)UL q(x/ -(oFxyBXt1`BAq$GQ|{=q͌8`}xtmx}x8Q}>c*˜V1.Sp*sW,2="*2@`sB#Z"tO;1zJU"bl^ߑ-en#A0uG0uD:mgSe EZI9vń`(F<0Q[Wr- +1Sd0h|c0Z@Y]}V?ͬܞCsL0s `z];K3 Dσ K9z_4`N_3#E>V[~A!_Y9uō_U/(]6~+xpS/!ˋ3}{ >ホ1jKLgK_Y9uM_P(F_t4KnK=ѻa[ЗEw5gK q!qXV8:YI4D>stream +HtWK$ +ܷ!OP& Y-쭪}Gex +}g~xC^c~XpZ +d\?Lv C K_j\Fb\mKdmۉlW:S G"ý1w"cԎᘳ Y~;mK;6uw'ppĒyKvF]F,9k*pH.[k"{4⣪a&~uy54LD,Yggg8NS(rǹb`i r~pG'w&V=۪A@gj+SB M$z.wxƻw1,;qmP\W{_!ޥ!xǢ,r˲yt EWoz-=4g~"M`ɚ_Lg %ϋhZw OIR2 (Q-6鈸ngsQ9JPU+Â:I^$Cl,٧;o8nVl 7/hWX$Gߐfр>E[)7-(nX64zZHH/߹pgAQ%W5¾ewU<"֢>}8|vЍ9B_O,%)i^Kт-*vI^&٧Fap)/D"RhOncMO#eSĈQ",H;-+=:wYo؄NJFՉY +* I X2ڹ N"8FYQ* OچvTwfY-)u &8 " +A*)+HxV*o,ڻ?0__O o4"z9;y-. Dm6k\IAQZ0ѐn6բ @vǼ4SHM9$e`y}>j]7h1;vb*#r^!bR6v_ثSna'6]]/9;“0X fD==A{u\'rk+#``(ҥC7k-]9"LǓ^73OAta?;%a h6B|'2MξPލFE,I`T6[Ӫ" [k2fG 71Cj5PR%/^?X6cU.!\Lozz `iO 3ZڵhWqmVSY +j\6P;^ )iFC0z.N/D*RYm-?/bX+*YkǰU!G !qðJFQ 2Nhrq9iWoE7N%:[eu& #EQQPuh-2ݢ+Vsh6f1cIGxUV[yz)J_<0mzA.i[M3YVvHL_O V]i<)Q$sol.M0$&*w@ٟi%^g#~]-Yv0l+=.޴lQEv9'W ز-Ab>%{zhիM_뢮ZU[v<]Q{$vSy "=`&"5-u fㅁ||ŦhQ`!ts\9 Hw[97Mms n~Iyx;ώi)"94Q_6er>+&iSE$cy W|Ůa?zݩDC6i2B'TbTSs{_c}b>u}-b?1V]6)Ej¨.PLZt}`mZd(>0'-imKb9#bߓ[dJoϫ`^eSkQ[ʼn\:m"W <$]Cyn3Qcr?1W<&CIVz-y!u}P+=h诒"uOP#+['# j*E8}Ƕ?IzHQk~tGܙ*H]hPγ("U㼋zo9F]9b<'{k~=WU/u4?[3Iz57b׭yCӔݚߟ&)Ӭj^~ڭG+Bzqk~+KXJR>5?n!^'6_kؚb ,RGOؚX\)k혘_PdW$T_5?.{bxH.fdH s50I]6Z4Gp@V7KTS|Q-fTg)j73bQޝ/o!;B&XګG˳f8ʉ1)ȹ쁮1Z#__L1P^.\9|;LrunyExQ_+V,Rn\T[J9`$zjڅOg!\57,qC>TjJ$?B=f6o' +Wm# NK16mXgdKm1iK iVuSŧ*H\/{FVߎ'((TOɝMa\b:r;coch.E-)=#ON5PV]Ńhh%VM:0/1]PLm0'<Ӵ]E9Mpw {b·dFL7i<Ib(mG)֬8B)5mI UJxҞnEUw_5k ҭ6Iw1δ rRx.k.To lѭ'V4mzH1Z q~g;)侓@2HkNb%i93r];O`scڊwuN3<[e$G `m;\׉5|ݮ ,jVI@n @"1F$ks)?<'M8sG '_7 '#CI ^P:@J3 %0)%TyF}p6'Ƌ 1V{N` +.7r$Rө aن p2d"ny=ZvLdR/|Cry ;1bfviSuMed䲮iz?N*nC &4"4\"Xi++HN} =6ewS+vI/NIlu/O^wR~㗢3ޚ|_jN^,vpRE +jG̪c߁Uiql36Yܥ܃g[g EDQu6!r:{x~WZcCۀw"J&Zwl1|.\]O= ?YC  +- j€B2^UԔZTSKd;V< +}%J:|uXD5cQgjD#븇.80-jP&.aNظP7lI ɲ=V~-Zu<%Z] x#qN,MtƁiϗ? ȱNMɐ+a8\7`?Et݁OKZ,=}SxXCu<>o{C,pِz~Dþ[^NZ)b ߪyD;_y+u5ڦ=Y".AJx?fK/`}k~7B@`kh>%fb-S#.D#3V=HͦwrZXuG0bK|湵]p/Kew#01" OQEߑKVAr(S:Nb@õzc(ujS&MAlUʹ<ωx\U!~mq>_,t=ȟ1y5ܼ-P@c',p~;Բt+tL0傪>bq$vcɲ= >u;+|2m(}@@D[c:Z/srQ V"4xKQYLW]ymdIڋ늮o!n({@)s(槄ZC!-.kmƝ7?dLmh ODy<, +hE~LNB-mWn{,n1E4ԁUEԱ1՝ttem'S +4:vtZI걼cf}52 %]|ª٢Mk&|jW`nɹhMkKUv@ʁ m)},jjmQ+‡lU@&)Xy;%snC虎g7JC +sbf}L@(TiF˒2=3c"hDP]+p菒BA24p;C3o\yJʹa(Dxz}:3]5E:'و| ()a9%ݮr^}Gã~wsFKT!mHnGQ- U ұȎb̳HҮۄKI%J_>NMr,Џj3.&cɚ촏gȢ?FT#mQ[>D0b85FVF|. d$%K0$C ,*^0X)N"W ̓`By6%R-̜ZHOc3R`@tِmmuEQkq r^iq]}OU7X9Z69jQn(V- _f5̑k t7;`dk]N\yA +G~OꑟL;tFlЊ!S\ykǤ/\'!uwfs8`5w=AU)1'9C`{ͥ:RӍFu`~pYGԃSnd2Qm )G0RB7wQ.W-?j)DYxG3dwVPEq`" /N3v,tQj>ծyaɊ'ݬQOQ<!ײ})B|PP)JO͆氡X2"(Ewsʪီ:;&=k; ^ɀﯜhՓ'Fﮔ]w@7!)ԥu.Ge0@Q1'Nc%rewxtraԲe"oW(-vN NRݳSyOP$ +cՅlYr=џh;y҈(/@m/ I`4n>jku@jHvo:vcQc8: kDyti#xQ7?3 sQ 2lz(OHeޔ'ez< DpU!۶2U1 BzO^#[mgwi"Ht'VMwBt/߽@"c=`|p[xᇰ0Hcr#x npW|~O +|Cs=}맀dfmJY_]%fm;0+QСC8 +DYf2bh\.fVfV I\yU++ȕI4+^Uk7^Pq.\թ&fu :-\v rmT_Z8ojmdVٽ̛[`Qĭ%ujq#&-[b9fM p[ I[w&W !WjMnv_RgbW+oZEĤ'6[223"kEL5-C*&ȵb\ O5o@6'>jH3z٦ w39JJXZvȳB6 V;֥d "U^$ NJY﯅W!«ACT8Op\ykåZ!w:},ڟNϨD,}i펊|aGw맺ZAfE-6^,'+F+V5orlbHhgn +(Vr)U>ء`uE1 mi䣲P^U; />ޚ#>v,!CF{aX]s#H2.@4 YC͐ qMN_$ݎ- p] ]OqKp5+!a| 9NC#++j`U7dxhWum ;\݅c*%Zж C{~6VYsۂAwuL`OMouC:n +qUDnB-k se1 64;:!=1x9 iǯ';]ۯÎ!sx6_B8SW\e ,ĨE dq4w=561̙ü~xS| T8STު ɢG|F am~ D&]LD)Vq^~\6 Bg,(L{hX Or0 ֛C +{Rj_4UǯU% t5$3d *{32 ga"=_vKֳמ +"P],u_`2x0V[5bMs ޠ`FXN֓ +-W6QLBÂw [cqSm0T>Ϣ.o벡/^ =gMۻ5 +ZG&<@9{:{זTbH 2%^52ZoKz4!iYzmt$V<(a9a +{Ŭʑ.uBDs loMcQGULp=0T=W:ѕ8PesTN7컜@a`zH A +!Gp<2[aQC2^Չl=w9?¿3G{(8(>A-sDnsCp-ǖz P!H0TbHJ 8ѭS. )꾏>ZA~"N4^  l,_KɅV}A1mnMc.F j!> Lv ˭gG nb"AÅq/Z$Bv^Olw/{m>IbsxBYDwQiuTWNvBL~o=i\`=Hi#?(#K5}=t ][&4?;e͓!4>+ewH'ƠiwAc{u-4 9)SZgbɰ( MWA{⎲{HogA +-A:O2}VOwcI $vߝj/_ mWEee߇AYDHuw00boWqΏ~>Ө~oFF<Q8D gz]11$Bt1TE1ot3E~ư 1 ++f"\-o +x/v#:tA% +,~ͦx@u/9VsMT>Bڒ DA֚69\߾~.^ܒ*vyYVrqpvT@=t'00%P#5Sy@I=w +`c +gИ(C0o !IElmж5؆1H Y,0sIQ؀DuJHT/?`"0YDu^z)h(`SBR!R}tr!t3Ў)ާZqo{I\!+7i!}- *UṊRF;HXtf0(F_ɩ#fto,{m:"JRDsRwDE_Or ן9LUֆğ+|@x\H'D[x/`]c{ld>o9fOyĭS:"KwkH\E,\`%f>8P^6G{l xn=/ (,W>nF6C 9 =z!aT|b`̽d$X]>;tesWP1bM)XnXVtf~((ƣKf@ڈz<ٌir":.x=u SN{ TE(&|6O>y8  lTjPOzZH#CIEH;lY!\꫁2JHfL6eC$N'E44C8t hhQ,<0D?b񂀜1٭Ə/b4S@gҊ7>z$"fVSw:y ) <𔱬-3Z4T0*Yc"1 H\ALtg +H\dE0 Jg/>ӏ O(\j"k#Z*=sdc}st9C3G!\pf8XyJ6K< ;j:_9FbVkMAEJx\ŝu]pV p>3uȢu] +H(@:I0EYeu]@ +FZ\!!u5[n:#G\gŵ]ՓEqmn٪6׃M7^q}p"R!S@Nqq)LZ4VĶ#<ŵ"};Z]I穆 ?Ȝlk`Jl T%sV9:M]-LI\k,c.9>uQiZBj)0YL\B\T`3k$SJCZy/LA F%&u,C*9xPFph6Cp¼܉5x1lqqQI5R tx 2̷l2*Xj㿁1|opAi03Ϟٟ0U2<+(q`͓?5je,<^2`DStC-p3 چWP_|8h{c9nϢ9 :Aюsu9LģȬr rk񒘂>$FdU*9,Y풘·0\a'Rn>SpX:va*~d몽?( ,Hc0h$/YXl,\Y[BЀ`A2+cF_vp/E]K0(G퇿Fx\2͇Ve1eb:Qe#׬`vnf ab`y" x'0.gHw5oǤDMpPdu"W ȴ(}( UU7QRd⠬AZx_h}s"ԬHr6Qe(j*?k[PHr6S]MYH96`J n+v"k7P$N)d*)/Йb @>HY `7ı9{`e1Vff;+y]HB.K 5J,A $> \JCFhж:uh @FeD`_6"c +y[6$6]0zfZ$*sfyff<{3u> $ï8`D);I03Bd% 5b׌XDfmV,y}(Y,XDZ * WKF`ɠ *jbղ`1דﻂF.Xn<`9Qz%'z6۫W. +TP+zk+H,26-9$?Ւ] +B3}fW ؙ]4RT *d0 1 +]Ȳ+: ׮lcW6,ew+./܊dnfžˬShʠݬ̊y.bfVt˔ݬ\f[җYi0)skBf'ڿ]Z3SZQTݘTj1Ag> ΒcM3pw趜# 1T)ہi +.@( +a] \d~ged>9s/uq]Ff|8/ ւ܃T#0-0o9YcA=\)O'=lo0|\Ԣ{@T{nS +72-w 3S.K/yabRt +wg=Q"t%*D D :DF?Stwvjʰs[ ' +\) +c2Fv>г'G4/LGe>F2^a--e̊:9q1I%xqc= ,Fxb9| RB=2bsX7oorX( *m 5QUXIppy$XôTNQr2W^")fb<NsC2\q.CSZ?Ղ<6h3!'ZQLoU3ƿd6ĦqF͑fatIDWa`ʟJP\]ŔseL'K}>XZ0gzfvEeaIlyW ;{5>5'bEVkx<4fq p3fW+o|-nqThCYa\{ SK"n z~Q#Ī2'hᜣhQ17_DiDB:<]J R[cumd[#I0}sh; v ׏ϝ#BfAYz͹2#͍R?{O5#R_[ JDgėGMyK[Ҝ5=9apCgai1 L{(pֈy̑9B77@booN@zm!aa1eEV h?!i=!l]˺l݇aK{Jk|Y%Z/oWiT~Y^~tTo`s +}ܣg~ɜѲx=Q7J@FI< ]s(.l1vkג|ΙP_9wUd5KpR(4ʮԟ!e?lH9We o&|r;󏿰_h?eU HHa`k}VeF@> E@@]E#{GUª@Ӽ;qC9J9UY6O|]l@e!OyFLh"!) uW[Ry6R bQz0GEN+-fb0-V[^;ly*VfCVzF\Qht7JX(mzv-d'ܱXKMqUrNΈTAF>9I`n YgY-O"IpaUZQ@59L_#u +,7:WQ\B+} (~=@Z(YƔҦRt,)3w{/Yho+KTrI {3;wc&4Ybh P*X #OVv;Եj߱=//O 4X83 +w'cɍ%/(y6q~ȂePqHעiԻuHc~:CulViju \v;™#O_?RfLq}agOXP`*mOig_4eD[ gb#j/7uUɭ[]z,JJ*J!+ A on @Zi=Z՞42 jcL@\|GE\ꙛW.!fMe+r㸙b*N^@Tg*u ^͙j+sW09ya^,emPٯR33sI}#P~1ڒlUUPT!+L$䒕9֜N:e$k]]qVqnͷ'RtK}ԭ 1[ע0Sd;f$c'K8cnIQ4HNec7sF X}{[,慛[ ^nP q/ |-ZHb@l/jN2PRJyS XP<`A-ܦ[p|s[xe>sE){~$i˃R: 2gR\ `4qkXB1 81eٶZsIH}7w5?S&<LPI@oiKn~ccmLߨ)n tO#^oqR}5p TIym>xز#w~YAz< W/( USW\0%s?*`kϒ~ɵ^x$j:N@rR[M&beke8'ym]̋.seP?I@H-O +騹C':%y쾇¥ϴ^J`=iժw[4-zh] 6BCfhȅƻ) E+8[/5yXöRKh 5}މY[`V.+,ṃ +,Zm9`!@OvPV9:|)qlq5t!QJ(m0oǴ&gE 8/2bE ZTjr+oG6)/džj& yPE0E`ME1,ll:.*?gK`J"N'Y<-:{ȘjtʕG x-{T-s[|1ov2cnSW^._z"fxښ[m*gkI{&:9PΫJ'09R3Tk Pulej* ZuihWϜ%G48,ڼ̪^sGC|w\HDYok7cɐk6kr)RK0d5 TKcA"oU(yL! +ҍZcmˇ@M%̂bq!,VϮHc(e`c=)N329xp첽XvkjQ8iKlC&/z {SI%ioŹ7ٜX]H[JĿTqhk谑E cWA NCa`]!L g%eQW猤bw:7c^vG=" ť2)bI҆ "C;Ԝu\DӫF ?86m, cS&Yݾaf-qsz\{SApGf-"Y4D6_UEFyϯiH?r1PApNQl)rmbq8i-}A9K +uAƞnEFxE'akз~%[&Y/_lW䧩&$=7 +5b °gʽa[#;~v~+1 ~d[{ZK%h=Z03w,k ΓN@1ks1EoLہjkoA`#w-9Ƈd8jYm<@ȹ À&2 c +ŷ](9+ZhAKuti`إoΎv"R7% r,?+)[&NEMpb:RGI!zZ|J@~^^v:K{WEUp %eu?pv{̫*F_EoЎM7O;xξ% ts]WwLM Zǿ5GٻYdV+Z:TnA3|U{_xE(Aɿ`LLySApZp`2ѧ o݄cZd=fbpQm&Lf_n3So/H솼x bsuevn/4_<./@jo+|Ym%>{[ p??+@ke%ZO8}BҭK-J80N‘8 $:vl'qAr|K>$UFg aG{L©Hĝ5c$v.#{~}3J_`" GHscSe +EryYw8:z.u$iI*3x%;ߜUn~(fi}n~Ǭ znڸ[C'Qk)^~ȠSUpR?r@ 04'44]%?-NYcH0MnjE!;%%lJ{"_zcP51^GZ/qE6zǪx 騲8֕R -kGq vފH}=@|I5ql P\szcd:G\V -ʭ:8 Q7]sR}gQΎ%;;GvHv>UvKS4ƲԇnO-w7ˬnQ$עh h׼Y/α0 T +endstream endobj 449 0 obj <>stream +HW[}a%$Oh#$$ůDN> B}N]gfwQD"!s..4Vyo=ݳ. jZRU@JoxϣȔ#Ҩ~Qf8iL!ä}7Ob*[9_1ީdH6 rp1 P`xIٻp?GFk. {ZLݽc P.y&SSQIÅŃfvQ˩rHXB +MwV..ݳO?ꯛe@=T={u@a3S+v(FMl1\V{FPH@ H#w"TDW [CX@ɩ.p)~긲{;vUf rC1*p[U`6& 2ʀ?Rk-C*>M5~>A=,xM,f2#΍^4G>cr y^;7w%:3)cb`<s7qq-ūt}6^5?F|& _\ #9J{&{ Z߭īgLַG! .,<YW+͊ +ncZŦo~S##Q`w5tM^v,Aͩ_Єjiz\#Pb,Tm0sPCys!AqbZ(wvQ31|w`h=ҵ#^Yĥl~ +cGê=ĹFY +5 +Hr2i_R>3@R2@C5RdyôkV3QtO]\(0 X>{Oܔ;oZ@ֹIMY]T {u3óxs0k?d IBmn'$MXi< jQ (>\şڂR]fg̟::&(s?Z}yƐ#;9[!d.QΧ'g-gyvr#e#-^=^2_/]{0_ +?XuI_[ݯkmwkKp֠z;c}L]Pmn0E3M3[RMd7 `nx{_F<#,%X %x$-)]ܻo`gt<=‚dǍ_l `f ''0KzCo~Ak틍tHr__{o~ +}~ۗ/x +٩b5 w/ѵPNmh1?tWɎ] /1$Q԰F + dpWFwOI|H ?ou?יVՆHSYK1) /u&&t`ihGmh/[VPrU,7%' +|J1&Ш0<T>R-=K@SUi$KN80w霺،Zܠ%زK vCV(&FOnZ*8:*hj'{;GIsMm_zV5L`ϰ}Yԙyl-SPh)+D}M3o>ͥ'}UhKr-۩W|kN`ʕoj>Ϡ'޳Ri@DZB`k٬i4 +Lޤ=h-IFdWFUxTaٖѲ浂X4" G\0u=-%cnզZ><~ A1 +|"` F1o) ]z={h٦ϠTdI KObœ-VZՒ[p}w`y BoOb 'm%#%`yOY3mTyLĔo =1um<6Ե$L/B\]>bн1llݾ:!Ҍ ] 96aX+P^*sT5Zshʼnrs#w0;fRNOI{3a8|-ǖg†W W㣫~~4\*QhkD0ҿE &Rb +LvCό 5zbPw2 + =CO>=Q +4H~[jUCo9TX2b3]G|ޒb3g_b &Gn.JgM൫G(؞'>̻N`@ZI}90(1r,\fPI + +~Pr0ϊ"b=SLޖfp4R;Xbv:W{ e &t +$YϷCߒ# "%=Ii10*.N;q/l9P[븣a($>o̪>佸?ey`Av)|1eP98 >{χl.{=}Ʃ9 ' !.K_,x î +ǥW`clꃬ'Ua_4 +,??4{վ{`/JS;\ į [AU9v*G bOfufN[]Y?FCZ<Xp\q5WiWgyh|[<\\! R , ,h-0wU8ϵLj7Zi6vںSMɥCR8X|`A eLY&T j5.뢛2^rQKiꁅeL*GCഷvDP~jq^ Kbnd#}{d܅P׽5 #֝ +gRF[ض0D珺ÚM"bj 6og’V !8SU ݨ8uVϿ3Մ2z7:!J,ss5-iހG-':6P$+^*3 鲘QF9=4:^ʌK- Wg^m&xî( «z[vw߱ + M1UX{abYA ci(h%`iDCsb~\,M^6Uӧuo .M%7+E!TYBf*[Uf.0^hV%Lu݆ nOյOKz.hy z@eFT uAZ6U1F m!E|,f~2!\ۀيFQVǭV90P )njp4E +4X[G +Ѭipi$xBoع5HU>N&3j;Qo%TȀzZχc~KW=PfD$v~bkl/-(_$YWT.cE |QyǙN,xpW{en&~NumH|5kլ>Wb_I ǒ +ߓߥ +l^f#SrD Ux +-2wMޛV,}4`05N94cЏ4 -9a\NO-9X6gw^춄jsj;G3O?_2H+(K 4XM+m֚ g:/ o4:)~7/G`A1fq\}M6; +Gۃ}f"HcFU+M6(@#̥l/k+?\s..J)ٖ3X/wm?]`){cBcJ ۚטl7 (}t ~ltۻV= Z"_| %>c|]u8"ZDЏ=AyA'et1,%5Aq~z؉ׂSԅJvS“ޕW\#en. nO\kϭ+N E fB@0B^ll[&)o Ed2 -oXgg2Q.7D4vfڽkc Do`fpMk*X +fu8f(Rq9M/0M5D#v4{M[8~ו4`Cbpt PRhXgұ|EUUC4=oL0՞l/V1Ԟ ]˷C:,~3Py }DǷ[Zu"WS~&VKJ%1ٚ+D!!}A"IM I6DtᅢWlw$ty*]7T\jzShCԿ2^}ߥ9e >_1@R\ЪiXzt(]h ^26v`dC$6m4;XjW+v9~MC< ZBq>U,&W-%at`<OZᄉ՟>VMD}gHu[!w3dD Uwi"&82Z.3C:B&X%[l'`-&'n, W Sg n`ww)/*"8:m07FǓ<b fhDfM;ގEg: x=n.>wtPٻ#tO[3a8+gTQ MSO D4g|BgÈ_m3W]t]U,;{Tp[(BEiZ񺟼U mVq2bTO//{2P/y>f:zgceN0aI--JwΤfU.۲QY.Zp?UkRХ=yj=tRAq_iBhыWN7ЏÜS_2V-8SʑూDŽ(=RQdʠ6k t>'PTP ^8_O q 2,א# Ojwl5HhOa#8H]cO%vϯxZ̚dOh97/ȃ=ѳ|_g\dmK;BlSH{Eg'#2BxD,=de@ې](ty-旅ڧܾ\?/?UzۖкVL`KomW^J ?ylDKS$o2s~lIӊ +0Vx.dj/,Z#yYţYE4heȦԒ$I<gϮ5J.iK <10vvDh^ Dċ<@nrJq;hfVAO{ `P{6-3χNx)]B;S=_QJPY{Rr] z;^RA!^>xiHkjqs-fteI휠C1)mJՇ{Hl5WSZFy`\G@sBKN?(=Da$({kewegwժꯄYɾƭ<L. NY2<9J눶}ZȬ(]cR>Y &8=9 /=liYư*´b| ˗Z =VliH&$y)#^=>lj7BdZtʸk229MDe'+~'Hy3]y+%ԷJnýg Hk{2qM"#(7u2kXuaM57 mMeYS\"e(ђ {5'oP +Y {= _·{4s. +ZRI*j7J G6CT3=]5k 7 .3Ztidj.kfP+y5OCl_WB] 53,w|bL+1͹]Yz'bYcە^WJ崈!mBJE۰:mۯx2O~S;cmFAz(!5'kɝw^kۭ|#uka' )a5qsnEbrY'B+6[!P0+hVd=U< MܪiSK`E6JhK#XAk> l'{֩J*rÙSk|?ӢJTYOTZ +BUW T%'bi :Cʆ*JX蹝 +6T!.yK zjVjX`V;C5Pŏ҆PC_gƙ[V*J2K5"JTt*朁H@UsU`Tש +Zs$eMU# +=M +]DŔ;U'UUQz*;ǩ +A,֘`dHSw6r>x+r! +^4JImBN(KpW`-6ٜ(`QFݞun5Mu(]u,rE_)kA.n̅ZkC:H]| +vSuzwp qs[xgRR2f259!6}RKȑip'L8%a8qz+%{S_^a-]}[ҟR? CJn"$KwHū뢊 BJJX%WSFp8i-bg.&iac84+;\cnBs5=Dɩ32ʦ +%ۤ8vƌ/yg@*vu4uMH Y\ +FgVs_v[jnPf^EŪrH&WZz~k_I6\<`\6@ͽ).qiOc#y WS<+| \0FSrXt4piTʟK˦?=To~ ۛno~./^pϏ=]oo|xr/<_>|||`ӱՏ/eg +endstream endobj 450 0 obj <>stream +HWF߯ts.P919c0oG5}U/‴{@x)މviy,m 82P#c{Q+֫;ؘ8mTa0l/ns3rHq<gX΢l-q}o_ϱD *~kTj*u01E3)BkX74ڴ=ZX12Pt겧egNx;N0xxrc l SY7‹6E^^tZU¿n}Ȱ|J( J즯D? _78'}`Lr7E[X>(}^sYk0+s\geʼn(;Фj(1DEep p2݊WX{`{VR&4UMkjʺoDhm<:,\ GIfzQjrgG(Q0!;3]S=*q1%1TRVJi#ądOtJBTICdE (@3xG aZޅ#Hp4ꥼv_uTVB/`k٦ =5v!L"&1ooݨ={4GhN8ńR~˩6b*u n{/t,I͛,q1x_`aĺ%֯ktPjmW}Nf3ڹJGG%lz 4^e=]SRo$&YXw՚Q}\fg `>p[| Xd *]G{ iRiC:Y,E4Je4 ԑl^ߺBs{ y;*:TxmJeA$W])q4ªCݓI^vM?ۆUSJ~:v6S'!8NCp1+?U1kJv؞ѡGb>DQ񖞲n%B"M^9 RIe|ரAd'n(yw>>{~>FjJ})+@3V<|sR^1oTG7X/~#f/`dxFo=^C Q _TY1AW"h* }^my(Uwur$I&YIquii~8n<'ċk)ʨܕGDvMw+T/ !%N +P{eP T&PՀsZ{I\/E|Z+Y@QMIjP`.j +kE}& +Q9Կy~jj[#w1FIzg}zԸ2tqP d>lKK(koU]_;*3ҟ8Vټs't?Φ7i>ӼM]OaCBū꼳 EUfDX: F`Cei.PUYu?Z@umegXd]*]ΩáX'v[f@CY/:eb܉-ϣCN,t?%Z=1Q2q5-crhZQNH"gEH?оS؞gQ:[{_QoQS^)߬[u v33V{SW`:U;dݸ[Nj]/hsZa!Jc6}`|1` ĘjiדITuDպP6NTmOcgHH>ΜXjq|ύ^bV#_^xZR" jQ/يKU{NM_m`Qvƙ砂͊5aF RFK1>6*U%d+Z +^vLT dB}o)ʔfiۼg\sFYe-2bfjQaPJ{OU (~tELdzT2xZm͋iYF'F}y2<"X7d%~\lZZe"Ot_# j5_]6k0^$vY(:lM|dݜJ->X?S,WZLznDZܥ)Ӎh 5ڝx= t[8ʭEew7)c6Ẓ+ pSXaX+Q!p !Ѫ!9)H!bd"qSNꘘļ_t8NҊZfTY24ViaLL7gIf%<<3.'u.UD" DLwk8Y }& ;dֺ.ȸ +(aX_= +TlHdo"1w85Iw%pe{EWN_4y%ݡƝخҔ:蛹9OxvolƕXQv^F989qy[♠XqI?,݊/&r2)+ڢ?C2;̗slrnm-ҋk"T.=|,`\bs4 +]R -OtHb~4xg[DVFyI0~,'ِKU2 )la }|>(oVZj ]Jk/T>pH nՈ+Dn^?mҋޣW}ǕTaRmi41r9,M8t#@k>"_// G3KjsygL}=B5Iw#*FH+G>/ p{1}592Jo6mnoN~.EBrA JB;v* cx]ZZ-$1ߍ(Vmdlީ5x;\i +?[PlOL{z"#8jW &1{"c¢vFҟA8fd=Cvu3"[p˽ܮ҇[4j'f8m43:!/īL=u᪅M)v9gŀ1QQ$gAuޮgQlـy +bӍfm״}5%KoCϻ<.M!#Tٗg.ϫQ*x8N3x\&^rxÎs1*:!d*vR٪1-ew60 14RYMg Y?K;D>|-{:f ~I8fRA{kF6 Zp1f8$vK^Acq*6TFD%3"?<_0c~3D*4EqSDKC +UfͱP F^m Au4 IQJp.9Ru\$.A)< N~,CǜPmT[96KiިAE3|{ `^N d !cAg iG,eZ'HH[l^5>";jG)㡻m:al;7ol]7O<"&ʬVKbeOox׸Yv$⍫e-rv.FImvG3eu}Dް2bgAӻ f:- % GF19ruMdU3NUVd;b@Jí{kTYzN8 ̍=Th.EݾRVΐq*&݄7q8JXYp֕ iߑvvVsƉU +I$>q=wx09bm^LsHpUr0!K/{n q]ek2щR,?Bc'zWɉ>o"ߪ[VZۛoCIyMH?փ;:(8ihaܚ7i +'JӢnriƇo"և󇧍ܜQ~e1wZ\E tJ@7o!*ˬXلCL t@PCk͖%VeZBb~5+x(z25HCG +K:nu?7,# +_(^$%X 3i=sc ghAhNt%"Hb?GM1ZAvV޺X]{{C]W\)<J) mR%^%@Wb3Wh+~- ݵAuLQ%3QGSI} _ߩK8(N%t6(4hgEhsJjm?-ibsUj~Ehe5nJ '>X]ej&6dvwe:|~2NoIq@q&zcdQd›{N/ kWQwL2{l6mI)t4Zj7a.Ү.? Pa~>!\ECB6^)Z2z&A6S|TJ,5P5蠐b=6ˮ]&[!ζ-S\nT\omq鸗YoBC۶j %Ɖĥ4ĕ: &5G`ea|hլm}+ɮW\Qa0;{qu1Ԭpva\L`v :'e!J.xC >w8Ihw$98ʡY3(_rmvqr괓g cbL[-1O˪_1m~U y :wOg~wgHSM[%U i[RIn!. Gc5'# m3±S[g i\en_ueOEw| l̋w{ MAKth5_^P?6qMb-w$#|18Mu`_Ȳ\+2_^P(&KU߇/ >POdorҶzKo%\~Tnàf^v E%P#D] X?=V7fT#csMm/0b坅/|HJ[P P,{LpdEs_KiyzǽCٷw}&(S%S¥nʒد0/U:{5}Ϩ9ٗ"OH\[yXxv.7 ?.h6ӭ@M|oͳudzp}P)벵UY;BsHyJ:5@|vEÁNyC6E.>2vPiqS-,$p:&KQrWYE ZfWbT]sG`%X|mp4i + _6U9gLnz.W=`/SMSBz$>$& gRF~%|~%͍xAhתQ^X 6Eَs6B/4Sq&МevQZΉȯ篐XtiTXVr ¡tf4y+uq;9S@=0%zyX9 v*^6U>n쥬H)lj9% [&P x6AI \=Ykc%7w]!Ku|wZZfB5OHxGi*\qRR,sB +}*sw="&8(,+89tv}v K7`,}bsƒέhnV +OͩxkiYkm/=g4z`EBOݩɱ@+LZL<&y!LL4e&NȐ'[Ha\{P^LzBݒҬΛ tXF{5lQ8O(h< "$*Bn8}OTE~̗Ike4144zJs^e#Zj+XS=TtޑCx#ڣe ޣZ.C9z2f:j]Z4I䮭ʚ,@; ĔXg[䏣:}nYwk{oEmcT*1?~[DeаK {o%uztL42Wa,rsL~92̵ adSk%nL>oe5>/GhU֡#j0=Oc E쌺՞~@t|njȮlX-rׄ(RC!/V.βծ8vUP !iL \Mg)OOpe^ur &X9YQ*cW9r:n$>%D̔5!{ؑm[WI]h~!02YLk1yՃy9=NZXٱCmȍXg3Qn)?p?r;dț*δ!贘;Tm0d뗇薽,ZLuސg#3uP6w&M!ɻK!>a:t^pS딷on^WgFSFQ)Kd+X_n.:"'{*K}5X^}Pǧ1*|3j#my]x[UFj$|w_]I B@O"&ƙ=7qV^2kPL|^9'%n,&{cdGXIg"Ѱ0u\ s:Hkzk” Lս¸ޯdMÖj(k?{o]j>Z|˻}wѪljPl[Au42t Ihn`2#F~ڠ<2m^yꔸA烋.6 PM#J jWS@4GWS(GWbv#8oa5-+yν,٥ ]+ٖ خuJդ7;@q5˾Dƨfnd/*"ƽ$K_[Ktjwlܯb/8YTO'XU6yu΍2K (zk$k_4d}NØRHNjaھqیҟ{YٹD[6 s2Ѡ t&܏T=OT0yU'v1Ĝ#IbaLw|Ǝ=,U7qkRߝKW^[&̖Uյ--R;Cνή}n2ReL2ɝ57_!Dn4TmT-G FR`:h>C~>bܵ׌AQ9V9|% SaM4W|vܫki,nam6}̟ /ros$w2^&gk4xܵM[_AT#V!JWـ-]m JgXU,fT+UaH|$GEfjVs>gK͇3AP[.myB; \^nT}9Mp3+ZڿMfK2:ZL.[ o& 8m͗ȆINY#"2]R<3);#&ВҫFHpZBi^*Љd[ rw,Sk֠L XTUVf2\e'i࿪N`oJW%6B}qԒʧnVs;n AXclltPV*~Z+d6S0#@: Ng}\l~ h> +"o Ալds6бs)펈*{Ća}Tee =. ?$9^=le5:|3 '{oPW .`W{?mz;RۗT,­pn=qĺV5IR+NCZJ_)}*J>' f}Tܢh9_†^UJT(\/ZMyFBv]~t[cM ²{-d/D35gץ,uA] VMEo 6[*FH+ +k|-r\,‰ֶ4#fX+7F\7Y.yVVe}kkM~ o$݇ԛS[0߻:cy 93ݩ-CG*iʄk_گw~0L:C{y6-ta`$1ĄbJmڪwvʭr8}lF}/~Q̴d])˩fiR: ++z28 r913ƭ'-/GzK5SW|@@"nEDXEAqU^s4Oǥ ^6O c~|C\ƭvX[7Lױ -.GZ)DqK伾oK7^b$”bjR_1<wpL+gp]%ŀYa3F'`uEѻlM/)컝v|fSoD\!(O!`nRU$hVgmɢ=g8fPD%&o'} }Ԣ0w \>"*L3SĆbکKܑ&t [{m%e2ą9 _Q/♸w\f*t',-X41C=/Èc~YUZƞ}{GiU` =4,j&@7(&TdehTn'*0&Ix/M)Xعص}C2T.y1BJ2x$nTM$ahACVaKカikc16z>B.1thZJR^&+SyX2$ FK?x0A'0&qUUmǂ gKni/URB)ɅtS|6 UTEg  tP-C@5 Xr_T<>#nio_%.,E~7fitGq(0Gٓ^l˓Wjin@YZeKpA~^/'.sOXv!o\(r(9+gT:Լ@eVG,fr1 h WnO--{ 0hii(zw:|h9ԍ1`<;xtd}S@ωT hYÂR@g<ȋZrT/w/}O7riJwedTJ9|Ϟz_f,4.<. mA !O&jQR7z9s6lRHԵR0jmp ۥzX'~ =vXo3=QأrlWgqD5x^h1HVa%Z pou£='/=Ċh1cE0|-m,fE@P䤈jj/OOާVZY@*"|[ts97q|yޞD/]Q +cR\He]!q(A;h,.Yv6"~V2w6 &-Yj9a|zÝJs1'5T]t:7|0Nljc*bCa7Yk^Ow.V\Q#QٍPјKsI\v$ +@Lbm9$zz1i]GiO^"[Wptkw(YiAխw Nj:O;(j8-:BCl1bUä}˝-{|?U-.m~bYLpɁ=G7:5Gmy)|06Msؒm qDJ3goigB=p ~[4uvO;+?ʗ]0(H~/iE[1T!Vl p'27?{Ղٙ98-7&KZAZ#JZ_bڄi4J/#K:Qwն@lk/-KW9YgFQ b|Pa02Vf\*Iw <is #$8yr +_~޹Mx7 6rzo# rj21yf[GBG-&bPX%،UB񾥵 F(lxrܘEeib26P-x\RYóh!1aѤuvs%*8jJDeDi{8}-z\O,(q}f9(Wq ^֓710{Y P kqzWg 6cR`xD*R4s6@;U;/o(?c"dF]ه6qwO˱#xn x]ZT~Sy|=ogȹJ;C]p3sv([XڛME 4z+*m?{M潕OG坻+J߫kΫRsm  NqȌ.Ӷ.?ZO} U9#Xv;Tou,ˊ@,k^Ez]A Oxjg0QY1-ք`A>m T*;d 5,h)UH1\NuNOL٪]ټ-4$!xo"K8Vw2EW B^H͙gxx5 b؏HDg4dSerfÔME]pվ + ejd< +Fs:+˚_XΞ7n!퐹*Ez.daYIdpPpFGAiuqN߾aDY^ISj\.FHj4-]4y5F^wm[b:o7)UfI[B%{kQQܔRWҪܲXISҺKFSJXԺhJbک:p|@:};" + z#@NxS0- + %*`w08,K@\;sAVJ1YVF,ۈn~U}5W 2qTڤ@f՗' j#ipx޽ejG5EU;mÑ \b/6ȹ?=/,#WG58PALWECT5V󚓒U ;l.w*XuW $i fkTØi[dup@& O2 '{jhݦ0<9T$h*|^V<}i; ָf9eI<_bp1.6,>v(&dƶ^9 %}3;Z&%<֚ mS,iw`}gK:S%|53lUObd\8F/~TJr!SM!P2'އ̤^rH&9d3@3'eխV}ydrIҡ&ƅ}sO8Ќ(8*qƖU +(@cXzFAf =V@~ {5 krqsscYoqFK}.d3pɹ7O|;@LtaG*WC里4[W8"eN>iyE:BO|ֽ[G :ij^fg [x=H1dRB:b'*fS|.dO~ZNR}[ǽH<#A{~&Pҕ)-szO~Da3>rnxl68P N27H袎3 <'|WM_ \*;M+.sGfѾou׸&B[I<}R__LT$lNr2b믙h8}@ݽ`'Jo3\{?/~`^owN [Gs'aZ0:ӈwe;M-Fvdv 3~T?cut\U~8?򵭷mӮsxѫkc +ٯ,Qݎz~>Wѳ5'elkBY삪U +symQX.NcٰD^j75:BY{tZk )4WBzl뎯i:ZZqCc=fmmsyp~s#B 賐1GM~z>-ϝs/{t>b̖UU0, "( 3"(.y}OX7QyTfe=qU a)]ݠv-.7J27.P!A  ⼇ s?e. iSV3;)[EyJmH>2^7 J#/d?lHVoy2Ghm7izӵV7)8hr֬9OAniH~`P6RD=ubRD>z~Z/w:(o4#13GI"C[:FmwRgVdj0igҋtb)Scv}3q98zq"QLHPi+ZQ0㩌JC%6o"=,Oz f`B$b;Xb{S'J&ljIGƌF6786k {Gz`B?s˥_0X\{1ZY ]q"2Eos}s4*fN.3ѡѩoq︎o=%DRy^ xYovwKU FT\RG.♐`֤f:} 23IFRF4!ӆ)i?钪EEe繜qBADE@&A`wDwuYr&sePC!+@ Z5% +D`{!M$e N,N^X]{kPs6uoOT *'jިʼ9sV+q?IdPZb\uџ[ڟUU\y ԁ@fGhk ; ġ' %4 6Q5'X)^9͜C֋eە Q{gؤ&5| @6(@I 번#(HCKȚ2 + +&:X-xR/VqnW0^SHDf@!tj7 p-zPl}~}:e4x:$Ek6PM)8g(Y%݀$h"1-U٩?Vfk9鞱G2Ñʎ@Y6 r,z9{ЙkzP+)T*Ļ<;< [ =h4l}eMG3FKxiyAdTt,ݶ39ry{=b沧B\K=y6%_4&K_l~{hWiz6J-|)e/En=_JYVҦ\5c i4^hN4XKfN9F!f$.'x;t|!D,h QI uw PAP%2i GKcN=϶͎lW./Cܴv*C.g{םsp*R4)'Tu.qB"v?LE{."jP| FsX:^"R(f%\zu:FSȇ hrD쐎 4Sf0{QHLo^ ߞ EG@E0- W8|rNĶc*n/FhfVt^)%JNkx?g@n* $~={ۏ|"qEb|?[VF ̹<3kꄔԩPϫNzW'_sEL"LF{=wov ^O|~ )ԑdQ@)nݱ+dVBñ5^䍺~Fu {L,_iј,˻(f+l+HVX^[aԆՎQͯL~Z+zmܠj喢n_bV ݋W^f;bQ}E GpDIA@@@QD ޿?tIW߬Y;öٱOnlsSEQ0¹AkR^A:I_:MZeA;'i9s*M[ȉr^}ۋw sO4)ߺOU/7r^u9g؎6W)FtKx}=>szfYM TLw +,m"J~^Pgu}D0ݬqS~FIg1o 5B2 g>c$ؖ 薋īHR' +͟H%MRHJi{QcA6䚎.,40\v>V [?p4$Ua$"D44q C^rƽr5yTMXc"Eyu2@"?~DF3>tun =6+kvTv6݇3no ՟(Lb7 " [k {Ĺ_r*kZP4:Pi +n$ⳳE/?}vɫSa]X"Ϭ053n坓GEMvwUWJ +5b;!ETe ?;k _/[WU[.Ϧ;s](FKŇY7;wͮlLCP(UqKse*cwH<(<3˥uڎFC՝P`*۰Kn{R.۳i7GK'LKOAOE5Za3#!Q\'^nlFѷPHє1.J \P[ޜss +~ȇ8wm<22 0jh}z帟oW=~8c5v5a=ᛰzvG$@%do $ `D+CTq |D柇[#BmVݍ6 #2Lx!5pe\aM8;F{^h<XktS,0q[Vh+K~*-{I8][]_wx};w"=Mw4d1\K DI@h$9 o<9_7h5ՋV>.^ +ҝGrVh`fڦff. +E";SU~X[u?a-: +{ۈ_w`U %u+^-M,{Oz1L.03meܹ8O'.8ivȘBRjLf?(4z'gf6ɘZAxv&&qc3=|:Ԗ|zG +tZedfrfձqѾ6Ez=d?4v?qп2=,GmD_3$blNpŶ~Lw(|əc+#vX̅8 ZU3۱ܯfvWZRCyC69WP[[1߱JRb4粫Č{f= snc zqȕ"`  :'KusxίxB2=ڀ3[BtTq߈P"prpXT5,vU0zm VGtuabWc *iVDppA>J ȭcm48>ghAT=6F ϘFIH' = f!W'@lVG +Vz"] +pӼnť#pQLX-lS&eKWB;1 ΅rh`@6p^ Հآћ:5٬"l@-.JW& \2;t&xO/}ng +@D$NF@$Br@: $5p[ 4G@7L$$DޭE;W^zΤ8~`<9yz,`EFK3*[Y'P~@ [4fDef2eG u7xGkBoiFAR~ b[UzUқ;{zS$=F{[w0zZG1 ( ~NH/! 4O@O(gu\E. a,i fk9wx};SM[fOPd9-CҸXtKSۢ_ +TrƯ +SW: +2!~VuD +} u:c& vLߙ=GwӐbvrb?4k߾i[9 }s)Ndc@#"S*Ѕ)~k L~fKa>9zAD1x{qYsT|ldoVӥ~9;:i`veŢ[jg,TjgG;ՙԀ^k`;'x!A2kxIfNLa0tfٓH7=*㨔TFRUKW!Sk^>'E+L[;5L.xyz;h mˮoi,v=1_*9:#u"]6wz +ӅHF(+>t1ň kŨ3WM8i?bWQ_[.71y?NffbEz ``;)lSϥ).=ikF<i}y~DŒOcz??Cev.!;JL#+~z^R@z4y(|lds#k6׆mLL2-o/n 'DP$}i.s>3Nic*,2yB8]objx5jBeU]%ۮ:9SYyVJO/#E%ve)lB(vrQ"{S#x4(t;g~|WϿ/%e, +?%Ci2$ 2 + +*wDG>un>Y+ε]7k5nQ+d~'aMKk>*+bqj63SHAևب^ӃQ#*= _V Aa5!tU:P- +elsxg{4 +{tȥ?"pͼCW)OA~e߀I`~dm +JmϰvځeyS滧vk}@X4nOU͔{~w~DŽ _v՗=zw'l kif;,bg3E˳ ĨD*{}V#ߑQa~.f.%fL=;6@mn_Oh+;jg߶7b991so1o)\PtFNX&UyBDj ™ +ۿng%Gq͛*5JVh?Rp#LR L<8hHoyw|ݴ`6kg#ܞ3-<7SzNKԒ/?- X<]2zB厎6#eAjC5,+db΢at{Cd^1V-:gt^V%0MN81iO eM1pNZ['@I?d2RG7\!:~fB6P7Zxuri9?HDtr? ˽u yBaC)ũiҮ,n(ӯBR6ؗW"B1DT%7`}ǒ= iO&kLw٢⾴L#]^2h¾B vV>% ʃJ Ƨ?wS?h{k_?9/;z;FPJDbhHh2kV}qN_9 7:wLONh.E}HYh׈1fjꞦO&&o#hF*3⮪+,҉yЄ +aR ի'd'+_- +?ϼ8֡ ͅ 1bMQ(K\AҮg9CrKyBd:ed)C`#%͇\ +HtN}dSKJkm*&[]Id;Ev%U-J0K]_7Y8i +$H>?h\fmlx>K'I*6y#=A1uEa6EO&)yӃZA>9[԰-Պ#ٞ K S+-S0`hեX T'lt݁x!GfolZXP/L'H MgHXqG,(Ə./`CC<dligw { uF#\2HQ#Jȶ}p.Z;dĤ4G% Oo UGnNg )a!8 :]7@N?84 K)|+ /tfIpv<)\$V)L5V%K@ne6U VH*#|4cpx= +1hB,}x?I}67R@ 7 {5t>PNYMGOEj(L|]9Τ/ˈmQ(,Ҿlg&Y,uȟE$# b_@ӣe=6Cb\{aԁZ@RO. hzlP~i˳ӵ[ lAK[NJSk's=9 _D#DR_!O4h T; uXhEVwFM93*ȣ1;eG|6Ś,+͕joHqn;76- ?@aLkATQ]үMv9O>05/b'%.۹O= +9φDqZ) 9,_ihruSEuz—b]Si}62g D1$ʓ[s¦LMso,`JJ3CwbCQ,lW~ #]VI~JޗYi;}l%3XR^^GS+8A]D{YF5̷AfIw-}>R%i7䩯}tsm2oiۚũ9S}no- /LmɟJ{(̿,"B"x_X GV SIO!skѼg+*bc&<[ZN+'b`n%)BVM}q>TC2-?~>DXm#7GH'GK@ٵO݄`*TJ) hW/| KX㖕ך4/0X9߶uPOɵ9c@ 1osUBk.6 Ip@RHӐiNCbe/h]ӾnmZ\<{[#\C2r Ϸ9|]e 8.TxeoW (̟ NYZ*@IUjTw͑c} +~q#i#9Cc>:w|VQCK}&9DEY{}JZZqvF(l'؆@VJ=N?m` {@&^A^870fd0}~\$/#"vUK]ᾇiX 6n%Fo @ 7fI푥Ie=2_7,.:%&Sw.ˏUC)oV7|B] 3)5E}}h6-iИ{ޝ5샳fqhRfZ%ܮ,6n:#lz/0?Rt^{`$́.mU$ ?aoPL* DAbp|s1gYG77Ոs+TM=F9Ifa+yy/%h~&^aVnk~zD5ra~ky'y'BlA -4_sdm5ʏr 2WL" [lVȕn$oՁ߇hp 8`m u͸^qFֶ̻JW.f,Y.hgXj顭QQ>" [m]Z\畵xNnqk&ì9xՏ{إ=GӹrGO66q7'BnW,t,7$`ٜL{{~RnAFyY(${zIbnjcXmW= ͫբ{ԛuG=u=Q[ٻYCY7(e (:-`:W!L}=Mb ;Wo>ٶUkta/жW(K.rm4=QaF'{}_گh[ I:Qu.W2-֍4$Gp7>EQ),2^j*>@fD樍y,S{teSsۉT$sG1yѴ9>1e+2N2Fp!!rR!*sww2,>Rkt!5Uw2Z&A $b™foj}Κ5[5CJv=^^̿?dݘyh4KʤtߝXc2BW@+Y*S7$#c.#49"R:` ̘w57>l2C>7[ t:ӯȪtN|,4\&|{WK~!:f4΃5 \9ɑPIAs,ȟ[5 #ۏYT))~RZj@Se(jeG ,Wݰ)ъRlfc֯45p)ECoHs=|pT7s3v6R)%(y6* MwOipk +#T1g؄آ?<Is-gV}tz3fdy׼P#9C ׼a2Q*猏JziBRNx qƜlyg]\fc2yB:/qw|X $\UMܽd'^cL`PUO?6LwB D,+@v־['t}>Dg rX'DaK܎\Lo#K=TwqRLMV'Fq_J9@-Ձ<g@CI -MĚK 0@d +9d1 _(P^/,JG|ƾ"x E3/PTX!gX#p L"!SiT)c#1az[k?q/.2/FŶ E-d$"j{] .He|G$Pe8w k +1:xV"-x;;J^ا׷g1p< ZaX-?(oT|0QM&S՘@=DR!%r<#glƝ ?3"^ׇeߨ #ltPbƳ`Rv%h7?"ɢ|lQwo"O@-'2w6^T q HY?ddk(oi_Q=V0hZX;) +*U2&ԒSIW=T܎HmDF,@tB>8s RQ Zj@A CT3ǣO*Pi&MPKD-^傛-4@+Vn^8lK|^ڒ *o oWh-67]mDN0&?SjGB"ZR .Lq:~Z\xtws:8s(\##[:([vf^*Zׅ +dj@j+PdۑnSvZ2/9aǺ{gqCɻȌμثrsh>jLGU)ٲ`5V%c>Tw@L;(jg]H;R1vw +~]SuɧhXڷZjVMq{ik&ܱnܔ S%^ګ+'@lGU=,vo{W{B w?󐷕R@r>+GJk%orKߓ 5ʛH<ډq4}a#a9ޡc[ j*{(踵w\ St2PJ@Sikx.}?ekoC߂j+67ЩŴWtײE a1`Nu M5OusW AҽCݣ~w<]?4n Nnس׳e1iڍ_?Ev:V8pV&BF&u&/߷.ї,Ф^_rWtȶG# +'_5g7'5\@jv37ɱ~aAf.#2R8ᯮOnP=!S+ NwTtybpN۹y<)xs)\{ewH Nm`sƉ'=Y~,.*o;&>K~BA /upH ΎĠ~2w6]9XyVyl#:SLX7jѓo;j*wi$w%TQO(,W*HS}KR +w;7Hz9fm +@;jk<{/y +WhELhp*2pexj ɁKaS^goPt.8?oO N~U|-#Ip+ϙ#\Ϝj'8T]kvbC,|<4-raDLqDq!/OAЗs? &TngBəU$pxO­¬;o*enx~7cӶET-hcP) NĘӗPH3@g̎5Ϟۤ% 'S,ͤb}$ +{E482д<]ԕѩ3sΊ= l) D)eZh&/bȃç$ٮв\X_XWdS-F +8 Ϥ [KnXDWU7=mcdѯځ*(vj+6,}ǹd)~!Ua;Y|%8ex9P2hMv-N/qU,gEfM.@._,ا^^ |_ g,"pOފQ ]/mլ+uT +yVid"o)6<9]PV: glPڮO9#bIf1$(2bD;m[ Cީbެm[G lh eu_@G*Iwl&(@\(u֥JatP[ }\ 4im=Tsh& +4yjh$i)2u'.7·|H@SHaۉ,#-If/z=*jFU|sޭK9.8=/cؠ_1$1chPԫnSyԦcā㒖77x\쾡af%5PCv^ք8;<~jyd.^e&  b/l+6 XP:]$~\+bgƅ^4{ ғK0#uvջo6o_UDsiXaXHu +Iuwx1VNr57K5j9&|ê0}o>ODBoѵU 3nJEX"핸u-u;yr;kǮ]b7R3MT^7wi<|7Wlw:9` rz*j0C0%B1R;;6[bA:ecמs(M`MBHSKBC:m_}E+IUufq5OuxM]PW :TFq+z!YZg_H7NuZN-47)y(q]?>>^>1i|FeݮݗfP+DU:GVqlݐç=[3I%WNO[^˧#ePS_1'SԪtx$diUnTڔ%Ī}v)xc?yJ巩휶/uK깨/ `n,116{zNj3@UMzd6oxҭH15D[SqWQD1x("UtDbrl]jl 툴Plm3XIщF:̑%g9˳;=Ae1Ei{8 >}W8,1^-]ƇDٴ^3sr.3۟NbCq: 9Ĝ'}HLB~3 S>!p7@HV $#H,$M*Olzoڀ)r]|wl_<#@U Ds7@y;r^?̔4 v:|EԐb Zi#piI +pKe \>> ",z^v|CVaШ%0[]btHMo`jx|?OY@L=qfװ\nZ}ф=?h.x$NC'wP攷.3H=b|zl&!TIEy0a糈yf+A_w޿2j ERH?O] c3tx#5ݞ4a_3s 2w`pKZ Sc})&; Yv uJ/K"Q0=yQ<-VhW]9bdoǝi#a*uFuoR@lśp>E3 x\lK\1xbIY"x3Ob4alAp=.z{zrr͇crRjS )nH"̆4rOA 8t 9yZĶ̌)\?fצ?ڌD +?ٌӄ _4[W][vFۦk쨤!(T&[|+ǟ VΝTWzт`ގDG':PK)h UK>?$n}Iwt\ +]Vaצe +eN6S֋Z7}yOK@JK"Jz/+W/,<풫xkS@C,>G7o!wέnadilCS٬*DBTEbΑ U>_awbe)cuR*D6QM6edSeYb^˛g3kbl< j\vO7WgExbs91:."M֟H;/]1y #QZwb*{ ǭqop+9u:?ώl7ېE!ڿKnN]lgvQ3u1.ye#/XV8|CVsGN"xIH|4^c3/pmTtolwA N3&gZ{kfNݹJtdA3.\J /Y&~p{.6гEu%[̓eZ -}:ƿ`u.]כD^l4r$XUrY:jx1:yg5r^!,FhBKK2)$jSCC! c#\C8D o!=+2wØRYķGܛV; g^ _ONٰd0c QN}G=&h"\C6puftq6 )Hi+yvm @,q%@P6htlG{ڵ;@7DU:/.7ÊcDXU,߉ q vP|2@P6pGFPItzy6Vk 0$Fk0bUHXYX]/@v"? S54`HAXf kL̀u?7f4%]`/apd8 +>K*DG)+Ò.فu`zΡw@2T2SP`G +dlF 07rX8˷.(&pݛnf~S/׉Nzb_n#|J:Uen1<*-ZzI ,e +}~2m"]~76o*3(ȍkL=QDMhޅC>5jSV B9ԉ1jRoB{<^,߸dvڑʡJ_4hR$^Hj2 x4b-wtDU4χnL=[+oa|}&ޚ2kRUoQE[<'pIe~UKi^;ٱW X,}HLPeٙgyP(W0Y\sf}2ET9 ͻQ6MA1ۊ~*pg!&!&ZL/8ȥ(pv"4$}4~n5:z x \{o18}~oۈj +i޿02Lv|l9d [^.R*隌S!{rwO7g-׊Yw|sA+̀ Uk߀^ "˥*j.c?7ȭn k_3WI{AV(&fcw&tc.B@W,8D#lae{xX]Wq#o$QM>ڔ7P; {0-cf/2Vx%$lt9coOTt24]iЈ-)DH oDJ+Wbykg9})|NY/xb3;":A5H1_ Z +endstream endobj 451 0 obj <>stream +Ht׮8Qޱ# +6spJbG’܎C̲p9,`~N@9 `oD4˜`tQ6F::iƿ'U`ce 퐧bZַ +2\E+u 8ynt((#ځ^ ]h#wһÞw&jIXG8>v#qU֚vT=;+Cl`7e4#Oj_?na}e`* A3%ysKc3wx N~;FR-`5eD6[;y|sJy{2lt cGWR~Ce8fx#- qxc&E{d7-W]}tۮ\[x`8s޼E`P߮@eTٚCzɒq(S,iQCأR sm#EdxBա  J6AQ}RK_%lS}k)|4)w:#MBZ9U͗*ze+B}0|ÈKr {/ +7tS\U.t4.c'B{tX|s5O>yO9k۪_ꗝ)N׺b6 2nD%yV4EQ!7%%RW&PSb}Ίb\3 W xSyzFǟa}'G IM~ LNs^UR|VrS.,HB\]@),h9l'Y:~@ o׉:e8v ]t֭a%}LއxW/ݖgjٹ9E˪=& 2ʤ2yK8JN‘*uQq`el[m'X !245:oN/JB*nr|a_D`mhɃ?b˾6?߲/7K|0gEo<*rzB??W.if6]˪2i`BI(sފnn}f3CiuZ]r0l;rɣШ(c)O=. h9a_VK&nu2uou:}+:=[ 3i;4D1

p!x Z}µOEn(.ޖ'-r n>WWaVc#񫲰d}l{mgJZgm aWˇF?94KϖdI4wN-F:NugQk)T,YJj˯BYٱ|\7mܢEߖQ |l%ۊM&Lcx M+ť/$4~ÒrMfT io+z=1_-϶GJT`.gҊ++KWM&-iQ)j$rirutrl3K]MX7"s~a\خ\{R]k(EC{`d.ax t֧1-*6gx$D,/SbGLXLrb?T Bʹ( RP/8|7zg=yqPsyCxVNd}*P[ `ΕeQVU%_#Z +pt SDOLb z}qHopBԷE8tK 9ph~*aD(/8B1 P8Є2Ft7A|=ghַz~ +B&`Ǻymx@1n:ܽi:jy[|k]YC UH46hɧscF)F#|7{;,.ŏSẄ0`]R(ܼyu3{OsCşިF- ++ HvpYˍ ' # 4+<5FzXj~r3*J+}Ӗw84oXqUZzпo@ S!$3"$ fpׄZ?+n' OaގɥVλk{ u]N;v?F^ 6;sηlyD?:-7/4/yUL ߀؃ x8g $4VuQujR[T0f3 HI@D0٭ڪ恾z9ݽZ#y@VimJОByA.+ŏ#@LNX}D>֓}cڃI . Lgi _ s h)尛Y:ˠɌJ +qZ B͞Z%[sY̩}";lJk77+Fp-rUb 5f+s>;f!eKgUd`J2)MGɚ4MQs}fpN^'MW.4*ym8Cqгo?QIzNMښl{˰R_=^S7Lho` 8AɠGu|6Av+c*̺+MUx>]lgv6<2?y*n|sGfX{&ur6+FhvF\9=/U!L$gR|`dQi=Us{ʩ'(3UvUF`;6iNY1Fއ4u:hp5՚VW;cd_\v&Ǎt'\UV!xo|A/ 6eYƒKn*N-mCЦ)+cTד;z{ʠb0#2$yqk]3/XF8f f<;Nmju;h7qUn. 4 +j+W%EX4Tuv $a#b (q&U4baL1Z$D^yj=٤. mۚ[}֝KлZ3}Q3\ Zif8Jzg"7 MZpSWP,T6$bW!ZqɹaU!A/·st(|'ٚ&2c):@Tul#<`*K`ٓr,ML.JY,O=~E&q vwnsg` VY2@8Eg`=L=Ns41ULk +KZ[7&eDiZ,DXvV qt/mcOpc{1p7c Co;ΠW 8 qpmXQ~)g%lɒPH͉oPY +x/S>^\C;Ǜ}PFlodma1srDw!4Ls0W~:W󆉬u"k9ndxץq~0׷Gn 76Jl_ԙouJ~4=U/.qtsejD22V{ +q.AңaDQ́(>1{I^ (!9VlTv0Ό@U. _aCZ uqlk_me '1&+@[KY 6w~QѡMȲ,ҝB|nj,I1 ŵMM\c2?S`}|vB;.B6BJz:i[D6T*$d=6L\>YZc4YC`os,7AÖ>N3߹s:]iz=UOdcۣE8rwqᰰf6;X֐c`r-7Wugv#{۩nnMSOi7eetJxiSex|j\Z5n+m[)߈_}7##cUzn-~ޛA·YjYnY2(wW-[4O8hXnk\CH{-j6r=g)kY*r*ҫsB+ٔii˘MӞq`gt~;elF5Gz[-͎M-5R]]V*>z8ޮJ@~ZA#cwߔ Nwd?d/eb$5d!7`Lt ѻK32yRC;85}kBlJ[XJ#d jqrqץSkkKbJԙ9e6l!EŻq7+ܣ <+2^.2 b.^OkDG!TYޚ.ѓ;m-0ZˊRb}P n~5@ Vbv'L^@߅x&Z\6:jS\yոJW?AqPLj7{ J.oUnT"YcEu`ֲ}LlıaiHמqLKդNbЯEEA%7<ʞZFz. Ʌ` 5#d=ei]?'+ !+|7ָ?y[;ʅmx~- V) +O-;%#7

e{!j JW#Y.4h4xqj4gT ;yWEZtIH0(bο%, X'쀂e6d7e?Ǘ_csR Ζ#]X#%zVr/W c +.K7Ynr0 +d>&֠:`ܘxqVu $ b8=H\ D$H +PӜS%.@{Yzլ_L +=çq Pj CCM[Km@J*WQ[I猄Ρ*,7빑5lKo@-FLفk'wRgo-m fg'|`u I\]͹lqV.իU"ɕ[݆9/@amh +~6D.$ +ߞMg ۾K, u n*.aբTxPBTev / +Қ򂛽dsbU%lD-w8y r"[8F0mnQ/y.qk|O4_dռ+/-za%H/)t'oI%cۀwLSRLj]0k(dbdk32X, K +!XӾAOdk +PsGp9uVa8l){a2F41=fHo{4RPV~q)/VVb*|7OM&k(sCY)~}Bp?b?~*oLcS3f 1`Ja=?(}`uX +=/j%ڎuG*bcXrMCOɧũ;V8[쨼0<8iK؏Nٟ3gXhD"c%HYaA9l4T bսgŁ\neZ᫓Rw)?^EHN-bXY1l vAdըp1zI>{<.Vٳ!rZ/@@ƓGXW煲dN&Vtf0wDm³|< &8ݬ8H 2N9 l9cǪzH9?h uKG,%~9C'sUy ; 'rIFgҪZkyi,F$&)p?'o&IE bΘŠu?PC?x{:hݮ$#Uv?݉37[˖|xN-1Z*he>p=դ,PD(1缻{Wu_N)Yt13+r[?'mby BL=~b'olQA] +\=\ݲdUCnRfZ8RJg[K?@VGS \LλAuv<8]ف^X}j0!5^ǐo-N1?ͥK +O;KM˟=YB:͞Cy/rH;]6|+Mmf?>9|ej"g MφO44ݾ~k[D2;\nm+Ouh= AO։e g';aZ7;R:!wF[}w`_~ l2QSܛF\=i_KqL +_Q*IXwFAd4{"ȦLoY"-5eTԋ:WǍ%DGr`G,,FߢfHeSq]YLIÍ׻{/}:ޱq +ս1{27=IeӰ΍sm +U5`;ANIsP)/D{ʌ@:Js%^\$QU@fYޙ4il(#o?jNM9Zh$F]%|)xȯ=9YEf-Ml"B捳X萰XtUق*A%iX@,e*MaYnV1Z4vZhe:HPMוܒd) a+A!J'$g40hLX=&FIf޶ģOŝ7wJK7%n=!n?زy<͂`fw> #!I3ԖWU5*4 Z}c6n\gyc:o$0sSЛ Dh`5Oi4_4?4wBaUm8ˌ9&mA-Y3Ά[Pk6Ɖrp NO+ 9j[y5=]a= QVtp8כNI#p|"q/n0׉UZT< 6O^{D +wd8]g+^К}0B-ӽjx6MVOZ⚷/gslM!+7%9 =^ʷe2 V<~YƇq7yEGt<Il f<,"KEIzgZU6+Y9v!%kF|}9*m^ Tu4z<yp1q^I!脼e +oL2ri,{ff%j"b- s[<74 +~ּ{CzAJPZ!.]U)4% ,!Rm/С@R }pc`*DQqx.K4oDMOzv%$ѹZ)Vw\jzK#/v-sM%'`7X^?6$j4 + Q:h`}~ctpiw||*\7hr9q#DX?tْgAvUYAA@f)OvXq:ι", #K+ccLpsaa|]T@Ly-BM7B&r8@2M+E&LP⾐ &w7}gx;1uReoB턪%mv_muPSwIa[QgaS"]>,! ~=yW5CL]F\ r>B>Y -H:`>p$h)r! eˀ+ZI v"cu +9C$#ZF"vGN;KiWvQ9ۡmbOoVU +oF@}"^ ̿'N|}wL6 6H7*!kUXRi=zP;ڏ[;Ix-ѧ^6/pNFrh8krm\XƮODzs: / Uk0{; XdM)&*dž[GVFB/OPagrK|V +zĞ%}\Il{= +F "-]H=#pkuأa򊭿H3>S'<""G4HvwjV{ z_)}?)l=`]uܒVMkbY=^T2iJj2FROwgG蹝 Z-njURX !EF%:b30m\R&d7v\8+k8[JnjG95)#y9&e= gb}Q6 +{]tRQIH)ቯ +ĺcϖnren;ٻǢruǵM.B֟Q+g-&\f ]kh,Ǥ9ELH3gYqmT/ҷھR,Teqaޜ޺V*)vur ߗg!׽K M3gؤx 21z\ +ӋTU!"Win:k9R6wM~_]d_yP:Dd +yY\4ϡ5k~jN:KPh5E3:;5}ExeQꤶ亊SwMbۚ^g}#=O|u@TV,欈$#z=z|W?P$Kyjmxj{TKTWzL|E=AҎۉw`|7xx ܋rk?N,fnX۷J5953U9kJ N TBDofus./,Ja>?qvsbLj߲ m{Oֲ~fkp7d4.;5F755_rO@*rp y4.L%Բ/F>1 3NrܵA)mn]➃N!Ma׭owdV_xw@;UR:MZ*ȼ(ͫ (;+Z\HlL$x2v{{37QZ?,դo1u?k=*e={=jvB? &CU7n}v(3ٮfݑ^d+M*I\X]!'F`]`jh.T_Si䆯o](&? 'w칶ubd# e˂/R}ҁ b:3j=GU52KXa!MQZ4IJT@DD2/u[BpyX\f6س՟RYZ%IsI=|SI%"Miq®%GL8׹^Lwًì'LgANcjIzޑz^% +z ˃q :,c;mkIw'b-i 5%G] +}o*)Z>3X hU #Լ"~Iܱ= 뜠X͇ +Q3QF76\Թվ~.b&W, +RQiBgh^$*8펻2:H5rN'dYDp3cAS>iI\,'RB36p5Y\ݩK\W W(Br%/ř%GSR~pDIܠ*5Y!(12ggw"3U~Rl 701qZsF^ +F[0&wGg#O2/[7n >1zzxsY)'@\Pk/Q8:MI^Пt.M39Ah >O -2$ (H@H&$$ +ar\ϗs6o6ádJ ;r\_\ZaS@ +sYAoOK DDݦ:;0ۓxցBCKXcܩif튞({ATH&~B w`S^jqJ'< ǼeSݵk>tV~26ʓ.\:X%eb(qtQrhgB cbs.nWc@dкR5 Cq UYO]e,%`YQu]=a^#f_+k,2='ExIM/CQM-k [ Ϛ8O,\̇[~ۗt.K`<`sOĦ 8lC/\[˘ *Ӳl}=>zyS]C7ծllWQ6=y0 lzRAq4ap 8w4q%Ф'.h25kxDҽS v}'(rSGL,1{3~r襬D=j?C[6tc)B7;O!Vr&][ѠY1 +Vp^4S#A "O@ V yOC(V:a!lc*v;Bp goO_^~ -&X. jD,z|hVy `ms +? !.b3GeP ]DZjQESZKȝtu@ZԾfDKhB?=(̬O < :X*=ᰧ(GdZGD͋("o!XB[""bQ:`AihEYƛ`D '$N¾>n|(k}E?Utz]A^xJHTg7[3dn߼ +%!-h=D(9L{z_%gZFe|98?߸J{a;{%}LԾىX6&vJ +_an8Et>FX70g Rx)% +MMJ$,6ԣқT+[b(/ >hJou;A߈:H? kASfkHv*,ƲڅFwM'HSiPh0(:{0&L$oNv)W6=!G9rB\ڵEAd@op<4λ噟]"6[^JzmVz~+U≒~/9.ӯ/ߡW1\)ŀhi`.p$cE',ԫGj9TY܊TbNy_gp_uOl5tf=o7,WFciicȜ)rz ˋZSX/6NPb=? 9~|J;mm}QwmO:h'vbfq"r>ֻvS]*9)~uy5+,Q YPQDbtjW7O_^a-UT6/xF \OH+CĈmʵɪ+gfуyŪ@|Yg$ϵo(6[WYD鸔SQUoZY/xL?zX)c<PqJzX׆$nẐ5YD^nڔZvj~hS_㮠ږY>yws~&GY~uwB62BDv񏌟StvV^Ԇzn1亼<)lt-3.BS:aqA CuS7 '"ll[zčS5(JyW xuzA y:@7b: T9"!K~n\i }f2،nn3w#0zʺ;)@i@T/ ?.TTYSޢ#W[: @B/!'*'ܫ_{&l,Y2mK uÿjqu7hjD1YFC^4iCqT(k]|dsSzCY49IF8qi㪑{ҭrUڍO2'Ӿ:隒s*Junb6݉~ jCζBF5j~|d@Ý8 :tG*̻lbb*e לMRQ֥Y$.MAHGlHs#.J83RO8/ܸ%9Kߒ> l!Šu)g'ՑWak GXی*UV \?C0 {kcԒ +^V29JGoI d(Ԅ4[/R#x4a'꜕_Mt2+~XpAO53Ӈ/|fƵ? +,=fuAsBk.Odqᩥwv!H<ĸŖL z +]Wo "=M15&#cb!e)S7Z)T}Ȋ* Vm/p[wlX|Mmz_CMڄ%*u P؊KІ憿9—77"ߦ5 $KSLnbLjBJ!jvw?[K3]m`߼)8Ȫި1(!u!o+ +&hoފ K-R"•*S1(44ga)n8ר dX}2ahT$ Q!akqC2껇sܼTbTs $,c{ +Y˽F4'92K<2 !3թM7]}|L-W+)#& /0ddgͯ*Ewy>WVfwiKLu>=6s> Pu,9cOn^70aŘiM4i8f.!xi>6?2dJ,aR-Z+ F/6zp=ͩ۾Sx5;dpKk|~"eMO}4P)wj ꀪGB`LݾMvkz~|r `]{S7JS*.v5<$`ZBrgQPu}[.PPPyp%w N45& +|O_ iW,P=a1CmVjhii-avrNC(#d#!WC1vN) -ۢgיhzᢙUj[jOr4D7ÍiDYk,jZgo +Pyd*EԭbRi7%"uTWxa~U|,V\\]u9lWܓg}r0<0r7ۓ͎=e;'6=uܷ ox>@>JD &2Mދ,ᶇ%F`cvJu} A!_+e( ΌM(zLĔFLC&|̚+EsqÚ\gp൲{"C4R^*u-{e,{k ?iӴ2D &U6!76P`s%U˔(hYix&1U%Ǜefy9;1EzYyv Ҝƣ3J$B w x9/f4+% +z"D;&G/ qKx\ ,yUAߡuBk +ZTͯɯkEBj. {rd"o깢3٫ǓZ\q).w"ń-ݫ3ٍEs-LC(==-Zp`nJ`)x- L! 8g]ƀ{mo܍Hg-̇Iy-g;?ONn7Jʫ>-j,)< +s쬉qr5g.D܋^*{<%HvcGD_tUxDz; $:VRL= 4N9)5KPhT0:oe\O%%I.I3tM-Ez1@7`͸@V_o@4-_#%;~]t!@.) ׺ +2(U1e6(ܔ +/ R(y?BMJod T-!zg:esϦiA4:1Kgʻ&C$ ю3xMsd4?Nʬâӻm[Ժ\o6PU4 \>EOG̖Ԓ(-," "(XoTGegOd +4VkviW +_ ? $ZVg21u4~F +|3ߵ}U5d\UTI_qv:-dP0ЭY,<*7雵>1x;1 Mt?HSȍi}3tQmu*K?@g߶ qowE1 cS_S'eeRgi +|yF4f+uh`}#ߧxBF0vJ:CUv>ZAVyx#z+"9m]ӗ7vr-"Uj3*ҙiU`Ժq O7gv$Jg7;R&MG}U@PLR>9[x_j.GSϦH}d8RJ"0y9fF[oz S}XH/j8eڹCu9{\S9$5'+#r$9ͬ/BݓKwKLĬ?};zIJ.5u2{9%8_:B&勉U:Zrz6 +j@zM@x1e6﩮hIZ~}#0{@Q^Qd3s"//£ܹˏ#ח]#sSP M +nGJcɮ*Ϭu%&$2nr{{+wRG +Pf!F`!i~u/_(4W +$k*aE.[4+Zcj'R:ʪ2h%n;}o\%N=!&x"kB227^PMLHC$?d +8i7oe!5kꢉ~9D>e#DI+(S|mn\ոE}2(:n2"֛A4EP>$HFG +&Y%^L/2ML4g5+C.tuWĮ~>b-U:eI7 o×jn\O{ևJYbw +Pю]F}@L2gjK)^0O4| D@K,CsGh838/}I׌[i^uSWmݸiS4auEeȚ-B>ѢvM/."\f[Įԩ_lSL!}v; I: 4gH׮"Wc.?0Yk +";ȸI-\d~S}od]ՊwC (nS쭳U[)7W(;bھ}{P&;ْWX j4aBmӽcB *Sȿ4t(^bZV跀b%IDQTMyYVU]MԚGo +2Õ$/(Bnl!LO&9JfIL?#&OEƗp!l./؃+8^= zLQuO1}~:cԖ"MXƙ<݄8lɇ:;Qߑr>0Q/,5& 1Il`XooeY5'-w˫ۤG,9Gs Dg#V[MXWOZ[LI>XM@:uex9،[)b=1ѭH,EgN3v,9L~4qҩ_ +tg<̫nsar\\pЉ_!Fwt5'[qR(ӭv wV*& +h*ǓNBw%(&-n9\ٓv&3u삔g7:zup̠n9qhy,N !`Mo[HUe)1u^4ʞ{]-,w_6v ;Լ>ME<7K^y0+P-zlS`j萼b$x"2ģ U+cٖDM[ y!B)mUi3hM4s+X~5lo$?άY\R~<B'oG@;${eČ fX[J& +9VrfPeI_ V-?l^L_Ix|b4{^~yV-V̐$lO#Fae+0mkXu+elTm`oW>gշ]+cIԧ92]5yaq55c='6-g4lcRR.~9t/]#~!ڬ[~?/q;gLj+&p9uSNdyGegay3ુW ]+`CZLd`{˙lfLdz5nT+K>!r:v#Lw)^ | d$5dXpg-}nnya%''6NۭpzT\S*P^] ZE:p:DW sGS|aK 5yXJ$ԓ3pm\0?D9HPeVYlPU@5q-NTҾɒU0h~ 4pƲ 32h.xi^zKT8 ->Z*WXe<3b48qQ+|`>!$s[f SЭO7MOP!_dFthZ>~%ZwKD*]]r\{s)'H.@X ` Ef``[Ƶ^ێe8BBDJ()"y]q 1Bys(ϦP@wɚ]Uxw7J +#݌raGP ,axS?>FٹFR8e'Lt=~eSt.jˮD Z>v|gVp%g\ =ۯX<a?:E1G/tGPw^Ǵeo>c[kc,f? L4^$*g#z +8#8@Y #'] 1@lF, _6,/z2,qkڭ9^Rύ=9A|TYki~A^rz,@h =Y@ + ;]H4߇ xɁ`$:cHISZAb9IC rd!J "d؅2}CO>ewh~fɆߪ]1)6ٻ>Zì6_~qB琛#5,!ZBN4"NOHutcΞ mFGW&[Z0{f4!xol@J %Yoxut&ؚْKؖ֙f؝~.H{|W&/-@rΪϘLԌ&kiN:5BH5}HmID[Y#+X)di@%tNvqsw)%\z[-sM.7&Udsl  O:ZrjOԧp<!?}լ]luq;}ۡI ̮ VVhm/?cb͊hx=x9ѯS7NOSѢұb-Ak?/cEL9%9e2Jÿ?;%I-`ʵjPy3s=y2ỳ92b#2v}y9ezF\jmF(ca*H'{ +Uˈ爽o=/ _6lo59c۝6:?pm:c`Q亿$/,'z!aTK$sq LzQTIo8ƥ'JZ[W̝W$ KIq!::=}P4=Ӷ*nӏD``oS%)5%tm]z1+re>CHxyMWkVЧl5 á',/XfϤ].pܫ~89y90߷Zo͞u箵yB7[ؕYϒaՖِ.ee >a n33bB0cVP_j߬\S5_ 5i}I*wMgZ_]|1ančqE?'vG|)^)m﬙#ߵhv2Y_Ca? 쭪߇`˪M;<wTFC^2fMxw5V@܇'`?vծ%3ILrhj[X,Vߘ8 }VՃ똟rf]G-IKrYpuJÂTxA)H\4df `NlQeˠ+M}ŽN^yX);IHv>{U1k8#Fi_ Ɔ_+a{7-ojfZ}He{:V+Ϡ$EAK[@J[mmfP0ؔ4NpVx// S7ȭS'Չ; UGP_6ٔ)Qj{EeGz,_"]y쓲|XҎ#I+*}HL['Z49)vE԰8.w"T'j}Ʃݢ+S*ut%h+QkRJꪋ,TAPbQD(SVFU !kEhDK-ͅa w=y2+Ԉ=L=d}wy +_? zv8:LzYB?iѷ4vՅ[꽮*" +Xcy}riwK)Jmx +wl1/ɣo˚ō8?F7 Y1FbCf rtHpM1u:k4TN4f" +iY9$a*^@x[@NEP8.rueѳpbs`5,fă}@lDMaK+-SʭVnk{%3yþ@q!ɀ(\㭹<"0qd5e~:dTǤq^{VOP6s؈]wK 8ÆTL~f/1J2Ӑ!Wf.#VÐVҵރLR!8ipM'Xq{bBg3Jzi+LI I0@ZC)jw>DZ2B#|PFg/Pol6;u"&wAt{W#no6ν:qwd?!dJ R(FY>**9`ˆ#b@Lg,>?T+jQ*4 euzpߑVYX0[:aXed7OVEwQ=;Wsћ]thx0(<0*'ntUcC~^4G?8]ܶ(#7:S^}Wٯ ,QcfR:) +;U)L I+}:za:m:/eHU*alcoMwbWjv)QLrʨuK$ + ÇX!f.V_}l{]b$a uLiUZeK"tjocM߲3q|5o=!8@1~nX3IqQK%F=T>x~jjY7:ۖnr L96P$k':,BO7!zPok q(zQzgfu4ǭɏ󚯘?P1FT3.q Rx/jy)x$;l_r' ŐX&aY'i {ͳZ낶KOm:գ]mo(k:~D.k{2:o.0;w(ias'bq.s7c>fЍK9׫JErT~?4Kν3"crk oM5*wZY7/zo}/ +ر^B +F0vܹ|ZY JӣrvgݙLr#mo7/.g|m|o$.nVq:檛N-˜-^;QXIo!`s`L2`2efw5HtJ_Wu ?JTgn74jhov.aC{27eBIvFM/^8F4&jc6[PN%6oc0GMޕ}ūí,P.}_DՎ~zɑ̨CkYq# I?@$s,<9v9vY _#Ƿ5"eyaycχS֓j!_/S?dYgوOJYv(I:n~)<~!଍zAB{gCtcsxd :\oۜzXrl[m{bHe_K{ @Xa0^TJncNNiyd0M,fݵ?n޻ܤ=h@*gMuSv~W-MLƙvZ]rߥ~KӦXhvVmW! +`eStLRK[-,o6|fpq 3д:# py/Uݭ/LV;[5態Zۻ% +R㏲ͥLjc[T" DNjǁ~>K졡ZMGC?v]~i*\Vޥ So%âְמniZsBiKJiQSǮ`FWY{anN,v,rZfEtF}n"M2R=QGhH92 6C9toW* JTײ* {L~wS%If2@y\x{l ,P>fw^3=!rS*SF*e6nP*DZ~]…^C?3[תQh.œ e(bq3Zi Ot ͽ +%5*yRX nysct'4RM†0aZ/N_!NBh ⦙q'f]y]a &94yrq|ix4O1 +$4"CFR 27Jܚ9a6-LjcT{3>2gdw2=P8~rs.l4oL* 4`az}ut\}QqFړ&H$ +n$ͺ78 %i-{gx)pY RFfUOWP z;!q?ȴ3D}M ]8|7Ƴd n +JԞz$PRS_v9^۱sW\~x>g;g5xM)iO[e1` 0q_W j{2G ?} Ǭ嵭*g)PDArf`^b21Yݣ!+ʿū,O~oO=4-[Va2Ӷ~>妥l`u0bF 4}W܎:5ft="ca~I1Eff%t&/2ȕn +Z+*ع~֎.X>--dH%u̪n6MF9DIA :M1$j秳S;uQEDfxq52}I<\2޴IL&5+Gh’#+FK o%^fu 2F#d}/ƦޟUm-́zixsKǂ2`׼ GbY?Y}pYT!F0;nInR^eq{1=,z6U*eP:2efQBTvCQV%ӵ_WT?x$`nmHUyr|!报Lr"`8U?dW3^TVI=^Yݖ)jf0l}ʤm-ULTiy*zDzaTK+".wKbKX}vJ|;(Vyu&<~v6KI')bB@ֲ̯,L(MqȼM&&$DiɆ WrDt[+<8CrS.^7|hS*EPhθQty}o Lgz:#Vrn(ݜz{|346VC4;-f\3,1[N ?78Mqh[}meρ~l`L]*6{YvTԋ@VE*coU3DSYeg*a3 %9+BnPv/v= ˤj2s7.uW;ӜFG0I8R+WqnpUp~ŬcY(pC2b$xZ-&pY{%\Ҥ{cvYOe~Sjr}=ɲmh9><.mmñXDIx/i@BH RhQnK~rF6Wq&TA(g (~JFY}PK&b.Mo=Gq6Vnjp-4Qw(o@f q]fMP-8kc;+3 +(($osNu}Y[IHBV a<a~*ݴu #_W,6U"Zd8P% 7]:OczwX<=||G-b^Ll7A-͎_Mkh-k4f*$ܰ>IO_Nv$v&#[6$/)lOi~'aB^Yox.QV +B 8ޫ{sgbB=۶ׯXcrNbJrcj4o97#Z&͙xU.+4t^~[?zQօ~6bG޹ڹWh1NB%oԺz̃2߱Jw6w3܉tԽE7DCELV{1c-8 YRSϨU7fZ돴,E[i֙i L w<5 uҥi1c*6̓ݺJς^~Uyc5YKKȗ(ܹw_|gud'Y .I)lWTUnI @QoBt.a?zyV =Lz2<4zke[HF&J xΛ>v=}k +u(HݞZӻO4zh*XR.i^c> y*rސ<ѓj ۝Qq[*Dv+Ib`"mrMCŴ}K*|.ꇜ!6Cn/ +&OeRe @axnq.uz*цtvehS?ȖDSӼK_1zu{m%bTf1dG?-n b+!cޢ%?;ds^Ve3S G*tj#&6}OFH6GݨE+\\ =^9[LY{̶tŵv qA]kC6ǭݫ́۵eA*=*z+`^i +ii\h(e=)XKMMe~/fs(q\/}&lNt^ND!:א\ߓ "8A rS7׌mΕq"D?* +nuٛwC~Gty,P[ (" +H99"b:sMEu.i ĴG %tn7^p1kջod~W>۪-vSGb*ZC&g7 *aɂ_X_+kWW?-wMEl|<םk{{*W PGoSO7\ݠ?"MNo{hU:HwDГ2:/]z5?麟68$HR[!mpɣ^?tKa"@bn؎]6ws ;LİGOFIwKPdKX,޸1TTrJ[,S-Uu.*CJ(4b$¾aeHԂZV|h=^^74R䬥B54{bKc|R`*yW!g2@wx䕞R~oD_Hb'j&ݺ`vKm#17?MiOsLqfX ]w u{g8V~摡:ZHE^ ++W򧊀'1$mVJ- jAu +]y\y4n1{+'v026ͤNm˻ +P:}`luiX. qVdVU)2W1˥!y#UGAE H+5TzZPUO#݈K`au%aG֡`}hPh*'3#/:6~pXIqMVA_'3sg磃a6g^GAQ':by5;u!r?5ܥpSα7Qj/eM\tMs(108(wDC$qӣﳶpk:ym=+𗆎rCN扔%DPw$|{Acr=ϊ{Zb 3ϝ0LcHKg%r,U;Y-0 0D(,>'a4vQÛF(p9%6ԩdh>ߤ;c#K8-쳤Z0 +W-Nkh~3ɨme] iAIĞ4C>NUkt$VXl е禽ڳҔETEkͭp䡚 +7 w g4)d0d6 ߇G ~|^3ʝktzQB'ݪNRyR{̟fa ra1/?A|l¯פG?yଷi@_)(52N QNQE6S ^6F(j'+YymQ兄Rm֌2hF=iF_GTEx. 9in߁nh,4 O qwXE +2B>h߳<|OK.|G\y[]Ʊd vo#ٺ*ι'(M?뵋Tn oy*B%B1r(ǔOj _{֤g3tbV^6"vǍk3M D]D<x/`fb=kiy XG +7۔?7o׶%[_:)Cީ-C)jO͢O.kU{zt>ABp'Fy: qNBT]( A1i5hNiSh&]cZ͵'ɲ5Mu +=B1HrǡoX` l(#~ ^0eFD `*0V0C`%QGurUǮ)м!HzЩw4F*#_ 8N2701`ZadXM[`7:_w*˷̓r;|( &Ԭ";>ͤ<Ƞ^#OBFLԈZ]NY^xuy's[/l7#͏tJ0 qDC4I/#z=}Z6xaeVDMaJ_Pvs#Wi%ĥ=jPy1ixG=s'Tzw!;?\=nb _*Ri+:yXiu 7GkeVY7Kp52 K +żӮniN%^5gyhY>ECt{0kW^3Ɨu/Eƛ6 |m97S+–3nMќ -D珹P?ś5c e6^8g m1+RҪeZB͛zRX=_s Zu9cۍ:\d ܼlu}r4kVۍSIO[\P<-ToyafYW nm#idZEq}gŝrFB3嵬,k1G0JQ2*  U'ϜMOuͼ,͉Z{ƔʝYu ݀F8X Gj鲝( c҇fBi8AL}`՗X|dΐlvɄR4ɰ5(@jyr ?\7ΜxS7ĉёHVh׊M]ӂ{ o2P1kT=hV.O7#>ل +S󃭲wKn59BS+ W@/ljs$d٪ϭ nٛ6"Q}3sU0\_@MoըLbLLY2T(J[$3d36B{-ƂjKknX?܆^Od̫y6<:a".N4kWPPI *Ǔ|JVw}$'9!O'#n{mBzTf}ږdLу#ǁVVJjUEcu;5F/*ߪn=Q)@=U,ʧռ.> ]4^AZDJp"v xj_r|r96 V)1rT4t?약nMy=22m؜tκռQw+TU҇6Szj-^*ų4:Vb +"Dkǵbb4w9Φ6m6_0Ik1ch龁>2ZS>n1A骛唦f&5v*hg|^4dzAQ"@Gj Nhkdڈ9$ȨhL 8(k7NrFNΞxHhҥY9͆)kRjv{* *}hC:{6Rxo?_5Y)J, >'LwAQp"6BՄLA"C44mlX1I^Nɢ@*uyf52/uPQiNN_#^aUd] @O{cJ+Mr@77[LPrdGt\(b4[*Ó??"zv\QH9o1%.Z?^q+=1Ÿ@#">/#p(g>DHnJ0:Ӓ_<lŵZ3LFN@_X,GXc4$iatnr젅I_g5`MwL |:oKk&^vEL|k߬ U& fʱϴ<#?f+RӇ ek+THaCD[h⤟Wr^#GÏ}?r?gVKיn:}$c*q^y. d1ۢnz>+QZ,,xwh(g$IkZi,ש1ѽs#Xlal=A]W!RVIm) >coTd/'s +3REk_)aMQɟjH?aY]o@;2En;z7α9 3丯TkKMc@d=䱱efӚX7aտӂ˭S B&H +gpv(=5U%Jlk6e~ςQn5q=$zv4FsY_F\3?kcb׭ky\MXvC K;}cw=ݜ~܎qGNMyqt~e:;1f&-a_Z2*%yr')w^Nvr5swg'&=v=WQd'i K_bkM.jL:[gJ:b<1%۽}sӿLf̭elN-&< &9^ܟ+*aCGL.BV9GdysY+;*lq@_%f?WƸ oxn5>gtoBv}XDQuz VOV͊1&P(ZO&+Gi.>o\·cGq3wcV4&=gS>|HmZػ&LV +=4VbBn+WL6?m[G咿]]Q2Jھ=4~ߚR|j{m6' .Z=eFVF}͋{zw!540n2v)0@%e3Ic':0:᧸`8d +(wTk$jwas-|Ǻ2pQm pEKp#2OR.&s꾂'e92f2XCLL `G} +\u-:W5|9b8X(?0j2jxEtBF#NRBmtxFTqT~N9zk=SVUnxE _vkbX xI"ji#n^]L V~'ѯL>/9 k?(< P,UAUΠ4@ Ц +v'4Gѩt )A;3_>܋ [0SI_q|+C_d +o`t\D$]dzyq}':yoLDm> )Կ#NkxKi<\s[^l{}Zp$?C; ΞМ57sW/~\$ì|KvI|?AՉa#<ҙD+]BĕO/̳O%/4g*,z1{mx94t33=2)rlz:UֲYo?_u5>d,ż*0S\|G9.=9, lO#WЂ"RhL2X\{ߌtU U 4CrG Qɥ*ڻ'\,~p+.CmҙZ!ݡ-k͈-Mt{ۑmJlhu5ZJڴ4T{j4w +c x|p/va,ze86rmaw w46#W~X$t} u\5sZy`F Tr*.-I;]+YzmURJc"I+oW>BKu(6҇,BshY)$7 KFkjOڑZ= +[mv>+Fy:6eJv% $ j<F_JZ [O^ BZQbOT'ᵼ(>:Af^_S}!{#yW^kyWEZ&EBH-R? HmR'٠L $ݻzZ{$VT ũW02K<\.ޣ|dtBy엎Gr/55\–= +tT#sE&Q fq0֦) J WV5{%@hH!\T@ +Qp2__;\d?@Uj HwPu/P (6^oM?;@3֕\Zhɗo,5r^vr,Z\eU6J3rW3=Kī[aadˌv٩L!ߨt*c`X^Wܢ_`3~)h{=<BkSZ-"ڔ /3X:==ǷRvP8=dj[fI=ujfW[&oZ[ֵmݿA,Ō|US E}Z+Pr%1[([['?zm{r%7JܠD}>6kǒ8A`h{:IhQASS]LVւ:>i"$!V P1<΢jo>}/}jE՞CY49u"s#L%Ƴw'֢@ c +jD5g0m[%(G 2L.2CvMh}RbD-wkYU%߂$9*"""`f 2F\S ԥ ڹky W{ZA{atkJ fvә|@Bc 95ϊ4齵7w6x"ĭ˪.*NJVz=-$"!t{,䊊 +M܌=JnrxIY͉R[Soʧ&:W%xoM'џ`OVQK]t/WTw#Znr}I.LcgW{x c!S؎oqvV A6t.YZk~~,ۢ&JS7]1Y"!;N + ?|~4pX>1waL7:Eg&Fo3k/cU=y䪦JPKoYOp(r0Q +X|+#$.xIk*"\=V`ӽ"o&ɪ!Oe{SRט˥KZ^vԻ!_Y#ks>ůaGy`4 3CLulZh4RW +-| q inWIL&9" vk" IMQJ'lLyNNR+.;%lZYXɆ|dE yΫȪvӑen Z>Zt?YI/u^GV}Æ;3ѼsRWkՒǝ3X+c9;P> ަײ'qo,mL2+=k!j6vRϺ58͋BhW:Dl}VNc:%.o^ݠ}݊<:fӓl$4:ե- <}%`o2hprJwn Wl]dBMxS]#Em>OǙUJ2j_X>J:5VAa/vEPv_0-f>E`S}'ߎRsņ5 +juD`\cApqVj1mڄ_ +>|}`iUoմ [H֋9z-lO[&Zf=Fƽ)Ȯ.:eZI~k]}~fNTV>|ָeu3,ӪWTz?.eUu->u oQE9?/3$\8 +k5L͡ԩU;G|"vZei(-Y"Ѥ>t6k9Vҁlk F`n IȰ?Bx@^{[sDם;!\]}S;tǂc:)4 Tɒ8!>GR*CKyPŴ TkP[dp hlDxMN7+ЙdIFH z:@V<Z6Sk;Pܬ.ߘn@I w')1rg(sК@{+ q|&70_1 -30 k3́]`t !flXmEe,s7%9%!eu_JzV{hџ&{8~@&}>`&(VH&)7+ +p`gأXnFU8\MMLphnznz6V*Y\# v.fi}g2h,i3 x +XSoD&_E\PϖY:zT :@XXC=…AR !եBXXђ76 +~%T&5 Af:_17pz^j/ ӱ h + +Ӓ +Fiph 7b3x YR:BTwnP/гe]i=ts85} 'bN2Ƅr%ˮ'-ڧT g0?q @H9Ha@˘P6ޓ=4 r1@G# r6|9W,BGq\պE8a"doƬ011$ҥɲ x;F{V?_jFSx l9ΨS`ʐ:!Nt{hŲUwsh6f̘xCmZ}Ng?,זJFE1'%$ bP8}Vwuď%ICg=l?wbmvo +a ŸQ0qUC?tDN>^tV;_ҮZ`v[BOOJNW#'&--]{NՖV֒- jZv+9Nh>p}@vyf-/ DBE<"G_[~;r8J +7|ݼOܫ&9>v|v}5V Ys-$Y%Xn'?'>}B:xG]5(KMiA(JXhh-W/ `(UH̊ΠYg\$Ŭ?//MJu {{{|2-oSEC-k_pqVF6Z-d(~saeo,eB> KoMY5b)a'aq3%z6uL*Nj8*ѫPS7Mm%M`#g;Qt|#~"Ģ ~0=ÆÐ^ȃx?<JBzDI˩z_otY;i\RLҒ*'bt+(=/biWF᳉zzy, Bxnx0h %CXzBpY [)!3'N&&NN؊ˮ ֱAPkR*MKF2wیr!wKR y ZY MDA1`99+l&~vT(ON뤺U\;\ApBEo^G'kc d+VT&J +Q4ē`c0bYayuZ\Ե|쑧qRVD^I|\sK,Q]Gj֗Etlk32/Q~'OOP{3|3\*Q^%7,v:w]zb3vM +qWd]tOc*air1X Ej>tz1ds^|.h?0b~'KY1 j7yt8h1l%NAIv$ ++~nྜ~mүޅtq%0;7 vQzdG}=j\ȵ7㒨:ƳZ 91;^|~F|<W::Iv[NJ{o?el_Ol"51JY*~g9%ԄKBV,7S[7d/z%5>eoғMHԽ4f|KԝQ8X$MWz1q!L{~ݳh^TITNQdz'skXhJ타Dm@oB"!z7*ǾNyY!L#W!]D r{@LP} p6V Ś' $S@5 RcڿjzZ=@j \2{ԉ u*0Թ_Ga5׳Cm]/:'V>3zr;bLِlјb6)+Ay8 @~ [oAN ւyBC?-1PPeJEX Z=T?GFk2YkC:TpP|1~!6"i[^ (}zOP(/qJ) J":f5Pc*A}&ƥ kAc9P? r.n<$L`}+ +\熿EVG7 ?7)wH5XԎ6>XДKx~(sZ/%W~G,a4 0 +FQc<_j)6n /__s[`e9޽Y/P4GN_̟NTMw3 8`*%h\jH$Ls`ZK`,.Xf3&Ɏzʈ(O\NI jnS'N‡dQ1StYi܆C%>stream +Hd[=r>E)"RDD*_-?79fJVs͹i59om&6T$n1kruQyAZiŇ@ykn_)zHAzx.ϔkl8MR9f/Mg5,s"o;O/{Ć'FgB+~*g|B+٘?l*RM_?>ȳ +IG7UR߱&7hψNݽ1n+54mg 6=6D7s/ß ~DD"m.rxjSydvջXnxW+yfd#cdoXS}l;s{.Ӡ/hǵ/4.^Juztq~LbW'sW6^ܔ͏ CNXK2SHy8\twtjҵ} ]RXU{a3Jv0Od}JR[bv$nEx"Zs)b0?Y snxU*&B`<ht*uʔ #|'0npJ➄P["j~Up#ЭDm,ghryqq d5-Ts50>P_8<9&RfAګՄ$z@.=#[upu;Me׎Ti ^uh"ʬ):Dq+įOaYg&ӦPAit2$Hq&U6Suy⮺bCA< 4M3>[ґl?j_F//.}V[v[tf +szV| 8:vp_H^ +SQv=v>i9<33g0+-xo452p}qp,=o7YO^]x pząƤ7å-S6Tm3]w[Al%urt;gdvpw9?m,kX# 0sC5j @(wjg +k6q[x" )okYU 2 +%` fyYS]S{Zr͔w9hiaE&PN/ q0f%IWUkxſ-FA+Bƣ1WmGr{\X9<(kҭETD9Bh{~P!-yv-s]u6u(خ+Omfz =%{ b;y6ݡ?U-sVԣ@ӫJlKWLN=IADMq:_օ0mp[!Ά+Y+k'%PB8wo0^0Ra*Gvd(k.6dFH\c1.I-+2>Q.L|TOa\4>⋽EeO',Uy!έ+۪ܽJ2# +\}ٗi'CBHIeMM3CMH݇K[lgD6R5SgwO{AjI3ӗwܓ3'턨Ӻ5y 5.z8}%˱-R+Tz0pD_dFqHyyȷSDL<}B_D׿R;!(EsϲCkc*_ƺ٩ލg}inh%SY)N~<v0ݜ3 mCr0jP1<_*XG̓F=6yO5 ËSl:2Fne͗ם&7=5ҽum^Fl8{3wMhxvLdDdkEO5}4-=`Whf08ѷN ÇzRLaL`jҔիw+y5?#CR)u?niP>Q:(=*hZdXuu4P(kH{IYM % ⻡FB!>L9M*߀cAxaoC݊G.bWnT:XmXlLT!uv:vX3Q\b܆Zs?@#sxZXڍn*(鍍uSjxGg-|[?~W'Twpry[,1a'h_(P[V=cE+dX8Ϡe* j lGP%,w{u} fzoy7=bMNkyf,q-d;.n.FiV*=GF~0RPj%ѿ<9A?"T\} +/ CyzdMce ȉôl<,k__lזFe$)0" +" 0ؼ)f5f-ªkiR5n<|/զ39-dd}(G} RmK,hO tG~ ` @l)q꽀f>44^э’y$cUGtn.֖~Z0bFBXfWMN5#z +sčV̵fi@6ѹ!`@b lP&!@Z>;2*@cR(L鉦TIxDj9KUweyGd<\M;Y'5?n9۝@}sPsS:E@,ЭMgh65ЅQN΀^o@+) +c1{b(T'~juv YRC3f!-ou[.Z@[ +W`;0FTB`ƛ70[<LB{mT( e8ơcх~Ĕkelm1͛ұϣ7uVK~̨H>N:C{%P)(k|7P+9WG*ʋ; `>VUU+ZB+48 +3IEѺb*ђ e5馟ϿU׭Ο"4d +j RQvܬ.wj oA]sP}[mj}uMA͢e>Wr`럢1nSr?Z28]RM*P#^pjD{Ct>P4i|nRLso.8n!8n;eqCnߦ/g#£ISC1O?N[A]IMK_z2 Le]l8֍=Ċ3ڊ:+=ӟ&FȲ=J{%q9 5|PkK?ޗN}:A戯9?s}w͋ծ&skcZp<-&Jl?W҉ґ>uf玿+wca19 䈛.b],=-׮l[V5 &ɚfⶺ݉`и m~qำ`\|j\0b߭首WJ MrvvY9tA7C;:vW"ML`w:v Tb>]Jߐ W/i?ػ3qO݌C0TB筰#Bp+/\v7X]{ a/cAϑ޲ǘ04aDɺu.1f=3κ^]@`1ƾ@HۋӋ-oҬk7^k=z쇝[~JdܚJ]ۈQW^oˇSd Xnɩ \·tu_Qz =a*CONJQaμn5FWve6ٸ3MCEQPָԕZm@Œ?I'3 X`f^yN'#@SآoB 8ć9R.՟[: Dض=iVgU\Ɗ i{ +>*|Onflq_oX3Kqa@iP aTSm:LO TC"x;تfThf : zt?%VFʵ '^cD<*ܨjC1O-fab=s}EUgG%iZqj[ַFfH8Ase"4U6<trț +D}`xUivW: xyHٶ&.b"^?5%bnM}0QK|9&Eſ>[TOBɽfmk &3ҽAb.8Pli1k&(3Waˡ.;kp6VPs6DŽdz]=\~M$sv_K?GQ,*ظ>2u)N(+1Bd +Lrc+n=ځ5#/:rҩ­,@ +B%~r#}NBg1Ox=8#ۍswi S|NrB A#_&.\>nݒZ5R䫫;tj]f!.ҽZsbFס^]ug|*M{@{y'i\dЍY7/piIr=+Lj j`~xpR\z Ƴ1f#W8rqw\o[ZmqbE=hVSJ/غ@ϚB!ebڏMU~u7[>A!$# \A$W :dF Բ/ doE$3$Xp,$ B=A +\ $T_USy1?o4Y7=:x!2Jw%w{mZM:eb+]xA#RA=[" T!=??!krֽB~QDBi.2xG#qXܜ!{r9@HnaZNnla{eIz[1\n^>lB6d} /de%BO(E{ e|A1( 'YRCp@3{%؊PV*[(QP^"=u,:qp٦?gm(_PswuɌz ŏ?oSؔ-\kt5A>NoG56{~GZ[-B ]s=Dq?/hT򠽬EOBy1_U[Ѿ~` yMd(_[R@k oRH)GgHm,Raz$Am\ 040k0( GT`l*Y*q_Aw)׈!u$kK-wARa0t݀ ,m&u,)|:55R&p hXNi>DqxhOU])>yLt}TMK̟90a~%QKde6.uѤm4=Cؽ"s4zLW. d2xbGȗdx9?tْ@EQpedMEQEpCA?JUnwnhg4'E@济 Tf_wg:{.WrRAM.4G6LMlƺ uX+(.wʚrAΘy0= xwd74p.4HigPT^'Q^? ,W)7B:-'o}r+o@O; vꄽPB+rG 8=pzLP3yRޅzmla;t6vߘ?riqx{{EWV->]}aw} 6s1anvrJJe'(Y^Q1 NuYur1vWY4].-=_bt_@gaڀI+4jL}T 0L|Cj^<&Kz Gj]8-9A?|Zyɖ7;`jí*Bj T/[jZzAHL|h ٔZMfd4/U54d,EÓR,]w{.iA"tXzDh{^HuFbW̫J&|,Py 6svzNУ2g2w ׌m8@Q)hsW$( YB YI>_< [Vs8ܘMmwo[(牥6 _@Yef;ӉHsi2ckN *N*MgSR>j} +h8,=ٷDqnun*\+4#>uw*# ^^'X J _VdN >vJ %J:'DSm㹚cP6Ю`@{7AR=ffD_׶@E$Q1g1yhVoUUdYMK3#i1#xI4B$`=KIJR+dvGC|cx6HyKᲶu[Jz\h݉kCNSaIÃ"g[56^l]سTG9;,oڹroV7vQrf"œ;Bqy8pwh0#[bxNxTo1i)H՘U"pD:ӹq# 0zv[i|`J{qzsʭC]*.hYpK{w >{@)xڒ9K= )MoQE#i450Pa,YlSmX~Q9ؤDS`Z}6aر/,c5F[p=Riӎ&,G+Jzw%78}Cw$[~KF/ `HSh72MX>F9 G<1>&'S{~ǧ|K8x(YYu_Uw"8[-t5|2E PS  -fˀ @b HAj҈ts@C/ 2." ;p-(|@ +  k;H]kڃOyZUzr!:~gDG9HP+]{+fTFL]jk,sS')r +IH)hu/wg4Ͼ +R".k@Xq*sMG0 < +;߀@,g49ܾL@]TPa`ٌb>ʩĕC7pe c_21w?w  H4\qsJU,@}9 hS +(\*U@/YgH nFfE%ja,WhbTo +2Ǎdw=$.@$(ɘ5Z@3N' +Ov5 0Ӏ =zmްUF mtOU@D L8kG\QУ*$& +Rru MYK-= +zt@ I \#|;x 9c\`F @8`ڢ!,Uf:?BLhWl\_CPLEyMYm"`S$@1` R.ۙ +w'1Wv{8xI`Wzة0mi2bRlᗺ|> ~T7nQ%JWvđDBT%uǵ%hBiu~Nr'@rCmB-(1z)(=g +3[qdV .RLdn.!O(6$aX/Gb87U(qYJJJ圍$7e~N^@hl./*^*LvPPT{%S~Sc~90ӣHH,Z{-)lq=1]=<\jg4p[SKǥsaAK3=pTąMr.7(>y8œH!؟YfgƻOCmP蝲-h뫕7;;$q8}9ܞ~C:/ut +E&=$6N ZTC Ÿ\zkIX 9əܼb3)rK3qsvSj~Uwԫ- tAs#ܯuWrli\|=ЖZ9:Z@3yhLwsq۵W>=s,2gȉO3;aa6ɼO#(/6+=w2wA[H/7n&y^gpIyax{ɹt2vdm:,LxE3] ~YV'K}~ߟ+3uv͏jcjz*,zQ([{5?ىq.RC1 WE:Y<3!S Zz ch: S/`zVENѼYqύarWb~D1_j@k&9Ұ[hVP>^o[ ~f~=md/ٱ1uXS̪\VweSk6M:M&ך5XIbٙMO]R!WQLA+T? 7!v-JKX.T0rSq=0Ofb;4~!)Xo1'FŽA'ܩDWJAoBqѠFdƏ]td7iW},Rs,R _RŔc" _ +#[5&+J. bP‘fp>d-C w/cȔ>Jw䎪7ҝ9G0Ag:Ez Vvs䭉7D DM.12~,Vѩ[UJv~vɾQ܉* +/MƠ*7![G&bZu{z i(k4&$wd8""ߣ' 75g״6hxmVuH@<5jm o0a ZF!œ!غյL@| /^$6 ;VM࠙ET}_ 41C-VȦz9"L&M}?;sg/Y4\БS# @t^˾ܠzT TQA5*a9Ȁ~2ѮkEeNX&m2lP&sb [8#=j a2I5^!BW"h q.ah<:ڋ*SL?n[iJ&U-`ʱ}H2@]UyKF| c.KÊ!ݚ83nS3ao~Mc?^dP WA_͸䃨'=\cw*W^(U !`W5+R_Fy"bwh(\P< Aia?l5rkg|גEeKB#{xq!=p#n! "2^A~cIfY踖gwڍwAZY +o5EPtNߦ]d>Gܡ(ѯeI's,-.D̫[4c&<׭Ȅm朙Γi؏.sN8(zFZڱ8וASeA}Xhݝ1ptrW٫"ɪ_ۖ6AeBp܂oxã)$Sou|A/+f|[ҞYT5y))c~jĔ<7.6l{3pb!AD hx  cF/.YLBLBЯ,cO~&U +{N˻;-wrzB4QYzt%o,'38Vz<-t}߈+ّ"iwvƎHOu':OA/ B\:)n ҂4o +1(Gn&Ww5b ' +q;Bw+/_{j|dwqk5ҷR٦ŬCF Y3r'4~qDb )I^J)͘"iBFYC=A A`:L ^hA>sSwd.>vؽ1Fפ1긢L{FRM/2X jdHHtADMB; œPj^ʼR*T;ҁZn̠N;܃`B,u.ރ:}7P#갚̍S/IMf;OکQrfh7$PM _Nlz1(J2ҭF$"ߘ3imhFoN54XGՀe,ص Fcs)8tvᮩ1ٛnr& .S$"^ΟgRtQ(}ѫ 74όo[Y&Xι^k,p<5Ձ}NkX]qOZ_h|J =-:b: ?`aARHja%!Ɓ`{s౐}pZq&4h|KߵӀۛDZq~r}ؘ?뱽qVŸW/L.?'X뿨(4c]A$bN(J$ $AAQ(fy:?TwJWI,)OwzӏIbٍy}{k$<s8?+no66ah<痝Ο[c} Ȩ5xTgtﮉlj $<1΁ČbyVv: +q&ͳ߭G"hۅBu2Y;fr@B֧6rNڋkjY᷻8x1KPy/AVa{;%H.fb3633{ +-a\RD;ֱeUٕfl[>vѳcY~vv^:Yp([Vain۴3!lȄnˮXfA|PwSկ;Ľc&"ٓjZ?\%q&N2=`PݖdYGrYZ]%;`_y4s`]XD;MLd1 uv{Mʩ8wsR=&vM_ +ܬX9ͷBu&p37xd#lk23cL6=6EҩUaXLL=LOK𵃠PYS#@I,:^KӾHXÌ9&mRtD!d'N+v$\ [[a+d?w)Diī?Z lZ#:&܎*@ES)ЖE+?\?>ĊT-Q%#_9jh}Z"5eY;vE6/~7(,k'(XV[><A|/Mie - kc&)GoF1#O.!ı8Wxk Ee*W{1Uo2;8:=^ +JSesQ_<ʣڷȱWJR;{;MSya`-+gB%A`ryhSI]4z;7F귁m +ƭq}};=Cήr4y6EFsfکmER]`JQ'+촫)"b,lKYR$$L""s;v;O'WUVeLjΜ1 +%jb=gkG5jQ݈*զ}Uhw$ʛsJ޳GkI%i5 žn /LߌnYlf!fed\yLi4B˥x#8E ,7_wM0fb?rJ$=|Je+}KYsH_tAg8G9h¤U* cz/ g9>iLLBo WB.Ymj7 *i4҈=T( Q +bz~ :R߼,B2 MbWo&Wo$nM+̺;wRq6|=Y_Ai#_\3jeYjɈu=UͫwpM/~T䘆DN"%qw Wmӟ#tʩ7z2VesSos SR9ο{J'W2,6Un9sT`..i^$_y!+Ax%S`?kwWmAG ѭO43ގU7 Am h/u @ pGfXG?guNQ%^i-3[TP:J1ߙHr;U\,3^;uxvN)HēJV}.k=ACŒ %56Ah\pJuU^.ü|]Ii0WQXcU['LDcMh<`7nz&g=z֍ ._Q- d(-w@9J3i TYjaR?׫:Wͩ5G֏{4R]XfAM)%ޤ]>ߐ*.ޮ|&y?,2<7wTQzēa)z.\x+f*8@y.\ȩ_C:kL}[["MMUI9=x, Fo'R-֧#7 `;~\ȾGU5 ua-_BTFEpK 5!d<WhdS$۵r&}Ezyrt4")\>Bnjc/Y>`fR#D"٬b*XUk a6AyU/X\@, :ԧQh] 5Lkhw'rNAJ7XJQR+G({sen\A  G̮9#CЏā 1̚@bsj@9<  RH֟+WXRaNUT7 +LF'xIaw2!c*|d[ND+@R( 45T;:~v O5{x@5Po(u% +(ss3SN~l?m%׍/JQ|$/HO$~u>}X 'iMnVμ +t#k@i$݊ HdAz 8u]icG6.H/HSԣ>HH^ Xoք$쓁V=*gH5>mxļ Rsia#ܻ 0Y"]rDkeO=9}yj4gZs*t +MI]D0,O7)xWoUyLϜ (=(ܶ'(|j_֫4Ϙ7HvR?F 8*?#fLғϯbڹ G| Zb\lR84a3\r.?o^bW+ZX&Ήi䮸椎CL\TI/q^A!վ)^ܩ$ NX3 ^9x[r\J '|9B1 +2^@_9Q5@ (6)dՒ]van\(~5!G2Nj/R8_@L<61 SB6B?Pm`3 ~tH@QKP,]5` LJf*_L>䔝3MڷW+C.@r%{u 9o` +ɨlJO@](_ PfI`efa_Ci!JQ tbܶp\mm('[ +Jj'M 췏I՝2 [oroJ`&GB47}?Є8Iow3j%nkeurPfʹcplI yV{?!?L*z'( );"iN'x{'iNL2Wֱs>9굩ÈAUOϼSLVq؂ʬٴ@JD~v7 -JlrPO~5PFϙoƥ-'g13kՍ]3kg͹k֢f1o|#g@_+lˤfA؝??!L}WMa?MO͢gI3|=׿]u!A 䞰3ѭEouzC68+Y0K-3+z/ Y?uaf۪6GTJVV':O?֘й7j`wُ_/]l֛3{A蠖YaKc<6\lUZlA8͐yɟJ nnDD񱋧ęq FQ#(rW~ h馻(֦Q{&HiI;1s}CXjra)Yd2eibukc UC' mtb}ӦZWb hחY۪أav%^\b4\Y)歎+Wv[@ZavBKz>r߻h-a~+o=kAr "|ߑ =$J=bvIպht{VVu.*YQbq׵M\A -ߝ`VR%ͧG/okĞ9 '0i[5z*ݣvv ?Z:x*}fV +tZ5WBm5ETUL)#{ؒpoφ(iӪ7=cobp8^6asȤ~V_1:#/&.ǸtsAd ˰*l0muhW$-bknjuZJm=H4wDԫAFdBhRs[Ԅ\]Z%M֝"u"ylSA^W,+ #LԴu뛲bpk]QxM ŦJo&v{ + K78.mӁ ?K˜<$L$ Axn^oU=׾̮7FtTGv緒mU!S6zuܝ4Iͥp'GSV֛z@󪭭z%7Ulo1~'iKgg~F>k yNVG-^[zFfYdnY9*c{#Zc=T9nr}*M7rL KG޼&+ ;(MVKK(oNsno%ʕl5@y*K2e8/l$)2|q:$mLE=R5կJM==9#yj/8H:$>/]$ (:ï9TR*ouPpZ)Bm8F7e'qǛ[ǧʁ7ko\QHdde);tFϸ^i'lʯ݊wLEKgKeNv\p,8 iPN6٬çT%$]XYAxkjѲKKb6+u""K[ q8۾`?,ۿuM7 lVld4%yzQ\|œL,Z* 4Cnt-kwZ=<aLᏤXCM˷Ȯ5Q|@  'LEB;D)AkimMmˡJjUgF9o9zV\X݌@ ˣ=p;> +ƀ }GK +"(k~i1nMJ '] %-8 O> Lj CT3qX7HW/rlG'Nf/O'ˤ|3X숆\c󭑡|odvG-̉\*y +&@z̰@H~ 2, ְb>r`WzAr\//o77gTv()K[LH4tc6h,>B6^յT'X /@] +O@Ƹ5(a W'Ȼ䪊9l-ZO? 24YR49P*wX՜ZDo;4|7XnQ :>3tqvqvM+=vXS h_q\mqO4uo%"8g]cfc6jJB8]^i}HQ*cE?US5s.,q_ͺ2wIokhA,7fE}قag0 FiKb`RjSƀ_}ȗĢs0 ٣hj%[ /{='$e +FsJGk s[vXLC`w \hհG5t`oeޭ8XWn=R֐:t$_[1]AjnS +(.-&̍4V.TR</oKV>=G=p \j 1V*1)ju+!#/;.{5S;Wɕ8;O +njo>UHKLeB0{~PTź)w!b{c0_Hs@Y +!Ƶ );@3 -gd{]o|;_KٍzV-:k-{ǔZl*@+Ӡޓ/_7:usɩC!Tn21CODA qn 6C4 f*i2M"݈Cxt+ŞJhv0іmGrL+VGOio7A PLDeKm՟!$+ 2˽Yo!gqlYːCppjBr6I#Tq5H#"ANp~& Iս{fUZrǥr8i13vr;,{TVm.^"84i;H!1m;Fm~ro3ot&XV.Ϯ!U;a0ҍ= +;qjl\^JJku7A.p>3g멸'f2?Ar'Cۯ`bjv7œ:^DZϿ0}we}*T_K@^q# ?QV8v.vs EDt>Jxoo=kfi-\"BxA$ϳ.2us˃?3_hS*nCx>{/pNd~hpɵ]pH孺[Z ٵk?"⪹H ~zxo<}0,Jiv83yz2)zvO'.=[wmxcJ_;sZ_cFbmM@o*noXT=~}xz͐= M#(ĤdvN65z24;hk8s,%ZEׂ".F)m'Gna0Ҏt6kh\65iBߦ[;+QG!acAقvTfju!ne\$ڈm 9H]v@ڂu΃j_ J_>tF-#gOMD>E6*ce)%fѠ2{M1G && .{YP| Q{i5b'Fͷ]`_@p)3yK>y!)YWHwӵujt[͉./NXP  dOxNצV'M: )v8e7BECۢ'+g_lHJZ۟}7^?]W.l Om +jm6 SwW'_YNRg_ԕ~SM,!tI=LJR`ixntxJU^*b=iK k VnxM\gwYr֔*j௡iw>h״Dڵܨ+ߚ+3d`Ɂ@ks| + PZۢya&S{Z*kv!1c+)v͖ʎc'Hi"!يw(kJSao)}H3-TRs&.7y kO&Y&c*c&v5!' 'y:0Nd`A9)2!$B +S8 1$D蝜i-Az7I F Na0SRKڄ}6*< )*؊ȓ炐-OaWzY4ycLA SA:mX CiIJ:1Zݹ Gz=,)Z!!(0'eٳd&!=KENiyiaՉNX,5 :mc[I39X= <~'^!(264W֝kA 2j)*#ƚ~:zs%Ʊo%DB^:kUA[,5oε*&ybdJcoQ:[7 @wZ>cG|8ߗ)ӻ/]`y8g@g= +']-Ck |f W=C벽oZ" "3 s7^ef]}Qu Sxc\?sKY rE00 msךQr=T*-f>Bΐ^;=)hN +`?>ThלÆ[?Rw[b0g '2GP'^Tzc963ҺGEGuݎ27zJ>kY){]MBgj_J_hIz:0' +~zsϖ] }J+]eT| żLFJW#TGշ[> ;R}eeJŨ;+_\4;>_\odg66kgub|^@2?:ɁXg r,_;nu(dw )>>B/Wd /R=c!D>+4;nNRt!_]H%Ҭ^B1Rhg&f1žwqv^k4f2D18@ p37~)o3ˎ7n! r vlwaAD 9jfIhd!2Z[ +0O3j8pԬ_\E;UܭLFKnf/6f` 9^C.ޙ*|JhP~2$>=P*òl/(7%yP̭E +b 8OȥKLZj _h<7VF(D.3Zv>n =ӊ3d +޿CP/@eus~=w 4;a8\ӍZGPj$)C5TsvNcnkll(Uh񊲫6Nܨ lB[(j?dj.LkKF'4f@[& ZMN)c n=@LA nA;h݇훫AsK^P # *cm{PoUR!"sOn,N}TcQUŸК0BA&*ЮMtkІA g跒sCVzr}m`B9 ۤq>t)0cB>nWޱhOzBuaE? +c3g?l:ĺsN2+,` ƭw;;0-=]ڭҞitdS/%SAt%oߣė5 +$HJM꒹5OlK2= Yڛ‡vr`[dPAϤ?TtS@Cd_,EOa U _%meb6 mw̏c,Sm4d/pXvLc~b8>.c&{+BAj9f<9,G۞ Ř;8Mc! +3+2<3̭zgU^y,7稪'ؓƇ u]A9&>1(nȓ"a+KiX;Zwzp0xl&13cO%Z_ZEuՒ{G^xi[M) m x&#] fLGq(~w) cJR@o'f;Eg*eZ4%:q̿R'}5js\Qr"ۑw_ "xO[\v;{ᾎEeq US(\*bP|{Y#r h'8 +f@ +TvB`L-{Io>EVg a0 c|e(tD+-;\(4y^0) 8; pPp:ɾw5s N#fi;ix>lQYzXژ-Wsskqr{!Zi>PfUNj0rpeѬОS73_:\|]|6BK p@ޓ33ҝɓ7S7I9Fz, (='Z>_3n]B=aB9Ww5Y ='fˆI(tMqoiҥUDq͇e`S%ʊ~R1xd*ͰR|IĚ[۝VU[1S<.ovsv e[ +׭_6ؿp + pmQVE uDk\plvszn `gz~G<Gz9-L?ވgm)̯쌏SJ!|ecer{!n= R id@N@A2Tb1#s O=oRs< sohIN_a!sncc|^vG;kc <ΐcU0[d$YfݘCq>,_͚Z3pwww3 2\ɢr +\dxH"ě3xk2-/:i1g 5!qwy +F"]2V@O72}y=ܣN&.rD($'wChK.:PrCJ|]L'b""I5/@cc +ܑzcw *fדrг^ [tPZ@  S{ٙtaD{9J􌍼[o3սJ08P~Z2@wGX X>7#X5QG,e180} c-Pd%όrs6.ρfWA~7$'5`e +jN^Wk==V %D>/O;Xۅ3s!ϱ%=#*4[,l*F7,G`Odw5D@Cb@{ +ah,+jXz-H'ɕVBYz&`_oi/Ksb "LaO0j# rX @ڔqM4)|^$cUAǠ+ LI|&yMgQ/Sm.M .Ժ*$# $R jѫBCv=$@kCFb߳@4Hꐮ=[BZdF.r[7 ֥;rAV49 : @gE䵶T + M,T-:қ-_缠Խ U.슓**~m" +M/3*婃wND0lw*[=MǚхVvzذj^"xu'j+$xE+HXGCݟb~OC1roše8Moġm.b{;m|'i:s}B LHED!e͚;/x56QkgFM4ˆs(!RXYK@gC1HAMIxBB۔E9\eWkMez/ks,RAr^{uє{َj)?>[3'Ζh,_sGnpRKґ~m}'[Տ3Y^ʞ ;yAZO\K Vj+M_(H+ {]J4ZOh]J4ӮDR1mńBX4!D[R'&.86ڿ~0>MWLaӠ&FTd' V#'w<| ]gQA1rTq<%n [}Ʋt:7-_x yO}{4C{vlb4NQDHH󙗏6|o#R2/rmYye.־ggɜ_f>}CuvG- wq(sJm_ WtrtBޠ3#z|Wcҿ4!ymPӿ~di]5L}sA ߷-4!E5N}#K7/-4!Մ~-` 7Vs&UMx? iBN#/<ôςoMz+9*}^ ( i,E2Gu-T/ypmS- QD/$N^6 pXtc[.>?|vGȮgʮm{9y;z/663gp*)U[^،Pi%FO*.ZskOT7^6(Q>4d|oP1Ht$RcW&~K50gUQgg1v1 +vK{mGe_$Z{枵eg;PF98bZ@/Wp/ͤ\'N6gfXCt0>8{ +lRԗoJb\ێnFu㒝庙%@/d6mBeT8԰`Ʃ9)G1Z2}o䲰jq]~-wWMhG9ů:34?9(5i5iĐ`j hEnl27gFJ:?O +RK4/]?~&ђ1F?_VZGe)`zp"z*󴞥 @{Kvfm&,.dHmHk(D{PUٮNҒ8]K{YK[b̵բ-Uv-: +fϟe'HC/ -N^"iZ"CwCZm&2~)/<2|QehP&sv uk^7zUj"ZGvevoUSI,W}9 BE(jSk3U ;;'1ʦw"O$P>;KmLΊԞS`LȚp@ /LYX/웮M6^Œ+gcݱfAAtG2U}׈ȴ֥Wp9/l纲^' 3do ):#/~ɚpg҉$xqb3H-޶^\6f1,J_ǚ "&= ~U܏ѓqjcwψ,7: 9a4 brZ+t r' %tUl`F>eiiO +/;8fxŖ#|[+γ8Tܰ +_JAgNWG :`=BA5w[ +iBNjBNa-pNHrUK!M_u^[ Z +iBNV iBNg RHrUrr r^gTy]u1ɢvVpHkcRP+OKFfY=r읞"cꔛDŠSNQa3 URSωS=\,62O:o&OznʭE@nPgi|%! +Y{wOjD 6(k_ʐ͉w;'=ϞDɈi/\Կ;@#@8kd'OkQ93E[jb+p1ZN(ٿt nG8CVz-^]3:i^{t쐂1u.CW>\Z? M]&0<@kֈ*[8?Xߟې+XWVs4!Vf K=: +.t>7ՈUڧ2]=iA.߯aA:TNfIo\_ͱ[fYY* yIX:Vν 7~ Vxi3[)hfHp$'hPk^ָT + Qՠ@ii@H`A-D[M9Pm]BUsr+sǎ}ibFC;/iZ_2Թɵ*o¤?4<>$3`2vjO: Z-/ Fơ(ru>&'.sv=//"jy0ϘkPx/P4>Ŏ^ct\AZ6 +<(N%fAE:LIg#l>"Z9H 5/ CaGbs(q6gܴc sp|7̶$Z%қ5"&b|-6JCjC~9~;.ah,fZ??2|Y.~UZTgi3Ή^ &\p>+K{|1ґ.ΗE CƩ=foF@hV#@BLzA{Mʮ5Iʷ f8,7% F4SYYˆI +S٩/oXWq:sǰ,z'1Ⱥ#G-Qwx|Xu6'?<{/Zv|I 拻|>RlOYפ| I%%uW!$mKߗ2nQ@Qo/<[+)S9Noi1BM-~~䞣ӳtN + E5&.m;I!l4:bEjtٲ~OJ} ݋vg#kRE.lZ+]wn9iiKwFe2?}ގ4@4+qX c rW mn]ۂH<* S̝8Zw;^V8s53g %1 F^LR)73oJvuN/ɽڔ-2+~Tiʓ2o@CN +,C"F &g֏FIk#얳Ǭ?v׷']`rhLmEi&_CG,-#^dunR(!Pm7̸me-o{v2װ;3j=1xGAT,3oOe?j= 3 ;&U /:dB S8u.֜IW_Lӹ:RV:fLID.%&WZVq^1Ux-O;B/{!N9\[aX2ouZ$ \EG允v;06Y[Bi[*ś#{ fMy>p .w u}]ռ*?z ynR1lo{j@Y1bV@粧z; +(Ren*W#k(Y&l8\WN[Zs^RhϨRIp]m8ACZyEEӼHgd X{%s*Jι^D !ev&c!)f5ʱ{1)9-tdP֘MJ*Y#r3.ݍx|O^x*:VYHu_c =>|\-% ]k}:1 E`oʦ"*ouتn{6e# }F:YuGT.ͽH͟'$:סn=$2v΋G-䥺zۃ>2ج-O] +ےo2zniԢIW7ǰ[Qr4:΋"uH?)\wuft73X\8R>)?Bu|`װ6,2)&@JwK+8@1_AgjW~yV:IݵI52@8buרs\n<^wVC|y5cs9iz襣)̯_O2_ }'|c~OH?c| )?}>JdbbOI ':R > endobj 98 0 obj <> endobj 188 0 obj <> endobj 280 0 obj <> endobj 293 0 obj [/View/Design] endobj 294 0 obj <>>> endobj 201 0 obj [/View/Design] endobj 202 0 obj <>>> endobj 111 0 obj [/View/Design] endobj 112 0 obj <>>> endobj 20 0 obj [/View/Design] endobj 21 0 obj <>>> endobj 371 0 obj [370 0 R] endobj 453 0 obj <> endobj xref +0 464 +0000000004 65535 f +0000000016 00000 n +0000000208 00000 n +0000030921 00000 n +0000000005 00000 f +0000000007 00000 f +0002675090 00000 n +0000000009 00000 f +0000030980 00000 n +0000000010 00000 f +0000000011 00000 f +0000000012 00000 f +0000000013 00000 f +0000000014 00000 f +0000000015 00000 f +0000000016 00000 f +0000000017 00000 f +0000000018 00000 f +0000000019 00000 f +0000000022 00000 f +0002675751 00000 n +0002675782 00000 n +0000000023 00000 f +0000000024 00000 f +0000000025 00000 f +0000000026 00000 f +0000000027 00000 f +0000000028 00000 f +0000000029 00000 f +0000000030 00000 f +0000000031 00000 f +0000000032 00000 f +0000000033 00000 f +0000000034 00000 f +0000000035 00000 f +0000000036 00000 f +0000000037 00000 f +0000000038 00000 f +0000000039 00000 f +0000000040 00000 f +0000000041 00000 f +0000000042 00000 f +0000000043 00000 f +0000000044 00000 f +0000000045 00000 f +0000000046 00000 f +0000000047 00000 f +0000000048 00000 f +0000000049 00000 f +0000000050 00000 f +0000000051 00000 f +0000000052 00000 f +0000000053 00000 f +0000000054 00000 f +0000000055 00000 f +0000000056 00000 f +0000000057 00000 f +0000000058 00000 f +0000000059 00000 f +0000000060 00000 f +0000000061 00000 f +0000000062 00000 f +0000000063 00000 f +0000000064 00000 f +0000000065 00000 f +0000000066 00000 f +0000000067 00000 f +0000000068 00000 f +0000000069 00000 f +0000000070 00000 f +0000000071 00000 f +0000000072 00000 f +0000000073 00000 f +0000000074 00000 f +0000000075 00000 f +0000000076 00000 f +0000000077 00000 f +0000000078 00000 f +0000000079 00000 f +0000000080 00000 f +0000000081 00000 f +0000000082 00000 f +0000000083 00000 f +0000000084 00000 f +0000000085 00000 f +0000000086 00000 f +0000000087 00000 f +0000000088 00000 f +0000000089 00000 f +0000000090 00000 f +0000000091 00000 f +0000000092 00000 f +0000000093 00000 f +0000000094 00000 f +0000000095 00000 f +0000000096 00000 f +0000000097 00000 f +0000000099 00000 f +0002675164 00000 n +0000000100 00000 f +0000000101 00000 f +0000000102 00000 f +0000000103 00000 f +0000000104 00000 f +0000000105 00000 f +0000000106 00000 f +0000000107 00000 f +0000000108 00000 f +0000000109 00000 f +0000000110 00000 f +0000000113 00000 f +0002675633 00000 n +0002675665 00000 n +0000000114 00000 f +0000000115 00000 f +0000000116 00000 f +0000000117 00000 f +0000000118 00000 f +0000000119 00000 f +0000000120 00000 f +0000000121 00000 f +0000000122 00000 f +0000000123 00000 f +0000000124 00000 f +0000000125 00000 f +0000000126 00000 f +0000000127 00000 f +0000000128 00000 f +0000000129 00000 f +0000000130 00000 f +0000000131 00000 f +0000000132 00000 f +0000000133 00000 f +0000000134 00000 f +0000000135 00000 f +0000000136 00000 f +0000000137 00000 f +0000000138 00000 f +0000000139 00000 f +0000000140 00000 f +0000000141 00000 f +0000000142 00000 f +0000000143 00000 f +0000000144 00000 f +0000000145 00000 f +0000000146 00000 f +0000000147 00000 f +0000000148 00000 f +0000000149 00000 f +0000000150 00000 f +0000000151 00000 f +0000000152 00000 f +0000000153 00000 f +0000000154 00000 f +0000000155 00000 f +0000000156 00000 f +0000000157 00000 f +0000000158 00000 f +0000000159 00000 f +0000000160 00000 f +0000000161 00000 f +0000000162 00000 f +0000000163 00000 f +0000000164 00000 f +0000000165 00000 f +0000000166 00000 f +0000000167 00000 f +0000000168 00000 f +0000000169 00000 f +0000000170 00000 f +0000000171 00000 f +0000000172 00000 f +0000000173 00000 f +0000000174 00000 f +0000000175 00000 f +0000000176 00000 f +0000000177 00000 f +0000000178 00000 f +0000000179 00000 f +0000000180 00000 f +0000000181 00000 f +0000000182 00000 f +0000000183 00000 f +0000000184 00000 f +0000000185 00000 f +0000000186 00000 f +0000000187 00000 f +0000000189 00000 f +0002675241 00000 n +0000000190 00000 f +0000000191 00000 f +0000000192 00000 f +0000000193 00000 f +0000000194 00000 f +0000000195 00000 f +0000000196 00000 f +0000000197 00000 f +0000000198 00000 f +0000000199 00000 f +0000000200 00000 f +0000000203 00000 f +0002675515 00000 n +0002675547 00000 n +0000000204 00000 f +0000000205 00000 f +0000000206 00000 f +0000000207 00000 f +0000000208 00000 f +0000000209 00000 f +0000000210 00000 f +0000000211 00000 f +0000000212 00000 f +0000000213 00000 f +0000000214 00000 f +0000000215 00000 f +0000000216 00000 f +0000000217 00000 f +0000000218 00000 f +0000000219 00000 f +0000000220 00000 f +0000000221 00000 f +0000000222 00000 f +0000000223 00000 f +0000000224 00000 f +0000000225 00000 f +0000000226 00000 f +0000000227 00000 f +0000000228 00000 f +0000000229 00000 f +0000000230 00000 f +0000000231 00000 f +0000000232 00000 f +0000000233 00000 f +0000000234 00000 f +0000000235 00000 f +0000000236 00000 f +0000000237 00000 f +0000000238 00000 f +0000000239 00000 f +0000000240 00000 f +0000000241 00000 f +0000000242 00000 f +0000000243 00000 f +0000000244 00000 f +0000000245 00000 f +0000000246 00000 f +0000000247 00000 f +0000000248 00000 f +0000000249 00000 f +0000000250 00000 f +0000000251 00000 f +0000000252 00000 f +0000000253 00000 f +0000000254 00000 f +0000000255 00000 f +0000000256 00000 f +0000000257 00000 f +0000000258 00000 f +0000000259 00000 f +0000000260 00000 f +0000000261 00000 f +0000000262 00000 f +0000000263 00000 f +0000000264 00000 f +0000000265 00000 f +0000000266 00000 f +0000000267 00000 f +0000000268 00000 f +0000000269 00000 f +0000000270 00000 f +0000000271 00000 f +0000000272 00000 f +0000000273 00000 f +0000000274 00000 f +0000000275 00000 f +0000000276 00000 f +0000000277 00000 f +0000000278 00000 f +0000000279 00000 f +0000000281 00000 f +0002675319 00000 n +0000000282 00000 f +0000000284 00000 f +0000965705 00000 n +0000000285 00000 f +0000000286 00000 f +0000000287 00000 f +0000000288 00000 f +0000000289 00000 f +0000000290 00000 f +0000000291 00000 f +0000000292 00000 f +0000000295 00000 f +0002675397 00000 n +0002675429 00000 n +0000000296 00000 f +0000000297 00000 f +0000000298 00000 f +0000000299 00000 f +0000000300 00000 f +0000000301 00000 f +0000000302 00000 f +0000000303 00000 f +0000000304 00000 f +0000000305 00000 f +0000000306 00000 f +0000000307 00000 f +0000000308 00000 f +0000000309 00000 f +0000000310 00000 f +0000000311 00000 f +0000000312 00000 f +0000000313 00000 f +0000000314 00000 f +0000000315 00000 f +0000000316 00000 f +0000000317 00000 f +0000000318 00000 f +0000000319 00000 f +0000000320 00000 f +0000000321 00000 f +0000000322 00000 f +0000000323 00000 f +0000000324 00000 f +0000000325 00000 f +0000000326 00000 f +0000000327 00000 f +0000000328 00000 f +0000000329 00000 f +0000000330 00000 f +0000000331 00000 f +0000000332 00000 f +0000000333 00000 f +0000000334 00000 f +0000000335 00000 f +0000000336 00000 f +0000000337 00000 f +0000000338 00000 f +0000000339 00000 f +0000000340 00000 f +0000000341 00000 f +0000000342 00000 f +0000000343 00000 f +0000000344 00000 f +0000000345 00000 f +0000000346 00000 f +0000000347 00000 f +0000000348 00000 f +0000000349 00000 f +0000000350 00000 f +0000000351 00000 f +0000000352 00000 f +0000000353 00000 f +0000000354 00000 f +0000000355 00000 f +0000000356 00000 f +0000000357 00000 f +0000000358 00000 f +0000000359 00000 f +0000000360 00000 f +0000000361 00000 f +0000000362 00000 f +0000000363 00000 f +0000000364 00000 f +0000000365 00000 f +0000000366 00000 f +0000000367 00000 f +0000000368 00000 f +0000000000 00000 f +0000967815 00000 n +0000967619 00000 n +0002675867 00000 n +0000037946 00000 n +0000989115 00000 n +0000989001 00000 n +0000965772 00000 n +0000967054 00000 n +0000967104 00000 n +0000967697 00000 n +0000967729 00000 n +0000968217 00000 n +0000968466 00000 n +0000989191 00000 n +0000991156 00000 n +0000992244 00000 n +0001006978 00000 n +0001048973 00000 n +0001098995 00000 n +0001131642 00000 n +0001143405 00000 n +0001163081 00000 n +0001184449 00000 n +0001206145 00000 n +0001227517 00000 n +0001249190 00000 n +0001271039 00000 n +0001310512 00000 n +0001332178 00000 n +0001353158 00000 n +0001375283 00000 n +0001396274 00000 n +0001417763 00000 n +0001439494 00000 n +0001461422 00000 n +0001475598 00000 n +0001494004 00000 n +0001513514 00000 n +0001565861 00000 n +0001583268 00000 n +0001604549 00000 n +0001626156 00000 n +0001647632 00000 n +0001668867 00000 n +0001690304 00000 n +0001711930 00000 n +0001733295 00000 n +0001754599 00000 n +0001775727 00000 n +0001810255 00000 n +0001832239 00000 n +0001853917 00000 n +0001876167 00000 n +0001897731 00000 n +0001920024 00000 n +0001941789 00000 n +0001963140 00000 n +0001985093 00000 n +0002006913 00000 n +0002028261 00000 n +0002077437 00000 n +0002099708 00000 n +0002122172 00000 n +0002143709 00000 n +0002164953 00000 n +0002186151 00000 n +0002207765 00000 n +0002229110 00000 n +0002250442 00000 n +0002272385 00000 n +0002294601 00000 n +0002340103 00000 n +0002362602 00000 n +0002384103 00000 n +0002405790 00000 n +0002426764 00000 n +0002448300 00000 n +0002470114 00000 n +0002492153 00000 n +0002513779 00000 n +0002535452 00000 n +0002545302 00000 n +0002584331 00000 n +0002633797 00000 n +0002675894 00000 n +0000034054 00000 n +0000035112 00000 n +0000035268 00000 n +0000031376 00000 n +0000031568 00000 n +0000037775 00000 n +0000036484 00000 n +0000036667 00000 n +0000036319 00000 n +0000036961 00000 n +trailer +<<5A47203AAF28614E8B3DEE5BC7B95E54>]>> +startxref +2676148 +%%EOF diff --git a/docs/PERIBOARD-810.pdf b/docs/PERIBOARD-810.pdf new file mode 100644 index 0000000..d094c1c Binary files /dev/null and b/docs/PERIBOARD-810.pdf differ diff --git a/docs/PERIBOARD-810_Intl.pdf b/docs/PERIBOARD-810_Intl.pdf new file mode 100644 index 0000000..712209a Binary files /dev/null and b/docs/PERIBOARD-810_Intl.pdf differ diff --git a/docs/PS2_Mouse.pdf b/docs/PS2_Mouse.pdf new file mode 100644 index 0000000..17f5bce Binary files /dev/null and b/docs/PS2_Mouse.pdf differ diff --git a/docs/Sanwa_SKB-L1_Keyboard.pdf b/docs/Sanwa_SKB-L1_Keyboard.pdf new file mode 100644 index 0000000..1961f27 Binary files /dev/null and b/docs/Sanwa_SKB-L1_Keyboard.pdf differ diff --git a/main/BT.cpp b/main/BT.cpp deleted file mode 120000 index 9e5c96c..0000000 --- a/main/BT.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/BT.cpp \ No newline at end of file diff --git a/main/BT.cpp b/main/BT.cpp new file mode 100644 index 0000000..a611254 --- /dev/null +++ b/main/BT.cpp @@ -0,0 +1,788 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: BT.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Bluetooth base class. +// This source file contains the class to encapsulate the Bluetooth ESP API. Both +// BLE and BT Classic are supported. Allows for scanning, pairing and connection +// to a peripheral device such as a Keyboard or Mouse. +// +// The application uses the Espressif Development environment with Arduino components. +// This is necessary as the class uses the Arduino methods for GPIO manipulation. I +// was considering using pure Espressif IDF methods but considered the potential +// of also using this class on an Arduino project. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_bt_device.h" +#include "esp_spp_api.h" +#include "Arduino.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "BT.h" + +#define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(*a)) + +// Out of object pointer to a singleton class for use in the ESP IDF API callback routines which werent written for C++. Other methods can be used but this one is the simplest +// to understand and the class can only ever be singleton. +BT *pBTThis = NULL; + +// Method to locate a valid scan entry in the results list. +// +BT::t_scanListItem* BT::findValidScannedDevice(esp_bd_addr_t bda, std::vector &scanList) +{ + // Locals. + // + + // Loop through the scan results list looking for a valid entry, return entry if found. + for(std::size_t idx = 0; idx < scanList.size(); idx++) + { + if (memcmp(bda, scanList[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + return &scanList[idx]; + } + } + return(nullptr); +} + +#ifdef CONFIG_CLASSIC_BT_ENABLED +// Method to add a valid BT Classic device onto the scan list. +// +void BT::addBTScanDevice(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi) +{ + // Locals. + t_scanListItem item; + + // Find a valid device in the BT Classic scan results. If a device is found then this callback is with new data. + t_scanListItem* result = findValidScannedDevice(bda, btCtrl.btScanList); + if(result) + { + // Information can be updated through several calls. + if(result->name.length() == 0 && name && name_len) + { + result->name.assign((char *)name, name_len); + } + if(result->bt.uuid.len == 0 && uuid->len) + { + memcpy(&result->bt.uuid, uuid, sizeof(esp_bt_uuid_t)); + } + if(rssi != 0) + { + result->rssi = rssi; + } + return; + } + + // Populate new list item with device results. + item.transport = ESP_HID_TRANSPORT_BT; + memcpy(item.bda, bda, sizeof(esp_bd_addr_t)); + memcpy(&item.bt.cod, cod, sizeof(esp_bt_cod_t)); + memcpy(&item.bt.uuid, uuid, sizeof(esp_bt_uuid_t)); + item.usage = esp_hid_usage_from_cod((uint32_t)cod); + item.rssi = rssi; + item.name = ""; + + // Store device name if present. This is possibly provided in a seperate callback. + if(name_len && name) + { + item.name.assign((char *)name, name_len); + } + + // Add new item onto list. + btCtrl.btScanList.push_back(item); + return; +} +#endif + +// Method to add a valid BLE device to our scan list. +// +void BT::addBLEScanDevice(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi) +{ + // Locals. + // + t_scanListItem item; + + // See if the device is already in the list, exit if found as data updates with seperate callbacks not normal under BLE. + if(findValidScannedDevice(bda, btCtrl.bleScanList)) + { + ESP_LOGW(TAG, "Result already exists!"); + return; + } + + // Populate the item with data. + item.transport = ESP_HID_TRANSPORT_BLE; + memcpy(item.bda, bda, sizeof(esp_bd_addr_t)); + item.ble.appearance = appearance; + item.ble.addr_type = addr_type; + item.usage = esp_hid_usage_from_appearance(appearance); + item.rssi = rssi; + item.name = ""; + + // Store device name if present. + if(name_len && name) + { + item.name.assign((char *)name, name_len); + } + + // Add new item onto list. + btCtrl.bleScanList.push_back(item); + return; +} + +#ifdef CONFIG_CLASSIC_BT_ENABLED +// Method to process a device data resulting from a BT scan. +// +void BT::processBTDeviceScanResult(esp_bt_gap_cb_param_t * param) +{ + // Locals + // + uint32_t codv = 0; + esp_bt_cod_t *cod = (esp_bt_cod_t *)&codv; + int8_t rssi = 0; + uint8_t *name = nullptr; + uint8_t name_len = 0; + esp_bt_uuid_t uuid; + uint8_t len = 0; + uint8_t *data = 0; + + uuid.len = ESP_UUID_LEN_16; + uuid.uuid.uuid16 = 0; + + for (int i = 0; i < param->disc_res.num_prop; i++) + { + esp_bt_gap_dev_prop_t * prop = ¶m->disc_res.prop[i]; + if(prop->type != ESP_BT_GAP_DEV_PROP_EIR) + { + } + if(prop->type == ESP_BT_GAP_DEV_PROP_BDNAME) + { + name = (uint8_t *) prop->val; + name_len = strlen((const char *)name); + } + else if(prop->type == ESP_BT_GAP_DEV_PROP_RSSI) + { + rssi = *((int8_t *) prop->val); + } + else if(prop->type == ESP_BT_GAP_DEV_PROP_COD) + { + memcpy(&codv, prop->val, sizeof(uint32_t)); + } + else if(prop->type == ESP_BT_GAP_DEV_PROP_EIR) + { + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_CMPL_16BITS_UUID, &len); + + if(data == nullptr) + { + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_INCMPL_16BITS_UUID, &len); + } + + if(data && len == ESP_UUID_LEN_16) + { + uuid.len = ESP_UUID_LEN_16; + uuid.uuid.uuid16 = data[0] + (data[1] << 8); + continue; + } + + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_CMPL_32BITS_UUID, &len); + + if(data == nullptr) + { + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_INCMPL_32BITS_UUID, &len); + } + + if(data && len == ESP_UUID_LEN_32) + { + uuid.len = len; + memcpy(&uuid.uuid.uuid32, data, sizeof(uint32_t)); + continue; + } + + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_CMPL_128BITS_UUID, &len); + + if(data == nullptr) + { + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_INCMPL_128BITS_UUID, &len); + } + + if(data && len == ESP_UUID_LEN_128) + { + uuid.len = len; + memcpy(uuid.uuid.uuid128, (uint8_t *)data, len); + continue; + } + + //try to find a name + if (name == nullptr) + { + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &len); + + if (data == nullptr) + { + data = esp_bt_gap_resolve_eir_data((uint8_t *) prop->val, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &len); + } + + if (data && len) + { + name = data; + name_len = len; + } + } + } + } + + // If the found device is a peripheral or a second call on an existing device, add/update the device. + if ((cod->major == ESP_BT_COD_MAJOR_DEV_PERIPHERAL) || (findValidScannedDevice(param->disc_res.bda, btCtrl.btScanList) != nullptr)) + { + addBTScanDevice(param->disc_res.bda, cod, &uuid, name, name_len, rssi); + } +} +#endif + +#ifdef CONFIG_CLASSIC_BT_ENABLED +// BT GAP Event Handler. +// +void BT::processBTGapEvent(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + // Locals. + // + + switch(event) + { + case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: + { + ESP_LOGI(TAG, "BT GAP DISC_STATE %s", (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) ? "START" : "STOP"); + if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) + { + // Release semaphore on which the initiator is waiting, this signals processing complete and results ready. + xSemaphoreGive(pBTThis->btCtrl.bt_hidh_cb_semaphore); + } + break; + } + case ESP_BT_GAP_DISC_RES_EVT: + { + pBTThis->processBTDeviceScanResult(param); + break; + } + case ESP_BT_GAP_KEY_NOTIF_EVT: + ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%d", param->key_notif.passkey); + if(pBTThis->btCtrl.pairingHandler != nullptr) (*pBTThis->btCtrl.pairingHandler)(param->key_notif.passkey, 1); + break; + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(TAG, "BT GAP MODE_CHG_EVT mode:%d", param->mode_chg.mode); + break; + case ESP_BT_GAP_AUTH_CMPL_EVT: + ESP_LOGI(TAG, "BT GAP MODE AUTH_CMPL:%s (%d)", param->auth_cmpl.device_name, param->auth_cmpl.stat); + if(pBTThis->btCtrl.pairingHandler != nullptr) (*pBTThis->btCtrl.pairingHandler)((uint32_t)param->auth_cmpl.stat, 2); + break; + default: + ESP_LOGI(TAG, "BT GAP EVENT %s", pBTThis->bt_gap_evt_str(event)); + break; + } +} +#endif + +// Method to process a device data resulting from a BLE scan. +// +void BT::processBLEDeviceScanResult(esp_ble_gap_cb_param_t *param) +{ + // Locals. + // + uint16_t uuid = 0; + uint16_t appearance = 0; + char name[64] = ""; + uint8_t uuid_len = 0; + uint8_t *uuid_d = esp_ble_resolve_adv_data(param->scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, &uuid_len); + uint8_t appearance_len = 0; + uint8_t *appearance_d = esp_ble_resolve_adv_data(param->scan_rst.ble_adv, ESP_BLE_AD_TYPE_APPEARANCE, &appearance_len); + uint8_t adv_name_len = 0; + uint8_t *adv_name = esp_ble_resolve_adv_data(param->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); + + if (uuid_d != nullptr && uuid_len) + { + uuid = uuid_d[0] + (uuid_d[1] << 8); + } + + if (appearance_d != nullptr && appearance_len) + { + appearance = appearance_d[0] + (appearance_d[1] << 8); + } + + if (adv_name == nullptr) + { + adv_name = esp_ble_resolve_adv_data(param->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_SHORT, &adv_name_len); + } + + if (adv_name != nullptr && adv_name_len) + { + memcpy(name, adv_name, adv_name_len); + name[adv_name_len] = 0; + } + + if (uuid == ESP_GATT_UUID_HID_SVC) + { + addBLEScanDevice(param->scan_rst.bda, + param->scan_rst.ble_addr_type, + appearance, adv_name, adv_name_len, + param->scan_rst.rssi); + } +} + +// BLE GAP Event Handler. +// +void BT::processBLEGapEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t * param) +{ + switch(event) + { + // SCAN + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + { + ESP_LOGI(TAG, "BLE GAP EVENT SCAN_PARAM_SET_COMPLETE"); + + // Release semaphore, this releases the caller who initiated the scan as we are now complete. + xSemaphoreGive(pBTThis->btCtrl.ble_hidh_cb_semaphore); + break; + } + case ESP_GAP_BLE_SCAN_RESULT_EVT: + { + switch (param->scan_rst.search_evt) + { + case ESP_GAP_SEARCH_INQ_RES_EVT: + { + pBTThis->processBLEDeviceScanResult(param); + break; + } + case ESP_GAP_SEARCH_INQ_CMPL_EVT: + ESP_LOGI(TAG, "BLE GAP EVENT SCAN DONE: %d", param->scan_rst.num_resps); + + // Release semaphore, this releases the caller who initiated the scan as we are now complete. + xSemaphoreGive(pBTThis->btCtrl.ble_hidh_cb_semaphore); + break; + default: + break; + } + break; + } + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + { + ESP_LOGI(TAG, "BLE GAP EVENT SCAN CANCELED"); + break; + } + + // ADVERTISEMENT + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + ESP_LOGI(TAG, "BLE GAP ADV_DATA_SET_COMPLETE"); + break; + + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + ESP_LOGI(TAG, "BLE GAP ADV_START_COMPLETE"); + break; + + // AUTHENTICATION + case ESP_GAP_BLE_AUTH_CMPL_EVT: + if (!param->ble_security.auth_cmpl.success) + { + ESP_LOGE(TAG, "BLE GAP AUTH ERROR: 0x%x", param->ble_security.auth_cmpl.fail_reason); + } + else + { + ESP_LOGI(TAG, "BLE GAP AUTH SUCCESS"); + } + break; + + case ESP_GAP_BLE_KEY_EVT: //shows the ble key info share with peer device to the user. + ESP_LOGI(TAG, "BLE GAP KEY type = %s", pBTThis->ble_key_type_str(param->ble_security.ble_key.key_type)); + break; + + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // ESP_IO_CAP_OUT + // The app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + // Show the passkey number to the user to input it in the peer device. + ESP_LOGI(TAG, "BLE GAP PASSKEY_NOTIF passkey:%d", param->ble_security.key_notif.passkey); + if(pBTThis->btCtrl.pairingHandler != nullptr) (*pBTThis->btCtrl.pairingHandler)(param->ble_security.key_notif.passkey, 3); + break; + + case ESP_GAP_BLE_NC_REQ_EVT: // ESP_IO_CAP_IO + // The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability. + // show the passkey number to the user to confirm it with the number displayed by peer device. + ESP_LOGI(TAG, "BLE GAP NC_REQ passkey:%d", param->ble_security.key_notif.passkey); + esp_ble_confirm_reply(param->ble_security.key_notif.bd_addr, true); + break; + + case ESP_GAP_BLE_PASSKEY_REQ_EVT: // ESP_IO_CAP_IN + // The app will receive this evt when the IO has Input capability and the peer device IO has Output capability. + // See the passkey number on the peer device and send it back. + ESP_LOGI(TAG, "BLE GAP PASSKEY_REQ"); + //esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, 1234); + break; + + case ESP_GAP_BLE_SEC_REQ_EVT: + ESP_LOGI(TAG, "BLE GAP SEC_REQ"); + // Send the positive(true) security response to the peer device to accept the security request. + // If not accept the security request, should send the security response with negative(false) accept value. + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + break; + + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + ESP_LOGI(TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d", + param->update_conn_params.status, + param->update_conn_params.min_int, + param->update_conn_params.max_int, + param->update_conn_params.conn_int, + param->update_conn_params.latency, + param->update_conn_params.timeout); + break; + + default: + ESP_LOGI(TAG, "BLE GAP EVENT %s", pBTThis->ble_gap_evt_str(event)); + break; + } +} + +#ifdef CONFIG_CLASSIC_BT_ENABLED +// Method to scan for BT Classic devices. +// +esp_err_t BT::scanForBTDevices(uint32_t timeout) +{ + // Locals. + // + esp_err_t result = ESP_OK; + + // Start BT GAP Discovery, wait for 'timeout' seconds for a valid result. + if((result = esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, (int)(timeout / 1.28), 0)) != ESP_OK) + { + ESP_LOGE(TAG, "esp_bt_gap_start_discovery failed: %d", result); + } + return(result); +} +#endif + +// Method to scan for BLE Devices. +// +esp_err_t BT::scanForBLEDevices(uint32_t timeout) +{ + // Locals. + // + esp_err_t result = ESP_OK; + // Setup BLE scan parameters structure, defined in ESP IDF documentation. + static esp_ble_scan_params_t hid_scan_params = { + .scan_type = BLE_SCAN_TYPE_ACTIVE, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_interval = 0x50, + .scan_window = 0x30, + .scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE, + }; + + // Set scan parameters using populated structure. + if((result = esp_ble_gap_set_scan_params(&hid_scan_params)) != ESP_OK) + { + ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", result); + return(result); + } + + // Wait for result, this is done by taking possession of a semaphore which is released in the callback when scan complete. + xSemaphoreTake(btCtrl.ble_hidh_cb_semaphore, portMAX_DELAY); + + if((result = esp_ble_gap_start_scanning(timeout)) != ESP_OK) + { + ESP_LOGE(TAG, "esp_ble_gap_start_scanning failed: %d", result); + return(result); + } + return(result); +} + +// Method to scan for Bluetooth devices. +// +esp_err_t BT::scanForAllDevices(uint32_t timeout, size_t *noDevices, std::vector &scanList) +{ + // Locals. + // + + // Clear previous lists. + #ifdef CONFIG_CLASSIC_BT_ENABLED + btCtrl.btScanList.clear(); + #endif + btCtrl.bleScanList.clear(); + + // Scan for BLE devices. + if(scanForBLEDevices(timeout) == ESP_OK) + { + // Wait for result, this is done by taking possession of a semaphore which is released in the callback when scan complete. + xSemaphoreTake(btCtrl.ble_hidh_cb_semaphore, portMAX_DELAY); + } + else + { + return(ESP_FAIL); + } + + #ifdef CONFIG_CLASSIC_BT_ENABLED + // Scan for BT devices + if(scanForBTDevices(timeout) == ESP_OK) + { + // Wait for result, this is done by taking possession of a semaphore which is released in the callback when scan complete. + xSemaphoreTake(btCtrl.bt_hidh_cb_semaphore, portMAX_DELAY); + } + else + { + return(ESP_FAIL); + } + #endif + + //esp_bt_gap_cancel_discovery(); + //esp_ble_gap_stop_scanning(); + + // Process results into a merged list. + #ifdef CONFIG_CLASSIC_BT_ENABLED + for(std::size_t idx = 0; idx < btCtrl.btScanList.size(); idx++) + { + scanList.push_back(btCtrl.btScanList[idx]); + } + #endif + for(std::size_t idx = 0; idx < btCtrl.bleScanList.size(); idx++) + { + scanList.push_back(btCtrl.bleScanList[idx]); + } + + // Update the final list with display values. + for(std::size_t idx = 0; idx < scanList.size(); idx++) + { + char buf[50]; + sprintf(buf, ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(scanList[idx].bda)); + scanList[idx].deviceAddr = buf; + if(scanList[idx].transport == ESP_HID_TRANSPORT_BLE) + { + scanList[idx].deviceType = "BLE"; + } + #ifdef CONFIG_CLASSIC_BT_ENABLED + if(scanList[idx].transport == ESP_HID_TRANSPORT_BT) + { + scanList[idx].deviceType = "BT"; + } + #endif + } + + // Save number of entries. + *noDevices = scanList.size(); + + // Clear BT/BLE lists as data no longer needed. + #ifdef CONFIG_CLASSIC_BT_ENABLED + btCtrl.btScanList.clear(); + #endif + btCtrl.bleScanList.clear(); + + return(ESP_OK); +} + +// Method to scan and build a list for all available devices. +void BT::getDeviceList(std::vector &scanList, int waitTime) +{ + // Locals. + // + size_t devicesFound = 0; + + ESP_LOGD(TAG, "SCAN..."); + + // Clear previous entries. + scanList.clear(); + + // Start scan for HID devices + scanForAllDevices(waitTime, &devicesFound, scanList); + + ESP_LOGD(TAG, "SCAN: %u results", devicesFound); +} + +// Method to configure Bluetooth and register required callbacks. +bool BT::setup(t_pairingHandler *handler) +{ + // Locals. + // + esp_err_t result; + const esp_bt_mode_t mode = HIDH_BTDM_MODE; + uint8_t key_size = 16; + uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + uint32_t passkey = 123456; + uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE; + uint8_t oob_support = ESP_BLE_OOB_DISABLE; + + // Check for multiple instantiations, only one instance allowed. + if(pBTThis != nullptr) + { + ESP_LOGE(TAG, "Setup called more than once. Only one instance of BT is allowed."); + return false; + } + + // Store current object and handlers. + pBTThis = this; + btCtrl.pairingHandler = handler; + + // Bluetooth not enabled, exit. + if(mode == HIDH_IDLE_MODE) + { + ESP_LOGE(TAG, "Please turn on BT HID host or BLE!"); + return false; + } + + #ifdef CONFIG_CLASSIC_BT_ENABLED + // Create BT Classic semaphore, used to halt caller whilst underlying receives and proceses data. + btCtrl.bt_hidh_cb_semaphore = xSemaphoreCreateBinary(); + if (btCtrl.bt_hidh_cb_semaphore == nullptr) + { + ESP_LOGE(TAG, "xSemaphoreCreateMutex BT failed!"); + return false; + } + #endif + + // Create BLE semaphore, used to halt caller whilst underlying receives and proceses data. + btCtrl.ble_hidh_cb_semaphore = xSemaphoreCreateBinary(); + if(btCtrl.ble_hidh_cb_semaphore == nullptr) + { + ESP_LOGE(TAG, "xSemaphoreCreateMutex BLE failed!"); + + #ifdef CONFIG_CLASSIC_BT_ENABLED + // Delete BT semaphore as both BT and BLE need to be active, return fail to caller. + vSemaphoreDelete(btCtrl.bt_hidh_cb_semaphore); + btCtrl.bt_hidh_cb_semaphore = nullptr; + #endif + return false; + } + + #ifdef CONFIG_CLASSIC_BT_ENABLED + // Setup default config for BT Classic. + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + bt_cfg.mode = mode; + bt_cfg.bt_max_acl_conn = 3; + bt_cfg.bt_max_sync_conn = 3; + + // Configure Bluetooth controller for BT Classic operation. + if((result = esp_bt_controller_init(&bt_cfg))) + { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %d", result); + return false; + } + + // Enable Bluetooth Classic mode. + if((result = esp_bt_controller_enable(mode))) + { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %d", result); + return false; + } + esp_bredr_tx_power_set(ESP_PWR_LVL_P9, ESP_PWR_LVL_P9); + #endif + + // Setup and initialise Bluetooth BLE mode. + if((result = esp_bluedroid_init())) + { + ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", result); + return false; + } + if((result = esp_bluedroid_enable())) + { + ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", result); + return false; + } + esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9); + + #ifdef CONFIG_CLASSIC_BT_ENABLED + // Classic Bluetooth GAP + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); + + // Set default parameters for Legacy Pairing + // Use fixed pin code + // + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED; + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_set_pin(pin_type, 4, pin_code); + + if((result = esp_bt_gap_register_callback(processBTGapEvent))) + { + ESP_LOGE(TAG, "esp_bt_gap_register_callback failed: %d", result); + return false; + } + + // Allow BT devices to connect back to us + if((result = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_NON_DISCOVERABLE))) + { + ESP_LOGE(TAG, "esp_bt_gap_set_scan_mode failed: %d", result); + return false; + } + #endif + + // BLE GAP + if((result = esp_ble_gap_register_callback(processBLEGapEvent))) + { + ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", result); + return false; + } + + // Setup security, no password. + esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND; // Bonding with peer device after authentication + esp_ble_io_cap_t iocapble = ESP_IO_CAP_NONE; // Set the IO capability to No output No input + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocapble, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t)); + + // Initialise parameters. + btCtrl.batteryLevel = -1; + return true; +} + +// Basic constructor, do nothing! +BT::BT(void) +{ + btCtrl.hidhDevHdl = NULL; + #ifdef CONFIG_CLASSIC_BT_ENABLED + btCtrl.pairingHandler = nullptr; + btCtrl.bt_hidh_cb_semaphore = nullptr; + #endif + btCtrl.ble_hidh_cb_semaphore = nullptr; + pBTThis = NULL; + // +} + +// Basic destructor, do nothing! Only ever called for instantiation of uninitialsed class to prove version data.Used for probing versions etc. +BT::~BT(void) +{ + // +} diff --git a/main/BTHID.cpp b/main/BTHID.cpp deleted file mode 120000 index e25db07..0000000 --- a/main/BTHID.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/BTHID.cpp \ No newline at end of file diff --git a/main/BTHID.cpp b/main/BTHID.cpp new file mode 100644 index 0000000..68e34cd --- /dev/null +++ b/main/BTHID.cpp @@ -0,0 +1,1011 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: BTHID.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Bluetooth Keyboard Class. +// This source file contains the class to encapsulate a Bluetooth keyboard as a sub +// class of the BT base class. +// It provides connection, key retrieval and first stage mapping to be compatible +// with a PS/2 keyboard prior to host mapping. +// +// The application uses the Espressif Development environment with Arduino components. +// This is necessary as the class uses the Arduino methods for GPIO manipulation. I +// was considering using pure Espressif IDF methods but considered the potential +// of also using this class on an Arduino project. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// Jun 2022 - Updated with latest findings. Now checks the bonded list and opens +// connections or scans for new devices if no connections exist. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "BTHID.h" + +// Out of object pointer to a singleton class for use in the ESP IDF API callback routines which werent written for C++. Other methods can be used but this one is the simplest +// to understand and the class can only ever be singleton. +BTHID *pBTHID = NULL; + +// Method to open a connection with a paired device. +// +bool BTHID::openDevice(esp_bd_addr_t bda, esp_hid_transport_t transport, esp_ble_addr_type_t addrType) +{ + // Locals. + // + bool found = false; + t_activeDev device; + + // Call underlying IDF API to open the device. Store handle for future use. + device.hidhDevHdl = esp_hidh_dev_open(bda, transport, addrType); + + // Add device to list of known devices. + for(std::size_t idx = 0; idx < pBTHID->btHIDCtrl.devices.size(); idx++) + { + // Already on list? + if (memcmp(bda, btHIDCtrl.devices[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + found = true; + } + } + if(!found && device.hidhDevHdl != 0) + { + memcpy(device.bda, bda, sizeof(esp_bd_addr_t)); + device.transport = transport; + device.addrType = addrType; + device.open = true; + device.nextCheckTime = milliSeconds() + 5000L; + btHIDCtrl.devices.push_back(device); + } + + // Return connection status. + return(device.hidhDevHdl == NULL ? false : true); +} + +// Method to close a connection with a paired device. +// +bool BTHID::closeDevice(esp_bd_addr_t bda) +{ + // Locals. + // + esp_err_t result = ESP_OK; + + // Locate device and close it out. + for(std::size_t idx = 0; idx < pBTHID->btHIDCtrl.devices.size(); idx++) + { + // Already on list? + if (memcmp(bda, btHIDCtrl.devices[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + if(btHIDCtrl.devices[idx].hidhDevHdl != NULL) + { + result = esp_hidh_dev_close(btHIDCtrl.devices[idx].hidhDevHdl); + btHIDCtrl.devices[idx].open = false; + } + } + } + + return(result); +} + +// Callback to handle Bluetooth HID data. This method is called whenever an event occurs, such as a new device being opened or existing one closed. Also +// called with data reports from connected devices. The method determines source of data and routes it to the correct channel (keyboard or mouse). +// +// Example BT Classic Device +// BDA:17:27:6d:85:25:e9, Status: OK, Connected: YES, Handle: 0, Usage: KEYBOARD +// Name: , Manufacturer: , Serial Number: +// PID: 0x7021, VID: 0x04e8, VERSION: 0x011b +// Report Map Length: 295 +// CCONTROL INPUT REPORT, ID: 255, Length: 1 +// CCONTROL INPUT REPORT, ID: 5, Length: 1 +// GENERIC INPUT REPORT, ID: 4, Length: 1 +// CCONTROL INPUT REPORT, ID: 3, Length: 7 +// CCONTROL INPUT REPORT, ID: 2, Length: 3 +// KEYBOARD OUTPUT BOOT, ID: 1, Length: 1 +// KEYBOARD OUTPUT REPORT, ID: 1, Length: 1 +// KEYBOARD INPUT BOOT, ID: 1, Length: 8 +// KEYBOARD INPUT REPORT, ID: 1, Length: 8 +// +// Example BLE Device +// BDA:cf:4a:a8:5c:5e:8c, Appearance: 0x03c0, Connection ID: 0 +// Name: M585/M590, Manufacturer: Logitech, Serial Number: A2C4E0DBF89C6DFA +// PID: 0xb01b, VID: 0x046d, VERSION: 0x0011 +// Battery: Handle: 29, CCC Handle: 0 +// Report Maps: 1 +// Report Map Length: 140 +// VENDOR OUTPUT REPORT, ID: 17, Length: 19, Permissions: 0x0e, Handle: 56, CCC Handle: 0 +// VENDOR INPUT REPORT, ID: 17, Length: 19, Permissions: 0x12, Handle: 52, CCC Handle: 53 +// MOUSE INPUT REPORT, ID: 2, Length: 7, Permissions: 0x12, Handle: 48, CCC Handle: 49 +// KEYBOARD INPUT REPORT, ID: 1, Length: 7, Permissions: 0x12, Handle: 44, CCC Handle: 45 +// MOUSE INPUT BOOT, ID: 2, Length: 3, Permissions: 0x12, Handle: 39, CCC Handle: 40 +// KEYBOARD OUTPUT BOOT, ID: 0, Length: 8, Permissions: 0x0e, Handle: 37, CCC Handle: 0 +// KEYBOARD INPUT BOOT, ID: 1, Length: 8, Permissions: 0x12, Handle: 34, CCC Handle: 35 +// +void BTHID::hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void * event_data) +{ + // Locals. + // + t_activeDev device; + esp_hidh_event_t event = (esp_hidh_event_t) id; + esp_hidh_event_data_t *param = (esp_hidh_event_data_t *) event_data; + + switch (event) + { + case ESP_HIDH_OPEN_EVENT: + { + const uint8_t *bda = esp_hidh_dev_bda_get(param->open.dev); + esp_hid_usage_t usage = esp_hidh_dev_usage_get(param->open.dev); + if (param->open.status == ESP_OK) + { + // Update status of device in list. + bool found = false; + for(std::size_t idx = 0; idx < pBTHID->btHIDCtrl.devices.size(); idx++) + { + // Try and re-open closed devices. + if(memcmp(bda, pBTHID->btHIDCtrl.devices[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + pBTHID->btHIDCtrl.devices[idx].open = true; + pBTHID->btHIDCtrl.devices[idx].usage = usage; + found = true; + break; + } + } + // If device not found on device list it will be a previous pairing which has woken up so add to list. + if(found == false) + { + memcpy(device.bda, bda, sizeof(esp_bd_addr_t)); + device.transport = esp_hidh_dev_transport_get(param->open.dev); + device.addrType = BLE_ADDR_TYPE_RANDOM; + device.open = true; + device.usage = usage; + pBTHID->btHIDCtrl.devices.push_back(device); + } + + // Ask for the current LED status on keyboards, this is used to pre-set the function locks. + if(usage == ESP_HID_USAGE_KEYBOARD) + { + esp_hidh_dev_get_report(param->open.dev, 0, 0x1, ESP_HID_REPORT_TYPE_OUTPUT, 10); + } + ESP_LOGD(TAG, ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev)); + esp_hidh_dev_dump(param->open.dev, stdout); + vTaskDelay(100); + } else + { + // Update status of device in list. + for(std::size_t idx = 0; idx < pBTHID->btHIDCtrl.devices.size(); idx++) + { + // Try and re-open closed devices. + if(bda != NULL && pBTHID->btHIDCtrl.devices[idx].bda != NULL && memcmp(bda, pBTHID->btHIDCtrl.devices[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + pBTHID->btHIDCtrl.devices[idx].open = false; + } + } + ESP_LOGE(TAG, " OPEN failed!"); + // pBTHID->closeDevice(); + } + break; + } + case ESP_HIDH_BATTERY_EVENT: + { + const uint8_t *bda = esp_hidh_dev_bda_get(param->battery.dev); + ESP_LOGD(TAG, ESP_BD_ADDR_STR " BATTERY: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level); + pBTHID->setBatteryLevel(param->battery.level); + break; + } + case ESP_HIDH_INPUT_EVENT: + { + const uint8_t *bda = esp_hidh_dev_bda_get(param->input.dev); + ESP_LOGD(TAG, ESP_BD_ADDR_STR " INPUT: %8s, MAP: %2u, ID: %3u, Len: %d, Data:", + ESP_BD_ADDR_HEX(bda), + esp_hid_usage_str(param->input.usage), + param->input.map_index, + param->input.report_id, + param->input.length); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, param->input.data, param->input.length, ESP_LOG_DEBUG); + + // Add data to queue for later filtering and processing. + pBTHID->pushKeyToFIFO(param->input.usage, param->input.dev, param->input.data, param->input.length); + break; + } + case ESP_HIDH_FEATURE_EVENT: + { + const uint8_t *bda = esp_hidh_dev_bda_get(param->feature.dev); + + for(std::size_t idx = 0; idx < pBTHID->btHIDCtrl.devices.size(); idx++) + { + // Matched device? + if(memcmp(bda, pBTHID->btHIDCtrl.devices[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + // Is this an LED update report? + if(pBTHID->btHIDCtrl.devices[idx].usage == ESP_HID_USAGE_KEYBOARD && param->feature.map_index == 0 && param->feature.report_id == 0x01 && param->feature.length == 0x01) + { + if(param->feature.data[0] & BT_LED_NUMLOCK) + { + pBTHID->btHIDCtrl.kbd.btFlags |= BT_NUM_LOCK; + } else + { + pBTHID->btHIDCtrl.kbd.btFlags &= ~BT_NUM_LOCK; + } + if(param->feature.data[0] & BT_LED_CAPSLOCK) + { + pBTHID->btHIDCtrl.kbd.btFlags |= BT_CAPS_LOCK; + } else + { + pBTHID->btHIDCtrl.kbd.btFlags &= ~BT_CAPS_LOCK; + } + if(param->feature.data[0] & BT_LED_SCROLLLOCK) + { + pBTHID->btHIDCtrl.kbd.btFlags |= BT_SCROLL_LOCK; + } else + { + pBTHID->btHIDCtrl.kbd.btFlags &= ~BT_SCROLL_LOCK; + } + pBTHID->btHIDCtrl.kbd.statusLED = param->feature.data[0]; + } + break; + } + } + ESP_LOGD(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", + ESP_BD_ADDR_HEX(bda), + esp_hid_usage_str(param->feature.usage), + param->feature.map_index, + param->feature.report_id, + param->feature.length); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, param->feature.data, param->feature.length, ESP_LOG_DEBUG); + break; + } + case ESP_HIDH_CLOSE_EVENT: + { + const uint8_t *bda = esp_hidh_dev_bda_get(param->close.dev); + if(bda != NULL) + { + for(std::size_t idx = 0; idx < pBTHID->btHIDCtrl.devices.size(); idx++) + { + // Device which has closed? + if(memcmp(bda, pBTHID->btHIDCtrl.devices[idx].bda, sizeof(esp_bd_addr_t)) == 0) + { + ESP_LOGD(TAG, "Closing device:%d,%s", idx, esp_hidh_dev_name_get(param->close.dev)); + pBTHID->btHIDCtrl.devices[idx].open = false; + } + } + ESP_LOGD(TAG, ESP_BD_ADDR_STR " CLOSE: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev)); + } + break; + } + default: + ESP_LOGD(TAG, "EVENT: %d", event); + break; + } +} + +// Method to process a received key or mouse movement, keys go onto an internal FIFO queue buffering until the application requests them, +// mouse movements are dispatched immediately via callback as latency is important with a mouse. +// NB: Overflow data is lost so application needs to process data in a timely fashion. +// +void BTHID::pushKeyToFIFO(esp_hid_usage_t src, esp_hidh_dev_t *hdlDev, uint8_t *keys, uint8_t size) +{ + // Locals. + KeyInfo keyInfo; + PS2Mouse::MouseData mouseData; + + // Use FreeRTOS queue manager to push the key record onto the FIFO. + if(src == ESP_HID_USAGE_KEYBOARD || src == ESP_HID_USAGE_CCONTROL) + { + for(int idx=0; idx < MAX_KEYBOARD_DATA_BYTES; idx++) + { + if(idx < size) + { + keyInfo.keys[idx] = keys[idx]; + } else + { + keyInfo.keys[idx] = 0x00; + } + } + keyInfo.length = size; + keyInfo.cControl = (src == ESP_HID_USAGE_CCONTROL ? true : false); + keyInfo.hdlDev = hdlDev; + xQueueSendFromISR(btHIDCtrl.kbd.rawKeyQueue, &keyInfo, 0); + } + else if(src == ESP_HID_USAGE_MOUSE) + { + // Mouse data is processed realtime. It is massaged into PS/2 data, encapsulated, then passed to the provided + // callback which handles it. + mouseData.overrun = false; + mouseData.valid = true; + + // Ensure a movement report. + if(size > 3) + { + // Bit 3 is always set on PS/2 messages. + mouseData.status = keys[0] | 0x08; + + // The resolution of a BT mouse is typically 12bit signed, ie. -2048 .. +2047 on both axis. PS/2 was typically 9bit, ie. -255 .. +254 and the Sharp + // hosts are typically -128 .. +127 so the values need to be scaled down after applying any configurable setting. + mouseData.position.x = keys[3] & 0x08 ? (-2048 + ((((keys[3]&0x07) << 8)) | keys[2])) : (((keys[3]&0x07) << 8) | keys[2]); + mouseData.position.y = -(keys[4] & 0x80 ? (-2048 + (((keys[4]&0x7f) << 4) | ((keys[3]&0xf0) >> 4))) : (((keys[4]&0x7f) << 4) | ((keys[3]&0xf0) >> 4))); + + // Apply any PS/2 configurable settings which have meaning. + // + mouseData.position.x = mouseData.position.x * btHIDCtrl.ms.scaling; + mouseData.position.y = mouseData.position.y * btHIDCtrl.ms.scaling; + mouseData.position.x = mouseData.position.x * btHIDCtrl.ms.resolution; + mouseData.position.y = mouseData.position.y * btHIDCtrl.ms.resolution; + + // Set the wheel value. + mouseData.wheel = keys[5]; + } + + // If a data callback has been setup, invoke otherwise data is wasted. + // + if(btHIDCtrl.ms.mouseDataCallback != NULL) + btHIDCtrl.ms.mouseDataCallback(mouseData); + } + return; +} + +// Method to check devices for connectivity. This generally entails re-opening closed devices as BT links are self maintaining until closure. +// +void BTHID::checkBTDevices(void) +{ + // Locals. + // + bool nonFound = true; + std::vector scanList; + + // Loop through list of known devices and open a connection with them. If no devices exist or no connection can be opened, start + // a scan for new devices. Normally, bonded devices when activated will connect but sometimes a physical open is needed hence this + // logic. + for(std::size_t idx = 0; idx < btHIDCtrl.devices.size(); idx++) + { + if(btHIDCtrl.devices[idx].open == true) + { + nonFound = false; + } else + { + // If the timer has expired on this entry, make an open attempt. + if(btHIDCtrl.devices[idx].nextCheckTime <= milliSeconds()) + { + ESP_LOGI(TAG, ESP_BD_ADDR_STR " PAIREDOPEN", ESP_BD_ADDR_HEX(btHIDCtrl.devices[idx].bda)); + if(openDevice(btHIDCtrl.devices[idx].bda, btHIDCtrl.devices[idx].transport, btHIDCtrl.devices[idx].addrType) == true) + { + btHIDCtrl.devices[idx].open = true; + } else + { + btHIDCtrl.devices[idx].nextCheckTime = milliSeconds() + 5000L; + } + } + nonFound = false; + } + } + if(nonFound) + { + // Get list of devices which can be seen by bluetooth receiver and try to connect to known/pairing devices. + getDeviceList(scanList, 5); + + for(int idx = 0; idx < scanList.size(); idx++) + { + ESP_LOGI(TAG, ESP_BD_ADDR_STR " SCANOPEN", ESP_BD_ADDR_HEX(scanList[idx].bda)); + openDevice(scanList[idx].bda, scanList[idx].transport, scanList[idx].ble.addr_type); + } + } + return; +} + +//********************************************************************************************************************************************* +//** Mouse handler Methods. +//********************************************************************************************************************************************* +// +// Protocol: Finding accurate information for the Mouse low level protocol is not so easy and what you do find doesnt match the data reports +// sent by the ESP HIDH. The protocol as worked out (so far) is: +// +// +// = Status or Button Report. Bit 2 = Wheel/Middle Button, Bit 1 = Right Button, Bit 0 = Left Button. '1' = Button pressed. +// = 0x00 +// = LSB [7:0] of X co-ordinate, Signed range -2048:+2047 +// = MSNIBBLE [11:8] contained in bits [3:0] of x co-ordinate. LSNIBBLE [3:0] contained in bits [7:4] or y co-ordinate. +// = MSB [11:4] of Y co-ordinate, Signed range -2048:+2047 +// = 0x00 +// = 0x00 +// Reading the data, sample rate is 125 frames per second. +// + +// Public method to set the mouse resolution in pixels per millimeter, valid values are 0..3. +// This method is for compatibility with a PS/2 Mouse, any use of the value has to be programmtical in this module prior to delivery +// of the fixed data streamed from the BT HID. +// +bool BTHID::setResolution(enum PS2Mouse::PS2_RESOLUTION resolution) +{ + // Locals. + // + bool result = false; + + // Sanity check. + if(resolution >= PS2Mouse::PS2_MOUSE_RESOLUTION_1_1 && resolution < PS2Mouse::PS2_MOUSE_RESOLUTION_1_8) + { + switch(to_underlying(resolution)) + { + case 0: // 1pixel per mm. + btHIDCtrl.ms.resolution = 1; + break; + case 1: // 2pixles per mm. + btHIDCtrl.ms.resolution = 2; + break; + case 2: // 4pixels per mm. + btHIDCtrl.ms.resolution = 4; + break; + case 3: // 8pixels per mm. + default: + btHIDCtrl.ms.resolution = 8; + break; + } + result = true; + } + + // Return result. + return(result); +} + +// Public method to set the mouse scaling, either Normal 1:1 (scaling = 0) or non-linear 2:1 (scaling = 1). +// This method is for compatibility with a PS/2 Mouse, any use of the value has to be programmtical in this module prior to delivery +// of the fixed data streamed from the BT HID. +// +bool BTHID::setScaling(enum PS2Mouse::PS2_SCALING scaling) +{ + // Locals. + // + bool result = false; + + // Sanity check. + if(scaling >= PS2Mouse::PS2_MOUSE_SCALING_1_1 && scaling < PS2Mouse::PS2_MOUSE_SCALING_2_1) + { + btHIDCtrl.ms.scaling = to_underlying(scaling)+1; + result = true; + } + + // Return result. + return(result); +} + +// Public method to set the automatic sample rate. +// This method is for compatibility with a PS/2 Mouse, any use of the value has to be programmtical in this module prior to delivery +// of the fixed data streamed from the BT HID. +// +bool BTHID::setSampleRate(enum PS2Mouse::PS2_SAMPLING rate) +{ + // Locals. + // + bool result = false; + + // Sanity check. + if(rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_10 || rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_20 || rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_40 || rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_60 || rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_80 || rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_100 || rate == PS2Mouse::PS2_MOUSE_SAMPLE_RATE_200) + { + btHIDCtrl.ms.sampleRate = to_underlying(rate); + result = true; + } + + // Return result. + return(result); +} + +//********************************************************************************************************************************************* +//** Keyboard handler Methods. +//********************************************************************************************************************************************* + +// Method to map a Bluetooth Media Key (ESP HIDH specific) Scan Code to its PS/2 equivalent or 0x0000 if not mappable. +uint16_t BTHID::mapBTMediaToPS2(uint32_t key) +{ + // Locals. + // + uint16_t retKey = 0x0000; + + // Loop through mapping table to find a match. + for(int idx=0; idx < btHIDCtrl.kbd.kmeMediaRows; idx++) + { + if(btHIDCtrl.kbd.kmeMedia[idx].mediaKey == key) + { + retKey = (btHIDCtrl.kbd.kmeMedia[idx].ps2Ctrl << 8) | btHIDCtrl.kbd.kmeMedia[idx].ps2Key; + break; + } + } + + // Return map result or 0x00 if not mappable. + return(retKey); +} + +// Method to map a Bluetooth Scan Code to its PS/2 equivalent or 0x00 if not mappable. +uint16_t BTHID::mapBTtoPS2(uint8_t key) +{ + // Locals. + // + uint16_t retKey = 0x0000; + + // Loop through mapping table to find a match. + for(int idx=0; idx < btHIDCtrl.kbd.kmeRows && retKey == 0x0000; idx++) + { + // Find a match. + if(btHIDCtrl.kbd.kme[idx].btKeyCode == key && (btHIDCtrl.kbd.kme[idx].btCtrl == btHIDCtrl.kbd.btFlags || btHIDCtrl.kbd.kme[idx].btCtrl == BT_NONE)) + { + retKey = (uint16_t)btHIDCtrl.kbd.kme[idx].ps2KeyCode; + if((retKey <= PS2_KEY_SPACE || retKey >= PS2_KEY_F1) && retKey != PS2_KEY_BTICK && retKey != PS2_KEY_HASH && retKey != PS2_KEY_EUROPE2) retKey |= PS2_FUNCTION; + if(btHIDCtrl.kbd.btFlags & BT_CTRL_LEFT || btHIDCtrl.kbd.btFlags & BT_CTRL_RIGHT) retKey |= PS2_CTRL; + if(btHIDCtrl.kbd.btFlags & BT_SHIFT_LEFT || btHIDCtrl.kbd.btFlags & BT_SHIFT_RIGHT) retKey |= PS2_SHIFT; + if(btHIDCtrl.kbd.btFlags & BT_ALT_LEFT) retKey |= PS2_ALT; + if(btHIDCtrl.kbd.btFlags & BT_ALT_RIGHT) retKey |= PS2_ALT_GR; + if(btHIDCtrl.kbd.btFlags & BT_GUI_LEFT || btHIDCtrl.kbd.btFlags & BT_GUI_RIGHT) retKey |= PS2_GUI; + } + } + + // Return map result or 0x00 if not mappable. + return(retKey); +} + +// Method to set a status LED on the keyboard. +// +void BTHID::setStatusLED(esp_hidh_dev_t *dev, uint8_t led) +{ + // Locals + + // Set flag in LED status byte then forward to the keyboard for actual display. + btHIDCtrl.kbd.statusLED |= led; + esp_hidh_dev_output_set(dev, 0, 0x1, &btHIDCtrl.kbd.statusLED, 1); + return; +} + +// Method to clear a status LED on the keyboard. +// +void BTHID::clearStatusLED(esp_hidh_dev_t *dev, uint8_t led) +{ + // Locals + + // Clear flag in LED status byte then forward to the keyboard for actual display. + btHIDCtrl.kbd.statusLED &= ~led; + esp_hidh_dev_output_set(dev, 0, 0x1, &btHIDCtrl.kbd.statusLED, 1); + return; +} + +// Method to process the incoming Bluetooth keyboard data stream and convert it into PS/2 compatible values. +// +// Protocol (received after pre-processing by the BT module) +// -------- +// KEYBOARD: +// <0x00> +// All scan codes are set to overflow (0x01) if more than 6 keys are pressed. +// +// Modifier Byte: +// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 +// Right GUI Right Alt Right Shift Right Ctrl Left GUI Left Alt Left Shift Left Ctrl +// 1 = Key Active, 0 = Inactive. +// +// CCONTROL: (the esp idf splits the bluetooth report of keys into keys and media control) +// ESP havent documented the HIDH module so the values below are worked out, needs updating when they provide documentation. +// +// +// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 +// SEARCH HOME +// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 +// BRITEDN BRITEUP +// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 +// MUTE VOL DOWN VOL UP TRK PREV +// +// +// A down event sees the scan code appear in the list, an up event it disappears. For the modifier bits, the bit is set for down event and cleared for up event. +// +// Control mapping - BT modifier needs to be mapped to these bits: +// Define name bit description +// PS2_BREAK 15 1 = Break key code +// (MSB) 0 = Make Key code +// PS2_SHIFT 14 1 = Shift key pressed as well (either side) +// 0 = NO shift key +// PS2_CTRL 13 1 = Ctrl key pressed as well (either side) +// 0 = NO Ctrl key +// PS2_CAPS 12 1 = Caps Lock ON +// 0 = Caps lock OFF +// PS2_ALT 11 1 = Left Alt key pressed as well +// 0 = NO Left Alt key +// PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well +// 0 = NO Right Alt key +// PS2_GUI 9 1 = GUI key pressed as well (either) +// 0 = NO GUI key +// PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) +// 0 = standard character key +// +// Mapped data/events is pushed onto a queue which is read by the calling API. +// +void BTHID::processBTKeys(void) +{ + // Locals. + uint16_t genKey; + uint32_t mediaKey; + KeyInfo keyInfo; + + // Process all the queued event data. + while(xQueueReceive(btHIDCtrl.kbd.rawKeyQueue, &keyInfo, 0) == pdTRUE) + { + // Process normal scancodes. + if(keyInfo.cControl == false) + { + // Only process if the size is correct. + if(keyInfo.length <= MAX_KEYBOARD_DATA_BYTES) + { + // Process control keys and set flags. + if(keyInfo.keys[0] & BT_CTRL_LEFT) btHIDCtrl.kbd.btFlags |= BT_CTRL_LEFT; else btHIDCtrl.kbd.btFlags &= ~(BT_CTRL_LEFT); + if(keyInfo.keys[0] & BT_SHIFT_LEFT) btHIDCtrl.kbd.btFlags |= BT_SHIFT_LEFT; else btHIDCtrl.kbd.btFlags &= ~(BT_SHIFT_LEFT); + if(keyInfo.keys[0] & BT_ALT_LEFT) btHIDCtrl.kbd.btFlags |= BT_ALT_LEFT; else btHIDCtrl.kbd.btFlags &= ~BT_ALT_LEFT; + if(keyInfo.keys[0] & BT_GUI_LEFT) btHIDCtrl.kbd.btFlags |= BT_GUI_LEFT; else btHIDCtrl.kbd.btFlags &= ~BT_GUI_LEFT; + if(keyInfo.keys[0] & BT_CTRL_RIGHT) btHIDCtrl.kbd.btFlags |= BT_CTRL_RIGHT; else btHIDCtrl.kbd.btFlags &= ~(BT_CTRL_RIGHT); + if(keyInfo.keys[0] & BT_SHIFT_RIGHT) btHIDCtrl.kbd.btFlags |= BT_SHIFT_RIGHT; else btHIDCtrl.kbd.btFlags &= ~(BT_SHIFT_RIGHT); + if(keyInfo.keys[0] & BT_ALT_RIGHT) btHIDCtrl.kbd.btFlags |= BT_ALT_RIGHT; else btHIDCtrl.kbd.btFlags &= ~(BT_ALT_RIGHT); + if(keyInfo.keys[0] & BT_GUI_RIGHT) btHIDCtrl.kbd.btFlags |= BT_GUI_RIGHT; else btHIDCtrl.kbd.btFlags &= ~(BT_GUI_RIGHT); + + // Process the control(modifier) keys and generate events. + // CTRL keys + if((keyInfo.keys[0] & BT_CTRL_LEFT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_CTRL_LEFT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_CTRL | PS2_FUNCTION | PS2_KEY_L_CTRL; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_CTRL_LEFT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_CTRL_LEFT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_L_CTRL; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_CTRL_RIGHT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_CTRL_RIGHT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_CTRL | PS2_FUNCTION | PS2_KEY_R_CTRL; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_CTRL_RIGHT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_CTRL_RIGHT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_R_CTRL; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + // SHIFT Keys + if((keyInfo.keys[0] & BT_SHIFT_LEFT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_SHIFT_LEFT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_SHIFT | PS2_FUNCTION | PS2_KEY_L_SHIFT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_SHIFT_LEFT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_SHIFT_LEFT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_L_SHIFT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_SHIFT_RIGHT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_SHIFT_RIGHT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_SHIFT | PS2_FUNCTION | PS2_KEY_R_SHIFT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_SHIFT_RIGHT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_SHIFT_RIGHT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_R_SHIFT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + // ALT Keys + if((keyInfo.keys[0] & BT_ALT_LEFT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_ALT_LEFT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_ALT | PS2_FUNCTION | PS2_KEY_L_ALT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_ALT_LEFT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_ALT_LEFT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_L_ALT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_ALT_RIGHT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_ALT_RIGHT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_ALT_GR | PS2_FUNCTION | PS2_KEY_R_ALT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_ALT_RIGHT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_ALT_RIGHT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_R_ALT; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + // GUI Keys + if((keyInfo.keys[0] & BT_GUI_LEFT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_GUI_LEFT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_GUI | PS2_FUNCTION | PS2_KEY_L_GUI; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_GUI_LEFT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_GUI_LEFT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_L_GUI; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_GUI_RIGHT) != 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_GUI_RIGHT) == 0) + { + // First time key was pressed send a Make event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_GUI | PS2_FUNCTION | PS2_KEY_R_GUI; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + if((keyInfo.keys[0] & BT_GUI_RIGHT) == 0 && (btHIDCtrl.kbd.lastKeys[0] & BT_GUI_RIGHT) != 0) + { + // Key being released generates a BREAK event. + genKey = (btHIDCtrl.kbd.ps2Flags & 0xFF00) | PS2_BREAK | PS2_FUNCTION | PS2_KEY_R_GUI; + xQueueSend(btHIDCtrl.kbd.keyQueue, &genKey, 0); + } + + // Loop through the 6 scan codes and if a code appears in this set but not in the last generate a Make event. + // + for(int idx=1; idx < MAX_KEYBOARD_DATA_BYTES; idx++) + { + if(keyInfo.keys[idx] != 0) + { + bool found = false; + for(int idx2=1; idx2 < MAX_KEYBOARD_DATA_BYTES; idx2++) + { + if(keyInfo.keys[idx] == btHIDCtrl.kbd.lastKeys[idx2]) found = true; + } + if(!found) + { + // Process CAPS Lock. + if(keyInfo.keys[idx] == BT_KEY_CAPSLOCK && (btHIDCtrl.kbd.btFlags & BT_CAPS_LOCK) == 0) + { + btHIDCtrl.kbd.btFlags |= BT_CAPS_LOCK; + setStatusLED(keyInfo.hdlDev, BT_LED_CAPSLOCK); + } + else if(keyInfo.keys[idx] == BT_KEY_CAPSLOCK && (btHIDCtrl.kbd.btFlags & BT_CAPS_LOCK) != 0) + { + btHIDCtrl.kbd.btFlags &= ~(BT_CAPS_LOCK); + clearStatusLED(keyInfo.hdlDev, BT_LED_CAPSLOCK); + } + + // Process NUM Lock. + if(keyInfo.keys[idx] == BT_KEY_NUMLOCK && (btHIDCtrl.kbd.btFlags & BT_NUM_LOCK) == 0) + { + btHIDCtrl.kbd.btFlags |= BT_NUM_LOCK; + setStatusLED(keyInfo.hdlDev, BT_LED_NUMLOCK); + } + else if(keyInfo.keys[idx] == BT_KEY_NUMLOCK && (btHIDCtrl.kbd.btFlags & BT_NUM_LOCK) != 0) + { + btHIDCtrl.kbd.btFlags &= ~(BT_NUM_LOCK); + clearStatusLED(keyInfo.hdlDev, BT_LED_NUMLOCK); + } + + // Process SCROLL Lock. + if(keyInfo.keys[idx] == BT_KEY_SCROLLLOCK && (btHIDCtrl.kbd.btFlags & BT_SCROLL_LOCK) == 0) + { + btHIDCtrl.kbd.btFlags |= BT_SCROLL_LOCK; + setStatusLED(keyInfo.hdlDev, BT_LED_SCROLLLOCK); + } + else if(keyInfo.keys[idx] == BT_KEY_SCROLLLOCK && (btHIDCtrl.kbd.btFlags & BT_SCROLL_LOCK) != 0) + { + btHIDCtrl.kbd.btFlags &= ~(BT_SCROLL_LOCK); + clearStatusLED(keyInfo.hdlDev, BT_LED_SCROLLLOCK); + } + + // Mimicking the PS/2 class, set Function for certain mapped keys. + uint16_t mapKey = mapBTtoPS2(keyInfo.keys[idx]); + ESP_LOGI(TAG, "BTKEYMAP:%02x:%04x -> %04x", keyInfo.keys[idx], btHIDCtrl.kbd.btFlags, mapKey); + + // Do not forward certain keys. + if(mapKey != 0x0000 && keyInfo.keys[idx] != BT_KEY_NUMLOCK) + { + // Create a Make event. + xQueueSend(btHIDCtrl.kbd.keyQueue, &mapKey, 0); + } + } + } + // Now repeat in reverse, has a break event occurred? + if(btHIDCtrl.kbd.lastKeys[idx] != 0) + { + bool found = false; + for(int idx2=1; idx2 < MAX_KEYBOARD_DATA_BYTES; idx2++) + { + if(btHIDCtrl.kbd.lastKeys[idx] == keyInfo.keys[idx2]) found = true; + } + if(!found) + { + uint16_t mapKey = mapBTtoPS2(btHIDCtrl.kbd.lastKeys[idx]); + mapKey |= PS2_BREAK; // Send break event by adding PS2_BREAK control flag. + + // Do not forward certain keys. + if(mapKey != 0x0000 && btHIDCtrl.kbd.lastKeys[idx] != BT_KEY_NUMLOCK) + { + // Create a Break event. + xQueueSend(btHIDCtrl.kbd.keyQueue, &mapKey, 0); + } + } + } + } + } + } + // Media control keys, for some reason these come as a seperate BT report and are 24bits wide. + else + { + // Only process if size is correct. + if(keyInfo.length == MAX_CCONTROL_DATA_BYTES) + { + // Assemble 24bit map, easier to work with. + mediaKey = (keyInfo.keys[0] << 16) | (keyInfo.keys[1] << 8) | (keyInfo.keys[2]); + + // Check for key Make events. + for(int idx=0; idx < 23; idx++) + { + uint32_t mask = (1 << idx); + + // Make event. + if((mediaKey & mask) != 0 && (btHIDCtrl.kbd.lastMediaKey & mask) == 0) + { + uint16_t mapKey = mapBTMediaToPS2(mediaKey & mask); + xQueueSend(btHIDCtrl.kbd.keyQueue, &mapKey, 0); + + } + // Break event. + if((mediaKey & mask) == 0 && (btHIDCtrl.kbd.lastMediaKey & mask) != 0) + { + uint16_t mapKey = mapBTMediaToPS2(btHIDCtrl.kbd.lastMediaKey & mask); + xQueueSend(btHIDCtrl.kbd.keyQueue, &mapKey, 0); + } + } + + // Store last processed keymap for next loop. + btHIDCtrl.kbd.lastMediaKey = mediaKey; + } + } + + // Copy current to last. + for(int idx=0; idx < MAX_KEYBOARD_DATA_BYTES; idx++) + { + btHIDCtrl.kbd.lastKeys[idx] = keyInfo.keys[idx]; + } + } + + return; +} + +// Method to retrieve a key from the BT stack. The key is mapped from BT scancodes to PS/2 scancodes. +// +uint16_t BTHID::getKey(uint32_t timeout) +{ + // Locals. + // + uint16_t key; + bool result = false; + uint32_t timeCurrent = milliSeconds(); + + // Loop processing BT keys until a key received or timeout occurs. + do { + // Process latest BT keys. + processBTKeys(); + + // Get the next key from the processed queue and return to caller. + result = (xQueueReceive(btHIDCtrl.kbd.keyQueue, &key, 0) == pdTRUE ? true : false); + } while(timeout > 0 && timeCurrent+timeout > milliSeconds() && result == false); + + // Return key if one has been read else 0x00. + return(result == true ? key : 0x00); +} + +// Method to configure Bluetooth and register required callbacks. +bool BTHID::setup(t_pairingHandler *handler) +{ + // Locals. + // + bool result = false; + + // Check for multiple instantiations, only one instance allowed. + if(pBTHID != nullptr) + { + ESP_LOGE(TAG, "Setup called more than once. Only one instance of BTHID is allowed."); + } else + { + // Invoke the base class method which sets up the bluetooth layer. + BT::setup(handler); + + // Store current object for use in callback handlers. + pBTHID = this; + + // Create a FIFO queue to store incoming keyboard keys and mouse movements. + btHIDCtrl.kbd.rawKeyQueue = xQueueCreate(10, sizeof(KeyInfo)); + btHIDCtrl.kbd.keyQueue = xQueueCreate(10, sizeof(uint16_t)); + + ESP_ERROR_CHECK(esp_ble_gattc_register_callback(esp_hidh_gattc_event_handler)); + esp_hidh_config_t config = { + .callback = hidh_callback, + .event_stack_size = 4*1024, + .callback_arg = nullptr + }; + ESP_ERROR_CHECK(esp_hidh_init(&config)); + result = true; + + // Go through bonded lists and add to our control vector or known devices. + // First BLE devices. + int bleDevNum = esp_ble_get_bond_device_num(); + esp_ble_bond_dev_t *bleDevList = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * bleDevNum); + esp_ble_get_bond_device_list(&bleDevNum, bleDevList); + for (int idx = 0; idx < bleDevNum; idx++) + { + t_activeDev device; + memcpy(device.bda, bleDevList[idx].bd_addr, sizeof(esp_bd_addr_t)); + device.transport = ESP_HID_TRANSPORT_BLE; + device.addrType = BLE_ADDR_TYPE_RANDOM; + device.open = false; + device.nextCheckTime = milliSeconds() + 3000L; + btHIDCtrl.devices.push_back(device); + ESP_LOGW(TAG, "BLE BONDED DEVICE: " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bleDevList[idx].bd_addr)); + } + free(bleDevList); + + // Next BT devices. + int btDevNum = esp_bt_gap_get_bond_device_num(); + esp_bd_addr_t *btDevList = (esp_bd_addr_t *)malloc(sizeof(esp_bd_addr_t) * btDevNum); + esp_bt_gap_get_bond_device_list(&btDevNum, btDevList); + for (int idx = 0; idx < btDevNum; idx++) + { + t_activeDev device; + memcpy(device.bda, btDevList[idx], sizeof(esp_bd_addr_t)); + device.transport = ESP_HID_TRANSPORT_BT; + device.addrType = BLE_ADDR_TYPE_RANDOM; + device.open = false; + device.nextCheckTime = milliSeconds() + 3000L; + btHIDCtrl.devices.push_back(device); + ESP_LOGW(TAG, "BT BONDED DEVICE: " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(btDevList[idx])); + } + free(btDevList); + } + + // False = failed to setup, true = success. + return(result); +} + +// Basic constructor, do nothing! +BTHID::BTHID(void) +{ + btHIDCtrl.kbd.rawKeyQueue = NULL; + btHIDCtrl.kbd.keyQueue = NULL; + memset((void *)&btHIDCtrl.kbd.lastKeys, 0x00, 6); + btHIDCtrl.kbd.lastMediaKey = 0x00000000; + btHIDCtrl.kbd.ps2Flags = 0x0000; + btHIDCtrl.kbd.btFlags = 0x0000; + btHIDCtrl.kbd.statusLED = 0x00; + btHIDCtrl.kbd.kme = BTKeyToPS2.kme; + btHIDCtrl.kbd.kmeRows = MAX_BT2PS2_MAP_ENTRIES; + btHIDCtrl.kbd.kmeMedia = MediaKeyToPS2.kme; + btHIDCtrl.kbd.kmeMediaRows = MAX_BTMEDIA2PS2_MAP_ENTRIES; + btHIDCtrl.ms.mouseDataCallback = NULL; + btHIDCtrl.ms.resolution = 8; + btHIDCtrl.ms.scaling = 1; + btHIDCtrl.ms.sampleRate = 100; + btHIDCtrl.ms.xDivisor = 8; + btHIDCtrl.ms.yDivisor = 8; + + // btHIDCtrl.repeatPeriod = pdMS_TO_TICKS(120); +} + +// Basic destructor, do nothing! Only ever called for instantiation of uninitialsed class to prove version data.Used for probing versions etc. +BTHID::~BTHID(void) +{ + // +} diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt deleted file mode 120000 index a086ba4..0000000 --- a/main/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/CMakeLists.txt \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..94e811d --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS SharpKey.cpp NVS.cpp LED.cpp SWITCH.cpp KeyInterface.cpp MZ2528.cpp X1.cpp X68K.cpp Mouse.cpp MZ5665.cpp PC9801.cpp HID.cpp WiFi.cpp PS2KeyAdvanced.cpp PS2Mouse.cpp BT.cpp BTHID.cpp esp_efuse_custom_table.c) +set(COMPONENT_ADD_INCLUDEDIRS "." "include") + +register_component() diff --git a/main/HID.cpp b/main/HID.cpp deleted file mode 120000 index ba6daf2..0000000 --- a/main/HID.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/HID.cpp \ No newline at end of file diff --git a/main/HID.cpp b/main/HID.cpp new file mode 100644 index 0000000..ecb72a5 --- /dev/null +++ b/main/HID.cpp @@ -0,0 +1,1018 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: HID.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Final class for the encapsulation and presentation of differing input devices to +// an instantiating object for the provision of HID input services. This class +// provides a public API which a caller uses to receive keyboard and mouse data. +// No other HID devices are planned at this time but given Bluetooth is being used, +// the potential exists for other devices to be used. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to support Bluetooth keyboard and mouse. The mouse can be +// a primary device or a secondary device for hosts which support +// keyboard and mouse over one physical port. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_system.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "sdkconfig.h" +#include "HID.h" + +// Tag for ESP HID logging. +#define HIDTAG "HID" + +// Out of object pointer needed in the ESP API callbacks. +HID *pHIDThis = NULL; + +// Method to start Bluetooth pairing. +// The SharpKey doesnt have an output device so it is not possible to select a device for pairing or allow pairing key input. +// This limits us to mainly BLE devices and some BT devices which dont require a pairing key. +// The method used is to scan and select the first device found which is in pairing mode. It will be the users responsibility +// to ensure no other Bluetooth devices are close by and pairing. +// +void HID::btStartPairing(void) +{ + // Locals. + // + int scanCnt = 0; + std::vector scanList; + + // Only pair if bluetooth is enabled. + if(hidCtrl.hidDevice == HID_DEVICE_BT_KEYBOARD || hidCtrl.hidDevice == HID_DEVICE_BT_MOUSE || hidCtrl.hidDevice == HID_DEVICE_BLUETOOTH) + { + ESP_LOGW(HIDTAG, "Bluetooth Pairing Requested\n"); + + // Scan for a device in 5 second chunks, maximum 60 seconds before giving up. + do { + vTaskDelay(1); + btHID->getDeviceList(scanList, 5); // Required to discover new keyboards and for pairing + scanCnt++; + + // For debug purposes, print out any device found. + for(std::size_t idx = 0; idx < scanList.size(); idx++) + { + ESP_LOGI(HIDTAG, "We have device:%s, %s, %d, %s", scanList[idx].deviceAddr.c_str(), scanList[idx].name.c_str(), scanList[idx].rssi, scanList[idx].deviceType.c_str()); + } + // If devices were found, try and open them until success or end of list. + for(std::size_t idx = 0; idx < scanList.size(); idx++) + { + // Try and open the device, on failure move onto next device. + if(btHID->openDevice(scanList[idx].bda, scanList[idx].transport, scanList[idx].ble.addr_type) == true) + { + ESP_LOGI(HIDTAG, "BT enabled on device:%s, %s, %d, %s", scanList[idx].deviceAddr.c_str(), scanList[idx].name.c_str(), scanList[idx].rssi, scanList[idx].deviceType.c_str()); + } + } + } while(scanCnt < 11); + } else + { + ESP_LOGW(HIDTAG, "Bluetooth Pairing disabled\n"); + } + return; +} + +// Method to set the suspend flag. This is needed as input functionality may clash with WiFi, especially Bluetooth. +// +void HID::suspendInterface(bool suspendIf) +{ + this->suspend = suspendIf; +// WAIT FOR MUTEX? +} + +// Method to test to see if the interface has been suspended. +// Two modes, one just tests and returns the state, the second waits in a loop until the interface suspends. +// +bool HID::isSuspended(bool waitForSuspend) +{ + // If flag set, loop waiting for the suspended flag to be set. + while(waitForSuspend == true && this->suspended == false) + { + vTaskDelay(1); + } + + // Return the suspended status. + return(this->suspended); +} + +// Method to read data from the underlying input device (keyboard). +// +uint16_t HID::read(void) +{ + // Locals. + // + uint16_t result = 0; + + // Ensure we have exclusive access before reading the input device. + if(hidCtrl.mutexInternal != NULL) + { + // Take controol of the semaphore to block all actions whilst reading key data. Other processes such as keyboard check and validation may + // require access hence waiting for exclusive access. + if(xSemaphoreTake(hidCtrl.mutexInternal, (TickType_t)100) == pdTRUE) + { + // Call the device method according to type. + // + switch(hidCtrl.hidDevice) + { + case HID_DEVICE_PS2_KEYBOARD: + // Get a 16bit code from the keyboard: [15:8] = Control bits, [7:0] = Data bits. + if((result = ps2Keyboard->read()) != 0) + { + hidCtrl.ps2CheckTimer = xTaskGetTickCount(); + } + break; + + case HID_DEVICE_BLUETOOTH: + case HID_DEVICE_BT_KEYBOARD: + // Get a 16bit code from the keyboard: [15:8] = Control bits, [7:0] = Data bits. + if((result = btHID->getKey(0)) != 0) + { + hidCtrl.ps2CheckTimer = xTaskGetTickCount(); + } + break; + + // Mouse processing is different, based on callbacks. + case HID_DEVICE_TYPE_MOUSE: + break; + + default: + break; + } + + // Release mutex, internal or external methods can now access the HID devices. + xSemaphoreGive(hidCtrl.mutexInternal); + } + } + + // Return 0 if no data available otherwise the key or mouse code. + return(result); +} + +// Method to allow update of the mouse resolution. The config is updated and the device configured but the change is not persisted. +// +void HID::setMouseResolution(enum HID_MOUSE_RESOLUTION resolution) +{ + // Update the resolution in the config. + hidConfig.mouse.resolution = resolution; + + // Set the updated flag to trigger an update. + hidCtrl.updated = false; +} + +// Method to allow update of the host side scaling. The config is updated and is actioned realtime. The change is not persisted. +// +void HID::setMouseHostScaling(enum HID_MOUSE_HOST_SCALING scaling) +{ + // Update the mouse scaling in the config. + hidConfig.host.scaling = scaling; +} + +// Method to allow update of the mouse scaling. The config is updated and the device configured but the change is not persisted. +// +void HID::setMouseScaling(enum HID_MOUSE_SCALING scaling) +{ + // Update the mouse scaling in the config. + hidConfig.mouse.scaling = scaling; + + // Set the updated flag to trigger an update. + hidCtrl.updated = false; +} + +// Method to allow update of the mouse sample rate. The config is updated and the device configured but the change is not persisted. +// +void HID::setMouseSampleRate(enum HID_MOUSE_SAMPLING sampleRate) +{ + // Update the mouse sample rate in the config. + hidConfig.mouse.sampleRate = sampleRate; + + // Set the updated flag to trigger an update. + hidCtrl.updated = false; +} + +// Method to detect if the PS2 Mouse is connected and/or re-initialise it. +// This method is called on startup to detect if a PS2 device is connected, if it isnt then Bluetooth is started. +// If a PS2 mouse is detected on startup then this method is called periodically to check for it being unplugged and reconnected +// initialising it as required. +// Returns: true - mouse connected, false - not detected. +// +bool HID::checkPS2Mouse(void) +{ + // Locals. + // + bool result = false; + + // Ask the mouse for it's ID. No mouse connected or error = 0xFF. + result = ps2Mouse->getDeviceId() == 0xFF ? false : true; + + // Return current status. + return(result); +} + +// Method to check the mouse, if it goes offline then perform reset and configuration once online. Also allow third wheel configuration +// of mouse parameters. +void HID::processPS2Mouse( void ) +{ + // Locals. + + // PS/2 mouse check involves periodically requesting the device Id. If no device Id is returned, then reset the mouse until it responds and re-initialise. + // + // Ensure we have exclusive access before checking mouse. + if(xSemaphoreTake(hidCtrl.mutexInternal, (TickType_t)10) == pdTRUE) + { + // If this mouse has gone offline (ie. unplugged), keep sending the RESET command until it becomes available. + if(hidCtrl.active == false) + { + // Issue a reset, if we dont get an acknowledgement back then the mouse is offline. + if(ps2Mouse->reset() == false) + { + vTaskDelay(100); + } else + { + hidCtrl.active = true; // Set active. + hidCtrl.updated = true; // Configure the mouse with latest settings. + + // As the mouse has been reset, update the Intelli Mouse configuration as a different mouse could have been plugged in. + ps2Mouse->checkIntelliMouseExtensions(); + + // Mouse is online now so reset the check counter. + hidCtrl.noValidMouseMessage = 0; + } + } else + { + // If the mouse configuration has changed, send the updated values. + if(hidCtrl.updated) + { + hidCtrl.updated = false; + ps2Mouse->setResolution((enum PS2Mouse::PS2_RESOLUTION)hidConfig.mouse.resolution); + ps2Mouse->setScaling((enum PS2Mouse::PS2_SCALING)hidConfig.mouse.scaling); + ps2Mouse->setSampleRate((enum PS2Mouse::PS2_SAMPLING)hidConfig.mouse.sampleRate); + } + + // Read mouse data. This triggers a callback if data is available. + ps2Mouse->readData(); + + // Keep a count of the number of times no valid message is received, reset when a valid message is received. This is used to determine if the mouse has gone + // offline. If the counter goes above a threshold then request the mouse ID, if it is not sent, mouse is offline. + if(hidCtrl.noValidMouseMessage++ > MAX_MOUSE_INACTIVITY_TIME) + { + // Check to see if the mouse is online. + if(checkPS2Mouse()) + { + // Mouse is online just not being used. + hidCtrl.noValidMouseMessage = 0; + } else + { + hidCtrl.active = false; + } + } + } + + // Release mutex, external access now possible to the input devices. + xSemaphoreGive(hidCtrl.mutexInternal); + } + + // Done! + return; +} + +// Callback to process mouse data originating from a PS/2 or Bluetooth mouse. The data is encapsulated in a PS/2 +// message and processed into a host message. +// +void HID::mouseReceiveData(uint8_t src, PS2Mouse::MouseData mouseData) +{ + // Locals. + // + uint32_t loopTime = (milliSeconds() - hidCtrl.loopTimer)/1000; + t_mouseMessageElement mouseMsg; + + ESP_LOGD(HIDTAG, "Valid:%d, Overrun:%d, Status:%d, X:%d, Y:%d, Wheel:%d", mouseData.valid, mouseData.overrun, mouseData.status, mouseData.position.x, mouseData.position.y, mouseData.wheel); + + // Check the loop timer and set the blink rate according to the mode which is determined by the range of the loop timer. + if((hidCtrl.mouseData.status & 0x04) == 0 && hidCtrl.middleKeyPressed == true && hidCtrl.configMode == HOST_CONFIG_OFF) + { + // Do nothing, time exceeded to configuration cancelled. + if(loopTime >= 4 * hidConfig.params.optionAdvanceDelay) + { + led->setLEDMode(LED::LED_MODE_ON, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } else + // Approx 2 times the delay setting stored in the config. + if(loopTime >= 2 * hidConfig.params.optionAdvanceDelay) + { + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_30, (uint32_t)(hidConfig.mouse.resolution)+1, 250000L, 1000L); + } else + // First configuration to be selected in the first optionAdvanceDelay/HID_MOUSE_DATA_POLL_DELAY seconds. + if(loopTime >= 1) + { + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_20, (uint32_t)(hidConfig.host.scaling)+1, 150000L, 1000L); + } + } + + // Copy data into control structure as it is needed by the update process above. + memcpy((void *)&hidCtrl.mouseData, (void *)&mouseData, sizeof(PS2Mouse::MouseData)); + + // Process data if valid - normally the case on a callback but could be an overrun occurred invalidating the data. + if(hidCtrl.mouseData.valid) + { + // If configuration mode is enabled then the wheel value is used to increment/decrement an option value. + // + int16_t wheel = -(((hidCtrl.mouseData.wheel & 0x80) ? 0xFF80 : 0x0000) | (hidCtrl.mouseData.wheel & 0x7F)); + if(hidCtrl.configMode != HOST_CONFIG_OFF) + { + hidCtrl.wheelCnt += wheel; + if(hidCtrl.wheelCnt > 4) + { + if(hidCtrl.configMode == HOST_CONFIG_SCALING) + { + hidConfig.host.scaling = static_cast(static_cast(hidConfig.host.scaling) + 1); + } else + if(hidCtrl.configMode == HOST_CONFIG_RESOLUTION) + { + hidConfig.mouse.resolution = static_cast(static_cast(hidConfig.mouse.resolution) + 1); + } + } + if(hidCtrl.wheelCnt < -4) + { + if(hidCtrl.configMode == HOST_CONFIG_SCALING) + { + hidConfig.host.scaling = static_cast(static_cast(hidConfig.host.scaling) - 1); + } else + if(hidCtrl.configMode == HOST_CONFIG_RESOLUTION) + { + hidConfig.mouse.resolution = static_cast(static_cast(hidConfig.mouse.resolution) - 1); + } + } + if(hidCtrl.wheelCnt < -4 || hidCtrl.wheelCnt > 4) + { + if(hidCtrl.configMode == HOST_CONFIG_SCALING) + { + if(hidConfig.host.scaling > HID::HID_MOUSE_HOST_SCALING_1_5) hidConfig.host.scaling = HID::HID_MOUSE_HOST_SCALING_1_5; + if(hidConfig.host.scaling < HID::HID_MOUSE_HOST_SCALING_1_1) hidConfig.host.scaling = HID::HID_MOUSE_HOST_SCALING_1_1; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_20, static_cast(hidConfig.host.scaling)+1, 150000L, 1000L); + } else + if(hidCtrl.configMode == HOST_CONFIG_RESOLUTION) + { + if(hidConfig.mouse.resolution > HID::HID_MOUSE_RESOLUTION_1_8) hidConfig.mouse.resolution = HID::HID_MOUSE_RESOLUTION_1_8; + if(hidConfig.mouse.resolution < HID::HID_MOUSE_RESOLUTION_1_1) hidConfig.mouse.resolution = HID::HID_MOUSE_RESOLUTION_1_1; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_30, static_cast(hidConfig.mouse.resolution)+1, 250000L, 1000L); + hidCtrl.updated = true; + } + hidCtrl.wheelCnt = 0; + } + } + + // If the middle key has been pressed, reset the timer and set the flag. + if((hidCtrl.mouseData.status & 0x04) && hidCtrl.middleKeyPressed == false) + { + hidCtrl.loopTimer = milliSeconds(); + hidCtrl.middleKeyPressed = true; + led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } + // When the key has been released the timer can be used to decide on function required. + if((hidCtrl.mouseData.status & 0x04) == 0 && hidCtrl.middleKeyPressed == true && loopTime >= 1) + { + // If the middle button is set we start configuration mode. This entails the wheel value being used to select the scaling required and the LED blink rate indicates + // the mode to the user. When the middle button is clicked a second time the configuration is disabled. + if(hidCtrl.configMode == HOST_CONFIG_OFF) + { + if(loopTime >= 1 && loopTime < 2 * hidConfig.params.optionAdvanceDelay) + { + hidCtrl.configMode = HOST_CONFIG_SCALING; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_20, (uint32_t)(hidConfig.host.scaling)+1, 150000L, 1000L); + } else + if(loopTime >= 2 * hidConfig.params.optionAdvanceDelay && loopTime < 4 * hidConfig.params.optionAdvanceDelay) + { + hidCtrl.configMode = HOST_CONFIG_RESOLUTION; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_30, (uint32_t)(hidConfig.mouse.resolution)+1, 250000L, 1000L); + } else + // If the button is held too long, do nothing, configuration mode cancelled. + if(loopTime >= 4 * hidConfig.params.optionAdvanceDelay) + { + } + } else + if(hidCtrl.configMode != HOST_CONFIG_OFF) + { + hidCtrl.configMode = HOST_CONFIG_OFF; + + // Persist the changes. + persistConfig(); + + // Turn off LED as we have exitted configuration mode. + led->setLEDMode(LED::LED_MODE_ON, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } + hidCtrl.loopTimer = milliSeconds(); + hidCtrl.middleKeyPressed = false; + } + + // Build the next message with all data, scaled and filtered as necessary. + // Firstly, for PS/2 extend the X,Y 9bit movement values into 16bit for easier manipulation. + if(src == 0) + { + mouseMsg.xPos = (((hidCtrl.mouseData.status & 0x10) ? 0xFF00 : 0x0000) | hidCtrl.mouseData.position.x); + mouseMsg.yPos = (((hidCtrl.mouseData.status & 0x20) ? 0xFF00 : 0x0000) | hidCtrl.mouseData.position.y); + } else + { + mouseMsg.xPos = hidCtrl.mouseData.position.x / 16; + mouseMsg.yPos = hidCtrl.mouseData.position.y / 16; + } + + switch(hidConfig.mouse.resolution) + { + case HID_MOUSE_RESOLUTION_1_1: + mouseMsg.xPos = mouseMsg.xPos / 8; + mouseMsg.yPos = mouseMsg.yPos / 8; + break; + + case HID_MOUSE_RESOLUTION_1_2: + mouseMsg.xPos = mouseMsg.xPos / 4; + mouseMsg.yPos = mouseMsg.yPos / 4; + break; + + case HID_MOUSE_RESOLUTION_1_4: + mouseMsg.xPos = mouseMsg.xPos / 2; + mouseMsg.yPos = mouseMsg.yPos / 2; + break; + + case HID_MOUSE_RESOLUTION_1_8: + default: + mouseMsg.xPos = mouseMsg.xPos / 1; + mouseMsg.yPos = mouseMsg.yPos / 1; + break; + } + + // Perform any in-situ scaling and adjustments. + switch(hidConfig.host.scaling) + { + case HID::HID_MOUSE_HOST_SCALING_1_2: + mouseMsg.xPos = mouseMsg.xPos / 2; + mouseMsg.yPos = mouseMsg.yPos / 2; + break; + + case HID::HID_MOUSE_HOST_SCALING_1_3: + mouseMsg.xPos = mouseMsg.xPos / 3; + mouseMsg.yPos = mouseMsg.yPos / 3; + break; + + case HID::HID_MOUSE_HOST_SCALING_1_4: + mouseMsg.xPos = mouseMsg.xPos / 4; + mouseMsg.yPos = mouseMsg.yPos / 4; + break; + + case HID::HID_MOUSE_HOST_SCALING_1_5: + mouseMsg.xPos = mouseMsg.xPos / 5; + mouseMsg.yPos = mouseMsg.yPos / 5; + break; + + // No scaling needed for 1:1, the data is clipped at 8bit 2's compliment threshold and overflow/underflow set accordingly. + case HID::HID_MOUSE_HOST_SCALING_1_1: + default: + break; + } + + // Add in status and wheel data to complete message. + // + mouseMsg.status = hidCtrl.mouseData.status; + mouseMsg.wheel = hidCtrl.mouseData.wheel; + + // If a data callback has been setup, invoke otherwise data is wasted. + // + if(hidCtrl.dataCallback != NULL) + hidCtrl.dataCallback(mouseMsg); + + // Reset the mouse activity check counter. + hidCtrl.noValidMouseMessage = 0; + } +} + +// Method to check and process the Bluetooth mouse which operates slightly differently to the PS/2 Mouse. +// Data arriving over a BT connection is queued and we read and process it, invoking the application callback for sending the mouse data +// to the host. +// The Bluetooth HAL is responsible for maintaining a connection and if it goes offline, it will be closed. We detect this and invoke an open +// until it comes back online. +// +void HID::checkBTMouse( void ) +{ + // Locals. + + // One common function for BT. The protocol manages checks and reconnections but should a device go out of range we initiate + // a new scan and connect. + btHID->checkBTDevices(); + + // If the mouse configuration has changed, send the updated values to the BTHID. + if(hidCtrl.updated) + { + hidCtrl.updated = false; + btHID->setResolution((enum PS2Mouse::PS2_RESOLUTION)hidConfig.mouse.resolution); + btHID->setScaling((enum PS2Mouse::PS2_SCALING)hidConfig.mouse.scaling); + btHID->setSampleRate((enum PS2Mouse::PS2_SAMPLING)hidConfig.mouse.sampleRate); + } + + // Done! + return; +} + +// Method to detect if the PS2 Keyboard is connected and/or re-initialise it. +// This method is called on startup to detect if a PS2 device is connected if it isnt then Bluetooth is started. +// If a PS2 keyboard is detected on startup then this method is called periodically to check for it being unplugged and reconnected +// initialising it as required. +// Returns: true - keyboard connected, false - not detected. +// +bool HID::checkPS2Keyboard(void) +{ + // Locals. + // + uint16_t scanCode = 0x0000; + + // Check to see if the keyboard is still available, no keyboard = no point!! + // Firstly, ping keyboard to see if it is there. + ps2Keyboard->echo(); + vTaskDelay(6); + scanCode = ps2Keyboard->read(); + + // If the keyboard doesnt answer back, then it has been disconnected. + if( (scanCode & 0xFF) != PS2_KEY_ECHO && (scanCode & 0xFF) != PS2_KEY_BAT) + { + hidCtrl.noEchoCount++; + + // Re-initialise the subsystem, if the keyboard is plugged in then it will be detected on next loop. + if(hidCtrl.noEchoCount > 5) ps2Keyboard->begin(CONFIG_PS2_HW_DATAPIN, CONFIG_PS2_HW_CLKPIN); + + // First entry print out message that the keyboard has disconnected. + if(hidCtrl.noEchoCount == 10 && (hidCtrl.ps2Active == 1 || hidCtrl.ps2CheckTimer == 0)) + { + // Turn on LED when keyboard is detached. + led->setLEDMode(LED::LED_MODE_ON, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + + ESP_LOGE(HIDTAG, "No PS2 keyboard detected, please connect."); + } + hidCtrl.ps2Active = 0; + + hidCtrl.ps2CheckTimer = xTaskGetTickCount(); // Check every second when offline. + } else + { + // First entry after keyboard starts responding, print out message. + if(hidCtrl.ps2Active == 0) + { + ESP_LOGW(HIDTAG, "PS2 keyboard detected and online."); + hidCtrl.ps2Active = 1; + + // If indication was given that the keyboard has gone offline, issue a new message to show it is back online. + // This coding is necessary due to KVM devices which can idle the PS/2 connection randomly or when another device such as the mouse is in use. + if(hidCtrl.noEchoCount > 10) + { + ESP_LOGW(HIDTAG, "PS2 keyboard detected and online."); + + // Flash LED to indicate Keyboard recognised. + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_50, 5, 100000L, 0L); + } + } + hidCtrl.noEchoCount = 0L; + hidCtrl.ps2CheckTimer = xTaskGetTickCount(); + } + + // Return current status. + return(hidCtrl.ps2Active); +} + +// Method to verify keyboard connectivity. If the keyboard goes offline, once detected, it is re-initialised. +// +void HID::checkKeyboard( void ) +{ + // Locals. + + switch(hidCtrl.hidDevice) + { + case HID_DEVICE_PS2_KEYBOARD: + // PS/2 keyboard checks involve sending an echo and reading back the response. If no response is received then the keyboard is unplugged, once an echo returns then + // re-initialise the keyboard so it continues to function. + // + // Check the keyboard is online, this is done at startup and periodically to cater for user disconnect. + if((xTaskGetTickCount() - hidCtrl.ps2CheckTimer) > 1000 && (ps2Keyboard->keyAvailable() == 0 || hidCtrl.ps2Active == 0)) + { + // Ensure we have exclusive access before checking keyboard. + if(xSemaphoreTake(hidCtrl.mutexInternal, (TickType_t)10) == pdTRUE) + { + // Check if the PS/2 keyboard is available. + checkPS2Keyboard(); + + // Release mutex, external access now possible to the input devices. + xSemaphoreGive(hidCtrl.mutexInternal); + } + } + break; + + case HID_DEVICE_BLUETOOTH: + case HID_DEVICE_BT_KEYBOARD: + // Bluetooth checks involve reconnection on closed handles, ie. when keyboard goes out of range or is switched off, keep retrying to connect. + if(hidCtrl.hidDevice == HID_DEVICE_BT_KEYBOARD) + { + btHID->checkBTDevices(); + } + break; + + default: + break; + } + + // Done. + return; +} + +// Method to verify mouse connectivity and process any pending updates/changes. +// +void HID::checkMouse(void) +{ + // Locals. + + switch(hidCtrl.hidDevice) + { + case HID_DEVICE_PS2_MOUSE: + // Process any data and check PS/2 mouse status. + processPS2Mouse(); + break; + + case HID_DEVICE_BLUETOOTH: + case HID_DEVICE_BT_MOUSE: + // Process any pending setting updates and rescan for new device if current device goes out of range. + checkBTMouse(); + break; + + default: + break; + } + + // Done. + return; +} + +// Method to manage and maintain input device connectivity. +// +IRAM_ATTR void HID::hidControl( void * pvParameters ) +{ + // Locals. + // + int checkCnt = 0; + #define HIDCTRLTAG "hidControl" + + // Map the instantiating object so we can access its methods and data. + HID* pThis = (HID*)pvParameters; + + // Infinite loop, performing maintenance and control checks. + while(true) + { + // Run through the checks, first keyboard. Assumes PS/2 keyboard or singular Bluetooth keyboard. + if(pThis->hidCtrl.deviceType == HID_DEVICE_TYPE_KEYBOARD) + { + // Check keyboard. + pThis->checkKeyboard(); + + // Relinquish CPU for other tasks. + vTaskDelay(100); + } + // Scan mouse if enabled. Assumes PS/2 mouse or singular Bluetooth mouse. + else if(pThis->hidCtrl.deviceType == HID_DEVICE_TYPE_MOUSE) + { + // Check mouse. + pThis->checkMouse(); + + // Yield if the suspend flag is set. + vTaskDelay(HID_MOUSE_DATA_POLL_DELAY); + } + // Scan bluetooth mouse and keyboard as one or both can be active. + else if(pThis->hidCtrl.deviceType == HID_DEVICE_TYPE_BLUETOOTH) + { + // Check keyboard. Mouse needs more frequent checking so we base delay on mouse period. + if(checkCnt-- == 0) + { + pThis->checkKeyboard(); + checkCnt = 100/HID_MOUSE_DATA_POLL_DELAY; + } + + // Check mouse. + pThis->checkMouse(); + + // Yield if the suspend flag is set. + vTaskDelay(HID_MOUSE_DATA_POLL_DELAY); + } + + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(HIDCTRLTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + } + return; +} + +// Testing pairing method. The security has been disabled so this method shouldnt be called. +// +void HID::btPairingHandler(uint32_t pid, uint8_t trigger) +{ + // Trigger indicates which part of the BT/BLE stack triggered the password request. + // BT: 1 + // BT AUTH: 2 + // BLE: 3 + // BLE shouldnt require a pin as the stack has been setup to authorise without pin, BT may require a PIN and so a BT AUTH callback will be made. + // BT AUTH pid = status, if 0 then successful connection, no pin, if 9 then FAIL, so raise an alert via the LED that a PIN is required for this device. + switch(trigger) + { + case 1: + std::cout << "Please enter the following pairing code, " + << std::endl + << "followed with ENTER on your keyboard: " + << pid + << std::endl; + ESP_LOGE(HIDTAG, "Password request for BT pairing device, normally this should be AUTH, please log details."); + break; + case 2: + if(pid == 0) + { + pHIDThis->led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } + else if(pid == 9) + { + pHIDThis->led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_80, 3, 250000L, 1000L); + } + break; + case 3: + default: + ESP_LOGE(HIDTAG, "Password request for pairing device. Auth disabled so this shouldnt occur, please log details."); + break; + } +} + +// Method to see if the enabled underlying HID device is Bluetooth. +// +bool HID::isBluetooth(void) +{ + return(hidCtrl.hidDevice == HID_DEVICE_BT_KEYBOARD || hidCtrl.hidDevice == HID_DEVICE_BT_MOUSE || hidCtrl.hidDevice == HID_DEVICE_BLUETOOTH); +} + +// Method to re-initialise the bluetooth subsystem after being disabled. +// At the moment this is a stub because WiFi is used for configuration and once complete a reboot takes place. +void HID::enableBluetooth(void) +{ + if(isBluetooth()) + { + } + return; +} + +// Method to disable the bluetooth subsystem. This is necessary if WiFi is required as the two wireless devices share the same +// antenna and dont coexist very well. +void HID::disableBluetooth(void) +{ + if(isBluetooth()) + { + // Disable and de-initialse BT and BLE to free up the antenna. + // + esp_bluedroid_disable(); + esp_bluedroid_deinit(); + esp_bt_controller_disable(); + esp_bt_controller_deinit(); + } + return; +} + +// Method to persist the current configuration into NVS storage. +// +bool HID::persistConfig(void) +{ + // Locals + bool result = true;; + + // Update persistence with changed data. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &hidConfig, sizeof(t_hidConfig)) == false) + { + ESP_LOGW(HIDTAG, "Persisting Mouse configuration data failed, updates will not persist in future power cycles."); + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + result = false; + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(nvs->commitData() == false) + { + ESP_LOGW(HIDTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + result = false; + } + + // Return result, true = success. + return(result); +} + +// Base initialisation for generic HID hardware used. +void HID::init(const char *className, enum HID_DEVICE_TYPES deviceType) +{ + // Locals + #define INITTAG "init" + + // Initialise variables. + hidCtrl.mutexInternal = NULL; + hidCtrl.dataCallback = NULL; + hidCtrl.configMode = HOST_CONFIG_OFF; + hidCtrl.loopTimer = milliSeconds(); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(className, &this->hidConfig, sizeof(t_hidConfig)) == false) + { + ESP_LOGW(INITTAG, "HID configuration set to default, no valid config in NVS found."); + hidConfig.mouse.resolution = HID_MOUSE_RESOLUTION_1_8; + hidConfig.mouse.scaling = HID_MOUSE_SCALING_1_1; + hidConfig.mouse.sampleRate = HID_MOUSE_SAMPLE_RATE_60; + hidConfig.host.scaling = HID_MOUSE_HOST_SCALING_1_1; + hidConfig.params.optionAdvanceDelay = 1; + + // Persist the data for next time. + if(nvs->persistData(className, &this->hidConfig, sizeof(t_hidConfig)) == false) + { + ESP_LOGW(INITTAG, "Persisting Default HID configuration data failed, check NVS setup.\n"); + } + // Commit data, ensuring values are written to NVS and the mutex is released. + else if(nvs->commitData() == false) + { + ESP_LOGW(INITTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } + + // Store the class name for later use, ie. NVS key access. + this->className = className; + + // Initially I wanted PS/2 and Bluetooth to work in tandem. They do, sort of, but Bluetooth heavy resource usage and high priority + // interferes with the PS/2 interrupt latency and consequently the PS/2 data can become corrupt. Also the Bluetooth stack is not that + // stable especially with BLE (Logitech K780 keyboard when it connects will sometimes hang, if it disconnects it may hang reconnecting). Too many issues + // so decided to seperate the logic, if a PS/2 device cannot be seen on startup it is disabled and bluetooth enabled. + // This setup is for the primary device expected by the host. ie. If the host is detected as an X68000 then logically it needs a keyboard which is true for PS/2 + // as you can only have one PS/2 device connected at a time. For Bluetooth though, it is possible to have a keyboard and mouse connected and the X68000 interface + // allows for both over one port, so if the host is detected as an X68000 it will check for a PS/2 keyboard, if not found it will enable Bluetooth and then the + // logic can connect both a mouse and keyboard and channel them to the X68000. + switch(deviceType) + { + case HID_DEVICE_TYPE_KEYBOARD: + { + // Instantiate the PS/2 Keyboard object and initialise. + ESP_LOGW(INITTAG, "Initialise PS2 keyboard."); + ps2Keyboard = new PS2KeyAdvanced(); + ps2Keyboard->begin(CONFIG_PS2_HW_DATAPIN, CONFIG_PS2_HW_CLKPIN); + + // If no PS/2 keyboard detected then default to Bluetooth. + if(checkPS2Keyboard() == false) + { + // Remove the PS/2 keyboard object, free up memory and disable the interrupts. + ESP_LOGW(INITTAG, "PS2 keyboard not available."); + delete ps2Keyboard; + hidCtrl.hidDevice = HID_DEVICE_BT_KEYBOARD; + + // Instantiate Bluetooth HID object. + ESP_LOGW(INITTAG, "Initialise Bluetooth keyboard."); + btHID = new BTHID(); + btHID->setup(btPairingHandler); + sw->setBTPairingEventCallback(&HID::btStartPairing, this); + + // Setup a mouse callback as it is possible to receive mouse data when the primary input method is a keyboard. This data can be used by a registered + // mouse interface to provide dual services to a host. + btHID->setMouseDataCallback(&HID::mouseReceiveData, this); + + hidCtrl.deviceType = HID_DEVICE_TYPE_BLUETOOTH; + hidCtrl.hidDevice = HID_DEVICE_BLUETOOTH; + } else + { + hidCtrl.deviceType = HID_DEVICE_TYPE_KEYBOARD; + hidCtrl.hidDevice = HID_DEVICE_PS2_KEYBOARD; + } + break; + } + + case HID_DEVICE_TYPE_MOUSE: + { + // Instantiate the PS/2 Keyboard object and initialise. + ESP_LOGW(INITTAG, "Initialise PS2 Mouse."); + ps2Mouse = new PS2Mouse(CONFIG_PS2_HW_CLKPIN, CONFIG_PS2_HW_DATAPIN); + ps2Mouse->initialize(); + + // Test to see if a PS/2 Mouse is connected. IF it isnt, delete the PS/2 Mouse object and initiate a Bluetooth object. + if(checkPS2Mouse() == false) + { + ESP_LOGW(INITTAG, "PS2 Mouse not available."); + delete ps2Mouse; + hidCtrl.deviceType = HID_DEVICE_TYPE_BLUETOOTH; + hidCtrl.hidDevice = HID_DEVICE_BT_MOUSE; + + // Instantiate Bluetooth HID object. + ESP_LOGW(INITTAG, "Initialise Bluetooth mouse."); + btHID = new BTHID(); + btHID->setup(btPairingHandler); + btHID->setMouseDataCallback(&HID::mouseReceiveData, this); + sw->setBTPairingEventCallback(&HID::btStartPairing, this); + } else + { + hidCtrl.deviceType = HID_DEVICE_TYPE_MOUSE; + hidCtrl.hidDevice = HID_DEVICE_PS2_MOUSE; + + // Set the mouse to streaming mode so all movements generate data. + ps2Mouse->setMouseDataCallback(&HID::mouseReceiveData, this); + ps2Mouse->setStreamMode(); + ps2Mouse->enableStreaming(); + hidCtrl.hidDevice = HID_DEVICE_PS2_MOUSE; + } + break; + } + + default: + break; + } + + // Setup mutex's. + hidCtrl.mutexInternal = xSemaphoreCreateMutex(); + xSemaphoreGive(hidCtrl.mutexInternal); + + // Core 0 - Application + // HID control thread. + ESP_LOGW(HIDTAG, "Starting HID thread..."); + ::xTaskCreatePinnedToCore(&this->hidControl, "HID", 4096, this, 0, &this->TaskHID, 0); + + // All done, no return code! + return; +} + +// Constructor, Initialise interface. +HID::HID(enum HID_DEVICE_TYPES deviceType, NVS *hdlNVS, LED *hdlLED, SWITCH *hdlSWITCH) +{ + // Check for multiple instantiations, only one instance allowed. + if(pHIDThis != nullptr) + { + // If the constructor to create an object with the underlying hardware is called more than once, flag it and set to a basic object as only one object can access + // hardware at a time. + ESP_LOGE(HIDTAG, "Constructor called more than once. Only one instance of HID with hardware allowed."); + this->nvs = hdlNVS; + return; + } + + // Store current object, used in ESP API callbacks (C based). + pHIDThis = this; + + // Save the NVS object so we can persist and retrieve config data. + this->nvs = hdlNVS; + + // Save the LED object so it can be used to warn the user. + this->led = hdlLED; + + // Save the SWITCH object so it can be used to enable Bluetooth pairing. + this->sw = hdlSWITCH; + + init(getClassName(__PRETTY_FUNCTION__), deviceType); +} + +// Basic constructor, no input device defined, just NVS for config retrieval and persistence. +HID::HID(NVS *hdlNVS) +{ + // Save the NVS object so we can persist and retrieve config data. + this->nvs = hdlNVS; +} + +// Basic constructor, do nothing! Used for probing versions etc. +HID::HID(void) +{ + // +} + +// Basic destructor, do nothing! Only ever called for instantiation of uninitialsed class to prove version data.Used for probing versions etc. +HID::~HID(void) +{ + // Release object pointer if set. + if(pHIDThis == this) + { + pHIDThis = NULL; + } +} diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild deleted file mode 120000 index 54ade14..0000000 --- a/main/Kconfig.projbuild +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/Kconfig.projbuild \ No newline at end of file diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..14cbad4 --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,309 @@ +menu "SharpKey Configuration" + + choice BUILD_TARGET + prompt "Build target" + default SHARPKEY + help + Choose the target of the next build, SHARPKEY for the multi-host SharpKey Interface, MZ25KEY_MZ2500 for the mz25key Interface which will be used + with an MZ-2500 or MZ25KEY_MZ2800 for the mz25key Interface which will be used with an MZ-2800. + + config SHARPKEY + bool "SharpKey" + help + Target build for the SharpKey multi-host Interface. + + config MZ25KEY_MZ2500 + bool "mz25key - MZ2500" + help + Target build for the mz25key Interface for use on an MZ-2500 + + config MZ25KEY_MZ2800 + bool "mz25key - MZ2800" + help + Target build for the mz25key Interface for use on an MZ-2800 + endchoice + + choice FEATURE_SECURITY + prompt "Runtime Feature Security" + default DISABLE_FEATURE_SECURITY + help + Choose wether to enable features in the firmware based on fuse settings or to disable the feature. + + config DISABLE_FEATURE_SECURITY + bool "Disable feature security" + help + Disable the feature security, enabling all inbuilt modules of the SharpKey firmware. + + config ENABLE_FEATURE_SECURITY + bool "Enable feature security" + help + Enable feature security, modules will only be available if the corresponding eFuse is set. + endchoice + + menu "PS2 Keyboard" + + config PS2_HW_DATAPIN + int "GPIO pin number used for the PS/2 Keyboard DATA line" + range 0 46 + default 14 + help + GPIO number (IOxx) used to connect with the PS/2 Keyboard DATA line. + Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to I2C. + GPIOs 35-39 are input-only so cannot be used as outputs. + + config PS2_HW_CLKPIN + int "GPIO pin number used for the PS/2 Keyboard CLK line" + range 0 46 + default 13 + help + GPIO number (IOxx) used to connect with the PS/2 Keyboard CLK line. + This pin must be interrupt capable. + Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to I2C. + GPIOs 35-39 are input-only so cannot be used as outputs. + + endmenu + + menu "Host Interface" + menu "4Bit Strobe Input" + + config HOST_KDB0 + int "KDB0 GPIO pin number" + range 0 46 + default 23 + help + GPIO number (IOxx) used to connect the 4bit bidirectional data bus Bit 0 with the ESP32. See schematic for actual used value. May change with revisions. + + config HOST_KDB1 + int "KDB1 GPIO pin number" + range 0 46 + default 25 + help + GPIO number (IOxx) used to connect the 4bit bidirectional data bus Bit 1 with the ESP32. See schematic for actual used value. May change with revisions. + + config HOST_KDB2 + int "KDB2 GPIO pin number" + range 0 46 + default 26 + help + GPIO number (IOxx) used to connect the 4bit bidirectional data bus Bit 2 with the ESP32. See schematic for actual used value. May change with revisions. + + config HOST_KDB3 + int "KDB3 GPIO pin number" + range 0 46 + default 27 + help + GPIO number (IOxx) used to connect the 4bit bidirectional data bus Bit 3 with the ESP32. See schematic for actual used value. May change with revisions. + endmenu + + menu "8Bit Scan Data Output" + config HOST_KDO0 + int "KDO0 GPIO pin number" + range 0 46 + default 14 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 0 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO1 + int "KDO1 GPIO pin number" + range 0 46 + default 15 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 1 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO2 + int "KDO2 GPIO pin number" + range 0 46 + default 16 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 2 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO3 + int "KDO3 GPIO pin number" + range 0 46 + default 17 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 3 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO4 + int "KDO4 GPIO pin number" + range 0 46 + default 18 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 4 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO5 + int "KDO5 GPIO pin number" + range 0 46 + default 19 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 5 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO6 + int "KDO6 GPIO pin number" + range 0 46 + default 21 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 6 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + + config HOST_KDO7 + int "KDO7 GPIO pin number" + range 0 46 + default 21 + help + GPIO number (IOxx) used to connect the 8bit scan data output Bit 7 to the 74HCT257 IC. See schematic for actual used value. May change with revisions. + endmenu + + choice MOUSE_UART_CHOICE + prompt "Mouse host side UART" + default HOST_BITBANG_UART + help + Select the hardware method of sending mouse data to the host. The two possible methods are bitbang (software UART) and UART Hardware. + config HOST_BITBANG_UART + bool "Bitbang UART" + help + Use the Bitbang UART (software). + config HOST_HW_UART + bool "Hardware UART" + help + Use one of the ESP32 Hardware UART's. + endchoice + + config HOST_RTSNI + int "RTSNi GPIO pin number" + range 0 46 + default 35 + help + GPIO number (IOxx) used to connect the RTSN line with the ESP32. See schematic for actual used value. May change with revisions. + + config HOST_MPXI + int "MPXi GPIO pin number" + range 0 46 + default 12 + help + GPIO number (IOxx) used to connect the MPX line with the ESP32. See schematic for actual used value. May change with revisions. + + config HOST_KDI4 + int "KDI4 GPIO pin number" + range 0 46 + default 13 + help + GPIO number (IOxx) used to connect the KDI4 line with the ESP32. See schematic for actual used value. May change with revisions. + + endmenu + + menu "WiFi" + + config IF_WIFI_ENABLED + bool "Enable WiFi connectivity" + default false + help + Allow interface to act as an Access Point to allow external connectivity. Once connected the WiFi is intended to be used for making + key mapping changes. + This is an experimental feature and under development. + + config IF_WIFI_EN_KEY + int "WiFi Enable GPIO pin number" + range 0 46 + default 34 + depends on IF_WIFI_ENABLED + help + GPIO number (IOxx) used by the WiFi En switch to enable wifi connectivity. + + config IF_WIFI_SSID + string "Default SSID in Access Point Mode" + default "sharpkey" + depends on IF_WIFI_ENABLED + help + The SSID broadcast whilst the sharpkey module advertises wireless connectivity. + + config IF_WIFI_DEFAULT_SSID_PWD + string "Default password for initial connection to Access Point Mode" + default "sharpkey" + depends on IF_WIFI_ENABLED + help + The initial password needed to connect and logon to access point. + + config IF_WIFI_MAX_RETRIES + int "Maximum number of connection retries." + range 0 100 + default 10 + depends on IF_WIFI_ENABLED + help + Number of retries allowed for making a wireless connection with a client. + + config IF_WIFI_AP_CHANNEL + int "Channel of the Access Point." + range 0 13 + default 7 + depends on IF_WIFI_ENABLED + help + Channel use by the Access Point, default is 7. + + config IF_WIFI_SSID_HIDDEN + int "Broadcast SSID?" + range 0 1 + default 0 + depends on IF_WIFI_ENABLED + help + Broadcast the SSID (0) or hide it (1). + + config IF_WIFI_MAX_CONNECTIONS + int "Maximum simultaneous connections." + range 0 20 + default 5 + depends on IF_WIFI_ENABLED + help + Maximum number of simultaneous open connections supported. + + endmenu + + menu "Debug Options" + + config DEBUG_SERIAL + bool "Serial debug output" + default false + help + Enable debug output (non ESP logging) on the serial port. + + config DEBUG_DISABLE_KDB + bool "Disable input mode actuation of the KDB data bus" + default false + help + Disable the Host KDB input configuration step, useful feature for debugging. + + config DEBUG_DISABLE_KDO + bool "Disable output mode actuation of the KDO strobe row" + default false + help + Disable the Host KDO output configuration step, useful feature for debugging. + + config DEBUG_DISABLE_RTSNI + bool "Disable input mode actuation of the RTSNi signal" + default false + help + Disable the Host RTSNi input configuration step, useful feature for debugging. + + config DEBUG_DISABLE_MPXI + bool "Disable input mode actuation of the MPXi signal" + default false + help + Disable the Host MPXi input configuration step, useful feature for debugging. + + config DEBUG_DISABLE_KDI + bool "Disable input mode actuation of the KDI4 signal" + default false + help + Disable the Host KDI input configuration step, useful feature for debugging. + endmenu + + config PWRLED + int "GPIO pin number used for Power On and Status LED" + range 0 46 + default 25 + help + GPIO number (IOxx) used to control the Power On/Status LED. + Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to I2C. + GPIOs 35-39 are input-only so cannot be used as outputs. + +endmenu + diff --git a/main/KeyInterface.cpp b/main/KeyInterface.cpp deleted file mode 120000 index 8b319c1..0000000 --- a/main/KeyInterface.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/KeyInterface.cpp \ No newline at end of file diff --git a/main/KeyInterface.cpp b/main/KeyInterface.cpp new file mode 100644 index 0000000..2e327c1 --- /dev/null +++ b/main/KeyInterface.cpp @@ -0,0 +1,211 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: KeyInterface.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Base class with virtual abstraction of key methods on which all host interfaces, +// instantiated as a singleton, are based. This module comprises all common interface +// code and the header contains the virtual abstraction methods overriden by the +// sub-class which forms an actual interface. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_system.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "sdkconfig.h" +#include "KeyInterface.h" + + +// Method to reconfigure the GPIO on ADC2. This is necessary due to an ESP32 issue regarding WiFi Client mode and ADC2. If the +// pins are input and wrong value the WiFi Client mode wont connect, if they are output it will connect!! A few issues with the +// ESP32 you have to work around, next design, try use ADC2 as outputs only! +void KeyInterface::reconfigADC2Ports(bool setAsOutput) +{ + // Locals. + // + gpio_config_t io_conf; + + // Configure 4 inputs to be the Strobe Row Number which is used to index the virtual key matrix and the strobe data returned. + #if !defined(CONFIG_DEBUG_DISABLE_KDB) + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = (setAsOutput == true ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT); + io_conf.pin_bit_mask = (1ULL<suspend = suspendIf; +} + +// Method to test to see if the interface has been suspended. +// Two modes, one just tests and returns the state, the second waits in a loop until the interface suspends. +// +bool KeyInterface::isSuspended(bool waitForSuspend) +{ + // If flag set, loop waiting for the suspended flag to be set. + while(waitForSuspend == true && this->suspended == false) + { + vTaskDelay(1); + } + + // Return the suspended status. + return(this->suspended); +} + +// Method to test to see if the interface is running and not suspended. +// Two modes, one just tests and returns the state, the second waits in a loop until the interface runs. +bool KeyInterface::isRunning(bool waitForRelease) +{ + // If flag set, loop waiting for the suspended flag to be set. + while(waitForRelease == true && this->suspended == true) + { + vTaskDelay(1); + } + + // Return the suspended status. + return(this->suspended); +} + +// Base initialisation for generic hardware used by all sub-classes. The sub-class invokes the init +// method manually from within it's init method. +void KeyInterface::init(const char *subClassName, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, uint32_t ifMode) +{ + // Locals + #define INITTAG "init" + + // Store the NVS object. + this->nvs = hdlNVS; + + // Store the LED object. + this->led = hdlLED; + + // Store the HID object. + this->hid = hdlHID; + + // Store the sub-class name for later use, ie. NVS key access. + this->subClassName = subClassName; + + // Set LED to on. + led->setLEDMode(LED::LED_MODE_ON, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + + // All done, no return code! + return; +} + +// Base initialisation for generic hardware used by all sub-classes. The sub-class invokes the init +// method manually from within it's init method. +// This method doesnt initialise hardware, used for objects probing this object data. +void KeyInterface::init(const char *subClassName, NVS *hdlNVS, HID *hdlHID) +{ + // Locals + #define INITTAG "init" + + // Store the NVS object. + this->nvs = hdlNVS; + + // Store the HID object. + this->hid = hdlHID; + + // Store the sub-class name for later use, ie. NVS key access. + this->subClassName = subClassName; + + // All done, no return code! + return; +} + + +// Constructor, basically initialise the Singleton interface and let the threads loose. +//KeyInterface::KeyInterface(uint32_t ifMode) +//{ +// // init(className(__PRETTY_FUNCTION__), ifMode); +//} + +// Basic constructor, do nothing! +//KeyInterface::KeyInterface(void) +//{ +// // +//} diff --git a/main/LED.cpp b/main/LED.cpp deleted file mode 120000 index b67f606..0000000 --- a/main/LED.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/LED.cpp \ No newline at end of file diff --git a/main/LED.cpp b/main/LED.cpp new file mode 100644 index 0000000..db504a3 --- /dev/null +++ b/main/LED.cpp @@ -0,0 +1,341 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: LED.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Base class for the encapsulation and control methods of an LED used primarily to +// indicate to users the status of the application. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_system.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "sdkconfig.h" +#include "LED.h" + +// Method to set the LED mode, duty cycle and duty period. Once the current LED cycle has come to an end, the control +// thread will replace the working configuration with the new configuration set here. +// +bool LED::setLEDMode(enum LED_MODE mode, enum LED_DUTY_CYCLE dutyCycle, uint32_t maxBlinks, uint64_t usDutyPeriod, uint64_t msInterPeriod) +{ + // Locals. + // + bool result = true; + + // If a setup is already waiting to be processed, exit with fail. This could be stacked into a vector but not really beneficial. + if(ledCtrl.newConfig.updated == false) + { + // Ensure we have exclusive access, the LED can be controlled by numerous threads, so ensure only one can access and setup at a time. + if(xSemaphoreTake(ledCtrl.mutexInternal, (TickType_t)1000) == pdTRUE) + { + ledCtrl.newConfig.mode = mode; + ledCtrl.newConfig.dutyCycle = dutyCycle; + ledCtrl.newConfig.maxBlinks = maxBlinks; + ledCtrl.newConfig.dutyPeriod = usDutyPeriod; + ledCtrl.newConfig.interPeriod = msInterPeriod; + ledCtrl.newConfig.updated = true; + + // Release mutex so other threads can set the LED. + xSemaphoreGive(ledCtrl.mutexInternal); + } else + { + result = false; + } + } else + { + result = false; + } + return(result); +} + +// Thread method to provide LED control. +IRAM_ATTR void LED::ledInterface(void *pvParameters) +{ + // Locals. + // + uint32_t LED_MASK; + uint64_t delayTimer = 0LL; + uint64_t curTime = 0LL; + enum LEDSTATE { + LEDSTATE_IDLE = 0, + LEDSTATE_BLINK_MARK = 1, + LEDSTATE_BLINK_SPACE = 2, + LEDSTATE_BLINK_INTER = 3, + } fsmState = LEDSTATE_IDLE; + #define LEDIFTAG "ledInterface" + + // Map the instantiating object so we can access its methods and data. + LED* pThis = (LED*)pvParameters; + + // Set the LED GPIO mask according to the defined pin. This code needs updating if GPIO1 pins are used. + LED_MASK = (1 << pThis->ledCtrl.ledPin); + + // Sign on. + ESP_LOGW("ledInterface", "Starting LED control thread."); + + // Turn off LED. + GPIO.out_w1tc = LED_MASK; + + // Loops forever. + for(;;) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(LEDIFTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // If a new configuration is set, then once the running FSM has returned to idle, update the configuration prior to the next FSM run. + // + if(pThis->ledCtrl.newConfig.updated) + { + // Take control of the Mutex so we are able to take on the data without a new setup clashing. If the Mutex is taken then continue on with the state machine logic till next loop. + if(xSemaphoreTake(pThis->ledCtrl.mutexInternal, (TickType_t)1) == pdTRUE) + { + pThis->ledCtrl.currentConfig = pThis->ledCtrl.newConfig; + pThis->ledCtrl.currentConfig.valid = true; + pThis->ledCtrl.newConfig.updated = false; + pThis->ledCtrl.blinkCnt = 0; + + // Got new setup so release mutex. + xSemaphoreGive(pThis->ledCtrl.mutexInternal); + } + } + + // Only run if we have a valid configuration. + if(pThis->ledCtrl.currentConfig.valid) + { + do { + // Get the current timer value, only run the FSM when the timer is idle. + timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &curTime); + if(curTime >= delayTimer) + { + // Ensure the timer is stopped. + timer_pause(TIMER_GROUP_0, TIMER_1); + delayTimer = 0LL; + + // Mini finite state machine for LED control. + switch(fsmState) + { + case LEDSTATE_IDLE: + // For on/off, no need for the FSM, just apply setting to LED and loop. + switch(pThis->ledCtrl.currentConfig.mode) + { + case LED_MODE_ON: + // Turn on LED. + GPIO.out_w1ts = LED_MASK; + delayTimer = 1000UL; + break; + + case LED_MODE_BLINK_ONESHOT: + // If the number of blinks is not 0 then on reaching the count, switch to LED off mode. + if(pThis->ledCtrl.currentConfig.maxBlinks > 0 && pThis->ledCtrl.blinkCnt++ >= pThis->ledCtrl.currentConfig.maxBlinks) + { + pThis->ledCtrl.currentConfig.mode = LED_MODE_OFF; + } else + { + fsmState = LEDSTATE_BLINK_MARK; + } + break; + + case LED_MODE_BLINK: + // Normal blink mode increments the count which is used for determining inter blink period. + pThis->ledCtrl.blinkCnt++; + fsmState = LEDSTATE_BLINK_MARK; + break; + + case LED_MODE_OFF: + default: + // Turn off LED. + GPIO.out_w1tc = LED_MASK; + delayTimer = 1000UL; + break; + + } + break; + + case LEDSTATE_BLINK_MARK: + // Turn on LED. + GPIO.out_w1ts = LED_MASK; + + // Next state, SPACE. + fsmState = LEDSTATE_BLINK_SPACE; + + // Calculate time to SPACE. + switch(pThis->ledCtrl.currentConfig.dutyCycle) + { + case LED_DUTY_CYCLE_10: + delayTimer = (pThis->ledCtrl.currentConfig.dutyPeriod / 10); + break; + case LED_DUTY_CYCLE_20: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 2); + break; + case LED_DUTY_CYCLE_30: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 3); + break; + case LED_DUTY_CYCLE_40: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 4); + break; + case LED_DUTY_CYCLE_50: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 5); + break; + case LED_DUTY_CYCLE_60: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 6); + break; + case LED_DUTY_CYCLE_70: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 7); + break; + case LED_DUTY_CYCLE_80: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 8); + break; + case LED_DUTY_CYCLE_90: + delayTimer = ((pThis->ledCtrl.currentConfig.dutyPeriod / 10) * 9); + break; + // We shouldnt be here if duty cycle is off, so back to idle. + case LED_DUTY_CYCLE_OFF: + default: + GPIO.out_w1tc = LED_MASK; + delayTimer = 0; + fsmState = LEDSTATE_IDLE; + break; + } + break; + + case LEDSTATE_BLINK_SPACE: + // Turn off LED. + GPIO.out_w1tc = LED_MASK; + + // Calculate time to next MARK. + delayTimer = pThis->ledCtrl.currentConfig.dutyPeriod - delayTimer; + + // Now add an interblink delay prior to next blink. + fsmState = LEDSTATE_BLINK_INTER; + break; + + case LEDSTATE_BLINK_INTER: + // If we are in normal mode with a blink limit set and limit reached or in limited mode, then add an interblink delay as configured. + if((pThis->ledCtrl.currentConfig.mode == LED_MODE_BLINK && pThis->ledCtrl.currentConfig.maxBlinks > 0 && pThis->ledCtrl.blinkCnt >= pThis->ledCtrl.currentConfig.maxBlinks) || + (pThis->ledCtrl.currentConfig.mode == LED_MODE_BLINK_ONESHOT)) + { + // Interblink delay is given in milli-seconds, so multiply up and set delay. + delayTimer = pThis->ledCtrl.currentConfig.interPeriod * 1000; + + // Reset blink counter to trigger next interperiod delay. + if(pThis->ledCtrl.currentConfig.mode == LED_MODE_BLINK) + pThis->ledCtrl.blinkCnt = 0; + } + + // We return to IDLE to allow time for reconfiguration if requested. + fsmState = LEDSTATE_IDLE; + break; + + // Unknown or not programmed state, return to IDLE. + default: + fsmState = LEDSTATE_IDLE; + break; + } + + // If a new delay is requested, reset the value in the timer and start. + if(delayTimer > 0LL) + { + timer_set_counter_value(TIMER_GROUP_0, TIMER_1, 0LL); + timer_start(TIMER_GROUP_0, TIMER_1); + } + } + + // Give the OS some time... + taskYIELD(); + } while(fsmState != LEDSTATE_IDLE); + + } + } +} + +// Method to set the GPIO pin to be used for LED output. +void LED::ledInit(uint8_t ledPin) +{ + // Initialise variables. + this->ledCtrl.currentConfig.valid = false; + this->ledCtrl.currentConfig.updated = false; + this->ledCtrl.currentConfig.mode = LED_MODE_OFF; + this->ledCtrl.currentConfig.dutyCycle = LED_DUTY_CYCLE_OFF; + this->ledCtrl.currentConfig.dutyPeriod = 0LL; + this->ledCtrl.currentConfig.interPeriod = 0LL; + this->ledCtrl.newConfig = this->ledCtrl.currentConfig; + + // Store GPIO pin to which LED is connected. + this->ledCtrl.ledPin = ledPin; + + // Configure a timer to be used for the LED blink rate. + timer_config_t timerConfig = { + .alarm_en = TIMER_ALARM_DIS, // No alarm, were not using interrupts as we are in a dedicated thread. + .counter_en = TIMER_PAUSE, // Timer paused until required. + .intr_type = TIMER_INTR_LEVEL, // No interrupts used. + .counter_dir = TIMER_COUNT_UP, // Timing a fixed period. + .auto_reload = TIMER_AUTORELOAD_DIS, // No need for auto reload, fixed time period. + .divider = 80 // 1Mhz operation giving 1uS resolution. + }; + ESP_ERROR_CHECK(timer_init(TIMER_GROUP_0, TIMER_1, &timerConfig)); + ESP_ERROR_CHECK(timer_set_counter_value(TIMER_GROUP_0, TIMER_1, 0)); + + // Setup mutex's. + ledCtrl.mutexInternal = xSemaphoreCreateMutex(); + + // Core 0 - Application + // LED control thread - dedicated thread to control the LED according to set mode. + ESP_LOGW("ledInit", "Starting LEDif thread..."); + ::xTaskCreatePinnedToCore(&this->ledInterface, "ledif", 4096, this, 0, &this->TaskLEDIF, 0); +} + +// Constructor, basically initialise the Singleton interface and let the control thread loose. +LED::LED(uint32_t hwPin) +{ + // Store the class name for later use, ie. NVS key access. + this->className = getClassName(__PRETTY_FUNCTION__); + + // Configure the Power LED used for activity and user interaction. Initial state is ON until a keyboard is detected when it turns off and only blinks on keyboard activity. + ledInit(hwPin); + + // Initial state, turn on LED to indicate LED control is working. + setLEDMode(LED::LED_MODE_ON, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); +} + +// Basic constructor, do nothing! +LED::LED(void) +{ + // Store the class name for later use, ie. NVS key access. + this->className = getClassName(__PRETTY_FUNCTION__); +} diff --git a/main/MZ2528.cpp b/main/MZ2528.cpp deleted file mode 120000 index 9e11183..0000000 --- a/main/MZ2528.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/MZ2528.cpp \ No newline at end of file diff --git a/main/MZ2528.cpp b/main/MZ2528.cpp new file mode 100644 index 0000000..1910c93 --- /dev/null +++ b/main/MZ2528.cpp @@ -0,0 +1,1244 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: MZ2528.cpp +// Created: Jan 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: MZ2500/2800 Key Matrix logic. +// This source file contains the application logic to obtain PS/2 scan codes, map them +// into a virtual keyboard matrix as per the Sharp MZ series key matrix and the +// logic to transmit the virtual key matrix to the MZ2500/2800. +// +// The application uses a modified version of the PS2KeyAdvanced +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The application uses the Espressif Development environment with Arduino components. +// This is necessary for the PS2KeyAdvanced class, which I may in future convert to +// use esp-idf library calls rather than Arduino. +// +// The Espressif environment is necessary in order to have more control over the build. +// It is important, for timing, that Core 1 is dedicated to MZ Interface +// logic and Core 0 is used for all other RTOS/Interrupts tasks. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Jan 2022 - Initial write. +// Feb 2022 - Updates and fixes. Added logic to detect PS/2 disconnect and reconnect, +// added 3 alternative maps selected by ALT+F1 (MZ2500), ALT+F2(MZ2000) +// ALT+F3(MZ80B) due to slight differences in the key layout. +// Added framework for wifi so that the interface can enter AP mode to +// acquire local net parameters then connect to local net. Needs the web +// interface writing. +// Mar 2022 - Copied from the mz25key and seperated logic into modules as the sharpkey +// supports multiple host types. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updated interface to yield Core 1 when no key has been pressed. This is +// necessary to allow Bluetooth and NVS to work (even though BT is pinned +// to core 0, the NVS seems to require both CPU's). +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "Arduino.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "sys/stat.h" +#include "esp_littlefs.h" +#include "PS2KeyAdvanced.h" +#include "sdkconfig.h" +#include "MZ2528.h" + +// Tag for ESP main application logging. +#define MAINTAG "mz25key" + + +// Method to connect and interact with the MZ-2500 keyboard controller. This method is seperate from the MZ-2800 +// as the scan is different and as it is time critical it needs to be per target machine. +// +// The basic requirement is to: +// 1. Detect a falling edge on the RTSN signal +// 2. Read the provided ROW number. +// 3. Lookup the matrix data for given ROW. +// 4. Output data to LS257 Mux. +// 5. Wait for RTSN to return inactive. +// 5. Loop +// +// The hidInterface method is responsible for obtaining scancodes from either a PS/2 or Bluetooth Keyboard and +// creating the corresponding virtual matrix. +// +// NB: As this method holds Core 1 under spinlock, no FreeRTOS or Arduino access +// can be made except for basic I/O ports. The spinlock has to be released for non +// I/O work. +// +// The MZ timing period is ~600ns RTSN Low to High (Latch Row data coming from MZ machine), +// MPX (connected direct to external Quad 2-1 Mux goes high as RTSN goes low, the MPX +// signal is held high for 300ns then goes low for 300ns. The period when RTSN is low is +// when the MZ machine reads the scan row data. The cycle is ~1.2uS repeating, 14 rows +// so ~16.8uS for one full key matrix, The MZ machine if stuck in a tight loop will take +// approx 100uS to scan the matrix so the Gate Arrays are over sampling 6 times. +// +// WARNING: The GPIO's are configurable via menuconfig BUT it is assumed all except RTSNi +// are in the first GPIO bank and RTSNi is in the second GPIO bank. Modify the code +// if RTSNi is set in the first bank or KDB[3:0], KDI4 are in the second bank. +// +IRAM_ATTR void MZ2528::mz25Interface( void * pvParameters ) +{ + // Locals. + bool critical = false; + volatile uint32_t gpioIN; + volatile uint8_t strobeRow = 1; + + // Mask values declared as variables, let the optimiser decide wether they are constants or placed in-memory. + uint32_t rowBitMask = (1 << CONFIG_HOST_KDB3) | (1 << CONFIG_HOST_KDB2) | (1 << CONFIG_HOST_KDB1) | (1 << CONFIG_HOST_KDB0); + uint32_t colBitMask = (1 << CONFIG_HOST_KDO7) | (1 << CONFIG_HOST_KDO6) | (1 << CONFIG_HOST_KDO5) | (1 << CONFIG_HOST_KDO4) | + (1 << CONFIG_HOST_KDO3) | (1 << CONFIG_HOST_KDO2) | (1 << CONFIG_HOST_KDO1) | (1 << CONFIG_HOST_KDO0); + uint32_t KDB3_MASK = (1 << CONFIG_HOST_KDB3); + uint32_t KDB2_MASK = (1 << CONFIG_HOST_KDB2); + uint32_t KDB1_MASK = (1 << CONFIG_HOST_KDB1); + uint32_t KDB0_MASK = (1 << CONFIG_HOST_KDB0); + uint32_t KDI4_MASK = (1 << CONFIG_HOST_KDI4); + uint32_t RTSNI_MASK = (1 << (CONFIG_HOST_RTSNI - 32)); + //uint32_t MPXI_MASK = (1 << CONFIG_HOST_MPXI); + + // Retrieve pointer to object in order to access data. + MZ2528* pThis = (MZ2528*)pvParameters; + + // Initialise the MUTEX which prevents this core from being released to other tasks. + pThis->mzMutex = portMUX_INITIALIZER_UNLOCKED; + + // Setup starting state. + GPIO.out_w1ts = colBitMask; + + // Sign on. + ESP_LOGW(MAINTAG, "Starting mz25Interface thread, colBitMask=%08x, rowBitMask=%08x.", colBitMask, rowBitMask); + + // Permanent loop, just wait for an RTSN strobe, latch the row, lookup matrix and output. + // Timings with Power LED = LED Off to On = 108ns, LED On to Off = 392ns + for(;;) + { + // Suspend processing if there are no new key presses or a suspend request has been made, ie from WiFi interface. + if(pThis->yieldHostInterface == true) + { + // Exit spinlock. + if(critical) portEXIT_CRITICAL(&pThis->mzMutex); + + // Requested to suspend? + if(pThis->suspendRequested()) + { + // Setting the ADC2 ports to output mode is required due to the ESP32 Client Mode which has many issues, one is if the ports are set to input and receiving data + // it fails to start! + // ESP32 WiFi/ADC2 workaround. The ESP32 wont connect to a router in station mode if the ADC2 pins are set to input and have an alternating signal present. + pThis->reconfigADC2Ports(true); + + // All bits to 1, ie. inactive - this is necessary otherwise the host could see a key being held. + GPIO.out_w1ts = colBitMask; + //GPIO.out_w1ts = KDB3_MASK | KDB2_MASK | KDB1_MASK | KDB0_MASK | KDI4_MASK | MPXI_MASK; + + // Yield until the core is released. + pThis->yield(0); + + // Restore the GPIO. + pThis->reconfigADC2Ports(false); + } else + { + // All bits to 1, ie. inactive - this is necessary otherwise the host could see a key being held. The normal state for inputs is all high + // if no key has been pressed. + GPIO.out_w1ts = colBitMask; + + // Yield to allow other tasks to run. + while(pThis->yieldHostInterface == true) vTaskDelay(0); + } + + // Enter spinlock. + portENTER_CRITICAL(&pThis->mzMutex); + critical = true; + } + + // Detect RTSN going high, the MZ will send the required row during this cycle. + if(REG_READ(GPIO_IN1_REG) & RTSNI_MASK) + { + // Read the GPIO ports to get latest Row and KDI4 states. + gpioIN = REG_READ(GPIO_IN_REG); + + // Assemble the required matrix row from the configured bits. + strobeRow = ((gpioIN&KDB3_MASK) >> (CONFIG_HOST_KDB3-3)) | ((gpioIN&KDB2_MASK) >> (CONFIG_HOST_KDB2-2)) | ((gpioIN&KDB1_MASK) >> (CONFIG_HOST_KDB1-1)) | ((gpioIN&KDB0_MASK) >> CONFIG_HOST_KDB0); + + // Clear all KDO bits - clear state = '1' + GPIO.out_w1ts = colBitMask; // Reset all scan data bits to '1', inactive. + + // KDI4 indicates if row data is needed or a single byte ANDing all the keys together, ie. to detect a key press without strobing all rows. + if(gpioIN & KDI4_MASK) + { + // Set all required KDO bits according to keyMatrix, set state = '0'. + GPIO.out_w1tc = pThis->mzControl.keyMatrixAsGPIO[strobeRow]; // Set to '0' active bits. + } else + { + // Set all required KDO bits according to the strobe all value. set state = '0'. + GPIO.out_w1tc = pThis->mzControl.strobeAllAsGPIO; // Set to '0' active bits. + } + + // Wait for RTSN to go low. No lockup guarding as timing is critical also the watchdog is disabled, if RTSN never goes low then the user has probably unplugged the interface! + while((REG_READ(GPIO_IN1_REG) & RTSNI_MASK) && pThis->yieldHostInterface == false); + } + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect + } +} + +// Method to connect and interact with the MZ-2800 keyboard controller. This method is seperate from the MZ-2500 +// as the scan is different and as it is time critical it needs to be per target machine. +// +// The basic requirement is to: +// 1. Detect a rising edge on the RTSN signal +// 2. Wait at least 200ns before sampling KD4 +// 3. Wait at least 650ns before reading ROW. +// 4. Read the provided ROW number. +// 5. If KD4 = 0 then output logical AND of all columns to LS257 Mux. +// 6. If KD4 = 1 then lookup data for given row and output to LS257 Mux. +// 7. Wait for RTSN to return low. +// 7. Loop +// +// The hidInterface method is responsible for obtaining scancodes from either a PS/2 or Bluetooth Keyboard and +// creating the corresponding virtual matrix. +// +// NB: As this method holds Core 1 under spinlock, no FreeRTOS or Arduino access +// can be made except for basic I/O ports. The spinlock has to be released for non +// I/O work. +// +// The MZ 2800 timing period is 1.78uS RTSN going active high, KD4 changing state 150ns after RTSN goes active, +// ROW number being set 650ns after RTSN goes active. MPX directly controls the LS257 latch so only need to write out +// and 8 bit value prior to RTSN going inactive. +// Normally the keyboard is in STROBE ALL mode. When a key is pressed, it commences a scan and when it arrives at the pressed +// key, RTSN cycle is suspended for varying amounts of time (ie 500us or 19ms) as the controller is looking for debounce and repeat. +// +// WARNING: The GPIO's are configurable via menuconfig BUT it is assumed all except RTSNi +// are in the first GPIO bank and RTSNi is in the second GPIO bank. Modify the code +// if RTSNi is set in the first bank or KDB[3:0], KDI4 are in the second bank. +// +IRAM_ATTR void MZ2528::mz28Interface( void * pvParameters ) +{ + // Locals. + bool critical = false; + volatile uint32_t gpioIN; + volatile uint8_t strobeRow = 1; + + // Mask values declared as variables, let the optimiser decide wether they are constants or placed in-memory. + uint32_t rowBitMask = (1 << CONFIG_HOST_KDB3) | (1 << CONFIG_HOST_KDB2) | (1 << CONFIG_HOST_KDB1) | (1 << CONFIG_HOST_KDB0); + uint32_t colBitMask = (1 << CONFIG_HOST_KDO7) | (1 << CONFIG_HOST_KDO6) | (1 << CONFIG_HOST_KDO5) | (1 << CONFIG_HOST_KDO4) | + (1 << CONFIG_HOST_KDO3) | (1 << CONFIG_HOST_KDO2) | (1 << CONFIG_HOST_KDO1) | (1 << CONFIG_HOST_KDO0); + uint32_t KDB3_MASK = (1 << CONFIG_HOST_KDB3); + uint32_t KDB2_MASK = (1 << CONFIG_HOST_KDB2); + uint32_t KDB1_MASK = (1 << CONFIG_HOST_KDB1); + uint32_t KDB0_MASK = (1 << CONFIG_HOST_KDB0); + uint32_t KDI4_MASK = (1 << CONFIG_HOST_KDI4); + uint32_t RTSNI_MASK = (1 << (CONFIG_HOST_RTSNI - 32)); + //uint32_t MPXI_MASK = (1 << CONFIG_HOST_MPXI); + + // Retrieve pointer to object in order to access data. + MZ2528* pThis = (MZ2528*)pvParameters; + + // Initialise the MUTEX which prevents this core from being released to other tasks. + pThis->mzMutex = portMUX_INITIALIZER_UNLOCKED; + + // Sign on. + ESP_LOGW(MAINTAG, "Starting mz28Interface thread, colBitMask=%08x, rowBitMask=%08x.", colBitMask, rowBitMask); + + // Permanent loop, just wait for an RTSN strobe, latch the row, lookup matrix and output. + for(;;) + { + // Suspend processing if there are no new key presses or a suspend request has been made, ie from WiFi interface. + if(pThis->yieldHostInterface == true) + { + // Exit spinlock. + if(critical) portEXIT_CRITICAL(&pThis->mzMutex); + + // Requested to suspend? + if(pThis->suspendRequested()) + { + // Setting the ADC2 ports to output mode is required due to the ESP32 Client Mode which has many issues, one is if the ports are set to input and receiving data + // it fails to start! + // ESP32 WiFi/ADC2 workaround. The ESP32 wont connect to a router in station mode if the ADC2 pins are set to input and have an alternating signal present. + pThis->reconfigADC2Ports(true); + + // All bits to 1, ie. inactive - this is necessary otherwise the host could see a key being held. + GPIO.out_w1ts = colBitMask; + //GPIO.out_w1ts = KDB3_MASK | KDB2_MASK | KDB1_MASK | KDB0_MASK | KDI4_MASK | MPXI_MASK; + + // Yield until the core is released. + pThis->yield(0); + + // Restore the GPIO. + pThis->reconfigADC2Ports(false); + } else + { + // All bits to 1, ie. inactive - this is necessary otherwise the host could see a key being held. The normal state for inputs is all high + // if no key has been pressed. + GPIO.out_w1ts = colBitMask; + + // Yield to allow other tasks to run. + while(pThis->yieldHostInterface == true) vTaskDelay(0); + } + + // Enter spinlock. + portENTER_CRITICAL(&pThis->mzMutex); + critical = true; + } + + // Detect RTSN going high, the MZ will send the required row during this cycle. + if(REG_READ(GPIO_IN1_REG) & RTSNI_MASK) + { + // Slight delay needed as KD4 lags behind RTSN by approx 200ns and ROW number lags 850ns behind RTSN. + for(volatile uint32_t delay=0; delay < 8; delay++); + + // Read the GPIO ports to get latest Row and KDI4 states. + gpioIN = REG_READ(GPIO_IN_REG); + + // Assemble the required matrix row from the configured bits. + strobeRow = ((gpioIN&KDB3_MASK) >> (CONFIG_HOST_KDB3-3)) | ((gpioIN&KDB2_MASK) >> (CONFIG_HOST_KDB2-2)) | ((gpioIN&KDB1_MASK) >> (CONFIG_HOST_KDB1-1)) | ((gpioIN&KDB0_MASK) >> CONFIG_HOST_KDB0); + + // Clear all KDO bits - clear state = '1' + GPIO.out_w1ts = colBitMask; // Reset all scan data bits to '1', inactive. + + // Another short delay once the row has been assembled as we dont want to change the latch setting too soon, changing to soon leads to ghosting on previous row. + for(volatile uint32_t delay=0; delay < 5; delay++); + + // KDI4 indicates if row data is needed or a single byte ANDing all the keys together, ie. to detect a key press without strobing all rows. + if(gpioIN & KDI4_MASK) + { + // Set all required KDO bits according to keyMatrix, set state = '0'. + GPIO.out_w1tc = pThis->mzControl.keyMatrixAsGPIO[strobeRow]; // Set to '0' active bits. + } else + { + // Set all required KDO bits according to the strobe all value. set state = '0'. + GPIO.out_w1tc = pThis->mzControl.strobeAllAsGPIO; // Set to '0' active bits. + } + + // Wait for RTSN to go low. No lockup guarding as timing is critical also the watchdog is disabled, if RTSN never goes low then the user has probably unplugged the interface! + while((REG_READ(GPIO_IN1_REG) & RTSNI_MASK) && pThis->yieldHostInterface == false); + } + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect + } +} + +// Method to select keyboard configuration options. When a key sequence is pressed, ie. SHIFT+CTRL+ESC then the fourth simultaneous key is the required option and given to this +// method to act on. Options can be machine model, keyboard map etc. +// +void MZ2528::selectOption(uint8_t optionCode) +{ + // Locals. + // + bool updated = true; + #define SELOPTTAG "selectOption" + + // Simple switch to decode the required option and act on it. + switch(optionCode) + { + // Select a keymap using 1..8 or default (STANDARD) using 0. + case PS2_KEY_1: + this->mzConfig.params.activeKeyboardMap = KEYMAP_UK_WYSE_KB3926; + break; + case PS2_KEY_2: + this->mzConfig.params.activeKeyboardMap = KEYMAP_JAPAN_OADG109; + break; + case PS2_KEY_3: + this->mzConfig.params.activeKeyboardMap = KEYMAP_JAPAN_SANWA_SKBL1; + break; + case PS2_KEY_4: + this->mzConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_4; + break; + case PS2_KEY_5: + this->mzConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_5; + break; + case PS2_KEY_6: + this->mzConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_6; + break; + case PS2_KEY_7: + this->mzConfig.params.activeKeyboardMap = KEYMAP_UK_PERIBOARD_810; + break; + case PS2_KEY_8: + this->mzConfig.params.activeKeyboardMap = KEYMAP_UK_OMOTON_K8508; + break; + case PS2_KEY_0: + this->mzConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + break; + + // Select the active machine model. If we are connected to an MZ-2500 host then it is possible to enable an MZ-2000/MZ-80B mapping. + case PS2_KEY_END: + this->mzConfig.params.activeMachineModel = (this->mzControl.mode2500 ? MZ_2500 : MZ_2800); + break; + case PS2_KEY_DN_ARROW: + if(this->mzControl.mode2500) + { + this->mzConfig.params.activeMachineModel = MZ_2000; + } + break; + case PS2_KEY_PGDN: + if(this->mzControl.mode2500) + { + this->mzConfig.params.activeMachineModel = MZ_80B; + } + break; + + // Unknown option so ignore. + default: + updated = false; + break; + } + + // If an update was made, persist it for power cycles. + // + if(updated) + { + this->mzControl.persistConfig = true; + } + + return; +} + +// Method to refresh the transposed matrix used in the MZ interface. The normal key matrix is transposed to save valuable time +// because even though a core is dedicated to the MZ interface the timing is critical and the ESP-32 doesnt have spare horse power! +// +void MZ2528::updateMirrorMatrix(void) +{ + // Locals. + + // To save time in the MZ Interface, a mirror keyMatrix is built up, 32bit (GPIO Bank 0) wide, with the keyMatrix 8 bit data + // mapped onto the configured pins in the 32bit register. This saves precious time in order to meet the tight 1.2uS cycle. + // + mzControl.noKeyPressed = true; + for(int idx=0; idx < 15; idx++) + { + mzControl.keyMatrixAsGPIO[idx] = (((mzControl.keyMatrix[idx] >> 7) & 0x01) ^ 0x01) << CONFIG_HOST_KDO7 | + (((mzControl.keyMatrix[idx] >> 6) & 0x01) ^ 0x01) << CONFIG_HOST_KDO6 | + (((mzControl.keyMatrix[idx] >> 5) & 0x01) ^ 0x01) << CONFIG_HOST_KDO5 | + (((mzControl.keyMatrix[idx] >> 4) & 0x01) ^ 0x01) << CONFIG_HOST_KDO4 | + (((mzControl.keyMatrix[idx] >> 3) & 0x01) ^ 0x01) << CONFIG_HOST_KDO3 | + (((mzControl.keyMatrix[idx] >> 2) & 0x01) ^ 0x01) << CONFIG_HOST_KDO2 | + (((mzControl.keyMatrix[idx] >> 1) & 0x01) ^ 0x01) << CONFIG_HOST_KDO1 | + (((mzControl.keyMatrix[idx] ) & 0x01) ^ 0x01) << CONFIG_HOST_KDO0 ; + if(mzControl.keyMatrixAsGPIO[idx] != 0x00) mzControl.noKeyPressed = false; + } + + // Re-calculate the Strobe All (KD4 = 1) signal, this indicates if any bit (key) in the matrix is active. + mzControl.strobeAll = 0xFF; + mzControl.strobeAllAsGPIO = 0x00000000; + for(int idx2=0; idx2 < 15; idx2++) + { + mzControl.strobeAll &= mzControl.keyMatrix[idx2]; + } + + // To speed up the mzInterface logic, pre-calculate the strobeAll value as a 32bit GPIO output value. + mzControl.strobeAllAsGPIO |= (((mzControl.strobeAll >> 7) & 0x01) ^ 0x01) << CONFIG_HOST_KDO7 | + (((mzControl.strobeAll >> 6) & 0x01) ^ 0x01) << CONFIG_HOST_KDO6 | + (((mzControl.strobeAll >> 5) & 0x01) ^ 0x01) << CONFIG_HOST_KDO5 | + (((mzControl.strobeAll >> 4) & 0x01) ^ 0x01) << CONFIG_HOST_KDO4 | + (((mzControl.strobeAll >> 3) & 0x01) ^ 0x01) << CONFIG_HOST_KDO3 | + (((mzControl.strobeAll >> 2) & 0x01) ^ 0x01) << CONFIG_HOST_KDO2 | + (((mzControl.strobeAll >> 1) & 0x01) ^ 0x01) << CONFIG_HOST_KDO1 | + (((mzControl.strobeAll ) & 0x01) ^ 0x01) << CONFIG_HOST_KDO0 ; + if(mzControl.strobeAllAsGPIO != 0x00) mzControl.noKeyPressed = false; + return; +} + +// Method to map the PS2 scan code into a key matrix representation which the MZ-2500/2800 is expecting. +// +uint32_t MZ2528::mapKey(uint16_t scanCode) +{ + // Locals. + uint8_t idx; + bool changed = false; + bool matchExact = false; + uint8_t keyCode = (scanCode & 0xFF); + bool mapped = false; + #define MAPKEYTAG "mapKey" + + // Intercept control keys and set state variables. + // + // + if(scanCode & PS2_BREAK) + { + // Any break key clears the option select flag. + this->mzControl.optionSelect = false; + + // Clear any feature LED blinking. + led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } else + { + // Special mapping to allow selection of keyboard options. If the user presses CTRL+SHIFT+ESC then a flag becomes active and should a fourth key be pressed before a BREAK then the fourth key is taken as an option key and processed accordingly. + if(this->mzControl.optionSelect == true && keyCode != PS2_KEY_ESC) { mapped = true; this->mzControl.optionSelect = false; selectOption(keyCode); } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT)) { mapped = true; this->mzControl.optionSelect = true; } + + // Special mapping to allow selection of keyboard options. If the user presses CTRL+SHIFT+ESC then a flag becomes active and should a fourth key be pressed before a BREAK then the fourth key is taken as an option key and processed accordingly. + if(this->mzControl.optionSelect == true && keyCode != PS2_KEY_ESC) + { + mapped = true; + this->mzControl.optionSelect = false; + selectOption(keyCode); + } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT) && this->mzControl.optionSelect == false) + { + // Prime flag ready for fourth option key and start LED blinking periodically. + mapped = true; + this->mzControl.optionSelect = true; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_50, 1, 500L, 500L); + } + } + + // If the key has been mapped as a special key, no further processing. + if(mapped == true) + { + ESP_LOGW(MAPKEYTAG, "Mapped special key\n"); + } else + { + // Loop through the entire conversion table to find a match on this key, if found appy the conversion to the virtual + // switch matrix. + // + for(idx=0, changed=false, matchExact=false; idx < mzControl.kmeRows && (changed == false || (changed == true && matchExact == false)); idx++) + { + // Match key code? Make sure the current machine and keymap match as well. + if(mzControl.kme[idx].ps2KeyCode == (uint8_t)(scanCode&0xFF) && ((mzControl.kme[idx].machine == MZ_ALL) || (mzControl.kme[idx].machine & mzConfig.params.activeMachineModel) != 0) && ((mzControl.kme[idx].keyboardModel & mzConfig.params.activeKeyboardMap) != 0)) + { + // Match Raw, Shift, Function, Control, ALT or ALT-Gr? + if( (((mzControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0) && ((mzControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0) && ((mzControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0) && ((mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALT) == 0) && ((mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALTGR) == 0)) || + ((scanCode & PS2_SHIFT) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || + ((scanCode & PS2_CTRL) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || + ((scanCode & PS2_ALT) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALT) != 0) || + ((scanCode & PS2_ALT_GR) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALTGR) != 0) || + ((scanCode & PS2_GUI) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || + ((scanCode & PS2_FUNCTION) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) ) + { + // Exact entry match, data + control key? On an exact match we only process the first key. On a data only match we fall through to include additional data and control key matches to allow for un-mapped key combinations, ie. Japanese characters. + matchExact = (((scanCode & PS2_SHIFT) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || ((scanCode & PS2_SHIFT) == 0 && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0)) && + (((scanCode & PS2_CTRL) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || ((scanCode & PS2_CTRL) == 0 && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0)) && + (((scanCode & PS2_ALT) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALT) != 0) || ((scanCode & PS2_ALT) == 0 && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALT) == 0)) && + (((scanCode & PS2_ALT_GR) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALTGR) != 0) || ((scanCode & PS2_ALT_GR) == 0 && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_ALTGR) == 0)) && + (((scanCode & PS2_GUI) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || ((scanCode & PS2_GUI) == 0 && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0)) && + (((scanCode & PS2_FUNCTION) && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) || ((scanCode & PS2_FUNCTION) == 0 && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) ? true : false; + + // If the exact flag is set, skip as we cannot process this entry when we dont have an exact match. + if(matchExact == false && (mzControl.kme[idx].ps2Ctrl & PS2CTRL_EXACT) != 0) + continue; + + // RELEASE (PS2_BREAK == 1) or PRESS? + if((scanCode & PS2_BREAK)) + { + // Special case for the PAUSE / BREAK key. The underlying logic has been modified to send a BREAK key event immediately + // after a PAUSE make, this is necessary as the Sharp MZ machines require SHIFT (pause) BREAK so the PS/2 CTRL+BREAK wont + // work (unless logic is added to insert a SHIFT, pause, add BREAK). The solution was to generate a BREAK event + // and add a slight delay for the key matrix to register it. + if((scanCode&0x00FF) == PS2_KEY_PAUSE) + { + vTaskDelay(100); + } + + // Loop through all the row/column combinations and if valid, apply to the matrix. + for(int row=0; row < PS2TBL_MZ_MAX_MKROW; row++) + { + // Reset the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix. + if(mzControl.kme[idx].mkRow[row] != 0xFF) + { + mzControl.keyMatrix[mzControl.kme[idx].mkRow[row]] |= mzControl.kme[idx].mkKey[row]; + changed = true; + } + } + + // Loop through all the key releases associated with this key and reset the relevant matrix bit which was cleared on + // initial keydown. + // + for(int row=0; row < PS2TBL_MZ_MAX_BRKROW; row++) + { + if(mzControl.kme[idx].brkRow[row] != 0xFF) + { + mzControl.keyMatrix[mzControl.kme[idx].brkRow[row]] &= ~mzControl.kme[idx].brkKey[row]; + changed = true; + } + } + } else + { + // Loop through all the key releases associated with this key and clear the relevant matrix bit. + // This is done first so as to avoid false key detection in the MZ logic. + // + for(int row=0; row < PS2TBL_MZ_MAX_BRKROW; row++) + { + if(mzControl.kme[idx].brkRow[row] != 0xFF) + { + mzControl.keyMatrix[mzControl.kme[idx].brkRow[row]] |= mzControl.kme[idx].brkKey[row]; + changed = true; + } + } + + // If a release key has been actioned, update the matrix and insert a slight pause to avoid + // the MZ logic seeing the released keys in combination with the newly pressed keys. + if(changed) + { + updateMirrorMatrix(); + changed = false; + vTaskDelay(10); + } + + // Loop through all the row/column combinations and if valid, apply to the matrix. + for(int row=0; row < PS2TBL_MZ_MAX_MKROW; row++) + { + // Set the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix. + if(mzControl.kme[idx].mkRow[row] != 0xFF) + { + mzControl.keyMatrix[mzControl.kme[idx].mkRow[row]] &= ~mzControl.kme[idx].mkKey[row]; + changed = true; + } + } + } + + // Only spend time updating signals if an actual change occurred. Some keys arent valid so no change will be effected. + if(changed) + { + updateMirrorMatrix(); + } + } // match key or a special function + } // match key code + } // for loop + } // mapped + + // Return flag to indicate if a match occurred and the matrix updated. + return((uint32_t)changed); +} + +// Primary HID thread, running on Core 0. +// This thread is responsible for receiving PS/2 or BT scan codes and mapping them to an MZ-2500/2800 keyboard matrix. +// +IRAM_ATTR void MZ2528::hidInterface( void * pvParameters ) +{ + // Locals. + uint16_t scanCode = 0x0000; + + // Map the instantiating object so we can access its methods and data. + MZ2528* pThis = (MZ2528*)pvParameters; + + // Thread never exits, just polls the keyboard and updates the matrix. + while(1) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAPKEYTAG, "THREAD STACK SPACE(%d)",uxTaskGetStackHighWaterMark(NULL)); + } + + // Check for PS/2 keyboard scan codes. + while((scanCode = pThis->hid->read()) != 0) + { + // Scan Code Breakdown: + // Define name bit description + // PS2_BREAK 15 1 = Break key code + // (MSB) 0 = Make Key code + // PS2_SHIFT 14 1 = Shift key pressed as well (either side) + // 0 = No shift key + // PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + // 0 = No Ctrl key + // PS2_CAPS 12 1 = Caps Lock ON + // 0 = Caps lock OFF + // PS2_ALT 11 1 = Left Alt key pressed as well + // 0 = No Left Alt key + // PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + // 0 = No Right Alt key + // PS2_GUI 9 1 = GUI key pressed as well (either) + // 0 = No GUI key + // PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + // 0 = standard character key + // 7-0 PS/2 Key code. + // + // BREAK code means all keys released so clear out flags and send update. + ESP_LOGW(MAPKEYTAG, "SCANCODE:%04x",scanCode); + + // Update the virtual matrix with the new key value. + pThis->mapKey(scanCode); + + // Toggle LED to indicate data flow. + if((scanCode & PS2_BREAK) == 0) + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 1, 100L, 0L); + } + + // If all keys have been releaased or a suspend is requested, set the yieldInterface flag. This flag is intentional due to time + // being critical in the host interface thread. then after a short count, + if(pThis->mzControl.noKeyPressed == true || pThis->suspendRequested()) + { + vTaskDelay(10); + pThis->yieldHostInterface = true; + } + else { + pThis->yieldHostInterface = false; + } + + // NVS writes require both CPU cores to be free, so we any config write must wait until the host interface is idle. + if(pThis->yieldHostInterface == true && pThis->mzControl.persistConfig == true) + { + if(pThis->nvs->persistData(pThis->getClassName(__PRETTY_FUNCTION__), &pThis->mzConfig, sizeof(t_mzConfig)) == false) + { + ESP_LOGW(SELOPTTAG, "Persisting MZ-2500/MZ-2800 configuration data failed, updates will not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(pThis->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + } + + // Clear flag so we dont persist in a loop. + pThis->mzControl.persistConfig = false; + } + + // Yield if the suspend flag is set. + pThis->yield(10); + } +} + +// A method to load the keyboard mapping table into memory for use in the interface mapping logic. If no persistence file exists or an error reading persistence occurs, the keymap +// uses the internal static default. If no persistence file exists and attempt is made to create it with a copy of the inbuilt static map so that future operations all +// work with persistence such that modifications can be made. +// +bool MZ2528::loadKeyMap(void) +{ + // Locals. + // + bool result = false; + int fileRows = 0; + struct stat keyMapFileNameStat; + + // See if the file exists, if it does, get size so we can compute number of mapping rows. + if(stat(mzControl.keyMapFileName.c_str(), &keyMapFileNameStat) == -1) + { + ESP_LOGW(MAINTAG, "No keymap file, using inbuilt definitions."); + } else + { + // Get number of rows in the file. + fileRows = keyMapFileNameStat.st_size/sizeof(t_keyMapEntry); + + // Subsequent reloads, delete memory prior to building new map, primarily to conserve precious resources rather than trying the memory allocation trying to realloc and then having to copy. + if(mzControl.kme != NULL && mzControl.kme != PS2toMZ.kme) + { + delete mzControl.kme; + mzControl.kme = NULL; + } + + // Allocate memory for the new keymap table. + mzControl.kme = new t_keyMapEntry[fileRows]; + if(mzControl.kme == NULL) + { + ESP_LOGW(MAINTAG, "Failed to allocate memory for keyboard map, fallback to inbuilt!"); + } else + { + // Open the keymap extension file for binary reading to add data to our map table. + std::fstream keyFileIn(mzControl.keyMapFileName.c_str(), std::ios::in | std::ios::binary); + + int idx=0; + while(keyFileIn.good()) + { + keyFileIn.read((char *)&mzControl.kme[idx], sizeof(t_keyMapEntry)); + if(keyFileIn.good()) + { + idx++; + } + } + // Any errors, we wind back and use the inbuilt mapping table. + if(keyFileIn.bad()) + { + keyFileIn.close(); + ESP_LOGW(MAINTAG, "Failed to read data from keymap extension file:%s, fallback to inbuilt!", mzControl.keyMapFileName.c_str()); + } else + { + // No longer need the file. + keyFileIn.close(); + + // Max rows in the KME table. + mzControl.kmeRows = fileRows; + + // Good to go, map ready for use with the interface. + result = true; + } + } + } + + // Any failures, free up memory and use the inbuilt mapping table. + if(result == false) + { + if(mzControl.kme != NULL && mzControl.kme != PS2toMZ.kme) + { + delete mzControl.kme; + mzControl.kme = NULL; + } + + // No point allocating memory if no extensions exist or an error occurs, just point to the static table. + mzControl.kme = PS2toMZ.kme; + mzControl.kmeRows = PS2TBL_MZ_MAXROWS; + + // Persist the data so that next load comes from file. + saveKeyMap(); + } + + // Return code. Either memory map was successfully loaded, true or failed, false. + return(result); +} + +// Method to save the current keymap out to an extension file. +// +bool MZ2528::saveKeyMap(void) +{ + // Locals. + // + bool result = false; + int idx = 0; + + // Has a map been defined? Cannot save unless loadKeyMap has been called which sets mzControl.kme to point to the internal keymap or a new memory resident map. + // + if(mzControl.kme == NULL) + { + ESP_LOGW(MAINTAG, "KeyMap hasnt yet been defined, need to call loadKeyMap."); + } else + { + // Request mutex from NVS to prevent it from accessing the NVS - LittleFS is based on NVS. + if(nvs->takeMutex() == true) + { + // Open file for binary writing, trunc specified to clear out the file, we arent appending. + std::fstream keyFileOut(mzControl.keyMapFileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + + // Loop whilst no errors and data rows still not written. + while(keyFileOut.good() && idx < mzControl.kmeRows) + { + keyFileOut.write((char *)&mzControl.kme[idx], sizeof(t_keyMapEntry)); + idx++; + } + if(keyFileOut.bad()) + { + ESP_LOGW(MAINTAG, "Failed to write data from the keymap to file:%s, deleting as state is unknown!", mzControl.keyMapFileName.c_str()); + keyFileOut.close(); + std::remove(mzControl.keyMapFileName.c_str()); + } else + { + // Success. + keyFileOut.close(); + result = true; + } + + // Relinquish mutex now write is complete. + nvs->giveMutex(); + } + } + + // Return code. Either memory map was successfully saved, true or failed, false. + return(result); +} + +// Public method to open a keymap file for data upload. +// This method opens the file and makes any validation checks as necessary. +// +bool MZ2528::createKeyMapFile(std::fstream &outFile) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Attempt to open a temporary keymap file for writing. + // + fileName = mzControl.keyMapFileName; + replaceExt(fileName, "tmp"); + outFile.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to validate and store data provided by caller into an open file created by 'createKeyMapFile'. +// +bool MZ2528::storeDataToKeyMapFile(std::fstream &outFile, char *data, int size) +{ + // Locals. + // + bool result = true; + + // Check that the file is still writeable then add data. + if(outFile.good()) + { + outFile.write(data, size); + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Polymorphic alternative to take a vector of bytes for writing to the output file. +// +bool MZ2528::storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray) +{ + // Locals. + // + bool result = true; + char data[1]; + + // Check that the file is still writeable then add data. Not best for performace but ease of use and minimum memory. + if(outFile.good()) + { + for(std::size_t idx = 0; idx < dataArray.size(); idx++) + { + data[0] = (char)dataArray[idx]; + outFile.write((char *)&data, 1); + } + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to close and commit a data file, created by 'createKeyMapFile' and populated by 'storeDataToKeyMapFile'. +// This involves renaming the original keymap file, closing the new file and renaming it to the original keymap filename. +// +bool MZ2528::closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Check the file is still accessible and close. + // + outFile.close(); + if(!cleanupOnly) + { + if(outFile.good()) + { + // Rename the original file. + fileName = mzControl.keyMapFileName; + replaceExt(fileName, "bak"); + // Remove old backup file. Dont worry if it is not there! + std::remove(fileName.c_str()); + replaceExt(fileName, "tmp"); + // Rename new file to active. + if(std::rename(fileName.c_str(), mzControl.keyMapFileName.c_str()) != 0) + { + result = false; + } + } else + { + result = false; + } + } + + // Send result. + return(result); +} + +// Method to return the keymap column names as header strings. +// +void MZ2528::getKeyMapHeaders(std::vector& headerList) +{ + // Add the names. + // + headerList.push_back(PS2TBL_PS2KEYCODE_NAME); + headerList.push_back(PS2TBL_PS2CTRL_NAME); + headerList.push_back(PS2TBL_KEYBOARDMODEL_NAME); + headerList.push_back(PS2TBL_MACHINE_NAME); + headerList.push_back(PS2TBL_MZ_MK_ROW1_NAME); + headerList.push_back(PS2TBL_MZ_MK_KEY1_NAME); + headerList.push_back(PS2TBL_MZ_MK_ROW2_NAME); + headerList.push_back(PS2TBL_MZ_MK_KEY2_NAME); + headerList.push_back(PS2TBL_MZ_MK_ROW3_NAME); + headerList.push_back(PS2TBL_MZ_MK_KEY3_NAME); + headerList.push_back(PS2TBL_MZ_BRK_ROW1_NAME); + headerList.push_back(PS2TBL_MZ_BRK_KEY1_NAME); + headerList.push_back(PS2TBL_MZ_BRK_ROW2_NAME); + headerList.push_back(PS2TBL_MZ_BRK_KEY2_NAME); + + return; +} + +// A method to return the Type of data for a given column in the KeyMap table. +// +void MZ2528::getKeyMapTypes(std::vector& typeList) +{ + // Add the types. + // + typeList.push_back(PS2TBL_PS2KEYCODE_TYPE); + typeList.push_back(PS2TBL_PS2CTRL_TYPE); + typeList.push_back(PS2TBL_KEYBOARDMODEL_TYPE); + typeList.push_back(PS2TBL_MACHINE_TYPE); + typeList.push_back(PS2TBL_MZ_MK_ROW1_TYPE); + typeList.push_back(PS2TBL_MZ_MK_KEY1_TYPE); + typeList.push_back(PS2TBL_MZ_MK_ROW2_TYPE); + typeList.push_back(PS2TBL_MZ_MK_KEY2_TYPE); + typeList.push_back(PS2TBL_MZ_MK_ROW3_TYPE); + typeList.push_back(PS2TBL_MZ_MK_KEY3_TYPE); + typeList.push_back(PS2TBL_MZ_BRK_ROW1_TYPE); + typeList.push_back(PS2TBL_MZ_BRK_KEY1_TYPE); + typeList.push_back(PS2TBL_MZ_BRK_ROW2_TYPE); + typeList.push_back(PS2TBL_MZ_BRK_KEY2_TYPE); + + return; +} + +// Method to return a list of key:value entries for a given keymap column. This represents the +// feature which can be selected and the value it uses. Features can be combined by ORing the values +// together. +bool MZ2528::getKeyMapSelectList(std::vector>& selectList, std::string option) +{ + // Locals. + // + bool result = true; + + // Build up a map, depending on the list required, of name to value. This list can then be used + // by a user front end to select an option based on a name and return its value. + if(option.compare(PS2TBL_PS2CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_SHIFT, PS2CTRL_SHIFT)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CTRL, PS2CTRL_CTRL)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CAPS, PS2CTRL_CAPS)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_ALT, PS2CTRL_ALT)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_ALTGR, PS2CTRL_ALTGR)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GUI, PS2CTRL_GUI)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_FUNC, PS2CTRL_FUNC)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_EXACT, PS2CTRL_EXACT)); + } + else if(option.compare(PS2TBL_KEYBOARDMODEL_TYPE) == 0) + { + selectList.push_back(std::make_pair(KEYMAP_SEL_STANDARD, KEYMAP_STANDARD)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_WYSE_KB3926, KEYMAP_UK_WYSE_KB3926)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_OADG109, KEYMAP_JAPAN_OADG109)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_SANWA_SKBL1, KEYMAP_JAPAN_SANWA_SKBL1)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_4, KEYMAP_NOT_ASSIGNED_4)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_5, KEYMAP_NOT_ASSIGNED_5)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_6, KEYMAP_NOT_ASSIGNED_6)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_PERIBOARD_810, KEYMAP_UK_PERIBOARD_810)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_OMOTON_K8508, KEYMAP_UK_OMOTON_K8508)); + } + else if(option.compare(PS2TBL_MACHINE_TYPE) == 0) + { + selectList.push_back(std::make_pair(MZ2528_SEL_ALL, MZ_ALL)); + selectList.push_back(std::make_pair(MZ2528_SEL_MZ_80B, MZ_80B)); + selectList.push_back(std::make_pair(MZ2528_SEL_MZ_2000, MZ_2000)); + selectList.push_back(std::make_pair(MZ2528_SEL_MZ_2500, MZ_2500)); + selectList.push_back(std::make_pair(MZ2528_SEL_MZ_2800, MZ_2800)); + } + else if(option.compare(PS2TBL_MZ_MK_ROW1_TYPE) == 0 || option.compare(PS2TBL_MZ_MK_ROW2_TYPE) == 0 || option.compare(PS2TBL_MZ_MK_ROW3_TYPE) == 0 || option.compare(PS2TBL_MZ_BRK_ROW1_TYPE) == 0 || option.compare(PS2TBL_MZ_BRK_ROW2_TYPE) == 0) + { + for(int idx=0; idx < 15; idx++) + { + std::string rowStr = "Strobe_Row_" + std::to_string(idx); + selectList.push_back(std::make_pair(rowStr.c_str(), idx)); + } + selectList.push_back(std::make_pair("Disabled", 255)); + } else + { + // Not found! + result = false; + } + + // Return result, false if the option not found, true otherwise. + // + return(result); +} + + +// Method to read the Keymap array, 1 row at a time and return it to the caller. +// +bool MZ2528::getKeyMapData(std::vector& dataArray, int *row, bool start) +{ + // Locals. + // + bool result = false; + + // If start flag is set, set row to 0. + if(start == true) + { + (*row) = 0; + } + + // Bound check and if still valid, push data onto the vector. + if((*row) >= mzControl.kmeRows) + { + result = true; + } else + { + dataArray.push_back(mzControl.kme[*row].ps2KeyCode); + dataArray.push_back(mzControl.kme[*row].ps2Ctrl); + dataArray.push_back(mzControl.kme[*row].keyboardModel); + dataArray.push_back(mzControl.kme[*row].machine); + for(int idx=0; idx < PS2TBL_MZ_MAX_MKROW; idx++) + { + dataArray.push_back(mzControl.kme[*row].mkRow[idx]); + dataArray.push_back(mzControl.kme[*row].mkKey[idx]); + } + for(int idx=0; idx < PS2TBL_MZ_MAX_BRKROW; idx++) + { + dataArray.push_back(mzControl.kme[*row].brkRow[idx]); + dataArray.push_back(mzControl.kme[*row].brkKey[idx]); + } + (*row) = (*row) + 1; + } + + // True if no more rows, false if additional rows can be read. + return(result); +} + + +// Initialisation routine. Start two threads, one to handle the incoming PS/2 keyboard data and map it, the second to handle the host interface. +void MZ2528::init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Initialise the basic components. + init(hdlNVS, hdlHID); + + // Mode is important for configuring hardware and launching correct interface. + mzControl.mode2500 = (ifMode == 2500 ? true : false); + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); + + // Create a task pinned to core 1 which will fulfill the MZ-2500/2800 interface. This task has the highest priority + // and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means + // all other tasks running on Core 1 will suspend. The PS/2 controller will be serviced with core 0. + // + // Core 1 - MZ Interface + if(mzControl.mode2500) + { + ESP_LOGW(MAINTAG, "Starting mz25if thread..."); + ::xTaskCreatePinnedToCore(&this->mz25Interface, "mz25if", 4096, this, 25, &this->TaskHostIF, 1); + } else + { + ESP_LOGW(MAINTAG, "Starting mz28if thread..."); + ::xTaskCreatePinnedToCore(&this->mz28Interface, "mz28if", 2048, this, (configMAX_PRIORITIES - 1), &this->TaskHostIF, 1); + } + vTaskDelay(1500); + + // Core 0 - Application + // HID Interface handler thread. + ESP_LOGW(MAINTAG, "Starting hidInterface thread..."); + ::xTaskCreatePinnedToCore(&this->hidInterface, "hidIf", 4096, this, 0, &this->TaskHIDIF, 0); + vTaskDelay(1500); +} + +// Initialisation routine without hardware. +void MZ2528::init(NVS *hdlNVS, HID *hdlHID) +{ + // Initialise control variables. + mzControl.strobeAll = 0xFF; + mzControl.strobeAllAsGPIO = 0x00000000; + for(int idx=0; idx < NUMELEM(mzControl.keyMatrix); idx++) { mzControl.keyMatrix[idx] = 0xFF; } + for(int idx=0; idx < NUMELEM(mzControl.keyMatrixAsGPIO); idx++) { mzControl.keyMatrixAsGPIO[idx] = 0x00000000; } + mzControl.mode2500 = true; + mzControl.optionSelect = false; + mzControl.keyMapFileName = mzControl.fsPath.append("/").append(MZ2528IF_KEYMAP_FILE); + mzControl.kmeRows = 0; + mzControl.kme = NULL; + mzControl.noKeyPressed = true; + mzControl.persistConfig = false; + yieldHostInterface = true; + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); + + // Load the keyboard mapping table into memory. If the file doesnt exist, create it. + loadKeyMap(); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(getClassName(__PRETTY_FUNCTION__), &this->mzConfig, sizeof(t_mzConfig)) == false) + { + ESP_LOGW(MAINTAG, "MZ-2500/MZ-2800 configuration set to default, no valid config in NVS found."); + mzConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + mzConfig.params.activeMachineModel = (mzControl.mode2500 ? MZ_2500 : MZ_2800); + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->mzConfig, sizeof(t_mzConfig)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Default MZ-2500/MZ-2800 configuration data failed, check NVS setup.\n"); + } + // Commit data, ensuring values are written to NVS and the mutex is released. + else if(nvs->commitData() == false) + { + ESP_LOGW(MAINTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } +} + +// Constructor, basically initialise the Singleton interface and let the threads loose. +MZ2528::MZ2528(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->mzControl.fsPath = fsPath; + + // Initialise the interface. + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, initialise the Singleton interface without hardware. +MZ2528::MZ2528(NVS *hdlNVS, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->mzControl.fsPath = fsPath; + + // Initialise the interface. + init(hdlNVS, hdlHID); +} + +// Constructor, used for version reporting so no hardware is initialised. +MZ2528::MZ2528(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +MZ2528::~MZ2528(void) +{ + return; +} diff --git a/main/MZ5665.cpp b/main/MZ5665.cpp deleted file mode 120000 index 08ec66d..0000000 --- a/main/MZ5665.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/MZ5665.cpp \ No newline at end of file diff --git a/main/MZ5665.cpp b/main/MZ5665.cpp new file mode 100644 index 0000000..f1677b3 --- /dev/null +++ b/main/MZ5665.cpp @@ -0,0 +1,1052 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: MZ5665.cpp +// Created: Apr 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: HID (PS/2 or BT keyboard) to Sharp MZ-5600/MZ-6500 series Interface logic. +// This source file contains the singleton class containing logic to obtain +// PS/2 or BT scan codes, map them into Sharp MZ-5600/MZ-6500 keys and transmit the key +// to the MZ-5600/MZ-6500 host. +// +// The class uses a modified version of the PS2KeyAdvanced +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The whole application of which this class is a member, uses the Espressif Development +// environment with Arduino components. This is necessary for the PS2KeyAdvanced class, +// which I may in future convert to use esp-idf library calls rather than Arduino. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Apr 2022 - Initial framework, waiting on arrival of real machine to progress further. +// v1.01 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sys/stat.h" +#include "esp_littlefs.h" +#include "PS2KeyAdvanced.h" +#include "sdkconfig.h" +#include "MZ5665.h" + +// Tag for ESP main application logging. +#define MAINTAG "mz5665key" + +// FreeRTOS Queue handle to pass messages from the HID Keyboard Mapper into the MZ5665 transmission logic. +static QueueHandle_t xmitQueue; + +// MZ-5600/MZ-6500 Protocol +// ------------------------ +// + + +// Function to push a keycode onto the key queue ready for transmission. +// +void MZ5665::pushKeyToQueue(uint32_t key) +{ + // Locals. + t_xmitQueueMessage xmitMsg; + #define PUSHKEYTAG "pushKeyToQueue" + + xmitMsg.keyCode = key; + if( xQueueSend(xmitQueue, (void *)&xmitMsg, 10) != pdPASS) + { + ESP_LOGW(PUSHKEYTAG, "Failed to put scancode:%04x into xmitQueue", key); + } + return; +} + +// Method to realise the MZ-5600/MZ-6500 4 wire serial protocol in order to transmit key presses to the +// MZ-5600/MZ-6500. +// This method uses Core 1 and it will hold it in a spinlock as necessary to ensure accurate timing. +// A key is passed into the method via the FreeRTOS Queue handle xmitQueue. +IRAM_ATTR void MZ5665::mzInterface( void * pvParameters ) +{ + // Locals. + //t_xmitQueueMessage rcvMsg; + + // Mask values declared as variables, let the optimiser decide wether they are constants or placed in-memory. +// uint32_t X1DATA_MASK = (1 << CONFIG_HOST_KDO0); + //uint64_t delayTimer = 0LL; + //uint64_t curTime = 0LL; + //bool bitStart = true; + //uint32_t bitCount = 0; + //enum XMITSTATE { + // FSM_IDLE = 0, + // FSM_STARTXMIT = 1, + // FSM_HEADER = 2, + // FSM_START = 3, + // FSM_DATA = 4, + // FSM_STOP = 5, + // FSM_ENDXMIT = 6 + //} state = FSM_IDLE; + + // Retrieve pointer to object in order to access data. + MZ5665* pThis = (MZ5665*)pvParameters; + + // Initialise the MUTEX which prevents this core from being released to other tasks. + pThis->mzMutex = portMUX_INITIALIZER_UNLOCKED; + + // Initial delay needed because the xQueue will assert probably on a suspended task ALL if delay not inserted! + vTaskDelay(1000); + + // Sign on. + ESP_LOGW(MAINTAG, "Starting MZ-6500 thread."); + +// // X1 data out default state is high. +// GPIO.out_w1ts = X1DATA_MASK; +// +// // Configure a timer to be used for X1 protocol spacing with 1uS resolution. The default clock source is the APB running at 80MHz. +// timer_config_t timerConfig = { +// .alarm_en = TIMER_ALARM_DIS, // No alarm, were not using interrupts as we are in a dedicated thread. +// .counter_en = TIMER_PAUSE, // Timer paused until required. +// .intr_type = TIMER_INTR_LEVEL, // No interrupts used. +// .counter_dir = TIMER_COUNT_UP, // Timing a fixed period. +// .auto_reload = TIMER_AUTORELOAD_DIS, // No need for auto reload, fixed time period. +// .divider = 80 // 1Mhz operation giving 1uS resolution. +// }; +// ESP_ERROR_CHECK(timer_init(TIMER_GROUP_0, TIMER_0, &timerConfig)); +// ESP_ERROR_CHECK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0)); +// +// // Permanent loop, wait for an incoming message on the key to send queue, read it then transmit to the X1, repeat! +// for(;;) +// { +// // Get the current timer value, only run the FSM when the timer is idle. +// timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &curTime); +// if(curTime >= delayTimer) +// { +// // Ensure the timer is stopped. +// timer_pause(TIMER_GROUP_0, TIMER_0); +// delayTimer = 0LL; +// +// // Finite state machine to retrieve a key for transmission then serialise it according to the X1 protocol. +// switch(state) +// { +// case FSM_IDLE: +// // Yield if the suspend flag is set. +// pThis->yield(0); +// +// // Check stack space, report if it is getting low. +// if(uxTaskGetStackHighWaterMark(NULL) < 1024) +// { +// ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); +// } +// +// // If a new message arrives, start the serialiser to send it to the X1. +// if(xQueueReceive(xmitQueue, (void *)&rcvMsg, 0) == pdTRUE) +// { +// ESP_LOGW(MAINTAG, "Received:%08x, %d", rcvMsg.keyCode, rcvMsg.modeB); +// state = FSM_STARTXMIT; +// +// // Create, initialise and hold a spinlock so the current core is bound to this one method. +// portENTER_CRITICAL(&pThis->mzMutex); +// } +// break; +// +// case FSM_STARTXMIT: +// // Ensure all variables and states correct before entering serialisation. +// bitStart = true; +// GPIO.out_w1ts = X1DATA_MASK; +// state = FSM_HEADER; +// if(rcvMsg.modeB) +// bitCount = 24; +// else +// bitCount = 16; +// break; +// +// case FSM_HEADER: +// if(bitStart) +// { +// // Send out the header by bringing X1DATA low for 1000us then high for 700uS. +// GPIO.out_w1tc = X1DATA_MASK; +// delayTimer = pThis->mzCtrl.modeB ? 400LL : 1000LL; +// } else +// { +// // Bring high for 700us. +// GPIO.out_w1ts = X1DATA_MASK; +// delayTimer = pThis->mzCtrl.modeB ? 200LL : 700LL; +// state = FSM_DATA; // Jump past the Start Bit, I think the header is the actual start bit as there is an error in the X1 Center specs. +// } +// bitStart = !bitStart; +// break; +// +// // The original X1 Center specification shows a start bit but this doesnt seem necessary, in fact it is interpreted as a data bit, hence the +// // FSM jumps this state. +// case FSM_START: +// if(bitStart) +// { +// // Send out the start bit by bringing X1DATA low for 250us then high for 750uS. +// GPIO.out_w1tc = X1DATA_MASK; +// delayTimer = pThis->mzCtrl.modeB ? 250LL : 250LL; +// } else +// { +// // Bring high for 750us. +// GPIO.out_w1ts = X1DATA_MASK; +// delayTimer = pThis->mzCtrl.modeB ? 250LL : 750LL; +// state = FSM_DATA; +// } +// bitStart = !bitStart; +// break; +// +// case FSM_DATA: +// if(bitCount > 0) +// { +// if(bitStart) +// { +// // Send out the data bit by bringing X1DATA low for 250us then high for 1750uS when bit = 1 else 750uS when bit = 0. +// GPIO.out_w1tc = X1DATA_MASK; +// delayTimer = 250LL; +// delayTimer = pThis->mzCtrl.modeB ? 250LL : 250LL; +// } else +// { +// // Bring X1DATA high... +// GPIO.out_w1ts = X1DATA_MASK; +// +// // ... Mode A 1750us as bit = 1, mode B 750uS. +// if((rcvMsg.modeB && rcvMsg.keyCode & 0x800000) || (!rcvMsg.modeB && rcvMsg.keyCode & 0x8000)) +// { +// delayTimer = pThis->mzCtrl.modeB ? 750LL : 1750LL; +// } else +// // ... Mode A 750us as bit = 0, mode B 250uS. +// { +// delayTimer = pThis->mzCtrl.modeB ? 250LL : 750LL; +// } +// rcvMsg.keyCode = (rcvMsg.keyCode << 1); +// bitCount--; +// } +// bitStart = !bitStart; +// } else +// { +// state = FSM_STOP; +// } +// break; +// +// case FSM_STOP: +// if(bitStart) +// { +// // Send out the stop bit, same in Mode A and B, by bringing X1DATA low for 250us then high for 250uS. +// GPIO.out_w1tc = X1DATA_MASK; +// delayTimer = 250LL; +// delayTimer = pThis->mzCtrl.modeB ? 250LL : 250LL; +// } else +// { +// // Bring high for 250us. +// GPIO.out_w1ts = X1DATA_MASK; +// delayTimer = pThis->mzCtrl.modeB ? 250LL : 250LL; +// state = FSM_ENDXMIT; +// } +// bitStart = !bitStart; +// break; +// +// case FSM_ENDXMIT: +// // End of critical timing loop, release the core. +// portEXIT_CRITICAL(&pThis->mzMutex); +// state = FSM_IDLE; +// break; +// +// } +// +// // If a new delay is requested, set the value into the timer and start. +// if(delayTimer > 0LL) +// { +// timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0LL); +// timer_start(TIMER_GROUP_0, TIMER_0); +// } +// } +// +// // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. +// //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable +// //TIMERG0.wdt_feed=1; // feed dog +// //TIMERG0.wdt_wprotect=0; // write protect +// //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable +// //TIMERG1.wdt_feed=1; // feed dog +// //TIMERG1.wdt_wprotect=0; // write protect +// } +} + +// Method to select keyboard configuration options. When a key sequence is pressed, ie. SHIFT+CTRL+ESC then the fourth simultaneous key is the required option and given to this +// method to act on. Options can be machine model, keyboard map etc. +// +void MZ5665::selectOption(uint8_t optionCode) +{ + // Locals. + // + bool updated = true; + #define SELOPTTAG "selectOption" + + // Simple switch to decode the required option and act on it. + switch(optionCode) + { + // Select a keymap using 1..8 or default (STANDARD) using 0. + case PS2_KEY_1: + this->mzConfig.params.activeKeyboardMap = KEYMAP_UK_WYSE_KB3926; + break; + case PS2_KEY_2: + this->mzConfig.params.activeKeyboardMap = KEYMAP_JAPAN_OADG109; + break; + case PS2_KEY_3: + this->mzConfig.params.activeKeyboardMap = KEYMAP_JAPAN_SANWA_SKBL1; + break; + case PS2_KEY_4: + this->mzConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_4; + break; + case PS2_KEY_5: + this->mzConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_5; + break; + case PS2_KEY_6: + this->mzConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_6; + break; + case PS2_KEY_7: + this->mzConfig.params.activeKeyboardMap = KEYMAP_UK_PERIBOARD_810; + break; + case PS2_KEY_8: + this->mzConfig.params.activeKeyboardMap = KEYMAP_UK_OMOTON_K8508; + break; + case PS2_KEY_0: + this->mzConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + break; + +// // Select the model of the host to enable specific mappings. +// case PS2_KEY_END: +// this->mzConfig.params.activeMachineModel = X1_ORIG; +// break; +// case PS2_KEY_DN_ARROW: +// this->mzConfig.params.activeMachineModel = X1_TURBO; +// break; +// case PS2_KEY_PGDN: +// this->mzConfig.params.activeMachineModel = X1_TURBOZ; +// break; +// case PS2_KEY_INSERT: +// this->mzConfig.params.activeMachineModel = X1_ALL; +// break; +// +// // Switch to keyboard Mode A. This mode is not persisted. +// case PS2_KEY_HOME: +// updated = false; +// this->mzCtrl.modeB = false; +// break; +// // Switch to keyboard Mode B. This mode is not persisted. +// case PS2_KEY_PGUP: +// updated = false; +// this->mzCtrl.modeB = true; +// break; +// + // Unknown option so ignore. + default: + updated = false; + break; + } + + // If an update was made, persist it for power cycles. + // + if(updated) + { + if(this->nvs->persistData(this->getClassName(__PRETTY_FUNCTION__), &this->mzConfig, sizeof(t_mzConfig)) == false) + { + ESP_LOGW(SELOPTTAG, "Persisting MZ-6500 configuration data failed, updates will not persist in future power cycles."); + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(this->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + } + } + + return; +} + +// Method to take a PS/2 key and control data and map it into an MZ-6500 key and control equivalent, updating state values accordingly (ie. CAPS). +// A mapping table is used which maps a key and state values into an MZ-6500 key and control values, the emphasis being on readability and easy configuration +// as opposed to concatenated byte tables. +// +uint32_t MZ5665::mapKey(uint16_t scanCode) +{ + // Locals. + uint32_t idx; + uint8_t keyCode = (scanCode & 0xFF); + bool mapped = false; + bool matchExact = false; + uint32_t mappedKey = 0x00000000; + #define MAPKEYTAG "mapKey" + + // Intercept control keys and set state variables. + // + // + if(scanCode & PS2_BREAK) + { + // if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT) == 0) { mapped=true; this->mzCtrl.keyCtrl |= X1_CTRL_SHIFT; } + // if((keyCode == PS2_KEY_L_CTRL || keyCode == PS2_KEY_R_CTRL) && (scanCode & PS2_CTRL) == 0) { mapped=true; this->mzCtrl.keyCtrl |= X1_CTRL_CTRL; } + + // Any break key clears the option select flag. + this->mzCtrl.optionSelect = false; + + // Clear any feature LED blinking. + led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } else + { + // if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT)) { mapped=true; this->mzCtrl.keyCtrl &= ~X1_CTRL_SHIFT; } + // if((keyCode == PS2_KEY_L_CTRL || keyCode == PS2_KEY_R_CTRL) && (scanCode & PS2_CTRL)) { mapped=true; this->mzCtrl.keyCtrl &= ~X1_CTRL_CTRL; } + // if(keyCode == PS2_KEY_L_ALT) { mapped = true; this->mzCtrl.keyCtrl ^= X1_CTRL_KANA; } + // if(keyCode == PS2_KEY_R_ALT) { mapped = true; this->mzCtrl.keyCtrl ^= X1_CTRL_GRAPH; } + // if(keyCode == PS2_KEY_CAPS) { mapped = true; this->mzCtrl.keyCtrl ^= X1_CTRL_CAPS; } + // Special mapping to allow selection of keyboard options. If the user presses CTRL+SHIFT+ESC then a flag becomes active and should a fourth key be pressed before a BREAK then the fourth key is taken as an option key and processed accordingly. + if(this->mzCtrl.optionSelect == true) { mapped = true; this->mzCtrl.optionSelect = false; selectOption(keyCode); } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT)) { mapped = true; this->mzCtrl.optionSelect = true; } + if(this->mzCtrl.optionSelect == true && keyCode != PS2_KEY_ESC) + { + mapped = true; + this->mzCtrl.optionSelect = false; + selectOption(keyCode); + } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT) && this->mzCtrl.optionSelect == false) + { + // Prime flag ready for fourth option key and start LED blinking periodically. + mapped = true; + this->mzCtrl.optionSelect = true; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_50, 1, 500L, 500L); + } + } + + // If the key already mapped, ie. due to control signals, send the update as <0x00> so the MZ5665 knows the current control signal state. + if(mapped == true) + { + ESP_LOGW(MAPKEYTAG, "Mapped special key:%02x\n", this->mzCtrl.keyCtrl); + mappedKey = (this->mzCtrl.keyCtrl << 8) | 0x00; + } else + { + // Loop through the entire conversion table to find a match on this key, if found map to MZ5665 equivalent. + // switch matrix. + // + for(idx=0, mapped=false, matchExact=false; idx < mzCtrl.kmeRows && (mapped == false || (mapped == true && matchExact == false)); idx++) + { + // Match key code? Make sure the current machine and keymap match as well. + if(mzCtrl.kme[idx].ps2KeyCode == (uint8_t)(scanCode&0xFF) && ((mzCtrl.kme[idx].machine == MZ5665_ALL) || ((mzCtrl.kme[idx].machine & mzConfig.params.activeMachineModel) != 0)) && ((mzCtrl.kme[idx].keyboardModel & mzConfig.params.activeKeyboardMap) != 0)) + { + // If CAPS lock is set in the table and in the scanCode, invert SHIFT so we send the correct value. + if((scanCode & PS2_CAPS) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) != 0) + { + scanCode ^= PS2_SHIFT; + } + + // Match Raw, Shift, Function, Control, ALT or ALT-Gr? + if( (((mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0) && ((mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0) && ((mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA) == 0) && ((mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) == 0) && ((mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0) && ((mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) || + ((scanCode & PS2_SHIFT) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || + ((scanCode & PS2_CTRL) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || + // ((this->mzCtrl.keyCtrl & X1_CTRL_KANA) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA) != 0) || + // ((this->mzCtrl.keyCtrl & X1_CTRL_GRAPH) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) != 0) || + ((scanCode & PS2_GUI) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || + ((scanCode & PS2_FUNCTION) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) ) + { + + // Exact entry match, data + control key? On an exact match we only process the first key. On a data only match we fall through to include additional data and control key matches to allow for un-mapped key combinations, ie. Japanese characters. + matchExact = (((scanCode & PS2_SHIFT) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || ((scanCode & PS2_SHIFT) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0)) && + (((scanCode & PS2_CTRL) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || ((scanCode & PS2_CTRL) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0)) && + // (((this->mzCtrl.keyCtrl & X1_CTRL_KANA) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA) != 0) || ((this->mzCtrl.keyCtrl & X1_CTRL_KANA) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA) == 0)) && + // (((this->mzCtrl.keyCtrl & X1_CTRL_GRAPH) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) != 0) || ((this->mzCtrl.keyCtrl & X1_CTRL_GRAPH) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) == 0)) && + (((scanCode & PS2_GUI) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || ((scanCode & PS2_GUI) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0)) && + (((scanCode & PS2_FUNCTION) && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) || ((scanCode & PS2_FUNCTION) == 0 && (mzCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) + ? true : false; + + // RELEASE (PS2_BREAK == 1) or PRESS? + if((scanCode & PS2_BREAK)) + { + // Special case for the PAUSE / BREAK key. The underlying logic has been modified to send a BREAK key event immediately + // after a PAUSE make, this is necessary as the Sharp machines require SHIFT (pause) BREAK so the PS/2 CTRL+BREAK wont + // work (unless logic is added to insert a SHIFT, pause, add BREAK). The solution was to generate a BREAK event + // when SHIFT+PAUSE is pressed. + if(keyCode == PS2_KEY_PAUSE) + { + vTaskDelay(100); + } + + // Mode A sends a release with 0x00. + // if(this->mzCtrl.modeB == false) + // { + // mappedKey = (0xFF << 8) | 0x00; + // mapped = true; + // // vTaskDelay(300); + // } else + // if(this->mzCtrl.modeB == true) + // { + // Clear only the bits relevant to the released key. + // mappedKey &= ((mzCtrl.kme[idx].x1Ctrl << 16) | (mzCtrl.kme[idx].x1Key2 << 8) | mzCtrl.kme[idx].x1Key); + // } + } else + { + // Mode A return the key in the table, mode B OR the key to build up a final map. + // if(this->mzCtrl.modeB == false) + // mappedKey = ((mzCtrl.kme[idx].x1Ctrl & this->mzCtrl.keyCtrl) << 8) | mzCtrl.kme[idx].x1Key; + // else + // mappedKey |= ((mzCtrl.kme[idx].x1Ctrl << 16) | (mzCtrl.kme[idx].x1Key2 << 8) | mzCtrl.kme[idx].x1Key); + // mapped = true; + } + } + } + } + } + return(mappedKey); +} + +// Primary HID thread, running on Core 0. +// This thread is responsible for receiving HID (PS/2 or BT) keyboard scan codes and mapping them to Sharp MZ5665 equivalent keys, updating state flags as needed. +// The HID data is received via interrupt. The data to be sent to the MZ5665 is pushed onto a FIFO queue. +// +IRAM_ATTR void MZ5665::hidInterface( void * pvParameters ) +{ + // Locals. + uint16_t scanCode = 0x0000; + uint32_t mzKey = 0x00000000; + + // Map the instantiating object so we can access its methods and data. + MZ5665* pThis = (MZ5665*)pvParameters; + + // Thread never exits, just polls the keyboard and updates the matrix. + while(1) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // Check for HID keyboard scan codes. + while((scanCode = pThis->hid->read()) != 0) + { + // Scan Code Breakdown: + // Define name bit description + // PS2_BREAK 15 1 = Break key code + // (MSB) 0 = Make Key code + // PS2_SHIFT 14 1 = Shift key pressed as well (either side) + // 0 = No shift key + // PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + // 0 = No Ctrl key + // PS2_CAPS 12 1 = Caps Lock ON + // 0 = Caps lock OFF + // PS2_ALT 11 1 = Left Alt key pressed as well + // 0 = No Left Alt key + // PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + // 0 = No Right Alt key + // PS2_GUI 9 1 = GUI key pressed as well (either) + // 0 = No GUI key + // PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + // 0 = standard character key + // 7-0 PS/2 Key code. + // + // BREAK code means all keys released so clear out flags and send update. + ESP_LOGW(MAPKEYTAG, "SCANCODE:%04x",scanCode); + + // Map the PS/2 key to an MZ5665 CTRL + KEY + mzKey = pThis->mapKey(scanCode); + if(mzKey != 0L) { pThis->pushKeyToQueue(mzKey); } + + // Toggle LED to indicate data flow. + if((scanCode & PS2_BREAK) == 0) + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 1, 100L, 0L); + } + + // Yield if the suspend flag is set. + pThis->yield(10); + } +} + +// A method to load the keyboard mapping table into memory for use in the interface mapping logic. If no persistence file exists or an error reading persistence occurs, the keymap +// uses the internal static default. If no persistence file exists and attempt is made to create it with a copy of the inbuilt static map so that future operations all +// work with persistence such that modifications can be made. +// +bool MZ5665::loadKeyMap(void) +{ + // Locals. + // + bool result = false; + int fileRows = 0; + struct stat keyMapFileNameStat; + + // See if the file exists, if it does, get size so we can compute number of mapping rows. + if(stat(mzCtrl.keyMapFileName.c_str(), &keyMapFileNameStat) == -1) + { + ESP_LOGW(MAINTAG, "No keymap file, using inbuilt definitions."); + } else + { + // Get number of rows in the file. + fileRows = keyMapFileNameStat.st_size/sizeof(t_keyMapEntry); + + // Subsequent reloads, delete memory prior to building new map, primarily to conserve precious resources rather than trying the memory allocation trying to realloc and then having to copy. + if(mzCtrl.kme != NULL && mzCtrl.kme != PS2toMZ5665.kme) + { + delete mzCtrl.kme; + mzCtrl.kme = NULL; + } + + // Allocate memory for the new keymap table. + mzCtrl.kme = new t_keyMapEntry[fileRows]; + if(mzCtrl.kme == NULL) + { + ESP_LOGW(MAINTAG, "Failed to allocate memory for keyboard map, fallback to inbuilt!"); + } else + { + // Open the keymap extension file for binary reading to add data to our map table. + std::fstream keyFileIn(mzCtrl.keyMapFileName.c_str(), std::ios::in | std::ios::binary); + + int idx=0; + while(keyFileIn.good()) + { + keyFileIn.read((char *)&mzCtrl.kme[idx], sizeof(t_keyMapEntry)); + if(keyFileIn.good()) + { + idx++; + } + } + // Any errors, we wind back and use the inbuilt mapping table. + if(keyFileIn.bad()) + { + keyFileIn.close(); + ESP_LOGW(MAINTAG, "Failed to read data from keymap extension file:%s, fallback to inbuilt!", mzCtrl.keyMapFileName.c_str()); + } else + { + // No longer need the file. + keyFileIn.close(); + + // Max rows in the KME table. + mzCtrl.kmeRows = fileRows; + + // Good to go, map ready for use with the interface. + result = true; + } + } + } + + // Any failures, free up memory and use the inbuilt mapping table. + if(result == false) + { + if(mzCtrl.kme != NULL && mzCtrl.kme != PS2toMZ5665.kme) + { + delete mzCtrl.kme; + mzCtrl.kme = NULL; + } + + // No point allocating memory if no extensions exist or an error occurs, just point to the static table. + mzCtrl.kme = PS2toMZ5665.kme; + mzCtrl.kmeRows = PS2TBL_MZ5665_MAXROWS; + + // Persist the data so that next load comes from file. + saveKeyMap(); + } + + // Return code. Either memory map was successfully loaded, true or failed, false. + return(result); +} + +// Method to save the current keymap out to an extension file. +// +bool MZ5665::saveKeyMap(void) +{ + // Locals. + // + bool result = false; + int idx = 0; + + // Has a map been defined? Cannot save unless loadKeyMap has been called which sets mzCtrl.kme to point to the internal keymap or a new memory resident map. + // + if(mzCtrl.kme == NULL) + { + ESP_LOGW(MAINTAG, "KeyMap hasnt yet been defined, need to call loadKeyMap."); + } else + { + // Open file for binary writing, trunc specified to clear out the file, we arent appending. + std::fstream keyFileOut(mzCtrl.keyMapFileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + + // Loop whilst no errors and data rows still not written. + while(keyFileOut.good() && idx < mzCtrl.kmeRows) + { + keyFileOut.write((char *)&mzCtrl.kme[idx], sizeof(t_keyMapEntry)); + idx++; + } + if(keyFileOut.bad()) + { + ESP_LOGW(MAINTAG, "Failed to write data from the keymap to file:%s, deleting as state is unknown!", mzCtrl.keyMapFileName.c_str()); + keyFileOut.close(); + std::remove(mzCtrl.keyMapFileName.c_str()); + } else + { + // Success. + keyFileOut.close(); + result = true; + } + } + + // Return code. Either memory map was successfully saved, true or failed, false. + return(result); +} + +// Public method to open a keymap file for data upload. +// This method opens the file and makes any validation checks as necessary. +// +bool MZ5665::createKeyMapFile(std::fstream &outFile) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Attempt to open a temporary keymap file for writing. + // + fileName = mzCtrl.keyMapFileName; + replaceExt(fileName, "tmp"); + outFile.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to validate and store data provided by caller into an open file created by 'createKeyMapFile'. +// +bool MZ5665::storeDataToKeyMapFile(std::fstream &outFile, char *data, int size) +{ + // Locals. + // + bool result = true; + + // Check that the file is still writeable then add data. + if(outFile.good()) + { + outFile.write(data, size); + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Polymorphic alternative to take a vector of bytes for writing to the output file. +// +bool MZ5665::storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray) +{ + // Locals. + // + bool result = true; + char data[1]; + + // Check that the file is still writeable then add data. Not best for performace but ease of use and minimum memory. + if(outFile.good()) + { + for(std::size_t idx = 0; idx < dataArray.size(); idx++) + { + data[0] = (char)dataArray[idx]; + outFile.write((char *)&data, 1); + } + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to close and commit a data file, created by 'createKeyMapFile' and populated by 'storeDataToKeyMapFile'. +// This involves renaming the original keymap file, closing the new file and renaming it to the original keymap filename. +// +bool MZ5665::closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Check the file is still accessible and close. + // + outFile.close(); + if(!cleanupOnly) + { + if(outFile.good()) + { + // Rename the original file. + fileName = mzCtrl.keyMapFileName; + replaceExt(fileName, "bak"); + // Remove old backup file. Dont worry if it is not there! + std::remove(fileName.c_str()); + replaceExt(fileName, "tmp"); + // Rename new file to active. + if(std::rename(fileName.c_str(), mzCtrl.keyMapFileName.c_str()) != 0) + { + result = false; + } + } else + { + result = false; + } + } + + // Send result. + return(result); +} + +// Method to return the keymap column names as header strings. +// +void MZ5665::getKeyMapHeaders(std::vector& headerList) +{ + // Add the names. + // + headerList.push_back(PS2TBL_PS2KEYCODE_NAME); + headerList.push_back(PS2TBL_PS2CTRL_NAME); + headerList.push_back(PS2TBL_KEYBOARDMODEL_NAME); + headerList.push_back(PS2TBL_MACHINE_NAME); +// headerList.push_back(PS2TBL_X1MODE_NAME); +// headerList.push_back(PS2TBL_X1KEYCODE_NAME); +// headerList.push_back(PS2TBL_X1KEYCODE_BYTE2_NAME); +// headerList.push_back(PS2TBL_X1_CTRL_NAME); + + return; +} + +// A method to return the Type of data for a given column in the KeyMap table. +// +void MZ5665::getKeyMapTypes(std::vector& typeList) +{ + // Add the types. + // + typeList.push_back(PS2TBL_PS2KEYCODE_TYPE); + typeList.push_back(PS2TBL_PS2CTRL_TYPE); + typeList.push_back(PS2TBL_KEYBOARDMODEL_TYPE); + typeList.push_back(PS2TBL_MACHINE_TYPE); +// typeList.push_back(PS2TBL_X1MODE_TYPE); +// typeList.push_back(PS2TBL_X1KEYCODE_TYPE); +// typeList.push_back(PS2TBL_X1KEYCODE_BYTE2_TYPE); +// typeList.push_back(PS2TBL_X1CTRL_TYPE); + + return; +} + +// Method to return a list of key:value entries for a given keymap column. This represents the +// feature which can be selected and the value it uses. Features can be combined by ORing the values +// together. +bool MZ5665::getKeyMapSelectList(std::vector>& selectList, std::string option) +{ + // Locals. + // + bool result = true; + + // Build up a map, depending on the list required, of name to value. This list can then be used + // by a user front end to select an option based on a name and return its value. + if(option.compare(PS2TBL_PS2CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_SHIFT, PS2CTRL_SHIFT)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CTRL, PS2CTRL_CTRL)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CAPS, PS2CTRL_CAPS)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_KANA, PS2CTRL_KANA)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GRAPH, PS2CTRL_GRAPH)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GUI, PS2CTRL_GUI)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_FUNC, PS2CTRL_FUNC)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_EXACT, PS2CTRL_EXACT)); + } + else if(option.compare(PS2TBL_KEYBOARDMODEL_TYPE) == 0) + { + selectList.push_back(std::make_pair(KEYMAP_SEL_STANDARD, KEYMAP_STANDARD)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_WYSE_KB3926, KEYMAP_UK_WYSE_KB3926)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_OADG109, KEYMAP_JAPAN_OADG109)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_SANWA_SKBL1, KEYMAP_JAPAN_SANWA_SKBL1)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_4, KEYMAP_NOT_ASSIGNED_4)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_5, KEYMAP_NOT_ASSIGNED_5)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_6, KEYMAP_NOT_ASSIGNED_6)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_PERIBOARD_810, KEYMAP_UK_PERIBOARD_810)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_OMOTON_K8508, KEYMAP_UK_OMOTON_K8508)); + } + else if(option.compare(PS2TBL_MACHINE_TYPE) == 0) + { + selectList.push_back(std::make_pair(MZ5665_SEL_ALL, MZ5665_ALL)); + } + else if(option.compare(PS2TBL_MZ5665_CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(MZ5665_CTRL_SEL_GRAPH, MZ5665_CTRL_GRAPH)); + selectList.push_back(std::make_pair(MZ5665_CTRL_SEL_CAPS, MZ5665_CTRL_CAPS)); + selectList.push_back(std::make_pair(MZ5665_CTRL_SEL_KANA, MZ5665_CTRL_KANA)); + selectList.push_back(std::make_pair(MZ5665_CTRL_SEL_SHIFT, MZ5665_CTRL_SHIFT)); + selectList.push_back(std::make_pair(MZ5665_CTRL_SEL_CTRL, MZ5665_CTRL_CTRL)); + } else + { + // Not found! + result = false; + } + + // Return result, false if the option not found, true otherwise. + // + return(result); +} + + +// Method to read the Keymap array, 1 row at a time and return it to the caller. +// +bool MZ5665::getKeyMapData(std::vector& dataArray, int *row, bool start) +{ + // Locals. + // + bool result = false; + + // If start flag is set, set row to 0. + if(start == true) + { + (*row) = 0; + } + + // Bound check and if still valid, push data onto the vector. + if((*row) >= mzCtrl.kmeRows) + { + result = true; + } else + { + dataArray.push_back(mzCtrl.kme[*row].ps2KeyCode); + dataArray.push_back(mzCtrl.kme[*row].ps2Ctrl); + dataArray.push_back(mzCtrl.kme[*row].keyboardModel); + dataArray.push_back(mzCtrl.kme[*row].machine); + // dataArray.push_back(mzCtrl.kme[*row].x1Mode); + // dataArray.push_back(mzCtrl.kme[*row].x1Key); + // dataArray.push_back(mzCtrl.kme[*row].x1Key2); + // dataArray.push_back(mzCtrl.kme[*row].x1Ctrl); + (*row) = (*row) + 1; + } + + // True if no more rows, false if additional rows can be read. + return(result); +} + + +// Initialisation routine. Start two threads, one to handle the incoming PS/2 keyboard data and map it, the second to handle the host interface. +void MZ5665::init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Basic initialisation. + init(hdlNVS, hdlHID); + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); + + // Create a task pinned to core 1 which will fulfill the Sharp MZ-6500 interface. This task has the highest priority + // and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means + // all other tasks running on Core 1 will suspend as needed. The PS/2 controller will be serviced with core 0. + // + // Core 1 - MZ-6500 Interface + ESP_LOGW(MAINTAG, "Starting mz5600/mz6500 if thread..."); + ::xTaskCreatePinnedToCore(&this->mzInterface, "mzif", 4096, this, 25, &this->TaskHostIF, 1); + vTaskDelay(500); + + // Core 0 - Application + // HID Interface handler thread. + ESP_LOGW(MAINTAG, "Starting hidIf thread..."); + ::xTaskCreatePinnedToCore(&this->hidInterface, "hidIf", 8192, this, 22, &this->TaskHIDIF, 0); + + // Create queue for buffering incoming keys prior to transmitting to the MZ-6500. + xmitQueue = xQueueCreate(MAX_MZ5665_XMIT_KEY_BUF, sizeof(t_xmitQueueMessage)); +} + +// Initialisation routine without hardware. +void MZ5665::init(NVS *hdlNVS, HID *hdlHID) +{ + // Initialise control variables. + this->mzCtrl.keyCtrl = 0xFF; // Negative logic, 0 - active, 1 = inactive. + mzCtrl.optionSelect = false; + mzCtrl.keyMapFileName = mzCtrl.fsPath.append("/").append(MZ5665IF_KEYMAP_FILE); + mzCtrl.kmeRows = 0; + mzCtrl.kme = NULL; + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); + + // Load the keyboard mapping table into memory. If the file doesnt exist, create it. + loadKeyMap(); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(getClassName(__PRETTY_FUNCTION__), &this->mzConfig, sizeof(t_mzConfig)) == false) + { + ESP_LOGW(MAINTAG, "MZ5665 configuration set to default, no valid config in NVS found."); + mzConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + mzConfig.params.activeMachineModel = MZ5665_ALL; + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->mzConfig, sizeof(t_mzConfig)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Default MZ5665 configuration data failed, check NVS setup.\n"); + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(this->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } +} + +// Constructor, basically initialise the Singleton interface and let the threads loose. +MZ5665::MZ5665(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->mzCtrl.fsPath = fsPath; + + // Initialise the interface. + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, basic initialisation without hardware. +MZ5665::MZ5665(NVS *hdlNVS, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->mzCtrl.fsPath = fsPath; + + // Initialise the interface. + init(hdlNVS, hdlHID); +} + +// Constructor, used for version reporting so no hardware is initialised. +MZ5665::MZ5665(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +MZ5665::~MZ5665(void) +{ + return; +} diff --git a/main/Mouse.cpp b/main/Mouse.cpp deleted file mode 120000 index 4f1a342..0000000 --- a/main/Mouse.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/Mouse.cpp \ No newline at end of file diff --git a/main/Mouse.cpp b/main/Mouse.cpp new file mode 100644 index 0000000..5277e22 --- /dev/null +++ b/main/Mouse.cpp @@ -0,0 +1,729 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: Mouse.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: PS/2 Mouse to Sharp Host Interface logic. +// This source file contains the singleton class containing logic to obtain +// PS/2 mouse data (position, keys etc), map them into Sharp compatible codes and +// transmit the data to the connected host. +// +// The whole application of which this class is a member, uses the Espressif Development +// environment with Arduino components. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// Updates to reflect moving functionality into the HID and to support +// Bluetooth as a primary mouse or secondary mouse. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "Arduino.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sdkconfig.h" +#include "Mouse.h" + +// Tag for ESP main application logging. +#define MAINTAG "Mouse" + +// Mouse Protocol +// -------------- +// +// +// The Sharp (X68000/X1/MZ-2500/MZ-2800) mouse uses an asynchronous serial protocol over two wires (MSDATA/MSCTRL). +// The MSCTRL signal is an enable signal, idle state = HIGH, it goes low prior to transmission of data by at least 1mS and goes high after +// transmission of last bit by ~2.56mS. +// The MSDATA signal is a standard asynchronous signal, idle = HIGH, 1 start bit, 8 data bits, 2 stop bits @ 4800baud. +// +// Protocol: +// Idle State (MSDATA/MSCTRL) = High. +// Transmission: MSCTRL -> LOW +// 1ms delay +// MSDATA -> low, first start bit. +// 3 bytes transmitted in a <1xStart><8xdata><2xstop> format. +// MSDATA -> high +// 2.56ms delay. +// MSCTRL -> HIGH +// Data bytes: +// CTRL = [7] - Mouse rolling forward when high, backward when low. +// [6] +// [5] - Mouse rolling left, right when low. +// [4] +// [3] +// [2] +// [1] - Right button pressed = HIGH. +// [0] - Left button pressed = HIGH. +// POS X [7:0] - X Position data. +// POS Y [7:0] - Y Position data. + + +// Method to realise the Sharp host Mouse protocol. +// This method uses Core 1 and it will hold it in a spinlock as necessary to ensure accurate timing. +// Mouse data is passed into the method via a direct object, using the FreeRTOS Queue creates a time lag resulting in the mouse data being out of sync with hand movement. +IRAM_ATTR void Mouse::hostInterface( void * pvParameters ) +{ + // Locals. + // + Mouse* pThis = (Mouse*)pvParameters; // Retrieve pointer to object in order to access data. + bool msctrlEdge = false; + uint8_t txBuf[4]; + uint32_t MSCTRL_MASK; + uint32_t MSDATA_MASK; + #ifdef CONFIG_HOST_BITBANG_UART + int txPos; + int txCnt; + uint32_t shiftReg; + uint64_t delayTimer = 0LL; + uint64_t curTime = 0LL; + uint32_t bitCount = 0; + enum HOSTXMITSTATE { + FSM_IDLE = 0, + FSM_STARTXMIT = 1, + FSM_STARTBIT = 2, + FSM_DATA = 3, + FSM_STOP = 4, + FSM_ENDXMIT = 5 + } state = FSM_IDLE; + #endif + + // Initialise the MUTEX which prevents this core from being released to other tasks. + pThis->x1Mutex = portMUX_INITIALIZER_UNLOCKED; + + if(pThis->hostControl.secondaryIf == false) + { + MSCTRL_MASK = (1 << CONFIG_HOST_KDB0); + MSDATA_MASK = (1 << CONFIG_HOST_KDB1); + } else + { + MSCTRL_MASK = (1 << CONFIG_HOST_KDB0); + MSDATA_MASK = (1 << CONFIG_HOST_KDI4); + } + + gpio_config_t ioConf; + ioConf.intr_type = GPIO_INTR_DISABLE; + ioConf.mode = GPIO_MODE_INPUT; + ioConf.pull_down_en = GPIO_PULLDOWN_DISABLE; + ioConf.pull_up_en = GPIO_PULLUP_ENABLE; + // Both Hardware UART and bitbang need MSCTRL setting as an input. + if(pThis->hostControl.secondaryIf == false) + { + ioConf.pin_bit_mask = (1ULL<hostControl.secondaryIf == false) + { + ioConf.pin_bit_mask = (1ULL<hostControl.secondaryIf == false) || curTime >= delayTimer) + { + // Ensure the timer is stopped. + timer_pause(TIMER_GROUP_0, TIMER_0); + delayTimer = 0LL; + + // Finite state machine to retrieve a key for transmission then serialise it according to the X1 protocol. + switch(state) + { + case FSM_IDLE: + // Yield if the suspend flag is set. + pThis->yield(0); + + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + if(pThis->hostControl.secondaryIf == false) + { + // Detect high to low edge. On mouse primary mode the MSCTRL signal forces the tempo. On mouse secondary mode (operating in tandem to keyboard), + // the timer forces the tempo. + // + msctrlEdge = (REG_READ(GPIO_IN_REG) & MSCTRL_MASK) != 0 ? true : msctrlEdge; + } + + // Wait for a window when MSCTRL goes low. + if(pThis->hostControl.secondaryIf == true || (msctrlEdge == true && (REG_READ(GPIO_IN_REG) & MSCTRL_MASK) == 0)) + { + // Wait for incoming mouse movement message. + if(pThis->xmitMsg.valid) + { + txBuf[0] = (uint8_t)pThis->xmitMsg.status; + txBuf[1] = (uint8_t)pThis->xmitMsg.xPos; + txBuf[2] = (uint8_t)pThis->xmitMsg.yPos; + pThis->xmitMsg.valid = false; // Shouldnt be a race state here but consider a mutex if mouse gets out of sync. + txBuf[3] = 0x00; + txPos = 0; + txCnt = 3; + } else + { + // Sharp host protocol requires us to send zero change messages on a regular period regardless of new data. + txBuf[0] = 0x00; + txBuf[1] = 0x00; + txBuf[2] = 0x00; + txBuf[3] = 0x00; + txPos = 0; + txCnt = 3; + } + + // Advance to first start bit. + state = FSM_STARTXMIT; + + // Clear edge detect for next loop. + msctrlEdge = false; + } + break; + + case FSM_STARTXMIT: + // Ensure all variables and states correct before entering serialisation. + GPIO.out_w1ts = MSDATA_MASK; + state = FSM_STARTBIT; + bitCount = 8; + shiftReg = txBuf[txPos++]; + txCnt--; + + // Create, initialise and hold a spinlock so the current core is bound to this one method. + portENTER_CRITICAL(&pThis->x1Mutex); + + break; + + case FSM_STARTBIT: + // Send out the start bit by bringing MSDATA low for 208us (4800 baud 1bit time period). + GPIO.out_w1tc = MSDATA_MASK; + delayTimer = BITBANG_UART_BIT_TIME; + state = FSM_DATA; + break; + + case FSM_DATA: + if(bitCount > 0) + { + // Setup the bit on MSDATA + if(shiftReg & 0x00000001) + { + GPIO.out_w1ts = MSDATA_MASK; + } else + { + GPIO.out_w1tc = MSDATA_MASK; + } + + // Shift the data to the next bit for transmission. + shiftReg = shiftReg >> 1; + + // 1 bit period. + delayTimer = BITBANG_UART_BIT_TIME; + + // 1 Less bit in frame. + bitCount--; + } else + { + state = FSM_STOP; + } + break; + + case FSM_STOP: + // Send out the stop bit, 2 are needed so just adjust the time delay. + GPIO.out_w1ts = MSDATA_MASK; + delayTimer = BITBANG_UART_BIT_TIME * 2; + state = FSM_ENDXMIT; + break; + + case FSM_ENDXMIT: + // End of critical timing loop, release the core so other tasks can run whilst we load up the next byte. + portEXIT_CRITICAL(&pThis->x1Mutex); + + // Any more bytes to transmit, loop and send if there are. + if(txCnt > 0) + { + state = FSM_STARTXMIT; + } else + { + // Reset timer for next loop. + delayTimer = 20000UL; + state = FSM_IDLE; + } + break; + } + + // If a new delay is requested, set the value into the timer and start. + if(delayTimer > 0LL) + { + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0LL); + timer_start(TIMER_GROUP_0, TIMER_0); + } + } + #endif + + #ifdef CONFIG_HOST_HW_UART + // Get the current timer value, we need to wait 20ms between transmissions. + timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &curTime); + if(curTime >= delayTimer) + { + // Wait for a window when MSCTRL goes low. + if(pThis->hostControl.secondaryIf == true || (REG_READ(GPIO_IN_REG) & MSCTRL_MASK) == 0) + { + // Ensure the timer is stopped, initialise to 0 and restart. + timer_pause(TIMER_GROUP_0, TIMER_0); + delayTimer = 20000LL; + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0LL); + timer_start(TIMER_GROUP_0, TIMER_0); + + // Wait for incoming mouse movement message. + if(pThis->xmitMsg.valid) + { + txBuf[0] = (uint8_t)pThis->xmitMsg.status; + txBuf[1] = (uint8_t)pThis->xmitMsg.xPos; + txBuf[2] = (uint8_t)pThis->xmitMsg.yPos; + pThis->xmitMsg.valid = false; // Shouldnt be a race state here but consider a mutex if mouse gets out of sync. + txBuf[3] = 0x00; + txPos = 0; + txCnt = 3; + } else + { + // Sharp host protocol requires us to send zero change messages on a regular period regardless of new data. + txBuf[0] = 0x00; + txBuf[1] = 0x00; + txBuf[2] = 0x00; + txBuf[3] = 0x00; + txPos = 0; + txCnt = 3; + } + + // Send the bytes and wait. + uart_write_bytes(pThis->hostControl.uartNum, (const char *)txBuf, 3); + + // This method doesnt actually return after the last byte is transmitted, it returns well before, so we tack on a 10ms delay which is the width for 3 bytes at 4800 baud. + uart_wait_tx_done(pThis->hostControl.uartNum, 25000); + vTaskDelay(10); + } + + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAPKEYTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // Yield if the suspend flag is set. + pThis->yield(0); + } + #endif + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect + } +} + +// Primary HID routine. +// This method is responsible for receiving HID (PS/2 or BT) mouse scan data and mapping it into Sharp compatible mouse data. +// The HID mouse data once received is mapped and pushed onto a FIFO queue for transmission to the host. +// +void Mouse::mouseReceiveData(HID::t_mouseMessageElement mouseMessage) +{ + // Locals. + uint8_t status; + + // Invert Y as the Sharp host is inverted compared to a PS/2 on the Y axis. + mouseMessage.yPos = -mouseMessage.yPos; + + // Initialise the status flag, on the Sharp host it is <1><0> + status = (((mouseMessage.xPos >> 8) & 0x01) << 4) | (mouseMessage.status & 0x0F ); + + // Check bounds and set flags accordingly. + if(mouseMessage.xPos > 127) + { + mouseMessage.xPos = 127; // Maximum resolution of Sharp host X movement. + status |= (1UL << 4); // Set overflow bit. + } + if(mouseMessage.xPos < -128) + { + mouseMessage.xPos = -128; // Minimum resolution of Sharp host X movement. + status |= (1UL << 5); // Set underflow bit. + } + if(mouseMessage.yPos > 127) + { + mouseMessage.yPos = 127; // Maximum resolution of Sharp host Y movement. + status |= (1UL << 6); // Set overflow bit. + } + if(mouseMessage.yPos < -128) + { + mouseMessage.yPos = -128; // Minimum resolution of Sharp host Y movement. + status |= (1UL << 7); // Set underflow bit. + } + + // Convert back to 8bit 2's compliment and store in the host message to the host thread. + xmitMsg.xPos = (int8_t)mouseMessage.xPos; + xmitMsg.yPos = (int8_t)mouseMessage.yPos; + xmitMsg.status = status; + xmitMsg.wheel = mouseMessage.wheel; + xmitMsg.valid = true; + + return; +} + +// A method to return the Type of data for a given column in the KeyMap table. +// +void Mouse::getMouseConfigTypes(std::vector& typeList) +{ + // Add the types. + // + typeList.push_back(HID_MOUSE_HOST_SCALING_TYPE); + typeList.push_back(HID_MOUSE_SCALING_TYPE); + typeList.push_back(HID_MOUSE_RESOLUTION_TYPE); + typeList.push_back(HID_MOUSE_SAMPLING_TYPE); + return; +} + +// Method to return a list of key:value entries for a given config category. This represents the +// feature which can be selected and the value it uses. Features can be combined by ORing the values +// together. +bool Mouse::getMouseSelectList(std::vector>& selectList, std::string option) +{ + // Locals. + // + bool result = true; + + // Build up a map, depending on the list required, of name to value. This list can then be used + // by a user front end to select an option based on a name and return its value. + if(option.compare(HID_MOUSE_HOST_SCALING_TYPE) == 0) + { + selectList.push_back(std::make_pair("ACTIVE", mouseConfig.host.scaling)); + selectList.push_back(std::make_pair(HID_MOUSE_HOST_SCALING_1_1_NAME, HID::HID_MOUSE_HOST_SCALING_1_1)); + selectList.push_back(std::make_pair(HID_MOUSE_HOST_SCALING_1_2_NAME, HID::HID_MOUSE_HOST_SCALING_1_2)); + selectList.push_back(std::make_pair(HID_MOUSE_HOST_SCALING_1_3_NAME, HID::HID_MOUSE_HOST_SCALING_1_3)); + selectList.push_back(std::make_pair(HID_MOUSE_HOST_SCALING_1_4_NAME, HID::HID_MOUSE_HOST_SCALING_1_4)); + selectList.push_back(std::make_pair(HID_MOUSE_HOST_SCALING_1_5_NAME, HID::HID_MOUSE_HOST_SCALING_1_5)); + } + else if(option.compare(HID_MOUSE_SCALING_TYPE) == 0) + { + selectList.push_back(std::make_pair("ACTIVE", mouseConfig.mouse.scaling)); + selectList.push_back(std::make_pair(HID_MOUSE_SCALING_1_1_NAME, HID::HID_MOUSE_SCALING_1_1)); + selectList.push_back(std::make_pair(HID_MOUSE_SCALING_2_1_NAME, HID::HID_MOUSE_SCALING_2_1)); + } + else if(option.compare(HID_MOUSE_RESOLUTION_TYPE) == 0) + { + selectList.push_back(std::make_pair("ACTIVE", mouseConfig.mouse.resolution)); + selectList.push_back(std::make_pair(HID_MOUSE_RESOLUTION_1_1_NAME, HID::HID_MOUSE_RESOLUTION_1_1)); + selectList.push_back(std::make_pair(HID_MOUSE_RESOLUTION_1_2_NAME, HID::HID_MOUSE_RESOLUTION_1_2)); + selectList.push_back(std::make_pair(HID_MOUSE_RESOLUTION_1_4_NAME, HID::HID_MOUSE_RESOLUTION_1_4)); + selectList.push_back(std::make_pair(HID_MOUSE_RESOLUTION_1_8_NAME, HID::HID_MOUSE_RESOLUTION_1_8)); + } + else if(option.compare(HID_MOUSE_SAMPLING_TYPE) == 0) + { + selectList.push_back(std::make_pair("ACTIVE", mouseConfig.mouse.sampleRate)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_10_NAME, HID::HID_MOUSE_SAMPLE_RATE_10)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_20_NAME, HID::HID_MOUSE_SAMPLE_RATE_20)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_40_NAME, HID::HID_MOUSE_SAMPLE_RATE_40)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_60_NAME, HID::HID_MOUSE_SAMPLE_RATE_60)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_80_NAME, HID::HID_MOUSE_SAMPLE_RATE_80)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_100_NAME, HID::HID_MOUSE_SAMPLE_RATE_100)); + selectList.push_back(std::make_pair(HID_MOUSE_SAMPLE_RATE_200_NAME, HID::HID_MOUSE_SAMPLE_RATE_200)); + } else + { + // Not found! + result = false; + } + + // Return result, false if the option not found, true otherwise. + // + return(result); +} + +// Public method to set the mouse configuration parameters. +// +bool Mouse::setMouseConfigValue(std::string paramName, std::string paramValue) +{ + // Locals. + // + bool dataError = false; + int value(0); + std::stringstream testVal(paramValue); + + // Match the parameter name to a known mouse parameter, type and data check the parameter value and assign to the config accordingly. + if(paramName.compare(HID_MOUSE_HOST_SCALING_TYPE) == 0) + { + // Exception handling is disabled, stringstream is used to catch bad input. + dataError = (static_cast(testVal >> value) ? false : true); + if(dataError == false) + { + if(value >= to_underlying(HID::HID_MOUSE_HOST_SCALING_1_1) && value <= to_underlying(HID::HID_MOUSE_HOST_SCALING_1_5)) + { + mouseConfig.host.scaling = static_cast(value); + hid->setMouseHostScaling(mouseConfig.host.scaling); + } else + { + dataError = true; + } + } + } + if(paramName.compare(HID_MOUSE_SCALING_TYPE) == 0) + { + dataError = (static_cast(testVal >> value) ? false : true); + if(dataError == false) + { + if(value >= to_underlying(HID::HID_MOUSE_SCALING_1_1) && value <= to_underlying(HID::HID_MOUSE_SCALING_2_1)) + { + mouseConfig.mouse.scaling = static_cast(value); + hid->setMouseScaling(mouseConfig.mouse.scaling); + } else + { + dataError = true; + } + } + } + if(paramName.compare(HID_MOUSE_RESOLUTION_TYPE) == 0) + { + dataError = (static_cast(testVal >> value) ? false : true); + if(dataError == false) + { + if(value >= to_underlying(HID::HID_MOUSE_RESOLUTION_1_1) && value <= to_underlying(HID::HID_MOUSE_RESOLUTION_1_8)) + { + mouseConfig.mouse.resolution = static_cast(value); + hid->setMouseResolution(mouseConfig.mouse.resolution); + } else + { + dataError = true; + } + } + } + if(paramName.compare(HID_MOUSE_SAMPLING_TYPE) == 0) + { + dataError = (static_cast(testVal >> value) ? false : true); + if(dataError == false) + { + if(value >= to_underlying(HID::HID_MOUSE_SAMPLE_RATE_10) && value <= to_underlying(HID::HID_MOUSE_SAMPLE_RATE_200)) + { + mouseConfig.mouse.sampleRate = static_cast(value); + hid->setMouseSampleRate(mouseConfig.mouse.sampleRate); + } else + { + dataError = true; + } + } + } + + // Error = true, success = false. + return(dataError); +} + +// Method to save (persist) the configuration into NVS RAM. +bool Mouse::persistConfig(void) +{ + // Locals. + bool result = true; + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->mouseConfig, sizeof(t_mouseConfig)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Mouse configuration data failed, check NVS setup.\n"); + result = false; + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(nvs->commitData() == false) + { + ESP_LOGW(MAINTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + + // Request persistence in the HID module. + result |= hid->persistConfig(); + + // Error = false, success = true. + return(result); +} + +// Initialisation routine. Start two threads, one to handle the incoming PS/2 mouse data and map it, the second to handle the host interface. +void Mouse::init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Initialise control variables. + #ifdef CONFIG_HOST_HW_UART + hostControl.uartNum = UART_NUM_2; + hostControl.uartBufferSize = 256; + hostControl.uartQueueSize = 10; + #endif + + // Initialise the basic components. + init(hdlNVS, hdlHID); + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); + + // There are two build possibilities, hardware UART and BITBANG. I initially coded using hardware but whilst trying to find a bug, wrote a bitbang + // technique and both are fit for purpose, so enabling either yields the same result. + #ifdef CONFIG_HOST_HW_UART + // Prepare the UART to be used for communications with the Sharp host. + // The Sharp host Mouse uses an Asynchronous protocol with 2 stop bits no parity 4800 baud. + // + uart_config_t uartConfig = { + .baud_rate = 4800, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_2, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 122, + .source_clk = UART_SCLK_APB, + }; + + // Configure UART parameters and pin assignments, software flow control, not RTS/CTS. + // The mouse only uses a Tx line, the MSCTRL line is used as a gate signal, so assign the Rx line to an unused pin. + ESP_ERROR_CHECK(uart_param_config(hostControl.uartNum, &uartConfig)); + ESP_ERROR_CHECK(uart_set_pin(hostControl.uartNum, CONFIG_HOST_KDB1, CONFIG_HOST_KDB2, -1, -1)); + // Install UART driver. Use RX/TX buffers without event queue. + ESP_ERROR_CHECK(uart_driver_install(hostControl.uartNum, hostControl.uartBufferSize, hostControl.uartBufferSize, 0, NULL, 0)); + #endif + + // Register the streaming callback for the mouse, this will receive data, process it and send to the hostInterface for transmission to the host. + hid->setDataCallback(&Mouse::mouseReceiveData, this); + + // Create a task pinned to core 1 which will fulfill the Sharp Mouse host interface. This task has the highest priority + // and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means + // all other tasks running on Core 1 will suspend as needed. The HID mouse controller will be serviced with core 0. + // + // Core 1 - Sharp Mouse Host Interface + ESP_LOGW(MAINTAG, "Starting mouseIf thread..."); + ::xTaskCreatePinnedToCore(&this->hostInterface, "mouseIf", 4096, this, 25, &this->TaskHostIF, 1); + vTaskDelay(500); +} + +// Initialisation routine without hardware. +void Mouse::init(NVS *hdlNVS, HID *hdlHID) +{ + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(getClassName(__PRETTY_FUNCTION__), &this->mouseConfig, sizeof(t_mouseConfig)) == false) + { + ESP_LOGW(MAINTAG, "Mouse configuration set to default, no valid config in NVS found."); + mouseConfig.mouse.resolution= HID::HID_MOUSE_RESOLUTION_1_8; + mouseConfig.mouse.scaling = HID::HID_MOUSE_SCALING_1_1; + mouseConfig.mouse.sampleRate= HID::HID_MOUSE_SAMPLE_RATE_60; + mouseConfig.host.scaling = HID::HID_MOUSE_HOST_SCALING_1_2; + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->mouseConfig, sizeof(t_mouseConfig)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Default Mouse configuration data failed, check NVS setup.\n"); + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(nvs->commitData() == false) + { + ESP_LOGW(MAINTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } +} + +// Constructor, basically initialise the Singleton interface and let the threads loose. +Mouse::Mouse(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Operating in uni-mode. + hostControl.secondaryIf = false; + + // Initialise the interface + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, basic initialisation without hardware. +Mouse::Mouse(NVS *hdlNVS, HID *hdlHID) +{ + // Operating in uni-mode. + hostControl.secondaryIf = false; + + // Initialise the interface + init(hdlNVS, hdlHID); +} + +// Constructor for use when mouse operates in tandem with a keyboard. +Mouse::Mouse(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, bool secondaryIf) +{ + // The interface can act in primary mode, ie. sole interface or secondary mode where it acts in tandem to a keyboard host. Slight processing differences occur + // in secondary mode, for example, the pin used to output mouse data differs. + hostControl.secondaryIf = secondaryIf; + + // Initialise the interface + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, used for version reporting so no hardware is initialised. +Mouse::Mouse(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +Mouse::~Mouse(void) +{ + return; +} diff --git a/main/NVS.cpp b/main/NVS.cpp deleted file mode 120000 index cd4cee6..0000000 --- a/main/NVS.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/NVS.cpp \ No newline at end of file diff --git a/main/NVS.cpp b/main/NVS.cpp new file mode 100644 index 0000000..53a6132 --- /dev/null +++ b/main/NVS.cpp @@ -0,0 +1,293 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: NVS.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Base class for encapsulating the Espressif C API for the Non Volatile Storage. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sdkconfig.h" +#include "NVS.h" + +// Method to externally take the NVS mutex for situations where another IDF module requires access to the NVS subsystem. +// +bool NVS::takeMutex(void) +{ + // Locals. + bool result = false; + + // Ensure a handle has been opened to the NVS. + if(nvsCtrl.nvsHandle != (nvs_handle_t)0) + { + // Request exclusive access. + if(xSemaphoreTake(nvsCtrl.mutexInternal, (TickType_t)1000) == pdTRUE) + { + result = true; + } + } + return(result); +} + +// Method to release the NVS mutex previously taken. +void NVS::giveMutex(void) +{ + // Locals. + + // Release mutex, external access now possible to the input devices. + xSemaphoreGive(nvsCtrl.mutexInternal); +} + +// Method to persist data into the NVS RAM. This method takes a pointer to any memory object and writes it into the NVS using the handle opened at initialisation time. +// +bool NVS::persistData(const char *key, void *pData, uint32_t size) +{ + // Locals. + // + esp_err_t nvsStatus; + bool result = true; + #define NVSPERSISTTAG "persistData" + + // Ensure a handle has been opened to the NVS. + if(nvsCtrl.nvsHandle != (nvs_handle_t)0) + { + // Ensure we have exclusive access before accessing NVS. + if(xSemaphoreTake(nvsCtrl.mutexInternal, (TickType_t)1000) == pdTRUE) + { + // Write a binary blob of data straight from memory pointed to by pData for readSize bytes into the NVS. This allows for individual variables or entire structures. + nvsStatus = nvs_set_blob(this->nvsCtrl.nvsHandle, key, pData, size); + if(nvsStatus != ESP_OK) + { + ESP_LOGW(NVSPERSISTTAG, "Failed to persist NVS data, key:%s, size:%d, nvsStatus:%d", key, size, nvsStatus); + result = false; + } + } else + { + result = false; + } + } else + { + result = false; + } + + // NB: Mutex only released in COMMIT. + + // Return result code. + return(result); +} + +// Method to retrieve persisted data from the NVS RAM. This method takes a pointer to a pre-allocated memoery block along with size and retrieves a data block from NVS upto size bytes. +// +bool NVS::retrieveData(const char *key, void *pData, uint32_t size) +{ + // Locals. + // + esp_err_t nvsStatus; + size_t readSize = size; + bool result = true; + #define NVSRTRVTAG "retrieveData" + + // Ensure a handle has been opened to the NVS. + if(nvsCtrl.nvsHandle != (nvs_handle_t)0) + { + // Ensure we have exclusive access before accessing NVS. + if(xSemaphoreTake(nvsCtrl.mutexInternal, (TickType_t)1000) == pdTRUE) + { + // Get a binary blob of data straight into the memory pointed to by pData for readSize. This allows for individual variables or entire structures. + nvsStatus = nvs_get_blob(this->nvsCtrl.nvsHandle, key, pData, &readSize); + if(nvsStatus != ESP_OK || readSize != size) + { + ESP_LOGW(NVSRTRVTAG, "Failed to retrieve NVS data, key:%s, size:%d, requested size:%d, nvsStatus:%d", key, readSize, size, nvsStatus); + result = false; + } + + // Release mutex, external access now possible to the input devices. + xSemaphoreGive(nvsCtrl.mutexInternal); + } else + { + result = false; + } + } else + { + result = false; + } + + // Return result code. + return(result); +} + +// Method to ensure all data written to NVS is flushed and committed. This step is necessary as a write may be buffered and requires flushing to ensure persistence. +// +bool NVS::commitData(void) +{ + // Locals. + // + esp_err_t nvsStatus; + bool result = true; + #define NVSCOMMITTAG "commitData" + + // Ensure a handle has been opened to the NVS. + if(nvsCtrl.nvsHandle != (nvs_handle_t)0) + { + + // Check that the Mutex has been taken, if we grab it then it hasnt been taken in the persistData method, so exit as a call to persistData is mandatory. + if(xSemaphoreTake(nvsCtrl.mutexInternal, (TickType_t)0) == pdTRUE) + { + xSemaphoreGive(nvsCtrl.mutexInternal); + } else + { + // Request a commit transaction and return response accordingly. + nvsStatus = nvs_commit(this->nvsCtrl.nvsHandle); + if(nvsStatus != ESP_OK) + { + ESP_LOGW(NVSCOMMITTAG, "Failed to commit pending NVS data."); + result = false; + } + + // Release mutex, external access now possible to the input devices. + xSemaphoreGive(nvsCtrl.mutexInternal); + } + } else + { + result = false; + } + + // Return result code. + return(result); +} + +// Method to erase all the NVS and return to factory default state. The method closes any open handle, +// de-initialises the NVS then performs a flash erase. +// +void NVS::eraseAll(void) +{ + // Locals. + // + #define NVSERATAG "eraseAll" + + // Ensure we have exclusive access before accessing NVS. + while(xSemaphoreTake(nvsCtrl.mutexInternal, (TickType_t)1000) != pdTRUE); + + // Ensure a handle has been opened to the NVS. + if(nvsCtrl.nvsHandle != (nvs_handle_t)0) + { + // Close open handle. + nvs_close(nvsCtrl.nvsHandle); + nvsCtrl.nvsHandle = NULL; + } + + // Stop the flash driver. + nvs_flash_deinit(); + + ESP_LOGW(NVSERATAG, "Erasing flash, disable for production!\n"); + ESP_ERROR_CHECK(nvs_flash_erase()); + + // Release mutex, external access now possible to the input devices. + xSemaphoreGive(nvsCtrl.mutexInternal); + + return; +} + +// Method to initialise the NVS subsystem. +void NVS::init(void) +{ + // Locals. + esp_err_t nvsStatus; + #define NVSINITTAG "nvsInit" + + // Initialise variables. + nvsCtrl.nvsHandle = (nvs_handle_t)0; + + //ESP_LOGW(NVSINITTAG, "Erasing flash, disable for production!\n"); + //ESP_ERROR_CHECK(nvs_flash_erase()); + + // Initialize NVS + ESP_LOGW(NVSINITTAG, "Initialising NVS."); + nvsStatus = nvs_flash_init(); + if(nvsStatus == ESP_ERR_NVS_NO_FREE_PAGES || nvsStatus == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + // NVS partition was truncated and needs to be erased + ESP_ERROR_CHECK(nvs_flash_erase()); + + // Retry nvs_flash_init + nvsStatus = nvs_flash_init(); + } + ESP_ERROR_CHECK(nvsStatus); + + // Setup mutex's. + nvsCtrl.mutexInternal = xSemaphoreCreateMutex(); + + return; +} + +// Method to open a namespace on the NVS given a key. +// +bool NVS::open(std::string keyName) +{ + // Locals. + bool result = true; + #define NVSOPENTAG "nvsOpen" + + // Only process if no handle has been opened. Currently only coded for one session at a time. + if(nvsCtrl.nvsHandle == (nvs_handle_t)0) + { + // Store the key name under which all data is stored. + this->nvsCtrl.nvsKeyName = keyName; + + // Open handle to persistence using the base-class name as the key which represents the global namespace. Sub-classes and objects accessing the public methods will + // use there own class name as a sub-key which represents the class namespace within NVS. Data is then stored within the class namespace using a key:value pair. + esp_err_t nvsStatus = nvs_open(nvsCtrl.nvsKeyName.c_str(), NVS_READWRITE, &this->nvsCtrl.nvsHandle); + if (nvsStatus != ESP_OK) + { + ESP_LOGW(NVSOPENTAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(nvsStatus)); + result = false; + } + } else + { + result = false; + } + return(result); +} + +// Basic constructor, init variables! +NVS::NVS(void) +{ + // Store the class name for later use, ie. NVS key access. + this->nvsCtrl.nvsClassName = getClassName(__PRETTY_FUNCTION__); +} diff --git a/main/PC9801.cpp b/main/PC9801.cpp deleted file mode 120000 index 1e56610..0000000 --- a/main/PC9801.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/PC9801.cpp \ No newline at end of file diff --git a/main/PC9801.cpp b/main/PC9801.cpp new file mode 100644 index 0000000..79e25e9 --- /dev/null +++ b/main/PC9801.cpp @@ -0,0 +1,1054 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: PC9801.cpp +// Created: Apr 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: HID (PS/2 or BT keyboard) to NEC PC-9801 series Interface logic. +// This source file contains the singleton class containing logic to obtain +// PS/2 or BT scan codes, map them into NEC PC-9801 key codes and transmit the key +// data to the NEC PC-9801 host. +// +// The class uses a modified version of the PS2KeyAdvanced +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The whole application of which this class is a member, uses the Espressif Development +// environment with Arduino components. This is necessary for the PS2KeyAdvanced class, +// which I may in future convert to use esp-idf library calls rather than Arduino. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Apr 2022 - Initial framework, waiting on arrival of real machine to progress further. +// v1.01 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sys/stat.h" +#include "esp_littlefs.h" +#include "PS2KeyAdvanced.h" +#include "sdkconfig.h" +#include "PC9801.h" + +// Tag for ESP main application logging. +#define MAINTAG "pc9801key" + +// FreeRTOS Queue handle to pass messages from the HID Keyboard Mapper into the PC9801 transmission logic. +static QueueHandle_t xmitQueue; +static QueueHandle_t rcvQueue; + +// NEC PC-9801 Protocol +// -------------------- +// +// The NEC PC-9801 uses an asynchronous serial protocol over two wires (+ 2 control wires and +5V/GND). The basic protocol is 19200, 8 bits, 1 start bit, 1 stop bit, ODD parity at TTL level (ie. not RS-232). +// The 4 signals are (from the SharpKey side): +// DATA (TxD) - Data sent serially from SharpKey to PC-9801 +// /RST (RxD) - Reset signal from PC-9801 to SharpKey, active low. Doubles up for later models as the Rxd Serial connection for the PC-9801 to send the SharpKey some commands. +// /RDY - Ready signal from PC-9801 to SharpKey, active low. When active, PC-9801 can receive key data. +// /RTY - Retry signal from PC-9801 to SharpKey, active low. When active, PC-9801 requires the SharpKey to retransmit the last key. +// +//// Protocol: +// Idle State (RXD/TXD) = High. +// +// +// The following keymaps (from the TMK project, https://github.com/tmk/tmk_keyboard/wiki/PC-9801-Keyboard) show the PC-9801 keyboard layout +// and generated scan codes. +// +// ,---. ,---. ,------------------------. ,------------------------. ,------------------------. +// | 60| | 61| | 62 | 63 | 64 | 65 | 66 | | 67 | 68 | 69 | 6A | 6B | | 52 | 53 | 54 | 55 | 56 | +// `---' `---' `------------------------' `------------------------' `------------------------' +// ,-------------------------------------------------------------. ,-------. ,---------------. +// | 00 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 0A| 0B| 0C| 0D| 0E | | 38| 39| | 3E| 3F| 4D| 41| +// |-------------------------------------------------------------| |-------| |---------------| +// | 0F | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 1A| 1B| | | 36| 37| | 42| 43| 44| 45| +// |-------------------------------------------------------` 1C | |-------| |---------------| +// | 74| 71| 1D| 1E| 1F| 20| 21| 22| 23| 24| 25| 26| 27| 28| | | 3A | | 46| 47| 48| 49| +// |-------------------------------------------------------------| |-------| |---------------| +// | 70 | 29| 2A| 2B| 2C| 2D| 2E| 2F| 30| 31| 32| 33| 70/7D | | 3B| 3C| | 4A| 4B| 4C| 4D| +// `-------------------------------------------------------------' |-------| |---------------| +// | 72 | 77| 73| 51 | 34 | 35 | 79| 78| 5E| | 3D | | 4E| 4F| 50| 1C| +// `-------------------------------------------------------' `-------' `---------------' +// 5E: HOME on PC98XL keyboard +// 79: ALT on DIGITAL WAVE Dboard +// 77,78,79: Left Win, right Win and Application on PC-9821 keyboard +// +// ,---. ,---. ,------------------------. ,------------------------. ,------------------------. +// |Stp| |Cpy| | F1 | F2 | F3 | F4 | F5 | | F6 | F7 | F8 | F9 | F10| | F11| F12| F13| F14| F15| +// `---' `---' `------------------------' `------------------------' `------------------------' +// ,-------------------------------------------------------------. ,-------. ,---------------. +// | Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =|JPY|Bspc| |Ins|Del| |Hom|Hlp| -| /| +// |-------------------------------------------------------------| |-------| |---------------| +// | Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| | |RUp|RDn| | 7| 8| 9| *| +// |-------------------------------------------------------`Enter| |-------| |---------------| +// |Ctl|Cap| A| S| D| F| G| H| J| K| L| ;| '| \| | | Up | | 4| 5| 6| +| +// |-------------------------------------------------------------| |-------| |---------------| +// | Shift | Z| X| C| V| B| N| M| ,| .| /| RO| Shift | |Lef|Rig| | 1| 2| 3| =| +// `-------------------------------------------------------------' |-------| |---------------| +// |Kana|Win|GRP|NFER| Space |XFER| ^2|Win| ^3| | Down | | 0| ,| .| ^1| +// `-------------------------------------------------------' `-------' `---------------' +// ^1: Enter on keypad is identical to Enter on alphanumeric portion +// ^2: ALT on DIGITAL WAVE Dboard while Menu on PC-9821 keyboard +// ^3: HOME on PC98XL keyboard +// ^4: CAPS and Kana are locking keys, either mechanically or by firmware emulation +// +// The following keymaps are for the later NEC keyboad, PC-9801-114 KBDPO3 PC-PTOS +// ,-------------------------------------------------------------------------------------------. +// | 73| 62| 63| 64| 65| 66| 67| 68| 69| 6A| 6B| 52| 53| 54| 55| 56| 36| 37| | | | | | +// `-------------------------------------------------------------------------------------------' +// ,-------------------------------------------------------------. ,-------. ,-------------------. +// | 00 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 0A| 0B| 0C| 0D| 0E | | 38| 39| | 3F| 3E| 61| 4D| 60| +// |-------------------------------------------------------------| |-------| |-------------------| +// | 0F | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 1A| 1B| | | 3A | | 42| 43| 44| | 41| +// |-------------------------------------------------------` 1C | |-------| |-------------------| +// | 74| 71| 1D| 1E| 1F| 20| 21| 22| 23| 24| 25| 26| 27| 28| | | 3B| 3C| | 46| 47| 48| | 45| +// |-------------------------------------------------------------| |-------| |-------------------| +// | | 70 | 29| 2A| 2B| 2C| 2D| 2E| 2F| 30| 31| 32| 33| 70/7D | | 3D | | 4A| 4B| 4C| | 4D| +// |-------------------------------------------------------------| |-------| |-----------| 1C|---| +// | | | 51 | 34 | 35 | | 72 | | | | | 4E| 4F| 50| | 49| +// `-------------------------------------------------------------' `-------' `-------------------' +// +// ,-------------------------------------------------------------------------------------------. +// |FNC| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|F13|F14|F15|F16|F17|F18|F19|F20|F21|F22| +// `-------------------------------------------------------------------------------------------' +// ,-------------------------------------------------------------. ,-------. ,-------------------. +// | Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| ^|JPY|Bskp| |Skp|Rst| |Hlp|Hom|Cpy| =|Stp| +// |-------------------------------------------------------------| |-------| |-------------------| +// | Tab | Q| W| E| R| T| Y| U| I| O| P| @| [| | | Up | | 7| 8| 9| II| /| +// |-------------------------------------------------------`Enter| |-------| |-------------------| +// |Ctl|Cap| A| S| D| F| G| H| J| K| L| ;| :| ]| | |Lef|Rig| | 4| 5| 6| I| *| +// |-------------------------------------------------------------| |-------| |-------------------| +// |SW1|Shift| Z| X| C| V| B| N| M| ,| .| /| RO| Shift | | Down | | 1| 2| 3| | -| +// |-------------------------------------------------------------' |-------| |-----------|Ent|---| +// |SW2| Eisu | MHEN | Space | HENK | HIRA | KANA |Z/H| | Exec | | 0|000| .| | +| +// `-------------------------------------------------------------' `-------' `-------------------' + + +// Function to push a keycode onto the key queue ready for transmission. +// +IRAM_ATTR void PC9801::pushKeyToQueue(uint32_t key) +{ + // Locals. + t_xmitQueueMessage xmitMsg; + #define PUSHKEYTAG "pushKeyToQueue" + + xmitMsg.keyCode = key; + if( xQueueSend(xmitQueue, (void *)&xmitMsg, 10) != pdPASS) + { + ESP_LOGW(PUSHKEYTAG, "Failed to put scancode:%04x into xmitQueue", key); + } + return; +} + +// Function to push a host command onto the processing queue. +// +IRAM_ATTR void PC9801::pushHostCmdToQueue(uint8_t cmd) +{ + // Locals. + t_rcvQueueMessage rcvMsg; + #define PUSHCMDTAG "pushHostCmdToQueue" + + rcvMsg.hostCmd = cmd; + if( xQueueSend(rcvQueue, (void *)&rcvMsg, 10) != pdPASS) + { + ESP_LOGW(PUSHCMDTAG, "Failed to put host command:%02x onto rcvQueue", cmd); + } + return; +} + +// Method to realise the NEC PC-9801 4 wire serial protocol in order to transmit key presses to the +// NEC PC-9801. +// +// This method uses Core 1 and it will hold it in a spinlock as necessary to ensure accurate timing. +// A key is passed into the method via the FreeRTOS Queue handle xmitQueue. +// +IRAM_ATTR void PC9801::pcInterface( void * pvParameters ) +{ + // Locals. + t_xmitQueueMessage rcvMsg; + uint8_t uartData[128]; + int uartXmitCnt; + size_t uartRcvCnt; + + // Retrieve pointer to object in order to access data. + PC9801* pThis = (PC9801*)pvParameters; + + // Initialise the MUTEX which prevents this core from being released to other tasks. + pThis->pcMutex = portMUX_INITIALIZER_UNLOCKED; + + // Initial delay needed because the xQueue will assert probably on a suspended task ALL if delay not inserted! + vTaskDelay(1000); + + // Sign on. + ESP_LOGW(MAINTAG, "Starting NEC PC-9801 thread."); + + // Permanent loop, wait for an incoming message on the key to send queue, read it then transmit to the PC-9801, repeat! + for(;;) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + if(xQueueReceive(xmitQueue, (void *)&rcvMsg, 0) == pdTRUE) + { + ESP_LOGW(MAINTAG, "Received:%08x\n", rcvMsg.keyCode); + + // Allow for multi byte transmissions, MSB sent first. + if(rcvMsg.keyCode != 0x00000000) + { + uartXmitCnt = 0; + while((rcvMsg.keyCode & 0xff000000) == 0x00) { rcvMsg.keyCode = rcvMsg.keyCode << 8; } + for(int idx=0; idx < 4 && (rcvMsg.keyCode & 0xff000000) != 0x00; idx++) + { + if((rcvMsg.keyCode & 0xff000000) != 0) + { + uartData[idx] = (uint8_t)((rcvMsg.keyCode & 0xFF000000) >> 24); + uartXmitCnt++; + } + rcvMsg.keyCode = rcvMsg.keyCode << 8; + } + if(uartXmitCnt > 0) + { + ESP_LOGW(MAINTAG, "Received:%08x, Count=%d\n", rcvMsg.keyCode, uartXmitCnt); + uart_write_bytes(pThis->pcCtrl.uartNum, (const char *)uartData, uartXmitCnt); + } + } + } + + // Get command data from the PC-9801 if any sent- this is where the PC-9801, later models, can send any relevant commands for processing. + uart_get_buffered_data_len(pThis->pcCtrl.uartNum, &uartRcvCnt); + if(uartRcvCnt > 0) + { + do { + uartRcvCnt = uart_read_bytes(pThis->pcCtrl.uartNum, uartData, (128 - 1), 20 / portTICK_PERIOD_MS); + for(int idx=0; idx < uartRcvCnt; idx++) + { + // Filter out polling commands and send valid commands to the rcvQueue. + if(uartData[idx] != 0x40 && uartData[idx] != 0x41) + { + pThis->pushHostCmdToQueue(uartData[idx]); + } + } + } while(uartRcvCnt > 0); + } + + // Yield if the suspend flag is set. + pThis->yield(50); + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect + } +} + +// Method to select keyboard configuration options. When a key sequence is pressed, ie. SHIFT+CTRL+ESC then the fourth simultaneous key is the required option and given to this +// method to act on. Options can be machine model, keyboard map etc. +// +void PC9801::selectOption(uint8_t optionCode) +{ + // Locals. + // + bool updated = true; + #define SELOPTTAG "selectOption" + + // Simple switch to decode the required option and act on it. + switch(optionCode) + { + // Select a keymap using 1..8 or default (STANDARD) using 0. + case PS2_KEY_1: + this->pcConfig.params.activeKeyboardMap = KEYMAP_UK_WYSE_KB3926; + break; + case PS2_KEY_2: + this->pcConfig.params.activeKeyboardMap = KEYMAP_JAPAN_OADG109; + break; + case PS2_KEY_3: + this->pcConfig.params.activeKeyboardMap = KEYMAP_JAPAN_SANWA_SKBL1; + break; + case PS2_KEY_4: + this->pcConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_4; + break; + case PS2_KEY_5: + this->pcConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_5; + break; + case PS2_KEY_6: + this->pcConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_6; + break; + case PS2_KEY_7: + this->pcConfig.params.activeKeyboardMap = KEYMAP_UK_PERIBOARD_810; + break; + case PS2_KEY_8: + this->pcConfig.params.activeKeyboardMap = KEYMAP_UK_OMOTON_K8508; + break; + case PS2_KEY_0: + this->pcConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + break; + + // Unknown option so ignore. + default: + updated = false; + break; + } + + // If an update was made, persist it for power cycles. + // + if(updated) + { + this->pcCtrl.persistConfig = true; + } + + return; +} + +// Method to take a PS/2 key and control data and map it into an NEC PC-9801 key and control equivalent, updating state values accordingly (ie. CAPS). +// A mapping table is used which maps a key and state values into an NEC PC-9801 key and control values, the emphasis being on readability and easy configuration +// as opposed to concatenated byte tables. +// +uint32_t PC9801::mapKey(uint16_t scanCode) +{ + // Locals. + uint32_t idx; + uint8_t keyCode = (scanCode & 0xFF); + bool mapped = false; + bool matchExact = false; + uint32_t mappedKey = 0x00000000; + #define MAPKEYTAG "mapKey" + + // Intercept control keys and set state variables. + // + // + if(scanCode & PS2_BREAK) + { + // if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT) == 0) { mapped=true; this->pcCtrl.keyCtrl |= PC9801_CTRL_SHIFT; } +// if(keyCode == PS2_KEY_KANA && (scanCode & PS2_CTRL) == 0) { mapped=true; this->pcCtrl.keyCtrl &= ~PC9801_CTRL_KANA; } + + // Any break key clears the option select flag. + this->pcCtrl.optionSelect = false; + + // Clear any feature LED blinking. + led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } else + { + // if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT)) { mapped=true; this->pcCtrl.keyCtrl &= ~PC9801_CTRL_SHIFT; } +// if(keyCode == PS2_KEY_KANA && (scanCode & PS2_CTRL)) { mapped=true; this->pcCtrl.keyCtrl |= PC9801_CTRL_KANA; } + // Special mapping to allow selection of keyboard options. If the user presses CTRL+SHIFT+ESC then a flag becomes active and should a fourth key be pressed before a BREAK then the fourth key is taken as an option key and processed accordingly. + if(this->pcCtrl.optionSelect == true && keyCode != PS2_KEY_ESC) + { + mapped = true; + this->pcCtrl.optionSelect = false; + selectOption(keyCode); + } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT) && this->pcCtrl.optionSelect == false) + { + // Prime flag ready for fourth option key and start LED blinking periodically. + mapped = true; + this->pcCtrl.optionSelect = true; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_50, 1, 500L, 500L); + } + } + + // If the key has been mapped as a special key, no further processing. + if(mapped == true) + { + ESP_LOGW(MAPKEYTAG, "Mapped special key:%02x\n", this->pcCtrl.keyCtrl); + // mappedKey = (this->pcCtrl.keyCtrl << 8) | 0x00; + } else + { + // Loop through the entire conversion table to find a match on this key, if found map to X68000 equivalent. + // switch matrix. + // + for(idx=0, mapped=false, matchExact=false; idx < pcCtrl.kmeRows && (mapped == false || (mapped == true && matchExact == false)); idx++) + { + // Match key code? Make sure the current machine and keymap match as well. + if(pcCtrl.kme[idx].ps2KeyCode == (uint8_t)(scanCode&0xFF) && ((pcCtrl.kme[idx].machine == PC9801_ALL) || ((pcCtrl.kme[idx].machine & pcConfig.params.activeMachineModel) != 0)) && ((pcCtrl.kme[idx].keyboardModel & pcConfig.params.activeKeyboardMap) != 0)) + { + // Match Raw, Shift, Function, Control, ALT or ALT-Gr? + //if( (((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) || + if( (((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0) && ((pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) || + ((scanCode & PS2_SHIFT) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || + ((scanCode & PS2_CTRL) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || + ((scanCode & PS2_GUI) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || + // ((this->pcCtrl.keyCtrl & PC9801_CTRL_KANA) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA)!= 0) || + // ((scanCode & PS2_CAPS) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) != 0) || + ((scanCode & PS2_FUNCTION) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) ) + { + + // Exact entry match, data + control key? On an exact match we only process the first key. On a data only match we fall through to include additional data and control key matches to allow for un-mapped key combinations, ie. Japanese characters. + matchExact = (((scanCode & PS2_SHIFT) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || ((scanCode & PS2_SHIFT) == 0 && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0)) && + (((scanCode & PS2_CTRL) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || ((scanCode & PS2_CTRL) == 0 && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0)) && + (((scanCode & PS2_GUI) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || ((scanCode & PS2_GUI) == 0 && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0)) && + // (((this->pcCtrl.keyCtrl & PC9801_CTRL_KANA) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA)!= 0) || ((this->pcCtrl.keyCtrl & PC9801_CTRL_KANA) == 0 && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_KANA)== 0)) && + // (((scanCode & PS2_CAPS) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) != 0) || ((scanCode & PS2_GUI) == 0 && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) == 0)) && + (((scanCode & PS2_FUNCTION) && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) || ((scanCode & PS2_FUNCTION) == 0 && (pcCtrl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) + ? true : false; + + // RELEASE (PS2_BREAK == 1) or PRESS? + if((scanCode & PS2_BREAK)) + { + // Special case for the PAUSE / BREAK key. The underlying logic has been modified to send a BREAK key event immediately + // after a PAUSE make, this is necessary as the Sharp machines require SHIFT (pause) BREAK so the PS/2 CTRL+BREAK wont + // work (unless logic is added to insert a SHIFT, pause, add BREAK). The solution was to generate a BREAK event + // when SHIFT+PAUSE is pressed. + if(keyCode == PS2_KEY_PAUSE) + { + vTaskDelay(100); + } + mappedKey = 0x80 | (pcCtrl.kme[idx].pcKey & 0x7F); + mapped = true; + } else + { + // Map key actioning any control overrides. + if((pcCtrl.kme[idx].pcCtrl & PC9801_CTRL_RELEASESHIFT) != 0) + { + // RELEASESHIFT infers that the X68000 must cancel the current shift status prior to receiving the key code. This is necessary when using foreign keyboards and a character appears + // on a shifted key whereas on the original X68000 keyboard the character is the primary key. + // + mappedKey = ((0x80 | PC9801_KEY_SHIFT) << 16) | 0x00 | ((pcCtrl.kme[idx].pcKey & 0x7F) << 8) | (0x00 | PC9801_KEY_SHIFT); + } else + if((pcCtrl.kme[idx].pcCtrl & PC9801_CTRL_SHIFT) != 0) + { + // SHIFT infers that the X68000 must invoke shift status prior to receiving the key code. This is necessary when using foreign keyboards and a character appears + // as a primary key on the foreign keyboard but as a shifted key on the X68000 keyboard. + // + mappedKey = ((0x00 | PC9801_KEY_SHIFT) << 16) | 0x00 | ((pcCtrl.kme[idx].pcKey & 0x7F) << 8) | (0x80 | PC9801_KEY_SHIFT); + } + else + { + mappedKey = 0x00 | (pcCtrl.kme[idx].pcKey & 0x7F); + } + mapped = true; + } + } + } + } + } + return(mappedKey); +} + +// Primary HID thread, running on Core 0. +// This thread is responsible for receiving HID (PS/2 or BT) keyboard scan codes and mapping them to Sharp PC9801 equivalent keys, updating state flags as needed. +// The HID data is received via interrupt. The data to be sent to the PC9801 is pushed onto a FIFO queue. +// +IRAM_ATTR void PC9801::hidInterface( void * pvParameters ) +{ + // Locals. + uint16_t scanCode = 0x0000; + uint32_t pcKey = 0x00000000; + t_rcvQueueMessage rcvMsg; + + // Map the instantiating object so we can access its methods and data. + PC9801* pThis = (PC9801*)pvParameters; + + // Thread never exits, just polls the keyboard and updates the matrix. + while(1) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // Check for HID keyboard scan codes. + while((scanCode = pThis->hid->read()) != 0) + { + // Scan Code Breakdown: + // Define name bit description + // PS2_BREAK 15 1 = Break key code + // (MSB) 0 = Make Key code + // PS2_SHIFT 14 1 = Shift key pressed as well (either side) + // 0 = No shift key + // PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + // 0 = No Ctrl key + // PS2_CAPS 12 1 = Caps Lock ON + // 0 = Caps lock OFF + // PS2_ALT 11 1 = Left Alt key pressed as well + // 0 = No Left Alt key + // PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + // 0 = No Right Alt key + // PS2_GUI 9 1 = GUI key pressed as well (either) + // 0 = No GUI key + // PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + // 0 = standard character key + // 7-0 PS/2 Key code. + // + // BREAK code means all keys released so clear out flags and send update. + ESP_LOGW(MAPKEYTAG, "SCANCODE:%04x",scanCode); + + // Map the PS/2 key to an PC9801 CTRL + KEY + pcKey = pThis->mapKey(scanCode); + if(pcKey != 0L) { pThis->pushKeyToQueue(pcKey); } + + // Toggle LED to indicate data flow. + if((scanCode & PS2_BREAK) == 0) + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 1, 100L, 0L); + } + + // Check for incoming host keyboard commands and execute them. + if(xQueueReceive(rcvQueue, (void *)&rcvMsg, 0) == pdTRUE) + { + ESP_LOGD(MAINTAG, "Received Host Cmd:%02x\n", rcvMsg.hostCmd); + } + + // NVS writes require both CPU cores to be free so write config out at a known junction. + if(pThis->pcCtrl.persistConfig == true) + { + // Request and wait for the interface to suspend. This ensures that the host cpu is not held in a spinlock when NVS update is requested avoiding deadlock. + pThis->suspendInterface(true); + pThis->isSuspended(true); + + if(pThis->nvs->persistData(pThis->getClassName(__PRETTY_FUNCTION__), &pThis->pcConfig, sizeof(t_pcConfig)) == false) + { + ESP_LOGW(SELOPTTAG, "Persisting PC-9801 configuration data failed, updates will not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(pThis->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + } + + // Release interface. + pThis->suspendInterface(false); + + // Clear flag so we dont persist in a loop. + pThis->pcCtrl.persistConfig = false; + } + + // Yield if the suspend flag is set. + pThis->yield(25); + } +} + +// A method to load the keyboard mapping table into memory for use in the interface mapping logic. If no persistence file exists or an error reading persistence occurs, the keymap +// uses the internal static default. If no persistence file exists and attempt is made to create it with a copy of the inbuilt static map so that future operations all +// work with persistence such that modifications can be made. +// +bool PC9801::loadKeyMap(void) +{ + // Locals. + // + bool result = false; + int fileRows = 0; + struct stat keyMapFileNameStat; + + // See if the file exists, if it does, get size so we can compute number of mapping rows. + if(stat(pcCtrl.keyMapFileName.c_str(), &keyMapFileNameStat) == -1) + { + ESP_LOGW(MAINTAG, "No keymap file, using inbuilt definitions."); + } else + { + // Get number of rows in the file. + fileRows = keyMapFileNameStat.st_size/sizeof(t_keyMapEntry); + + // Subsequent reloads, delete memory prior to building new map, primarily to conserve precious resources rather than trying the memory allocation trying to realloc and then having to copy. + if(pcCtrl.kme != NULL && pcCtrl.kme != PS2toPC9801.kme) + { + delete pcCtrl.kme; + pcCtrl.kme = NULL; + } + + // Allocate memory for the new keymap table. + pcCtrl.kme = new t_keyMapEntry[fileRows]; + if(pcCtrl.kme == NULL) + { + ESP_LOGW(MAINTAG, "Failed to allocate memory for keyboard map, fallback to inbuilt!"); + } else + { + // Open the keymap extension file for binary reading to add data to our map table. + std::fstream keyFileIn(pcCtrl.keyMapFileName.c_str(), std::ios::in | std::ios::binary); + + int idx=0; + while(keyFileIn.good()) + { + keyFileIn.read((char *)&pcCtrl.kme[idx], sizeof(t_keyMapEntry)); + if(keyFileIn.good()) + { + idx++; + } + } + // Any errors, we wind back and use the inbuilt mapping table. + if(keyFileIn.bad()) + { + keyFileIn.close(); + ESP_LOGW(MAINTAG, "Failed to read data from keymap extension file:%s, fallback to inbuilt!", pcCtrl.keyMapFileName.c_str()); + } else + { + // No longer need the file. + keyFileIn.close(); + + // Max rows in the KME table. + pcCtrl.kmeRows = fileRows; + + // Good to go, map ready for use with the interface. + result = true; + } + } + } + + // Any failures, free up memory and use the inbuilt mapping table. + if(result == false) + { + if(pcCtrl.kme != NULL && pcCtrl.kme != PS2toPC9801.kme) + { + delete pcCtrl.kme; + pcCtrl.kme = NULL; + } + + // No point allocating memory if no extensions exist or an error occurs, just point to the static table. + pcCtrl.kme = PS2toPC9801.kme; + pcCtrl.kmeRows = PS2TBL_PC9801_MAXROWS; + + // Persist the data so that next load comes from file. + saveKeyMap(); + } + + // Return code. Either memory map was successfully loaded, true or failed, false. + return(result); +} + +// Method to save the current keymap out to an extension file. +// +bool PC9801::saveKeyMap(void) +{ + // Locals. + // + bool result = false; + int idx = 0; + + // Has a map been defined? Cannot save unless loadKeyMap has been called which sets pcCtrl.kme to point to the internal keymap or a new memory resident map. + // + if(pcCtrl.kme == NULL) + { + ESP_LOGW(MAINTAG, "KeyMap hasnt yet been defined, need to call loadKeyMap."); + } else + { + // Open file for binary writing, trunc specified to clear out the file, we arent appending. + std::fstream keyFileOut(pcCtrl.keyMapFileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + + // Loop whilst no errors and data rows still not written. + while(keyFileOut.good() && idx < pcCtrl.kmeRows) + { + keyFileOut.write((char *)&pcCtrl.kme[idx], sizeof(t_keyMapEntry)); + idx++; + } + if(keyFileOut.bad()) + { + ESP_LOGW(MAINTAG, "Failed to write data from the keymap to file:%s, deleting as state is unknown!", pcCtrl.keyMapFileName.c_str()); + keyFileOut.close(); + std::remove(pcCtrl.keyMapFileName.c_str()); + } else + { + // Success. + keyFileOut.close(); + result = true; + } + } + + // Return code. Either memory map was successfully saved, true or failed, false. + return(result); +} + +// Public method to open a keymap file for data upload. +// This method opens the file and makes any validation checks as necessary. +// +bool PC9801::createKeyMapFile(std::fstream &outFile) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Attempt to open a temporary keymap file for writing. + // + fileName = pcCtrl.keyMapFileName; + replaceExt(fileName, "tmp"); + outFile.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to validate and store data provided by caller into an open file created by 'createKeyMapFile'. +// +bool PC9801::storeDataToKeyMapFile(std::fstream &outFile, char *data, int size) +{ + // Locals. + // + bool result = true; + + // Check that the file is still writeable then add data. + if(outFile.good()) + { + outFile.write(data, size); + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Polymorphic alternative to take a vector of bytes for writing to the output file. +// +bool PC9801::storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray) +{ + // Locals. + // + bool result = true; + char data[1]; + + // Check that the file is still writeable then add data. Not best for performace but ease of use and minimum memory. + if(outFile.good()) + { + for(std::size_t idx = 0; idx < dataArray.size(); idx++) + { + data[0] = (char)dataArray[idx]; + outFile.write((char *)&data, 1); + } + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to close and commit a data file, created by 'createKeyMapFile' and populated by 'storeDataToKeyMapFile'. +// This involves renaming the original keymap file, closing the new file and renaming it to the original keymap filename. +// +bool PC9801::closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Check the file is still accessible and close. + // + outFile.close(); + if(!cleanupOnly) + { + if(outFile.good()) + { + // Rename the original file. + fileName = pcCtrl.keyMapFileName; + replaceExt(fileName, "bak"); + // Remove old backup file. Dont worry if it is not there! + std::remove(fileName.c_str()); + replaceExt(fileName, "tmp"); + // Rename new file to active. + if(std::rename(fileName.c_str(), pcCtrl.keyMapFileName.c_str()) != 0) + { + result = false; + } + } else + { + result = false; + } + } + + // Send result. + return(result); +} + +// Method to return the keymap column names as header strings. +// +void PC9801::getKeyMapHeaders(std::vector& headerList) +{ + // Add the names. + // + headerList.push_back(PS2TBL_PS2KEYCODE_NAME); + headerList.push_back(PS2TBL_PS2CTRL_NAME); + headerList.push_back(PS2TBL_KEYBOARDMODEL_NAME); + headerList.push_back(PS2TBL_MACHINE_NAME); +// headerList.push_back(PS2TBL_X1MODE_NAME); +// headerList.push_back(PS2TBL_X1KEYCODE_NAME); +// headerList.push_back(PS2TBL_X1KEYCODE_BYTE2_NAME); +// headerList.push_back(PS2TBL_X1_CTRL_NAME); + + return; +} + +// A method to return the Type of data for a given column in the KeyMap table. +// +void PC9801::getKeyMapTypes(std::vector& typeList) +{ + // Add the types. + // + typeList.push_back(PS2TBL_PS2KEYCODE_TYPE); + typeList.push_back(PS2TBL_PS2CTRL_TYPE); + typeList.push_back(PS2TBL_KEYBOARDMODEL_TYPE); + typeList.push_back(PS2TBL_MACHINE_TYPE); +// typeList.push_back(PS2TBL_X1MODE_TYPE); +// typeList.push_back(PS2TBL_X1KEYCODE_TYPE); +// typeList.push_back(PS2TBL_X1KEYCODE_BYTE2_TYPE); +// typeList.push_back(PS2TBL_X1CTRL_TYPE); + + return; +} + +// Method to return a list of key:value entries for a given keymap column. This represents the +// feature which can be selected and the value it uses. Features can be combined by ORing the values +// together. +bool PC9801::getKeyMapSelectList(std::vector>& selectList, std::string option) +{ + // Locals. + // + bool result = true; + + // Build up a map, depending on the list required, of name to value. This list can then be used + // by a user front end to select an option based on a name and return its value. + if(option.compare(PS2TBL_PS2CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_SHIFT, PS2CTRL_SHIFT)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CTRL, PS2CTRL_CTRL)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CAPS, PS2CTRL_CAPS)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_KANA, PS2CTRL_KANA)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GRAPH, PS2CTRL_GRAPH)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GUI, PS2CTRL_GUI)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_FUNC, PS2CTRL_FUNC)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_EXACT, PS2CTRL_EXACT)); + } + else if(option.compare(PS2TBL_KEYBOARDMODEL_TYPE) == 0) + { + selectList.push_back(std::make_pair(KEYMAP_SEL_STANDARD, KEYMAP_STANDARD)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_WYSE_KB3926, KEYMAP_UK_WYSE_KB3926)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_OADG109, KEYMAP_JAPAN_OADG109)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_SANWA_SKBL1, KEYMAP_JAPAN_SANWA_SKBL1)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_4, KEYMAP_NOT_ASSIGNED_4)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_5, KEYMAP_NOT_ASSIGNED_5)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_6, KEYMAP_NOT_ASSIGNED_6)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_PERIBOARD_810, KEYMAP_UK_PERIBOARD_810)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_OMOTON_K8508, KEYMAP_UK_OMOTON_K8508)); + } + else if(option.compare(PS2TBL_MACHINE_TYPE) == 0) + { + selectList.push_back(std::make_pair(PC9801_SEL_ALL, PC9801_ALL)); +// selectList.push_back(std::make_pair(X1_SEL_ORIG, X1_ORIG)); +// selectList.push_back(std::make_pair(X1_SEL_TURBO, X1_TURBO)); +// selectList.push_back(std::make_pair(X1_SEL_TURBOZ, X1_TURBOZ)); + } + else if(option.compare(PS2TBL_PC9801_CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(PC9801_CTRL_SEL_GRAPH, PC9801_CTRL_GRAPH)); + selectList.push_back(std::make_pair(PC9801_CTRL_SEL_CAPS, PC9801_CTRL_CAPS)); + selectList.push_back(std::make_pair(PC9801_CTRL_SEL_KANA, PC9801_CTRL_KANA)); + selectList.push_back(std::make_pair(PC9801_CTRL_SEL_SHIFT, PC9801_CTRL_SHIFT)); + selectList.push_back(std::make_pair(PC9801_CTRL_SEL_CTRL, PC9801_CTRL_CTRL)); + } else + { + // Not found! + result = false; + } + + // Return result, false if the option not found, true otherwise. + // + return(result); +} + + +// Method to read the Keymap array, 1 row at a time and return it to the caller. +// +bool PC9801::getKeyMapData(std::vector& dataArray, int *row, bool start) +{ + // Locals. + // + bool result = false; + + // If start flag is set, set row to 0. + if(start == true) + { + (*row) = 0; + } + + // Bound check and if still valid, push data onto the vector. + if((*row) >= pcCtrl.kmeRows) + { + result = true; + } else + { + dataArray.push_back(pcCtrl.kme[*row].ps2KeyCode); + dataArray.push_back(pcCtrl.kme[*row].ps2Ctrl); + dataArray.push_back(pcCtrl.kme[*row].keyboardModel); + dataArray.push_back(pcCtrl.kme[*row].machine); + // dataArray.push_back(pcCtrl.kme[*row].x1Mode); + // dataArray.push_back(pcCtrl.kme[*row].x1Key); + // dataArray.push_back(pcCtrl.kme[*row].x1Key2); + // dataArray.push_back(pcCtrl.kme[*row].x1Ctrl); + (*row) = (*row) + 1; + } + + // True if no more rows, false if additional rows can be read. + return(result); +} + + +// Initialisation routine. Start two threads, one to handle the incoming PS/2 keyboard data and map it, the second to handle the host interface. +void PC9801::init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Basic initialisation. + init(hdlNVS, hdlHID); + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); + + // Prepare the UART to be used for communications with the PC-9801. + // The PC-9801 uses Asynchronous protocol with 1 start bit, 1 stop bit, odd parity, 19200 baud. + // + uart_config_t uartConfig = { + .baud_rate = 19200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_ODD, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 122, + .source_clk = UART_SCLK_APB, + }; + + // Install UART driver. Use RX/TX buffers without event queue. + ESP_ERROR_CHECK(uart_driver_install(pcCtrl.uartNum, pcCtrl.uartBufferSize, pcCtrl.uartBufferSize, 0, NULL, 0)); + + // Configure UART parameters and pin assignments, software flow control, not RTS/CTS. + ESP_ERROR_CHECK(uart_param_config(pcCtrl.uartNum, &uartConfig)); + ESP_ERROR_CHECK(uart_set_pin(pcCtrl.uartNum, CONFIG_HOST_KDB0, CONFIG_HOST_KDB3, -1, -1)); + + // Create queue for buffering incoming HID keys prior to transmitting to the PC-9801. + xmitQueue = xQueueCreate(MAX_PC9801_XMIT_KEY_BUF, sizeof(t_xmitQueueMessage)); + // Create queue for buffering incoming PC-9801 data for later processing. + rcvQueue = xQueueCreate(MAX_PC9801_RCV_KEY_BUF, sizeof(t_rcvQueueMessage)); + + // Create a task pinned to core 1 which will fulfill the NEC PC-9801 interface. This task has the highest priority + // and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means + // all other tasks running on Core 1 will suspend as needed. The HID devices will be serviced with core 0. + // + // Core 1 - NEC PC-9801 Interface + ESP_LOGW(MAINTAG, "Starting NEC PC-9801 if thread..."); + ::xTaskCreatePinnedToCore(&this->pcInterface, "pc9801if", 4096, this, 25, &this->TaskHostIF, 1); + vTaskDelay(500); + + // Core 0 - Application + // HID Interface handler thread. + ESP_LOGW(MAINTAG, "Starting hidIf thread..."); + ::xTaskCreatePinnedToCore(&this->hidInterface, "hidIf", 8192, this, 22, &this->TaskHIDIF, 0); +} + +// Initialisation routine without hardware. +void PC9801::init(NVS *hdlNVS, HID *hdlHID) +{ + // Initialise control variables. + this->pcCtrl.keyCtrl = 0xFF; // Negative logic, 0 - active, 1 = inactive. + pcCtrl.optionSelect = false; + pcCtrl.uartNum = UART_NUM_2; + pcCtrl.uartBufferSize = 256; + pcCtrl.uartQueueSize = 10; + pcCtrl.keyMapFileName = pcCtrl.fsPath.append("/").append(PC9801IF_KEYMAP_FILE); + pcCtrl.kmeRows = 0; + pcCtrl.kme = NULL; + pcCtrl.persistConfig = false; + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); + + // Load the keyboard mapping table into memory. If the file doesnt exist, create it. + loadKeyMap(); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(getClassName(__PRETTY_FUNCTION__), &this->pcConfig, sizeof(t_pcConfig)) == false) + { + ESP_LOGW(MAINTAG, "PC9801 configuration set to default, no valid config in NVS found."); + pcConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + pcConfig.params.activeMachineModel = PC9801_ALL; + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->pcConfig, sizeof(t_pcConfig)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Default PC9801 configuration data failed, check NVS setup.\n"); + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(this->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } +} + +// Constructor, basically initialise the Singleton interface and let the threads loose. +PC9801::PC9801(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->pcCtrl.fsPath = fsPath; + + // Initialise the interface. + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, basic without hardware. +PC9801::PC9801(NVS *hdlNVS, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->pcCtrl.fsPath = fsPath; + + // Initialise the interface. + init(hdlNVS, hdlHID); +} + +// Constructor, used for version reporting so no hardware is initialised. +PC9801::PC9801(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +PC9801::~PC9801(void) +{ + return; +} diff --git a/main/PS2KeyAdvanced.cpp b/main/PS2KeyAdvanced.cpp deleted file mode 120000 index da2eff6..0000000 --- a/main/PS2KeyAdvanced.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/PS2KeyAdvanced.cpp \ No newline at end of file diff --git a/main/PS2KeyAdvanced.cpp b/main/PS2KeyAdvanced.cpp new file mode 100644 index 0000000..a220349 --- /dev/null +++ b/main/PS2KeyAdvanced.cpp @@ -0,0 +1,1113 @@ +/* Version V1.0.9 + PS2KeyAdvanced.cpp - PS2KeyAdvanced library + Copyright (c) 2007 Free Software Foundation. All right reserved. + Written by Paul Carpenter, PC Services + Created September 2014 + Updated January 2016 - Paul Carpenter - add tested on Due and tidy ups for V1.5 Library Management + January 2020 Fix typos, correct keyboard reset status improve library.properties + and additional platform handling and some documentation + March 2020 Add SAMD1 as recognised support as has been tested by user + Improve different architecture handling + November 2020 Add support for STM32 from user Hiabuto-de + Tested on STM32Duino-Framework and PlatformIO on STM32F103C8T6 and an IBM Model M + July 2021 Add workaround for ESP32 issue with Silicon (hardware) from user submissions + + IMPORTANT WARNING + + If using a DUE or similar board with 3V3 I/O you MUST put a level translator + like a Texas Instruments TXS0102 or FET circuit as the signals are + Bi-directional (signals transmitted from both ends on same wire). + + Failure to do so may damage your Arduino Due or similar board. + + Test History + September 2014 Uno and Mega 2560 September 2014 using Arduino V1.6.0 + January 2016 Uno, Mega 2560 and Due using Arduino 1.6.7 and Due Board + Manager V1.6.6 + + + Assumption - Only ONE keyboard added to one Arduino + - No stream support + + This is for a LATIN style keyboard using Scan code set 2. See various + websites on what different scan code sets use. Scan Code Set 2 is the + default scan code set for PS2 keyboards on power up. + + Fully featured PS2 keyboard library to provide + All keys as a keycode (A-Z and 0-9 as ASCII equivalents) + All function and movement keys supported even multi-lingual + Parity checking of data sent/received on receive request keyboard resend + Resends data when needed handles keyboard protocol for RESEND and ECHO + Functions for get and set of + Scancode set in use READ only + LED and LOCK control + ReadID + Reset keyboard + Send ECHO + Handles NUM, _CAPS and SCROLL lock keys to LEDs + Handles NUM/SCROLL internally + + Returns an uint16_t containing + Make/Break status + CAPS status + SHIFT, CTRL, ALT, ALT GR, GUI keys + Flag for function key not a displayable/printable character + 8 bit key code + + Code Ranges(bottom byte of uint16_t) see PS2KeyAdvanced.h for details + 0 invalid/error + 1-1F Functions (_CAPS, _SHIFT, _ALT, Enter, DEL... ) + 1A-1F Functions with ASCII control code + (DEL, BS, TAB, ESC, ENTER, SPACE) + 20-60 Printable characters noting + 0-9 = 0x30 to 0x39 as ASCII + A to Z = 0x41 to 0x5A as upper case ASCII type codes + 8B Extra European key + 61-A0 Function keys and other special keys (plus F2 and F1 less 8B) + 61-78 F1 to F24 + 79-8A Multimedia + 8C-8E ACPI power + 91-A0 and F2 and F1 - Special multilingual + A8-FF Keyboard communications commands (note F2 and F1 are special + codes for special multi-lingual keyboards) + + By using these ranges it is possible to perform detection of any key and do + easy translation to ASCII/UTF-8 avoiding keys that do not have a valid code. + + Top Byte is 8 bits denoting as follows with defines for bit code + + Define name bit description + PS2_BREAK 15 1 = Break key code + (MSB) 0 = Make Key code + PS2_SHIFT 14 1 = Shift key pressed as well (either side) + 0 = NO shift key + PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + 0 = NO Ctrl key + PS2_CAPS 12 1 = Caps Lock ON + 0 = Caps lock OFF + PS2_ALT 11 1 = Left Alt key pressed as well + 0 = NO Left Alt key + PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + 0 = NO Right Alt key + PS2_GUI 9 1 = GUI key pressed as well (either) + 0 = NO GUI key + PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + 0 = standard character key + + Error Codes + Most functions return 0 or 0xFFFF as error, other codes to note and + handle appropriately value in bottom byte + 0xAA keyboard has reset and passed power up tests + will happen if keyboard plugged in after code start + 0xFC Keyboard General error or power up fail + + It is responsibility of your programme to deal with converting special cases + like <_CTRL>+ sends a special code to something else. A better method + is to use PS2KeyMap library and add your own table to that library. If you + wish to do that make a NEW library called SOMETHING different NOT a variant + or revision of this one, as you are changing base functionality + + See PS2KeyCode.h for codes from the keyboard this library uses to decode. + (may disappear in updates do not rely on this file or definitions) + + See PS2KeyAvanced.h for returned definitions of Keys and accessible + definitions + + See PS2KeyMap.h for tables currently supported + + To get the key as ASCII/UTF-8 single byte character conversion requires use + of PS2KeyMap library AS WELL. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include +// Internal headers for library defines/codes/etc +#include "PS2KeyAdvanced.h" +#include "PS2KeyCode.h" +#include "PS2KeyTable.h" + + +// Private function declarations +void send_bit( void ); +void send_now( uint8_t ); +int16_t send_next( void ); +void ps2_reset( void ); +uint8_t decode_key( uint8_t ); +void pininput( uint8_t ); +void set_lock( ); + +/* Constant control functions to flags array + in translated key code value order */ +#if defined( PS2_REQUIRES_PROGMEM ) +const uint8_t PROGMEM control_flags[] = { +#else +const uint8_t control_flags[] = { +#endif + _SHIFT, _SHIFT, _CTRL, _CTRL, + _ALT, _ALT_GR, _GUI, _GUI + }; + +// Private Variables +volatile uint8_t _ps2mode; /* _ps2mode contains + _PS2_BUSY bit 7 = busy until all expected bytes RX/TX + _TX_MODE bit 6 = direction 1 = TX, 0 = RX (default) + _BREAK_KEY bit 5 = break code detected + _WAIT_RESPONSE bit 4 = expecting data response + _E0_MODE bit 3 = in E0 mode + _E1_MODE bit 2 = in E1 mode + _LAST_VALID bit 1 = last sent valid in case we receive resend + and not sent anything */ + +/* volatile RX buffers and variables accessed via interrupt functions */ +volatile uint16_t _rx_buffer[ _RX_BUFFER_SIZE ]; // buffer for data from keyboard +volatile uint8_t _head; // _head = last byte written +uint8_t _tail; // _tail = last byte read (not modified in IRQ ever) +volatile int8_t _bytes_expected; +volatile uint8_t _bitcount; // Main state variable and bit count for interrupts +volatile uint8_t _shiftdata; +volatile uint8_t _parity; + +/* TX variables */ +volatile uint8_t _tx_buff[ _TX_BUFFER_SIZE ]; // buffer for keyboard commands +volatile uint8_t _tx_head; // buffer write pointer +volatile uint8_t _tx_tail; // buffer read pointer +volatile uint8_t _last_sent; // last byte if resend requested +volatile uint8_t _now_send; // immediate byte to send +volatile uint8_t _response_count; // bytes expected in reply to next TX +volatile uint8_t _tx_ready; // TX status for type of send contains + /* _HANDSHAKE 0x80 = handshaking command (ECHO/RESEND) + _COMMAND 0x01 = other command processing */ + +/* Output key buffering */ +uint16_t _key_buffer[ _KEY_BUFF_SIZE ]; // Output Buffer for translated keys +uint8_t _key_head; // Output buffer WR pointer +uint8_t _key_tail; // Output buffer RD pointer +uint8_t _mode = 0; // Mode for output buffer contains + /* _NO_REPEATS 0x80 No repeat make codes for _CTRL, _ALT, _SHIFT, _GUI + _NO_BREAKS 0x08 No break codes */ + +// Arduino settings for pins and interrupts Needed to send data +uint8_t PS2_DataPin; +uint8_t PS2_IrqPin; + +// Key decoding variables +uint8_t PS2_led_lock = 0; // LED and Lock status +uint8_t PS2_lockstate[ 4 ]; // Save if had break on key for locks +uint8_t PS2_keystatus; // current CAPS etc status for top byte + + +/*------------------ Code starts here -------------------------*/ + +/* The ISR for the external interrupt + To receive 11 bits - start 8 data, ODD parity, stop + To send data calls send_bit( ) + Interrupt every falling incoming clock edge from keyboard */ +IRAM_ATTR void ps2interrupt( void ) +{ +// Workaround for ESP32 SILICON error see extra/Porting.md +#ifdef PS2_ONLY_CHANGE_IRQ +if( digitalRead( PS2_IrqPin ) ) + return; +#endif +if( _ps2mode & _TX_MODE ) + send_bit( ); +else + { + static uint32_t prev_ms = 0; + uint32_t now_ms; + uint8_t val, ret; + + val = digitalRead( PS2_DataPin ); + /* timeout catch for glitches reset everything */ + now_ms = millis( ); + if( now_ms - prev_ms > 250 ) + { + _bitcount = 0; + _shiftdata = 0; + } + prev_ms = now_ms; + _bitcount++; // Now point to next bit + switch( _bitcount ) + { + case 1: // Start bit + _parity = 0; + _ps2mode |= _PS2_BUSY; // set busy + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: // Data bits + _parity += val; // another one received ? + _shiftdata >>= 1; // right _SHIFT one place for next bit + _shiftdata |= ( val ) ? 0x80 : 0; // or in MSbit + break; + case 10: // Parity check + _parity &= 1; // Get LSB if 1 = odd number of 1's so parity bit should be 0 + if( _parity == val ) // Both same parity error + _parity = 0xFD; // To ensure at next bit count clear and discard + break; + case 11: // Stop bit lots of spare time now + if( _parity >= 0xFD ) // had parity error + { + send_now( PS2_KC_RESEND ); // request resend + _tx_ready |= _HANDSHAKE; + } + else // Good so save byte in _rx_buffer + { + // Check _SHIFTed data for commands and action + ret = decode_key( _shiftdata ); + if( ret & 0x2 ) // decrement expected bytes + _bytes_expected--; + if( _bytes_expected <= 0 || ret & 4 ) // Save value ?? + { + val = _head + 1; + if( val >= _RX_BUFFER_SIZE ) + val = 0; + if( val != _tail ) + { + // get last byte to save + _rx_buffer[ val ] = uint16_t( _shiftdata ); + // save extra details + _rx_buffer[ val ] |= uint16_t( _ps2mode ) << 8; + _head = val; + } + } + if( ret & 0x10 ) // Special command to send (ECHO/RESEND) + { + send_now( _now_send ); + _tx_ready |= _HANDSHAKE; + } + else + if( _bytes_expected <= 0 ) // Receive data finished + { + // Set mode and status for next receive byte + _ps2mode &= ~( _E0_MODE + _E1_MODE + _WAIT_RESPONSE + _BREAK_KEY ); + _bytes_expected = 0; + _ps2mode &= ~_PS2_BUSY; + send_next( ); // Check for more to send + } + } + _bitcount = 0; // end of byte + break; + default: // in case of weird error and end of byte reception re-sync + _bitcount = 0; + } + } +} + + +/* Decode value received to check for errors commands and responses + NOT keycode translate yet + returns bit Or'ing + 0x10 send command in _now_send (after any saves and decrements) + 0x08 error abort reception and reset status and queues + 0x04 save value ( complete after translation ) + 0x02 decrement count of bytes to expected + + Codes like EE, AA and FC ( Echo, BAT pass and fail) treated as valid codes + return code 6 +*/ +uint8_t decode_key( uint8_t value ) +{ +uint8_t state; + +state = 6; // default state save and decrement + +// Anything but resend received clear valid value to resend +if( value != PS2_KC_RESEND ) + _ps2mode &= ~( _LAST_VALID ); + +// First check not a valid response code from a host command +if( _ps2mode & _WAIT_RESPONSE ) + if( value < 0xF0 ) + return state; // Save response and decrement + +// E1 Pause mode special case just decrement +if( _ps2mode & _E1_MODE ) + return 2; + +switch( value ) + { + case 0: // Buffer overrun Errors Reset modes and buffers + case PS2_KC_OVERRUN: + ps2_reset( ); + state = 0xC; + break; + case PS2_KC_RESEND: // Resend last byte if we have sent something + if( ( _ps2mode & _LAST_VALID ) ) + { + _now_send = _last_sent; + state = 0x10; + } + else + state = 0; + break; + case PS2_KC_ERROR: // General error pass up but stop any sending or receiving + _bytes_expected = 0; + _ps2mode = 0; + _tx_ready = 0; + state = 0xE; + break; + case PS2_KC_KEYBREAK: // break Code - wait the final key byte + _bytes_expected = 1; + _ps2mode |= _BREAK_KEY; + state = 0; + break; + case PS2_KC_ECHO: // Echo if we did not originate echo back + state = 4; // always save + if( _ps2mode & _LAST_VALID && _last_sent != PS2_KC_ECHO ) + { + _now_send = PS2_KC_ECHO; + state |= 0x10; // send _command on exit + } + break; + case PS2_KC_BAT: // BAT pass + _bytes_expected = 0; // reset as if in middle of something lost now + state = 4; + break; + case PS2_KC_EXTEND1: // Major extend code (PAUSE key only) + if( !( _ps2mode & _E1_MODE ) ) // First E1 only + { + _bytes_expected = 7; // seven more bytes + _ps2mode |= _E1_MODE; + _ps2mode &= ~_BREAK_KEY; // Always a make + } + state = 0; + break; + case PS2_KC_EXTEND: // Two byte Extend code + _bytes_expected = 1; // one more byte at least to wait for + _ps2mode |= _E0_MODE; + state = 0; + break; + } +return state; +} + + +/* Send data to keyboard + Data pin direction should already be changed + Start bit would be already set so each clock setup for next clock + parity and _bitcount should be 0 already and busy should be set + + Start bit setting is due to bug in attachinterrupt not clearing pending interrupts + Also no clear pending interrupt function */ +void send_bit( void ) +{ +uint8_t val; + +_bitcount++; // Now point to next bit +switch( _bitcount ) + { + case 1: +#if defined( PS2_CLEAR_PENDING_IRQ ) + // Start bit due to Arduino bug + digitalWrite( PS2_DataPin, LOW ); + break; +#endif + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + // Data bits + val = _shiftdata & 0x01; // get LSB + digitalWrite( PS2_DataPin, val ); // send start bit + _parity += val; // another one received ? + _shiftdata >>= 1; // right _SHIFT one place for next bit + break; + case 10: + // Parity - Send LSB if 1 = odd number of 1's so parity should be 0 + digitalWrite( PS2_DataPin, ( ~_parity & 1 ) ); + break; + case 11: // Stop bit write change to input pull up for high stop bit + pininput( PS2_DataPin ); + break; + case 12: // Acknowledge bit low we cannot do anything if high instead of low + if( !( _now_send == PS2_KC_ECHO || _now_send == PS2_KC_RESEND ) ) + { + _last_sent = _now_send; // save in case of resend request + _ps2mode |= _LAST_VALID; + } + // clear modes to receive again + _ps2mode &= ~_TX_MODE; + if( _tx_ready & _HANDSHAKE ) // If _HANDSHAKE done + _tx_ready &= ~_HANDSHAKE; + else // else we finished a command + _tx_ready &= ~_COMMAND; + if( !( _ps2mode & _WAIT_RESPONSE ) ) // if not wait response + send_next( ); // check anything else to queue up + _bitcount = 0; // end of byte + break; + default: // in case of weird error and end of byte reception re-sync + _bitcount = 0; + } +} + + +/* Takes a byte sets up variables and starts the data sending processes + Starts the actual byte transmission + calling code must make sure line is idle and able to send + Whilst this function adds long delays the process of the delays + will STOP the interrupt source (keyboard) externally when clock held low + _tx_ready contains 2 flags checked in this order + _HANDSHAKE command sent as part of receiving e.g. ECHO, RESEND + _COMMAND other commands not part of receiving + Main difference _bytes_expected is NOT altered in _HANDSHAKE mode + in command mode we update _bytes_expected with number of response bytes +*/ +void send_now( uint8_t command ) +{ +_shiftdata = command; +_now_send = command; // copy for later to save in last sent +#if defined( PS2_CLEAR_PENDING_IRQ ) +_bitcount = 0; // AVR/SAM ignore extra interrupt +#else +_bitcount = 1; // Normal processors +#endif +_parity = 0; +_ps2mode |= _TX_MODE + _PS2_BUSY; + +// Only do this if sending a command not from Handshaking +if( !( _tx_ready & _HANDSHAKE ) && ( _tx_ready & _COMMAND ) ) + { + _bytes_expected = _response_count; // How many bytes command will generate + _ps2mode |= _WAIT_RESPONSE; + } + +// STOP interrupt handler +// Setting pin output low will cause interrupt before ready +detachInterrupt( digitalPinToInterrupt( PS2_IrqPin ) ); +// set pins to outputs and high +digitalWrite( PS2_DataPin, HIGH ); +pinMode( PS2_DataPin, OUTPUT ); +digitalWrite( PS2_IrqPin, HIGH ); +pinMode( PS2_IrqPin, OUTPUT ); +// Essential for PS2 spec compliance +delayMicroseconds( 10 ); +// set Clock LOW +digitalWrite( PS2_IrqPin, LOW ); +// Essential for PS2 spec compliance +// set clock low for 60us +delayMicroseconds( 60 ); +// Set data low - Start bit +digitalWrite( PS2_DataPin, LOW ); +// set clock to input_pullup data stays output while writing to keyboard +pininput( PS2_IrqPin ); +// Restart interrupt handler +attachInterrupt( digitalPinToInterrupt( PS2_IrqPin ), ps2interrupt, FALLING ); +// wait clock interrupt to send data +} + + +/* Send next byte/command from TX queue and start sending + Must be ready to send and idle + Assumes commands consist of 1 or more bytes and wait for response then may or + not be followed by further bytes to send with or without response + Checks + 1/ Buffer empty return empty buffer + 2/ Busy return busy (will be checked by interrupt routines later) + 3/ Read next byte (next byte to send) + 4/ Check if following byte(s) are command/data or response + + Returns 1 if started transmission or queued + -134 if already busy + -2 if buffer empty + + Note PS2_KEY_IGNORE is used to denote a byte(s) expected in response */ +int16_t send_next( void ) +{ +uint8_t i; +int16_t val; + +val = -1; +// Check buffer not empty +i = _tx_tail; +if( i == _tx_head ) + return -2; + +// set command bit in _tx_ready as another command to do +_tx_ready |= _COMMAND; + +// Already item waiting to be sent or sending interrupt routines will call back +if( _tx_ready & _HANDSHAKE ) + return -134; + +// if busy let interrupt catch and call us again +if( _ps2mode & _PS2_BUSY ) + return -134; + +// Following only accessed when not receiving or sending protocol bytes +// Scan for command response and expected bytes to follow +_response_count = 0; +do + { + i++; + if( i >= _TX_BUFFER_SIZE ) + i = 0; + if( val == -1 ) + val = _tx_buff[ i ]; + else + if( _tx_buff[ i ] != PS2_KEY_IGNORE ) + break; + else + _response_count++; + _tx_tail = i; + } +while( i != _tx_head ); +// Now know what to send and expect start the actual wire sending +send_now( val ); +return 1; +} + + +/* Send a byte to the TX buffer + Value in buffer of PS2_KEY_IGNORE signifies wait for response, + use one for each byte expected + + Returns -4 - if buffer full (buffer overrun not written) + Returns 1 byte written when done */ +int send_byte( uint8_t val ) +{ +uint8_t ret; + +ret = _tx_head + 1; +if( ret >= _TX_BUFFER_SIZE ) + ret = 0; +if( ret != _tx_tail ) + { + _tx_buff[ ret ] = val; + _tx_head = ret; + return 1; + } +return -4; +} + + +// initialize a data pin for input +void pininput( uint8_t pin ) +{ +#ifdef INPUT_PULLUP +pinMode( pin, INPUT_PULLUP ); +#else +digitalWrite( pin, HIGH ); +pinMode( pin, INPUT ); +#endif +} + + +void ps2_reset( void ) +{ +/* reset buffers and states */ +_tx_head = 0; +_tx_tail = 0; +_tx_ready = 0; +_response_count = 0; +_head = 0; +_tail = 0; +_bitcount = 0; +PS2_keystatus = 0; +PS2_led_lock = 0; +_ps2mode = 0; +} + + + +/* Translate PS2 keyboard code sequence into our key code data + PAUSE key (_E1_MODE) is de_ALT with as special case, and + command responses not translated + + Called from read function as too long for in interrupt + + Returns 0 for no valid key or processed internally ignored or similar + 0 for empty buffer + */ +uint16_t translate( void ) +{ + uint8_t index, length, data, status; + uint16_t retdata; + + // get next character + // Check first something to fetch + index = _tail; + + // check for empty buffer + if( index == _head ) + return 0; + index++; + if( index >= _RX_BUFFER_SIZE ) + index = 0; + + // Special handling for PAUSE/BREAK, PAUSE key doesnt send a BREAK code yet MZ machines need SHIFT (hold) -> BREAK to recognise a BREAK, CTRL+BREAK will not work. + // In this case we inject a BREAK code by setting the flag on the received code. + status = (( _rx_buffer[ index ] & 0xFF00 ) >> 8); + if( (status & _E1_MODE) && (status & _BREAK)) + { + _rx_buffer[index] &= ~PS2_BREAK; + } else + { + _tail = index; + } + + // Get the flags byte break modes etc in this order + data = _rx_buffer[ index ] & 0xFF; + index = ( _rx_buffer[ index ] & 0xFF00 ) >> 8; + + // Catch special case of PAUSE key + if( index & _E1_MODE ) + { + return ( (uint16_t)PS2_keystatus << 8 ) | PS2_KEY_PAUSE | PS2_FUNCTION | (status & _BREAK ? 0 : PS2_BREAK); + } + + // Ignore anything not actual keycode but command/response + // Return untranslated as valid + if( ( data >= PS2_KC_BAT && data != PS2_KC_LANG1 && data != PS2_KC_LANG2 ) || ( index & _WAIT_RESPONSE ) ) + { + return ( uint16_t )data; + } + + // Gather the break of key status + if( index & _BREAK_KEY ) + { + PS2_keystatus |= _BREAK; + } else + { + PS2_keystatus &= ~_BREAK; + } + + retdata = 0; // error code by default + + // Scan appropriate table + if( index & _E0_MODE ) + { + length = sizeof( extended_key ) / sizeof( extended_key[ 0 ] ); + for( index = 0; index < length; index++ ) + { + #if defined( PS2_REQUIRES_PROGMEM ) + if( data == pgm_read_byte( &extended_key[ index ][ 0 ] ) ) + { + retdata = pgm_read_byte( &extended_key[ index ][ 1 ] ); + #else + if( data == extended_key[ index ][ 0 ] ) + { + retdata = extended_key[ index ][ 1 ]; + #endif + break; + } + } + } else + { + length = sizeof( single_key ) / sizeof( single_key[ 0 ] ); + for( index = 0; index < length; index++ ) + { + #if defined( PS2_REQUIRES_PROGMEM ) + if( data == pgm_read_byte( &single_key[ index ][ 0 ] ) ) + { + retdata = pgm_read_byte( &single_key[ index ][ 1 ] ); + #else + if( data == single_key[ index ][ 0 ] ) + { + retdata = single_key[ index ][ 1 ]; + #endif + break; + } + } + } + + // trap not found key + if( index == length ) + { + retdata = 0; + } + + /* valid found values only */ + if( retdata > 0 ) + { + if( retdata <= PS2_KEY_CAPS ) + { // process lock keys need second make to turn off + if( PS2_keystatus & _BREAK ) + { + PS2_lockstate[ retdata ] = 0; // Set received a break so next make toggles LOCK status + // MZ-2500 uses one make-break cycle to enable and one make-break to disable, so the BREAK is needed. + if(retdata != PS2_KEY_CAPS) + { + retdata = PS2_KEY_IGNORE; // ignore key + } + } else { + + if( PS2_lockstate[ retdata ] == 1 ) + { + retdata = PS2_KEY_IGNORE; // ignore key if make and not received break + // As per above, MZ-2500 needs both events, so this code changed from original authors. + } else + { + PS2_lockstate[ retdata ] = 1; + switch( retdata ) + { + case PS2_KEY_CAPS: + index = PS2_LOCK_CAPS; + + // Set CAPS lock if not set before + if( PS2_keystatus & _CAPS ) + { + PS2_keystatus &= ~_CAPS; + } else + { + PS2_keystatus |= _CAPS; + } + break; + case PS2_KEY_SCROLL: + index = PS2_LOCK_SCROLL; + break; + case PS2_KEY_NUM: + index = PS2_LOCK_NUM; + break; + } + // Now update PS2_led_lock status to match + if( PS2_led_lock & index ) + { + PS2_led_lock &= ~index; + if(index != PS2_LOCK_CAPS) + { + PS2_keystatus |= _BREAK; // send as break + } + } else + { + PS2_led_lock |= index; + } + set_lock( ); + } + } + } else + { + + if( retdata >= PS2_KEY_L_SHIFT && retdata <= PS2_KEY_R_GUI ) + { // Update bits for _SHIFT, _CTRL, _ALT, _ALT GR, _GUI in status + #if defined( PS2_REQUIRES_PROGMEM ) + index = pgm_read_byte( &control_flags[ retdata - PS2_KEY_L_SHIFT ] ); + #else + index = control_flags[ retdata - PS2_KEY_L_SHIFT ]; + #endif + if( PS2_keystatus & _BREAK ) + { + PS2_keystatus &= ~index; + } + + // if already set ignore repeats if flag set + else if( ( PS2_keystatus & index ) && ( _mode & _NO_REPEATS ) ) + { + retdata = PS2_KEY_IGNORE; // ignore repeat _SHIFT, _CTRL, _ALT, _GUI + } else + { + PS2_keystatus |= index; + } + } else + { + // Numeric keypad ONLY works in numlock state or when _SHIFT status + if( retdata >= PS2_KEY_KP0 && retdata <= PS2_KEY_KP_DOT ) + { + if( !( PS2_led_lock & PS2_LOCK_NUM ) || ( PS2_keystatus & _SHIFT ) ) + { + #if defined( PS2_REQUIRES_PROGMEM ) + retdata = pgm_read_byte( &scroll_remap[ retdata - PS2_KEY_KP0 ] ); + #else + retdata = scroll_remap[ retdata - PS2_KEY_KP0 ]; + #endif + } + } + } + + // Sort break code handling or ignore for all having processed the _SHIFT etc status + if( ( PS2_keystatus & _BREAK ) && ( _mode & _NO_BREAKS ) ) + { + return ( uint16_t )PS2_KEY_IGNORE; + } + + // Assign Function keys _mode + if( ( retdata <= PS2_KEY_SPACE || retdata >= PS2_KEY_F1) && retdata != PS2_KEY_BTICK && retdata != PS2_KEY_HASH && retdata != PS2_KEY_EUROPE2 ) + { + PS2_keystatus |= _FUNCTION; + } else + { + PS2_keystatus &= ~_FUNCTION; + } + } + } + return ( retdata | ( (uint16_t)PS2_keystatus << 8 ) ); +} + + +/* Build command to send lock status + Assumes data is within range */ +void set_lock( ) +{ +// ESP32: Heltec 32 Wifi - Bug found with a DELL KB-3926 keyboard. Basically sending more than one byte using this class +// mechanism of waiting for a response in the send/interrupt procedures results in a lockup. +// Workaround: Send one byte, wait for one response with a delay loop, send next byte. +send_byte( PS2_KC_LOCK ); // send command +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait ACK + send_next( ); // if idle start transmission +// A delay of less than 10mS will cause lockup or abort!!! +delayMicroseconds( 10000 ); +send_byte( PS2_led_lock ); // send data from internal variable +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait ACK + send_next( ); // if idle start transmission +} + + +/* Send echo command to keyboard + returned data in keyboard buffer read as keys */ +void PS2KeyAdvanced::echo( void ) +{ +send_byte( PS2_KC_ECHO ); // send command +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait data PS2_KC_ECHO + send_next( ); // if idle start transmission +} + + +/* Get the ID used in keyboard + returned data in keyboard buffer read as keys */ +void PS2KeyAdvanced::readID( void ) +{ +send_byte( PS2_KC_READID ); // send command +send_byte( PS2_KEY_IGNORE ); // wait ACK +send_byte( PS2_KEY_IGNORE ); // wait data +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait data + send_next( ); // if idle start transmission +} + + +/* Get the current Scancode Set used in keyboard + returned data in keyboard buffer read as keys */ +void PS2KeyAdvanced::getScanCodeSet( void ) +{ +send_byte( PS2_KC_SCANCODE ); // send command +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait data + send_next( ); // if idle start transmission +delayMicroseconds( 10000 ); +send_byte( 0 ); // send data 0 = read +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait data + send_next( ); // if idle start transmission +} + + +/* Returns the current status of Locks */ +uint8_t PS2KeyAdvanced::getLock( ) +{ +return( PS2_led_lock ); +} + + +/* Sets the current status of Locks and LEDs */ +void PS2KeyAdvanced::setLock( uint8_t code ) +{ +code &= 0xF; // To allow for rare keyboards with extra LED +PS2_led_lock = code; // update our lock copy +PS2_keystatus &= ~_CAPS; // Update copy of _CAPS lock as well +PS2_keystatus |= ( code & PS2_LOCK_CAPS ) ? _CAPS : 0; +set_lock( ); +} + + +/* Set library to not send break key codes + 1 = no break codes + 0 = send break codes */ +void PS2KeyAdvanced::setNoBreak( uint8_t data ) +{ +_mode &= ~_NO_BREAKS; +_mode |= data ? _NO_BREAKS : 0; +} + + /* Set library to not repeat make codes for _CTRL, _ALT, _GUI, _SHIFT + 1 = no repeat codes + 0 = send repeat codes */ +void PS2KeyAdvanced::setNoRepeat( uint8_t data ) +{ +_mode &= ~_NO_REPEATS; +_mode |= data ? _NO_REPEATS : 0; +} + + +/* Resets keyboard when reset has completed + keyboard sends AA - Pass or FC for fail */ +void PS2KeyAdvanced::resetKey( ) +{ +send_byte( PS2_KC_RESET ); // send command +send_byte( PS2_KEY_IGNORE ); // wait ACK +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait data PS2_KC_BAT or PS2_KC_ERROR + send_next( ); // if idle start transmission +// LEDs and KeyStatus Reset too... to match keyboard +PS2_led_lock = 0; +PS2_keystatus = 0; +} + + +/* Send Typematic rate/delay command to keyboard + First Parameter rate is 0 - 0x1F (31) + 0 = 30 CPS + 0x1F = 2 CPS + default in keyboard is 0xB (10.9 CPS) + Second Parameter delay is 0 - 3 for 0.25s to 1s in 0.25 increments + default in keyboard is 1 = 0.5 second delay + Returned data in keyboard buffer read as keys + + Error returns 0 OK + -5 parameter error + */ +int PS2KeyAdvanced::typematic( uint8_t rate, uint8_t delay ) +{ +if( rate > 31 || delay > 3 ) + return -5; +send_byte( PS2_KC_RATE ); // send command +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait ACK + send_next( ); // if idle start transmission +delayMicroseconds( 10000 ); +send_byte( ( delay << 5 ) + rate ); // Send values +if( ( send_byte( PS2_KEY_IGNORE ) ) ) // wait ACK + send_next( ); // if idle start transmission +return 0; +} + +uint8_t PS2KeyAdvanced::keyAvailable( ) +{ +int8_t i; + +i = _head - _tail; +if( i < 0 ) + i += _RX_BUFFER_SIZE; +return uint8_t( i ); +} + + +/* Returns count of available processed key codes + + If processed key buffer (_key_buffer) buffer returns max count + else processes input key code buffer until + either input buffer empty + or output buffer full + returns actual count + + Returns 0 buffer empty + 1 to buffer size less 1 as 1 to full buffer + + As with other ring buffers here when pointers match + buffer empty so cannot actually hold buffer size values */ +uint8_t PS2KeyAdvanced::available( ) +{ +int8_t i, idx; +uint16_t data; + +// check output queue +i = _key_head - _key_tail; +if( i < 0 ) + i += _KEY_BUFF_SIZE; +while( i < ( _KEY_BUFF_SIZE - 1 ) ) // process if not full +{ + if( keyAvailable( ) ) // not check for more keys to process + { + data = translate( ); // get next translated key + + if( data == 0 ) // unless in buffer is empty + break; + if( (data & 0xFF) != PS2_KEY_IGNORE && (data & 0xFF) > 0) + { + idx = _key_head + 1; // point to next space + if( idx >= _KEY_BUFF_SIZE ) // loop to front if necessary + idx = 0; + _key_buffer[ idx ] = data; // save the data to out buffer + _key_head = idx; + i++; // update count + } + } + else + break; // exit nothing coming in +} +return uint8_t( i ); +} + + +/* read a decoded key from the keyboard buffer + returns 0 for empty buffer */ +uint16_t PS2KeyAdvanced::read( ) +{ +uint16_t result; +uint8_t idx; + +while( ( result = available( ) ) ) + { + idx = _key_tail; + idx++; + if( idx >= _KEY_BUFF_SIZE ) // loop to front if necessary + idx = 0; + _key_tail = idx; + result = _key_buffer[ idx ]; + + // Filter out unwanted control data. + if((result & 0xFF) != PS2_KC_ACK && (result & 0xFF) != PS2_KC_RESEND && (result & 0xFF) != 0) + break; + } +return result; +} + +// Method to suspend the keyboard handler and disable the interrupts whilst other non-mutually inclusive tasks make +// use of system resources. +// +void PS2KeyAdvanced::suspend(bool suspend) +{ + // Suspend detaches the interrupt, ie. this module becomes inactive, attach re-enables the interrupt. + if(suspend) + { + detachInterrupt( digitalPinToInterrupt( PS2_IrqPin ) ); + } else + { + attachInterrupt( digitalPinToInterrupt( PS2_IrqPin ), ps2interrupt, FALLING ); + } + return; +} + +PS2KeyAdvanced::PS2KeyAdvanced( ) +{ +// nothing to do here, begin( ) does it all +} + +// Destructor - detach interrupts and free up resources. +PS2KeyAdvanced::~PS2KeyAdvanced(void) +{ + // Detach interrupts. + detachInterrupt( digitalPinToInterrupt( PS2_IrqPin ) ); +} + +/* instantiate class for keyboard */ +void PS2KeyAdvanced::begin( uint8_t data_pin, uint8_t irq_pin ) +{ +/* PS2 variables reset */ +ps2_reset( ); + +PS2_DataPin = data_pin; +PS2_IrqPin = irq_pin; + +// initialize the pins +pininput( PS2_IrqPin ); /* Setup Clock pin */ +pininput( PS2_DataPin ); /* Setup Data pin */ + +// Start interrupt handler +attachInterrupt( digitalPinToInterrupt( irq_pin ), ps2interrupt, FALLING ); +} diff --git a/main/PS2Mouse.cpp b/main/PS2Mouse.cpp deleted file mode 120000 index dfb287c..0000000 --- a/main/PS2Mouse.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/PS2Mouse.cpp \ No newline at end of file diff --git a/main/PS2Mouse.cpp b/main/PS2Mouse.cpp new file mode 100644 index 0000000..ec0f470 --- /dev/null +++ b/main/PS2Mouse.cpp @@ -0,0 +1,682 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: PS2Mouse.cpp +// Created: Jan 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: PS/2 Mouse Class. +// This source file contains the class to encapsulate a PS/2 mouse. Given two GPIO +// pins, datapin and clkpin, it is able to communicate, configure and return mouse +// data via a rich set of methods. +// +// This class borrows ideas from the interrupt concept of the PS2KeyAdvanced class +// for communicating via the PS/2 protocol. +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The application uses the Espressif Development environment with Arduino components. +// This is necessary as the class uses the Arduino methods for GPIO manipulation. I +// was considering using pure Espressif IDF methods but considered the potential +// of also using this class on an Arduino project. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "PS2Mouse.h" + +// Global handle to allow the static interrupt routine to access the instantiated object. This does limit this class to being Singleton +// but it is unusual to have more than 1 PS/2 mouse on a project so less of a problem. +PS2Mouse *pThis; + +// Constructor. Simple assign the hardware data and clock pins to internal variables and setup +// variables. Actual real initialisation is performed by a public method so re-initialisation can +// be made if required. +// +PS2Mouse::PS2Mouse(int clockPin, int dataPin) +{ + ps2Ctrl.clkPin = clockPin; + ps2Ctrl.dataPin = dataPin; + ps2Ctrl.supportsIntelliMouseExtensions = false; + ps2Ctrl.mouseDataCallback = NULL; +} + +// Destructor - Detach interrupts and free resources. +// +PS2Mouse::~PS2Mouse() +{ + // Disable interrupts. + detachInterrupt( digitalPinToInterrupt( ps2Ctrl.clkPin ) ); +} + +// The interrupt handler triggered on each falling edge of the clock pin. +// Rx Mode: 11 bits - <8 data bits> +// Tx Mode: 11 bits - <8 data bits> +IRAM_ATTR void PS2Mouse::ps2interrupt( void ) +{ + // Locals. + // + static uint32_t timeLast = 0; + uint32_t timeCurrent; + uint8_t dataBit; + + // Workaround for ESP32 SILICON error see extra/Porting.md + #ifdef PS2_ONLY_CHANGE_IRQ + if( digitalRead( ps2Ctrl.clkPin ) ) + return; + #endif + + // TRANSMIT MODE. + if( pThis->ps2Ctrl.mode & _TX_MODE ) + { + // Received data not valid when transmitting. + pThis->ps2Ctrl.rxPos = 0; + + // Now point to next bit + pThis->ps2Ctrl.bitCount++; + + // BIT 1 - START BIT + if(pThis->ps2Ctrl.bitCount == 1) + { + #if defined( PS2_CLEAR_PENDING_IRQ ) + // Start bit due to Arduino bug + digitalWrite(pThis->ps2Ctrl.dataPin, LOW); + break; + #endif + } else + // BIT 2->9 - DATA BIT MSB->LSB + if(pThis->ps2Ctrl.bitCount >= 2 && pThis->ps2Ctrl.bitCount <= 9) + { + // Data bits + dataBit = pThis->ps2Ctrl.shiftReg & 0x01; // get LSB + digitalWrite(pThis->ps2Ctrl.dataPin, dataBit); // send start bit + pThis->ps2Ctrl.parity += dataBit; // another one received ? + pThis->ps2Ctrl.shiftReg >>= 1; // right _SHIFT one place for next bit + } else + // BIT 10 - PARITY BIT + if(pThis->ps2Ctrl.bitCount == 10) + { + // Parity - Send LSB if 1 = odd number of 1's so ps2Ctrl.parity should be 0 + digitalWrite( pThis->ps2Ctrl.dataPin, ( ~pThis->ps2Ctrl.parity & 1 ) ); + } else + // BIT 11 - STOP BIT + if(pThis->ps2Ctrl.bitCount == 11) + { + // Stop bit write change to input pull up for high stop bit + digitalWrite( pThis->ps2Ctrl.dataPin, HIGH ); + pinMode( pThis->ps2Ctrl.dataPin, INPUT ); + } else + // BIT 12 - ACK BIT + if(pThis->ps2Ctrl.bitCount == 12) + { + // Acknowledge bit low we cannot do anything if high instead of low + // clear modes to receive again + pThis->ps2Ctrl.mode &= ~_TX_MODE; + pThis->ps2Ctrl.bitCount = 0; // end of byte + } else + { + // in case of weird error and end of byte reception re-sync + pThis->ps2Ctrl.bitCount = 0; + } + } + + // RECEIVE MODE. + else + { + // Read latest bit. + dataBit = digitalRead( pThis->ps2Ctrl.dataPin ); + + // Get current time. + timeCurrent = millis( ); + + // Reset the receive byte buffer pointer if the gap from the last received byte to the current time is greater than a packet interbyte delay. + if(timeCurrent - timeLast > 100) + { + pThis->ps2Ctrl.rxPos = 0; + } + + // Catch glitches, any clock taking longer than 250ms is either a glitch, an error or start of a new packet. + if( timeCurrent - timeLast > 250 ) + { + pThis->ps2Ctrl.bitCount = 0; + pThis->ps2Ctrl.shiftReg = 0; + } + + // Store current time for next loop to detect timing issues. + timeLast = timeCurrent; + + // Now point to next bit + pThis->ps2Ctrl.bitCount++; + + // BIT 1 - START BIT + if(pThis->ps2Ctrl.bitCount == 1) + { + // Start bit + pThis->ps2Ctrl.parity = 0; + pThis->ps2Ctrl.mode |= _PS2_BUSY; // set busy + } else + // BIT 2->9 - DATA BIT MSB->LSB + if(pThis->ps2Ctrl.bitCount >= 2 && pThis->ps2Ctrl.bitCount <= 9) + { + // Data bits + pThis->ps2Ctrl.parity += dataBit; // another one received ? + pThis->ps2Ctrl.shiftReg >>= 1; // right _SHIFT one place for next bit + pThis->ps2Ctrl.shiftReg |= ( dataBit ) ? 0x80 : 0; // or in MSbit + } else + // BIT 10 - PARITY BIT + if(pThis->ps2Ctrl.bitCount == 10) + { + // Parity check + pThis->ps2Ctrl.parity &= 1; // Get LSB if 1 = odd number of 1's so ps2Ctrl.parity bit should be 0 + if( pThis->ps2Ctrl.parity == dataBit ) // Both same ps2Ctrl.parity error + pThis->ps2Ctrl.parity = 0xFD; // To ensure at next bit count clear and discard + } else + // BIT 11 - STOP BIT + if(pThis->ps2Ctrl.bitCount == 11) + { + // Streaming mode, assemble the data into the buffer. + if(pThis->ps2Ctrl.streamingEnabled) + { + if(pThis->ps2Ctrl.rxPos == 0 && pThis->streaming.newData == true) pThis->streaming.overrun = true; + if(pThis->ps2Ctrl.rxPos == 0) pThis->streaming.mouseData.status = pThis->ps2Ctrl.shiftReg; + if(pThis->ps2Ctrl.rxPos == 1) pThis->streaming.mouseData.position.x = pThis->ps2Ctrl.shiftReg; + if(pThis->ps2Ctrl.rxPos == 2) pThis->streaming.mouseData.position.y = pThis->ps2Ctrl.shiftReg; + if(pThis->ps2Ctrl.rxPos == 3) pThis->streaming.mouseData.wheel = pThis->ps2Ctrl.shiftReg; + if( (pThis->ps2Ctrl.supportsIntelliMouseExtensions == false && pThis->ps2Ctrl.rxPos == 2) || (pThis->ps2Ctrl.supportsIntelliMouseExtensions == true && pThis->ps2Ctrl.rxPos == 3)) + { + pThis->streaming.newData = true; + pThis->streaming.overrun = false; + pThis->ps2Ctrl.rxPos = 0; + } else + { + pThis->ps2Ctrl.rxPos++; + } + } else + { + // Save the received byte and parity, let consumer decide on it's validity. + pThis->ps2Ctrl.rxBuf[pThis->ps2Ctrl.rxPos++] = (pThis->ps2Ctrl.parity << 8 | pThis->ps2Ctrl.shiftReg); + } + // Set mode and status for next receive byte + pThis->ps2Ctrl.mode &= ~( _WAIT_RESPONSE ); + pThis->ps2Ctrl.mode &= ~_PS2_BUSY; + pThis->ps2Ctrl.bitCount = 0; // end of byte + } else + { + // in case of weird error and end of byte reception re-sync + pThis->ps2Ctrl.bitCount = 0; + } + } +} + +// Method to write a byte (control or parameter) to the Mouse. This method encapsulates the protocol necessary +// to invoke Host -> PS/2 Mouse transmission and the interrupts, on falling clock edge, process the byte to send +// and bitbang accordingly. +// +void PS2Mouse::writeByte(uint8_t command) +{ + // Locals. + // + uint32_t currentTime = millis(); + + // Test to see if a transmission is underway, block until the xmit buffer becomes available or timeout expires (no mouse). + // + while((ps2Ctrl.mode & _TX_MODE) && currentTime+100 > millis()); + + // If TX_MODE has been reset, interrupt processing has occurred so line up next byte, + // + if((ps2Ctrl.mode & _TX_MODE) == 0) + { + // Initialise the ps2 control variables. + ps2Ctrl.shiftReg = command; + ps2Ctrl.bitCount = 1; + ps2Ctrl.parity = 0; + ps2Ctrl.mode |= _TX_MODE + _PS2_BUSY; + ps2Ctrl.rxPos = 0; + + // Initialise the streaming buffer. + streaming.mouseData.valid = false; + streaming.mouseData.status = 0; + streaming.mouseData.position.x = 0; + streaming.mouseData.position.y = 0; + streaming.mouseData.wheel = 0; + streaming.newData = false; + streaming.overrun = false; + + // STOP the interrupt handler - Setting pin output low will cause interrupt before ready + detachInterrupt( digitalPinToInterrupt( ps2Ctrl.clkPin ) ); + + // Set data and clock pins to output and high + digitalWrite(ps2Ctrl.dataPin, HIGH); + pinMode(ps2Ctrl.dataPin, OUTPUT); + digitalWrite(ps2Ctrl.clkPin, HIGH); + pinMode(ps2Ctrl.clkPin, OUTPUT); + + // Essential for PS2 spec compliance + delayMicroseconds(10); + + // Set Clock LOW - trigger Host -> Mouse transmission. Mouse controls the clock but dragging clock low is used by the mouse to detect a host write and clock + // data in accordingly. + digitalWrite( ps2Ctrl.clkPin, LOW ); + + // Essential for PS2 spec compliance, set clock low for 60us + delayMicroseconds(60); + + // Set data low - Start bit + digitalWrite( ps2Ctrl.dataPin, LOW ); + + // Set clock to input_pullup data stays output while writing to keyboard + digitalWrite(ps2Ctrl.clkPin, HIGH); + pinMode(ps2Ctrl.clkPin, INPUT); + + // Restart interrupt handler + attachInterrupt( digitalPinToInterrupt( ps2Ctrl.clkPin ), ps2interrupt, FALLING ); + } + + // Everything is now processed in the interrupt handler. + return; +} + +// Setup and initialise the running object and Mouse hardware. This method must be called at startup and anytime a full reset is required. +// +void PS2Mouse::initialize() +{ + // Setup variables. + ps2Ctrl.mode = 0; + ps2Ctrl.supportsIntelliMouseExtensions = false; + ps2Ctrl.streamingEnabled = false; + ps2Ctrl.bitCount = 0; + ps2Ctrl.shiftReg = 0; + ps2Ctrl.parity = 0; + ps2Ctrl.rxPos = 0; + // Clear the receive buffer. + for(int idx=0; idx < 16; idx++) ps2Ctrl.rxBuf[idx] = 0x00; + + // Set data and clock pins to input. + digitalWrite(ps2Ctrl.dataPin, HIGH); + pinMode(ps2Ctrl.dataPin, INPUT); + digitalWrite(ps2Ctrl.clkPin, HIGH); + pinMode(ps2Ctrl.clkPin, INPUT); + + // Initialise the control structure. + ps2Ctrl.bitCount = 0; + ps2Ctrl.mode = 0; + ps2Ctrl.rxPos = 0; + + // As the interrupt handler is static it wont have reference to the instantiated object methods so we need to store the object in a pointer + // which is then used by the interrupt handler. + pThis = this; + + // Attach the clock line to a falling low interrupt trigger and handler. The Mouse toggles the clock line for each bit to be sent/received + // so we interrupt on each falling clock edge. + attachInterrupt( digitalPinToInterrupt( ps2Ctrl.clkPin ), ps2interrupt, FALLING ); + + // Setup the mouse, make a reset, check and set Intellimouse extensions, set the resolution, scaling, sample rate to defaults and switch to remote (polled) mode. + reset(); + checkIntelliMouseExtensions(); + setResolution(PS2_MOUSE_RESOLUTION_1_8); + setScaling(PS2_MOUSE_SCALING_1_1); + setSampleRate(PS2_MOUSE_SAMPLE_RATE_40); + setRemoteMode(); + + // All done. + return; +} + +// Public method to force a mouse reset. Used on startup and anytime the client believes the mouse has hungup. +// +bool PS2Mouse::reset(void) +{ + // Locals. + // + uint8_t respBuf[5]; + bool result = false; + + // Send command to reset the mouse, if it returns an ACK then reset succeeded. + // + if(sendCmd(MOUSE_CMD_RESET, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + result = true; + } + + // Return result. + return(result); +} + +// Private method to check and see if the mouse suports Microsoft Intellimouse extensions. It sets an internal state flag accordingly. +// +bool PS2Mouse::checkIntelliMouseExtensions(void) +{ + // Locals. + // + char deviceId; + + // IntelliMouse detection sequence, error checking isnt used. + setSampleRate(PS2_MOUSE_SAMPLE_RATE_200); + setSampleRate(PS2_MOUSE_SAMPLE_RATE_100); + setSampleRate(PS2_MOUSE_SAMPLE_RATE_80); + + // Get device Id and if the mouse supports Intellimouse extensions, it will reveal itself as an INTELLI_MOUSE. + deviceId = getDeviceId(); + ps2Ctrl.supportsIntelliMouseExtensions = (deviceId == INTELLI_MOUSE); + + // Return flag to indicate support (true) or no support (false). + return(ps2Ctrl.supportsIntelliMouseExtensions); +} + +// Public method to set the automatic sample rate. +// +bool PS2Mouse::setSampleRate(enum PS2_SAMPLING rate) +{ + // Locals. + // + uint8_t respBuf[5]; + bool result = false; + + // Sanity check. + if(rate == PS2_MOUSE_SAMPLE_RATE_10 || rate == PS2_MOUSE_SAMPLE_RATE_20 || rate == PS2_MOUSE_SAMPLE_RATE_40 || rate == PS2_MOUSE_SAMPLE_RATE_60 || rate == PS2_MOUSE_SAMPLE_RATE_80 || rate == PS2_MOUSE_SAMPLE_RATE_100 || rate == PS2_MOUSE_SAMPLE_RATE_200) + { + // Send command to set the mouse resolution. + // + if(sendCmd(MOUSE_CMD_SET_SAMPLE_RATE, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + // Send the rate, if ACK is returned, then resolution set otherwise error. + if(sendCmd((uint8_t)rate, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + result = true; + } + } + } + + // Return result. + return(result); +} + +// Public method to request the mouse Id which can be used to identify the mouse capabilities. +// +char PS2Mouse::getDeviceId(void) +{ + // Locals. + // + uint8_t respBuf[5]; + + // Send command to set the mouse scaling, either 2:1 or 1:1. + // + if(sendCmd(MOUSE_CMD_GET_DEVICE_ID, 1, respBuf, DEFAULT_MOUSE_TIMEOUT) == false) + { + respBuf[0] = 0xFF; + } + + // Return result. + return(respBuf[0]); +} + +// Public method to set the mouse scaling, either Normal 1:1 (scaling = 0) or non-linear 2:1 (scaling = 1). +// +bool PS2Mouse::setScaling(enum PS2_SCALING scaling) +{ + // Locals. + // + uint8_t respBuf[5]; + bool result = false; + + // Sanity check. + if(scaling >= PS2_MOUSE_SCALING_1_1 && scaling < PS2_MOUSE_SCALING_2_1) + { + // Send command to set the mouse scaling, either 2:1 or 1:1. + // + if(sendCmd((uint8_t)scaling, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + result = true; + } + } + + // Return result. + return(result); +} + +// Public method to request the mouse enters remote mode. +// +bool PS2Mouse::setRemoteMode(void) +{ + // Locals. + // + uint8_t respBuf[5]; + + // Simply pass on the request to the mouse to enter remote mode. + return(sendCmd(MOUSE_CMD_SET_REMOTE_MODE, 1, respBuf, DEFAULT_MOUSE_TIMEOUT)); +} + +// Public method to request the mouse enters stream mode. This mode reports mouse movements as they change, albeit the streaming must also be enabled +// once set to Stream Mode via the enableStreaming method. +// +bool PS2Mouse::setStreamMode(void) +{ + // Locals. + // + uint8_t respBuf[5]; + + // Simply pass on the request to the mouse to enter stream mode. + return(sendCmd(MOUSE_CMD_SET_STREAM_MODE, 1, respBuf, DEFAULT_MOUSE_TIMEOUT)); +} + +// Public methods to enable and disable streaming (constant rate packet transmission from mouse to host). +// This module accepts the data and updates an in object set which the caller queries. No buffering takes place +// so should the caller fail to read the data then the arrival of the next packet from the mouse will override +// the in object values. +// +bool PS2Mouse::enableStreaming(void) +{ + // Locals. + // + uint8_t respBuf[5]; + + // Sanity check. + if(ps2Ctrl.streamingEnabled == false) + { + if(sendCmd(MOUSE_CMD_ENABLE_STREAMING, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + // Initialise the streaming buffer. + streaming.mouseData.valid = false; + streaming.mouseData.status = 0; + streaming.mouseData.position.x = 0; + streaming.mouseData.position.y = 0; + streaming.mouseData.wheel = 0; + streaming.newData = false; + streaming.overrun = false; + ps2Ctrl.streamingEnabled = true; + } + } + + // Return the enabled flag to indicate success. + return(ps2Ctrl.streamingEnabled); +} +bool PS2Mouse::disableStreaming(void) +{ + // Locals. + // + uint8_t respBuf[5]; + + // Sanity check. + if(ps2Ctrl.streamingEnabled == true) + { + if(sendCmd(MOUSE_CMD_DISABLE_STREAMING, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + ps2Ctrl.streamingEnabled = false; + } + } + + // Return the enabled flag to indicate success. + return(ps2Ctrl.streamingEnabled); +} + +// Public method to set the mouse resolution in pixels per millimeter, valid values are o..3. +// +bool PS2Mouse::setResolution(enum PS2_RESOLUTION resolution) +{ + // Locals. + // + uint8_t respBuf[5]; + bool result = false; + + // Sanity check. + if(resolution >= PS2_MOUSE_RESOLUTION_1_1 && resolution < PS2_MOUSE_RESOLUTION_1_8) + { + // Send command to set the mouse resolution. + // + if(sendCmd(MOUSE_CMD_SET_RESOLUTION, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + // Send the resolution, if ACK is returned, then resolution set otherwise error. + if(sendCmd((uint8_t)resolution, 0, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + result = true; + } + } + } + + // Return result. + return(result); +} + +// Public method to get the current mouse status. The status code is 3 bytes wide and has the following format: +// +// 7 6 5 4 3 2 1 0 +// Byte 1: 0 mode enable scaling 0 left btn middle right btn +// Byte 2: resolution +// Byte 3: sample rate +// +bool PS2Mouse::getStatus(uint8_t *respBuf) +{ + // Locals. + // + bool result = false; + + // Sanity check. + if(respBuf != NULL) + { + // Send command to set the mouse resolution. + // + if(sendCmd(MOUSE_CMD_GET_STATUS, 3, respBuf, DEFAULT_MOUSE_TIMEOUT)) + { + result = true; + } + } + + // Return result. + return(result); +} + +// Public method to obtain current mouse state data. +// +PS2Mouse::MouseData PS2Mouse::readData(void) +{ + // Locals. + MouseData data; + uint8_t dataBuf[8] = {0,0,0,0,0,0,0,0}; + + // If streaming mode enabled then set values according to data state. Data only valid if a new update has occurred since last call otherwise old data is returned and valid flag + // is cleared. + if(ps2Ctrl.streamingEnabled) + { + data.valid = streaming.newData; + data.overrun = streaming.overrun; + data.status = streaming.mouseData.status; + data.position.x = streaming.mouseData.position.x; + data.position.y = streaming.mouseData.position.y; + data.wheel = ps2Ctrl.supportsIntelliMouseExtensions ? streaming.mouseData.wheel : 0; + streaming.newData = false; + streaming.overrun = false; + + // If a data callback has been setup execute it otherwise data is read by caller. + // + if(ps2Ctrl.mouseDataCallback != NULL && data.valid) + ps2Ctrl.mouseDataCallback(data); + } else + // Single on-request data set from mouse. + { + // Request data from mouse via issuing get single data packet command. + if(requestData(ps2Ctrl.supportsIntelliMouseExtensions ? 3 : 3, dataBuf, DEFAULT_MOUSE_TIMEOUT)) + { + data.valid = true; + data.overrun = false; + data.status = dataBuf[0]; + data.position.x = dataBuf[1]; + data.position.y = dataBuf[2]; + data.wheel = ps2Ctrl.supportsIntelliMouseExtensions ? dataBuf[3] : 0; + } else + { + data.valid = false; + data.overrun = false; + } + } + + return data; +}; + +// Method to request the latest mouse movement, wheel and key data. The method blocks until data is available or the timeout is reached. A timeout of 0 +// will only return when the data has been received. +bool PS2Mouse::requestData(uint8_t expectedBytes, uint8_t *respBuf, uint32_t timeout) +{ + // Locals. + // + + // Simply pass on the request for the mouse to send data and await reply. + return(sendCmd(MOUSE_CMD_REQUEST_DATA, expectedBytes, respBuf, timeout)); +} + +// Method to send a command to the Mouse and await it's reply. If an ACK isnt returned then a resend request is made otherwise wait until all bytes +// arrive or we timeout. +// +bool PS2Mouse::sendCmd(uint8_t cmd, uint8_t expectedBytes, uint8_t *respBuf, uint32_t timeout) +{ + // Locals. + // + uint32_t currentTime = millis(); + uint32_t endTime = millis() + timeout; + uint8_t *pBuf = respBuf; + bool result = false; + + // Send command. + writeByte(cmd); + + // Wait for the expected number of bytes to arrive. + while(((timeout == 0) || (currentTime < endTime)) && ps2Ctrl.rxPos <= expectedBytes) + { + // If an ACK isnt received, request a resend. + if(ps2Ctrl.rxPos >= 1 && ps2Ctrl.rxBuf[0] != MOUSE_RESP_ACK) { writeByte(MOUSE_CMD_RESEND); } + + // Get latest time. + currentTime = millis(); + } + + // Store the response in callers buffer. + for(int idx=0; idx < expectedBytes; idx++) + { + (*pBuf) = ps2Ctrl.rxBuf[idx+1]; + pBuf++; + } + + // Set return code, true if a valid packet was received. + if(((timeout == 0) || (currentTime < endTime)) && ps2Ctrl.rxPos >= expectedBytes && ps2Ctrl.rxBuf[0] == MOUSE_RESP_ACK) result = true; + + // Debug print. + //printf("%d:%d:%02x,%02x,%02x,%02x, %02x, %d, result=%d, %d, %d, %d\n", result, ps2Ctrl.rxPos, ps2Ctrl.rxBuf[0], ps2Ctrl.rxBuf[1], ps2Ctrl.rxBuf[2], ps2Ctrl.rxBuf[3],ps2Ctrl.rxBuf[4], ps2Ctrl.bitCount, result, timeout, currentTime, endTime); + + // And complete with result! + return(result); +} diff --git a/main/SWITCH.cpp b/main/SWITCH.cpp deleted file mode 120000 index 4ff989e..0000000 --- a/main/SWITCH.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/SWITCH.cpp \ No newline at end of file diff --git a/main/SWITCH.cpp b/main/SWITCH.cpp new file mode 100644 index 0000000..d5fc80c --- /dev/null +++ b/main/SWITCH.cpp @@ -0,0 +1,216 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: SWITCH.cpp +// Created: May 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Base class for encapsulating the SharpKey WiFi/Config switch. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: May 2022 - Initial write. +// v1.00 Jun 2022 - Updates to add additional callbacks for RESET and CLEARNVS +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sdkconfig.h" +#include "SWITCH.h" + +// Primary SWITCH thread, running on Core 0. +// This thread is responsible for scanning the config/WiFi key on the SharpKey and generating callbacks according to state. +// +IRAM_ATTR void SWITCH::swInterface( void * pvParameters ) +{ + // Locals. + // + uint32_t keyDebCtr = 0; + uint32_t WIFIEN_MASK = (1 << (CONFIG_IF_WIFI_EN_KEY - 32)); + uint32_t resetTimer = 0; + #define WIFIIFTAG "swInterface" + + // Map the instantiating object so we can access its methods and data. + SWITCH* pThis = (SWITCH*)pvParameters; + + // Loop indefinitely. + while(true) + { + // Check the switch, has it gone to zero, ie. pressed? + // + if((REG_READ(GPIO_IN1_REG) & WIFIEN_MASK) == 0) + { + // First press detection turn LED off. + if(keyDebCtr == 0) + { + pThis->led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } + // Entering WiFi enable mode, blink LED + if(keyDebCtr == 10) + { + pThis->led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_50, 1, 50000L, 500L); + } + // Enter default AP mode. + if(keyDebCtr == 50) + { + pThis->led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_30, 1, 25000L, 250L); + } + // Enter BT pairing mode. + if(keyDebCtr == 100) + { + pThis->led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_10, 1, 10000L, 100L); + } + // Enter Clear NVS settings mode. + if(keyDebCtr == 150) + { + pThis->led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_80, 5, 10000L, 1000L); + } + // Increment counter so we know how long it has been held. + keyDebCtr++; + } else + if((REG_READ(GPIO_IN1_REG) & WIFIEN_MASK) != 0 && keyDebCtr > 1) + { + // On first 1/2 second press, if WiFi active, disable and reboot. + if(keyDebCtr > 1 && keyDebCtr < 10) + { + // If a cancel callback has been setup, invoke it. + // + if(pThis->swCtrl.cancelEventCallback != NULL) + pThis->swCtrl.cancelEventCallback(); + + // If the reset timer is running then a previous button press occurred. If it is less than 1 second then a RESET event + // is required. + if(resetTimer != 0 && (pThis->milliSeconds() - resetTimer) < 1000L) + { + // If a handler is installed call it. If the return value is true then a restart is possible. No handler then we just restart. + if(pThis->swCtrl.resetEventCallback != NULL) + { + if(pThis->swCtrl.resetEventCallback()) + esp_restart(); + } else + esp_restart(); + } else + { + resetTimer = pThis->milliSeconds(); + } + } + // If counter is in range 1 to 4 seconds then assume a WiFi on (so long as the client parameters have been configured). + else if(keyDebCtr > 10 && keyDebCtr < 40) + { + // If a wifi enable callback has been setup, invoke it. + // + if(pThis->swCtrl.wifiEnEventCallback != NULL) + pThis->swCtrl.wifiEnEventCallback(); + } + // If the key is held for 5 or more seconds, then enter Wifi Config Default AP mode. + else if(keyDebCtr > 50 && keyDebCtr < 100) + { + // If a wifi default enable callback has been setup, invoke it. + // + if(pThis->swCtrl.wifiDefEventCallback != NULL) + pThis->swCtrl.wifiDefEventCallback(); + } + // If the key is held for 10 seconds or more, invoke Bluetooth pairing mode. + else if(keyDebCtr >= 100 && keyDebCtr < 150) + { + // If a bluetooth start pairing callback has been setup, invoke it. + // + if(pThis->swCtrl.btPairingEventCallback != NULL) + pThis->swCtrl.btPairingEventCallback(); + } + // If the key is held for 15 seconds or more, invoke the clear NVS settings (factory) mode. + else if(keyDebCtr >= 150) + { + // If a clear NVS handler has been installed, call it. + // + if(pThis->swCtrl.clearNVSEventCallback != NULL) + pThis->swCtrl.clearNVSEventCallback(); + } + + // LED off, no longer needed. + pThis->led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + + // Re-init switch variables for next activation. + keyDebCtr = 0; + } + + // Reset the reset timer if not activated. + if(resetTimer != 0 && (pThis->milliSeconds() - resetTimer) > 2000L) { resetTimer = 0; } + + // Let other tasks run. NB. This value affects the debounce counter, update as necessary. + vTaskDelay(100); + } + return; +} + +// Initialisation routine. Setup variables and spawn a task to monitor the config switch. +// +void SWITCH::init(void) +{ + // Initialise control variables. + #define SWINITTAG "SWINIT" + + // Core 0 - Application + // SWITCH handler thread. + ESP_LOGW(SWINITTAG, "Starting SWITCH thread..."); + ::xTaskCreatePinnedToCore(&this->swInterface, "switch", 4096, this, 0, &this->swCtrl.TaskSWIF, 0); + vTaskDelay(1500); +} + +// Basic constructor, init variables! +SWITCH::SWITCH(LED *hdlLED) +{ + swCtrl.cancelEventCallback = NULL; + swCtrl.wifiEnEventCallback = NULL; + swCtrl.wifiDefEventCallback = NULL; + swCtrl.btPairingEventCallback = NULL; + + // Store the class name for later use. + this->swCtrl.swClassName = getClassName(__PRETTY_FUNCTION__); + + // Save the LED object so it can be used to warn the user. + this->led = hdlLED; + + // Initialse the SWITCH object. + init(); +} + +// Basic consructor, do nothing! +SWITCH::SWITCH(void) +{ + // Store the class name for later use. + this->swCtrl.swClassName = getClassName(__PRETTY_FUNCTION__); +} + +// Basic destructor. +SWITCH::~SWITCH(void) +{ +} diff --git a/main/SharpKey.cpp b/main/SharpKey.cpp deleted file mode 120000 index f65eab1..0000000 --- a/main/SharpKey.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/SharpKey.cpp \ No newline at end of file diff --git a/main/SharpKey.cpp b/main/SharpKey.cpp new file mode 100644 index 0000000..6df6cf7 --- /dev/null +++ b/main/SharpKey.cpp @@ -0,0 +1,1074 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: SharpKey.cpp +// Created: Jan 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: HID (PS/2, Bluetooth) to Sharp Keyboard and Mouse Interface. +// This source file contains the application logic to interface several Sharp MZ/X +// machines to a HID (PS/2 keyboard, PS/2 Mouse, Bluetooth Keyboard/Mouse). The type +// of host is determined by the host i/o lines and the necessary control threads +// instantiated accordingly. +// +// Please see the individual classes (singleton obiects) for a specific host logic. +// +// The application uses the Espressif Development environment with Arduino components. +// This is necessary for the PS2KeyAdvanced class, which may in future be converted to +// use esp-idf library calls rather than Arduino. I wrote the PS2Mouse class using +// Arduino as well, so both will need conversion eventually. +// +// The Espressif environment is necessary in order to have more control over the build. +// It is important, for timing, that Core 1 is dedicated to the MZ 2599/2800 Interface +// logic due to timing constraints and Core 0 is used for all RTOS/Interrupts tasks. +// Other host interface classes freely use both cores. +// +// The application is configured via the Kconfig system. Use 'idf.py menuconfig' to +// configure. +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Jan 2022 - Initial write. +// Feb 2022 - Updates and fixes. Added logic to detect PS/2 disconnect and reconnect, +// added 3 alternative maps selected by ALT+F1 (MZ2500), ALT+F2(MZ2000) +// ALT+F3(MZ80B) due to slight differences in the key layout. +// Added framework for wifi so that the interface can enter AP mode to +// acquire local net parameters then connect to local net. Needs the web +// interface writing. +// Mar 2022 - Split from mz25key and modularised to allow multiple host targets. +// Apr 2022 - Rewrote the application as C++ classes, the host interfaces are based +// on a Base class (KeyInterface) which virtualises the interface and +// provides some base methods and variables, each host inherits the Base class +// to form individual singleton objects. +// Apr 2022 - Added X1, X68000 Keyboard functionality and Mouse functionality. +// Apr 2022 - Started on tests for using Bluetooth, moved all input devices (Human +// Input Device) into its own class to encapsulate PS/2 Keyboard, Mouse and +// Bluetooth Keyboard/Mouse. +// v1.01 May 2022 - More objectifying the fundamental components to make it easier to upgrade. +// v1.02 May 2022 - Initial release version. +// v1.03 May 2022 - Added feature security and efuse build data. +// v1.04 Jun 2022 - Reworked the Wifi so that when requested, the SharpKey reboots and +// immediately starts up in WiFi mode without enabling BT or hardware I/F. +// This is necessary due to shared antenna in the ESP32 and also clashes +// in the IDF library stack. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_efuse.h" +#include "esp_efuse_table.h" +#include "esp_efuse_custom_table.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "Arduino.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" +#include "sdkconfig.h" +#include "MZ2528.h" +#include "X1.h" +#include "X68K.h" +#include "MZ5665.h" +#include "PC9801.h" +#include "Mouse.h" +#include "HID.h" +#include "NVS.h" +#include "WiFi.h" + +////////////////////////////////////////////////////////////////////////// +// Important: +// +// All configuration is performed via the 'idf.py menuconfig' command. +// The file 'sdkconfig' contains the configured parameter defines. +////////////////////////////////////////////////////////////////////////// + +// Constants. +#define SHARPKEY_NAME "SharpKey" +#define SHARPKEY_VERSION 1.04 +#define SHARPKEY_MODULES "SharpKey MZ2528 X1 X68K MZ5665 PC9801 Mouse KeyInterface HID NVS LED SWITCH WiFi FilePack" + +// Tag for ESP main application logging. +#define MAINTAG "sharpkey" + +// LittleFS default parameters. +#define LITTLEFS_DEFAULT_PATH "/littlefs" +#define LITTLEFS_DEFAULT_PARTITION "filesys" + +// Structure for configuration information stored in NVS. +struct SharpKeyConfig { + struct { + uint8_t bootMode; // Flag to indicate the mode SharpKey should boot into. + // 0 = Interface, 1 = WiFi (configured), 2 = WiFi (default). + } params; +} sharpKeyConfig; + +// Overloads for the EFUSE Custom MAC definitions. Limited Efuse space and Custom MAC not needed in eFuse in this design +// so we overload with custom flags. +// 0-7 Reserved +// 8-15 defined as Base configuration and enhanced set 1. +static const esp_efuse_desc_t ENABLE_BT[] = { + {EFUSE_BLK3, 8, 1}, +}; +static const esp_efuse_desc_t ENABLE_MZ5665[] = { + {EFUSE_BLK3, 9, 1}, +}; +static const esp_efuse_desc_t ENABLE_PC9801[] = { + {EFUSE_BLK3, 10, 1}, +}; +static const esp_efuse_desc_t ENABLE_MOUSE[] = { + {EFUSE_BLK3, 11, 1}, +}; +static const esp_efuse_desc_t ENABLE_X68000[] = { + {EFUSE_BLK3, 12, 1}, +}; +static const esp_efuse_desc_t ENABLE_X1[] = { + {EFUSE_BLK3, 13, 1}, +}; +static const esp_efuse_desc_t ENABLE_MZ2800[] = { + {EFUSE_BLK3, 14, 1}, +}; +static const esp_efuse_desc_t ENABLE_MZ2500[] = { + {EFUSE_BLK3, 15, 1}, +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_BT[] = { + &ENABLE_BT[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_MZ5665[] = { + &ENABLE_MZ5665[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_PC9801[] = { + &ENABLE_PC9801[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_X68000[] = { + &ENABLE_X68000[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_X1[] = { + &ENABLE_X1[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_MZ2800[] = { + &ENABLE_MZ2800[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_MZ2500[] = { + &ENABLE_MZ2500[0], + NULL +}; +const esp_efuse_desc_t* ESP_EFUSE_ENABLE_MOUSE[] = { + &ENABLE_MOUSE[0], + NULL +}; + +// Revision control, stored in EFUSE block. +typedef struct { + uint16_t hardwareRevision; // Hardware revision, stored as x/1000 - gives range 0.000 - 64.999 + uint16_t serialNo; // Serial Number of the device. + uint8_t buildDate[3]; // Build date of the hardware. + bool disableRestrictions; // Flag to indicate all firmware restrictions should be disabled. + bool enableMZ2500; // Flag to indicate MZ-2500 functionality should be enabled. + bool enableMZ2800; // Flag to indicate MZ-2800 functionality should be enabled. + bool enableX1; // Flag to indicate X1 functionality should be enabled. + bool enableX68000; // Flag to indicate X68000 functionality should be enabled. + bool enableMouse; // Flag to indicate Mouse functionality should be enabled. + bool enableBluetooth; // Flag to indicate Bluetooth functionality should be enabled. + bool enableMZ5665; // Flag to indicate MZ-6500 functionality should be enabled. + bool enablePC9801; // Flag to indicate NEC PC-9801 functionality should be enabled. +} t_EFUSE; + +// Method to check the efuse coding scheme is disabled. For this project it should be disabled. +bool checkEFUSE(void) +{ + // Locals. + bool result = false; + size_t secureVersion = 0; + + // Check the efuse coding scheme, should be NONE and the security version should be 0 for this project. + esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(EFUSE_BLK3); + if(coding_scheme == EFUSE_CODING_SCHEME_NONE) + { + ESP_ERROR_CHECK(esp_efuse_read_field_cnt(ESP_EFUSE_SECURE_VERSION, &secureVersion)); + if(secureVersion == 0) + { + result = true; + } + } + + // True = efuse present and correct, false = not recognised. + return(result); +} + +// Method to read out the stored configuration from EFUSE into the configuration structure +// for later appraisal. +bool readEFUSE(t_EFUSE &sharpkeyEfuses) +{ + // Locals. + bool result = true; + + // Manually read each fuse value into the given structure, any failures treat as a complete failure. + result = esp_efuse_read_field_blob(ESP_EFUSE_HARDWARE_REVISION, &sharpkeyEfuses.hardwareRevision, 16) == ESP_OK ? result : false; sharpkeyEfuses.hardwareRevision = __builtin_bswap16(sharpkeyEfuses.hardwareRevision); + result = esp_efuse_read_field_blob(ESP_EFUSE_SERIAL_NO, &sharpkeyEfuses.serialNo, 16) == ESP_OK ? result : false; sharpkeyEfuses.serialNo = __builtin_bswap16(sharpkeyEfuses.serialNo); + result = esp_efuse_read_field_blob(ESP_EFUSE_BUILD_DATE, &sharpkeyEfuses.buildDate, 24) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_DISABLE_RESTRICTIONS, &sharpkeyEfuses.disableRestrictions, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_BT, &sharpkeyEfuses.enableBluetooth, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_MZ2500, &sharpkeyEfuses.enableMZ2500, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_MZ2800, &sharpkeyEfuses.enableMZ2800, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_X1, &sharpkeyEfuses.enableX1, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_X68000, &sharpkeyEfuses.enableX68000, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_MZ5665, &sharpkeyEfuses.enableMZ5665, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_PC9801, &sharpkeyEfuses.enablePC9801, 1) == ESP_OK ? result : false; + result = esp_efuse_read_field_blob(ESP_EFUSE_ENABLE_MOUSE, &sharpkeyEfuses.enableMouse, 1) == ESP_OK ? result : false; + + // Return true = successful read, false = failed to read efuse or values. + return(result); +} + +// Method to write the configuration to one-time programmable FlashRAM EFuses. This setting persists for the life of the SharpKey +// and so minimal information is stored which cant be wiped, everything else uses reprogrammable FlashRAM via NVS. +bool writeEFUSE(t_EFUSE &sharpkeyEfuses) +{ + // Locals. + bool result = true; +#ifdef CONFIG_EFUSE_VIRTUAL + + // Write out the configuration structure member at a time. + result = esp_efuse_write_field_blob(ESP_EFUSE_HARDWARE_REVISION, &sharpkeyEfuses.hardwareRevision, 16) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_SERIAL_NO, &sharpkeyEfuses.serialNo, 16) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_BUILD_DATE, &sharpkeyEfuses.buildDate, 24) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_DISABLE_RESTRICTIONS, &sharpkeyEfuses.disableRestrictions, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_BT, &sharpkeyEfuses.enableBluetooth, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_MZ2500, &sharpkeyEfuses.enableMZ2500, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_MZ2800, &sharpkeyEfuses.enableMZ2800, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_X1, &sharpkeyEfuses.enableX1, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_X68000, &sharpkeyEfuses.enableX68000, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_MZ5665, &sharpkeyEfuses.enableMZ5665, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_PC9801, &sharpkeyEfuses.enablePC9801, 1) == ESP_OK ? result : false; + result = esp_efuse_write_field_blob(ESP_EFUSE_ENABLE_MOUSE, &sharpkeyEfuses.enableMouse, 1) == ESP_OK ? result : false; + +#endif // CONFIG_EFUSE_VIRTUAL + // Return true for success, false for 1 or more failures. + return(result); +} + +// Method to return the application version number. +float version(void) +{ + return(SHARPKEY_VERSION); +} + +// Method to startup the WiFi interface. +// Starting the WiFi method requires no Bluetooth or running host interface threads. It is started after a fresh boot. This is necessary due to the ESP IDF +// and hardware antenna constraints. +// +#if defined(CONFIG_IF_WIFI_ENABLED) +void startWiFi(NVS &nvs, LED *led, bool defaultMode, uint32_t ifMode) +{ + // Locals. + // + KeyInterface *keyIf = NULL; + KeyInterface *mouseIf = NULL; + HID *hid = NULL; + SWITCH *sw = NULL; + WiFi *wifi = NULL; + + // The WiFi interface needs to report version numbers so an end user can view which version of an object is built-in, used for error tracking and firmware upgrades. + // In order to do this, build up a structure of object and version numbers which is passed into the WiFi interface object. The structure is defined within wifi.h as technically + // it belongs to that object but needs to be evaluated in main as it has access to all class/objects forming the SharpKey. + WiFi::t_versionList *versionList = new WiFi::t_versionList; + std::istringstream list(SHARPKEY_MODULES); + std::vector modules{std::istream_iterator{list}, std::istream_iterator{}}; + for(int idx = 0; idx < modules.size() && idx < OBJECT_VERSION_LIST_MAX; idx++, versionList->elements=idx) + { + versionList->item[idx] = new WiFi::t_versionItem; + versionList->item[idx]->object = modules[idx]; + if(modules[idx].compare("SharpKey") == 0) + { + versionList->item[idx]->version = version(); + } + else if(modules[idx].compare("HID") == 0) + { + hid = new HID(); + versionList->item[idx]->version = hid->version(); + delete hid; + } + else if(modules[idx].compare("NVS") == 0) + { + versionList->item[idx]->version = nvs.version(); + } + else if(modules[idx].compare("LED") == 0) + { + versionList->item[idx]->version = led->version(); + } + else if(modules[idx].compare("SWITCH") == 0) + { + sw = new SWITCH; + versionList->item[idx]->version = sw->version(); + delete sw; + } + else if(modules[idx].compare("MZ2528") == 0) + { + keyIf = new MZ2528(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("X1") == 0) + { + keyIf = new X1(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("X68K") == 0) + { + keyIf = new X68K(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("MZ5665") == 0) + { + keyIf = new MZ5665(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("PC9801") == 0) + { + keyIf = new PC9801(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("Mouse") == 0) + { + keyIf = new Mouse(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("WiFi") == 0) + { + WiFi *wifiIf = new WiFi(); + versionList->item[idx]->version = wifiIf->version(); + delete wifiIf; + } + else if(modules[idx].compare("KeyInterface") == 0) + { + keyIf = new KeyInterface(); + versionList->item[idx]->version = keyIf->version(); + delete keyIf; + } + else if(modules[idx].compare("FilePack") == 0) + { + // Look on the filesystem for the version file and read the first line contents as the version number. + std::string version = "0.00"; + std::stringstream fqfn; fqfn << LITTLEFS_DEFAULT_PATH << "/" << FILEPACK_VERSION_FILE; + std::ifstream inFile; inFile.open(fqfn.str()); + if(inFile.is_open()) + { + std::getline(inFile, version); + } + inFile.close(); + versionList->item[idx]->version = std::stof(version); + } + else + { + ESP_LOGE(MAINTAG, "Unknown class name in module configuration list:%s", modules[idx].c_str()); + } + } + keyIf = NULL; + + // Create a basic hid object for config persistence and retrieval. + hid = new HID(&nvs); + + // Create basic host interface objects without hardware configuration. This is needed as the WiFi object probes them for configuration parameters and to update + // the parameters. + switch(ifMode) + { + // MZ-2500 or MZ-2800 Host. + case 2500: + case 2800: + { + keyIf = new MZ2528(&nvs, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // Sharp X1 Host. + case 1: + { + keyIf = new X1(&nvs, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // Sharp X68000 Host. + case 68000: + { + keyIf = new X68K(&nvs, hid, LITTLEFS_DEFAULT_PATH); + mouseIf = new Mouse(&nvs, hid); + break; + } + + // MZ-5600/MZ-6500 Host. + case 5600: + case 6500: + { + keyIf = new MZ5665(&nvs, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // PC-9801 + case 9801: + { + keyIf = new PC9801(&nvs, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // Mouse + case 2: + { + mouseIf = new Mouse(&nvs, hid); + break; + } + + // Unknown host, so just bring up a basic WiFi configuration without interface object configuration. + default: + { + keyIf = new KeyInterface(&nvs, hid); + break; + } + } + + // There are a number of issues with the ESP32 WiFi and code base which you have to work around, some are hardware issues but others will no doubt be + // resolved in later IDF releases. On the MZ-2500/MZ-2800 there is an ESP32 issue regarding WiFi Client mode and ADC2. If the pins are input and wrong + // value the WiFi Client mode wont connect, it goes into a state where it wont connect and errors out - the same kind of error is seen if the voltage/current + // supplied to the ESP32 is out of parameter. + if(keyIf != NULL && (ifMode == 2500 || ifMode == 2800 || ifMode == 68000 || ifMode == 5600 || ifMode == 6500 || ifMode == 9801)) + { + keyIf->reconfigADC2Ports(true); + } + + // Create a new WiFi object. + wifi = new WiFi(keyIf, mouseIf, defaultMode, &nvs, led, LITTLEFS_DEFAULT_PATH, versionList); + + // Pass control, only returns if a reboot is needed. + wifi->run(); +} +#endif + +// Method to determine which host the SharpKey is connected to. This is done by examining the host I/O for tell tale signals +// or inputs wired in a fixed combination. +// +uint32_t getHostType(bool eFuseInvalid, t_EFUSE sharpkeyEfuses) +{ + // Locals. + // + uint32_t RTSNI_MASK = (1 << (CONFIG_HOST_RTSNI - 32)); + uint32_t MPXI_MASK = (1 << CONFIG_HOST_MPXI); + uint32_t KDB0_MASK = (1 << CONFIG_HOST_KDB0); + //uint32_t KDB1_MASK = (1 << CONFIG_HOST_KDB1); + //uint32_t KDB2_MASK = (1 << CONFIG_HOST_KDB2); + //uint32_t KDB3_MASK = (1 << CONFIG_HOST_KDB3); + + // Build selectable target. This software can be built to run on the SharpKey or mz25key interfaces. If a resistor is connected from MPX input to the ESP32 IO12 pin 14 then + // the SharpKey build can be used even though the mz25key only supports one target at a time. If no resistor is connected then you will need to build for a specific target + // as the detection logic will not be able to determine if it is connected to an MZ-2500 or MZ-2800. Use menuconfig to select the target. + #ifdef CONFIG_SHARPKEY + + // Connected host detection. + // + // MZ-2800 - RTSN Goes High and Low, MPX goes High and Low. RTSN, depending upon the machine mode may only oscillate a few times per second, so need to count + // MPX pulses to determine MZ-2800 mode. + // MZ-2500 - RTSN Goes High and Low. MPX goes High and Low. On the falling edge of RTSN sample MPX 50ns in, if it is high then + // select MZ-2500 mode. + // X1 - Output 1010 onto KDO[3:0] and read KDB[3:0] - if match then X1 mode. + // X68000 = KD4 = low, MPX = low, RTSN = high + // Mouse = KD4 = high, MPX = low, RTSN = high + // + // NB: The above tests ASSUME the interface is plugged into the host, only powered by the host and the host is switched on. Development cycles where the interface + // is powered by the UART adapter and/or the host is switched off will not detect the correct host. + // + // First up, sample RTSN and MPX to see if they are alternating. The ratio of RTSN to MPX will yield the type of host. + uint32_t cntRTSN = 0; + uint32_t cntMPX = 0; + uint32_t ifMode = 0; + volatile uint32_t gpioIN, gpioINLast; + gpioIN = gpioINLast = REG_READ(GPIO_IN_REG); + for(uint32_t idx=0; idx < 400; idx++) + { + gpioIN = REG_READ(GPIO_IN_REG); + if((gpioIN & MPXI_MASK) && (gpioIN & MPXI_MASK) != (gpioINLast & MPXI_MASK)) cntMPX++; + gpioINLast = gpioIN; + } + gpioIN = gpioINLast = REG_READ(GPIO_IN1_REG); + for(uint32_t idx=0; idx < 400; idx++) + { + gpioIN = REG_READ(GPIO_IN1_REG); + if((gpioIN & RTSNI_MASK) && (gpioIN & RTSNI_MASK) != (gpioINLast & RTSNI_MASK)) cntRTSN++; + gpioINLast = gpioIN; + } + + // If RTSN and MPX are alternating then the number identifies the host, the MZ-2500 has 1 RTSN per 1 MPX repeating every 1.2uS whereas + // the MZ-2800 has 1 RTSN per ~14 or more MPX cycles albeit there are periods of 1ms where no activity can be seen. + if(cntMPX > 1) + { + if(cntRTSN > 20 && cntMPX > 20 && eFuseInvalid == false && (sharpkeyEfuses.disableRestrictions == true || sharpkeyEfuses.enableMZ2500 == true)) + ifMode = 2500; + + // RTSN may not oscillate in the small capture window depending on run mode, so check MPX and if it is oscillating, at a lower rate than the MZ-2500, select MZ-2800 mode. + else if(cntMPX > 5 && eFuseInvalid == false && (sharpkeyEfuses.disableRestrictions == true || sharpkeyEfuses.enableMZ2800 == true)) + ifMode = 2800; + + if(ifMode > 0) + ESP_LOGW(MAINTAG, "Detected MZ-%d host, counts:RTSN=%d, MPX=%d.", ifMode, cntRTSN, cntMPX); + } else + { + // Check for X1 - this is accomplished by writing a value to KDO and reading it back on KDB. This works because RTSN is tied low on the X1 cable as the X1 protocol is output only. + // Clear all KDO bits - clear state = '0' + GPIO.out_w1tc = (1 << CONFIG_HOST_KDO7) | (1 << CONFIG_HOST_KDO6) | (1 << CONFIG_HOST_KDO5) | (1 << CONFIG_HOST_KDO4) | + (1 << CONFIG_HOST_KDO3) | (1 << CONFIG_HOST_KDO2) | (1 << CONFIG_HOST_KDO1) | (1 << CONFIG_HOST_KDO0); + vTaskDelay(1); + // Set the test pattern. KDO[3:0] = 1010. + GPIO.out_w1ts = (1 << CONFIG_HOST_KDO7) | (1 << CONFIG_HOST_KDO6) | (1 << CONFIG_HOST_KDO5) | (1 << CONFIG_HOST_KDO4) | + (1 << CONFIG_HOST_KDO3) | (0 << CONFIG_HOST_KDO2) | (1 << CONFIG_HOST_KDO1) | (0 << CONFIG_HOST_KDO0); + vTaskDelay(1); + // Now read back KDB. + gpioIN = REG_READ(GPIO_IN_REG); + if((gpioIN & (1 << CONFIG_HOST_KDB3)) && (gpioIN & (1 << CONFIG_HOST_KDB2)) == 0 && (gpioIN & (1 << CONFIG_HOST_KDB1)) && (gpioIN & (1 << CONFIG_HOST_KDB0)) == 0 && + eFuseInvalid == false && (sharpkeyEfuses.disableRestrictions == true || sharpkeyEfuses.enableX1 == true)) + { + ifMode = 1; + } + else + { + // Need to reconfigure the Mouse CTRL pin so we can detect counts. + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = (1ULL< 1 && (gpioIN & (1 << CONFIG_HOST_KDI4)) != 0 && (gpioIN & (1 << CONFIG_HOST_MPXI)) == 0 && (REG_READ(GPIO_IN1_REG) & RTSNI_MASK) != 0 && + eFuseInvalid == false && (sharpkeyEfuses.disableRestrictions == true || sharpkeyEfuses.enableMouse == true)) + { + ifMode = 2; + } + } + } + } + //ESP_LOGW(MAINTAG, "RTSN(%d) and MPX(%d) counts.", cntRTSN, cntMPX); + #endif + + // Target build for an MZ-2500 using the mz25key hardware. + #ifdef CONFIG_MZ25KEY_MZ2500 + uint32_t ifMode = 2500; + #endif + + // Target build for an MZ-2500 using the mz25key hardware. + #ifdef CONFIG_MZ25KEY_MZ2800 + uint32_t ifMode = 2800; + #endif +ifMode = 9801; + // Return a value which represents the detected host type. + return(ifMode); +} + +// Method triggered on a WiFi Enable switch event. Set the boot mode and restart to enter WiFi handler and webserver. +void wifiEnableCallback(void) +{ + ESP_LOGW(MAINTAG, "Setting WiFi Enable mode."); + sharpKeyConfig.params.bootMode = 1; +} + +// Method triggered on a WiFi Default Mode Enable switch event. Set the boot mode and restart to enter WiFi handler and webserver. +void wifiDefaultCallback(void) +{ + ESP_LOGW(MAINTAG, "Setting WiFi Default Enable mode."); + sharpKeyConfig.params.bootMode = 2; +} + +// Method triggered on a Clear NVS switch event. Close the NVS and erase its contents setting the SharpKey back to factory default. +void clearNVSCallback(void) +{ + ESP_LOGW(MAINTAG, "Clearing NVS..."); + sharpKeyConfig.params.bootMode = 255; +} + +// Setup method to configure ports, devices and threads prior to application run. +// Configuration: +// PS/2 Keyboard over 2 wire interface +// PS/2 Mouse over 2 wire interface +// Power/Status LED +// Bluetooth, in-built. +// 4 bit input - MZ-2500/2800 Row Number +// 8 bit output - MZ-2500/2800 Scan data +// 1 bit input - RTSN strobe line, low indicating a new Row Number available. +// 1 bit input - KD4, High = Key scan data required, Low = AND of all key matrix rows required. +// +void setup(NVS &nvs) +{ + // Locals. + uint32_t ifMode; + bool eFuseInvalid = false; + KeyInterface *keyIf = NULL; + KeyInterface *mouseIf = NULL; + HID *hid = NULL; + LED *led = NULL; + SWITCH *sw = NULL; + t_EFUSE sharpkeyEfuses; + esp_vfs_littlefs_conf_t lfsConf; + esp_err_t lfsStatus; + gpio_config_t io_conf; + #define SETUPTAG "setup" + + + // Check the efuse and retrieve configured values for later appraisal. + if(checkEFUSE() == false) { eFuseInvalid = true; } + memset((void *)&sharpkeyEfuses, 0x00, sizeof(t_EFUSE)); + if(readEFUSE(sharpkeyEfuses) == true) + { + // If the hw revision, build date and/or serial number havent been set, ie. an unconfigured ESP32 eFuse, obsfucate it. + if(sharpkeyEfuses.hardwareRevision == 0) { sharpkeyEfuses.hardwareRevision = 1300; } + if(sharpkeyEfuses.buildDate[0] == 0) { sharpkeyEfuses.buildDate[0] = 1; sharpkeyEfuses.buildDate[1] = 6; sharpkeyEfuses.buildDate[2] = 22; } + if(sharpkeyEfuses.serialNo == 0) { sharpkeyEfuses.serialNo = (uint16_t)((rand() * 65534) + 1); } + // Bug in Efuse programming workaround. + if(sharpkeyEfuses.buildDate[0] == 31 && sharpkeyEfuses.buildDate[1] == 6) { sharpkeyEfuses.buildDate[0] = 1; } + ESP_LOGW(SETUPTAG, "EFUSE:Hardware Rev=%f, Build Date:%d/%d/%d, Serial Number:%05d %s%s%s%s%s%s%s%s%s", + ((float)sharpkeyEfuses.hardwareRevision)/1000, + sharpkeyEfuses.buildDate[0],sharpkeyEfuses.buildDate[1],sharpkeyEfuses.buildDate[2], + sharpkeyEfuses.serialNo, + sharpkeyEfuses.disableRestrictions == true ? "disableRestrictions" : " ", + sharpkeyEfuses.enableMZ2500 == true ? "enableMZ2500" : " ", + sharpkeyEfuses.enableMZ2800 == true ? "enableMZ2800" : " ", + sharpkeyEfuses.enableX1 == true ? "enableX1" : " ", + sharpkeyEfuses.enableX68000 == true ? "enableX68000" : " ", + sharpkeyEfuses.enableMouse == true ? "enableMouse" : " ", + sharpkeyEfuses.enableBluetooth == true ? "enableBluetooth" : " ", + sharpkeyEfuses.enableMZ5665 == true ? "enableMZ5665" : " ", + sharpkeyEfuses.enablePC9801 == true ? "enablePC9801" : ""); + } else + { + eFuseInvalid = true; + ESP_LOGW(SETUPTAG, "EFUSE not programmed/readable."); + } + #if defined(CONFIG_DISABLE_FEATURE_SECURITY) + sharpkeyEfuses.disableRestrictions = true; + #endif + + // Configure 4 inputs to be the Strobe Row Number which is used to index the virtual key matrix and the strobe data returned. + #if !defined(CONFIG_DEBUG_DISABLE_KDB) + ESP_LOGW(SETUPTAG, "Configuring 4 bit (KDB[3:0] Row Number Inputs."); + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = (1ULL<setWifiEnEventCallback(&wifiEnableCallback); + sw->setWifiDefEventCallback(&wifiDefaultCallback); + sw->setClearNVSEventCallback(&clearNVSCallback); + + // Initialise the HID and find out what input device is connected. Bluetooth can support two devices, keyboard and mouse, so this + // can be used by selected hosts to provide both keyboard/mouse simulateously. + if(ifMode == 2) + { + // When the detected host is a Mouse port then only one service, a mouse service, can be provided. + hid = new HID(HID::HID_DEVICE_TYPE_MOUSE, &nvs, led, sw); + } else + { + // When the detected host is a Keyboard port then it is possible, if using Bluetooth, to simultaneously offer a Mouse service at the same time, host dependent. + hid = new HID(HID::HID_DEVICE_TYPE_KEYBOARD, &nvs, led, sw); + } + + // Setup host interface according to the detected host. We run the interface regardless of optional extras such as LittleFS/WiFi as + // keyboard protocol conversion is this devices priority. + switch(ifMode) + { + // MZ-2500 Host. + case 2500: + { + keyIf = new MZ2528(ifMode, &nvs, led, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // MZ-8500 Host. + case 2800: + { + keyIf = new MZ2528(ifMode, &nvs, led, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // Sharp X1 Host. + case 1: + { + ESP_LOGW(SETUPTAG, "Detected Sharp X1 host."); + keyIf = new X1(ifMode, &nvs, led, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // Sharp X68000 Host. + case 68000: + { + ESP_LOGW(SETUPTAG, "Detected Sharp X68000 host."); + keyIf = new X68K(ifMode, &nvs, led, hid, LITTLEFS_DEFAULT_PATH); + + // See if Bluetooth is available, if yes then we can offer mouse services simultaneously. + if(hid->isBluetooth()) + { + mouseIf = new Mouse(ifMode, &nvs, led, hid, true); + } + break; + } + + // MZ-5600/MZ-6500 Host. + case 5600: + case 6500: + { + ESP_LOGW(SETUPTAG, "Detected Sharp MZ-5600/MZ-6500 host."); + keyIf = new MZ5665(ifMode, &nvs, led, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // PC-9801 + case 9801: + { + ESP_LOGW(SETUPTAG, "Detected NEC PC-9801 host."); + keyIf = new PC9801(ifMode, &nvs, led, hid, LITTLEFS_DEFAULT_PATH); + break; + } + + // Mouse + case 2: + { + ESP_LOGW(SETUPTAG, "Detected Mouse."); + mouseIf = new Mouse(ifMode, &nvs, led, hid, false); + break; + } + + // Unknown host or detected interface feature not enabled. Log the details, flash the LED and if WiFi is built-in,enable it and then just wait for RESET or user interaction with the WiFi. + default: + { + ESP_LOGW(SETUPTAG, "Connected host is unknown."); + ESP_LOGW(SETUPTAG, "GPIO:%08x, %08x", REG_READ(GPIO_IN_REG), REG_READ(GPIO_IN_REG) & (1 << CONFIG_HOST_MPXI)); + ESP_LOGW(SETUPTAG, "GPIO1:%08x,%08x", REG_READ(GPIO_IN1_REG), REG_READ(GPIO_IN1_REG) & (1 << CONFIG_HOST_RTSNI)); + + // Initialise a base object so we have access to NVS and the LED, these are needed for the WiFi. + keyIf = new KeyInterface(ifMode, &nvs, led, hid); + break; + } + } + + // Disable the brownout detector, when WiFi starts up it randomly triggers the brownout even though the voltage at the WROOM input is 3.3V. It is posisbly a hardware bug + // as adding larger capacitors doesnt solve it. + // + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); + } + + // All running, wont reach here if WiFi is enabled. + // + ESP_LOGW(SETUPTAG, "Free Heap (%d)", xPortGetFreeHeapSize()); +} + +// ESP-IDF Application entry point. +// +extern "C" void app_main() +{ + // Locals. + NVS nvs; + + // Setup hardware and start primary control threads, + setup(nvs); + + // Loop waiting on callback events and action accordingly. + while(true) + { + // Change in boot mode requires persisting and reboot. + // + if(sharpKeyConfig.params.bootMode == 1 || sharpKeyConfig.params.bootMode == 2) + { + // Set boot mode to wifi, save and restart. + // + ESP_LOGW(MAINTAG, "Persisting WiFi mode."); + if(nvs.persistData(SHARPKEY_NAME, &sharpKeyConfig, sizeof(struct SharpKeyConfig)) == false) + { + ESP_LOGW(SETUPTAG, "Persisting SharpKey configuration data failed, updates will not persist in future power cycles."); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(nvs.commitData() == false) + { + ESP_LOGW(SETUPTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + + // Restart and the SharpKey will come up in Wifi mode. + esp_restart(); + } + + // Piggy backing off the bootMode is a flag to indicate NVS flash erase and reboot. + // + if(sharpKeyConfig.params.bootMode == 255) + { + // Close out NVS and erase. + nvs.eraseAll(); + + // Need to reboot as the in-memory config still holds the old settings. + esp_restart(); + } + + // Sleep, not much to be done other than look at event flags. + vTaskDelay(500); + } + + // Lost in space.... this thread is no longer required! +} diff --git a/main/WiFi.cpp b/main/WiFi.cpp deleted file mode 120000 index 7cc1075..0000000 --- a/main/WiFi.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/WiFi.cpp \ No newline at end of file diff --git a/main/WiFi.cpp b/main/WiFi.cpp new file mode 100644 index 0000000..cf54535 --- /dev/null +++ b/main/WiFi.cpp @@ -0,0 +1,2872 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: WiFi.cpp +// Created: Jan 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: The WiFi AP/Client Interface. +// This source file contains the application logic to provide WiFi connectivity in +// order to allow remote query and configuration of the sharpkey interface. +// +// The module provides Access Point (AP) functionality to allow initial connection +// in order to configure local WiFi credentials. +// +// The module provides Client functionality, using the configured credentials, +// to connect to a local Wifi net and present a browser session for querying and +// mapping configuration of the sharpkey interface. +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Jan 2022 - Initial write. +// Mar 2022 - Split out from main.cpp. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Seperated out the WiFi Enable switch and made the WiFi module active +// via a reboot process. This is necessary now that Bluetooth is inbuilt +// as the ESP32 shares an antenna and both operating together electrically +// is difficult but also the IDF stack conflicts as well. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This is an optional compile time module, only compile if configured. +#include "sdkconfig.h" +#if defined(CONFIG_IF_WIFI_ENABLED) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_ota_ops.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "Arduino.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include +#include "esp_tls_crypto.h" +#include +#include "esp_littlefs.h" +#include "WiFi.h" + +// FreeRTOS event group to signal when we are connected +static EventGroupHandle_t s_wifi_event_group; + +// Template to convert a given type into a std::string. +// +template std::string to_str(const Type & t, int precision, int base) +{ + // Locals. + // + std::ostringstream os; + + if(precision != 0) + { + os << std::fixed << std::setw(precision) << std::setprecision(precision) << std::setfill('0') << t; + } else + { + if(base == 16) + { + os << "0x" << std::hex << t; + } else + { + os << t; + } + } + return os.str(); +} + +// Method to convert the idf internal partition type to a readable string for output to the browser. +// +std::string WiFi::esp32PartitionType(esp_partition_type_t type) +{ + // Locals. + // + std::string result = "Unknown"; + + switch(static_cast(type)) + { + case ESP_PARTITION_TYPE_APP: + result = "App"; + break; + + case ESP_PARTITION_TYPE_DATA: + result = "Data"; + break; + + default: + result = "n/a"; + break; + } + + // Return the string version of the enum. + return(result); +} + +// Method to convert the idf internal partition subtype to a readable string for output to the browser. +// +std::string WiFi::esp32PartitionSubType(esp_partition_subtype_t subtype) +{ + // Locals. + // + std::string result = "Unknown"; + + switch(static_cast(subtype)) + { + case ESP_PARTITION_SUBTYPE_APP_FACTORY: + result = "Factory"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_PHY: + result = "phy"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_NVS: + result = "nvs"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_COREDUMP: + result = "core"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS: + result = "nvs_keys"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM: + result = "efuse"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD: + result = "httpd"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_FAT: + result = "fat"; + break; + + case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: + result = "spiffs"; + break; + + case ESP_PARTITION_SUBTYPE_APP_OTA_0: + case ESP_PARTITION_SUBTYPE_APP_OTA_1: + case ESP_PARTITION_SUBTYPE_APP_OTA_2: + case ESP_PARTITION_SUBTYPE_APP_OTA_3: + case ESP_PARTITION_SUBTYPE_APP_OTA_4: + case ESP_PARTITION_SUBTYPE_APP_OTA_5: + case ESP_PARTITION_SUBTYPE_APP_OTA_6: + case ESP_PARTITION_SUBTYPE_APP_OTA_7: + case ESP_PARTITION_SUBTYPE_APP_OTA_8: + case ESP_PARTITION_SUBTYPE_APP_OTA_9: + case ESP_PARTITION_SUBTYPE_APP_OTA_10: + case ESP_PARTITION_SUBTYPE_APP_OTA_11: + case ESP_PARTITION_SUBTYPE_APP_OTA_12: + case ESP_PARTITION_SUBTYPE_APP_OTA_13: + case ESP_PARTITION_SUBTYPE_APP_OTA_14: + case ESP_PARTITION_SUBTYPE_APP_OTA_15: + case ESP_PARTITION_SUBTYPE_APP_OTA_MAX: + result = "ota_" + to_str(subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN, 0, 10); + break; + + default: + result = to_str(subtype, 0, 10); + break; + } + + // Return the string version of the enum. + return(result); +} + +// Method to return the version number of a given module. +float WiFi::getVersionNumber(std::string name) +{ + // Locals. + // + int idx = 0; + + // Loop through the version list looking for FilePack. + while(idx < wifiCtrl.run.versionList->elements && wifiCtrl.run.versionList->item[idx]->object.compare(name) != 0) + { + idx++; + } + + // Return the version number if found. + return(idx == wifiCtrl.run.versionList->elements ? 0.00 : wifiCtrl.run.versionList->item[idx]->version); +} + +// Method to request KeyMap table headers from the underlying interface and send as a Javascript array. +// +esp_err_t WiFi::sendKeyMapHeaders(httpd_req_t *req) +{ + // Locals. + // + esp_err_t result = ESP_OK; + std::string jsArray; + std::vector headerList; + + // Call the underlying interface to fill the vector with a list of header names. + keyIf->getKeyMapHeaders(headerList); + + // Build up a javascript array and send to the browser direct. + // + jsArray = "["; + for(std::size_t idx = 0; idx < headerList.size(); idx++) + { + jsArray.append("\"").append(headerList[idx]).append("\""); + if(idx == headerList.size()-1) + { + jsArray.append(",\"\"]"); + } else + { + jsArray.append(","); + } + } + + // Send array and return result. + result=httpd_resp_send_chunk(req, jsArray.c_str(), jsArray.size()); + + // Send result, ESP_OK = all successful, anything else a transmission or data error occurred. + return(result); +} + +// Method to request KeyMap table data types from the underlying interface and send as a Javascript array. +esp_err_t WiFi::sendKeyMapTypes(httpd_req_t *req) +{ + // Locals. + // + int startPos; + esp_err_t result = ESP_OK; + std::string jsArray; + std::vector typeList; + + // Call the underlying interface to fill a vector with the type of each keymap column. + keyIf->getKeyMapTypes(typeList); + + // Build up a javascript array containing the column types mapping if needed to an EditTable value. + // + jsArray = "["; + for(std::size_t idx = 0; idx < typeList.size(); idx++) + { + // Strip out the custom tag, (custom_ttp_ where tt = type, ie. rd = Radio, cb = Checkbox, p = polarity, p = positive, = negative) not needed, use as an internal marker to identify custom fields. + if((startPos = typeList[idx].find("custom_")) >= 0) + { + jsArray.append("\"").append(typeList[idx].substr(startPos+11, std::string::npos)).append("\""); + } else + { + jsArray.append("\"").append(typeList[idx]).append("\""); + } + + if(idx == typeList.size()-1) + { + jsArray.append(",\"checkbox\"]"); + } else + { + jsArray.append(","); + } + } + + // Send array and return result. + result=httpd_resp_send_chunk(req, jsArray.c_str(), jsArray.size()); + + // Send result, ESP_OK = all successful, anything else a transmission or data error occurred. + return(result); +} + +// Method to expand the custom type fields in the interface to custom fields in the EditTable configuration code. +// This code is injected into the javascript setup and will invoke a custom popover or select UI. +esp_err_t WiFi::sendKeyMapCustomTypeFields(httpd_req_t *req) +{ + // Locals. + // + int startPos; + esp_err_t result = ESP_OK; + std::string typeStr = ""; + std::vector typeList; + + // Call the underlying interface to fill a vector with the type of each keymap column. + keyIf->getKeyMapTypes(typeList); + + for(std::size_t idx = 0; idx < typeList.size(); idx++) + { + // Custom field? + if((startPos = typeList[idx].find("custom")) >= 0) + { + // Find any duplicate by searching the vector just processed. + bool duplicate = false; + for(std::size_t idx2 = 0; idx2 < idx; idx2++) { if(typeList[idx].compare(typeList[idx2]) == 0) { duplicate = true; break; } } + if(duplicate) continue; + + // Negative or positive value? + bool negate = (typeList[idx].substr(startPos+9, 1)[0] == 'p' ? false : true); + + // Build the custom type definition which is injected into the javascript setup of EditTable. + typeStr += " '" + typeList[idx].substr(startPos+11, std::string::npos) + "' : { \n" + + " html: '', \n"; + + // As some of the interface parameters are negative active, if the 'custom' label is followed by a minus '-' this means the value sent or received needs to be negated. + // This is because the UI works in positive values. If the 'custom' label is followed by an underscore '_' then no data change is made. + typeStr.append(" getValue: function (input) { \n"); + typeStr.append(" var $thisVal = $(input).val(); \n"); + if(negate) + typeStr.append(" return hexConvert($thisVal, true);\n"); + else + typeStr.append(" return hexConvert($thisVal, false);\n"); + typeStr.append(" }, \n"); + typeStr.append(" setValue: function (input, inVal) { \n"); + if(negate) + typeStr.append(" var $thisVal = $(input).attr(\"value\", hexConvert(inVal, true));\n"); + else + typeStr.append(" var $thisVal = $(input).attr(\"value\", hexConvert(inVal, false));\n"); + typeStr.append(" return $thisVal; \n"); + typeStr.append(" } \n"); + typeStr.append(" },\n"); + } + } + + // Send array and return result. + result=httpd_resp_send_chunk(req, typeStr.c_str(), typeStr.size()); + + // Send result, ESP_OK = all successful, anything else a transmission or data error occurred. + return(result); +} + +// Method to request KeyMap table entries, row at a time, from the underlying interface and send as a Javascript array. +// This method could involve large amounts of data which may overflow the heap so data is requested and sent row by row. +esp_err_t WiFi::sendKeyMapData(httpd_req_t *req) +{ + // Locals. + // + esp_err_t result = ESP_OK; + bool startMode = true; + bool firstRow = true; + int row = 0; + std::string jsArray = ""; + std::vector data; + + // Initiate a loop, calling the underlying interface to return data row by row until the end of the keymap data. + while(result == ESP_OK && keyIf->getKeyMapData(data, &row, startMode) == false) + { + // At start, we initialise the data retrieval and also setup the Javascript array designator. + if(startMode == true) + { + startMode = false; + jsArray = "["; + } + if(firstRow == false) + { + jsArray = ","; + } else + { + firstRow = false; + } + jsArray.append("["); + for(std::size_t idx = 0; idx < data.size(); idx++) + { + jsArray.append("\"").append(to_str(data[idx], 0, 16)).append("\""); + if(idx == data.size()-1) + { + jsArray.append(",false]"); + } else + { + jsArray.append(","); + } + } + data.clear(); + + // Send array and return result. + result=httpd_resp_send_chunk(req, jsArray.c_str(), jsArray.size()); + } + + // At the end we need to close the javascript array designator. No way to do this in the loop as the data get method doesnt provide next state information. + if(result == ESP_OK) + { + jsArray = "]"; + // Send array and return result. + result=httpd_resp_send_chunk(req, jsArray.c_str(), jsArray.size()); + } + + // Send result, ESP_OK = all successful, anything else a transmission or data error occurred. + return(result); +} + +// Method for building up the popover modals which are used to enable a user to select values by tick rather than work out a hex value. +// +esp_err_t WiFi::sendKeyMapPopovers(httpd_req_t *req) +{ + // Locals. + // + int startPos; + esp_err_t result = ESP_OK; + std::string jsArray; + std::string jsClass; + std::vector headerList; + std::vector typeList; + std::vector> selectList; + + // Get list of column headers, these are used as the popover title. + keyIf->getKeyMapHeaders(headerList); + + // Get list of types, these are needed to setup a popup for each custom field. + keyIf->getKeyMapTypes(typeList); + + // Loop through the types, process any custom field into a popover modal. + for(std::size_t idx = 0; result == ESP_OK && idx < typeList.size(); idx++) + { + // Custom field? Skip if not custom type. + if((startPos = typeList[idx].find("custom_")) >= 0) + { + // Find any duplicate by searching the vector just processed. + bool duplicate = false; + for(std::size_t idx2 = 0; idx2 < idx; idx2++) { if(typeList[idx].compare(typeList[idx2]) == 0) { duplicate = true; break; } } + if(duplicate) continue; + + jsClass = typeList[idx].substr(startPos+11, std::string::npos); + jsArray = "

\n"); + + // Send array and return result. + result=httpd_resp_send_chunk(req, jsArray.c_str(), jsArray.size()); + + // Free up memory for next iteration. + selectList.clear(); + } + } + + // Send result, ESP_OK = all successful, anything else a transmission or data error occurred. + return(result); +} + +// Method to render the radio select for Mouse Host Scaling. +// +esp_err_t WiFi::sendMouseRadioChoice(httpd_req_t *req, const char *option) +{ + // Locals. + // + int startPos; + esp_err_t result = ESP_OK; + std::string typeStr = ""; + std::string typeHead = ""; + std::string typeBody = ""; + std::vector typeList; + std::vector> selectList; + KeyInterface *activeMouseIf = (mouseIf == NULL ? keyIf : mouseIf); + + // Call the underlying interface to fill a vector with the type of config parameters. + activeMouseIf->getMouseConfigTypes(typeList); + + for(std::size_t idx = 0; idx < typeList.size(); idx++) + { + // Custom field? + if((startPos = typeList[idx].find(option)) >= 0) + { + // Find any duplicate by searching the vector just processed. + bool duplicate = false; + for(std::size_t idx2 = 0; idx2 < idx; idx2++) { if(typeList[idx].compare(typeList[idx2]) == 0) { duplicate = true; break; } } + if(duplicate) continue; + + // Get the select list of values for the current config type. + activeMouseIf->getMouseSelectList(selectList, typeList[idx]); + typeStr.append("
\n"); + for(auto iter = std::begin(selectList); iter != std::end(selectList); iter++) + { + // Skip current value item. + if(iter == std::begin(selectList)) continue; + + typeStr.append(" first).append("\" type=\"radio\" name=\"").append(typeList[idx]).append("\" value=\"").append(to_str(iter->second, 0, 10)).append("\"").append(selectList[0].second == iter->second ? "checked" : "").append("/>") + .append(" \n"); + } + typeStr.append("

"); + } + } + + // Send array and return result. + result=httpd_resp_send_chunk(req, typeStr.c_str(), typeStr.size()); + + // Send result, ESP_OK = all successful, anything else a transmission or data error occurred. + return(result); +} + +// Method to expand variable macros into variable values within a string buffer. The buffer will contain HTML/CSS text prior to despatch to a browser. +// +esp_err_t WiFi::expandVarsAndSend(httpd_req_t *req, std::string str) +{ + // Locals. + // + bool largeMacroDetected = false; + int startPos; + t_kvPair keyValue; + esp_err_t result = ESP_OK; + std::vector pairs; + + // Build up the list of pairs, place holder to value, this is used to expand the given string with latest runtime values. + // Certain macros return a lot of data so they cannot be added into the mapping vector due to RAM constraints, these are handled in-situ. + // + keyValue.name = "%SK_WIFIMODEAP%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? "checked" : ""); pairs.push_back(keyValue); + keyValue.name = "%SK_WIFIMODECLIENT%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_CLIENT ? "checked" : ""); pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTSSID%"; keyValue.value = wifiConfig.clientParams.ssid; pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTPWD%"; keyValue.value = wifiConfig.clientParams.pwd; pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTDHCPON%"; keyValue.value = (wifiConfig.clientParams.useDHCP == true ? "checked" : ""); pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTDHCPOFF%"; keyValue.value = (wifiConfig.clientParams.useDHCP == false ? "checked" : ""); pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTIP%"; keyValue.value = wifiConfig.clientParams.ip; pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTNM%"; keyValue.value = wifiConfig.clientParams.netmask; pairs.push_back(keyValue); + keyValue.name = "%SK_CLIENTGW%"; keyValue.value = wifiConfig.clientParams.gateway; pairs.push_back(keyValue); + keyValue.name = "%SK_APSSID%"; keyValue.value = wifiConfig.apParams.ssid; pairs.push_back(keyValue); + keyValue.name = "%SK_APPWD%"; keyValue.value = wifiConfig.apParams.pwd; pairs.push_back(keyValue); + keyValue.name = "%SK_APIP%"; keyValue.value = wifiConfig.apParams.ip; pairs.push_back(keyValue); + keyValue.name = "%SK_APNM%"; keyValue.value = wifiConfig.apParams.netmask; pairs.push_back(keyValue); + keyValue.name = "%SK_APGW%"; keyValue.value = wifiConfig.apParams.gateway; pairs.push_back(keyValue); + keyValue.name = "%SK_CURRENTSSID%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? wifiCtrl.ap.ssid : wifiCtrl.client.ssid); pairs.push_back(keyValue); + keyValue.name = "%SK_CURRENTPWD%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? wifiCtrl.ap.pwd : wifiCtrl.client.pwd); pairs.push_back(keyValue); + keyValue.name = "%SK_CURRENTIP%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? wifiCtrl.ap.ip : wifiCtrl.client.ip); pairs.push_back(keyValue); + keyValue.name = "%SK_CURRENTNM%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? wifiCtrl.ap.netmask : wifiCtrl.client.netmask); pairs.push_back(keyValue); + keyValue.name = "%SK_CURRENTGW%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? wifiCtrl.ap.gateway : wifiCtrl.client.gateway); pairs.push_back(keyValue); + keyValue.name = "%SK_CURRENTIF%"; keyValue.value = keyIf->ifName().append(" "); pairs.push_back(keyValue); + keyValue.name = "%SK_SECONDIF%"; keyValue.value = (mouseIf != NULL ? mouseIf->ifName().append(" ") : ""); pairs.push_back(keyValue); + keyValue.name = "%SK_REBOOTBUTTON%"; keyValue.value = (wifiCtrl.run.rebootButton == true ? "block" : "none"); pairs.push_back(keyValue); + keyValue.name = "%SK_ERRMSG%"; keyValue.value = wifiCtrl.run.errorMsg; pairs.push_back(keyValue); + keyValue.name = "%SK_PRODNAME%"; keyValue.value = (wifiCtrl.run.versionList->elements > 1 ? wifiCtrl.run.versionList->item[0]->object : "Unknown"); pairs.push_back(keyValue); + keyValue.name = "%SK_PRODVERSION%"; keyValue.value = (wifiCtrl.run.versionList->elements > 1 ? to_str(wifiCtrl.run.versionList->item[0]->version, 2, 10) : "Unknown"); pairs.push_back(keyValue); + keyValue.name = "%SK_MODULES%"; if(wifiCtrl.run.versionList->elements > 1) + { + std::ostringstream list; + list << ""; + for(int idx=0, cols=0; idx < wifiCtrl.run.versionList->elements; idx++) + { + // Ignore SharpKey/FilePack, they are part of our version list but not relevant as a module. + if(wifiCtrl.run.versionList->item[idx]->object.compare("SharpKey") == 0 || wifiCtrl.run.versionList->item[idx]->object.compare("FilePack") == 0) + continue; + + if((cols++ % 6) == 0) + { + list << ""; + if(idx < wifiCtrl.run.versionList->elements) { list << ""; } + } + list << " "; + } + list << "
" << wifiCtrl.run.versionList->item[idx]->object << " (v" << to_str(wifiCtrl.run.versionList->item[idx]->version, 2, 10) << ")   
"; + keyValue.value = list.str(); + } else { keyValue.value = "Unknown"; }; pairs.push_back(keyValue); + keyValue.name = "%SK_FILEPACK%"; { + std::ostringstream list; + list << ""; + list << " "; + list << "
FilePack (v" << to_str(getVersionNumber("FilePack"), 2, 10) << ")
"; + keyValue.value = list.str(); + } pairs.push_back(keyValue); + keyValue.name = "%SK_PARTITIONS%"; { + std::ostringstream list; + const esp_partition_t *runPart = esp_ota_get_running_partition(); + esp_partition_iterator_t it; + it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + esp_err_t err; + esp_app_desc_t appDesc; + for (; it != NULL; it = esp_partition_next(it)) { + const esp_partition_t *part = esp_partition_get(it); + err = esp_ota_get_partition_description(part, &appDesc); + list << "" + << "" << part->label << "" + << "" << esp32PartitionType(part->type) << "" + << "" << esp32PartitionSubType(part->subtype) << "" + << "" << to_str(part->address, 0, 16) << "" + << "" << to_str(part->size, 0, 16) << "" + << "" << (err == ESP_OK ? appDesc.version : part->subtype == ESP_PARTITION_SUBTYPE_DATA_SPIFFS ? to_str(getVersionNumber("FilePack"), 2, 10) : "") << "" + << "" << (err == ESP_OK ? appDesc.date : "") << " " + << (err == ESP_OK ? appDesc.time : "") << "" + << "" << (runPart->address == part->address ? "Yes" : "") << "" + << ""; + } + esp_partition_iterator_release(it); + + keyValue.value = list.str(); + } pairs.push_back(keyValue); + keyValue.name = "%SK_KEYMAPHEADER%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_KEYMAPTYPES%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_KEYMAPJSFIELDS%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_KEYMAPDATA%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_KEYMAPPOPOVER%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_MOUSEHOSTSCALING%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_MOUSEPS2SCALING%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_MOUSEPS2RESOLUTION%"; keyValue.value = ""; pairs.push_back(keyValue); + keyValue.name = "%SK_MOUSEPS2SAMPLERATE%"; keyValue.value = ""; pairs.push_back(keyValue); + + // Go through list of place holders to expand and replace. + // + for(auto pair : pairs) + { + // If the varname exists, replace with value. + if((startPos = str.find(pair.name)) >= 0) + { + // Dont expand large data macros yet, they can potentially generate too much data for the limited ESP32 RAM. + if(pair.name.compare("%SK_KEYMAPHEADER%") != 0 && pair.name.compare("%SK_KEYMAPTYPES%") != 0 && pair.name.compare("%SK_KEYMAPDATA%") != 0 && + pair.name.compare("%SK_KEYMAPJSFIELDS%") != 0 && pair.name.compare("%SK_KEYMAPPOPOVER%") != 0 && pair.name.compare("%SK_MOUSEHOSTSCALING%") != 0 && + pair.name.compare("%SK_MOUSEPS2SCALING%") != 0 && pair.name.compare("%SK_MOUSEPS2RESOLUTION%") != 0 && pair.name.compare("%SK_MOUSEPS2SAMPLERATE%") != 0 + ) + { + str.replace(startPos, pair.name.length(), pair.value); + } else + { + largeMacroDetected = true; + } + } + } + // Complete line ready for transmission. + str.append("\n"); + + // Normal macros have been expanded, if no large macros were detected, send the expanded string and return. + // + if(largeMacroDetected == false) + { + // Send as a chunk to the browser. + if(str.size() > 0) + { + result=httpd_resp_send_chunk(req, str.c_str(), str.size()); + } + } else + { + // Repeat the key:value search, locating the large macro, only 1 is allowed per line. + for(auto pair : pairs) + { + // If the macro name exists, process, it will be the large macro, only 1 allowed per line. + if((startPos = str.find(pair.name)) >= 0) + { + // Ease of reading. + int endMacroPos = startPos+pair.name.size(); + int sizeMacro = pair.name.size(); + int sizeEndStr = str.size() - startPos - sizeMacro; + if(sizeEndStr < 0) sizeEndStr = 0; + + // Send the first part of the string upto but excluding the macro. + if(startPos > 0) + result=httpd_resp_send_chunk(req, str.substr(0, startPos).c_str(), startPos); + if(result == ESP_OK) + { + // Keymap Table header. The underlying interface converts its keyboard mapping table into a javascript format list of column headers. + if(pair.name.compare("%SK_KEYMAPHEADER%") == 0) + { + result = sendKeyMapHeaders(req); + } + // Keymap Table types. The underlying interface converts its keyboard mapping table into a javascript format list of column types. + if(pair.name.compare("%SK_KEYMAPTYPES%") == 0) + { + result = sendKeyMapTypes(req); + } + // Keymap field definition for custom fields. + if(pair.name.compare("%SK_KEYMAPJSFIELDS%") == 0) + { + result = sendKeyMapCustomTypeFields(req); + } + // Keymap Table data. This is the big one where the underlying interface converts its keyboard mapping table into a javascript format list of column types. + if(pair.name.compare("%SK_KEYMAPDATA%") == 0) + { + result = sendKeyMapData(req); + } + // Popover boxes, aid data input in a more user friendly manner. + if(pair.name.compare("%SK_KEYMAPPOPOVER%") == 0) + { + result = sendKeyMapPopovers(req); + } + // Mouse host scaling - Radio selection of the scaling required for adaption of the PS/2 mouse data to host. + if(pair.name.compare("%SK_MOUSEHOSTSCALING%") == 0) + { + result = sendMouseRadioChoice(req, "host_scaling"); + } + if(pair.name.compare("%SK_MOUSEPS2SCALING%") == 0) + { + result = sendMouseRadioChoice(req, "mouse_scaling"); + } + if(pair.name.compare("%SK_MOUSEPS2RESOLUTION%") == 0) + { + result = sendMouseRadioChoice(req, "mouse_resolution"); + } + if(pair.name.compare("%SK_MOUSEPS2SAMPLERATE%") == 0) + { + result = sendMouseRadioChoice(req, "mouse_sampling"); + } + + // If the input string had any data after the macro then send it to complete transmission. + if(result == ESP_OK && sizeEndStr > 0) + { + result=httpd_resp_send_chunk(req, str.substr(endMacroPos, std::string::npos).c_str(), sizeEndStr); + } + } + break; + } + } + } + + // Debug, track heap size. + ESP_LOGD(WIFITAG, "After expansion Free Heap (%d)", xPortGetFreeHeapSize()); + + // Return result of expansion/transmission. + return(result); +} + +// A method to open and read a file line by line, expanding any macros therein and sending the result to the open socket connection. +// +esp_err_t WiFi::expandAndSendFile(httpd_req_t *req, const char *basePath, std::string fileName) +{ + // Locals. + // + std::string line; + std::ifstream inFile; + esp_err_t result = ESP_OK; + + // Build the FQFN for reading. + std::string fqfn = basePath; fqfn += "/"; fqfn += fileName; + + // Ensure the content type is set correctly. + setContentTypeFromFileType(req, fileName); + + // Read the file into an input stream, read a line, expand it and r and then store into a string buffer to be returned to caller. + inFile.open(fqfn.c_str()); + while(result == ESP_OK && std::getline(inFile, line)) + { + // Call method to output line after expanding, in-situ, any macros into variable values. + if((result=expandVarsAndSend(req, line)) != ESP_OK) + { + // Abort sending file. + httpd_resp_sendstr_chunk(req, NULL); + + // Respond with 500 Internal Server Error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); + break; + } + } + + // Successful, end the response with a NULL string. + if(result == ESP_OK) + { + result = httpd_resp_send_chunk(req, NULL, 0); + } + + // Tidy up for exit. + inFile.close(); + + // Return result code. + return(result); +} + +bool WiFi::isFileExt(std::string fileName, std::string extension) +{ + // Locals. + // + bool result = false; + + // Match the extension. + if(strcasecmp(fileName.substr(fileName.find_last_of(".")).c_str(), extension.substr(extension.find_last_of(".")).c_str()) == 0) + { + // If this is a multi part extension, match the whole extension too. + // + result = true; + if(extension.find_first_of(".") != extension.find_last_of(".") && strcasecmp(fileName.substr(fileName.find_first_of(".")).c_str(), extension.c_str()) != 0) + { + result = false; + } + } + + // Extension match? + return(result); +} + +// Method to set the HTTP response content type according to file extension. +// +esp_err_t WiFi::setContentTypeFromFileType(httpd_req_t *req, std::string fileName) +{ + if (isFileExt(fileName, ".pdf")) + { + return httpd_resp_set_type(req, "application/pdf"); + } + else if (isFileExt(fileName, ".html")) + { + return httpd_resp_set_type(req, "text/html"); + } + else if (isFileExt(fileName, ".css")) + { + return httpd_resp_set_type(req, "text/css"); + } + else if (isFileExt(fileName, ".js")) + { + return httpd_resp_set_type(req, "application/javascript"); + } + else if (isFileExt(fileName, ".ico")) + { + return httpd_resp_set_type(req, "image/x-icon"); + } + else if (isFileExt(fileName, ".jpeg") || isFileExt(fileName, ".jpg")) + { + return httpd_resp_set_type(req, "image/jpeg"); + } + else if (isFileExt(fileName, ".bin")) + { + return httpd_resp_set_type(req, "application/octet-stream"); + } + else if (isFileExt(fileName, ".bmp")) + { + return httpd_resp_set_type(req, "image/bmp"); + } + else if (isFileExt(fileName, ".gif")) + { + return httpd_resp_set_type(req, "image/gif"); + } + else if (isFileExt(fileName, ".jar")) + { + return httpd_resp_set_type(req, "application/java-archive"); + } + else if (isFileExt(fileName, ".js")) + { + return httpd_resp_set_type(req, "text/javascript"); + } + else if (isFileExt(fileName, ".json")) + { + return httpd_resp_set_type(req, "application/json"); + } + else if (isFileExt(fileName, ".png")) + { + return httpd_resp_set_type(req, "image/png"); + } + else if (isFileExt(fileName, ".php")) + { + return httpd_resp_set_type(req, "application/x-httod-php"); + } + else if (isFileExt(fileName, ".rtf")) + { + return httpd_resp_set_type(req, "application/rtf"); + } + else if (isFileExt(fileName, ".tif") || isFileExt(fileName, ".tiff")) + { + return httpd_resp_set_type(req, "image/tiff"); + } + else if (isFileExt(fileName, ".txt")) + { + return httpd_resp_set_type(req, "text/plain"); + } + else if (isFileExt(fileName, ".xml")) + { + return httpd_resp_set_type(req, "application/xml"); + } + else if (isFileExt(fileName, ".ico")) { + return httpd_resp_set_type(req, "image/x-icon"); + } + // Default to plain text. + return httpd_resp_set_type(req, "text/plain"); +} + +// Locates the path within URI anc copies it into a string. +// +esp_err_t WiFi::getPathFromURI(std::string& destPath, std::string& destFile, const char *basePath, const char *uri) +{ + // Locals. + // + size_t pathlen = strlen(uri); + const char *question = strchr(uri, '?'); + const char *hash = strchr(uri, '#'); + + // Question in the URI - skip. + if(question) + { + pathlen = MIN(pathlen, question - uri); + } + // Hash in the URI - skip. + if(hash) + { + pathlen = MIN(pathlen, hash - uri); + } + + // Construct full path (base + path) + destPath = basePath; + destPath.append(uri, pathlen); + + // Extract filename. + destFile = ""; + destFile.append(uri, 1, pathlen-1); + + // Result, fail if no path extracted. + return(destFile.size() == 0 ? ESP_FAIL : ESP_OK); +} + +// Overloaded method to get the remaining URI from the triggering base path. +// +esp_err_t WiFi::getPathFromURI(std::string& destPath, const char *basePath, const char *uri) +{ + // Locals. + // + esp_err_t result = ESP_OK; + size_t pathlen = strlen(uri); + const char *question = strchr(uri, '?'); + const char *hash = strchr(uri, '#'); + + // Question in the URI - skip. + if(question) + { + pathlen = MIN(pathlen, question - uri); + } + // Hash in the URI - skip. + if(hash) + { + pathlen = MIN(pathlen, hash - uri); + } + + // Extract the path without starting base path and without trailing variables. + destPath = ""; + destPath.append(uri, pathlen); + result = (destPath.find(basePath) != std::string::npos ? ESP_OK : ESP_FAIL); + if(result == ESP_OK) + { + destPath.erase(0, strlen(basePath)); + } + + // Result, fail if no base path was found. + return(result); +} + +// Handler to read and send static files. HTML/CSS are expanded with embedded vars. +// +esp_err_t WiFi::defaultFileHandler(httpd_req_t *req) +{ + // Locals. + // + FILE *fd = NULL; + struct stat file_stat; + char *buf; + int bufLen; + std::string gzipFile = ""; + std::string disposition = ""; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Get required Header values for processing. + bufLen = httpd_req_get_hdr_value_len(req, "Host") + 1; + if(bufLen > 1) + { + buf = new char[bufLen]; + // Copy null terminated value string into buffer + if(httpd_req_get_hdr_value_str(req, "Host", buf, bufLen) == ESP_OK) + { + // Assign to control structure for later use. + pThis->wifiCtrl.session.host = buf; + } + + // Free up memory to complete. + delete buf; + } + // Get encoding methods. + bufLen = httpd_req_get_hdr_value_len(req, "Accept-Encoding") + 1; + if(bufLen > 1) + { + buf = new char[bufLen]; + // Set flags to indicate allowed encoding methods. + if(httpd_req_get_hdr_value_str(req, "Accept-Encoding", buf, bufLen) == ESP_OK) + { + pThis->wifiCtrl.session.gzip = (strstr(buf, "gzip") != NULL ? true : false); + pThis->wifiCtrl.session.deflate = (strstr(buf, "deflate") != NULL ? true : false); + } + + // Free up memory to complete. + delete buf; + } + + // Get and store the URL query string. + bufLen = httpd_req_get_url_query_len(req) + 1; + if (bufLen > 1) + { + buf = new char[bufLen]; + if (httpd_req_get_url_query_str(req, buf, bufLen) == ESP_OK) + { + pThis->wifiCtrl.session.queryStr = buf; + ESP_LOGI(WIFITAG, "Found URL query => %s", pThis->wifiCtrl.session.queryStr.c_str()); + } + + // Free up memory to complete. + delete buf; + } + + // Look for a filename in the URI and construct the file path returning both. If filename isnt valid, respond with 500 Internal Server Error and exit. + if(pThis->getPathFromURI(pThis->wifiCtrl.session.filePath, pThis->wifiCtrl.session.fileName, pThis->wifiCtrl.run.basePath, req->uri) == ESP_FAIL) + { + // Check for root URL. + if(strlen(req->uri) == 1 && req->uri[0] == '/') + { + pThis->wifiCtrl.session.fileName = "/"; + } else + { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename invalid"); + return(ESP_FAIL); + } + } + + // See if the provided name matches a static handler, such as root, if so execute response directly. + if(pThis->wifiCtrl.session.fileName.compare("/") == 0 || pThis->wifiCtrl.session.fileName.compare("index.html") == 0 || pThis->wifiCtrl.session.fileName.compare("index.htm") == 0) + { + // Open the given file, read and expand macros and send to open connection. + return pThis->expandAndSendFile(req, pThis->wifiCtrl.run.basePath, "index.html"); + } + + // Is this a macro to specify keymap file? Keymap file name changes depending on runmode so adjust filename accordingly. + if(pThis->wifiCtrl.session.fileName.compare("keymap") == 0) + { + pThis->wifiCtrl.session.fileName = std::regex_replace(pThis->wifiCtrl.session.fileName, std::regex("keymap"), pThis->keyIf->getKeyMapFileName()); + pThis->wifiCtrl.session.filePath = std::regex_replace(pThis->wifiCtrl.session.filePath, std::regex("keymap"), pThis->keyIf->getKeyMapFileName()); + disposition = "attachment; filename=" + pThis->keyIf->getKeyMapFileName(); + if(httpd_resp_set_hdr(req, "Content-Disposition", disposition.c_str()) != ESP_OK) + { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Set content disposition filename failed"); + return(ESP_FAIL); + } + } + + // Get details of the file, throw error 404 - File Not Found on error. + if(stat(pThis->wifiCtrl.session.filePath.c_str(), &file_stat) == -1) + { + // Prepare gzip version, size remains 0 if normal file is found. + if(pThis->wifiCtrl.session.gzip) + gzipFile = pThis->wifiCtrl.session.filePath + ".gz"; + + // Check to see if the file is compressed. Tag on .gz and retry, if success then set encoding content and carry on as normal. + // + if(pThis->wifiCtrl.session.gzip == true && stat(gzipFile.c_str(), &file_stat) == -1) + { + // Respond with 404 Not Found. + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist"); + return(ESP_FAIL); + } + + // Set the content encoding to gzip to comply with specs. + // WARNING: Do not gzip html or library css files as they get parsed and expanded. + if(httpd_resp_set_hdr(req, "Content-Encoding", "gzip") != ESP_OK) + { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Set content encoding to gzip failed"); + return(ESP_FAIL); + } + } + + // If the file is HTML, JS or CSS then process externally as we need to subsitute embedded variables as required. Note the guard around static evaluation, ie. gzipFile.size. This is to cater for gzipped html, js or css as we cannot + // parse and expand, it is served 'as is'. + if((pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".html") || (pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".js") && !pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".min.js")) || (pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".css") && !pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".min.css"))) && gzipFile.size() == 0) + { + // Open the given file, read and expand macros and send to open connection. + pThis->expandAndSendFile(req, pThis->wifiCtrl.run.basePath, pThis->wifiCtrl.session.fileName); + } else + { + // Try to open the file, we performed a stat so is does exist but perhaps a FAT corruption occurred?. + fd = fopen(gzipFile.size() > 0 ? gzipFile.c_str() : pThis->wifiCtrl.session.filePath.c_str(), "r"); + if(!fd) + { + // Respond with 500 Internal Server Error + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file"); + return(ESP_FAIL); + } + + ESP_LOGI(WIFITAG, "Sending %sfile : %s (%ld bytes)...", gzipFile.size() > 0 ? "gzip " : " ", pThis->wifiCtrl.session.fileName.c_str(), file_stat.st_size); + pThis->setContentTypeFromFileType(req, pThis->wifiCtrl.session.fileName); + + // Allocate a buffer for chunking the file. The file could be binary, so unlike the HTML/CSS handler, strings cant be used + // thus we read chunks according to our buffer size and send accordingly. + char *chunk = new char[MAX_CHUNK_SIZE]; + size_t chunksize; + do { + // Read file in chunks into the temporary buffer. + chunksize = fread(chunk, 1, MAX_CHUNK_SIZE, fd); + + if (chunksize > 0) + { + // Send the buffer contents as HTTP response chunk. + if(httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) + { + + // Release memory and close files, error!! + fclose(fd); + delete chunk; + + // Abort sending file. + httpd_resp_sendstr_chunk(req, NULL); + + // Respond with 500 Internal Server Error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); + return(ESP_FAIL); + } + } + + // Keep looping till the whole file is sent. + } while (chunksize != 0); + + // Release memory to complete. + delete chunk; + + // Close file after sending complete. + fclose(fd); + ESP_LOGI(WIFITAG, "File sending complete"); + } + + // Respond with an empty chunk to signal HTTP response completion. + httpd_resp_send_chunk(req, NULL, 0); + return ESP_OK; +} + +// Handler to send data sets. The handler is triggered on the /data URI and subpaths define the data to be sent. +// +esp_err_t WiFi::defaultDataGETHandler(httpd_req_t *req) +{ + // Locals. + // + char *buf; + int bufLen; + esp_err_t result = ESP_OK; + std::string uriStr; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Get required Header values for processing. + bufLen = httpd_req_get_hdr_value_len(req, "Host") + 1; + if(bufLen > 1) + { + buf = new char[bufLen]; + // Copy null terminated value string into buffer + if(httpd_req_get_hdr_value_str(req, "Host", buf, bufLen) == ESP_OK) + { + // Assign to control structure for later use. + pThis->wifiCtrl.session.host = buf; + } + + // Free up memory to complete. + delete buf; + } + + // Get and store the URL query string. + bufLen = httpd_req_get_url_query_len(req) + 1; + if (bufLen > 1) + { + buf = new char[bufLen]; + if (httpd_req_get_url_query_str(req, buf, bufLen) == ESP_OK) + { + pThis->wifiCtrl.session.queryStr = buf; + ESP_LOGI(WIFITAG, "Found URL query => %s", pThis->wifiCtrl.session.queryStr.c_str()); + } + + // Free up memory to complete. + delete buf; + } + + // Get the subpath from the URI. + if(pThis->getPathFromURI(uriStr, "/data/", req->uri) == ESP_FAIL) + { + // Respond with 500 Internal Server Error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to extract URI"); + return(ESP_FAIL); + } + + // Match URI and execute required data retrieval and return. + if(uriStr.compare("keymap/table/headers") == 0) + { + result = pThis->sendKeyMapHeaders(req); + } else + if(uriStr.compare("keymap/table/types") == 0) + { + result = pThis->sendKeyMapTypes(req); + } else + if(uriStr.compare("keymap/table/data") == 0) + { + result = pThis->sendKeyMapData(req); + } else + { + result = ESP_FAIL; + } + + // Check the result, if the data send failed we need to tidy up and send an error code. + if(result != ESP_OK) + { + // Abort sending file. + httpd_resp_sendstr_chunk(req, NULL); + + // Respond with 500 Internal Server Error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send data."); + } else + { + // Successful, end the response with a NULL string. + result = httpd_resp_send_chunk(req, NULL, 0); + } + + // Return result, successful data send == ESP_OK. + return(result); +} + +// A method, activated on a client side POST using AJAX file upload, to accept incoming data and write it into the next free OTA partition. If successful +// set the active partition to the newly loaded one. +// +IRAM_ATTR esp_err_t WiFi::otaFirmwareUpdatePOSTHandler(httpd_req_t *req) +{ + // Locals. + // + esp_err_t ret = ESP_OK; + std::string resp = ""; + bool checkImageHeader = true; + esp_ota_handle_t updateHandle = 0 ; + esp_app_desc_t newAppInfo; + esp_app_desc_t runningAppInfo; + esp_app_desc_t invalidAppInfo; + const esp_partition_t *lastInvalidApp; + const esp_partition_t *runningApp; + const esp_partition_t *updatePartition; + + // Retrieve pointer to object in order to access data. + //WiFi* pThis = (WiFi*)req->user_ctx; + + // Get current configuration and next available partition in round-robin style. + lastInvalidApp = esp_ota_get_last_invalid_partition(); + runningApp = esp_ota_get_running_partition(); + updatePartition = esp_ota_get_next_update_partition(NULL); + if(runningApp == NULL || updatePartition == NULL) + { + // Respond with 500 Internal Server Error as we couldnt get primary information on running partition or next available partition for update. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to resolve current/next NVS OTA partition information."); + return(ESP_FAIL); + } + + // Allocate heap space for our receive buffer. + // + char *chunk = new char[MAX_CHUNK_SIZE]; + size_t chunkSize; + + // Use the Content length as the size of the file to be uploaded. + int remaining = req->content_len; + + // Loop while data is still expected. + while(remaining > 0) + { + ESP_LOGI(WIFITAG, "Remaining size : %d", remaining); + + // The file is received in chunks according to the free memory available for a buffer. It has to be at least the size of the firmware application header + // so that it can be read and evaluated in one chunk. + if((chunkSize = httpd_req_recv(req, chunk, MIN(remaining, MAX_CHUNK_SIZE))) <= 0) + { + // Retry if timeout occurred. + if (chunkSize == HTTPD_SOCK_ERR_TIMEOUT) + continue; + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error when a file error occurs. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file"); + return(ESP_FAIL); + } + + // If this is the first read, check the header information and make sure we are not uploading a bad file or one the same as the current image. + // + if(checkImageHeader == true) + { + // The size should be at least that of the application header structures. + // + if (chunkSize > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) + { + // Check current version being downloaded. + memcpy(&newAppInfo, &chunk[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); + if(newAppInfo.magic_word != ESP_APP_DESC_MAGIC_WORD) + { + // Release memory, error!! + delete chunk; + + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "File image is not a valid firmware file."); + return(ESP_FAIL); + } + + // Get information on the running application image. + if(esp_ota_get_partition_description(runningApp, &runningAppInfo) == ESP_OK) + { + ESP_LOGI(WIFITAG, "Running firmware version: %s, new: %s", runningAppInfo.version, newAppInfo.version); + } + + // Compare and make sure we are not trying to upload the same image as the running image. + if(strcmp(newAppInfo.version, runningAppInfo.version) == 0) + { + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error - Same firmware version as running image being uploaded. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Firmware version is same as current version."); + return(ESP_FAIL); + } + + // This part is crucial. If a previous image upload failed to boot, it is marked as bad and the previous working image is rebooted. + // Checks need to be made to ensure we are not trying to upload the same bad image as we may not be so lucky next time detecting it as bad! + // + if (esp_ota_get_partition_description(lastInvalidApp, &invalidAppInfo) == ESP_OK) + { + ESP_LOGI(WIFITAG, "Last invalid firmware version: %s", invalidAppInfo.version); + } + + // Check current version with last invalid partition version. On first factory load there wont be a last partition. + if(lastInvalidApp != NULL) + { + if (memcmp(invalidAppInfo.version, newAppInfo.version, sizeof(newAppInfo.version)) == 0) + { + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error - New image is a previous bad image, cannot upload. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Firmware version is known as bad, it previously failed to boot."); + return(ESP_FAIL); + } + } + + // Dont repeat the check! + checkImageHeader = false; + + // Start the update procedure, data is written as it arrives from the client in chunks. + ret = esp_ota_begin(updatePartition, OTA_WITH_SEQUENTIAL_WRITES, &updateHandle); + if(ret != ESP_OK) + { + esp_ota_abort(updateHandle); + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error - Failed to initialise NVS for writing. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to initialise NVS OTA partition for writing."); + return(ESP_FAIL); + } + } else + { + esp_ota_abort(updateHandle); + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error - Failed to receive sufficient bytes from file to identify header. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive sufficient bytes from file to identify image."); + return(ESP_FAIL); + } + } + // Write out this chunk of data. + ret = esp_ota_write( updateHandle, (const void *)chunk, chunkSize); + if(ret != ESP_OK) + { + esp_ota_abort(updateHandle); + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error - Failed to write packet to NVS OTA partition. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write data to NVS partition."); + return(ESP_FAIL); + } + + // Keep track of remaining size to download. + remaining -= chunkSize; + } + // Release memory, all done! + delete chunk; + + // Complete the NVS write transaction. + ret = esp_ota_end(updateHandle); + if(ret != ESP_OK) + { + if(ret == ESP_ERR_OTA_VALIDATE_FAILED) + { + // Respond with 500 Internal Server Error - Image validation failed. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Image validation failed, image is corrupt."); + return(ESP_FAIL); + } else + { + // Respond with 500 Internal Server Error - Image completion failed. + std::string errMsg = "Image completion failed:"; errMsg += esp_err_to_name(ret); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, errMsg.c_str()); + return(ESP_FAIL); + } + } + + // The image has been successfully downloaded, it is not duplicate or a known dud and validation has passed so set it up as the next boot partition. + ret = esp_ota_set_boot_partition(updatePartition); + if(ret != ESP_OK) + { + std::string errMsg = "Set boot parition to new image failed:"; errMsg += esp_err_to_name(ret); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, errMsg.c_str()); + return(ESP_FAIL); + } + + // Done, send positive status. + vTaskDelay(500); + httpd_resp_set_status(req, "200 OK"); + httpd_resp_sendstr(req, ""); + + return(ret); +} + +// Method to update the LittleFS filesystem OTA. The image file is pushed via an AJAX xhttp POST, received in chunks and written direct +// to the parition. There is no rollback, any failure will see the filesystem corrupted. On a bigger ESP32 Flash chip, rollback may be +// possible but not with the 4MB standard IC. +IRAM_ATTR esp_err_t WiFi::otaFilepackUpdatePOSTHandler(httpd_req_t *req) +{ + // Locals. + // + esp_err_t ret = ESP_OK; + std::string resp = ""; + bool checkImageHeader = true; + uint32_t partStartAddr; + uint32_t partOffsetAddr = 0; + uint32_t partSize = 0; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Find the filesystem partition. + esp_partition_iterator_t it; + it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); + if(it == NULL) + { + // Respond with 500 Internal Server Error - Couldnt find the filesystem partition. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Couldnt find a filesysten partition, is this ESP32 configured?"); + return(ESP_FAIL); + } + + // Get the partition information from the iterator. + const esp_partition_t *part = esp_partition_get(it); + + // Setup the addresses of the partition. + partStartAddr = part->address; + partSize = part->size; + + // Check to ensure the file to upload is not larger than the partition. + // + if(req->content_len > partSize) + { + // Respond with 500 Internal Server Error - File is too large. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Upload file size too large."); + return(ESP_FAIL); + } + + // Erase the partition. + ret = esp_partition_erase_range(part, partOffsetAddr, partSize); + if(ret != ESP_OK) + { + // Respond with 500 Internal Server Error - Partition erase failure. + std::string errMsg = "Failed to erase partition:"; errMsg += esp_err_to_name(ret); errMsg += ", you may need to connect external programmer."; + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, errMsg.c_str()); + return(ESP_FAIL); + } + + // Allocate heap space for our receive buffer. + // + char *chunk = new char[MAX_CHUNK_SIZE]; + size_t chunkSize; + + // Use the Content length as the size of the file to be uploaded. + int remaining = req->content_len; + + // Loop while data is still expected. + while(remaining > 0) + { + ESP_LOGI(WIFITAG, "Remaining size : %d", remaining); + + // The file is received in chunks according to the free memory available for a buffer. It has to be at least the size of the firmware application header + // so that it can be read and evaluated in one chunk. + if((chunkSize = httpd_req_recv(req, chunk, MIN(remaining, MAX_CHUNK_SIZE))) <= 0) + { + // Retry if timeout occurred. + if (chunkSize == HTTPD_SOCK_ERR_TIMEOUT) + continue; + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error when a file error occurs. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file"); + return(ESP_FAIL); + } + + // If this is the first read, check the header information and make sure we are not uploading a bad file or one the same as the current image. + // + if(checkImageHeader == true) + { + // Simple check, look for the base path name in the image. Also the max size was checked earlier, smaller sizes are fine as the littlefs + // filestructure is valid but larger files will overwrite NVS data. + if(strncmp(pThis->wifiCtrl.run.fsPath+1, &chunk[8], strlen(pThis->wifiCtrl.run.fsPath)-1) != 0) + { + // Release memory, error!! + delete chunk; + + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filepack image is not a valid file."); + return(ESP_FAIL); + } + + // Dont repeat the check! + checkImageHeader = false; + } + + // Write out the chunk we received, any errors we abort - probably means the user needs to use an external programmer to correct to error. + ret = esp_partition_write_raw(part, partOffsetAddr, (const void *)chunk, chunkSize); + if(ret != ESP_OK) + { + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error - Write failure. + std::string errMsg = "Write failure: "; errMsg += esp_err_to_name(ret); errMsg += " @ "; errMsg += to_str(partStartAddr+partOffsetAddr, 0, 16); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, errMsg.c_str()); + return(ESP_FAIL); + } + + // Update counters. + partOffsetAddr += chunkSize; + remaining -= chunkSize; + } + // Release memory, all done! + delete chunk; + + // Done, send positive status. + vTaskDelay(500); + httpd_resp_set_status(req, "200 OK"); + httpd_resp_sendstr(req, ""); + + // Send result. + return(ESP_OK); +} + +// Method to upload a file and store it onto the filesystem tagged according to the current running I/F. +// This method could be merged with the Firmware/Filepack methods but at the moment kept seperate to allow for any +// unseen requirements. +// The data should be passed to the I/F object so that it can verify +// +esp_err_t WiFi::keymapUploadPOSTHandler(httpd_req_t *req) +{ + // Locals. + // + std::string resp = ""; + std::fstream keyFileOut; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Attempt to open the keymap file for writing. + // + if(pThis->keyIf->createKeyMapFile(keyFileOut) == false) + { + // Respond with 500 Internal Server Error - File creation error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create temporary keymap file"); + return(ESP_FAIL); + } + + // Allocate heap space for our receive buffer. + // + char *chunk = new char[MAX_CHUNK_SIZE]; + size_t chunkSize; + + // Use the Content length as the size of the file to be uploaded. + int remaining = req->content_len; + + // Loop while data is still expected. + while(remaining > 0) + { + ESP_LOGI(WIFITAG, "Remaining size : %d", remaining); + + // The file is received in chunks according to the free memory available for a buffer. + if((chunkSize = httpd_req_recv(req, chunk, MIN(remaining, MAX_CHUNK_SIZE))) <= 0) + { + // Retry if timeout occurred. + if (chunkSize == HTTPD_SOCK_ERR_TIMEOUT) + continue; + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error when a reception error occurs. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file"); + return(ESP_FAIL); + } + + // Store the data chunk into the keymap file. + if(pThis->keyIf->storeDataToKeyMapFile(keyFileOut, chunk, chunkSize) == false) + { + // Cleanup the mess! + pThis->keyIf->closeAndCommitKeyMapFile(keyFileOut, true); + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error when a file error occurs. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write data into file"); + return(ESP_FAIL); + } + + // Update counters. + remaining -= chunkSize; + } + // Release memory, all done! + delete chunk; + + // Close and commit the file. + if(pThis->keyIf->closeAndCommitKeyMapFile(keyFileOut, false) == false) + { + // Respond with 500 Internal Server Error if the commit fails. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write data into file"); + return(ESP_FAIL); + } + + // Done, send positive status. + vTaskDelay(500); + httpd_resp_set_status(req, "200 OK"); + httpd_resp_sendstr(req, ""); + + // Send result. + return(ESP_OK); +} + + +// Method to store the keymap table data. The POST data is captured in chunks and sent to the underlying interface method +// for parsing and extraction. +esp_err_t WiFi::keymapTablePOSTHandler(httpd_req_t *req) +{ + // Locals. + // + int startPos; + int endPos; + int commaPos; + std::string resp = ""; + std::string jsonData = ""; + std::string jsonArray = ""; + std::fstream keyFileOut; + std::vector dataArray; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Attempt to open the keymap file for writing. + // + if(pThis->keyIf->createKeyMapFile(keyFileOut) == false) + { + // Respond with 500 Internal Server Error - File creation error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create temporary keymap file"); + return(ESP_FAIL); + } + + // Allocate heap space for our receive buffer. + // + char *chunk = new char[MAX_CHUNK_SIZE]; + size_t chunkSize; + + // Use the Content length as the size of the JSON array to be uploaded. + int remaining = req->content_len; + + // Loop while data is still expected. + while(remaining > 0) + { + ESP_LOGI(WIFITAG, "Remaining size : %d", remaining); + + // The file is received in chunks according to the free memory available for a buffer. + if((chunkSize = httpd_req_recv(req, chunk, MIN(remaining, MAX_CHUNK_SIZE))) <= 0) + { + // Retry if timeout occurred. + if (chunkSize == HTTPD_SOCK_ERR_TIMEOUT) + continue; + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error when a reception error occurs. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file"); + return(ESP_FAIL); + } + + // Build up the JSON Data array and process piecemeal to keep overall memory usage low. + jsonData.append(chunk, chunkSize); + do { + startPos = jsonData.find("[["); + if(startPos != std::string::npos) { startPos++; } else { startPos = jsonData.find("["); } + endPos = jsonData.find("]"); + + if(startPos != std::string::npos && endPos != std::string::npos) + { + // Extract the array and parse into bytes. + jsonArray = jsonData.substr(startPos, endPos+1); + do { + commaPos = jsonArray.find("\""); + if(commaPos != std::string::npos) + { + jsonArray.erase(0, commaPos+1); + commaPos = jsonArray.find("\""); + if(commaPos != std::string::npos) + { + std::istringstream iss(jsonArray.substr(0, commaPos)); + uint32_t word; + iss >> std::hex >> word; + dataArray.push_back(word); + } + commaPos = jsonArray.find(","); + if(commaPos != std::string::npos) + { + jsonArray.erase(0, commaPos+1); + } + } + } while(jsonArray.size() > 0 && commaPos != std::string::npos); + + // Remove the array and the comma (or ending ]) seperator. + jsonData.erase(0, endPos + 2); + } + } while(startPos != std::string::npos && endPos != std::string::npos); + + // Store the data chunk into the keymap file. + if(pThis->keyIf->storeDataToKeyMapFile(keyFileOut, dataArray) == false) + { + // Cleanup the mess! + pThis->keyIf->closeAndCommitKeyMapFile(keyFileOut, true); + + // Release memory, error!! + delete chunk; + + // Respond with 500 Internal Server Error when a file error occurs. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write data into file"); + return(ESP_FAIL); + } + + // Free up vector for next loop. + dataArray.clear(); + + // Update counters. + remaining -= chunkSize; + } + // Release memory, all done! + delete chunk; + + // Close and commit the file. + if(pThis->keyIf->closeAndCommitKeyMapFile(keyFileOut, false) == false) + { + // Respond with 500 Internal Server Error if the commit fails. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write data into file"); + return(ESP_FAIL); + } + + // Done, send positive status. + vTaskDelay(500); + httpd_resp_set_status(req, "200 OK"); + httpd_resp_sendstr(req, ""); + + // Send result. + return(ESP_OK); +} + +// Method to extract the Key/Value pairs from a received POST request. +// +esp_err_t WiFi::getPOSTData(httpd_req_t *req, std::vector *pairs) +{ + // Locals. + // + char buf[100]; + int ret; + int rcvBytes = req->content_len; + std::string post; + t_kvPair keyValue; + + // Loop retrieving the POST in chunks and assemble into a string. + while(rcvBytes > 0) + { + // Read data for the request. + if((ret = httpd_req_recv(req, buf, MIN(rcvBytes, sizeof(buf)-1))) <= 0) + { + if(ret == HTTPD_SOCK_ERR_TIMEOUT) + { + /* Retry receiving if timeout occurred */ + continue; + } + return(ESP_FAIL); + } + buf[ret] = '\0'; + + // Add to our string for tokenising. + post = post + buf; + + // Update bytes still to arrive. + rcvBytes -= ret; + } + + // Split the POST into key pairs. + std::vector keys = this->split(post, "&"); + for(auto key : keys) + { + size_t pos = key.find('='); + keyValue.name = key.substr(0, pos); + keyValue.value = key.substr(pos + 1); + pairs->push_back(keyValue); + } + + // Successful extraction of the Key/Value pairs from the POST. + return(ESP_OK); +} + +// Method to process POST data specifically for the WiFi configuration. The key:value pairs are parsed, data extracted +// and validated. Any errors are sent back to the UI/Browser. +esp_err_t WiFi::wifiDataPOSTHandler(httpd_req_t *req, std::vector pairs, std::string& resp) +{ + // Locals. + // + bool dataError = false; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Loop through all the URI key pairs, updating configuration values as necessary. + for(auto pair : pairs) + { + //printf("%s->%s\n", pair.name.c_str(), pair.value.c_str()); + if(pair.name.compare("wifiMode") == 0) + { + if(pair.value.compare("ap") == 0) + { + pThis->wifiConfig.params.wifiMode = WIFI_CONFIG_AP; + dataError = false; + } else + { + pThis->wifiConfig.params.wifiMode = WIFI_CONFIG_CLIENT; + dataError = false; + } + } + if(pair.name.compare("clientSSID") == 0) + { + strncpy(pThis->wifiConfig.clientParams.ssid, pair.value.c_str(), MAX_WIFI_SSID_LEN); + } + if(pair.name.compare("apSSID") == 0) + { + strncpy(pThis->wifiConfig.apParams.ssid, pair.value.c_str(), MAX_WIFI_SSID_LEN); + } + if(pair.name.compare("clientPWD") == 0) + { + strncpy(pThis->wifiConfig.clientParams.pwd, pair.value.c_str(), MAX_WIFI_PWD_LEN); + } + if(pair.name.compare("apPWD") == 0) + { + strncpy(pThis->wifiConfig.apParams.pwd, pair.value.c_str(), MAX_WIFI_PWD_LEN); + } + if(pair.name.compare("dhcpMode") == 0) + { + if(pair.value.compare("on") == 0) + { + pThis->wifiConfig.clientParams.useDHCP = true; + } else + { + pThis->wifiConfig.clientParams.useDHCP = false; + } + } + if(pair.name.compare("clientIP") == 0) + { + strncpy(pThis->wifiConfig.clientParams.ip, pair.value.c_str(), MAX_WIFI_IP_LEN); + } + if(pair.name.compare("apIP") == 0) + { + strncpy(pThis->wifiConfig.apParams.ip, pair.value.c_str(), MAX_WIFI_IP_LEN); + } + if(pair.name.compare("clientNETMASK") == 0) + { + strncpy(pThis->wifiConfig.clientParams.netmask, pair.value.c_str(), MAX_WIFI_NETMASK_LEN); + } + if(pair.name.compare("apNETMASK") == 0) + { + strncpy(pThis->wifiConfig.apParams.netmask, pair.value.c_str(), MAX_WIFI_NETMASK_LEN); + } + if(pair.name.compare("clientGATEWAY") == 0) + { + // Gateway isnt mandatory in client mode. + if(pair.value.size() > 0 && pThis->wifiConfig.params.wifiMode == WIFI_CONFIG_CLIENT) + { + strncpy(pThis->wifiConfig.clientParams.gateway, pair.value.c_str(), MAX_WIFI_GATEWAY_LEN); + } + } + if(pair.name.compare("apGATEWAY") == 0) + { + // Access point mode, if no gateway is given, assign the IP address as the gateway. + strncpy(pThis->wifiConfig.apParams.gateway, pThis->wifiConfig.apParams.ip, MAX_WIFI_GATEWAY_LEN+1); + } + } + + // Validate the data if no error was raised for individual fields. + if(dataError == false) + { + if(pThis->wifiConfig.params.wifiMode == WIFI_CONFIG_AP) + { + if(strlen(pThis->wifiConfig.apParams.ssid) == 0) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "SSID not given!"; + dataError = true; + } + if(strlen(pThis->wifiConfig.apParams.pwd) == 0) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Password not given!"; + dataError = true; + } + if(!pThis->validateIP(pThis->wifiConfig.apParams.ip)) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Illegal AP IP address(" + pThis->wifiConfig.apParams.ip + ")"; + dataError = true; + } + if(!pThis->validateIP(pThis->wifiConfig.apParams.netmask)) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Illegal AP Netmask address(" + pThis->wifiConfig.apParams.netmask + ")"; + dataError = true; + } + // Gateway isnt mandatory, but if filled in, validate it. + if(strlen(pThis->wifiConfig.apParams.gateway) == 0 || !pThis->validateIP(pThis->wifiConfig.apParams.gateway)) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Illegal AP Gateway address(" + pThis->wifiConfig.clientParams.gateway + ")"; + dataError = true; + } + } + // Only verify client parameters when active. + else if(pThis->wifiConfig.params.wifiMode == WIFI_CONFIG_CLIENT) + { + if(pThis->wifiConfig.clientParams.useDHCP == false) + { + if(strlen(pThis->wifiConfig.clientParams.ssid) == 0) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "SSID not given!"; + dataError = true; + } + if(strlen(pThis->wifiConfig.clientParams.pwd) == 0) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Password not given!"; + dataError = true; + } + if(!pThis->validateIP(pThis->wifiConfig.clientParams.ip)) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Illegal IP address(" + pThis->wifiConfig.clientParams.ip + ")"; + dataError = true; + } + if(!pThis->validateIP(pThis->wifiConfig.clientParams.netmask)) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Illegal Netmask address(" + pThis->wifiConfig.clientParams.netmask + ")"; + dataError = true; + } + // Gateway isnt mandatory, but if filled in, validate it. + if(strlen(pThis->wifiConfig.clientParams.gateway) > 0 && !pThis->validateIP(pThis->wifiConfig.clientParams.gateway)) + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Illegal Gateway address(" + pThis->wifiConfig.clientParams.gateway + ")"; + dataError = true; + } + } + } else + { + resp = resp + (resp.size() > 0 ? "," : ""); + resp = resp + "Unknown WiFi Mode (" + to_str(pThis->wifiConfig.params.wifiMode, 0, 10) + "), internal coding error, please contact support."; + dataError = true; + } + } + + // No errors, save wifi configuration. + if(dataError == false) + { + // Mark data as valid. + pThis->wifiConfig.clientParams.valid = true; + if(pThis->nvs->persistData(pThis->wifiCtrl.run.thisClass.c_str(), &pThis->wifiConfig, sizeof(t_wifiConfig)) == false) + { + ESP_LOGI(WIFITAG, "Persisting SharpKey(%s) configuration data failed, updates will not persist in future power cycles.", pThis->wifiCtrl.run.thisClass.c_str()); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(pThis->nvs->commitData() == false) + { + ESP_LOGI(WIFITAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + } + } + + return(dataError == false ? ESP_OK : ESP_FAIL); +} + +// Method to process POST data specifically for the Mouse interface. The key:value pairs are sent to the Mouse +// interface for parsing and storing, any errors are sent back to the UI/Browser. +esp_err_t WiFi::mouseDataPOSTHandler(httpd_req_t *req, std::vector pairs, std::string& resp) +{ + // Locals. + // + bool dataError = false; + KeyInterface *activeMouseIf = (mouseIf == NULL ? keyIf : mouseIf); + + // Run through pairs and send to the mouse interface to interpret. + resp = ""; + for(auto pair : pairs) + { + // Call the Mouse configuration handler tp validate and set the parameter. + dataError = activeMouseIf->setMouseConfigValue(pair.name, pair.value); + if(dataError) + { + resp.append("Variable:" + pair.name + " has an invalid value:" + pair.value); + dataError = false; + } + } + + // Update success status. + if(resp.size() > 0) dataError = true; + + // Persist the values if no errors occurred. + if(dataError == false) + { + dataError = activeMouseIf->persistConfig() ? false : true; + if(dataError) + { + resp.append("Save config to NVS RAM failed, retry, if 2nd attempt fails, power cycle the interface."); + } + } + + return(dataError == false ? ESP_OK : ESP_FAIL); +} + +// /data POST handler. Process the request and call service as required. +// +esp_err_t WiFi::defaultDataPOSTHandler(httpd_req_t *req) +{ + // Locals. + // + std::vector pairs; + esp_err_t ret = ESP_OK; + std::string resp = ""; + std::string uriStr; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Get the subpath from the URI. + if(pThis->getPathFromURI(uriStr, "/data/", req->uri) == ESP_FAIL) + { + // Respond with 500 Internal Server Error. + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to extract URI"); + return(ESP_FAIL); + } + + // Process the POST into key/value pairs. + ret = pThis->getPOSTData(req, &pairs); + if(ret == ESP_OK) + { + if(uriStr.compare("wifi") == 0) + { + if((ret = pThis->wifiDataPOSTHandler(req, pairs, resp)) == ESP_OK) + { + // Success so add reboot button. + pThis->wifiCtrl.run.rebootButton = true; + resp = "Data values accepted. Press 'Reboot' to initiate network connection with the new configuration."; + } + } + if(uriStr.compare("mouse") == 0) + { + if((ret = pThis->mouseDataPOSTHandler(req, pairs, resp)) == ESP_OK) + { + // Success so indicate all ok. + pThis->wifiCtrl.run.rebootButton = true; + resp = "Data values accepted. Press 'Reboot' to restart interface with new values."; + } + } + } else + { + resp = "

No values in POST, check browser!

"; + } + + // Add in an error message if one has been generated. + pThis->wifiCtrl.run.errorMsg = "wifiCtrl.run.errorMsg += (ret == ESP_OK ? "green" : "red"); + pThis->wifiCtrl.run.errorMsg += "\">" + resp + ""; + + // Send message directly, it will appear as an error or success message. + httpd_resp_send_chunk(req, pThis->wifiCtrl.run.errorMsg.c_str(), pThis->wifiCtrl.run.errorMsg.size()+1); + + // End response + httpd_resp_send_chunk(req, NULL, 0); + return(ret); +} + +// /reboot POST handler. +// Simple handler, send a message indicating reboot taking place with a reload URL statement. +// +esp_err_t WiFi::defaultRebootHandler(httpd_req_t *req) +{ + // Locals. + // + esp_err_t ret = ESP_OK; + std::string resp = ""; + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)req->user_ctx; + + // Build a response message. + if(pThis->wifiConfig.clientParams.useDHCP == false) + { + resp = " wifiConfig.clientParams.ip; + resp += "/\" /> Rebooting... Please wait."; + } else + { + resp = " Rebooting...

Please look in your router admin panel for the assigned IP address and enter http://<router assigned ip address> into browser to continue.

"; + } + + // Send the response and wait a while, then request reboot. + httpd_resp_send(req, resp.c_str(), resp.size()+1); + vTaskDelay(100); + pThis->wifiCtrl.run.reboot = true; + + // Get out, a reboot will occur very soon. + return(ret); +} + +// Method to start the basic HTTP webserver. +// +bool WiFi::startWebserver(void) +{ + // Locals. + // + bool result = false; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + + // Tweak default settings. + config.stack_size = 10240; + config.uri_match_fn = httpd_uri_match_wildcard; + config.lru_purge_enable = true; + config.max_uri_handlers = 12; + + // Setup the required paths and descriptors then register them with the server. + const httpd_uri_t dataPOST = { + .uri = "/data", + .method = HTTP_POST, + .handler = defaultDataPOSTHandler, + .user_ctx = this + }; + const httpd_uri_t dataSubPOST = { + .uri = "/data/*", + .method = HTTP_POST, + .handler = defaultDataPOSTHandler, + .user_ctx = this + }; + const httpd_uri_t dataGET = { + .uri = "/data", + .method = HTTP_GET, + .handler = defaultDataGETHandler, + .user_ctx = this + }; + const httpd_uri_t dataSubGET = { + .uri = "/data/*", + .method = HTTP_GET, + .handler = defaultDataGETHandler, + .user_ctx = this + }; + const httpd_uri_t keymapTablePOST = { + .uri = "/keymap/table", + .method = HTTP_POST, + .handler = keymapTablePOSTHandler, + .user_ctx = this + }; + const httpd_uri_t keymap = { + .uri = "/keymap", + .method = HTTP_POST, + .handler = keymapUploadPOSTHandler, + .user_ctx = this + }; + const httpd_uri_t otafw = { + .uri = "/ota/firmware", + .method = HTTP_POST, + .handler = otaFirmwareUpdatePOSTHandler, + .user_ctx = this + }; + const httpd_uri_t otafp = { + .uri = "/ota/filepack", + .method = HTTP_POST, + .handler = otaFilepackUpdatePOSTHandler, + .user_ctx = this + }; + const httpd_uri_t rebootPOST = { + .uri = "/reboot", + .method = HTTP_POST, + .handler = defaultRebootHandler, + .user_ctx = this + }; + const httpd_uri_t rebootGET = { + .uri = "/reboot", + .method = HTTP_GET, + .handler = defaultRebootHandler, + .user_ctx = this + }; + const httpd_uri_t root = { + .uri = "/", + .method = HTTP_GET, + .handler = defaultFileHandler, + .user_ctx = this + }; + // Catch all, assume files if no handler setup. + const httpd_uri_t files = { + .uri = "/*", + .method = HTTP_GET, + .handler = defaultFileHandler, + .user_ctx = this + }; + + // Store the file system basepath on t + strlcpy(this->wifiCtrl.run.basePath, this->wifiCtrl.run.fsPath, sizeof(this->wifiCtrl.run.basePath)); + + // Start the web server. + ESP_LOGI(WIFITAG, "Starting server on port: '%d'", config.server_port); + + if (httpd_start(&wifiCtrl.run.server, &config) == ESP_OK) + { + // Set URI handlers + ESP_LOGI(WIFITAG, "Registering URI handlers"); + + // Root directory handler. Equivalent to index.html/index.htm. The method, based on the current mode (AP/Client) decides on which + // file to serve. + httpd_register_uri_handler(wifiCtrl.run.server, &root); + + // POST handlers. + httpd_register_uri_handler(wifiCtrl.run.server, &dataSubPOST); + httpd_register_uri_handler(wifiCtrl.run.server, &dataPOST); + httpd_register_uri_handler(wifiCtrl.run.server, &dataSubGET); + httpd_register_uri_handler(wifiCtrl.run.server, &dataGET); + httpd_register_uri_handler(wifiCtrl.run.server, &keymapTablePOST); + httpd_register_uri_handler(wifiCtrl.run.server, &keymap); + httpd_register_uri_handler(wifiCtrl.run.server, &otafw); + httpd_register_uri_handler(wifiCtrl.run.server, &otafp); + httpd_register_uri_handler(wifiCtrl.run.server, &rebootPOST); + httpd_register_uri_handler(wifiCtrl.run.server, &rebootGET); + + // If no URL matches then default to serving files. + httpd_register_uri_handler(wifiCtrl.run.server, &files); + result = true; + } + + // Return result of startup. + return(result); +} + +// Method to stop the basic HTTP webserver. +// +void WiFi::stopWebserver(void) +{ + // Stop the web server and set the handle to NULL to indicate state. + httpd_stop(wifiCtrl.run.server); + wifiCtrl.run.server = NULL; + return; +} + +// Event handler for Client mode Wifi event callback. +// +IRAM_ATTR void WiFi::wifiClientHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + // Locals. + // + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)arg; + + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) + { + esp_wifi_connect(); + } + else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + if(pThis->wifiCtrl.client.clientRetryCnt < CONFIG_IF_WIFI_MAX_RETRIES) + { + esp_wifi_connect(); + pThis->wifiCtrl.client.clientRetryCnt++; + ESP_LOGI(WIFITAG, "retry to connect to the AP"); + } else + { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(WIFITAG,"connect to the AP fail"); + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + + // Copy details into control structure for ease of use in rendering pages. + strncpy(pThis->wifiCtrl.ap.ssid, pThis->wifiConfig.clientParams.ssid, MAX_WIFI_SSID_LEN+1); + strncpy(pThis->wifiCtrl.ap.pwd, pThis->wifiConfig.clientParams.pwd, MAX_WIFI_PWD_LEN+1); + sprintf(pThis->wifiCtrl.client.ip, IPSTR, IP2STR(&event->ip_info.ip)); + sprintf(pThis->wifiCtrl.client.netmask, IPSTR, IP2STR(&event->ip_info.netmask)); + sprintf(pThis->wifiCtrl.client.gateway, IPSTR, IP2STR(&event->ip_info.gw)); + pThis->wifiCtrl.client.connected = true; + pThis->wifiCtrl.client.clientRetryCnt = 0; + + ESP_LOGI(WIFITAG, "got ip:" IPSTR " Netmask:" IPSTR " Gateway:" IPSTR, IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.netmask), IP2STR(&event->ip_info.gw)); + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + + // Start the webserver if it hasnt been configured. + if(pThis->wifiCtrl.run.server == NULL) + { + pThis->startWebserver(); + } + } + return; +} + +// Event handler for Access Point mode Wifi event callback. +// +IRAM_ATTR void WiFi::wifiAPHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + // Locals. + // + + // Retrieve pointer to object in order to access data. + WiFi* pThis = (WiFi*)arg; + + if (event_id == WIFI_EVENT_AP_STACONNECTED) + { + wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; + ESP_LOGI(WIFITAG, "station " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid); + + // Start the webserver if it hasnt been configured. + if(pThis->wifiCtrl.run.server == NULL) + { + pThis->startWebserver(); + } + } + else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) + { + wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; + ESP_LOGI(WIFITAG, "station " MACSTR " leave, AID=%d", MAC2STR(event->mac), event->aid); + } + return; +} + +// Method to initialise the interface as a client to a known network, the SSID +// and password have already been setup. +// +bool WiFi::setupWifiClient(void) +{ + // Locals. + // + wifi_init_config_t wifiInitConfig = WIFI_INIT_CONFIG_DEFAULT(); + esp_netif_t *netConfig; + esp_netif_ip_info_t ipInfo; + esp_event_handler_instance_t instID; + esp_event_handler_instance_t instIP; + EventBits_t bits; + wifi_config_t wifiConfig = { .sta = { + /* ssid */ {}, + /* password */ {}, + /* scan_method */ {}, + /* bssid_set */ {}, + /* bssid */ {}, + /* channel */ {}, + /* listen_interval */ {}, + /* sort_method */ {}, + /* threshold */ { + /* rssi */ {}, + /* authmode */ WIFI_AUTH_WPA2_PSK + }, + /* pmf_cfg */ { + /* capable */ true, + /* required */ false + }, + /* rm_enabled */ {}, + /* btm_enabled */ {}, + /* mbo_enabled */ {}, // For IDF 4.4 and higher + /* reserved */ {} + } + }; + + // Add in configured SSID/Password parameters. + strncpy((char *)wifiConfig.sta.ssid, this->wifiConfig.clientParams.ssid, MAX_WIFI_SSID_LEN+1); + strncpy((char *)wifiConfig.sta.password, this->wifiConfig.clientParams.pwd, MAX_WIFI_PWD_LEN+1); + + //nvs_handle_t net80211_handle; + //nvs_open("nvs.net80211", NVS_READWRITE, &net80211_handle); + //nvs_erase_all(net80211_handle); + //nvs_commit(net80211_handle); + //nvs_close(net80211_handle); + + // Initialise control structure. + // + wifiCtrl.client.connected = false; + wifiCtrl.client.ip[0] = '\0'; + wifiCtrl.client.netmask[0] = '\0'; + wifiCtrl.client.gateway[0] = '\0'; + + // Create an event handler group to manage callbacks. + s_wifi_event_group = xEventGroupCreate(); + + // Setup the network interface. + if(esp_netif_init()) + { + ESP_LOGI(WIFITAG, "Couldnt initialise netif, disabling WiFi."); + return(false); + } + + // Setup the event loop. + if(esp_event_loop_create_default()) + { + ESP_LOGI(WIFITAG, "Couldnt initialise event loop, disabling WiFi."); + return(false); + } + + // Setup the wifi client (station). + netConfig = esp_netif_create_default_wifi_sta(); + // If fixed IP is configured, set it up. + if(!this->wifiConfig.clientParams.useDHCP) + { + int a, b, c, d; + esp_netif_dhcpc_stop(netConfig); + if(!splitIP(this->wifiConfig.clientParams.ip, &a, &b, &c, &d)) + { + ESP_LOGI(WIFITAG, "Client IP invalid:%s", this->wifiConfig.clientParams.ip); + return false; + } + IP4_ADDR(&ipInfo.ip, a, b, c, d); + + if(!splitIP(this->wifiConfig.clientParams.netmask, &a, &b, &c, &d)) + { + ESP_LOGI(WIFITAG, "Client NETMASK invalid:%s", this->wifiConfig.clientParams.netmask); + return false; + } + IP4_ADDR(&ipInfo.netmask, a, b, c, d); + + if(!splitIP(this->wifiConfig.clientParams.gateway, &a, &b, &c, &d)) + { + ESP_LOGI(WIFITAG, "Client GATEWAY invalid:%s", this->wifiConfig.clientParams.gateway); + return false; + } + IP4_ADDR(&ipInfo.gw, a, b, c, d); + esp_netif_set_ip_info(netConfig, &ipInfo); + } + + // Set TX power to max. + esp_wifi_set_max_tx_power(127); + + // Setup the config for wifi. + wifiInitConfig = WIFI_INIT_CONFIG_DEFAULT(); + if(esp_wifi_init(&wifiInitConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt initialise wifi with default parameters, disabling WiFi."); + return(false); + } + + // Register event handlers. + if(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifiClientHandler, this, &instID)) + { + ESP_LOGI(WIFITAG, "Couldnt register event handler for ID, disabling WiFi."); + return(false); + } + if(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifiClientHandler, this, &instIP)) + { + ESP_LOGI(WIFITAG, "Couldnt register event handler for IP, disabling WiFi."); + return(false); + } + if(esp_wifi_set_mode(WIFI_MODE_STA)) + { + ESP_LOGI(WIFITAG, "Couldnt set Wifi mode to Client, disabling WiFi."); + return(false); + } + if(esp_wifi_set_config(WIFI_IF_STA, &wifiConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt configure client mode, disabling WiFi."); + return(false); + } + if(esp_wifi_start()) + { + ESP_LOGI(WIFITAG, "Couldnt start Client session, disabling WiFi."); + return(false); + } + + // Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + // number of re-tries (WIFI_FAIL_BIT). The bits are set by wifiClientHandler() (see above) + bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + // xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + // happened. + if(bits & WIFI_CONNECTED_BIT) + { + ESP_LOGI(WIFITAG, "Connected: SSID:%s password:%s", this->wifiConfig.clientParams.ssid, this->wifiConfig.clientParams.pwd); + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(WIFITAG, "Connection Fail: SSID:%s, password:%s.", this->wifiConfig.clientParams.ssid, this->wifiConfig.clientParams.pwd); + return(false); + } + else + { + ESP_LOGE(WIFITAG, "Unknown evemt, bits:%d", bits); + return(false); + } + + // No errors. + return(true); +} + +// Method to initialise the interface as a Soft Access point with a given SSID +// and password. +// The Access Point mode is basically to bootstrap a Client connection where the +// client connecting provides the credentials in order to connect as a client to +// another AP to join a local network. +// +bool WiFi::setupWifiAP(void) +{ + // Locals. + // + esp_err_t retcode; + wifi_init_config_t wifiInitConfig; + esp_netif_t *wifiAP; + esp_netif_ip_info_t ipInfo; + wifi_config_t wifiConfig = { .ap = { + /* ssid */ CONFIG_IF_WIFI_SSID, + /* password */ CONFIG_IF_WIFI_DEFAULT_SSID_PWD, + /* ssid_len */ strlen(CONFIG_IF_WIFI_SSID), + /* channel */ CONFIG_IF_WIFI_AP_CHANNEL, + /* authmode */ WIFI_AUTH_WPA_WPA2_PSK, + /* hidden */ CONFIG_IF_WIFI_SSID_HIDDEN, + /* max_connection */ CONFIG_IF_WIFI_MAX_CONNECTIONS, + /* beacon_interval */ 100, + /* pairwise_cipher */ WIFI_CIPHER_TYPE_TKIP, + /* ftm_responder */ 0, + // /* pmf_cfg */ { + // /* capable */ true, + // /* required */ false + // } + } + }; + + // Intialise the network interface. + if(esp_netif_init()) + { + ESP_LOGI(WIFITAG, "Couldnt initialise network interface, disabling WiFi."); + return(false); + } + if((retcode = esp_event_loop_create_default())) + { + ESP_LOGI(WIFITAG, "Couldnt create default loop(%d), disabling WiFi.", retcode); + return(false); + } + + // Create the default Access Point. + // + wifiAP = esp_netif_create_default_wifi_ap(); + + // Setup the base parameters of the Access Point which may differ from ESP32 defaults. + int a, b, c, d; + if(!splitIP(this->wifiConfig.apParams.ip, &a, &b, &c, &d)) + { + ESP_LOGI(WIFITAG, "AP IP invalid:%s", this->wifiConfig.apParams.ip); + return false; + } + IP4_ADDR(&ipInfo.ip, a, b, c, d); + + if(!splitIP(this->wifiConfig.apParams.netmask, &a, &b, &c, &d)) + { + ESP_LOGI(WIFITAG, "AP NETMASK invalid:%s", this->wifiConfig.apParams.netmask); + return false; + } + IP4_ADDR(&ipInfo.netmask, a, b, c, d); + + if(!splitIP(this->wifiConfig.apParams.gateway, &a, &b, &c, &d)) + { + ESP_LOGI(WIFITAG, "AP GATEWAY invalid:%s", this->wifiConfig.apParams.gateway); + return false; + } + IP4_ADDR(&ipInfo.gw, a, b, c, d); + + // Update the SSID/Password from NVS. + strncpy((char *)wifiConfig.ap.ssid, this->wifiConfig.apParams.ssid, MAX_WIFI_SSID_LEN+1); + strncpy((char *)wifiConfig.ap.password, this->wifiConfig.apParams.pwd, MAX_WIFI_PWD_LEN+1); + wifiConfig.ap.ssid_len = (uint8_t)strlen(this->wifiConfig.apParams.ssid); + + // Copy the configured params into the runtime params, just in case they change prior to next boot. + // (this) used for clarity as wifi config local have similar names to global persistence names. + strncpy(this->wifiCtrl.ap.ssid, this->wifiConfig.apParams.ssid, MAX_WIFI_SSID_LEN+1); + strncpy(this->wifiCtrl.ap.pwd, this->wifiConfig.apParams.pwd, MAX_WIFI_PWD_LEN+1); + strncpy(this->wifiCtrl.ap.ip, this->wifiConfig.apParams.ip, MAX_WIFI_IP_LEN+1); + strncpy(this->wifiCtrl.ap.netmask, this->wifiConfig.apParams.netmask, MAX_WIFI_NETMASK_LEN+1); + strncpy(this->wifiCtrl.ap.gateway, this->wifiConfig.apParams.gateway, MAX_WIFI_GATEWAY_LEN+1); + + // Reconfigure the DHCP Server. + esp_netif_dhcps_stop(wifiAP); + esp_netif_set_ip_info(wifiAP, &ipInfo); + esp_netif_dhcps_start(wifiAP); + + // Set TX power to max. + esp_wifi_set_max_tx_power(127); + + // Initialise AP with default parameters. + wifiInitConfig = WIFI_INIT_CONFIG_DEFAULT(); + if(esp_wifi_init(&wifiInitConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt setup AP with default parameters, disabling WiFi."); + return(false); + } + + // Setup callback handlers for wifi events. + if(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifiAPHandler, this, NULL)) + { + ESP_LOGI(WIFITAG, "Couldnt setup event handlers, disabling WiFi."); + return(false); + } + + // If there is no password for the access point set authentication to open. + if (strlen(CONFIG_IF_WIFI_DEFAULT_SSID_PWD) == 0) + { + wifiConfig.ap.authmode = WIFI_AUTH_OPEN; + } + + // Setup as an Access Point. + if(esp_wifi_set_mode(WIFI_MODE_AP)) + { + ESP_LOGI(WIFITAG, "Couldnt set mode to Access Point, disabling WiFi."); + return(false); + } + // Configure the Access Point + if(esp_wifi_set_config(WIFI_IF_AP, &wifiConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt configure Access Point, disabling WiFi."); + return(false); + } + // Start the Access Point. + if(esp_wifi_start()) + { + ESP_LOGI(WIFITAG, "Couldnt start Access Point session, disabling WiFi."); + return(false); + } + + // No errors. + return(true); +} + +// Method to disable the wifi turning the transceiver off. +// +bool WiFi::stopWifi(void) +{ + if(esp_wifi_stop()) + { + ESP_LOGI(WIFITAG, "Couldnt stop the WiFi, reboot needed."); + return(false); + } + if(esp_wifi_deinit()) + { + ESP_LOGI(WIFITAG, "Couldnt deactivate WiFi, reboot needed."); + return(false); + } + + // No errors. + return(true); +} + +// WiFi interface runtime logic. This method provides a browser interface to the SharpKey for status query and configuration. +// +void WiFi::run(void) +{ + // Locals. + #define WIFIIFTAG "wifiRun" + + // If Access Point mode has been forced, set the config parameter to AP so that Access Point mode is entered regardless of NVS setting. + if(wifiCtrl.run.wifiMode == WIFI_CONFIG_AP) + { + wifiConfig.params.wifiMode = WIFI_CONFIG_AP; + + // Reset the configured addresses, SSID and password of the Access Point to factory default. This ensures known connection data. + strncpy(wifiConfig.apParams.ssid, CONFIG_IF_WIFI_SSID, MAX_WIFI_SSID_LEN); + strncpy(wifiConfig.apParams.pwd, CONFIG_IF_WIFI_DEFAULT_SSID_PWD, MAX_WIFI_PWD_LEN); + strncpy(wifiConfig.apParams.ip, WIFI_AP_DEFAULT_IP, MAX_WIFI_IP_LEN); + strncpy(wifiConfig.apParams.netmask, WIFI_AP_DEFAULT_NETMASK, MAX_WIFI_NETMASK_LEN); + strncpy(wifiConfig.apParams.gateway, WIFI_AP_DEFAULT_GW, MAX_WIFI_GATEWAY_LEN); + } + + // Enable Access Point mode if configured. + if(wifiConfig.params.wifiMode == WIFI_CONFIG_AP) + { + if(!setupWifiAP()) + { + wifiCtrl.run.reboot = true; + } else + { + wifiCtrl.run.wifiMode = wifiConfig.params.wifiMode; + + // Flash LED to indicate access point mode is active. + // + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 5, 10000L, 500L); + } + } else + { + // Setup as a client for general browser connectivity. + if(!setupWifiClient()) + { + wifiCtrl.run.reboot = true; + } else + { + wifiCtrl.run.wifiMode = wifiConfig.params.wifiMode; + + // Flash LED to indicate client mode is active. + // + led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_50, 5, 10000L, 500L); + } + } + + // Enter a loop, only exitting if a reboot is required. + do { + // Let other tasks run. NB. This value affects the debounce counter, update as necessary. + vTaskDelay(500); + } while(wifiCtrl.run.reboot == false); + + return; +} + +// Constructor. No overloading methods. +WiFi::WiFi(KeyInterface *hdlKeyIf, KeyInterface *hdlMouseIf, bool defaultMode, NVS *nvs, LED *led, const char *fsPath, t_versionList *versionList) +{ + // Initialise variables. + // + wifiCtrl.client.clientRetryCnt = 0; + wifiCtrl.run.server = NULL; + wifiCtrl.run.errorMsg = ""; + wifiCtrl.run.rebootButton = false; + wifiCtrl.run.reboot = false; + wifiCtrl.run.wifiMode = (defaultMode == true ? WIFI_CONFIG_AP : WIFI_ON); + + // The Non Volatile Storage object is bound to this object for storage and retrieval of configuration data. + this->nvs = nvs; + + // The LED activity indicator object. + this->led = led; + + // Setup the default path on the underlying filesystem. + this->wifiCtrl.run.fsPath = fsPath; + + // Store the version list, used in html variable expansion for version number reporting. + this->wifiCtrl.run.versionList = versionList; + + // Store the classname, used for NVS keys. + this->wifiCtrl.run.thisClass = keyIf->getClassName(__PRETTY_FUNCTION__); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(this->wifiCtrl.run.thisClass.c_str(), &this->wifiConfig, sizeof(t_wifiConfig)) == false) + { + ESP_LOGI(WIFITAG, "Wifi configuration set to default, no valid config in NVS found."); + // Empty set for the client parameters until configured. + wifiConfig.clientParams.valid = false; + wifiConfig.clientParams.ssid[0] = '\0'; + wifiConfig.clientParams.pwd[0] = '\0'; + wifiConfig.clientParams.ip[0] = '\0'; + wifiConfig.clientParams.netmask[0] = '\0'; + wifiConfig.clientParams.gateway[0] = '\0'; + strncpy(wifiConfig.apParams.ssid, CONFIG_IF_WIFI_SSID, MAX_WIFI_SSID_LEN); + strncpy(wifiConfig.apParams.pwd, CONFIG_IF_WIFI_DEFAULT_SSID_PWD, MAX_WIFI_PWD_LEN); + strncpy(wifiConfig.apParams.ip, WIFI_AP_DEFAULT_IP, MAX_WIFI_IP_LEN); + strncpy(wifiConfig.apParams.netmask, WIFI_AP_DEFAULT_NETMASK, MAX_WIFI_NETMASK_LEN); + strncpy(wifiConfig.apParams.gateway, WIFI_AP_DEFAULT_GW, MAX_WIFI_GATEWAY_LEN); + wifiConfig.params.wifiMode = WIFI_CONFIG_AP; + + // Persist the data for next time. + if(nvs->persistData(wifiCtrl.run.thisClass.c_str(), &this->wifiConfig, sizeof(t_wifiConfig)) == false) + { + ESP_LOGI(WIFITAG, "Persisting Default Wifi configuration data failed, check NVS setup."); + } + // Commit data, ensuring values are written to NVS and the mutex is released. + else if(nvs->commitData() == false) + { + ESP_LOGI(WIFITAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } + + // The interface objects are bound to this object so that configuration and rendering of web pages can take place. As the KeyInterface class knows about its + // data set and configuration requirements it is the only object which can render web pages for it. + // + keyIf = NULL; + mouseIf = NULL; + if(hdlKeyIf != NULL && hdlMouseIf == NULL) + { + this->keyIf = hdlKeyIf; + } + else if(hdlKeyIf == NULL && hdlMouseIf != NULL) + { + this->keyIf = mouseIf; + } + else if(hdlKeyIf != NULL && hdlMouseIf != NULL) + { + this->keyIf = hdlKeyIf; + this->mouseIf = hdlMouseIf; + } +} + +// Constructor, used for version reporting so no hardware is initialised. +WiFi::WiFi(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +WiFi::~WiFi(void) +{ + return; +} + +// End of compile time enabled build of the WiFi module. +#endif diff --git a/main/X1.cpp b/main/X1.cpp deleted file mode 120000 index e0c3ded..0000000 --- a/main/X1.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/X1.cpp \ No newline at end of file diff --git a/main/X1.cpp b/main/X1.cpp new file mode 100644 index 0000000..2c64f89 --- /dev/null +++ b/main/X1.cpp @@ -0,0 +1,1124 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: X1.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: HID (PS/2 or BT keyboard) to Sharp X1 Interface logic. +// This source file contains the singleton class containing logic to obtain +// PS/2 or BT scan codes, map them into Sharp X1 keys and transmit the key to the X1 host. +// +// The class uses a modified version of the PS2KeyAdvanced +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The whole application of which this class is a member, uses the Espressif Development +// environment with Arduino components. This is necessary for the PS2KeyAdvanced class, +// which I may in future convert to use esp-idf library calls rather than Arduino. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sys/stat.h" +#include "esp_littlefs.h" +#include "PS2KeyAdvanced.h" +#include "sdkconfig.h" +#include "X1.h" + +// Tag for ESP main application logging. +#define MAINTAG "x1key" + +// FreeRTOS Queue handle to pass messages from the HID Keyboard Mapper into the X1 transmission logic. +static QueueHandle_t xmitQueue; + +// X1 Protocol +// ----------- +// Mode A (general) - = 16bits per key. +// Mode B (game) - +// +// Mode A +// 16 bits: <8bit CTRL><8bit ASCII Code> +// +// : +// /TEN /KIN /REP /GRPH /CAPS /KANA /SFT /CTRL +// +// Parameter name value Parameter description +// /TEN 0 Input from the numeric keypad, function keys, and special keys +// 1 Normal key +// /KIN 0 Key Press +// 1 No key or key Release +// /REP 0 Repeat key input +// 1 First key input +// /GRPH 0 GRAPH key ON +// 1 GRAPH key OFF +// /CAPS 0 CAPS key ON +// 1 CAPS key OFF +// /KANA 0 Kana key ON +// 1 Kana key OFF +// /SFT 0 Shift key ON +// 1 Shift key OFF +// /CTRL 0 CTRL key ON +// 1 CTRL key OFF +// +// : +// ASCII code in range 00H - FFH where 00H is no key (used when sending control only updates). +// +// Mode B +// 24 bits: +// +// Mode B is intended for gaming and sends a subset of keys as direct bit representation. 24bits are transmitted on each key press/change and using a faster serial protocol so minimise lag. +// 7 6 5 4 3 2 1 0 +// BYTE1 : Q W E A D Z X C - Direct key press, 0 if pressed. +// BYTE2 : 7 4 1 8 2 9 6 3 +// BYTE3 : E 1 - + * H S R +// S T P E +// C A T +// B +// Keys which use the numeric keypad as well as normal keys: 1,2,3,4,6,7,8,9, *, +,- +// RET key is the main keyboard RET. + +// Function to push a keycode onto the key queue ready for transmission. +// +void X1::pushKeyToQueue(bool keybMode, uint32_t key) +{ + // Locals. + t_xmitQueueMessage xmitMsg; + #define PUSHKEYTAG "pushKeyToQueue" + + xmitMsg.modeB = keybMode; + xmitMsg.keyCode = key; + if( xQueueSend(xmitQueue, (void *)&xmitMsg, 10) != pdPASS) + { + ESP_LOGW(PUSHKEYTAG, "Failed to put scancode:%04x into xmitQueue", key); + } + return; +} + +// Method to realise the X1 1 wire serial protocol in order to transmit key presses to the X1. +// This method uses Core 1 and it will hold it in a spinlock as necessary to ensure accurate timing. +// A key is passed into the method via the FreeRTOS Queue handle xmitQueue. +IRAM_ATTR void X1::x1Interface( void * pvParameters ) +{ + // Locals. + t_xmitQueueMessage rcvMsg; + + // Mask values declared as variables, let the optimiser decide wether they are constants or placed in-memory. + uint32_t X1DATA_MASK = (1 << CONFIG_HOST_KDO0); + uint64_t delayTimer = 0LL; + uint64_t curTime = 0LL; + bool bitStart = true; + uint32_t bitCount = 0; + enum X1XMITSTATE { + FSM_IDLE = 0, + FSM_STARTXMIT = 1, + FSM_HEADER = 2, + FSM_START = 3, + FSM_DATA = 4, + FSM_STOP = 5, + FSM_ENDXMIT = 6 + } state = FSM_IDLE; + + // Retrieve pointer to object in order to access data. + X1* pThis = (X1*)pvParameters; + + // Initialise the MUTEX which prevents this core from being released to other tasks. + pThis->x1Mutex = portMUX_INITIALIZER_UNLOCKED; + + // Initial delay needed because the xQueue will assert probably on a suspended task ALL if delay not inserted! + vTaskDelay(1000); + + // Sign on. + ESP_LOGW(MAINTAG, "Starting X1 thread."); + + // X1 data out default state is high. + GPIO.out_w1ts = X1DATA_MASK; + + // Configure a timer to be used for X1 protocol spacing with 1uS resolution. The default clock source is the APB running at 80MHz. + timer_config_t timerConfig = { + .alarm_en = TIMER_ALARM_DIS, // No alarm, were not using interrupts as we are in a dedicated thread. + .counter_en = TIMER_PAUSE, // Timer paused until required. + .intr_type = TIMER_INTR_LEVEL, // No interrupts used. + .counter_dir = TIMER_COUNT_UP, // Timing a fixed period. + .auto_reload = TIMER_AUTORELOAD_DIS, // No need for auto reload, fixed time period. + .divider = 80 // 1Mhz operation giving 1uS resolution. + }; + ESP_ERROR_CHECK(timer_init(TIMER_GROUP_0, TIMER_0, &timerConfig)); + ESP_ERROR_CHECK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0)); + + // Permanent loop, wait for an incoming message on the key to send queue, read it then transmit to the X1, repeat! + for(;;) + { + // Get the current timer value, only run the FSM when the timer is idle. + timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &curTime); + if(curTime >= delayTimer) + { + // Ensure the timer is stopped. + timer_pause(TIMER_GROUP_0, TIMER_0); + delayTimer = 0LL; + + // Finite state machine to retrieve a key for transmission then serialise it according to the X1 protocol. + switch(state) + { + case FSM_IDLE: + // Yield if the suspend flag is set. + pThis->yield(0); + + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // If a new message arrives, start the serialiser to send it to the X1. + if(xQueueReceive(xmitQueue, (void *)&rcvMsg, 0) == pdTRUE) + { + ESP_LOGW(MAINTAG, "Received:%08x, %d", rcvMsg.keyCode, rcvMsg.modeB); + state = FSM_STARTXMIT; + + // Create, initialise and hold a spinlock so the current core is bound to this one method. + portENTER_CRITICAL(&pThis->x1Mutex); + } + break; + + case FSM_STARTXMIT: + // Ensure all variables and states correct before entering serialisation. + bitStart = true; + GPIO.out_w1ts = X1DATA_MASK; + state = FSM_HEADER; + if(rcvMsg.modeB) + bitCount = 24; + else + bitCount = 16; + break; + + case FSM_HEADER: + if(bitStart) + { + // Send out the header by bringing X1DATA low for 1000us then high for 700uS. + GPIO.out_w1tc = X1DATA_MASK; + delayTimer = pThis->x1Control.modeB ? 400LL : 1000LL; + } else + { + // Bring high for 700us. + GPIO.out_w1ts = X1DATA_MASK; + delayTimer = pThis->x1Control.modeB ? 200LL : 700LL; + state = FSM_DATA; // Jump past the Start Bit, I think the header is the actual start bit as there is an error in the X1 Center specs. + } + bitStart = !bitStart; + break; + + // The original X1 Center specification shows a start bit but this doesnt seem necessary, in fact it is interpreted as a data bit, hence the + // FSM jumps this state. + case FSM_START: + if(bitStart) + { + // Send out the start bit by bringing X1DATA low for 250us then high for 750uS. + GPIO.out_w1tc = X1DATA_MASK; + delayTimer = pThis->x1Control.modeB ? 250LL : 250LL; + } else + { + // Bring high for 750us. + GPIO.out_w1ts = X1DATA_MASK; + delayTimer = pThis->x1Control.modeB ? 250LL : 750LL; + state = FSM_DATA; + } + bitStart = !bitStart; + break; + + case FSM_DATA: + if(bitCount > 0) + { + if(bitStart) + { + // Send out the data bit by bringing X1DATA low for 250us then high for 1750uS when bit = 1 else 750uS when bit = 0. + GPIO.out_w1tc = X1DATA_MASK; + delayTimer = 250LL; + delayTimer = pThis->x1Control.modeB ? 250LL : 250LL; + } else + { + // Bring X1DATA high... + GPIO.out_w1ts = X1DATA_MASK; + + // ... Mode A 1750us as bit = 1, mode B 750uS. + if((rcvMsg.modeB && rcvMsg.keyCode & 0x800000) || (!rcvMsg.modeB && rcvMsg.keyCode & 0x8000)) + { + delayTimer = pThis->x1Control.modeB ? 750LL : 1750LL; + } else + // ... Mode A 750us as bit = 0, mode B 250uS. + { + delayTimer = pThis->x1Control.modeB ? 250LL : 750LL; + } + rcvMsg.keyCode = (rcvMsg.keyCode << 1); + bitCount--; + } + bitStart = !bitStart; + } else + { + state = FSM_STOP; + } + break; + + case FSM_STOP: + if(bitStart) + { + // Send out the stop bit, same in Mode A and B, by bringing X1DATA low for 250us then high for 250uS. + GPIO.out_w1tc = X1DATA_MASK; + delayTimer = 250LL; + delayTimer = pThis->x1Control.modeB ? 250LL : 250LL; + } else + { + // Bring high for 250us. + GPIO.out_w1ts = X1DATA_MASK; + delayTimer = pThis->x1Control.modeB ? 250LL : 250LL; + state = FSM_ENDXMIT; + } + bitStart = !bitStart; + break; + + case FSM_ENDXMIT: + // End of critical timing loop, release the core. + portEXIT_CRITICAL(&pThis->x1Mutex); + state = FSM_IDLE; + break; + + } + + // If a new delay is requested, set the value into the timer and start. + if(delayTimer > 0LL) + { + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0LL); + timer_start(TIMER_GROUP_0, TIMER_0); + } + } + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect + } +} + +// Method to select keyboard configuration options. When a key sequence is pressed, ie. SHIFT+CTRL+ESC then the fourth simultaneous key is the required option and given to this +// method to act on. Options can be machine model, keyboard map etc. +// +void X1::selectOption(uint8_t optionCode) +{ + // Locals. + // + bool updated = true; + #define SELOPTTAG "selectOption" + + // Simple switch to decode the required option and act on it. + switch(optionCode) + { + // Select a keymap using 1..8 or default (STANDARD) using 0. + case PS2_KEY_1: + this->x1Config.params.activeKeyboardMap = KEYMAP_UK_WYSE_KB3926; + break; + case PS2_KEY_2: + this->x1Config.params.activeKeyboardMap = KEYMAP_JAPAN_OADG109; + break; + case PS2_KEY_3: + this->x1Config.params.activeKeyboardMap = KEYMAP_JAPAN_SANWA_SKBL1; + break; + case PS2_KEY_4: + this->x1Config.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_4; + break; + case PS2_KEY_5: + this->x1Config.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_5; + break; + case PS2_KEY_6: + this->x1Config.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_6; + break; + case PS2_KEY_7: + this->x1Config.params.activeKeyboardMap = KEYMAP_UK_PERIBOARD_810; + break; + case PS2_KEY_8: + this->x1Config.params.activeKeyboardMap = KEYMAP_UK_OMOTON_K8508; + break; + case PS2_KEY_0: + this->x1Config.params.activeKeyboardMap = KEYMAP_STANDARD; + break; + + // Select the model of the host to enable specific mappings. + case PS2_KEY_END: + this->x1Config.params.activeMachineModel = X1_ORIG; + break; + case PS2_KEY_DN_ARROW: + this->x1Config.params.activeMachineModel = X1_TURBO; + break; + case PS2_KEY_PGDN: + this->x1Config.params.activeMachineModel = X1_TURBOZ; + break; + case PS2_KEY_INSERT: + this->x1Config.params.activeMachineModel = X1_ALL; + break; + + // Switch to keyboard Mode A. This mode is not persisted. + case PS2_KEY_HOME: + updated = false; + this->x1Control.modeB = false; + break; + // Switch to keyboard Mode B. This mode is not persisted. + case PS2_KEY_PGUP: + updated = false; + this->x1Control.modeB = true; + break; + + // Unknown option so ignore. + default: + updated = false; + break; + } + + // If an update was made, persist it for power cycles. + // + if(updated) + { + this->x1Control.persistConfig = true; + } + + return; +} + +// Method to take a PS/2 key and control data and map it into an X1 key and control equivalent, updating state values accordingly (ie. CAPS). +// A mapping table is used which maps a key and state values into an X1 key and control values, the emphasis being on readability and easy configuration +// as opposed to concatenated byte tables. +// +uint32_t X1::mapKey(uint16_t scanCode) +{ + // Locals. + uint32_t idx; + uint8_t keyCode = (scanCode & 0xFF); + bool mapped = false; + bool matchExact = false; + uint32_t mappedKey = 0x00000000; + #define MAPKEYTAG "mapKey" + + // Intercept control keys and set state variables. + // + // + if(scanCode & PS2_BREAK) + { + if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT) == 0) { mapped=true; this->x1Control.keyCtrl |= X1_CTRL_SHIFT; } + if((keyCode == PS2_KEY_L_CTRL || keyCode == PS2_KEY_R_CTRL) && (scanCode & PS2_CTRL) == 0) { mapped=true; this->x1Control.keyCtrl |= X1_CTRL_CTRL; } + if(keyCode == PS2_KEY_SCROLL) { mapped = true; this->x1Control.modeB = false; } + + // Any break key clears the option select flag. + this->x1Control.optionSelect = false; + + // Clear any feature LED blinking. + led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } else + { + if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT)) { mapped=true; this->x1Control.keyCtrl &= ~X1_CTRL_SHIFT; } + if((keyCode == PS2_KEY_L_CTRL || keyCode == PS2_KEY_R_CTRL) && (scanCode & PS2_CTRL)) { mapped=true; this->x1Control.keyCtrl &= ~X1_CTRL_CTRL; } + if(keyCode == PS2_KEY_L_ALT) { mapped = true; this->x1Control.keyCtrl ^= X1_CTRL_KANA; } + if(keyCode == PS2_KEY_R_ALT) { mapped = true; this->x1Control.keyCtrl ^= X1_CTRL_GRAPH; } + if(keyCode == PS2_KEY_CAPS) { mapped = true; this->x1Control.keyCtrl ^= X1_CTRL_CAPS; } + if(keyCode == PS2_KEY_SCROLL) { mapped = true; this->x1Control.modeB = true; } + // Special mapping to allow selection of keyboard options. If the user presses CTRL+SHIFT+ESC then a flag becomes active and should a fourth key be pressed before a BREAK then the fourth key is taken as an option key and processed accordingly. + if(this->x1Control.optionSelect == true) { mapped = true; this->x1Control.optionSelect = false; selectOption(keyCode); } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT)) { mapped = true; this->x1Control.optionSelect = true; } + if(this->x1Control.optionSelect == true && keyCode != PS2_KEY_ESC) + { + mapped = true; + this->x1Control.optionSelect = false; + selectOption(keyCode); + } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT) && this->x1Control.optionSelect == false) + { + // Prime flag ready for fourth option key and start LED blinking periodically. + mapped = true; + this->x1Control.optionSelect = true; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_50, 1, 500L, 500L); + } + } + + // If the key already mapped, ie. due to control signals, send the update as <0x00> so the X1 knows the current control signal state. + if(mapped == true) + { + ESP_LOGW(MAPKEYTAG, "Mapped special key:%02x\n", this->x1Control.keyCtrl); + mappedKey = (this->x1Control.keyCtrl << 8) | 0x00; + } else + { + // Loop through the entire conversion table to find a match on this key, if found map to X1 equivalent. + // switch matrix. + // + for(idx=0, mapped=false, matchExact=false; idx < x1Control.kmeRows && (mapped == false || (mapped == true && matchExact == false)); idx++) + { + // Match key code? Make sure the current machine and keymap match as well. + if(x1Control.kme[idx].ps2KeyCode == (uint8_t)(scanCode&0xFF) && ((x1Control.kme[idx].machine == X1_ALL) || ((x1Control.kme[idx].machine & x1Config.params.activeMachineModel) != 0)) && ((x1Control.kme[idx].keyboardModel & x1Config.params.activeKeyboardMap) != 0) && ((x1Control.kme[idx].x1Mode == X1_MODE_A && this->x1Control.modeB == false) || (x1Control.kme[idx].x1Mode == X1_MODE_B && this->x1Control.modeB == true))) + { + // If CAPS lock is set in the table and in the scanCode, invert SHIFT so we send the correct value. + if((scanCode & PS2_CAPS) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_CAPS) != 0) + { + scanCode ^= PS2_SHIFT; + } + + // Match Raw, Shift, Function, Control, ALT or ALT-Gr? + if( (((x1Control.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0) && ((x1Control.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0) && ((x1Control.kme[idx].ps2Ctrl & PS2CTRL_KANA) == 0) && ((x1Control.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) == 0) && ((x1Control.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0) && ((x1Control.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) || + ((scanCode & PS2_SHIFT) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || + ((scanCode & PS2_CTRL) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || + ((this->x1Control.keyCtrl & X1_CTRL_KANA) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_KANA) != 0) || + ((this->x1Control.keyCtrl & X1_CTRL_GRAPH) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) != 0) || + ((scanCode & PS2_GUI) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || + ((scanCode & PS2_FUNCTION) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) ) + { + + // Exact entry match, data + control key? On an exact match we only process the first key. On a data only match we fall through to include additional data and control key matches to allow for un-mapped key combinations, ie. Japanese characters. + matchExact = (((scanCode & PS2_SHIFT) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || ((scanCode & PS2_SHIFT) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0)) && + (((scanCode & PS2_CTRL) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || ((scanCode & PS2_CTRL) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0)) && + (((this->x1Control.keyCtrl & X1_CTRL_KANA) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_KANA) != 0) || ((this->x1Control.keyCtrl & X1_CTRL_KANA) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_KANA) == 0)) && + (((this->x1Control.keyCtrl & X1_CTRL_GRAPH) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) != 0) || ((this->x1Control.keyCtrl & X1_CTRL_GRAPH) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_GRAPH) == 0)) && + (((scanCode & PS2_GUI) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || ((scanCode & PS2_GUI) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0)) && + (((scanCode & PS2_FUNCTION) && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) || ((scanCode & PS2_FUNCTION) == 0 && (x1Control.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)); + + // RELEASE (PS2_BREAK == 1) or PRESS? + if((scanCode & PS2_BREAK)) + { + // Special case for the PAUSE / BREAK key. The underlying logic has been modified to send a BREAK key event immediately + // after a PAUSE make, this is necessary as the Sharp machines require SHIFT (pause) BREAK so the PS/2 CTRL+BREAK wont + // work (unless logic is added to insert a SHIFT, pause, add BREAK). The solution was to generate a BREAK event + // when SHIFT+PAUSE is pressed. + if(keyCode == PS2_KEY_PAUSE) + { + vTaskDelay(100); + } + + // Mode A sends a release with 0x00. + if(this->x1Control.modeB == false) + { + mappedKey = (0xFF << 8) | 0x00; + mapped = true; + // vTaskDelay(300); + } else + if(this->x1Control.modeB == true) + { + // Clear only the bits relevant to the released key. + mappedKey &= ((x1Control.kme[idx].x1Ctrl << 16) | (x1Control.kme[idx].x1Key2 << 8) | x1Control.kme[idx].x1Key); + } + } else + { + // Mode A return the key in the table, mode B OR the key to build up a final map. + if(this->x1Control.modeB == false) + mappedKey = ((x1Control.kme[idx].x1Ctrl & this->x1Control.keyCtrl) << 8) | x1Control.kme[idx].x1Key; + else + mappedKey |= ((x1Control.kme[idx].x1Ctrl << 16) | (x1Control.kme[idx].x1Key2 << 8) | x1Control.kme[idx].x1Key); + mapped = true; + //printf("%02x,%02x,%d,%d\n", (x1Control.kme[idx].x1Ctrl & this->x1Control.keyCtrl), x1Control.kme[idx].x1Key, idx,this->x1Control.modeB); + } + } + } + } + } + return(mappedKey); +} + +// Primary HID thread, running on Core 0. +// This thread is responsible for receiving HID (PS/2 or BT) keyboard scan codes and mapping them to Sharp X1 equivalent keys, updating state flags as needed. +// The HID data is received via interrupt. The data to be sent to the X1 is pushed onto a FIFO queue. +// +IRAM_ATTR void X1::hidInterface( void * pvParameters ) +{ + // Locals. + uint16_t scanCode = 0x0000; + uint32_t x1Key = 0x00000000; + + // Map the instantiating object so we can access its methods and data. + X1* pThis = (X1*)pvParameters; + + // Thread never exits, just polls the keyboard and updates the matrix. + while(1) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // Check for HID keyboard scan codes. + while((scanCode = pThis->hid->read()) != 0) + { + // Scan Code Breakdown: + // Define name bit description + // PS2_BREAK 15 1 = Break key code + // (MSB) 0 = Make Key code + // PS2_SHIFT 14 1 = Shift key pressed as well (either side) + // 0 = No shift key + // PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + // 0 = No Ctrl key + // PS2_CAPS 12 1 = Caps Lock ON + // 0 = Caps lock OFF + // PS2_ALT 11 1 = Left Alt key pressed as well + // 0 = No Left Alt key + // PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + // 0 = No Right Alt key + // PS2_GUI 9 1 = GUI key pressed as well (either) + // 0 = No GUI key + // PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + // 0 = standard character key + // 7-0 PS/2 Key code. + // + // BREAK code means all keys released so clear out flags and send update. + ESP_LOGW(MAPKEYTAG, "SCANCODE:%04x", scanCode); + + // Map the PS/2 key to an X1 CTRL + KEY + x1Key = pThis->mapKey(scanCode); + if(x1Key != 0L) { pThis->pushKeyToQueue(pThis->x1Control.modeB, x1Key); } + + // Toggle LED to indicate data flow. + if((scanCode & PS2_BREAK) == 0) + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 1, 100L, 0L); + } + + // NVS writes require both CPU cores to be free so write config out at a known junction. + if(pThis->x1Control.persistConfig == true) + { + // Request and wait for the interface to suspend. This ensures that the host cpu is not held in a spinlock when NVS update is requested avoiding deadlock. + pThis->suspendInterface(true); + pThis->isSuspended(true); + + if(pThis->nvs->persistData(pThis->getClassName(__PRETTY_FUNCTION__), &pThis->x1Config, sizeof(t_x1Config)) == false) + { + ESP_LOGW(SELOPTTAG, "Persisting X1 configuration data failed, updates will not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(pThis->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + } + + // Release interface. + pThis->suspendInterface(false); + + // Clear flag so we dont persist in a loop. + pThis->x1Control.persistConfig = false; + } + + // Yield if the suspend flag is set. + pThis->yield(10); + } +} + +// A method to load the keyboard mapping table into memory for use in the interface mapping logic. If no persistence file exists or an error reading persistence occurs, the keymap +// uses the internal static default. If no persistence file exists and attempt is made to create it with a copy of the inbuilt static map so that future operations all +// work with persistence such that modifications can be made. +// +bool X1::loadKeyMap(void) +{ + // Locals. + // + bool result = false; + int fileRows = 0; + struct stat keyMapFileNameStat; + + // See if the file exists, if it does, get size so we can compute number of mapping rows. + if(stat(x1Control.keyMapFileName.c_str(), &keyMapFileNameStat) == -1) + { + ESP_LOGW(MAINTAG, "No keymap file, using inbuilt definitions."); + } else + { + // Get number of rows in the file. + fileRows = keyMapFileNameStat.st_size/sizeof(t_keyMapEntry); + + // Subsequent reloads, delete memory prior to building new map, primarily to conserve precious resources rather than trying the memory allocation trying to realloc and then having to copy. + if(x1Control.kme != NULL && x1Control.kme != PS2toX1.kme) + { + delete x1Control.kme; + x1Control.kme = NULL; + } + + // Allocate memory for the new keymap table. + x1Control.kme = new t_keyMapEntry[fileRows]; + if(x1Control.kme == NULL) + { + ESP_LOGW(MAINTAG, "Failed to allocate memory for keyboard map, fallback to inbuilt!"); + } else + { + // Open the keymap extension file for binary reading to add data to our map table. + std::fstream keyFileIn(x1Control.keyMapFileName.c_str(), std::ios::in | std::ios::binary); + + int idx=0; + while(keyFileIn.good()) + { + keyFileIn.read((char *)&x1Control.kme[idx], sizeof(t_keyMapEntry)); + if(keyFileIn.good()) + { + idx++; + } + } + // Any errors, we wind back and use the inbuilt mapping table. + if(keyFileIn.bad()) + { + keyFileIn.close(); + ESP_LOGW(MAINTAG, "Failed to read data from keymap extension file:%s, fallback to inbuilt!", x1Control.keyMapFileName.c_str()); + } else + { + // No longer need the file. + keyFileIn.close(); + + // Max rows in the KME table. + x1Control.kmeRows = fileRows; + + // Good to go, map ready for use with the interface. + result = true; + } + } + } + + // Any failures, free up memory and use the inbuilt mapping table. + if(result == false) + { + if(x1Control.kme != NULL && x1Control.kme != PS2toX1.kme) + { + delete x1Control.kme; + x1Control.kme = NULL; + } + + // No point allocating memory if no extensions exist or an error occurs, just point to the static table. + x1Control.kme = PS2toX1.kme; + x1Control.kmeRows = PS2TBL_X1_MAXROWS; + + // Persist the data so that next load comes from file. + saveKeyMap(); + } + + // Return code. Either memory map was successfully loaded, true or failed, false. + return(result); +} + +// Method to save the current keymap out to an extension file. +// +bool X1::saveKeyMap(void) +{ + // Locals. + // + bool result = false; + int idx = 0; + + // Has a map been defined? Cannot save unless loadKeyMap has been called which sets x1Control.kme to point to the internal keymap or a new memory resident map. + // + if(x1Control.kme == NULL) + { + ESP_LOGW(MAINTAG, "KeyMap hasnt yet been defined, need to call loadKeyMap."); + } else + { + // Open file for binary writing, trunc specified to clear out the file, we arent appending. + std::fstream keyFileOut(x1Control.keyMapFileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + + // Loop whilst no errors and data rows still not written. + while(keyFileOut.good() && idx < x1Control.kmeRows) + { + keyFileOut.write((char *)&x1Control.kme[idx], sizeof(t_keyMapEntry)); + idx++; + } + if(keyFileOut.bad()) + { + ESP_LOGW(MAINTAG, "Failed to write data from the keymap to file:%s, deleting as state is unknown!", x1Control.keyMapFileName.c_str()); + keyFileOut.close(); + std::remove(x1Control.keyMapFileName.c_str()); + } else + { + // Success. + keyFileOut.close(); + result = true; + } + } + + // Return code. Either memory map was successfully saved, true or failed, false. + return(result); +} + +// Public method to open a keymap file for data upload. +// This method opens the file and makes any validation checks as necessary. +// +bool X1::createKeyMapFile(std::fstream &outFile) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Attempt to open a temporary keymap file for writing. + // + fileName = x1Control.keyMapFileName; + replaceExt(fileName, "tmp"); + outFile.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to validate and store data provided by caller into an open file created by 'createKeyMapFile'. +// +bool X1::storeDataToKeyMapFile(std::fstream &outFile, char *data, int size) +{ + // Locals. + // + bool result = true; + + // Check that the file is still writeable then add data. + if(outFile.good()) + { + outFile.write(data, size); + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Polymorphic alternative to take a vector of bytes for writing to the output file. +// +bool X1::storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray) +{ + // Locals. + // + bool result = true; + char data[1]; + + // Check that the file is still writeable then add data. Not best for performace but ease of use and minimum memory. + if(outFile.good()) + { + for(std::size_t idx = 0; idx < dataArray.size(); idx++) + { + data[0] = (char)dataArray[idx]; + outFile.write((char *)&data, 1); + } + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to close and commit a data file, created by 'createKeyMapFile' and populated by 'storeDataToKeyMapFile'. +// This involves renaming the original keymap file, closing the new file and renaming it to the original keymap filename. +// +bool X1::closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Check the file is still accessible and close. + // + outFile.close(); + if(!cleanupOnly) + { + if(outFile.good()) + { + // Rename the original file. + fileName = x1Control.keyMapFileName; + replaceExt(fileName, "bak"); + // Remove old backup file. Dont worry if it is not there! + std::remove(fileName.c_str()); + replaceExt(fileName, "tmp"); + // Rename new file to active. + if(std::rename(fileName.c_str(), x1Control.keyMapFileName.c_str()) != 0) + { + result = false; + } + } else + { + result = false; + } + } + + // Send result. + return(result); +} + +// Method to return the keymap column names as header strings. +// +void X1::getKeyMapHeaders(std::vector& headerList) +{ + // Add the names. + // + headerList.push_back(PS2TBL_PS2KEYCODE_NAME); + headerList.push_back(PS2TBL_PS2CTRL_NAME); + headerList.push_back(PS2TBL_KEYBOARDMODEL_NAME); + headerList.push_back(PS2TBL_MACHINE_NAME); + headerList.push_back(PS2TBL_X1MODE_NAME); + headerList.push_back(PS2TBL_X1KEYCODE_NAME); + headerList.push_back(PS2TBL_X1KEYCODE_BYTE2_NAME); + headerList.push_back(PS2TBL_X1_CTRL_NAME); + + return; +} + +// A method to return the Type of data for a given column in the KeyMap table. +// +void X1::getKeyMapTypes(std::vector& typeList) +{ + // Add the types. + // + typeList.push_back(PS2TBL_PS2KEYCODE_TYPE); + typeList.push_back(PS2TBL_PS2CTRL_TYPE); + typeList.push_back(PS2TBL_KEYBOARDMODEL_TYPE); + typeList.push_back(PS2TBL_MACHINE_TYPE); + typeList.push_back(PS2TBL_X1MODE_TYPE); + typeList.push_back(PS2TBL_X1KEYCODE_TYPE); + typeList.push_back(PS2TBL_X1KEYCODE_BYTE2_TYPE); + typeList.push_back(PS2TBL_X1CTRL_TYPE); + + return; +} + +// Method to return a list of key:value entries for a given keymap column. This represents the +// feature which can be selected and the value it uses. Features can be combined by ORing the values +// together. +bool X1::getKeyMapSelectList(std::vector>& selectList, std::string option) +{ + // Locals. + // + bool result = true; + + // Build up a map, depending on the list required, of name to value. This list can then be used + // by a user front end to select an option based on a name and return its value. + if(option.compare(PS2TBL_PS2CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_SHIFT, PS2CTRL_SHIFT)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CTRL, PS2CTRL_CTRL)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CAPS, PS2CTRL_CAPS)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_KANA, PS2CTRL_KANA)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GRAPH, PS2CTRL_GRAPH)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GUI, PS2CTRL_GUI)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_FUNC, PS2CTRL_FUNC)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_EXACT, PS2CTRL_EXACT)); + } + else if(option.compare(PS2TBL_KEYBOARDMODEL_TYPE) == 0) + { + selectList.push_back(std::make_pair(KEYMAP_SEL_STANDARD, KEYMAP_STANDARD)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_WYSE_KB3926, KEYMAP_UK_WYSE_KB3926)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_OADG109, KEYMAP_JAPAN_OADG109)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_SANWA_SKBL1, KEYMAP_JAPAN_SANWA_SKBL1)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_4, KEYMAP_NOT_ASSIGNED_4)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_5, KEYMAP_NOT_ASSIGNED_5)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_6, KEYMAP_NOT_ASSIGNED_6)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_PERIBOARD_810, KEYMAP_UK_PERIBOARD_810)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_OMOTON_K8508, KEYMAP_UK_OMOTON_K8508)); + } + else if(option.compare(PS2TBL_MACHINE_TYPE) == 0) + { + selectList.push_back(std::make_pair(X1_SEL_ALL, X1_ALL)); + selectList.push_back(std::make_pair(X1_SEL_ORIG, X1_ORIG)); + selectList.push_back(std::make_pair(X1_SEL_TURBO, X1_TURBO)); + selectList.push_back(std::make_pair(X1_SEL_TURBOZ, X1_TURBOZ)); + } + else if(option.compare(PS2TBL_X1MODE_TYPE) == 0) + { + selectList.push_back(std::make_pair(X1_SEL_MODE_A, X1_MODE_A)); + selectList.push_back(std::make_pair(X1_SEL_MODE_B, X1_MODE_B)); + } + else if(option.compare(PS2TBL_X1CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(X1_CTRL_SEL_TENKEY, X1_CTRL_TENKEY)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_PRESS, X1_CTRL_PRESS)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_REPEAT, X1_CTRL_REPEAT)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_GRAPH, X1_CTRL_GRAPH)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_CAPS, X1_CTRL_CAPS)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_KANA, X1_CTRL_KANA)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_SHIFT, X1_CTRL_SHIFT)); + selectList.push_back(std::make_pair(X1_CTRL_SEL_CTRL, X1_CTRL_CTRL)); + } else + { + // Not found! + result = false; + } + + // Return result, false if the option not found, true otherwise. + // + return(result); +} + + +// Method to read the Keymap array, 1 row at a time and return it to the caller. +// +bool X1::getKeyMapData(std::vector& dataArray, int *row, bool start) +{ + // Locals. + // + bool result = false; + + // If start flag is set, set row to 0. + if(start == true) + { + (*row) = 0; + } + + // Bound check and if still valid, push data onto the vector. + if((*row) >= x1Control.kmeRows) + { + result = true; + } else + { + dataArray.push_back(x1Control.kme[*row].ps2KeyCode); + dataArray.push_back(x1Control.kme[*row].ps2Ctrl); + dataArray.push_back(x1Control.kme[*row].keyboardModel); + dataArray.push_back(x1Control.kme[*row].machine); + dataArray.push_back(x1Control.kme[*row].x1Mode); + dataArray.push_back(x1Control.kme[*row].x1Key); + dataArray.push_back(x1Control.kme[*row].x1Key2); + dataArray.push_back(x1Control.kme[*row].x1Ctrl); + (*row) = (*row) + 1; + } + + // True if no more rows, false if additional rows can be read. + return(result); +} + + +// Initialisation routine. Start two threads, one to handle the incoming PS/2 keyboard data and map it, the second to handle the host interface. +void X1::init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Call the more basic initialisation. + init(hdlNVS, hdlHID); + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); + + // Create a task pinned to core 1 which will fulfill the Sharp X1 interface. This task has the highest priority + // and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means + // all other tasks running on Core 1 will suspend as needed. The PS/2 controller will be serviced with core 0. + // + // Core 1 - X1 Interface + ESP_LOGW(MAINTAG, "Starting x1if thread..."); + ::xTaskCreatePinnedToCore(&this->x1Interface, "x1if", 4096, this, 25, &this->TaskHostIF, 1); + vTaskDelay(500); + + // Core 0 - Application + // HID Interface handler thread. + ESP_LOGW(MAINTAG, "Starting hidIf thread..."); + ::xTaskCreatePinnedToCore(&this->hidInterface, "hidIf", 8192, this, 22, &this->TaskHIDIF, 0); + + // Create queue for buffering incoming keys prior to transmitting to the X1. + xmitQueue = xQueueCreate(MAX_X1_XMIT_KEY_BUF, sizeof(t_xmitQueueMessage)); +} + +// Initialisation routine without hardware. +void X1::init(NVS *hdlNVS, HID *hdlHID) +{ + // Initialise control variables. + this->x1Control.keyCtrl = 0xFF; // Negative logic, 0 - active, 1 = inactive. + x1Control.modeB = false; + x1Control.optionSelect = false; + x1Control.keyMapFileName = x1Control.fsPath.append("/").append(X1IF_KEYMAP_FILE); + x1Control.kmeRows = 0; + x1Control.kme = NULL; + x1Control.persistConfig = false; + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); + + // Load the keyboard mapping table into memory. If the file doesnt exist, create it. + loadKeyMap(); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(getClassName(__PRETTY_FUNCTION__), &this->x1Config, sizeof(t_x1Config)) == false) + { + ESP_LOGW(MAINTAG, "X1 configuration set to default, no valid config in NVS found."); + x1Config.params.activeKeyboardMap = KEYMAP_STANDARD; + x1Config.params.activeMachineModel = X1_ALL; + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->x1Config, sizeof(t_x1Config)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Default X1 configuration data failed, check NVS setup.\n"); + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(this->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } +} + +// Constructor, basically initialise the Singleton interface and let the threads loose. +X1::X1(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->x1Control.fsPath = fsPath; + + // Initialise the interface. + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, initialise the Singleton interface without hardware. +X1::X1(NVS *hdlNVS, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->x1Control.fsPath = fsPath; + + // Initialise the interface. + init(hdlNVS, hdlHID); +} + +// Constructor, used for version reporting so no hardware is initialised. +X1::X1(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +X1::~X1(void) +{ + return; +} diff --git a/main/X68K.cpp b/main/X68K.cpp deleted file mode 120000 index da1913b..0000000 --- a/main/X68K.cpp +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/X68K.cpp \ No newline at end of file diff --git a/main/X68K.cpp b/main/X68K.cpp new file mode 100644 index 0000000..e65ffd7 --- /dev/null +++ b/main/X68K.cpp @@ -0,0 +1,1067 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: X68K.cpp +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: HID (PS/2 or BT Keyboard) to Sharp X68000 Interface logic. +// This source file contains the singleton class containing logic to obtain +// PS/2 or BT scan codes, map them into Sharp X68000 keys and transmit the key to the X68000 +// host. +// +// The class uses a modified version of the PS2KeyAdvanced +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The whole application of which this class is a member, uses the Espressif Development +// environment with Arduino components. This is necessary for the PS2KeyAdvanced class, +// which I may in future convert to use esp-idf library calls rather than Arduino. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "sys/stat.h" +#include "esp_littlefs.h" +#include "PS2KeyAdvanced.h" +#include "sdkconfig.h" +#include "X68K.h" + +// Tag for ESP main application logging. +#define MAINTAG "x68kkey" + +// FreeRTOS Queue handles to pass messages from the HID Keyboard Mapper into the X68000 transmission logic +// and from the X68000 reception logic for later processing. +static QueueHandle_t xmitQueue; +static QueueHandle_t rcvQueue; + +// X68000 Protocol +// --------------- +// +// The X68000 uses an asynchronous serial protocol over two wires (RXD/TXD) with a READY state signal. +// +// Protocol: +// Idle State (RXD/TXD) = High. +// +// +// The READY signal is sent by the X68000 to the keyboard, 1 (High) = READY to accept key data, 0 (Low) = NOT READY to accept key data. +// +// DATA (KEYBOARD -> X68000): +// Bit [7] - Key MAKE (0), BREAK (1) +// Bit [6:0] - Scan Code. +// +// DATA (X68000 -> KEYBOARD): +// LED control: Set following LED's ON / OFF (0/1) +// bit [7] - Command specifier, set to "1" +// bit [6] - LED: full-width +// bit [5] - LED: Hiragana +// bit [4] - LED: INS +// bit [3] - LED: CAPS +// bit [2] - LED: Code input +// bit [1] - LED: Romaji +// bit [0] - LED: Kana +// +// Brightness Control +// bit [7:2] - Command specifier, set to "010101" +// bit [1:0] - 00 = LED's are full brightness +// 01 = slightly bright. +// 10 = slightly dark. +// 11 = dark. +// +// Set Repeat delay +// bit [7:4] - Command specifier, set to "0110" +// bit [3:0] - Delay period in formula, REPEAT DELAY = 200 + (int(bit[3:0])) * 100(ms)). Default delay = 500ms +// +// Set Repeat time +// bit [7:4] - Command specifier, set to "0111" +// bit [3:0] - Repeat time period in formula, REPEAT TIME = 30 + (((int(bit[3:0]))^2) * 5(ms)). Default repeat time = 110ms +// +// Scan Codes +// ---------- +// ,---. ,---. ,-------------------, ,-------------------. ,-----------. ,---------------. +// | 61| | 62| | 63| 64| 65| 66| 67| | 68| 69| 6A| 6B| 6C| | 5A| 5B| 5C| | 5D| 52| 53| 54| +// `---' `---' `-------------------' `-------------------' `-----------' `---------------' +// ,-----------------------------------------------------------. ,-----------. ,---------------. +// | 01| 02| 03| 04| 05| 06| 07| 08| 09| 0A| 0B| 0C| 0D| 0E| 0F| | 36| 5E| 37| | 3F| 40| 41| 42| +// |-----------------------------------------------------------| |------------ |---------------| +// | 10 | 11| 12| 13| 14| 15| 16| 17| 18| 19| 1A| 1B| 1C| | | 38| 39| 3A| | 43| 44| 45| 46| +// |------------------------------------------------------. 1D | `---=====---' |---------------| +// | 71 | 1E| 1F| 20| 21| 2l| 23| 24| 25| 26| 27| 28| 29| | ___| 3C|___ | 47| 48| 49| 4A| +// |-----------------------------------------------------------| | 3B|---| 3D| |-----------|---| +// | 70 | 2A| 2B| 2C| 2D| 2E| 2F| 30| 31| 32| 33| 34| 70 | `---| 3E|---' | 4B| 4C| 4D| | +// `-----------------------------------------------------------| .---=====---. |-----------| 4E| +// | 5F| 55 | 56 | 35 | 57 | 58 | 59 | 60| | 72 | 73 | | 4F| 50| 51| | +// `---------------------------------------------' `-----------' `---------------' +// +// ,---. ,---. ,-------------------, ,-------------------. ,-----------. ,---------------. +// |BRK| |CPY| | F1| F2| F3| F4| F5| | F6| F7| F8| F9|F10| | | | | |CAP| | |HLP| +// `---' `---' `-------------------' `-------------------' `-----------' `---------------' +// ,-----------------------------------------------------------. ,-----------. ,---------------. +// |ESC| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | ^ | Yn| BS| |H | | | | | | | | +// |-----------------------------------------------------------| |------------ |---------------| +// | | | | | | | | | | | | | | | | | | | | | | | | +// |------------------------------------------------------. | `---=====---' |---------------| +// | | | | | | l| | | | | | | | | ___| |___ | | | | | +// |-----------------------------------------------------------| | |---| | |-----------|---| +// | | | | | | | | | | | | | | `---| |---' | | | | | +// `-----------------------------------------------------------| .---=====---. |-----------| | +// | | | | | | | | | | | | | | | | | +// `---------------------------------------------' `-----------' `---------------' + + +// Function to push a keycode onto the key queue ready for transmission. +// +IRAM_ATTR void X68K::pushKeyToQueue(uint32_t key) +{ + // Locals. + t_xmitQueueMessage xmitMsg; + #define PUSHKEYTAG "pushKeyToQueue" + + xmitMsg.keyCode = key; + if( xQueueSend(xmitQueue, (void *)&xmitMsg, 10) != pdPASS) + { + ESP_LOGW(PUSHKEYTAG, "Failed to put scancode:%04x into xmitQueue", key); + } + return; +} + +// Function to push a host command onto the processing queue. +// +IRAM_ATTR void X68K::pushHostCmdToQueue(uint8_t cmd) +{ + // Locals. + t_rcvQueueMessage rcvMsg; + #define PUSHCMDTAG "pushHostCmdToQueue" + + rcvMsg.hostCmd = cmd; + if( xQueueSend(rcvQueue, (void *)&rcvMsg, 10) != pdPASS) + { + ESP_LOGW(PUSHCMDTAG, "Failed to put host command:%02x onto rcvQueue", cmd); + } + return; +} + +// Method to interface with the X68000. +// The X68000 uses a standard 2400 baud Asynchronous Serial protocol with READY state signal. +// Data to be sent is received on the event queue filled by the PS/2 interface. Data received is pushed +// to an event queue for processing by the relevant processor. +IRAM_ATTR void X68K::x68kInterface( void * pvParameters ) +{ + // Locals. + t_xmitQueueMessage rcvMsg; + uint8_t uartData[128]; + int uartXmitCnt; + size_t uartRcvCnt; + + // Retrieve pointer to object in order to access data. + X68K* pThis = (X68K*)pvParameters; + + // Initialise the MUTEX which prevents this core from being released to other tasks. + //pThis->x68kMutex = portMUX_INITIALIZER_UNLOCKED; + + // Initial delay needed because the xQueue will assert probably on a suspended task ALL if delay not inserted! + vTaskDelay(1000); + + // Sign on. + ESP_LOGW(MAINTAG, "Starting X68000 thread."); + + // Permanent loop, wait for an incoming message on the key to send queue, read it then transmit to the X68K, repeat! + for(;;) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + if(xQueueReceive(xmitQueue, (void *)&rcvMsg, 0) == pdTRUE) + { + //ESP_LOGW(MAINTAG, "Received:%08x\n", rcvMsg.keyCode); + + // Allow for multi byte transmissions, MSB sent first. + if(rcvMsg.keyCode != 0x00000000) + { + uartXmitCnt = 0; + while((rcvMsg.keyCode & 0xff000000) == 0x00) { rcvMsg.keyCode = rcvMsg.keyCode << 8; } + for(int idx=0; idx < 4 && (rcvMsg.keyCode & 0xff000000) != 0x00; idx++) + { + if((rcvMsg.keyCode & 0xff000000) != 0) + { + uartData[idx] = (uint8_t)((rcvMsg.keyCode & 0xFF000000) >> 24); + uartXmitCnt++; + } + rcvMsg.keyCode = rcvMsg.keyCode << 8; + } + if(uartXmitCnt > 0) + { + uart_write_bytes(pThis->x68kControl.uartNum, (const char *)uartData, uartXmitCnt); + } + } + } + + // Get data from X68000 - send any relevant commands for processing. + uart_get_buffered_data_len(pThis->x68kControl.uartNum, &uartRcvCnt); + if(uartRcvCnt > 0) + { + do { + uartRcvCnt = uart_read_bytes(pThis->x68kControl.uartNum, uartData, (128 - 1), 20 / portTICK_PERIOD_MS); + for(int idx=0; idx < uartRcvCnt; idx++) + { + // Filter out polling commands and send valid commands to the rcvQueue. + if(uartData[idx] != 0x40 && uartData[idx] != 0x41) + { + pThis->pushHostCmdToQueue(uartData[idx]); + } + } + } while(uartRcvCnt > 0); + } + + // Yield if the suspend flag is set. + pThis->yield(50); + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect + } +} + +// Method to select keyboard configuration options. When a key sequence is pressed, ie. SHIFT+CTRL+ESC then the fourth simultaneous key is the required option and given to this +// method to act on. Options can be machine model, keyboard map etc. +// +void X68K::selectOption(uint8_t optionCode) +{ + // Locals. + // + bool updated = true; + #define SELOPTTAG "selectOption" + + // Simple switch to decode the required option and act on it. + switch(optionCode) + { + // Select a keymap using 1..8 or default (STANDARD) using 0. + case PS2_KEY_1: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_UK_WYSE_KB3926; + break; + case PS2_KEY_2: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_JAPAN_OADG109; + break; + case PS2_KEY_3: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_JAPAN_SANWA_SKBL1; + break; + case PS2_KEY_4: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_4; + break; + case PS2_KEY_5: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_5; + break; + case PS2_KEY_6: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_NOT_ASSIGNED_6; + break; + case PS2_KEY_7: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_UK_PERIBOARD_810; + break; + case PS2_KEY_8: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_UK_OMOTON_K8508; + break; + case PS2_KEY_0: + this->x68kConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + break; + case PS2_KEY_END: + this->x68kConfig.params.activeMachineModel = X68K_ORIG; + break; + case PS2_KEY_DN_ARROW: + this->x68kConfig.params.activeMachineModel = X68K_ACE; + break; + case PS2_KEY_PGDN: + this->x68kConfig.params.activeMachineModel = X68K_EXPERT; + break; + case PS2_KEY_L_ARROW: + this->x68kConfig.params.activeMachineModel = X68K_PRO; + break; + case PS2_KEY_KP5: + this->x68kConfig.params.activeMachineModel = X68K_SUPER; + break; + case PS2_KEY_R_ARROW: + this->x68kConfig.params.activeMachineModel = X68K_XVI; + break; + case PS2_KEY_HOME: + this->x68kConfig.params.activeMachineModel = X68K_COMPACT; + break; + case PS2_KEY_UP_ARROW: + this->x68kConfig.params.activeMachineModel = X68K_X68030; + break; + case PS2_KEY_INSERT: + this->x68kConfig.params.activeMachineModel = X68K_ALL; + break; + + // Unknown option so ignore. + default: + updated = false; + break; + } + + // If an update was made, persist it for power cycles. + // + if(updated) + { + this->x68kControl.persistConfig = true; + } + + return; +} + +// Method to take a PS/2 key and control data and map it into an X68000 key and control equivalent, updating state values accordingly (ie. CAPS). +// A mapping table is used which maps a key and state values into an X68000 key and control values, the emphasis being on readability and easy configuration +// as opposed to concatenated byte tables. +// +uint32_t X68K::mapKey(uint16_t scanCode) +{ + // Locals. + uint32_t idx; + uint8_t keyCode = (scanCode & 0xFF); + bool mapped = false; + bool matchExact = false; + uint32_t mappedKey = 0x00000000; + #define MAPKEYTAG "mapKey" + + // Intercept control keys and set state variables. + // + // + if(scanCode & PS2_BREAK) + { + // if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT) == 0) { mapped=true; this->x68kControl.keyCtrl |= X68KCTRL_SHIFT; } + if(keyCode == PS2_KEY_R_CTRL && (scanCode & PS2_CTRL) == 0) { mapped=true; this->x68kControl.keyCtrl &= ~X68K_CTRL_R_CTRL; } + + // Any break key clears the option select flag. + this->x68kControl.optionSelect = false; + + // Clear any feature LED blinking. + led->setLEDMode(LED::LED_MODE_OFF, LED::LED_DUTY_CYCLE_OFF, 0, 0L, 0L); + } else + { + // if((keyCode == PS2_KEY_L_SHIFT || keyCode == PS2_KEY_R_SHIFT) && (scanCode & PS2_SHIFT)) { mapped=true; this->x68kControl.keyCtrl &= ~X68KCTRL_SHIFT; } + if(keyCode == PS2_KEY_R_CTRL && (scanCode & PS2_CTRL)) { mapped=true; this->x68kControl.keyCtrl |= X68K_CTRL_R_CTRL; } + // Special mapping to allow selection of keyboard options. If the user presses CTRL+SHIFT+ESC then a flag becomes active and should a fourth key be pressed before a BREAK then the fourth key is taken as an option key and processed accordingly. + if(this->x68kControl.optionSelect == true && keyCode != PS2_KEY_ESC) + { + mapped = true; + this->x68kControl.optionSelect = false; + selectOption(keyCode); + } + if(keyCode == PS2_KEY_ESC && (scanCode & PS2_CTRL) && (scanCode & PS2_SHIFT) && this->x68kControl.optionSelect == false) + { + // Prime flag ready for fourth option key and start LED blinking periodically. + mapped = true; + this->x68kControl.optionSelect = true; + led->setLEDMode(LED::LED_MODE_BLINK, LED::LED_DUTY_CYCLE_50, 1, 500L, 500L); + } + } + + // If the key has been mapped as a special key, no further processing. + if(mapped == true) + { + ESP_LOGW(MAPKEYTAG, "Mapped special key:%02x\n", this->x68kControl.keyCtrl); + // mappedKey = (this->x68kControl.keyCtrl << 8) | 0x00; + } else + { + // Loop through the entire conversion table to find a match on this key, if found map to X68000 equivalent. + // switch matrix. + // + for(idx=0, mapped=false, matchExact=false; idx < x68kControl.kmeRows && (mapped == false || (mapped == true && matchExact == false)); idx++) + { + // Match key code? Make sure the current machine and keymap match as well. + if(x68kControl.kme[idx].ps2KeyCode == (uint8_t)(scanCode&0xFF) && ((x68kControl.kme[idx].machine == X68K_ALL) || ((x68kControl.kme[idx].machine & x68kConfig.params.activeMachineModel) != 0)) && ((x68kControl.kme[idx].keyboardModel & x68kConfig.params.activeKeyboardMap) != 0)) + { + // Match Raw, Shift, Function, Control, ALT or ALT-Gr? + if( (((x68kControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0) && ((x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0) && ((x68kControl.kme[idx].ps2Ctrl & PS2CTRL_R_CTRL) == 0) && ((x68kControl.kme[idx].ps2Ctrl & PS2CTRL_ALTGR) == 0) && ((x68kControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0) && ((x68kControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) || + ((scanCode & PS2_SHIFT) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || + ((scanCode & PS2_CTRL) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || + ((scanCode & PS2_GUI) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || + ((this->x68kControl.keyCtrl & X68K_CTRL_R_CTRL) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_R_CTRL)!= 0) || + // ((scanCode & PS2_CAPS) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) != 0) || + ((scanCode & PS2_FUNCTION) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) ) + { + + // Exact entry match, data + control key? On an exact match we only process the first key. On a data only match we fall through to include additional data and control key matches to allow for un-mapped key combinations, ie. Japanese characters. + matchExact = (((scanCode & PS2_SHIFT) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) != 0) || ((scanCode & PS2_SHIFT) == 0 && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_SHIFT) == 0)) && + (((scanCode & PS2_CTRL) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) != 0) || ((scanCode & PS2_CTRL) == 0 && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CTRL) == 0)) && + (((scanCode & PS2_GUI) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) != 0) || ((scanCode & PS2_GUI) == 0 && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_GUI) == 0)) && + (((this->x68kControl.keyCtrl & X68K_CTRL_R_CTRL) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_R_CTRL)!= 0) || ((this->x68kControl.keyCtrl & X68K_CTRL_R_CTRL) == 0 && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_R_CTRL)== 0)) && + // (((scanCode & PS2_CAPS) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) != 0) || ((scanCode & PS2_GUI) == 0 && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_CAPS) == 0)) && + (((scanCode & PS2_FUNCTION) && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) != 0) || ((scanCode & PS2_FUNCTION) == 0 && (x68kControl.kme[idx].ps2Ctrl & PS2CTRL_FUNC) == 0)) + ? true : false; + + // RELEASE (PS2_BREAK == 1) or PRESS? + if((scanCode & PS2_BREAK)) + { + // Special case for the PAUSE / BREAK key. The underlying logic has been modified to send a BREAK key event immediately + // after a PAUSE make, this is necessary as the Sharp machines require SHIFT (pause) BREAK so the PS/2 CTRL+BREAK wont + // work (unless logic is added to insert a SHIFT, pause, add BREAK). The solution was to generate a BREAK event + // when SHIFT+PAUSE is pressed. + if(keyCode == PS2_KEY_PAUSE) + { + vTaskDelay(100); + } + mappedKey = 0x80 | (x68kControl.kme[idx].x68kKey & 0x7F); + mapped = true; + } else + { + // Map key actioning any control overrides. + if((x68kControl.kme[idx].x68kCtrl & X68K_CTRL_RELEASESHIFT) != 0) + { + // RELEASESHIFT infers that the X68000 must cancel the current shift status prior to receiving the key code. This is necessary when using foreign keyboards and a character appears + // on a shifted key whereas on the original X68000 keyboard the character is the primary key. + // + mappedKey = ((0x80 | X68K_KEY_SHIFT) << 16) | 0x00 | ((x68kControl.kme[idx].x68kKey & 0x7F) << 8) | (0x00 | X68K_KEY_SHIFT); + } else + if((x68kControl.kme[idx].x68kCtrl & X68K_CTRL_SHIFT) != 0) + { + // SHIFT infers that the X68000 must invoke shift status prior to receiving the key code. This is necessary when using foreign keyboards and a character appears + // as a primary key on the foreign keyboard but as a shifted key on the X68000 keyboard. + // + mappedKey = ((0x00 | X68K_KEY_SHIFT) << 16) | 0x00 | ((x68kControl.kme[idx].x68kKey & 0x7F) << 8) | (0x80 | X68K_KEY_SHIFT); + } + else + { + mappedKey = 0x00 | (x68kControl.kme[idx].x68kKey & 0x7F); + } + mapped = true; + } + } + } + } + } + return(mappedKey); +} + +// Primary HID thread, running on Core 0. +// This thread is responsible for receiving HID (PS/2 or BT) keyboard scan codes and mapping them to Sharp X68000 equivalent keys, updating state flags as needed. +// The HID data is received via interrupt. The data to be sent to the X68000 is pushed onto a FIFO queue. +// +IRAM_ATTR void X68K::hidInterface( void * pvParameters ) +{ + // Locals. + uint16_t scanCode = 0x0000; + uint32_t x68kKey = 0x00000000; + t_rcvQueueMessage rcvMsg; + + // Map the instantiating object so we can access its methods and data. + X68K* pThis = (X68K*)pvParameters; + + // Thread never exits, just polls the keyboard and updates the matrix. + while(1) + { + // Check stack space, report if it is getting low. + if(uxTaskGetStackHighWaterMark(NULL) < 1024) + { + ESP_LOGW(MAINTAG, "THREAD STACK SPACE(%d)\n",uxTaskGetStackHighWaterMark(NULL)); + } + + // Check for HID keyboard scan codes. + while((scanCode = pThis->hid->read()) != 0) + { + // Scan Code Breakdown: + // Define name bit description + // PS2_BREAK 15 1 = Break key code + // (MSB) 0 = Make Key code + // PS2_SHIFT 14 1 = Shift key pressed as well (either side) + // 0 = No shift key + // PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + // 0 = No Ctrl key + // PS2_CAPS 12 1 = Caps Lock ON + // 0 = Caps lock OFF + // PS2_ALT 11 1 = Left Alt key pressed as well + // 0 = No Left Alt key + // PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + // 0 = No Right Alt key + // PS2_GUI 9 1 = GUI key pressed as well (either) + // 0 = No GUI key + // PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + // 0 = standard character key + // 7-0 PS/2 Key code. + // + // BREAK code means all keys released so clear out flags and send update. + ESP_LOGW(MAPKEYTAG, "SCANCODE:%04x",scanCode); + + // Map the PS/2 key to an X68000 CTRL + KEY + x68kKey = pThis->mapKey(scanCode); + if(x68kKey != 0L) + { + pThis->pushKeyToQueue(x68kKey); + } + + // Toggle LED to indicate data flow. + if((scanCode & PS2_BREAK) == 0) + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 1, 100L, 0L); + } + + // Check for incoming host keyboard commands and execute them. + if(xQueueReceive(rcvQueue, (void *)&rcvMsg, 0) == pdTRUE) + { + ESP_LOGD(MAINTAG, "Received Host Cmd:%02x\n", rcvMsg.hostCmd); + } + + // NVS writes require both CPU cores to be free so write config out at a known junction. + if(pThis->x68kControl.persistConfig == true) + { + // Request and wait for the interface to suspend. This ensures that the host cpu is not held in a spinlock when NVS update is requested avoiding deadlock. + pThis->suspendInterface(true); + pThis->isSuspended(true); + + if(pThis->nvs->persistData(pThis->getClassName(__PRETTY_FUNCTION__), &pThis->x68kConfig, sizeof(t_x68kConfig)) == false) + { + ESP_LOGW(SELOPTTAG, "Persisting X68000 configuration data failed, updates will not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 1000L, 0L); + } else + // Few other updates so make a commit here to ensure data is flushed and written. + if(pThis->nvs->commitData() == false) + { + ESP_LOGW(SELOPTTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + pThis->led->setLEDMode(LED::LED_MODE_BLINK_ONESHOT, LED::LED_DUTY_CYCLE_10, 200, 500L, 0L); + } + + // Release interface. + pThis->suspendInterface(false); + + // Clear flag so we dont persist in a loop. + pThis->x68kControl.persistConfig = false; + } + + // Yield if the suspend flag is set. + pThis->yield(25); + } +} + +// A method to load the keyboard mapping table into memory for use in the interface mapping logic. If no persistence file exists or an error reading persistence occurs, the keymap +// uses the internal static default. If no persistence file exists and attempt is made to create it with a copy of the inbuilt static map so that future operations all +// work with persistence such that modifications can be made. +// +bool X68K::loadKeyMap(void) +{ + // Locals. + // + bool result = false; + int fileRows = 0; + struct stat keyMapFileNameStat; + + // See if the file exists, if it does, get size so we can compute number of mapping rows. + if(stat(x68kControl.keyMapFileName.c_str(), &keyMapFileNameStat) == -1) + { + ESP_LOGW(MAINTAG, "No keymap file, using inbuilt definitions."); + } else + { + // Get number of rows in the file. + fileRows = keyMapFileNameStat.st_size/sizeof(t_keyMapEntry); + + // Subsequent reloads, delete memory prior to building new map, primarily to conserve precious resources rather than trying the memory allocation trying to realloc and then having to copy. + if(x68kControl.kme != NULL && x68kControl.kme != PS2toX68K.kme) + { + delete x68kControl.kme; + x68kControl.kme = NULL; + } + + // Allocate memory for the new keymap table. + x68kControl.kme = new t_keyMapEntry[fileRows]; + if(x68kControl.kme == NULL) + { + ESP_LOGW(MAINTAG, "Failed to allocate memory for keyboard map, fallback to inbuilt!"); + } else + { + // Open the keymap extension file for binary reading to add data to our map table. + std::fstream keyFileIn(x68kControl.keyMapFileName.c_str(), std::ios::in | std::ios::binary); + + int idx=0; + while(keyFileIn.good()) + { + keyFileIn.read((char *)&x68kControl.kme[idx], sizeof(t_keyMapEntry)); + if(keyFileIn.good()) + { + idx++; + } + } + // Any errors, we wind back and use the inbuilt mapping table. + if(keyFileIn.bad()) + { + keyFileIn.close(); + ESP_LOGW(MAINTAG, "Failed to read data from keymap extension file:%s, fallback to inbuilt!", x68kControl.keyMapFileName.c_str()); + } else + { + // No longer need the file. + keyFileIn.close(); + + // Max rows in the KME table. + x68kControl.kmeRows = fileRows; + + // Good to go, map ready for use with the interface. + result = true; + } + } + } + + // Any failures, free up memory and use the inbuilt mapping table. + if(result == false) + { + if(x68kControl.kme != NULL && x68kControl.kme != PS2toX68K.kme) + { + delete x68kControl.kme; + x68kControl.kme = NULL; + } + + // No point allocating memory if no extensions exist or an error occurs, just point to the static table. + x68kControl.kme = PS2toX68K.kme; + x68kControl.kmeRows = PS2TBL_X68K_MAXROWS; + + // Persist the data so that next load comes from file. + saveKeyMap(); + } + + // Return code. Either memory map was successfully loaded, true or failed, false. + return(result); +} + +// Method to save the current keymap out to an extension file. +// +bool X68K::saveKeyMap(void) +{ + // Locals. + // + bool result = false; + int idx = 0; + + // Has a map been defined? Cannot save unless loadKeyMap has been called which sets x68kControl.kme to point to the internal keymap or a new memory resident map. + // + if(x68kControl.kme == NULL) + { + ESP_LOGW(MAINTAG, "KeyMap hasnt yet been defined, need to call loadKeyMap."); + } else + { + // Open file for binary writing, trunc specified to clear out the file, we arent appending. + std::fstream keyFileOut(x68kControl.keyMapFileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + + // Loop whilst no errors and data rows still not written. + while(keyFileOut.good() && idx < x68kControl.kmeRows) + { + keyFileOut.write((char *)&x68kControl.kme[idx], sizeof(t_keyMapEntry)); + idx++; + } + if(keyFileOut.bad()) + { + ESP_LOGW(MAINTAG, "Failed to write data from the keymap to file:%s, deleting as state is unknown!", x68kControl.keyMapFileName.c_str()); + keyFileOut.close(); + std::remove(x68kControl.keyMapFileName.c_str()); + } else + { + // Success. + keyFileOut.close(); + result = true; + } + } + + // Return code. Either memory map was successfully saved, true or failed, false. + return(result); +} + +// Public method to open a keymap file for data upload. +// This method opens the file and makes any validation checks as necessary. +// +bool X68K::createKeyMapFile(std::fstream &outFile) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Attempt to open a temporary keymap file for writing. + // + fileName = x68kControl.keyMapFileName; + replaceExt(fileName, "tmp"); + outFile.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to validate and store data provided by caller into an open file created by 'createKeyMapFile'. +// +bool X68K::storeDataToKeyMapFile(std::fstream &outFile, char *data, int size) +{ + // Locals. + // + bool result = true; + + // Check that the file is still writeable then add data. + if(outFile.good()) + { + outFile.write(data, size); + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Polymorphic alternative to take a vector of bytes for writing to the output file. +// +bool X68K::storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray) +{ + // Locals. + // + bool result = true; + char data[1]; + + // Check that the file is still writeable then add data. Not best for performace but ease of use and minimum memory. + if(outFile.good()) + { + for(std::size_t idx = 0; idx < dataArray.size(); idx++) + { + data[0] = (char)dataArray[idx]; + outFile.write((char *)&data, 1); + } + } + if(outFile.bad()) + { + result = false; + } + + // Send result. + return(result); +} + +// Public method to close and commit a data file, created by 'createKeyMapFile' and populated by 'storeDataToKeyMapFile'. +// This involves renaming the original keymap file, closing the new file and renaming it to the original keymap filename. +// +bool X68K::closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly) +{ + // Locals. + // + bool result = true; + std::string fileName; + + // Check the file is still accessible and close. + // + outFile.close(); + if(!cleanupOnly) + { + if(outFile.good()) + { + // Rename the original file. + fileName = x68kControl.keyMapFileName; + replaceExt(fileName, "bak"); + // Remove old backup file. Dont worry if it is not there! + std::remove(fileName.c_str()); + replaceExt(fileName, "tmp"); + // Rename new file to active. + if(std::rename(fileName.c_str(), x68kControl.keyMapFileName.c_str()) != 0) + { + result = false; + } + } else + { + result = false; + } + } + + // Send result. + return(result); +} + +// Method to return the keymap column names as header strings. +// +void X68K::getKeyMapHeaders(std::vector& headerList) +{ + // Add the names. + // + headerList.push_back(PS2TBL_PS2KEYCODE_NAME); + headerList.push_back(PS2TBL_PS2CTRL_NAME); + headerList.push_back(PS2TBL_KEYBOARDMODEL_NAME); + headerList.push_back(PS2TBL_MACHINE_NAME); + headerList.push_back(PS2TBL_X68KKEYCODE_NAME); + headerList.push_back(PS2TBL_X68KCTRL_NAME); + + return; +} + +// A method to return the Type of data for a given column in the KeyMap table. +// +void X68K::getKeyMapTypes(std::vector& typeList) +{ + // Add the types. + // + typeList.push_back(PS2TBL_PS2KEYCODE_TYPE); + typeList.push_back(PS2TBL_PS2CTRL_TYPE); + typeList.push_back(PS2TBL_KEYBOARDMODEL_TYPE); + typeList.push_back(PS2TBL_MACHINE_TYPE); + typeList.push_back(PS2TBL_X68KKEYCODE_TYPE); + typeList.push_back(PS2TBL_X68KCTRL_TYPE); + + return; +} + +// Method to return a list of key:value entries for a given keymap column. This represents the +// feature which can be selected and the value it uses. Features can be combined by ORing the values +// together. +bool X68K::getKeyMapSelectList(std::vector>& selectList, std::string option) +{ + // Locals. + // + bool result = true; + + // Build up a map, depending on the list required, of name to value. This list can then be used + // by a user front end to select an option based on a name and return its value. + if(option.compare(PS2TBL_PS2CTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_SHIFT, PS2CTRL_SHIFT)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CTRL, PS2CTRL_CTRL)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_CAPS, PS2CTRL_CAPS)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_R_CTRL, PS2CTRL_R_CTRL)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_ALTGR, PS2CTRL_ALTGR)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_GUI, PS2CTRL_GUI)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_FUNC, PS2CTRL_FUNC)); + selectList.push_back(std::make_pair(PS2TBL_PS2CTRL_SEL_EXACT, PS2CTRL_EXACT)); + } + else if(option.compare(PS2TBL_KEYBOARDMODEL_TYPE) == 0) + { + selectList.push_back(std::make_pair(KEYMAP_SEL_STANDARD, KEYMAP_STANDARD)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_WYSE_KB3926, KEYMAP_UK_WYSE_KB3926)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_OADG109, KEYMAP_JAPAN_OADG109)); + selectList.push_back(std::make_pair(KEYMAP_SEL_JAPAN_SANWA_SKBL1, KEYMAP_JAPAN_SANWA_SKBL1)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_4, KEYMAP_NOT_ASSIGNED_4)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_5, KEYMAP_NOT_ASSIGNED_5)); + selectList.push_back(std::make_pair(KEYMAP_SEL_NOT_ASSIGNED_6, KEYMAP_NOT_ASSIGNED_6)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_PERIBOARD_810, KEYMAP_UK_PERIBOARD_810)); + selectList.push_back(std::make_pair(KEYMAP_SEL_UK_OMOTON_K8508, KEYMAP_UK_OMOTON_K8508)); + } + else if(option.compare(PS2TBL_MACHINE_TYPE) == 0) + { + selectList.push_back(std::make_pair(X68K_SEL_ALL, X68K_ALL)); + selectList.push_back(std::make_pair(X68K_SEL_ORIG, X68K_ORIG)); + selectList.push_back(std::make_pair(X68K_SEL_ACE, X68K_ACE)); + selectList.push_back(std::make_pair(X68K_SEL_EXPERT, X68K_EXPERT)); + selectList.push_back(std::make_pair(X68K_SEL_PRO, X68K_PRO)); + selectList.push_back(std::make_pair(X68K_SEL_SUPER, X68K_SUPER)); + selectList.push_back(std::make_pair(X68K_SEL_XVI, X68K_XVI)); + selectList.push_back(std::make_pair(X68K_SEL_COMPACT, X68K_COMPACT)); + selectList.push_back(std::make_pair(X68K_SEL_X68030, X68K_X68030)); + } + else if(option.compare(PS2TBL_X68KCTRL_TYPE) == 0) + { + selectList.push_back(std::make_pair(X68K_CTRL_SEL_SHIFT, X68K_CTRL_SHIFT)); + selectList.push_back(std::make_pair(X68K_CTRL_SEL_RELEASESHIFT, X68K_CTRL_RELEASESHIFT)); + selectList.push_back(std::make_pair(X68K_CTRL_SEL_R_CTRL, X68K_CTRL_R_CTRL)); + } else + { + // Not found! + result = false; + } + + // Return result, false if the option not found, true otherwise. + // + return(result); +} + + +// Method to read the Keymap array, 1 row at a time and return it to the caller. +// +bool X68K::getKeyMapData(std::vector& dataArray, int *row, bool start) +{ + // Locals. + // + bool result = false; + + // If start flag is set, set row to 0. + if(start == true) + { + (*row) = 0; + } + + // Bound check and if still valid, push data onto the vector. + if((*row) >= x68kControl.kmeRows) + { + result = true; + } else + { + dataArray.push_back(x68kControl.kme[*row].ps2KeyCode); + dataArray.push_back(x68kControl.kme[*row].ps2Ctrl); + dataArray.push_back(x68kControl.kme[*row].keyboardModel); + dataArray.push_back(x68kControl.kme[*row].machine); + dataArray.push_back(x68kControl.kme[*row].x68kKey); + dataArray.push_back(x68kControl.kme[*row].x68kCtrl); + (*row) = (*row) + 1; + } + + // True if no more rows, false if additional rows can be read. + return(result); +} + + +// Initialisation routine. Start two threads, one to handle the incoming PS/2 keyboard data and map it, the second to handle the host interface. +void X68K::init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) +{ + // Basic initialisation. + init(hdlNVS, hdlHID); + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); + + // Prepare the UART to be used for communications with the X68000. + // The X68000 uses Asynchronous protocol with 1 stop bit no parity 2400 baud. + // + uart_config_t uartConfig = { + .baud_rate = 2400, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 122, + .source_clk = UART_SCLK_APB, + }; + + // Install UART driver. Use RX/TX buffers without event queue. + ESP_ERROR_CHECK(uart_driver_install(x68kControl.uartNum, x68kControl.uartBufferSize, x68kControl.uartBufferSize, 0, NULL, 0)); + + // Configure UART parameters and pin assignments, software flow control, not RTS/CTS. + ESP_ERROR_CHECK(uart_param_config(x68kControl.uartNum, &uartConfig)); + ESP_ERROR_CHECK(uart_set_pin(x68kControl.uartNum, CONFIG_HOST_KDB0, CONFIG_HOST_KDB1, -1, -1)); + + // Create queue for buffering incoming HID keys prior to transmitting to the X68000. + xmitQueue = xQueueCreate(MAX_X68K_XMIT_KEY_BUF, sizeof(t_xmitQueueMessage)); + // Create queue for buffering incoming X68000 data for later processing. + rcvQueue = xQueueCreate(MAX_X68K_RCV_KEY_BUF, sizeof(t_rcvQueueMessage)); + + // Create a task pinned to core 1 which will fulfill the Sharp X68000 interface. This task has the highest priority + // and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means + // all other tasks running on Core 1 will suspend as needed. The HID devices will be serviced with core 0. + // + // Core 1 - X68000 Interface + ESP_LOGW(MAINTAG, "Starting x68kif thread..."); + ::xTaskCreatePinnedToCore(&this->x68kInterface, "x68kif", 4096, this, 25, &this->TaskHostIF, 1); + vTaskDelay(500); + + // Core 0 - Application + // HID Interface handler thread. + ESP_LOGW(MAINTAG, "Starting hidIf thread..."); + ::xTaskCreatePinnedToCore(&this->hidInterface, "hidIf", 8192, this, 22, &this->TaskHIDIF, 0); +} + +// Initialisation routine without hardware. +void X68K::init(NVS *hdlNVS, HID *hdlHID) +{ + // Initialise control variables. + this->x68kControl.keyCtrl = 0x00; + x68kControl.optionSelect = false; + x68kControl.uartNum = UART_NUM_2; + x68kControl.uartBufferSize = 256; + x68kControl.uartQueueSize = 10; + x68kControl.keyMapFileName = x68kControl.fsPath.append("/").append(X68KIF_KEYMAP_FILE); + x68kControl.kmeRows = 0; + x68kControl.kme = NULL; + x68kControl.persistConfig = false; + + // Invoke the prototype init which initialises common variables and devices shared by all subclass. + KeyInterface::init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); + + // Load the keyboard mapping table into memory. If the file doesnt exist, create it. + loadKeyMap(); + + // Retrieve configuration, if it doesnt exist, set defaults. + // + if(nvs->retrieveData(getClassName(__PRETTY_FUNCTION__), &this->x68kConfig, sizeof(t_x68kConfig)) == false) + { + ESP_LOGW(MAINTAG, "X68000 configuration set to default, no valid config in NVS found."); + x68kConfig.params.activeKeyboardMap = KEYMAP_STANDARD; + x68kConfig.params.activeMachineModel = X68K_ALL; + + // Persist the data for next time. + if(nvs->persistData(getClassName(__PRETTY_FUNCTION__), &this->x68kConfig, sizeof(t_x68kConfig)) == false) + { + ESP_LOGW(MAINTAG, "Persisting Default X68000 configuration data failed, check NVS setup.\n"); + } + // Few other updates so make a commit here to ensure data is flushed and written. + else if(this->nvs->commitData() == false) + { + ESP_LOGW(MAINTAG, "NVS Commit writes operation failed, some previous writes may not persist in future power cycles."); + } + } +} + +// Constructor, basically initialise the Singleton interface and let the threads loose. +X68K::X68K(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->x68kControl.fsPath = fsPath; + + // Initialise the interface. + init(ifMode, hdlNVS, hdlLED, hdlHID); +} + +// Constructor, basic initialisation without hardware. +X68K::X68K(NVS *hdlNVS, HID *hdlHID, const char* fsPath) +{ + // Setup the default path on the underlying filesystem. + this->x68kControl.fsPath = fsPath; + + // Initialise the interface. + init(hdlNVS, hdlHID); +} + +// Constructor, used for version reporting so no hardware is initialised. +X68K::X68K(void) +{ + return; +} + +// Destructor - only ever called when the class is used for version reporting. +X68K::~X68K(void) +{ + return; +} diff --git a/main/component.mk b/main/component.mk deleted file mode 120000 index 9678c69..0000000 --- a/main/component.mk +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/main/component.mk \ No newline at end of file diff --git a/main/component.mk b/main/component.mk new file mode 100644 index 0000000..61f8990 --- /dev/null +++ b/main/component.mk @@ -0,0 +1,8 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/main/include/BT.h b/main/include/BT.h deleted file mode 120000 index 1ffaf30..0000000 --- a/main/include/BT.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/BT.h \ No newline at end of file diff --git a/main/include/BT.h b/main/include/BT.h new file mode 100644 index 0000000..5156351 --- /dev/null +++ b/main/include/BT.h @@ -0,0 +1,222 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: BT.h +// Created: Jan 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header file for the Bluetooth Class. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef BT_H_ +#define BT_H_ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_bt.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_hidh.h" +#include "esp_hid_common.h" +#include "esp_gap_bt_api.h" +#include "esp_gap_ble_api.h" + +// Bluetooth interface class. Provides Mouse and Keyboard functionality via the Bluetooth wireless interface. +class BT { + #define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(*a)) + + public: + typedef void t_pairingHandler(uint32_t code, uint8_t trigger); + + // Structure to contain details of a single device forming a scanned device list. + // + typedef struct { + esp_bd_addr_t bda; + std::string name; + int8_t rssi; + esp_hid_usage_t usage; + esp_hid_transport_t transport; //BT, BLE or USB + + union { + struct { + esp_bt_cod_t cod; + esp_bt_uuid_t uuid; + } bt; + struct { + esp_ble_addr_type_t addr_type; + uint16_t appearance; + } ble; + }; + + // Display format values. + std::string deviceAddr; // MAC address of the Bluetooth device. + std::string deviceType; // BT, BLE or USB + } t_scanListItem; + + + // Prototypes. + BT(void); + virtual ~BT(void); + void getDeviceList(std::vector &scanList, int waitTime); + bool setup(t_pairingHandler *handler = nullptr); + + inline uint8_t getBatteryLevel() { return btCtrl.batteryLevel; } + inline void setBatteryLevel(uint8_t level) { btCtrl.batteryLevel = level; } + + private: + static constexpr char const *TAG = "BT"; + #ifdef CONFIG_CLASSIC_BT_ENABLED + const char *gap_bt_prop_type_names[5] = { "", "BDNAME", "COD", "RSSI", "EIR" }; + const char *bt_gap_evt_names[10] = { "DISC_RES", "DISC_STATE_CHANGED", "RMT_SRVCS", "RMT_SRVC_REC", "AUTH_CMPL", "PIN_REQ", "CFM_REQ", "KEY_NOTIF", "KEY_REQ", "READ_RSSI_DELTA" }; + #endif + const char *ble_gap_evt_names[28] = { "ADV_DATA_SET_COMPLETE", "SCAN_RSP_DATA_SET_COMPLETE", "SCAN_PARAM_SET_COMPLETE", "SCAN_RESULT", "ADV_DATA_RAW_SET_COMPLETE", + "SCAN_RSP_DATA_RAW_SET_COMPLETE", "ADV_START_COMPLETE", "SCAN_START_COMPLETE", "AUTH_CMPL", "KEY", + "SEC_REQ", "PASSKEY_NOTIF", "PASSKEY_REQ", "OOB_REQ", "LOCAL_IR", + "LOCAL_ER", "NC_REQ", "ADV_STOP_COMPLETE", "SCAN_STOP_COMPLETE", "SET_STATIC_RAND_ADDR", + "UPDATE_CONN_PARAMS", "SET_PKT_LENGTH_COMPLETE", "SET_LOCAL_PRIVACY_COMPLETE", "REMOVE_BOND_DEV_COMPLETE", "CLEAR_BOND_DEV_COMPLETE", + "GET_BOND_DEV_COMPLETE", "READ_RSSI_COMPLETE", "UPDATE_WHITELIST_COMPLETE" }; + const char *ble_addr_type_names[4] = { "PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM" }; + + // Define possible HIDH host modes. + static const esp_bt_mode_t HIDH_IDLE_MODE = (esp_bt_mode_t) 0x00; + static const esp_bt_mode_t HIDH_BLE_MODE = (esp_bt_mode_t) 0x01; + static const esp_bt_mode_t HIDH_BT_MODE = (esp_bt_mode_t) 0x02; + static const esp_bt_mode_t HIDH_BTDM_MODE = (esp_bt_mode_t) 0x03; + + // Structure to maintain control variables. + typedef struct { + #ifdef CONFIG_CLASSIC_BT_ENABLED + std::vector btScanList; + #endif + std::vector bleScanList; + + t_pairingHandler *pairingHandler; + esp_hidh_dev_t *hidhDevHdl; + + int8_t batteryLevel; + + #ifdef CONFIG_CLASSIC_BT_ENABLED + xSemaphoreHandle bt_hidh_cb_semaphore; + #endif + xSemaphoreHandle ble_hidh_cb_semaphore; + + BT *pThis; + } t_btCtrl; + + // All control variables are stored in a struct for ease of reference. + t_btCtrl btCtrl; + + + // Prototypes. + static void processBTGapEvent(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t * param); + static void processBLEGapEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t * param); + t_scanListItem* findValidScannedDevice(esp_bd_addr_t bda, std::vector &scanList); + void processBLEDeviceScanResult(esp_ble_gap_cb_param_t * scan_rst); + void addBLEScanDevice(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi); + + #ifdef CONFIG_CLASSIC_BT_ENABLED + void processBTDeviceScanResult(esp_bt_gap_cb_param_t * param); + void addBTScanDevice(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi); + #endif + esp_err_t scanForBLEDevices(uint32_t timeout); + esp_err_t scanForBTDevices(uint32_t timeout); + esp_err_t scanForAllDevices(uint32_t timeout, size_t *noDevices, std::vector &scanList); + void printUUID(esp_bt_uuid_t * uuid); + + const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type) + { + if (ble_addr_type > BLE_ADDR_TYPE_RPA_RANDOM) + { + return "UNKNOWN"; + } + return ble_addr_type_names[ble_addr_type]; + } + + const char *ble_gap_evt_str(uint8_t event) + { + if (event >= SIZEOF_ARRAY(ble_gap_evt_names)) + { + return "UNKNOWN"; + } + return ble_gap_evt_names[event]; + } + + #ifdef CONFIG_CLASSIC_BT_ENABLED + const char *bt_gap_evt_str(uint8_t event) + { + if (event >= SIZEOF_ARRAY(bt_gap_evt_names)) + { + return "UNKNOWN"; + } + return bt_gap_evt_names[event]; + } + #endif + + const char *ble_key_type_str(esp_ble_key_type_t key_type) + { + const char *key_str = nullptr; + switch (key_type) + { + case ESP_LE_KEY_NONE: + key_str = "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = "INVALID BLE KEY TYPE"; + break; + } + + return key_str; + } +}; + +#endif // BT_H_ diff --git a/main/include/BTHID.h b/main/include/BTHID.h deleted file mode 120000 index 431f68d..0000000 --- a/main/include/BTHID.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/BTHID.h \ No newline at end of file diff --git a/main/include/BTHID.h b/main/include/BTHID.h new file mode 100644 index 0000000..917bceb --- /dev/null +++ b/main/include/BTHID.h @@ -0,0 +1,701 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: BTHID.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header file for the Bluetooth Keyboard Class. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// Jun 2022 - Updated with latest findings. Now checks the bonded list and opens +// connections or scans for new devices if no connections exist. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef BT_KEYBOARD_H_ +#define BT_KEYBOARD_H_ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_bt.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_hidh.h" +#include "esp_hid_common.h" +#include "esp_gap_bt_api.h" +#include "esp_gap_ble_api.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "BT.h" + +// Keyboard is a sub-class of BT which provides methods to setup BT for use by a keyboard. +class BTHID : public BT { + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Global constants + #define MAX_KEYBOARD_DATA_BYTES 8 + #define MAX_CCONTROL_DATA_BYTES 3 + #define MAX_MOUSE_DATA_BYTES 7 + #define MAX_BT2PS2_MAP_ENTRIES 179 + #define MAX_BTMEDIA2PS2_MAP_ENTRIES 8 + + // LED's + #define BT_LED_NUMLOCK 0x01 + #define BT_LED_CAPSLOCK 0x02 + #define BT_LED_SCROLLLOCK 0x04 + + // Control keys. + #define BT_NONE 0x0000 + #define BT_CTRL_LEFT 0x0001 + #define BT_SHIFT_LEFT 0x0002 + #define BT_ALT_LEFT 0x0004 + #define BT_GUI_LEFT 0x0008 + #define BT_CTRL_RIGHT 0x0010 + #define BT_SHIFT_RIGHT 0x0020 + #define BT_ALT_RIGHT 0x0040 + #define BT_GUI_RIGHT 0x0080 + #define BT_CAPS_LOCK 0x0100 + #define BT_NUM_LOCK 0x0200 + #define BT_SCROLL_LOCK 0x0400 + #define BT_DUPLICATE 0xFFFF // Duplicate BT flags onto PS/2 flags. + + #define BT_PS2_FUNCTION 0x01 + #define BT_PS2_GUI 0x02 + #define BT_PS2_ALT_GR 0x04 + #define BT_PS2_ALT 0x08 + #define BT_PS2_CAPS 0x10 + #define BT_PS2_CTRL 0x20 + #define BT_PS2_SHIFT 0x40 + #define BT_PS2_BREAK 0x80 + + #define BT_KEY_NONE 0x00 // No key pressed + #define BT_KEY_ERR_OVF 0x01 // Keyboard Error Roll Over + // 0x02 // Keyboard POST Fail + // 0x03 // Keyboard Error Undefined + #define BT_KEY_A 0x04 // Keyboard a and A + #define BT_KEY_B 0x05 // Keyboard b and B + #define BT_KEY_C 0x06 // Keyboard c and C + #define BT_KEY_D 0x07 // Keyboard d and D + #define BT_KEY_E 0x08 // Keyboard e and E + #define BT_KEY_F 0x09 // Keyboard f and F + #define BT_KEY_G 0x0a // Keyboard g and G + #define BT_KEY_H 0x0b // Keyboard h and H + #define BT_KEY_I 0x0c // Keyboard i and I + #define BT_KEY_J 0x0d // Keyboard j and J + #define BT_KEY_K 0x0e // Keyboard k and K + #define BT_KEY_L 0x0f // Keyboard l and L + #define BT_KEY_M 0x10 // Keyboard m and M + #define BT_KEY_N 0x11 // Keyboard n and N + #define BT_KEY_O 0x12 // Keyboard o and O + #define BT_KEY_P 0x13 // Keyboard p and P + #define BT_KEY_Q 0x14 // Keyboard q and Q + #define BT_KEY_R 0x15 // Keyboard r and R + #define BT_KEY_S 0x16 // Keyboard s and S + #define BT_KEY_T 0x17 // Keyboard t and T + #define BT_KEY_U 0x18 // Keyboard u and U + #define BT_KEY_V 0x19 // Keyboard v and V + #define BT_KEY_W 0x1a // Keyboard w and W + #define BT_KEY_X 0x1b // Keyboard x and X + #define BT_KEY_Y 0x1c // Keyboard y and Y + #define BT_KEY_Z 0x1d // Keyboard z and Z + + #define BT_KEY_1 0x1e // Keyboard 1 and ! + #define BT_KEY_2 0x1f // Keyboard 2 and @ + #define BT_KEY_3 0x20 // Keyboard 3 and # + #define BT_KEY_4 0x21 // Keyboard 4 and $ + #define BT_KEY_5 0x22 // Keyboard 5 and % + #define BT_KEY_6 0x23 // Keyboard 6 and ^ + #define BT_KEY_7 0x24 // Keyboard 7 and & + #define BT_KEY_8 0x25 // Keyboard 8 and * + #define BT_KEY_9 0x26 // Keyboard 9 and ( + #define BT_KEY_0 0x27 // Keyboard 0 and ) + + #define BT_KEY_ENTER 0x28 // Keyboard Return (ENTER) + #define BT_KEY_ESC 0x29 // Keyboard ESCAPE + #define BT_KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace) + #define BT_KEY_TAB 0x2b // Keyboard Tab + #define BT_KEY_SPACE 0x2c // Keyboard Spacebar + #define BT_KEY_MINUS 0x2d // Keyboard - and _ + #define BT_KEY_EQUAL 0x2e // Keyboard = and + + #define BT_KEY_LEFTBRACE 0x2f // Keyboard [ and { + #define BT_KEY_RIGHTBRACE 0x30 // Keyboard ] and } + #define BT_KEY_BACKSLASH 0x31 // Keyboard \ and | + #define BT_KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~ + #define BT_KEY_SEMICOLON 0x33 // Keyboard ; and : + #define BT_KEY_APOSTROPHE 0x34 // Keyboard ' and " + #define BT_KEY_GRAVE 0x35 // Keyboard ` and ~ + #define BT_KEY_COMMA 0x36 // Keyboard , and < + #define BT_KEY_DOT 0x37 // Keyboard . and > + #define BT_KEY_SLASH 0x38 // Keyboard / and ? + #define BT_KEY_CAPSLOCK 0x39 // Keyboard Caps Lock + + #define BT_KEY_F1 0x3a // Keyboard F1 + #define BT_KEY_F2 0x3b // Keyboard F2 + #define BT_KEY_F3 0x3c // Keyboard F3 + #define BT_KEY_F4 0x3d // Keyboard F4 + #define BT_KEY_F5 0x3e // Keyboard F5 + #define BT_KEY_F6 0x3f // Keyboard F6 + #define BT_KEY_F7 0x40 // Keyboard F7 + #define BT_KEY_F8 0x41 // Keyboard F8 + #define BT_KEY_F9 0x42 // Keyboard F9 + #define BT_KEY_F10 0x43 // Keyboard F10 + #define BT_KEY_F11 0x44 // Keyboard F11 + #define BT_KEY_F12 0x45 // Keyboard F12 + + #define BT_KEY_SYSRQ 0x46 // Keyboard Print Screen + #define BT_KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock + #define BT_KEY_PAUSE 0x48 // Keyboard Pause + #define BT_KEY_INSERT 0x49 // Keyboard Insert + #define BT_KEY_HOME 0x4a // Keyboard Home + #define BT_KEY_PAGEUP 0x4b // Keyboard Page Up + #define BT_KEY_DELETE 0x4c // Keyboard Delete Forward + #define BT_KEY_END 0x4d // Keyboard End + #define BT_KEY_PAGEDOWN 0x4e // Keyboard Page Down + #define BT_KEY_RIGHT 0x4f // Keyboard Right Arrow + #define BT_KEY_LEFT 0x50 // Keyboard Left Arrow + #define BT_KEY_DOWN 0x51 // Keyboard Down Arrow + #define BT_KEY_UP 0x52 // Keyboard Up Arrow + + #define BT_KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear + #define BT_KEY_KPSLASH 0x54 // Keypad / + #define BT_KEY_KPASTERISK 0x55 // Keypad * + #define BT_KEY_KPMINUS 0x56 // Keypad - + #define BT_KEY_KPPLUS 0x57 // Keypad + + #define BT_KEY_KPENTER 0x58 // Keypad ENTER + #define BT_KEY_KP1 0x59 // Keypad 1 and End + #define BT_KEY_KP2 0x5a // Keypad 2 and Down Arrow + #define BT_KEY_KP3 0x5b // Keypad 3 and PageDn + #define BT_KEY_KP4 0x5c // Keypad 4 and Left Arrow + #define BT_KEY_KP5 0x5d // Keypad 5 + #define BT_KEY_KP6 0x5e // Keypad 6 and Right Arrow + #define BT_KEY_KP7 0x5f // Keypad 7 and Home + #define BT_KEY_KP8 0x60 // Keypad 8 and Up Arrow + #define BT_KEY_KP9 0x61 // Keypad 9 and Page Up + #define BT_KEY_KP0 0x62 // Keypad 0 and Insert + #define BT_KEY_KPDOT 0x63 // Keypad . and Delete + + #define BT_KEY_102ND 0x64 // Keyboard Non-US \ and | + #define BT_KEY_COMPOSE 0x65 // Keyboard Application + #define BT_KEY_POWER 0x66 // Keyboard Power + #define BT_KEY_KPEQUAL 0x67 // Keypad = + + #define BT_KEY_F13 0x68 // Keyboard F13 + #define BT_KEY_F14 0x69 // Keyboard F14 + #define BT_KEY_F15 0x6a // Keyboard F15 + #define BT_KEY_F16 0x6b // Keyboard F16 + #define BT_KEY_F17 0x6c // Keyboard F17 + #define BT_KEY_F18 0x6d // Keyboard F18 + #define BT_KEY_F19 0x6e // Keyboard F19 + #define BT_KEY_F20 0x6f // Keyboard F20 + #define BT_KEY_F21 0x70 // Keyboard F21 + #define BT_KEY_F22 0x71 // Keyboard F22 + #define BT_KEY_F23 0x72 // Keyboard F23 + #define BT_KEY_F24 0x73 // Keyboard F24 + + #define BT_KEY_OPEN 0x74 // Keyboard Execute + #define BT_KEY_HELP 0x75 // Keyboard Help + #define BT_KEY_PROPS 0x76 // Keyboard Menu + #define BT_KEY_FRONT 0x77 // Keyboard Select + #define BT_KEY_STOP 0x78 // Keyboard Stop + #define BT_KEY_AGAIN 0x79 // Keyboard Again + #define BT_KEY_UNDO 0x7a // Keyboard Undo + #define BT_KEY_CUT 0x7b // Keyboard Cut + #define BT_KEY_COPY 0x7c // Keyboard Copy + #define BT_KEY_PASTE 0x7d // Keyboard Paste + #define BT_KEY_FIND 0x7e // Keyboard Find + #define BT_KEY_MUTE 0x7f // Keyboard Mute + #define BT_KEY_VOLUMEUP 0x80 // Keyboard Volume Up + #define BT_KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down + // 0x82 Keyboard Locking Caps Lock + // 0x83 Keyboard Locking Num Lock + // 0x84 Keyboard Locking Scroll Lock + #define BT_KEY_KPCOMMA 0x85 // Keypad Comma + // 0x86 Keypad Equal Sign + #define BT_KEY_RO 0x87 // Keyboard International1 + #define BT_KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2 + #define BT_KEY_YEN 0x89 // Keyboard International3 + #define BT_KEY_HENKAN 0x8a // Keyboard International4 + #define BT_KEY_MUHENKAN 0x8b // Keyboard International5 + #define BT_KEY_KPJPCOMMA 0x8c // Keyboard International6 + // 0x8d Keyboard International7 + // 0x8e Keyboard International8 + // 0x8f Keyboard International9 + #define BT_KEY_HANGEUL 0x90 // Keyboard LANG1 + #define BT_KEY_HANJA 0x91 // Keyboard LANG2 + #define BT_KEY_KATAKANA 0x92 // Keyboard LANG3 + #define BT_KEY_HIRAGANA 0x93 // Keyboard LANG4 + #define BT_KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5 + // 0x95 Keyboard LANG6 + // 0x96 Keyboard LANG7 + // 0x97 Keyboard LANG8 + // 0x98 Keyboard LANG9 + // 0x99 Keyboard Alternate Erase + // 0x9a Keyboard SysReq/Attention + // 0x9b Keyboard Cancel + // 0x9c Keyboard Clear + // 0x9d Keyboard Prior + // 0x9e Keyboard Return + // 0x9f Keyboard Separator + // 0xa0 Keyboard Out + // 0xa1 Keyboard Oper + // 0xa2 Keyboard Clear/Again + // 0xa3 Keyboard CrSel/Props + // 0xa4 Keyboard ExSel + + // 0xb0 Keypad 00 + // 0xb1 Keypad 000 + // 0xb2 Thousands Separator + // 0xb3 Decimal Separator + // 0xb4 Currency Unit + // 0xb5 Currency Sub-unit + #define BT_KEY_KPLEFTPAREN 0xb6 // Keypad ( + #define BT_KEY_KPRIGHTPAREN 0xb7 // Keypad ) + // 0xb8 Keypad { + // 0xb9 Keypad } + // 0xba Keypad Tab + // 0xbb Keypad Backspace + // 0xbc Keypad A + // 0xbd Keypad B + // 0xbe Keypad C + // 0xbf Keypad D + // 0xc0 Keypad E + // 0xc1 Keypad F + // 0xc2 Keypad XOR + // 0xc3 Keypad ^ + // 0xc4 Keypad % + // 0xc5 Keypad < + // 0xc6 Keypad > + // 0xc7 Keypad & + // 0xc8 Keypad && + // 0xc9 Keypad | + // 0xca Keypad || + // 0xcb Keypad : + // 0xcc Keypad # + // 0xcd Keypad Space + // 0xce Keypad @ + // 0xcf Keypad ! + // 0xd0 Keypad Memory Store + // 0xd1 Keypad Memory Recall + // 0xd2 Keypad Memory Clear + // 0xd3 Keypad Memory Add + // 0xd4 Keypad Memory Subtract + // 0xd5 Keypad Memory Multiply + // 0xd6 Keypad Memory Divide + // 0xd7 Keypad +/- + // 0xd8 Keypad Clear + // 0xd9 Keypad Clear Entry + // 0xda Keypad Binary + // 0xdb Keypad Octal + // 0xdc Keypad Decimal + // 0xdd Keypad Hexadecimal + + #define BT_KEY_LEFTCTRL 0xe0 // Keyboard Left Control + #define BT_KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift + #define BT_KEY_LEFTALT 0xe2 // Keyboard Left Alt + #define BT_KEY_LEFTMETA 0xe3 // Keyboard Left GUI + #define BT_KEY_RIGHTCTRL 0xe4 // Keyboard Right Control + #define BT_KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift + #define BT_KEY_RIGHTALT 0xe6 // Keyboard Right Alt + #define BT_KEY_RIGHTMETA 0xe7 // Keyboard Right GUI + + #define BT_KEY_MEDIA_PLAYPAUSE 0xe8 + #define BT_KEY_MEDIA_STOPCD 0xe9 + #define BT_KEY_MEDIA_PREVIOUSSONG 0xea + #define BT_KEY_MEDIA_NEXTSONG 0xeb + #define BT_KEY_MEDIA_EJECTCD 0xec + #define BT_KEY_MEDIA_VOLUMEUP 0xed + #define BT_KEY_MEDIA_VOLUMEDOWN 0xee + #define BT_KEY_MEDIA_MUTE 0xef + #define BT_KEY_MEDIA_WWW 0xf0 + #define BT_KEY_MEDIA_BACK 0xf1 + #define BT_KEY_MEDIA_FORWARD 0xf2 + #define BT_KEY_MEDIA_STOP 0xf3 + #define BT_KEY_MEDIA_FIND 0xf4 + #define BT_KEY_MEDIA_SCROLLUP 0xf5 + #define BT_KEY_MEDIA_SCROLLDOWN 0xf6 + #define BT_KEY_MEDIA_EDIT 0xf7 + #define BT_KEY_MEDIA_SLEEP 0xf8 + #define BT_KEY_MEDIA_COFFEE 0xf9 + #define BT_KEY_MEDIA_REFRESH 0xfa + #define BT_KEY_MEDIA_CALC 0xfb + + // Media key definition. On the ESP module a seperate usage type, CCONTROL is created for media keys and it delivers a 24bit word, each bit signifying a key. + #define BT_MEDIA_SEARCH 0x00200000 + #define BT_MEDIA_HOME 0x00080000 + #define BT_MEDIA_BRIGHTNESS_UP 0x00004000 + #define BT_MEDIA_BRIGHTNESS_DOWN 0x00008000 + #define BT_MEDIA_MUTE 0x00000040 + #define BT_MEDIA_VOL_DOWN 0x00000020 + #define BT_MEDIA_VOL_UP 0x00000010 + #define BT_MEDIA_TRACK_PREV 0x00000001 + + // PS2 Flag definitions. + #define PS2_FLG_NONE 0x00 // No keys active = 0 + #define PS2_FLG_SHIFT PS2_SHIFT >> 8 // Shift Key active = 1 + #define PS2_FLG_CTRL PS2_CTRL >> 8 // Ctrl Key active = 1 + #define PS2_FLG_CAPS PS2_CAPS >> 8 // CAPS active = 1 + #define PS2_FLG_ALT PS2_ALT >> 8 // ALT flag used as Right CTRL flag, active = 1 + #define PS2_FLG_ALTGR PS2_ALT_GR >> 8 // ALTGR active = 1 + #define PS2_FLG_GUI PS2_GUI >> 8 // GUI Key active = 1 + #define PS2_FLG_FUNC PS2_FUNCTION >> 8 // Special Function Keys active = 1 + #define PS2_FLG_BREAK PS2_BREAL >> 8 // BREAK Key active = 1 + + + public: + + struct KeyInfo { + uint8_t keys[MAX_KEYBOARD_DATA_BYTES]; + uint8_t length; + bool cControl; + esp_hidh_dev_t *hdlDev; + }; + + // Prototypes. + BTHID(void); + virtual ~BTHID(void); + bool setup(t_pairingHandler *handler); + bool openDevice(esp_bd_addr_t bda, esp_hid_transport_t transport, esp_ble_addr_type_t addrType); + bool closeDevice(esp_bd_addr_t bda); + void checkBTDevices(void); + bool setResolution(enum PS2Mouse::PS2_RESOLUTION resolution); + bool setScaling(enum PS2Mouse::PS2_SCALING scaling); + bool setSampleRate(enum PS2Mouse::PS2_SAMPLING rate); + void processBTKeys(void); + uint16_t getKey(uint32_t timeout = 0); + + // Method to register an object method for callback with context. + template + void setMouseDataCallback(A func_ptr, B obj_ptr) + { + btHIDCtrl.ms.mouseDataCallback = bind(func_ptr, obj_ptr, 1, std::placeholders::_1); + } + + // Template to aid in conversion of an enum to integer. + template constexpr typename std::underlying_type::type to_underlying(E e) noexcept + { + return static_cast::type>(e); + } + + private: + static constexpr char const * TAG = "BTHID"; + + // Structure to hold details of an active or post-active connection. + typedef struct { + esp_bd_addr_t bda; + esp_hid_transport_t transport; + esp_ble_addr_type_t addrType; + esp_hid_usage_t usage; + esp_hidh_dev_t *hidhDevHdl; + uint32_t nextCheckTime; + bool open; + } t_activeDev; + + // Structure to encapsulate a single key map from Bluetooth to PS/2. + typedef struct { + uint8_t btKeyCode; + uint16_t btCtrl; + uint8_t ps2KeyCode; + uint16_t ps2Ctrl; + } t_keyMapEntry; + + // Structure to encapsulate the entire static keyboard mapping table. + typedef struct { + t_keyMapEntry kme[MAX_BT2PS2_MAP_ENTRIES]; + } t_keyMap; + + // Structure to contain a media key map. + typedef struct { + uint32_t mediaKey; // 24bit Media key value. + uint8_t ps2Key; // Equivalent PS/2 key for media key. + uint16_t ps2Ctrl; // PS/2 translated control flags. + } t_mediaMapEntry; + + // Structure to encapsulate Media key mappings. + typedef struct { + t_mediaMapEntry kme[MAX_BTMEDIA2PS2_MAP_ENTRIES]; + } t_mediaKeyMap; + + // Structure to maintain control variables. + typedef struct { + // Array of active devices which connect with the SharpKey. + std::vector devices; + + // Keyboard handling. + struct { + // Queues for storing data in the 2 processing stages. + xQueueHandle rawKeyQueue; + xQueueHandle keyQueue; + + uint8_t lastKeys[MAX_KEYBOARD_DATA_BYTES]; // Required to generate a PS/2 break event when a key is released. + uint32_t lastMediaKey; // Required to detect changes in the media control keys, ie. release. + uint16_t btFlags; // Bluetooth control flags. + uint16_t ps2Flags; // PS/2 translated control flags. + uint8_t statusLED; // Keyboard LED state. + t_keyMapEntry *kme; // Pointer to the mapping array. + t_mediaMapEntry *kmeMedia; // Pointer to the media key mapping array. + int kmeRows; // Number of entries in the BT to PS/2 mapping table. + int kmeMediaRows; // Number of entries in the BT to PS/2 media key mapping table. + } kbd; + + // Mouse handling. + struct { + int resolution; // PS/2 compatible resolution (pixels per mm) setting. + int scaling; // PS/2 compatible scaling (1:1 or 2:1). + int sampleRate; // PS/2 compatible sample rate (10 .. 200). + int xDivisor; // Divisor on the X plane to scale down the 12bit BT resolution. + int yDivisor; // Divisor on the Y plane to scale down the 12bit BT resolution. + + // Callback for streaming processed mouse data to HID handler. + std::function mouseDataCallback; + } ms; + + BTHID *pThis; + } t_btHIDCtrl; + + // All control variables are stored in a struct for ease of reference. + t_btHIDCtrl btHIDCtrl; + + // Prototypes. + static void hidh_callback(void * handler_args, esp_event_base_t base, int32_t id, void * event_data); + void pushKeyToFIFO(esp_hid_usage_t src, esp_hidh_dev_t *hdlDev, uint8_t *keys, uint8_t size); + void setStatusLED(esp_hidh_dev_t *dev, uint8_t led); + void clearStatusLED(esp_hidh_dev_t *dev, uint8_t led); + uint16_t mapBTMediaToPS2(uint32_t key); + uint16_t mapBTtoPS2(uint8_t key); + inline uint32_t milliSeconds(void) + { + return( (uint32_t) (clock() ) ); + } + + // Mapping for Media keys. ESP module seperates them but not properly, some media keys are sent as normal key scancodes others as control key bit maps. + // Hence two mapping tables, one for normal scancodes and one for media codes. + t_mediaKeyMap MediaKeyToPS2 = { + { + { BT_MEDIA_SEARCH, PS2_KEY_WEB_SEARCH, PS2_FLG_NONE, }, + { BT_MEDIA_HOME, PS2_KEY_WEB_HOME, PS2_FLG_NONE, }, + { BT_MEDIA_BRIGHTNESS_UP, PS2_KEY_WEB_FORWARD, PS2_FLG_NONE, }, + { BT_MEDIA_BRIGHTNESS_DOWN, PS2_KEY_WEB_BACK, PS2_FLG_NONE, }, + { BT_MEDIA_MUTE, PS2_KEY_MUTE, PS2_FLG_NONE, }, + { BT_MEDIA_VOL_DOWN, PS2_KEY_VOL_DN, PS2_FLG_NONE, }, + { BT_MEDIA_VOL_UP, PS2_KEY_VOL_UP, PS2_FLG_NONE, }, + { BT_MEDIA_TRACK_PREV, PS2_KEY_PREV_TR, PS2_FLG_NONE, }, + }}; + + // Mapping table between BT Keyboard Scan Codes and PS/2 Keyboard Scan Codes. + // + t_keyMap BTKeyToPS2 = { + { + // Bluetooth Key Bluetooth Control, PS/2 Key PS/2 Control, + { BT_KEY_A, BT_NONE, PS2_KEY_A, PS2_FLG_NONE, }, + { BT_KEY_B, BT_NONE, PS2_KEY_B, PS2_FLG_NONE, }, + { BT_KEY_C, BT_NONE, PS2_KEY_C, PS2_FLG_NONE, }, + { BT_KEY_D, BT_NONE, PS2_KEY_D, PS2_FLG_NONE, }, + { BT_KEY_E, BT_NONE, PS2_KEY_E, PS2_FLG_NONE, }, + { BT_KEY_F, BT_NONE, PS2_KEY_F, PS2_FLG_NONE, }, + { BT_KEY_G, BT_NONE, PS2_KEY_G, PS2_FLG_NONE, }, + { BT_KEY_H, BT_NONE, PS2_KEY_H, PS2_FLG_NONE, }, + { BT_KEY_I, BT_NONE, PS2_KEY_I, PS2_FLG_NONE, }, + { BT_KEY_J, BT_NONE, PS2_KEY_J, PS2_FLG_NONE, }, + { BT_KEY_K, BT_NONE, PS2_KEY_K, PS2_FLG_NONE, }, + { BT_KEY_L, BT_NONE, PS2_KEY_L, PS2_FLG_NONE, }, + { BT_KEY_M, BT_NONE, PS2_KEY_M, PS2_FLG_NONE, }, + { BT_KEY_N, BT_NONE, PS2_KEY_N, PS2_FLG_NONE, }, + { BT_KEY_O, BT_NONE, PS2_KEY_O, PS2_FLG_NONE, }, + { BT_KEY_P, BT_NONE, PS2_KEY_P, PS2_FLG_NONE, }, + { BT_KEY_Q, BT_NONE, PS2_KEY_Q, PS2_FLG_NONE, }, + { BT_KEY_R, BT_NONE, PS2_KEY_R, PS2_FLG_NONE, }, + { BT_KEY_S, BT_NONE, PS2_KEY_S, PS2_FLG_NONE, }, + { BT_KEY_T, BT_NONE, PS2_KEY_T, PS2_FLG_NONE, }, + { BT_KEY_U, BT_NONE, PS2_KEY_U, PS2_FLG_NONE, }, + { BT_KEY_V, BT_NONE, PS2_KEY_V, PS2_FLG_NONE, }, + { BT_KEY_W, BT_NONE, PS2_KEY_W, PS2_FLG_NONE, }, + { BT_KEY_X, BT_NONE, PS2_KEY_X, PS2_FLG_NONE, }, + { BT_KEY_Y, BT_NONE, PS2_KEY_Y, PS2_FLG_NONE, }, + { BT_KEY_Z, BT_NONE, PS2_KEY_Z, PS2_FLG_NONE, }, + { BT_KEY_1, BT_NONE, PS2_KEY_1, PS2_FLG_NONE, }, + { BT_KEY_2, BT_NONE, PS2_KEY_2, PS2_FLG_NONE, }, + { BT_KEY_3, BT_NONE, PS2_KEY_3, PS2_FLG_NONE, }, + { BT_KEY_4, BT_NONE, PS2_KEY_4, PS2_FLG_NONE, }, + { BT_KEY_5, BT_NONE, PS2_KEY_5, PS2_FLG_NONE, }, + { BT_KEY_6, BT_NONE, PS2_KEY_6, PS2_FLG_NONE, }, + { BT_KEY_7, BT_NONE, PS2_KEY_7, PS2_FLG_NONE, }, + { BT_KEY_8, BT_NONE, PS2_KEY_8, PS2_FLG_NONE, }, + { BT_KEY_9, BT_NONE, PS2_KEY_9, PS2_FLG_NONE, }, + { BT_KEY_0, BT_NONE, PS2_KEY_0, PS2_FLG_NONE, }, + { BT_KEY_ENTER, BT_NONE, PS2_KEY_ENTER, PS2_FLG_NONE, }, + { BT_KEY_ESC, BT_NONE, PS2_KEY_ESC, PS2_FLG_NONE, }, + { BT_KEY_BACKSPACE, BT_NONE, PS2_KEY_BS, PS2_FLG_NONE, }, + { BT_KEY_TAB, BT_NONE, PS2_KEY_TAB, PS2_FLG_NONE, }, + { BT_KEY_SPACE, BT_NONE, PS2_KEY_SPACE, PS2_FLG_NONE, }, + { BT_KEY_MINUS, BT_NONE, PS2_KEY_MINUS, PS2_FLG_NONE, }, + { BT_KEY_EQUAL, BT_NONE, PS2_KEY_EQUAL, PS2_FLG_NONE, }, + { BT_KEY_LEFTBRACE, BT_NONE, PS2_KEY_OPEN_SQ, PS2_FLG_NONE, }, + { BT_KEY_RIGHTBRACE, BT_NONE, PS2_KEY_CLOSE_SQ, PS2_FLG_NONE, }, + { BT_KEY_BACKSLASH, BT_NONE, PS2_KEY_BACK, PS2_FLG_NONE, }, + { BT_KEY_HASHTILDE, BT_NONE, PS2_KEY_HASH, PS2_FLG_NONE, }, + { BT_KEY_SEMICOLON, BT_NONE, PS2_KEY_SEMI, PS2_FLG_NONE, }, + { BT_KEY_APOSTROPHE, BT_NONE, PS2_KEY_APOS, PS2_FLG_NONE, }, + { BT_KEY_GRAVE, BT_NONE, PS2_KEY_BTICK, PS2_FLG_NONE, }, + { BT_KEY_COMMA, BT_NONE, PS2_KEY_COMMA, PS2_FLG_NONE, }, + { BT_KEY_DOT, BT_NONE, PS2_KEY_DOT, PS2_FLG_NONE, }, + { BT_KEY_SLASH, BT_NONE, PS2_KEY_DIV, PS2_FLG_NONE, }, + { BT_KEY_CAPSLOCK, BT_NONE, PS2_KEY_CAPS, PS2_FLG_NONE, }, + { BT_KEY_F1, BT_NONE, PS2_KEY_F1, PS2_FLG_NONE, }, + { BT_KEY_F2, BT_NONE, PS2_KEY_F2, PS2_FLG_NONE, }, + { BT_KEY_F3, BT_NONE, PS2_KEY_F3, PS2_FLG_NONE, }, + { BT_KEY_F4, BT_NONE, PS2_KEY_F4, PS2_FLG_NONE, }, + { BT_KEY_F5, BT_NONE, PS2_KEY_F5, PS2_FLG_NONE, }, + { BT_KEY_F6, BT_NONE, PS2_KEY_F6, PS2_FLG_NONE, }, + { BT_KEY_F7, BT_NONE, PS2_KEY_F7, PS2_FLG_NONE, }, + { BT_KEY_F8, BT_NONE, PS2_KEY_F8, PS2_FLG_NONE, }, + { BT_KEY_F9, BT_NONE, PS2_KEY_F9, PS2_FLG_NONE, }, + { BT_KEY_F10, BT_NONE, PS2_KEY_F10, PS2_FLG_NONE, }, + { BT_KEY_F11, BT_NONE, PS2_KEY_F11, PS2_FLG_NONE, }, + { BT_KEY_F12, BT_NONE, PS2_KEY_F12, PS2_FLG_NONE, }, + { BT_KEY_SYSRQ, BT_NONE, PS2_KEY_PRTSCR, PS2_FLG_NONE, }, + { BT_KEY_SCROLLLOCK, BT_NONE, PS2_KEY_SCROLL, PS2_FLG_NONE, }, + { BT_KEY_PAUSE, BT_NONE, PS2_KEY_PAUSE, PS2_FLG_NONE, }, + { BT_KEY_INSERT, BT_NONE, PS2_KEY_INSERT, PS2_FLG_NONE, }, + { BT_KEY_HOME, BT_NONE, PS2_KEY_HOME, PS2_FLG_NONE, }, + { BT_KEY_PAGEUP, BT_NONE, PS2_KEY_PGUP, PS2_FLG_NONE, }, + { BT_KEY_DELETE, BT_NONE, PS2_KEY_DELETE, PS2_FLG_NONE, }, + { BT_KEY_END, BT_NONE, PS2_KEY_END, PS2_FLG_NONE, }, + { BT_KEY_PAGEDOWN, BT_NONE, PS2_KEY_PGDN, PS2_FLG_NONE, }, + { BT_KEY_RIGHT, BT_NONE, PS2_KEY_R_ARROW, PS2_FLG_NONE, }, + { BT_KEY_LEFT, BT_NONE, PS2_KEY_L_ARROW, PS2_FLG_NONE, }, + { BT_KEY_DOWN, BT_NONE, PS2_KEY_DN_ARROW, PS2_FLG_NONE, }, + { BT_KEY_UP, BT_NONE, PS2_KEY_UP_ARROW, PS2_FLG_NONE, }, + { BT_KEY_NUMLOCK, BT_NONE, PS2_KEY_NUM, PS2_FLG_NONE, }, + { BT_KEY_KPSLASH, BT_NONE, PS2_KEY_KP_DIV, PS2_FLG_NONE, }, + { BT_KEY_KPASTERISK, BT_NONE, PS2_KEY_KP_TIMES, PS2_FLG_NONE, }, + { BT_KEY_KPMINUS, BT_NONE, PS2_KEY_KP_MINUS, PS2_FLG_NONE, }, + { BT_KEY_KPPLUS, BT_NONE, PS2_KEY_KP_PLUS, PS2_FLG_NONE, }, + { BT_KEY_KPENTER, BT_NONE, PS2_KEY_KP_ENTER, PS2_FLG_NONE, }, + { BT_KEY_KP1, BT_NUM_LOCK, PS2_KEY_KP1, PS2_FLG_NONE, }, + { BT_KEY_KP2, BT_NUM_LOCK, PS2_KEY_KP2, PS2_FLG_NONE, }, + { BT_KEY_KP3, BT_NUM_LOCK, PS2_KEY_KP3, PS2_FLG_NONE, }, + { BT_KEY_KP4, BT_NUM_LOCK, PS2_KEY_KP4, PS2_FLG_NONE, }, + { BT_KEY_KP5, BT_NUM_LOCK, PS2_KEY_KP5, PS2_FLG_NONE, }, + { BT_KEY_KP6, BT_NUM_LOCK, PS2_KEY_KP6, PS2_FLG_NONE, }, + { BT_KEY_KP7, BT_NUM_LOCK, PS2_KEY_KP7, PS2_FLG_NONE, }, + { BT_KEY_KP8, BT_NUM_LOCK, PS2_KEY_KP8, PS2_FLG_NONE, }, + { BT_KEY_KP9, BT_NUM_LOCK, PS2_KEY_KP9, PS2_FLG_NONE, }, + { BT_KEY_KP0, BT_NUM_LOCK, PS2_KEY_KP0, PS2_FLG_NONE, }, + { BT_KEY_KPDOT, BT_NUM_LOCK, PS2_KEY_KP_DOT, PS2_FLG_NONE, }, + { BT_KEY_KP1, BT_NONE, PS2_KEY_END, PS2_FLG_NONE, }, + { BT_KEY_KP2, BT_NONE, PS2_KEY_DN_ARROW, PS2_FLG_NONE, }, + { BT_KEY_KP3, BT_NONE, PS2_KEY_PGDN, PS2_FLG_NONE, }, + { BT_KEY_KP4, BT_NONE, PS2_KEY_L_ARROW, PS2_FLG_NONE, }, + { BT_KEY_KP5, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_KP6, BT_NONE, PS2_KEY_R_ARROW, PS2_FLG_NONE, }, + { BT_KEY_KP7, BT_NONE, PS2_KEY_HOME, PS2_FLG_NONE, }, + { BT_KEY_KP8, BT_NONE, PS2_KEY_UP_ARROW, PS2_FLG_NONE, }, + { BT_KEY_KP9, BT_NONE, PS2_KEY_PGUP, PS2_FLG_NONE, }, + { BT_KEY_KP0, BT_NONE, PS2_KEY_INSERT, PS2_FLG_NONE, }, + { BT_KEY_KPDOT, BT_NONE, PS2_KEY_DELETE, PS2_FLG_NONE, }, + { BT_KEY_102ND, BT_NONE, PS2_KEY_BACK, PS2_FLG_NONE, }, + { BT_KEY_COMPOSE, BT_NONE, PS2_KEY_MENU, PS2_FLG_NONE, }, + { BT_KEY_POWER, BT_NONE, PS2_KEY_POWER, PS2_FLG_NONE, }, + { BT_KEY_KPEQUAL, BT_NONE, PS2_KEY_KP_EQUAL, PS2_FLG_NONE, }, + { BT_KEY_F13, BT_NONE, PS2_KEY_F13, PS2_FLG_NONE, }, + { BT_KEY_F14, BT_NONE, PS2_KEY_F14, PS2_FLG_NONE, }, + { BT_KEY_F15, BT_NONE, PS2_KEY_F15, PS2_FLG_NONE, }, + { BT_KEY_F16, BT_NONE, PS2_KEY_F16, PS2_FLG_NONE, }, + { BT_KEY_F17, BT_NONE, PS2_KEY_F17, PS2_FLG_NONE, }, + { BT_KEY_F18, BT_NONE, PS2_KEY_F18, PS2_FLG_NONE, }, + { BT_KEY_F19, BT_NONE, PS2_KEY_F19, PS2_FLG_NONE, }, + { BT_KEY_F20, BT_NONE, PS2_KEY_F20, PS2_FLG_NONE, }, + { BT_KEY_F21, BT_NONE, PS2_KEY_F21, PS2_FLG_NONE, }, + { BT_KEY_F22, BT_NONE, PS2_KEY_F22, PS2_FLG_NONE, }, + { BT_KEY_F23, BT_NONE, PS2_KEY_F23, PS2_FLG_NONE, }, + { BT_KEY_F24, BT_NONE, PS2_KEY_F24, PS2_FLG_NONE, }, + { BT_KEY_OPEN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_HELP, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_PROPS, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_FRONT, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_STOP, BT_NONE, PS2_KEY_STOP, PS2_FLG_NONE, }, + { BT_KEY_AGAIN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_UNDO, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_CUT, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_COPY, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_PASTE, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_FIND, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MUTE, BT_NONE, PS2_KEY_MUTE, PS2_FLG_NONE, }, + { BT_KEY_VOLUMEUP, BT_NONE, PS2_KEY_VOL_UP, PS2_FLG_NONE, }, + { BT_KEY_VOLUMEDOWN, BT_NONE, PS2_KEY_VOL_DN, PS2_FLG_NONE, }, + { BT_KEY_KPCOMMA, BT_NONE, PS2_KEY_KP_COMMA, PS2_FLG_NONE, }, + { BT_KEY_RO, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_KATAKANAHIRAGANA, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_YEN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_HENKAN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MUHENKAN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_KPJPCOMMA, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_HANGEUL, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_HANJA, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_KATAKANA, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_HIRAGANA, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_ZENKAKUHANKAKU, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_KPLEFTPAREN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_KPRIGHTPAREN, BT_NONE, 0x00, PS2_FLG_NONE, }, + // Control keys. + { BT_KEY_LEFTCTRL, BT_NONE, PS2_KEY_L_CTRL, PS2_FLG_FUNC | PS2_FLG_CTRL, }, + { BT_KEY_LEFTSHIFT, BT_NONE, PS2_KEY_L_SHIFT, PS2_FLG_FUNC | PS2_FLG_SHIFT, }, + { BT_KEY_LEFTALT, BT_NONE, PS2_KEY_L_ALT, PS2_FLG_FUNC | PS2_FLG_ALT, }, + { BT_KEY_LEFTMETA, BT_NONE, PS2_KEY_L_GUI, PS2_FLG_FUNC | PS2_FLG_GUI, }, + { BT_KEY_RIGHTCTRL, BT_NONE, PS2_KEY_R_CTRL, PS2_FLG_FUNC | PS2_FLG_CTRL, }, + { BT_KEY_RIGHTSHIFT, BT_NONE, PS2_KEY_R_SHIFT, PS2_FLG_FUNC | PS2_FLG_SHIFT, }, + { BT_KEY_RIGHTALT, BT_NONE, PS2_KEY_R_ALT, PS2_FLG_FUNC | PS2_FLG_ALTGR, }, + { BT_KEY_RIGHTMETA, BT_NONE, PS2_KEY_R_GUI, PS2_FLG_FUNC | PS2_FLG_NONE, }, + // Media keys + { BT_KEY_MEDIA_PLAYPAUSE, BT_NONE, PS2_KEY_PLAY, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_STOPCD, BT_NONE, PS2_KEY_STOP, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_PREVIOUSSONG, BT_NONE, PS2_KEY_PREV_TR, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_NEXTSONG, BT_NONE, PS2_KEY_NEXT_TR, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_EJECTCD, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_VOLUMEUP, BT_NONE, PS2_KEY_VOL_UP, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_VOLUMEDOWN, BT_NONE, PS2_KEY_VOL_DN, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_MUTE, BT_NONE, PS2_KEY_MUTE, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_WWW, BT_NONE, PS2_KEY_WEB_SEARCH, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_BACK, BT_NONE, PS2_KEY_WEB_BACK, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_FORWARD, BT_NONE, PS2_KEY_WEB_FORWARD, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_STOP, BT_NONE, PS2_KEY_WEB_STOP, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_FIND, BT_NONE, PS2_KEY_WEB_SEARCH, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_SCROLLUP, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_SCROLLDOWN, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_EDIT, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_SLEEP, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_COFFEE, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_REFRESH, BT_NONE, 0x00, PS2_FLG_NONE, }, + { BT_KEY_MEDIA_CALC, BT_NONE, 0x00, PS2_FLG_NONE, }, + }}; +}; + +#endif // BT_KEYBOARD_H_ diff --git a/main/include/HID.h b/main/include/HID.h deleted file mode 120000 index e3155f6..0000000 --- a/main/include/HID.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/HID.h \ No newline at end of file diff --git a/main/include/HID.h b/main/include/HID.h new file mode 100644 index 0000000..d545d88 --- /dev/null +++ b/main/include/HID.h @@ -0,0 +1,385 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: HID.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: A HID Class definition, used to instantiate differing input device classes and +// present a standard API. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to support Bluetooth keyboard and mouse. The mouse can be +// a primary device or a secondary device for hosts which support +// keyboard and mouse over one physical port. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HID_H +#define HID_H + +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "BTHID.h" +#include "LED.h" +#include "SWITCH.h" + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Define a class which acts as the encapsulation object of many base classes which provide input device functionality. +class HID { + + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define HID_VERSION 1.02 + #define HID_MOUSE_DATA_POLL_DELAY 10 + #define MAX_MOUSE_INACTIVITY_TIME 500 * HID_MOUSE_DATA_POLL_DELAY + + // Categories of configuration possible with the mouse. These are used primarily with the web based UI for rendering selection choices. + #define HID_MOUSE_HOST_SCALING_TYPE "host_scaling" + #define HID_MOUSE_SCALING_TYPE "mouse_scaling" + #define HID_MOUSE_RESOLUTION_TYPE "mouse_resolution" + #define HID_MOUSE_SAMPLING_TYPE "mouse_sampling" + + #define HID_MOUSE_HOST_SCALING_1_1_NAME "1:1" + #define HID_MOUSE_HOST_SCALING_1_2_NAME "1:2" + #define HID_MOUSE_HOST_SCALING_1_3_NAME "1:3" + #define HID_MOUSE_HOST_SCALING_1_4_NAME "1:4" + #define HID_MOUSE_HOST_SCALING_1_5_NAME "1:5" + + // Names for the configuration value settings. + #define HID_MOUSE_RESOLUTION_1_1_NAME "1 c/mm" + #define HID_MOUSE_RESOLUTION_1_2_NAME "2 c/mm" + #define HID_MOUSE_RESOLUTION_1_4_NAME "4 c/mm" + #define HID_MOUSE_RESOLUTION_1_8_NAME "8 c/mm" + #define HID_MOUSE_SCALING_1_1_NAME "1:1" + #define HID_MOUSE_SCALING_2_1_NAME "2:1" + #define HID_MOUSE_SAMPLE_RATE_10_NAME "10 S/s" + #define HID_MOUSE_SAMPLE_RATE_20_NAME "20 S/s" + #define HID_MOUSE_SAMPLE_RATE_40_NAME "40 S/s" + #define HID_MOUSE_SAMPLE_RATE_60_NAME "60 S/s" + #define HID_MOUSE_SAMPLE_RATE_80_NAME "80 S/s" + #define HID_MOUSE_SAMPLE_RATE_100_NAME "100 S/s" + #define HID_MOUSE_SAMPLE_RATE_200_NAME "200 S/s" + + public: + // Types of devices the HID class can support. + enum HID_DEVICE_TYPES { + HID_DEVICE_TYPE_KEYBOARD = 0x00, + HID_DEVICE_TYPE_MOUSE = 0x01, + HID_DEVICE_TYPE_BLUETOOTH = 0x02, + }; + + // HID class can encapsulate many input device objects, only one at a time though. On startup the device is enumerated and then all + // functionality serves the device object. + enum HID_INPUT_DEVICE { + HID_DEVICE_PS2_KEYBOARD = 0x00, + HID_DEVICE_PS2_MOUSE = 0x01, + HID_DEVICE_BLUETOOTH = 0x02, + HID_DEVICE_BT_KEYBOARD = 0x03, + HID_DEVICE_BT_MOUSE = 0x04 + }; + + // Scaling - The host receiving mouse data may have a different resolution to that of the mouse, so we use configurable host side scaling to compensate. The mouse data received + // is scaled according to the enum setting. + enum HID_MOUSE_HOST_SCALING { + HID_MOUSE_HOST_SCALING_1_1 = 0x00, + HID_MOUSE_HOST_SCALING_1_2 = 0x01, + HID_MOUSE_HOST_SCALING_1_3 = 0x02, + HID_MOUSE_HOST_SCALING_1_4 = 0x03, + HID_MOUSE_HOST_SCALING_1_5 = 0x04, + }; + + // Resolution - the mouse can digitize movement from 1mm to 1/8mm, the default being 1/4 (ie. 1mm = 4 counts). This allows configuration for a finer or rougher + // tracking digitisation. + enum HID_MOUSE_RESOLUTION { + HID_MOUSE_RESOLUTION_1_1 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_1, + HID_MOUSE_RESOLUTION_1_2 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_2, + HID_MOUSE_RESOLUTION_1_4 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_4, + HID_MOUSE_RESOLUTION_1_8 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_8, + }; + + // Scaling - the mouse can provide linear (1:1 no scaling) or non liner (2:1 scaling) adaptation of the digitised data. This allows configuration for amplification of movements. + enum HID_MOUSE_SCALING { + HID_MOUSE_SCALING_1_1 = PS2Mouse::PS2_MOUSE_SCALING_1_1, + HID_MOUSE_SCALING_2_1 = PS2Mouse::PS2_MOUSE_SCALING_2_1, + }; + + // Sampling rate - the mouse, in streaming mode, the mouse sends with movement updates. This allows for finer or rougher digitisation of movements. The default is 100 samples per + // second and the X68000 is fixed at 100 samples per second. + enum HID_MOUSE_SAMPLING { + HID_MOUSE_SAMPLE_RATE_10 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_10, + HID_MOUSE_SAMPLE_RATE_20 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_20, + HID_MOUSE_SAMPLE_RATE_40 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_40, + HID_MOUSE_SAMPLE_RATE_60 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_60, + HID_MOUSE_SAMPLE_RATE_80 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_80, + HID_MOUSE_SAMPLE_RATE_100 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_100, + HID_MOUSE_SAMPLE_RATE_200 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_200, + }; + + // Suspend flag. When active, the interface components enter an idle state after completing there latest cycle. + bool suspend = false; + bool suspended = true; + + // Element to store mouse data in a queue. The data is actual mouse movements, any control data and private data for the actual mouse is stripped. + typedef struct { + int16_t xPos; + int16_t yPos; + uint8_t status; + uint8_t wheel; + } t_mouseMessageElement; + + // Prototypes. + HID(enum HID_DEVICE_TYPES, NVS *hdlNVS, LED *hdlLED, SWITCH *hdlSWITCH); + HID(NVS *hdlNVS); + HID(void); + virtual ~HID(void); + bool isBluetooth(void); + void enableBluetooth(void); + void disableBluetooth(void); + bool isSuspended(bool waitForSuspend); + void suspendInterface(bool suspendIf); + bool persistConfig(void); + uint16_t read(void); + void setMouseResolution(enum HID_MOUSE_RESOLUTION resolution); + void setMouseHostScaling(enum HID_MOUSE_HOST_SCALING scaling); + void setMouseScaling(enum HID_MOUSE_SCALING scaling); + void setMouseSampleRate(enum HID_MOUSE_SAMPLING sampleRate); + void btStartPairing(void); + void btCancelPairing(void); + + + // Method to register an object method for callback with context. + template + void setDataCallback(A func_ptr, B obj_ptr) + { + hidCtrl.dataCallback = bind(func_ptr, obj_ptr, std::placeholders::_1); + } + + // Method to suspend input device activity, yielding to the OS until suspend is cleared. + inline virtual void yield(uint32_t delay) + { + // If suspended, go into a permanent loop until the suspend flag is reset. + if(this->suspend) + { + // Suspend the keyboard interface. + if(hidCtrl.deviceType == HID_DEVICE_TYPE_KEYBOARD) { printf("SUSPEND\n"); ps2Keyboard->suspend(true); } + this->suspended = true; + + // Sleep while suspended. + while(this->suspend) + { + vTaskDelay(100); + } + + // Release the keyboard interface. + if(hidCtrl.deviceType == HID_DEVICE_TYPE_KEYBOARD) ps2Keyboard->suspend(false); + this->suspended = false; + } else + // Otherwise just delay by the required amount for timing and to give other threads a time slice. + { + vTaskDelay(delay); + } + return; + } + + // Method to see if the interface must enter suspend mode. + // + inline virtual bool suspendRequested(void) + { + return(this->suspend); + } + + // Helper method to identify the sub class, this is used in non volatile key management. + // Warning: This method wont work if optimisation for size is enabled on the compiler. + const char *getClassName(const std::string& prettyFunction) + { + // First find the CLASS :: METHOD seperation. + size_t colons = prettyFunction.find("::"); + + // None, then this is not a class. + if (colons == std::string::npos) + return "::"; + + // Split out the class name. + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = colons - begin; + + // Return the name. + return(prettyFunction.substr(begin,end).c_str()); + } + + // Template to aid in conversion of an enum to integer. + template constexpr typename std::underlying_type::type to_underlying(E e) noexcept + { + return static_cast::type>(e); + } + + // Method to return the class version number. + virtual float version(void) + { + return(HID_VERSION); + } + + // Method to return the name of this class. + virtual std::string ifName(void) + { + return(className); + } + + protected: + + private: + // Prototypes. + void init(const char *className, enum HID_DEVICE_TYPES deviceTypes); + bool nvsPersistData(const char *key, void *pData, uint32_t size); + bool nvsRetrieveData(const char *key, void *pData, uint32_t size); + bool nvsCommitData(void); + void checkKeyboard( void ); + bool checkPS2Keyboard( void ); + bool checkPS2Mouse( void ); + void checkMouse( void ); + void processPS2Mouse( void ); + void checkBTMouse( void ); + void mouseReceiveData(uint8_t src, PS2Mouse::MouseData mouseData); + IRAM_ATTR static void hidControl( void * pvParameters ); + static void btPairingHandler(uint32_t pid, uint8_t trigger); + inline uint32_t milliSeconds(void) + { + return( (uint32_t) (clock() ) ); + } + + + enum HOST_CONFIG_MODES { + HOST_CONFIG_OFF = 0x00, + HOST_CONFIG_SCALING = 0x01, + HOST_CONFIG_RESOLUTION = 0x02, + }; + + // Structure to maintain configuration for the HID. + // + typedef struct { + + struct { + // Mouse data Adjustment and filtering options. + // + enum HID_MOUSE_RESOLUTION resolution; + enum HID_MOUSE_SCALING scaling; + enum HID_MOUSE_SAMPLING sampleRate; + } mouse; + + struct { + // Host data for adjustment and configuration. + enum HID_MOUSE_HOST_SCALING scaling; + } host; + + struct { + // Configuration mode time period used to select configuration option. Once the middle key is held, the configuration option starts at 1, after this number of seconds + // the configuration option advances to the next configuration item, and so on... + uint16_t optionAdvanceDelay; + } params; + + } t_hidConfig; + + // Structure to maintain an active settings for HID devices. + typedef struct { + enum HID_INPUT_DEVICE hidDevice; // Active HID device, only one can be active. + enum HID_DEVICE_TYPES deviceType; // Type of device which is active. + bool ps2Active; // Flag to indicate PS/2 device is online and active. + uint32_t noEchoCount = 0L; // Echo back counter, used for testing if a keyboard is online. + TickType_t ps2CheckTimer = 0; // Check timer, used for timing periodic keyboard checks. + + // Mouse control variables. + uint32_t noValidMouseMessage = 0; + int wheelCnt = 0; + uint32_t loopTimer = 0; + bool middleKeyPressed = false; + PS2Mouse::MouseData mouseData; + + // Flag to indicate the mouse is active and online. + bool active; + + // Flag to indicate the configuration data has been updated. + bool updated; + + // Configuration mode selected when middle button pressed. + enum HOST_CONFIG_MODES configMode; + + // Mutex to block access during maintenance tasks. + SemaphoreHandle_t mutexInternal; + + // Callback for streaming input devices with data to be processed. + std::function dataCallback; + } t_hidControl; + + // Current configuration of the HID. + t_hidConfig hidConfig; + + // Variables to control the HID. + t_hidControl hidCtrl; + + // Handle to the persistent storage api. + nvs_handle_t nvsHandle; + + // Name of this class, used for NVS access. + std::string className; + + // NVS persistence object. + NVS *nvs; + + // LED activity object handle. + LED *led; + + // SWITCH object handle. + SWITCH *sw; + + // Keyboard object for PS/2 data retrieval and management. + PS2KeyAdvanced *ps2Keyboard; + + // Keyboard object for Bluetooth data retrieval and management. + BTHID *btHID; + + // Mouse object for PS/2 data retrieval and management. + PS2Mouse *ps2Mouse; + + // Thread handle for the HID control thread. + TaskHandle_t TaskHID = NULL; +}; +#endif // HID_H diff --git a/main/include/KeyInterface.h b/main/include/KeyInterface.h deleted file mode 120000 index 90f7212..0000000 --- a/main/include/KeyInterface.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/KeyInterface.h \ No newline at end of file diff --git a/main/include/KeyInterface.h b/main/include/KeyInterface.h new file mode 100644 index 0000000..ba6c229 --- /dev/null +++ b/main/include/KeyInterface.h @@ -0,0 +1,203 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: KeyInterface.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Virtual class definition on which all host interfaces, instantiated as a singleton, +// are based. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef KEYINTERFACE_H +#define KEYINTERFACE_H + +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_system.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "NVS.h" +#include "LED.h" +#include "HID.h" + + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Define a virtual class which acts as the base and specification of all super classes forming host +// interface objects. +class KeyInterface { + + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define KEYIF_VERSION 1.01 + + public: + // Suspend flag. When active, the interface components enter an idle state after completing there latest cycle. + bool suspend = false; + bool suspended = true; + + // NVS object. + NVS *nvs; + + // LED object. + LED *led; + + // HID object, used for keyboard input. + HID *hid; + + // Prototypes. + KeyInterface(void) {}; + virtual ~KeyInterface(void) {}; + KeyInterface(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID) { init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlLED, hdlHID, ifMode); }; + KeyInterface(NVS *hdlNVS, HID *hdlHID) { init(getClassName(__PRETTY_FUNCTION__), hdlNVS, hdlHID); }; + void reconfigADC2Ports(bool setAsOutput); + void suspendInterface(bool suspendIf); + virtual bool isSuspended(bool waitForSuspend); + virtual bool isRunning(bool waitForRelease); + virtual void identify(void) { }; + virtual void init(const char * subClassName, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, uint32_t ifMode); + virtual void init(const char * subClassName, NVS *hdlNVS, HID *hdlHID); + // Persistence. + virtual bool persistConfig(void) { return(true); } + + // Key mapping. + virtual IRAM_ATTR uint32_t mapKey(uint16_t scanCode) { return(0); }; + virtual bool createKeyMapFile(std::fstream &outFile) { return(false); }; + virtual bool storeDataToKeyMapFile(std::fstream &outFile, char *data, int size) { return(false); }; + virtual bool storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray) { return(false); } + virtual bool closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly) { return(false); }; + virtual std::string getKeyMapFileName(void) { return("nokeymap.bin"); }; + virtual void getKeyMapHeaders(std::vector& headerList) { }; + virtual void getKeyMapTypes(std::vector& typeList) { }; + virtual bool getKeyMapSelectList(std::vector>& selectList, std::string option) { return(true); } + virtual bool getKeyMapData(std::vector& dataArray, int *row, bool start) { return(true); }; + // Mouse config. + virtual void getMouseConfigTypes(std::vector& typeList) { }; + virtual bool getMouseSelectList(std::vector>& selectList, std::string option) { return(true); } + virtual bool setMouseConfigValue(std::string paramName, std::string paramValue) { return(true); } + + // Method to suspend an active interface thread by holding in a tight loop, yielding to the OS. This method was chosen rather than the more conventional + // vTaskSuspend as it allows multiple threads, without giving a handle, to yield if required for a fixed period or indefinitely until the suspend mode is de-activated. + // The method is inline to avoid a call overhead as it is generally used in time sensitive interface timing. + inline virtual void yield(uint32_t delay) + { + // If suspended, go into a permanent loop until the suspend flag is reset. + if(this->suspend) + { + this->suspended = true; + while(this->suspend) + { + vTaskDelay(100); + } + this->suspended = false; + } else + // Otherwise just delay by the required amount for timing and to give other threads a time slice. + { + vTaskDelay(delay); + } + } + + // Method to see if the interface must enter suspend mode. + // + inline virtual bool suspendRequested(void) + { + return(this->suspend); + } + + // Helper method to identify the sub class, this is used in non volatile key management. + // Warning: This method wont work if optimisation for size is enabled on the compiler. + const char *getClassName(const std::string& prettyFunction) + { + // First find the CLASS :: METHOD seperation. + size_t colons = prettyFunction.find("::"); + + // None, then this is not a class. + if (colons == std::string::npos) + return "::"; + + // Split out the class name. + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = colons - begin; + + // Return the name. + return(prettyFunction.substr(begin,end).c_str()); + } + + // Helper method to change a file extension. + void replaceExt(std::string& fileName, const std::string& newExt) + { + // Locals. + std::string::size_type extPos = fileName.rfind('.', fileName.length()); + + if(extPos != std::string::npos) + { + fileName.replace(extPos+1, newExt.length(), newExt); + } + return; + } + + // Template to aid in conversion of an enum to integer. + template constexpr typename std::underlying_type::type to_underlying(E e) noexcept + { + return static_cast::type>(e); + } + + // Method to return the class version number. + virtual float version(void) + { + return(KEYIF_VERSION); + } + + // Method to return the name of the inferface class. + virtual std::string ifName(void) + { + return(subClassName); + } + + protected: + + private: + // Prototypes. + virtual IRAM_ATTR void selectOption(uint8_t optionCode) {}; + + // Name of the sub-class for this instantiation. + std::string subClassName; + + // Thread handle for the LED control thread. + TaskHandle_t TaskLEDIF = NULL; +}; +#endif // KEYINTERFACE_H diff --git a/main/include/LED.h b/main/include/LED.h deleted file mode 120000 index d4460a3..0000000 --- a/main/include/LED.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/LED.h \ No newline at end of file diff --git a/main/include/LED.h b/main/include/LED.h new file mode 100644 index 0000000..ee9ddfe --- /dev/null +++ b/main/include/LED.h @@ -0,0 +1,181 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: LED.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Class definition for the control of a single LED. The LED is used to indicate to a +// user a desired status. This class is normally instantiated as a singleton and +// manipulated by public methods. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef LED_H +#define LED_H + +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "PS2KeyAdvanced.h" +#include "PS2Mouse.h" +#include "NVS.h" + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Define a class to encapsulate a LED and required control mechanisms, +class LED { + + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define LED_VERSION 1.01 + + public: + // Interface LED activity modes. + enum LED_MODE { + LED_MODE_OFF = 0x00, + LED_MODE_ON = 0x01, + LED_MODE_BLINK_ONESHOT = 0x02, + LED_MODE_BLINK = 0x03, + }; + + // Interface LED duty cycle. + enum LED_DUTY_CYCLE { + LED_DUTY_CYCLE_OFF = 0x00, + LED_DUTY_CYCLE_10 = 0x01, + LED_DUTY_CYCLE_20 = 0x02, + LED_DUTY_CYCLE_30 = 0x03, + LED_DUTY_CYCLE_40 = 0x04, + LED_DUTY_CYCLE_50 = 0x05, + LED_DUTY_CYCLE_60 = 0x06, + LED_DUTY_CYCLE_70 = 0x07, + LED_DUTY_CYCLE_80 = 0x08, + LED_DUTY_CYCLE_90 = 0x09, + }; + + // Prototypes. + LED(uint32_t hwPin); + LED(void); + virtual ~LED(void) {}; + void identify(void) { }; + + // LED Control. + bool setLEDMode(enum LED_MODE mode, enum LED_DUTY_CYCLE dutyCycle, uint32_t maxBlinks, uint64_t usDutyPeriod, uint64_t msInterPeriod); + IRAM_ATTR static void ledInterface(void *pvParameters); + void ledInit(uint8_t ledPin); + + // Helper method to identify the sub class, this is used in non volatile key management. + // Warning: This method wont work if optimisation for size is enabled on the compiler. + const char *getClassName(const std::string& prettyFunction) + { + // First find the CLASS :: METHOD seperation. + size_t colons = prettyFunction.find("::"); + + // None, then this is not a class. + if (colons == std::string::npos) + return "::"; + + // Split out the class name. + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = colons - begin; + + // Return the name. + return(prettyFunction.substr(begin,end).c_str()); + } + + // Template to aid in conversion of an enum to integer. + template constexpr typename std::underlying_type::type to_underlying(E e) noexcept + { + return static_cast::type>(e); + } + + // Method to return the class version number. + virtual float version(void) + { + return(LED_VERSION); + } + + // Method to return the name of the inferface class. + virtual std::string ifName(void) + { + return(className); + } + + protected: + + private: + // Prototypes. + + // Structure to maintain configuration for the LED. + // + typedef struct { + bool valid; // The configuration is valid and should be processed. + bool updated; // This configuration is an update to override current configuration. + + enum LED_MODE mode; // Mode of LED activity. + enum LED_DUTY_CYCLE dutyCycle; // Duty cycle of the BLINK LED period. + uint32_t maxBlinks; // Maximum number of blinks before switching to LED off mode. + uint64_t dutyPeriod; // Period, is micro-seconds of the full duty cycle. + uint64_t interPeriod; // Period, is milli-seconds between LED activity. + } t_ledConfig; + + // Structure to maintain an active setting for the LED. The LED control thread uses these values to effect the required lighting of the LED. + typedef struct { + // Current, ie. working LED config acted upon by the LED thread. + t_ledConfig currentConfig; + // New config to replace current on next state. + t_ledConfig newConfig; + + // Led GPIO pin. + uint8_t ledPin; + + // Runtime parameters for state machine and control. + uint32_t blinkCnt; // count of blink on periods. + + // Mutex to block access to limit one thread at a time. + SemaphoreHandle_t mutexInternal; + } t_ledControl; + + // Variables to control the LED. + t_ledControl ledCtrl; + + // Name of the class for this instantiation. + std::string className; + + // Thread handle for the LED control thread. + TaskHandle_t TaskLEDIF = NULL; +}; +#endif // LED_H diff --git a/main/include/MZ2528.h b/main/include/MZ2528.h deleted file mode 120000 index df69112..0000000 --- a/main/include/MZ2528.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/MZ2528.h \ No newline at end of file diff --git a/main/include/MZ2528.h b/main/include/MZ2528.h new file mode 100644 index 0000000..851d809 --- /dev/null +++ b/main/include/MZ2528.h @@ -0,0 +1,534 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: MZ2528.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the MZ-2500/MZ-2800 PS/2 logic. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect bluetooth. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef MZ2528_H +#define MZ2528_H + +// Include the specification class. +#include "KeyInterface.h" +#include "NVS.h" +#include "LED.h" +#include "HID.h" +#include +#include + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Encapsulate the MZ-2500/MZ-2800 interface. +class MZ2528 : public KeyInterface { + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define MZ2528IF_VERSION 1.02 + #define MZ2528IF_KEYMAP_FILE "MZ2528_KeyMap.BIN" + #define PS2TBL_MZ_MAXROWS 165 + #define PS2TBL_MZ_MAX_MKROW 3 + #define PS2TBL_MZ_MAX_BRKROW 2 + + // PS2 Flag definitions. + #define PS2CTRL_NONE 0x00 // No keys active = 0 + #define PS2CTRL_SHIFT 0x01 // Shfit Key active = 1 + #define PS2CTRL_CTRL 0x02 // Ctrl Key active = 1 + #define PS2CTRL_CAPS 0x04 // CAPS active = 1 + #define PS2CTRL_ALT 0x08 // ALT active = 1 + #define PS2CTRL_ALTGR 0x10 // ALTGR active = 1 + #define PS2CTRL_GUI 0x20 // GUI Key active = 1 + #define PS2CTRL_FUNC 0x40 // Special Function Keys active = 1 + #define PS2CTRL_BREAK 0x80 // BREAK Key active = 1 + #define PS2CTRL_EXACT 0x80 // EXACT Match active = 1 + + // The MZ-2500 machine can emulate 3 models, the MZ-80B, MZ-2000 and the MZ-2500. The MZ-2800 provides a new mode as well as the MZ-2500 mode and each has slight + // keyboard differences. This requires tagging of machine specific mappings. Normally a mapping would be MZ_ALL, ie. applied to all models, but if a machine specific + // mapping appears and it matches the current machine mode, this mapping is chosen. + #define MZ_ALL 0xFF + #define MZ_80B 0x01 + #define MZ_2000 0x02 + #define MZ_2500 0x04 + #define MZ_2800 0x08 + + // Keyboard mapping table select list for target machine. + #define MZ2528_SEL_ALL "ALL" + #define MZ2528_SEL_MZ_80B "MZ80B" + #define MZ2528_SEL_MZ_2000 "MZ2000" + #define MZ2528_SEL_MZ_2500 "MZ2500" + #define MZ2528_SEL_MZ_2800 "MZ2800" + + // The initial mapping is made inside the PS2KeyAdvanced class from Scan Code Set 2 to ASCII + // for a selected keyboard. Special functions are detected and combined inside this module + // before mapping with the table below to MZ Scan Matrix. + // ie. PS/2 Scan Code -> ASCII + Flags -> MZ Scan Matrix + + + // Keyboard mapping table column names. + #define PS2TBL_PS2KEYCODE_NAME "PS/2 KeyCode" + #define PS2TBL_PS2CTRL_NAME "PS/2 Control Key" + #define PS2TBL_KEYBOARDMODEL_NAME "For Keyboard" + #define PS2TBL_MACHINE_NAME "For Host Model" + #define PS2TBL_MZ_MK_ROW1_NAME "Make Row 1" + #define PS2TBL_MZ_MK_KEY1_NAME "Key 1" + #define PS2TBL_MZ_MK_ROW2_NAME "Row 2" + #define PS2TBL_MZ_MK_KEY2_NAME "Key 2" + #define PS2TBL_MZ_MK_ROW3_NAME "Row 3" + #define PS2TBL_MZ_MK_KEY3_NAME "Key 3" + #define PS2TBL_MZ_BRK_ROW1_NAME "Break Row 1" + #define PS2TBL_MZ_BRK_KEY1_NAME "Key 1" + #define PS2TBL_MZ_BRK_ROW2_NAME "Row 2" + #define PS2TBL_MZ_BRK_KEY2_NAME "Key 2" + + // Keyboard mapping table column types. + #define PS2TBL_PS2KEYCODE_TYPE "hex" + #define PS2TBL_PS2CTRL_TYPE "custom_cbp_ps2ctrl" + #define PS2TBL_KEYBOARDMODEL_TYPE "custom_cbp_keybmodel" + #define PS2TBL_MACHINE_TYPE "custom_cbp_machine" + #define PS2TBL_MZ_MK_ROW1_TYPE "custom_rdp_mzrow" + #define PS2TBL_MZ_MK_KEY1_TYPE "hex" + #define PS2TBL_MZ_MK_ROW2_TYPE "custom_rdp_mzrow" + #define PS2TBL_MZ_MK_KEY2_TYPE "hex" + #define PS2TBL_MZ_MK_ROW3_TYPE "custom_rdp_mzrow" + #define PS2TBL_MZ_MK_KEY3_TYPE "hex" + #define PS2TBL_MZ_BRK_ROW1_TYPE "custom_rdp_mzrow" + #define PS2TBL_MZ_BRK_KEY1_TYPE "hex" + #define PS2TBL_MZ_BRK_ROW2_TYPE "custom_rdp_mzrow" + #define PS2TBL_MZ_BRK_KEY2_TYPE "hex" + + // Keyboard mapping table select list for PS2CTRL. + #define PS2TBL_PS2CTRL_SEL_NONE "NONE" + #define PS2TBL_PS2CTRL_SEL_SHIFT "SHIFT" + #define PS2TBL_PS2CTRL_SEL_CTRL "CTRL" + #define PS2TBL_PS2CTRL_SEL_CAPS "CAPS" + #define PS2TBL_PS2CTRL_SEL_ALT "ALT" + #define PS2TBL_PS2CTRL_SEL_ALTGR "ALTGR" + #define PS2TBL_PS2CTRL_SEL_GUI "GUI" + #define PS2TBL_PS2CTRL_SEL_FUNC "FUNC" + #define PS2TBL_PS2CTRL_SEL_EXACT "EXACT" + + // Keyboard mapping table select list for Model of keyboard. + #define KEYMAP_SEL_STANDARD "ALL" + #define KEYMAP_SEL_UK_WYSE_KB3926 "UK_WYSE_KB3926" + #define KEYMAP_SEL_JAPAN_OADG109 "JAPAN_OADG109" + #define KEYMAP_SEL_JAPAN_SANWA_SKBL1 "JAPAN_SANWA_SKBL1" + #define KEYMAP_SEL_NOT_ASSIGNED_4 "KEYBOARD_4" + #define KEYMAP_SEL_NOT_ASSIGNED_5 "KEYBOARD_5" + #define KEYMAP_SEL_NOT_ASSIGNED_6 "KEYBOARD_6" + #define KEYMAP_SEL_UK_PERIBOARD_810 "UK_PERIBOARD_810" + #define KEYMAP_SEL_UK_OMOTON_K8508 "UK_OMOTON_K8508" + + // Keyboard models. The base on which this interface was created was a Wyse KB3926 PS/2 Keyboard and this is deemed STANDARD. Other models need to insert difference maps + // prior to the STANDARD entry along with the keyboard model so that it is processed first thus allowing differing keyboards with different maps. + #define KEYMAP_STANDARD 0xFF + #define KEYMAP_UK_WYSE_KB3926 0x01 + #define KEYMAP_JAPAN_OADG109 0x02 + #define KEYMAP_JAPAN_SANWA_SKBL1 0x04 + #define KEYMAP_NOT_ASSIGNED_4 0x08 + #define KEYMAP_NOT_ASSIGNED_5 0x10 + #define KEYMAP_NOT_ASSIGNED_6 0x20 + #define KEYMAP_UK_PERIBOARD_810 0x40 + #define KEYMAP_UK_OMOTON_K8508 0x80 + + public: + // Prototypes. + MZ2528(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char *fsPath); + MZ2528(NVS *hdlNVS, HID *hdlHID, const char *fsPath); + MZ2528(void); + ~MZ2528(void); + bool createKeyMapFile(std::fstream &outFile); + bool storeDataToKeyMapFile(std::fstream &outFile, char *data, int size); + bool storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray); + bool closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly); + std::string getKeyMapFileName(void) { return(MZ2528IF_KEYMAP_FILE); }; + void getKeyMapHeaders(std::vector& headerList); + void getKeyMapTypes(std::vector& typeList); + bool getKeyMapSelectList(std::vector>& selectList, std::string option); + bool getKeyMapData(std::vector& dataArray, int *row, bool start); + + // Overloaded method to see if the interface must enter suspend mode, either triggered by an external event or internal. + // + inline bool suspendRequested(void) + { + return(this->suspend); + } + +// // Method to overload the suspend mechanism and include the core release mechanism. Core release is needed in order to use ESP32 API's such as NVS. +// // The method is inline to avoid a call overhead as it is generally used in time sensitive interface timing. +// inline void yield(uint32_t delay) +// { +// // If suspended, go into a permanent loop until the suspend flag is reset. +// if(this->suspend) +// { +// this->suspended = true; +// while(this->suspend) +// { +// vTaskDelay(100); +// } +// this->suspended = false; +// } else +// // Otherwise just delay by the required amount for timing and to give other threads a time slice. +// { + // vTaskDelay(delay); +// } +// return; +// } + + // Method to return the class version number. + float version(void) + { + return(MZ2528IF_VERSION); + } + + protected: + + private: + // Prototypes. + void updateMirrorMatrix(void); + uint32_t mapKey(uint16_t scanCode); + IRAM_ATTR static void mz25Interface(void *pvParameters ); + IRAM_ATTR static void mz28Interface(void *pvParameters ); + IRAM_ATTR static void hidInterface(void *pvParameters ); + void selectOption(uint8_t optionCode); + bool loadKeyMap(); + bool saveKeyMap(void); + void init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + void init(NVS *hdlNVS, HID *hdlHID); + + // Overload the base yield method to include suspension of the PS/2 Keyboard interface. This interface uses interrupts which are not mutex protected and clash with the + // WiFi API methods. +// inline void yield(uint32_t delay) +// { +// // If suspended, go into a permanent loop until the suspend flag is reset. +// if(this->suspend) +// { +// // Suspend the keyboard interface. +// Keyboard->suspend(true); +// +// // Use the base method logic. +// KeyInterface::yield(delay); +// +// // Release the keyboard interface. +// Keyboard->suspend(false); +// } else +// // Otherwise just delay by the required amount for timing and to give other threads a time slice. +// { +// KeyInterface::yield(delay); +// } +// return; +// } + + // Structure to encapsulate a single key map from PS/2 to MZ-2500/MZ-2800. + typedef struct { + uint8_t ps2KeyCode; + uint8_t ps2Ctrl; + uint8_t keyboardModel; + uint8_t machine; + uint8_t mkRow[PS2TBL_MZ_MAX_MKROW]; + uint8_t mkKey[PS2TBL_MZ_MAX_MKROW]; + uint8_t brkRow[PS2TBL_MZ_MAX_BRKROW]; + uint8_t brkKey[PS2TBL_MZ_MAX_BRKROW]; + } t_keyMapEntry; + + // Structure to encapsulate the entire static keyboard mapping table. + typedef struct { + t_keyMapEntry kme[PS2TBL_MZ_MAXROWS]; + } t_keyMap; + + // Structure to maintain the MZ2528 interface configuration data. This data is persisted through powercycles as needed. + typedef struct { + struct { + uint8_t activeKeyboardMap; // Model of keyboard a keymap entry is applicable to. + uint8_t activeMachineModel; // Machine model a keymap entry is applicable to. + } params; + } t_mzConfig; + + // Configuration data. + t_mzConfig mzConfig; + + // Structure to manage the translated key matrix. This is updated by the ps2Interface thread and read by the mzInterface thead. + typedef struct { + uint8_t strobeAll; // Strobe All flag, 16 possible rows have the same column AND'd together to create this 8bit map. It is used to see if any key has been pressed. + uint32_t strobeAllAsGPIO; // Strobe All signal but as a GPIO bit map to save time in the interface thread. + uint8_t keyMatrix[16]; // Key matrix as a 16x8 matrix. + uint32_t keyMatrixAsGPIO[16]; // Key matrix mapped as GPIO bits to save time in the interface thread. + bool mode2500; + bool optionSelect; // Flag to indicate a user requested keyboard configuration option is being selected. + std::string fsPath; // Path on the underlying filesystem where storage is mounted and accessible. + t_keyMapEntry *kme; // Pointer to an array in memory to contain PS2 to MZ-2500/MZ-2800 mapping values. + int kmeRows; // Number of rows in the kme table. + std::string keyMapFileName; // Name of file where extension or replacement key map entries are stored. + bool noKeyPressed; // Flag to indicate no key has been pressed. + bool persistConfig; // Flag to request saving of the config into NVS storage. + } t_mzControl; + + // Thread handles - one per function, ie. HID interface and host target interface. + TaskHandle_t TaskHostIF = NULL; + TaskHandle_t TaskHIDIF = NULL; + + // Control structure to control interaction and mapping of keys for the host. + t_mzControl mzControl; + + // Spin lock mutex to hold a coresied to an uninterruptable method. This only works on dual core ESP32's. + portMUX_TYPE mzMutex; + + // Flag to indicate host interface should yield the CPU. + volatile bool yieldHostInterface; + +// // Keyboard object for PS/2 data retrieval and management. +// PS2KeyAdvanced *Keyboard; + + // HID object, used for keyboard input. +// HID *hid; + + // Lookup table to matrix row/column co-ordinates. + // + // Given that the MZ-2500 can emulate 3 machines and each machine has it's own mapping, differences are tagged by machine name, ie. ALL, MZ80B, MZ2000, MZ2500 + // + // If a PS2 key is matched, then the Matrix is updated using MK_ROW to point into the array with MK_KEY being the column value, equivalent of strobe line and + // the required KEY bits to be set. Upto 3 matrix bits can be set (3 key presses on the MZ-2500 keyboard) per PS/2 key. Upto 2 matrix releases can be set per + // PS/2 key. A key release is used when a modifier may already have been pressed, ie. SHIFT and it needs to be released to set the required key into the matrix. + // A set bit = 1, reset bits = 0 but is inverted in the actual matrix (1 = inactive, 0 = active), this applies for releases two, if bit = 1 then that key will be released. + // The table is scanned for a match from top to bottom. The first match is used so order is important. Japanese characters are being added as I gleam more information. + + /////////////////////////// + // MZ-2500 Keyboard Map. // + /////////////////////////// + // + // Row D7 D6 D5 D4 D3 D2 D1 D0 + //---------------------------------------------------------------------------------- + // 0 F8 F7 F6 F5 F4 F3 F2 F1 + // 1 KP - KP + KP . KP , KP 9 KP 8 F1O F9 + // 2 KP 7 KP 6 KP 5 KP 4 KP 3 KP 2 KP 1 KP 0 + // 3 BREAK RIGHT LEFT DOWN UP RETURN SPACE TAB + // 4 G F E D C B A / ? + // 5 O N M L K J I H + // 6 W V U T S R Q P + // 7 , < . > _ YEN | ^ '¿ Z ¿ Y X ¿ + // 8 7 ' 6 & 5 % 4 $ 3 # 2 " 1 ! 0 + // 9 [ { @ ` - = ; + : * 9 ) 8 ( + // 10 KP / KP * ESC BACKSPACE INST/DEL CLR/HOME COPY ] } + // 11 CTRL KANA SHIFT LOCK GRAPH + // 12 KJ2 KJ1 + // 13 HELP ARGO + // + // Col 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + // -------------------------------------------------------------------------------------------------------------------------------------- + // D0 F1 F9 KP 0 TAB / ? H P X 0 8 ( ] } GRAPH KJ1 ARGO + // D1 F2 F10 KP 1 SPACE A I Q Y 1 ! 9 ) COPY LOCK KJ2 HELP + // D2 F3 KP 8 KP 2 RETURN B J R Z 2 " : * CLR/HOME SHIFT + // D3 F4 KP 9 KP 3 UP C K S ^ '¿ 3 # ; + INST/DEL KANA + // D4 F5 KP , KP 4 DOWN D L T YEN | 4 $ - = BACKSPACE CTRL + // D5 F6 KP . KP 5 LEFT E M U _ 5 % @ ` ESC + // D6 F7 KP + KP 6 RIGHT F N V . > 6 & [ { KP * + // D7 F8 KP - KP 7 BREAK G O W , < 7 ' KP / + // + // This initial mapping is for the UK Wyse KB-3926 PS/2 keyboard and his equates to KEYMAP_STANDARD. + // + t_keyMap PS2toMZ = { + { + // < Keys to be applied on match > < Keys to be reset on match > + // PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MK_ROW1 MK_ROW2 MK_ROW3 MK_KEY1 MK_KEY2 MK_KEY3 BRK_ROW1 BRK_ROW2 BRK_KEY1 BRK_KEY2 + { PS2_KEY_F1, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F1 + { PS2_KEY_F2, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F2 + { PS2_KEY_F3, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F3 + { PS2_KEY_F4, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F4 + { PS2_KEY_F5, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F5 + { PS2_KEY_F6, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F6 + { PS2_KEY_F7, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F7 + { PS2_KEY_F8, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x00, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F8 + { PS2_KEY_F9, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F9 + { PS2_KEY_F10, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F10 + { PS2_KEY_F11, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0D, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // HELP + { PS2_KEY_F12, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // COPY + { PS2_KEY_TAB, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // TAB + { PS2_KEY_0, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x02, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Close Right Bracket ) + { PS2_KEY_0, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 0 + { PS2_KEY_1, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x02, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Exclamation + { PS2_KEY_1, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 1 + { PS2_KEY_2, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Double quote. + { PS2_KEY_2, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 2 + { PS2_KEY_3, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Pound Sign -> Hash + { PS2_KEY_3, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 3 + { PS2_KEY_4, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Dollar + { PS2_KEY_4, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 4 + { PS2_KEY_5, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x20, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Percent + { PS2_KEY_5, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 5 + { PS2_KEY_6, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // Kappa + { PS2_KEY_6, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 6 + { PS2_KEY_7, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x40, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Ampersand + { PS2_KEY_7, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 7 + { PS2_KEY_8, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Star + { PS2_KEY_8, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 8 + { PS2_KEY_9, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Open Left Bracket ( + { PS2_KEY_9, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // 9 + { PS2_KEY_A, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // a + { PS2_KEY_A, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // A + { PS2_KEY_B, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // b + { PS2_KEY_B, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // B + { PS2_KEY_C, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // c + { PS2_KEY_C, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // C + { PS2_KEY_D, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // d + { PS2_KEY_D, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // D + { PS2_KEY_E, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // e + { PS2_KEY_E, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // E + { PS2_KEY_F, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // f + { PS2_KEY_F, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // F + { PS2_KEY_G, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // g + { PS2_KEY_G, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // G + { PS2_KEY_H, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // h + { PS2_KEY_H, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // H + { PS2_KEY_I, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // i + { PS2_KEY_I, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // I + { PS2_KEY_J, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // j + { PS2_KEY_J, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // J + { PS2_KEY_K, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // k + { PS2_KEY_K, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // K + { PS2_KEY_L, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // l + { PS2_KEY_L, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // L + { PS2_KEY_M, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // m + { PS2_KEY_M, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // M + { PS2_KEY_N, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // n + { PS2_KEY_N, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // N + { PS2_KEY_O, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // o + { PS2_KEY_O, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x05, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // O + { PS2_KEY_P, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // p + { PS2_KEY_P, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // P + { PS2_KEY_Q, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // q + { PS2_KEY_Q, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Q + { PS2_KEY_R, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // r + { PS2_KEY_R, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // R + { PS2_KEY_S, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // s + { PS2_KEY_S, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // S + { PS2_KEY_T, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // t + { PS2_KEY_T, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // T + { PS2_KEY_U, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // u + { PS2_KEY_U, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // U + { PS2_KEY_V, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // v + { PS2_KEY_V, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // V + { PS2_KEY_W, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // w + { PS2_KEY_W, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x06, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // W + { PS2_KEY_X, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // x + { PS2_KEY_X, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // X + { PS2_KEY_Y, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // y + { PS2_KEY_Y, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Y + { PS2_KEY_Z, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // z + { PS2_KEY_Z, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Z + // PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MK_ROW1 MK_ROW2 MK_ROW3 MK_KEY1 MK_KEY2 MK_KEY3 BRK_ROW1 BRK_ROW2 BRK_KEY1 BRK_KEY2 + { PS2_KEY_SPACE, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Space + { PS2_KEY_COMMA, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0x0B, 0xFF, 0x80, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Less Than < + { PS2_KEY_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Comma , + { PS2_KEY_SEMI, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // Colon : + { PS2_KEY_SEMI, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Semi-Colon ; + { PS2_KEY_DOT, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0x0B, 0xFF, 0x40, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Greater Than > + { PS2_KEY_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Full stop . + { PS2_KEY_DIV, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_2000, 0x07, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // Question ? + { PS2_KEY_DIV, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_80B, 0x07, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // Question ? + { PS2_KEY_DIV, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x04, 0x0B, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Question ? + { PS2_KEY_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x04, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Divide / + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_2000, 0x08, 0x0B, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Upper bar + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_80B, 0x08, 0x0B, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Upper bar + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // Underscore + { PS2_KEY_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // + + { PS2_KEY_APOS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_80B, 0x09, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // At @ + { PS2_KEY_APOS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0x0B, 0xFF, 0x04, 0xFF, }, // At @ + { PS2_KEY_APOS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x80, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Single quote ' + + { PS2_KEY_OPEN_SQ, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x40, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Open Left Brace { + { PS2_KEY_OPEN_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Open Left Square Bracket [ + { PS2_KEY_EQUAL, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Plus + + { PS2_KEY_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Equal = + { PS2_KEY_CAPS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x0B, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // LOCK + { PS2_KEY_ENTER, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // ENTER/RETURN + { PS2_KEY_CLOSE_SQ, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0x0B, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Close Right Brace } + { PS2_KEY_CLOSE_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Close Right Square Bracket ] + { PS2_KEY_BACK, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0x0B, 0xFF, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // + { PS2_KEY_BACK, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x07, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Back slash maps to Yen + { PS2_KEY_BTICK, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0x07, 0x0B, 0xFF, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Pipe + { PS2_KEY_BTICK, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x09, 0x0B, 0xFF, 0x20, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Back tick ` + { PS2_KEY_HASH, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_2000, 0x07, 0x0B, 0xFF, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Tilde + { PS2_KEY_HASH, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_80B, 0x07, 0x0B, 0xFF, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Tilde + { PS2_KEY_HASH, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Tilde has no mapping. + { PS2_KEY_HASH, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x08, 0x0B, 0xFF, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Hash + { PS2_KEY_BS, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Backspace + { PS2_KEY_ESC, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // ESCape + { PS2_KEY_SCROLL, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Not assigned. + { PS2_KEY_INSERT, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0x0B, 0xFF, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // INSERT + { PS2_KEY_HOME, PS2CTRL_FUNC | PS2CTRL_SHIFT | PS2CTRL_EXACT, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0x0B, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // CLR + { PS2_KEY_HOME, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // HOME + { PS2_KEY_PGUP, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Not assigned. + { PS2_KEY_DELETE, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // DELETE + { PS2_KEY_END, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Not assigned. + { PS2_KEY_PGDN, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_80B|MZ_2000|MZ_2500, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Not mapped + { PS2_KEY_PGDN, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_2800, 0x0C, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Japanese Key - Previous + { PS2_KEY_UP_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Up Arrow + { PS2_KEY_L_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Left Arrow + { PS2_KEY_DN_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Down Arrow + { PS2_KEY_R_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Right Arrow + { PS2_KEY_NUM, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Not assigned. + + // Keypad. + { PS2_KEY_KP0, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 0 + { PS2_KEY_KP1, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 1 + { PS2_KEY_KP2, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 2 + { PS2_KEY_KP3, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 3 + { PS2_KEY_KP4, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 4 + { PS2_KEY_KP5, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 5 + { PS2_KEY_KP6, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 6 + { PS2_KEY_KP7, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x02, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 7 + { PS2_KEY_KP8, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 8 + { PS2_KEY_KP9, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad 9 + { PS2_KEY_KP_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Comma , + { PS2_KEY_KP_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x01, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x0A, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Divide / + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Keypad Ebter / + + // PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MK_ROW1 MK_ROW2 MK_ROW3 MK_KEY1 MK_KEY2 MK_KEY3 BRK_ROW1 BRK_ROW2 BRK_KEY1 BRK_KEY2 + // Special keys. + { PS2_KEY_PRTSCR, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0D, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // ARGO KEY + { PS2_KEY_PAUSE, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // BREAK KEY + { PS2_KEY_L_GUI, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, MZ_ALL, 0x0B, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // GRAPH KEY + { PS2_KEY_L_ALT, PS2CTRL_FUNC | PS2CTRL_ALT, KEYMAP_STANDARD, MZ_ALL, 0x0C, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // KJ1 Sentence + { PS2_KEY_R_ALT, PS2CTRL_FUNC | PS2CTRL_ALTGR, KEYMAP_STANDARD, MZ_ALL, 0x0C, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // KJ2 Transform + { PS2_KEY_R_GUI, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, MZ_ALL, 0x0B, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // KANA KEY + { PS2_KEY_MENU, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Not assigned. + // Modifiers are last, only being selected if an earlier match isnt made. + { PS2_KEY_L_SHIFT, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0B, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + { PS2_KEY_R_SHIFT, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0B, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + { PS2_KEY_L_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_ALL, 0x0B, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + { PS2_KEY_R_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_80B|MZ_2000|MZ_2500, 0x0B, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Map to Control + { PS2_KEY_R_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ_2800, 0x0C, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, // Japanese Key - Cancel + { 0, PS2CTRL_NONE, KEYMAP_STANDARD, MZ_ALL, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + }}; +}; + +#endif // MZ2528_H diff --git a/main/include/MZ5665.h b/main/include/MZ5665.h deleted file mode 120000 index 24ca752..0000000 --- a/main/include/MZ5665.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/MZ5665.h \ No newline at end of file diff --git a/main/include/MZ5665.h b/main/include/MZ5665.h new file mode 100644 index 0000000..1fa6bb9 --- /dev/null +++ b/main/include/MZ5665.h @@ -0,0 +1,574 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: MZ5665.h +// Created: Apr 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the Sharp MZ-6500 to HID (PS/2, Bluetooth) interface logic. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Apr 2022 - Initial write. +// v1.01 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef MZ5665_H +#define MZ5665_H + +// Include the specification class. +#include "KeyInterface.h" +#include "NVS.h" +#include "LED.h" +#include "HID.h" +#include +#include + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Encapsulate the Sharp MZ-6500 interface. +class MZ5665 : public KeyInterface { + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define MZ5665IF_VERSION 1.01 + #define MZ5665IF_KEYMAP_FILE "MZ5665_KeyMap.BIN" + #define MAX_MZ5665_XMIT_KEY_BUF 16 + #define PS2TBL_MZ5665_MAXROWS 349 + + // MZ-6500 Key control bit mask. + #define MZ5665_CTRL_GRAPH ((unsigned char) (1 << 4)) + #define MZ5665_CTRL_CAPS ((unsigned char) (1 << 3)) + #define MZ5665_CTRL_KANA ((unsigned char) (1 << 2)) + #define MZ5665_CTRL_SHIFT ((unsigned char) (1 << 1)) + #define MZ5665_CTRL_CTRL ((unsigned char) (1 << 0)) + + // Special key definition. + #define MZ5665_KEY_UP 0x1E // ↑ + #define MZ5665_KEY_DOWN 0x1F // ↓ + #define MZ5665_KEY_LEFT 0x1D // ← + #define MZ5665_KEY_RIGHT 0x1C // → → + #define MZ5665_KEY_INS 0x12 // INS + #define MZ5665_KEY_DEL 0x08 // DEL + #define MZ5665_KEY_CLR 0x0C // CLR + #define MZ5665_KEY_HOME 0x0B // HOME + + // PS2 Flag definitions. + #define PS2CTRL_NONE 0x00 // No keys active = 0 + #define PS2CTRL_SHIFT 0x01 // Shfit Key active = 1 + #define PS2CTRL_CTRL 0x02 // Ctrl Key active = 1 + #define PS2CTRL_CAPS 0x04 // CAPS active = 1 + #define PS2CTRL_KANA 0x08 // KANA active = 1 + #define PS2CTRL_GRAPH 0x10 // GRAPH active = 1 + #define PS2CTRL_GUI 0x20 // GUI Key active = 1 + #define PS2CTRL_FUNC 0x40 // Special Function Keys active = 1 + #define PS2CTRL_BREAK 0x80 // BREAK Key active = 1 + #define PS2CTRL_EXACT 0x80 // EXACT Match active = 1 + + // The initial mapping is made inside the PS2KeyAdvanced class from Scan Code Set 2 to ASCII + // for a selected keyboard. Special functions are detected and combined inside this module + // before mapping with the table below to extract the MZ-6500 key code and control data. + // ie. PS/2 Scan Code -> ASCII + Flags -> MZ-6500 Key Code + Ctrl Data + + // Keyboard mapping table column names. + #define PS2TBL_PS2KEYCODE_NAME "PS/2 KeyCode" + #define PS2TBL_PS2CTRL_NAME "PS/2 Control Key" + #define PS2TBL_KEYBOARDMODEL_NAME "For Keyboard" + #define PS2TBL_MACHINE_NAME "For Host Model" + #define PS2TBL_MZ5665_KEYCODE_NAME "MZ5665 KeyCode" + #define PS2TBL_MZ5665__CTRL_NAME "MZ5665 Control Key" + + // Keyboard mapping table column types. + #define PS2TBL_PS2KEYCODE_TYPE "hex" + #define PS2TBL_PS2CTRL_TYPE "custom_cbp_ps2ctrl" + #define PS2TBL_KEYBOARDMODEL_TYPE "custom_cbp_keybmodel" + #define PS2TBL_MACHINE_TYPE "custom_cbp_machine" + #define PS2TBL_MZ5665_KEYCODE_TYPE "hex" + #define PS2TBL_MZ5665_CTRL_TYPE "custom_cbn_x1ctrl" + + // Keyboard mapping table select list for PS2CTRL. + #define PS2TBL_PS2CTRL_SEL_NONE "NONE" + #define PS2TBL_PS2CTRL_SEL_SHIFT "SHIFT" + #define PS2TBL_PS2CTRL_SEL_CTRL "CTRL" + #define PS2TBL_PS2CTRL_SEL_CAPS "CAPS" + #define PS2TBL_PS2CTRL_SEL_KANA "KANA" + #define PS2TBL_PS2CTRL_SEL_GRAPH "GRAPH" + #define PS2TBL_PS2CTRL_SEL_GUI "GUI" + #define PS2TBL_PS2CTRL_SEL_FUNC "FUNC" + #define PS2TBL_PS2CTRL_SEL_EXACT "EXACT" + + // Keyboard mapping table select list for Model of keyboard. + #define KEYMAP_SEL_STANDARD "ALL" + #define KEYMAP_SEL_UK_WYSE_KB3926 "UK_WYSE_KB3926" + #define KEYMAP_SEL_JAPAN_OADG109 "JAPAN_OADG109" + #define KEYMAP_SEL_JAPAN_SANWA_SKBL1 "JAPAN_SANWA_SKBL1" + #define KEYMAP_SEL_NOT_ASSIGNED_4 "KEYBOARD_4" + #define KEYMAP_SEL_NOT_ASSIGNED_5 "KEYBOARD_5" + #define KEYMAP_SEL_NOT_ASSIGNED_6 "KEYBOARD_6" + #define KEYMAP_SEL_UK_PERIBOARD_810 "UK_PERIBOARD_810" + #define KEYMAP_SEL_UK_OMOTON_K8508 "UK_OMOTON_K8508" + + // Keyboard mapping table select list for target machine. + #define MZ5665_SEL_ALL "ALL" + + // Keyboard mapping table select list for MZ5665 Control codes. + #define MZ5665_CTRL_SEL_GRAPH "GRAPH" + #define MZ5665_CTRL_SEL_CAPS "CAPS" + #define MZ5665_CTRL_SEL_KANA "KANA" + #define MZ5665_CTRL_SEL_SHIFT "SHIFT" + #define MZ5665_CTRL_SEL_CTRL "CTRL" + + // The Sharp MZ-6500 Series was released over a number of years and each iteration added changes/updates. In order to cater for differences, it is possible to assign a key mapping + // to a specific machine type(s) or all of the series by adding the flags below into the mapping table. + #define MZ5665_ALL 0xFF + + // Keyboard models. The base on which this interface was created was a Wyse KB3926 PS/2 Keyboard and this is deemed STANDARD. Other models need to insert difference maps + // prior to the STANDARD entry along with the keyboard model so that it is processed first thus allowing differing keyboards with different maps. + #define KEYMAP_STANDARD 0xFF + #define KEYMAP_UK_WYSE_KB3926 0x01 + #define KEYMAP_JAPAN_OADG109 0x02 + #define KEYMAP_JAPAN_SANWA_SKBL1 0x04 + #define KEYMAP_NOT_ASSIGNED_4 0x08 + #define KEYMAP_NOT_ASSIGNED_5 0x10 + #define KEYMAP_NOT_ASSIGNED_6 0x20 + #define KEYMAP_UK_PERIBOARD_810 0x40 + #define KEYMAP_UK_OMOTON_K8508 0x80 + + public: + // Prototypes. + MZ5665(void); + MZ5665(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char *fsPath); + MZ5665(NVS *hdlNVS, HID *hdlHID, const char *fsPath); + ~MZ5665(void); + bool createKeyMapFile(std::fstream &outFile); + bool storeDataToKeyMapFile(std::fstream &outFile, char *data, int size); + bool storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray); + bool closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly); + std::string getKeyMapFileName(void) { return(MZ5665IF_KEYMAP_FILE); }; + void getKeyMapHeaders(std::vector& headerList); + void getKeyMapTypes(std::vector& typeList); + bool getKeyMapSelectList(std::vector>& selectList, std::string option); + bool getKeyMapData(std::vector& dataArray, int *row, bool start); + + // Method to return the class version number. + float version(void) + { + return(MZ5665IF_VERSION); + } + + protected: + + private: + // Prototypes. + void pushKeyToQueue(uint32_t key); + IRAM_ATTR static void mzInterface( void * pvParameters ); + IRAM_ATTR static void hidInterface( void * pvParameters ); + void selectOption(uint8_t optionCode); + uint32_t mapKey(uint16_t scanCode); + bool loadKeyMap(); + bool saveKeyMap(void); + void init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + void init(NVS *hdlNVS, HID *hdlHID); + +// // Overload the base yield method to include suspension of the PS/2 Keyboard interface. This interface uses interrupts which are not mutex protected and clash with the +// // WiFi API methods. +// inline void yield(uint32_t delay) +// { +// // If suspended, go into a permanent loop until the suspend flag is reset. +// if(this->suspend) +// { +// // Suspend the keyboard interface. +// Keyboard->suspend(true); +// +// // Use the base method logic. +// KeyInterface::yield(delay); +// +// // Release the keyboard interface. +// Keyboard->suspend(false); +// } else +// // Otherwise just delay by the required amount for timing and to give other threads a time slice. +// { +// KeyInterface::yield(delay); +// } +// return; +// } + + // Structure to encapsulate a single key map from PS/2 to MZ-5600/MZ-6500. + typedef struct { + uint8_t ps2KeyCode; + uint8_t ps2Ctrl; + uint8_t keyboardModel; + uint8_t machine; + uint8_t mzKey; + uint8_t mzCtrl; + } t_keyMapEntry; + + // Structure to encapsulate the entire static keyboard mapping table. + typedef struct { + t_keyMapEntry kme[PS2TBL_MZ5665_MAXROWS]; + } t_keyMap; + + // Structure to maintain the MZ-5600/MZ-6500 interface configuration data. This data is persisted through powercycles as needed. + typedef struct { + struct { + uint8_t activeKeyboardMap; // Model of keyboard a keymap entry is applicable to. + uint8_t activeMachineModel; // Machine model a keymap entry is applicable to. + } params; + } t_mzConfig; + + // Configuration data. + t_mzConfig mzConfig; + + // Structure to manage the control signals signifying the state of the MZ-6500 keyboard. + typedef struct { + bool optionSelect; // Flag to indicate a user requested keyboard configuration option is being selected. + uint8_t keyCtrl; // Keyboard state flag control. + + std::string fsPath; // Path on the underlying filesystem where storage is mounted and accessible. + t_keyMapEntry *kme; // Pointer to an array in memory to contain PS2 to MZ-6500 mapping values. + int kmeRows; // Number of rows in the kme table. + std::string keyMapFileName; // Name of file where extension or replacement key map entries are stored. + } t_mzControl; + + // Transmit buffer queue item. + typedef struct { + uint32_t keyCode; // 16bit, bits 8:0 represent the key, 9 if CTRL to be sent, 10 if ALT to be sent. + } t_xmitQueueMessage; + + // Thread handles - one per function, ie. HID interface and host target interface. + TaskHandle_t TaskHostIF = NULL; + TaskHandle_t TaskHIDIF = NULL; + + // Control structure to control interaction and mapping of keys for the host. + t_mzControl mzCtrl; + + // Spin lock mutex to hold a coresied to an uninterruptable method. This only works on dual core ESP32's. + portMUX_TYPE mzMutex; + + // + // This mapping is for the UK Wyse KB-3926 PS/2 keyboard + // + t_keyMap PS2toMZ5665 = { + { + // HELP + // COPY + ////PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MZ5665 Data MZ5665 Ctrl (Flags to Set). + //{ PS2_KEY_F1, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 'v', 0x00, }, // SHIFT+F1 + //{ PS2_KEY_F2, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 'w', 0x00, }, // SHIFT+F2 + //{ PS2_KEY_F3, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 'x', 0x00, }, // SHIFT+F3 + //{ PS2_KEY_F4, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 'y', 0x00, }, // SHIFT+F4 + //{ PS2_KEY_F5, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 'z', 0x00, }, // SHIFT+F5 + //{ PS2_KEY_F1, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 'q', 0x00, }, // F1 + //{ PS2_KEY_F2, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 'r', 0x00, }, // F2 + //{ PS2_KEY_F3, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 's', 0x00, }, // F3 + //{ PS2_KEY_F4, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 't', 0x00, }, // F4 + //{ PS2_KEY_F5, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 'u', 0x00, }, // F5 + //{ PS2_KEY_F6, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0xEC, 0x00, }, // F6 + //{ PS2_KEY_F7, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0xEB, 0x00, }, // F7 + //{ PS2_KEY_F8, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0xE2, 0x00, }, // F8 + //{ PS2_KEY_F9, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0xE1, 0x00, }, // F9 + //{ PS2_KEY_F10, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // XFER + //{ PS2_KEY_F11, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0xFE, 0x00, }, // HELP + //{ PS2_KEY_F12, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // COPY + //{ PS2_KEY_TAB, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x09, 0x00, }, // TAB + // Numeric keys. + { PS2_KEY_0, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '0', 0x00, }, // 0 + { PS2_KEY_1, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '1', 0x00, }, // 1 + { PS2_KEY_2, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '2', 0x00, }, // 2 + { PS2_KEY_3, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '3', 0x00, }, // 3 + { PS2_KEY_4, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '4', 0x00, }, // 4 + { PS2_KEY_5, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '5', 0x00, }, // 5 + { PS2_KEY_6, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '6', 0x00, }, // 6 + { PS2_KEY_7, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '7', 0x00, }, // 7 + { PS2_KEY_8, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '8', 0x00, }, // 8 + { PS2_KEY_9, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '9', 0x00, }, // 9 + // Punctuation keys. + { PS2_KEY_0, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, ')', 0x00, }, // Close Right Bracket ) + { PS2_KEY_1, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '!', 0x00, }, // Exclamation + { PS2_KEY_2, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '"', 0x00, }, // Double quote. + { PS2_KEY_3, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0x23, 0x00, }, // Pound Sign -> Hash + { PS2_KEY_4, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '$', 0x00, }, // Dollar + { PS2_KEY_5, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '%', 0x00, }, // Percent + { PS2_KEY_6, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '^', 0x00, }, // Kappa + { PS2_KEY_7, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '&', 0x00, }, // Ampersand + { PS2_KEY_8, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '*', 0x00, }, // Star + { PS2_KEY_9, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '(', 0x00, }, // Open Left Bracket ( + // ALPHA keys, lower and uppercase. + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MZ5665 Data MZ5665 Ctrl (Flags to Set). + { PS2_KEY_A, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'a', 0x00, }, // a + { PS2_KEY_A, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'A', 0x00, }, // A + { PS2_KEY_B, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'b', 0x00, }, // b + { PS2_KEY_B, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'B', 0x00, }, // B + { PS2_KEY_C, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'c', 0x00, }, // c + { PS2_KEY_C, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'C', 0x00, }, // C + { PS2_KEY_D, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'd', 0x00, }, // d + { PS2_KEY_D, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'D', 0x00, }, // D + { PS2_KEY_E, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'e', 0x00, }, // e + { PS2_KEY_E, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'E', 0x00, }, // E + { PS2_KEY_F, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'f', 0x00, }, // f + { PS2_KEY_F, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'F', 0x00, }, // F + { PS2_KEY_G, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'g', 0x00, }, // g + { PS2_KEY_G, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'G', 0x00, }, // G + { PS2_KEY_H, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'h', 0x00, }, // h + { PS2_KEY_H, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'H', 0x00, }, // H + { PS2_KEY_I, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'i', 0x00, }, // i + { PS2_KEY_I, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'I', 0x00, }, // I + { PS2_KEY_J, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'j', 0x00, }, // j + { PS2_KEY_J, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'J', 0x00, }, // J + { PS2_KEY_K, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'k', 0x00, }, // k + { PS2_KEY_K, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'K', 0x00, }, // K + { PS2_KEY_L, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'l', 0x00, }, // l + { PS2_KEY_L, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'L', 0x00, }, // L + { PS2_KEY_M, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'm', 0x00, }, // m + { PS2_KEY_M, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'M', 0x00, }, // M + { PS2_KEY_N, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'n', 0x00, }, // n + { PS2_KEY_N, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'N', 0x00, }, // N + { PS2_KEY_O, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'o', 0x00, }, // o + { PS2_KEY_O, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'O', 0x00, }, // O + { PS2_KEY_P, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'p', 0x00, }, // p + { PS2_KEY_P, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'P', 0x00, }, // P + { PS2_KEY_Q, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'q', 0x00, }, // q + { PS2_KEY_Q, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'Q', 0x00, }, // Q + { PS2_KEY_R, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'r', 0x00, }, // r + { PS2_KEY_R, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'R', 0x00, }, // R + { PS2_KEY_S, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 's', 0x00, }, // s + { PS2_KEY_S, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'S', 0x00, }, // S + { PS2_KEY_T, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 't', 0x00, }, // t + { PS2_KEY_T, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'T', 0x00, }, // T + { PS2_KEY_U, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'u', 0x00, }, // u + { PS2_KEY_U, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'U', 0x00, }, // U + { PS2_KEY_V, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'v', 0x00, }, // v + { PS2_KEY_V, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'V', 0x00, }, // V + { PS2_KEY_W, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'w', 0x00, }, // w + { PS2_KEY_W, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'W', 0x00, }, // W + { PS2_KEY_X, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'x', 0x00, }, // x + { PS2_KEY_X, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'X', 0x00, }, // X + { PS2_KEY_Y, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'y', 0x00, }, // y + { PS2_KEY_Y, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'Y', 0x00, }, // Y + { PS2_KEY_Z, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'z', 0x00, }, // z + { PS2_KEY_Z, PS2CTRL_CAPS, KEYMAP_STANDARD, MZ5665_ALL, 'Z', 0x00, }, // Z + + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MZ5665 Data MZ5665 Ctrl (Flags to Set). + { PS2_KEY_SPACE, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, ' ', 0x00, }, // Space + { PS2_KEY_COMMA, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '<', 0x00, }, // Less Than < + { PS2_KEY_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, ',', 0x00, }, // Comma , + { PS2_KEY_SEMI, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, ':', 0x00, }, // Colon : + { PS2_KEY_SEMI, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, ';', 0x00, }, // Semi-Colon ; + { PS2_KEY_DOT, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '>', 0x00, }, // Greater Than > + { PS2_KEY_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '.', 0x00, }, // Full stop . + { PS2_KEY_DIV, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '?', 0x00, }, // Question ? + { PS2_KEY_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '/', 0x00, }, // Divide / + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '_', 0x00, }, // Underscore + { PS2_KEY_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '-', 0x00, }, + { PS2_KEY_APOS, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '@', 0x00, }, // At @ + { PS2_KEY_APOS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '\'', 0x00, }, // Single quote ' + { PS2_KEY_OPEN_SQ, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '{', 0x00, }, // Open Left Brace { + { PS2_KEY_OPEN_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '[', 0x00, }, // Open Left Square Bracket [ + { PS2_KEY_EQUAL, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '+', 0x00, }, // Plus + + { PS2_KEY_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '=', 0x00, }, // Equal = + { PS2_KEY_CAPS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, ' ', 0x00, }, // LOCK + { PS2_KEY_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x0D, 0x00, }, // ENTER/RETURN + { PS2_KEY_CLOSE_SQ, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '}', 0x00, }, // Close Right Brace } + { PS2_KEY_CLOSE_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, ']', 0x00, }, // Close Right Square Bracket ] + { PS2_KEY_BACK, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '|', 0x00, }, // + { PS2_KEY_BACK, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '\\', 0x00, }, // Back slash maps to Yen + { PS2_KEY_BTICK, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '`', 0x00, }, // Pipe + { PS2_KEY_BTICK, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '|', 0x00, }, // Back tick ` + { PS2_KEY_HASH, PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, '~', 0x00, }, // Tilde has no mapping. + { PS2_KEY_HASH, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '#', 0x00, }, // Hash + { PS2_KEY_BS, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x08, 0x00, }, // Backspace + { PS2_KEY_ESC, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x1B, 0x00, }, // ESCape + { PS2_KEY_SCROLL, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, ' ', 0x00, }, // Not assigned. + { PS2_KEY_INSERT, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_INS, 0x00, }, // INSERT + { PS2_KEY_HOME, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_CLR, 0x00, }, // CLR + { PS2_KEY_HOME, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_HOME, 0x00, }, // HOME + { PS2_KEY_DELETE, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_DEL, 0x00, }, // DELETE + { PS2_KEY_END, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x11, 0x00, }, // END + { PS2_KEY_PGUP, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x0E, 0x00, }, // Roll Up. + { PS2_KEY_PGDN, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x0F, 0x00, }, // Roll Down + { PS2_KEY_UP_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_UP, 0x00, }, // Up Arrow + { PS2_KEY_L_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_LEFT, 0x00, }, // Left Arrow + { PS2_KEY_DN_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_DOWN, 0x00, }, // Down Arrow + { PS2_KEY_R_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, MZ5665_KEY_RIGHT, 0x00, }, // Right Arrow + { PS2_KEY_NUM, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // Not assigned. + // GRPH (Alt Gr) + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MZ5665 Data MZ5665 Ctrl (Flags to Set). + { PS2_KEY_0, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xFA, 0x00, }, // GRPH+0 + { PS2_KEY_1, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF1, 0x00, }, // GRPH+1 + { PS2_KEY_2, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF2, 0x00, }, // GRPH+2 + { PS2_KEY_3, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF3, 0x00, }, // GRPH+3 + { PS2_KEY_4, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF4, 0x00, }, // GRPH+4 + { PS2_KEY_5, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF5, 0x00, }, // GRPH+5 + { PS2_KEY_6, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF6, 0x00, }, // GRPH+6 + { PS2_KEY_7, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF7, 0x00, }, // GRPH+7 + { PS2_KEY_8, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF8, 0x00, }, // GRPH+8 + { PS2_KEY_9, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF9, 0x00, }, // GRPH+9 + { PS2_KEY_A, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x7F, 0x00, }, // GRPH+A + { PS2_KEY_B, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x84, 0x00, }, // GRPH+B + { PS2_KEY_C, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x82, 0x00, }, // GRPH+C + { PS2_KEY_D, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xEA, 0x00, }, // GRPH+D + { PS2_KEY_E, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE2, 0x00, }, // GRPH+E + { PS2_KEY_F, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xEB, 0x00, }, // GRPH+F + { PS2_KEY_G, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xEC, 0x00, }, // GRPH+G + { PS2_KEY_H, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xED, 0x00, }, // GRPH+H + { PS2_KEY_I, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE7, 0x00, }, // GRPH+I + { PS2_KEY_J, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xEE, 0x00, }, // GRPH+J + { PS2_KEY_K, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xEF, 0x00, }, // GRPH+K + { PS2_KEY_L, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x8E, 0x00, }, // GRPH+L + { PS2_KEY_M, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x86, 0x00, }, // GRPH+M + { PS2_KEY_N, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x85, 0x00, }, // GRPH+N + { PS2_KEY_O, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xF0, 0x00, }, // GRPH+O + { PS2_KEY_P, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x8D, 0x00, }, // GRPH+P + { PS2_KEY_Q, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE0, 0x00, }, // GRPH+Q + { PS2_KEY_R, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE3, 0x00, }, // GRPH+R + { PS2_KEY_S, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE9, 0x00, }, // GRPH+S + { PS2_KEY_T, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE4, 0x00, }, // GRPH+T + { PS2_KEY_U, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE6, 0x00, }, // GRPH+U + { PS2_KEY_V, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x83, 0x00, }, // GRPH+V + { PS2_KEY_W, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE1, 0x00, }, // GRPH+W + { PS2_KEY_X, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x81, 0x00, }, // GRPH+X + { PS2_KEY_Y, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE5, 0x00, }, // GRPH+Y + { PS2_KEY_Z, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x80, 0x00, }, // GRPH+Z + { PS2_KEY_COMMA, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x87, 0x00, }, // GRPH+, + { PS2_KEY_SEMI, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x89, 0x00, }, // GRPH+; + { PS2_KEY_DOT, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x88, 0x00, }, // GRPH+. + { PS2_KEY_DIV, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xFE, 0x00, }, // GRPH+/ + { PS2_KEY_MINUS, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x8C, 0x00, }, // GRPH+- + { PS2_KEY_APOS, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x8A, 0x00, }, // GRPH+' + { PS2_KEY_OPEN_SQ, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xFC, 0x00, }, // GRPH+[ + { PS2_KEY_CLOSE_SQ, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0xE8, 0x00, }, // GRPH+] + { PS2_KEY_BACK, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x90, 0x00, }, // GRPH+Backslash + { PS2_KEY_KP0, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x8F, 0x00, }, // GRPH+Keypad 0 + { PS2_KEY_KP1, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x99, 0x00, }, // GRPH+Keypad 1 + { PS2_KEY_KP2, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x92, 0x00, }, // GRPH+Keypad 2 + { PS2_KEY_KP3, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x98, 0x00, }, // GRPH+Keypad 3 + { PS2_KEY_KP4, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x95, 0x00, }, // GRPH+Keypad 4 + { PS2_KEY_KP5, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x96, 0x00, }, // GRPH+Keypad 5 + { PS2_KEY_KP6, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x94, 0x00, }, // GRPH+Keypad 6 + { PS2_KEY_KP7, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x9A, 0x00, }, // GRPH+Keypad 7 + { PS2_KEY_KP8, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x93, 0x00, }, // GRPH+Keypad 8 + { PS2_KEY_KP9, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x97, 0x00, }, // GRPH+Keypad 9 + { PS2_KEY_KP_DOT, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x91, 0x00, }, // GRPH+Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x9D, 0x00, }, // GRPH+Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x9C, 0x00, }, // GRPH+Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x9B, 0x00, }, // GRPH+Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x9E, 0x00, }, // GRPH+Keypad Divide / + { PS2_KEY_KP_ENTER, PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x90, 0x00, }, // GRPH+Keypad Ebter / + // KANA (Alt) + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MZ5665 Data MZ5665 Ctrl (Flags to Set). + { PS2_KEY_0, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA6, 0x00, }, // KANA+SHIFT+0 + { PS2_KEY_0, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xDC, 0x00, }, // KANA+0 + { PS2_KEY_1, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC7, 0x00, }, // KANA+1 + { PS2_KEY_2, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xCC, 0x00, }, // KANA+2 + { PS2_KEY_3, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA7, 0x00, }, // KANA+SHIFT+3 + { PS2_KEY_3, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB1, 0x00, }, // KANA+3 + { PS2_KEY_4, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA9, 0x00, }, // KANA+SHIFT+4 + { PS2_KEY_4, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB3, 0x00, }, // KANA+4 + { PS2_KEY_5, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xAA, 0x00, }, // KANA+SHIFT+5 + { PS2_KEY_5, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB4, 0x00, }, // KANA+5 + { PS2_KEY_6, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xAB, 0x00, }, // KANA+SHIFT+6 + { PS2_KEY_6, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB5, 0x00, }, // KANA+6 + { PS2_KEY_7, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xAC, 0x00, }, // KANA+SHIFT+7 + { PS2_KEY_7, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD4, 0x00, }, // KANA+7 + { PS2_KEY_8, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xAD, 0x00, }, // KANA+SHIFT+8 + { PS2_KEY_8, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD5, 0x00, }, // KANA+8 + { PS2_KEY_9, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xAE, 0x00, }, // KANA+SHIFT+9 + { PS2_KEY_9, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD6, 0x00, }, // KANA+9 + { PS2_KEY_A, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC1, 0x00, }, // KANA+A + { PS2_KEY_B, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xBA, 0x00, }, // KANA+B + { PS2_KEY_C, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xBF, 0x00, }, // KANA+C + { PS2_KEY_D, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xBC, 0x00, }, // KANA+D + { PS2_KEY_E, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA8, 0x00, }, // KANA+SHIFT+E + { PS2_KEY_E, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB2, 0x00, }, // KANA+E + { PS2_KEY_F, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xCA, 0x00, }, // KANA+F + { PS2_KEY_G, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB7, 0x00, }, // KANA+G + { PS2_KEY_H, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB8, 0x00, }, // KANA+H + { PS2_KEY_I, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC6, 0x00, }, // KANA+I + { PS2_KEY_J, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xCF, 0x00, }, // KANA+J + { PS2_KEY_K, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC9, 0x00, }, // KANA+K + { PS2_KEY_L, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD8, 0x00, }, // KANA+L + { PS2_KEY_M, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD3, 0x00, }, // KANA+M + { PS2_KEY_N, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD0, 0x00, }, // KANA+N + { PS2_KEY_O, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD7, 0x00, }, // KANA+O + { PS2_KEY_P, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xBE, 0x00, }, // KANA+P + { PS2_KEY_Q, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC0, 0x00, }, // KANA+Q + { PS2_KEY_R, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xBD, 0x00, }, // KANA+R + { PS2_KEY_S, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC4, 0x00, }, // KANA+S + { PS2_KEY_T, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xB6, 0x00, }, // KANA+T + { PS2_KEY_U, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC5, 0x00, }, // KANA+U + { PS2_KEY_V, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xCB, 0x00, }, // KANA+V + { PS2_KEY_W, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC3, 0x00, }, // KANA+W + { PS2_KEY_X, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xBB, 0x00, }, // KANA+X + { PS2_KEY_Y, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xDD, 0x00, }, // KANA+Y + { PS2_KEY_Z, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xAF, 0x00, }, // KANA+SHIFT+Z + { PS2_KEY_Z, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC2, 0x00, }, // KANA+Z + { PS2_KEY_COMMA, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA4, 0x00, }, // KANA+SHIFT+, + { PS2_KEY_COMMA, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xC8, 0x00, }, // KANA+, + { PS2_KEY_SEMI, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xDA, 0x00, }, // KANA+; + { PS2_KEY_DOT, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA1, 0x00, }, // KANA+SHIFT+. + { PS2_KEY_DOT, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD9, 0x00, }, // KANA+. + { PS2_KEY_DIV, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA5, 0x00, }, // KANA+SHIFT+/ + { PS2_KEY_DIV, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD2, 0x00, }, // KANA+/ + { PS2_KEY_MINUS, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xCE, 0x00, }, // KANA+- + { PS2_KEY_APOS, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xDE, 0x00, }, // KANA+' + { PS2_KEY_OPEN_SQ, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA2, 0x00, }, // KANA+SHIFT+[ + { PS2_KEY_OPEN_SQ, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xDF, 0x00, }, // KANA+[ + { PS2_KEY_CLOSE_SQ, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0xA3, 0x00, }, // KANA+SHIFT+] + { PS2_KEY_CLOSE_SQ, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xD1, 0x00, }, // KANA+] + { PS2_KEY_BACK, PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0xDB, 0x00, }, // KANA+Backslash + { PS2_KEY_BS, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, MZ5665_ALL, 0x12, 0x00, }, // KANA+SHIFT+Backspace + // Keypad. + { PS2_KEY_KP0, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '0', 0x00, }, // Keypad 0 + { PS2_KEY_KP1, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '1', 0x00, }, // Keypad 1 + { PS2_KEY_KP2, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '2', 0x00, }, // Keypad 2 + { PS2_KEY_KP3, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '3', 0x00, }, // Keypad 3 + { PS2_KEY_KP4, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '4', 0x00, }, // Keypad 4 + { PS2_KEY_KP5, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '5', 0x00, }, // Keypad 5 + { PS2_KEY_KP6, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '6', 0x00, }, // Keypad 6 + { PS2_KEY_KP7, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '7', 0x00, }, // Keypad 7 + { PS2_KEY_KP8, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '8', 0x00, }, // Keypad 8 + { PS2_KEY_KP9, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '9', 0x00, }, // Keypad 9 + { PS2_KEY_KP_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, ',', 0x00, }, // Keypad Comma , + { PS2_KEY_KP_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '.', 0x00, }, // Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '+', 0x00, }, // Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '-', 0x00, }, // Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '*', 0x00, }, // Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, '/', 0x00, }, // Keypad Divide / + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x0D, 0x00, }, // Keypad Ebter / + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine MZ5665 Data MZ5665 Ctrl (Flags to Set). + // Special keys. + { PS2_KEY_PRTSCR, PS2CTRL_FUNC, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // ARGO KEY + { PS2_KEY_PAUSE, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x03, 0x00, }, // BREAK KEY + { PS2_KEY_L_GUI, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // GRAPH KEY + //{ PS2_KEY_L_ALT, PS2CTRL_FUNC | PS2CTRL_KANA, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // KJ1 Sentence + //{ PS2_KEY_R_ALT, PS2CTRL_FUNC | PS2CTRL_GRAPH, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // KJ2 Transform + { PS2_KEY_R_GUI, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // KANA KEY + { PS2_KEY_MENU, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // Not assigned. + // Modifiers are last, only being selected if an earlier match isnt made. + { PS2_KEY_L_SHIFT, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, + { PS2_KEY_R_SHIFT, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, + { PS2_KEY_L_CTRL, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, + { PS2_KEY_R_CTRL, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, // Map to Control + { 0, PS2CTRL_NONE, KEYMAP_STANDARD, MZ5665_ALL, 0x00, 0x00, }, + }}; +}; + +#endif // MZ5665_H diff --git a/main/include/Mouse.h b/main/include/Mouse.h deleted file mode 120000 index 13f7c7c..0000000 --- a/main/include/Mouse.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/Mouse.h \ No newline at end of file diff --git a/main/include/Mouse.h b/main/include/Mouse.h new file mode 100644 index 0000000..cf0f792 --- /dev/null +++ b/main/include/Mouse.h @@ -0,0 +1,151 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: Mouse.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the PS/2 Mouse to Sharp Host interface logic. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// Updates to reflect moving functionality into the HID and to support +// Bluetooth as a primary mouse or secondary mouse. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef MOUSE_H +#define MOUSE_H + +// Include the specification class. +#include "KeyInterface.h" +#include "NVS.h" +#include "HID.h" + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Encapsulate the Mouse interface. +class Mouse : public KeyInterface { + + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define MOUSEIF_VERSION 1.02 + #define MAX_MOUSE_XMIT_KEY_BUF 128 + #define BITBANG_UART_BIT_TIME 208UL + + public: + + // Prototypes. + Mouse(void); + Mouse(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + Mouse(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, bool secondaryIf); + Mouse(NVS *hdlNVShdlHID, HID *hdlHID); + ~Mouse(void); + void getMouseConfigTypes(std::vector& typeList); + bool getMouseSelectList(std::vector>& selectList, std::string option); + bool setMouseConfigValue(std::string paramName, std::string paramValue); + void mouseReceiveData(HID::t_mouseMessageElement mouseMessage); + bool persistConfig(void); + + // Method to return the class version number. + float version(void) + { + return(MOUSEIF_VERSION); + } + + protected: + + private: + // Prototypes. + IRAM_ATTR static void hostInterface( void * pvParameters ); + void init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + void init(NVS *hdlNVS, HID *hdlHID); + + // Structure to maintain mouse interface configuration data. This data is persisted through powercycles as needed. + typedef struct { + struct { + // PS/2 Mouse data Adjustment and filtering options. + // + enum HID::HID_MOUSE_RESOLUTION resolution; + enum HID::HID_MOUSE_SCALING scaling; + enum HID::HID_MOUSE_SAMPLING sampleRate; + } mouse; + + struct { + // Host data for adjustment and configuration. + enum HID::HID_MOUSE_HOST_SCALING scaling; + } host; + + struct { + } params; + } t_mouseConfig; + + // Configuration data. + t_mouseConfig mouseConfig; + + // Structure to manage the Mouse control variables signifying the state of the Mouse. + typedef struct { + } t_msControl; + + // Mouse Control variables. + volatile t_msControl msCtrl; + + // Structure to manage the Sharp host control variables which define control and data mapping of the host interface and data sent. + // + typedef struct { + #ifdef CONFIG_HOST_HW_UART + int uartNum; + int uartBufferSize; + int uartQueueSize; + #endif + bool secondaryIf; // Mouse runs in tandem with a keyboard interface. + + // Data adjustment and processing options applied to the PS/2 data. + bool updated; + } t_hostControl; + + // Host Control variables. + volatile t_hostControl hostControl; + + // PS/2 to HOST serialiser buffer item. + typedef struct { + uint8_t xPos; + uint8_t yPos; + uint8_t status; + uint8_t wheel; + bool valid; + } t_xmitMessage; + + // Create an object for storing the data to be sent to the Host. This data has already been converted and adjusted from the incoming PS/2 message. + t_xmitMessage xmitMsg; + + // Thread handles - one per function, ie. ps/2 interface, host target interface, wifi interface. + TaskHandle_t TaskHostIF = NULL; + TaskHandle_t TaskHIDIF = NULL; + + // Spin lock mutex to hold a coresied to an uninterruptable method. This only works on dual core ESP32's. + portMUX_TYPE x1Mutex; +}; + +#endif // MOUSE_H diff --git a/main/include/NVS.h b/main/include/NVS.h deleted file mode 120000 index d2577cc..0000000 --- a/main/include/NVS.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/NVS.h \ No newline at end of file diff --git a/main/include/NVS.h b/main/include/NVS.h new file mode 100644 index 0000000..a77553c --- /dev/null +++ b/main/include/NVS.h @@ -0,0 +1,162 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: NVS.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Class definition to encapsulate the Espressif Non Volatile Storage into a thread safe +// object, The underlying API is supposed to be thread safe but experience has shown +// that two threads, each with there own handle can cause a lockup. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NVS_H +#define NVS_H + +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" + + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Define a virtual class which acts as the base and specification of all super classes forming host +// interface objects. +class NVS { + + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define NVS_VERSION 1.01 + + public: + + // Prototypes. + NVS(void); + NVS(std::string keyName); + virtual ~NVS(void) {}; + void eraseAll(void); + void init(void); + bool takeMutex(void); + void giveMutex(void); + // Persistence. + bool open(std::string keyName); + bool persistData(const char *key, void *pData, uint32_t size); + bool retrieveData(const char *key, void *pData, uint32_t size); + bool commitData(void); + + // Helper method to identify the sub class, this is used in non volatile key management. + // Warning: This method wont work if optimisation for size is enabled on the compiler. + const char *getClassName(const std::string& prettyFunction) + { + // First find the CLASS :: METHOD seperation. + size_t colons = prettyFunction.find("::"); + + // None, then this is not a class. + if (colons == std::string::npos) + return "::"; + + // Split out the class name. + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = colons - begin; + + // Return the name. + return(prettyFunction.substr(begin,end).c_str()); + } + + // Helper method to change a file extension. + void replaceExt(std::string& fileName, const std::string& newExt) + { + // Locals. + std::string::size_type extPos = fileName.rfind('.', fileName.length()); + + if(extPos != std::string::npos) + { + fileName.replace(extPos+1, newExt.length(), newExt); + } + return; + } + + // Template to aid in conversion of an enum to integer. + template constexpr typename std::underlying_type::type to_underlying(E e) noexcept + { + return static_cast::type>(e); + } + + // Method to return the class version number. + virtual float version(void) + { + return(NVS_VERSION); + } + + // Method to return the name of the class. + virtual std::string ifName(void) + { + return(nvsCtrl.nvsClassName); + } + + // Method to return the name of the nvs key. + virtual std::string keyName(void) + { + return(nvsCtrl.nvsKeyName); + } + + protected: + + private: + + // Structure to maintain an active setting for the LED. The LED control thread uses these values to effect the required lighting of the LED. + typedef struct { + // Handle to the persistent storage api. + nvs_handle_t nvsHandle; + + // Name of the class for this instantiation. + std::string nvsClassName; + + // Name of the key under which NVS was opened. + std::string nvsKeyName; + + // Mutex to block access to limit one thread at a time. + SemaphoreHandle_t mutexInternal; + } t_nvsControl; + + // Var to store all NVS control variables. + t_nvsControl nvsCtrl; + +}; +#endif // NVS_H diff --git a/main/include/PC9801.h b/main/include/PC9801.h deleted file mode 120000 index cf20392..0000000 --- a/main/include/PC9801.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/PC9801.h \ No newline at end of file diff --git a/main/include/PC9801.h b/main/include/PC9801.h new file mode 100644 index 0000000..aab4bc3 --- /dev/null +++ b/main/include/PC9801.h @@ -0,0 +1,529 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: PC9801.h +// Created: Apr 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the NEC PC-9801 to HID (PS/2, Bluetooth) interface logic. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Apr 2022 - Initial write. +// v1.01 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef PC9801_H +#define PC9801_H + +// Include the specification class. +#include "KeyInterface.h" +#include "NVS.h" +#include "LED.h" +#include "HID.h" +#include +#include + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Encapsulate the NEC PC-9801 interface. +class PC9801 : public KeyInterface { + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define PC9801IF_VERSION 1.00 + #define PC9801IF_KEYMAP_FILE "PC9801_KeyMap.BIN" + #define MAX_PC9801_XMIT_KEY_BUF 16 + #define MAX_PC9801_RCV_KEY_BUF 16 + + // NEC PC-9801 Key control bit mask. + #define PC9801_CTRL_SHIFT ((unsigned char) (1 << 5)) + #define PC9801_CTRL_RELEASESHIFT ((unsigned char) (1 << 4)) + #define PC9801_CTRL_CTRL ((unsigned char) (1 << 3)) + #define PC9801_CTRL_GRAPH ((unsigned char) (1 << 2)) + #define PC9801_CTRL_CAPS ((unsigned char) (1 << 1)) + #define PC9801_CTRL_KANA ((unsigned char) (1 << 0)) + #define PC9801_CTRL_NONE 0x00 + + // Special key definition. +// #define PC9801_KEY_UP 0x1E // ↑ +// #define PC9801_KEY_DOWN 0x1F // ↓ +// #define PC9801_KEY_LEFT 0x1D // ← +// #define PC9801_KEY_RIGHT 0x1C // → → +// #define PC9801_KEY_INS 0x12 // INS +// #define PC9801_KEY_DEL 0x08 // DEL +// #define PC9801_KEY_CLR 0x0C // CLR +// #define PC9801_KEY_HOME 0x0B // HOME + + // PS2 Flag definitions. + #define PS2CTRL_NONE 0x00 // No keys active = 0 + #define PS2CTRL_SHIFT 0x01 // Shfit Key active = 1 + #define PS2CTRL_CTRL 0x02 // Ctrl Key active = 1 + #define PS2CTRL_CAPS 0x04 // CAPS active = 1 + #define PS2CTRL_KANA 0x08 // KANA active = 1 + #define PS2CTRL_GRAPH 0x10 // GRAPH active = 1 + #define PS2CTRL_GUI 0x20 // GUI Key active = 1 + #define PS2CTRL_FUNC 0x40 // Special Function Keys active = 1 + #define PS2CTRL_BREAK 0x80 // BREAK Key active = 1 + #define PS2CTRL_EXACT 0x80 // EXACT Match active = 1 + + // The initial mapping is made inside the PS2KeyAdvanced class from Scan Code Set 2 to ASCII + // for a selected keyboard. Special functions are detected and combined inside this module + // before mapping with the table below to extract the PC-9801 key code and control data. + // ie. PS/2 Scan Code -> ASCII + Flags -> PC-9801 Key Code + Ctrl Data + #define PS2TBL_PC9801_MAXCOLS 6 + #define PS2TBL_PC9801_MAXROWS 131 + + // The initial mapping is made inside the PS2KeyAdvanced class from Scan Code Set 2 to ASCII + // for a selected keyboard. Special functions are detected and combined inside this module + // before mapping with the table below to extract the NEC PC-9801 key code and control data. + // ie. PS/2 Scan Code -> ASCII + Flags -> NEC PC-9801 Key Code + Ctrl Data + + // Keyboard mapping table column names. + #define PS2TBL_PS2KEYCODE_NAME "PS/2 KeyCode" + #define PS2TBL_PS2CTRL_NAME "PS/2 Control Key" + #define PS2TBL_KEYBOARDMODEL_NAME "For Keyboard" + #define PS2TBL_MACHINE_NAME "For Host Model" + #define PS2TBL_PC9801_KEYCODE_NAME "PC9801 KeyCode" + #define PS2TBL_PC9801__CTRL_NAME "PC9801 Control Key" + + // Keyboard mapping table column types. + #define PS2TBL_PS2KEYCODE_TYPE "hex" + #define PS2TBL_PS2CTRL_TYPE "custom_cbp_ps2ctrl" + #define PS2TBL_KEYBOARDMODEL_TYPE "custom_cbp_keybmodel" + #define PS2TBL_MACHINE_TYPE "custom_cbp_machine" + #define PS2TBL_PC9801_KEYCODE_TYPE "hex" + #define PS2TBL_PC9801_CTRL_TYPE "custom_cbn_x1ctrl" + + // Keyboard mapping table select list for PS2CTRL. + #define PS2TBL_PS2CTRL_SEL_NONE "NONE" + #define PS2TBL_PS2CTRL_SEL_SHIFT "SHIFT" + #define PS2TBL_PS2CTRL_SEL_CTRL "CTRL" + #define PS2TBL_PS2CTRL_SEL_CAPS "CAPS" + #define PS2TBL_PS2CTRL_SEL_KANA "KANA" + #define PS2TBL_PS2CTRL_SEL_GRAPH "GRAPH" + #define PS2TBL_PS2CTRL_SEL_GUI "GUI" + #define PS2TBL_PS2CTRL_SEL_FUNC "FUNC" + #define PS2TBL_PS2CTRL_SEL_EXACT "EXACT" + + // Keyboard mapping table select list for target machine. + #define PC9801_SEL_ALL "ALL" + #define PC9801_SEL_ORIG "ORIGINAL" + + // Keyboard mapping table select list for Model of keyboard. + #define KEYMAP_SEL_STANDARD "ALL" + #define KEYMAP_SEL_UK_WYSE_KB3926 "UK_WYSE_KB3926" + #define KEYMAP_SEL_JAPAN_OADG109 "JAPAN_OADG109" + #define KEYMAP_SEL_JAPAN_SANWA_SKBL1 "JAPAN_SANWA_SKBL1" + #define KEYMAP_SEL_NOT_ASSIGNED_4 "KEYBOARD_4" + #define KEYMAP_SEL_NOT_ASSIGNED_5 "KEYBOARD_5" + #define KEYMAP_SEL_NOT_ASSIGNED_6 "KEYBOARD_6" + #define KEYMAP_SEL_UK_PERIBOARD_810 "UK_PERIBOARD_810" + #define KEYMAP_SEL_UK_OMOTON_K8508 "UK_OMOTON_K8508" + + // Keyboard mapping table select list for PC9801 Control codes. + #define PC9801_CTRL_SEL_GRAPH "GRAPH" + #define PC9801_CTRL_SEL_CAPS "CAPS" + #define PC9801_CTRL_SEL_KANA "KANA" + #define PC9801_CTRL_SEL_SHIFT "SHIFT" + #define PC9801_CTRL_SEL_CTRL "CTRL" + + // The NEC PC-9801 Series was released over a number of years and each iteration added changes/updates. In order to cater for differences, it is possible to assign a key mapping + // to a specific machine type(s) or all of the series by adding the flags below into the mapping table. + #define PC9801_ALL 0xFF + + // Keyboard models. The base on which this interface was created was a Wyse KB3926 PS/2 Keyboard and this is deemed STANDARD. Other models need to insert difference maps + // prior to the STANDARD entry along with the keyboard model so that it is processed first thus allowing differing keyboards with different maps. + #define KEYMAP_STANDARD 0xFF + #define KEYMAP_UK_WYSE_KB3926 0x01 + #define KEYMAP_JAPAN_OADG109 0x02 + #define KEYMAP_JAPAN_SANWA_SKBL1 0x04 + #define KEYMAP_NOT_ASSIGNED_4 0x08 + #define KEYMAP_NOT_ASSIGNED_5 0x10 + #define KEYMAP_NOT_ASSIGNED_6 0x20 + #define KEYMAP_UK_PERIBOARD_810 0x40 + #define KEYMAP_UK_OMOTON_K8508 0x80 + + // PC-9801 Scan codes - PS2 codes along with function keys (SHIFT, CTRL etc) are mapped to the X68000 scan codes below. + #define PC9801_KEY_ESC 0x00 + #define PC9801_KEY_0 0x0A + #define PC9801_KEY_1 0x01 + #define PC9801_KEY_2 0x02 + #define PC9801_KEY_3 0x03 + #define PC9801_KEY_4 0x04 + #define PC9801_KEY_5 0x05 + #define PC9801_KEY_6 0x06 + #define PC9801_KEY_7 0x07 + #define PC9801_KEY_8 0x08 + #define PC9801_KEY_9 0x09 + #define PC9801_KEY_A 0x1D + #define PC9801_KEY_B 0x2D + #define PC9801_KEY_C 0x2B + #define PC9801_KEY_D 0x1F + #define PC9801_KEY_E 0x12 + #define PC9801_KEY_F 0x20 + #define PC9801_KEY_G 0x21 + #define PC9801_KEY_H 0x22 + #define PC9801_KEY_I 0x17 + #define PC9801_KEY_J 0x23 + #define PC9801_KEY_K 0x24 + #define PC9801_KEY_L 0x25 + #define PC9801_KEY_M 0x2F + #define PC9801_KEY_N 0x2E + #define PC9801_KEY_O 0x18 + #define PC9801_KEY_P 0x19 + #define PC9801_KEY_Q 0x10 + #define PC9801_KEY_R 0x13 + #define PC9801_KEY_S 0x1E + #define PC9801_KEY_T 0x14 + #define PC9801_KEY_U 0x16 + #define PC9801_KEY_V 0x2C + #define PC9801_KEY_W 0x11 + #define PC9801_KEY_X 0x2A + #define PC9801_KEY_Y 0x15 + #define PC9801_KEY_Z 0x29 + #define PC9801_KEY_AT 0x1A // Requires SHIFT + #define PC9801_KEY_MINUS 0x0B + #define PC9801_KEY_CIRCUMFLEX 0x0C + #define PC9801_KEY_YEN 0x0D + #define PC9801_KEY_BS 0x0E + #define PC9801_KEY_TAB 0x0F + #define PC9801_KEY_OPEN_SQ 0x1A + #define PC9801_KEY_CLOSE_SQ 0x1B + #define PC9801_KEY_RETURN 0x1C + #define PC9801_KEY_SEMI 0x26 + #define PC9801_KEY_COLON 0x27 + #define PC9801_KEY_COMMA 0x30 + #define PC9801_KEY_DOT 0x31 + #define PC9801_KEY_DIV 0x32 + #define PC9801_KEY_UNDERLINE 0x0D // Requires SHIFT + #define PC9801_KEY_SPACE 0x34 + #define PC9801_KEY_HOME 0x3E + #define PC9801_KEY_ROLLUP 0x36 + #define PC9801_KEY_ROLLDN 0x37 + #define PC9801_KEY_UNDO 0x3 // Not known3 + #define PC9801_KEY_L_ARROW 0x3B + #define PC9801_KEY_UP_ARROW 0x3A + #define PC9801_KEY_R_ARROW 0x3C + #define PC9801_KEY_DN_ARROW 0x3D + #define PC9801_KEY_CLR 0x3F // Not known + #define PC9801_KEY_KP0 0x4E + #define PC9801_KEY_KP1 0x4A + #define PC9801_KEY_KP2 0x4B + #define PC9801_KEY_KP3 0x4C + #define PC9801_KEY_KP4 0x46 + #define PC9801_KEY_KP5 0x47 + #define PC9801_KEY_KP6 0x48 + #define PC9801_KEY_KP7 0x42 + #define PC9801_KEY_KP8 0x43 + #define PC9801_KEY_KP9 0x44 + #define PC9801_KEY_KP_DIV 0x41 + #define PC9801_KEY_KP_TIMES 0x45 + #define PC9801_KEY_KP_MINUS 0x4D + #define PC9801_KEY_KP_PLUS 0x49 + #define PC9801_KEY_KP_EQUAL 0x4D + #define PC9801_KEY_KP_ENTER 0x1C + #define PC9801_KEY_KP_COMMA 0x4F + #define PC9801_KEY_KP_DOT 0x50 + #define PC9801_KEY_SYMBOL 0x52 // Not known + #define PC9801_KEY_HELP 0x3F + #define PC9801_KEY_CAPS 0x71 + #define PC9801_KEY_INS 0x38 + #define PC9801_KEY_DEL 0x39 + #define PC9801_KEY_BREAK 0x60 // Stop + #define PC9801_KEY_COPY 0x61 + #define PC9801_KEY_SHIFT 0x70 + #define PC9801_KEY_R_SHIFT 0x7D + #define PC9801_KEY_CTRL 0x74 + #define PC9801_KEY_GRAPH 0x73 + #define PC9801_KEY_XFER 0x35 + #define PC9801_KEY_NFER 0x51 + #define PC9801_KEY_KATAKANA 0x72 + #define PC9801_KEY_ROMAJI 0x33 + #define PC9801_KEY_F1 0x62 + #define PC9801_KEY_F2 0x63 + #define PC9801_KEY_F3 0x64 + #define PC9801_KEY_F4 0x65 + #define PC9801_KEY_F5 0x66 + #define PC9801_KEY_F6 0x67 + #define PC9801_KEY_F7 0x68 + #define PC9801_KEY_F8 0x69 + #define PC9801_KEY_F9 0x6A + #define PC9801_KEY_F10 0x6B + #define PC9801_KEY_F11 0x52 + #define PC9801_KEY_F12 0x53 + #define PC9801_KEY_F13 0x54 + #define PC9801_KEY_F14 0x55 + #define PC9801_KEY_F15 0x56 + #define PC9801_KEY_NULL 0xFF + + public: + // Prototypes. + PC9801(void); + PC9801(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char *fsPath); + PC9801(NVS *hdlNVS, HID *hdlHID, const char *fsPath); + ~PC9801(void); + bool createKeyMapFile(std::fstream &outFile); + bool storeDataToKeyMapFile(std::fstream &outFile, char *data, int size); + bool storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray); + bool closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly); + std::string getKeyMapFileName(void) { return(PC9801IF_KEYMAP_FILE); }; + void getKeyMapHeaders(std::vector& headerList); + void getKeyMapTypes(std::vector& typeList); + bool getKeyMapSelectList(std::vector>& selectList, std::string option); + bool getKeyMapData(std::vector& dataArray, int *row, bool start); + + // Method to return the class version number. + float version(void) + { + return(PC9801IF_VERSION); + } + + protected: + + private: + // Prototypes. + IRAM_ATTR void pushKeyToQueue(uint32_t key); + IRAM_ATTR void pushHostCmdToQueue(uint8_t cmd); + IRAM_ATTR static void pcInterface( void * pvParameters ); + IRAM_ATTR static void hidInterface( void * pvParameters ); + void selectOption(uint8_t optionCode); + uint32_t mapKey(uint16_t scanCode); + bool loadKeyMap(); + bool saveKeyMap(void); + void init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + void init(NVS *hdlNVS, HID *hdlHID); + + // Structure to encapsulate a single key map from PS/2 to NEC PC-9801 + typedef struct { + uint8_t ps2KeyCode; + uint8_t ps2Ctrl; + uint8_t keyboardModel; + uint8_t machine; + uint8_t pcKey; + uint8_t pcCtrl; + } t_keyMapEntry; + + // Structure to encapsulate the entire static keyboard mapping table. + typedef struct { + t_keyMapEntry kme[PS2TBL_PC9801_MAXROWS]; + } t_keyMap; + + // Structure to maintain the NEC PC-9801 interface configuration data. This data is persisted through powercycles as needed. + typedef struct { + struct { + uint8_t activeKeyboardMap; // Model of keyboard a keymap entry is applicable to. + uint8_t activeMachineModel; // Machine model a keymap entry is applicable to. + bool useOnlyPersisted; // Flag to indicate wether the inbuilt keymap array should be combined with persisted values or the inbuilt array is ignored and only persisted values used. + } params; + } t_pcConfig; + + // Configuration data. + t_pcConfig pcConfig; + + // Structure to manage the control signals signifying the state of the NEC PC-9801 keyboard. + typedef struct { + uint8_t keyCtrl; // Keyboard state flag control. + bool optionSelect; // Flag to indicate a user requested keyboard configuration option is being selected. + int uartNum; + int uartBufferSize; + int uartQueueSize; + + std::string fsPath; // Path on the underlying filesystem where storage is mounted and accessible. + t_keyMapEntry *kme; // Pointer to an array in memory to contain PS2 to NEC PC-9801 mapping values. + int kmeRows; // Number of rows in the kme table. + std::string keyMapFileName; // Name of file where extension or replacement key map entries are stored. + bool persistConfig; // Flag to request saving of the config into NVS storage. + } t_pcControl; + + // Transmit buffer queue item. + typedef struct { + uint32_t keyCode; // Key data to be sent to PC-9801, 4 bytes to allow for extended sequences.. + } t_xmitQueueMessage; + + // Receive buffer queue item. + typedef struct { + uint8_t hostCmd; // Keyboard configuration command received from X68000. + } t_rcvQueueMessage; + + // Thread handles - one per function, ie. HID interface and host target interface. + TaskHandle_t TaskHostIF = NULL; + TaskHandle_t TaskHIDIF = NULL; + + // Control structure to control interaction and mapping of keys for the host. + t_pcControl pcCtrl; + + // Spin lock mutex to hold a coresied to an uninterruptable method. This only works on dual core ESP32's. + portMUX_TYPE pcMutex; + + // + // This mapping is for the UK Wyse KB-3926 PS/2 keyboard + // + t_keyMap PS2toPC9801 = { + { + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine PC-9801 Data PC-9801 Ctrl (Flags to Set). + // Function keys + // { PS2_KEY_F1, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_HIRAGANA, PC9801_CTRL_NONE, }, // CTRL + F1 = Hiragana + // { PS2_KEY_F2, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_FULLWIDTH, PC9801_CTRL_NONE, }, // CTRL + F2 = Full Width + // { PS2_KEY_F3, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KATAKANA, PC9801_CTRL_NONE, }, // CTRL + F3 = Katakana + // { PS2_KEY_F4, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_ROMAJI, PC9801_CTRL_NONE, }, // CTRL + F4 = Romaji + // { PS2_KEY_F5, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_TRANSPOSE, PC9801_CTRL_NONE, }, // CTRL + F5 = Tranpose + // { PS2_KEY_F6, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_SYMBOL, PC9801_CTRL_NONE, }, // CTRL + F6 = Symbol + // { PS2_KEY_F7, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_REGISTRATION, PC9801_CTRL_NONE, }, // CTRL + F7 = Registration - maybe a poor translation, needs better one! + // { PS2_KEY_F9, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_COPY, PC9801_CTRL_NONE, }, // CTRL + F9 = Copy + // { PS2_KEY_F10, PS2CTRL_FUNC | PS2CTRL_CTRL, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_HELP, PC9801_CTRL_NONE, }, // CTRL + F10 = Help + { PS2_KEY_F1, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F1, PC9801_CTRL_NONE, }, // F1 + { PS2_KEY_F2, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F2, PC9801_CTRL_NONE, }, // F2 + { PS2_KEY_F3, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F3, PC9801_CTRL_NONE, }, // F3 + { PS2_KEY_F4, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F4, PC9801_CTRL_NONE, }, // F4 + { PS2_KEY_F5, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F5, PC9801_CTRL_NONE, }, // F5 + { PS2_KEY_F6, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F6, PC9801_CTRL_NONE, }, // F6 + { PS2_KEY_F7, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F7, PC9801_CTRL_NONE, }, // F7 + { PS2_KEY_F8, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F8, PC9801_CTRL_NONE, }, // F8 + { PS2_KEY_F9, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F9, PC9801_CTRL_NONE, }, // F9 + { PS2_KEY_F10, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F10, PC9801_CTRL_NONE, }, // F10 + // { PS2_KEY_F11, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_OPT_1, PC9801_CTRL_NONE, }, // F11 - OPT.1 + // { PS2_KEY_F12, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_OPT_2, PC9801_CTRL_NONE, }, // F12 - OPT.2 + //PS2 Code PS2 Ctrl (Flags to Match) Machine PC-9801 Data PC-9801 Ctrl (Flags to Set). + // ALPHA keys, case is maaped in the PC-9801 via the SHIFT key event or CAPS key. + { PS2_KEY_A, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_A, PC9801_CTRL_NONE, }, // A + { PS2_KEY_B, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_B, PC9801_CTRL_NONE, }, // B + { PS2_KEY_C, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_C, PC9801_CTRL_NONE, }, // C + { PS2_KEY_D, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_D, PC9801_CTRL_NONE, }, // D + { PS2_KEY_E, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_E, PC9801_CTRL_NONE, }, // E + { PS2_KEY_F, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_F, PC9801_CTRL_NONE, }, // F + { PS2_KEY_G, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_G, PC9801_CTRL_NONE, }, // G + { PS2_KEY_H, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_H, PC9801_CTRL_NONE, }, // H + { PS2_KEY_I, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_I, PC9801_CTRL_NONE, }, // I + { PS2_KEY_J, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_J, PC9801_CTRL_NONE, }, // J + { PS2_KEY_K, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_K, PC9801_CTRL_NONE, }, // K + { PS2_KEY_L, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_L, PC9801_CTRL_NONE, }, // L + { PS2_KEY_M, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_M, PC9801_CTRL_NONE, }, // M + { PS2_KEY_N, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_N, PC9801_CTRL_NONE, }, // N + { PS2_KEY_O, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_O, PC9801_CTRL_NONE, }, // O + { PS2_KEY_P, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_P, PC9801_CTRL_NONE, }, // P + { PS2_KEY_Q, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_Q, PC9801_CTRL_NONE, }, // Q + { PS2_KEY_R, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_R, PC9801_CTRL_NONE, }, // R + { PS2_KEY_S, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_S, PC9801_CTRL_NONE, }, // S + { PS2_KEY_T, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_T, PC9801_CTRL_NONE, }, // T + { PS2_KEY_U, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_U, PC9801_CTRL_NONE, }, // U + { PS2_KEY_V, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_V, PC9801_CTRL_NONE, }, // V + { PS2_KEY_W, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_W, PC9801_CTRL_NONE, }, // W + { PS2_KEY_X, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_X, PC9801_CTRL_NONE, }, // X + { PS2_KEY_Y, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_Y, PC9801_CTRL_NONE, }, // Y + { PS2_KEY_Z, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_Z, PC9801_CTRL_NONE, }, // Z + // Numeric keys. + { PS2_KEY_0, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_9, PC9801_CTRL_NONE, }, // Close Bracket ) + { PS2_KEY_0, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_0, PC9801_CTRL_NONE, }, // 0 + { PS2_KEY_1, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_1, PC9801_CTRL_NONE, }, // 1 + { PS2_KEY_2, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_2, PC9801_CTRL_NONE, }, // 2 + { PS2_KEY_3, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_3, PC9801_CTRL_NONE, }, // 3 + { PS2_KEY_4, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_4, PC9801_CTRL_NONE, }, // 4 + { PS2_KEY_5, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_5, PC9801_CTRL_NONE, }, // 5 + { PS2_KEY_6, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CIRCUMFLEX, PC9801_CTRL_RELEASESHIFT, }, // Circumflex ^ + { PS2_KEY_6, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_6, PC9801_CTRL_NONE, }, // 6 + { PS2_KEY_7, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_6, PC9801_CTRL_NONE, }, // & + { PS2_KEY_7, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_7, PC9801_CTRL_NONE, }, // 7 + { PS2_KEY_8, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_COLON, PC9801_CTRL_NONE, }, // Start * + { PS2_KEY_8, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_8, PC9801_CTRL_NONE, }, // 8 + { PS2_KEY_9, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_8, PC9801_CTRL_NONE, }, // Open Bracket ( + { PS2_KEY_9, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_9, PC9801_CTRL_NONE, }, // 9 + //PS2 Code PS2 Ctrl (Flags to Match) Machine PC-9801 Data PC-9801 Ctrl (Flags to Set). + // Punctuation keys. + { PS2_KEY_SPACE, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_SPACE, PC9801_CTRL_NONE, }, // Space + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CIRCUMFLEX, PC9801_CTRL_NONE, }, // Upper Bar + { PS2_KEY_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_MINUS, PC9801_CTRL_NONE, }, // Minus - + { PS2_KEY_EQUAL, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_SEMI, PC9801_CTRL_SHIFT, }, // Plus + + { PS2_KEY_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_MINUS, PC9801_CTRL_SHIFT, }, // Equal = + { PS2_KEY_DOT, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_DOT, PC9801_CTRL_NONE, }, // Greater Than > + { PS2_KEY_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_DOT, PC9801_CTRL_NONE, }, // Dot + { PS2_KEY_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_DIV, PC9801_CTRL_NONE, }, // Divide / + { PS2_KEY_SEMI, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_COLON, PC9801_CTRL_RELEASESHIFT, }, // Colon : + { PS2_KEY_SEMI, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_SEMI, PC9801_CTRL_NONE, }, // Semi-Colon ; + { PS2_KEY_OPEN_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_OPEN_SQ, PC9801_CTRL_NONE, }, // [ + { PS2_KEY_CLOSE_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CLOSE_SQ, PC9801_CTRL_NONE, }, // ] + { PS2_KEY_APOS, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_AT, PC9801_CTRL_RELEASESHIFT, }, // @ + { PS2_KEY_APOS, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_7, PC9801_CTRL_SHIFT, }, // ' + { PS2_KEY_BACK, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_YEN, PC9801_CTRL_NONE, }, // Back slash maps to Yen + { PS2_KEY_BACK, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_YEN, PC9801_CTRL_NONE, }, // Back slash maps to Yen + { PS2_KEY_HASH, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_3, PC9801_CTRL_SHIFT, }, // Hash + { PS2_KEY_COMMA, PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_COMMA, PC9801_CTRL_NONE, }, // Less Than < + { PS2_KEY_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_COMMA, PC9801_CTRL_NONE, }, // Comma , + { PS2_KEY_BTICK, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_UNDERLINE, PC9801_CTRL_SHIFT, }, // Underline + { PS2_KEY_BTICK, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_AT, PC9801_CTRL_SHIFT, }, // Back Tick ` + // Control keys. + { PS2_KEY_TAB, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_TAB, PC9801_CTRL_NONE, }, // TAB + { PS2_KEY_BS, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_BS, PC9801_CTRL_NONE, }, // Backspace + { PS2_KEY_ESC, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_ESC, PC9801_CTRL_NONE, }, // ESCape + { PS2_KEY_INSERT, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_INS, PC9801_CTRL_NONE, }, // INSERT + { PS2_KEY_HOME, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CLR, PC9801_CTRL_NONE, }, // CLR + { PS2_KEY_HOME, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_HOME, PC9801_CTRL_NONE, }, // HOME + { PS2_KEY_DELETE, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_DEL, PC9801_CTRL_NONE, }, // DELETE + { PS2_KEY_UP_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_UP_ARROW, PC9801_CTRL_NONE, }, // Up Arrow + { PS2_KEY_L_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_L_ARROW, PC9801_CTRL_NONE, }, // Left Arrow + { PS2_KEY_DN_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_DN_ARROW, PC9801_CTRL_NONE, }, // Down Arrow + { PS2_KEY_R_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_R_ARROW, PC9801_CTRL_NONE, }, // Right Arrow + { PS2_KEY_PGUP, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_ROLLUP, PC9801_CTRL_NONE, }, // Roll Up. + { PS2_KEY_PGDN, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_ROLLDN, PC9801_CTRL_NONE, }, // Roll Down + { PS2_KEY_SCROLL, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, ' ', PC9801_CTRL_NONE, }, // Not assigned. + { PS2_KEY_ENTER, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_RETURN, PC9801_CTRL_NONE, }, // Not assigned. + { PS2_KEY_CAPS, PS2CTRL_CAPS, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CAPS, PC9801_CTRL_NONE, }, // CAPS + { PS2_KEY_END, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_UNDO, PC9801_CTRL_NONE, }, // UNDO + // Keypad. + { PS2_KEY_KP0, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP0, PC9801_CTRL_NONE, }, // Keypad 0 + { PS2_KEY_KP1, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP1, PC9801_CTRL_NONE, }, // Keypad 1 + { PS2_KEY_KP2, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP2, PC9801_CTRL_NONE, }, // Keypad 2 + { PS2_KEY_KP3, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP3, PC9801_CTRL_NONE, }, // Keypad 3 + { PS2_KEY_KP4, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP4, PC9801_CTRL_NONE, }, // Keypad 4 + { PS2_KEY_KP5, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP5, PC9801_CTRL_NONE, }, // Keypad 5 + { PS2_KEY_KP6, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP6, PC9801_CTRL_NONE, }, // Keypad 6 + { PS2_KEY_KP7, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP7, PC9801_CTRL_NONE, }, // Keypad 7 + { PS2_KEY_KP8, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP8, PC9801_CTRL_NONE, }, // Keypad 8 + { PS2_KEY_KP9, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP9, PC9801_CTRL_NONE, }, // Keypad 9 + { PS2_KEY_KP_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_COMMA, PC9801_CTRL_NONE, }, // Keypad Comma , + { PS2_KEY_KP_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_DOT, PC9801_CTRL_NONE, }, // Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_PLUS, PC9801_CTRL_NONE, }, // Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_MINUS, PC9801_CTRL_NONE, }, // Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_TIMES, PC9801_CTRL_NONE, }, // Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_DIV, PC9801_CTRL_NONE, }, // Keypad Divide / + { PS2_KEY_KP_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_MINUS, PC9801_CTRL_SHIFT, }, // Keypad Equal = + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_ENTER, PC9801_CTRL_NONE, }, // Keypad Ebter / + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_KP_EQUAL, PC9801_CTRL_NONE, }, // Keypad Ebter / + //PS2 Code PS2 Ctrl (Flags to Match) Machine PC-9801 Data PC-9801 Ctrl (Flags to Set). + // Special keys. + { PS2_KEY_PRTSCR, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, 0x00, PC9801_CTRL_NONE, }, // + { PS2_KEY_PAUSE, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_BREAK, PC9801_CTRL_RELEASESHIFT, }, // BREAK KEY + // { PS2_KEY_L_GUI, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_XF1, PC9801_CTRL_NONE, }, // XF1 + // { PS2_KEY_L_ALT, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_XF2, PC9801_CTRL_NONE, }, // XF2 + // { PS2_KEY_R_ALT, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_XF3, PC9801_CTRL_NONE, }, // XF3 + // { PS2_KEY_R_GUI, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_XF4, PC9801_CTRL_NONE, }, // XF4 + // { PS2_KEY_MENU, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_XF5, PC9801_CTRL_NONE, }, // XF5 + // Modifiers are last, only being selected if an earlier match isnt made. + { PS2_KEY_L_SHIFT, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_SHIFT, PC9801_CTRL_NONE, }, // + { PS2_KEY_R_SHIFT, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_SHIFT, PC9801_CTRL_NONE, }, // + { PS2_KEY_L_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CTRL, PC9801_CTRL_NONE, }, // Map to Control + { PS2_KEY_R_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_CTRL, PC9801_CTRL_NONE, }, // Map to Control + { 0, PS2CTRL_NONE, KEYMAP_STANDARD, PC9801_ALL, PC9801_KEY_NULL, PC9801_CTRL_NONE, }, // + }}; +}; + +#endif // PC9801_H diff --git a/main/include/PS2KeyAdvanced.h b/main/include/PS2KeyAdvanced.h deleted file mode 120000 index 1cd7d19..0000000 --- a/main/include/PS2KeyAdvanced.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/PS2KeyAdvanced.h \ No newline at end of file diff --git a/main/include/PS2KeyAdvanced.h b/main/include/PS2KeyAdvanced.h new file mode 100644 index 0000000..034d3bc --- /dev/null +++ b/main/include/PS2KeyAdvanced.h @@ -0,0 +1,451 @@ +/* Version V1.0.9 + PS2KeyAdvanced.h - PS2KeyAdvanced library + Copyright (c) 2007 Free Software Foundation. All right reserved. + Written by Paul Carpenter, PC Services + Created September 2014 + Updated January 2016 - Paul Carpenter - add tested on Due and tidy ups for V1.5 Library Management + January 2020 Fix typos, correct keyboard reset status improve library.properties + and additional platform handling and some documentation + March 2020 Add SAMD1 as recognised support as has been tested by user + Improve different architecture handling + November 2020 Add support for STM32 from user Hiabuto-de + Tested on STM32Duino-Framework and PlatformIO on STM32F103C8T6 and an IBM Model M + July 2021 Add workaround for ESP32 issue with Silicon (hardware) from user submissions + + IMPORTANT WARNING + + If using a DUE or similar board with 3V3 I/O you MUST put a level translator + like a Texas Instruments TXS0102 or FET circuit as the signals are + Bi-directional (signals transmitted from both ends on same wire). + + Failure to do so may damage your Arduino Due or similar board. + + Test History + September 2014 Uno and Mega 2560 September 2014 using Arduino V1.6.0 + January 2016 Uno, Mega 2560 and Due using Arduino 1.6.7 and Due Board + Manager V1.6.6 + + ONLY use defines in this file others may disappear on updates. + + This is for a LATIN style keyboard using Scan code set 2. See various + websites on what different scan code sets use. Scan Code Set 2 is the + default scan code set for PS2 keyboards on power up. + + Will support most keyboards even ones with multimedia keys or even 24 function keys. + + Fully featured PS2 keyboard library to provide + All function and movement keys supported even multi-lingual + Parity checking of data sent/received on receive request keyboard resend + Resends data when needed handles keyboard protocol for RESEND and ECHO + Functions for get and set of + Scancode set in use READ only + LED and LOCK control + ReadID + Reset keyboard + Send ECHO + Ignore Break codes for keys + Ignore typematic repeat of CTRL, SHIFT, ALT, Num, Scroll, Caps + Handles NUM, CAPS and SCROLL lock keys to LEDs + Handles NUM/SCROLL internally + + Read function Returns an UNSIGNED INT containing + Make/Break status + Caps status + Shift, CTRL, ALT, ALT GR, GUI keys + Flag for function key not a displayable/printable character + 8 bit key code + + Code Ranges (bottom byte of unsigned int) + 0 invalid/error + 1-1F Functions (Caps, Shift, ALT, Enter, DEL... ) + 1A-1F Functions with ASCII control code + (DEL, BS, TAB, ESC, ENTER, SPACE) + 20-61 Printable characters noting + 0-9 = 0x30 to 0x39 as ASCII + A to Z = 0x41 to 0x5A as upper case ASCII type codes + 8B Extra European key + 61-A0 Function keys and other special keys (plus F2 and F1) + 61-78 F1 to F24 + 79-8A Multimedia + 8B NOT included + 8C-8E ACPI power + 91-A0 and F2 and F1 - Special multilingual + A8-FF Keyboard communications commands (note F2 and F1 are special + codes for special multi-lingual keyboards) + + By using these ranges it is possible to perform detection of any key and do + easy translation to ASCII/UTF-8 avoiding keys that do not have a valid code. + + Top Byte is 8 bits denoting as follows with defines for bit code + + Define name bit description + PS2_BREAK 15 1 = Break key code + (MSB) 0 = Make Key code + PS2_SHIFT 14 1 = Shift key pressed as well (either side) + 0 = NO shift key + PS2_CTRL 13 1 = Ctrl key pressed as well (either side) + 0 = NO Ctrl key + PS2_CAPS 12 1 = Caps Lock ON + 0 = Caps lock OFF + PS2_ALT 11 1 = Left Alt key pressed as well + 0 = NO Left Alt key + PS2_ALT_GR 10 1 = Right Alt (Alt GR) key pressed as well + 0 = NO Right Alt key + PS2_GUI 9 1 = GUI key pressed as well (either) + 0 = NO GUI key + PS2_FUNCTION 8 1 = FUNCTION key non-printable character (plus space, tab, enter) + 0 = standard character key + + Error Codes + Most functions return 0 or 0xFFFF as error, other codes to note and + handle appropriately + 0xAA keyboard has reset and passed power up tests + will happen if keyboard plugged in after code start + 0xFC Keyboard General error or power up fail + + It is responsibility of your programme to deal with converting special cases like + + sends a special code to something else. If you wish to do that make a + NEW library called SOMETHING different NOT a variant or revision of this one, as you + are changing base functionality + + See PS2KeyCode.h for codes from the keyboard this library uses to decode. + (may disappear in updates do not rely on that file or definitions) + + See this file for returned definitions of Keys + + Note defines starting + PS2_KC_* are internal defines for codes from the keyboard + PS2_KEY_* are the codes this library returns + PS2_* remaining defines for use in higher levels + + To get the key as ASCII/UTF-8 single byte character conversion requires use + of PS2KeyMap library AS WELL. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef PS2KeyAdvanced_h +#define PS2KeyAdvanced_h + +// Platform specific areas +// Harvard architecture settings for PROGMEM +// Add separate for EACH architecture as easier to maintain +// AVR (includes Teensy 2.0) +#if defined( ARDUINO_ARCH_AVR ) +#define PS2_SUPPORTED 1 +#define PS2_REQUIRES_PROGMEM 1 +#define PS2_CLEAR_PENDING_IRQ 1 +#endif +// SAM (Due) +#if defined( ARDUINO_ARCH_SAM ) +#define PS2_SUPPORTED 1 +#define PS2_CLEAR_PENDING_IRQ 1 +#endif +// SAMD1 +#if defined( ARDUINO_ARCH_SAMD1 ) +#define PS2_SUPPORTED 1 +#define PS2_CLEAR_PENDING_IRQ 1 +#endif +// STM32 +#if defined( ARDUINO_ARCH_STM32 ) +#define PS2_SUPPORTED 1 +#define PS2_CLEAR_PENDING_IRQ 1 +#endif +// ESP32 +#if defined( ARDUINO_ARCH_ESP32 ) +#define PS2_SUPPORTED 1 +#define PS2_ONLY_CHANGE_IRQ 1 +#endif + +// Invalid architecture +#if !( defined( PS2_SUPPORTED ) ) +#warning Library is NOT supported on this board Use at your OWN risk +#endif + +/* Flags/bit masks for status bits in returned unsigned int value */ +#define PS2_BREAK 0x8000 +#define PS2_SHIFT 0x4000 +#define PS2_CTRL 0x2000 +#define PS2_CAPS 0x1000 +#define PS2_ALT 0x800 +#define PS2_ALT_GR 0x400 +#define PS2_GUI 0x200 +#define PS2_FUNCTION 0x100 + +/* General defines of communications codes */ +/* Command or response */ +#define PS2_KEY_RESEND 0xFE +#define PS2_KEY_ACK 0xFA +#define PS2_KEY_ECHO 0xEE +/* Responses */ +#define PS2_KEY_BAT 0xAA +// Actually buffer overrun +#define PS2_KEY_OVERRUN 0xFF +// Below is general error code +#define PS2_KEY_ERROR 0xFC + +/* Command parameters for functions */ +/* LED codes OR together */ +#define PS2_LOCK_SCROLL 0x01 +#define PS2_LOCK_NUM 0x02 +#define PS2_LOCK_CAPS 0x04 +/* Only useful for very few keyboards */ +#define PS2_LOCK_EXTRA 0x08 + +/* Returned keycode definitions */ +/* Do NOT change these codings as you will break base + functionality use PS2KeyMap for that and internationalisation */ +#define PS2_KEY_NUM 0x01 +#define PS2_KEY_SCROLL 0x02 +#define PS2_KEY_CAPS 0x03 +#define PS2_KEY_PRTSCR 0x04 +#define PS2_KEY_PAUSE 0x05 +#define PS2_KEY_L_SHIFT 0x06 +#define PS2_KEY_R_SHIFT 0x07 +#define PS2_KEY_L_CTRL 0X08 +#define PS2_KEY_R_CTRL 0X09 +#define PS2_KEY_L_ALT 0x0A +#define PS2_KEY_R_ALT 0x0B +/* Sometimes called windows key */ +#define PS2_KEY_L_GUI 0x0C +#define PS2_KEY_R_GUI 0x0D +#define PS2_KEY_MENU 0x0E +/* Break is CTRL + PAUSE generated inside keyboard */ +#define PS2_KEY_BREAK 0x0F +/* Generated by some keyboards by ALT and PRTSCR */ +#define PS2_KEY_SYSRQ 0x10 +#define PS2_KEY_HOME 0x11 +#define PS2_KEY_END 0x12 +#define PS2_KEY_PGUP 0x13 +#define PS2_KEY_PGDN 0x14 +#define PS2_KEY_L_ARROW 0x15 +#define PS2_KEY_R_ARROW 0x16 +#define PS2_KEY_UP_ARROW 0x17 +#define PS2_KEY_DN_ARROW 0x18 +#define PS2_KEY_INSERT 0x19 +#define PS2_KEY_DELETE 0x1A +#define PS2_KEY_ESC 0x1B +#define PS2_KEY_BS 0x1C +#define PS2_KEY_TAB 0x1D +#define PS2_KEY_ENTER 0x1E +#define PS2_KEY_SPACE 0x1F +#define PS2_KEY_KP0 0x20 +#define PS2_KEY_KP1 0x21 +#define PS2_KEY_KP2 0x22 +#define PS2_KEY_KP3 0x23 +#define PS2_KEY_KP4 0x24 +#define PS2_KEY_KP5 0x25 +#define PS2_KEY_KP6 0x26 +#define PS2_KEY_KP7 0x27 +#define PS2_KEY_KP8 0x28 +#define PS2_KEY_KP9 0x29 +#define PS2_KEY_KP_DOT 0x2A +#define PS2_KEY_KP_ENTER 0x2B +#define PS2_KEY_KP_PLUS 0x2C +#define PS2_KEY_KP_MINUS 0x2D +#define PS2_KEY_KP_TIMES 0x2E +#define PS2_KEY_KP_DIV 0x2F +#define PS2_KEY_0 0X30 +#define PS2_KEY_1 0X31 +#define PS2_KEY_2 0X32 +#define PS2_KEY_3 0X33 +#define PS2_KEY_4 0X34 +#define PS2_KEY_5 0X35 +#define PS2_KEY_6 0X36 +#define PS2_KEY_7 0X37 +#define PS2_KEY_8 0X38 +#define PS2_KEY_9 0X39 +#define PS2_KEY_APOS 0X3A +#define PS2_KEY_COMMA 0X3B +#define PS2_KEY_MINUS 0X3C +#define PS2_KEY_DOT 0X3D +#define PS2_KEY_DIV 0X3E +/* Some Numeric keyboards have an '=' on right keypad */ +#define PS2_KEY_KP_EQUAL 0x3F +/* Single quote or back quote */ +#define PS2_KEY_SINGLE 0X40 +#define PS2_KEY_A 0X41 +#define PS2_KEY_B 0X42 +#define PS2_KEY_C 0X43 +#define PS2_KEY_D 0X44 +#define PS2_KEY_E 0X45 +#define PS2_KEY_F 0X46 +#define PS2_KEY_G 0X47 +#define PS2_KEY_H 0X48 +#define PS2_KEY_I 0X49 +#define PS2_KEY_J 0X4A +#define PS2_KEY_K 0X4B +#define PS2_KEY_L 0X4C +#define PS2_KEY_M 0X4D +#define PS2_KEY_N 0X4E +#define PS2_KEY_O 0X4F +#define PS2_KEY_P 0X50 +#define PS2_KEY_Q 0X51 +#define PS2_KEY_R 0X52 +#define PS2_KEY_S 0X53 +#define PS2_KEY_T 0X54 +#define PS2_KEY_U 0X55 +#define PS2_KEY_V 0X56 +#define PS2_KEY_W 0X57 +#define PS2_KEY_X 0X58 +#define PS2_KEY_Y 0X59 +#define PS2_KEY_Z 0X5A +#define PS2_KEY_SEMI 0X5B +#define PS2_KEY_BACK 0X5C +#define PS2_KEY_OPEN_SQ 0X5D +#define PS2_KEY_CLOSE_SQ 0X5E +#define PS2_KEY_EQUAL 0X5F +/* Some Numeric keypads have a comma key */ +#define PS2_KEY_KP_COMMA 0x60 +#define PS2_KEY_F1 0X61 +#define PS2_KEY_F2 0X62 +#define PS2_KEY_F3 0X63 +#define PS2_KEY_F4 0X64 +#define PS2_KEY_F5 0X65 +#define PS2_KEY_F6 0X66 +#define PS2_KEY_F7 0X67 +#define PS2_KEY_F8 0X68 +#define PS2_KEY_F9 0X69 +#define PS2_KEY_F10 0X6A +#define PS2_KEY_F11 0X6B +#define PS2_KEY_F12 0X6C +#define PS2_KEY_F13 0X6D +#define PS2_KEY_F14 0X6E +#define PS2_KEY_F15 0X6F +#define PS2_KEY_F16 0X70 +#define PS2_KEY_F17 0X71 +#define PS2_KEY_F18 0X72 +#define PS2_KEY_F19 0X73 +#define PS2_KEY_F20 0X74 +#define PS2_KEY_F21 0X75 +#define PS2_KEY_F22 0X76 +#define PS2_KEY_F23 0X77 +#define PS2_KEY_F24 0X78 +#define PS2_KEY_NEXT_TR 0X79 +#define PS2_KEY_PREV_TR 0X7A +#define PS2_KEY_STOP 0X7B +#define PS2_KEY_PLAY 0X7C +#define PS2_KEY_MUTE 0X7D +#define PS2_KEY_VOL_UP 0X7E +#define PS2_KEY_VOL_DN 0X7F +#define PS2_KEY_MEDIA 0X80 +#define PS2_KEY_EMAIL 0X81 +#define PS2_KEY_CALC 0X82 +#define PS2_KEY_COMPUTER 0X83 +#define PS2_KEY_WEB_SEARCH 0X84 +#define PS2_KEY_WEB_HOME 0X85 +#define PS2_KEY_WEB_BACK 0X86 +#define PS2_KEY_WEB_FORWARD 0X87 +#define PS2_KEY_WEB_STOP 0X88 +#define PS2_KEY_WEB_REFRESH 0X89 +#define PS2_KEY_WEB_FAVOR 0X8A +#define PS2_KEY_EUROPE2 0X8B +#define PS2_KEY_POWER 0X8C +#define PS2_KEY_SLEEP 0X8D +#define PS2_KEY_WAKE 0X90 +#define PS2_KEY_INTL1 0X91 +#define PS2_KEY_INTL2 0X92 +#define PS2_KEY_INTL3 0X93 +#define PS2_KEY_INTL4 0X94 +#define PS2_KEY_INTL5 0X95 +#define PS2_KEY_LANG1 0X96 +#define PS2_KEY_LANG2 0X97 +#define PS2_KEY_LANG3 0X98 +#define PS2_KEY_LANG4 0X99 +#define PS2_KEY_LANG5 0xA0 +#define PS2_KEY_BTICK 0X9A +#define PS2_KEY_HASH 0X9B + +/* + Purpose: Provides advanced access to PS2 keyboards + Public class definitions + + See standard error codes for error code returns + */ +class PS2KeyAdvanced { + public: + /* This constructor does basically nothing. Please call the begin(int,int) + method before using any other method of this class. */ + PS2KeyAdvanced( ); + + // Destructor - disable and detach interrupts and free up resources. + ~PS2KeyAdvanced( ); + + /* Starts the keyboard "service" by registering the external interrupt. + setting the pin modes correctly and driving those needed to high. + Sets default LOCK status (LEDs) to passed in value or default of all off + The best place to call this method is in the setup routine. */ + void begin( uint8_t, uint8_t ); + + // Additional key available check which doesnt affect the queue. + uint8_t keyAvailable(void); + + /* Returns number of codes available or 0 for none */ + uint8_t available( ); + + /* Returns the key last read from the keyboard. + If there is no key available, 0 is returned. */ + uint16_t read( ); + + /* Returns the current status of Locks + Use Macro to mask out bits from + PS2_LOCK_NUM PS2_LOCK_CAPS PS2_LOCK_SCROLL */ + uint8_t getLock( ); + + /* Sets the current status of Locks and LEDs + Use macro defines added together from + PS2_LOCK_NUM PS2_LOCK_CAPS PS2_LOCK_SCROLL */ + void setLock( uint8_t ); + + /* Set library to not send break key codes + 1 = no break codes + 0 = send break codes */ + void setNoBreak( uint8_t ); + + /* Set library to not repeat make codes for CTRL, ALT, GUI, SHIFT + 1 = no repeat codes + 0 = send repeat codes */ + void setNoRepeat( uint8_t ); + + /* Resets keyboard when reset has completed + keyboard sends AA - Pass or FC for fail + Read from keyboard data buffer */ + void resetKey( ); + + /* Get the current Scancode Set used in keyboard + returned data in keyboard buffer read as keys */ + void getScanCodeSet( void ); + + /* Get the current Scancode Set used in keyboard + returned data in keyboard buffer read as keys */ + void readID( void ); + + /* Send Echo command to keyboard + returned data in keyboard buffer read as keys */ + void echo( void ); + + // Method to suspend PS2 activity, primarily by disabling the interrupts. + void suspend(bool suspend); + + /* Send Typematic rate/delay command to keyboard + First Parameter rate is 0 - 0x1F (31) + 0 = 30 CPS + 0x1F = 2 CPS + default in keyboard is 0xB (10.9 CPS) + Second Parameter delay is 0 - 3 for 0.25s to 1s in 0.25 increments + default in keyboard is 1 = 0.5 second delay + Returned data in keyboard buffer read as keys */ + int typematic( uint8_t , uint8_t ); +}; +#endif diff --git a/main/include/PS2KeyCode.h b/main/include/PS2KeyCode.h deleted file mode 120000 index cbee121..0000000 --- a/main/include/PS2KeyCode.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/PS2KeyCode.h \ No newline at end of file diff --git a/main/include/PS2KeyCode.h b/main/include/PS2KeyCode.h new file mode 100644 index 0000000..ec4da88 --- /dev/null +++ b/main/include/PS2KeyCode.h @@ -0,0 +1,276 @@ +/* Version V1.0.8 + PS2KeyCode.h - PS2KeyAdvanced library Internal actual PS2 key code sequences + Copyright (c) 2007 Free Software Foundation. All right reserved. + Written by Paul Carpenter, PC Services + Created September 2014 + Updated January 2016 - Paul Carpenter - add tested on Due and tidy ups for V1.5 Library Management + Updated December 2019 - Paul Carpenter - Fix typo in code for Multimedia STOP + + PRIVATE to library + + Test History + September 2014 Uno and Mega 2560 September 2014 using Arduino V1.6.0 + January 2016 Uno, Mega 2560 and Due using Arduino 1.6.7 and Due Board + Manager V1.6.6 + + This is for a LATIN style keyboard. Will support most keyboards even ones + with multimedia keys or even 24 function keys. + + Definitions used for key codes from a PS2 keyboard, do not use in your + code these are to be handled INTERNALLY by the library. + (may disappear in updates do not rely on this file or definitions) + + See PS2KeyAdvanced.h for codes returned from library and flag settings + + Defines are in three groups + + Special codes definition of communications bytes + + Single Byte codes returned as key codes + + Two byte Codes preceded by E0 code returned as keycodes + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PS2KeyCode_h +#define PS2KeyCode_h + +/* Ignore code for key code translation */ +#define PS2_KEY_IGNORE 0xBB + +// buffer sizes keyboard RX and TX, then key reading buffer +// Minimum size 8 can be larger +#define _RX_BUFFER_SIZE 8 +// Minimum size 6 can be larger +#define _TX_BUFFER_SIZE 6 +// Output Buffer of unsigned int values. Minimum size 4 can be larger +#define _KEY_BUFF_SIZE 4 + +/* private defines for library files not global */ +/* _ps2mode status flags */ +#define _PS2_BUSY 0x80 +#define _TX_MODE 0x40 +#define _BREAK_KEY 0x20 +#define _WAIT_RESPONSE 0x10 +#define _E0_MODE 0x08 +#define _E1_MODE 0x04 +#define _LAST_VALID 0x02 + +/* _tx_ready flags */ +#define _HANDSHAKE 0x80 +#define _COMMAND 0x01 + +/* Key Repeat defines */ +#define _NO_BREAKS 0x08 +#define _NO_REPEATS 0x80 + +/* PS2_keystatus byte masks (from 16 bit int masks) */ +#define _BREAK ( PS2_BREAK >> 8 ) +#define _SHIFT ( PS2_SHIFT >> 8 ) +#define _CTRL ( PS2_CTRL >> 8 ) +#define _CAPS ( PS2_CAPS >> 8 ) +#define _ALT ( PS2_ALT >> 8 ) +#define _ALT_GR ( PS2_ALT_GR >> 8 ) +#define _GUI ( PS2_GUI >> 8 ) +#define _FUNCTION ( PS2_FUNCTION >> 8 ) + +/* General defines of comms codes */ +/* Command or response */ +#define PS2_KC_RESEND 0xFE +#define PS2_KC_ACK 0xFA +#define PS2_KC_ECHO 0xEE +/* Responses */ +#define PS2_KC_BAT 0xAA +// Actually buffer overrun +#define PS2_KC_OVERRUN 0xFF +// Below is general error code +#define PS2_KC_ERROR 0xFC +#define PS2_KC_KEYBREAK 0xF0 +#define PS2_KC_EXTEND1 0xE1 +#define PS2_KC_EXTEND 0xE0 +/* Commands */ +#define PS2_KC_RESET 0xFF +#define PS2_KC_DEFAULTS 0xF6 +#define PS2_KC_DISABLE 0xF5 +#define PS2_KC_ENABLE 0xF4 +#define PS2_KC_RATE 0xF3 +#define PS2_KC_READID 0xF2 +#define PS2_KC_SCANCODE 0xF0 +#define PS2_KC_LOCK 0xED + +/* Single Byte Key Codes */ +#define PS2_KC_NUM 0x77 +#define PS2_KC_SCROLL 0x7E +#define PS2_KC_CAPS 0x58 +#define PS2_KC_L_SHIFT 0x12 +#define PS2_KC_R_SHIFT 0x59 +/* This is Left CTRL and ALT but Right version is in E0 with same code */ +#define PS2_KC_CTRL 0X14 +#define PS2_KC_ALT 0x11 +/* Generated by some keyboards by ALT and PRTSCR */ +#define PS2_KC_SYSRQ 0x84 +#define PS2_KC_ESC 0x76 +#define PS2_KC_BS 0x66 +#define PS2_KC_TAB 0x0D +#define PS2_KC_ENTER 0x5A +#define PS2_KC_SPACE 0x29 +#define PS2_KC_KP0 0x70 +#define PS2_KC_KP1 0x69 +#define PS2_KC_KP2 0x72 +#define PS2_KC_KP3 0x7A +#define PS2_KC_KP4 0x6B +#define PS2_KC_KP5 0x73 +#define PS2_KC_KP6 0x74 +#define PS2_KC_KP7 0x6C +#define PS2_KC_KP8 0x75 +#define PS2_KC_KP9 0x7D +#define PS2_KC_KP_DOT 0x71 +#define PS2_KC_KP_PLUS 0x79 +#define PS2_KC_KP_MINUS 0x7B +#define PS2_KC_KP_TIMES 0x7C +/* Some keyboards have an '=' on right keypad */ +#define PS2_KC_KP_EQUAL 0x0F +#define PS2_KC_0 0X45 +#define PS2_KC_1 0X16 +#define PS2_KC_2 0X1E +#define PS2_KC_3 0X26 +#define PS2_KC_4 0X25 +#define PS2_KC_5 0X2E +#define PS2_KC_6 0X36 +#define PS2_KC_7 0X3D +#define PS2_KC_8 0X3E +#define PS2_KC_9 0X46 +#define PS2_KC_APOS 0X52 +#define PS2_KC_COMMA 0X41 +#define PS2_KC_MINUS 0X4E +#define PS2_KC_DOT 0X49 +#define PS2_KC_DIV 0X4A +/* Single quote or back apostrophe */ +#define PS2_KC_BTICK 0X0E +#define PS2_KC_A 0X1C +#define PS2_KC_B 0X32 +#define PS2_KC_C 0X21 +#define PS2_KC_D 0X23 +#define PS2_KC_E 0X24 +#define PS2_KC_F 0X2B +#define PS2_KC_G 0X34 +#define PS2_KC_H 0X33 +#define PS2_KC_I 0X43 +#define PS2_KC_J 0X3B +#define PS2_KC_K 0X42 +#define PS2_KC_L 0X4B +#define PS2_KC_M 0X3A +#define PS2_KC_N 0X31 +#define PS2_KC_O 0X44 +#define PS2_KC_P 0X4D +#define PS2_KC_Q 0X15 +#define PS2_KC_R 0X2D +#define PS2_KC_S 0X1B +#define PS2_KC_T 0X2C +#define PS2_KC_U 0X3C +#define PS2_KC_V 0X2A +#define PS2_KC_W 0X1D +#define PS2_KC_X 0X22 +#define PS2_KC_Y 0X35 +#define PS2_KC_Z 0X1A +#define PS2_KC_SEMI 0X4C +#define PS2_KC_BACK 0X5D +// Extra key left of Z on 102 keyboards +#define PS2_KC_EUROPE2 0x61 +#define PS2_KC_OPEN_SQ 0X54 +#define PS2_KC_CLOSE_SQ 0X5B +#define PS2_KC_EQUAL 0X55 +#define PS2_KC_F1 0X05 +#define PS2_KC_F2 0X06 +#define PS2_KC_F3 0X04 +#define PS2_KC_F4 0X0C +#define PS2_KC_F5 0X03 +#define PS2_KC_F6 0X0B +#define PS2_KC_F7 0X83 +#define PS2_KC_F8 0X0A +#define PS2_KC_F9 0X01 +#define PS2_KC_F10 0X09 +#define PS2_KC_F11 0X78 +#define PS2_KC_F12 0X07 +#define PS2_KC_F13 0X08 +#define PS2_KC_F14 0X10 +#define PS2_KC_F15 0X18 +#define PS2_KC_F16 0X20 +#define PS2_KC_F17 0X28 +#define PS2_KC_F18 0X30 +#define PS2_KC_F19 0X38 +#define PS2_KC_F20 0X40 +#define PS2_KC_F21 0X48 +#define PS2_KC_F22 0X50 +#define PS2_KC_F23 0X57 +#define PS2_KC_F24 0X5F +#define PS2_KC_KP_COMMA 0X6D +#define PS2_KC_INTL1 0X51 +#define PS2_KC_INTL2 0X13 +#define PS2_KC_INTL3 0X6A +#define PS2_KC_INTL4 0X64 +#define PS2_KC_INTL5 0X67 +#define PS2_KC_LANG1 0XF2 +#define PS2_KC_LANG2 0XF1 +#define PS2_KC_LANG3 0X63 +#define PS2_KC_LANG4 0X62 +#define PS2_KC_LANG5 0X5F + +/* Extended key codes E0 table for two byte codes */ +/* PS2_CTRL and PS2_ALT Need using in any table for the right keys */ +/* first is special case for PRTSCR not always used so ignored by decoding */ +#define PS2_KC_IGNORE 0x12 +#define PS2_KC_PRTSCR 0x7C +/* Sometimes called windows key */ +#define PS2_KC_L_GUI 0x1F +#define PS2_KC_R_GUI 0x27 +#define PS2_KC_MENU 0x2F +/* Break is CTRL + PAUSE generated inside keyboard */ +#define PS2_KC_BREAK 0x7E +#define PS2_KC_HOME 0x6C +#define PS2_KC_END 0x69 +#define PS2_KC_PGUP 0x7D +#define PS2_KC_PGDN 0x7A +#define PS2_KC_L_ARROW 0x6B +#define PS2_KC_R_ARROW 0x74 +#define PS2_KC_UP_ARROW 0x75 +#define PS2_KC_DN_ARROW 0x72 +#define PS2_KC_INSERT 0x70 +#define PS2_KC_DELETE 0x71 +#define PS2_KC_KP_ENTER 0x5A +#define PS2_KC_KP_DIV 0x4A +#define PS2_KC_NEXT_TR 0X4D +#define PS2_KC_PREV_TR 0X15 +#define PS2_KC_STOP 0X3B +#define PS2_KC_PLAY 0X34 +#define PS2_KC_MUTE 0X23 +#define PS2_KC_VOL_UP 0X32 +#define PS2_KC_VOL_DN 0X21 +#define PS2_KC_MEDIA 0X50 +#define PS2_KC_EMAIL 0X48 +#define PS2_KC_CALC 0X2B +#define PS2_KC_COMPUTER 0X40 +#define PS2_KC_WEB_SEARCH 0X10 +#define PS2_KC_WEB_HOME 0X3A +#define PS2_KC_WEB_BACK 0X38 +#define PS2_KC_WEB_FORWARD 0X30 +#define PS2_KC_WEB_STOP 0X28 +#define PS2_KC_WEB_REFRESH 0X20 +#define PS2_KC_WEB_FAVOR 0X18 +#define PS2_KC_POWER 0X37 +#define PS2_KC_SLEEP 0X3F +#define PS2_KC_WAKE 0X5E +#endif diff --git a/main/include/PS2KeyTable.h b/main/include/PS2KeyTable.h deleted file mode 120000 index 6ab62d1..0000000 --- a/main/include/PS2KeyTable.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/PS2KeyTable.h \ No newline at end of file diff --git a/main/include/PS2KeyTable.h b/main/include/PS2KeyTable.h new file mode 100644 index 0000000..c0a304e --- /dev/null +++ b/main/include/PS2KeyTable.h @@ -0,0 +1,393 @@ +/* Version V1.0.8 + PS2KeyTable.h - PS2KeyAdvanced library keycode values to return values + Copyright (c) 2007 Free Software Foundation. All right reserved. + Written by Paul Carpenter, PC Services + Created September 2014 + V1.0.2 Updated January 2016 - Paul Carpenter - add tested on Due and tidy ups for V1.5 Library Management + + PRIVATE to library + + Test History + September 2014 Uno and Mega 2560 September 2014 using Arduino V1.6.0 + January 2016 Uno, Mega 2560 and Due using Arduino 1.6.7 and Due Board + Manager V1.6.6 + + Internal to library private tables + (may disappear in updates do not rely on this file or definitions) + + This is for a LATIN style keyboard. Will support most keyboards even ones + with multimedia keys or even 24 function keys. + + Definitions used for key codes from a PS2 keyboard, do not use in your + code these are handled by the library. + + See PS2KeyAdvanced.h for codes returned from library and flag settings + + Two sets of tables + + Single Byte codes returned as key codes + + Two byte Codes preceded by E0 code returned as keycodes + + Same tables used for make and break decode + + Special cases are - + + PRTSCR that ignores one of the sequences (E0,12) as this is not always sent + especially with modifier keys or some keyboards when typematic repeat comes on. + + PAUSE as this is an 8 byte sequence only one starting E1 so main code gets E1 + and waits for 7 more valid bytes to make the coding. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef PS2KeyTable_h +#define PS2KeyTable_h + +/* Table contents are pairs of numbers + first code from keyboard + second is either PS2_KEY_IGNOPRE code or key code to return + + Single byte Key table + In codes can only be 1 - 0x9F, plus 0xF2 and 0xF1 + Out Codes in range 1 to 0x9F +*/ +const uint8_t single_key[][ 2 ] = { + { PS2_KC_NUM, PS2_KEY_NUM }, + { PS2_KC_SCROLL, PS2_KEY_SCROLL }, + { PS2_KC_CAPS, PS2_KEY_CAPS }, + { PS2_KC_L_SHIFT, PS2_KEY_L_SHIFT }, + { PS2_KC_R_SHIFT, PS2_KEY_R_SHIFT }, + { PS2_KC_CTRL, PS2_KEY_L_CTRL }, + { PS2_KC_ALT, PS2_KEY_L_ALT }, + { PS2_KC_SYSRQ, PS2_KEY_SYSRQ }, + { PS2_KC_ESC, PS2_KEY_ESC }, + { PS2_KC_BS, PS2_KEY_BS }, + { PS2_KC_TAB, PS2_KEY_TAB }, + { PS2_KC_ENTER, PS2_KEY_ENTER }, + { PS2_KC_SPACE, PS2_KEY_SPACE }, + { PS2_KC_KP0, PS2_KEY_KP0 }, + { PS2_KC_KP1, PS2_KEY_KP1 }, + { PS2_KC_KP2, PS2_KEY_KP2 }, + { PS2_KC_KP3, PS2_KEY_KP3 }, + { PS2_KC_KP4, PS2_KEY_KP4 }, + { PS2_KC_KP5, PS2_KEY_KP5 }, + { PS2_KC_KP6, PS2_KEY_KP6 }, + { PS2_KC_KP7, PS2_KEY_KP7 }, + { PS2_KC_KP8, PS2_KEY_KP8 }, + { PS2_KC_KP9, PS2_KEY_KP9 }, + { PS2_KC_KP_DOT, PS2_KEY_KP_DOT }, + { PS2_KC_KP_PLUS, PS2_KEY_KP_PLUS }, + { PS2_KC_KP_MINUS, PS2_KEY_KP_MINUS }, + { PS2_KC_KP_TIMES, PS2_KEY_KP_TIMES }, + { PS2_KC_KP_EQUAL, PS2_KEY_KP_EQUAL }, + { PS2_KC_0, PS2_KEY_0 }, + { PS2_KC_1, PS2_KEY_1 }, + { PS2_KC_2, PS2_KEY_2 }, + { PS2_KC_3, PS2_KEY_3 }, + { PS2_KC_4, PS2_KEY_4 }, + { PS2_KC_5, PS2_KEY_5 }, + { PS2_KC_6, PS2_KEY_6 }, + { PS2_KC_7, PS2_KEY_7 }, + { PS2_KC_8, PS2_KEY_8 }, + { PS2_KC_9, PS2_KEY_9 }, + { PS2_KC_APOS, PS2_KEY_APOS }, + { PS2_KC_COMMA, PS2_KEY_COMMA }, + { PS2_KC_MINUS, PS2_KEY_MINUS }, + { PS2_KC_DOT, PS2_KEY_DOT }, + { PS2_KC_DIV, PS2_KEY_DIV }, + { PS2_KC_BTICK, PS2_KEY_BTICK }, + { PS2_KC_A, PS2_KEY_A }, + { PS2_KC_B, PS2_KEY_B }, + { PS2_KC_C, PS2_KEY_C }, + { PS2_KC_D, PS2_KEY_D }, + { PS2_KC_E, PS2_KEY_E }, + { PS2_KC_F, PS2_KEY_F }, + { PS2_KC_G, PS2_KEY_G }, + { PS2_KC_H, PS2_KEY_H }, + { PS2_KC_I, PS2_KEY_I }, + { PS2_KC_J, PS2_KEY_J }, + { PS2_KC_K, PS2_KEY_K }, + { PS2_KC_L, PS2_KEY_L }, + { PS2_KC_M, PS2_KEY_M }, + { PS2_KC_N, PS2_KEY_N }, + { PS2_KC_O, PS2_KEY_O }, + { PS2_KC_P, PS2_KEY_P }, + { PS2_KC_Q, PS2_KEY_Q }, + { PS2_KC_R, PS2_KEY_R }, + { PS2_KC_S, PS2_KEY_S }, + { PS2_KC_T, PS2_KEY_T }, + { PS2_KC_U, PS2_KEY_U }, + { PS2_KC_V, PS2_KEY_V }, + { PS2_KC_W, PS2_KEY_W }, + { PS2_KC_X, PS2_KEY_X }, + { PS2_KC_Y, PS2_KEY_Y }, + { PS2_KC_Z, PS2_KEY_Z }, + { PS2_KC_SEMI, PS2_KEY_SEMI }, + { PS2_KC_BACK, PS2_KEY_HASH }, + { PS2_KC_OPEN_SQ, PS2_KEY_OPEN_SQ }, + { PS2_KC_CLOSE_SQ, PS2_KEY_CLOSE_SQ }, + { PS2_KC_EQUAL, PS2_KEY_EQUAL }, + { PS2_KC_EUROPE2, PS2_KEY_BACK }, + { PS2_KC_F1, PS2_KEY_F1 }, + { PS2_KC_F2, PS2_KEY_F2 }, + { PS2_KC_F3, PS2_KEY_F3 }, + { PS2_KC_F4, PS2_KEY_F4 }, + { PS2_KC_F5, PS2_KEY_F5 }, + { PS2_KC_F6, PS2_KEY_F6 }, + { PS2_KC_F7, PS2_KEY_F7 }, + { PS2_KC_F8, PS2_KEY_F8 }, + { PS2_KC_F9, PS2_KEY_F9 }, + { PS2_KC_F10, PS2_KEY_F10 }, + { PS2_KC_F11, PS2_KEY_F11 }, + { PS2_KC_F12, PS2_KEY_F12 }, + { PS2_KC_KP_COMMA, PS2_KEY_KP_COMMA }, + { PS2_KC_INTL1, PS2_KEY_INTL1 }, + { PS2_KC_INTL2, PS2_KEY_INTL2 }, + { PS2_KC_INTL3, PS2_KEY_INTL3 }, + { PS2_KC_INTL4, PS2_KEY_INTL4 }, + { PS2_KC_INTL5, PS2_KEY_INTL5 }, + { PS2_KC_LANG1, PS2_KEY_LANG1 }, + { PS2_KC_LANG2, PS2_KEY_LANG2 }, + { PS2_KC_LANG3, PS2_KEY_LANG3 }, + { PS2_KC_LANG4, PS2_KEY_LANG4 }, + { PS2_KC_LANG5, PS2_KEY_LANG5 } + }; + +/* Two byte Key table after an E0 byte received */ +const uint8_t extended_key[][ 2 ] = { + { PS2_KC_IGNORE, PS2_KEY_IGNORE }, + { PS2_KC_PRTSCR, PS2_KEY_PRTSCR }, + { PS2_KC_CTRL, PS2_KEY_R_CTRL }, + { PS2_KC_ALT, PS2_KEY_R_ALT }, + { PS2_KC_L_GUI, PS2_KEY_L_GUI }, + { PS2_KC_R_GUI, PS2_KEY_R_GUI }, + { PS2_KC_MENU, PS2_KEY_MENU }, + { PS2_KC_BREAK, PS2_KEY_BREAK }, + { PS2_KC_HOME, PS2_KEY_HOME }, + { PS2_KC_END, PS2_KEY_END }, + { PS2_KC_PGUP, PS2_KEY_PGUP }, + { PS2_KC_PGDN, PS2_KEY_PGDN }, + { PS2_KC_L_ARROW, PS2_KEY_L_ARROW }, + { PS2_KC_R_ARROW, PS2_KEY_R_ARROW }, + { PS2_KC_UP_ARROW, PS2_KEY_UP_ARROW }, + { PS2_KC_DN_ARROW, PS2_KEY_DN_ARROW }, + { PS2_KC_INSERT, PS2_KEY_INSERT }, + { PS2_KC_DELETE, PS2_KEY_DELETE }, + { PS2_KC_KP_ENTER, PS2_KEY_KP_ENTER }, + { PS2_KC_KP_DIV, PS2_KEY_KP_DIV }, + { PS2_KC_NEXT_TR, PS2_KEY_NEXT_TR }, + { PS2_KC_PREV_TR, PS2_KEY_PREV_TR }, + { PS2_KC_STOP, PS2_KEY_STOP }, + { PS2_KC_PLAY, PS2_KEY_PLAY }, + { PS2_KC_MUTE, PS2_KEY_MUTE }, + { PS2_KC_VOL_UP, PS2_KEY_VOL_UP }, + { PS2_KC_VOL_DN, PS2_KEY_VOL_DN }, + { PS2_KC_MEDIA, PS2_KEY_MEDIA }, + { PS2_KC_EMAIL, PS2_KEY_EMAIL }, + { PS2_KC_CALC, PS2_KEY_CALC }, + { PS2_KC_COMPUTER, PS2_KEY_COMPUTER }, + { PS2_KC_WEB_SEARCH, PS2_KEY_WEB_SEARCH }, + { PS2_KC_WEB_HOME, PS2_KEY_WEB_HOME }, + { PS2_KC_WEB_BACK, PS2_KEY_WEB_BACK }, + { PS2_KC_WEB_FORWARD, PS2_KEY_WEB_FORWARD }, + { PS2_KC_WEB_STOP, PS2_KEY_WEB_STOP }, + { PS2_KC_WEB_REFRESH, PS2_KEY_WEB_REFRESH }, + { PS2_KC_WEB_FAVOR, PS2_KEY_WEB_FAVOR }, + { PS2_KC_POWER, PS2_KEY_POWER }, + { PS2_KC_SLEEP, PS2_KEY_SLEEP }, + { PS2_KC_WAKE, PS2_KEY_WAKE } + }; + +/* Scroll lock numeric keypad re-mappings for NOT NUMLOCK */ +/* in translated code order order is important */ +const uint8_t scroll_remap[] = { + PS2_KEY_INSERT, // PS2_KEY_KP0 + PS2_KEY_END, // PS2_KEY_KP1 + PS2_KEY_DN_ARROW, // PS2_KEY_KP2 + PS2_KEY_PGDN, // PS2_KEY_KP3 + PS2_KEY_L_ARROW, // PS2_KEY_KP4 + PS2_KEY_IGNORE, // PS2_KEY_KP5 + PS2_KEY_R_ARROW, // PS2_KEY_KP6 + PS2_KEY_HOME, // PS2_KEY_KP7 + PS2_KEY_UP_ARROW, // PS2_KEY_KP8 + PS2_KEY_PGUP, // PS2_KEY_KP9 + PS2_KEY_DELETE // PS2_KEY_KP_DOT + }; + +//const uint8_t single_key[][ 2 ] = { +// { PS2_KC_NUM, PS2_KEY_NUM }, +// { PS2_KC_SCROLL, PS2_KEY_SCROLL }, +// { PS2_KC_CAPS, PS2_KEY_CAPS }, +// { PS2_KC_L_SHIFT, PS2_KEY_L_SHIFT }, +// { PS2_KC_R_SHIFT, PS2_KEY_R_SHIFT }, +// { PS2_KC_CTRL, PS2_KEY_L_CTRL }, +// { PS2_KC_ALT, PS2_KEY_L_ALT }, +// { PS2_KC_SYSRQ, PS2_KEY_SYSRQ }, +// { PS2_KC_ESC, PS2_KEY_ESC }, +// { PS2_KC_BS, PS2_KEY_BS }, +// { PS2_KC_TAB, PS2_KEY_TAB }, +// { PS2_KC_ENTER, PS2_KEY_ENTER }, +// { PS2_KC_SPACE, PS2_KEY_SPACE }, +// { PS2_KC_KP0, PS2_KEY_KP0 }, +// { PS2_KC_KP1, PS2_KEY_KP1 }, +// { PS2_KC_KP2, PS2_KEY_KP2 }, +// { PS2_KC_KP3, PS2_KEY_KP3 }, +// { PS2_KC_KP4, PS2_KEY_KP4 }, +// { PS2_KC_KP5, PS2_KEY_KP5 }, +// { PS2_KC_KP6, PS2_KEY_KP6 }, +// { PS2_KC_KP7, PS2_KEY_KP7 }, +// { PS2_KC_KP8, PS2_KEY_KP8 }, +// { PS2_KC_KP9, PS2_KEY_KP9 }, +// { PS2_KC_KP_DOT, PS2_KEY_KP_DOT }, +// { PS2_KC_KP_PLUS, PS2_KEY_KP_PLUS }, +// { PS2_KC_KP_MINUS, PS2_KEY_KP_MINUS }, +// { PS2_KC_KP_TIMES, PS2_KEY_KP_TIMES }, +// { PS2_KC_KP_EQUAL, PS2_KEY_KP_EQUAL }, +// { PS2_KC_0, PS2_KEY_0 }, +// { PS2_KC_1, PS2_KEY_1 }, +// { PS2_KC_2, PS2_KEY_2 }, +// { PS2_KC_3, PS2_KEY_3 }, +// { PS2_KC_4, PS2_KEY_4 }, +// { PS2_KC_5, PS2_KEY_5 }, +// { PS2_KC_6, PS2_KEY_6 }, +// { PS2_KC_7, PS2_KEY_7 }, +// { PS2_KC_8, PS2_KEY_8 }, +// { PS2_KC_9, PS2_KEY_9 }, +// { PS2_KC_APOS, PS2_KEY_APOS }, +// { PS2_KC_COMMA, PS2_KEY_COMMA }, +// { PS2_KC_MINUS, PS2_KEY_MINUS }, +// { PS2_KC_DOT, PS2_KEY_DOT }, +// { PS2_KC_DIV, PS2_KEY_DIV }, +// { PS2_KC_BTICK, PS2_KEY_BTICK }, +// { PS2_KC_A, PS2_KEY_A }, +// { PS2_KC_B, PS2_KEY_B }, +// { PS2_KC_C, PS2_KEY_C }, +// { PS2_KC_D, PS2_KEY_D }, +// { PS2_KC_E, PS2_KEY_E }, +// { PS2_KC_F, PS2_KEY_F }, +// { PS2_KC_G, PS2_KEY_G }, +// { PS2_KC_H, PS2_KEY_H }, +// { PS2_KC_I, PS2_KEY_I }, +// { PS2_KC_J, PS2_KEY_J }, +// { PS2_KC_K, PS2_KEY_K }, +// { PS2_KC_L, PS2_KEY_L }, +// { PS2_KC_M, PS2_KEY_M }, +// { PS2_KC_N, PS2_KEY_N }, +// { PS2_KC_O, PS2_KEY_O }, +// { PS2_KC_P, PS2_KEY_P }, +// { PS2_KC_Q, PS2_KEY_Q }, +// { PS2_KC_R, PS2_KEY_R }, +// { PS2_KC_S, PS2_KEY_S }, +// { PS2_KC_T, PS2_KEY_T }, +// { PS2_KC_U, PS2_KEY_U }, +// { PS2_KC_V, PS2_KEY_V }, +// { PS2_KC_W, PS2_KEY_W }, +// { PS2_KC_X, PS2_KEY_X }, +// { PS2_KC_Y, PS2_KEY_Y }, +// { PS2_KC_Z, PS2_KEY_Z }, +// { PS2_KC_SEMI, PS2_KEY_SEMI }, +// { PS2_KC_BACK, PS2_KEY_BACK }, +// { PS2_KC_OPEN_SQ, PS2_KEY_OPEN_SQ }, +// { PS2_KC_CLOSE_SQ, PS2_KEY_CLOSE_SQ }, +// { PS2_KC_EQUAL, PS2_KEY_EQUAL }, +// { PS2_KC_EUROPE2, PS2_KEY_BACK }, +// { PS2_KC_F1, PS2_KEY_F1 }, +// { PS2_KC_F2, PS2_KEY_F2 }, +// { PS2_KC_F3, PS2_KEY_F3 }, +// { PS2_KC_F4, PS2_KEY_F4 }, +// { PS2_KC_F5, PS2_KEY_F5 }, +// { PS2_KC_F6, PS2_KEY_F6 }, +// { PS2_KC_F7, PS2_KEY_F7 }, +// { PS2_KC_F8, PS2_KEY_F8 }, +// { PS2_KC_F9, PS2_KEY_F9 }, +// { PS2_KC_F10, PS2_KEY_F10 }, +// { PS2_KC_F11, PS2_KEY_F11 }, +// { PS2_KC_F12, PS2_KEY_F12 }, +// { PS2_KC_KP_COMMA, PS2_KEY_KP_COMMA }, +// { PS2_KC_INTL1, PS2_KEY_INTL1 }, +// { PS2_KC_INTL2, PS2_KEY_INTL2 }, +// { PS2_KC_INTL3, PS2_KEY_INTL3 }, +// { PS2_KC_INTL4, PS2_KEY_INTL4 }, +// { PS2_KC_INTL5, PS2_KEY_INTL5 }, +// { PS2_KC_LANG1, PS2_KEY_LANG1 }, +// { PS2_KC_LANG2, PS2_KEY_LANG2 }, +// { PS2_KC_LANG3, PS2_KEY_LANG3 }, +// { PS2_KC_LANG4, PS2_KEY_LANG4 }, +// { PS2_KC_LANG5, PS2_KEY_LANG5 } +// }; +// +///* Two byte Key table after an E0 byte received */ +//const uint8_t extended_key[][ 2 ] = { +// { PS2_KC_IGNORE, PS2_KEY_IGNORE }, +// { PS2_KC_PRTSCR, PS2_KEY_PRTSCR }, +// { PS2_KC_CTRL, PS2_KEY_R_CTRL }, +// { PS2_KC_ALT, PS2_KEY_R_ALT }, +// { PS2_KC_L_GUI, PS2_KEY_L_GUI }, +// { PS2_KC_R_GUI, PS2_KEY_R_GUI }, +// { PS2_KC_MENU, PS2_KEY_MENU }, +// { PS2_KC_BREAK, PS2_KEY_BREAK }, +// { PS2_KC_HOME, PS2_KEY_HOME }, +// { PS2_KC_END, PS2_KEY_END }, +// { PS2_KC_PGUP, PS2_KEY_PGUP }, +// { PS2_KC_PGDN, PS2_KEY_PGDN }, +// { PS2_KC_L_ARROW, PS2_KEY_L_ARROW }, +// { PS2_KC_R_ARROW, PS2_KEY_R_ARROW }, +// { PS2_KC_UP_ARROW, PS2_KEY_UP_ARROW }, +// { PS2_KC_DN_ARROW, PS2_KEY_DN_ARROW }, +// { PS2_KC_INSERT, PS2_KEY_INSERT }, +// { PS2_KC_DELETE, PS2_KEY_DELETE }, +// { PS2_KC_KP_ENTER, PS2_KEY_KP_ENTER }, +// { PS2_KC_KP_DIV, PS2_KEY_KP_DIV }, +// { PS2_KC_NEXT_TR, PS2_KEY_NEXT_TR }, +// { PS2_KC_PREV_TR, PS2_KEY_PREV_TR }, +// { PS2_KC_STOP, PS2_KEY_STOP }, +// { PS2_KC_PLAY, PS2_KEY_PLAY }, +// { PS2_KC_MUTE, PS2_KEY_MUTE }, +// { PS2_KC_VOL_UP, PS2_KEY_VOL_UP }, +// { PS2_KC_VOL_DN, PS2_KEY_VOL_DN }, +// { PS2_KC_MEDIA, PS2_KEY_MEDIA }, +// { PS2_KC_EMAIL, PS2_KEY_EMAIL }, +// { PS2_KC_CALC, PS2_KEY_CALC }, +// { PS2_KC_COMPUTER, PS2_KEY_COMPUTER }, +// { PS2_KC_WEB_SEARCH, PS2_KEY_WEB_SEARCH }, +// { PS2_KC_WEB_HOME, PS2_KEY_WEB_HOME }, +// { PS2_KC_WEB_BACK, PS2_KEY_WEB_BACK }, +// { PS2_KC_WEB_FORWARD, PS2_KEY_WEB_FORWARD }, +// { PS2_KC_WEB_STOP, PS2_KEY_WEB_STOP }, +// { PS2_KC_WEB_REFRESH, PS2_KEY_WEB_REFRESH }, +// { PS2_KC_WEB_FAVOR, PS2_KEY_WEB_FAVOR }, +// { PS2_KC_POWER, PS2_KEY_POWER }, +// { PS2_KC_SLEEP, PS2_KEY_SLEEP }, +// { PS2_KC_WAKE, PS2_KEY_WAKE } +// }; +// +///* Scroll lock numeric keypad re-mappings for NOT NUMLOCK */ +///* in translated code order order is important */ +//const uint8_t scroll_remap[] = { +// PS2_KEY_INSERT, // PS2_KEY_KP0 +// PS2_KEY_END, // PS2_KEY_KP1 +// PS2_KEY_DN_ARROW, // PS2_KEY_KP2 +// PS2_KEY_PGDN, // PS2_KEY_KP3 +// PS2_KEY_L_ARROW, // PS2_KEY_KP4 +// PS2_KEY_IGNORE, // PS2_KEY_KP5 +// PS2_KEY_R_ARROW, // PS2_KEY_KP6 +// PS2_KEY_HOME, // PS2_KEY_KP7 +// PS2_KEY_UP_ARROW, // PS2_KEY_KP8 +// PS2_KEY_PGUP, // PS2_KEY_KP9 +// PS2_KEY_DELETE // PS2_KEY_KP_DOT +// }; +// +#endif diff --git a/main/include/PS2Mouse.h b/main/include/PS2Mouse.h deleted file mode 120000 index d32271c..0000000 --- a/main/include/PS2Mouse.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/PS2Mouse.h \ No newline at end of file diff --git a/main/include/PS2Mouse.h b/main/include/PS2Mouse.h new file mode 100644 index 0000000..5134567 --- /dev/null +++ b/main/include/PS2Mouse.h @@ -0,0 +1,187 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: PS2Mouse.h +// Created: Jan 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header file for the PS/2 Mouse Class. +// +// Credits: +// Copyright: (c) 2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef MOUSE_H_ +#define MOUSE_H_ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "Arduino.h" +#include "driver/gpio.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" + +// PS/2 Mouse Class. +class PS2Mouse { + + // mode status flags + #define _PS2_BUSY 0x04 + #define _TX_MODE 0x02 + #define _WAIT_RESPONSE 0x01 + + // Constants. + #define MAX_PS2_XMIT_KEY_BUF 16 + #define MAX_PS2_RCV_KEY_BUF 16 + #define INTELLI_MOUSE 3 + #define SCALING_1_TO_1 0xE6 + #define DEFAULT_MOUSE_TIMEOUT 100 + + public: + // Public structures used for containment of mouse movement and control data. These are used by the insantiating + // object to request and process mouse data. + // Postional data - X/Y co-ordinates of mouse movement. + typedef struct { + int x, y; + } Position; + + // Mouse data, containing positional data, status, wheel data and validity. + typedef struct { + bool valid; + bool overrun; + int status; + Position position; + int wheel; + } MouseData; + + // Enumeration of all processed mouse responses. + enum Responses { + MOUSE_RESP_ACK = 0xFA, + }; + + // Enumeration of all processed mouse commands. + enum Commands { + MOUSE_CMD_SET_SCALING_1_1 = 0xE6, + MOUSE_CMD_SET_SCALING_2_1 = 0xE7, + MOUSE_CMD_SET_RESOLUTION = 0xE8, + MOUSE_CMD_GET_STATUS = 0xE9, + MOUSE_CMD_SET_STREAM_MODE = 0xEA, + MOUSE_CMD_REQUEST_DATA = 0xEB, + MOUSE_CMD_SET_REMOTE_MODE = 0xF0, + MOUSE_CMD_GET_DEVICE_ID = 0xF2, + MOUSE_CMD_SET_SAMPLE_RATE = 0xF3, + MOUSE_CMD_ENABLE_STREAMING = 0xF4, + MOUSE_CMD_DISABLE_STREAMING = 0xF5, + MOUSE_CMD_RESEND = 0xFE, + MOUSE_CMD_RESET = 0xFF, + }; + + // Resolution - the PS/2 mouse can digitize movement from 1mm to 1/8mm, the default being 1/4 (ie. 1mm = 4 counts). This allows configuration for a finer or rougher + // tracking digitisation. + enum PS2_RESOLUTION { + PS2_MOUSE_RESOLUTION_1_1 = 0x00, + PS2_MOUSE_RESOLUTION_1_2 = 0x01, + PS2_MOUSE_RESOLUTION_1_4 = 0x02, + PS2_MOUSE_RESOLUTION_1_8 = 0x03, + }; + + // Scaling - the PS/2 mouse can provide linear (1:1 no scaling) or non liner (2:1 scaling) adaptation of the digitised data. This allows configuration for amplification of movements. + enum PS2_SCALING { + PS2_MOUSE_SCALING_1_1 = 0x00, + PS2_MOUSE_SCALING_2_1 = 0x01, + }; + + // Sampling rate - the PS/2 mouse, in streaming mode, the mouse sends with movement updates. This allows for finer or rougher digitisation of movements. The default is 100 samples per + // second and the X68000 is fixed at 100 samples per second. Adjusting the ps/2 sample rate will affect tracking granularity on the X68000. + enum PS2_SAMPLING { + PS2_MOUSE_SAMPLE_RATE_10 = 10, + PS2_MOUSE_SAMPLE_RATE_20 = 20, + PS2_MOUSE_SAMPLE_RATE_40 = 40, + PS2_MOUSE_SAMPLE_RATE_60 = 60, + PS2_MOUSE_SAMPLE_RATE_80 = 80, + PS2_MOUSE_SAMPLE_RATE_100 = 100, + PS2_MOUSE_SAMPLE_RATE_200 = 200, + }; + + // Public accessible prototypes. + PS2Mouse(int clockPin, int dataPin); + ~PS2Mouse(); + void writeByte(uint8_t); + bool setResolution(enum PS2_RESOLUTION resolution); + bool setStreamMode(void); + bool setRemoteMode(void); + bool setScaling(enum PS2_SCALING scaling); + char getDeviceId(void); + bool checkIntelliMouseExtensions(void); + bool setSampleRate(enum PS2_SAMPLING rate); + bool enableStreaming(void); + bool disableStreaming(void); + bool getStatus(uint8_t *respBuf); + bool reset(void); + MouseData readData(void); + void initialize(void); + + // Method to register an object method for callback with context. + template + void setMouseDataCallback(A func_ptr, B obj_ptr) + { + ps2Ctrl.mouseDataCallback = bind(func_ptr, obj_ptr, 0, std::placeholders::_1); + } + + private: + // PS/2 Control structure - maintains all data and variables relevant to forming a connection with a PS/2 mouse, interaction and processing of its data. + struct { + int clkPin; // Hardware clock pin - bidirectional. + int dataPin; // Hardware data pin - bidirectional. + volatile uint8_t mode; // mode contains _PS2_BUSY bit 2 = busy until all expected bytes RX/TX + // _TX_MODE bit 1 = direction 1 = TX, 0 = RX (default) + // _WAIT_RESPONSE bit 0 = expecting data response + bool supportsIntelliMouseExtensions; // Intellimouse extensions supported. + bool streamingEnabled; // Streaming mode has been enabled. + volatile uint8_t bitCount; // Main state variable and bit count for interrupts + volatile uint8_t shiftReg; // Incoming/Outgoing data shift register. + volatile uint8_t parity; // Parity flag for data being sent/received. + uint16_t rxBuf[16]; // RX buffer - assembled bytes are stored in this buffer awaiting processing. + int rxPos; // Position in buffer to store next byte. + + // Callback for streaming processed mouse data to HID handler. + std::function mouseDataCallback; + } ps2Ctrl; + + // Structure to store incoming streamed mouse data along with validity flags. + struct { + MouseData mouseData; + bool newData; // An update has occurred since the last query. + bool overrun; // A data overrun has occurred since the last query. + } streaming; + + // Interrupt handler - needs to be declared static and assigned to internal RAM (within the ESP32) to function correctly. + IRAM_ATTR static void ps2interrupt( void ); + + // Prototypes. + bool requestData(uint8_t expectedBytes, uint8_t *respBuf, uint32_t timeout); + bool sendCmd(uint8_t cmd, uint8_t expectedBytes, uint8_t *respBuf, uint32_t timeout); + +}; + +#endif // MOUSE_H_ diff --git a/main/include/SWITCH.h b/main/include/SWITCH.h deleted file mode 120000 index ee1ef71..0000000 --- a/main/include/SWITCH.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/SWITCH.h \ No newline at end of file diff --git a/main/include/SWITCH.h b/main/include/SWITCH.h new file mode 100644 index 0000000..5c84761 --- /dev/null +++ b/main/include/SWITCH.h @@ -0,0 +1,232 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: SWITCH.h +// Created: May 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Class definition to encapsulate the SharpKey WiFi/Config Switch. +// +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: May 2022 - Initial write. +// v1.00 Jun 2022 - Updates to add additional callbacks for RESET and CLEARNVS +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SWITCH_H +#define SWITCH_H + +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "LED.h" + + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Switch class. +class SWITCH { + + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define SWITCH_VERSION 1.00 + + public: + + // Prototypes. + SWITCH(LED *led); + SWITCH(void); + virtual ~SWITCH(void); + + // Method to register an object method for callback with context. + template + void setCancelEventCallback(A func_ptr, B obj_ptr) + { + swCtrl.cancelEventCallback = std::bind(func_ptr, obj_ptr); + } + template + void setCancelEventCallback(A func_ptr) + { + swCtrl.cancelEventCallback = std::bind(func_ptr); + } + // Wifi enable (configured mode). + template + void setWifiEnEventCallback(A func_ptr, B obj_ptr) + { + swCtrl.wifiEnEventCallback = std::bind(func_ptr, obj_ptr); + } + template + void setWifiEnEventCallback(A func_ptr) + { + swCtrl.wifiEnEventCallback = std::bind(func_ptr); + } + // Wifi default mode enable. + template + void setWifiDefEventCallback(A func_ptr, B obj_ptr) + { + swCtrl.wifiDefEventCallback = std::bind(func_ptr, obj_ptr); + } + template + void setWifiDefEventCallback(A func_ptr) + { + swCtrl.wifiDefEventCallback = std::bind(func_ptr); + } + // Bluetooth start pairing event. + template + void setBTPairingEventCallback(A func_ptr, B obj_ptr) + { + swCtrl.btPairingEventCallback = std::bind(func_ptr, obj_ptr); + } + template + void setBTPairingEventCallback(A func_ptr) + { + swCtrl.btPairingEventCallback = std::bind(func_ptr); + } + // RESET event - callback is executed prior to issuing an esp_restart(). + template + void setResetEventCallback(A func_ptr, B obj_ptr) + { + swCtrl.resetEventCallback = std::bind(func_ptr, obj_ptr); + } + template + void setResetEventCallback(A func_ptr) + { + swCtrl.resetEventCallback = std::bind(func_ptr); + } + // CLEARNVS event - callback when user requests all NVS settings are erased. + template + void setClearNVSEventCallback(A func_ptr, B obj_ptr) + { + swCtrl.clearNVSEventCallback = std::bind(func_ptr, obj_ptr); + } + template + void setClearNVSEventCallback(A func_ptr) + { + swCtrl.clearNVSEventCallback = std::bind(func_ptr); + } + + // Helper method to identify the sub class, this is used in non volatile key management. + // Warning: This method wont work if optimisation for size is enabled on the compiler. + const char *getClassName(const std::string& prettyFunction) + { + // First find the CLASS :: METHOD seperation. + size_t colons = prettyFunction.find("::"); + + // None, then this is not a class. + if (colons == std::string::npos) + return "::"; + + // Split out the class name. + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = colons - begin; + + // Return the name. + return(prettyFunction.substr(begin,end).c_str()); + } + + // Helper method to change a file extension. + void replaceExt(std::string& fileName, const std::string& newExt) + { + // Locals. + std::string::size_type extPos = fileName.rfind('.', fileName.length()); + + if(extPos != std::string::npos) + { + fileName.replace(extPos+1, newExt.length(), newExt); + } + return; + } + + // Template to aid in conversion of an enum to integer. + template constexpr typename std::underlying_type::type to_underlying(E e) noexcept + { + return static_cast::type>(e); + } + + // Method to return the class version number. + virtual float version(void) + { + return(SWITCH_VERSION); + } + + // Method to return the name of the class. + virtual std::string ifName(void) + { + return(swCtrl.swClassName); + } + + protected: + + private: + + // Prototypes. + void init(void); + IRAM_ATTR static void swInterface( void * pvParameters ); + inline uint32_t milliSeconds(void) + { + return( (uint32_t) clock() ); + } + + // Structure to maintain an active setting for the LED. The LED control thread uses these values to effect the required lighting of the LED. + typedef struct { + // Name of the class for this instantiation. + std::string swClassName; + + // Thread handles - Switch interface. + TaskHandle_t TaskSWIF = NULL; + + // Callback for Cancel Event. + std::function cancelEventCallback; + + // Callback for WiFi Enable Event. + std::function wifiEnEventCallback; + + // Callback for WiFi Default Event. + std::function wifiDefEventCallback; + + // Callback for Bluetooth Pairing Event. + std::function btPairingEventCallback; + + // Callback is executed prior to issuing an esp_restart(). + std::function resetEventCallback; + + // Callback when user requests all NVS settings are erased. + std::function clearNVSEventCallback; + } t_swControl; + + // Var to store all SWITCH control variables. + t_swControl swCtrl; + + // LED activity object handle. + LED *led; +}; +#endif // SWITCH_H diff --git a/main/include/WiFi.h b/main/include/WiFi.h deleted file mode 120000 index 2456c65..0000000 --- a/main/include/WiFi.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/WiFi.h \ No newline at end of file diff --git a/main/include/WiFi.h b/main/include/WiFi.h new file mode 100644 index 0000000..9fdb2cc --- /dev/null +++ b/main/include/WiFi.h @@ -0,0 +1,400 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: WiFi.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the WiFi AP/Client logic. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Seperated out the WiFi Enable switch and made the WiFi module active/ +// via a reboot process. This is necessary now that Bluetooth is inbuilt +// as the ESP32 shares an antenna and both operating together electrically +// is difficult but also the IDF stack conflicts as well. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef WIFI_H +#define WIFI_H + +#if defined(CONFIG_IF_WIFI_ENABLED) + #include "freertos/event_groups.h" + #include "esp_system.h" + #include "esp_wifi.h" + #include "esp_event.h" + #include "nvs_flash.h" + #include "lwip/err.h" + #include "lwip/sys.h" + #include + #include "esp_littlefs.h" + #include + #include + #include + #include + #include "NVS.h" + #include "LED.h" + #include "HID.h" + + // Include the specification class. + #include "KeyInterface.h" + + // Encapsulate the WiFi functionality. + class WiFi { + // Constants. + #define WIFI_VERSION 1.02 + #define OBJECT_VERSION_LIST_MAX 18 + #define FILEPACK_VERSION_FILE "version.txt" + #define WIFI_AP_DEFAULT_IP "192.168.4.1" + #define WIFI_AP_DEFAULT_GW "192.168.4.1" + #define WIFI_AP_DEFAULT_NETMASK "255.255.255.0" + + // The event group allows multiple bits for each event, but we only care about two events: + // - we are connected to the AP with an IP + // - we failed to connect after the maximum amount of retries + #define WIFI_CONNECTED_BIT BIT0 + #define WIFI_FAIL_BIT BIT1 + + // Tag for ESP WiFi logging. + #define WIFITAG "WiFi" + + // Menu selection types. + enum WIFIMODES { + WIFI_OFF = 0x00, // WiFi is disabled. + WIFI_ON = 0x01, // WiFi is enabled. + WIFI_CONFIG_AP = 0x02, // WiFi is set to enable Access Point to configure WiFi settings. + WIFI_CONFIG_CLIENT = 0x03 // WiFi is set to enable Client mode using persisted settings. + }; + + // Default WiFi parameters. + #define MAX_WIFI_SSID_LEN 31 + #define MAX_WIFI_PWD_LEN 63 + #define MAX_WIFI_IP_LEN 15 + #define MAX_WIFI_NETMASK_LEN 15 + #define MAX_WIFI_GATEWAY_LEN 15 + + // Buffer size for sending file data in chunks to the browser. + #define MAX_CHUNK_SIZE 4096 + + // Max length a file path can have on the embedded storage device. + #define FILE_PATH_MAX (15 + CONFIG_LITTLEFS_OBJ_NAME_LEN) + + public: + // Types for holding and maintaining a class/object to version number array. + typedef struct { + std::string object; + float version; + } t_versionItem; + typedef struct { + int elements; + t_versionItem *item[OBJECT_VERSION_LIST_MAX]; + } t_versionList; + + // Prototypes. + WiFi(KeyInterface *hdlKeyIf, KeyInterface *hdlMouseIf, bool defaultMode, NVS *nvs, LED *led, const char *fsPath, t_versionList *versionList); + WiFi(void); + ~WiFi(void); + void run(void); + + // Primary encapsulated interface object handle. + KeyInterface *keyIf; + + // Secondary encapsulated interface object handle. + KeyInterface *mouseIf; + + // Non Volatile Storage handle. + NVS *nvs; + + // LED activity handle. + LED *led; + + // Method to return the class version number. + float version(void) + { + return(WIFI_VERSION); + } + + protected: + + private: + + // Type for key:value pairs. + typedef struct { + std::string name; + std::string value; + } t_kvPair; + + // Structure to maintain wifi configuration data. This data is persisted through powercycles as needed. + typedef struct { + // Client access parameters, these, when valid, are used for binding to a known wifi access point. + struct { + bool valid; + char ssid[MAX_WIFI_SSID_LEN+1]; + char pwd[MAX_WIFI_PWD_LEN+1]; + bool useDHCP; + char ip[MAX_WIFI_IP_LEN+1]; + char netmask[MAX_WIFI_NETMASK_LEN+1]; + char gateway[MAX_WIFI_GATEWAY_LEN+1]; + } clientParams; + + // Structure to maintain Access Point parameters. These are configurable to allow possibility of changing them. + struct { + char ssid[MAX_WIFI_SSID_LEN+1]; + char pwd[MAX_WIFI_PWD_LEN+1]; + char ip[MAX_WIFI_IP_LEN+1]; + char netmask[MAX_WIFI_NETMASK_LEN+1]; + char gateway[MAX_WIFI_GATEWAY_LEN+1]; + } apParams; + + // General runtime control parameters. + struct { + // Configured mode of the Wifi: Access Point or Client. + enum WIFIMODES wifiMode; + } params; + } t_wifiConfig; + + // Configuration data. + t_wifiConfig wifiConfig; + + // Structure to manage the WiFi control variables, signifying the state of the Client or Access Point, runtime dependent, and + // necessary dedicated run variables (as opposed to locals). + typedef struct { + // Client mode variables, active when in client mode. + struct { + int clientRetryCnt; + bool connected; + char ssid[MAX_WIFI_SSID_LEN+1]; + char pwd[MAX_WIFI_PWD_LEN+1]; + char ip[MAX_WIFI_IP_LEN+1]; + char netmask[MAX_WIFI_NETMASK_LEN+1]; + char gateway[MAX_WIFI_GATEWAY_LEN+1]; + } client; + + // Access Point mode variabls, active when in AP mode. + struct { + char ssid[MAX_WIFI_SSID_LEN+1]; + char pwd[MAX_WIFI_PWD_LEN+1]; + char ip[MAX_WIFI_IP_LEN+1]; + char netmask[MAX_WIFI_NETMASK_LEN+1]; + char gateway[MAX_WIFI_GATEWAY_LEN+1]; + } ap; + + // HTTP session variables, parsed out of incoming connections. The sessions are synchronous so only maintain + // one copy. + struct { + std::string host; + std::string queryStr; + std::string fileName; + std::string filePath; + bool gzip; + bool deflate; + } session; + + // Runtime variables, used for global control of the WiFi module. + // + struct { + // Default path on the underlying filesystem. This is where the NVS/SD partition is mounted and all files under this directory are accessible. + const char * fsPath; + + // Version list of all objects used to build the SharpKey interface along with their version numbers. + t_versionList *versionList; + + // Run mode of the Wifi: Off, On or Access Point. + enum WIFIMODES wifiMode; + + // Handle to http server. + httpd_handle_t server; + + // Class name, used for NVS keys. + std::string thisClass; + + // Flag to raise a reboot button on the displayed page. + bool rebootButton; + + // Flag to indicate a hard reboot needed. + bool reboot; + + // Base path of file storag. + char basePath[FILE_PATH_MAX]; + + // String to hold any response error message. + std::string errorMsg; + } run; + } t_wifiControl; + + // Control data. + t_wifiControl wifiCtrl; + + // Prototypes. + bool setupWifiClient(void); + bool setupWifiAP(void); + bool stopWifi(void); + bool startWebserver(void); + void stopWebserver(void); + float getVersionNumber(std::string name); + esp_err_t expandAndSendFile(httpd_req_t *req, const char *basePath, std::string fileName); + esp_err_t expandVarsAndSend(httpd_req_t *req, std::string str); + esp_err_t sendKeyMapHeaders(httpd_req_t *req); + esp_err_t sendKeyMapTypes(httpd_req_t *req); + esp_err_t sendKeyMapCustomTypeFields(httpd_req_t *req); + esp_err_t sendKeyMapData(httpd_req_t *req); + esp_err_t sendKeyMapPopovers(httpd_req_t *req); + esp_err_t sendMouseRadioChoice(httpd_req_t *req, const char *option); + + + + esp_err_t wifiDataPOSTHandler(httpd_req_t *req, std::vector pairs, std::string& resp); + esp_err_t mouseDataPOSTHandler(httpd_req_t *req, std::vector pairs, std::string& resp); + static esp_err_t defaultDataPOSTHandler(httpd_req_t *req); + static esp_err_t defaultDataGETHandler(httpd_req_t *req); + IRAM_ATTR static esp_err_t otaFirmwareUpdatePOSTHandler(httpd_req_t *req); + IRAM_ATTR static esp_err_t otaFilepackUpdatePOSTHandler(httpd_req_t *req); + static esp_err_t keymapUploadPOSTHandler(httpd_req_t *req); + static esp_err_t keymapTablePOSTHandler(httpd_req_t *req); + + static esp_err_t defaultRebootHandler(httpd_req_t *req); + esp_err_t getPOSTData(httpd_req_t *req, std::vector *pairs); + + bool isFileExt(std::string fileName, std::string extension); + esp_err_t setContentTypeFromFileType(httpd_req_t *req, std::string fileName); + esp_err_t getPathFromURI(std::string& destPath, std::string& destFile, const char *basePath, const char *uri); + esp_err_t getPathFromURI(std::string& destPath, const char *basePath, const char *uri); + static esp_err_t defaultFileHandler(httpd_req_t *req); + std::string esp32PartitionType(esp_partition_type_t type); + std::string esp32PartitionSubType(esp_partition_subtype_t subtype); + + + IRAM_ATTR static void pairBluetoothDevice(void *pvParameters); + IRAM_ATTR static void wifiAPHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); + IRAM_ATTR static void wifiClientHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); + + // Method to split a string based on a delimiter and store in a vector. + std::vector split(std::string s, std::string delimiter) + { + // Locals. + size_t pos_start = 0; + size_t pos_end; + size_t delim_len = delimiter.length(); + std::string token; + std::vector res; + + // Loop through the string locating delimiters and split on each occurrence. + while((pos_end = s.find (delimiter, pos_start)) != std::string::npos) + { + token = s.substr (pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + // Push each occurrence onto Vector. + res.push_back (token); + } + + // Store last item in vector. + res.push_back (s.substr (pos_start)); + return res; + } + + // check if a given string is a numeric string or not + bool isNumber(const std::string &str) + { + // `std::find_first_not_of` searches the string for the first character + // that does not match any of the characters specified in its arguments + return !str.empty() && + (str.find_first_not_of("[0123456789]") == std::string::npos); + } + + // Function to split string `str` using a given delimiter + std::vector split(const std::string &str, char delim) + { + auto i = 0; + std::vector list; + + auto pos = str.find(delim); + + while (pos != std::string::npos) + { + list.push_back(str.substr(i, pos - i)); + i = ++pos; + pos = str.find(delim, pos); + } + + list.push_back(str.substr(i, str.length())); + + return list; + } + + // Function to validate an IP address + bool validateIP(std::string ip) + { + // split the string into tokens + std::vector list = split(ip, '.'); + + // if the token size is not equal to four + if (list.size() != 4) { + return false; + } + + // validate each token + for (std::string str: list) + { + // verify that the string is a number or not, and the numbers + // are in the valid range + if (!isNumber(str) || std::stoi(str) > 255 || std::stoi(str) < 0) { + return false; + } + } + + return true; + } + + // Method to split an IP4 address into its components, checking each for validity. + bool splitIP(std::string ip, int *a, int *b, int *c, int *d) + { + // Init. + *a = *b = *c = *d = 0; + + // split the string into tokens + std::vector list = split(ip, '.'); + + // if the token size is not equal to four + if (list.size() != 4) { +printf("Size:%d\n", list.size()); + return false; + } + // Loop through vector and check each number for validity before assigning. + for(int idx=0; idx < 4; idx++) + { + // verify that the string is a number or not, and the numbers + // are in the valid range + if (!isNumber(list.at(idx)) || std::stoi(list.at(idx)) > 255 || std::stoi(list.at(idx)) < 0) { +printf("Item:%d, %s\n", idx, list.at(idx).c_str()); + return false; + } + int frag = std::stoi(list.at(idx)); + if(idx == 0) *a = frag; + if(idx == 1) *b = frag; + if(idx == 2) *c = frag; + if(idx == 3) *d = frag; + } + return true; + } + + }; +#endif + +#endif // WIFI_H diff --git a/main/include/X1.h b/main/include/X1.h deleted file mode 120000 index 9c4fcb6..0000000 --- a/main/include/X1.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/X1.h \ No newline at end of file diff --git a/main/include/X1.h b/main/include/X1.h new file mode 100644 index 0000000..51b15fd --- /dev/null +++ b/main/include/X1.h @@ -0,0 +1,687 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: X1.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the Sharp X1 to PS/2 interface logic. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// v1.03 Jun 2022 - Further updates adding in keymaps for UK BT and Japan OADG109. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef X1_H +#define X1_H + +// Include the specification class. +#include "KeyInterface.h" +#include "NVS.h" +#include "LED.h" +#include "HID.h" +#include +#include + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Encapsulate the Sharp X1 interface. +class X1 : public KeyInterface { + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define X1IF_VERSION 1.03 + #define X1IF_KEYMAP_FILE "X1_KeyMap.BIN" + #define MAX_X1_XMIT_KEY_BUF 16 + #define PS2TBL_X1_MAXROWS 349 + + // X1 Key control bit mask. + #define X1_CTRL_TENKEY ((unsigned char) (1 << 7)) + #define X1_CTRL_PRESS ((unsigned char) (1 << 6)) + #define X1_CTRL_REPEAT ((unsigned char) (1 << 5)) + #define X1_CTRL_GRAPH ((unsigned char) (1 << 4)) + #define X1_CTRL_CAPS ((unsigned char) (1 << 3)) + #define X1_CTRL_KANA ((unsigned char) (1 << 2)) + #define X1_CTRL_SHIFT ((unsigned char) (1 << 1)) + #define X1_CTRL_CTRL ((unsigned char) (1 << 0)) + + // X1 Special Key definitions. + #define X1KEY_UP 0x1E // ↑ + #define X1KEY_DOWN 0x1F // ↓ + #define X1KEY_LEFT 0x1D // ← + #define X1KEY_RIGHT 0x1C // → → + #define X1KEY_INS 0x12 // INS + #define X1KEY_DEL 0x08 // DEL + #define X1KEY_CLR 0x0C // CLR + #define X1KEY_HOME 0x0B // HOME + + // PS2 Flag definitions. + #define PS2CTRL_NONE 0x00 // No keys active = 0 + #define PS2CTRL_SHIFT 0x01 // Shfit Key active = 1 + #define PS2CTRL_CTRL 0x02 // Ctrl Key active = 1 + #define PS2CTRL_CAPS 0x04 // CAPS active = 1 + #define PS2CTRL_KANA 0x08 // KANA active = 1 + #define PS2CTRL_GRAPH 0x10 // GRAPH active = 1 + #define PS2CTRL_GUI 0x20 // GUI Key active = 1 + #define PS2CTRL_FUNC 0x40 // Special Function Keys active = 1 + #define PS2CTRL_BREAK 0x80 // BREAK Key active = 1 + #define PS2CTRL_EXACT 0x80 // EXACT Match active = 1 + + // The initial mapping is made inside the PS2KeyAdvanced class from Scan Code Set 2 to ASCII + // for a selected keyboard. Special functions are detected and combined inside this module + // before mapping with the table below to extract the X1 key code and control data. + // ie. PS/2 Scan Code -> ASCII + Flags -> X1 Key Code + Ctrl Data + + // Keyboard mapping table column names. + #define PS2TBL_PS2KEYCODE_NAME "PS/2 KeyCode" + #define PS2TBL_PS2CTRL_NAME "PS/2 Control Key" + #define PS2TBL_KEYBOARDMODEL_NAME "For Keyboard" + #define PS2TBL_MACHINE_NAME "For Host Model" + #define PS2TBL_X1MODE_NAME "X1 Mode" + #define PS2TBL_X1KEYCODE_NAME "X1 KeyCode 1" + #define PS2TBL_X1KEYCODE_BYTE2_NAME "X1 KeyCode 2" + #define PS2TBL_X1_CTRL_NAME "X1 Control Key" + + // Keyboard mapping table column types. + #define PS2TBL_PS2KEYCODE_TYPE "hex" + #define PS2TBL_PS2CTRL_TYPE "custom_cbp_ps2ctrl" + #define PS2TBL_KEYBOARDMODEL_TYPE "custom_cbp_keybmodel" + #define PS2TBL_MACHINE_TYPE "custom_cbp_machine" + #define PS2TBL_X1MODE_TYPE "custom_cbp_x1mode" + #define PS2TBL_X1KEYCODE_TYPE "hex" + #define PS2TBL_X1KEYCODE_BYTE2_TYPE "hex" + #define PS2TBL_X1CTRL_TYPE "custom_cbn_x1ctrl" + + // Keyboard mapping table select list for PS2CTRL. + #define PS2TBL_PS2CTRL_SEL_NONE "NONE" + #define PS2TBL_PS2CTRL_SEL_SHIFT "SHIFT" + #define PS2TBL_PS2CTRL_SEL_CTRL "CTRL" + #define PS2TBL_PS2CTRL_SEL_CAPS "CAPS" + #define PS2TBL_PS2CTRL_SEL_KANA "KANA" + #define PS2TBL_PS2CTRL_SEL_GRAPH "GRAPH" + #define PS2TBL_PS2CTRL_SEL_GUI "GUI" + #define PS2TBL_PS2CTRL_SEL_FUNC "FUNC" + #define PS2TBL_PS2CTRL_SEL_EXACT "EXACT" + + // Keyboard mapping table select list for Model of keyboard. + #define KEYMAP_SEL_STANDARD "ALL" + #define KEYMAP_SEL_UK_WYSE_KB3926 "UK_WYSE_KB3926" + #define KEYMAP_SEL_JAPAN_OADG109 "JAPAN_OADG109" + #define KEYMAP_SEL_JAPAN_SANWA_SKBL1 "JAPAN_SANWA_SKBL1" + #define KEYMAP_SEL_NOT_ASSIGNED_4 "KEYBOARD_4" + #define KEYMAP_SEL_NOT_ASSIGNED_5 "KEYBOARD_5" + #define KEYMAP_SEL_NOT_ASSIGNED_6 "KEYBOARD_6" + #define KEYMAP_SEL_UK_PERIBOARD_810 "UK_PERIBOARD_810" + #define KEYMAP_SEL_UK_OMOTON_K8508 "UK_OMOTON_K8508" + + // Keyboard mapping table select list for keyboard mode. + #define X1_SEL_MODE_A "Mode_A" + #define X1_SEL_MODE_B "Mode_B" + + // Keyboard mapping table select list for target machine. + #define X1_SEL_ALL "ALL" + #define X1_SEL_ORIG "ORIGINAL" + #define X1_SEL_TURBO "TURBO" + #define X1_SEL_TURBOZ "TURBOZ" + + // Keyboard mapping table select list for X1 Control codes. + #define X1_CTRL_SEL_TENKEY "TENKEY" + #define X1_CTRL_SEL_PRESS "PRESS" + #define X1_CTRL_SEL_REPEAT "REPEAT" + #define X1_CTRL_SEL_GRAPH "GRAPH" + #define X1_CTRL_SEL_CAPS "CAPS" + #define X1_CTRL_SEL_KANA "KANA" + #define X1_CTRL_SEL_SHIFT "SHIFT" + #define X1_CTRL_SEL_CTRL "CTRL" + + // The Sharp X1 Series was released over a number of years and each iteration added changes/updates. In order to cater for differences, it is possible to assign a key mapping + // to a specific machine type(s) or all of the series by adding the flags below into the mapping table. + #define X1_ALL 0xFF + #define X1_ORIG 0x01 + #define X1_TURBO 0x02 + #define X1_TURBOZ 0x04 + + // The X1 Turbo onwards had a mode switch on the keyboard, Mode A was normal use, Mode B was for games, speeding up the key press by shortening the timing and setting common game keys pressed in a 24bit bit map. + // The mapping table caters for both, OR'ing data in Mode B so that multiple key presses are sent across as a bit map. + #define X1_MODE_A 0x01 + #define X1_MODE_B 0x02 + + // Keyboard models. The base on which this interface was created was a Wyse KB3926 PS/2 Keyboard and this is deemed STANDARD. Other models need to insert difference maps + // prior to the STANDARD entry along with the keyboard model so that it is processed first thus allowing differing keyboards with different maps. + #define KEYMAP_STANDARD 0xFF + #define KEYMAP_UK_WYSE_KB3926 0x01 + #define KEYMAP_JAPAN_OADG109 0x02 + #define KEYMAP_JAPAN_SANWA_SKBL1 0x04 + #define KEYMAP_NOT_ASSIGNED_4 0x08 + #define KEYMAP_NOT_ASSIGNED_5 0x10 + #define KEYMAP_NOT_ASSIGNED_6 0x20 + #define KEYMAP_UK_PERIBOARD_810 0x40 + #define KEYMAP_UK_OMOTON_K8508 0x80 + + public: + // Prototypes. + X1(void); + X1(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char *fsPath); + X1(NVS *hdlNVS, HID *hdlHID, const char *fsPath); + ~X1(void); + bool createKeyMapFile(std::fstream &outFile); + bool storeDataToKeyMapFile(std::fstream &outFile, char *data, int size); + bool storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray); + bool closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly); + std::string getKeyMapFileName(void) { return(X1IF_KEYMAP_FILE); }; + void getKeyMapHeaders(std::vector& headerList); + void getKeyMapTypes(std::vector& typeList); + bool getKeyMapSelectList(std::vector>& selectList, std::string option); + bool getKeyMapData(std::vector& dataArray, int *row, bool start); + + // Method to return the class version number. + float version(void) + { + return(X1IF_VERSION); + } + + protected: + + private: + // Prototypes. + void pushKeyToQueue(bool keybMode, uint32_t key); + IRAM_ATTR static void x1Interface( void * pvParameters ); + IRAM_ATTR static void hidInterface( void * pvParameters ); + void selectOption(uint8_t optionCode); + uint32_t mapKey(uint16_t scanCode); + bool loadKeyMap(); + bool saveKeyMap(void); + void init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + void init(NVS *hdlNVS, HID *hdlHID); + +// // Overload the base yield method to include suspension of the PS/2 Keyboard interface. This interface uses interrupts which are not mutex protected and clash with the +// // WiFi API methods. +// inline void yield(uint32_t delay) +// { +// // If suspended, go into a permanent loop until the suspend flag is reset. +// if(this->suspend) +// { +// // Suspend the keyboard interface. +// Keyboard->suspend(true); +// +// // Use the base method logic. +// KeyInterface::yield(delay); +// +// // Release the keyboard interface. +// Keyboard->suspend(false); +// } else +// // Otherwise just delay by the required amount for timing and to give other threads a time slice. +// { +// KeyInterface::yield(delay); +// } +// return; +// } + + // Structure to encapsulate a single key map from PS/2 to X1. + typedef struct { + uint8_t ps2KeyCode; + uint8_t ps2Ctrl; + uint8_t keyboardModel; + uint8_t machine; + uint8_t x1Mode; + uint8_t x1Key; + uint8_t x1Key2; + uint8_t x1Ctrl; + } t_keyMapEntry; + + // Structure to encapsulate the entire static keyboard mapping table. + typedef struct { + t_keyMapEntry kme[PS2TBL_X1_MAXROWS]; + } t_keyMap; + + // Structure to maintain the X1 interface configuration data. This data is persisted through powercycles as needed. + typedef struct { + struct { + uint8_t activeKeyboardMap; // Model of keyboard a keymap entry is applicable to. + uint8_t activeMachineModel; // Machine model a keymap entry is applicable to. + } params; + } t_x1Config; + + // Configuration data. + t_x1Config x1Config; + + // Structure to manage the control signals signifying the state of the X1 keyboard. + typedef struct { + bool optionSelect; // Flag to indicate a user requested keyboard configuration option is being selected. + bool modeB; // Mode B (Game mode) flag. If set, Mode B active, clear, Mode A active (normal keyboard). + uint8_t keyCtrl; // Keyboard state flag control. + + std::string fsPath; // Path on the underlying filesystem where storage is mounted and accessible. + t_keyMapEntry *kme; // Pointer to an array in memory to contain PS2 to X1 mapping values. + int kmeRows; // Number of rows in the kme table. + std::string keyMapFileName; // Name of file where extension or replacement key map entries are stored. + bool persistConfig; // Flag to request saving of the config into NVS storage. + } t_x1Control; + + // Transmit buffer queue item. + typedef struct { + uint32_t keyCode; // 32bit because normal mode A is 16bit, game mode B is 24bit + bool modeB; // True if in game mode B. + } t_xmitQueueMessage; + + // Thread handles - one per function, ie. HID interface and host target interface. + TaskHandle_t TaskHostIF = NULL; + TaskHandle_t TaskHIDIF = NULL; + + // Control structure to control interaction and mapping of keys for the host. + t_x1Control x1Control; + + // Spin lock mutex to hold a coresied to an uninterruptable method. This only works on dual core ESP32's. + portMUX_TYPE x1Mutex; + + // Lookup table to match PS/2 codes to X1 Key and Control Data. + // + // Given that the X1 had many variants, with potential differences between them, the mapping table allows for ALL or variant specific entries, the first entry matching is selected. + // + + // + // This mapping is for the UK Wyse KB-3926 PS/2 keyboard + // + t_keyMap PS2toX1 = { + { + // HELP + // COPY + // ModeB Byte1 ModeB Byte2 ModeB Byte3 + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine X1 Keyb Mode X1 Data X1 Ctrl (Flags to Set). + { PS2_KEY_F1, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'v', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // SHIFT+F1 + { PS2_KEY_F2, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'w', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // SHIFT+F2 + { PS2_KEY_F3, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'x', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // SHIFT+F3 + { PS2_KEY_F4, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'y', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // SHIFT+F4 + { PS2_KEY_F5, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'z', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // SHIFT+F5 + { PS2_KEY_F1, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'q', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F1 + { PS2_KEY_F2, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'r', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F2 + { PS2_KEY_F3, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 's', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F3 + { PS2_KEY_F4, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 't', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F4 + { PS2_KEY_F5, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'u', 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F5 + { PS2_KEY_F6, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEC, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F6 + { PS2_KEY_F7, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEB, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F7 + { PS2_KEY_F8, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE2, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F8 + { PS2_KEY_F9, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE1, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // F9 + { PS2_KEY_F10, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // XFER + { PS2_KEY_F11, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xFE, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // HELP + { PS2_KEY_F12, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // COPY + { PS2_KEY_TAB, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x09, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // TAB + // Control keys. + { PS2_KEY_APOS, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-@ + { PS2_KEY_A, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x01, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-A + { PS2_KEY_B, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x02, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-B + { PS2_KEY_C, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x03, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-C + { PS2_KEY_D, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x04, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-D + { PS2_KEY_E, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x05, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-E + { PS2_KEY_F, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x06, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-F + { PS2_KEY_G, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x07, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-G + { PS2_KEY_H, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x08, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-H + { PS2_KEY_I, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x09, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-I + { PS2_KEY_J, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0A, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-J + { PS2_KEY_K, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0B, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-K + { PS2_KEY_L, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0C, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-L + { PS2_KEY_M, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0D, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-M + { PS2_KEY_N, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0E, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-N + { PS2_KEY_O, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0F, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-O + { PS2_KEY_P, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x10, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-P + { PS2_KEY_Q, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x11, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-Q + { PS2_KEY_R, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x12, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-R + { PS2_KEY_S, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x13, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-S + { PS2_KEY_T, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x14, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-T + { PS2_KEY_U, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x15, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-U + { PS2_KEY_V, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x16, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-V + { PS2_KEY_W, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x17, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-W + { PS2_KEY_X, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x18, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-X + { PS2_KEY_Y, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x19, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-Y + { PS2_KEY_Z, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1A, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-Z + { PS2_KEY_OPEN_SQ, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1B, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-[ + { PS2_KEY_BACK, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1C, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-BACKSLASH + { PS2_KEY_CLOSE_SQ, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1D, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-] + { PS2_KEY_6, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1E, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-^ + { PS2_KEY_MINUS, PS2CTRL_CTRL, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1F, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CTRL-_ + // Numeric keys. + { PS2_KEY_0, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '0', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 0 + { PS2_KEY_1, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '1', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 1 + { PS2_KEY_2, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '2', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 2 + { PS2_KEY_3, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '3', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 3 + { PS2_KEY_4, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '4', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 4 + { PS2_KEY_5, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '5', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 5 + { PS2_KEY_6, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '6', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 6 + { PS2_KEY_7, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '7', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 7 + { PS2_KEY_8, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '8', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 8 + { PS2_KEY_9, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '9', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // 9 + // Punctuation keys. + { PS2_KEY_0, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ')', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Close Right Bracket ) + { PS2_KEY_1, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '!', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Exclamation + { PS2_KEY_2, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '"', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Double quote. + { PS2_KEY_3, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x23, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Pound Sign -> Hash + { PS2_KEY_4, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '$', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Dollar + { PS2_KEY_5, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '%', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Percent + { PS2_KEY_6, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '^', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Kappa + { PS2_KEY_7, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '&', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Ampersand + { PS2_KEY_8, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '*', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Star + { PS2_KEY_9, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '(', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Open Left Bracket ( + // ALPHA keys, lower and uppercase. + // ModeB Byte1 ModeB Byte2 ModeB Byte3 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X1 Keyb Mode X1 Data X1 Ctrl (Flags to Set). + { PS2_KEY_A, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'a', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // a + { PS2_KEY_A, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'A', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // A + { PS2_KEY_B, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'b', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // b + { PS2_KEY_B, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'B', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // B + { PS2_KEY_C, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'c', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // c + { PS2_KEY_C, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'C', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // C + { PS2_KEY_D, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'd', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // d + { PS2_KEY_D, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'D', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // D + { PS2_KEY_E, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'e', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // e + { PS2_KEY_E, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'E', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // E + { PS2_KEY_F, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'f', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // f + { PS2_KEY_F, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'F', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // F + { PS2_KEY_G, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'g', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // g + { PS2_KEY_G, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'G', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // G + { PS2_KEY_H, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'h', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // h + { PS2_KEY_H, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'H', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // H + { PS2_KEY_I, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'i', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // i + { PS2_KEY_I, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'I', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // I + { PS2_KEY_J, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'j', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // j + { PS2_KEY_J, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'J', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // J + { PS2_KEY_K, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'k', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // k + { PS2_KEY_K, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'K', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // K + { PS2_KEY_L, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'l', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // l + { PS2_KEY_L, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'L', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // L + { PS2_KEY_M, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'm', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // m + { PS2_KEY_M, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'M', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // M + { PS2_KEY_N, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'n', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // n + { PS2_KEY_N, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'N', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // N + { PS2_KEY_O, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'o', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // o + { PS2_KEY_O, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'O', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // O + { PS2_KEY_P, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'p', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // p + { PS2_KEY_P, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'P', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // P + { PS2_KEY_Q, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'q', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // q + { PS2_KEY_Q, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'Q', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Q + { PS2_KEY_R, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'r', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // r + { PS2_KEY_R, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'R', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // R + { PS2_KEY_S, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 's', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // s + { PS2_KEY_S, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'S', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // S + { PS2_KEY_T, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 't', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // t + { PS2_KEY_T, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'T', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // T + { PS2_KEY_U, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'u', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // u + { PS2_KEY_U, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'U', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // U + { PS2_KEY_V, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'v', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // v + { PS2_KEY_V, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'V', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // V + { PS2_KEY_W, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'w', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // w + { PS2_KEY_W, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'W', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // W + { PS2_KEY_X, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'x', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // x + { PS2_KEY_X, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'X', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // X + { PS2_KEY_Y, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'y', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // y + { PS2_KEY_Y, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'Y', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Y + { PS2_KEY_Z, PS2CTRL_SHIFT | PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'z', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // z + { PS2_KEY_Z, PS2CTRL_CAPS, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 'Z', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Z + // Mode B Mappings. + { PS2_KEY_Q, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b10000000, 0b00000000, 0b00000000, }, // MODE B - Q + { PS2_KEY_W, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b01000000, 0b00000000, 0b00000000, }, // MODE B - W + { PS2_KEY_E, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00100000, 0b00000000, 0b00000000, }, // MODE B - E + { PS2_KEY_A, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00010000, 0b00000000, 0b00000000, }, // MODE B - A + { PS2_KEY_D, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00001000, 0b00000000, 0b00000000, }, // MODE B - D + { PS2_KEY_Z, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000100, 0b00000000, 0b00000000, }, // MODE B - Z + { PS2_KEY_X, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000010, 0b00000000, 0b00000000, }, // MODE B - X + { PS2_KEY_C, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000001, 0b00000000, 0b00000000, }, // MODE B - C + { PS2_KEY_I, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b01000000, }, // MODE B - I - this is not 100%, the specs arent clear. + { PS2_KEY_1, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00100000, 0b00000000, }, // MODE B - 1 + { PS2_KEY_2, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00001000, 0b00000000, }, // MODE B - 2 + { PS2_KEY_3, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000001, 0b00000000, }, // MODE B - 3 + { PS2_KEY_4, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b01000000, 0b00000000, }, // MODE B - 4 + { PS2_KEY_6, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000010, 0b00000000, }, // MODE B - 6 + { PS2_KEY_7, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b10000000, 0b00000000, }, // MODE B - 7 + { PS2_KEY_8, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00010000, 0b00000000, }, // MODE B - 8 + { PS2_KEY_9, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000100, 0b00000000, }, // MODE B - 9 + { PS2_KEY_ESC, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b10000000, }, // MODE B - ESC + { PS2_KEY_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00100000, }, // MODE B - MINUS + { PS2_KEY_EQUAL, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00010000, }, // MODE B - PLUS + { PS2_KEY_8, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00001000, }, // MODE B - TIMES + { PS2_KEY_TAB, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00000100, }, // MODE B - TAB + { PS2_KEY_SPACE, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00000010, }, // MODE B - SPACE + { PS2_KEY_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00000001, }, // MODE B - RET + { PS2_KEY_KP1, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00100000, 0b00000000, }, // MODE B - KeyPad 1 + { PS2_KEY_KP2, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00001000, 0b00000000, }, // MODE B - KeyPad 2 + { PS2_KEY_KP3, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000001, 0b00000000, }, // MODE B - KeyPad 3 + { PS2_KEY_KP4, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b01000000, 0b00000000, }, // MODE B - KeyPad 4 + { PS2_KEY_KP6, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000010, 0b00000000, }, // MODE B - KeyPad 6 + { PS2_KEY_KP7, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b10000000, 0b00000000, }, // MODE B - KeyPad 7 + { PS2_KEY_KP8, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00010000, 0b00000000, }, // MODE B - KeyPad 8 + { PS2_KEY_KP9, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000100, 0b00000000, }, // MODE B - KeyPad 9 + { PS2_KEY_KP_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00100000, }, // MODE B - KeyPad MINUS + { PS2_KEY_KP_PLUS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00010000, }, // MODE B - KeyPad PLUS + { PS2_KEY_KP_TIMES, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_B, 0b00000000, 0b00000000, 0b00001000, }, // MODE B - KeyPad TIMES + + // ModeB Byte1 ModeB Byte2 ModeB Byte3 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X1 Keyb Mode X1 Data X1 Ctrl (Flags to Set). + { PS2_KEY_SPACE, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ' ', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Space + { PS2_KEY_COMMA, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '<', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Less Than < + { PS2_KEY_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ',', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Comma , + { PS2_KEY_SEMI, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ':', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Colon : + { PS2_KEY_SEMI, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ';', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Semi-Colon ; + { PS2_KEY_DOT, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '>', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Greater Than > + { PS2_KEY_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '.', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Full stop . + { PS2_KEY_DIV, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '?', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Question ? + { PS2_KEY_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '/', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Divide / + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '_', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Underscore + { PS2_KEY_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '-', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, + { PS2_KEY_APOS, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '@', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // At @ + { PS2_KEY_APOS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '\'', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Single quote ' + { PS2_KEY_OPEN_SQ, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '{', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Open Left Brace { + { PS2_KEY_OPEN_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '[', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Open Left Square Bracket [ + { PS2_KEY_EQUAL, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '+', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Plus + + { PS2_KEY_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '=', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Equal = + { PS2_KEY_CAPS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ' ', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // LOCK + { PS2_KEY_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0D, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // ENTER/RETURN + { PS2_KEY_CLOSE_SQ, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '}', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Close Right Brace } + { PS2_KEY_CLOSE_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ']', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Close Right Square Bracket ] + { PS2_KEY_BACK, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '|', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // + { PS2_KEY_BACK, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '\\', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Back slash maps to Yen + { PS2_KEY_BTICK, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '`', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Pipe + { PS2_KEY_BTICK, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '|', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Back tick ` + { PS2_KEY_HASH, PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '~', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Tilde has no mapping. + { PS2_KEY_HASH, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '#', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Hash + { PS2_KEY_BS, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x08, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Backspace + { PS2_KEY_ESC, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x1B, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // ESCape + { PS2_KEY_SCROLL, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ' ', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Not assigned. + { PS2_KEY_INSERT, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_INS, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // INSERT + { PS2_KEY_HOME, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_CLR, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // CLR + { PS2_KEY_HOME, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_HOME, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // HOME + { PS2_KEY_DELETE, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_DEL, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // DELETE + { PS2_KEY_END, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x11, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // END + { PS2_KEY_PGUP, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0E, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // Roll Up. + { PS2_KEY_PGDN, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0F, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // Roll Down + { PS2_KEY_UP_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_UP, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Up Arrow + { PS2_KEY_L_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_LEFT, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Left Arrow + { PS2_KEY_DN_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_DOWN, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Down Arrow + { PS2_KEY_R_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, X1KEY_RIGHT, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Right Arrow + { PS2_KEY_NUM, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Not assigned. + // GRPH (Alt Gr) + // ModeB Byte1 ModeB Byte2 ModeB Byte3 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X1 Keyb Mode X1 Data X1 Ctrl (Flags to Set). + { PS2_KEY_0, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xFA, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+0 + { PS2_KEY_1, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF1, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+1 + { PS2_KEY_2, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF2, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+2 + { PS2_KEY_3, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF3, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+3 + { PS2_KEY_4, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF4, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+4 + { PS2_KEY_5, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF5, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+5 + { PS2_KEY_6, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF6, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+6 + { PS2_KEY_7, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF7, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+7 + { PS2_KEY_8, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF8, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+8 + { PS2_KEY_9, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF9, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+9 + { PS2_KEY_A, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x7F, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+A + { PS2_KEY_B, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x84, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+B + { PS2_KEY_C, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x82, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+C + { PS2_KEY_D, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEA, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+D + { PS2_KEY_E, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE2, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+E + { PS2_KEY_F, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEB, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+F + { PS2_KEY_G, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEC, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+G + { PS2_KEY_H, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xED, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+H + { PS2_KEY_I, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE7, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+I + { PS2_KEY_J, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEE, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+J + { PS2_KEY_K, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xEF, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+K + { PS2_KEY_L, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x8E, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+L + { PS2_KEY_M, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x86, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+M + { PS2_KEY_N, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x85, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+N + { PS2_KEY_O, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xF0, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+O + { PS2_KEY_P, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x8D, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+P + { PS2_KEY_Q, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE0, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+Q + { PS2_KEY_R, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE3, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+R + { PS2_KEY_S, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE9, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+S + { PS2_KEY_T, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE4, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+T + { PS2_KEY_U, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE6, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+U + { PS2_KEY_V, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x83, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+V + { PS2_KEY_W, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE1, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+W + { PS2_KEY_X, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x81, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+X + { PS2_KEY_Y, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE5, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+Y + { PS2_KEY_Z, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x80, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+Z + { PS2_KEY_COMMA, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x87, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+, + { PS2_KEY_SEMI, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x89, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+; + { PS2_KEY_DOT, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x88, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+. + { PS2_KEY_DIV, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xFE, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+/ + { PS2_KEY_MINUS, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x8C, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+- + { PS2_KEY_APOS, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x8A, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+' + { PS2_KEY_OPEN_SQ, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xFC, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+[ + { PS2_KEY_CLOSE_SQ, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xE8, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+] + { PS2_KEY_BACK, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x90, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRPH+Backslash + { PS2_KEY_KP0, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x8F, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 0 + { PS2_KEY_KP1, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x99, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 1 + { PS2_KEY_KP2, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x92, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 2 + { PS2_KEY_KP3, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x98, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 3 + { PS2_KEY_KP4, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x95, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 4 + { PS2_KEY_KP5, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x96, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 5 + { PS2_KEY_KP6, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x94, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 6 + { PS2_KEY_KP7, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x9A, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 7 + { PS2_KEY_KP8, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x93, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 8 + { PS2_KEY_KP9, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x97, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad 9 + { PS2_KEY_KP_DOT, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x91, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x9D, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x9C, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x9B, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x9E, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad Divide / + { PS2_KEY_KP_ENTER, PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x90, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // GRPH+Keypad Enter / + // KANA (Alt) + // ModeB Byte1 ModeB Byte2 ModeB Byte3 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X1 Keyb Mode X1 Data X1 Ctrl (Flags to Set). + { PS2_KEY_0, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA6, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+0 + { PS2_KEY_0, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xDC, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+0 + { PS2_KEY_1, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC7, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+1 + { PS2_KEY_2, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xCC, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+2 + { PS2_KEY_3, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA7, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+3 + { PS2_KEY_3, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB1, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+3 + { PS2_KEY_4, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA9, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+4 + { PS2_KEY_4, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB3, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+4 + { PS2_KEY_5, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xAA, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+5 + { PS2_KEY_5, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB4, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+5 + { PS2_KEY_6, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xAB, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+6 + { PS2_KEY_6, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB5, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+6 + { PS2_KEY_7, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xAC, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+7 + { PS2_KEY_7, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD4, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+7 + { PS2_KEY_8, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xAD, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+8 + { PS2_KEY_8, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD5, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+8 + { PS2_KEY_9, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xAE, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+9 + { PS2_KEY_9, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD6, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+9 + { PS2_KEY_A, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC1, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+A + { PS2_KEY_B, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xBA, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+B + { PS2_KEY_C, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xBF, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+C + { PS2_KEY_D, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xBC, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+D + { PS2_KEY_E, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA8, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+E + { PS2_KEY_E, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB2, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+E + { PS2_KEY_F, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xCA, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+F + { PS2_KEY_G, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB7, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+G + { PS2_KEY_H, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB8, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+H + { PS2_KEY_I, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC6, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+I + { PS2_KEY_J, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xCF, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+J + { PS2_KEY_K, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC9, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+K + { PS2_KEY_L, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD8, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+L + { PS2_KEY_M, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD3, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+M + { PS2_KEY_N, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD0, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+N + { PS2_KEY_O, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD7, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+O + { PS2_KEY_P, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xBE, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+P + { PS2_KEY_Q, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC0, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+Q + { PS2_KEY_R, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xBD, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+R + { PS2_KEY_S, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC4, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+S + { PS2_KEY_T, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xB6, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+T + { PS2_KEY_U, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC5, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+U + { PS2_KEY_V, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xCB, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+V + { PS2_KEY_W, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC3, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+W + { PS2_KEY_X, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xBB, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+X + { PS2_KEY_Y, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xDD, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+Y + { PS2_KEY_Z, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xAF, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+Z + { PS2_KEY_Z, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC2, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+Z + { PS2_KEY_COMMA, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA4, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+, + { PS2_KEY_COMMA, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xC8, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+, + { PS2_KEY_SEMI, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xDA, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+; + { PS2_KEY_DOT, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA1, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+. + { PS2_KEY_DOT, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD9, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+. + { PS2_KEY_DIV, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA5, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+/ + { PS2_KEY_DIV, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD2, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+/ + { PS2_KEY_MINUS, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xCE, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+- + { PS2_KEY_APOS, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xDE, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+' + { PS2_KEY_OPEN_SQ, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA2, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+[ + { PS2_KEY_OPEN_SQ, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xDF, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+[ + { PS2_KEY_CLOSE_SQ, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xA3, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+] + { PS2_KEY_CLOSE_SQ, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xD1, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+] + { PS2_KEY_BACK, PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0xDB, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+Backslash + { PS2_KEY_BS, PS2CTRL_KANA | PS2CTRL_SHIFT, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x12, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA+SHIFT+Backspace + // Keypad. + { PS2_KEY_KP0, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '0', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 0 + { PS2_KEY_KP1, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '1', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 1 + { PS2_KEY_KP2, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '2', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 2 + { PS2_KEY_KP3, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '3', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 3 + { PS2_KEY_KP4, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '4', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 4 + { PS2_KEY_KP5, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '5', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 5 + { PS2_KEY_KP6, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '6', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 6 + { PS2_KEY_KP7, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '7', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 7 + { PS2_KEY_KP8, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '8', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 8 + { PS2_KEY_KP9, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '9', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad 9 + { PS2_KEY_KP_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, ',', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Comma , + { PS2_KEY_KP_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '.', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '+', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '-', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '*', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '/', 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Divide / + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x0D, 0x00, 0xFF & ~(X1_CTRL_TENKEY | X1_CTRL_PRESS), }, // Keypad Enter / + { PS2_KEY_KP_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, '=', 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Keypad Equal = + // ModeB Byte1 ModeB Byte2 ModeB Byte3 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X1 Keyb Mode X1 Data X1 Ctrl (Flags to Set). + // Special keys. + { PS2_KEY_PRTSCR, PS2CTRL_FUNC, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // ARGO KEY + { PS2_KEY_PAUSE, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x03, 0x00, 0xFF & ~(X1_CTRL_PRESS | X1_CTRL_TENKEY), }, // BREAK KEY + { PS2_KEY_L_GUI, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // GRAPH KEY + //{ PS2_KEY_L_ALT, PS2CTRL_FUNC | PS2CTRL_KANA, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KJ1 Sentence + //{ PS2_KEY_R_ALT, PS2CTRL_FUNC | PS2CTRL_GRAPH, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KJ2 Transform + { PS2_KEY_R_GUI, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // KANA KEY + { PS2_KEY_MENU, PS2CTRL_FUNC | PS2CTRL_GUI, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Not assigned. + // Modifiers are last, only being selected if an earlier match isnt made. + { PS2_KEY_L_SHIFT, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, + { PS2_KEY_R_SHIFT, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, + { PS2_KEY_L_CTRL, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, + { PS2_KEY_R_CTRL, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, // Map to Control + { 0, PS2CTRL_NONE, KEYMAP_STANDARD, X1_ALL, X1_MODE_A, 0x00, 0x00, 0xFF & ~(X1_CTRL_PRESS), }, + }}; +}; + +#endif // X1_H diff --git a/main/include/X68K.h b/main/include/X68K.h deleted file mode 120000 index fc88748..0000000 --- a/main/include/X68K.h +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/main/include/X68K.h \ No newline at end of file diff --git a/main/include/X68K.h b/main/include/X68K.h new file mode 100644 index 0000000..b33d402 --- /dev/null +++ b/main/include/X68K.h @@ -0,0 +1,533 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: X68K.h +// Created: Mar 2022 +// Version: v1.0 +// Author(s): Philip Smart +// Description: Header for the Sharp X68000 to PS/2 interface logic class. +// Credits: +// Copyright: (c) 2019-2022 Philip Smart +// +// History: Mar 2022 - Initial write. +// v1.01 May 2022 - Initial release version. +// v1.02 Jun 2022 - Updates to reflect changes realised in other modules due to addition of +// bluetooth and suspend logic due to NVS issues using both cores. +// v1.03 Jun 2022 - Further updates adding in keymaps for UK BT and Japan OADG109. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// This source file is free software: you can redistribute it and#or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef X68K_H +#define X68K_H + +// Include the specification class. +#include "KeyInterface.h" +#include "NVS.h" +#include "LED.h" +#include "HID.h" +#include +#include + +// NB: Macros definitions put inside class for clarity, they are still global scope. + +// Encapsulate the Sharp X68K interface. +class X68K : public KeyInterface { + // Macros. + // + #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) + + // Constants. + #define X68KIF_VERSION 1.03 + #define X68KIF_KEYMAP_FILE "X68K_KeyMap.BIN" + #define MAX_X68K_XMIT_KEY_BUF 16 + #define MAX_X68K_RCV_KEY_BUF 16 + + // PS2 Flag definitions. + #define PS2CTRL_NONE 0x00 // No keys active = 0 + #define PS2CTRL_SHIFT 0x01 // Shfit Key active = 1 + #define PS2CTRL_CTRL 0x02 // Ctrl Key active = 1 + #define PS2CTRL_CAPS 0x04 // CAPS active = 1 + #define PS2CTRL_R_CTRL 0x08 // ALT flag used as Right CTRL flag, active = 1 + #define PS2CTRL_ALTGR 0x10 // ALTGR active = 1 + #define PS2CTRL_GUI 0x20 // GUI Key active = 1 + #define PS2CTRL_FUNC 0x40 // Special Function Keys active = 1 + #define PS2CTRL_BREAK 0x80 // BREAK Key active = 1 + #define PS2CTRL_EXACT 0x80 // EXACT Match active = 1 + + // The initial mapping is made inside the PS2KeyAdvanced class from Scan Code Set 2 to ASCII + // for a selected keyboard. Special functions are detected and combined inside this module + // before mapping with the table below to extract the X68K key code and control data. + // ie. PS/2 Scan Code -> ASCII + Flags -> X68K Key Code + Ctrl Data + #define PS2TBL_X68K_MAXCOLS 6 + #define PS2TBL_X68K_MAXROWS 131 + + // Keyboard mapping table column names. + #define PS2TBL_PS2KEYCODE_NAME "PS/2 KeyCode" + #define PS2TBL_PS2CTRL_NAME "PS/2 Control Key" + #define PS2TBL_KEYBOARDMODEL_NAME "For Keyboard" + #define PS2TBL_MACHINE_NAME "For Host Model" + #define PS2TBL_X68KKEYCODE_NAME "X68K KeyCode" + #define PS2TBL_X68KCTRL_NAME "X68K Control Key" + + // Keyboard mapping table column types. + #define PS2TBL_PS2KEYCODE_TYPE "hex" + #define PS2TBL_PS2CTRL_TYPE "custom_cbp_ps2ctrl" + #define PS2TBL_KEYBOARDMODEL_TYPE "custom_cbp_keybmodel" + #define PS2TBL_MACHINE_TYPE "custom_cbp_machine" + #define PS2TBL_X68KKEYCODE_TYPE "hex" + #define PS2TBL_X68KCTRL_TYPE "custom_cbp_x68kctrl" + + // Keyboard mapping table select list for PS2CTRL. + #define PS2TBL_PS2CTRL_SEL_NONE "NONE" + #define PS2TBL_PS2CTRL_SEL_SHIFT "SHIFT" + #define PS2TBL_PS2CTRL_SEL_CTRL "CTRL" + #define PS2TBL_PS2CTRL_SEL_CAPS "CAPS" + #define PS2TBL_PS2CTRL_SEL_R_CTRL "RCTRL" + #define PS2TBL_PS2CTRL_SEL_ALTGR "ALTGR" + #define PS2TBL_PS2CTRL_SEL_GUI "GUI" + #define PS2TBL_PS2CTRL_SEL_FUNC "FUNC" + #define PS2TBL_PS2CTRL_SEL_EXACT "EXACT" + + // Keyboard mapping table select list for target machine. + #define X68K_SEL_ALL "ALL" + #define X68K_SEL_ORIG "ORIGINAL" + #define X68K_SEL_ACE "ACE" + #define X68K_SEL_EXPERT "EXPERT" + #define X68K_SEL_PRO "PRO" + #define X68K_SEL_SUPER "SUPER" + #define X68K_SEL_XVI "XVI" + #define X68K_SEL_COMPACT "COMPACT" + #define X68K_SEL_X68030 "68030" + + // Keyboard mapping table select list for Model of keyboard. + #define KEYMAP_SEL_STANDARD "ALL" + #define KEYMAP_SEL_UK_WYSE_KB3926 "UK_WYSE_KB3926" + #define KEYMAP_SEL_JAPAN_OADG109 "JAPAN_OADG109" + #define KEYMAP_SEL_JAPAN_SANWA_SKBL1 "JAPAN_SANWA_SKBL1" + #define KEYMAP_SEL_NOT_ASSIGNED_4 "KEYBOARD_4" + #define KEYMAP_SEL_NOT_ASSIGNED_5 "KEYBOARD_5" + #define KEYMAP_SEL_NOT_ASSIGNED_6 "KEYBOARD_6" + #define KEYMAP_SEL_UK_PERIBOARD_810 "UK_PERIBOARD_810" + #define KEYMAP_SEL_UK_OMOTON_K8508 "UK_OMOTON_K8508" + + // Keyboard mapping table select list for X68K Control codes. + #define X68K_CTRL_SEL_NONE "NONE" + #define X68K_CTRL_SEL_SHIFT "SHIFT" + #define X68K_CTRL_SEL_RELEASESHIFT "RELEASESHIFT" + #define X68K_CTRL_SEL_R_CTRL "RCTRL" + + // X68K Key control bit mask. + #define X68K_CTRL_SHIFT ((unsigned char) (1 << 7)) + #define X68K_CTRL_RELEASESHIFT ((unsigned char) (1 << 6)) + #define X68K_CTRL_R_CTRL ((unsigned char) (1 << 0)) + #define X68K_CTRL_NONE 0x00 + + // The Sharp X68000 Series was released over a number of years with several iterations containing changes/updates. Generally Sharp kept the X68000 compatible through the range but just in case + // differences are found, it is possible to assign a key mapping to a specific machine type(s) or all of the series by adding the flags below into the mapping table. + #define X68K_ALL 0xFF + #define X68K_ORIG 0x01 + #define X68K_ACE 0x02 + #define X68K_EXPERT 0x04 + #define X68K_PRO 0x08 + #define X68K_SUPER 0x10 + #define X68K_XVI 0x20 + #define X68K_COMPACT 0x40 + #define X68K_X68030 0x80 + + // Keyboard models. The base on which this interface was created was a Wyse KB3926 PS/2 Keyboard and this is deemed STANDARD. Other models need to insert difference maps + // prior to the STANDARD entry along with the keyboard model so that it is processed first thus allowing differing keyboards with different maps. + #define KEYMAP_STANDARD 0xFF + #define KEYMAP_UK_WYSE_KB3926 0x01 + #define KEYMAP_JAPAN_OADG109 0x02 + #define KEYMAP_JAPAN_SANWA_SKBL1 0x04 + #define KEYMAP_NOT_ASSIGNED_4 0x08 + #define KEYMAP_NOT_ASSIGNED_5 0x10 + #define KEYMAP_NOT_ASSIGNED_6 0x20 + #define KEYMAP_UK_PERIBOARD_810 0x40 + #define KEYMAP_UK_OMOTON_K8508 0x80 + + // X68000 Scan codes - PS2 codes along with function keys (SHIFT, CTRL etc) are mapped to the X68000 scan codes below. + #define X68K_KEY_NULL 0x00 + #define X68K_KEY_ESC 0x01 + #define X68K_KEY_0 0x0B + #define X68K_KEY_1 0x02 + #define X68K_KEY_2 0x03 + #define X68K_KEY_3 0x04 + #define X68K_KEY_4 0x05 + #define X68K_KEY_5 0x06 + #define X68K_KEY_6 0x07 + #define X68K_KEY_7 0x08 + #define X68K_KEY_8 0x09 + #define X68K_KEY_9 0x0A + #define X68K_KEY_A 0x1E + #define X68K_KEY_B 0x2E + #define X68K_KEY_C 0x2C + #define X68K_KEY_D 0x20 + #define X68K_KEY_E 0x13 + #define X68K_KEY_F 0x21 + #define X68K_KEY_G 0x22 + #define X68K_KEY_H 0x23 + #define X68K_KEY_I 0x18 + #define X68K_KEY_J 0x24 + #define X68K_KEY_K 0x25 + #define X68K_KEY_L 0x26 + #define X68K_KEY_M 0x30 + #define X68K_KEY_N 0x2F + #define X68K_KEY_O 0x19 + #define X68K_KEY_P 0x1A + #define X68K_KEY_Q 0x11 + #define X68K_KEY_R 0x14 + #define X68K_KEY_S 0x1F + #define X68K_KEY_T 0x15 + #define X68K_KEY_U 0x17 + #define X68K_KEY_V 0x2D + #define X68K_KEY_W 0x12 + #define X68K_KEY_X 0x2B + #define X68K_KEY_Y 0x16 + #define X68K_KEY_Z 0x2A + #define X68K_KEY_AT 0x1B + #define X68K_KEY_MINUS 0x0C + #define X68K_KEY_CIRCUMFLEX 0x0D + #define X68K_KEY_YEN 0x0E + #define X68K_KEY_BS 0x0F + #define X68K_KEY_TAB 0x10 + #define X68K_KEY_OPEN_SQ 0x1C + #define X68K_KEY_CLOSE_SQ 0x29 + #define X68K_KEY_RETURN 0x1D + #define X68K_KEY_SEMI 0x27 + #define X68K_KEY_COLON 0x28 + #define X68K_KEY_COMMA 0x31 + #define X68K_KEY_DOT 0x32 + #define X68K_KEY_DIV 0x33 + #define X68K_KEY_UNDERLINE 0x34 + #define X68K_KEY_SPACE 0x35 + #define X68K_KEY_HOME 0x36 + #define X68K_KEY_ROLLUP 0x38 + #define X68K_KEY_ROLLDN 0x39 + #define X68K_KEY_UNDO 0x3A + #define X68K_KEY_L_ARROW 0x3B + #define X68K_KEY_UP_ARROW 0x3C + #define X68K_KEY_R_ARROW 0x3D + #define X68K_KEY_DN_ARROW 0x3E + #define X68K_KEY_CLR 0x3F + #define X68K_KEY_KP0 0x4F + #define X68K_KEY_KP1 0x4B + #define X68K_KEY_KP2 0x4C + #define X68K_KEY_KP3 0x4D + #define X68K_KEY_KP4 0x47 + #define X68K_KEY_KP5 0x48 + #define X68K_KEY_KP6 0x49 + #define X68K_KEY_KP7 0x43 + #define X68K_KEY_KP8 0x44 + #define X68K_KEY_KP9 0x45 + #define X68K_KEY_KP_DIV 0x40 + #define X68K_KEY_KP_TIMES 0x41 + #define X68K_KEY_KP_MINUS 0x42 + #define X68K_KEY_KP_PLUS 0x46 + #define X68K_KEY_KP_EQUAL 0x4A + #define X68K_KEY_KP_ENTER 0x4E + #define X68K_KEY_KP_COMMA 0x50 + #define X68K_KEY_KP_DOT 0x51 + #define X68K_KEY_SYMBOL 0x52 + #define X68K_KEY_HELP 0x54 + #define X68K_KEY_CAPS 0x5D + #define X68K_KEY_INS 0x5E + #define X68K_KEY_DEL 0x37 + #define X68K_KEY_BREAK 0x61 + #define X68K_KEY_COPY 0x62 + #define X68K_KEY_SHIFT 0x70 + #define X68K_KEY_CTRL 0x71 + #define X68K_KEY_XF1 0x55 + #define X68K_KEY_XF2 0x56 + #define X68K_KEY_XF3 0x57 + #define X68K_KEY_XF4 0x58 + #define X68K_KEY_XF5 0x59 + #define X68K_KEY_REGISTRATION 0x53 + #define X68K_KEY_KATAKANA 0x5A + #define X68K_KEY_ROMAJI 0x5B + #define X68K_KEY_TRANSPOSE 0x5C + #define X68K_KEY_HIRAGANA 0x5F + #define X68K_KEY_FULLWIDTH 0x60 + #define X68K_KEY_F1 0x63 + #define X68K_KEY_F2 0x64 + #define X68K_KEY_F3 0x65 + #define X68K_KEY_F4 0x66 + #define X68K_KEY_F5 0x67 + #define X68K_KEY_F6 0x68 + #define X68K_KEY_F7 0x69 + #define X68K_KEY_F8 0x6A + #define X68K_KEY_F9 0x6B + #define X68K_KEY_F10 0x6C + #define X68K_KEY_OPT_1 0x72 + #define X68K_KEY_OPT_2 0x73 + + public: + // Prototypes. + X68K(void); + X68K(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID, const char *fsPath); + X68K(NVS *hdlNVS, HID *hdlHID, const char *fsPath); + ~X68K(void); + bool createKeyMapFile(std::fstream &outFile); + bool storeDataToKeyMapFile(std::fstream &outFile, char *data, int size); + bool storeDataToKeyMapFile(std::fstream & outFile, std::vector& dataArray); + bool closeAndCommitKeyMapFile(std::fstream &outFile, bool cleanupOnly); + std::string getKeyMapFileName(void) { return(X68KIF_KEYMAP_FILE); }; + void getKeyMapHeaders(std::vector& headerList); + void getKeyMapTypes(std::vector& typeList); + bool getKeyMapSelectList(std::vector>& selectList, std::string option); + bool getKeyMapData(std::vector& dataArray, int *row, bool start); + + // Method to return the class version number. + float version(void) + { + return(X68KIF_VERSION); + } + + protected: + + private: + // Prototypes. + IRAM_ATTR void pushKeyToQueue(uint32_t key); + IRAM_ATTR void pushHostCmdToQueue(uint8_t cmd); + IRAM_ATTR static void x68kInterface( void * pvParameters ); + IRAM_ATTR static void hidInterface( void * pvParameters ); + void selectOption(uint8_t optionCode); + uint32_t mapKey(uint16_t scanCode); + bool loadKeyMap(); + bool saveKeyMap(void); + void init(uint32_t ifMode, NVS *hdlNVS, LED *hdlLED, HID *hdlHID); + void init(NVS *hdlNVS, HID *hdlHID); + + // Structure to encapsulate a single key map from PS/2 to X68K. + typedef struct { + uint8_t ps2KeyCode; + uint8_t ps2Ctrl; + uint8_t keyboardModel; + uint8_t machine; + uint8_t x68kKey; + uint8_t x68kCtrl; + } t_keyMapEntry; + + // Structure to encapsulate the entire static keyboard mapping table. + typedef struct { + t_keyMapEntry kme[PS2TBL_X68K_MAXROWS]; + } t_keyMap; + + // Structure to maintain the X68000 interface configuration data. This data is persisted through powercycles as needed. + typedef struct { + struct { + uint8_t activeKeyboardMap; // Model of keyboard a keymap entry is applicable to. + uint8_t activeMachineModel; // Machine model a keymap entry is applicable to. + bool useOnlyPersisted; // Flag to indicate wether the inbuilt keymap array should be combined with persisted values or the inbuilt array is ignored and only persisted values used. + } params; + } t_x68kConfig; + + // Configuration data. + t_x68kConfig x68kConfig; + + // Structure to manage the control signals signifying the state of the X68K keyboard. + typedef struct { + uint8_t keyCtrl; // Keyboard state flag control. + bool optionSelect; // Flag to indicate a user requested keyboard configuration option is being selected. + int uartNum; + int uartBufferSize; + int uartQueueSize; + + std::string fsPath; // Path on the underlying filesystem where storage is mounted and accessible. + t_keyMapEntry *kme; // Pointer to an array in memory to contain PS2 to X68K mapping values. + int kmeRows; // Number of rows in the kme table. + std::string keyMapFileName; // Name of file where extension or replacement key map entries are stored. + bool persistConfig; // Flag to request saving of the config into NVS storage. + } t_x68kControl; + + // Transmit buffer queue item. + typedef struct { + uint32_t keyCode; // Key data to be sent to X68000. + } t_xmitQueueMessage; + + // Receive buffer queue item. + typedef struct { + uint8_t hostCmd; // Keyboard configuration command received from X68000. + } t_rcvQueueMessage; + + // Thread handles - one per function, ie. HID interface and host target interface. + TaskHandle_t TaskHostIF = NULL; + TaskHandle_t TaskHIDIF = NULL; + + // Control structure to control interaction and mapping of keys for the host. + t_x68kControl x68kControl; + + // Spin lock mutex to hold a coresied to an uninterruptable method. This only works on dual core ESP32's. + portMUX_TYPE x68kMutex; + + // Lookup table to match PS/2 codes to X68K Key and Control Data. + // + // Given that the X68K had many variants, with potential differences between them, the mapping table allows for ALL or variant specific entries, the first entry matching is selected. + // + // This mapping is for the UK Wyse KB-3926 PS/2 keyboard which is deemed the KEYMAP_STANDARD and all other variants need to add additional mappings below, position sensitive, ie. add non-standard entries before standard entry. + // + //const unsigned char PS2toX68K[PS2TBL_X68K_MAXROWS][PS2TBL_X68K_MAXCOLS] = + //t_keyMapEntry PS2toX68K[PS2TBL_X68K_MAXROWS] = + t_keyMap PS2toX68K = { + { + //PS2 Code PS2 Ctrl (Flags to Match) Keyboard Model Machine X68K Data X68K Ctrl (Flags to Set). + // Function keys + { PS2_KEY_F1, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_HIRAGANA, X68K_CTRL_NONE, }, // R_CTRL + F1 = Hiragana + { PS2_KEY_F2, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_FULLWIDTH, X68K_CTRL_NONE, }, // R_CTRL + F2 = Full Width + { PS2_KEY_F3, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KATAKANA, X68K_CTRL_NONE, }, // R_CTRL + F3 = Katakana + { PS2_KEY_F4, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_ROMAJI, X68K_CTRL_NONE, }, // R_CTRL + F4 = Romaji + { PS2_KEY_F5, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_TRANSPOSE, X68K_CTRL_NONE, }, // R_CTRL + F5 = Tranpose + { PS2_KEY_F6, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_SYMBOL, X68K_CTRL_NONE, }, // R_CTRL + F6 = Symbol + { PS2_KEY_F7, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_REGISTRATION, X68K_CTRL_NONE, }, // R_CTRL + F7 = Registration - maybe a poor translation, needs better one! + { PS2_KEY_F9, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_COPY, X68K_CTRL_NONE, }, // R_CTRL + F9 = Copy + { PS2_KEY_F10, PS2CTRL_FUNC | PS2CTRL_CTRL | PS2CTRL_R_CTRL, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_HELP, X68K_CTRL_NONE, }, // R_CTRL + F10 = Help + { PS2_KEY_F1, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F1, X68K_CTRL_NONE, }, // F1 + { PS2_KEY_F2, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F2, X68K_CTRL_NONE, }, // F2 + { PS2_KEY_F3, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F3, X68K_CTRL_NONE, }, // F3 + { PS2_KEY_F4, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F4, X68K_CTRL_NONE, }, // F4 + { PS2_KEY_F5, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F5, X68K_CTRL_NONE, }, // F5 + { PS2_KEY_F6, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F6, X68K_CTRL_NONE, }, // F6 + { PS2_KEY_F7, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F7, X68K_CTRL_NONE, }, // F7 + { PS2_KEY_F8, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F8, X68K_CTRL_NONE, }, // F8 + { PS2_KEY_F9, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F9, X68K_CTRL_NONE, }, // F9 + { PS2_KEY_F10, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F10, X68K_CTRL_NONE, }, // F10 + { PS2_KEY_F11, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_OPT_1, X68K_CTRL_NONE, }, // F11 - OPT.1 + { PS2_KEY_F12, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_OPT_2, X68K_CTRL_NONE, }, // F12 - OPT.2 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X68K Data X68K Ctrl (Flags to Set). + // ALPHA keys, case is maaped in the X68000 via the SHIFT key event or CAPS key. + { PS2_KEY_A, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_A, X68K_CTRL_NONE, }, // A + { PS2_KEY_B, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_B, X68K_CTRL_NONE, }, // B + { PS2_KEY_C, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_C, X68K_CTRL_NONE, }, // C + { PS2_KEY_D, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_D, X68K_CTRL_NONE, }, // D + { PS2_KEY_E, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_E, X68K_CTRL_NONE, }, // E + { PS2_KEY_F, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_F, X68K_CTRL_NONE, }, // F + { PS2_KEY_G, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_G, X68K_CTRL_NONE, }, // G + { PS2_KEY_H, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_H, X68K_CTRL_NONE, }, // H + { PS2_KEY_I, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_I, X68K_CTRL_NONE, }, // I + { PS2_KEY_J, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_J, X68K_CTRL_NONE, }, // J + { PS2_KEY_K, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_K, X68K_CTRL_NONE, }, // K + { PS2_KEY_L, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_L, X68K_CTRL_NONE, }, // L + { PS2_KEY_M, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_M, X68K_CTRL_NONE, }, // M + { PS2_KEY_N, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_N, X68K_CTRL_NONE, }, // N + { PS2_KEY_O, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_O, X68K_CTRL_NONE, }, // O + { PS2_KEY_P, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_P, X68K_CTRL_NONE, }, // P + { PS2_KEY_Q, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_Q, X68K_CTRL_NONE, }, // Q + { PS2_KEY_R, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_R, X68K_CTRL_NONE, }, // R + { PS2_KEY_S, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_S, X68K_CTRL_NONE, }, // S + { PS2_KEY_T, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_T, X68K_CTRL_NONE, }, // T + { PS2_KEY_U, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_U, X68K_CTRL_NONE, }, // U + { PS2_KEY_V, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_V, X68K_CTRL_NONE, }, // V + { PS2_KEY_W, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_W, X68K_CTRL_NONE, }, // W + { PS2_KEY_X, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_X, X68K_CTRL_NONE, }, // X + { PS2_KEY_Y, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_Y, X68K_CTRL_NONE, }, // Y + { PS2_KEY_Z, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_Z, X68K_CTRL_NONE, }, // Z + // Numeric keys. + { PS2_KEY_0, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_9, X68K_CTRL_NONE, }, // Close Bracket ) + { PS2_KEY_0, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_0, X68K_CTRL_NONE, }, // 0 + { PS2_KEY_1, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_1, X68K_CTRL_NONE, }, // 1 + { PS2_KEY_2, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_2, X68K_CTRL_NONE, }, // 2 + { PS2_KEY_3, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_3, X68K_CTRL_NONE, }, // 3 + { PS2_KEY_4, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_4, X68K_CTRL_NONE, }, // 4 + { PS2_KEY_5, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_5, X68K_CTRL_NONE, }, // 5 + { PS2_KEY_6, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CIRCUMFLEX, X68K_CTRL_RELEASESHIFT, }, // Circumflex ^ + { PS2_KEY_6, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_6, X68K_CTRL_NONE, }, // 6 + { PS2_KEY_7, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_6, X68K_CTRL_NONE, }, // & + { PS2_KEY_7, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_7, X68K_CTRL_NONE, }, // 7 + { PS2_KEY_8, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_COLON, X68K_CTRL_NONE, }, // Start * + { PS2_KEY_8, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_8, X68K_CTRL_NONE, }, // 8 + { PS2_KEY_9, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_8, X68K_CTRL_NONE, }, // Open Bracket ( + { PS2_KEY_9, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_9, X68K_CTRL_NONE, }, // 9 + //PS2 Code PS2 Ctrl (Flags to Match) Machine X68K Data X68K Ctrl (Flags to Set). + // Punctuation keys. + { PS2_KEY_SPACE, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_SPACE, X68K_CTRL_NONE, }, // Space + { PS2_KEY_MINUS, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CIRCUMFLEX, X68K_CTRL_NONE, }, // Upper Bar + { PS2_KEY_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_MINUS, X68K_CTRL_NONE, }, // Minus - + { PS2_KEY_EQUAL, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_SEMI, X68K_CTRL_SHIFT, }, // Plus + + { PS2_KEY_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_MINUS, X68K_CTRL_SHIFT, }, // Equal = + { PS2_KEY_DOT, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_DOT, X68K_CTRL_NONE, }, // Greater Than > + { PS2_KEY_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_DOT, X68K_CTRL_NONE, }, // Dot + { PS2_KEY_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_DIV, X68K_CTRL_NONE, }, // Divide / + { PS2_KEY_SEMI, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_COLON, X68K_CTRL_RELEASESHIFT, }, // Colon : + { PS2_KEY_SEMI, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_SEMI, X68K_CTRL_NONE, }, // Semi-Colon ; + { PS2_KEY_OPEN_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_OPEN_SQ, X68K_CTRL_NONE, }, // [ + { PS2_KEY_CLOSE_SQ, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CLOSE_SQ, X68K_CTRL_NONE, }, // ] + { PS2_KEY_APOS, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_AT, X68K_CTRL_RELEASESHIFT, }, // @ + { PS2_KEY_APOS, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_7, X68K_CTRL_SHIFT, }, // ' + { PS2_KEY_BACK, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_YEN, X68K_CTRL_NONE, }, // Back slash maps to Yen + { PS2_KEY_BACK, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_YEN, X68K_CTRL_NONE, }, // Back slash maps to Yen + { PS2_KEY_HASH, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_3, X68K_CTRL_SHIFT, }, // Hash + { PS2_KEY_COMMA, PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_COMMA, X68K_CTRL_NONE, }, // Less Than < + { PS2_KEY_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_COMMA, X68K_CTRL_NONE, }, // Comma , + { PS2_KEY_BTICK, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_UNDERLINE, X68K_CTRL_SHIFT, }, // Underline + { PS2_KEY_BTICK, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_AT, X68K_CTRL_SHIFT, }, // Back Tick ` + // Control keys. + { PS2_KEY_TAB, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_TAB, X68K_CTRL_NONE, }, // TAB + { PS2_KEY_BS, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_BS, X68K_CTRL_NONE, }, // Backspace + { PS2_KEY_ESC, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_ESC, X68K_CTRL_NONE, }, // ESCape + { PS2_KEY_INSERT, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_INS, X68K_CTRL_NONE, }, // INSERT + { PS2_KEY_HOME, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CLR, X68K_CTRL_NONE, }, // CLR + { PS2_KEY_HOME, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_HOME, X68K_CTRL_NONE, }, // HOME + { PS2_KEY_DELETE, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_DEL, X68K_CTRL_NONE, }, // DELETE + { PS2_KEY_UP_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_UP_ARROW, X68K_CTRL_NONE, }, // Up Arrow + { PS2_KEY_L_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_L_ARROW, X68K_CTRL_NONE, }, // Left Arrow + { PS2_KEY_DN_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_DN_ARROW, X68K_CTRL_NONE, }, // Down Arrow + { PS2_KEY_R_ARROW, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_R_ARROW, X68K_CTRL_NONE, }, // Right Arrow + { PS2_KEY_PGUP, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_ROLLUP, X68K_CTRL_NONE, }, // Roll Up. + { PS2_KEY_PGDN, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_ROLLDN, X68K_CTRL_NONE, }, // Roll Down + { PS2_KEY_SCROLL, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, ' ', X68K_CTRL_NONE, }, // Not assigned. + { PS2_KEY_ENTER, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_RETURN, X68K_CTRL_NONE, }, // Not assigned. + { PS2_KEY_CAPS, PS2CTRL_CAPS, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CAPS, X68K_CTRL_NONE, }, // CAPS + { PS2_KEY_END, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_UNDO, X68K_CTRL_NONE, }, // UNDO + // Keypad. + { PS2_KEY_KP0, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP0, X68K_CTRL_NONE, }, // Keypad 0 + { PS2_KEY_KP1, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP1, X68K_CTRL_NONE, }, // Keypad 1 + { PS2_KEY_KP2, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP2, X68K_CTRL_NONE, }, // Keypad 2 + { PS2_KEY_KP3, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP3, X68K_CTRL_NONE, }, // Keypad 3 + { PS2_KEY_KP4, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP4, X68K_CTRL_NONE, }, // Keypad 4 + { PS2_KEY_KP5, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP5, X68K_CTRL_NONE, }, // Keypad 5 + { PS2_KEY_KP6, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP6, X68K_CTRL_NONE, }, // Keypad 6 + { PS2_KEY_KP7, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP7, X68K_CTRL_NONE, }, // Keypad 7 + { PS2_KEY_KP8, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP8, X68K_CTRL_NONE, }, // Keypad 8 + { PS2_KEY_KP9, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP9, X68K_CTRL_NONE, }, // Keypad 9 + { PS2_KEY_KP_COMMA, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_COMMA, X68K_CTRL_NONE, }, // Keypad Comma , + { PS2_KEY_KP_DOT, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_DOT, X68K_CTRL_NONE, }, // Keypad Full stop . + { PS2_KEY_KP_PLUS, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_PLUS, X68K_CTRL_NONE, }, // Keypad Plus + + { PS2_KEY_KP_MINUS, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_MINUS, X68K_CTRL_NONE, }, // Keypad Minus - + { PS2_KEY_KP_TIMES, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_TIMES, X68K_CTRL_NONE, }, // Keypad Times * + { PS2_KEY_KP_DIV, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_DIV, X68K_CTRL_NONE, }, // Keypad Divide / + { PS2_KEY_KP_EQUAL, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_MINUS, X68K_CTRL_SHIFT, }, // Keypad Equal = + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_ENTER, X68K_CTRL_NONE, }, // Keypad Ebter / + { PS2_KEY_KP_ENTER, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_KP_EQUAL, X68K_CTRL_NONE, }, // Keypad Ebter / + //PS2 Code PS2 Ctrl (Flags to Match) Machine X68K Data X68K Ctrl (Flags to Set). + // Special keys. + { PS2_KEY_PRTSCR, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, 0x00, X68K_CTRL_NONE, }, // + { PS2_KEY_PAUSE, PS2CTRL_FUNC | PS2CTRL_SHIFT, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_BREAK, X68K_CTRL_RELEASESHIFT, }, // BREAK KEY + { PS2_KEY_L_GUI, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_XF1, X68K_CTRL_NONE, }, // XF1 + { PS2_KEY_L_ALT, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_XF2, X68K_CTRL_NONE, }, // XF2 + { PS2_KEY_R_ALT, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_XF3, X68K_CTRL_NONE, }, // XF3 + { PS2_KEY_R_GUI, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_XF4, X68K_CTRL_NONE, }, // XF4 + { PS2_KEY_MENU, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_XF5, X68K_CTRL_NONE, }, // XF5 + // Modifiers are last, only being selected if an earlier match isnt made. + { PS2_KEY_L_SHIFT, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_SHIFT, X68K_CTRL_NONE, }, // + { PS2_KEY_R_SHIFT, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_SHIFT, X68K_CTRL_NONE, }, // + { PS2_KEY_L_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CTRL, X68K_CTRL_NONE, }, // Map to Control + { PS2_KEY_R_CTRL, PS2CTRL_FUNC, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_CTRL, X68K_CTRL_NONE, }, // Map to Control + { 0, PS2CTRL_NONE, KEYMAP_STANDARD, X68K_ALL, X68K_KEY_NULL, X68K_CTRL_NONE, }, // + }}; +}; + +#endif // X68K_H diff --git a/webserver/css/bootstrap.min.css.gz b/webserver/css/bootstrap.min.css.gz deleted file mode 120000 index 8c999ad..0000000 --- a/webserver/css/bootstrap.min.css.gz +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/css/bootstrap.min.css.gz \ No newline at end of file diff --git a/webserver/css/bootstrap.min.css.gz b/webserver/css/bootstrap.min.css.gz new file mode 100644 index 0000000..f519656 Binary files /dev/null and b/webserver/css/bootstrap.min.css.gz differ diff --git a/webserver/css/jquery.edittable.min.css b/webserver/css/jquery.edittable.min.css deleted file mode 120000 index cdf68cf..0000000 --- a/webserver/css/jquery.edittable.min.css +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/css/jquery.edittable.min.css \ No newline at end of file diff --git a/webserver/css/jquery.edittable.min.css b/webserver/css/jquery.edittable.min.css new file mode 100644 index 0000000..b77cb44 --- /dev/null +++ b/webserver/css/jquery.edittable.min.css @@ -0,0 +1,18 @@ +table.inputtable{width:100%;border:1px solid #ddd;border-collapse:collapse;border-spacing:0;-moz-box-shadow:0 1px 3px rgba(0,0,0,.075);-webkit-box-shadow:0 1px 3px rgba(0,0,0,.075);box-shadow:0 1px 3px rgba(0,0,0,.075);margin:15px 0} +table.inputtable a.icon-button{background-color:#ccc;display:inline-block;width:18px;height:18px;text-decoration:none;color:#fff;font-weight:800;line-height:16px;text-align:center;font-size:14px;-moz-border-radius:1px;-webkit-border-radius:1px;border-radius:1px;-moz-box-shadow:0 0 1px rgba(0,0,0,0.2);-webkit-box-shadow:0 0 1px rgba(0,0,0,0.2);box-shadow:0 0 1px rgba(0,0,0,0.2)} +table.inputtable a.icon-button.addcol,table.inputtable a.icon-button.addrow{background-color:#81b71a} +table.inputtable a.icon-button.delcol,table.inputtable a.icon-button.delrow{background-color:#db4a39} +table.inputtable a.icon-button.disabled{background-color:#eee} +table.inputtable td:last-child,table.inputtable th:last-child{width:54px;border:1px solid #eee;border-right:thick;} +table.inputtable td,table.inputtable th{border:1px solid #eee;text-align:center;height:40px;vertical-align:middle;font-size:14px} +table.inputtable th{background-color:#f1f1f1;border-top:none;border-bottom:2px solid #ddd;border-color:#ddd} +table.inputtable td input[type=text]{border:0;width:90%;height:100%;text-align:center;padding:0 5%} +table.inputtable tr td input:focus{background-color:#fafafa} +table.inputtable.wh tbody tr:nth-child(1),table.inputtable.wh tbody tr:nth-child(1) input{background-color:#fdfdfd;font-weight:800} +table.inputtable th:first-child,table.inputtable td:first-child{border-left:none} +table.inputtable tr:last-child td{border-bottom:none} +@media only screen and max-width 480px { +table.inputtable td,table.inputtable th{min-width:40px;height:80px} +table.inputtable a.icon-button{width:40px;height:40px;font-size:18px;min-width:40px;line-height:40px;margin:3px 0} +table.inputtable td input{height:80px} +} diff --git a/webserver/css/sb-admin.css b/webserver/css/sb-admin.css deleted file mode 120000 index bdce139..0000000 --- a/webserver/css/sb-admin.css +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/css/sb-admin.css \ No newline at end of file diff --git a/webserver/css/sb-admin.css b/webserver/css/sb-admin.css new file mode 100644 index 0000000..f371f07 --- /dev/null +++ b/webserver/css/sb-admin.css @@ -0,0 +1,159 @@ +/* +Author: Start Bootstrap - http://startbootstrap.com +'SB Admin' HTML Template by Start Bootstrap + +All Start Bootstrap themes are licensed under Apache 2.0. +For more info and more free Bootstrap 3 HTML themes, visit http://startbootstrap.com! +*/ + +/* ATTN: This is mobile first CSS - to update 786px and up screen width use the media query near the bottom of the document! */ + +/* Global Styles */ + +body { + margin-top: 50px; +} + +#wrapper { + padding-left: 0; +} + +#page-wrapper { + width: 100%; + padding: 5px 15px; +} + +/* Nav Messages */ + +.messages-dropdown .dropdown-menu .message-preview .avatar, +.messages-dropdown .dropdown-menu .message-preview .name, +.messages-dropdown .dropdown-menu .message-preview .message, +.messages-dropdown .dropdown-menu .message-preview .time { + display: block; +} + +.messages-dropdown .dropdown-menu .message-preview .avatar { + float: left; + margin-right: 15px; +} + +.messages-dropdown .dropdown-menu .message-preview .name { + font-weight: bold; +} + +.messages-dropdown .dropdown-menu .message-preview .message { + font-size: 12px; +} + +.messages-dropdown .dropdown-menu .message-preview .time { + font-size: 12px; +} + + +/* Nav Announcements */ + +.announcement-heading { + font-size: 50px; + margin: 0; +} + +.announcement-text { + margin: 0; +} + +/* Table Headers */ + +table.tablesorter thead { + cursor: pointer; +} + +table.tablesorter thead tr th:hover { + background-color: #f5f5f5; +} + +/* Flot Chart Containers */ + +.flot-chart { + display: block; + height: 400px; +} + +.flot-chart-content { + width: 100%; + height: 100%; +} + +/* Edit Below to Customize Widths > 768px */ +@media (min-width:768px) { + + /* Wrappers */ + + #wrapper { + padding-left: 225px; + } + + #page-wrapper { + padding: 15px 25px; + } + + /* Side Nav */ + + .side-nav { + margin-left: -225px; + left: 225px; + width: 225px; + position: fixed; + top: 50px; + height: 100%; + border-radius: 0; + border: none; + background-color: #222222; + overflow-y: auto; + } + + /* Bootstrap Default Overrides - Customized Dropdowns for the Side Nav */ + + .side-nav>li.dropdown>ul.dropdown-menu { + position: relative; + min-width: 225px; + margin: 0; + padding: 0; + border: none; + border-radius: 0; + background-color: transparent; + box-shadow: none; + -webkit-box-shadow: none; + } + + .side-nav>li.dropdown>ul.dropdown-menu>li>a { + color: #999999; + padding: 15px 15px 15px 25px; + } + + .side-nav>li.dropdown>ul.dropdown-menu>li>a:hover, + .side-nav>li.dropdown>ul.dropdown-menu>li>a.active, + .side-nav>li.dropdown>ul.dropdown-menu>li>a:focus { + color: #fff; + background-color: #080808; + } + + .side-nav>li>a { + width: 225px; + } + + .navbar-inverse .navbar-nav>li>a:hover, + .navbar-inverse .navbar-nav>li>a:focus { + background-color: #080808; + } + + /* Nav Messages */ + + .messages-dropdown .dropdown-menu { + min-width: 300px; + } + + .messages-dropdown .dropdown-menu li a { + white-space: normal; + } + +} diff --git a/webserver/css/sharpkey.css b/webserver/css/sharpkey.css deleted file mode 120000 index 331d03e..0000000 --- a/webserver/css/sharpkey.css +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/css/sharpkey.css \ No newline at end of file diff --git a/webserver/css/sharpkey.css b/webserver/css/sharpkey.css new file mode 100644 index 0000000..9bd439e --- /dev/null +++ b/webserver/css/sharpkey.css @@ -0,0 +1,266 @@ +.wm-button { + width: 95px; + height: 25px; + background: blue; + border: none; + vertical-align: top; + margin-left: 25px; + cursor: pointer; + color: white; + transition: 2s; + border-radius: 12px; +} + +.wm-button:disabled { + background: #999; + color: #555; + cursor: not-allowed; +} + +.wm-button:hover { + background-color: #81ecec; + border: 2px solid #00cec9; +} + +input[type="file"] { + display: none; +} + +.firmware-file-upload { + width: 95px; + height: 25px; + background: blue; + border: none; + vertical-align: middle; + text-align: center; + line-height: 25px; + margin-left: 35px; + margin-bottom: 0px; + cursor: pointer; + color: white; + transition: 2s; + border-radius: 12px; +} + +.firmware-file-upload:hover { + background-color: #81ecec; + border: 2px solid #00cec9; +} + + +.keymap-file-upload { + width: 95px; + height: 25px; + background: blue; + border: none; + vertical-align: middle; + text-align: center; + line-height: 25px; + margin-left: 35px; + margin-bottom: 0px; + cursor: pointer; + color: white; + transition: 2s; + border-radius: 12px; +} + +.keymap-file-upload:hover { + background-color: #81ecec; + border: 2px solid #00cec9; +} + +input[type=file]::file-selector-button { + border: 2px solid #6c5ce7; + padding: .2em .4em; + border-radius: .2em; + background-color: #a29bfe; + transition: 1s; +} + +input[type=file]::file-selector-button:hover { + background-color: #81ecec; + border: 2px solid #00cec9; +} + +.sk-modules-table { + border: none; + border-collapse: collapse; +} +.sk-modules-table caption { + padding-bottom: 0.5em; +} +.sk-modules-table th, .sk-modules-table td { + border: none; + padding: 0.2rem 2rem; +} +.sk-modules-table td { + white-space: nowrap; +} +.sk-modules-table th { + font-weight: normal; +} +.sk-modules-table td { + border-style: none; + vertical-align: top; +} +.sk-modules-table th { + padding: 0.2em; + vertical-align: middle; + text-align: center; +} + +.sk-modules-table tbody td:first-child::after { + content: leader(". "); +} + +.sk-client-wifi-config-table { + border: none; + border-collapse: collapse; + width: 1px !important; + table-layout: auto !important; +} +.sk-client-wifi-config-table caption { + padding-bottom: 0.5em; +} +.sk-client-wifi-config-table th, .sk-client-wifi-config-table td { + border: none; + padding: 0.2rem 2rem; + padding-left: 0; + padding-right: 4rem; +} +.sk-client-wifi-config-table td { + white-space: nowrap; + overflow: hidden; + border-style: none; + vertical-align: top; + width: auto !important; +} +.sk-client-wifi-config-table th { + font-weight: normal; + padding: 0.2em; + vertical-align: middle; + text-align: center; + width: auto !important; +} + +#client-wifi-config-area { + overflow-x: scroll; + width: fit-content; +} + +#firmware-revision-area { + overflow-x: scroll; + width: fit-content; +} + +#esp32-partitions-area { + overflow-x: scroll; + width: fit-content; +} + +.sk-partitions-table { + border: none; + border-collapse: collapse; +} +.sk-partitions-table caption { + padding-bottom: 0.5em; +} +.sk-partitions-table th, .sk-partitions-table td { + border: none; + padding: 0.2rem 2rem; +} +.sk-partitions-table td { + white-space: nowrap; + border-style: none; + vertical-align: top; +} +.sk-partitions-table th { + font-weight: normal; + padding: 0.2em; + vertical-align: middle; + text-align: center; +} + +.sk-partitions-table tbody td:first-child::after { + content: leader(". "); +} + +.sk-partitions-table tbody td:nth-child(4) { + text-align: right; +} +.sk-partitions-table tbody td:nth-child(5) { + text-align: right; +} +.sk-partitions-table tbody td:nth-child(6) { + text-align: center; +} +.sk-partitions-table tbody td:nth-child(8) { + text-align: center; +} + +.table-condensed .progress { + margin-bottom: 0 !important; + width: 400px; + display: none; +} + +.table-borderless > tbody > tr > td, +.table-borderless > thead > tr > td, +.table-borderless { + border-bottom: 0; + border-top: 0; + padding: 4px; + padding-top: 2px; + padding-bottom: 0; + white-space: nowrap; +} + +.table-responsive { + border-bottom: 0; + border-top: 0; + border-left: 0; + border-right: 0; + padding: 4px; + padding-top: 2px; + padding-bottom: 0; + white-space: nowrap; + table-layout: auto; +} + +.table { + margin-bottom: 10px; +} + +.justify { + text-align: justify; + text-justify: inter-word; +} + +.keymap { + max-height: 36.2em; + overflow: auto; +} + +.hr_no_margin { + margin-top: 0.2em; + margin-bottom: 0.2em; +} + +fieldset { + overflow: hidden; +} +.radio-mouse { + padding-right: 1em; +} +.radio-mouse label { + float: left; + clear: none; + display: block; + padding: 0px 1em 0px 8px; +} +input[type=radio] .radio-mouse, +input.radio .radio-mouse { + float: left; + clear: none; + margin: 2px 0 0 2px; +} diff --git a/webserver/css/style.css b/webserver/css/style.css deleted file mode 120000 index 4d7d9e2..0000000 --- a/webserver/css/style.css +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/css/style.css \ No newline at end of file diff --git a/webserver/css/style.css b/webserver/css/style.css new file mode 100644 index 0000000..11e8166 --- /dev/null +++ b/webserver/css/style.css @@ -0,0 +1,253 @@ +/* +body { + margin: 0; +} +*/ + +/* +h1 { + text-align: center; + font-family: Tahoma, Arial, sans-serif; + color: #06D85F; + margin: 10px; +} + +*/ +.box { + width: 40%; + margin: 0 auto; + background: rgba(255,255,255,0.2); + padding: 35px; + border: 2px solid #fff; + border-radius: 20px/50px; + background-clip: padding-box; + text-align: center; +} + +.overlay { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.7); + //transition: opacity 500ms; + visibility: hidden; + opacity: 0; +} +.overlay:target { + visibility: visible; + opacity: 1; +} + +.popup { + margin: 70px auto; + padding: 20px; + background: #fff; + border-radius: 5px; + width: 30%; + position: relative; + transition: all 5s ease-in-out; +} + +.popup h2 { + margin-top: 0; + color: #333; + font-family: Tahoma, Arial, sans-serif; +} +.popup .close { + position: absolute; + top: 20px; + right: 30px; + transition: all 200ms; + font-size: 30px; + font-weight: bold; + text-decoration: none; + color: #333; +} +.popup .close:hover { + color: #06D85F; +} +.popup .content { + max-height: 30%; + overflow: auto; +} + +@media screen and (max-width: 700px){ + .box{ + width: 90%; + } + .popup{ + width: 90%; + } +} + +/* +html { + font-family: Arial, Helvetica, sans-serif; + display: inline-block; + text-align: center; +} + +h1 { + font-size: 1.8rem; + color: white; +} + +p { + font-size: 1.1rem; +} +*/ + +.topnav { + overflow: hidden; + background-color: #0A1128; +} + +.content { + padding: 5%; +} + +.card-grid { + max-width: 800px; + margin: 0 auto; + display: grid; + grid-gap: 2rem; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); +} + +.card { + background-color: lightyellow; + box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); +} + +.card-title { + font-size: 1.1rem; + font-weight: bold; + color: #034078 +} +/* +input[type=submit] { + border: none; + color: #FEFCFB; + background-color: #034078; + padding: 15px 15px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 18px; + width: 30%; + margin-right: 10px; + border-radius: 4px; + transition-duration: 0.4s; + } + +input[type=submit]:hover { + background-color: #1282A2; +} + +input[type=text], input[type=number], select { + width: 40%; + padding: 8px 10px; + margin: 12px; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +input[type=url], input[type=number], select { + width: 50%; + padding: 12px 20px; + margin: 18px; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +label { + font-size: 1rem; +} +*/ +.value{ + font-size: 1rem; + color: #1282A2; +} +.state { + font-size: 1rem; + color: #1282A2; +} + +.button { + font-size: 2em; + padding: 3px; + color: #dbd75e; + border: 1px solid #000; + border-radius: 5px; + text-decoration: none; + cursor: pointer; + transition: all 1s ease-out; + + background: #1b06d8; +} +.button:hover { + background: #06D85F; +} + +/* +button { + //border: none; + // color: #FEFCFB; + // padding: 15px 32px; + // text-align: center; + // font-size: 30px; + // width: 100px; + // border-radius: 4px; + transition-duration: 0.4s; +} +.button-on { + // background-color: #034078; +} +.button-on:hover { + // background-color: #1282A2; +} +.button-off { + // background-color: #858585; +} +.button-off:hover { + // background-color: #252524; +} +*/ +#sk-modules-table { + border: solid thin; + border-collapse: collapse; +} +#sk-modules-table caption { + padding-bottom: 0.5em; +} +#sk-modules-table th, +#sk-modules-table td { + border: solid thin; + padding: 0.5rem 2rem; +} +#sk-modules-table td { + white-space: nowrap; +} +#sk-modules-table th { + font-weight: normal; +} +#sk-modules-table td { + border-style: none solid; + vertical-align: top; +} +#sk-modules-table th { + padding: 0.2em; + vertical-align: middle; + text-align: center; +} + +#sk-modules-table tbody td:first-child::after { + content: leader(". "); +} + diff --git a/webserver/css/styles.css b/webserver/css/styles.css deleted file mode 120000 index aec5f86..0000000 --- a/webserver/css/styles.css +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/css/styles.css \ No newline at end of file diff --git a/webserver/css/styles.css b/webserver/css/styles.css new file mode 100644 index 0000000..e362361 --- /dev/null +++ b/webserver/css/styles.css @@ -0,0 +1 @@ +body { padding-bottom: 70px; } diff --git a/webserver/favicon.ico b/webserver/favicon.ico deleted file mode 120000 index 6830c13..0000000 --- a/webserver/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/favicon.ico \ No newline at end of file diff --git a/webserver/favicon.ico b/webserver/favicon.ico new file mode 100644 index 0000000..0babb7d Binary files /dev/null and b/webserver/favicon.ico differ diff --git a/webserver/font-awesome/css/font-awesome.css b/webserver/font-awesome/css/font-awesome.css deleted file mode 120000 index 72ee9bc..0000000 --- a/webserver/font-awesome/css/font-awesome.css +++ /dev/null @@ -1 +0,0 @@ -../../../../sharpkey/webserver/font-awesome/css/font-awesome.css \ No newline at end of file diff --git a/webserver/font-awesome/css/font-awesome.css b/webserver/font-awesome/css/font-awesome.css new file mode 100644 index 0000000..43b43ca --- /dev/null +++ b/webserver/font-awesome/css/font-awesome.css @@ -0,0 +1,2337 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); + src: url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper-pp:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-resistance:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} +.fa-gitlab:before { + content: "\f296"; +} +.fa-wpbeginner:before { + content: "\f297"; +} +.fa-wpforms:before { + content: "\f298"; +} +.fa-envira:before { + content: "\f299"; +} +.fa-universal-access:before { + content: "\f29a"; +} +.fa-wheelchair-alt:before { + content: "\f29b"; +} +.fa-question-circle-o:before { + content: "\f29c"; +} +.fa-blind:before { + content: "\f29d"; +} +.fa-audio-description:before { + content: "\f29e"; +} +.fa-volume-control-phone:before { + content: "\f2a0"; +} +.fa-braille:before { + content: "\f2a1"; +} +.fa-assistive-listening-systems:before { + content: "\f2a2"; +} +.fa-asl-interpreting:before, +.fa-american-sign-language-interpreting:before { + content: "\f2a3"; +} +.fa-deafness:before, +.fa-hard-of-hearing:before, +.fa-deaf:before { + content: "\f2a4"; +} +.fa-glide:before { + content: "\f2a5"; +} +.fa-glide-g:before { + content: "\f2a6"; +} +.fa-signing:before, +.fa-sign-language:before { + content: "\f2a7"; +} +.fa-low-vision:before { + content: "\f2a8"; +} +.fa-viadeo:before { + content: "\f2a9"; +} +.fa-viadeo-square:before { + content: "\f2aa"; +} +.fa-snapchat:before { + content: "\f2ab"; +} +.fa-snapchat-ghost:before { + content: "\f2ac"; +} +.fa-snapchat-square:before { + content: "\f2ad"; +} +.fa-pied-piper:before { + content: "\f2ae"; +} +.fa-first-order:before { + content: "\f2b0"; +} +.fa-yoast:before { + content: "\f2b1"; +} +.fa-themeisle:before { + content: "\f2b2"; +} +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: "\f2b3"; +} +.fa-fa:before, +.fa-font-awesome:before { + content: "\f2b4"; +} +.fa-handshake-o:before { + content: "\f2b5"; +} +.fa-envelope-open:before { + content: "\f2b6"; +} +.fa-envelope-open-o:before { + content: "\f2b7"; +} +.fa-linode:before { + content: "\f2b8"; +} +.fa-address-book:before { + content: "\f2b9"; +} +.fa-address-book-o:before { + content: "\f2ba"; +} +.fa-vcard:before, +.fa-address-card:before { + content: "\f2bb"; +} +.fa-vcard-o:before, +.fa-address-card-o:before { + content: "\f2bc"; +} +.fa-user-circle:before { + content: "\f2bd"; +} +.fa-user-circle-o:before { + content: "\f2be"; +} +.fa-user-o:before { + content: "\f2c0"; +} +.fa-id-badge:before { + content: "\f2c1"; +} +.fa-drivers-license:before, +.fa-id-card:before { + content: "\f2c2"; +} +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: "\f2c3"; +} +.fa-quora:before { + content: "\f2c4"; +} +.fa-free-code-camp:before { + content: "\f2c5"; +} +.fa-telegram:before { + content: "\f2c6"; +} +.fa-thermometer-4:before, +.fa-thermometer:before, +.fa-thermometer-full:before { + content: "\f2c7"; +} +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before { + content: "\f2c8"; +} +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: "\f2c9"; +} +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: "\f2ca"; +} +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: "\f2cb"; +} +.fa-shower:before { + content: "\f2cc"; +} +.fa-bathtub:before, +.fa-s15:before, +.fa-bath:before { + content: "\f2cd"; +} +.fa-podcast:before { + content: "\f2ce"; +} +.fa-window-maximize:before { + content: "\f2d0"; +} +.fa-window-minimize:before { + content: "\f2d1"; +} +.fa-window-restore:before { + content: "\f2d2"; +} +.fa-times-rectangle:before, +.fa-window-close:before { + content: "\f2d3"; +} +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: "\f2d4"; +} +.fa-bandcamp:before { + content: "\f2d5"; +} +.fa-grav:before { + content: "\f2d6"; +} +.fa-etsy:before { + content: "\f2d7"; +} +.fa-imdb:before { + content: "\f2d8"; +} +.fa-ravelry:before { + content: "\f2d9"; +} +.fa-eercast:before { + content: "\f2da"; +} +.fa-microchip:before { + content: "\f2db"; +} +.fa-snowflake-o:before { + content: "\f2dc"; +} +.fa-superpowers:before { + content: "\f2dd"; +} +.fa-wpexplorer:before { + content: "\f2de"; +} +.fa-meetup:before { + content: "\f2e0"; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} diff --git a/webserver/font-awesome/css/font-awesome.min.css b/webserver/font-awesome/css/font-awesome.min.css deleted file mode 120000 index 7a206f2..0000000 --- a/webserver/font-awesome/css/font-awesome.min.css +++ /dev/null @@ -1 +0,0 @@ -../../../../sharpkey/webserver/font-awesome/css/font-awesome.min.css \ No newline at end of file diff --git a/webserver/font-awesome/css/font-awesome.min.css b/webserver/font-awesome/css/font-awesome.min.css new file mode 100644 index 0000000..8912420 --- /dev/null +++ b/webserver/font-awesome/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"} diff --git a/webserver/font-awesome/css/font-awesome.min.css.orig b/webserver/font-awesome/css/font-awesome.min.css.orig deleted file mode 120000 index 26f3a76..0000000 --- a/webserver/font-awesome/css/font-awesome.min.css.orig +++ /dev/null @@ -1 +0,0 @@ -../../../../sharpkey/webserver/font-awesome/css/font-awesome.min.css.orig \ No newline at end of file diff --git a/webserver/font-awesome/css/font-awesome.min.css.orig b/webserver/font-awesome/css/font-awesome.min.css.orig new file mode 100644 index 0000000..8912420 --- /dev/null +++ b/webserver/font-awesome/css/font-awesome.min.css.orig @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"} diff --git a/webserver/font-awesome/fonts/fontawesome-webfont.woff b/webserver/font-awesome/fonts/fontawesome-webfont.woff deleted file mode 120000 index 3ecd29c..0000000 --- a/webserver/font-awesome/fonts/fontawesome-webfont.woff +++ /dev/null @@ -1 +0,0 @@ -../../../../sharpkey/webserver/font-awesome/fonts/fontawesome-webfont.woff \ No newline at end of file diff --git a/webserver/font-awesome/fonts/fontawesome-webfont.woff b/webserver/font-awesome/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/webserver/font-awesome/fonts/fontawesome-webfont.woff differ diff --git a/webserver/index.html b/webserver/index.html deleted file mode 120000 index bb47430..0000000 --- a/webserver/index.html +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/index.html \ No newline at end of file diff --git a/webserver/index.html b/webserver/index.html new file mode 100644 index 0000000..50f3937 --- /dev/null +++ b/webserver/index.html @@ -0,0 +1,166 @@ + + + + + + + + + Dashboard - SharpKey Admin + + + + + + + + + + + + +
+ + + + + + + + diff --git a/webserver/js/140medley.min.js b/webserver/js/140medley.min.js deleted file mode 120000 index c81a78b..0000000 --- a/webserver/js/140medley.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/140medley.min.js \ No newline at end of file diff --git a/webserver/js/140medley.min.js b/webserver/js/140medley.min.js new file mode 100644 index 0000000..d1495d1 --- /dev/null +++ b/webserver/js/140medley.min.js @@ -0,0 +1,2 @@ +var t=function(a,b){return function(c,d){return a.replace(/#{([^}]*)}/g,function(a,f){return Function("x","with(x)return "+f).call(c,d||b||{})})}},s=function(a,b){return b?{get:function(c){return a[c]&&b.parse(a[c])},set:function(c,d){a[c]=b.stringify(d)}}:{}}(this.localStorage||{},JSON),p=function(a,b,c,d){c=c||document;d=c[b="on"+b];a=c[b]=function(e){d=d&&d(e=e||c.event);return(a=a&&b(e))?b:d};c=this},m=function(a,b,c){b=document;c=b.createElement("p");c.innerHTML=a;for(a=b.createDocumentFragment();b= +c.firstChild;)a.appendChild(b);return a},$=function(a,b){a=a.match(/^(\W)?(.*)/);return(b||document)["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2])},j=function(a){for(a=0;a<4;a++)try{return a?new ActiveXObject([,"Msxml2","Msxml3","Microsoft"][a]+".XMLHTTP"):new XMLHttpRequest}catch(b){}}; diff --git a/webserver/js/bootstrap.min.js.gz b/webserver/js/bootstrap.min.js.gz deleted file mode 120000 index 399e6b3..0000000 --- a/webserver/js/bootstrap.min.js.gz +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/bootstrap.min.js.gz \ No newline at end of file diff --git a/webserver/js/bootstrap.min.js.gz b/webserver/js/bootstrap.min.js.gz new file mode 100644 index 0000000..99c5170 Binary files /dev/null and b/webserver/js/bootstrap.min.js.gz differ diff --git a/webserver/js/index.js b/webserver/js/index.js deleted file mode 120000 index f1d4f19..0000000 --- a/webserver/js/index.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/index.js \ No newline at end of file diff --git a/webserver/js/index.js b/webserver/js/index.js new file mode 100644 index 0000000..778eeab --- /dev/null +++ b/webserver/js/index.js @@ -0,0 +1,66 @@ +function showIPConfig() +{ + if(document.getElementById("wifiCfg0checked")) + { + document.getElementById("wifiCfg0checked").style.display = 'compact'; + document.getElementById("wifiCfg").style.display = 'none'; + + if(document.getElementById("wifiCfg3checked")) + { + document.getElementById("wifiCfg3checked").style.display = 'compact'; + document.getElementById("wifiCfg3").style.display = 'none'; + } else + { + document.getElementById("wifiCfg3checked").style.display = 'none'; + document.getElementById("wifiCfg3").style.display = 'compact'; + } + + if(document.getElementById("wifiCfg1checked")) + { + document.getElementById("wifiCfg1checked").style.display = 'compact'; + } else + { + document.getElementById("wifiCfg1").style.display = 'none'; + } + } else + { + document.getElementById("wifiCfg0").style.display = 'none'; + document.getElementById("wifiCfgchecked").style.display = 'compact'; + } +} + +// Method to enable the correct side-bar menu for the underlying host interface. +function enableIfConfig() +{ + // Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when + // no host was detected to invoke a host specific sub-class. + if(activeInterface === "KeyInterface ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + } + // Mouse interface active? + else if(activeInterface === "Mouse ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("keyMapAvailable").style.display = 'compact'; + + // Secondary interface available? + if(secondaryInterface == "Mouse ") + { + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("mouseCfgAvailable").style.display = 'none'; + } + } +} + +// On document load, setup the items viewable on the page according to set values. +document.addEventListener("DOMContentLoaded", function setPageDefaults() +{ + showIPConfig(); + enableIfConfig(); +}); diff --git a/webserver/js/jquery.edittable.js b/webserver/js/jquery.edittable.js deleted file mode 120000 index b4a7091..0000000 --- a/webserver/js/jquery.edittable.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/jquery.edittable.js \ No newline at end of file diff --git a/webserver/js/jquery.edittable.js b/webserver/js/jquery.edittable.js new file mode 100644 index 0000000..4ec702b --- /dev/null +++ b/webserver/js/jquery.edittable.js @@ -0,0 +1,340 @@ +/*! editTable v0.2.0 by Alessandro Benoit */ +(function ($, window, i) { + + 'use strict'; + + $.fn.editTable = function (options) { + + // Settings + var s = $.extend({ + data: [['']], + tableClass: 'inputtable', + jsonData: false, + headerCols: false, + maxRows: 999, + first_row: true, + row_template: false, + field_templates: false, + validate_field: function (col_id, value, col_type, $element) { + return true; + } + }, options), + $el = $(this), + defaultTableContent = '', + $table = $('', { + class: s.tableClass + ((s.first_row) ? ' wh' : ''), + html: defaultTableContent + }), + addRowCallBack = null, + defaultth = '', + colnumber, + rownumber, + reset, + is_validated = true; + + // Increment for IDs + i = i + 1; + + // Build cell + function buildCell(content, type) { + content = (content === 0) ? "0" : (content || ''); + // Custom type + if (type && 'text' !== type){ + var field = s.field_templates[type]; + return ''; + } + // Default + return ''; + } + + // Build row + function buildRow(data, len) { + + var rowcontent = '', b; + + data = data || ''; + + if (!s.row_template) { + // Without row template + for (b = 0; b < (len || data.length); b += 1) { + rowcontent += buildCell(data[b]); + } + } else { + // With row template + for (b = 0; b < s.row_template.length; b += 1) { + // For each field in the row + rowcontent += buildCell(data[b], s.row_template[b]); + } + } + + return $('', { + html: rowcontent + '' + }); + + } + + // Check button status (enable/disabled) + function checkButtons() { + if (colnumber < 2) { + $table.find('.delcol').addClass('disabled'); + } + if (rownumber < 2) { + $table.find('.delrow').addClass('disabled'); + } + if (s.maxRows && rownumber === s.maxRows) { + $table.find('.addrow').addClass('disabled'); + } + } + + // Extension method to change or set the table header. This method should be called before fillTableData. + function setTableHeader(header) { + s.headerCols = header; + } + + // Extension method to change or set the table column types. This method should be called before fillTableData. + function setTableType(types) { + s.row_template = types; + } + + // Fill table with data + function fillTableData(data) { + + var a, crow = Math.min(s.maxRows, data.length); + + // Clear table + $table.html(defaultTableContent); + + // If headers or row_template are set + if (s.headerCols || s.row_template) { + + // Fixed columns + var col = s.headerCols || s.row_template; + + // Table headers + for (a = 0; a < col.length; a += 1) { + var col_title = s.headerCols[a] || ''; + $table.find('thead tr').append(''); + } + $table.find('thead tr').append(''); + + // Table content + for (a = 0; a < crow; a += 1) { + // For each row in data + buildRow(data[a], col.length).appendTo($table.find('tbody')); + } + + } else if ( data[0] ) { + + // Variable columns + for (a = 0; a < data[0].length; a += 1) { + $table.find('thead tr').append(defaultth); + } + + for (a = 0; a < crow; a += 1) { + buildRow(data[a]).appendTo($table.find('tbody')); + } + + } + + // Append missing th + //$table.find('thead tr').append(''); + + // Count rows and columns + colnumber = $table.find('thead th').length - 1; + rownumber = $table.find('tbody tr').length; + + checkButtons(); + } + + // Export data + function exportData() { + var row = 0, data = [], value; + + is_validated = true; + + $table.find('tbody tr').each(function () { + + row += 1; + data[row] = []; + + $(this).find('td:not(:last-child)').each(function (i, v) { + if ( s.row_template && 'text' !== s.row_template[i] ){ + var field = s.field_templates[s.row_template[i]], + el = $(this).find($(field.html).prop('tagName')); + + value = field.getValue(el); + if ( !s.validate_field(i, value, s.row_template[i], el) ){ + is_validated = false; + } + data[row].push(value); + } else { + value = $(this).find('input[type="text"]').val(); + if ( !s.validate_field(i, value, 'text', v) ){ + is_validated = false; + } + data[row].push(value); + } + }); + + }); + + // Remove undefined + data.splice(0, 1); + + return data; + } + + // Fill the table with data from textarea or given properties + if ($el.is('textarea')) { + + try { + reset = JSON.parse($el.val()); + } catch (e) { + reset = s.data; + } + + $el.after($table); + + // If inside a form set the textarea content on submit + if ($table.parents('form').length > 0) { + $table.parents('form').submit(function () { + $el.val(JSON.stringify(exportData())); + }); + } + + } else { + reset = (JSON.parse(s.jsonData) || s.data); + $el.append($table); + } + + fillTableData(reset); + + // Add column + $table.on('click', '.addcol', function () { + + var colid = parseInt($(this).closest('tr').children().index($(this).parent('th')), 10); + + colnumber += 1; + + $table.find('thead tr').find('th:eq(' + colid + ')').after(defaultth); + + $table.find('tbody tr').each(function () { + $(this).find('td:eq(' + colid + ')').after(buildCell()); + }); + + $table.find('.delcol').removeClass('disabled'); + + return false; + }); + + // Remove column + $table.on('click', '.delcol', function () { + + if ($(this).hasClass('disabled')) { + return false; + } + + var colid = parseInt($(this).closest('tr').children().index($(this).parent('th')), 10); + + colnumber -= 1; + + checkButtons(); + + $(this).parent('th').remove(); + + $table.find('tbody tr').each(function () { + $(this).find('td:eq(' + colid + ')').remove(); + }); + + return false; + }); + + // Add row + $table.on('click', '.addrow', function () { + + if ($(this).hasClass('disabled')) { + return false; + } + + rownumber += 1; + + $(this).closest('tr').after(buildRow(0, colnumber)); + + $table.find('.delrow').removeClass('disabled'); + + checkButtons(); + + // Addition, raise callback after row has been added. + if(addRowCallBack) + { + addRowCallBack(rownumber, $(this).closest('tr').next('tr'), $table); + } + + return false; + }); + + // Delete row + $table.on('click', '.delrow', function () { + + if ($(this).hasClass('disabled')) { + return false; + } + + rownumber -= 1; + + checkButtons(); + + $(this).closest('tr').remove(); + + $table.find('.addrow').removeClass('disabled'); + + return false; + }); + + // Select all content on click + $table.on('click', 'input', function () { + $(this).select(); + }); + + // Return functions + return { + // Get an array of data + getData: function () { + return exportData(); + }, + // Get the JSON rappresentation of data + getJsonData: function () { + return JSON.stringify(exportData()); + }, + // Load an array of data + loadData: function (data) { + reset = data; + fillTableData(data); + }, + // Load a JSON rappresentation of data + loadJsonData: function (data) { + reset = JSON.parse(data); + fillTableData(JSON.parse(data)); + }, + // Extension method to change or set the table header. This method should be called before fillTableData. + setHeaders: function (headers) { + setTableHeader(headers); + }, + // Extension method to change or set the table column types. This method should be called before fillTableData. + setTypes: function (types) { + setTableType(types); + }, + // Reset data to the first instance + reset: function () { + fillTableData(reset); + }, + isValidated: function () { + return is_validated; + }, + addRowCallBack: function (func) { + addRowCallBack = func; + } + }; + }; + +})(jQuery, this, 0); diff --git a/webserver/js/jquery.edittable.min.js b/webserver/js/jquery.edittable.min.js deleted file mode 120000 index 0c2173e..0000000 --- a/webserver/js/jquery.edittable.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/jquery.edittable.min.js \ No newline at end of file diff --git a/webserver/js/jquery.edittable.min.js b/webserver/js/jquery.edittable.min.js new file mode 100644 index 0000000..c2527ec --- /dev/null +++ b/webserver/js/jquery.edittable.min.js @@ -0,0 +1,2 @@ +/*! editTable v0.2.0 by Alessandro Benoit */ +(function(e,t,n){"use strict";e.fn.editTable=function(t){function p(e,t){e=e===0?"0":e||"";if(t&&"text"!==t){var n=r.field_templates[t];return""}return''}function d(t,n){var i="",s;t=t||"";if(!r.row_template){for(s=0;s<(n||t.length);s+=1){i+=p(t[s])}}else{for(s=0;s",{html:i+''})}function v(){if(f<2){u.find(".delcol").addClass("disabled")}if(l<2){u.find(".delrow").addClass("disabled")}if(r.maxRows&&l===r.maxRows){u.find(".addrow").addClass("disabled")}}function m(e){var t,n=Math.min(r.maxRows,e.length);u.html(o);if(r.headerCols||r.row_template){var i=r.headerCols||r.row_template;for(t=0;t"+s+"")}for(t=0;t");f=u.find("thead th").length-1;l=u.find("tbody tr").length;v()}function g(){var t=0,n=[],i;h=true;u.find("tbody tr").each(function(){t+=1;n[t]=[];e(this).find("td:not(:last-child)").each(function(s,o){if(r.row_template&&"text"!==r.row_template[s]){var u=r.field_templates[r.row_template[s]],a=e(this).find(e(u.html).prop("tagName"));i=u.getValue(a);if(!r.validate_field(s,i,r.row_template[s],a)){h=false}n[t].push(i)}else{i=e(this).find('input[type="text"]').val();if(!r.validate_field(s,i,"text",o)){h=false}n[t].push(i)}})});n.splice(0,1);return n}var r=e.extend({data:[[""]],tableClass:"inputtable",jsonData:false,headerCols:false,maxRows:999,first_row:true,row_template:false,field_templates:false,validate_field:function(e,t,n,r){return true}},t),s=e(this),o="",u=e("
' + field.setValue(field.html, content)[0].outerHTML + '
/ ' + col_title + '"+n.setValue(n.html,e)[0].outerHTML+"+ -
",{"class":r.tableClass+(r.first_row?" wh":""),html:o}),a='',f,l,c,h=true;n=n+1;if(s.is("textarea")){try{c=JSON.parse(s.val())}catch(y){c=r.data}s.after(u);if(u.parents("form").length>0){u.parents("form").submit(function(){s.val(JSON.stringify(g()))})}}else{c=JSON.parse(r.jsonData)||r.data;s.append(u)}m(c);u.on("click",".addcol",function(){var t=parseInt(e(this).closest("tr").children().index(e(this).parent("th")),10);f+=1;u.find("thead tr").find("th:eq("+t+")").after(a);u.find("tbody tr").each(function(){e(this).find("td:eq("+t+")").after(p())});u.find(".delcol").removeClass("disabled");return false});u.on("click",".delcol",function(){if(e(this).hasClass("disabled")){return false}var t=parseInt(e(this).closest("tr").children().index(e(this).parent("th")),10);f-=1;v();e(this).parent("th").remove();u.find("tbody tr").each(function(){e(this).find("td:eq("+t+")").remove()});return false});u.on("click",".addrow",function(){if(e(this).hasClass("disabled")){return false}l+=1;e(this).closest("tr").after(d(0,f));u.find(".delrow").removeClass("disabled");v();return false});u.on("click",".delrow",function(){if(e(this).hasClass("disabled")){return false}l-=1;v();e(this).closest("tr").remove();u.find(".addrow").removeClass("disabled");return false});u.on("click","input",function(){e(this).select()});return{getData:function(){return g()},getJsonData:function(){return JSON.stringify(g())},loadData:function(e){m(e)},loadJsonData:function(e){m(JSON.parse(e))},reset:function(){m(c)},isValidated:function(){return h}}}})(jQuery,this,0) \ No newline at end of file diff --git a/webserver/js/jquery.min.js.gz b/webserver/js/jquery.min.js.gz deleted file mode 120000 index 53701da..0000000 --- a/webserver/js/jquery.min.js.gz +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/jquery.min.js.gz \ No newline at end of file diff --git a/webserver/js/jquery.min.js.gz b/webserver/js/jquery.min.js.gz new file mode 100644 index 0000000..ccc6d93 Binary files /dev/null and b/webserver/js/jquery.min.js.gz differ diff --git a/webserver/js/keymap.js b/webserver/js/keymap.js deleted file mode 120000 index 62e1966..0000000 --- a/webserver/js/keymap.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/keymap.js \ No newline at end of file diff --git a/webserver/js/keymap.js b/webserver/js/keymap.js new file mode 100644 index 0000000..9533428 --- /dev/null +++ b/webserver/js/keymap.js @@ -0,0 +1,653 @@ +// Method to display a message in a message field. The existing html is saved and replaced +// with the new html. After a timeout period the original html is restored. +// +$origMessage = null; +$origId = null; +$msgTimerId = null; +function showMessage(timeout, id, message) +{ + // Is this a new message whilst one is active? + if($origMessage !== null) + { + // Cancel timer and restore original message. + clearTimeout($msgTimerId); + $('#' + $origId).html($origMessage); + } + + // Store original message and Id so that on timer expiry it can be replaced.. + $origMessage = $('#' + id).html(); + $origId = id; + + // Change HTML and set timer to restore it. + $('#' + id).html(message); + $msgTimerId = setTimeout(function(msgFieldId) + { + $('#' + msgFieldId).html($origMessage); + $origMessage = null; + }, timeout, id); +} + +// Ajax download function, from user:leo on stackexchange, customised for this application. +// +function downloadFile() +{ + var req = new XMLHttpRequest(); + var downloadUrl = "/keymap"; + req.open("GET", downloadUrl, true); + req.responseType = "blob"; + + req.onload = function (event) + { + var blob = req.response; + var fileName = null; + var contentType = req.getResponseHeader("content-type"); + + // IE/EDGE seems not returning some response header + if (req.getResponseHeader("content-disposition")) + { + var contentDisposition = req.getResponseHeader("content-disposition"); + fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1); + } else + { + fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1); + } + + if (window.navigator.msSaveOrOpenBlob) + { + // Internet Explorer + window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName); + } else + { + var el = document.getElementById("keymapDownloadLink"); + el.href = window.URL.createObjectURL(blob); + el.download = fileName; + el.click(); + } + }; + showMessage(1000, 'keymapMsg', "

Downloading keymap file, please wait...

"); + req.send(); +} + +// Listener for the Download button click. Once clicked commence download via ajax function. +// +document.getElementById('keymapDownload').onclick = function keymapFileDownload(e) +{ + downloadFile(); +} + + +// Listener for an event change on the INPUT file tag 'keymapUpload'. Once pressed and a filename available, this method +// commences transmission via a POST method to the SharpKey server. +document.getElementById('keymapUpload').onchange = function keymapFileUpload(e) +{ + var fileInput = document.getElementById("keymapUpload").files; + + // Reset the last status. Used to track state change. + lastStatus = 0; + + // Client side checks, no point initiating an upload if the file isnt valid! + if (fileInput.length == 0) + { + alert("No file selected!"); + + // Sane size check, a keymap entry is 6-12 bytes long, so a 1000 rows should be the max size. + } else if (fileInput[0].size > 12*1024) + { + alert("File size must be less than 12KB!"); + } else + { + var xhttp; + if(window.XMLHttpRequest) + { + xhttp = new XMLHttpRequest(); + } else + { + xhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + + // Install listeners to handle state change. + xhttp.onreadystatechange = function() + { + if (xhttp.readyState == 4) + { + lastStatus = xhttp.status; + if (xhttp.status == 200) + { + showMessage(5000, 'keymapMsg', "

Upload complete. Please press Reboot to activate new key map.

"); + } else if (xhttp.status == 500) + { + showMessage(5000, 'keymapMsg', "

Error: " + xhttp.responseText + " - Press Reboot

"); + } + else if (xhttp.status == 0) + { + showMessage(5000, 'keymapMsg', "

Error: Server closed the connection abrubtly, status unknown! - Please retry or press Reboot

"); + } else + { + showMessage(5000, 'keymapMsg', "

Error: " + xhttp.responseText + " - Press Reboot

"); + } + } + }; + showMessage(10000, 'keymapMsg', "

Uploading keymap file, please wait...

"); + xhttp.open("POST", "/keymap", true); + xhttp.send(fileInput[0]); + } +} + + +// Method to enable the correct side-bar menu for the underlying host interface. +function enableIfConfig() +{ + // Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when + // no host was detected to invoke a host specific sub-class. + if(activeInterface === "KeyInterface ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + } + // Mouse interface active? + else if(activeInterface === "Mouse ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("keyMapAvailable").style.display = 'compact'; + + // Secondary interface available? + if(secondaryInterface == "Mouse ") + { + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("mouseCfgAvailable").style.display = 'none'; + } + } +} + +// Method to validate a hex input field. Basically call hexConvert and it will return a +// valid number, 0 if invalid or 255 if out of range. +function validateHexInput(event) +{ + event.target.value=hexConvert(event.target.value, false); + return true; +}; + +// Use variables to remember last popover and value as the popover mechanism, probably due to race states, isnt uniform. +// Also use the shown/hidden events and toggle as hide doesnt trigger reliably. +var $currentPopover = null; +var $currentPopoverVal = -1; +var $currentClass = null; +var $currentSelectCount = 0; +var $currentTimerId = null; +var $currentDataSetModified = false; + +function updateButtons() +{ + if($currentSelectCount == 2) + { + $('#keymapSwap').removeAttr('disabled'); + } else + { + $('#keymapSwap').attr('disabled', 'disabled'); + } + if($currentSelectCount > 0) + { + $('#keymapDelete').removeAttr('disabled'); + } else + { + $('#keymapDelete').attr('disabled', 'disabled'); + } + if($currentDataSetModified == true) + { + $('#keymapSave').removeAttr('disabled'); + $('#keymapReload').removeAttr('disabled'); + } else + { + $('#keymapSave').attr('disabled', 'disabled'); + $('#keymapReload').attr('disabled', 'disabled'); + } +} + +function addPopover(field, tableRow) +{ + + // Popover auto-hide timer alarm. + const popoverAlarm = { + + setup: function(timerId, timeout) { + if (timerId != null) { + timerId = this.cancel(timerId); + } + + timerId = setTimeout(function() { + + if($currentPopover) { + $currentPopover.popover('toggle'); + } + }, timeout); + + return(timerId); + }, + + cancel: function(timerId) { + clearTimeout(timerId); + timerId = null; + return(timerId); + }, + }; + + // Setup popover menus on each custom field to aid with conversion of a feature into a hex number. + $(field).popover({ + html: true, + title: function () { + + return $('#popover-' + field.className).find('.head').html(); + }, + content: function () { + return $('#popover-' + field.className).find('.content').html(); + } + }) + .on("show.bs.popover", function(e) + { + var className = this.getAttribute('class'); + var $target = $(e.target); + + // Detect a class change, user has gone from one column to another, need to close out current + // popover ready for initialisation of new. + if($currentClass != null && $currentClass != className) + { + if($currentPopover) + { + if($currentTimerId != 0) + { + $currentTimerId = popoverAlarm.cancel($currentTimerId); + } + + if ($currentPopover && ($currentPopover.get(0) != $target.get(0))) { + $currentPopover.popover('toggle'); + } + $currentPopover = null; + } + } + $currentClass = className; + + if($currentPopover) + { + $currentPopoverVal = 0; + $('#popover-' + field.className).find('input').each( function( index ) { + + if($(this).attr("checked") === "checked") + { + $currentPopoverVal += parseInt(this.getAttribute('data-value'), 10); + } + }); + } else + { + $currentPopoverVal = -1; + } + + // Go through all the checkboxes and setup the initial value. + $('#popover-' + field.className).find('input').each( function( index ) { + if((e.target.value & this.getAttribute('data-value')) == this.getAttribute('data-value')) + { + $(this).attr('checked', true); + } else + { + $(this).attr('checked', false); + } + }); + }) + .on("shown.bs.popover", function(e) + { + var $target = $(e.target); + + if ($currentPopover && ($currentPopover.get(0) != $target.get(0))) { + $currentPopover.popover('toggle'); + } + $currentPopover = $target; + + // Setup an alarm to remove a visible popover if not clicked closed. + $currentTimerId = popoverAlarm.setup($currentTimerId, 5000); + }) + .on("hidden.bs.popover", function(e) + { + var $target = $(e.target); + + if ($currentPopover && ($currentPopover.get(0) == $target.get(0))) { + if($currentPopoverVal == -1) + { + $currentPopoverVal = 0; + $('#popover-' + field.className).find('input').each( function( index ) { + + if($(this).attr("checked") === "checked") + { + $currentPopoverVal += parseInt(this.getAttribute('data-value'), 10); + } + }); + } + + if(e.target.value != hexConvert($currentPopoverVal, false)) + { + e.target.value = hexConvert($currentPopoverVal, false); + $currentDataSetModified = true; + updateButtons(); + } + $currentPopover = null; + // Cancel the alarm on the hidden popover. + $currentTimerId = popoverAlarm.cancel($currentTimerId); + } + $currentPopoverVal = -1; + }) + + .on("hide.bs.popover", function(e) + { + // $currentTimerId = popoverAlarm.cancel($currentTimerId); + }) + .on("click", function(e) + { + console.log('click: ' + e.target.value); + }) + .on("change", function(e) + { + if(e.target.className === 'checkbox') + { + if($(e.target).attr('checked') == 'checked') + { + $currentSelectCount += 1; + //$(e.target).attr("checked", true); + } + else if($(e.target).attr('checked') != 'checked') + { + $currentSelectCount -= 1; + //$(e.target).attr("checked", false); + } + } else + { + if(e.target.value != hexConvert($currentPopoverVal, false)) + { + e.target.value = hexConvert(e.target.value, false); + $currentDataSetModified = true; + } + } + updateButtons(); + }) + + .blur(function () { + }); + // End popover definition + + // Only attach popover handlers to classes in the first row, subsequent rows will use the same popover. + // + if(tableRow == 1) + { + var search = '#popover-' + field.className; + $(search + ' input').each(function () { + + $('body').on('click', '#' + this.id, function(e) { + + $currentTimerId = popoverAlarm.setup($currentTimerId, 5000); + + $(search + ' input').each(function() { + if(this.id === e.target.id) + { + $(this).attr("checked", e.target.checked ? true : false); + } + }); + }); + }); + } +} + +// Method to convert a string/numeric into a hex number and range check it to be within and 8bit unsigned range. +function hexConvert(inVal, invert) +{ + if(isNaN(inVal) == true && isNaN('0x' + inVal) == false) + { + inVal = parseInt(inVal, 16); + } + else if(isNaN(inVal) == false && typeof inVal == 'string' && inVal.length > 0) + { + if(inVal.toUpperCase().indexOf("0X") == 0) + { + inVal = parseInt(inVal, 16); + } else + { + inVal = parseInt(inVal, 10); + } + } + else if(isNaN(inVal) == true || inVal.length == 0) + { + inVal = 0; + } + if(inVal < 0) + { + inVal = 0; + } + else if(inVal > 255) + { + inVal = 255; + } + if(invert == true) + { + inVal = (~inVal) & 0xFF; + } + return('0x' + (inVal).toString(16).toUpperCase()); +} + +// Method to swap 2 tables rows. This is necessary as the keymap table has top down priority in mapping. +function swapKeyMapData() +{ + // Locals. + var firstRow = null; + var secondRow = null; + + // Get row numbers of the rows to be swapped. + var rowcnt = 1; + $('.inputtable tr .checkbox').each(function() { + if(firstRow == null && this.checked == true) + { + firstRow = rowcnt; + } else + if(firstRow != null && secondRow == null && this.checked == true) + { + secondRow = rowcnt; + } + rowcnt++; + }); + + // If rows were matched (should be due to count) then perform swap. + if(firstRow != null && secondRow != null) + { + $('.inputtable > tbody > tr:nth-child(' + firstRow + ')').replaceWith($('.inputtable > tbody > tr:nth-child(' + secondRow + ')').after($('.inputtable > tbody > tr:nth-child(' + firstRow + ')').clone(true))); + } + + // Update the modified state and update button state. + $currentDataSetModified = true; + updateButtons(); + + return true; +} + +// Method to delete 1 or multiple rows. An individual row can be deleted by the +/- buttons but multiple requires selection and then running through the table, deleting the selected rows. +function deleteKeyMapData() +{ + // Locals. + var toDeleteRows = []; + + // Get row numbers of the rows to be swapped. + var rowcnt = 1; + $('.inputtable tr .checkbox').each(function() { + if(this.checked == true) + { + toDeleteRows.push(rowcnt); + } + rowcnt++; + }); + + // Iterate through the array of rows to delete and actually perform the deletion. + // We work in reverse order, ie. deleting the last row first due to the index changing if we delete first to last. + for(var idx = toDeleteRows.length; idx > 0; idx--) + { + $('.inputtable > tbody > tr:nth-child(' + toDeleteRows[idx-1] + ')').remove(); + } + + // Reset the select counter and update button state. + $currentSelectCount = 0; + $currentDataSetModified = true; + updateButtons(); + + return true; +} + +// Method to save the current keymap table data to the interface. +function saveKeyMapData() +{ + var data = keymapTable.getData(); + + // Reset the last status. Used to track state change. + lastStatus = 0; + + // Client side checks, no point initiating an upload if the file isnt valid! + if(data.length == 0) + { + alert("No data to save!!"); + + } else + { + var xhttp; + if(window.XMLHttpRequest) + { + xhttp = new XMLHttpRequest(); + } else + { + xhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + + // Install listeners to handle state change. + xhttp.onreadystatechange = function() + { + if (xhttp.readyState == 4) + { + lastStatus = xhttp.status; + if (xhttp.status == 200) + { + showMessage(5000, 'keymapEditorMsg', "

Upload complete. Please press Reboot to activate new key map.

"); + + // Reset the data modified flag and update button state. + $currentDataSetModified = false; + updateButtons(); + } else if (xhttp.status == 500) + { + showMessage(5000, 'keymapEditorMsg', "

Error: " + xhttp.responseText + " - Press Reboot

"); + } + else if (xhttp.status == 0) + { + showMessage(5000, 'keymapEditorMsg', "

Error: Server closed the connection abrubtly, status unknown! - Please retry Save

"); + } else + { + showMessage(5000, 'keymapEditorMsg', "

Error: " + xhttp.responseText + " - Press Reboot

"); + } + } + }; + showMessage(5000, 'keymapEditorMsg', "

Uploading keymap data, please wait...

"); + xhttp.open("POST", "/keymap/table", true); + xhttp.send(JSON.stringify(data)); + } + + return true; +} + +// Method to reload the initial table data set. +function reloadKeyMapData() +{ + // Request reload of the initial data. + keymapTable.reset(); + + // Re-install the listeners as the old dataset was deleted. + setupTableListeners(); + + // Complete message. + showMessage(5000, 'keymapEditorMsg', "

Reload complete, data reset to initial values.

"); + + // Reset the select counter and update button state. + $currentSelectCount = 0; + $currentDataSetModified = false; + updateButtons(); + return true; +} + +// Callback function triggered after a row has been added to the edittable. Use the event to add popover listeners to the new fields. +// +function tableRowAdded(row, rowHandle, tableHandle) +{ + $(rowHandle).children().children().each(function() { + if(this.className !== "") + { + addPopover(this, 0); + } + }); +} + +// Method to install listeners on the table data for popover aids. +// +function setupTableListeners() +{ + // Setup a popover on each input field where a modal exists. + $('.inputtable >tbody > tr').each(function (tridx) { + + // We seperate out the Row from the input as we want to watch every input but only setup 1 popover per input type. + $(this).find('input').each(function (inpidx) { + if(this.className !== "") + { + addPopover(this, tridx); + } + }); + }); + // + // Setup a callback on new rows added to table. + keymapTable.addRowCallBack(tableRowAdded); +} + +$(document).ready(function() { + + // Load up the table headers, types and data. This is done with JQuery fetch as it is easier. The SharpKey is synchronous + // but fetch order can vary so we nest the requests to ensure all headers and types are in place before the data. + fetch('/data/keymap/table/headers') + .then((response) => { + return(response.json()); + }) + .then((headers) => { + keymapTable.setHeaders(headers); + + fetch('/data/keymap/table/types') + .then((response) => { + return(response.json()); + }) + .then((types) => { + keymapTable.setTypes(types); + + fetch('/data/keymap/table/data') + .then((response) => { + return(response.json()); + }) + .then((data) => { + keymapTable.loadData(data); + setupTableListeners(); + }); + }); + }); + + // Disable buttons, enabled when user action requires them. + $('#keymapSwap').attr('disabled', 'disabled'); + $('#keymapDelete').attr('disabled', 'disabled'); + $('#keymapSave').attr('disabled', 'disabled'); + $('#keymapReload').attr('disabled', 'disabled'); + + // Add listeners to the buttons. + $('#keymapSwap').on('click', function() { swapKeyMapData(); }); + $('#keymapDelete').on('click', function() { deleteKeyMapData(); }); + $('#keymapSave').on('click', function() { saveKeyMapData(); }); + $('#keymapReload').on('click', function() { reloadKeyMapData(); }); + + // Setup the menu options according to underlying interface. + enableIfConfig(); +}); diff --git a/webserver/js/mouse.js b/webserver/js/mouse.js deleted file mode 120000 index b04dd1c..0000000 --- a/webserver/js/mouse.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/mouse.js \ No newline at end of file diff --git a/webserver/js/mouse.js b/webserver/js/mouse.js new file mode 100644 index 0000000..8c9536e --- /dev/null +++ b/webserver/js/mouse.js @@ -0,0 +1,141 @@ +// Method to display a message in a message field. The existing html is saved and replaced +// with the new html. After a timeout period the original html is restored. +// +$origMessage = null; +$origId = null; +$msgTimerId = null; +function showMessage(timeout, id, message) +{ + // Is this a new message whilst one is active? + if($origMessage !== null) + { + // Cancel timer and restore original message. + clearTimeout($msgTimerId); + $('#' + $origId).html($origMessage); + } + + // Store original message and Id so that on timer expiry it can be replaced.. + $origMessage = $('#' + id).html(); + $origId = id; + + // Change HTML and set timer to restore it. + $('#' + id).html(message); + $msgTimerId = setTimeout(function(msgFieldId) + { + $('#' + msgFieldId).html($origMessage); + $origMessage = null; + }, timeout, id); +} + +// Method to enable the correct side-bar menu for the underlying host interface. +function enableIfConfig() +{ + // Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when + // no host was detected to invoke a host specific sub-class. + if(activeInterface === "KeyInterface ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + } + // Mouse interface active? + else if(activeInterface === "Mouse ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("keyMapAvailable").style.display = 'compact'; + + // Secondary interface available? + if(secondaryInterface == "Mouse ") + { + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("mouseCfgAvailable").style.display = 'none'; + } + } +} + +// Method to convert a string/numeric into a hex number and range check it to be within and 8bit unsigned range. +function hexConvert(inVal, invert) +{ + if(isNaN(inVal) == true && isNaN('0x' + inVal) == false) + { + inVal = parseInt(inVal, 16); + } + else if(isNaN(inVal) == false && typeof inVal == 'string' && inVal.length > 0) + { + if(inVal.toUpperCase().indexOf("0X") == 0) + { + inVal = parseInt(inVal, 16); + } else + { + inVal = parseInt(inVal, 10); + } + } + else if(isNaN(inVal) == true || inVal.length == 0) + { + inVal = 0; + } + if(inVal < 0) + { + inVal = 0; + } + else if(inVal > 255) + { + inVal = 255; + } + if(invert == true) + { + inVal = (~inVal) & 0xFF; + } + return('0x' + (inVal).toString(16).toUpperCase()); +} + +$(document).ready(function() { + + // Setup the menu options according to underlying interface. + enableIfConfig(); + + // AJAX code to post in a controlled manner so that we can receive back an error/success message to the commit. + $("#mouseHostCfgSave").submit( function(e) + { + var form = $(this); + var actionUrl = form.attr('action'); + + // Prevent default submit action, we want to manually submit and be able to receive a response for errors/success. + e.preventDefault(); + + $.ajax( + { + type: "POST", + url: actionUrl, + data: form.serialize(), // serializes the form's elements. + success: function(data) + { + // Show the message then revert back to original text. + showMessage(10000, "mouseHostCfgMsg", data); + } + }); + }); + $("#mousePS2CfgSave").submit( function(e) + { + var form = $(this); + var actionUrl = form.attr('action'); + + // Prevent default submit action, we want to manually submit and be able to receive a response for errors/success. + e.preventDefault(); + + $.ajax( + { + type: "POST", + url: actionUrl, + data: form.serialize(), // serializes the form's elements. + success: function(data) + { + // Show the message then revert back to original text. + showMessage(10000, "mousePS2CfgMsg", data); + } + }); + }); +}); diff --git a/webserver/js/ota.js b/webserver/js/ota.js deleted file mode 120000 index 7086b86..0000000 --- a/webserver/js/ota.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/ota.js \ No newline at end of file diff --git a/webserver/js/ota.js b/webserver/js/ota.js new file mode 100644 index 0000000..522fc25 --- /dev/null +++ b/webserver/js/ota.js @@ -0,0 +1,300 @@ +var lastStatus = 0; + +// Method to display a message in a message field. The existing html is saved and replaced +// with the new html. After a timeout period the original html is restored. +// +$origMessage = null; +$origId = null; +$msgTimerId = null; +function showMessage(timeout, id, message) +{ + // Is this a new message whilst one is active? + if($origMessage !== null) + { + // Cancel timer and restore original message. + clearTimeout($msgTimerId); + $('#' + $origId).html($origMessage); + } + + // Store original message and Id so that on timer expiry it can be replaced.. + $origMessage = $('#' + id).html(); + $origId = id; + + // Change HTML and set timer to restore it. + $('#' + id).html(message); + $msgTimerId = setTimeout(function(msgFieldId) + { + $('#' + msgFieldId).html($origMessage); + $origMessage = null; + }, timeout, id); +} + +// Clear the file input array. +function clearFileInput(ctrl) +{ + try { + ctrl.value = null; + } catch(ex) { } + if (ctrl.value) { + ctrl.parentNode.replaceChild(ctrl.cloneNode(true), ctrl); + } +} + +// Firmware handlers. +document.getElementById('firmwareUpload').onchange = function getFirmwareFileName(e) +{ + var default_path = document.getElementById("firmwareUpload").files[0].name; + + // Put the name of the file into the table cell. + document.getElementById('firmwareName').innerHTML = "=>" + default_path; + document.getElementById('firmwareUpgrade').value = document.getElementById('firmwareUpload').files[0].name; + + // Disable select and enable upgrade/cancel. + document.getElementById('firmwareUploadLabel').style.display = 'none'; + document.getElementById('firmwareUpgrade').disabled = false; + document.getElementById('firmwareUpgrade').style.display = 'block'; + document.getElementById('firmwareCancel').disabled = false; + document.getElementById('firmwareCancel').style.display = 'block'; + document.getElementById('firmwareMsg').innerHTML = "Press Upgrade to upload and flash the firmware into the SharpKey or Cancel to cancel and re-select file."; +} +document.getElementById('firmwareCancel').onclick = function cancelFirmwareUpload(e) +{ + var default_path = document.getElementById("firmwareUpload").files[0].name; + + // Reset the selected filename. + document.getElementById('firmwareName').innerHTML = ""; + document.getElementById('firmwareUpgrade').value = []; + clearFileInput(document.getElementById('firmwareUpload')); + + // Enable select and disable upgrade/cancel. + document.getElementById('firmwareUploadLabel').style.display = 'block'; + document.getElementById('firmwareUpgrade').disabled = true; + document.getElementById('firmwareUpgrade').style.display = 'none'; + document.getElementById('firmwareCancel').disabled = true; + document.getElementById('firmwareCancel').style.display = 'none'; + document.getElementById('firmwareMsg').innerHTML = "Select a firmware image file with which to upgrade the SharpKey Operating System."; +} + +// Firmware upgrade handler. +function firmwareUpdateProgress(e) +{ + if (e.lengthComputable) + { + var percentage = Math.round((e.loaded/e.total)*100); + document.getElementById('firmwareProgressBar').style.width = percentage + '%'; + } + else + { + document.getElementById('firmwareProgressBar').innerHTML = "Unable to compute progress information since the total size is unknown"; + } +} +document.getElementById('firmwareUpgrade').onclick = function firmwareUpload() +{ + var fileInput = document.getElementById("firmwareUpload").files; + + // Reset the last status. Used to track state change. + lastStatus = 0; + + // Client side checks, no point initiating a firmware update if the file isnt valid! + if (fileInput.length == 0) + { + alert("No file selected!"); + } else if (fileInput[0].size > 1664*1024) + { + alert("File size must be less than 1.6MB!"); + } else + { + document.getElementById("firmwareUpload").disabled = true; + document.getElementById("firmwareUploadLabel").style.display = 'none'; + document.getElementById("firmwareUpgrade").disabled = true; + document.getElementById("firmwareUpgrade").style.display = 'none'; + document.getElementById("firmwareCancel").style.display = 'none'; + document.getElementById("firmwareProgress").style.display = 'block'; + + var xhttp; + if(window.XMLHttpRequest) + { + xhttp = new XMLHttpRequest(); + } else + { + xhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + + // Install listeners to + xhttp.upload.addEventListener("progress", firmwareUpdateProgress, false); + xhttp.onreadystatechange = function() + { + if (xhttp.readyState == 4) + { + lastStatus = xhttp.status; + if (xhttp.status == 200) + { + document.getElementById('firmwareMsg').innerHTML = "

Transfer complete, firmware successfully flashed onto the SharpKey. Please press Reboot to activate.

"; + document.getElementById("firmwareProgress").style.display = 'none'; + document.getElementById('firmwareName').style.display = 'none'; + } else if (xhttp.status == 500) + { + document.getElementById('firmwareMsg').innerHTML = "

Error: " + xhttp.responseText + " - Press Reboot

"; + } + else if (xhttp.status == 0) + { + document.getElementById('firmwareMsg').innerHTML = "

Error: Server closed the connection abrubtly, flash status unknown! - Press Reboot

"; + } else + { + document.getElementById('firmwareMsg').innerHTML = "

Error: " + xhttp.responseText + " - Press Reboot

"; + } + } + }; + document.getElementById('firmwareMsg').innerHTML = "

Uploading and flashing the new firmware, please wait...

"; + xhttp.open("POST", "/ota/firmware", true); + xhttp.send(fileInput[0]); + } +} + +// Filepack handlers. +document.getElementById('filepackUpload').onchange = function getFilepackFileName(e) +{ + var default_path = document.getElementById("filepackUpload").files[0].name; + + // Put the name of the file into the table cell. + document.getElementById('filepackName').innerHTML = "=>" + default_path; + document.getElementById('filepackUpgrade').value = document.getElementById('filepackUpload').files[0].name; + + // Disable select and enable upgrade/cancel. + document.getElementById('filepackUploadLabel').style.display = 'none'; + document.getElementById('filepackUpgrade').disabled = false; + document.getElementById('filepackUpgrade').style.display = 'block'; + document.getElementById('filepackCancel').disabled = false; + document.getElementById('filepackCancel').style.display = 'block'; + document.getElementById('filepackWarning').style.display = 'block'; + document.getElementById('filepackMsg').innerHTML = "Press Upgrade to upload and flash the filepack onto the SharpKey filesystem or Cancel to cancel and re-select file."; +} +document.getElementById('filepackCancel').onclick = function cancelFilepackUpload(e) +{ + var default_path = document.getElementById("filepackUpload").files[0].name; + + // Reset the selected filename. + document.getElementById('filepackName').innerHTML = ""; + clearFileInput(document.getElementById('filepackUpload')); + + // Enable select and disable upgrade/cancel. + document.getElementById('filepackUploadLabel').style.display = 'block'; + document.getElementById('filepackUpgrade').disabled = true; + document.getElementById('filepackUpgrade').style.display = 'none'; + document.getElementById('filepackCancel').disabled = true; + document.getElementById('filepackCancel').style.display = 'none'; + document.getElementById('filepackWarning').style.display = 'none'; + document.getElementById('filepackMsg').innerHTML = "Select a filepack image file with which to upgrade the SharpKey filesystem."; +} + +// Filepack upgrade handler. +function filepackUpdateProgress(e) +{ + if (e.lengthComputable) + { + var percentage = Math.round((e.loaded/e.total)*100); + document.getElementById('filepackProgressBar').style.width = percentage + '%'; + } + else + { + document.getElementById('filepackProgressBar').innerHTML = "Unable to compute progress information since the total size is unknown"; + } +} +document.getElementById('filepackUpgrade').onclick = function filepackUpload() +{ + var fileInput = document.getElementById("filepackUpload").files; + + // Reset the last status. Used to track state change. + lastStatus = 0; + + // Client side checks, no point initiating a filepack update if the file isnt valid! + if (fileInput.length == 0) + { + alert("No file selected!"); + } else if (fileInput[0].size > 640*1024) + { + alert("File size must be less than 640K!"); + } else + { + document.getElementById("filepackUpload").disabled = true; + document.getElementById("filepackUploadLabel").style.display = 'none'; + document.getElementById("filepackUpgrade").disabled = true; + document.getElementById("filepackUpgrade").style.display = 'none'; + document.getElementById("filepackCancel").style.display = 'none'; + document.getElementById("filepackProgress").style.display = 'block'; + + var xhttp; + if(window.XMLHttpRequest) + { + xhttp = new XMLHttpRequest(); + } else + { + xhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + + // Install listeners to + xhttp.upload.addEventListener("progress", filepackUpdateProgress, false); + xhttp.onreadystatechange = function() + { + if (xhttp.readyState == 4) + { + lastStatus = xhttp.status; + if (xhttp.status == 200) + { + document.getElementById('filepackMsg').innerHTML = "

Transfer complete, filepack successfully flashed onto SharpKey filesystem. Please press Reboot to activate.

"; + document.getElementById("filepackProgress").style.display = 'none'; + document.getElementById('filepackName').style.display = 'none'; + } else if (xhttp.status == 500) + { + document.getElementById('filepackMsg').innerHTML = "

Error: " + xhttp.responseText + " - Press Reboot

"; + } + else if (xhttp.status == 0) + { + document.getElementById('filepackMsg').innerHTML = "

Error: Server closed the connection abrubtly, flash status unknown! - Press Reboot

"; + } else + { + document.getElementById('filepackMsg').innerHTML = "

Error: " + xhttp.responseText + " - Press Reboot

"; + } + } + }; + document.getElementById('filepackMsg').innerHTML = "

Uploading and flashing the new filepack, please wait...

"; + xhttp.open("POST", "/ota/filepack", true); + xhttp.send(fileInput[0]); + } +} + + +// Method to enable the correct side-bar menu for the underlying host interface. +function enableIfConfig() +{ + // Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when + // no host was detected to invoke a host specific sub-class. + if(activeInterface === "KeyInterface ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + } + // Mouse interface active? + else if(activeInterface === "Mouse ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("keyMapAvailable").style.display = 'compact'; + + // Secondary interface available? + if(secondaryInterface == "Mouse ") + { + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("mouseCfgAvailable").style.display = 'none'; + } + } +} + +// On document load, setup the items viewable on the page according to set values. +document.addEventListener("DOMContentLoaded", function setPageDefaults() +{ + enableIfConfig(); +}); diff --git a/webserver/js/wifimanager.js b/webserver/js/wifimanager.js deleted file mode 120000 index 16fdd44..0000000 --- a/webserver/js/wifimanager.js +++ /dev/null @@ -1 +0,0 @@ -../../../sharpkey/webserver/js/wifimanager.js \ No newline at end of file diff --git a/webserver/js/wifimanager.js b/webserver/js/wifimanager.js new file mode 100644 index 0000000..7927ed3 --- /dev/null +++ b/webserver/js/wifimanager.js @@ -0,0 +1,178 @@ +// Method to display a message in a message field. The existing html is saved and replaced +// with the new html. After a timeout period the original html is restored. +// +$origMessage = null; +$origId = null; +$msgTimerId = null; +function showMessage(timeout, id, message) +{ + // Is this a new message whilst one is active? + if($origMessage !== null) + { + // Cancel timer and restore original message. + clearTimeout($msgTimerId); + $('#' + $origId).html($origMessage); + } + + // Store original message and Id so that on timer expiry it can be replaced.. + $origMessage = $('#' + id).html(); + $origId = id; + + // Change HTML and set timer to restore it. + $('#' + id).html(message); + $msgTimerId = setTimeout(function(msgFieldId) + { + $('#' + msgFieldId).html($origMessage); + $origMessage = null; + }, timeout, id); +} + + +function showWiFiAPInput() +{ + document.getElementById('inputWiFiClient').style.display = 'none'; + document.getElementById('inputWiFiAP').style.display = 'block'; + document.getElementById("errorMsgAP").innerHTML = ""; +} + +function showWiFiClientInput() +{ + document.getElementById('inputWiFiClient').style.display = 'block'; + document.getElementById('inputWiFiAP').style.display = 'none'; + document.getElementById("errorMsgClient").innerHTML = ""; +} + +function hideFixedIPInput() +{ + document.getElementById('rowClientIP').style.display = 'none'; + document.getElementById('rowClientNETMASK').style.display = 'none'; + document.getElementById('rowClientGATEWAY').style.display = 'none'; + document.getElementById('dhcpInput').style.display = 'block'; + document.getElementById("errorMsgClient").innerHTML = ""; +} + +function showFixedIPInput() +{ + document.getElementById('rowClientIP').style.display = 'table-row'; + document.getElementById('rowClientNETMASK').style.display = 'table-row'; + document.getElementById('rowClientGATEWAY').style.display = 'table-row'; + document.getElementById('dhcpInput').style.display = 'none'; + document.getElementById("errorMsgClient").innerHTML = ""; +} + +function showIPConfig() +{ + if(document.getElementById("wifiCfg0checked")) + { + document.getElementById("wifiCfg0checked").style.display = 'compact'; + document.getElementById("wifiCfg").style.display = 'none'; + + if(document.getElementById("wifiCfg3checked")) + { + document.getElementById("wifiCfg3checked").style.display = 'compact'; + document.getElementById("wifiCfg3").style.display = 'none'; + } else + { + document.getElementById("wifiCfg3checked").style.display = 'none'; + document.getElementById("wifiCfg3").style.display = 'compact'; + } + + if(document.getElementById("wifiCfg1checked")) + { + document.getElementById("wifiCfg1checked").style.display = 'compact'; + } else + { + document.getElementById("wifiCfg1").style.display = 'none'; + } + } else + { + document.getElementById("wifiCfg0").style.display = 'none'; + document.getElementById("wifiCfgchecked").style.display = 'compact'; + } +} + + +// Method to enable the correct side-bar menu for the underlying host interface. +function enableIfConfig() +{ + // Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when + // no host was detected to invoke a host specific sub-class. + if(activeInterface === "KeyInterface ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + } + // Mouse interface active? + else if(activeInterface === "Mouse ") + { + document.getElementById("keyMapAvailable").style.display = 'none'; + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("keyMapAvailable").style.display = 'compact'; + + // Secondary interface available? + if(secondaryInterface == "Mouse ") + { + document.getElementById("mouseCfgAvailable").style.display = 'compact'; + } else + { + document.getElementById("mouseCfgAvailable").style.display = 'none'; + } + } +} + +// On document load, setup the items viewable on the page according to set values. +document.addEventListener("DOMContentLoaded", function setPageDefaults() +{ + document.getElementById('wifiModeAccessPoint').onclick = showWiFiAPInput; + document.getElementById('wifiModeClient').onclick = showWiFiClientInput; + document.getElementById('dhcpModeEnabled').onclick = hideFixedIPInput; + document.getElementById('dhcpModeDisabled').onclick = showFixedIPInput; + + // Setup AP/Client display. + if(document.getElementById('wifiModeClient').checked) + { + showWiFiClientInput(); + } else + { + showWiFiAPInput(); + } + if(document.getElementById('dhcpModeEnabled').checked) + { + hideFixedIPInput(); + } else + { + showFixedIPInput(); + } + + // AJAX code to post in a controlled manner so that we can receive back an error/success message to the commit. + $("#wifiman").submit( function(e) + { + var form = $(this); + var actionUrl = form.attr('action'); + + // Prevent default submit action, we want to manually submit and be able to receive a response for errors/success. + e.preventDefault(); + + // Clear message window before making a POST, allows for a new message if one is provided or blank if it isnt. + document.getElementById("errorMsgClient").innerHTML = ""; + document.getElementById("errorMsgAP").innerHTML = ""; + + $.ajax( + { + type: "POST", + url: actionUrl, + data: form.serialize(), // serializes the form's elements. + success: function(data) + { + // JQuery not rendering HTML correcly so revert to DOM. + document.getElementById("errorMsgClient").innerHTML = data; + document.getElementById("errorMsgAP").innerHTML = data; + //form.find('#errorMsg').html(data); + } + }); + }); + + showIPConfig(); + enableIfConfig(); +}); diff --git a/webserver/keymap.html b/webserver/keymap.html deleted file mode 120000 index eba4287..0000000 --- a/webserver/keymap.html +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/keymap.html \ No newline at end of file diff --git a/webserver/keymap.html b/webserver/keymap.html new file mode 100644 index 0000000..3abf816 --- /dev/null +++ b/webserver/keymap.html @@ -0,0 +1,239 @@ + + + + + + + + + Dashboard - SharpKey Admin + + + + + + + + + + + + + + +
+ + + + +
+ +
+
+

%SK_CURRENTIF%KeyMap

+ +
+ +

Configure the keyboard mapping of the interface from PS/2 to %SK_CURRENTIF% via the table below. The priority is top down and a copy can be saved or uploaded to/from your disk for backup or offline editting.

+
+
+
+ + +
+
+
+
+

KeyMap Editor

+
+
+
+%SK_KEYMAPPOPOVER% + +
+
+
+

+

Directly edit the table, click to add a row, to delete, select () 2 rows to Swap or select multiple rows for multi-row Delete.
Press Save to commit changes or Reload to discard changes and reload active i/f map.

+
+
+ -
+ + + + + + + + +
+ + + + + + + +
+ + + + + + + + +
+
+
+
+

Direct KeyMap Access

+
+
+
+
+

The KeyMap for the %SK_CURRENTIF% interface can be directly manipulated via the buttons below. You can download the file for backup or to modify locally then upload to restore or implement changes.

+

Select Upload file (to SharpKey) or Download (to your PC) via the buttons below.

+ + + + + + + +
+ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/webserver/mouse.html b/webserver/mouse.html deleted file mode 120000 index 6b4aa00..0000000 --- a/webserver/mouse.html +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/mouse.html \ No newline at end of file diff --git a/webserver/mouse.html b/webserver/mouse.html new file mode 100644 index 0000000..dc2322e --- /dev/null +++ b/webserver/mouse.html @@ -0,0 +1,167 @@ + + + + + + + + + Dashboard - SharpKey Admin + + + + + + + + + + + + + + +
+ + + + +
+ +
+
+

Mouse Config

+ +
+ +

Configure the Mouse settings in the panel below. Some of the mouse settings can be configured by the mouse wheel, please refer to the documentation for details.

+
+
+
+ + +
+
+
+
+

Mouse Host Configuration

+
+
+
+

Host Scaling

+
+ %SK_MOUSEHOSTSCALING% +
+
+
+

Setup the host side mouse interface parameters. Commit changes by pressing Save.

+
+
+
+ + + + + + +
+ +
+
+
+
+
+
+
+ +
+
+
+
+

Mouse Configuration

+
+
+
+

Mouse Scaling

+
+ %SK_MOUSEPS2SCALING% +
+

Mouse Resolution

+
+ %SK_MOUSEPS2RESOLUTION% +
+

Mouse Sampling Rate

+
+ %SK_MOUSEPS2SAMPLERATE% +
+
+
+

Setup the mouse parameters which are PS/2 settings, Bluetooth mice will use these parameters via internal mapping.
Commit changes by pressing Save.

+
+
+
+ + + + + + +
+ +
+
+ +
+
+
+
+ +
+ + + + + + + + + diff --git a/webserver/ota.html b/webserver/ota.html deleted file mode 120000 index a5f4c13..0000000 --- a/webserver/ota.html +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/ota.html \ No newline at end of file diff --git a/webserver/ota.html b/webserver/ota.html new file mode 100644 index 0000000..5e81850 --- /dev/null +++ b/webserver/ota.html @@ -0,0 +1,238 @@ + + + + + + + + + Dashboard - SharpKey Admin + + + + + + + + + + + + +
+ + + + +
+ +
+
+

Over The Air Update

+ +
+ +

Welcome to the SharpKey OTA Update page.

This page allows you to check current firmware/filepack version information and upgrade them as necessary. Please read the documentation if you need help.

+
+
+
+ +
+
+ +
+
+
+
+

Version Information

+
+
+
+
+ %SK_PRODNAME% v%SK_PRODVERSION% © P.D. Smart, 2018-22

+ Modules
+ %SK_MODULES% + File Pack
+ %SK_FILEPACK% +
+
+
+
+
+
+ +
+
+
+
+

ESP32 Partitions

+
+
+
+
+ + + + + + + + + + + + + + + %SK_PARTITIONS% + +
Partition NameTypeSub-TypeAddressSizeVersionTimestampActive (Running)
+
+
+
+
+
+
+ +
+
+
+
+

Firmware Upload

+
+
+
+
+

Firmware is a binary file containing the latest operating system for the SharpKey interface.

+
+

Select a firmware image file with which to upgrade the SharpKey Operating System.

+
+ + + + + + + + + + +
+ + +
+
+ + + + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+

File Pack Upload

+
+
+
+
+

A filepack is a SharpKey filesystem stored in a binary image file which contains static and template files for the webserver interface.

+ +
+

Select a filepack image file with which to upgrade the SharpKey filesystem.

+
+ + + + + + + + + + +
+ + +
+
+ + + + +
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + + + + + + diff --git a/webserver/version.txt b/webserver/version.txt deleted file mode 120000 index 73e7e0d..0000000 --- a/webserver/version.txt +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/version.txt \ No newline at end of file diff --git a/webserver/version.txt b/webserver/version.txt new file mode 100644 index 0000000..1010473 --- /dev/null +++ b/webserver/version.txt @@ -0,0 +1 @@ +1.02 diff --git a/webserver/wifimanager.html b/webserver/wifimanager.html deleted file mode 120000 index 923d18b..0000000 --- a/webserver/wifimanager.html +++ /dev/null @@ -1 +0,0 @@ -../../sharpkey/webserver/wifimanager.html \ No newline at end of file diff --git a/webserver/wifimanager.html b/webserver/wifimanager.html new file mode 100644 index 0000000..bf4c8e0 --- /dev/null +++ b/webserver/wifimanager.html @@ -0,0 +1,312 @@ + + + + + + + + + Dashboard - SharpKey Admin + + + + + + + + + + + + + +
+ + + + +
+ +
+
+

WiFi Manager

+ +
+ +

Welcome to the SharpKey WiFi Manager page.

This page allows you to configure the SharpKey WiFi Access Point (you connect to the SharpKey) or + Client (SharpKey connects to your network) mode.

+
+
+
+ +
+
+ +
+
+
+
+

WiFi Configuration

+
+
+
+
+ +
+ + + + + + + + + + + + + + +
SSID:%SK_APSSID%
Password:%SK_APPWD%
IP (AP):%SK_CURRENTIP%NETMASK:%SK_CURRENTNM%GATEWAY:%SK_CURRENTGW%
+
+
+ + + + + + + + + + + + + + + + + + + +
SSID:%SK_CLIENTSSID%
DHCP:Enabled
IP (assigned):%SK_CURRENTIP%NETMASK:%SK_CURRENTNM%GATEWAY:%SK_CURRENTGW%
IP (fixed):%SK_CURRENTIP%NETMASK:%SK_CURRENTNM%GATEWAY:%SK_CURRENTGW%
+
+
+
+
+
+
+
+ +
+
+
+
+

Configure WiFi

+
+
+
+
+
+ + + + + + + + +
WiFi Mode: +
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Name of Wifi router to use. +
Password of WiFi router.
DHCP Mode: +
+ + +
+
+
+ + +
+
Static IP address to assign to SharpKey
Netmask for Static IP
Gateway or router IP Address (if needed).
+
+
+

DHCP Mode enabled.
The SharpKey will use the credentials specified above (SSID/Password) to connect to the router and obtain + an IP address, Netmask and Gateway. Use your router admin page to determine the IP address allocated and connect to the SharpKey via http://<assigned IP>

+
+
+
+ %SK_ERRMSG% +
+
+
+ + + + + + +
+
+
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name of this WiFi Access Point. +
Password required by this AP to authenticate clients.
IP address assigned to this Access Point. ie. 192.168.4.1
Netmask of this AP network.
Gateway this AP will assign to a client. Normally same as IP.
+
+
+
+ %SK_ERRMSG% +
+
+
+ + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + + + + + +
+ + + + +
+ +
+
+

Status

+ +
+ +

Welcome to the SharpKey embedded browser based configuration App which enables keyboard map changes, mouse configuration, Over The Air firmware updates and more.
No help text is available as NVRAM is at a premium, so please read the documentation if you need help.

+
+
+
+ +
+
+
+
+

WiFi Configuration

+
+
+
+
+
+ + + + + + + + + + + + + + +
SSID:%SK_APSSID%
Password:%SK_APPWD%
IP (AP):%SK_CURRENTIP%NETMASK:%SK_CURRENTNM%GATEWAY:%SK_CURRENTGW%
+
+
+ + + + + + + + + + + + + + + + + + + +
SSID:%SK_CLIENTSSID%
DHCP:Enabled
IP (assigned):%SK_CURRENTIP%NETMASK:%SK_CURRENTNM%GATEWAY:%SK_CURRENTGW%
IP (fixed):%SK_CURRENTIP%NETMASK:%SK_CURRENTNM%GATEWAY:%SK_CURRENTGW%
+
+
+
+
+
+
+
+ +
+
+
+
+

Version Information

+
+
+
+
+ %SK_PRODNAME% v%SK_PRODVERSION% © P.D. Smart, 2018-22

+ Modules
+ %SK_MODULES% + File Pack
+ %SK_FILEPACK% +
+
+
+
+
+
+ + +
+ +

@uLG~bHM[x6]U+k?k濑nFkv›;>u\KO6z^Ï& ,/1Y6Zb0K$̍ނ jx-Z#jI6؆7eH>;X72w)ZrWƁXc4Ѡ!ot{d&Dud7,@lMZ{٦Sb?4Й\`y!HZ8O,ԏSqn sHX\XiKUO8m&N!D"ڷh(4oU}ss ijRUMkGj<),xdoj4\1~@LCb'a:dOtP+O+'dvRH6O_8_^+[N5Ng9Q/9Zt:dM3 + a~C{87X7qف1U +C"F-KZ6GtivSKzj&3w $}t[YMu"`6'9 V39amO%W_JF!>_eB4%_ȫͩii -Ɠ +t'OƔLqnug`D@EFm&gˠ: b 1kLp %60Mr7ޮrޙV9P jɄw|L[.P{ ''pjaO6irؓ _nƘ lԤ5 tt$_\9)]I9$b"Hv"K0+杽 +$e{{6VA;ǪBfermF6O1.P00ύ}X?d . -Y}0~;$dhg^ќ[B'u$uBTdYSEYs3Xx ek5/iDt^I7ɮo;{odÐ=&de )ԑg^XC/qw^Y_M,O'Ͼ4 [w .ف 57n 6%MWP^}ÇY7a z>"(&*iOy c4Aؒӣ/ƩOg@{%@ 7ʠQ# Y걽X.pֺ­бscOt mnᄮ"Yr{F(n> +Ceؑ&;/I^Irbl^LQh8C`j!t<;bJ%f׭CF $uЇꀐMC*`7+7]~kfχ_:W,iG HMNPS@SlI;Ƈ߆p|8>ȁ_$NӚpnb߇O MϾ,xљTK9kHBځsYFA X;HZG Ҿt} !mN& +́:5$#`cMPDx&X|ڤHl2؇&j #+ +ӊhYC_py'E0+XE|9]Gbs^rN^]I֭#K?s +y',$ 3LZgU +-qg^4o|-\ +lQزBN3|n7tSpU5N5jo 3w瓦!/MhM[U? +&YP,C*d~ ]΋8͎^ʈe(ABx݂iitǒP=ےRj 3ј Hݡxk0C)5`itZc]*]"mTKyrEVQzH(2Ǽ<żMǜ-^ bys᱗0zz{JY=oT 'Ogl珋gW?<5t/{me[.Y`덫%n|'.x% ϖ_ezCwFa^XW@n7oʋÈn*ef6ݛWwϳ%|ϟBv0l`2zswlr!~Kn<+ri};-[E3l]N֫9zˏ?z<[}|-VWWof娓@{+Rn::?0%-aE^߯@n|#Qo#y943Jn'5h\[8YFuMY {iZ[_Zp/;Qu&v,o>aR|sk _>@3H'+ď;OքNͤSݪkJ +_ZR| v[7&_[[;jwC ANE4d2/\>;xjX%z2T+W0sanr?N~?;:AB{'߿0qhϽ ?L 0׿Q~Z7OwO;b[f2cPhiϸ[4!@m mr&P:T qxl eVlSZX,ɧ8uhum(e +(ȒksA,yLC bsJa$\ͮXAmXFBk +aD5هIbP:M  mI` +['A*y +t5-Mu`̞Q)K z01$T6,Hp0|G4P!zRJf{?@T0&?u,GH. D.XV~ ZA<+,h 6w%$.rSMcIhTŦ5jژA#o z`l%SpP6h2XiXILM&Zx+HL2.--`Hzo $砥rW^C伓 DRZc1 $P-9 aW}D.$Ob'1sT{p@,4Ԋ!y1 +@ΑeiKqp3=$ʥ  &/$Hxa[xny+8@Rv +v -h_u!UGuV&Y+!A]œ|ҀWR̈́'`*z[;[I1EvA,ADZ5\ rsV%)hMɄ䘡/VVdU*C`H&OKAW˓ !Pyb&?-m%2шK>BbJjAαCJϓH7N,gbt"JuR\,{x\X9%">o5"kB;$$%?~ѹ^^jGwd׵°t=`q4˶^_FI-eu{ET7/c( &LlZiIq,@D*F^sbgvrAJrq54Z=tF~ل,F3K"v{yrH+UhlX^VR 8"n܀K 7瞏){4 ]';߀ϕ@<*^3iVڎm9sب k&42blyGOFWnJ:lOF;z04>:mI5ea4Y## co\6vXDDZa62YʅVë~(\20>Л(tW'hAcͪ;*GTt #=Q +M+mAbZwuJsU2rq,eW@I7ؗG2y~ GF# 2sc64 x2ZӦM,b0'aY;:)]rs"_Gf)Rz.7 |Y)߃$W̳' M٨Dp5Q i|8y)}Ʀ%A l싶?ݧa[1sٜv"x8Tݤg #ku 2fAs5Oԑ(HtI +SIK`fBZa'iΡה=,Un6ike!}yUR$G R)&=,l3l+ zңƠpҁƍsͤ F{6#2f"碝2*5jdD JhWʮ9G.cTЋ>: +84sdIo_}(<e%#M~`[}$iA{+'l1~QEͨPw5$[w)YУeң,nlsQĸJdgC銸/EkT +=K5CFMK +HY-m̋|0|^3 T7p{XltcW:'>P㣤Us+.a.I\gh޸jŽP-G軎ԢG^YުPaV,s/s'm~  sq!ف\w;ڍȟ܇-Cb`L'oWOtF% +;?eZ@hew +!qc==(ӵh +wf>cυ@<ua.TyDk;XP\ 2 9q&GTڞ٧)= "{ɡjr/_ ~mɷ ŭ#]{f 03N# b0zegLAbY+ցTV UZw[hj!3o^*g1 6)s+/5FlsƅٵLQƀ* ;nb^F/2 WԖL3=#>8JvJ M NwAy`L Ac&vBhQ4aE\&@ƃiT?Mz٤cM5OS׉ʃ0**s:ԧ0[kr#<̍ܟs0E/EI爵uf7tOc1\YEha:YPE;돫RgY⽗kͨ^.Uقu_ e/j}RP2Hs*%"!@Pt#J+%-n7e XߺQvDbܦu5֮kI.˩$mV̓dtW9z; OO ~mnֹTK$HB8[y؋?k>NɌT\i7E(rsGdlfksj6PwU-&g3Fb2 <Н'6 ݙ)#6S]DͲf&B;l+_nn 'w29! sNXf3[QV&U%z5nndɦT|?إ޿U6{31Jr/.n +x߾ X9g,6tb~ilk'mBҎ1?MP\u~Pև4s pe/=j#+=2{d=up~AOGKcӄOvո|AGކWZ%L͔FoSJpcᣍ=F`Y{>ao+$UIDڞ\s@x~C*FvUz+lΩmZ.jz7a13oM@}l}6αkODF*Si[u媥τźƇgkmƲTPCՌPns]K z`?l f0/v& PE#Ak4p>,(#oݩg%2apk:k澱Z0i\G5^e$T0,a@i:Fdk,DC"ɖsOLv#VE몫P~}XS +ATs!~:gF14֏ Fv;Z<͵d9 Ұl_|KVg͔ƪ]bvz#o8A';@*^䩇ꖉ^Rjmdѡ\8 kޥyǶNГ2-tߛ_Y\l8`Z`\3,z/89Ż>WsyJ5Ԣ{#µWd{<*Y^9(0sg_x4j__zc~=(\㎧QtXmtD79- Tv%b(ej|1{\F=h2 0=2ՠux[vؔP. JH..QW wz8k"jic+/7[لz.pt$x@S-yYՖGT*S\U31~g8SwJB!̌!D?(ߨ[;ڮ`ݾcXhiʁ(7|zPC8b&2m6xI{6V}@yA;Թ p`dBص/El bXoFD0Z D!=ȉ4)-e.a2wo;mi89 |`4B0&-o +21^H* y!;2>u? C\ac6k)g='T˴Rz#SQxs$<mS|FR'c{Zaˌʭk`z M@kwL|sgx\h }/*T/zˍ bY:aZbH`բynM8_Y]v`, arEǸwո:+,(ѵ/oX÷];ޫW߷ײoxIHžvX(>K%p!x,A2On|*ȧKYN m +@(`K;fr"5` 8 +]kQtU5/3 P]q\ڭ$^"y"^5ݝrRy\0iYk{&\Zi kj=eaM8EX%1/XW3O]P555f%Q^pZ.TCj&\I + !55׈^'5Mjf "5 ^OiŖ2q7qJRZ&FsRS)tH Q]nR 5|/|S݃OPa+9G nlqBL%/7zGMq + $&ĭ'%gE#<^x#en`D1}o[Y1a+ZDgGe7[Q[uюڢ:¤PG-ǎq)j?'lR]v!9fLGX$a~P 67OGB^EB%sQ){C+DV']NRm)G/Um*!Ի=~?D E|x="5`nFP7Xir}U)e܌p~j ~\<z-fn~.L57ǙOm%B4.2t}5UkƃP\hUg[$c++;'vW.ɳ`dC` +endstream endobj 390 0 obj <>stream +H|WK;ܿw@Aqݳ-1Tu$~}=?@#G"ڈY!$9@y1f` ޗrZ҄}H<ŗI 6i*32ډu[OV)n(Sg]x{;,!|'~51Ӷ=~8YcFzר%0|vIԘdsزB ް^;(Sx-i;X)qLLT ,/ {&\ӈ#u9l_[10I2[N(HgEٻ@+{:Axua+Z DJ#8QEL${$O\((YcMIәfxeκ_S'%:YVDe 3 x+\]ZO|iuveֹ%}G]Q3^nrQkcHEˊG +P(~/lKW&>_H߯=J?ubWf1$+>4"C<70G>Qo)>ه%L򒔈ԻUV:M5x v0DV腴sf:{UT `]UQi&ƅr9{d؎]N7_UQ\3bQ +U* R@EFom[n.E6B,ˉ?Ɛj_W~4gc\;Z_)đny@͆#+$+OX$@UII-Upn 6Tb;uEfj4ў@Nl6Dx6\_/a)8[=nyd^1c Cֿ?@LZ_ꢨ@i]5P%qY`9"{c^Y5fCDOϬDv9ti PI0֬u.81unpw`ݩ߿G7z70b)prvF964!b1jK P=%\Tg iE9PrNd,,?7~k^~A}Dc"}+>ڏ)  h +I?\7ΫB_׬zڌ4 4 uUy"J`o !!NU9&iy!msỲ u5 'IEX"B`+/Nb8k Q~b1DYb8.FxGv+vC kd İ<, *ɒ*Q\k*hw 9 4`c&FbLǂ 6oE{M+f˨:z!}kfk{@.Ui&~#sw\] V<(32h}Y˲VTYa4n;Bń;v;mF[i6q[3̦&WQm_tRZ%f-nD>tk4^:T؛^\AT\$5HǚT~="_^cp4,Y)i E@2<"d+ [Zbh$Io,aoc|\ISJWEL;κmgDmX"Ց4Zt(< +ĥ,Ϲb|4T!a*8k +M^[lWaRNJPh D*)f;E&.~&lÓ~Ȅݝ(a-YmkxXFyWH\c̤~Dr=vUP[Qʹ8F_󨢇cGM(<@{N,Npaٵv u̷u9>ڪpg_@90mo1 8<EM$fIB&qXN@5&ډx@]ՈvRX@^i] +S}HmoYD$K# AjSH)릵i$"J/?y +EGV~fDgBDpv@j0@mz3/U)4/>oi0(F>($fL|OOW@0)-1ifAdhf ]oy` )v_"1X>sp瘄y⍩aodu]̖z 6VmOC=!N(TzB2TT`̆vN72ɃQ]#93>cZh>ǐ@z[4d xܻn=?QޣZZ~l3"cQY14*$W$5:74ʀ[_<$"I98T-u:om9`2gi9T"^w<|9fYXRřłr ʆQ&QB7N&"ihm$秶u׊0Vz}M`^գ3$ Q[ߊ lJOƩz ~R @T?@y̎cT-n=DvF4L68xD;=ʸCj&|sebm>|=YDP' "Ԥ1 }˦By6lSǓY64x,^OYt:|D]]Ekz#K:g)TQI*,[r_4ub7'QZ3CՅZ3}p?}t,El^cFb[n;gyHj#( + 'duXkmWb_2׋j(vl%Z@hR 4%W?tÞqΙl> SO3xwA_ f&^닓QuRuiֽ,q' + +٩Exǣlͽy ߅}+7q274Ӊs2re\?􀘉e1AAl:6*@0*> +0E ^E1$&6,B@uN536T!(U/.RWD YkR`a9`*1 P<C_Q"l D{b2bػ0#(<+Ƭkb⻾U,ǎs AJֹizmZQ4nh;t΅ebўOqѝ. =Ǐ&:ňF)/P+AgMlfqy!xJrID*i$ޜˌI d@PS+`'29esXh%f+ [  \ZZ^5ͳHZ3' ^M(xۙw%j1v2V!: Ч^K&roBچPrݿ4(B@* bm퀦XP|%t~_mY +[?eL|7FfiG`Fl>@<Ӥ#J_J4dx&/4H(4aFY<J_gV#r>f g#gdwo 99#4U=ͲݰFNA+F!L`/9;}KjEqd߉i3cIϋIh{r܉Om1V)Aj))Np&fod{+D (H +`Ϲi*V\)#Zk%O*f4CScHM.mϱcOgo]=:},6z݃mQ^6Ʀ̣*ktO;Kxv*[l:@Ҳ2lVWI\U@Tr<ވ.2b&>nb=96R ~\֞|3k/cֹBBZk<*2K?l%sP{# ʯ'W|_/:eIM]GBEwpq|س3vb_C 3%b@w!?әϷʴ'`^"ro ?aw8ʿ6Aw=92Z#xr7IY`u5|dG`- ڈYcjiTBPsQa*y)fc[J6R00k@{,e܍i~@cn}{U'y\J0")|BvE]5,@Xq&KlF)Yƫtx}63feғ/Ү5ݵy%>M$WuI{O8< @` j{%*3ܧ8RNF|]=. ğ`SAӘX_@";-ShΕ_U _b[a ,Su6v'Ƕ-oA/P=%N3 0FBHuI~Qf cc*Y.nc z<ΣD'Fb*Y9rZwHAv^2\s<^\I}#ϱsɤ~$ loePܓt#\Є3.EpoEpNj5#؃ϝ7b5*t1=b-Q[:4ݪbҩ@#>ګ 48HJ\K`3Sz.UcNj5#?P*ī7R[;~E "JQξZdXW]2 %{Xۺ%6.ى4YPTA}g4y p2RS7`aW .{L?y:cWD͞^Z@$E.B=A;C%ktw^iα {FrnVOV':ɠ~My%(x7h_lrpl!3ͦd"#Wm&ۨ|aR,S1HYVvA^&7|p|/[qԔV5 Yimv11ʴ/&N^{IYTѫi?8[WCqXuy<`'K:j.MFiVjz(`i2==8dINv0WuFe:ߤ/;:DKn~ߘ qjv[6 Ǘ^VB?F)~-㧼ڂҼ+"c)%1h+q٠7&=RF:qwIõ`utHާ{\cٌ>^({ԛș2Yqyl}*wǢEmMڪ( 忦Q5yUݽnA3RGyQVӲ,J",bӶ2Mu^"W?Jmdݴo5Kd:.?_\u讖lKRZAlqvEDedrd$|I+2n5}3Md;Ds ԢkBLW#('Xjj|An[4~}\9|!*1Eڨ9e믍@b -sѱ&p% ɍUxn,繬 c%WݿGtd C<^?"Ivhͨ@^5*aN˖n;ζ C e^}ЂA=;>~F2\ڵT(Yw +9,jf:1_΢@2,jA[.`E,^V}yK(;D3~ DS2t`W몍5NS0n̖Yz+[~ra3x {"[߹o2G۬=*+! dI +Hw[y@r 7;sS|eҐ4 CbaNI}p!Dd왩F|HpH "*S1BPZ9DE6rRLqxe FG^]/փ *?׶s(=ۨnɫ˕tw=6wgѵ35.2h*582ڍu{xu"J)l"%av웮?Icz3Xhkp|{*m%;dlyB8 yvէ[zˀJ̑ D Gǜal`Iwp{%Mk^q 8>E_7*H͍qϟ^Na:rL."_}orP(i8fpgf1`ZCG ㄗW b~zA؞?J\|jN~&z#uͶn{ 1ikY +/Tn@x택(f#C]]Hyc*6ӟȗn!qUZQ^3w]U*"δgp~N+U-o4YCgN%$F2."(M[kre]Î{Ԯӏe+6jZLVM/Ip*[QX,K5@X!U rFI*ClU 0ħy(w͞Q%xm\e'ϾO}ˈBwb8r婆lB<2S-d&2Fc?=EbY2^hx5)}HkSrYIv϶K0x}W.'?9ܴ)!Is +PNYϪȽP;jnъ\2^Kf4Ijnh[O(TiU,8PG*^TE3Ga\({zEKa6Se!EPE u@.E|PY<.۠~O]ݽ]'n|uQR]d|(ϭ%vbyX?XKULe҈[OmM}=ƲƨB1 +n\ڈCE|gROmD^(۫(~.CU8FYct\~#:VW5iPG ^/u)վk#@s=Q_S_uj:F(LmyDL稧+' + H|RH/@Q#Q9ž)! (zi$IK$A'S$׏Hj ]HF([$Q*cP$hQ"y H EGyD2*S#7UWT2x|UY[;U2۩OTO]2G&K$K&!~s^2ÔI?8΁ +*\c* Dwն>`"Y04KjG݇fq! .I,t(jxp$::.3;9$}Rx}h + MAu\ՒIîk`mn1۞oG t +P`[fJv)z2H</e֌N?S|Spiẋ:HPQGʻGy ]%/mmG_&Dj/7I݋M|E$#r5O{4(yZ2Å!exjDFuz+ogrLƌN!?2D&]rŪ x ӵڑ~niH==Y`,1U,Y@FNx̍OI)N_$ )0o{cpA߇ڮB@_oGcC0YM@pVv"sT#"B}MDedY;o,?˒>ǐ4BЕ^ߝKa(2$"bVHWSwj7=GFwy-m5B +K?ѿf}+2m܈> 2@!$ODD1jƢ2nH;)HEf^ܛy2x© 9.=+;{LmکSNj8:?]6s_af+ +<"jIbGX疽&zwՊ5{< +S~mv;RJ8`jb-NN&W!Y.#pѮ*Ut0̚:UoEI oe}UfZLu,y=ʦgޫz|i"yOL#OCƜxYwW7%=l=]o-:s9<_VI|":1Txq? x'^h^v5l0썏ȮIE ` =l*&IA*inhaQHeධ +X6άy_;)o=&.__$U4 fJ0}Hf ϰZS{0vKe}'4ҌP +LD@F$E旭jm򢘫cЗi=&_;&hO.u2&M~bKi0ȭ0 V44H(}֭%bz3ZQى-$0L}.I }9o-(^;$ʁwpR;RiXJq<.# CZ˞?pXW)̹@27^>HbidԀ<˩=aF,D4ɓReN8Y zՙY ŋZRN0LB^B'ۨ‡ve?}mun[CG5\ VAѲ_ %b;0,^i=%- 5-"+w[yMv?7v? 9n.m1NF^Cg,}܏ZRwܲKZG7bnd7"?Uyfr:cu:^XlI4wnP?Pf_P{+.8 +%$AV=56V]oy1`Ń8WAB"ǂٶ:Vdۖz +V@=KtT2&6'z9H/)p{`ʪo_HֽUt ls@t5Dc%b7`ʼ@O)8@Kj˭^";̡qTYY 2OxWR{pÐy@P\9\Ֆ+}$rS$/51#ofkWّuXQj_C}:sIbfd<]DzK3GoL?dch`tO0^jqa̔j&;ؓ*h.uIJJ)=`TėHQ.RraiƏa8C9e +c(DLT'!Eޗ8s MqQM\Pi8a2*؃VZՏΆ2t)?;GѤTJv'37)P"8g.K10vm);j [(} Udv7Cwë7jS?-:}J}{_N45a!trB+"ia=-/eU*qf9gR<_8K;%()5ˠ㐑1bM*= KeY:3">}pcpӘ7hi Y(~=HA|P +rie E;z(\Jqblʩ10d ,7FJp\Qjpol2CTQ4=ʯQ}6b1?OAu2wf=h[_kXc gA0Y'$V4"\Yj胠̴M`1TUKu68XpO|-@E(A]cڽ!٭vM &g (N$M-6eVf@uM37u1(Iwo5l2R { LFN2!F¸8n?ċ@F0톴]nOȍaZOmeVҟrSr1Ɓ !_QڸeqW dֶ6V1vcυ}Lg1F|+-Ua0G'nϼg2gO<|=tfDF|H 4 +R2>ctTN@i V~c=zoqF=cY|2>uOrHm9iZ'NQa7q;Q[!`(Eþ8g/Z!*]ArɼM`dֵq&RV ]r2&G6 Vl0i?0 +k6Ι^V=4'njj{}>o&֏J^"RJ>Z7C]iRuz(5U"F+;u kAZ Gh25A?n1QFfeV|LXgU(cbqwev/8TUیkZ5oGѱ\!p??63dJyLMiHUQN6G25KJ"ere<J^ȕ7J2޾f{f_AWo'NE4Jc͓+o_Eޝr[<#AZvHx<#w2+a2{ѴO驶GƒڝrNI w§^${&<\ivL o!s%D;w›rz,uNy&yK\ <#*PL֕VA0ZLHmjuP +CbczbR|(U:)hC1g>A ףTToꁗ:~KE޻K1,MRk7򎜫\K0J@ͭS A:ȠCH$K\LT KZH ?€ +GHrfM9:|*gC@QEj%H[boIsXKX DP+启Zo,;[hhֶkF9q}+[FK|㡡p +IMhT:lIlZOR!ɔ9يnQe:Y&yoe=vU::L;r(iVHBlBy7TV] a?qވ\eS /u.Ky +)ZGB 7p2q +猸2E(c#䋃$ iD4K-6|1Ҍ +Ys0э-kxܘF>j?t,B4+N;}2;g6bqAZf &xu" xD,hV4=TH21UT> +-#SK|5P.h6N +uu}U悧Iٓ_] z vcPX3Вz`hmAXzVm.Ï‘C[xGKeUj{u~om}ڸaq,]rLmGVWF>ku"Vh?/%wIюߛ52>l"חynhF,9nb<(9M~8USg5,+bO)EBRvqc쵘obY̤ZK G5(G=싹x*hW-5`x[ hg*"ncM4zYx8Γ+Sfܽf 6/.v(FԸ®{jebcG|#?$U[9+xh5}?!E@kfR5<{Q +SI^XfgcKjnX + OBW*cK1BQ,AeWJy?į_;79[t(@ uNuw16I/t!*>uohUg7UpTCgEfiC]cҞGծ9}:|}G|:#i"-f +!`Oďe*yYʪ:gWWPsYXqV/z@ %{P2KQtcE95h}ʎ չU,"9 +W}V>?"`G +7y,MqJtr}GC8~VaDEšhiM=Œ4VC(}Wj?Ƒ+p[9WO]EBFpWrءn|7_1u:6}H2Gɏ򗏏Nr=z}zRSۀ`7{Y$bװZtzS v}K?xM=#tE| @9"LJ6IҒ/3"4MGS@~bע-uEQ9{/+J,q)_^7><膾=w&aU&YWcsm^t+O\ݨUZ\|דq/˷Li0: G;33hy2L)\Owsdh"q~%Nz#LrO~ ]Tz*쪠9;NH0cHk̄=v:# +j W?10k%#y$Qך^B¡ƲwgP d`yă$GЃ@~``BXͳlQYJE_ʳƄ]-~uevZ{cˋg޹ Mhm#k~67 +! XR?pK]BT)>86c5+ βqcj5Vx5-㩪F.ϾowsyvR Hx{S.,] >ʲiP|֛OmA(xYa/)עD;#>۷QL:ȇ-O^ݙ=" Dk^].K$o6VAaWV (,] +eGc&5] +ե(彬erD{aFe-U+k3}rt~(2#fײ>_"3"~Qe`g>ʰTKEmKI7 l~/:7zy# <$|yƒʼ g㙕 {4-9h%ЙyZ4t O:{ntGHgy6C]4{<,6W>d{N$/w%Qdv$3(/ ZvtsD`+m2*21f[8Pʠpjt(3ͦ/*^| =B[](PX'BP?P~Cz/SCC?@@Syd?^\N%WV8ԬY@X0( KsylQr_|ZhU.=.H]0`B87R(eP > 1*5=rLy hK%4Zn~aF[l[vQ~SnS_.M&  +\/+}н1ƛkjc6l 8?W.[xզʙYҊiE~ߊN?"5n旴QDDRؒ k@5{ۇQsjZZ*f14oj_pmXe^]| \/ NPNs1*'Ut~9sp'"+FJBWo\vzQX7'܈cmB QTV޸ʼnQˬ?Mhؖ(/ai76Zke4}Lhk# Tw,<X6>`m*SPz%3gxT?ޘ=$2-Tp|bX<ևnв|6G#UmeR."fGt[ke[oRhuP4+CAC9ŭ2"9-b"J3 +dyeݢNaN=[xp߾9s +G|&b_5A^}yVҽ~lRoVJ 5ck(L^и v&2"! j{uTCeucu{шF3e(DJ8A"@bDgBG?ZM}=Xv)BNhRg N +IHBi J0tG$fy/rBoLW'#\r*1q"D#2U b \4;(JP.JgJ8<(cW+T: Y'b+ +;ψcsżmu[ R ~I.ksk"^DqO .Y 2v8U?s0NfKg3 ʾ154kP@Q#0KZ4e8߮^KFː>Gx%2:#9f,,oGJ_ԫu)E^ȜZsARй9UQe6j b=˭̈]`UBRPNHby+Kdx5Bv-:[^?ajBLg"q +E@䥼{NnBÿh\/»JGmimWiWyi +R ubIt5Du|hAYQr#=fFf+ Q4(QZ]VZE>2aam2z^T=u"}TShڎM#19E[WkEVe2Ɩ@R8B9'0rL즍Qvi"(InċM2A= ZQ2-H/K(%-4eC P傥}!qu{tFUIM0g4^4ZE[^<.ͯīFˈn7#3d6K D"f-3zL Sˊ+qI:@%{Y<J%) ֤tF Kí]l[sk=6RlC)&)`y:9)cCf@CR"cR6_Mb s.EQDJӌF(n=cU]f#en&{ex/zMcMVnc >׬'Se2ا9~Ү?_(_ĵ3:|bUG/_Vf-J$:DzD7q7Ak [a"})}lf৖"r#W1~2nq<*GYXtnn[Rq1DU .Y +"_˴ɓH8Fݐ4n龻mjGI 生51d%d-hG- :}sDqD<1^%X}^_cHP^,@ r^r%m~ +IB8WtjkQ/.3 +N:%190/׫M>q>stream +HlWK$ +ܷ!O&~=˾EUQdDUܾwgl̈́rlƆǹ}c'#c^@\Dad, sFv]b.+z;Frl)WC~-ͷDsEX g^~s'ehW(7ɴ-#`4CtTى:9[rY"FaRaNE.qU}Lָx ˖xmZ$ޚA0gjw0&b:ct5}Go +sN\:}@4qFR#r0m8 A #v?ffV35ONX!2#7u8g58GF~FljQPrLU.s!rLH)2F%(hT5QGWZr 7^"kiOD:X#չa[b-ON渢\Jl='C^?OZ6aԺM-3 6]Xr1H4[=6AJa0Z領T&E6Zv7Bz)_]H;M3ɖ\ uEޯFz˜c+5&*C(_)w,y+pqtSr@q t}Ul3Bk^w+::ҚeCʫd`t롉TRd T0zW*yG5Uܕ(tN}U<>L@Oǯ.s&2?l''ީAJX4K_gvvT7dg'20U ?;{_g=CZn%)FpXUnV (w2Dx)MrR\ZT_}A(V{f- aj.Bw0i}*E0k٩.qC/OqѴ}='}HU>ͮ6kmXpEjk%yL,J3ONtu?y⁷@֒5 T]Wq8heFPbvȯ +5pՙ72C`CqyOz }^xAgB)ĮDj>_:`iHjkZwJJ@q!`>sb,9Nvi6;ioKFwϾE +/*I X*}h`d mgq'IϸIJ"f$x`؈P?v$9oc2}&<U7j&5Խ PL+!^"`{"R jxźwPu;e0nTa GZ괅[:W]4*o֭!{9nd4΍u?o(׽jŬ9!! YmFְ˳T4 :S-<ſXs`D,Irawr7IoD+ޔڏUYjYa\ƗwS[{hXpq'q; ywIz?Yق=!LdvปJ6۷"-hBv\T.Mv2Qk_0W^FQnHhK÷P²-h :yA037أ9X*EDjg h$S0 *15Xso&o/ux2P_&s҈ep +\|[=9rQ{'Sb?BQ2Q1NefсϦh7A@Q0Ofa/9!Kќ@~{'`}וUخ,;QӼ:,BמFnټڭ1_zJĶrukLoZQ FuS"ځ]uY/03ܸ&k0wSvn yFt0ˬvR"1crO&;>gM> YtP`1Zؾ$h vSy]꒬T бpu(\L+G[ֶU&:sn>A3yղ!#G3eHt_ HZGirf^s'=Tе/GFpSSXo$bJ湊"3j4~Tș@v(V)BT_,qOcU%͙iPd—~( O,]D,YDdثtëGP%UJo)`5Q8n鞸b]jO]ef}- T/]>Viw\ Sծ1VQ:($z^:7N "{)=AGq4iN]V+F/iHK3{E[d۲R+Gwb#*NI (0W#5U?ſ_}"5bP $<@ / z"z`/AlAρ`} |x;薽hR4D AQaM~K" nZ՘F-ht߂8orzGr{ 65v޴]6^ʺZ\qyUo6I:HJlA5Ul;O? OT7""2UH{: qn1jXtɁx]@hopl ; 7mVZy_H] :5N +ލ80*" +`L7<}e/(v%Y _U]VەGX5X&9bc] RklFtpO9[{W.go|)o,l1أQfgaYACaO˧XΖEemE26QM*49㪎d]5 5:vp Bb$S +;w LRX;s?ʘꠎ^L*"7yn^P:{3Sx\|&Ww4?/㥞ū\QZEռ؎{88S:dEˆ))Ḋږq.e`ڀ±JW77[sD&(&Z$RS~XٓՙV ˯ܚكV*.Dve2'˫綫 lj-Ww7j/''Oj9bz{@g;!%rbu45Z rc]Z/B~P:@0xHXljJ)9frLDze@tdT{Y@7oJHʁuXtnD\iS㑶؉6[>VѣQ=l7Y_ +&.o졮3qnK!SvzG_@tm,[@>w쎟)u?ty]x;5Ӳpcɱ]v?#̵6KfUqEPQh3@GAxIiyURj1J6Έi +927EBíZhQl)|j]vmruALPaեKCAP0]c$>_MΈsn"emB0=z)?p4V3"r#xߨWY\ 2TDIue8\(. efTULiD< +$~bMYҘ&!5RAD +Q{MHڴrT,<%0Um"Mfl%1tc8wb0\f 5UBZѹq\'e`dUڬ6ԂV7Ă1 JMw + c!k/嶐j+s>"sѤ\ 24 m;R13ScHyS 3ȹjI#a[`>.2M?}%Ρ%W +eKN_ +{1`66-(ms 2O]NM酨ێu#fJS}礵mrZH += eV<%v4ݿ/SہT+DlXJ^77BɛF%n(THn\(mԲuBb8u"8(4U/1=4q 43E'Hߙ}|J7V\p D2Z:8M1uӉEw[=#YJ1F5v45uGXuv?6̗8 +92(ٗ( j!>!{ZEӚd*1τ.>}BG8<]p=fEAbrV}zUp y, u빬}i57h~ Q׌ՙY_sLn{٣v%xG6SClh3;xwÌz_~\0,aAܭmm[g:F.)[:Ԟ- ٛKxKrwRJAH&WfFɐ_w<7w1Dyx\o;6% mD''?T3Kmm\LabjY +v͜4yAƸM[ܘ 'BJ'+~0%9MLB' +}нXnuRCϛu DYsB^ |ˁ`Pj2wSYZu˻[% +:e&ch ))UMdZ i6#{%_D@(,ЗBe]amNWXVuS;׌#K߇U(W!¿okg~Xr@6[ +z68Z&2؝h#BeGCbcBxrmQ ~J{g'E>aM}Z6%7ƌA>kqb0u $FׄЪLmG+c8/x5ؾ 5!_%=ҙw\U.ՋCN"kP\f-3ᆆ+,aQ;r撉?"C"0~%FBߤ7؛ۦ P+*f e yO!g㱬Bx"N(GJ +jsl+ +bH'hY&Oz"㐐Q}{F> k, M{Vi`>;A_wER1SuRz&64Ř@|n< 4BA('K.('Z:z'BhI)1324wlFt5@fD`?'&zOZdn뜈L |'CX+aevUc_v^43{&Ցݝr2td72"+gՎ-ǎö̑({KH@궝CW^XDvbyZFEOD m0эMsB-2}hv>rߠIZ5]yfeѶ1j5J@iUjFBV3 ipIrb5gG0_ ˸G`M÷hAy^Vѱz7;c9qq[EN:G2lR3*Bd5:mkk2PCӐkZ +/`>/}𑠹ycA.GbTKtϮ xVŔN/uΠ1$bs:`Vi~WɊ&C%Z:|x{1t2J9KU;Fk}1VGK=A}Ikŧ*L20. r$I߃PF̨R~tP߰ gף7]z|#@pw=[Nȿ~owsg?}zW { Ws6)+SqhD?B_ďW|wu^lcb?\}lZVb.xBȪ?Bj{Y_ ;R ھ2+KZV3"KJY.ajY\;VD\jvnpu<[( ' #/C sо@CȰQ߳%NY^;xu"YD"7+8Թҋc2g|HDZ2^lMjACQi+ cd>.al/Xf2Sd%,d،*)dR:o\5Sg6fBD꼆 ;VuqcIdu)Sr&`(o rrH.L*FOaynB4WS%׺ǎ>(}R=F@*W"KcS[fkF7Ec&)|AÓ @W됈"]^/װntdŐ1}gb 4#ˆlRM1h'F0W@ M8t~X5/1(DZu}>٘~EWd0 -Of+Hʭ,x9@Xak ROㄻ"؇P M`.LNsjh''=Oְ:6%`t eb6:G0G퍕Urg*5D$ V(N k33 T$rփ݌yb˩WM?fSŽg}۫ T9p‰SJ oba]W_sxfN 0ٺ$nO4.*7eؘͭRuL;rfZ!>Z kmڧjV]4ZiM8'E7gbG|)ׂDnwgQpS+I56葃;[u,$)Uqetm#eF|-m,6ì1Hz< IyN7 %_PתMMS{(c +g2l +D" +3~qcէQf"xq=zDfyz`b$D>fHɞcS[g[֞J:}rcucf'(*OzȖCSGeoϱYJyTEF~Fk.IyO~!1< u mGw! F^±EppS=rr&OK>ԉ!g}k-THA0V /6qЀl?Owdٱ°3qśvd[PP̺ud=Bid7ڢ(7L_evUҪW-͑q%͵5j*1,ײCOFU(/bB]f¶qUQȉ#wQ;*!awp[ZYVyqp!:z{<ɏ:I7[dI/@f'OzHMMzA1OOqȸCCQS#{/z18I/&=/vy\Ŏ--|b/KfM-%f D*-+܂6/rA˝s‹\0/u~XNjA@۟%}s魙e.TS\\)%dі\! nr-&ۋZp2Nf1]Mb9ofh<*RRf4nY1Zy1K]hĉ_ԲFxsOny-2$K\C.j{T=2&U?=aoDZ!8~z.8|f=%@)nǻ0콇ݶS:lVHa{촙ٶ"%L~(C7iy2JVpkLC8?\a{Nd\Ǐby/J\.6r+f|l/{w{Y(ՄL<&?|%#5}l4}σ8Rq$}Qg1p cǪVʲ4>kVscY +pcM +n+ʚ cnA{~B jYxp@ <ǙQNftgͺk4_ ORF3# ~2eS@\TJ1C(vkm +4OZOaR2En96ɣΘs-XABRC6ay3p[D{jYK&J _zjt)7` fk[,ײ%f}Wtli2iQ.əMc|;sp"uc5 s!ǐn{VM1<ҋ7>s Q+oH^d)D$ǍY 2x!uP\ ِrpjoP-%&&C 6 rk#F"{Z-X|9tMkc4GUKlZ)40n$c/ +xy zYW!hI^* +S俈*eupdys#"B^`k1A)2K3W7EgQqZ\ۙMd]ܚ˥tX[ݿ㦂RxG#CSyA3.Ŝ있i1T7{3c:U}6Im+"=̚h&IqdzD\N`hR)ښKu v +@4AH cQr=4{<ˍHS;M `kRϫuHāDY[(({ԨOry -/RN;|*ZKS{,hä@\{h/M[Ճm]NL"X8uQoF֖K RGVO|˔Tr,# Ͻ{s̑$#ҏ3`,j"Q3.]Hd ),.l ~4@jo[{41S!.ц3 gh0TX^NNZ׆3u˔^"Lt7"Ô`c:fWBzEi#Cn`/ +|4ǏJ^1FuU׵n[[s)V~6Cu躩N0/ 8f.?vEs*W)tK@cLo=j};-.7wV&@1#~|EQ񃾕&RgWۯ6S>h1D + (1C&cO$亶'&2PrP'amLJ[`eXkek!tNEYV/"w?fcRTf*Eð{ bato9{d!PXu?QDȨl'8%y= ߄l)vVPKq?n%$@]|#it1L z/xACu}LtcA#,Jm_va/W?Щ@:=㪠h @S9vN;2" Pq]:}~#a?R ]\d~`Q@بPqJ.gYԲ#N~αS-CTz~ˠwWFW{VmQ/F~*Ga13ԴUXmU/}V[|&FFL%/qA+( vYcP'ͥTf +fzSYAG= Nl?E0jvcM>3Zd}~)hk}| FǭiχZǠ$`qaMddlŘc  Me\fh(Y62@k (ذC$j0 VVvM",Hfm`my,(swq’;V̎u iudUR|]JlIb+l4La_^aJ1T+.`Fxq>^P&#!G^Y{%CdW 撒M [k.R@Fi,Y%,1J<)0i1䵤,  qtn5i(:s +/jK,z Gv S!9߰bH n O6i?Q+~FIdDduz؉@ dX,@8[v<jb+uӝDl|O@f`?UnGö 8o]dz/7mG +p#n5ùVYMuQ{H5 y20=sF\(F>Ej4-`[^k_{,0e7g!tnp쪯[ dҥ@ZDoZO7>eh+iXqesf#R.54ʮeHƪ6SbF㹡ĕO;/M{ɝKenz2R @DΔK-@@t"^&t?(LZQx[Ku:P%~-ܞڥR$g^nROC/)Ll~ɿ$z5{'?g@ݨQ0Ge;!ﱓI"qɿ[=إȤLOljվʱݽ '{ _.r}`VH$^Md{6nd,^U:)Ey͍uA I>4MMս 20 Md[xvT0 V^eTyqo%M@^yibز~vCbm[gh~N)aPewOFxٞ"+Ѫir#ӶSHsWGk( h(zjYEJɄ(q([eG4mj5fTS nQ~C[e+$- +ȀMi[kf﫞o +`KO{.F.k"}euO*}Bˢ1SIDjFLG@֬.Bk/X:'bw*?J6&^`ꉬՉLVc;glzREa|†\SϕI-R +D8\L}T)gP#vt2gCR\FbVqn5Y̫h}S񻋍C¹c׶{ 5JƗ\jEfhߕZ2?)ײ5+=oFт26=Mg`oVZcH?)2 puG>5%IYf.q/(hI#b {=EJؗ[ω1/D^it8UW*~TLFI2` I+AÌo8VF@%u,HoN}#Zw0AkIhf-`dN1qxFa[8? qc!O{^"dv)sN_v璳#=F":86;3vGX +su2C򢴪69^ꕸW_^'6{b2ΎAw1<Ӎ1]}H^,JEꁰt0j(,mneҕfR<*/TiCoTB=ջF^DsUiD%a$->ov+(-UKY'>h=SFV⦜+{-}?XQdV LmG^֧Y5YOʼn;MWfm >: eHס=g5B}pd*i8% FT~cxE:Z% +Gwcɵ㰭xs(NgN>nhZ_QQEj|MZJFEZ@9(0_)S{اDO8ʪ-Q,ʭxĎ?M=;L#uKsbF^4p\gDbְY]-0X8_)=c(mp5ԉ%:`KWu"F`V{~%Hΰ5]miH{`׹lwB ӈ̘ъ\* IW T! #7[p)2ʬ<BQkܮ<ڼȶTM iȪmwQܰFA@y %wVxe0. [D^0U$aBcVwЉJؠGKZ=@HGX4{j@l9E̼ eH: _KJcFl_IFf+ǵ¾zL1*Ԑ6HmZW}1WCyRS 3(I/5;Fo:.6'@!&u^̂^,]Տ z)rigƔ*r +ۮ+0?4P6?ibŕEm(*nyV{.;tP,:c` ,~UHU'.la /jX* deJY 2^]B5'̇O$D .]*t-o{G8#A_Iv!,?w\K|'x\) +nwn3[1o<, (I{Pr+P^gqڊiusofT{!GިU__Jhb\PMʴ XH K?oQSALb~3ظv+DJ*{ȓ,z6P\PxyFA}o{C=IzgA$G3k%R KDL׶6m:g`*iH^/vZfJ@uS34v慬2qN\v_T)ZcKJAhַ~'63e5!&ؚsIvn }`|%s ϮM%fo͇WzEѰCtXu;onBTiSR"iӀM0MLɰ!a78<$vP&Xo?<cﵢ l/$:r@W,a[4y>> HB +]/Ty8kW`ܝ;xܗRnvl~*^I^ϹQn"; >?} +8:> +g6Y׮GVY\[5`+^1;^.t2S[$O5ԮA~ +=[*P :] +#,/LQ&AXsE9&PιdfgMbO,peEvZ MV$*Hk1OA*B A0Uxg7 ~/de,λgG{.~V5ݎm8l/N4)& >.٦Qx6FtlSm +dȀئx9$ |٦mtI5m +ji^= HKئ& Vel^n )dRdlh' kaO +endstream endobj 392 0 obj <>stream +HtWAc +OܡN @Z,W~ ۳4!Hʏ5ݿ䱝n ?e%>P-z?ocoCmc?L|?7lW":#DbVl+ck!& +}Gq=m9Q G( qL@Yjǯ0tϭYy'Lg9c[HƦ4,mǷ(!Lo1sB"E.5ߪ]=⌄#I_C|#Jk3$W/C;4h$RҚ=Wk!3+Md Dlԙj,.&~S`vBdA67mBفG6ۯ?8ÞafB:^$ԼH!hD Ou8d 8NO&vm&Z)e'Ҏb}>c\ =*v,٭ +6Ώ/ {Yl"|bg Zf1,WQ(@L<|!B9?_ppԢBh01ڎN 2 +E9\%fUD6pԕcD M$S>Ҵb̷mo&70M@S Cj#悬1sZfGV ̆㢛EpԤ<*Tp8@*`| {P w&|G\~wOf^٦ &u7V2ʌ3]A}/Tu_Ajʷ i~\G3^&E4oM  ΢x4n{McK^*@!̝$F9C nu_mf*" ˖ߑ`Պ|#O +rd7x 5K1}xwt㚪W%/pE+)Y;ŋ=9#6m'Bai2AiGcy1IC$0-{e"h1"vXte\<+e9GDtsV:D2v"mSfb_mH +C;ڱVVhLN080R d"1bb9Rҋ\6`|fj*%8#c٭~ݒ!hFHAHΖSt# VHe5qn6+Bo|a6iJKw/!.dFFwp=?4Ǫs-Ec\Y trO KNv#v K!<&)x$(A,U@ +)OSq86C(Zh{>h {]fp;"'wF.㢋;[ΗCwܤf8$S:w]gZ!\!X3YwQ QgD9J6|W+B"Q #1")䛧'^bDr+Mfr! ;_ }7ːʞt^4,b;T.uJ^xF.9xҥjt@(o< +/kb[EK -Qx\-Q[\g6bgD*}\2է +;!+t+Z&*ltd@ +5 q ^rF\DƇI wIimEiȉ@Ụﷹ|zIegaUި#0HdT^"o)mԂ1&][Pz 讒1eTyɭOejȹED1'JjI >RtZķ"g R=wxHGp@8;V񠸨A1YD[\ ?|q2 .|f8]Ȍj .}<}zvTףDuHW$&~\Nە񚕳OrXjp\X6uRFrcoGZQ:b5Q DڑldWtRdAu)^GpׂQ$[=RLn..\n)(斵[ǷBOw#Y0"mr˝e$6*>Rԣ`Tҟ0E;ݣ%v5HNYOW  + C2$BkQ@ HA)0((B 4}0F̤<$22 nL 6OkCUp<͚yKAK 0ƷV8Ɩ]J bփT<^rn-Z*NRVh\.$5hNdic*`U =ݝ +Sd5JWl":OMάhmfy,ĊTJIGTjM]4ֺ 8*"<8$;D^?IC-Y m!=x`v9m?2"sGHsm} NK/|s:Z*E!:9dc5S~#L`N ɫJSw +n-;;ֆ |g!<>է>O,7@jF#$?SM/U? +1{ +x= +S`8PMKm ;ؚCT>>A)솕34:ao}.7E!.;v< +N-az}Q];d"s^h]Zİ-iP|^}.*.\JM`/GtlM2{BD;d!gsD`y,sVtb,Z>junw-9fS% \~V5#>Bq^!+M|4b{Wv*ο׳HF2!x1 &H._DZ0[Pl *F_x1ޔ3,UjKeh͕l"= 8&[$Fl'R쀹%⋢ +N1H~ Wibm%fV/ScƈZ1}cӉvS +q󼹈"28fvT;jEGr"ĝAY4STIp~67IQJ}Ώ6tKżUmٙ8dP- lO⇲ Vv d)sk NVd ba;gkMUQ.;4 AY xC>++~>&A^ha ͩ ܵB +.vRnc}؈%w-$dZa.2Tks=)l,}WλW׾/G#R̅i-EWb+ [`=6ӠG3T>bQ{z7\fXCF+ŞSMh1-h.@ͲJ?AaٽT/G*vn8o!EifQf즩RC$I9B_2$c(.tۢ_H(߯^ZbK";޿Nlޠܨ]_SO(r kIXGׯk$F 3(n|,QY$l +  dVi'P1 @D(C);LؑdN,} hW6H\"c%d82&5.HdU88bV2&}ArdBrH fDD0TBQ})ijӮnJAFM:Ҏ!وCW +w iC=v]4^/C^t[HVZϛqA"Y +A7ΪhS<.Z9sQ~=c"y yZ#k  uY|jpCO8sjx +ci.,Бoݞ:=7W0yth䴅3HihBU/FF Y c=wzEɠcף2RQx 6&Q7AA Y@7+x*qe7Af{izHu?##@^5kwe2\E e͛"1S dƦTiMչ1ĺ5F,\UGR|\ |4gCV, ZI/UIt#ZLt],/A @6C#l}0/Z;Ct"g>C&It Sӈ9L 4,apc>{{"vex _4jǮ$a[ +HŤ$ >w>h.E$~>U.3ցF NʩL궯Qb3徼#)K/cLI.1o\N'4ffQL4^k7v3ҷsODSA%`Ńw)xF5қQ+=Z< ϑhʎhK&ױxívx^$H7\i9VIulû>p;X!@ hth9y1xQe;tTFT%! ɒm70E1HҪnΑZDw@ħ'/O'5j391d<{EkUw^1;ܐ r]A:w/dΑ".H9] %|;!oSَ!^r/Sh>6Űw^v ä8]Fv>#S98`K*##Y{w#EΑ5Gַ oߍ +u 7e+R$@F27萐-]D浇 +9s!cr+b8%X]S)]z,G2$"?/bC&iz9|I6H -|TYJ.%wK,H Io^$xb`JsjhZQKҮe?mdn5ֲ9uI!]>JdT‘Yh[v6&H{^3uG+[5葚Z|rzjڶ306U*Ose3|U*D|^5>9pӎ9;0Vi<2SDِج8A>JǤ]@[{[.>ۖ;en(UՅ?Ŕ +/}0eO>_vQ:Vj2+UZ… ^iԲQf2T Y_f{2;g-Hc-t@0Ȗ#ٍ`¤Y.ST.<1Li_/wΒ_jZW?;9Y՟&Z@& 7Nj"2jPmK?!c5xYOSO|/+yC1ĥfWaaɽXڈ,ErGmBYjo)l aYIvt%ךRZ]Fvɶud?c (P}3%j&HDmҟRHb$Zt)KBsݺv!mv33Ʒ9U),9#?( +쀹4cHqGW( cvYnzVAYƾ!ݴ$Ь}-r}y r~#76] vITd\9 15jeWcBĮFS͖t̪FTzPn*զFќRveSTh#[1-*&A85ex +8׈ }!cz1WX{Hj$j"*.v*J1J-(}yɛn-͒i`7Vƙ):8N{,"?#T?7i~~?@)\% 8ЕޥPb[:"agHZJ[4ZЉ4A@ul :́\R&nbMox=4ݘ@¢ڹV[s xϼ"S6D>yQx@n37h%Os ˾ϓ\KLGT4$6G4ļY־vIY,HQ%hE*"XP'Ph_DjD c>PBÃ6@\2 +\TŹC%c82 @ru0i%\I7C! (e$#ۭ})DC|0tZsbyNQg[@:Gw#ttPL3? x0lßc.׾@d~fC8ʑY=}p̞H4<2c\=)hA{ P/ǰP]{.?o)I*:xTmX+a~1y!F zupPSHAB6\oBu`uG5+ʌA+I2e!%3R!>v*@9v umxʫ‡(2=Lt4H P=f;0*"5GDsals1D+ҕ=˛خO90I^UbF\ ^v߲h]P?UY 2Wm,Nl5Ϡ7El'Ȩ@c&bh:*z?Nhyu'6(+_.+S:-t$wyK|@A@B7s xUS>GÂO&y+aozTZ?rҍRyl-@իFJy= +*ֶyV@Zi/ +?FyL8&"yU#Pr ui1ȷ4>Fچ%R45 L[H 78b O|-yYe-etMW?, ~(bQ^4č"{u( wv v86BW9'92#\7Xc\ß^Y2F~ +Szj㡕:Q rUnK{Da+UX0p6Cct,_q#s@8 $c婨PݥC, f6j+ b/44[y.g(Rva]kfr)Vg*):1 3_fE92|LaܔUnBWbInE(U_h?χ %""W;}&9P0]3ɬΚǰ4*}.rluuST2XP}橊A6%_dj4[ QVEI)A ZMbRӭ4,bVB =Wּ31 \Ԏ(_ӑ8aє= [`y#5 7e9: dmd$t.2tn5@ ֒K&̿vĤ&8(wVz5CHy{̃xF)jrU~䭫*=U|Hd/)r,oTiSsɺNF땃,#6<Ar!(q 7hX)0;?Hz:B-ZNα+? /e2屓Ao$f>> `ED1'o?i=p\JguO=#IF2C"!;ݎƍw5_5)2DTGcEp+1EbG<-Jv%v{օ>ʎC ">[/>V@좪XzTT!, ɿzğ3x?/̾Vd=K.@1R\g3˅#Cz4X(}ZJȎ +XlD#, 7:V}ʖo%,9fuXXzm ۰$a"j V~KWɻz>Ll]-AoS0Hw {(@6>>V1#t\lpىrf +uΌߒ(Nl49Fۡ2!Ajсl#E4-7n3bk>ϊMV1\@B;!S!4[ϕ'k} 7a7:%7~(\u?#wC(|e Ey K+ 7 a%UD48ő16ԥyDc#ۑOj>pԌ-Z,:Kt b[^Ɉ-{L6_ESxbtxp<|1Okd0Qm; .ý,.'.=9Xޘ5h4Jё;5C"(Ti(fD + q!4ժ&1:Z,Y]- +j jݫm*0"B@G +GPY[kM9O\1ԁp[?Oչ҅KބJDwJ|+Z1u2ʏ"oJid8A]Qp|Ƞ(HTE( ַnÐܜmø);({ƌcuB[@\؆AFTpq3wNa"4DR{2^i1*8*&sxls&#(K :*cůl5ݷL,Y=(]Ywx1ȊhRr[uW=; ۣS!x,[TuN_i쓃ms \Ơ[OT=O|Sc^{T]&bI[0S[îXr!Zc (2 u&elrz貗XKwդג갭d3EO>u;Ktے3c^>My8Z ]S𕢠 [t򉇂)[n<'! 1_|谙NKn?PD$\#Լs:ЧFH!Ǥ=ze W& +) n퀯]we tbXsF7}t== 2Az3}^5 +޽f aYH@Z"MibD@&)C} I'7DJi72&snpppkCxm+x_lf6uXa J9 CUNFF+CdUXe`CQ1_6'%gFV+AqMkס5|۩ ֭P(1@sעue}_rq he]_kJπ 8HP۹۱ O*NPMVωSY*3 +]B{)Zmw] =x!Z(Dk4sNUC]C–.n{'E~7ĺ>$H(؏0W5!ヘ!ߪy!/Ou'N~n7&ykubzJVC7E>/qqޜ>Ͽ^p=vp1Ȩ7FI@;8KKZK> +Cn͋xzSǯ--I}LRMCsJAYqjoW pv?wv1WpjjqcZ 6_:Rg"SLWHz9/ce6WZtܮ)>  +x \-B q.˓`e2tΗE?qYZ|ۮ:2w)Wn +e}=G ߍmkʮJYQW }+,Ñҷ + +1an; s?HJ/^ШdFs&Oeizqv;Rb˾~>JZ)bL "P> NAA"z_\ 2ۖ*"C׭no'/ jJB;E;mt[75DO "v5 zk_' 5mNy#FyLƽ/\Jx6z2*sm܈kxxs"̞ȴD:BTF ~HF[\f2 +iKR^2ײlnJmǭzKW|U23o"+XlY}'X+'(S +癭~^UUW7Xfp5SO 00>i>#H%l JvKs<aiYY=;!=eH-XN=`Kmv+'B]jhKF:r +' ݩWM;#\D-:0c_]בRE~ÔA +NV`/S߈62c/,mpoC"A 4E7z_Yss @@5m)0:֓@KH%8gJ~ ܨp{>dUM@&㾵:5Hc_N#^^qӖk8ʹfgaO*g#fcxqM]sJȂzpGY4D{ߺqdк; ")W/I-) +0>:%nZr2|YcDhFf=, XR:[.$,Dvs-AQީ"[J\V?-F  0^8u/B3u9hVb# +2XV +7r z?y^|KW>%ͱǛ-fc@{\VgO 2ҦJ,Gp~܈$.}#<' Uюtvrt_d嗪0$2OhpYU!+LnXz LNJ6=Da|'Ɖ?臁J4Ժvեd1{ǻ2{5;o޶yneDOqW]U@{Y_:W붱9Gާo8;XA5( D2IkԌ5@\=bYIE zXaHG!kK`~KwW8RZ42U8&פXvʠɚ8;d- K*f@0=:1|'XX͇G+z#Bk.+n @ /$6f^׺@o%-yG8rEEAٷA8f.ka=Dv6YP%a#",̫_=9`<ķ3cIW)y5ȵ6Ҩ{QŽ:g#3KhkK/2H•defOE1:HI(} +21;b4 ٭2€2ƧTW((9y" "w4Bi(Fsx/4lךre;HyhŸּXFQZueՖv 쬽=W!Ęo3z9&b9U񦕶a:E,82n_1Rb\BY}f45^Mtf7UrH W11ʕ荑I.}xqTdsfX(` 1&kKqae"F&;/B +f|0*i*ϊ_ſ;,9byED>Y&Cq!| ǖ g˲X&e"]Qq+WyS3܇Y>]k^ r +g] ƕ +P]h2qYP|1|Ġejjz]f\Q\ƍ#qEcYŸӎs.yL}6Gn[m G0qfށhk(#3Vh6 +ײYrƜwYzP-xIueIy@Ԝ%;|D`h\d+ oɍ@WBhB{]F )\ aWtgKnkZ('_nZ.h%ifЈ<~yܷl旊tOuf̘T'!\L!3BWcyt5)V.w?R]3bH2Hؔ, Mvxմ,=o_1ҳm,YTLhqb;1{jtQ_c~ +[ܳ"rݹ,31<,wm /p9¸S aPjO>XzdQiPc7vh-\eUsr +[\`Ū@.Ss^zR('}:Ǫ 0sXtc;H[*]Coĸ-bյKc׾J',^z^!3i.kk|]"_Dq}FÝ"H3v"" C!tmbq_Yg0&, {nd qXdw`δJI4˭2Uy.託Nou$4w/ngQ7 y,-=19\ard&a۫DpÇY@'#o#e3}X6R^\Y{c2̍QDx]m١X=EF&aPFD56t/@!Lw9إ"ʮx{)5ϧt_1`p#eLiZd 0j5~MR‰]z1PD%ss*ҽ@An|࠯ͨ 4M~?O/Nsma'8JXfp"7W`9&܅ҖkZtc-3m\{r?-}.+meU4(]]Ȝ ?uoVB..%b+#?"'tߤd$t i2)WD*s͉9Q(j8@Qop@AF8֓5H?bc')OTG$+C_T`?Á:,,âkHnD\-*-jb?hQhѕ6iѵAJ-'ZOhQ?{-AoO^ONj/pr/b+\/'^TŃwhQt%hS۳Jb{.{Ң->aQ`Q&OXvOeeG'X(El@SPs^a]FEW@E Tӥ)=n1qEqmJJו_ߍ`,8z7j iѕ7B)C<J-z~{/Gk c4w}@,iZU qDH0sbYnE6Iɣy}^QZCIpA]VmiȭlZ+kl_6t܇5 pR_̙7EVT&;V+(U;=O{ŲO) +/-b¬Q+s,dEh?2U~eVÜ?}0HUb]zya5B>mJ藖Au\Js%pemqYZInk(Y +wy=޿~ ӓ;~STbt<#8#]֒G~a]_2«.%_}8k˺TB8coP Gyvlm>8.ː&HVDMu +D  #g٬MjcYV:oٜ "h':"Kz ֳd c,76V!wv yOZQ]e{٩]{-e_ rUohuU>%aJeFGw΂8`4Ʋ&ZQ"e ᤊƲ'ʣV{xMw1q‰3{avAY#o>akƖ]PW;kւR3ӎBqEpU'gE\k@1PNt5FM,d| =h-Ūi6@(KMb@طJX_ uސ]Zf}=„!dRζ)cMGB1ۉj[dzޔ1A|sݭfl:̣۷핪Pxl$Gհʂt;MDr]x?r5^4 +c?3̂(%R/ro&K*4V1&]8S/?¹)tky) lFKBGlZMpJD[\763gޱ 5nBn:y21|+"mm,|S9{P[H1KynXVldik’0 8z 7Nk/ڬ>su,ÅjԾݥj˓O䶓s`$xܨٰMCm;jUP\)/UOQ8YtD)C[0[Gi7Poo<+%m"ȓ}7N1:9Z0-оn!yK6X9ؿeT?"3By,?!˵Dٹ}NąLCIj!}"osK|^7CbQ,S!$Ev^7EX9{њlke'JtBǭBi̴EaYǺY$c;C%\w ػԷ2[qrcE}s/bӥ&Uym\5{?<"{{U}\6Y"Q9sGk4e{tE!6[8BNa1K2eNpʣIaJ^^@ײ`DfF`LkL`2JnhWǂh:/2g6`*:NVߏ˟q|>LFPnO 0Z})5=?FtLiMEBۻZzߢۻX:lVVG2&F=lo{E{7vCX΋O2],pߍ9c{S"77Vq½ilG+}Xr9|&?b}]Y~p%ܛ2Mf{.,O)s x ]1o%a޻b'Iz P~ M&{ɜG֌cY,н)E C {W +,?bҽ+OҽJc'9t類 Ik kÇݛ{Ŏm6ۂA(W~tԕo-һWv{WDx eཟkכ׸?G{ +[Jb_c#޻ x0az6'Zh ̀nނ \g] w(K2j?x +q"Mщ=dFuKp7AI.mǏ&Qh=)s rZ!Peu{(o^KVx-Mf@'ܽ#Z^C#^Q܃hqQ9Ձ8ĭ_9Ep9>SF@] os֗Pܵ9-CQ,jieni_]ǖn^$x '{v`>'2}R{8] 03(d,JK,ףLiQk9%$%0]*X)F!}|g_x' co`?vl^\;~9V+;Jj:LНV7 +B;Ŷo[Zׁ]h:Vaצ#αBZwxo7?/V[sROOkp~D_J?儸\SA솊vkU ;Z\9,54Ud|>e,d;laJkus/i,_~Lm +8tz.%M]c*U%(觅]1pm.ܵ'BDx;XXax9G 1}2;P 7L2ިD=3}&7,P+` ^ZRH4&'vG p1vhob_ ;Қ^ +Qc_wa"H$8HWQ2kNcF4nt4ȯ$.ɇRv(<?vp@_68)?@# =NWkVPv&DOiPѻOtO }:ͧ|:ç|:ͧ<2!56y͡,lsga7 ;Y6Wvq_S*E܅(vϻ~]R?o^7{ߑ=> >lP|#T6aDl #TȞO?ҜZ(Cué.Wl[ k [p*ԑ} 2 Ѫ-p]r\W&Cͩ=` 9+9 np'p?痳tXrH4|\ڶu r.pyʈ^^u'0Q$ +uRF'/vqMg2чW[:fX[<*q/k@ZzBHhaö*b/˭ܶ{TP&&6D$e-JT=q4Ŕ{~AMF> *''!1Z m^S7CZG +!N2fWBp{&F"*6[rx:#iKl*qnX\l099ZN1jiE:-rǼzi(@Gh9G#eYPŒчK3|Ωŕlġ|PZZhNw2HFLkJ]D85C(r a-e1٪Jbh4DK:q7[`c"-PaOZ%M*1OjJi0un +0;ʦIN.A|w[p2UJc t52B֕9:y(Z CT7FVN*|Lle +*v}vz5.0c\+d +endstream endobj 393 0 obj <>stream +HtWe _;%Ѧr4hsl0U3=pi۔Mx[y:m _ LiF f45 d۠ q-di@ ml`if 7{"%zc6'gĦr82Ion>gX tܿG.ZH9ѮH+cR:fPB؀Ȫmz:8q VՁO3 [2YTZcy3'q'eCDŕHݬ̌,8IkBDZ3Dn A 8)GbP4kpCt۱q8D4#?qn'PvH4 YvV$QJk}rTc)J6l;i>)vTB&_ACfȩRҼ>g/UB\%v*E06ŧBH~Ȧ0+̨ j6(zs5 +c`G$आO[7 SD,T_j6 ;X,Qդ+ +z3GUG!pI Z=&m"1~A<QXuDh`X"ԑ䙈eBh΁{$זgZ곺HC{cb2FӮ]~sh n)'>?NNؤrS{SJu^j+CW#ҭ}~^*2#%+ϼt s*H k^@b-Pe|ʡzQs3ք㎳#v' +r{!]4 6'3N5ls"+_IF+|ܐ f;nPm"NҰwģ$HZr̦%6$ P%EB_ÎЀKU'&Ru$~7Kvr\qʶ@;ˇS$WQ/g;9KZ`aL0\:Ty*^j^MkFp4ɦ^UJ/fzv!jUO3qwؔv&s;`1vgH RpX|(zmYk.$v +*.X9Ge4DmYT0TpVfa fn@[=)m;f3B={-k*Gg3֭9ٵr#$)IUfQx#zAS /Q[ee=ԗbKK\h 1fP@/ֵDz%wsRͩ(=6ֵS+;>leGvQI5v@;M8Q6FHXn z:u\続(K*&¢{=<΢)p~me`Ák5q#U SQg_[P޵+|u??ϊgע̞~ wg1>Gc!>zoz'Ig{}Lg[ϔOlKb]=G̕lzuaO:w+icOReVP@zޑw>VK=gM=EnlձP|ug<|3,oYMO+DP϶ߤG=g[;C=c}gS=&m|g vgaȖzn!_oņz%K C=WvӬS4PegK>^y^MA $t}MBqkyH誫ϻf]Q6!LEy$4r%c5tW;% ;-CA?nZW'w@,ic"$ D!" 9kC?[=9#"-cn[C":܅[6ZXlk8!I /ʿGa e*,ًn% ^oORsPU|)3n$KYۚL`lwW㺵͔%yTEy[wN+ +cxb߬#_7}hsd1|čJk=6V9||UH7&P./o'~c14; Z+G[$34ڀ̽TL6Ȩ%cĆɜo3-ע2=](+ٸ@2{U,; v<蠙 Yύb]#e +UzHO=v9euq'8C-rǬ~^5y82} D~){:z&Q|^t̿>Vsokur ]Ew]g@rs/>Y7vy⾩k$x'yw}_u αl}wգk b25g>ΧMnWqX8.ha~̣``GvX9G!< LBeT 'Z rrdMHw?ґŔ_Xj ԁ{5cQ,cైX+DzYIJ L2%Ոl"#Dfȣ GwUf%: .׎9FjKs)K3Q.Tq`]ƅX%7hfn' +G[C+j#UZ;ڄ^d5U@$N{qe ::s1僞`@B;d#aI dv6I)Mv1fkڍ@x@{e~ʲ՟YU_ ^Kz"Cfp1lQe:;bY>yn<)_gM/r nRNqA%zN0!6h,H#6=>@:4 +3ğb3 ǎ:Z\ؽMYKŅ@"27 k#G:sd߀HoF +wD͇#=o#U@[[:"}Y3ʼnw ru|I.c+S!$5]1;3UzO/cC BZ]eyn$cB&fM+٥<&_0tjg!LjF?I}RQT%Kytn\DƖķ3U[HJ4k3(liĄ̀$ed#3 :jv]e +)#O2w9,ls;K[q9I1ǹ1Af`E>]鈒doSQ4t:+ES b&/fFv66W7qh"9h|Ƹ +/8c7@s0n0} t*=ꛤ2D#MLEL m9 )\qf{ 9Eꁬ̶me+fS:74qvĠ&DM|c7R6twɯP|c4 투Q'@fQe~2k$CiK/ȵ|S/3Y9V co*.tZ5vBfJf  GC5$bW+f#)bIG5Ȓ0%ş9svZQ[&o̔N|8O9ꀇ(a)g=>&t 6uOvF28 mpQ>OTy|ب4c>ǿ¿0`13 !<2 +F4 +iNm}͋h ajAƏ"޶я>Z[rvp2vhfN߈OS';`pH8g,{Xk)h/ y3$SF`elPfew!ìՋ֓.V[a#.fO`MlFlubAlmb}Ac39ˆڂٟ n9$ +zHczԆe%e+ۓ><ur"tNX{]/a9b6v²j@Q.D|/%S76LNyDZiؿ]3դc{/]>gT[6s*Gm +)ZD^kFՒ04"KHB]H^J'Y cηq9jޗpU_{oڞ⮘%Rb8U6TmnB͡.r"t]Q*{t<@1#x tqZLDpp1;_<`o_@bUhά'*bGX9ŝ낈فLdƧ 5&@j.~$Zlw} +0 lO36.XU(11 uD9r [ :^iPVG5AG <''~54YqfΦ[bʩ>ڃDbQuW滃m̝byNc̞eY _3oqR@o ˊr}1ksSdT"Sꕵ< =qi 4E IN,}[U&bY/|5݃#Ze7 P̰Eq`}2= \C7"|yD*g\)"Kz~ŶhIǙy4y4(iUڴև|h=ȟ( +?D,^XBʨA͕vŸHh=ۘH "i=HgDPqDGɼf%^'>t#ؿAq3PB",ٽ]lwurcUr;.),>`uKbO>4i+M=D`->7 K]<`L"Q<v j /S zhz|5eՇG iS/lQ$<"ձt8@d;p樓EfayEg)ڕ ToQȢ)FFiv7[ $)x&ǃ%S榎sEYk wHFv5},kM(ckvn-YJwIbo-_:c%;cXCt:!R\=Yn{٪lNw"#UlM=בZ ۩fRVAUâ8~™d7spmeAGLp:):x p}ѫwwu܂nw,h ;np"sއrfkOFSG;An+>>mjx`Y̦8t7[9N҈5/5佬O9J~W㎻j׀A o\m>dZhq !qdp*BP05 #xhfG~eOӷѝO'۹9Gi+غ ޷LyQ)gJBR_X &!pH֬)?sI42ZmU3R`e~Bဟuz2PDMOIeԥ D;U<}|P6,o6a2%ǏE'p\[^6Sr Oj-ۂgbT"zGd[-jH$DB˖l40j~/r[Q{p! zXL$jЏVF=* dSn>lY}p.x ;/3}{2^In>G-dmy]r +XnڀLr( $P W3>݃]88_`PQ"{Vsr_{ 2L嫪/ZTRd ΙmFvru>!*[`Q*2(w"W>N$l&X!;YFa(+b4ObL$i3ǸfM1ue3v9|@f_0Xu[0B)oqWq\m\7_eY^Rџ/ ,٘Vڞ Qt|X "&9¸@1eB>dZ + G*EúN`T SN'+PyX=w@t_ɭC?SX MzXw>Ke_ T FP V@t ?!44C BnTLqL0Ķ#4^rD* Q~+pHǒTBh Rjhl'P[0fkk<ͷ϶ðŜ9$&,MM?M N*3B #>;h<m{+M ^Gyj575O;q)( +'`"*ѼtshQ'((AQFi60$fͪ<q+ f'7WXCT2X:/koB_##|o,VW6|`8'.3Zh ZzYiQgP9h~< +!dƠ^`lⲹkF$|%|b9b=:YO^[y lTnյ:!Ǡ2z 3zRaac̓ϴ,M 2qƂnQ/co5' S $(g GYy18Yaf?7Zt8?f^jd~3i~2|ܰn,Ht#[^r-Ji0zp1|r .r&';. ygfϐ^ 9qCEP8E1`;OڕQyL(gx݃qfm]6jB[(jY/bH<3W F1褺]DѾpxZ }(j24gUݍ]otwb;5mq}ΑHy$Zy #7{~oض H;JaD=_@Sm_wԢE^ Y$cYfo/"\.H<-Ju.xƄaRS@ٸjgL5,x"jg"{}Amt&w޶iw>RqU|E`sF9(W%#m¡Aj4Q,_ly8Nza,oNnj6! z2a`Dmaq +u*O~]\6\VhV^#$uА kE* Yz8ɍd<ljEbXP\VܴD}_* BމRTr7wGX/a`}#|!FtSAc5 #<݆}3zR"$} SJvK;YQ5V߭&j|-տhA@:͚UyR ~YNxNApuCڞ#q?R[ +>z;WvU+>GKsX#U3f L.1Lܠ4Ze1X:)[j'v/<=65ka.#XOs٣SL[|,@P~]Xv8l+;裁;uW*7H@w?+qATH AmWh s?ِ=mj*׭21-)^ x3K:<ۆdQ%i^}6qz¢ +NQwՠ۶_G&+L-x\|C +BmˍV^xibu)#b_޾"?RRa?-PlG opO;nU.P46pՔj+ ^s,{DMD&\]5q-=Zv}-LOwegRzm2We^n qYSo16뿁vv柯2Vo9UAtD$ƥorlGԑQ <{?Y\p8,Z +} #fmHn=TK"ވ8rhq +j_O"7 oE##Рj>{cO+;?]?Byɨb\kDSЕq6ISPK ++5Ŗ]n{ĒPITa<̚ЋMPRwjHA|#=wm(ϢmҪ-zm38Ax +ؚA0F{j`YM%4JUdmX1GS7/S9E4ݥ7,[qZooL3c̹=/SѬ%arpvw=6U]9:eR^>"m!muu&BRV)cٻB"NcΡsb{m[dFIfKjg>GCGż/'Wfϟ܍ quF$Z1W別aZ*?)ς9}]eH98nᕎ؃u8~ +º7]Cɣ?0iCY.c8_vUՉDYaWU 2'ͲҰķce}68 ! S dZ (ZfaE*-0`= +O0{INZm +ԤieK(1I;]o18-.Qȹ6+x^=bk%k (*lAEuuOYliU|VJϡ1[c/*qt)$Srb>]I>F3(t^ ttmև0I#$Ghf97ygZa{;|}NV{O/'~o +ӛ)]Tj,"ƶmj)U*xH{hJ:9*6h36]n5npѦh#A^lz7عCTE.]YhƎG7?[9旎1|_ĀJ3\Ɉd"4GN +漁: `@o'C-u{CD9=veKfGxܟKZtY8<ٳ"PcBZg٫Ϟ:mɠ,ɍ~[j;3#^u1Jrb +bdxzn8J(F@o;~wyl I+$V:^`8ʉ^/N1FهfEl#; QT^f!f'|*[yغp[GRpWa$#=uʏ ]2&ِ0%Zr:ܵB{DkNTaGl^fe>$3e4W3T~(`lGgZ;b(5/$ +ex@ wb 캎J}1^,ǵ&+ǩ{k4JA"9:ʡqtRtHoJZQ܊[DuCm>Ōy?_`MW{pm/iņw8NT"`3 ֍<]OR|ސ4h^X(3%74V?~W c[UWHf :BV܉ץwDZ[&));.bJVi8="L g_&~swKp?kR=` y834u^n~kJX(=AMCiFaFډ%ggvL +Re UJZ|& }^$X8Y*lPJؗUV˛50Wz4i)1v*E(ǨtJH5d?DNO +#X HXOO~m"Bjy4VDl:|` &K\u4.( @E\_ P^`s D6uԨG4 {§ &]=Y}|[6D.I]Ow$ڱd Ǚ.zԹe:$L>'ϿKKjWpBȵva "W"e1"hҰ(YX -2<0酨y(0qY%um"nDu1в^,ٞW +t7;\Ƕ( T+u/%%pjV#^,Q3ޑ?ثZ\"1on+3?Y c֡;9ԅLLIEJ{k1Ѐ:g<1kԩ YDP"!AVE"QqAƵwtge/bS`$cnmAb9)]/9XQlWy9"Մ ~)'G515W\'/$U?UR +[a_%-?9[>{gTյpp>k?<6gJıQnǰDQCp {JM$ F8{Mjn{CRm g@ mxl7AtCL|+-\5 +~$˿.T|=6e)f- twp)8;8Ѽ +OjU}E;1 +gB +?bc4-4OS^f*'[ +$# U"IMh&:iN$U&=jZ gLzCHx#DabmmӕjON kL|Oj +O;J[$W"ӾHʗu,wX$셌bkl=50dIz)jxj>0Y`35i% 'uXn +D. ѱyF `_dzg+ŅRS(%EM EWm{,"m6yt(IP@vo$QdžYK8?doJ ٷhrqyQi)@/GS@Ks8)#hao.&r.!*X +QQ%.4zn[=rhQ࣐vYƖ%hbNx#ux+*k-WÞc;L4Y,tT@-1OYg&`J0E@q*FTK ֘qA!Ԟ7R\S=2kr-^npL j4wmBUP|&  Q`#&Yj־M̏ + }>發x`E2'a!M= C?;؞&u!k\yi♧cTMê!ibbr^cik1ex: ѧFVV;cq|w&<&TXhn.MV#}Fi 'U6Si@ Ƹ:3>h`̦ -BҦx3 +9ci3QI#9y!?" _NCp^OSIZ u bצx4r +D +c],)t=%y:;RLա]/P`6k+0S-:7h( +NroG?[eDY`Zm"VD49V[5#phb-td[; }hZ T+8)A.lܢiۗM >t8g؝t0"M\ }`'ή{^P/`w ܳTHoV:ܞ{ќ(Er@­t{! +?Zmc2t/>*\B} ~ zQPن*Gtsܲ%ޜ\gY[. ̛д|@%S ٥L"r:?%q@O7-q)THٚ>`j.K6ma೦4H e +dMOaXF.m~"]7HPab5#+vJw"Zw;em(ذ1aAl=|y95>F##t KaJD\@lM?@(~:3B T \A>{"|BtԶ~pdS+Ėr |@߹䧶9fMTCau 6*o8Kܰ\MMABPSQk~^Om/Ȍ8n+˗H=Ds/d5nVvTYRg$T"7}:?ZP2eq+۫]*9)ic[mtT6=>1[n[[9~Ƈ6lQ/Ǝd7 +Dbqm/Qն) z~G['O([JHd[e^s0t5bN!L8f`@sDqno|m!e*( +`vw.$>#į'{ٜն ,3rIF&8NnK>dF҆)7Q ,0mFy[70 @ + qLxY&>$c]v6U\FڙTnn˼@ڕ38V"AL 5rY~)*qv7#r4'I2X6auˋMkGVO#xEF:-0Ȃ~dA^2H &rd4 c\ ?> 2l:$nXקAy A}cA.A +'"~V=-zYrsjiܻ^qO,"+H~#H[G/=2*HbᑸXzz$S==l8=Y.t#T|x$7 i<|ecJ==צ)T#KH$G"1Lbry$R>HmGz=y*yKH'^ei~(9-2dr.+##O%49=#N,.#G:y呅x{d!t:eBMҕEiZz$3"IVW$'IbL0I?^&j&y I`$7Lҙd};MQ;MRw$>k=M2.Y6ǟee7KBG%]z=]L钿!]R`j.ܥUV}˦QT&[g0,ڜ:V< +.t+^%Z\ހiofōŲQ72$ "R!Y^̚I!AӲ8*#e ]ji؛B00`zV %5bKQw0\Iʢ;~^ПH1"k"a3c;O+#WQ.,P0d$BļW&-.Z"aqRGײx}A,x5{;όɠ-\ɳޒ&-4\̐>̻ MyR O$G6Ze ^y;0 A1?iK`+އ}W٬Xj>Э#|_)"9 Ua[WG z'ÿm& }fܰH ěٸ +nlxU>,80 ] +{*sc_S NXMe"YnJM!5ˆzo<6/fdEљB@v\F2jچwɎ0.i,C&؉ hSl>QPQeNޒ4ͤbUg f"o]{dyh@|G~{PJQEñF Z2{T d~Aȡd Tu{ܗ=?"YM^$éaǪ"Քkp_{Zy"S|t2"iQS*m#`mF@K d$}HߍʪƉ3iw@m#ϺU+>Hix cÜtI^՞NsϮ5US"-6 V{5A{ Sx\i㢕4t<7~f|D;w kDY*mmǣfScv#\@6Oj׳LF}u>.Nq,c[׍mJ`H :[ *=I^ٸfZe6h'[!,4l!E)vGIhD? +W!;lj4r]t OO(r_(Md692- RIA:^@UCїW$Lyvᅣ`T3|Xf0O&T8b(!yEеOTX*1fton2>9_%+ԫ/~@K +Bmy[ͶT5T +aPP-[WT~AE9k`d؋= +&5(s1k3WF]y)7qw@?x t2[KjCu%X,/Xj׆f38?r6=Ag{Ӛ>} +Pw'uޠVNvHU|@kchQƺ6eddg A +Tt3u} A)F|WT\fr Qdp#hJ ΛUv4DQBCxvf]D^ңQ,i(KmnS5:@d4}W _XB'ԖEF-MT_mo&tedDM.`uB` -'YŸa\.!>P1 t8|+YV;&}:3M5ɷ-NIhL5fP+VX~{v~SS%YF!j,9c:A#5}̈́x\$IcFdAlw5d4`D0򾽷VgQ;C&VKɚILk]-Xeڃ_k_ +9;sk`=2kԻ:~WSUbk>] ۳Ix?Q(iϱcOgo菺3[bIRUP;$`oeVmȘ=j@}k uA&S?ʣ`n1/1nleqcPR^iƌ`rwI%:$ zcύ?* !O3sYk ] 'P,aR[`N}k>2%ʤmf!ɮArI%o G~]^Da5 [Vt͔7esi\6iE*[Uz $zY=YgO"Am#7Ђ{`/ƒ(EyڶA)OבpcH9IB[=H##kO92F \JAڪa^u=h9h+/?Z͍C& X4;J%>>P4=UY@ףYZԔ$'MNDxgQ;(ªWE+)P*jmBh!p&ddd'yΒk`N˛$:GQpk/ɦy׵ߌ0מ%&A;}6s[:Z2gY`m>[zOy]+#P0YkƬ2:(/n{X3a:]K9Gl9} )GFMf*\(XE^a'ͩSO z~sI,\8bVs`ŪnMMG)=|3_rdz {/+_ArDLHF4ׄx)yţi-5J7"kj@Jwu[&8>@(s+dd0r_];^P-9NO1$6bjeMY<FzfcN+;'z|ץ_%[;0.jQFrُ:޶FCȬdSa=Rj/Ai2޲C0Dz"$o23[ƄܥМܠE$k\碥vڮ=7bOzFWƞ$l,,A^jxߛH˪OD2n mvٳI'|p)83|2zq1n &1~ +endstream endobj 394 0 obj <>stream +HtWc9p!q 6tȣ몞 +IC!Ss=Z$x sz@ aZz8z$QbˆK +5Zdc>ɼ"VgΧ̾9Q!ٶ̧ɠ6Jl;rSqz&c(NYT28zܠ36#"nGF\Fێs<)Z(ZTʽD%t],6qԔg,\Pe6+Uk8?'3;:K>?ٴ{aTO~?dO$f~\Y )7-h98gzΝ]YMF;\pD~  >? t cԽtuBHɅxTFUYތ_xƁ39Y],Hf> -Ј{n\HTF!; +FLg1>Ti +M8aKw0q%=6 /&w6QIyI;|-͙kwʶ^"ny_/AAv$}Q{8°}^rQ⭘TQ,}͇6g(ޥrI{T52!VN%̓(6@ Б''+^wEۊp<g)x*I 9bʃŗj<)DPUoj)P^K/+L(v, a5PoM1AX'-3@0e1e|*Zf'SXʛ}m|{X\S=|:8scJH-; ζx}'Ġߝ.ti-O=]r=6omq':`@v{EQǭ4+tU׸`c1~W쑇@T} k>NU3fb4;lK1njbZ85efSyj:K$ rp{)beH@Ѣ v[ YyRB4y'ZztQ';0SmR' nBݴ󮈌GpP|:8Rec\6FI !,Üo)[^ kvg/FUoD,Xt$h1N]/A 4Z'UAُ2㋯תyJ_43vmd =$4lE T)R(EɅuS mJbH^@BSmV`:n u닫wqS1ܯihڤֱĝŽ4+WCS-.v$ehaS&R&d7kb_?_8TLݜ) ) {/󤕁TADVl J_ P`-P: KL :HڀVy UlܣcfkTzB I_ϟ#7t$*J)c,ɔKA( 5r B6 +62 ((eשPǠc1 ϬӖ%t됧`3Ds KcxQ)'c/pNtB%Y|'2c t; 6컿{2$T o㱯"y;AobG\|!?d6G|q" r7T?_ wHzCt ZE85WC'b]ƳcغHI[++BB b~GُLTZa bV|[98>Վm IW+t?YpH m'紮E m N;̪jZT'"s|5`4'~CpDq%{< +&4+I|#VjD0!:!ΊZefCpNmtKmb5(TJ1L3FuVIٺUk)YY Tě=Kdǩ5ajWIc/&DuEh" Trevw撈CWqf>jT?E#1e%lx|E$?OuI3U +S[Θ +7n]UQ;[a~4+1bu-G&UmW> L{oʶ*,с#K혐ĥv,"6e>XВnU|B05Lwt8W\6kIUP^D݁cl7<WF~h~ B.i&7-|w9;G "F4@݉ Dj2eYC +MUM;d(.P2UsNQHQ\1R}f ªkgq[jF'5?Xs7H X״ʕ5]u^ow +!ɃߖsZbX9#Di#./j@Ҽ:7G zCN{IkY"s[7Efa<D@tq:&v6o\iHny&Fy#uM{.kw)yUG 2尼jzϡra ]h蛒C1 3JD`VCݑ{Jpt'hkqyVrDjboS!\Jݖ0$+ș);o2(н?ʲv.a= ̞V͋"heac!\{/C#cHl‹N? uiCD)6#"mSn[a"<i Vvڒmӭdta9yu~U3UdSy&wTv N8]MQZDA"@CxFinfwGV+ +5d+R_χ6l?YJq d]޸l&Xէ5.r k\nУjd7yĉ#e뺪n\^Q=oD73$T]Ȏ,sGUd9AК+~|$oT +g~U~CRaU!rWhצkٸy2ܙA]4^ׁh=x?5Mbt?;RIBmt>KVaM{LĴ9UhRY mOf$-Gn쨎jaUV^p%^ kMaSZx{/ѱz}Qu_7yVZx~XzɁ]* 65\GJF}K4iD{Iw0_1vOX$ު>(ڕ,M`*\=I 6 nsg7H 1nmZ"P??u$ -l\\kqux*zۤ"ή+#YX2L\U6I;2F:m cٲy27ǽzD{똣X>wI ) KW#k)VV\ D])JpYȌgg5oVe7Je67B:VzopFf>2/`uE#U6U(IVʔp)2m^N*?h ~4cjwZe^BUāe%+l {a.@f? "eh$[f-*!@OӃֻNQl0[èkgcC1i?ejHfM\~Ӂ™":FX6U#:̚w̤R@ZEd,_Wp':dS1$yKcr*h-Nog}sj /p,KDI< zaȃ24=4.ټ)n-!po*B#UT pwdj7ZٞרOޗ +孥>f`dH7ioCM0(F;v|X=GlEg _A戈wkOnV1$1)Ժ(8ȏFYN?3|9ߠ'XUr/E1wQBb@\(i[><|=r識 a# KF2r_ <茕\`G-\]^~&H;:W[ˉJ=}GF]Ec~8rp(K}~e\ND)5x.lk:o#qj LDL3T#mQu#6kN|,wv#^>4g˜sɁ'V9g:b6x+.ub'@nPz>WB>b,8WI˫.4I*7X$*Q{`vXnzuKxr9I]yWfa^!ODZ}ȃXvnZ5ޏ95`ӯQJ~o4, Q +IQy=H۬"%EV)HYW6E<8kɍ8]W5fAuZ|9 @|H],}\Y[F2w+=jO0@[.6[vpai,.tqF[o*\ea|l6aZH􇳆Q8&bUh>@D(!\e-'JLkgu9gi35zӧ.;cV\4V]!6Ku@#~x +WuD;ġ*vQ. ^ԷtחYhfyQ}s!>pA.`r߯r?/Ncm}}O ~q#aMn3Dk-dvpn.{6Z#c﹮P;&P{'ueX\M~f%׋^7E~0*I9M~j~?h,C~k6ju\~"Rd?g#A"T@l?=e0X7qwv#gsIB,^E`g(EbkG:jZ-RTO$1}΃&4rnyqd >F/K 4+@"uzK;f$?:UF"MFX`&OHWbc'w;co~-y@Ϗ781lm!ԕIv~`"gwR@v0[2Ф2'+9¬ 37c1)TA|5fVAaa=_C,0-B`.E"T0EXFޡ!˜bu{!k_5VүRٸ=cdP̙B&H=!ҕoD{TU4996{LmP)rfֹ CHÌ" +2 zXRf"1AB(< ꮶl*0Xll/MzQ/u/u y2y@ah*-u8-AfMF5D7.um"\}kXۈˈ(&X*[ o +j}5JƦY7XGwz@%`jJl`@p_Zf{*遀\^\ X"+J/3Bq+Us*hDD=6Ī;_V"6ޓ6>ff::WJ̛oYg+:31/mid&m;vo)fM]"rokZobWeluRʰPS:NW-jԶ$m-̰2AO/VTriY%͠Ț`. JPɭU5}AȿtW]9p!z&tiO?EVGE[%Ydט4=IY~au2* G' F`N[T+DGAY! %s%;sQ6ipfgwG>SXKIQ 0;"D~Me ?7"HHu[( +Q:tƍg>v-ۻPϲ{YN.$"88ݶ.鞓_DS7ʹq/"-8HҋA|#3h>F7NɜhY);T/+|)%m&"EUU-9;DVz`[s tBmOMU3RrRI,"[~c_Y ĭFeg*msW]^D4Qm"uiܼi|X"p"'2]iB ȣd%@y+孳J _ %O|!]Dv证"}MCe钀ؐ+1sVŸy7 +j1d{!:=垟"P ] "ȝV(ۻǝA]S .pDIº^i}w aϚ\10~O/ve31WccF $/ʼnP Ѷ+R"Jmwqc?TJe~e9un[6uok5,ަ^cJa}1J"_{e9+ /pp+[9hD9ϫ,@@sTʾ`kUCrʨC\G0T0Oj~oz]-/WW6?ؚ|aWw? agpKVzNf9}2h{YР]z9@JVJ68P;6f* m`'op[_gV(Lߺfױۙ[!xfn#Q1\`Z!ao i.J I<l7Jv@"TDгq]I$#`D.g, \ r{Y} l{?۟ +Wz +3Ǒ 1Y-ݑ]ZAID\f/ST"u%:Xxih~_6&9wkpйNَ&0E+&?0<30k]î`lM`U=O[{6q{X6fKf +E4@Y؈NHߐδٹTˇǑocI9@ݥn\SG£t!,Ϋ=O': ./ٝoloDl4QB(ī/?)y A'<|1 H_ HQ}oyD<1) dm ũNk_7^u(k I-gc|b ;F+ƙYT2 ~:K9YrL^`Ptٽ6f(ߺjm2?@XMD8/%Z{3Go ]-@J2h4H9ԨZޥoT֫Pm!\k2i\eK>\Y&QG Pb:0~Pt1b%pZHAGݽFQ2w5^lo=tX˥\ +~T&'pjДG1fד}FAiICe6čA7^ ˖+ R=se#=t$G֙'1"v#e>QOq7iFZ/ w?<ݥue$7b HڏүMVZ' M9Gmu7J?z/` I-(HLgPBn썦}ժ_ +$ >J͵̶QGڑl>Yyq4,D(GM_ȹV_6jᳩ\!^Q פ +$Hyt*ˏ^V d9+Bl-xQ >/9ǘ<8Y~ +Ϋ[{UfО>!MUVvύQBjĎ(fDjY؂zX5|lHXTχ\d'c,Eg~mΨIT"*}yt}=-kP[ڏ\1d%xfg##9X]-{@}֓1QcmG]ϙe~S  2ќW]Z^ muX"{ +@B<~θ!5Gd(<-źXǮS%JbmUine?Crsπ#p'ku{|T͋` D]c2iq$۞_yx8:JXaI2y=;j83kBfIVizVGYxPͽ,.Rj;H$)RcoFeR^snPXyez;*R'0]m_Vy89Y=ҕ@*}-|3lskD]3I[&oWfo0ßR_7}<~C#mY$L2ph/kµ9ҦS`=Hl\HܺW-ݨ~yh<;'" >ܛ\RKsvK<7 + oFf[l֐yG]ځ2Ls0ײb^,P31zk3kALxfke捻w 'T&ŷlBVe(dz@q(aeКR=f*:kb6=1$'8P*t(4n$䢝G<)֒1q[,) +cTz`ZF qqE-6LCAHfT4z4`OکaLdPj p* [_:(vIsMpRrZ+Ѐm=/Xsz-PD?TGtEf˪X5W=G:Jy!~BN_H 9EQG ߂Y&RzoJg^4rL^2-]dN&ЈÐt _b ++O+^3g ;*O>dCnS{ނa}?z/[94=$qtin\ȣyAnݸ G8Q7)C785uçeѾS6YC6$ ?kEG7t,Zn(S7@BꆦtH!Pd~ǐ9(B>&7 ,e/piPS3L'j3 59 #[AFe%L 8{"j7E Qhp_te$JmyςFqsOdM do]KA-ӽQڂW޶_('j[7  \2C' Ќ?`aZAAȑP%=bů܆:eWn!C +v/{.Od5j7P(HUS`]F~e;`8w `K;:7m2;\V`J|Z=c3_`FPhY&ܨ9:O~fH : -ԽCYc!QFje5]',Qq!m=eP U]:˺n!uk_l'4u4`r!ne1A)OE:2V0Z^y&"t?~4{}>;]w՞%AnBwQGxՒG2JajW-\v.{7(Rf.ۡ+&Ba.T&Pd&'Q:-VMc۹:ٔ@X4/9t~ }m(:PSRZC%bx6OJ0OnlVDz[/=kZq5uNגWE[wBu[-B@Eg ]W qQغ-ę71?:^{Q0j W'4*h2j 4 dIw^@@ޝX}4kd4o@BV;Ի$CX %{ǝ% 2i=(ҟÓ> +=܎nuߎ'j2.rJ n{E#q5T\<$ZdIqU|5˾lmUoJIzrK(Ήt +U:z^a;v-,- Yʤ4 ^[6.䑍 lS;h hT=[ںUU0: |vƹ-wfX5R3վޚKq[3Y-NB6ZP.fxyG3d n{iƧ6P3t3C3mr#> g^4viTH5 + hHGt fs: (EiTYOÇ(ɜVʍH$t6cK\e-̯6yYKzIw:͠C[]#.+G5d{Hr,] +nJ'b-GFN78ʒ{?["kWٟ /x|{bG^3!2I{%cX<4MdFC:_هf/yn ehi'??S" xŊNg@0jttD2SH" ޒͮ[.At&7o]s4뇗%cj G`tDZҿxVvG wzW{e1?ePX KYuce"L8A= PGV3\ɭW_l`MPvL`vI)>9bK3_9i0z(}zxf|-Cηe6XPcݎ,5%\d_M@IA }ofA{NCk^{NLD0v2Fr] 7 +y/ 餘ZaУs^= DxY A3W(7 +yإ^_(ݯ[/on‡E-']")oXPv̛YGz/YZVK +4ݣͥn+' +~>x~t :pl>q?/[B?AloCH!t 8o/폵mSƖ!H[ /Q%O:R ?BK"1Q\R葰i2kuI!Zw3~i\ZC ~a ?U%[  +]v{>Է8'q(de `VCkaͥzjʡ o?` ۄmR7bI]( #Bg[ XMRVQ0]Ar}@^55ԛ)-=H ZSzdսs\s51_PS]qd#3UsTCfO/:E{# \Oa @v=XffgZ3<>Ҙ1T;i]J] ZɈ=pryԭS}b/ijue2~#=Q?i6uk=!$.Ӗ5pQe*#Gefʍj SkgW6c_!k@ v4L}tb'}疬d !.cC M#^Ͷ)fanVӝ ڨ^՛b\KW WouQ^e멠~UolŮO]u)pxWO%8ٍ}@{IV+]+1vzȓm +n-s۶y"NoN`k E4eNe=Q8WKH[V mrCܥP yid5"Dua',RgM5@>v$%@8\ kv巭IV<ˊKJ +wUnjeiejkՉUȪ73zOCKpUhP2帝ha"; }*nn!skp҅Bf.$a^Gcum○rt7`$KG r^kJ,ҏcAoRt5Ϭo8VF\%z .fUpdB-O ku aFjY{AׯQi6@} ãIDdNķ2Gu(kM=}Cw +endstream endobj 395 0 obj <>stream +HMoȵgS)l 9HQIvT,MjˆY8!iGm{*zt ,zhѢfdO }3CQLlvWB|_ͭ[퀏]3ڭ[;R` ,Imn"LIz򘊄p+7E,gD5diHibPk=p/_g~?{d +%)pc[ebY-D$Iد%k{n, +Xt ?n!J/!M<SN]~6QܧIC.ڙ3!D!<yG ɂ3"Γ#Ɇtźed5]x"ۂbͲ.VckF3gZƟ^0zB_Ii$ؖ4#"ϓO2"GNI8:r`p2ڪ@T[klm޷Fc&nt3H;_WSun[tҩqfi^m|,LY΍^ʚ8Z8tJGCNj#( Z1YCJD&A/A5;Dтi*XI+h(# +& x$_.`d5ӚW +F*x}mѽh4L@(H5Nsy(?P뉱 _+ зh(2:NjCF >8U V8o3@<фm0cΩը8]|j혰O:FXyׂ"^ DU LKzj`]+R -X]VlV0肆<~H""B,|acp dzbbR4Yt A UII0!¡ݐ% bhgcdZAi:(* ;E!%S4<,'Q׹ S9i9NP0iA7cIm2f@<˯c]"l}tD: 5tY_CuO糗_э溕!oajT8!`ޜ>˚VX+铗O@y"}->Z +Yo={'K q)' 8{zr%)n@Y~2y8n` +x7рnAKl = @q[yOZJt.=F9.@G +w(]܁4HU fD$WFsܚ>Ti(?A0-Hoc8`\IM7-ϢAZ(ΨdS$-oZսoUi"#y>^ǾV͞VN&6IBx8vmF!%5>O);VڀJoktot鴒9J΍+5_y\?p9Ο1Oaڣ'e+&Ax&|ͳ(XAηcFSi~kc~p?ѨZk/Qkv̚Y=oww0Sfrs+) zO`r.!1Kؘ,R.–c条Y*sHC"?cd +f) Zv(Ϻk6N_?ŧ}:]-…`b N4MhYpz4m2G%}29-I=у!YX.,0DŠfԉQ"KbR)"WŤJ}qJ}蔿` t|Pޙ ̼>D)C:2l֛` 1bV֔Ɛ]_pψ>hס.=kq + qLYTxP61nb&K_97/L !E=b(@BLĭ"e4+j`{m}7-[`j.cg!AԍӮ-K&hy11X5F.#D__M|l-H4עnjziMXjF1[ܵvUQwlüPn؃z쵹Ҭf=ޝ:`ush;icb3S#w.qض"!ю0]0\G"#Z_A.vn6*O+,2E\_[g Ƕ<(Rmpȵ>t!s$xa^ۍ +B%xyliEK؃,]L"4dz ++И51MV K-c{36uj&E*Gj~&'#)xFҨ;V[i:uYz^MA0N{1\4C܀ M֓CG[ktPXv9q^&6uU)q[)vY +J32bњaC!)sLE g4`1P@^U~.yi* +8`n;j+{`ob[zK׊mvV:+[C( w0)^f֨?DЫ@JzB/CKDP"6(33U꥿k6rh "DH(vFh:*CվR>^[oj-z-.6Ti5 $оgyJR9<,dedc7YnmOϰY2\,t؈`[υ eo"dw*j"M}U*]6Ϲ[(};+|̓N}!rG]{8|>>~r{%'N2sS1*O3 TX,JH %_.F`^lXS`-¸m{+*'@q`P2-󱺬C;F>1U'+נ%ʞ؁Dҗ5{4TZJX? Ȉ%Ac`SIokgG'zRNa77ީkN\c@hNhѲU_. )WMpkȺ)1;JHZ"/c }aU~u{6+ԍlкeHW_M;?6}bo6 12"e̽.bfsܴPgma SMtGUKxl/C"A礅olF$>kY/5ep6UytSg܎ʙ4w*8}F'͎L8g[Gw8Q;M};}hO_G'wo{z8wn|7PYV4}~o齻㓇{zt|ylۓgOB]@;|ًA,W:-+$HeE$+ ("" *SݷCa$+)hY_k+BYvs~|$JG_˙o^9CvY^">t +dAX_>7t<0X65b2u<0 d[S}O *?6'ꗊV:{^O|ebP7\P\~LRbFH}P5O=dnt2Z^?l _] 4B>AU}|.<ó >%QUM%+QbgIhϧ%"qbinH0Xҝ}r <5Jߧ`ul> E~aA#t 6K~WA+,ZU(sЈ`]݃o].8`2]6n]0*͎]̞]#3, +ruW=̤OZYSqż@"*u_loe߸)wqf+xcS(͋˱nj0-G'(e}A\P^"g}g~ bŐ :$(w#\7B}ґ$!Vu ]Ҍ J\;t9;XN#9sf\&t$.5i#:9S.[aJL*Lei̦.LV;|IR-;Us è1T[nUAEn( 2mjIw#y&Xnf/!Al6Hu5Z.:sl׶P@' p-ӔE.WmÔGǸ!XB~ddGL0WCƸ& +&\7Ҡ8p^Q%fcm0vityHwmU޵nR0}v˃S+5g0jd7EȄ 1 +k0ua fn6EKO-[ 6!'axhmtf+cV!~}g\j<m8P]gWm qY:V u P+LR#96N席SYKgZ0I-0#z1WU`u >7Q)o!@ܡ=& O 7knȐaX.`[,|df>j;Zx +C +Y,L1[1Hq$}"=1d8i"aj!N%gjLkq& yOȰ1 3C1ce9q0ZpxȟΧd=2 9e>wu4#ŬI\%"zF jE0yM kBJ +\j޲hjRD[%&IUe]$K*/ˤA:ئ5UMUjVTlck8`4<ڮ/5M7ŹZW퉑:r1uh2:/f!xH0abJ;Meyi!fژw"ӸLvʄ6hMv_1SW2HoV\i֬Mw2mv4ҸV\aRMvywo!ƾis؁ezGMNɪR9B朎g_AQ.5 +Dzwו #ad{7ˡxz|YBC#Sm o!RKev^~P6cIL&hh]"{,1[xZ"eGw'4H-ěrk9|BRI8qwTsӱ0\ ow."pEnpԙN;).rSLSWVҧ=Ԙv̂vQ;zW8 +u[g~j|tꍏ7F|}(׀bR<2cCͲZ.ń+O*j7?.r8T'=t^3DDm|^|.oEaPW +z[b~0dx}i6erYϞ . +GPz>GMoˆu Ҟ.po`ofDyreֶzF7]!p1cŬaO36Rڹ&rT\^{.nn=vr L? +zzx8}'Wndvr6/^]km)& +:rp6Lf<[}r74[aF"9{B~|u Tv`+1/'iY]ZAcWp2 E(4[9%̕mj/xM=6 >|o$1jվ%Nvɦ.,C48+@# ֻ/DDN}]: +C2$=;@|3Hn:ؓgm b%.y/FoT(p@t.Ł ̔\|[)% >HCSijꤹ+|s{XQ~gr*j32[ւ]2C0->3 (יN\}b_ToB:]g)ߴ @e}[TFb gk0ExWun*o! Z;{w8#CB`ԕ.T){V&).E0>!CHT2Del:4Q2 ֟j1s쭯Eo CW_ =^nӛƫ= +Ť0`<=,3,+ǧsE&**8α~2u +&oI9ǪqwߙAH ~( !ƥYje;\6GR#h[ +uIUtZm}nݵ;RR=E' QTCp0d+3&wy /`0k) NA'{,`©k(7D$nByTނ]qs|Ay*Q e2k;wAMiy<4OVh[푘[Жo*߶PZ > ; 4_sBu=E }$Ъi) nܪ cD-ifn;>3Іe 8 'ztLdC;ج=xvf5*6M&u> x$n{ cv8ي&5{~{E9(ݝx>C@5h`Ɯ~p%-ގt-~FlHNFJ.g~' BWbdG%yknXm$?5p{$79dM @>3DW'#UwHN9ܲs8ۛT~@sŴ| 0*3o҈ UA&tgxU +4I"Y r e%U1!e, &((?~se:6waA頪3ZݼX;_ 7^ U2~j%k"f#37ts\؊BA] * EbF̂Yp_fB>.|: :Lj6-G\ߩf:4q8ɛ@Aoi?++5*]{忕 |H'7jQvbM NN YC1C${ +|*Uqs?LzܺXE,\@n~ڽVN֜^K&iԃ uot}dɑݒ9<+Ts +CA>(|c2 =bN`vQfh~>,uF3V/ ܨ];i +Q#׬JU4k_]PU䍾~Г1v$1{sU` PYܵ餻D͎v131`TpK[t`J\&8`4/x]@oI[v> +;RHPxodH^0aa'&ծC^> ;OI \DܷVl,Ė׾C;=wFN +D3ɳz琥䭀 MB7g`/h%~g EYj,jw1r*x(6\_/=R 7ٺEqY+nӦa%QAղ#cЇ2x>&zT߇TBW~-d,̫Wnq8#m9bfmkKj{2JZMW +ɶ+ذ& rk+g,{?iXa rhwsIipyʱZQ&U 4fk6u?h4}~&]aa† LYCYY>/բC"< $<=\e"`R~~߳gfxfkTT\Y.s^ \|U \~$NPPH9/o: oIww.,}_?Dk5( ҤO\ &!4i?:W|{wdav B=Q[*Zo5>IlOj6^{&Zu%@v ]3L\z d=]CVV:v8+5:ݟ*<]u(Gh~E䗧;r0}ْSitNQٕec-XRK**Mt:oel_[8v<UgRx]D& sun ^gP@2MsuSeӁ"_^Uzz̬}St#o f{|++<ND!&ͼch!v (+ sͨ7%N_:-}/&. +4ߙ>؏'M"%$ybnU*u"vEqϗ{ӮJ9zA}s; \4w 7j ||<$ܤ:Kx,DJ+ji)ܥbٰ滶4yiԙ/_L:IM k?]:dy'RfT oxOs]" 5>NHL^p #~C(dnY-Fgn~ox k_EqUvy疉Q z)ɡ`S~_'蠀LOԷSvn¥N_HsVf%P4輾tV R}8p"sHOV0?WL/5Jq00ttgd%\'HQq^oߎU)IiR:eb_7P;!1˂^ס6/V ## ;P=#씿Y@a[vz@B:: ؚfAp;M1W["6^nSN ߄*4p/};|hl!Zp8ZR/rWmd+mҙ +'F=t-osJ ~5V8tD|vaiFn9[wI *]S$h~5csSi\{\"t"}_yMĴav0G +#Yc6xyWژF?&մؘ"n>{n] @ h)DX>w0q #n'"ɱ[*2.ܒ&Ԛ7-/H^*/!hɟv(ME%Y$LZVo=FV:}OYxf h"~/;2¼ +`D^ Y3wƙ !W*ʅwC'^Uz&vOoy8Kr{N[׆NQ\z, Ź7 3ңvuh} +{{*Yuas[Fn/KykBs땹V©4&PxU tYc]oCkmH:aW'?=\3.3ۋ O`9D+}?`>M>Vu;=35X+EצWvfϩP|[99؆'],G n2nz17<")we+7+mnk-E{^הM;ȵ0d&S\Cq\تA] >fTDHrR L-Qv<| +,=qL-!_t\X,m$ȃ,OzZQaRj + +,@N/@)VzVEGUg9zFC" +jp|WƋ {sh qNOj:bO_+'3N5չd?ڙŠ#=`Sex:5GTy@ .)aĦ]:\p_ ec=X_,H C+zEgk!Taa~:R2;vW4 [gpl#('i=M*Guջ.;=/w7]< EiRy3Qexe&ef|3p\ *w3A[VnُdtKNQzH P~nev8tJFrh(c .id[Zk^ k&Cs4掰U;(ϟoU**[hzvtjrEdpC%zPUWbܳ'KT2}!&>s+ŦVHU+T@Xضk>NO1<0iOs"rgޚ\R1J\<GZjʭ#)d0+٠!;_nu#xB&ھpicXZ.pŌצ|SF<=GEQ/֯rF\q|,}'-֬2SALZeEe<7KBf׊z~D-`[WWN[4J\s~_ІQpyyn/b" L,=_±RIItUB +oW +fl$ _\"\cjZxw5Yb﹉7b΀uߩ]&R Dyr>5UG0ZbbWsvK+lD8McarO܈}bz%mO,ns7 t|E#r'Ve~/x~+}bLw"^6_TKf^@2ȘʲP[2YLbg"v6cNGQ$ϡf`X~a̒J9my~ -W[;J(NfU\ӌVҒm%6)q5kytGn=n: 2Ak"C0 L=ӱw{I#X&\D Ƅ j2](OD{2 +хptwv=9**D=.FqEs7tc]偬(Dѵ`&L  *!ɄYԯp@SE{Pz{ga<on5V0!4ݭ6pjo`A#M`O]į[VEț~*"ֱZS94>7g; +;uZ4@$\k~$` 4yO'rmZ_pp\.ޮtgMnmGongyyn YU7*1t(W*T#0a`w]OHCFmh<7 ;!\*s$r Ƶ/Ծ'{HqB$ДZiٟE松9wv=P=C;cmt'bB k%mɢצqli ɖgtnsz7Z\`}MZ3|WkƒH_yibk&jN.nQŲ0ISa1#k{ ?C_s "ћ*Ec{s)l92_)"=!Vϻ懶õصEUx9fؒi3Upw1AS[3e$D~@-W䱛_{CZ-Cܤ":B[ 8˜Ҽ9po<Ï{vt7gjJT$N>˪3 e΀< fw=m'^a_{'ײj۠r ;mG@(1h.!&.k;W \. +Da,h.R?|ݛgkrnߩNJ^%KM~W4bR2<ۍ0'ֲF7.렋?1QPV̛z6,]ILu-m~XYIc$Hߘ>#a$vߖ6L*v[?cX;$k""&v$s 9̈́Hȸ0Ƭ}Et:J't__ҁ +nVEɼ[J5@Y@BWsg3a֤)qʣ ᖏ"C|&1:(A 1Aog=e|c&fLns_޶yݛ2 z &i^nd^̺(%_7(4<]gbjT~+ь]zv9s9R=Ua}I*ꮕlғQ3A֯k;Y5޸GެG4xxr^2Y8dsSvҝceBlnL_@,MyRV* bE +i"ʖ}s%w'ĈBYnd-EV.#q4eE@`r{.H,׳^5X8'.{OWI4mg;X(|m 'E"_kݱK >5|`՚SI,^ 4V̼|3n--$|B6?4t+Ұ:iUٖYl]_8tvLXGu0{AMV] +L9n`nfl_SzҷԌLP\\~17 @`6/ou)zb'|׏.C7LU= vʒyT[Z/ կY)=/'G_T3\Q\&jQdM"Kv֙iOB,o3ʸ =rH%3ԁ +~+j aC_fj/>`VHҰ~ٟu'$r5XI[ yƹ)xɺ;`se`p'eR_:nAW1ac(aA|b8姄':;@6hqm}_$qlEBADr=wkW%&ǦE˞[KsX%5A񥛋 8 wcNS +;8$E[k8mKƕ9*5CV5&9+}.,ll>UT88Fխ~!_ Hͷ+2τ +ݱ@D` h)Eel* kg"u7$&y)+'667ge@)8U[~Y1jĉ4&WB&Q]TdErU^\~^_Ӻ{2sOpwsT% `!vF疤7yL`l31B%9ʳ].'!yE@mTgbW؏- XsI:%ڧZv# zπgyPߓw: T-f'F0Zn8Ж{+PNꠕmѶ sE{n(݃b5Ĉn݃x0Ml|z4FtMӇXunV +]0LPJm*?G/Z{ce!ch?KQ*[MN 6ChX^iU\>вSd;lU->nK\ލR%,}%5i'TAMmEc͘uHqp͞#Ϫ]{s+u@7d^U92\i؎t*s"1'n4cUe5_ H#4eD* 5^ 2; ysZ-b7\bhr}[;NQfBJϷ#2W۴4|Z7He=Iƒ]FO1u ?nhL3a؟b02֎MTA7qCņ m2In^oᏢ4RiS%Qx:ڕ8Ԡy_!5%rL˪oix.&enԺ޼XLp(; ݺVrɱ$e~z~&[?470.|_eLYVx[UvO:]k%E G+S婍~0ԁDw[\ +e~0&:=Q=(&<1۶;\=h$N<ǔA%@&u7_g6-ؼ2!iQn~MN9,.esZu$w('Y,SW7<6W&Ys 3il/C56vbZ0k8-L}5ʺ7h4o\gs>xP#FTDkȬ v~̡i?ZNL伻:V&+0zw.D\$ST/0?|XVM{8֗LDQ.ޔRZuБ%e ?O.ZfC`8˸87Er}ti8`pwPRsvvߦGTENNOeHGF5Pa+ͬPe=pR4a2 2$YN ,HyuU<;"3 +*McO.t(:٪[JqsGO+yO/5u)Q/neZ׺@WAA?gN, HCAgqIlDM 5̢nm CQ+YqypoREuJBWIV-3MnfVϷn@vo?y:zDۻ{49dqjj9V +ìRc%Je]S lt!}ހs|e%ش4pVQu壜u/}.*YhOWɼ0OqP%Iq.ˢC@N}wcxrDzn8ʂGX_ntNH*%y?AZ0((}Ez/&9*Ivd.9 ߱ +*Q}$/&~Խuc.U`H$l˝HL5oafT;'wloE =_On)ν8a (qrک]&ɤ=YKRj{6SӴc{aq J/*҂k4'(2VYw]99Khu1ҧjU_2;5s M"B*z4},TZ?W͉Wߝ"Ӓ@:p˾'DUEyBU +l:~]FYupǏ$IVh ǏYttĻ\QUEJVB ׾+9g;u ˮ<:sa WFWL }{7`݋9Zi׶&Tt^6QMGk8(Ȅ۰] u_~BG;wat!r-]퐆/OicMS*^8vFW]T|W9ϟPdhA^J$*D7d(vS&ĥ0$@N޶T(~t.,W(sfo֪_`ЦttZCiۅ3_Z'X&y7"ݒ#c{A[|y^+%)/c ^t +iesEz3DBTL5`$Om}1Bğ07=lOOՓ`ֿgX+Ϙb?m<ݨo?vU|`3U&CvE(9J[xO;VPID +V)rԚsI@yԥ$wQDrO:dR?!TzC Ġji/wVn#ZG.אw:j3kXوgiߔ7 Z cG Ãܩu蠨j} ,t[CL٩0%@8GKT?&`KLm~.)O)t*_7*^%%=HIÇOt~tnӊ:P }K̉SZ"Z%Fli`͕\}yIw'on {t;UǠ%L qWsIn uqB7'JrWH7 _@c) <2/QDwˎxkM 5!)41/]XPGٺz޺5\Z Ftu|:a9~3JKGYML^"k^:k3}[\CF5TƲXzs&Վ(ӆfI^_n z=^%dS5W=JƼ84%5*vYi}[Czk?w:RN#]mif`iZcup?側*᳈ +Q.K1a`kwklkfu5rVtg ^kD؃`,+*e4f~߭9 _)$ǖ^qAqn~8U{@_SZ-n=' +j>`|c7髬>̤#ʢꋲ̵ +p>]SӚۏojVz ,t0*͢N8Ο!orيoZA/L8g%BĹ&塖޳1OYq'"bUMeY ^jY+vh@> Mg}k3A[:R4:fs&ԥCe%|jj<<3V'|{Ne@&w!)!A<%(֫SRQkV6gAej/#whOܒ#Om!5(+EEv<6$Z 5뽤-+` L4r]g[s)F_f) `IeLcEYl %0)gL#+ʝb|۹ü9&YQl4$45u AcXο-4{3Wa22rw|eo0 M%r&QQEڍh!ɆfV +{E5 +_$8gKeQЗ룪lV׽U PQSflugQ׀"ỹLҧBuKO l`^O6(Vl +P=0-\婡{AL3;ELnc9r,9'F@V]&]C9ʙE}?v!4gO5HEؒ9 + +9vVRw1E&t Ҷ:eR N|,v-{&.zBKSu ZkEySlH4|͠ڍ;hAp 5 yhP/ u)g-R{5x9 ^rvMWOgpBblLS-1:y?Ȁ]DA3ǧ-0A0Z&m*9%pkK_Fj~"C2c`I|i^b"ҦCNN&?'FD XbyKL`0IR8Jo wɊ st u +X? {n:;#E Vb[\Wngwh."Ɵ?z?3td)et>XO51)te3K't( +Lf'!wDnUlnٱ%nk +D0 +nMЍ -#94C :]`늊Ժ +$_z&G7O[-9;KJڠhqA34>xvNUq]˵ WWez,33A W +k=lұp+)BiAf)1';qcUD~`k'g;a,(=I$GjyWclzr t\0e)%*lPPd:`v@9& m-zlfɔSB, glۤ20@2+s&=sb;SW7eW8Zz`HaƲĤWL݇l[_GǔU~_ag}NGtyjAY, X@z Q`&;y%;үY|MҎpD2 OlgI-tg-Z``t܇a+;AFBh_g\1O"=vX1qtRԲ&j]XB'tIracxOQ{D>Wݝ<$.gc9Iɸ>D} 5ȭYz4c*ds&[r*<;3؊g6C(jcnpwȀ /;65~$ZNrpZE*Ub;O0{=rquVijS +iuf[@=,)n7S-U<E}يcҾKoߺpT; P^}ݜ+u:Yq@I|Ҟa^촆gi0|-{J9tn %P=G=mf(CvNՃ]؂a#9$n>#i^ptF7';=ʷVbOitoWOcZsV@Oo'L[8>tI"WMVWNbVgbe~F3 ̈ p&{vM( +f&%)b96  +VھЩ ]K07|A91Oln?0Bc:5 Um+'DvJdM' {_ދrj+HաZf׿I6 2Q}Gʟj>tv9qdvǦY$+hkQ]^Hid }k!2aGM<J-j 7i8?{݆WGa%*z~VSp TXء2-8ZYo-,wجY-+fѯ2|⤂u4(x#+ +&V%J= XΟҕ NZg7BZm,L!:ƆH2"H/Lq(GvO}v}XrsZgA +CgJI4ۯn9/TK$A.`\H[t]͒zVk,6gMK|G-x5g'_72GԷbb '('SWpqqALO"lB @@X /u'Lq<'e@W=T3A6QCX;5L(Ǽ?3z$ܠ,/}5@Zrv@l9lo; f w>6dٻR)o0)VDהWIu}]X ICx{ J P"=7|ߧ_, ڼ@?`U8/0JxwypYqHQMzzkA&=XWr{4Sm\!4P +'q-)ɂ=2mLĶu56EOdJ2;֩W e5:$v9]E%\(z-* [$%‹;99~`g!yg#w$~JLAֶ`۝gب< k-ClWney; +:`Om8WڋI;rtdHg%SPeK $6Yc@Ŧ%emҰ7>46M-( R!Q'ЛkXjR]Dzx}9vK+fJ;j3 \Uɦ2CN[u@R Wl5WUjxKk햮RՕVh&,hɓ(yew"n+=rj "IK႙}d@5"csQvO\13!O/XSE6ԆDXKu7hڻ?i_UGc+ZYhyw hb."?E܂%u1DEtzKwҩ?Vd\Q۱7zWEf] O0w0٪KҞixreO=DD@A:\gɅq5E!Ԙ^[v{X[<ʗ*grޮy 7ty\KL>c.^C!sFqgCwpORA,BfHK::ܚy'YFڋjpގ@]zw.C+2EF>Jc + +ZuS,~cE{u@! ="N)ֵ!VLnud;@Z@ߟt U$IE/AM/s;BКL0[i +*tg,*kAR&A/*snt+qSj̪z7Uc?2M%97!,{9 nΏ)+sOBk&T~u.cU(,r&mNRs5\E)at{])*?Z:5+x PSFffxl%'X#4UMʠ8s:E^.* )CP( PL_殩y72+=HҖ4^!T[MQ"[/śç{K:5zɬ*(a."-`῝6Dء\o*\CԶKYj;CZݶ3\.>0|)}}PSyjAz$A6AO/|7­|VkڀF9'1MX5v5hU4fbxcXwu xg6xW 9$]wN (k -c%ߵ@r_@5tK`:YQӬF,/w +P.o.fd|# XHԊo^HlP5jYf>y@NuzG}Sp .7s\k>*wV37Ε~>YHd_ѲO@~cլC}v^D0A)Zt#ߌBudh"I.?WgYXYZ|&iZR[XDE0 IE sMogߺg'S0hY\ +0@%gݸÇO479R=^/*&0 +_-ZB2oSR4od؄*c+3R81xUKyϝ$؋[6 u>XO*&\2lTn,%dx_ ?RÈw}:fLjօrK'?rNqzժzWj0e|g AWwK-,/#e?Ś0 '< %%)IP}AC1TikzR"[h{xGm{~ +r|sV,!z]6^syx 1D^`eMujo%~6> ֥+;nש68Ⱥ"{.k*$4'QB(w<~Լ`^]R{hH36A 2{j vWOvl T.\#;A nN]kM0? (l2Ivu&1*F +=FvڌIe}KRH\*>|(/l};c^z"B%xt1̷ w?- $a@@i)֓L B_b˕Eyan}33S4d1"(|q3 u9&@eRzD17侰Li'gmPiSHFd5&qE}?.PfTT, f/M~f¬c7F;CV^L5?qjNJ;"@L.rԒa&Y_E˹N|#l _0 +&>Iq< J1vlDd&z/g` ?}r? c}q=QMI2V6d;'dGqۭDzi|?HbIi> ߗXe[4|݄| uOB- yN(ߪhXޔ!|XO N +X0ٽ;|5st^"IWO+-'Xd}OM? .L , Cڵr/{$oKy.:78Z.,.g>(8.C}x?R!.J"LF@w%tJ\-o`cKMNf+DBQuo2w(g)toQmޫ*~m"uI+_Vrurge;9d%(tjϪAڥ %%6+˜,#[.d]#7O^F7>c:l#j3J>;:6F\ʹm I#TY6fܦ ²%t`JGs#Ӭɶ?qJٵ_PJw| + 0$%5xt{/0 +N,bٓ}Tz?1ȇ"K:%DyF~äK]B;1m~j0qХ,Hq&-̏!|GRe^;N).JoNfMeڞ̈́K4 +ckQGrNnxfS&Pf\yܭoX? +T,UyanrbI~\r8nQca 3塭DoAAEEE2Hc8f[`]cګ2Y֛EdXKUg|LE 'XSf]ф |kʥ~ȑ:4Zst5ITwoI?7[Yjb5 iNY/leNDyNgrJFj#4%^A1a]݉jˉ>x};4i-Ixzvd"h5pqAJh&TF8رѓ5#BUk#n?&3[xD4qEzKPz9~_Srh5),LNA^$'URJ\ҩL ]+ +djӴ]YД9Zh1wuޙ VG2d+n5.b>&poTI{L# Bhcߘ3=l],8؞8)ZcbMˋ͜3k2Š; &MȪV5 - {Ȕ.e,\LrHa7X˖J{sp [nuF\ g8O)BѨ7܆hi\&ޤۉD9UeH$kY*2с$H+5m5{$ɣoiPmfC-Y+c@3&.+/vB`dFOܤMŸ1ͷA<~f|jHYڱB4=ÔE; z+$t4MUkQh.˱'ފR&z Kv*n1Vs8{rPM;uK{ #7$q5hۑ\9JjJsj}0,~_g}q8j,B{yLr~3ű~a9Fq$+fMf+/J?иF% i9ȫk]4Jޫu}  Ɂ`?Sיsb`5gB{~ԡ nTtPDs.XM?_ +_䝤]T+ޭVMeUT0L8yжzcMf)J RvT쵑K!OI/R|#PO5H%M]͛?7:S=z֪Xf\.O@`4VEI#vh4P~Kfb9h׭C+|ʰh̛>HRJG\r1:ԙfoR[6"+(k}NA \bm]|Oܼ(OA`oقA*3" M1m%#):`r;H[ߵ⚎>u.󀩌>m:\}\ w~a|-BZy-PY<ֽН6A lMqbWRg=bHE&d+cZb3@ pNv|v j&>U\kǧ^6b)zlP]v&Z6vLu$/;@2ZteM]]VHidߑ%5W.K\vqߘߢw*B{g-1]KK4=l\e7B_,Vy-*7;umQ ϜS5Y$[Ip#/pjXCP4oX@BUlB(P^G.5;ܛrxs۝vcf/Rf3 + #bSgH~_1XЋB"+yd'F$&1~!^ xC8_djhW#QɈWeDCWGG*c]FӾTz#c9%O}V2OBߖ-z7%m]hh+j>0+: ZF^ Fq nw1-U;Pا*Lb +t!piNmnO^m^^a` +u&@QbԏuwFoB}N+@:e :rh$yIv}t)TKq`󂛕*KUcXҨWքW"j YzEc(v<ۥ5+X+&kmdWDklB=pB48%363:>7\"F6u 9D|p +(zם240p=㶓Kv\a}JvӶч$˵]ق율x0ȕ'ʦw1 1NAۨO>Mxu^4Z qakx??'70T[Er)FevИ|`58LrZS1iuS'=bkv]JPb7ӂ8>8 0>89w->˅H?GO-sk۩tB/1(ڞB(h ~u8HiuyQl)8";CA:6*$N<$!M6SRVm*ND>?v-YK,&ڈ#VNԳQ +iES:4ja_-<:-Ye pv~耂BӯNITAAde4 ˴7>1{VFB0DKbꙙmEa2;R ډCfvrx$Z5j8HZ8߶- + Hxwy)p$RK$io?ooǥ@mVm OE=/ mū䵒؝ªu% Kns@fsSH %5L w(hxVp{RCfׁ hd?75#TWZ\q7Rl)T%c.R(|6uSlka}7M:ߑRcZO<p=lwJv +!a¢dzH #uʣ}irB|4濣3_*C^N5̸g!·!?.ݒ#=6O}[.o.{P@nrIѤoBk,տP.St!댞.Zs,mV͠hh:˴HxfeE %WU4iB{b3^' _Jّ>%a)&yAeVKl vt(@v59L{[*x[䛝s-z]U\ac75C7`l "v= @ I:ߋ ̙Di ) {#]ic&ۢ0ᨩ,VEi }6>xyDػ8OA5zq ԗIedKI#l$D/Mv999on'D›x2r}vZ-P8d +T ?,N-N<%6A3yr}tsxcƔV>OekD5ezN5st&Ozi2*iq!)^^vbo=U %J忔7-񁓥4񽫉7Apf_t;h9>T)ߵvuc)Ӑk$F|^[rGHtL CrZ +(sD! 'b]~sZM2\L\%7~y۪"B(+uVhJ/uA,n/2Ϭwemt]Wq.wu3v!PJ[fI#&]ᵮtB4FAa̧0;M*ngLZȦB.-!+]@X^gYY'mmɌSÒ/Mi)LQf92s{fN|tU; %I1yBn64 ]~UE~ +itSA yaC-pgbgF("XM~6x7,۫% f)ϊVwߘ~,w#$E;(%)ɡٌnm8W9/{37 W(w.Fx++|Z(J*GT-aEQ|o얲2tU)1&3XS)ƣ-a֖\_/<_SsLQ^+-AMӀImugr;JU wc0~Swֳr"|3qDO^g$3aЖՐ xzw9vu{ +T6|\b-[]ew C}2Vޘ]YS2ׯ-3c?)+luO~u@3Xb}'J1WyA˫6wfm 3oŵMqx";qPt!( w׺ueC ،ޯl+{)YB;7ꌟrcEfsY4/I |#3$Ήwlc+qqq%dVH< 0T}21Xt) ❙j?se"}ds_z3=6e|x$820 P/2d +>)r&h.-+1}Up]!nJn#~ {Jۖ?tZFI%ۇ é_/͍kPUaq9+Ar/*MVS>@̎j +FvaU.2F^<"l9 W(wz۹= S:R5PpZV -[_w6َ57[3}p%v a.Wt8Dqą_rMZp\*w7B`ΒzXzsHs&N߷#e(A Xa@ec| &:֡T#WP?&G>G ҲFA#thB .[vQSҽo!Vs8sR9L3&} v Ղ>Y>b?1[qNNZAWa*R母Y<ixQʙ, p9)1G).bn +QQ^@ +g]dThcogٱ{(xBZJ .% @,E#@Z4LAM܅9}޶w"Sٙ%߁uF|e-V d'5-C0lJ Yp|[c[Z8edG]x$̼.؀n |5騕a|>zLlT|ZϽg돼&%pt<{*p̋ +'04j2D' U;޺%L /]3+9d94Gc<\N.,U% +EAmPDĀňYZo8ۆ`~aZKziU\++GAnx^ru_ϑRX @WU;Y6>}ˮZr. E:~UC>V\,SMcNfG[UP5*Nlb +#+dK1և‰0› d k|dUL&_YTfڝ 9qNGRs +c>vN| +oVHʵ2cspdI~AbjaR5DrngLXmoϪ`:BZ[mHϹ>]zHϠw1( Gv?e=89MP̡"xof,L3X_J׷~e"$j{=WHۡ7)Cus8.N}Rs,}4./ߛ\NFyksy{$5F$/x/H/!7\e+zϲ\5Oe SX+ pd{+^nP˔V~Y5_:䍻R@e2* XBe9l^Zz֙^q"izq};Hu*QÛ:Lc=F 0N]ֈw-C2NwE4ނ:6[ ;Pɸ~4GQz3,<\Ķtw!M+9,>i5|"jlp_64.@`NR??m +`@;ˏbNoG 02R +G@K_SPa-0Xd-~n6&^+0Ѫ^h]@5ro|g+yU{&dop7QrH]z+}sCs ;J5ʶ3瓄2;7[U.rמPfeƣPyE3 g\I0afL<1ʱ-jcMZi<0 qQ gȂ֥E!'6즞"^BbWa7,dFpKF HܚZ)$hw{;JJ(uVn;jc} ie6(׭ 4po=CKᵤE&BdlDo `ݏ7(Zrzvlkͅ%qt>űUɐ og'0WLW̪Z.vMUK  +@w|Q!QүzMЛ:IڡFNܘ:Bր`hߎI ț}`>]!{P}WivkU~F)3XЯΪsj3 +endstream endobj 396 0 obj <>stream +HtW; :ޠ!Ižh6{q;1}8|ssI$㝈.ϟ?dަ !f9ů߭k:h8h>]}"8,-1im"4e~??u 䧈U Y :PeUQ'c.F2 d > 'Պp{QRDlmw~-x5T r}bP'2+}¢*;vS|R]D]*E&;TN1~%Ź;ٔN LYI+C+4;L3d%+lM#H.B`i=̼Qa"!}b2l)ijMZbrk$}ݞFMPCW{L\P=*{3$qߥ8U;7|ǟ2|rb>[d^ǼdF&ؚq1~ &ye=DX{䵻R@0]fC00h^ŐS lta)9*1 Wޱcz}!z@2\@JOR]v*r뜫ByO;Q-.ž5 +bZњ&zȋeãrAwob]`Uz vjz:UY?~5Ӷנϥ 69 .5\KD4Vahf!$ /$ UH/|ˡE|lVB(k4HsQ{th"RJKcm8 +R*6;,T;'FO|/-YXK!/)X6;H{uN.t<3ࣶb S i6KP; VԼ"7.??x|"+OR |)cYY71Qp4e(#p'1)5$f!m7bS!e2Q vs7'i65pPŅ.> |)RZs*!7  +bL6pAy QX +h S۝J/ Lݢ8D}_"_vZɟE} 4TBV.\aCk1.;Q+OQ YMPYDN,f8SJ*z%,0B3KQjvSԭXL>c[P#$^GaCq *SWtm cqcv(OLE;b`TE;_Hz$D%Aumַx*7[DEf ƜUJ3z h?pyDd: {*(m6[[ڏ:nTWuQF*lvҾULFs NIJܨڕN L,ٻ< ͿS +W{f9ɇk +{8ѷ +)6'BRBKS,V>Qko7۰)ؾw>[tY_KϭVQ9_HQ>"&YLMM 1Ty6)1xa'f\·"fc7 *މIS +^ȺiՕhu!~La~xH56c_0:~"2Bߦ*&,׼*u%* ЌZ ^`^EN0j6dzYy{f^ :JDȚՋ*Fp'9z(1׫Z۳/c9_J OFn VTt/ |]F'~| y[ޜxm-pQ(^WU+ cgͱ91\>fT)#?= 5a+5B0c}lt뿊 ~YA1'm[kxݳplOYPdE(\f.h\0YW +̦|xj9WϟJqzqׁQ:`<X*EZAw)QPL +5?8ì=ݩJA yl)u#k[h"MجcA]W!Bе %|΃3k[ܧƂ?M^𕚟ӛ P41!v*&PW<)ћšc]#Qt΍%ƒXΧr%P(ӧn <l`tEЖ=#*]f޹Dک6f DYO8͜E+fdc\bb¬[m @W5({"Smsvġr$\C4<ݽ"էF=4Jx~4 e Pu,XfY|5(ˡ)_ő\`A CCf3m6fU&̲#ޜLQGJG#fHĻ]et5N^UЏKYX6 T]-ieHRџf)bQ.s>8BѰ7kG'_ f$?ί SRJ?;emppHkO[#whe[׹0bVz%@UҭFFkje#c|#cSbߩ`!)N[m ai{rZle3cZYR%; >\DE9;˵Yɍ2N3~#*WsN~p\MTi6JAfL^m1YkOGL'u@º#-X~ߑk21^Pvİ]-kdiS#lf6yK8Qi"suF ++_bHÉ9pƁ/{k#T8z2vnJFf'i#\AM_ +g`fsW^b2K8 вE&,{^Ns}\.(RsBx;NAZq͛}OwsUfԴ /wDzzv˷zoxTzbg[: -E+ qk#%j/v;UJ‰w]wv^1yŔ8Ϗ} u "/G\cARKxNUNθxR !p*N޾eA+ ?zQ|=@oّk)Qk;to%m~,~(EI)يZM9+ p_'w`l ɪYST,>mܖN[oLk1W3XZBsr\doOU緬0\-ד. 2YsC<)&L-Chp} k. Yy<8#~Z86wKrϦ +b)NRkN,dH p[vs(tP+zxg'&Y߿+U|lyU'b!eƻN\ *︿O6]0y81- _,*ь &D@Z>N)s9om}5֖@dMJ2\!bLUƯZ蔪C&ojz#y.D\O3zbXQpDP<Odnzy7D:{Hi'W)& t<(\fWxszx`uezCTz?^`@꼜[hۛQ435Mu}ZI\s@m'ûԴ%|؉g cXѬ֮. }wAb)0k`.Sq'ߧX= +'ݲa{TKj ƒ kQ2 z[K*hed]~-2zM*{2BP׽] )ͽ&{f,k@GjG$WlqvݔPԛY"CPBL1(Z{In>b4ĦqHah)k;(-Q*Q~Ud+ `::Vuuf~DEzTC[M:Dԭl  T+6.3.g|42 ;ӳv|hs<}|ts$h ;m/Gi QS]XyiA2Ff+8TRʅh/tX,+` ?zFAfedne"'#u1lZ_, fv9 +M+CKOF^bSzʿX~~^vkX˙ uc! H-u4oUҜz!qp8"|5rqϱV).b2vy{Y Co'סqcna챚禭z&c2PkO yӕ>k37{z5oRޑ}ce,YFV"/wqӇuhVS^-?hP섦U,ܵ>-rqgv63چ)^N?D}-ˢ-+"hRj}#>G7<靀i:'*OתRuך<ȎL)/3/dS#^7B"a F.76z^>  跙+bV?9V3M4I$ǃx$GJ&+)wjwxy&GCӗxDYy^]#=~EX=Onzq#_HHOD=""m(9DozD%9fP!)A;7="v!?B6ihɏ@ћ{?uw/L~tdYɏP?|͏׍v#`A"Eq3_|jr *U  +~ H_Һ+y!  8 ɱ$Rћ t:̇U?켗MCc l2dH}1$s/dHTgcӓ!/0CML2O!ݫ~QaH`D;.B_mV?Ȋ6b#>Ym?rW͑U*McUc ~'8g5,dΛ#ACf˟.gD jeMiF1byyb$by[ڽtZa ~ƖeãgV'K4i1VCdq>2S3M:9E\oiYULm}k|z* $S"~myU3dj{P2 DvYC3L v&yl4)dA2[jY7a%zYfu!]nAKD랹ttstt L x}Lm6+U*(418FIt ċfmT(b-Y\4 0yt@tvxPA^:qF. yD7~=bߝ|i^X_6u`歋)e:ܜ3` HMA){6)wϚ&V_w 5Qh88kvQk$`=c6n{U4#2s[f!}nm +SUCC~>XE .0XX:E 8v%6\T@rN%*ɑis=c"xMϑ7)& Y xd0J}&D~C4$Sk^Fe/r2dݷWnė?2x/і̛r[zGK<|eN >а̦7r,Kr#=۲/jW'hܹ &(I& X(ǣʍ [{6Ibdzl&^4F ,cx+ 9iʯe+v#Sњ,׾ƽtwU)N/$|϶(WK(h1Pjܹl,UAmR(:erXޤ;DW5Zh #_^HA)c$|>^Eušbmn!A'ͳx-Q(ݖ( +ue욼}iߐ2P2U P͟X !,u6Q1B[uONKGW"yV-^I!(pKwlX{XG@-{hj%ղUyI_`= t`Y W?/oWbn}!O"XJlQ?GǑVm2x7rd7$N)~`uf"dQzf0k~/BvoR{^֋ȑV +줐9Zc8Fuo@*~Uk!cd6$0Bdw+, +Ȝ)v tc 26n| (h(c9%ymC^{ںU-T{E 3 Ţsߨq\na0,MA7(lD=qi#JaF_o 0pi +5;0VHP+xJFM6&Ǵ^ &>b +yL{1@>&K ?y pb_*϶3 +1RX*>sA(d=6L8kx#{)_ˠtTb=jӢY3͇T@@ +q#n,@RAɝn0ف8ZEţjeA5U^Ws|ɺ9?H^de%pn}oT{)+)7n>w [HYDV71)Q|pJm22vUkMͤOVv0H*< +61fBK]oڍx:~";Oc-g,K h tqG&&]ssmkU"4ү i$(M1-}97.[;BNG{meYί~s6w7KhHV#72/ٗر/F㲒ėi8mTЦq@o?߃fZa`h]WeL")(Nv6ퟸ❦<6Hag[U1IJ쨝K5>{B{cc2m>ج;>̟ڏШ= /0 C99H侹5XX3\s^@h^6ETq R}Nr ϦR/yG-ەXU}lKӭw.UCizmT β+;jBj_\;Bż^=y w0|@"2U^mx5]qc>o: gu/Cțl$4UIyؕ@[C R6k1x/"eYYk#_K\V:rJ@Bq>fihbWhY I_Etdbݐ[\i|>KlaJ{j73w_@}  ̊O_;Q_ZyM-IajINL!fG3Wtj XvW=# +Y_%,W pPNh^5Z-7EnJ[~:5VfAOVO{|uwȔ,kiT"m XA^DD8P%. hJ@킗t+}%*RPv@:,ݒ-DbП-G6jj8ÕPUzވװ\eF _FU퍳<cH*2߃ I# }8MrE + ,N/K3Ά tc3j%)V'jGX6@q8nq'^!󨥉 3E2Fj,ۉE3)SVPh52v5Ȯ_ō ^jGk@w Ή+x߲$[^0n[򯎒}@*Dn`0d $iز9f.Zpm.hQκ pel-stiV^9:Z 43L`A-/gq2m5%GO~M5i| 8%Brf8b2 ˍ*{/DZ,!7Вq7J^8@#!sHD-g> t.,f'|qPiKO +_k<εABޣj~+x_lJ i:#}B +H}/Ȑk]cX: e澮._?^6+$]ґħJJ"Pj4#}Vvb( ʁ idB'Ve-/jl)lDu ȢnĚɽ:"w}NoO?>=Xxb/}Nή!HwLmΐcD!vdp _58e`zFx{L|4Z1+,ͦ iA܏6~$AqE4{[{_ #̏JKCs7qU~$D12X3s{I%UA=o-5־ow뇐{&TgگP^`6=_*,?@^ֵrJ |e$eLuGqSm0s>d4rrDM )YV}õR>=s +>'1KP@``᳣&)EqviĵT?h[%l8x [ 5}n\V^7v^UтU=(J[F!)ldY1xH_Z,C i:HTE>9*ow4 MA3~bߐٓbqF&XbW7<دE>%ݔ(,.jZ8T#H! dA9 +ϥB.H.}aחi˜,Czoe9r Geq+)Su9Dfyy;%py6׻O[ ñCz,]rk^,prMݪ,s(Fr\fB9_D5?(Z[s8O9n"*eg3rKx@kce5oY=ۚKo1bͭV{IpaN7Fj&?xj&0qgY獅u`nA%2Kڵb_mXzf[4rY4v>GFV)7ǽgۅ |d;'(D~OƢ@4^x7kYOW@>vhH߫`S_vr[-H\qıukZϬBt;w let%-3[D'ޫlw6"hȕ+As.KĴs:EbкN~TOo">to\cKث-mDH%tn+g)4,{h:^D,gTk,vӰ}'yC51tCԏ;@d^ȱcC1}cY  dT?Վdي== @H]nVII s_QFO*Uw{98};P#Y=omfIe;}fWLݙ?+vПOwpR+=^3k=X21fvʥ;s; FxrμXBde$3rf?2sU9Y[&i;J +:Mdxi[6Y±ivEZȩx~OĖ65?_8\߿ + =$.98LƟ@4XMÑP0%T':s[G|qd@:?.їJe e56*Ao<.9f]lJ~y*e= ph!aLYi_ EVقC1?t X~хs ^Qmh"VZ̵:Z6̦p92bniUjT-+w O(VJbT/2J1vxh7b^e'c (f~E)YzpŮk}WWrMsknI…QLLd]a?̔x|Xx*y |nj?7{A6 6 ]f/{5f/vpз% !/*s+~VCoE]'rҽ| A6. X}ڛ5E]@lP $ ]^ku9RC]L7u9R({$ +"v^8}~fX0 B&c._\kcw oxxK\PcF򊏠\]T+>$.4d탸Vy$.t2'+y/oXyYxbR.nkoZZph*tWwBM$=Xh`VVZيD\wF.ǖ.)ZskO$&+^V6-/Rtt\E3)KOǐGf˫ج1up563pQ6#^˾M~I+uA+k}۫}|Zo`Byz+BV~Be>y#5"Zɠ4*yzn|)s0FxJg%6rEJwPlRkSVjRʫ:vY3uq_)C~jY|:#hz ٳcK3 vz[/fqeYk)}CX㱍%=flI܊ss"!u` bVy2?l9W KShNY2Z(f[`z|h:e18.5Z9&ZmҦl9VȻ , ^r!Aܳ`,7$K θFuU+2XyO#VhmP[\~8Dl+ǺR̍*a$@CD}AɇgGݯ"Ư\!ݝS`/TeY>F?pt j$g dgzjw }z(^ -@_cW5yp%o3N +oRCZ9xr<JA̎,U: |>"x\|4<ߛv5{fGO^b!o3EVɘ*kM/zs6$?V9?5(ّUgu~Ç +`/d(5 3Sk>XǟfF( "2.CY\W ٕѓgOa0{k2=<t+#ضڮhdALPHO ~ j}ӞJաN3( +"4?k2ȿʲ}wuR:i16p qSa[sM `ϱ#]V"h?[ւ4(Tv0Di.k&=kRJAP7Ykm=!iP5TiA"@'yIkN$@Jm=JG׉0ڭEx$=iy!m%S5pcR"ޙc xcc[zh]V-eh!G˲\N0&k<'2ʘ2SBG %l7֪hxe RH#q$TKl5!K||'8&X'oFW {UL`a7ʵmKTnUr95ɜl^1#@2G;K/떋}}|u@ǿ~ W2Kۦwc(}`7Rw3s+}GvCGgȧ'hHEo6;S,ɡq>vRjl31e(#jjePkx;РX&{@N9xdB/A-~t?am2~"EwDbs/i jI1 =mpkQد6~i Ҟ#9Eh/ DyU"HZah:D\5 ڞSh"XJE+?1uGFbvĬV? 1`CJdKb|2_0g O0;G'}͑/C3V聁LQRJmy,Omdx.#Xs\,@G`r@*31DgG4}b*rE{Y*\.n1 +?WvMjjCtEOl皫%UX_XI})fѲ>ՅfsRh<L>="4s t/jFCg;kCxjg-,(J ;ѕ^Z9 +[I:\]IVeCD<$]MT]qMފJz@\p@\1LS>0kEM +ñƒ&B3Ÿl/ǚ{T_^ƫAiVĄCMQMI*XH  ϞǸsTWn1t枒^PV} ԋ4/љ+e_,\_Hu]`cfԮ>=SBe1z2+s~]~5*i[A >X6ƚL0̉,/xQ <vL*paY:¢MdczHҴ loԷHu%Ide}Y^u!(C;=-\e/zçEHgoEcOFt%\tbqXyfȥ!~Fש8WVN0R@ڰ-M^Uޭ[1d+$ 0ⲜHyS^)$0 *l:/q@*n_u,/ˎ,gsa-`*nb]܍;bI kĆEjf$|c(Ε*ǵ,]4 ~툉U,# XQv#v Nw%1@-ן@ ٱ.dm?2$h{/+xJ >:s'H:@Q4dN OVp4F` _hXqKdQ}O1YWa/gS[eJ(! Jho$ %_ֱl[bFw $7_4+܇ǖm`;L, + GdS+ڐz֝\&Y䚧|qx,:|2Z" LY|>]lN/peȡĬͮ4 c^8t*euDfeXgWBĒi #TںZJnKqх7yyf"F +M j Hk|lrAiz+lJB0.`E*z+]3*T9D9cxឺߜ+֭;d`l //̶cw9!^d ׉XtT7uf:^E]&D*ldj~rsudbӈx' \jCu6ME)^. Xr .݋AIwYg,%sƬ/5OשqT7^.hM+^U;g,aK 3K٘ҫ.)4a/BE+ЛY3?S#mZaƟ*G#aWP{¹ŤIHqQFbvLgx\fslPFMs/6fl+~%Ys|JqUa{nVrcftfQv;ryD ^6hKDo>=wQj} +$'1QRH Nz-f@M7B_d֦;ނ/Ө/^$ Z4y5ovU^*ce6"}0ul\V,p#Q}ՉL"I`GC$]!8 VY3m{A)JXJ<8 qӅ2ϤƓ"ҨCNձOĐyλ^l*vN3* MQrLD)?({\,-z>#?F*\@+\1aE+号pSrgwTPJ^IU"ij "lE+1鴙!dgz/Chfj٬=+\IysidqMD˱x4S:{fTFY-X?lASgR;]Ḙg +g'B2gr[pl.bt.*ljlBM($ +nXɔE+(jqYs(UȔi$ƮLOd?bYnX!a/>99g9gK*P^=q +endstream endobj 397 0 obj <>stream +Ht% s~;hH*n pMU3Ex@x?Pi0ׯtLJ*05|i +Lb(xa\8i%)6p.,YyW!X,l_o\l `)|ugt#Dawc +dE۟LfjlQjNp ,}_Dz>#$d}3]U=qW'4D|f"M7m'< +2"u*T}l3w׬ {Y6Le9'qIp.SG ;6;D,g%g2cv8i)fL8)$"ۆ1,B$_]%y.cܤLLjx;YԦWƌn t~veMO'#ƝW?y]cZ.%I2$ ţLp.oMb:͢m81ͲÛ>\]mUq縲EhE:8TT~c rUn5]=Nz_ +/{]11.~{5Fe̓\qجzNnjuc;zԫi \>'b]0{GݪW;ߛ%R 2yU,^e-vuVvάo8ߖRk+NWg3;+vՇ? VYXf(s5Q}OpBYkw!c> u~V{`{"/r]G WvlU;iuovNgShV5.v;eߵf|p=r&]3s"+YDYQG|#ي}m{+V*؊uL:C6gյuq8gFfK:ݺ.עC'X=k@P) a.zK^}ek!$oX!,Xoxy;x*b\3bV8D;b :Kx"u[.7*ȉѹT5>+)|1eAtV?=]sĞ>-zNf2K=E=;Sʌ|,t[Q{[8{J igz ,o[K3jwń +()a}uݳ統6п4S/|yunnnAeO^zxqq0q]v#Ts@!P=v~5_&dg}}_d(L=aǭPYfm +Z\Y@z/J׾>:Tœf[9<(odji +uˍ>&|xhp*#|P>+MPB2'Aaӫί˼^ӥ-P9SuA7Y7čV5:3{D܈ku#UW4JP<pco˪ n(O M +{56 ZɀǾ#{C[ͷ>C}PtV[cK>9x5T_.*qBP o5Xef71VH<~U89ߍl=s;Yl>F`5!.eh'N Aߌ䣜b:+f }!m2bE155S&vnZ2?I#Z*g,(ak4W]k}S'j"N1EkoTǵh~x?H&}ml0ep,Rs9b.ĐB!~sFO`HhC=D@p73-?׍o`}G0C:mNsm6󷨸n(e@zY x[N'-ˆ0+e Բf@^hCRA.ae"27-֛u8j^Z'$<9]huAs^/wVcvMؗ$l<(e +O1j꤇KݕonۈbKto f=T'ӅՌge]Bl$87ٕ'/ڣX*2.e")Po6ޑ˜jm85&[ZBӕ1=7y& {\Soo\CS^ Fg96^jJɰ9e&}IL_C&ޏ$!Y*b߼t>Y,X%럈^O֟$?ws~ 9a͋h˹4IokA@b68l~H? +{дpAz&}1ws'v!㋙"`1e@ش9 +U& ysm99CQǚ{y!W6 +b3OurՈ'r-5bxhOg s +ЛՂ@rNf",e\fzgd-֢ɍlj]M@*paxZbcGZ5QT3C*=uwѾjN!h5f"0w"Ǧj74U0,(B:#2XxShi)ddf;8ǙIqG][IT͍%ZiZ/9[P9_1i: /ThBT9, ш,wʿ`1s5ŝyC0=:C28\ǎ݈~ + +-À8WQd}j!{lX +-L"k9pͼ&d&/5 l%>_&ڸJW4ap{ S>psWTjλME.@25c3;'1qbF4hm|~r&/1'[;B70RJµi3x894dž58ÏGAi!Vc߭}ƥ(3<2ZK%YxQX;K2òƸC.Z> xemcT_ Uأ 6XiCIo*& Gl^'cb۩WL{/YZ4$^!\'=7v%Y\p>>[- m473XH*ԓ*^^R8*i^|2[ݟbv[V~綍^xkƜLցոߛ(\Z&^tteVU{rݨX5)ɹVoEyMgro߿ǯ?Wh^D/BHXh"G{N ēDG7O7䯗5! FﰳqMsЁ؛O$z7kkmv WCK.+k{%%o_m~ƶEc{cv_02?FQs`x[@= +x*_.mcǤsαo?˕_$6?Yi#"< 3w{Ddc:R <rI}kcǠs̱g?˕_"2?-3 7hk7raeuNb.6%U^m~ƎI眲r<&;;tޞSQ]xZ7@S ]#m ;h4>Mzxo+a68qp1^C `g8"ϲtEF49Ȱ3Ć?n^,^7$!,E6JX˪C8h8&Y"#,&Ro; a;0vzIbl4FV8Dg*Q >Swp\H+D]H:=7y lUf$)z6t2tl0WJDޅvs"^C1,ixyPYU }9tyr$ +䨎e%W&Lze9O])eq \Z!f3x\#DѾzκ !'iuaNgxI&=r}- S]JR勃˜@!A Qo D@֐nn;6H2:Ӧ7)EqHĚTzr +ITz)sVeσBFe@0t+(+nQj`Fl!e0%P,؁ַ*`0WD2ĺoMqL,C Fϑfذ̽Xy@~uVn͋͑!%&${9(+0"kKhrkyO)7 v0B-ݞ\6-NvOKXC[t2vG[=Tƺ n*DG]瓠xtyap16Y|?@FC +KLZ@RDm$Ggr*[V5jrQ DW]*{#ћ#,Y.stKcr"ccXT-1e>e*,ZѩAbAo3MWmP{ߊƭSG֕[Σ7jɲ#Ub[ ޅ>EHy'>.] !>DI:0}[t)t_Ywi\6^4r6>klvq՗pNGzUS.ѐ@ehJHu7zX'N xhc!/z|TLc7vƬ}⻻(8NgZ=DF @ ӲAkmlhgjٌ󉳶QiNв+hyǙ`b$aD0w/^KJ$ ~/1>9CY΃eYGñsĊ1H}y11J.>YR;t'e_ W*Y쁺vP}Q]7oU.2_\%2>} ef .T%}9$NVYR|rRiV{-n,M?>l}i悦:er2Ƨ:[S;MKs4]ms٠݋6kfو +䘄x^CK)Ce>.O_M2.au{~ h4/dV)Y ј9 b*X 6Ms$܋-)KҦCf=5Ye˥5nGQ/FNZ?~#{w*k ȹƖ}cª9 YDOF6#XBV/ |*L"^xXPQtQ:?̘dWMuaPq2 P#겞H_)wRm 7)13)} {0 [٬s~SPQX/*yAXj.iiЙgzot?,6s]J&e4LhUҧDV&ʦOIg_"W84!/nU."tBy>+OCk V$-l]aw^ND{"14R$.g2cV y%dcF>6dy80DA^En1ShʊjkjnZ1.pZhB"lTsUQ^LT" ۟W03CML4C㉭=Qo|x[FӶFs<|+sٚ^J⪥vO@D "r"lCIPB-rJԩ3N5=e)(pp +g*T.4m=Zp +k<2yO.-o2xA7b~{"^<]l)Y|| X>yكDU\6 1!<<\7HXv4ҽu=I _keןكY0QNߋ ^JҶ Z1$)qpLCF\nE]a2|'wAOufn]^2ySё mL58RYj5L#fW4彃w3)7Pʘz,?ퟩqv΍AkݕX":nq񡍌X#bLD6$<̔@]Lo6޹Mw&0֎X\e9| yH ϯ$rDم^O*҂ܛ,\˔u x/KYb3QXH VIC>}AM! 5(|?ć<٤zhךDl-?:+)W ۃ2;mIUF^7cr€)<[s 52 }dlSRl4ܖm 3 zW?3ax\&Y03QпV!U.GJiREa))|[{S(UDe(0, +#R)߁rD m^y@/V^`>HɳѦE bhH[T~EC]5}#m7/{㘿w}3{Ʊ,'oYu\}g:3yv;$)EU"/F4S%bsX<f'4Ua8w +,;)eOX'^-ǣdBt^޿.#yN,@@=#:,\HjNu$Ӊw6.-o* Ĩ|&J}Oә{GT̥ᫍ< 5_$Hnml h?Ϝ^b/f ]@/Ur@|BBI-X ́eqHAL+}\@&QYEH rD6[X]𮘗9r|w]߿d[P}Xgŏ <iM3t>lSE +!hS˼Ӧ3>|=Be +Y@BzpYgM. SN䗎X?_0U:m;9qזlefNTnxYw$'nGNC5b-Y(e??[oط-"q\~H= 8q&kUW#j *4DkQb@4nT >]oŸझnje35d 5 ;cx0%4S⻮W6ce+F٤^e*шD weh~wrBxQxrB~lzu :`q2+nc:G6cteh"t=m=c\Ju!acAQ: rb*^xBt jO7FOdԑ:l/Hj+ya-BOƛA>^A$[d>]K5Э H>`|;(H*^hG|Bx` +] 넨gbR3݉_79/r +͹W?9q_y'7 l 7A DC ] ưTzc/ٯ!ώvzi:2ibݰ&3mu/) `2Ŏ43H6Ly/x5I}=uLJIۣ #O k2z#N2 b\V*BR*_1 bQmp-r\XѫX>Ǵ쁥ّٳ[[ЋH4HKspt-K! : 2"A|a[I 2SJcIy5/0$ +4RªyΨ c}G%$0N.ևA&0Z{S#eЖːH9R9r^Hhr=>g\&dy$ [? =h p¹Ǿ:v:F]ݫ)>Vg%+oD4Q,#o2Rn|?4 fTR}”2*1A %]F[AAiȯJ^T.5gE\ɟ7I9ˑ*Jٶ—zwd1EE럋*5?x>nBwJQ)-\3Xm+БLl[Z{cn-KB.$,<7f 5N8>ղǍ=:bNcD֔a|*!OVp#ḄA D؃|HB[:x.뒓ĖFL Ƨ/֖!y .l g\, 2ݻ8ۊPv4|פo|QX씑ZyTT^RosL7cM+ݦC6U*5$!zˎ>dPփ3H #sVU`_7xO8s ~2]7{]ۙ=ڪ{\" GɖږE\v cJ AY~Y,D(e˖'#UHuhZY5qщT`HMCFakhe>my\'*G. (:&  HcQR#:b5­QWSI@0, ˆЪAB_d.E1xTGy@TEp,@ik޳3tJGrS?+[-m\&ݔIG;{5 g)U~qZ,kYꖊ"g5! A"jtt@xak~a*Wu"Yq̪sV,y".0*Z˜Pͱ` + \Bt2¿m+d.ѱ]SA52 +˱$'")MF"bѲ ,O5X\q'\ɼƁ_=HB$1{Oq h%է$ؿJz4r/( +Vڟyi}Msr\UF غOA6,SWݢpf@(q/'vDdh'eyq"ђYS=VMfS#eU걭Эј֙0F{wm_zv 7H]#~h3`0\`\`b9‰ 9.%#,R +)j2 9{yW޼d^~,x@?I\X爢t>b"\^t  Tkˠ쉍ӤҲĤbLKs$Kc$Hu_52'޹Ni|_ 7qEEIn Ģ8e 7بkʉ eScDI73<f7cp0N𬶏1`fcB@jmn{ϲUt#}~=y_]1r~İieGaUڮR?nˎp= -Й*[L< +A(eHCE9!ر83BƲZ/jlm<'~᠐';o, QAq]g滂 sZ^?my̔d(fۀn{ߐ; 4fS首, 1Vs̾Z?h!,47_gdb}HެzǞ'agZA$7 +gڣN!"ң %@eIUV+s $vY{sfV|VAi82l&?y/!wr"e8Ww h eaM|)pE{ΠG!"6 2Ūb.oC#?βb\v/mw8'7mϡfi 5K)mbğu;@Ի]ܪxc+9ZV!F(#*Zؠ=JCNEv< w#H"787Sku+}:%FC4,MϪD4s[hw\/*Y;VGA~V@OP`Ֆ -O@DH +.񼞈dZׇӯ#;jQ7U; +B~P%qRU2%8OF/MMe = +M*̷*\OU\\N +7WC uȋsUἨ +_g'UlzdpQU2AUiT_MUX/#Ut +2N0 ZqqU* U/F Bǘ'WAI,/ *\}p@%=JWm 8#U*y(' U=*|:j!+xsU$U\e_\⪏T=*a{}s.I\lHqsUP-.JѫYaV0خf쿽IZ `˲l_OT\S$ӽLC +XhR0f-U@KՃRK_-'ResEROE 7 +[,4꽬*VzX*毥ޫ:FdZ+!)Uy+S9_ AShh$ƲٔZ\Cn42qFUͺ\Ox5-+e Ses)3偭$m) +ȾTpkiz@ +kH|ׯ% ;m&Kݥ hAUx$/#>pӞ +d1bԬԃQS{~]7%Z4!*+)9c*ǿef3 ,3ȯW-p^go\gu]P(俅DѬM-@v4,;~Lz{>-//ψ8"Zl|T^i奠 è(XJ5RUg cYCvMj >!6SVfh62*vᬼV#HæδU]. + RخM__L2س@~^@"2oDQ'AVQ􆌰>3 r-OF *2UR ٳf **6;i\`)(djx$oyAA ,]CPW$4MDZ' _F%4B'h 7]'FM^HYt!Ϥ屄EܑP hJ ?مIO4DE+,bRٝ1\_VA澆pょY^lb/cꚶ +ol}@4?ܷp/s?ԴN]l{,`gtkuoȀہÌx^W Dtɍ˴U=%k;O~^H{O1;42@}h\F1 #h͟ |V@q8' m.^s:Driٺ`Ug +endstream endobj 398 0 obj <>stream +HtWK,9 +ܷYߡN&BͲo1fw88WD +q|mdz5qKoDzӑ_ x^|sPGw*s& 0.ls ]*~<}B@|9D 9ϵi6\yy?$<诽ەDl4IDsPA}Y[ڻ_α-K?qnцJ|\^N Nm#Lw"˕3FN\ڇGd'⑲`ҋ5o 2"3߲I»d%8nhqGp*TckZ_4g q6X6 ΋NQ,> +l09:״ yLOdwNh%MفYj(_gK\r4okvZ&[#(JdɃ ջ+z*"9HLA?anL=-̓pdwO3ݕIO&efEDvs{{Z9nq57"qo; :_ӊ$*ME\+L}\ D3|HV#uxG6ͼHB:8rf(2"p>N0v ,rr1Mm6m̤l&2:d"|bexy.ttS=7zǠN  c"m ^LZ_Ń5M溲=N% +mfC Tqlc!/+{kWz#vO3MTܛ1} +o9WBWAY,= |"잮~9چ{ Աn8**$_%,؊QH͉*L6$O%6 b^+7lMKL"]E6cb2 se7e$<8<&yz;3k.NoG\LiJ +2z`q-{L n짞1[2f ڢRGMXPUܫ9Zw<Z6j'81#̏@ש¼Eu;gݦ{%x^{9.۲ i m?8C?;hWJ`u-\λ[ly);dG"  o*t Czu[Qя8Fo4O3(uj`^0-̎B"t$CUE=T"ڠ.y <!xc!uBq3;qc,Y}iv [}E6f;)Q~BGl66T]ǝ9 +3 .wـCFQՌf4V`\U D)/Պ-b +2pAV9D`K :X`k=[;Tk^q.֎ں{=¸_ -U5#kt [fAZYn0M5zDٔAFaB[2lR`2 ?:fZQyO9!Y\ʐJ)CW)?k|Ocn~DS03S2h +eC]g6߇YJhjB Ƽ|qvSK/P6szA[f|!x֕ュrsE/6sлjkgaz[=->BDa6!J~4Ix֩jBx=azI%RkdWEOc! G!4> BXg&W?{7v `wtTׄfg{gfeUڣ mL#k&y ++n Z1ޤsq]JsPNY .~Jf7 o##7#{Uxp]=A }e:sMzVtxŨڞ%,vC\1et0ĹQA>Y=,E֮^(S!g_L>%o;<ӝ-ֆyb~\k1ZXT}{oYr*6Ri&]#x-]`ͨ &%P*bqUQ^Mֽ$W k-~1uv6$hD^p!YZ''n{80Hs'!S6n0%JhŜLDFУFz`+ ~hAE[~1.?c!МYeYzo$: T7`ICa1عs$90=PSB +l3VL B9yx,x/ w> d\!$lOQDyZ 9?UfI +î'lwشʶ7YPI GM_\q|{fɡu#Ẹ1s\|k1V>Wd YqT4;#VLQw48'"c+Yn@v9e<_FTvԬhe eוnsOh4vKH8(cI= 0bAB<[[ٓAe0c*&"dS^0HVƐ]^.).%;B-B @Xe(1XJ ~Љ; +EY/˛f,$549RF:;$AM)zƑ42/$Plaq5Yd(-"FHyvi~-'2>~vy$!4KW{i*Ԉj.@lX"q|lj44ޫ?v'EEbno|Tԇ̮wrԆVFBpq#̥іqEn myu.9)w?wAfO|S.s +퐦X%9.W< ]MRZU $ + aeN+Bg2cv E} ΜNRIucdH{eo9uo\J%eօ6kX:wD J#ƍh㮲bVoX͆_HE([tg>hE-mUeٻG,j j}݀Օ9[rY,9ՔY ]}<,Z?YyjbCYV5-CI4uURdZ8R]z k| Qu.=oxWZq'C}Y/>9bRRYJO1]3-YKkz'3[>BD:rJ?1@R,k, 4Յ8y_ilR%z/RU-z%R[#,4uoF"4$b7JQt? `EM$'HCģ"1vө6/xʁ2/xo^3{3FjVKCXԎu]nۙ  !#'t$؇eB6$@$^hls=$J7jps37F :"ug#ħ *%Z^HHwnL;f &plX<\$EȞgˤt@x+0Xsҡ[Mڎ_\#7ZH(PN P'Vgﳜ6Dwb\};UL,.۷t+W[[ Ґu iA `t8FY=|Z>366*uN i*QxBk݉aefubU3{影Oz\މӯ>K6f~ Zzh}yQA쥃Uiv|Zuo4!+e82$\2َ]ؕ8*ǨG@5fsqf߿ LIm!n%+÷!,- ?&վˢ'jY="&My/2`H35vl]HX_7A$*0A5o\X;Njd'J<gMxw{~{e#{"A}(HK;A͏LZ=TGKƨ;__gd/ uv7i+^|B{ B K:%#lҧ.Dkn)F$12Ro @fύeXKG骥i@-JhA` /R{iH^QY3sǢ ɷ>J?"ĀYMy z@vQţK{,Jܲ'ɚR}Jbh_ᇳv;_"Gf[#Ê,z$K:R/b '^SP^Ij{ײ%5먕!YHrq7Z0XyT7rrX6az؜ƍ ųlkP ;oc4Kd$veC}a| {9TkӑزOncK=Z-N2X;pIsu*q"􅜹w@QfnDKDJem\aۖ{y[:z@!~ kj&{ +omN CGpW( *0Ka\p,֥Gsea'(a`ԇJI@&WXZ,bx#:K Ѝ[ЀY@FnCfY;@=V.ۖ0YC dQ tGO8ـ +$RT+Sajoi)oVf/+ [%`$ 2ms$ k=]J%rFl>p WeqIHr)n䈤G5DRXxXi?7hfYV:H'G#-/5FWiI~*-ɞ" ,4o^%h[H6" jE)_" HI~n" rͲ# +-D%D?|$MD"F !V E9xEgmIwS$}+7KGgHT\&7~a?X%Yȡ<()FJ&2 o =#Z8;I.k-5FnU>8ʚ/vʷl +dB 3t8Aa?W )V# 0ޮZ{U3cvѥ;6 +\5YB80=Ɯ #=uz0u)ðhw @H-X %<{LzZsFmT|~4?/߲>$OV7"L'AU~_w}=]l{uUs׏`m̼K >>-zO}Q`o̺kمR4ĢߵU:;O8$ @֖J @IYe9 19R*Fwo~82ee#A}~ +G#qRȠVjh+HPm}<*Ie['4B>x@|,ד\:6o#M͚C@xP?|}4`#6"\^5gL,r\";5|x?9@hT=tZjH&3>HsB(޸A|N-sZs#Fe/ M]p` -auZLd3.t˭*a%jjbABzE<d>sBL$47m-0;3Rۖ#@VnHdI@ li$Jv‰ uP:k'#E؁@g!qhE-ɔ$lly -U!l^:pI+.x\UB*.;[}"ll9ØQ?8h`n> :k(_ f.hΑ кg9\ã攃Qeyưq#*5 wľ~? M.t` +<*&v K< _lːkv2"WڨY|}Z_]Oō5 +^{x ZdvKZ.#vznFY, ȷB+ײvraʱaUixR©}R8l`_4~?{ ՟4>R~뙷b"@vgLhGStCwL[*Ђa,T4v2@}j@L Wh춙u#B9bZ[;y 5a5 +r}#WҸ@?ڤBv.MCn< B7Qd=} m9eWC(UknEcs)ҷ7839%?ZEMz/:&(uV*JQ.ܷanެtp~q̈́H a"3O;[wv6S7-F7- M͔5ٙ.5ΕX8fajP1] kz[rʳ ENl+!uаpfya[:{D,R謬 aOϘY7MT@+c%d_\M>+%\%/ć䘾.G]%);H_P56rsvV#}#<؀hwz3#x]Ux>+ZT:ǰ&Ԝ_6p=鋵 ?Z_n$qUq}sdDB fLsm``'WdDZ郍\& *SB̴]9\9Ŝ2_7$,䧲<3D)Z4B_*P#z5xJ< +* (IZ76G/{jy;`b QvY_^cCz>w^crSLXJ,́I`mv~jSW +E)hWj f!dSdh9M|ߵ1z#? !,.XB/eփFkJ pã^m;hr-H$ Jк .>wɍGؐ%JR63!=k^ZG"j_Qh&"1ҭчuOQ|4O35GP6{ !u-m_ݻU0o@`(Q,44nly~K5oN-X3EV w_$*=S{Gi%0-]K6sxR}A7 ދ + D7P[ّ3 YYaf[Wg[Ct4(B,!mb<%jB,׼ 8k8&,}pcn3,b}eqؘHXhp5>9vlQA綘>)'}.s],fP8ÑJv%Tk^1#Jl)k˰Z6x/̆l̮)Hjs*{"jo`@`2&@B}ˌU? +5Q[b'KpK} @HIĔmbgُt%tlj@y8f뢺',ul;Hc{Zc1cǴBs*UH^]=c$G r`1P~3.3a)^.]T`/.|jXY4=m84J\S;yH¯k0VePlHE5"gR5wYZ'"+(& T]krGVJ~ 2ޑ> q*㏑C҃f_^Yڶ[I v*Æ}nYYk?i^S1%nOMCZЮ:4$YgBV?eV֙ Hr?j(jI-&—ndP VqS ;bhu4D Wd!RLkѲ>vv< M Z>2O\K[VVGta}Pgi:+kjuIC8ÚYJ|}YkCGף V-CCçv5FrJ tn"ѪmZ4|NGUف4_PjC(5-j@'?/̋b2$ C* k/űV+gDG!-SdKD%?ckeLRAS}>}9kFS·`KM>g?m@d6تi AJ%]uV$t+:-MV}݈^(rɹjbqq"鸊E+WI7hP| QW /k$ A"c%'&lY&0L |&~<4xL\GN/5b @KzLFl +Q@hVX#UBvecKyJ`thtee6㘉bh8 +$IuU`0-/[ jvz@cK2\?^m-Sʑ[8s_iUfYbPF!F˂ 2MZ+CMV)g^Ѝ;۸̦c,4i4~#~_ԊδGUHd7r9)'#MV l$%]{JB25,`< &¿/UAtm/go AwRMkIV:Rkۋ&A,2ejTb[gEv4q+}d~oӕÈ) +`#F|{k` HGlsaYcI#r &\Xim2<2as 9$z$ۯm|Ha߫}i' +֜zL;$cyDe +Xi0&P_׳-+{+6SVߴv!y?i2]*%/SeX RHQ EG6#njgh]crl>fz%W/wl;_JF|=~Ӑ#;ːTɍRSwT\"e0CA9pYuвY/dWcs ?hdLxTt/-l|23,?B.3tEc |;:HL媮nCo$} dN$7iK5[4KMU{YY>GHwǘ/|tɞQ/}yBfhɍg@,0*uNAPL &`Tt;fq o55C\T k.D"vq_}z%\TaݍYn4fN|V 2Y+Rw^\odAzcmbۘ<Ͽ*3YLWZiAT{Aʫ)i0YI tNj =}vMS,s҆i \JwJ^㒞O>wR|D{ʈDbUz +"DK)B( |~!cP>aqZ>*nq'/Ҩ<y='H;H[-}s`n?7q Y`2Ɓ ͜Q;\98+?zGڸ)arܓ^F<6A,1x:{ٷЗhpY#J]⋛z6^ԉлݔx.x3õ,s'8ԙ3ɼ +rl|u +H&@0F4@*]!$$O-IP ̙i{zk P# G<-u.IeFBDskq?GYgEU&gW5c6`eyV[xP*^D4S!ف7ge ]Ȇhƥ[I3KOsU8E#IdEj_9/R4)^rTEZ}TpZc@*+ +ŧ_}p|W!i̵E!b ;}Z.+'0lc0W0n+Nx$P9SS./K~T +:7N4?b5;ik>F6nW70J>g﫬Vg GpX =e9vN}^N#jIԱҞ bV$slRKRʖ&V:zeuV+~J;9X%)X7|/ +Kx)-\T7@tdvOi;gR +/Z_(GuGS`jߢ,ևɘDlmgM6{oz&}ӔB(bZ0ˠQ>.>7;m 6pUzƆNw@|n5Y+,.'&s > p)?X 22^vO ` e`,3SXG˾qUAeYv _}19a)Ľ`aZ(RHļ&HA#Ž:RZ 3̥#equiE ku⮹y!iU,y?əőe%I5s(zH#HHɍ(#fX֧oȬ45E x![e>uJYpyGKYVcчE]6ѷj*83^m]rG3@N#`sar uw7yef 9eelh8aP(ߓ♃NJ~|4Lk\h\Xd&HR%/0erKU+ƽR9 ZIXY zBoe2^LYv/տ|JӮvLcȑuwǀ0{5uR?Eb^6-5 > {RDҧѲ7RFcӋ&oBx%k3._LrO|GGDy6pfx!h,KY<7_`M[W_۴ +,HE0Hu?oZ|M,Ԡj 玭xiKZ;ODll5(P +QI.x-QXHϾu859X HlRiD&l~dEyijq8 `Y+H˸H#iώ-] HMGoy^ZgʘД7YnLk]CM.~mz DS/*cFl_u.վ*+9O;l(eU7>'"!T^|ދVx-OT؁4",Z: /t;*9UBibS!fV&k,@nj<|cK6"hP?V#ȇ.(F[xy1_L6$GP&<(g'0[ojg.1ҽ$pKMaWi ¥Oe3 m#4vn 驭J݈-Fz޻wࣲUy@F ֔WZ: L=!F޶+wiI4 1)U(ۛUiM&+$"(\l[kcٜy*7'i1`oŅ2 WIٍGb>`FM  {}%{f +Pj,|.ǤP$qX&hxM[1X7bjKVǽ*5HkHe5UJ9[$oK1]pD)@uح R"(MJ+ݤ( Y| Z@Ds70o*0 D0I%,gvN 75ʨ4<6 Y'kĭNnebOp$A8Fx`@Fγw#^Yވ9L< VzETa]|tXhk1`|v,mqt9,uIU*TsdrenTYEo-iZIlO ~$ L7JVPč.Dž* L^N 3DFWk]*b6Q*qBƞ$;9!׋P{l e87L2Ys"Ejœ ho]Ȑ}jO24GF4r9Az/+=@K/ܷi}F2cX|#nxA)(Nö+ܨAՎeYnr=LGE}qةW0iw$ z +-' .bo;sVڬ+z 1,z3+Ueu+&Sݘw(ڵaܸNst#V&ک/$?_[vRЛ:4Kƌ[^hޖ)q*LWQ7xnN5X1 ]Ssy% 9Q5~q[^(g, cɀ%bF4b {.ʛ)鉌l%<6>X-U7Ma;1Z} ^*5STޡ!Kisz}fdx +\>6~hH\=s%!GCƖȱ.oVK?M<3iN3dRkIZnQQ|M\[ z4ЊkIPBAaW~}1+!R;^'\ȃ$9x)c}\fu +mUQK5ٺQ1GT48m纲lsxD7pS7$TBT/3p+}]*BAsX*Z_=SbS'7 yN|HPԿUpJNEhEQ/ԕ?x7TSq o|pIAB%fWZ|#AT_}\28{ЖOx ;Q)`3C얦{՝Ŗ]oV:j3Ǝ\fQVʗ2⬅$4}QcIqJtLywv 0DH7HY䙲NxZݜP0by#iO}k s~S%mw-r"PVn\zX#Z@ճaEi35B{Tu OD>uHqsiB>sk u1{ڥ@zf`(`h?nM<\L׎u|AF߸f%V5|GJ Wەu15>Z0eRˣQ}g5\!BEW0n09~=s +N71K{m+\:5#$1λfËez6 n+svLokk%=އb6I = bL<6nw'! \r&+p]33+aI[27vi̅`O 2@.J\r,Ā1`MqD2~B N/S {Q.}7`GWU %/s%"Kt#{SV23Vm-óJ=a/yD~-Pfc}8v&22Pء62x.?o}|_3. +n +EGrmF$@.Ǯ{3Ɍ&8yZw<;z\e|Ԩ/mZK'4 +\>2Uvիǧ/}iĦ*Uܪ6:ؚ{mFO[i5f+4ckp=7!J AS),ƨJKe8n`kK6VY0)d3zc8 7Ny休XT=iAi{CAv!Fr/U dJF…<}@8@;f}!doX*8APZ(\^gMUF Sƒz +%ŏ)j +ŭR-[[ɤ$X08q{2!zu0-5v<ij)Aݤe8l  B ϕ6d^&'ΛnZ(JulO (F'z|/D(7hfZA?%u  4U*HRXji oyadX(lQyUZN:H _ _ö[T>b'g'OLZ1Ϛ@*{L8פJd Le`*Y礖2 %Lv hkc~CY@?A&uc b7HSD ќ:jONv'V:bQۼ +o{&!r$ +07C,c, Ha>FlVɺ\f(:nCdt4w%Ya2X){~q_i鮒ʒw891vWu& 0{$(eKC)/K7o.JNU2NP/z&6c AX d@`]RqK8+7oISQ^0' g3T<1fL瓠0,~P>QK8]AW^ $lOVx +=;N@6eλqA˒ד@^G6Tgx䮰A/ + ql?a9 +ËP,َG'+{Fm,ZW v'QjL=/!)4:_Wln4˰ W%RXB)ؖ#ErB\aMpԱDA*/SQJtG|7r>só<[Cvwr]|h/<[sAd00i;_@,} 4h}H$p3OOzUfg>Fpq a'oa懖&x`\CkDFwݒi(0{%AJ#\_Ԫ"Qm L DƵv֞smi]+U=T6KuAY qDn`<~]6mjvup2C6Y˧(9D2s!)ՖkVf㹤A/*Y7'>_Á%96XEdzDFiPg2[a(e){ ~(m>k].{W*|@TX2|q?_Iq?rN157C4V'Mt0E+mBq86s5 TV&1.}1vdR>w`?OePl{Ρj6\Wi?(_nȽ @9n Fv'گoI.bFuZOuW+t)twhNF-9m;òaAPPe^ּe-z7 +GrZc@ 2q{Ló'6:P_@Ζ!ˬpOof” +>7m#cw ʿ]cPе +FiqLqMs/mLp. Qioe:6Fl7e>MZlQM*gvzL'f!s~.˛i j[ᭇ,=ɋ:Ґ!QVYr9@6zbvj so8PqeKrlH= >xySz2sx!yV@nlUamS~ F\z4G#t СW30.|g[uV{@ӑ^ +,sfԐ݊Jf1VҸT/mS>kKP-l\VoȪp6a<#])T9r+5yfe~R\= } +jϿ] $b#[B9-,!c9ڡ9ٲٹ`:<0$f+b8LdO lijHk|+EFJOgcCrGt3=wݻWќ {}M :w'Mni-.8uńf۽z?(Pٮđt:a(O>Ve3, s*?UreA#:EA9O(s\+-42uXϙr2 @Hܗ@Nz.]lL߁/,j2?VQjGlwR4'Mq,(@0Zt9hq pzNݛ2pz`T^]Jop]hZ]j7#;G/YKگ[A\9r\fƋw*JDжU3ͧ-Yij0>stream +HtWKQY,} oվLega%)%2 `zG.xtp2|ϕtCg_^!o~6w8H=xjBM$4G 2~fm`-#8c,Kd؍<[B썄R_!!q]kdHF$;xG`xdJ|+\F 4K2JEcF+ALdA`1H?MA Z} ~"KG>Q影A\2Nq +%ٝQɛ a 4î7W&=GO朋c]Y N UvBL 2!|2d4:f$2#Wȿ( q౴_e(~43&Бދ*x(v1AX.&|cr"\RBFV e4t\A|5(ZDZ"F^:jƅ}ew8ɯUkӨ.8qwλP>A߾~bPRXts`Qw ,uEE}9kͥGg(=Y1zE,`9s_۲1Gk2w}RL}y R\TX*Վ/tW@7eR$ZF " |>w8ŠMlM "_Xt"#qao;ڿYX\fle >jf6u߆ĪU6:| s/VD }"ꕗ1GW@)J&^?pń U/Wnކ$>ʵ 6о:@L;  .X֮&b̀.z.G'C}Dw& <>~tz69SZ47(o._μ]pփ^!b3{ٜwIKl&"i1}3eڮ}Ry%uW] 2[2YMx] (0pe{n/ͬbC ">t$NtAńѸ`jƔ +LQF +thqE%I +`TGJ"1Ar7j 7' jy#s&Rl܃~"{c+9UWRd!ûfEm:+P6(ENNSƗc_< (V+ 9UI0DRi:}x_x͖{yB낤NluqL+ҳhH5s & Ȧ v@zS*7֝hoe1V:sA7pbd24ڸ;VedI&(g;1oԐ`&`8+VCŬVTJ;3+¸ō5.yC*II9O +Ŕc&Ao>V%'TsDRq^5ӨeYY1'L^WqK@-KN:ԂVW"evQngJ?j5[(am.le26z0msJ| ة1ViǢ꜖/I +ROs|/l +x`h\BSCdz*7p3 >l2Y-9;K[LUGh$%z K{4M!0u-QP>dβ;'Vp)=K9: + ̽amPMGh"BlvPW0e_AnUA2-:'Yޮ6z#??]*/[I_K\"6kkиo9;|ӱr.jY^V1tFՠDw1ALfanjc'W_0ޟU{zېi\;CJog@i{[3SM3=:V@'n ]۽q3yV +bDU{0J;U{'lVٻ;Wcg@۝0" <kV.KJǶ,L5) UFz#vq^@gQlB.kIZ +N/ON#/[cuzxOwcّð /ӹSRn|I% ǛJ$)H(D 0w-4x `o>#?I{#>`X^;=˔ڕJfЎ˗՟[US?񤾤ZvyS=^8^SQ5de]hk߽߈p]8r0`ߵmZS ׯx /1ԡ5A/$XRd\ dLE^mm/} U3 '#Feq@攗=$9^'f!@"~,*X^ DXB^OD qU.R/r' [yx @~=Y%xQk5'wޑ|X ]oׂ54+ᵰ65j^ }W;5$i&^긖ZY@ay-^_hyZ^銱_yj߂ AڽEZik b _ #H dU /^rDkȜ2rm后H&28_6d_GSLz[A[ vJoXu! +mhx*_ +ˁ?(d\zUMt\YJg 'l5*QGCae)isgC@FcC2s΋UԂ "u1ˑUfJ7aְ>U.SiΜA~j<2w\7%:LөڈpԻr򬬻K.JE>_=Ai՜ȗ[ۑ3`63KIh*v# ؙA)ͶU- @n䬮Aɺ  k8ϴHI2^^[J]9b:^EE` Fn_,& U00cB<Ɗ•-pyt$O7 RYMi,# {ʇͱ]I[!?% ~ː 4 cJ! OKD͢yoanߺ_p+vW]]F\J9@KWi)c+o.y>X IE`"T~L4dZ+*N{4]m0fhzэ}&>$u!+G]]K&=]˽l>JmؕI5rkj=U]dᕽ{ho+7mZo_E%xf5IrٽK9/jWgQ>{+YIKsIFsj ;CdP)E!Knnv\Ɔo 9BXk<3ΛXx`Qu0#kGV^8͓W{9ta-[ +pZlټ!MZE*QŖWkxCӔOQ6[{*ԞSWY?~jq^Dʖ)1+ P@||NŃ4Ӆ +lJBpɍlV@G+հp85$zZz ҊD*΋O 8^O]P Awd"C\SG +<6 =QI1W6ׇvݿ )>!9<%&QOG6iH BʡtIU0}v_0 .g_{a0lqoF]"[%gFy)#q #Vuvys'` e&KM[E5j1 }F\} .a`SZ^ⴋˣ2R a/?=pk#?Ⅸ0~p΍U2Bqibl UT8 + Kdi `m.ɹ^|FHmf5au ҦKj5lkNzTXHS4\nXV& h쨾,[諽FٮV$o +j?)Sp88–QJk4 i,R^t"j@o9EÁ&0zV9m>o׾QUZ>f $"7;,68 )V1eHloQG ,ג/uβS$2{>c+m97b4.Eנs_r׹g_"MRNS ŶcI$2c`A.+2odH3k:`iJ9KEyhCB0D9E*⪽Gհ L5@Gϝ=a n5})k{қ![8@v@#$CǗXL}@9SGW0QzV_?e6F ㍹a4$ (cpkn%T옮g|9ط0٨K0U97-Q6t _>R߽0>\nDO_|rgnh4 xɵ)pE_l<4u /k]~z r-_̷_4$I)F0~A.xMٛ3 }E_L_dep?~W= +EH/Dsp]釚6og—leE_~ 뛉/ޙv-.?QDV\9 Ý-1"ZZdN'Zd #U}[NWcD uzK\AIH &,g is\xnj4=(s,`YA{<5گS ]@*όLsBij?c t"ggi"7\*l2<6%N +@7'} 3D?4U)o$މj +WXN@,%|7%./֡TD_F2ýƜ, /T$ @QhW`W+]Y#xoB?us쏠tG8ým(?Xs0]׊`C#ʰH-H78[b}ٙӏH%""}!rz&}oŕ_:e;27q;2ԻS 3q@DľZzSo.?`YAww5^E}"r5M(EHwVDծjU5aa[|1 +~T`8A6GԸ@6S#87 @?Lй!uGcH}"POGģ [j /B؊K3ee˗T̫//>sƬ,H}#~)7GhZX)}#(CrA\˪ O;y_!RuӪE}5/BhqRW+SlYJB֯;=cϝ|V|0☦>L_[f*aQȰ{RoRB<@JL/JCjHO,2J->lwT*W4 `oh|-[ZyTIіZXS.ܶ3*P ??B0؜B>S`_ +@pl@fV׀~B}u*՗;C{YcqIcUG +{_;V/t7{͗c*]+, Jw q0@1IAdw<(щYmD:Q,A_v`Į^jAE^6`dicV?WBcĜ08`tAsd[u=Vck^ejieֺI"9 Irnbݢ2 Pq|̃:;:5 n Z.dQ9XSG1ceUy7+a7UǘE*W}j!)wۿ@7$ō/nG +Fj*I~վZsܕ+i?ihAն 7+#nbC7 +Iz衢oLH'zΒ7RezF.1c̲$Hq[ښ[ǽ2Rs뫴MLrL%YR'~B|=>T\v'a,)P +Ͷ'+" HSlr8< `ʋG@׶ IG$=qܩ +^(U@ ){w$77߭(kR ']k@,,'Eʤ,b@ 'NS/RLvp滤#i>lk#.WYߌq/1IBE^5MV.#H oc}GAƧ⢑GEVv$,omsٷLjDdlԷ:kQ~:zrem^~ +/U;2jkjln)RMW0xYUzżx +7%v< Z4uRk,a@cm!at ],>oDhI<•~@7BhL}]p[lBÅh5_I@>`+ >k F`1e{G(dC0ZHGjkU3yn,{g&\!ɳ]WDzYp̠>EuhƳ9Xƀ'.Yu6|@4 {QR:,477@Fkf FH8&gؤ{ZOޯm-n,|OJ9 u4(zäܖ`t+z/␨G +5dq2r=MKU FEN+G]k,w`_ ײ6bdk RȑAmgxd2_Z/ĉ{}*H1E&٘^1iA*ιX7 ۫w380:Wgˁ$"80 1mR|¾OrL9E Ö[#f fԕͯ7ΎW1r[gʴAk><_<9Ɛ͵ƳD"N;&ƪ:kSMЙ$vK%?x]^X;IUQ譢OD\S 6iLNyX;+{A}kMdPa8-]7]+2uN%UUR@油ϯ}Bs0RoTn(aZ͛joK +Y HUwWZWt ]BsHt^kzo^4"f00WŰMs[}˘d E]nN/fA]¬:*}1M)4ΥY `Ǭ:(" ϷYcV񢕍eVof54Z?/Q\r+p4wj>4jl0T +Uǫ^U=U?|;U9T~0 x˅Ŷ ƎiduOi^zzZ4ƼsG(x\*4VByAqFw"c]G~QJ*e_;%- >ڣ|89ɇU+W:e.%[f=#f]Xr8*}y\r@Y 6"0ZYjl}!svRvS~jJi&4&>h9,T +VC:3SY +IrY"MTAO~ֻ0)BGo{V /lmڌ_W.mI#Y˂E8{|[V8_wַ,mb$l]+s(GusP@*֭;zI{O"CdNDBh6#*UZ^ed]wrz[JԽvHG$:Z0u1Gw쓘s;-K(Eff@yU 'pi=imShe}l-ZSF*cd+4Uf`=ǖowգ7xow"پbZ 2ˆ7Pt"KL1D^2dF{1+=Ps.7^E8idW0Af*Z|@&g +:9CdK?(6ƥ3)6/QWgp-Ev2 yY@ohV5=amlh5c}:HѹhGK4vD'C +˂ݶtWDBoG>&eD|f*\XҁGmnx&!1ũFA_\o5vLO+ +)evϷл,20iϢnM&,?StT4 N92 %eRIe3Z:_+{RfȻ/ ͖Kxgo^PDP`mhvbf"k].czLYs׷u&lu" BG4 [JvQJ)M3qDmBh#, K8~qdteh 9ɫ0:B;4H<,%4HfG8֗fG59hDHD]y3ٻ&^xx4H[/ٗpdgW86ufY兩-0,;.VG椣A e41 H# h$ojr4H|0y4H{įua43-cFJ Xi]m zܤ}ŵ/Ids1}C LKdM9c&Y9kXZ 2ӄ#43u+~kFlo3 $9(<9,ZI.c0YyqcO[ v]csv́UT|GŐ踩qDLƢ%WFD^*sMub9wqigҿ{ˀbbLMj=ڑ [d耺\7ZD+Sol2Bݴ^6,n\WxcDf25/ ZJpmTgfJ+ʋ;sTF3= l[m*?b~,k]L}]dAO#Ur#}wւ=eGAŞX3bY bcr..T267$zIQMHߡ0ǬIqAB6(TPfԱ} wQRUJ~1^lNPƣJbw'9nqAy--6m,\-s"2ĥ1 $:]4g?FI;̬:ˡqAeE7͆mεIٜ޿O$0Qpȃ{&[emV\T4:ls(D|~FpiǖX)m.Djӿ.W1W]XqSq[+-LDgQ 4L)RKLGEme'H +\hzUž s|0#^ CM*tCH(ůLɵ59=NY&2(n.G+2@`~(N:I9d@7b7uE+oל?⃳}rrZ,=\5!{%VF/6nξ! +?HC )op +r}MS$d/YG~"MCrZTfKBӌilʚRx2:Ѿ-/wɎ : 'jA +/D`gEN3TH^9=v@RQ{L8ȓJ̲ +sRl+eۏf[JH4>EqeԾv`P2X#~CǒV($58&ۀ]YUUbI:E +K!NE☮e(j;3U|+QMM^ "ySK؆ӳmJv ~T+'͖$ڌlTsgF,I!lX1HiGW%6{m7|{g Yt5 nnL3qe)k:BxGuA!:PSǤ7218 + >t V2M~i8&w$rew&߈An'RjvhbawؤR0 ~5H{'&_(mh H)u#dm"132g4xGDxqT'ߊQ`:Hkh̎tWtp2$M.QA!^änuP$=JRկgmU'1A4(/# sfwks})7[AJ%W$_+}^yoS8ɯɔ4ֱBS-js3lh};ٚf5`& U!e$M-WmB~F`'Vr}sMI7K +ڎ79 +U-In>)1>4+l(>4)$i.Rx"5ni +ˤgI]fⵚ{tӼlG +wE)TT}%p׿ƪn2]TPUQjpf>1ǀ>4_8& >NP{;2k"ou1G!ق`졢 \J> b¸mΤD^.|Mî4kb*JB]CU]SHM qJIiC(CH8b)TTl?? `>a2F"&oX6AJЁQҋݬ$60%a @IwMIY҂$(y?1?c fo QųA"3"c-v!$L(DEYbG IXDTOTSlNv@ulc򎆱`pZdH@:z"b(|:Y{v-DQrLSQFv_9ez'U`pt+v ,6nri#[ȺY!3-~`b1o vW+YCً +Dеb !.rsufH~(i3m<HPW&6J⺈`x Hz]Iw[4>ks꒫ dU +a޽r>7'D41O#'Tb +ͺ-~LY*Dulz"=Տ-0rz+=a9X{B>^{,A WT'+VW"!lBC4 Ū揅)a5-".՝ƃ$jNV 厉T7ga;zXA\LYiԪYYXV]~wb2B|YQH!`^.mDa&I#GX};Aq"bjzY[H{Ql黝KYU2vIHnL8D`[Q[P4>J 6A/|]|gIvo1F#]r'S:DaAANQr `bv{L(7x]A$s.[ҹ1iN5r{U=:K7HܥbqĿ(Tԡ'p {HFة]LЮ^Cv>(TA=z(Ubஜ{$ Xbme\紧AB9K>i(r] S^; !jcEq4oo -f}&4IMv>H* "9&JC9"Gڽt߽Kn_?o?#7}aܚl?}F2NQȑt639gSK_}N>sB& FE̎uNBULEW1\7aloqTm ABC~SA$I=5FjV%qTT~- W0}C9f\sD_8zd,a)A\I!|Q Öp9Pi54SXa]]u_1W'ޓ(>/R[iZm7~[ H61T4!Ct4a0 !)z>f FİR;UBh_4sB\Iۤ|s0O?6.:IҼS]z11㇄ChP@h|L1}kAvwK;Xu߹lS) +RSIhcǬ|2T46C; +&eg~]lMоHw)U%_t7>z*SgA7 +נ)ȿExY]$ WIs!d<| +kτ#IDۋK}U <+% ;9:H :ʩs1 fm]+?P5mǧblΦaw=dwޠIu,K ^swnm텯m2ⳋo1c-j_@,д@]s1 +>tV''d\wJ \?IϜgXsk3&',aVV}Osh=$IfkFj:x^za YUZ.#sb5t/͹>')Hƣz".bٙlwg ,14lg0o4z+M̀?BH`>@pBtfF%/m'Vt`]_"2#ٛ@k#3c֪H]^E?} NFdαHKU<^BuЏ/B0C +a*v&(C.ij&L9x*ݺytNy*DJCERYD +rkRHig>meK}"!quuJR"cvy}X(|57o?p/}X$]5ވ Wrk٘yst( ABv`E}r Ŏ)82S5h@"Ċ'!O4®; Rh64y +bnSŵG-?+#\-5jy|\,Om)X1i;7HD|ky=\ǓHԤ ;7MV{xGJ v$i҈"-ӓԈ E咈b /ki i]_ȻS |DŕNdԵ_qCN- )Ly#ƩcyX7Opm*%z;מ kZ5MW1ṊhH1S}GRXR>sLC.S-+y1 pPyl:GDA}n?EՑ$u%b3E+;`X"hx + 7X,ZA#EcQA-υ׾ 6[/r#Vs)Y3ٵ#FF]gA26mqT<| +j(6^ _4;Dˑ2mץǨ!V J4Ԩ{vtedN6 '$+)eS6 첟}ʤCY*{Cb8"%~x!E{@iX%g{vG氳pμ7x{74B%1&{=0l-kBzD-Wd^5@vg d4ϭ V3;9tT'(oHdto'}>AzO"1jė|zc0aNc+NFq~Rn!Л+;Ҫ_ 0:,\6XW_`SˑU_(wMBL4Lb4k|g}Kj) Zb.=Hgc#Pg@g*H J|f"@׏|EXQ_ n߳"6`"*>b.@̑eRf$mȪ*9S̃XYY`N/:E2Bq/ W18.D{gee] ;{lj3/%1M( R~۳?5Lc0J%1lxi9!@&w}D"ٖM&u,aww#e͒)%xhŠ} vlpAwkL;G{hMl]T +*[Crk$̵?ƍp9=9 tM59E"n.YOR\ ћ1$7# vic=`jMG4'bz$L4Rh@,ISN ֶJp{\YZi vԙ>6 v5ZmySEw9Z\"mtIb%) D# BCC]U$BO,^b\|B1=h:<)6Q)ȵX +:E^ebPz"f&q)&ͮwoesE`DVxZ٣.q <}Sh](+EòMlu5#<'{R +peh%L$OUvnC.n +&pk3'XQeM K<b$;7xG7%rCzY!ޡ.-E_5P8`lp ?[층 E9MtGصpDaƦvd6EhaX +|tײ=.Ѱ80.I2oDע`\3KO?Bk_RzL3~s 䲭sVmvvY-@BlHRf{\9&aH4$qT XGH b|4pւ yJrW +endstream endobj 400 0 obj <>stream +Ht9@; *b1uVݏY%P5׿ƯM"_?_ݗK뿄2#.Ȅ뻡*mr]dQK=>y>JEa+ޤe ߎka߿Ph ܂e.)!}%ؓ u#wq> ؙ C}/ Ȗ-xd,c<&q?UM0$ԟ/+v9"cN4׵A~a[XoԱ(5p>B ͦX8I)_fEjaȑ7G0Bm" JNg9 +t{g~p< H|jkW;ĬA*.խ&]"Up2(AcZL h^1g~.;U<]F{nA"@=R"ə#(Ͳ$:<1C7jAqwDXMҋv[3BAV#I eLS:"ucS.4cGQPsH@{ +ٓ1ـd6-G˧ivH=?bfa\Ʉq,F!Y2肣Q$ѿ+\&$< IffIg(W> #7|rKYDr@8J1ᯜWDWiÀq::Ӟ1ccǫHNQ L[ܬsYu/s*wZ=~E.@qk mrts-4 Zhpv^lr-rӮz sK&Q1KYӋ\E׽I } +zGor{[s~mrͱH^2Gk0ԣC{˘EUk3! 1!= Z$adn=fC.е06ABau9Yyꔪ eV٪6IfNTd,/F8F&1Xht0dq|"1a %⿁nbJQ>1E2)>ݏ= +DX- &jqֹ3M`C48Ϟrasq|?f{&̂tLOm}_FckJb}}'NbxVưoӗbqcW30Xo heoOsд{QQ`,T6]%K~uJ{ϡ͐s]2;Ym&X¢3C%xČnК!2EQND/-Y D.ӽxBE邠e._#eg(;w ̨ڄk_{68@?/ >;8&F9PRo $sGuvN{٢mf-N8LpM2SuU)zsn|Mr-@VO 9qV~9~#|ѨT$JopK)ph'CI66ffƤfVzW*"YX"J4Q% +sX~F xR>h# +Sd=7ܴZW=h@֪ +hÏ `i'#,â^c/ޥD DOn_B>AQ222?b%5_Gw`yv+MrH4`kv^^^q6^ M +>Oc߄1@F[0+Ys=ʄ%! .FC[sZzT`)hVȉ'BD~3 V09+t^ע/rɑ-e07y"{C 3y,Qeֽ/9M f=y{P}J0.%,kI!6upʣ>}Ty|4n/v!;9be*dx1rUm\̈́w +eHU"l(v9sM Ix.=uAlRd^r7rۋ%V/ݱ\N+MXDtt¿92b%6Z*z]vA^5&T(^Wx0t.8;U['0䃂>wLUSn†Dz,êJ9/㈉4ꃆ.FYe,=qbL`(v ,Y2O7pB^47s*\DRBd@A{lZ K6^u˯rllM}SO\M[IOXy]Fωk]Ozb5I< 5鉳5p}E~/~^O gҾoz \wL>.e +Ķ({t.awcV Ě 1{5#~UA7]soӑ!|K^\ Z_ 49M*dX Hk Y*13U#H{m-qk7k{rFփBdTZ aԐ᪱ʽ؇BueFF, |mV XU?m5=&d|"TSNwh&U'X-]y5X4+!|(f{Ymu1Lqf`C$Y_ +/Vշ7^d?F97]+oo)N./ -- p3.8fK,T%rI J|#<~GV~p5_~lyOB̅zk?3Vl^duk߈my^S:P'2[/~VFkC`xs^2J.og:Q?oҨlȑQQQ,bH~r?~$3Xv! V4#z w?fz]\ߪMCV`Y¤P/2dNl霃OPdh\O2GF 1> ˵ޫH$&D ^N(U__(4$+`ꪞ@6S`|>@"CƯ)1iceGlyi\F]o˭_! H-iHU $"ĝ#-2iyZL`o}"4'5̹D9y5Q 9]4<ئ;]kH;PP[PѠkFh&fn^$#06HFvd"TN5M%h"*A8N~"xFppV/Y0ҲhJj,:ѐ16Gj.X>Ɗ 6]e 2&*ѧ-Zm^b"@7ktS7Kn~FޒʻVI@CE-{zo~tGB pQA:6#bHsɎ|p#Gi2$+6P'GVY{` UTNj`$ʌ[t7Gz\arкκݹ`_GF S\AlV7xo|]ioHh|<`xI좘|,3K@HwGžV)EȊ;}Y%L.^I(O?_@00oSIy⎜XtEN-ޚ}FHE̠s ZF$.׊D?1{jxXߵ7 t~_QuqEε@\g;@s~u[2+k D e*(炼ƈ;/xՓ]^3O~3nydB|u24I6hޜaEw^^U)6ٿ7LJ0Cpq}xWc=XorYnrylL:m8/6鄜Q1D)Dzf(3ɻR,bP6M7mkQ +F^1Wiֈ U#~ϟ$Ig<3Ze6l0'=OՒ֬ÊY ߩEe4tvO=Lm5)xg7pD:3hƿuP +>LDrۀVu[KMŏ7STHa]<,k^69L>g1ZܪU^G9XL Sm7y(͖2Dp/ }Ok_8~{LJ%#z˚TNDzK.]-Yv:p+@oػzC^R|.7P{"Ztu|%4iz +0ih~XB#rZF8,#ȴ%ib] Ck. J%):M1ٞ@mbx$ DV"ڲ -H4σx@/KL#%8?*ltbK'Dlj}'5I/MXhf+;H$xu`_wuPsD 6-'4Cjq qْ#Z9bg~a~lܴFYH&#ɲl|'/Nβq$|_eBߕda~}g0$)l|mvuvĊ͛_lV4$d!$ plZ$ d|#<ȕdo0Vl&Α$Y6}'YoيdʯWj_O֘} wFz-:}YG<ꎲjȂ_;ʾ;goֿ{9ԓb/ v(/~{w&nltֲDHռD3) "5 QK;yprdnԖqMƈU}|'R4Dهޮ'}!vtC5+ ͟ԸO{AB{@)F |io;hnE`k|yH\#64ֳaLcMz/H"VA ',:;ԲލGᠪ?FtJ +bNp([x m=ELow]e]v{A oU-ؗ$U2 \YXGA-tPy/Åq{8(iyA)z(pb>F^Y]oČ>M?ns&-7oJw KREǘ3xWǰ^c\3, R_ʢZ[JW N~|K_Qi_Ŋ,9uj (d0*m+Ԭ*8*z ]%2mGyP38NU-*}}FH0v=pY<{$FgYu)$K +)QKjuqfs汤VfνqD +F[X-ǾV u:g_N蠶¬i;8h!Oa&uFt]oIcg7,5Ti+p>qV8%&k0hXOnLXezfz^!BO>ژ+V鶢z#ٶ1sUeWpEF퇐Jf$K1.nx=(ɮk_}a%璝VuwKIحdc['6\feMi0…OleYR|*޷\8b% +^@`ܰIl.k7ԧo;U.kZ%q8ʋ4uwKV[ 6EsHxѵssm} ϣ*݌Ι3 ΈYj{jReV +Fm[wɾڷŒMX+B>^N)󜳷FV)=qd[)޻R۞CE8F3z|UN#V3)HşF.uOf0<@"F/ @' ϗ_Z}(;]}ڂfMXw*D<| vƐ`/MRoOͲnu-k*M}z `y\-Lrc sOŗm|Y Up H}v29 :Ɯd6i' X + U&+\ |l,UɝmxRl#.SySh@Xǚk]P.2Vy= FvP2P7ۃ&Ff}#3d_K > ƬȬ2Ff}#>ɬO2Gd^i + W 6ۃ@.2O27B72728dnodn2?Iu9"1v#FŴGnڸgFM + кJ*K;eUҪ6x^+*Ԋ#3Gl J%2eG ɭkp=' TVfc=|C󃓁 +)XȈSwS;fe&,Č%T̍سO!4!sZ$\c3E,j8vKkDB*`Nlx@( Kjl2/n>9 FE2 ,!t%G'bllTxE] +:'(pJ ˛9 +Y "YCzaΓ%2ZzrE9Ys(ٙ[zV.̔!OktIhye2.Z.m~ >XMr&8@,yUC97zd i2o@"08v^-+*ő<>j 4QƳXh5Tw_lz[\ۢb iT()\rR7иb$bw܏qJVJebdc}.4 rOj^M5]}8mkG{}ZKWL0C ^XZUԑ* ++B6fSU3uO >bƢimbGDZr!i4ÅBj kt[=Gb%V ˊG耆}14R%+:o5n̑czV٘/CHދ]aUcr0mzme)j ~9LƗ=M$dgzQ\1ܶd[}d#viYY-Q0yV5:|mqbh^u8ω@ya`Ύ"֟{ m' W +jq5kpZ/EG!XM =bp~$ZA曥!BŽ >em\(Ƚ;#›R0bc}c7f4SϊGNRWtG+!x6 ɦe*k ]5^61/&6K``k1Tw4wDM0TjV9߃y]wڻ@.ⰏXUNR +iD2žA*ʯglL25;O̧ɗ!q+D+y[g 'e5G?Vc=]íXe`Z;SMٜE|:۠N Qd$;_+vy_?KEX2n +`r9HD%َqBTޝ͊)Q>#*-Qz_G:&3Du@l-F(1QaxH ++h +52u]:/WA2DFar˪f(dO (p;G6 E.g"Of +,7~11L-3c1B2+FTLךѩ%j[][_Wc 5ImMZ7R9dZH;伂? ;I}aAjyZR烙H$5͟8AF u~Aj! +' vv|igf^+) @(VLVKȘ<a-t +R{j=?Z2Y3LG.+dkKn4,o?(6>/Q9+ |/ٓ%kΰ7X91(W4J `m#AuG^5Scaq +b#:&2ʊI4Wb#}ޟZ){`̣(8%4\D♣i*3^6/%ыH{r{# }__-q;JX ;Z?h4^Oj1mmqDZAHdb3pvJ>H|e\JuyP5 +M= JRa#u qX2P +oGe`!882949mS}+4I +G>%S5Tܸ"iZ%.i 3m )Ns ¤!D01bl5|S`k 87'J[I.&WڞW@QHχ`ɪae(5f20l5ĈD G0pR_?T;&$@P_٤ +`kRSv?O @j*(|I6o$fݔ{ZYݒou.05)>M4S~pc`7!Tg Q̻+ T}Q4c[A+9w&9c$4iȼD!lv]YNbޡ{jIds H<2=1FJمi,KPNjkԏҚi'Ew |@)]0W9[`!+G0 +2?2$+ =پ‗>(K~f۷#v~+?^`a/ Y@~A p܅Q@b]Q//Ȥ9ߨFTD`6DY<Rvq ݚM )g^ϕ[2{.lC|4(oT$v)/&E%A9q/̔a) )A@?_0|ط`I"l(%zO.:y=po⫻MWrfA2G;D[t ̃cf1Ydp!y\&&Dσ'ڪ0WVV{7ټF)530ߗ}_jxXUaM.$Z +Ck2gm, Ļ(Z*-iW~m.mX}/s4gjKf!c8q$SQcmD)6 +ة)-Eo^`w@0萉zsH=XIƪ$Kr2:T͚_6[SӚ;d~3ކO nMngN֌Vy_qV2bBfYTf2#JKdބfG26F1}fxf/Cs~BYFH)&.h78ռ)ꄑE:%H4;ǵF8fYްYgҘ68,ekF)!A™nَĬk2![84_ -XQ >5@ٖ\z]ؐ88G@ؔdkb va^͔!6IQ[h0]Qbq_mMg,{K7zF@OTmq D3n1A7]Lbj19à`cH09nGU&IQ5wq sb lhK ~u0 ۞R9/-S{;@&5/l5n=jRFfVr#yCaRw GɆaֶ;_ 4~_{jWں&/* 3 'z= 5KٕFu zfs!sQJђwo0B{ 69Z .i@ւKylZ`n^%E^~SlALwټTwQ!Hayxd#EC:$D=9-|hEo^J GUM2(nCi6cz6¯ΰW'P. %ȍGJjd1BQ^hՈ%^d @;8tWKnl \AqF>4~8 +tFA𶟢Xv_0삤#bQ|pJHCΪso S[yR u8RC8vWF˺ʽ}r\X yŪ%&˘o w&~ +pV;tRj[hu~WGrB&oG W&M??.k 8ބA4<Jwog|6ҩ@J9ixpMX[ ~!Xze9[{< 5j1tIaە@|h/wH9UvA3g.8Y7 u`y &b#2*,^P96BDZ7:x8RzXUU z&ׅ<7d|!gc߀]`sw>וFyFD|MdFsT6Zjk& dd#"7t|RKm*)ENWT=WHG q gkhcp4 v&G=xu= Mؖb$#27µ{\@{zLFi9#cDFxc1?ΓX1?1?{F1?λs0ѽ.,opS?G߽SKo/_ mGsQO74,oo߬7z>ĶD}(>eYR~zG> + .:/m 3e6aaMMs3AT`U$JwUl DoC}#x CQw:bZMjp" #CHW"z]~ qrXU&U=7ŀl$*}8%|5FӃjY:О|ħtw, +D-,&CU  Bu;PivP* MVON+[Ht6DVcw+溜nj^Ơ2ZE2D +w$U3 RĤCY1>ECPli +3ED\Zl tqK/- Gf~q,NȮy^iƒ,BZLquԊ@m];{*7JgbD DZ %zET !Fa|U(+sNwj2We.WX[0ZZ*ͬQHiֳI=ƨm)NhG,Iէ s*Ɔ:M$EZWAxeV${G2 _F N)n4M z+peϑ:Voܑ|v.Pnw 6zc >NqX%*i$hZ7v:U&Dvzj},mԲvzN]sekOU6'zD>'RjbwQ?2d>,A."o,Y d<6#([g~_aIIc7-iKsh$L:kq4I#1UXvI`*y툧hH/R**@maugOQLޕX@5-٨~\=BOOE!fa=WPh/0' O=8BBj*̬4e5^3m-wazyC>/5Iu1У >uah7QKK#)MP 3*?ZZ]!ɑ`*pILG0o6Y4KXkkI`2SJ1vH7+˛?/Й=d>@%1.jE])yc°ȗH@W"13"> M"#EV{ Q[o3 &UZ 6QC%dY"W߈Kx@[Gձ@oG$_Cヮ/d"5MNJP礙!2͖#j,[1[H21ԧWI dYMGWQ.5/fؑT?PT\:sS08\l|quhZ5Dծ;؄5/${Γ AyuTT} _@]'MOϽNfzg?~!!n/ +"+{Rsjy%BKAgF K nC? M.#4xt^o3Y#}072\Y~cXZ^<,[&{;fɶR95Gf"ZKIRUBr$ێqT nfI jA_96O,d(v0y#! ]=PO<]K0?9\{G0vz4 []ԽRdK$kH"D +2!-w4+i[ +%W}@ft<Ps3蚸oڳݓ/*QX.:*7bm-[Uo8˾>"!e#HFy,S5N(`9a.NG͂g 7v +e)='Ĥp]{w{r/Iy~r)VTM%8kF|oXp 8!ש!)ң/Z0F/ 3lƌj7;rJ;xY\՘b װ/^9 A6%R{pDjȡ99:O6W mCy +gs@[#pd;@2BJӨR8:|{,")Ƈ +[euD#ӦjC}JORn?  FZ =DT/+r +endstream endobj 401 0 obj <>stream +HtWIn]9 ;DQv/s  =?,WD=C8FYTBj1LceI@@fcGjmpa 3w墎)t MCmq/L +=p2Op98&U $ +g*;RqƋHYgo濻ЁYY5+'[@? sa0s A( eP@WH?yG~n =ՆNGuH+Dܼ^vvH3ԂK;ivK|p1T;;mꅬTy WΛ,[b9Rj}J 3esFqNŁE>6x #V hpvu!^'^ؑJ=U> q~lL{<dWq]Tgtpv2hO%y. ݳ&¯0x,؟u$hނH'{q\BҍNI!+ H9KH}+6yo{3 ^tK4EϫJ +U?9\V 9hhsXI6Vcb`װn2HqP#wL]x6?2jCC +Ԥ̲mt)n#fO+Cy&F/$+2wc BfGSi`N*F'ѰCxJϧ=@~=A9B{ L[Xf]1WU֖=W/s"[} <%xӝ.ۈeQhHL/0l'6/ON@ V4! yquiKZέ0nDR{U4 PFLp/cm(UKNLv^iZ[@ijvdkKCW>}ML95"+KL7&oFcwzqX[{ + `H։Q/eɸajax뭱ccưu'B!r_RbPyZf!=V4'IJ;vҜ'U=7$i7yXʃ.`+=e|Շ !&*%x2O:O׾utZ2a }F*%a$)!ͦE]DT$67f\FzG'sՒ/ խNd2 $@^&+e냨Yr4BYs uvD0pPvD/ovny뱧4ֱPnq4e&\ꋮvy u͋ʲ(Ϗ:`ԼS:Vsm *qkCnصAx*X@#X< b%:hTt.IGcd ^A22;5TёD%;谿<MThBw'Sqc -շI+5v toLm =[`DªŇ/U2 [myl`׬y£+rHrkl>lE%w_ odJ-c"Z9 đHKqEEGzO۵9b} ݯ2vnHK62YBy-#*P3U S 5Smd|: ͤc}쿗 &-N6 : R"Un5 +uFR:n%N8@,)+MM!|"[_~U/vI%ݱIbv׵8ȵ6 Pt;!J+TUE[ t)h*#OFXyJITZ"iZJ`K:cJ+ 騋Qy,dIa ^p5ޥ06$'.)We%ϥ};!uUt=l󍛋''e!8><J]ԿBw_rXX"N̪"f@S9(2m*3h[ݏMdV&fHn' ZHyl2!+8xeH.Z:պH#JWYjd5:+'(O<^fW5aD7Z5Lυ_ǣLt].e熋q'QE2!m8B +H3 BF9J:H>h":I&Ͼ9 +KM(.-]1ȣOޙ\:3Emch~ V<^!h^4ځH*(Ep! +:Ww \c=*3zE4@ !/={loW \^i IUZ/KlC-1m8X1;aY=Xo=dkNQ+$;5C= \_X3}h .:酆)0<ԡZڹOSޜV"5)9 "C{e_v?UӘQ#%  93i\ Wݎ$SMRVf_4;f` ci(֏-eWC"r뒚^YXTؾ4s,iIG(rrH +hU>[O7"XR!T$g,d6vGXkyRhPK])C5O$$ *wEz=,raoe ‡VbmL&)1 EOhS5$>˪QWF6}ITB6 m 7%NVxQq%smn0 fN#DaDZgMSkAiXeq3ŏ $5qAj&$в ?V)[% i0' Z2bDYM,sF7Py m|CRrjEA,ݨT6"ێ 6^ߦ$E) F1K۞RuOc;xs*c)T6g6ƐuͬoKVk 1e4ZC= +RJ#Ί +岨Աv\:D$ѶSֿ3ǐ؛F^j*mSrhpE) ɕH +nMT0fj3߫V}Z}t@Cb#t|X +L"-G1kCZȍd`ܔ84|ǝ_oa@o-(׻;׷ڵ^H`bj|AfB,̜myjkH|jN0 8V[yQ|2'ǐ]RЅZ 9Y4 +N!h 1.c r0NkYXDa/֒цB_fmV: !PJMm$,g)#BF3y:_Vanhs +>v z@.ŸʬaHOq5CDP˗ Qm1i!y9A}?p.}|e:@_}!yJLq |K"UgnL% +&bGpE[(d:5Bc;|G~5WQaq]Z6Rin,Kɦس3-r72>.px*ݣgS\|ql\JB^4*]<{?n3a`9b57'(A$_Ϸe *>)@V`>~j #pqjf\&r@%I=ݘ,q*YAKn|#M &2lgޣxfߪ.7s57YqD6V˷wV{>v'CzU#SC,N*u^ViBSC$UnɅMg8zLH4h}2U5FJѿ{B~$?wBp8ѫi Y=qc`hrA@N-lk-b4u0#XXWDl#}jcv0/Z۹oZH~%X'v +?? +dk+x* Dgtzow_LrDd`^fM$&̢j?Igڬ{w~n~Œ჌xץ`៿SrshGZ@ 0S +/Sa$sx82h }Q_Q_v] `Vm-[2'̜3bcs.n ͑xOĸyk#ns\bi1^fΥS<Z9rP]ͅL_(k"FwGd8 +kѵ!y }||!_uOtd昗 +0d( Xm/pt'\~&s,yh_?avpΘ)|&gv@bs|`=8"HtK^T*$)?eUJgU7xi Y8ޙ6g6eK?meS +?zw`` )E8ɪ t<Xd<TZ*$5"p_/8J^K97v^=$.j|˨ϭSQ6rVKzbtǿxM y6썠7 ' z:~n|P͍oc s;kjL?L2ksuy}=~<\6W_;w) O n m+Id O}H7 +rQxsV[т&]cIlpum{@KPk8z quT˂=5 4oGY{Sm"0VK49MŐ*ə>ϩ>@Y +-1l6!JvT5^tYj@}+}K[9&J.LlGv'B{>]Y($iԐxl%.eIW*i!,{Q'|pDMN๩0v42aMv_-4ֿ,NX>&0+{-'ez$Q{`Шrbuq5mw2!Z+92jyzD)o$t7D  :-.4PHɗn5J Y[0'Z *Z_R1azc6%[zzIZ9@=#l7͜@mgMd]-I3n`zYxme8(aout\AyN_Ng/glQDvS!N#rK1Djl0-ilͽ+C՗"$crx2y +S&tQPFWEG8odf`J5b'=}vU)̻X0%$q-:8 h~>#wY]$uOyBҞ&Vsc Bf[vLxk+?jlfjav9|p2\̿}_3ߛdZ^v\@ڣϑ*I@_i8U +A4Wz9Go*2CP-Z\tonZ,c$bz0SFr $|}c[,fFϒ0[*>P1$:y7xl#–?</=Ύod6-opg K46!n~qӞ _>eâ +ɾq6tj}[K&FPk;4ZȸV Ra7I3.aZR4X H !]E"'TY*@ݻ;U5HH71N]/DhH@G %r&эp]\"s}+ΐJF=.<k(_ i؃DRui0:8Q߻Z'x!%ɟ(-&NZ,R-ϻ0QfQ7ziTm8uӔiSogb +sDpsK jߝ65d VŴy"Uԭ[IDBs8 o$E6hc-8DP/O9* 탫Kw#Yv#Ȗ1=vGUN d$o]ctDG 5Zv"dNXb7v\&G!FiK˪l XMqIOP4EɷJ =N0~ v:d B$8H{-HlJnK ]'b褽ɳpr/cl>Z $ p >6.FGĚûe;QD&cc}NW +<(u0 M,S{Sy)S27|!Bg.1t`Y[Qh@(@+/Nk)zaghDxD2lo.C UGժpK92`.*֚\pgR 21벢]7ŤTxxX*Tkf}W22DT}t +yR ^LF;cn0g֔Rx%Gҟnd,R o ,R 6#f k#N\e<_iD(dt90%GT!3'bhQh EQ|oĮH3:+!i5_|^#:GY|)P@(R+f"cdpvz1bԎ6'Q `f6J4AW! 6'jFAuEC1#YO}zǨF/ RG"IZlEY]imkMY:ZxqGOŲ=v2, GzϞ̞R_o,t!Vat?o aG u I,{ #N%Z(8 +~jk ? q F^/,6LnLp!zKfƁ4ش^ȅ`\=U TK-^8;U;P:WN W㷤.=/D"^!^|6G8NJߌ'J<{FγWkbb2ǵZQ$b6EI+5MF٦n92onuacÿ3sRrхi6TxYW+e2TQd}02צ'2ZFQYM${y-Z6Y5D6:dѣƂG8;gTG0+2 x\<K?l3[ sܚcdB[(CH03fl(je }k{ xJ}(ecu}fz2/2uxpC{;#l\ud`ޤWg<#hϳ֪MaU8CxtcXIAًe])U 1׉TaC!H9YɁxn#OA<>sf?^_%'EʁM DMQ~]X)PB p%mt C k2Fr`-Cn̓2tni2N?;_f~t)\]I}RxZP1T\,q$=V{?3;LT6m!pHC ._22Kxt!|otSNvv#8qm<`g(YA D<q' sVϊ 7ƛ7x^e#A ]Dv+*E@f6N@j]! q6~<ōBSdl +.Z +3'f>l0z=Ŭ*nNe6LR.Zg=sd +RnRg^`f/,(Q+O~Եdk"ź-mW 5VuRn8VKm˟:J [ܮCe_r8B}ו`{h {4]u[]2JՏw4l)ݪ0W( Y@kM(Z d߿ WnH W?pu,u`_ &EjP힡 B`rĪcz%g.%b_Oo=` K+4%Gw갭dxǙȴWE +乹H6S+=K* +{h]W9O E(ij(bΙg~佳=qyY!ѿ'6Eæ%-?eCdMqqY͜ +pa]LǍHLUWߴPs!IbVM`=o Lg{S6fvxam Sl墼dਝmRr<xEWuj,tjW?nls{-+gi@Ҕyk"$z}O NkjM~zw;&*K3o==6a^ot{z?3Vccm}Ȣd +)":KRIjqmc;Րt*>%?޵αQ뽬53@mDƚ_ d}_w?9Y=] F;\5~!?bGԩ:<~txvX\#ɸx\X8)6]G!?DU(2%42RUp]`K Ea@#kʔ<.FQzVd5!"AAm;hֿ3c9t9"L./ɸVl3ף ߖ˂mזbGV޻F0S #Hs#(9TW#KS*Lpd%zcQH*-aeFYkWmUw˝f\`#2aI:V3*} 0Čd͒yksL/tkg.*Zn:}g ,ڈJrjn(3oo~ 64QE)"ZTlCm :ٔߗѪU\75NfoFt[?nGs*-& }6*چh@Įu&mO>Pia +GE=/]MV}<CɓQr5}rcAn C⦿~(=IN][ $EnR/l,XH;2?؝zb_}|+*+,U)|xj(uAf6NRGփ5.:XsAX%#c{]yC?*AB渔ώgh*ޜlL^ T )?e7qw<H|:pH$\*{ɍ >^j @E\& )-y@K|%^ IѲ?aHg~iۑT_%DoGZQ^ X|%kOM827ZWITy5 D\*(8-FlY7#F"|#}m$xp?w}6 *כt?l?/Iv%ׇa# 9U1Q5Ke1qT$H+ۂ @'@P找\Ƌ"C,+?1q] u%rƴJ(@^}ϧc6,0g>Hp%WB cx'vIbL:PfMO[iDĶː *~'A86:1]O:\ 2fm\Y[;y'ữDDAh˨RK86L" +Ӱcf9ihsrH.D D#q?BkF`ntD4 + $Q_ |H+ .1얄B%5k+AcRt2\LS3ޘոŷ՛<lF^#| z!C[:ט*+/ICJ#0Mj-S###`#]f5~i[,)3hSa UC- 3,M{C,N]DYyw8)V10Ds'uпzCƹ[ F\#+$"Ɣٌ^#ɩk{T;"f'ֱ; kI9߳N3/|ZtTA=T=o2J~JF zn8>[7D=wKbbUտ ^u1:6'FSsG$RVǀx;:1L2w{WM=*Z4!GiEp*'Xr/v˽ t+w;ҫeIxmdL%y0 jmf|}I83}xz?]jvM.(m((VxY4n[?s(0Pk/%qo˭_ n"9DڡaDnф)F$}BKElj1q/Ģz߿iy(:?qɞ0 l aMW?V7=z 6Whq +ӱ3Bҵ4l<_2 +o_^UXQݰZ^!6'l)HͮɇaVy+}F1fz%6zwqy!VU9Q)*"-o^'A3KN[^i;i=Ԍ ,ݬC: ~131͚0MrzDt9ZkQtDMː7@G8?|ųN7Zɍ(QOZTг~SW? ZDن/:R< R^bFz0Ic\|[vmC]ZcW[|*0"H$ʪ%TcW 'YU/SIpk3Im6"3%I{n6G}*\pHP tr1}wh'cSP8+Vu)( ~V9_)#VqZ] 9bWBRiow&\:`VjJR4֧}]8kRYU +ܥ9w?1aOht~j*o ("\kDZm@ZlRڻ3H4Fڕq`* \|Rx6M̑,G{%%^+p?'$ZTiy@/7̋Dq44}V@d؋nXsőq^sE T, Iken~Z)J}~䈕d"}.ux=sg9eķ52k+cUN;a:"t}s#SM3x 1ƕUQHۻR<1v&=lrjNN^!3i2I,'u|!;)?O>Hy&%QsiHkWMTC Kեc+/J_s`rr*L EcZv#Ƃa.qI|;yA\KER,c݋\Wp+Nh՞@_YaL0JOJlN'1TK!U!SfGN%=g}^lTi\9虷%S4,!XdZzrV(ƬfTg />ݤ:BzZ^(C+`^ZۯI")vLsHi֜4ĔprʋǤjsE92mwKKw#"IfơkarHW:Tߐح~˰K3gA)˃oyMi[RЍ̹g@8NZJjnq2C,D'b$_I͐vi=,Ap(e/׮xدz6Џ;pO 1^}bx'8-k>ϨC0UB$-ʁ4T_5mT@lܴ#tL/ F] NJjwwb>Zh>p"'ݎS>DHS^&>PX,HHnK"rϾ d߁m0}W&yᗠ߿sUA5s=^JcI]o{p)ECGV/fbXg{fݢs3C޺Fs׮[p?``g[̳?^q2 <%{v`,^v D\H-@@u&qp:bh ;/lk/t*y vmXY8 D['g(4zE*'9Ȫ^NE>g5?kn`Ǿ7)&HC.Dr$NғE%:2R;eڼ!;K:<~Ֆ-yҒ9 +^$ {kp uȔD,2(Uc+Wz_Ưs Qpڲc\{zzr'kmEp NM2AI7@]w[ #Gf3Fv&QUlXhCՙ}>bTF F\(x^cKe׏faY9;'R5w ~;z>*/W ƈ*glK\=]QrՏaKN_ NՍsFvSɃϱYNcڑT#gEuꪻt4X dd[O8e`11~*CύHo7`ͩ9JV) e +jwm-y0PM3qr8b$Uf *X<˱ZUt2s܅S(,v'[ UTCRAei$ŘۃU%.Eb +zF{bP0b_H:f{|\5dh2TsY&#>-;_2ߓ?Q¥'K?hO L&Ě9VJk)Z3 J>);1vY2e/Qױq35I Vj>^ }}N]Au% + upMM jXy "24A+)ND `N`̽D9";غU)ioU:H*3^[v@*L[OdnctrQzlw(7jG-a[؁K_<.GzN߄޿A<}VB: `Q3xxp?~7^gtcEӽޫzT\ L;\oo԰ rUm7CHͤ/VCM%rB&8 G;t+3YtWjA@l^q< 'bGmF@;\*UD<4ȜX`zDL@"xj闈gc 5l m(fetDDIp-5k1\uzd5Σ9'm"X;۶oNO +sѩ?!&Q5؝mq1 [rg,U6Qj& k^mX!xrWơrDSHmc46mf޴&㥧YKqEcy]18e˔+pi%ҭ&ju;u#U젶o^qb.'poεIBA855424c㲥Ag`btsEܾ]^[5ݚ?</Y +[ }ok?_ DžӍEq#Ka? +A4BPZ/~7K1 N30O$;\|ZESK;{<++9!S/F]4u"m2;N2#iOD @ܭ. 3Gi.(=]ms -$'<ܕoIHߦ;ݿ+zcjQvp仇NUN2+0Xu;LtS(eE JnBdwSYLQpUoO +endstream endobj 402 0 obj <>stream +HtW9rX;̧jb=o᪉6Er- Ͽ *(Ih/|FHǯ#75z.<-ӈޅʇYLas$-;1]}r+@ {0 텆ƵhԈcV=FG1u9~|5ha#QϏqFV<$} @ڎF s}G#@Q1+/B|DZ}D"h +]t5c2`"0 OǴ /ّ~Yl +6rd:Q" (1ӹ1Y?(@0*R tvc#ZBGAc9Y|`#+d[4@tC,ɒ*Q {!ܗGh + <_46M҄7R]:x{f:!D9.p"!MVAeDyU + +'<귰=Bv "jS>e`>iMD]t8hMWicޝ_..r_d:p8*5U㺴޵k@\QV٢.:ENt2*v !z +ryavCy۬BvV,2@U#T)MBN[ oVa6Ƕ}6k&;PFT*Xψre}LRрCBvNxOYF`6-r\,X{Aș=#( +_`?(oϢظ|[29|$4~/b)̧C|z YR򦎐@u.$o;#דBBD_aNW +v7y\7Qh M,28iFØ1f=2 /E3QFHC6ՊlM03q׌նAh*} #)Oqw+LɒPn;3gJ{8 JKc]vguC6 f1Fֈ뒓xm7h33CR8>:=yDp:Vnp6cgf^ouܐGepMEN&,`%‡:\~U!K,*cd4u9ZI[eyH *ONbTLq,Og9[L4'1CAɦBbatYƘ{z>CSl7g!NNY씙G({Z3gFT'ǂHԓϬsLx6J}gګtpӥ?hR{W%lr +t>t&]fj? 96?z)TSÅ&Z 33.\GsqJ/+סS 3̲RFсoC⾍'pMtʴ萣V/G*KkeprؗBj{.U08|ڏɤKp:]P)QMk?ٹ-SW@L#ʗ}[P摲"A䌍rHիCbhg{4yeף}6XiRњjt (M +:CA=Hzz"r'h+H^3.LF}f|Ԝ.&b&s*X"ⵠ&C IrpKd6~#^!/q}E<{s@hO&@'U?`܎*v}J>eWd0?MLȐ3_ Ԏe* I 1P߾1K?]v}zB^YS˻9>(#\Q>@y%_~d ZHdce7߰Eo㯿xSQ 48=Z{^SA.N ZiEퟫ*XfkC/F'X +_1eg0Zt{*7ꑖz9T"* GmPޤ *ڵ6/)KbT~TfA%cPˠ23}[Ț6a5H=ł}SD&'ivJCmO+h=%k)==%"*==eOoP{Jn`ZpVt*[4^ԣu +cN x97?,o1qK9(v._g͉ӺЙiR#kG ˆpkˆW{{-Գxq_76o z'z\mbe1[mFľ=}s?+Ld=WeݵZnx>hN@4ӰhDeʌ|X2;/y='"˒\T(#/ _Imfi1V}ER]gjsN~Cy_%YtMP F"Rr"k#5!41Ǜ/C2~1c߾ G +yH8ʚ茻lm&'1;BAtمѳPxQ6HToͱq6Bxl_YaޫԖ'Yĩ.{PƳ+:ۏ? +ܶPFpv"o) +`"Q~)O06GQDuMX)ݎ%`fQ%^XuZ9-1"w 7:N5j2f5XRGY9Dt[f*2/rƃPIQx,2L/5\PIB%p#) +هdQVj]ի,);^RމHfT뎉7 )reΨ)Av kWD1z.LIXC=!kVz;weghY{+jrGZF/$i"b^l [Kfe~Tt$|v3XjV-R?J7:&' hk?5^8$߀(A#mPHdvf]Y2wq{ܴV)|#$q՞Ұ, +jjN㢅: c`ѲZ\h~ßɞ@ +-77GP# 9!1lO j R0".`KUș2]*D, D~$^ђ8?s'x_0PmqR]_ϱNaBudۊݖZ>=I{p|޳ʓ 4u81j>kZ-M<}72,ƅkZ Џ=oLMƭWG1M4(s.l;QfIg#*F$Z3@E JJ]ݲTF=U9DžK`1)%.^~(yKMq d$%0*>MJ۶]"v/G"]J;/tE4SX2K^ƥ="*Õ6WDgYR_ fq:!Oy}Q {j&)zM,3z7 uc4 +CQ˙~1ԈpC)3EUxXIۨ_E6߷V +'z30Ot3P?O%j ݫxw[8Nc858BspY2 +^"Ͼ97b[WTlnÎ,dsbT]UA2>v_Jɹ+c_yhǴĻuOd[ (*NvO`f9\;@t\4RDUK>ݑ9^Ѧַ^MVmoӨ𒂉)N2K 6t^暈xY41h!E?yïo{