From b73bd1c69cfb83768492cd08d20c1ca8ef25cb88 Mon Sep 17 00:00:00 2001 From: Len Shustek Date: Mon, 6 Apr 2015 13:45:03 -0700 Subject: [PATCH] Made the source code friendlier to more compilers --- miditones_scroll.c | 369 +++++++++++++++++++++++++++++++++++++++++++ miditones_scroll.exe | Bin 0 -> 39936 bytes 2 files changed, 369 insertions(+) create mode 100644 miditones_scroll.c create mode 100644 miditones_scroll.exe diff --git a/miditones_scroll.c b/miditones_scroll.c new file mode 100644 index 0000000..838c4aa --- /dev/null +++ b/miditones_scroll.c @@ -0,0 +1,369 @@ +/********************************************************************************* +* +* MIDITONES_SCROLL +* +* Decode a PLAYTUNES bytestream of notes as a time-ordered scroll, sort of like a +* piano roll with non-uniform time. This is a command-line program with no GUI. +* +* +* There are two primary uses: +* +* (1) To debug programming errors that cause some MIDI scripts to sound strange. +* +* (2) To create a C-program array initialized with the bytestream, but annotated +* with the original notes. This is semantically the same as the normal output +* of MIDITONES, but is easier to edit manually. The downside is that the C +* source code file is much larger. +* +* In both cases it reads a .bin file that was created from a .mid file by MIDITONES +* using the -b option. +* +* For use case (1), just invoke the program with the base filename. The output is to the +* console, which can be directed to a file using the usual >file redirection. +* Starting with the original midi file "song.mid", say this: +* miditones -b song +* miditones_scroll song >song.txt +* and then the file "song.txt" will contain the piano roll. +* +* For use case (2), use the -c option to create a .c file. +* Starting with the original midi file "song.mid", say this: +* miditones -b song +* miditones_scroll -c song +* and then the file "song.c" will contain the PLAYTUNE bytestream C code. +* +*---------------------------------------------------------------------------------- +* (C) Copyright 2011,2013,2015 Len Shustek +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of version 3 of the GNU General Public License as +* published by the Free Software Foundation at http://www.gnu.org/licenses, +* with Additional Permissions under term 7(b) that the original copyright +* notice and author attibution must be preserved and under term 7(c) that +* modified versions be marked as different from the original. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +***********************************************************************************/ +/* +* Change log +* 26 February 2011, L.Shustek, V1.0 +* -Initial release +* 29 December 2013, L.Shustek, V1.1 +* - Add a "-c" option to create C code output. +* Thanks go to mats.engstrom for the idea. +* xx yyyyyyyy zzzz, L.Shustek, V1.2 +* - ??? +* 04 April 2015, L. Shustek, V1.3 +* -Made friendlier to other compilers: import source of strlcpy and strlcat, +* fixed various type mismatches that the LCC compiler didn't fret about. +* Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x. +* +*/ + +#define VERSION "1.3" + +#include +#include +#include +#include +#include +#include + + +/*********** Global variables ******************/ + +#define MAX_TONEGENS 6 /* max tone generators to display */ +#define SILENT -1 +static int gen_status[MAX_TONEGENS]; + +FILE *infile, *outfile; +unsigned char *buffer, *bufptr; +unsigned long buflen; + +unsigned long timenow = 0; +unsigned char cmd, gen; +unsigned char *lastbufptr; +unsigned delay; +bool codeoutput = false; + +static char *notename[128] = { // map from MIDI note number to octave and note name + "-1C ","-1C#","-1D ","-1D#","-1E ","-1F ","-1F#","-1G ","-1G#","-1A ","-1A#","-1B ", + " 0C "," 0C#"," 0D "," 0D#"," 0E "," 0F "," 0F#"," 0G "," 0G#"," 0A "," 0A#"," 0B ", + " 1C "," 1C#"," 1D "," 1D#"," 1E "," 1F "," 1F#"," 1G "," 1G#"," 1A "," 1A#"," 1B ", + " 2C "," 2C#"," 2D "," 2D#"," 2E "," 2F "," 2F#"," 2G "," 2G#"," 2A "," 2A#"," 2B ", + " 3C "," 3C#"," 3D "," 3D#"," 3E "," 3F "," 3F#"," 3G "," 3G#"," 3A "," 3A#"," 3B ", + " 4C "," 4C#"," 4D "," 4D#"," 4E "," 4F "," 4F#"," 4G "," 4G#"," 4A "," 4A#"," 4B ", + " 5C "," 5C#"," 5D "," 5D#"," 5E "," 5F "," 5F#"," 5G "," 5G#"," 5A "," 5A#"," 5B ", + " 6C "," 6C#"," 6D "," 6D#"," 6E "," 6F "," 6F#"," 6G "," 6G#"," 6A "," 6A#"," 6B ", + " 7C "," 7C#"," 7D "," 7D#"," 7E "," 7F "," 7F#"," 7G "," 7G#"," 7A "," 7A#"," 7B ", + " 8C "," 8C#"," 8D "," 8D#"," 8E "," 8F "," 8F#"," 8G "," 8G#"," 8A "," 8A#"," 8B ", + " 9C "," 9C#"," 9D "," 9D#"," 9E "," 9F "," 9F#"," 9G " +}; + + +/************** command-line processing *******************/ + +void SayUsage(char *programName){ + static char *usage[] = { + "Display a MIDITONES bytestream", + "Usage: miditones_scroll ", + " reads .bin", + "-c option creates an annotated C source file as .c", + "" }; + int i=0; + while (usage[i][0] != '\0') fprintf(stderr, "%s\n", usage[i++]); +} + +int HandleOptions(int argc,char *argv[]) { + /* returns the index of the first argument that is not an option; i.e. + does not start with a dash or a slash*/ + + int i,firstnonoption=0; + + /* --- The following skeleton comes from C:\lcc\lib\wizard\textmode.tpl. */ + for (i=1; i< argc;i++) { + if (argv[i][0] == '/' || argv[i][0] == '-') { + switch (toupper(argv[i][1])) { + case 'C': + codeoutput = true; + break; + case 'H': + case '?': + SayUsage(argv[0]); + exit(1); + /* add more option switches here */ +opterror: + default: + fprintf(stderr,"unknown option: %s\n",argv[i]); + SayUsage(argv[0]); + exit(4); + } + } + else { + firstnonoption = i; + break; + } + } + return firstnonoption; +} + +/*************** safe string copy *****************/ + +size_t strlcpy(char *dst, const char *src, size_t siz) { + char *d = dst; + const char *s = src; + size_t n = siz; + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + return (s - src - 1); /* count does not include NUL */ +} + +/*************** safe string concatenation *****************/ + +size_t strlcat(char *dst, const char *src, size_t siz) { + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + if (n == 0) + return (dlen + strlen(s)); + while (*s != '\0') + { + if (n != 1) + { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + return (dlen + (s - src)); /* count does not include NUL */ +} + + +/*************** Found a fatal input file format error ************************/ + +void file_error (char *msg, unsigned char *bufptr) { + unsigned char *ptr; + fprintf(stderr, "\n---> file format error at position %04X (%d): %s\n", bufptr-buffer, bufptr-buffer, msg); + /* print some bytes surrounding the error */ + ptr = bufptr - 16; + if (ptr < buffer) ptr = buffer; + for (; ptr <= bufptr+16 && ptr < buffer+buflen; ++ptr) fprintf (stderr, ptr==bufptr ? " [%02X] ":"%02X ", *ptr); + fprintf(stderr, "\n"); + exit(8); +} + +/************** Output a line for the current status as we start a delay **************/ + +// show the current time, status of all the tone generators, and the bytestream data that got us here + +void print_status(void) { + if (codeoutput) fprintf (outfile, "/*"); // start comment + // print the current timestamp + fprintf (outfile, "%5d %7d.%03d ", delay, timenow/1000, timenow%1000); + // print the current status of all tone generators + for (gen=0; gen>= 1) + count += bitmap & 1; + return count; +} + + +/********************* main loop ****************************/ + +int main(int argc,char *argv[]) { + int argno, i; + char *filebasename; +#define MAXPATH 80 + char filename[MAXPATH]; + unsigned int tonegens_used; // bitmap of tone generators used + unsigned int num_tonegens_used; // count of tone generators used + + + printf("MIDITONES_SCROLL V%s, (C) 2011,2015 Len Shustek\n", VERSION); + printf("See the source code for license information.\n\n"); + if (argc == 1) { /* no arguments */ + SayUsage(argv[0]); + return 1; + } + + /* process options */ + + argno = HandleOptions(argc,argv); + filebasename = argv[argno]; + + /* Open the input file */ + + strlcpy(filename, filebasename, MAXPATH); + strlcat(filename, ".bin", MAXPATH); + infile = fopen(filename, "rb"); + if (!infile) { + fprintf(stderr, "Unable to open input file %s", filename); + return 1; + } + + /* Create the output file */ + + if (codeoutput) { + strlcpy(filename, filebasename, MAXPATH); + strlcat(filename, ".c", MAXPATH); + outfile = fopen(filename, "w"); + if (!infile) { + fprintf(stderr, "Unable to open output file %s", filename); + return 1; + } + } + else outfile = stdout; + + /* Read the whole input file into memory */ + + fseek(infile, 0, SEEK_END); /* find file size */ + buflen = ftell(infile); + fseek(infile, 0, SEEK_SET); + buffer = (unsigned char *) malloc (buflen+1); + if (!buffer) { + fprintf(stderr, "Unable to allocate %ld bytes for the file", buflen); + return 1; + } + fread(buffer, buflen, 1, infile); + fclose(infile); + printf("Processing %s.bin, %ld bytes\n", filebasename, buflen); + if (codeoutput) { + time_t rawtime; + time (&rawtime); + fprintf(outfile, "// Playtune bytestream for file \"%s.bin\"", filebasename); + fprintf(outfile, " created by MIDITONES_SCROLL V%s on %s\n", VERSION, asctime(localtime(&rawtime))); + fprintf(outfile, "const byte PROGMEM score [] = {\n"); + } + + /* Process the commmands sequentially */ + + fprintf(outfile, "\n"); + if (codeoutput) fprintf(outfile, "//"); + fprintf(outfile, "duration time "); + for (i=0; i< MAX_TONEGENS; ++i) + fprintf(outfile, " gen%-2d", i); + fprintf(outfile," bytestream code\n\n"); + + for (gen=0; gen= MAX_TONEGENS) file_error ("too many tone generators used", bufptr); + cmd = cmd & 0xf0; + if (cmd == 0x90) { //****** note on + gen_status[gen] = *++bufptr; // note number + if (gen_status[gen] > 127) file_error ("note higher than 127", bufptr); + tonegens_used |= 1<zanuV{j)sOtpRm>`c>E25yFnP%GNS$P!dn%&H8{x z!ow5K3R*ZKuY-WUqc4tgUzduRDYgr5r5-Pc1idP@2E+xIiWwq#6TBbYtQtQ?J5}sH zuyVR*fzR3OTkG{Qru1t9#GQ=wb3By&>RJS;z_sIj5}yKNYZiE_9XT+#Mc)hxFrL@k0^L|K7#M& zBWT7LpXmM?f}@mtIvc?U6#FjWJw|xHqTq*=_c8@bh~Bvc2<8yV0S$t)DE1V^GO3~8 z6W*Wo2)ZbiumHhAs-~snpUy>aGsUFy5DZiD4GNYKFY#1RL!<%}dyg18L8ZT;;NPtX zTA`08Zq*{#HXA|PTm%Kwh3`{Ci_b!E5mg%`@@LFM5J#lyVGB>(O$5FrBG^gvEL5PU z4yNc37|%yAlk$eP$mtw3?75sEiGRed~Q}a9DX_%sUD*D;r9>N2FWBJu#Lcivw^9S zZ6wgEYPXFbVzQ}If^(4HLuKm^GN}yNNm5!z%JT`+0wW837oMhMeoEVKQH7W8>1R^$ zh*Xv&C2dQ2K20SJmxR+Zx(ZofUxH~kblqq;+}=iP1vqp0K0%Hw@|`J1miVR*==Ue? z{#GGVo!CZXX@%qLBuNR=?4e(R1c*t)wW@IXEKmm7IOIvg=!8@ZhFeNpLVY2VM#Je# zsC+c^?jIos0d!t@Cw`AToNnL@C86d)l$_i`eYAyyXbU@mH=srf?Y6;z#1nh}j0SCk zm8)-P*+(Z{h@(SzY=PjTQ5#T#c@qewD)TCl2zb?9^dbghXI)aDH#N^T zveC^;;}b`r_DFlLLnlnOB!AKYJ_^lfd|;X!ZzV>GM^c`gfw`p&QjDs9JTehur0Q>@ zWFR07yS9(-%FidPp+=4GqXhFz9i0XLf{fS!-bM(2Q=RAI)f>@=0@4R;BqI7Ck5@Q7 zHELi+K2|uSI#9t7(x1g4r(Xw)$MtOoruEY|jM4Aff|;TFdAn^CVmk=2xeg-;r_bT- zk%+bu{b857VJw^M+}Du}-BkH6KH0dWLS!&s+*qD?1{)jb3N%i=JoL+ts6^dvgAxwQ zhOE8pSgl>M9kU0ETbGB5i0HngTDz+N%>`|(_Rs}#7C2a)4?}U(ISR*`{wWbJbx5-&x%{KvS zc9eF{n}D_E@vO}O-o&g;M~Tdu4|}kI3#DPj+z*F7{DV?y&pRxB4{ru)99PS_NDQ$5 z;+Xvhsx%zQpBL4mUC8gI*@%j4qKbC`p^Ff(fJ(!lDMah~s4mU+fQ3TlFF*@~YIof? zzIRsvbK>5KD3N=2FV?>ZhqWpxnFuYHWUCZT*Yj3Wp>ls7j_pZ7l$-bDeBQoPd~CU^ z4?ILvnC!yOfer9G4xXUDQN$m8MyCiCjpA>(s(T6h`^?T>yn^o{-4fAM&Ys z^Mr-HC_G>bq3?WK6yn>WP+)Ut%H~r z=^eX#BvM97;2SV%9A}$)Yx1w;2I*zl0+O2x$5oe-d7UR`fDyvk)31(yp}P;UknjQY zd>3NziInc9=!vg^wt^byPdTYOH=oUE+g-g7NGKomVbv7utRXc1U@SKs$naN><0j?F zWRi@OE;AL^o)9FlC_)ODxsYzXCDP1sYUb$sN;AAFpE!UFHV-!Pt9 z*dL+Bnm}z^gc={B*kPF&p+X(;#|=cPWm2_Lx!MT_mXQNoTtJcsArO)W3e|U`FKxX( zWdN1@P|r||7N@iZP!T#Mg6ydpe>U2Yc(3fCn|PZE?QM0?mcVlqs%YClOPLw9Y1=^Y z0>q)nJZ_hQga<1^R-weT;LwB3vBfw+MvOSodc`+$X zyWYua+;z?d2O?i?OFUWY|F8u3erz8Ug73%7(2l3!SBdQR#%2$h`Hl&fgO7f%7%bhZ zPlw=s`uKh9leqVUJpg3@T#Db-9!zJO& z)q%Y~N5C8m65?{#qd=9?X|2N%dF(`*gVqPyyU5(n5TyuV4bHB=$iAqxO;IGIMb7K5?HUsI2 z+Ye`vvn!lMJO6MN9RkByWL3ghbVdkg(U~WlMJJqa7M)+iS>!wmX9+yambTzX&nb=yb2i`w4uoiCNC+g|mht=x53`m^-XhmT2d?TZuIS0}Y= zA*mZ*NElc9@L|+nUMl^%y?C^IC8`(eKFZVGa-}XjFeUi^Xq=?06C|9r)ACwwjvJ+h z{bxxrL6c%alwm5P#fu>+-?ms@haqSFi*QO-(Mh?M?N6jERj3Dy*+XY> z_A*ZA7DXkcT4*cBOdF30W1KlRUm7JNeY84Tsa0A=)i=VuwsYcL4$^`Eg`Q z>HSz$+h=s`qD1l$N~lAF;6GGz9MZDvM4y;{y6ck}(DC&9WRf8pFM9}OVgH%bK&M=w z>;22Uy5QHPO~sD5iHPk>=sJeRyNAKNivlV6DALPagOm}X;5dSC`Y3NHt$PTmfQ3S~ zd0MSH6adela{BhsUUgRnCXRxFbnTyAYwiH$?O>z2izbyluB1(pg}u&b{JEc1Ye<45g9p+j>22d%^fO)RwIIOOfWS4s!(5l4$fc%uTsVE0OFJVlqVlT_2R0vL{;9fGd#I0hqy{WScr)JxE_#PXIQazd zmjvC03n`pV`J{=@%Gx0%KZ=NyPD2WnqZjr@G`2Wj(dd?hT%1T!_c5db7Sd#_Q(csT zmxHWuz(QSQ{wd+~9U!KNV4wucxHJP6)pisyc%zS&gf__i-77gmlI`d?lF3ps(S&oR zgIJr8CT~?L9;$9nhOF3ipJOggneTt^4Cr%UFbN;5=o$jn>XfI{EuR!$j{!>1^$t|> zZ&V}kSD48~TRhs5NB2>9Q4to5l&7x9u$&3a;W=6$1UC7mZ={-8&(NuJ{R`76^JY~u z^Ffh^fyxHXK!jvNUf3_Z(u3bnML!IUcQhQHVA{&_bIZx|b8yz_=jVRg@1PNfRjI@h z_JVyu+wZU=RHkjvcGzA!1scBf?D_&ICq{nY9PCykraRPPpv;sVd!#+2FN#{FJZbyg zm(~E8|D9ig|F|gs@OaBRTIeu z+S{ImX(IO9dJqZV@YSjXZmD#E0dg;QF(?&=qGtna`fJh! zd3j7}N-G&y*AbwX!*35f=^*AM+;?psrL^{v4&F}`J{PB<-OyvG`&I3brN^G1x0&PsXsW?VKo`6% zZ&O|U=-U|DZ+Mfbikql1^wBXHZTuMgEmIg@j9*|RVdG4E_}q#?mD1Xpur35X3Huh5 z*q4X>>QJuC=8b!$^i~x0;QInAu|G}LftQ~fDgS&(nr! zrP7UH3hMM6+Sp=ZDXpD|l!k(6GtfI917M+>UMY99ARFdO9eijOVyt7^)}7!mSfJXv zJ@Jnq)OyH2h<)9M+u|3s_WC~wJ~xWYxxcw~+m)&-kuf5@^vtuUu;)bFhYw3H1>TAO z@FD4gZE;t`Aiq9_{9>?;>dV+Ka_q|01BpFbFsgQ}q)!m<;U}mwp*Ui7Un(E}8$y|H zeKjKs`C;7y)g;=kZdU6)n)^mO(l8h>+#{);#2(lcW*YKlZOb&KJh|8I+6CsY29lnV zamRMPEOqhLivoM%uN*i43tG89VG1m1QZS*3Vqfe$NBUEymR{JVE`X>%nft<$@kXAH zw|V;b@lM6?rj_G#va-Crtq%^{_WSoCJkWL+Aq`__%C8}=5e#!%*q>4!dJob=qMak~ zT{Sf10zC`_tjmc%;cDq?76v8s&ctLr0wQ*7SDzr7(emK^l#e4&;!jukVGzjthHk;P zX=zj`6 zY~X|t2aw}R0Cuo#5IO*64iYm;*6^PwYw;iLp>A|9urH%6?7O@)`~*oruHfniYxeLH zR4drV@x%V4@EiQ%hjf96W`b?~_+m;ja7}}eK}Ti8pUvYs7!e1$G$r`lI8;amAJLvK z;CM=d_NaV#g(nC@7M#qMYZb7|WrkJ~EB8?U1N$=Ny(09fa<%T|8$y<&UyXWyz5)3h z46bkLarUKa2RAD;DWP|2ZdP_7cfU+U0n_LS@W9 z4aNC#mTPJP>kYjRHh?>R#*ZH$J|456sI}*YMo<@OcE;0~sqE#;y4Ck!3z3{aVLGGm zf>GG0_UsV~EyDlpR*Zkvn-FH`F22dm$>rUeDgzi+l zgEn<{CZ*esL4XGmR{>Rpe_d4AorctbL^VR{&Vj@vUWy}EVuliX8k)q`zrqgOmWdqV zJ9>X1og4w}E0RZe4&C!gZl#=Mi6gF67{Ac0Uwu`B6eCi|6UX4qAT3Ls%QsV&ZzqNW z@=e6;iNCjWk3+g&RHn3f=VFz`^@rEK3UP6H2(mo3LQ4Nb^639uc?32mu?@-ObM@nZ zl?2UV$eO-kDwcNcD919LM2^>V(#5FOm8sjmNvf!vcIfidD0g7fanDqozWzX}n@Xyf6-T?b(rVq8>RhsUQe9vElZUrv=0+Nv=xZrmH| zKjM4InEJryB%#CY$wd1=B2lDgLNi17guwABav+gveHK3UD}hfFHlB^oP<+2zoYbDg zV>j*%9E?4qhO^Wsd3(9P7X6XbEqPVm`hNelwE2k(G^fV!GZXOY7voJ9^Bl*6Au%oeNTmzdj&MFGWZs*NW;y# z;0jend)er~S#SVwRl{Yh(ZHTL*l@z#0Hi5-wqfr~WI|(2%ac4fZjVY!pueN>SE=)$ zlZnU}NErB;uJ>~t2;c|)y55t|)BE~P?+@UPx`N1zfE&&pdO#Kqc6;PSBV|y|`s78U zlOuXNxA=H_ai3JyhfANRvWQoKJE5O{4LKVghH>={4{V0xPg*{!j`W z$Uhg(B3B=8KTXj=?&e~w#X?K%HSZPt)OQg-HfGV!lELH3eeoZ^(#{v&6aNCi1-P1ibtTSxQ~WRooy7E@G(Z?!LmVAn8PcK>TH9NIzwE$M}xLeT_2RcW%DSt zPvbUvEg}OKK;2v3AwKXy2IVi#xRS9w@1~wvHVSmeshfBPQiQ$F=>ND0{y7QAXKcy? z7wP2z3%UB?vBR}SiuiMs9DgLoH_NqVQ`GreYjZ8R37frBJiiJZ@&_)fEq3kZNVN&I zLO8t-5(?~tSBlmdoF@6V2L6R1ox=8XYB{iZ6suVex!(uy)pqm%=`xPze?{NXKiGr* zQ|tl0x=rZ6S0-Z@F$L#L2H2K8^fXSmRT@q|%6pP^Llj8kr;mdno5+A^;EGF{$|YILVatgHZINj53cyw22%z6D==2 z%v;`zyM$lb{rEZBBI^fn8~U!?Pnbl~VVv^9+_dmcV^B}$*e!p4NelFNzhzkfm$uV7 zq$$`@#fAF<`x9FJOnIK)uZ~&150^i`qLt&Xy8Tpq${U9t&gz5=V>?0fA9vVslk7an zmWhkN8E|)`LEx!~CoKknH=^avY(y0 zONe$PzEn&Eej6IP)F3#yBL1hv6e*~39nNd0kjcy!TM*^s>hZ4*SY8iBjFTi z-V82K!(TRry(HGPL0^6pKE#RmSQO>slt!@2SuIOq%E7OELmI10i1YE7g*i9oZ&bX~ zDrsS+jU9WPPZ}|b4>2fLCjDc`+NmF;6{Ml zuX62-oLgKKvrr(e#w6#gE>?DVnMbr6Lvu(&0Bk3!;WE5qptpuKvm)wlM`tp?kM zQ1Ji{)u;p?lk8V&JI)C*mw|@v2_hf9hZvCK(opywYFwcVSNwy4=ctOx{~akvG(Xy- z^56N&iNGguo;#M-5 zcq-yGU2~A&N>2QwAxoA8KH)208_pXW&*`=5^2kiUrwHhuv>)Py@2chJe`_T?NhiNZ zlIw30e%4@2e__spK^hx)P?KD+ark~tAu`E6MB;F5F0YLb77s7ChxY6v%Qp+kIU_pv zwjqzqzH1Hi1(HGC+Nt(X8}y9Z5C5-RE94S1Rg23d>!T$ zS1WZ>o?4tX_sx{2%BKW+XT|T=eT4fcxZiWGq&B_jnJRra_kGiN8$*&RoN6ijrdE~Gh9B9-j&q1MD6WSNhKAy!hNkMxR@7=(igUKC<;or zl#tR&-|R59!LpHBRuU}h2eDw8xz7f z_9RshKNAFT5ew9~&py{bXwrF>;^f)`&Y9`phG{1KUQc3yX^-b4sc0{l2UBTTXbbh| zU%Lv2mJ+zP!I=M;RM5l;;p7z}0;Mr*q!Jsv1E~^ZUp!1*DZs{0nh`EBK=|~YSGii+ z`&ZqHn^5)t#+l))-RN96tA~e0+?UYHnR~lg}5Bz`VjQ zfINvhDS>}n3E1aO&V^(6e*Dk=ti~O-$!AgmB!PSXQ~q7qbTfv&?sVL$VjcKR()2{i z=z?q-Aa#q1rTQ3~uURA@N$;8RyGThFDQTOMUL1KV`By6GE0uJik}g!zSManT%JlK( z`GP3Z$6HCu^aW9-kGGO9(-%aUz97o<@m9*Okm)P@3kv^&Mxn1!=xY@E8il?_p|4Ts zYZUq#g}z3iuTki075=pf|5}BlONXg}z>)uUF{n75aLGzFwiPSLo{% z`g(=FUZJ0@(9c%rXDjrx75do<{cMGPwn9H!p`WeL&sOMXEA+D!`Z)^y9EE<4LO(~L zpQF&vQRwF=^m7#YISTz8g?^4gKS!aTtI*F?=;tc*a~1lz3jJJ#ey&14SD~M)(9c!q z=PLAb75WB+zCodHQ0N;J`UZu*L7{I@=o=LJ28F&sp>I&=8x;C^3jI8Vex5=Qeo{TpVRB}I34vY`jmMc>zqcR-d*kX zH8wcCmEI~(V_ltKUhD8WYm{HCMZdvDc?49i_MTL{V6D4BDW6{@G&cF%jSWH-Fwvgi zXuyB`Ap#_-g++q5(eJ5p3Pf6P#1OJBsEV`^d1ra2F@MAQhQ^Hz3Q?m#dGt)1KYzYe zrc%@BsdxAUr^nOi5fEu=^tw3*S)zWGFej_}vaxCr(oDD^OVqBqQ4o+rF?ti_g_k3p zT`gqgRxijBb=8zlkd@AcV zoJE%jT2Z5!hag*6=4=p3U4F06xnAxFuZ@rBPN(2=IV0k!!k=jn&@rLTUFB@>I`QWl z9+E#~%K!L>+*wG$=WsW8$7DYblMq^6eG;EpGNTKka#7e~Md%LY zR^nd+b1)xsvklCNloxRa^C0xIrfAxSGJ#p}PvCXjsO9Fh9IOtpn@}E+N2CpUXMr9) zHO$B=fKiQb1FJ&KM*OQoX$4Z$TI9Wv>6n(|*70)UqXz#XDT$4LSv0!E46bLNNey{ zUz(hrx=n)Du>tKkV8*>r4xz^1P$k=AOyUKCtR>*d@AV^t76^~i>#u{_)!;A2)YTEE zF}13lRdvuXnQ^G5Gl~Qq+u&^Aq^VP}m6{se4L&IQ_{t8U+FeuQ^q|rNL>5frzhLCE z`aV%vAxL4u!{#{zZ$0>@X7QI%1mC76Ogb7L2N%3Us3U=(msAWvEUm$KLJ%~D#4hiO zqi&wCsnIWZF-S0V^$zd)ShI}UqyxN3-f2oXnn?E03e57gZXeYF`Nc5a&Spn_QyrF9 zVOsLkDwo3}RKW<%aSN9UV)Np~mzky|FM>T-M>6Mh$?D?|^(cI8g0^5OrqOjka8>9g zn)^D&&F$MnJ)xvLdPITN-Fi|bZlS)UJR>re^}gO&-{=7wv1&dxPK6LDRAy{S5FQuy zS|?U3bj7DgH>%}hmC?|+Hk2t=i6I2h(=~~8OWqHZ_%U8E*^69G%ugGw{o`ntz$!Fw z`%}*?(;{4ib;2gBhkdfao_JtgJg^eeSTb_TxQ$y2dqP%>Y>xm-B#lV5xMeaJp_@s{rZGyMV$9&Xvvr{=)3@UE4;0G_%@=VynYP{zIB9?KCXr%9p zBfswyMUlMX#%e#-{%h#hpwa{R%wI7kpv4Y%ogX7IGpkydkA;%sP#}*%)P@NSPH3sJ z@I(hTiVb_cK;A3)Aps}2CR@A!Jd-VsJR7~34Gq4U!YF-M5J$v_Mn+iCE$FY)Llz4Q zJJhVN?_1}ssPgy-HtMt$)pd1VpTDN20$bn)XS3UP>Y46~^ErOMS%Hz$ z;V-l0V82%b>ufmfPLKAkWTSi-JN%AlSXw`9ln-^t)>eUz7eM`ueJW8xc|Ks#{*&4u zI&=8H4ajqIUAUZG#zeq8jK2uY68S%k@)l3Z*G2PBH==7nw*h@BW~KO(Z$<11rh#P@ z(Yqq9o5UlPm17MmQOb%=vAl9m$Pb8L#81x_{DcZ#L$}g9dzbMkcNpCx+Tn#)rD-?l> zCyEi;!QsaH~IYEkU&XkQK|O7%dYl& z9=^KmzPEq$pH1iX(3-NySW$+Jinjs|oQk5xDt|o=4Bm=zHyxvdQlG!t-AHa8T?ITK zwb-kBD;B{O(OBm!Y-)lNhp%h8iX!J)|GJ4v7Bp3_jY#NxLjAOlyJG4m78bI3fY@^UE{kaUl%HXf%Q@0y@J}m&Rjxv|2H*lT0h$3@09yeafKI?p zz@va&fF3|E;2@w6a1_uF2myuwBLL(PunW)w=mi`E^Z||n`T-%p5MTtrYSBI*6_5cC0CNB$zyK%!ECJX7 zs{m^NE6}t`K0jvS604xC*02cz(fDax~v7>-z0gnJW0Ji|@02P3%06Bn50gnLx zuX*l#NX702>;yascoA?Ea2zlM_!KbZVHL{++=Tcwc&`QA4!9H033vps8}K}!56}7p>4{!|dD&QcX7w{zDhk!=_4*+%mwgYYhJFyq1)OelF_3ZGJ8lSVSj# zoq;=6_=ALuJ#dDv(cgrx@@(c5hZkSr@onx1&gm1uEk`JxhyRxDZo zH#%FXs$5jEqOxSgqROS$EL{OtY*1CXs;bF{kAL{I&WhqH9rfOIm2&Gx(W7-ve0p{_ zI6UjT?A*A@O0UybiBHp&^wkEx92E3CzM{43N}NrN9v|p6@SIYokAFg4gbNNn_GFw) zup+LqN~YY1SYtztd!5fklHj~M*?BU3e8HisSGd;#=j-D5O1jPf=ISe*%r=%{Z<4%G z`l<@jxgdwiAEpXU`6Tvxbi)%ENp85bQrm5?G#_7WeJ^(;TJ(%*nS zu4gxZBR-&&)oa(WKG49Z$R9PBH=$CAT}{2*(74`NxzXYC(w8$2mzNKB=xUlO@g?sE zBvE3!QtpIDFZa5E?LT8ER0{`bTsh~!A!^Fi#<+g*@KZj;=tYY;0ttOw(mVi0GxgIx`@FfQq9W1jo3&h!ilWZ&QG+1Vr ztynzYFkA5YFv%Tc?k%%7IlZ&UAuXodR zyT%&dd@N~3hqr#g2F+{%cQ)NMxN%(`+Zsp-g4yT672u_~)2mRs~9m!2IoNvf8?>1ESV)4!Yk%k-&fsyW4cp7|p4Y_rvToq4UV|ZBl%DrLB)z7%QM9^F;ko=&J*=wfq1RBLcBo?ir*1` zBt9WNBkmDj6_1J^h=OLG#-MTN>U3Y%HS6xsb?P3~{ZRLBx@UAR>R#2osq5Db>ORyl z{ZxIb{#^ZBy+&`)FV$bGU#Y)QU#<7+x9HpS+x7SBAJ#vn-=%+Ae?b45{!RU${zLtg z>@%~^%Dy=Jvh2l%n+>-awi@m<>@fU`VW;5{jM+~NJ%;BE`wTA{`V6lcKQJbmt}{7I zb*8VIHkiI)YBAkmx)USxi0Ox>-KG~XLJ8&>=5x&Fn=i)L%rk4ux#oPc&3wCAGT(20 z!yGoJVN5Qy%(awRDlO|Qbr_9S%XW;${g#ID( zbFEsd!CGi-vTm^6YQ5dM-FlC;%X%HjP*l->cBwd5w1`{8_rwA5PhyGYMa`R<5ly1@ zZ0$^~UTe}W($;A=YBy^G+O67eX`j?St9?oPt~M1Cd_vc!`;$WxNYp>c`v zT4TBKMq{;cz0qs@1~mFEX!QNYZsQM(PZu7@(-zb1rtPM?O`Tk$e*lU7 z6f!$xdd2jn=^fLc>3!43CYAY2vtZ6LZ!>q8?=|0Ne$f1=`A6m_&Ci+lnO`!$YJSuF z4mA6H^Qc*6Im0r;vdFT+(qh>PEq>oJ-Kw`*tc#$v_0ZY}tdClsx30*q%lG7O%kRj) zH~+qTR-j@DkXMdq5{tze@lx$P?PBe6?P_g}*00^By;u8)_ET-5u141gEo;%W>+aNj zTlZba=~>-D-BI1!y5H(f=oag*hE_G}Z`I$e|FOPD-wSOzrvE_yv0jxuCHsZ!L)mX< ze~|r2c5;r8m1oTJ=IzKE%~KoGjDm5V(O@hx+9Bl@<5i|^({m=j`7ZNE z(5NKKIhG48GcA`}nk|LaCDw!1Us@l}|4DvN{`2|!@?XT9dp-YH{=501{Qt@y&i^!@ z*;Nd#4Cu}jF-bg2%n&aWXNi}KqL?F^#Vf=m;x(9q*NbZ)pE}VaHjB52Y>kTHFO0DS zO{zXEJ0piRsn{%}n>0R6v*u>a7EP;Wm7&5=ZEzXt3{3`~;kbouQ858HOSCJpeYwrK zJ90a7ccZ^Ya*x73qM$>?(l8cGOc2%JBo%fb1C~GlH*;VOMA!oZI4Tf}z|%6(4z5;- z72vB{bb+%b(FfjchSs-=TgB~Shqy!R6z>yviVum8ijRxC#NA?#_^j9~?h_A+hs8ee z2rS7lv0pqchQvW}NE{YN#8HuH5;ST}k|tG?rpeG`Y6Q(J%^X-8QKN^&F>4AmMVcj= zWg5F?g=UqeLbFCwt#N7UG_if(s@bmT(CpB3YVOnQ)I6klRP(rImu9!7NAs+vSF=xZ zP;*$*r#YfIsyU|V*BsY`G=rKU%`oQZD6Y~aXw}*zZK@Wsn0zKBFe!omwG#OM$>dp~ literal 0 HcmV?d00001