From ff2d7f99edad5b2e37f6af53440a492946022f00 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 25 Jul 2021 06:27:53 +0300 Subject: [PATCH] initial commit --- .gitignore | 14 + Makefile | 64 + data/bnk/oside.bnk | Bin 0 -> 28680 bytes data/bnk/sfx.bnk | Bin 0 -> 480072 bytes data/org/oside.org | Bin 0 -> 25634 bytes iso.xml | 25 + psn00bsdk-setup.mk | 68 + src/cd.c | 229 + src/cd.h | 25 + src/main.c | 269 ++ src/org.c | 381 ++ src/org.h | 31 + src/spu.c | 146 + src/spu.h | 36 + src/spu_a.s | 28 + src/types.h | 8 + src/util.c | 97 + src/util.h | 18 + system.cnf | 4 + tools/Makefile | 15 + tools/make_banks.sh | 10 + tools/src/common.h | 32 + tools/src/dr_wav.h | 7932 +++++++++++++++++++++++++++++++++ tools/src/libpsxav/adpcm.c | 328 ++ tools/src/libpsxav/cdrom.c | 69 + tools/src/libpsxav/libpsxav.h | 89 + tools/src/orgconv.c | 273 ++ tools/src/sfxconv.c | 134 + 28 files changed, 10325 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 data/bnk/oside.bnk create mode 100644 data/bnk/sfx.bnk create mode 100644 data/org/oside.org create mode 100644 iso.xml create mode 100644 psn00bsdk-setup.mk create mode 100644 src/cd.c create mode 100644 src/cd.h create mode 100644 src/main.c create mode 100644 src/org.c create mode 100644 src/org.h create mode 100644 src/spu.c create mode 100644 src/spu.h create mode 100644 src/spu_a.s create mode 100644 src/types.h create mode 100644 src/util.c create mode 100644 src/util.h create mode 100644 system.cnf create mode 100644 tools/Makefile create mode 100644 tools/make_banks.sh create mode 100644 tools/src/common.h create mode 100644 tools/src/dr_wav.h create mode 100644 tools/src/libpsxav/adpcm.c create mode 100644 tools/src/libpsxav/cdrom.c create mode 100644 tools/src/libpsxav/libpsxav.h create mode 100644 tools/src/orgconv.c create mode 100644 tools/src/sfxconv.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4cc646a --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +build +data/*/* +!data/bnk/sfx.bnk +!data/bnk/oside.bnk +!data/org/oside.org +*.o +*.elf +*.exe +*.iso +*.psb +*.bsp +.vscode +compile_flags.txt +compile_commands.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1e889f7 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +include psn00bsdk-setup.mk + +# Project target name +TARGET = orgplay + +# Searches for C, C++ and S (assembler) files in specified directory +SRCDIR = src +CFILES = $(notdir $(wildcard $(SRCDIR)/*.c)) +CPPFILES = $(notdir $(wildcard $(SRCDIR)/*.cpp)) +AFILES = $(notdir $(wildcard $(SRCDIR)/*.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific include and library directories +# (use -I for include dirs, -L for library dirs) +INCLUDE += +LIBDIRS += + +# Libraries to link +LIBS = -lpsxgpu -lpsxspu -lpsxetc -lpsxapi -lpsxcd -lc + +# C compiler flags +CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections + +# C++ compiler flags +CPPFLAGS = $(CFLAGS) -fno-exceptions + +# Assembler flags +AFLAGS = -g + +# Linker flags (-Ttext specifies the program text address) +LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ + -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x + +all: $(TARGET).exe + +iso: $(TARGET).iso + +$(TARGET).iso: $(TARGET).exe + mkpsxiso -y -q iso.xml + +$(TARGET).exe: $(OFILES) + $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET).elf + elf2x -q $(TARGET).elf + +build/%.o: $(SRCDIR)/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +build/%.o: $(SRCDIR)/%.cpp + @mkdir -p $(dir $@) + $(CXX) $(AFLAGS) $(INCLUDE) -c $< -o $@ + +build/%.o: $(SRCDIR)/%.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ + +clean: + rm -rf build $(TARGET).elf $(TARGET).exe + +.PHONY: all iso clean diff --git a/data/bnk/oside.bnk b/data/bnk/oside.bnk new file mode 100644 index 0000000000000000000000000000000000000000..3cb47b001ff64ac0c979c9ec873f094ded81d6e6 GIT binary patch literal 28680 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ʓ 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 z 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 z zfj_)vOJBD>0H2Z#OK`>pqPv-x`H;7?xKvxfL(Yeu-fAu750WbEK-*0uy1H57-TP$p z`(G_pf04>y{%^MTsRAKlE(lo*c=!`Ax}5&J023WGgu#AGab-MH9*te?5MT4@F%ywA zIa{2MzJKry%YJutoQb?S)+d{nz!{$KaDpyjcq^csBg`q>c3!UYEli zkU_fX=24t4vcqz`puoEx{$UD$za?;Xh|Na>y6#-Tn8;H&X_l_+F)&8etd24$z=Vy;ULeS3Vz_6eh#Hmh87ds_pu za6ZHeH;iU0j=g(3mluo3=|As3cFE&8uie697Z&FUVb&E(jpI2(_kQ9sxZFtT;S3!2=IB|-qf@v;dw_eqV zuwJ_Kyu|OBpQ~*un4Tyi2OD{5Ud7}plfOe1%2kl-)5;H-o1@3o$ln-#a3U6N(&203 zqTUvz>0PxMF05xSj(ReB5&x}q)1wnkX`#;_>y%!Jx!FSG%gRNpf-Gx3B>s``EuY*} z2MnX3e>w3-?JvF?zoKUWzv5%G|DEkAwJAA|xYLZTzKOdPBzS9fZ?}?fB{_k~Dv5|3 zyTQoBB8QgsC}l?YC#qCP;Xp~%<~rn%D~-?}Hmid@(-L%J{CAi>$kV?X_IP2&jFi$r z3v}m8uzrQjX%*`t-@j!G-gjdf>_5+S8S>Ih*8DD~uUe-dX^Gtdr8|n1=$6I@v7XF} z5hdJd{_T%7AB;QR2>z>LZjuZp&ERlmCd#nh&l7K0Wt3*rBy8Clw4KFzB+r@0wWvGw zv6kDVI6rp8J8a{=tW6)oA6|93E0wsGe8WBX-vI+ zM5>9!(4R5IH+@#W0gPK)pvC)q!MM(J9_ImUb~Dx!aA;^H@_;X0YxeS3pNG_Pjdt42 z5bPGL^t^``gqkUKN;AcrON#XzOAQpTqh+;e%^|G6`A{SFjmttnUs9EZ{WWX@V>emL zn=Pr9d@ⅈ31Jwr0&kcSy^fyVXfP;6bnQEZ5CcC=8Gqh ztuwo(9h$55)rq^?4o0OCRuPryj?GgIx?Zrq(}DeSYImR|);0-jBtC=vsRUc3vCXjP zNS|In^##5htJ&j7>k0kE+jN|sZ?R#A4?FA~95@eulN^(t%5+N$UVDuVsMex=H6<24Yk&}6J{;f1iX$p%))wb%*ZOHV8N;Du6_ zX{|yWEZC2P^7+vDxs$9_R%*|JR$zaRk^=Dh^t)il9^!2idcHWu<2qC|ru#fS zq>L^8AJ2fgunMGsT58r2l05bJ zO+V8%&!Jw?)r0mP^v1hLQPrF(wfnPgV~N{2{H$m%`>5zMr8OG#`racKJ!0Dvum8(g z?R)YX*w1LyCqjV~`>-*#FT@z1vF!f1gwI2-rm(iTyTCs*ZprS{Xkwmov#0p(i(5X(}uXa6PWE2jLCd&VNP=E;~?=+DrYab@r9 zO=!qi@dM!T!@}CL&zxUZ@kH+lX^{&0^M$8J-1NU9>3FrU^_5lV@3&}5S>@1bwSRET zU@wgu zyUfwXy16%c3O=iXzK+WoBhPxwQs3w4{Q~$ny0=3V>&+zIq$=&+3ee|NskJEDh-qTv zg{~Yi8RUJw{?pbI&5c-mb51eIDYzIrk@(A&UG;Z(2Z6rbO~ucC#b0mYr7#K*lR365 z6ao!mL9?U){HsM`1Wl=2Qog{J5BQM0p&?+S&D@tU{h|cd%@Rz{yq$_K9QfH3ad~`A z?%WG}D;Iq^|i(vcTp6r%7)4{nYe#?yDjUk8}ut5Pn*vxjX)Yuc@8Q7no z_EywJhWFb03^>mKlQnNn5=0KI+dJ7Gd# zhMp%7Lj4FU2gcjd7lO?ewZ9@}>LJ+6Yv@u3{F%BqG>iO@*F+po+Y3-qQv}>0#!m1P zE!v2+6ehPK2@oJx4v57&nV3npb=;x|D5 zB@{)C6DZpwR9}LZ1?w+@zJvZ?U;&#in2b@!`1Cny2FURu8UcMu*nR=8p*i{}f=e~4 zrrpP5Jcn)x$|bkn?S;;Q=4bJS8X61~Nw1;Gz&ASM_973}H{uc1($ET^-|}i92yLr_b^2L}6}p)2YRCcvE+V8_7zgQmT)`V<#=2HX6^Js6dSx=v#P> z0)+gy>Ti!)FJ-Ay_I~{}9U>>KNQ?F(yB;s8omzCCmgm;v32xI*FRuHWBGsZHv_8B{ zn}Tnz6W1t}c%dps($i~#k%2)CZ5UDG+AZADOVucn!V9*@Ubhq|;a1b~)T|RQ&}+SJ zfQVk>Ev=qqNi~`!1-)^^&-2us4NMC_&jFqxKxHlU1t){PR~QO)H!Rt(7QuWG;Gs#y z^o;D-|Do-n9ke;6)xbQIhf7czdI6D0IG+t{aSFIWJB0DTP@#AjAp$eCz~3gpo+pgs z=noLBAH{)oc&;D7aJ5#TH%eKwfNsROg8WAb7S@p1bHGLl-QN{#A)&+w8RP-*8?3ZI zdU%Lhh4=j`m~(}A_;nKK*BdfG+(47n@Z>_96z_(x7zO<3b5sf>0RwPT`UObcAW$@i!V|`@I0%m{fT)eO4^5#D z!)ycZO956uPx!`jw3GIR0;jr*;sc(c;{XmgBiawhGUzk4R~6s^Rx3c(rwm^U)>q)6 z#IFMAsl6_Rt21Z-pA_s<(R}=XMHxT}eLC8U0S51(EW{bUruuNS!%Oiuj4O0}DI-Ap z(Ok4vt#wmS3-AkLlb-cb{km6xyA!_wF`6IOtW$uJlhO<714}{w1)xy+1_`KXE1V!I zp*;M?l@w%*YoH&B6hMLo;JLtiD1=Z7%@0~fCdyreYqb{_NR&Pz{bAMg3S1+ipQp^b zTF~K#>%;wU3hVdj?`r&~*?z7M1-%LY!y}Zb#`|jeb0GaZ|NmSMfJXT7{~A09^V9yx z#YG<-D?Yk&!N_AvR#*LEwVb?tZ06D5Ju`U6`p5UrS$)7n=Et7n27jR+`JsN~$>eb+ zaxyu;eSCiV?5D5Ke)`fJmc07#2OEZ$6%8(1aQlO2Dp_)F#-ZqiCuirMn;&&NtAQma zCe}fiCN~=Q$ea4uDec9qhU8m9%f_4!6mTxAy_IYO8@I5cT1pmYA`5SF>mS5_r zRF-_lKtJV}bGdy+Bap0#bm!E_N%j!$ZwSTm(>WpcyZBl8a~_~IG(UwP>M+mAFnzp3QIY3;-B!;FyJT3DXxQ^X7! z+nQT0CzF1>`eenj-A^AM7x&fdPekOEH%k66;~&?adVl?2p8Mgbh^*bS@`H!Qzd!rn z>a&-Zo)MAEs&P{q#&lTwE~JS}kT=~EI@KXApmyo(hvv4y|BAgf`$BQTp4p$xd}aH3 z%pKyn_0z65EGd6%{N4@lU*0pJ))dp0b6!0DP)hVghOFp_8o%2VeQ|?hQ1&hzLmvD% zulK9v=c6X?HpLyuV@U7C`Hq`&%VQ_hWhK;SvgGgE*Bz@|dZ1zUXO}0RydffM-Ws*` z<8gE9Qqx+qlq~!^!MNI$-dCSFcF!|U0$(gs+N^UM9$R+g;a5Hyw1goYol_ibId~p( zBxTGYhUms0j9S)lbwyu8X8gxmhCF&uJlA)%P=coiMcFWsxPr`@u{EMJZ>up8`r;9D zPPI*mX|qhJ?o5OJ>GZ;R2kXZ6b(#!L_|(CCFLUYAn-0xg^3|xtTiz6rY0n@0WXkw6 zgO~hsZ~n`lv!pdMt>$v>*xDO8k~;Z3Ow&jB$onQuxT**Orfd=hnLD zccYfYh{!(kQhTLkZc}|$wxbQ}@!3_Ck3ISASK~&v-%N^0gucM7mjxJvkKw#6W~?vT zFF!Ok?yGB$%&L5I-OI0u$dRJCO%$;9xj^ zvu;XEJq!6K!`s=DHl@0CX!cy_&nuFaKKk+AthI6P9?e@5!;rcw8IwatZsa~0`s7CL z6VO+`EBwBhJ3DyzN(Q`6N)KN#W=rbY@X^}Jk_EHlDk~Obk8P;SN>1w(kqZ`ys+*Ao z+}h-Mb=d#WC8Mep2;pCPUFn(VPP^`d#>ofS9cGh}w~lPkt0l0#?sbz_?0 z$BkY7sB_Pa+&QRCJqP3W#^$88vv%*hwRDN2;^KVZ&-eS@U-{&d`wpj0NNed4k#1ATimL$~dlfU|? z^L=sQEZMtP0N*8vhrZIx9oy2t=1g#k$o6Zm&h{MLQR?`|aVO~eumHBfeM_7X024zbtpOfgUXEU%LLh5?d z6J)iQxp_)g8`OB~9%#>=fpORKHZP8nO*kTNHMB_Ds4`EuX8*9sSf-n_N` zDH#6LvpEUP7rD98LpL(UgI{irTK6v>ZO>X8{oa`sOQ4^x95iRn!Fo$}%*D1TIDfXQ zV&R0C%bm#vcyL7Mc!KI&nsiU+jkLLSN2bDms;y$=l3(sU^Xl4do1cc)Q7_O<^6|~( zV-8&{e58gUXN)}Gjxzr@yH%|1~N z|6woxZqG||8(Xt7IszIZB8)oV(&u!xO^EyI;aPvf{+D&i$s<#jmG3E>^YK^9STg#( z!LQueZe827ujp5st}$f3xUNRru~@0l0>RY6Wo>sSxg-_PV;wkG=mkb;6P8TfUpX`Xn}^9QfaI`Hc! zz5)GbCSSBZUl%pE{K}lh)6a`Y*Xb2Q_D&uBuKAI-bx&*(k=R+=qL%C$^h){Qb=8B{ ziOBie>jwUsu8GL9^S73k51LambZ+gqWvvP_cyX5&SoGUR1um&gk_^ zL!6($pO{PI6Qx(jj{bOJ?=_TnF~9dn_NP&=RIG~Iyzb8|iOsH`c<18W^3*YVR@iUz zyEb=)Ey_&(b3+P9k1IyH3)RR-av$QBH^ zb~^5{suw@XzV+>l&qSo8{+FZ9zPNS%?SCwMYX#y~em~=_6*upD{EsD<@00f<{_Ea99RM9x8-af9*Gptkb;Pl`3s2seX(F#*z1FvMMNFLT$S(1qMia>? zlH8xvb}}W&K6ZNZ9TUkCoybT%<;izlU#|Nr;;F>CB$?`7)s0%&3VpAEhvNNso~1qB zdgR2!RNEMaWRCo6%tF_fdBgr9S?KyZPb{^ztfcla*4h*JN`SwzIV0nfF22 zs8IJco72x<$WzsDOU)$Enw)s?Mndpv%JtgRDewkDH2sAio_YP#N~3sle}JO%JUpPF~|g|uqOn?Xd9?C>TlbPiv-4)(Pk zM~bPlughwx_4KAfA6sotNV1%4OioH_gewzbj%{zs)QP*ArkZ=&!Jj9Z-5w93tI!V@ z_LKU|W|zIsWOjP{v?dqysVLs%4QjLq5T~K~&i>!j0AGXsG}u@2f(;)1=(oqCzoAF) z!ycGuV6#Y#E`zXThrY%~I4Bb6@)#e$C;BBM4r2X(cm4PC^_EHegtsqFVr!Ko_vLz^ zF@~Wrx{wlzCDkOCbS`O0-@tU+fGylY>4vVGT8d%QwV` zRsO_qb{92X58chplm@StO#|QCLcde+FT}G#)l1U+YT`6a{pn5c!Q;S#aKhS7zmr|7!32D^4MrWCR&SUq&-XmmJva;0Jspf)(rT?}c zCbEj=8@$XpHJMCJXW%*h^obKThgaqeP=AZl&|m9}1MSBzIf5C#A zTTm8|GsM7U>ZTXGbHH{<67mOm(kX+xY*%dZ1I=Q6dNV9vfd+;wYrg5Y?L-Lja8Rk1 zDfm?K?1E!`9;l-F0x*d68KGEk!OiaR?EComCS<;|Pm0||G3ZAVx z*XRgBX=~Qt+Pc@~3Bt)eFV|YEl|1apGSlE)6s5|g|02cu4x=LDwbuN6+Z~TbOO80t z>`qGer#*Ec)Awd7sAX!%o$98#F)@g@nUy@hCPtE|QQbN} zcmhl0)8G7)w9zliNtwnyb`|gUI<)RwgL;zr&7FT~WQYWT&UlUWNV_c2oB||z&EHbSN4038pX&|oZOth=bSw#V;COxX^)rwY3DgfvrBBwe`zyI zzWnr#LziYqS@qnxFC7?9)<2P7{m>`J?vDqfDT4eu^C5Z~j}?E{so1dQZ~Ay~lqjlPRZUGDMMj z*#@5hngDOguW0At4_mEg^_=D{eYdJx%@DQ5WUT_Wz*@sRZ3aWi+8sWkN->!Wj-3lq zcxkjc>%yvbparABdfalP(cTdxjMg%}qOl$EQ4xjKz-hML4yp7~H$yb${PuP)uoeYd zKtFg<&zmN_^#0$@os;-kq8XQ8y!mWzg28B+IxQdefnT=wd6EsH)YP-~7~q3nEjMWX zD36oZ>X-@O)X!eE1(W?P?x+!~e)Ir7UN*<8*1U9r>y>58ieNZo+#TXC7!guRBWpK; zzQ*Tv6co*T`rO~HkF{l_kO?jFt;1q+M%VJskCpG16UBWx?}!^0)E6FzPg;gRmF}!q z-K6h4N-vY)=S?YyKk85OsRD(`D>lYD4{FF)2(XbGGdd}8{)D~?=tCwI*e-dLQZ;Wj zy?5OMeDrag(KPOK*SVmNp6k$=Q;*wls3TxhnCrnGj>UAC@?cVSbbsGqXU%Rs7)t=G$5^3%}wh9eK@?jzNiHYroVf~u1-dK{o~A~Z}fe3CnGJL zHCb5l;-`*p`&vb@dEAS2=e$|u`LQ``dn+zF%xSfL%`(`(vGR;DM>LPhxTSgn^aa1Z z9&9qo_(^#sFa9k7_E0evUp^W3(0k{a9VP`QBEb~xO9y1<;e#VXYl8l*uD$P^(`cE< z4tsq2r&u2~#lSKCw4{aq_T^bQPJ|H2(DZe`m-8JJ2HrOe_Mpx0M$KOm)03vkdD+){ zT1##$^~T1eB&4i87aJhCa#EvEE+4C^@eJ+Is1BcINotDusNL)Dm3Asja?GcPzBm!= z=#}M~$fUYIWBq&5u6D*|=(1M+d+!hl_-DJ6$$9p9?MMocA}cQq;NWRm>Q z0tMp9{No&)n;27dn!s=!@X0^sf-{l3d~;k%eTx$Ilqy?@%Na4Qh{V+3T#ks`(cI{K zXv3DqEkAnZrC@zEa{j_;y&EN_&cX_h8tWZ@%J;Pae%+Gsmus{7u8PP%wC6f9nvIE5 z6{?5jyF}#dbq~VFTk}SkSDrn`h)8onPe8AUQm@_v5AZ?wlOZ17R6KLl2i}vteLk!w z)dti&f< zGh5QFF{EMsHy4=OnxfBl^aW)K?Nk#9UK1@+|BK|~%gZM$8{aA?eBDNcR0WH_R;F8` zlautr?@4;*l{ZXmadopT(C=jJ>GE8YJ5015as}s2_GuA)WNd+^v|90mA zd(fdG2G|42u9rbn)d$t4ESE7E0g za^j8+Zexi=q?hT*W#soe^PG}Q_)}-^iz*k1NTT)$YdSUExzxvX!@hCuV+Es#dXw#Z zyKPtg5e3;0-MK;88N;4tq?XqG3VKPh%& z0Q05F&#@|{nhVe{&4{13n?&hj){8a`e^4>*ZOktwt;J$3*i&U|w|N{^l7HM-G3WJX zzxyPr6D&hSo_Xx0=L$A&`SY=Jwoi{i{#8mc;XPi(a?+@pOiqhv6Z+0IX-i>P z{@Isu!Wx74j2ijjaRw0?XL--IB^2CI-qn=M;&7NOh80Vg<*seqhLqPv!SrK?M^oi> z=X~;GhNwK=LLRIn>1&hSmD^5>OD=7alU;e$epUuqf`}xH6Y+%fT#F^Bbt?Q9uUY%J&Usxx^ldFysSjo{QN_8xVPht-kYsp*bGv4s;H zCS~RyRs-M9*m^sfU@JmR)KnHS;g1hXn{`2rlyP>O5>41ye(h`GX~y}zkklnhB?;a- z7!SKHPq{jE-XO^PY6y|v`w8m6GMiXQV2Uyz%ihk+Y2^%tQy3y8-TPRCXcGurh5g&A zPls$pU?b!7Vr%|tn9+2@mwC6=HT|vIV`>td@Rveb^lW)=A-smzi}MA0e$BQG><~^Q z`U|JFkn@&54{8e}r`7u>_PKZk{K>FU@@l_-C9k?)Hrz}u%u!zQ^?92^xs4(j*0a)K zylQKS%g7~gvTE?lyrCL1H9>BX5SbnEPuKroGR$h2C3O_V={tE75jBhRlBe8|v+rCD zIztu`V)CNrWM9bpbcAwp-DL}@{$!}EzOz}USGBHr;g>9#S!MIu8?j)*ZhAA;=gv0v z?U7P0xier=$+gs<{WCZ?@&5kbZLwGF{Cg_-=7HURS@3T?V>)u<8E!K0MXfaSFr6Zi zdVy@{8Y^L%-D=sMgRLU}UhK`ne$)$VYXq~7BG&wct1Z}VZsnZpTA5K2NON{0eo7hQ zg@ru&L$KhZ=M62zj};V@$}*oVhG+0M5w9wG10(&RWH(`3HZX~?MBySaY*97GIag|u zx0=Zf?qbv@!*3tE-CpB7<<=2*w~uXDlPhUW?~u0|bfhqbNv_c*Glh~swvz?FiQPzI zWJbjM#mPKkA+l~4;w;QoEA^q3L%Jb<H)nx&U(Zg#sYSwyNrfGv0PJA(`3C$xfo{E5Dhq($b3%m0h6&Lk4sS`s}= zeloTqD<*r_P!Z8gTUEN_Z&g6pGQocqjKg4AWv^ORTu^bYAeQXrn5Y!?`-vRag57M` zL*f8S6@2|bBDJ+ce-Ua=Hp6KS-{OJMhD8M91|AYEwRkmKDh?zOtUma$#tvqmlMPUN zkKlPbTrhDh-cVuj!cx#X5+XZ-#V&@&0Xh;$ZCf(jGFH z@>i9vHW8yC-AE8(Wz+2tBA@6;pj_-$E0b|rCs&f4XCcw5@~FMlu`XA4WsAsYg1po% zXD4PXa)yE`iA+v+?sExSPNJ$+L=E^{J`@#5G2~1&yquXl(TMobZe5Q^w^k-gJn!$t zd|O2=EIJTU+d9r{Zv;IRG}x=gY7x9i`*d-Ej%cLGOkhy}AwW5$;Yl(11+2WZ9=Ol> z0*7vjf#2c)?INjY5xHF~@K>iTbwHR0{Ho4je{;(>o^$OUZ-xcdyyngk|+VsRoPMYiF23$?4BeN ze;V=_Tu=FrZX4R*Lwqr%CS+IQHjiC6c7w@RffImY$B3Ey_+1w1P#84{fI+-?PG@^MP>{PxQ4gVqR;X$J2 zTXKJXPHyT%m>RAwwZ8F<9nrhJO)%XSlYh5C)8koQOzaZAbW|!C!CV-@#mdbu%ZijE zx^-kpw@R&?l@}9e?g_NDC}5u=D81pZUm7h$NL9ckB*1{7%(} zH}^A`)Nj-Nw}aSzNtJD^W&_hROP4N{7Q zU_p=m(ZxJw+rUOl?XcIPanWM@)S5CN_fsZu{6{3mhuGC~nB}KVe>z-N%^qPFD^UQ4j$WxB|nUC(Hd%YMQfn$wy^(#d~ikyRI+=5 zPRv(S-A*MT0=q08`d4H$S=+o058y1=o5DEEOZ!g$)bu<9$}*Wl9eqAz0DtZXQmr4g zlSSYW?8mE753kj<3h|eqUxw`(+))azQ~NO;#8;hGo?>yFIM;>!G&ED?qKZond!GiB z&l(k4OM%u>e-1KZI9>y$RG59SI7SZrFQeAT;o#F;O9=t~9QqLVj@A#knD#f^RRn>B zip(2=|1N4)JCLY-uaLBW4$>gO%4*;-W*{UO$c3%L{-gcXe^-C}zk45u{F9^;&!Vy2 z!ol5$%1`+(fI;K+eF5`1%r9RasJiqe4MKIi{grRJfu(e)X=oy(UqHSH$LquVLPrt! z_5}E<7x9(j5%>oY2nk08Jxu9GK|Y8m3uXrbf_pa_J;&!wB5njA;k*%jjcnkM55*EI_SZT83wI!taw$q4 z?T1%J0ok`8;vJ30C=W3z7f@>rf!QU_zg_^Qs>s0}M5FSrNWl3vApnc;K}8ebqvQck zdy~U~c!y4D5|MV&@QCD$vfH3CxG|q0IM06CwW>cO&^peadDD9_KK^Z;2ww|)q*kYJ zPD|}efk(0E3X_M}Qfe|3;iTkNqMpql4j3Isrn{3f6NC%aEe!x9TdyF#>PRwVd%fS- zw=wFCtUg&i_Qy~WPT0bkB(N3x3Xx!Ykbm+awprzXCnF$L6lPDp>kr$^=e8 zM#ad{B4fz?eym-=FJ!U}ROHA+%>?!xG=SI>fJ%M^qUI^U&oMs!68cIB7z#4(js^B$ zuROrH11#pVQZ&Bah6iwkX(g(lU$q^f%NE>^YB$2)CF~e>MWF%9X}9nkQk`nMTE-Zq z46jNe=aZC#+d4wY$)+GQJ^1HSh@X~z2Y6vra?tZ5EIaV~k?VF(BjS)kpGajzAz~Gc z^bW97OXVW)Dy|$oG@(^Mh(hHe9|CJTys_jY;#~$;$bx>?aA!Bxi++H^e;V&>8;bgH zJ{e=>r?KIZ@t_3zFGvv-f}c$j;=wk)uac4GZdnLrQ~!^fxBTgEm%!#gDmLtgoTAYM zDLJkOs>;bWT0cwi855b`L}pc)bEe00W51LjJ-)&4%O%u2pfsic+2kcQ~J$!H((p8-WK0}1O9IMRe; z=)C+IVvMbqD-=;{OvLGn>*{L%2w^8{!PB$I91y}{%CNvkD*QUMU>~RQ&&FW4kwB&f zoK2Mh_JSebHK~s=lVog1$v57_^d$*WeFEZSMFTPv8KP-CQ+p(QBaPWr2>+)HQ%&cd z`w4UtG@chGxp%;Pz7vAJ(b*Wm(r{o7^AB|yYK48gtB3RR{%-OzarR^(*zB%bZc4xb z3@i9q8P!yPlN|(@mq8zbW3(K<8_W#eNsFQ11rcKQ!TA(Y0;Z}U_sjfSWucG(%xV+{ z5={{zDAPED$LM(5#-c@NYaWK{A@f9VSrzx;aKuORgRN8`K4{8o!z96aur4*v*8)GO z;V&gVQi872c$8LJ8K`CD*LLu~W6!O;l%e^EBJTt}2SfF&s5UCVuIUS2pzz3J z7$ye1o(j#FjGLRx`Qc_d0!{qZ-9Bs8t!zlQFn!kRBoM@Ir{rmn^E_ zR3*YN__ZK1T9XrsjRchnL?^@}Md&O{B9OOWZ_vu{i-JLENaiEhwF3Q8ghYr+j1Q-! z{Fwe>ene1qLg3GLtv^udXHZ}@CP&zJ;lQJiAn4KEZ#e7*3iEu*pLW2Bg90rl+>kJ_ zkibglU&&TPT1JV38Qcea@(?%@+6IoihMP%bkxJDd!!9dReS^3dz@sQm6_^7p8r55Z z|4vO$aVK#p2Dx_L#w*Q;CoIFshGvn!JPKMYl1k3K;R`kJ>RCS6)3G`&$}~!8ICLx< z!509Z?}gfj(`6l3ZX(?>pFbq6)F8>1guqX}Hl`dJM2vcMNzSInX@4lH11b)$7m_(? z{I$$~u-msHUhWbzz$fZ`nM(9HY+q2i1Nh-_c&dFNrBt@{vbRu6?l9#DYJyp&l90)O z58TO9-?l4COCPsR`YqPmGPRVH451X*+`}MV`5HewQILIsz#M-7_PJIouhrqeNnSzm zWBH(mb@!9qH-f4l4WsL1AkT)v5gDl*0yht&k5VhNRPS@35J)*09}h&XZZMuzrd zIR-(4o&nxlR+UAP1ODxde@%dFhw4d%_?bx@Gm6#B)S4g2(qv%2nC8TI;~hkf(-KT_ zSdjaL@YCgD?AMLA!tXJH5!>g%5xj{2?Rt25hMN2Ojo}i?FFIAK353ihDtCZu&;gle zQ&C)L#HWxcP}Hg*oNr>TX}P79)tZFTnHw zs+tY_g?!=%G7X_ZGC_aLWuK1pgIWZNC_984%-h)ihC2mezJ`w$v>g11;jfu3uRayh_hI}oKOk1& z@v5o`0u(J&UcBsu?^qgeOQj~MY!@7yB$ksmO$8qG)K7-B+mjZz_JE&W*~4h zz_l{qS0EQFsL$>JG;!3JbbQ`o&$&YmiDbEn{A=KkbUp?%VH6A_#eBf`DCBE>&Umk9 z4`G^tuinqgs-oe5fNs|#e)L;Ab}Ye^1+;{KV$xzFZUdgv;Z#BCCd?msgzF&wOmI!~ zmr`YJqF$xsR26hRk-%}U`C%!6htR!2zloW3!4Qlp$U8vu6V*{Xhk^izc_X}@I7=76 zcKZiqWzhG4-pppO6y(GO2t(%QWz4BqKXX)t@dYnk{wrsVOnTnsn;%d0J>ZPsm{OS^ zR^Vn*?rl z$(joYbbD=D7UVZZW>oSMGg7q1#l9GanSAy}U(gZqu8-4b6s9Z-csBRea9ItO5cR}x z6!D3*Sz3#Gq(+mg-YA0q6Z>+=2biXQ<@IzqVBhjD;lSo-4@8lGm5frJk5%cn2Ldk+ zVJ>{dlKo{MG$4OFpyl(zT z1E0xW64(ulq4Jvw`uS$JAF`(En#oc(9QtedEjTQixagn@S~Kbo7VK{XKwiAM6ia64 zYcTv^3IFs4PXQ^QpUN*7hXE-rUcG%sSsdgE!p!PJWZdBVc#fIu6OsMYc|WA@;b1E? zo(BgpSOdxy(Kx!VnaJ18zTgp#sqP7d61+Yd4@BVppjf(?QjmZ6nG&GDD2@7{8f1>! zf`|%=rAl22kFrYu1jY#t7K%+|ku$hHh6a4s)eYv=7#}(RbcQ*pDCM`lATz850{y80aAXx$&4c>maW6!Vr-N{@e}&SE&^`_$ zzg0FALfQAqVDJXig*}LTOV?9&4~7ydFpC(YPWT1jUblh6B}c*dp^pd!xMZ0tvyYSI z^#y=`0TItJEYE1w*qV#QUW~s)&XK8nCcm3u1^YR8?l7C_9*OPuEas;zpm@Sy39?(z zFD#E!bL?biD0BfVOp7^5rK*JK9c6GD*z0AP#lw=!oomiFIXe2lj~m~IqGgfC+Yyok zL$Dv9;v?nfz;lz$2L48qswf&Ve=7t2rh5g%^0n!mQE&Cwy?qcNp;e;%7n2aP(GU57 zsG@+S&=4?NIn2ia=4*ssU*nR=<|o1c1S&x&FbbjY`^5!lC273kV^s%efOW{91@Qk2 z#Y{ED{O_T-aX|hF`hwW0_ZKNmrfDXN9JNR|NX``AWTPMc%i<`W(ng~DWf(6oTC#!8 z&(RRPp`W?qXmmV(U!a?x93t3nI0cv1{29pN?q4gk{h&N<5ndNWX4*9D6&@7k96629*~1PUbXGNxuV-_4x|pc}9-)Zz)(i5^6!pA|($(A`NUCM8!{4 z0Qdw5lOoD6sfdz-^|wYl7n)C1AmynL_KyQ9D5GE%7}|J?k*Xli$Tl!C6-Fl>Sc~z2 zZ!0?fT$rDjAqMljn%8W-3bj$tmrDNlapTFRCi{l_6JZ@%>i=M52DI}f5R#vA-O8d4hOTW&bU zTNrMtU$dsZzU4jPrzWc}=1fbJU(u{Yd9M$m z>aSo(+F(>`pfAD+$8M%uts>pb%3j!WASrYv)7!(P2!#%LU%)eb2m?j!2WS#j^#yc& zO36JKTpo`V9(1zczY{1eAYnll;Gq8vzktuA)YnH*e=D%4^m@bKE5kK*zJx^MrD((a z`(-KKhsG+v3?u*Wc#<7(=5W<#2n93XKgIU@$h&2bYa#ELX^Sb!?SKM+?jJ`IK*8}N zXiEa!ZY~2D9$?OlfC}NVD!{8ZK;y!7Gb4ybwRMzA4eAWA^OZ)lSGUIH1EjIvFU+rG z3DoLzsqb%vRyY>=BjD#Cs`WuPw+?3?9A*k9tc2D2BA#^-} z3k;Sdv<%>H2wHi0Bxy7{=%m1JIKRMk2=vp?((D=Az!-yKp`M1m!Mhbx zzrD%fv9FKgu-^py1m6<21kiEFd$ntRiXSFr|MOsl$LAkEw2lxl%%VJI;%& ze-P8Ykgn;HBJ@*GAYizlFSO02j8;fq?@-bg@`b>Y3W30w*dcF0?~{n8Q0Z8E3;NNG zv|dyRMI@0W;kiFh6b}ITnRF%i#}wLGy5_giQLth?Kv}_=M34|Rn{b}#F8hZdCd_BF z{ZvhiMDfQAUty6{L-C99Eu=KMa1r1uEOiCF0`{kciD!DjB&cbz5XF9^z>P~sQ83eZ zKPc}Vii=@3IF$T=UC&SpCD!3kh+w{h=1UqnjmeHC@V9O?7$Ox2@qe+fzekHgbTpb! zA8kvcaDfldBUEMm2`z-})kpC}++e1I6 zSclT`0(55Pw}Gu={-vX8Mli5&!ufZ&WfYnseEeiTw3@=V{~3Z`0UkKtE~I`42<;0a zt3Xv^0&NG9@D`Ae&Q13wDp}IhKVA`d^q9D8 z^{2bz4KSRjg1@cE)CCJQI>zvy5>62S(fwznaHsM4b9lf%z0eZ~%Sdg&rvX$JUK}Vt z93HMfh|h%{RYa-{KOY}~Mwniojev-LiODe9tVoK;tC7~wjowK4w0pwceteCf35Bl? z9U5pq#HhQ2hZX`qHbhW~e!)N5z3E>15W;zZ-}EPS1ioQfHAiko@FV;XIRtpt3ZM`^ zD8BTUK)~5m`;%=HfFkS)@f*wl2~!OwKd4G8AwM&npr3GB zLKO;heH<=WKsd~z|7rh#c>P?K))$tf!~F(+Ro@+Ln{9xev=CR2eqLTbKM@|{#Rxw| z%jq^L#ZL+w2Z)t=aHIAf!tH@d``?db$NR!_p*Z>x#^(VDQTai8;T{r2`|nQIfR7Nb zZ{TgR74ZV^Nz^`~pBC{AQ1!!$LXGmcyZrVGK`l=gGI8Vi~)L%ObY}3jbw?GKhQ?n zp4x~2OG|`5REoC)js7p;7wP7I3coOG63Q5;U$_$duMN~qL3|gVkO6!<(*6M!`1ASo zC-N9@@1Fh&?+m~@0;MoL!UH%o#`L2fzv*wJd+C+>3BUUs`c0Z5AU22511|4a-F#QKp}GFTbTU z1NsS1*7w4TPMG1OyE{G@EEF>U@5l&++e2F(PGY*Be^2_);c+*^z4Stt#RKI>vJAX0 zT&uMLK17~Tlpk1#*YPwguOu+R^off0CxfrXoZx3C%)AcQG$pucpoM-upPnEL5E z_rN|GU>eg~z@07Y5@0uKe)({iAyi zenxJE@ih!9eBMhBLIpp2pZ$4#_l_rC7#Poff#m1;|8HOatUv!jexUssKn5V?|Lykv zPyJy$@BjS&8Xi~-{3J~j*9WLA5;p0fZ7zzV_9jSSZi^o#0(JXq*L*N7-Vm+O%JCeF zccd^6#(93>N_dGr(4cfEN}*d@Om%*F8rZQ7T;M03N1o98Nd2?~p;h7M;h(}i zA%Fj4yh5*ON&Q9Q`ktq>{q!ll70yNT)8c4;`mGuG6)r)5C)P6I6o9?h`uN>9SH$nW zvLX5-e4EMQ;6BY_-#+=Q(7xSQ^UP$-wwoK`-oDkqn5A z6uOO8EVPe)@1n2od;2QvHv;hOOaH7%S})GKBgP7_2e>1AVtgPLK7BF#zR+XgLlf@( zzzx(t@SFa+r#zurDz8BFUCw*b}s+~~{i}r1rX(qqFweOw+F#fam&0eu{ zb=;B$S|I3QQU5h93HUba=r*B(eY1tt@6C_$%V?z(pM?4ruiuF~`(~du zlSLaoD2~S?+UR16f5^3ywvje(rN0w|=bexK6;WPZ_{Y&!f_?^v+ zthw=}NjjP+_Z&W)O+x<;YSYjo!@q@KS}vse79&Z@!-SdH2=3AEDV{V!-|qYeGzYE! zf)>L6Z0VBV#l2 z{OtIS3}$Y;7Jh{v$U?_^skDbKwe7_mSdj2fEefH!d2+4&sy~UD#zn04wCiE*> zhPn1)sJhedpZ(O@=))p9Ie2R>eYWUaCcLNF&VEGwZ&GLPupq`C%AbNi4q7+ry489l z-%b64A`(Onl_B`HpilSG>Wi=6w&7mK16!|uWQ1O>ed%P5WMPyxM92so{_Tm4w=Mb| zUH?IBfuI|IJwnGp-+)>p_aIqV@6X;}59)Sh4qxm*E6`F5$*8V1Y z_pA!XtnMcJe%fAI7nXlCEVqB!xB;|#?Zv3z1nnQ_{>WYfnciEUqp#Q*Vf&D41=>5| zdc3El)0WcxSZ)UbqKrq47kp21ZGeBavl|TjV5J6qhGJrFI4~ZHM)mjU@43GW<&vCF zDGJgTr2e1tqr@aF7x{C)=L*T$2b_EPFZGQ)#QXCV(ho1F|6F+rJ@@8{N7j+@&whia zBkeo?8cYKio3?`Ga`xx{Ee-vv+f3_`$>P}`$j2$$<9q>BUXJuEJjgw#X<9UWKz~Q% z>E|C{cSE(Vp?A7z6V-~Q@(|ozO}9{Vnp1w**}IWn$k)!l!;>xGzb=GCwJfq_Y^aUwpMBcWZiq=M?fbhSZlXpBYl6(>I_~Z>B4mY`b`%mq0jm+$+PK^Z)rif_g^J* z>2qb#FX{UWUeol2uW9Av^B0W0S$PxL;dHRvlhX4kjy}riR|0#a{PxF68oJZzGr9Da2`Mj|Bfxc&5KI~zx z)z*AVm}$P;(*8%TgYv)X>&Kpzeo_8u3@+zj&uPrkH`^uH7d-KOY6}68GzOfW|InhG z%Zl8dL`eVl<-(sIRn&Jl1W9a!2J$vE_ zm`Ook`&gJ9jjuT^UaM)%mg>>cC~x@+1)iW*z(Lm$6ZS7MH9i*CQ=LyHhW?Q{_+Mfu z8wDD{bLoD)Dz_De{y$VtVUN`p*S7HdAyK@xPivv{9QfM}eO!3=dE!ObzyDchz_V|J z{ryQ}6}69*yg3zoQDQ4jh!_1{B#|;hOKVsISE|t#acnSPfAFp!+bTs6UEX|>w zWM--@#PY1nhnDGq5YpyiX|wD|z|1Fjx(@LJ3K9knGrvPB+fCq)v8eH7tF+NJ@6k!G z%1byA+4+TyhHXQDl&CWAB3lv#u3BIIXX_V#`Oey=FyR8H`zBA@6#mwYj=N_RxQNX-BrKX-cp$Vd}VnRF>mDi-`jh5@a}IuQ1-JK zsDEciw%6Ickm&c^7~bz9<2iPi?BUlwEvacTu8Q1tsHDulG5UvV-k6%Lx)S!xnGbJG z{q#Gprib_HPOXY$<{ms;G-vnNj?$Qa7VJqfnKK5leLkLX=JR!FnJ*Ia3iK zlZ#?D#~&J>8UFa#&ej>#MvwT`S(|S zgIJfg^<4G&m>w4~RRZ(b&9JIav0>nPnF`y0liEw0(a6&rs@5> z*Vw4fLjMh@X?yiD$;^fYGlT{?@1^{3-;YTBk>O=s#1%dWOG_ zYy`oZiFAdt&xScqo*AWPjACNrlj8miEBU-J_o|eeJj)29;8_%`NZ6;UuwT-dfES-9 zr37eU5v)F-2Az?vVh%_;(^yST2nYCCMn8NM&Yd8X!{5qbv0Qj%T+qX8hldQ*XCz=d z47bE^T3)ty7Mfrgdei*FrRyu3|C{9?uW!U4u~j z2JwKs1@zoqpGEFuD*d92bV4wt=AS)Dx(qb~`!N4$n7x`Q z>VjpzunzPX*81jeTpK=B>!$c%7Fl!RdlC24@FJHoDsuB&>}6pOLm+@LHV-i+^mN>9 zfDzqfqaGt>V*HsugM}^awUehuffgYRZwd#<`U04l>p@N-Lbo8@DoER?4!#oVLPn%(Cv2=Q$WWg!HmAyqIu>|x3-k?qx5CX-p ze^1RiL(+^DED_j+w7wF!=)l4U=jkG;pz9OVP_d*aSl3{7NL;Ues;`~k6K~aeUsb_B z>{GQlV{~|RG##lF>;WxnCG2Togr@TRI?xkb75~;1*tg_6iG){C2W0nOu^; z@W<|`_qrtbo7a87AME^gPm2~Rzx~u*Zt~EEXyfSNGwM*^%lrvPHQ7Jw;o-Y?7MGNj zFu!pO6_F*UGfiSQbKAysS36~?h`hWyd)xl9ZN~Rqe89r|?WkRMg2Q6(6>})g@QCrn!rpd_(CCvXA&b>lFWF!XBmc6O|IYS-_MjRl)AUc zdk@=+$g1Pdp16C{(NHRB^e0i@ftmeM*`Ham{=QF&+U@YSAx&d8-k(6wr@MAchy2z# zVff$WU{ig3YM}N+9p)#gSeMpEe{q6zSm{6!F*6c5u=!BfyT2^ijPb7sSIiM^D$@pa zwR>GDwYNV*D%4KFJD%CVj4vYBXI5UO-_{NE-Va;IB3S-S^TLC&l$1Mc+x+lHBaC(W z62d6HlfZhSfb>~^*H@Zf9BDL<4fSDu|M4o0 z6-##!>0}S{Mj6=_vn3JbyqNUa77df;!DM1v(;$-uy$v~}#uK`+U$mYb_2&ZO`$85c zvg2M6ePV&=jF5T3u2F-`V3Lg)eh2K6I-ZL)J=6TFy*poD*YmhqOa|LeBr-6*AMS+T z>XI5lKJq*$`N`G{{GjUn@YkB*VK!}DgkZLO2VX#Lf+13ZJOeYqP9`K4lScR)(Ahql z%&&5+I|X~9aW&zI;UZVjXVrHKhhZPv6?Y&2)W-N93*Y`s+z zq=oQDImRc;OMQ2X`-IR~*s~c-KeKn-%3;Lq1Lh%JIU(kfN~993M=DcH)}J8T^o4MO z;xidWsv-9i*`p^jv-$m*gex$eaD(MCBdi~<`h;Xau#@SsmY+{vKg< zUBBWtukC=!YfP zlfoV6H(+ut@TbwsVq#Thv89)u9>)hgd>#0)=G&WgUKXi_pS3S(yz+{g`iV*y8~Bwg zR^I~uUxL1!T6;2OP=wK$8dAdO@b@uyvJ~Z*^`o#;kHL26^-V9~cmY~oL~Ev+H2V#s z1lugwL#5XmmT)FuWiwV7{&A@zU-()OKMhB^#<8&fRzN~zeKGs=s40ym{C((;i%zcg z%I=JKIQ%f?zY$PLU_IGiizzJ@5H>TuCzzMt!F0W4DNUe1Vsk-)2})K4nboAj|KxM6 zum9&Sc7F2aYw+U&`|yW5hx0DmJF3-^^|u8Z{8evmKI-3C6RMcA>Ic(mi^=i+^4Z+N zjn7;?eB{~Hmllu-{fo$#h!J8M*;f~#ydF3!SimosUK5#KSK0;toV(hNwGXuJa2kYP zz5w~_LKlr5Bf(`#Ci|Q`mIMngo_LS1|8Zv2_!nYc#7CMQ-=#nKm*4(;)4?7A_6tOu zySgFLu;RN#Y*U+y@+Wq^cDloXnC{ipE1+LFv8s69M8#Fwwu^bG7yN*ouYZt24EthN zbwm9M@F!}nF4E#@DCB2f{?h6LKf&)Z^tu(}ms}EFb_+xOhiX<_YmIMuv9*W#Hx{&r z;;t+XyXQ`|5n4ZH(!n=A*!B7*>YWGte4kHHR(!WusVr6);Fo1-yR`Tw>Mo+ggZ3yw zI9$IR%PR<)3*}o30!l!0UyEyIN0yey`+Sk2dG!-rH7?{wlt5kA+ImvZk8MT$RxG6< zL_`IUH8Ou9;yEcrqMYottlr)54@B!rqrGt59D$t_zOSz-c8R5sUJ(nVn92*_nU$Kq zSuiI>`y?1u6@k~{#%8Gq@DpKop+lU3!&tm^0QpfMbuEe$2#(>D|59C}h@m1PQ@?4b zFAc{|^g=d`_7^7u42lSM8_-CFMiu3~AS)_^PY_I_vsruNS!_9=zmCAtFisa0mTJHP z-t-3tdRmGOdpmlT9kC5*#cv*Ihng>5AvU;_p}bnj>0{=k#w!SXLM4 zKjSNUo#!v(o<*_ezdx%a`PKJ$i3{x+xpH4$@ICz)*Z-7X^8dH||2KcfZNK@~o4))0 zODm@~-g@7zFII#}qd1u4!o1_AyS}kVPPxcHY0Ku)BfG*!c3nQSD-Jbu7N?tXgq{(GKT^wf`9YS%w>0`V!j9dhjv zPfNw9!Xw8JpTLuMro}(b(f@;So+GEnL*6*#Xs#IYb}bx=Hv9W+WNA~Bn;%o!zRMR3 zSNUxu&(kumzjpm2-IaYroMY6F9G6XRKyhPkdEP4ate@j;u>#$o7+0ML#>ZD`Epx5zhm9i)@vuXFPhNTZxY|Bn9k!`m zOZEOW%n`5aL}1zpnIQwCd|484t}pdAMWZpqzo|6(?_l~lxu3tP8|TT-kLFFDd}_{h zp9TN%Q33Q_xdMN0o}98=PH#Z~^87qs)^fq;Q9!d}-DJtQ@?EFoV@K^%rhF<~cZ4Oj zLgg_3{jR}>#eG_Bf+bzc_zlroRg4tw9T!ajUszAc5wbATZ^?Gx;Z_>$(RY7B)96!+1VDskY~&-GAl2{)hMtHd0aL z^p2MQF?dv~RbQQMBhxxYyCz(9AaOtQ+)ay3EJ2sd`Rn1KY;ElM`k8N?f-$^D=}O#y zPz*jt$q|Dkx0B6s$muyjZWrIFtz-%3A0?c2@9S8?y*y4njsEQKxQBaryl+hL{-%52 zcB?N>bh^3{RT6HzKEwxWxo_GY%Rc4Wt``>G)e3kGS~JMziL%4pt8Esgn)Y99~iIrgJS?`HKvyNXs3jE6JnZ`ejUO48x z7v{~rbk6f|6>>))hU_yk?*u}Q62w#PoK2g8?Mvf0(E)kvmdC$)!;X{N_uTFH)n^%o z+$pQ@Ni~b@3izDA@W0}Sr9!}A>CCL)$B*qxl#xGkH|KXc0YUf7B7k z2NSOp*AB2`=%}`Ye+J|0-q*Bj3gQEwVhTf@uhz_N`}9b5CC)mJUQzq;i4V0^{G(S? z{1YAq`qfIlX)Ma>SLeZXOAe8`6E{e?8-^S)ZWo$?QGSY1EexrxbiM_Qa_#F|!mS@C zlECD{e`G3C&n=ymFB~dIyzuVi9f9Uhm1hrff!}*5U8lc;7%3K8a@C;(Lmm>|309gf zkBxpZWb=eDpSy178>5w3t{~8QBqNg9;)ed}{9iAfnQ}8TXES7!e0<2}Rk{-knXWle zXhSrIJ$okxQ15Y`;lo9%(5LsLd={=6L#wo7(uIF@@vj?EDPz?7qx9>Hon2 zu%<@fdA^~3M9MBx`Jm8AH*%h&Ro+2bwFB!M7GzgU165spt%NYOKu1 zmjqfoLsf_eZ~WUOo~|=Pm5sc+n8tJaL0+KQIat}mPbf7p|2~-H96BBdCcSSh>kPhy z`DldNEwMP8{I_w=!H*eoh1WjNDd#!6W{mYF2N-fmz?SU{7C4iiT;d%>Iux*H6O$*J zY|nI}^nzuWaiv`GD|(b(`LKlXI`&R*Qi^?Pb=TaN9pfrlGC15)F)8c%-NW6L&BIZa z1jeP9!XI@7IzPUGD{6NUWt!6G-?C;f9_f+aFAI|!MRTqI<9XUQVAK~GD zZaDPT_1}wC4t{LeedF4}A9M~fURkv=FNmx)2kU`XC{p>%?npseHhNfcms<*}S;Ofo zj`8f9Rpj0e^t=tm2{GT^$k;pJ4~y$coOq@4=GM%K7IhH#TOcEraIoXs_g>N~+gb9< z4;qHN^AjI<_cHUZ*c&3OsxF>V5)jgQ(&umiUYggZ4Y?AU$fmrLTqJ%bvgHkB@S|zf zZO4mWtt3mP@Xkdf8@T5KqDT&rczla`l}yU-)9zFfy`cZ$*pBSw2_d3>B6TqENH#T= z4aPRjzaqhf6FY%V*JOlhqlZi6hZbZ~AtLmtOD9NCA*M6dyFuhH=*tRy7gt}N;6myf zA(H$J{3=>%e`3uKgam>7O>%PElZ;GaiDc!)h+kQ}Z+9Tv_EC}hlft4ch_|`8P-jH= zv*BeYgmk3ftpRzDIRBc&ae46fe+`rOx#UsS$#*qxToh2T{-xyDli}s=Ua6>WN9r2v zpB(cpdHEmSw}vWL7wk5%AMD#1p3Z85JKbYWW!=Od>TD0V`rcPU@stAn3#Yln5s>=( zy(QM-??fYnNrH{g$sJ(3nJA4H-~QJ-uCZl(6JrO>L@{q8Ra4agp56Ve1>a@kLXnO1 zZR%;5m=>zp-6^>}VA?a!LT@yF{{XaRw&em;4aIz zHO8Pmqf2sjc5V#Vl)}zI@XOQc(ExnG83xbk?|~oD<_kBsO;P$!4HQLM+YoP2Fm_*B zeYLBXuQGNYMEV-HiMW5eQ&P+=RD~g21@pm~UNRvA7#rX*Ail^?^&m-jy|w_>yF$5o zI@`{LjNLmHT;o;?=)Zd_1x5x1j}>Gbui7><$y@E(iQf1rW^5B6ki$ z-YFX<&UC8wh>J<5tMHX7@wSy0p$eUc{6zK{@sEmpT`8wa>FtdW?{C}zLlhEHUxr&W z!A(q;FP-K@pQeoyS+j`nJ_(L{T-!SJgk8dUvG3;}I9E-zd^Z=w4r)F-iT&=|HLEA8 z$A#*|?!V&v+c4GO#0b+dV3J-|!g;%fNoR>K-FBH$1DC^y->NVWvDOxWKjp*uTQ@C1 z^@7>O=q5pVU>rya!p^~=5J}_wiDRZpWXNG5q0u;;{}DSzr$OCddF^B9`INp@p+=5; zI3MQj3B6?e``@1LFv0%iV(nsMk1;$}iP>9y=_c$~9O2o;wTF7l;Dlgtn8*6nUNlX0 z_g9eO)^`rz{KDDtY>oxV^1Pe&Up3r|^bj-uP$|FnsD4vL*Ho4q9q@1mW>qs@9Wu8v z#5X;|&7Hk~6!cAxcb6ZAzT=o!=AF-D;=AK7J@$ReM*bRk^jniVKYzH&zT@_%e*pjO zyYnBt`UO%^|6s$MBMmmP{mIupNHx`ehpoJ?=H{Q`eAAgO+;sKx^^Vp{=5;&aKb545 zT1w`%9*T#wAfR=a*jkyI;$*7auFfg7k)MD6x1YCuOx9X#e09S^n4g1W?Ka8u%;z&~WXI8x@>?{PL5r(CzCsoc@lvLt$;?KQF#4 z`Onr#HMSjV*8d0m%|-gGS8ARo^?z(Qb!6?IZRE)v@oejl-J#`G|4eqwu#v6XR?h3H z>%08L`(B&y*;437&fSpt-Wzv(^zfgq&2K~dYvB~RETi`f=~|u}^ZDWX-hXAc;=Fs) zZ8OKy{UUfA&WOM%O%9iLZW;XB36r`W*nZ8i)ti)(J_Y-OUH{ZQkvmR)Yx5tjeES2m z$Jli7ro`|2?yM+Xzhk+Ltlwnw?IzD$|Hm(b)oXBGtXaA3KYO&jk585tMCKo}kq*Mm zcz1z!*0M{uQTkdN`SqnMZr!n@w{GQxibnS>HuAmp)@8A_Yj)1~EqQYa_LnCZb;XBw zR$0tn8=Q%D8~LSvEMq zxy43;WBR+-95joUM7;d|BQ{bnk&`P9m2HgAop#AYwC}3B$EK=dYYy!EN1Jmo;8FYC z8!LKuyvJ+7<~x>oY-CsG($;B%y!PReW;?ccWbs2HxpCHAr}jEKj$88)AH$hHK=K`@ zrxc{N%)oehXE^1D#@_kim#JApp<)~PFd@!}1oGIG$%5;D^QxOnT6n`%-nB1#PjFk- zUg||W%<6Q_wCa;W&!McbDvbTrrs5;-v?;#wW7}&ki9sIBzt>kSUD~gf?GOHV70UA` zX4lN}h=b8?5(R#AeNqy#-JThfpGb6+Kz`gv4jhRYk4PlWygz0c;zjm`xQG8x9q>NL zPAh@D2!xDjdN;2&LjunL0io0}W_^vf*3DWK8k>iBol}Ce1ZfG*r#uA>TZW7lI z_X%U4d7fO-O!^Dp@BFGQC!?~|S-`Ey{>%=3-nu7j;$Otk@mQo`EA+u!z1f~JPKe#f z4i3*Dh$nY~6gDSqKUn$sM=g&Ww-MV~CiLcP1by?h_mi7#WP)Fj*yvq@t$jVrWs`AU zT~uluE+ppWq0D}p>?Rpat@6&CBTTscxw@VqH~G*!DRs(H+tNFRGgIcd$z@Ic(x>n0 zAE-KgG|=gDlXs5hIc%Eeg}KBSxcXWf(PPU<=17MtTK?%(Y5@Fd-yJdK;Ngv9zmHIx z2K-@)6&Hp5V+zcSdPfrefHS|Xx$phI&0hBxeb6a>ZYOV!A1XWlpY0y5o|)fY zI9Q&j?Zf)UKi1(Lp73K2`_%EtcKE~Hw&>X1r{{EK*@UfW;Z#{BIH2T!!+ z%@V_=PW?g3XHu(XKiZyrS^Jeahd2pu6-g5EY)1TLz`A482AFluZ^zpy>M&)&lZmqPGUOV~8U0*oE3zhemb^oFz4u1Ye zH-1`NzU7a9>TfE2Ke8G4%#&oewps5yY_U4Z>tEB;=pKBtdT98HpZ^-`_wGIW3To!w zd(4~uU|{qX%!k&pyobN*-BtFzd*3VEUqIv==X`w0Kp8B8%GcK+X?qyvl_EojLbwH10Z`$X~c8zxur9UGvp z$vdrZQ)t<^w0*~-HamIa7^yI{^3v0#hqFB^F@C%6IaL*U@4w9MqjuR%xIGbR^!*Y|y=I)p2RR0C->5_S~@GQ0yhqu4dpjV|-NiYN_{hN$p$F+tD8_ zTM@gr+T7i9A~F7OiJgQVJUn=hQ`I|%m6UTI;KNsBoL9W6Hbq+txhCWf^pVDt7V|Xy zE`JZ;iNKPv_-@ynlxAo(9gycRLdRu_6>t(|h+JYP^OSk9e6cIF>19}2puI-L-a2+Bwif#Qke@91R5U!Lk%G7!iJ?D>8h&2?x;88ihGljo{G+Ln zqL?$jrmCRNRU!a>mxQyD?MUmCZTT@*0{G};f}^zx-Zp>gCW%G+f@QXNMa#7AE^Rp% z!Ti+9?SX#J9%WCW-9r*cJ5|=@)Du2d3wJ}_Be=MPoMnxCmoLx{{A`>O33wu6@+Q*b zOQAhA#Gxl$8k`TOKL}0%d=Y!x$#HuMIsP4(#h7X^IYyr`)JU721_djRu4VYxTKed63;jqm7yos@U_Q{iUC@@9l~e+KOj?h`!D zt_jVP4PQyxPKJ0z4Th{*K?_(VD!xy1;fz#O>5q7v(*TcfwqL1c+k~3Z{&E}QQ93#} z$cutUsV$PN67nq8>PtoVpc1uQ#elcscIs-ptj`nH!UXNH4HRA0y{L|1wM0@vd!SY} zMNg}HI-4D&*FneMjLkD$MwWLC_(eP7I}>GCPw5HKEG^=(Uw5@=!&yJhG9+L`f$ugo zAwE2m!#)(5d%%4eLfBj(k*J$5^GzoaVF$|FqqJr|%HL%lj20 zChE=z^N|v-ZISKgKt)Pl17k2VEfdPI+ z#HPZOD8Z3xk(Ljy@+qG;-peqSEQ9_9i8pEI3C2>^F`s;P%VbzfNpkIQ9SV4f0g=rn z8O`7jzXB0<^td16lhXZ)8TF!m%SW`JA>)`z+5taCOZwfS%ob+`rE1J4EoE7{$(vv% zWL)SEr>1qT%$HV~IAnhCM+NtK75(W*bD@S1y`X2VeVmXkOqBcBjHTI$w}=fe@u1z& zBP3G5Cn?LTT$N&ITtpOsPfh`Di7l1O_`J4EC)y_)4lpg+NtJG~=pXzQ>3WhA6gV`N z(H?)s$YxBP<4vF;^4rXIgXv=#gsAc8fS+RelN}jJ4|oT?kv?enApA`l5uG?$6D`0S z7K}DvpNP27c!2o|4213z2{1-LPXpfm@IHrLb;O%6l>o+9b};^`aEIaUl#<>Q@WIP- z35!W4!O|hXKRp14@dfU#xDJg`v@g=?WAqq&uk%sX0K95YvZf@PdgQbuv*?c>a$8ZC zRI##2R{=k8eOOEKWf+}PF&-S_wP&)KOv*}U14;Ck!V6HZwKF~y;|uk@rXbGNamDMD zF<&_7m(q}LKBHLgXh-|{IMOG@31<}{bS2`2qPWAq2jxXI{Yo%(mxZJhy< zhA4>RYD#)N#hzaX08DbN5vfoltyrLwsX|IdLc=oY@c8}5FNLyKp1%1$=FCA6V zf~si5h=YDZMAw@VWgm?3A~xWcir`eL7|5DQiP8^OTuded8OwJBL0Uo>kGMxk2N+%E z*_a*lAHjM3u0W^BcMkE+akt^1m&nNkjep89y+VQ~Sa6h} zj{XQn-10CjUdtyZseVRUb$Kn(mG&sTLp|t!MK;Lyt6s(-mqK`h`4v&sETeKppAYdZ z0AHw8X+@VRzeA5nv)jtyH*uMU8`=k&53WZr9&uh~In9XR9Oa4+Q+(8{l*qBr z_R@U{@E-~kpqvR+tF;RlPd#n zIKac38B{cvUQ${H^*O*ZkzrJj&q03^v@$S10&0r|ey61Nw;DR=oroFE%3xdRs_6r~ z1Y%Bca2A_Li2ZcE3Sik1O;?8lQA4Wm-bxD*(Z(Pk9v|0eeX2{<7$20k5D<~Rub@0x z*_liPD%^k<8mr3fNg%J@l(rt3ZhS!2GM_B zR82%IvAGNq%>Z9z2@a$CEZIm}-W+^+yAz?*VNH&B0Hl+lAg zB~~>A7s^Z6pc}&m+=i~^^tBj+NftrFF_7jNw2#x%ehF-ybR?ncGW1UzoTjM`GN7>< zEuRCTz?;3r8Nfq~C!+lIitv_?MODmjs4wlz3efCE`~yY8dpHHB&h*7H1T72!UnL(e zTKoDfMhqGv`WI!fJ;)$Dv57qh;KjWTFn&>kaUs$-F%@1k&|Vp+nNR2q4At`UvG(MplKKs8Sv*N23_-k$e~2E-||^$g*xSxeif%EY`Rmrf}?u0FruaGn^2);m4QvGPZl=MM;E!t-U-Pp3A zKQWW&aRy3Bd{zZKG@|xuSdMjG4bl3*z%@kZJAot%opgVL@WcWSl^}%0BZ)kswquE7 zS$_)$)->>mNrLB%HCr*owCF?qj3y|8Zu4S)MBno+BW#&A-50isl4zgg6S#n)>*0hq zA;s{X1Qo(m+Eg(FT95Wv3A90Skp!pG`G$oD003DU)C3FgWUw0}M%1Q z`qMsL%#?$NjVX!!Pe$^=JVA(+vjKGw_484m%s^LHHf^FC<71HuJ_<4u!;}Di`>~nF z7b5Y37mxNDf{1{Cy)Ylr8O_WiO+@8X4L_}F6XkEi{jY|grd+!}IY4CK6FA{8ga=;$ zD&zw6sX&}yGuW=Eia&|>(3mHpOEXm+WDxM?F#(|XhS=XQwFi6|&PYoPc<~~O2ok7Y zf?_>Ma*P}h=zJDo4M21SG1bzDfdTkQVnDD|FQ9{f610Dum$29spkNAb6|+KM7#|-< z=suJ$2o}c^pbJ(BodbVfll@7GMF1z;1bFJ2XheL1UFslOMoCV&Ljt3~=ZG2r}@wN!41^DMWRs zU|k_U0wjtu>kF*M-GL;1IrO21dCWPO?sZyC5%2q zI|5KlOy?oAQ&RIc4RC6^gK)qP^p(m&1nl|HbhMwOIZSlG0ji?duTa#&Y$FCjDAWB6 zAMi3NCOmf3mWb~W<_Ui65w8WJP95-6WgjRDko7vw3N(lp>_ z)h3yh>G%jn!i%j9BraHv&|lyJL!;?HX;A0UUj$W!&LC(J;!4o{mqpUD4s~~q5@4Ff zfEZ}Gr6bTv8vTXw8$!20K~l8@8EIgg5Aic4#lS?O_^Fx(RbU31()C+)VLxjb@Y;Z_ zBH*c5h+}2>usZ=!2fko{g^cB8Q4Kcg6kj3o;tZLROnQC+Aw;{djA0EVkQZ^J9XQBx zFlir9+G(0_O%63DKcf1C1ML`C7_cft#vq_Q62fzVn4wNIjLr{>N%#_Y1VErYjEv~u zc&+FRs4Vb1$+4`eG_g&Xet<9QW6@#IU&aLNfKLbm!Xmn*!bz?)kNOoBvlq3g4p2(8 zUuP)5bQ#eHss5_Zpp;2BL`JeGJXsxT*#g5MJ~(DD;E{n60Eiq26}CBO4=6SI%OMUE zdQR~l8v#9yH6kHW`ilA$3!@F<=T~X{>Hv$mg+`#?w7;mI$PiM}5yp67n&m8ua^w)- zL)Si#z>yNiwhW!GF!vG}#LSa%F604U&?@YYK!7np{m2gni;sb)Q=J~OfUk6>0v*wI z7fz5Ef1Szry&|Xw)({TqG(hgfg71YPl>z^+M5feDO>!I>m`8xU948ZpD$^SBu(;qn zlWYh4{49(8n1qHxZH5q+pwh@(1Aa=)G(QvzSfvI1GtqT={t|E$!F)hNRfVOcI#>n$ z7a;Luh@8O=5pY9(eG6%G^)ts3C`NlvEaaSoZoe!N}9 z{H1BMl(0?6#M1p8jju+bj|r6<4^U!8z@{@{`G6xBKZEWI80=kvRKO?nwnxn5ZUQU5^`2C!K42w+C~>);CWI;8ck~&kR7dn z9!J}!(&ou5pa3{K&|V~!q%3QyAGYVfH%3=nh}8tbWl;K#0Y%VNtTjLn>OX}}R9y+s z*$uqI__S*r>tNe8fJ0F+ejsD`h=pa*0HON605^dpd#ge%Ye64r0Ao3k%FG+1pydjS~(n=yo*0@Ik-^Q)KRnO| z&{*W-G<=Vx4Vx$Q2tO>+v~vIutEvPP3hiOhb)XOW>!4XE2kfxvlt-xayO+tC7Os0D^tQ5KZCo!mp0-QInq|yBfMnvV{`jwSsqM^M2xQr(t zI0JNiG52WY1bxo+pOAK#o+%h*C8;D)zUZTS^0Qa|xtp)kIS70WkAFpoui^1EJT8R5 z*Z6QDH2(Mcc3;EuYj|D=fv@r5LTG$FzrMzYukqnR2z-qX7eeD}`tUVA{C^7{z%KZg zOR-=7RXPBsG5jaMp+QaL-iU9Q7 z!^Cy!Vf(e4>n2O$t-mRUEIthd*AnZ!&SIW{yo@EACKzx3;>DWk6(5$obKHd3-3@7P za5EW`mv0wT7g!Y@8_VmfHiLOPE+6(+?oiy)b{+(qOoQBsu=#aDj1gR3U9TGhe_h}C z*|pBuSFQVWKbazDLoBIQ7KnVsf}g&4F!iFCP)N&y;aT0&mVC0$%^wiFRb+GVA4Zq# zFDna}OU9IeH3I&sxoAa%CrehWKKNRg49B=VRcDy1_CZp=`9zo;c10`s`SRmM@eTy> zt^#MJWPjt$XP}Xhk%RzC#w!bUytZTKCvo^$Y$^dyOA`Q-`O`}y^$ja_z%ZwR@D5TT zZy=-ci7!l|X*+8Rp6LI{D2;P;Mo21JD3nce`sb8~ZVF<2m#(S-Ntle*0e%tAwnMmV{v>=Y8Dfq3sA~gZ9)neDuWU*4|btj!>rxDJUTcDh~A@ zf(sUgB?sds9CMIO@vLfEnv2+Ciy0w*TvJeI#&B$ysIi3FT>=ke{ehX*B9`3SU^a}F zm##Kn{J;^5hsoC4I-^;3O1D(!SAsW&h!luQwjXk4)G3vivJ3VSoSoBUnIU6onM4tX zcoKtF8!-zM_}{GXtBXj><4gty=?7rH`u-bPYq{~GoB!r`>vSf}kg7UJuG5jIP}5{( zF&?hL=<_{$GlkB?<&SC_LsB{;iAUjFgf*2SsNAI{w+NMa4aYjdZ3EsgOYZqRB^6a^ zRn;Gl5vO22)-CwxplX&U=Zw~0^TJ*yI5#@|$8nz;ubt{U^)~z!wDMNyNAJc96|JZ^ z;v!G2x=f$jIb{A!nC1z>Kbh-YwQ;Q1>mrAruJZUXJ_&#EerJA$6frCQeJ|{-pe?GD zF4&Yx2~nmR_O}1r-6OzIq4a}RNFZFW$MxoeAw7rj`yQQ*`Euw;+9y1HYRiX2^a`yk zX-k>jOfa-81vNz>$P)LwY<`@up?MPr+Dxemf>uaA_vis^E{p=U<9 zE@E^WskUFf`txCvbNlYOLj8h=c!3<}UBA zKjq|qll=P%*4xN8#Y0`r+WD)W{^mPj5&qPQ$gc-%@3q(m9b^Bd%ng$Vdu`oJ)i*z! zmYHeH!T23&Z!>v0$_78#sis5-lT10ZYoG)SUsBw72JLNiY+inH!ADQ|x(An^g1x5r zMRN50f#6{9v2Q~WfW-Vsn><(3%>W6O@-Fi3E8B#kDZx4U`}#srLxJYlzLw?%yX-Gi znld!uq2KF|3>Ga2zp|r?PuIHOPs9A7kl$@o_=P_{xE}s1AzLx(oR{twOKiQc*Yx@G zpC8{;$(f@Y8(H`>ok7Z^OxJNY@4B&bP9a#seY+h(%wxE$7Krg-uXTqnb<0OTk3U`W zuCjVGOGd?1e=-i`Bx6%r3c>#CX3v7^zx_(wbVyixUxSN$+r~+Q&WcyZ?JSBZI!pS+ zqF}ec#4XH~>0@2wI%fckc)&#RS19uQ!C~ms&aYp( zeB;3gX-__P#Hiv<-ddaAs_a}NDG;7U%8(IG2zqMu6a-$+t{Ko6BSAx=@uQD?4#Xi$YmHTXj)USp?;snLx zTX0Fkn`n=tvkeNB#SMRJ&HBU$VIPvp#cwM+tG;h;tHS(Xe(i?zC}=&rr?@>7fL?4f zoAI))XL%`@&@13iwmdlP0f*O;rx7xQ^1tX4O!Je~p_;0T@{Lv-Sy*$jCgA$nU#Gk} z<3RQ0C{N~Rgz^==tb{EodjR%aAM;!5$A@qEF3UXeO58@4#l!Z}dw2ZmiBu}o4}UkR zyNw%CL-5mQE9Wmo{|5s~OuKw&?fa?MP0Xj6hf1nf`^e_lP4&Bbu>SIT;>LYOMwj<~ zmSAqTkza}%s-4E=_b%Te#M_H(V-P-~BT89(S0n+A-i610;i z6eqv;;PS3iL&KAQX@z}V-e#L9-oSh#SwV&&zwD91cb|IygKSu+IZpLSl=+XPo+f8f z1yzyCdzahEOBY!$PTqQZs`kF>W9>dWITj#=zSXxxo;xkH^;FwQcW;eR5&p|39?ZTd z^v%aES-!nBzGE6hEW*@fcCvg^J`}{VmgtK#JoY8*M<5R81>e%kBf~Kh`kT;d_eMh1 zpPk_~;2`v)B4lD!9OlH3-%xog30K2cn3aNf3T{5_WMBMX7QQTbxUCBBgZ%@%Y!L`` zL>KJGO)3w@9t$eit{x3)MUWRlbmc=}Uz)-Nc|Vd!U~h@?k(R@tqYxrPu7y|7{~wVJ zL21F~G9d5ATAsTgKEf@PKS7KIDN{1{d?XE#*V$bE)APvxy1D%KY^wrA6=P@MqA}y_LT#75|w|`Z}X5d8DCXRBfaxQpD7k+AQpkyV~H- z?${$M>eIch*Wn*Il`6SCpEclXLu~5D`qkUkYH49b{P`32uc*iVH}Y63wti{9s|GH% zS#nG3&a0=FHrxV@m?fjIzVO>!OUoZS)!z3?%~efZ-O!Xzkd9`rl7#G)%9fyI8{^@#HNriikl{`E~)Dxa~W*J*X^e6ZmXrK;36 z{fGpF{1P5>{uHLmXqFi0CSAqy|4@6B7 zT0xzzTC9(%eKGK)kAFdr@d51TFKbxcFk0XHdb)Q_LwyZPejeZSQccb4FSYJGV|DBb zL4M3ZWr`;&3%hL4!GeFZ!Sr2lW?p}9L%g*f@Sa9ac;G09*B9r{#Cm{mIQ1tiFQcb> zUq21_rXOkeOp7-()I_@5e)l5w^P_-b5P)8WwWkUBU)*`RqjjgEG`D^NH7&q<)h#Q2 z^0(HVn;yJn#fwjbN!7(bW8ZAJ9~7E0=`h)+>x$QKNvff)dN2?s?~Wr)$#nU`!n;d~ zw!ohVFm-wjqJ0C={r9Y>B2C!R-X`(-R-zw}c(nJBv~uSOLV}nE$;H?YDm3l@jSr5W z2=>Pabp1nXzW}up@}UbK1GGf{0_u5Bn`?1?c$oTczq%grf`B%_hx+=O=U?oNw`uT4 zS5xXELevXY9fLY-mJ{%Y09_uGw7$RWjMYtGziKoldgE(4LM|nCL^si%+7|3ZZ-c~N zWyn^X4<7nEFs3tYJ~C#Sh4(sx&!kmj(2!?Fp}eg2$o1uldmm;Jziz)mDjoXW^yRuy zaD@u`@4zwP?Jk6>ekkAN65ZrcONWY=3JaAq;`YEl^6sB)FABTdK2~Hl_gBF;B>(6s zkQg+8--`LNO@Q9wRhrgY{=*3(?1{-U_|$rOv<0QMNf>`P=fk*a+8Ti2mQkS3#sODF z0LmpYdP=Iufmsr+h0uT0jje!PJiY?H@23Miin#gpDxCLF7yg_61r!+qRZzM!jHI!R z7_-m`>`$R~Yr#wzc#b3qlXd+irxDu~PMe_wiuF*uwxhRa%@=~iTWWf3n9TO$MF)60(xBt(zTeceJ%M9=>ApU!@2zjO zweEU!&F2LASNNU-xb~ojX5u=pEjEWKdhmb;)e4<(W+ z*lRJQ?~NW)sJd^uOW*yAsYK0ss+TPa4Ys|xxzAHQ7V)4CYH#d}OjmBEcVo#{_w;Tm zxiI&#MN`SSFLP!5YAl(GFYo&VW&D=jjU`{*)4Tt}-n)l4ah>bpYa{^;V9jV`CvCu% zG?Gc1fG@h3HeegulAXrpYRhi0384WyU`R+4Fqb5xJ$pAKX}Z(B+otBykfaHXFJO`o zS|r)#lB5SqvTTyJJ6KmkdUmizGcqJiz?$=pxtZ*Sz+E?(2tL=rwCCpSpvb|QmjS9HOnAJ9_`*&$2HexJH_9c62er)>rm^c&d$y(~( z=6{l-FMb^Bdhi!EV)+-Ino*#8z}*m9k&$I1s*lBej(5y&PfgA!pjX+5Ya!n-IqTxb z)IsNolH)cq6{T|?=&K5&wCZCULFtqEC%z$0zeBgw-C!f~Q^ucxt6vV=$jn&;*RNf* z5l3?I@yoGHQ|^LbPM{d`Yr5bbqDyU5#D|uvpzkN*G$S_}BNmXwmHxbTyw<<4=}4ha z82)yQo$`<9or(jW>Km0d(qqZ|pz7(G8CMQAa^t)F%|<5N#049Ycg6)~tZ%iL z!t+i!F9^aJ$D|a$w-)Qy;h<-eDH?M(S6>MEix9t0D~)Y4X;b}AG?_)rFS}|Cgzqfl zaucqKeBdK-&pibSb!USqCQ94UK3Gq+I~g{#yRpQa0NzlLtq*o?PalnRF1H}+Gzo66 zNb1M7nM;#Bp;;&&8S+*rvwVB4syx9n5AjXJ-h<7{G;Di)nT|;)$XXMp@wsc*fu3Cz zc3EEcug)-fJJx7NBOMOtH?e{CsGM)L#v=H)mKu_tDyueeKtU?YxKzh| z+=W#ClScV@{o9x6nS%>0XZiT{QU%V>moGE;d6Rv z_4vp(GsSm0xIj<6Hi`GU3dnI!b9yA$;f!P*RFLv?y?31{z1K50JC)k1APzBd(4h$g zw=XmFzq}g!6Y&=sN&M4U7b1Lf0pfA_+_zd3z4hsAC?wZMG5x!%wS!&X;tZ)ayBYf7 z@dv+?bL{OOe5s)`p8>t1;q|v_2SZPk8~m=yeDJ5(UCyz+XVBNFU5$vhm>UX~CH2wv zMk?k#3Vy*9exQ!!2RbU!*(mzEpe$#s!%;>j`m$O4K5b;yBE&{xHrVqxh4%Y>%k9QQ@3v||ayMAf zzJbuXJ5;$rw_TCqI~625J*(emfrpr9UOH@Z$;cBdB?juXO1i)Mwgqy3^A5vks`Cz& zkce+U`C_p7tt1esvay0+LAKAeq=sB&7Lj+`vguM%HD`ZV^VY>T$cou%v$_>n?~QDu zNV2DXDHZEli}+V2A^31QT)MW^GSZNORI!2>YR-oK`eY3Oe<$8_D3hs=Z_C%Eo^Sv! zi;g_};Mr*Ra;t(JS|N|;MEvfIWGMc42}QfI5ic;(^YGjYw6{K8BtGTFOF<# zY;XA_W$(eYW+4o`wlX~u2z?j8M31Ws{X<_qIx9nncQ6Btb zK^kmAGJM7feY(4%GzR%{pj@l*dkV5+QfEEzRxuoJHYrn~<@QnHg}gV4On-AN{Et7b z;Ig2P9j#*pVaQ&R65W_xfS+}*&d2z$857}-3MCB9`}@i`Il#I!|0&>qBI{isl~yanx0z}F4BDsC1)DS2W9_w?-2S7`e{-Vr zBbOJ_v-fBH=EskL9?IU}Tth6rmJ{gCCdkh~@3w5czdhR|_!mN6#1h>#CZW6DnCRbK z0se_%6`<(7*Qymlb{69++-x%XJr%~Nd%0a1R}k-9jXJd5sO9&Rv4g#ce_A#iI^f_U zKG3z-gZ&I86W+!qC4Zwe)r0w@Aeji)r%R%%Q2~J>^BeI$X`$5KO&Ph-cHFll;%++| z39W~26v6nc7Lx8tBNyqeXQ?FOq0O_Bly|<(5OUvbMSn$o>&n=0&w8^)Iw<$IG2pAW zv2?IL-OR=}V1Ffd;O!rLn+%*5_QMAZ{Ngl4z<}n&vGxiR&cngVGLbrSpV1)17lOa3 ztYCe)l8x_nC=wAjXU_KrQZYx7b z^bJ;}7qGz9 zS@gDv3U$^R1kAr)+oet46i6t2)^YU8G}A!GDx9HqZnu}Fw&^+b_W&kFE`Iz9>TeQ(&l})LO zKUs$UgXl7uz%O=|2_qlakwrPZr$5NiMY5hzP&Dwz{Uk|ey{Ai(InU(h$m_+sEt-gD zxrw4j*4q%zH=1a$L6hz%VA&A(-KJquK8KOQxG|1mKI|_vjS9X>06P9Ex&2beXHwE( z40uKef?rumLhTfhV7-|VL&!%xLr{jP!+HeKc!@0nUg9f&6MB!+N~G?3xqP&LzLk>r zdX9~Rp}!b4RQLe+i1*mca(@jujAFuLgAa$lT*kAdP+__z8eC|GIB&rGqM<_bIZ6uG zTLr;C596Q0xHpQZok@t`_wzYX?60!2iBOexeExNCR3untV5DvSpp?7y( zcHrRF3dF~X?k!Ib?A~Tm1oqb9eFNbwR%P({x{Lj;wN~iIf!&)`V#j@TimttNxALgp z+6rTD$CfgI-`%1G-Wpop$kOgD&LsbQiy7tJ+c3a953N>FyB{**dEw5w460{ulbsE< z)*^mY*#CSJnCsbc4cq+_r;=!y{*DSq!q;AB6aDSLXRV4*`x@|0TTAJ%4|sD+IwiGN zSh#S<)@sB%8|d3&R|UN5uq5?8RF3$5qIa7;L-agfgIq9eO48(zcu%d32@h?~pg^yo zbyj60w6#1L^K7fYenrf`s~m$B+;0r~(0(xao_jI`v16S>L1VqL&4HYL+igmk@2t&c zfVX2_S4e$zMoR45o(+7T3^ha32}38>B(T3=Q%SwMYqd1*vVu{7cS9*m_0^>(6W-;s z2;zYSw_900xXrGQh1c7ZSP^5oRS5nK<&YcO(Ld>l(25Go27NZI6jBn8LmQJi99qHe zwy_Z<32v~kRB%1eg3!7$4W8FuZ>J(a)`W!H33@`<*bL%G71Rm819*8du-?MbzI7SN zM5mKOK4||23?6TRkpwHPpkJiBF`W|DTa>hCt`_tOuZLic7oFsVZjdnKF##?S`XP)Wp&Fwic0|r;Yq{?HWbI zyJaro;dDK}Wv=0vr=^;t+FMlQ4l($AV*yF+YAT>ZzeoJBY%Ra5DV^$iwkbpQ=iAd_ zBVFz-7Dd-jwiLiWVW4Ac1sCnuGS?Vs-%^fvBBJ*Rhn8`-)@Bp;9{k=)x}RTTk}mIV zvWxK7Yk#O98Svd_;iBE!?!fw3@NKQb%)1N9pMQ6SifE~j7Zi-UAe9HbD)NM^Ox2sx zqy7%ZMd0In#~KLty>l&8!h4@hj(6lO(HQ*Ok zN|#o75fQ7u*$?pUM;$N zzE*y}&nO5X88E7X6jTv>r4kDfR3`|{1oSNg#O%juq7Zv=h_8Tm@r4MR8XEtC_d~)O zMItRkXxY#>UY5X5Bp$&~5=Gz-JfslN1aAZ)Q-t>6JO?Of0`~>k-wu~UEG`zwhaCBN zG8Qi%Pyp{A|AWJotDv|1{rUlL9USL?EM7gx`wOas{aJvAu_)6&^Je^$S)_4WyaqB%^hwnVU}$P6KT@QGsZ7W^abEMy4W)> z&HDEIXcv3y@yo#QdovIA{Q9x7rk1i}6u>Y$PG&4tlpg zR->@wLjQ~h_trZLl(~Z79m4v(yS7@%34vbzeBUOc0r+)uR_I)8(KD}@=)lD}3UbKh z-sI3`!vSl+*KtpUwIoVK{NeaE z+&Ah?0uz_KyEi}RWDU^Q#j&Hix7%iE5$~kG2j#annX2=2V}M6|bFp88U?hsbdDk9$ zb~PvK|AMb`Z}Wp?4um@xNdD@3HvqM zAB6o^)N)txyvrwE8dYMa0?!L|JnYEl5Yf|nd5;t8F=HN&HDBA^c3Zv<5u-@Y(7!rl zFh4QU4ZQ&MF)CC2JwBVgR9mFO`@%BwQm8>}hTqm%(VSr+KbbWiU|cj<(8D ztj{pHrxM+LjrQz(9{umnzKePXVt;V8F@KazxcZ^+IDmR3MBU}{9nRGp zrwfGD3UX{ZDK%%lw=of$-43&Rte&hU+-+sWTfMzcn4siD9X4g$edZG&6Q0s@9iR zrWMbKn8|A=57~qJZI!x&D9n5Y_>)E3&ZuX>W6wG87R$t^3IWynm8066qbODmQ5JIh`N$TKKpb;Q=BCPTga* zUI6}()!)Bsn^Q$08dMPL>#eJmT&~XR?cP*i*I+#%g(96TWg10ZR(!~f^;@aZoX7!F zt+!j*JOX|VpQ?8{xMWh-L1+8mX^fKSy0eaV37ErO| zkgKz{Vp53``AD1Z42kGaa}En<@je7i8S%L0)+}X5c?tN-0aw*( zt3^u!f&S2Uov_!uKWD?8#UuLfyrDl~Nke%tbY@TO9RnoF>{k z%FENuq9%;~cAsyy+45i&+8cM>i}9l^Lm??N^sjZ*C8&SZaZwm3Jh^iISxf;ke{?=x z&8b8|@_5?oAU`Y$omvWd_H1?<^fB-U6OPi)q#ysRNSw`FdQd^C%?R_65JE0%xzURC zE1U9oD=juIKUY08^fB<#S!RX_qz>+CG?yr`KF!JO6&{&Uq**a1?X92x0QiqhX-JOs zpIKYRnMI5*;_+`QD8+aaL+*tYz|)Kfv>57Wc4)9#2LJfIHBOr{B_)F18hbwEuP!3A zyX)-f*whU4jpGmb8ht9_-CbQa33vxYnR{=oU8@lSzHT@8!&J)U^5G#*%Nj!x@?8vh zef8O#mDY#CBj7)~rHthy{=92bL8%GjO{BeNTs2lLP+AORtmjvACOJr#A83dX@&&;} z;%AOl)s{iy5g`Bl?sYYG#VDJ?nD{Q_t3v^Q?!-u6b9RkGFY^cA(^+3;OGiwSh|dLi z+E}htG7RlK&{kJ&23~0p=}_X-efAp8kOY5+M*5aFW!nsb820*6zP7-oi4tkd=dLQZ zalro)>iCeizS?5a3liR+@OG5flz@;D)_hHnC%Ox|6Q>6wiBsZ%J@WV1tm ziwCj(K58>zf)b>}NJlf~Gm{d*`UP*_6OL@7JpWO_{_eJ#8WT1o`7yNrXr0qwhOiIy zxz^b68Z8s$Ltbxr4GZ23puXLndO!=5nvQr}>%ebVwxy`Zh^yRU14hMz)q7%W6dYct7M#h3@uiKH004ouGq^}7H)zMxedD|Nu zoR$)$Zq&cR2{1t-?la{D=@jTChWdTZY7VibB15j#pg)J;4}MQaohe;U^S~YYTs0;G zBY=P0H8Q;!iuVVXI{{%OWqiGRTLo}0I>L89;aJ8ZAANLK5_a6F*XxSsWzI)`S<4Wx zO7eHNl{*ZmubzZF?&Z~Hqe2rH0)MS{I-qGs!hY}FmUPeuAW6{gv)QalI*~wogX`=y zc38;8hg=)1CfLU)a_7LtcE>bDN%I`cUx~P9yTg&M0lDMdz;`~lzbHD&sF6MX8Kpu4 z`#m}$rlj|ClWdX1pg+As_tumdMO1j?%oE_ZY!inVUkQJExz#G;j{=_>@j0ysc1R0> zu$c@jjh}@fi73$gs%e;?kc3YN#C;t$yV`j8j%6sDug{%b|Ye2B!Cpv1E1mWS~|fGdCJS={XInA2nXFZig;*}`U(;<+s!PJE{6Q`fj5*pv_Qh2-PTl|4NL)eif480-U4^)+Rz zr0x$ryTt+g#i)(+`u6N;*<5Qzjg+LL{kaxvHljBM!|l(*{?1_sY)CrV+L{_|THqHt zDdcY3WUV$5eOeFjnmsM$R%D&wQysguK92Th>vQQq$I*4Lpg|}o>>vA%Zgm3M5~Z-u z+Y0;PCNl@bKkn|^JlBr;BRWOEgEhJ-8@j@nlJs=6Hd(DK5_kx1H}a*}v&|wO@cDPG zYqXXE&tYJ3(zm_bX2w_*;=rr+G}hS6jKGINd$yxJmNGq7q(0wPhs|z*ePp87+qt_D z#gR?J4?JpDxzmc`;eOw#brpC&(9lH6)wgX;b%s%a`Uc{z2AkCgZVLCif$wZ8XAurB z5^|p^2TQ^pRe+k>?!?w96-juMq;I|5Zbi@vjQ6#x%Pa^2tSOp4AbqeR2Qjs1;Azk{pSEW=H`&XG5b10CtrGeY zjHbZVey`2M$@PcC@CO@US_hLd*jpUfwY6!D74}{U_pi1zsYspGki?^R{i>Yb=m~kqFka&%{2%^WI2`X zGZX&My)_m@n~d-fbGUDTN(W|hlyM3<{uF3+ugR=Ix^};>stoonFjSZP{&^ONco|O% zc_B^=G|csU^R0+i0>FUhgH}8d;jPe~pw*~FmB342df-9wS04ri$|X{W=L#~xt|}1^ zj(-7Ol;DF#7O{VT>4ac!GG0=Ohb%DDloE3DjPya##+`Y^#8#|z^3_!%;@ z0=2+5;5%~cHdGYfbd#^5eL1XL7nGL2jn`kkkt1hZmEVW&i7O@KBJ%s> z=gF7l661Ib8jR>W<5v+M<@$Gg1-^6T`uJs(xLQ63!sTjt0-fjDzpMDp_^AvB{onUD z{3^bqRB>;Pd_4EWq}vBy{7qgn*t+ozHeA< zFQEoYPv}@O=Vu=Zrz;;_S~-)>Pg^ACr|LZtd2itCU2*0IN6xXNaPjovDu1$M?R?+s zCtpK8*0!~eZ>pKKgfZnJFSVJZ{bun{^jPY}P2a26##XV=_wJ$x-qFVbr;EP)G05q6 zn6;(ad`b@kLj-IDSO?MM^V*i#G)4s}Au&36oE!VFIHzm>hby0izsVr2J4Ow>e)|6D z!*jq3?%?ju-#xeau?O#%yLqxyLd0X|qQ}oti{_Cm-Nr9jQr|o;EAKw_V&Bq{bIb39 zribvI!Xwc<Ud(rd{Cd)@ZkCtzlKOmfV>*v2LQjq`Y{_S%Q zwN%u6-)UZ~gX&o%opudf@{M?ZyR%|0konvJDstR(%aZwpv+q`PBj1@XvE0m+arsJ) z%7xuF&7_oL3_URTeWX9vV>eGRd(f<#@B8KJ>C+5)^0c%e?qs2UHAjJ_~QhoB_d!M=-6^Dg#h^0O1Jd$o3~1doUHg zl#0A?^x31!*MNS>g=xvk|3QWgL3UnIKJ5KM`hWP~f}uF4IB*W31XE%nC8YKHhrF{m zyB6!Y$BeUB#bqIPM$q#G^7H=Tg}SEAW!Ch9(Lf-p*FZD_$rDuZ82iYy`|s7spm))E zqA8N}sbd3V;l24}#!I8-9L4>;N=;$W9P|$pATepM_(=qU6%+ur6T*hOq?2#2?0HMC z{+E?z^3r09UUA{XCs{FZcmVoh$BV!I^0<)K%Bcx=mFIQLF=!$zulSAMtq`;Qyw4 z>$8CB7AE?dN~69Ln0b6;#2d1eSpe=U4f!O8DGUq`3e*@g?bVInVo83cq^b-E_m96c z3**5TjMtai%tn*O$S9DHH<~wNAh7$Vpli!!jMv%G=tt9!pDT*YpA#wpp74`D+}BdK z`q5jO+Fz>aS$<&#lkuBLW4(G5;q@O(sQy%EuIdj z_OT_1@BDhromHv)(1&amvmX9z(KEhFZ|=pGr^7jARtaI%dChmfb+&kpq~GzyY4}48 zo+}il#|DxgM$%4K0q=ciZp*U9RoOLXgJa=+SP!OrFYJ7%V(wg1d3p(zPngW)JfuJ9 z8huAg=?z#vt~xs>Eff#=&hwt1Y-P!lGo&T6Kety!UG47q5( z!b7#4|EIQ{TPv!avta*`mr(MWo__zSFTKzG;d?AukYEBAXKU(vW3SOVtOuX>^m(3Z zX%OOKvR@Etx~inNF_@iafOg3r7J#dLxK=tRcmS#46^{ zYGR8}R1WZw?@!`Wbk=#G;cxDK){_A%AR(hhw_qi8Djq zXW#tQIxYEN`<>hU4g1)|D10%>!dm=@9Upy9VNmw(fHVrYz3Q&BXds3Pe6W`iJMxU#n}xx@*^3N260qUft+^UD51YKzHWN>z}P4AGfvDIqNJ=J5Vaweo#P0m7(){ckbNM zwCO>VXJWd)vYFDMPrmr&z=fF<@p&B*!n`>?xvQeO%5~0o0(BW#)Nq4An!W$+(TM2 zWy`*$)VI6fbn&QfZ|q6)kZ`8m?j+Kvgo zywPa2DxrZWL|woQ@9y6}zqO^MQA_B%bt&oCX9q9dzAzlXZ>aOqW&Vt--R%Njp??-f zrm223`5V+RabWbD*c0E~zOALatlDNQF(Hc+79DW3;k~X?-ny1XJYSf;ajKA5dROG~ z;;fh7LVx~#UF{~zvQprS8ae)e7*7Q~L+)qS)jHR3;Lpo>X`#snFHRl(S+XaF=dHtN zvy^F!2q+1h8T^m+VOzVy(NtrHt32d~4!S?@4z;;lbr_kXd`DhkQX~n@mh)2D5a_km z>9CqgwR%KVC1?+y@VR}ht6LuA%yt8(Ao>LI!G!owds|DbjEbC5{CM~*4WE}nX=Pdn z{$p$G7K{VLZWe_w_%9SXb*}?xU^=Bl$au&LDKzHm3pN9_V>uT3iXo{H)d}DE;Hi7p zG#be|x=0;kGV@YQ@3L>Lz<$h*X&P3c$z>4dHyT6zd7syZm8ipF(ONKn$+3~K8_##Q zx79nX>Ey>xWiiJi!(SK2LO*&K`qG&`S0f7B;dKS4X|yL2N`Z$vkJei4Wk9*ziB-A( z8kzFK$V(%~L#Us-u|~e0kDR1X?SWTHbO1)FkA6!(j7?n&QRd&f()%YVLXXAYLQVow z4r*I849m#v0Nxgf=8ma*H_o86qEZijMf7SxhzOzhkO!7<>4+D8d*0Z|%O5^4Q{v+n zK_71@=&iR`11>bO&_5Bd6alCuL|KnVpPG?Qh6(j;Q5`w7Lt6W9gT-=x`hq?b=PcSh z0({7bFv?v)Uu*mCU0WMBrxWgsFy*I-{gNUi2#Y>NBLr$AVyNn9TCXPXhl1xEMk`4R zMBazLV>Xe88qsP4H1s=v+=@I9ED3KmQ&jSr3M@SW1nQL4hFJTI9de0j}3gfxL$m z3h=E9#uQR_1>0P0tvk155bXdp8sGnls`9{;gUG#s)t?W(ft6N_9}YgbXb*$<+q9%h zsS`Wy%plV;!ov&2PkB8yxQ6cr{^N3W>}pxlXt$avV-oS@5hxq|=W5^eED&53X`Ho` z(qI}8X?NV$3VWoZt`?`Ww%n*OC3)Z%p%L$pSAIleqtgic9WxBDQW`t~yL!9oGRUgT zLph1#;7$EL^)Uw4X0BapYn`iY=rFA9WP6<<>@1vVXsoc?jSBedYILGR_u`qZs;n`Z40t30`@=p7NHLH3(`vE6Q&+~<;CqCN2_AyH z5Wg=&g@DN*Snd@(2l!ENp%wB6y*!Th$k+~w^TEP0ykEX9R{?Iv@M{GAlY-|hGB(Lq z$Nitji_1UYP!#wL82-vXh;h_U29o5`SFT;de{c(z<@nIoO3U|Ud!X^BU%z;zygVbw z#s59NgZtOZ%1`)v?RCCd{;T*75>DRq@2lni*7?=?%SKFS0iOfmzeK*~{UcS&09RHv^{#MxmI*xyke@On@;B?iG ze^kJ$uYMi>0{{1G7uwb}wygtRedR${k4l2aU9W;1Z~arEFR&u_YLwfyx? z#r^C3+q$&{`ztt1_$bN5vsT#8T<@CkFJ|({jO7zPz@wyhU%7bw1AmDLXYam3eWkwC zSLzFY>znF(Q+ZU?OlE9(YTDWzw_p9hzWdtO@h@g_8c_xwYE(7z|z}0JKEiE{oaQ5%ZQv^x?B3)fOmJqU4r+ef2ks>$l$>qLU z9_JH&ZTHHX6%fP_-&c+V^8Ut|ETPBKD1c_5D~>2X{`cBZE`RNwd<`)Hu9p0-mCq+X z`YiRxd2J+#jd3OdyaD0?67kv*_A3+4ELcC@xF_HFkIM%k*5vY6{$4xc99OTM1#y4k znFY>;m6G(HMsw|0O2`|GZteABDeO^hoH4pmL>`z};=fxS`?uf}tW2*SF~0fxofu*m zhVoHQmP{vIs_RERnRrIJ^qBD{miT+i6UgI<1V_Y$n+Q%o&xs|jmA^(*l)ph-kX9~# z?VfxG_irLNMH<@>@uCsOqX={00)F9=|6V&%h!1h&%<07W^?ULiGby~~<>ig;s)zr=gfpkpGS2Ta z3_trMvEz33N1s_p>Zxhm5B~yHSo6=5(w?%AJ8>_7cnugAPtAb7{Il`DI6fl(YFxfy zNM0ST@?vDd8ON28wIbQCBU@?lC5n9Uxq-afrJC@$A+N89aRus+3SR$QlkeB&gzNJE z>IK}tIw4%S@E^_>_0GNqrx$KJ-+5@qoi{$1kM8`TEI#-DX#8UaJ+6Ko|APIDn=Y8i zO}F>|=jET8VJ0`;CBN59CcGRMSR2d%dDTeN{g}yprn*i>%f_-7<7Zbt?;f1|ZYjZG!pBVBd*$MU59&MN z?BaX!{Bc9Ov|#?2cvdLS7dKsy<;TPe*UIbUY)$w;U!0(C#Q6U}pS-SQ{4MQBBhow?-IHF4&(XeY%i6eOyqF=wwo@v}hS0|q5h7P8-9cqoyO@5y&8#Bg6AvY%zws>hD#4G&ph3@bnD$}yF& zkRy5HPgEcZ%}wR6l(@Nk0{ZRE`iZRXUDI9UJ6GiCjcb2zdFZ#-rt0x&Th?bMOy9sK zCZ4r!mCN6F8#lf_oLHan^^&YnT~))d{^D*(d%66?GkHCJt>l$*|98r#WMdBv!uyQl z0TXK=ukWwd0M&;7&P@cz`gmelyV zwx1ljs-?)|WugW%zW%;?Prd{GmCvs02)IAt%!2j!jeGJPx%|Wu|DVenuXHQ=YXSi| zxj(OyQ@$hj=ZzBoeLUoE;T!+2-Tx}Sqg81w>1DSk{_@lH|1|Ww<1;z3u@8uKP9gPh zA3IN6QcBKapO!w&9NavW+4{CvN(@=L1x<`s_B(o!dOz-qmvMTm-HWay36xQphuA@{c+2VQZn`6=hVsQ;9127 zSz8|6#gPp0J?ft-ri)dlBU~n)7psiqBVT$fw_mw`P8CNU>C=kyk0b_J;Uk@Jh{gU# zM&^>J2&8mlfISQuKZ-AsTQ(^W( zvB>wQiBcK2-zpk3N-chmpt@^^nE(Q{#CjjYegIqf-Z8B@^F)@8kBs$}lGD9P-JBCb z?*(#l@aYzo{EViQ2(PTrrj@)%R7P@k{tsKG6&_)Fe?B?)^rI}{44GMEBSogtQw~M2 z`=&%BK5}B9&_gB+ihJ_(4M5aOOi$v0X?NqeT^fJ z-maS`E{I1JSx4v>GgxxlH*%L4m|Iku08!@PeaCX6nkwIg*&Fx;WcmU#SvT$MyFg8U zS^UnR;XpM@UcR8%cId+g_Nfi}`?rdSM;l|l6&3XB5561yDAo&mj^NC$86{Qm5`FgO zymHxID-vf^@f|NeGPQ`ue$(_3gg%~~PxlX9)G+YJtK}YTDWQuQ%|}&plwsKCL|2|K zozZLd<(=RbY=M8zf@IKnzi!h4`+{ZF0_H!;ymDZ$hwIN2UHz9}Uy^)}3X!wZFGd(< zJ@!W>L(r1D`0P-eC_b{no?vtBlroMTb*{`6vgP;|Dq->QnTOx@>PMol1_Q9~S`@vc zH;z1Tkbb{Ng1y^&Tkp z?)#?%Ti(0ZgZ+4kS!&=)oJ+A|D$~zyd~9_#TX&mElV|w8q#*Yfo3O99AVO48J;8oa z&gE6VTphn7w*8HF`AsvFbM%RKa zs&TGo5$weyPafWV@=MQo>N7=P)B9LoE~8&=`;!oFdF1r=x(}4(HP2n@jcwa1Zza>fDd4T!eG=^ppVvH~i!7>L$f<-=>sc~WARi{xD|NF2;xLnk z`ZUhZ2&5@CPDz`B^o`ih`f^bJgXSq@R(I{;_ZBJ1<1>$eq8(pG^v5U7fc^bJrE9jL zZN|**OAqH8dzEA%aj{&8aapJ3SsE@XN%12ewe_V);hl)ASNaa(Q6usBlw0Ca1 z4r4#c`S!e)b$!duE!}uzLDyZVZ^7_L&7@HI$$6e>A?zQLNfcl6QOz=1`u(3sSV^8D zw(iDFb6lPn+2{iO=Vg#Ig)}9zML*rX5Bq(b`q92M#F=ccSH%~71b_blfnq##MBgxJ z#`yzEGTJv=adf=88e=c?hbcSl`R-{yZ2B=-^W&EL;a8UNHMO1^`0-?#i{^e?MbhHa zp03^bMpw%ZPLnAC#Mgc6uJ6|mWu7Cd_la^Q_V?Bd`xZ4uHrP8(d%`11lC?yl7Bzlk zDn(%5Ptf1thl$DGuzJpMKc{{G{8>N0no83Pje%8@2lEhb!X?F+RrQlew$$)mRa{Fl zex#f}I;=mh6xnGXG$_fj9S8&rsR6?dhG`(;jAL?bk>R=xr~S9G<>0 zDe&5zj0LfjnWsVGw{Ls-Nb$pI!{CPngg1F-zv~=YdamRFC3!!O%v|Ce_AZ(fSz(7i z^pVJ@np@eGOq=!e(wsERk~4SQCD4qzI+$=^ewPAA7~BTmapt;Caqe7 z58FL2I(|;_e$(>Fg&q5!`xW#zb^SjonN9Ww|C|>0zli= zltq3nsK|c%gv{Bf9F_FLnY11d^(sB=N>`{?h^6c9h7fCJ8|LWs-Z3{T_2lLvdAl~yo^Y=(^a*CZ_ZyVg{H1Y@MYjC@u0N$s z+BkK=zS|$zd_qM!?%x#f)xCbt$!Gi9(tvJ$&h<97xMnrdkzZ|{E@(;C19AtDe^$;^ zpQVPmAa8Qu|5zHi&oPx)-f6sO*#ZBFpP(N|)lLQT z_}uDWfL;MUt<~}KlhfDVDz^gswxIy+*wQ+qC9QTN+P^?`yVy9_`|*k?fhX$PROGwf zFT&^5_)DM$&`$`$mSMy2)-O;KJB94utl>QT>dE{Dd;8t+7x^+cq9zwLfrX79*-I{<{V%)zm?p&= zmt@1q=8q}@$*ED3MwfGb8PZ0;zW|~rZL;2*r)>L@!#UGfzlJ=(bvHZfy^B_3yb*6C zOFG`64v^ywsL$=LUw=n}41KZf*i8OQgeJN*AC$1~JIWe(nIGD$m`v^UPHFU!_xFIl z=Tl@MCMrudXTLvUtwhCkGH? zin#7R(H8dc1Y$K(pFbieq?J0C ziQHMPyS|V5A3mcwr?x+U*`im2@!;h6kwwl(sPSZn#d8VmL3cTI5MoZbWw3WR=skyo zwGU6W$NbJ375PXr;-87;d#g?YZ9sb&VD1sMlNlh2F4RYgsx|LbImv=_X+FpVHSF3A*o#+w%i+HvmUG|>?0nrHD6y#?~jE^#Xz z!M-6$iWIqI{}|5B=DU!en-3Inb1?oOWb-vNG=*ZL7yP#DuyZK0gwInt!9qCKEU4jp zBSmrx?H#Zm30}I=dzwg*^K22~(O8J!8gJstNwWQgzrp@d<}rR|R1GM%9{dKMo*G5~s7i0hJuXI6PvPbPNw8-*9z=bs+4e3V9lf)4zv@m;2QGEhjo17mnU7YJ%J zyGa$-6LT$Nq2pqZeTcLd7GYqt|knTfMplKDYW_s+L? zPp9#gJT2abIb5)4f|+*jhe3=F3!3f><|%`k1$Gn0Yp}RX#V@j+_Hv=L2=obwhWPhFBp@qnEoLWq@V%QG;IGZ)V2jPok4;G&0aE^En8VIEbu`>f{pa=0@*70QN zl`a+WeCz4Lpj7(dcyrMo8vZhpVQY%OO~L-z2$UWSYN*~J5)}I{^PrC&0daU`?-0zI z5^{ccuW>L9pv6M#C@0xJMEo6ZP@*FoEUlS*4fuUA2d0ydw)f1Z6Yvepu3l#)|FaN}IutX>i__4YP>ES%wj>hQ%7RUv4x>Emont zMp|es<2AuhrYO_H?yLHc11zZqAKN3~Psmc9V60t6Czq(mhlO?mS|y4s!B`6QG2=ln z0mnvX8vNND7&5-90C2s@z!%_eg0{={e0diuGo<>CXhzZUlt8N zmTOiDh)U1TiNU@p4F*L7!+{7eMZ)vPlV1mU5KWMZp|5cosmN#^&f+CrgZGgMg^42THd=!Iph?~^%m!d5mnbsg`8hGr6&!Sh252v+z(jKtP1cxdMr47P?ThdjV19TdsEYdI%J~0rvdH~| z^f_<~yFwT66AbDMESL&ylIbH;37;$N1AXyN4@edi29+XsKdfT$55eZ)O9p;t4N3fN zoEV(^dkvu8tHi+rL2r->#KGI;pCP|{Om^P8LY-KS%)hWi1+~Ey_!;nB`LA4cnPK?J z_>P8;lSe+v^^toB_duTO{(hh)Xo~vDcrfazmr-5$jVLdZ1;-ooK{5IF_%|L;YW%PK z9o+!?GnsH#ULk+M@8wgx^2$}5j~Bf1d-?u&ZN^K;Pn64}?)ayd2^Nv#QOSSR@+DLS z&(A^quKv?2P+qQ@{BN8>|F7Yv|9^eQ`|kSTtMnk_gXQ{d!m4EBS&ha3WR84|23|<> z3H}#EdU?Rx_rp-U6jQ#Ao==1&Gjwnb)8&&@xfXLCY=L3SNszgO97CXozq;IK`h4*x zzJW0-t|R0XWUNE5*o`9;;$h|L5W$Nk^|Sy>ZrF5!yBK^RBpzTG;JXIoiugK1KY~hO z&=WxqQHmkD4}lLRkkmQoKaEhSmB@K937 zn+{-<&h0NFQ*>xnZf|-{?tW1ras>GoHzI=!Uifr|Z2$d|NIiYh@9PN;b@=-b52NY} zS~r`f7iY1C#d@^=kcy#)Me2d+jPQyqEs+(&nSo{@2Sqlgfj#-3A)OLZVuyg)n8_#f zi=%P=b&7iB95hA&CeP$nhFXezJYQ!tOJEqxYE`hD)@n2tV1QdM+oLZbH0;XhE+oeI z(oFi?cl|kta7{zO*94so^oF;WDE@4+Y`9Kv9)tb*RECV|sGr@YV@gO4XitmOj;R!r zBm1NM1?KZ@ZLO^^@O`xv>tp_WihnARN_+}|bsqf>e^gp`T#QEHf%`7rTUN94SFKYS zOu&RWhIng??lujBIZ)4ljer+3x{Yy$pbFIKB-4ZaZNi9*E}~%QS0?EM_(ynM+d&i9 zP@jj@xS50_;l#Ayh%Y!a^kX&XD}B@F&HOZ#n$Gk8bR746-Duh~ltyIn76alVaa*Ls zf|&;Wa$23MhJOJS9i^B*SuAH~ab{zQ%y_6nUM69dqYn9~rEbS;&d@*$3o_+=Zs`nR zZh?jc?~YF+l94=ciRc#3L?04ezY_c({5ksPn?B!Of1i6_oP$2`E+P9S0nV5Iz}~Ix zQyzI!WYcxR9I}v4hJwKm|K#l&?zZaHkF#V3p%52|L5?WuHVs=sx+aO@`#Pe}5~+X^ z{FD?$an&@q0V_qOQis9NkHj&}uoyy8l%GFq28{3$VzhiO7>wh&ZyShCN5@1FinlI? z%qZA@py`9i<#YjGQb>J6^3w>JgHdssp=Iry^!@dy%l!{CQ}7xlcO~TUa~gXR;EH-m zccce1aHaUc%aczYO8+3MGN3Fa;?fyRZsESqDcwtlG7guLW2%7mw4gcC^BH!Mj+GKs zl`i_|rFC5wMdlfi0)Bi!3J0MMwY4EVPMd-tGYXHM-?ooO35nQE z0+j*j7AmP!g4&W)-KsFOB&qxV?RzWe^S{2ezO}x!-df*U?TGi$lOT!+mpWZg2>yooBnnjdzGT1yF-fynOOub&IxS*=KAQ$MG!RyS)cAwx2@56Z9ua7_adK|Ak znwR!M+!2hI)#%RqbZJbJt=k0u*a zAI^NJ=84>z!Hf{j`*a7Yt8oUXpa2Ra@%!ZCzRti~^#g~y1CNcS{5#N9I=zC~($HL1 z2722vszWfBIILSPm8ar-jioTxmdU=bm>-tnBg5b~Qae(%>1;~|*T!u#;{Hc0j4|X3 zhq%z%am9Hoz9$IUx2I`p61P#lvd!NdH9wMVxARBOr{twsn1L8()8<5@XlL8NAKZwC zo9e!=ta@JQht-JBt;Jrjwl-i8TD{g6D)l%&1{-9JIEraYI1|=STLTLOt|>HeW0(f@ zy^lRzdJ;zwN=tF3loTbqPnE505An<6=P|@%BKZs}dm2aO0s)+KK>QanBSN>Xu14Ij z7yPE%$GW&@C`!w^fQZ-Jp*3|*qg2$7Yu_TnY551%#g>HOn7blR-KVI z@IVCUlC&?WtgJm3ICKoG9`}1{4*R$A{HET=C(Mg+GB(;0X)aOTUe0sn;68b3?Cdum{w3)>a~F&`j9UoInwA0c9^6vcC2U6xRF>o0e{YO6*s#pD%|eo zA!$v;CFHLbgy!ao+X`L+BQ1bG7D2H@VrO8LDxmv#0zxikTT+VsDRZmcEOu@0>Z`j| zrz~UhSFgKB`S|(*Zg(l54lcIP!mja!=K%=YHpPm3G5j;}B5sKlV?_vQb+X+QSV1-qqFMFl!-`-l+PQw-Wpda#Ct;#+uLxDL3v@!-Zzt z^uSgwL%Jao_$`Sgsoe?KPIC@pD(XXByCl7a)wmrq$9WqB5Y^(sL(2<3 zKQW$GZ+`&!*P8{QstU2trSuc<#kGUYkCt6)!Nu6_cAO`aq>2gxk(VwJ#QAZoi~-UL zW)S?t`lZ5Y@ix@U|w<~MGGya6`(&_)rC zxV2+FU#Yws5wDe%1f=R}H>;Q0jXHMJvMO5};_B24V}bOj*blI{-H?w$CN8#PVsg-@ z-DjSGTz4U6^o2R&@}ZqEX+i?2HdQPzdPW8cDoTV`EB423=qH(2FPP@ z7#GgBt~J!LwczC?d=2w}pBBpl)Z#VFI-i9e4mBAY`FU(5MDIrMiyq#F0fe|-J&xk! zBcBFai7{-eiLJR%5C7;Y40a<q-`!kho=K3}-vw54J#E2oNnwQdO0k)djZ-oM30S_jh(~WeU8#-m0nq4?;9(`lvs z<|4i~AsbfC8B(`+6hs)pwe*NDz$w-@Fu+?NkT+I6gZSKS)t^I!dO{W$OMxG1mQ&C{ zk^ZDixY?AzTOhQroC(%q)qKd8>gun-44wj!d7yP5tA&h5Es)=cbMxjiVmQJ-m>Im!#!{{(v8d zuL1`|D=M0yb^t#EhM_Ls;}hxz93<1_@#6$SMMZ5b2&;@#jX>bl%BwI5`1VkL4N(t- zg+RZtP^L|19hw|NTf%!Cos>_MLL(I(RDfx#*)pK}B{0NtV$y&w*f^9YoNt&1GC{s5 zu*IjaRN(Cp8MRi{#B8A)jZY}nY>JgQjITlRGn=+8G=QGgSlQ^q7oZ63H$KGLLo+a6 zw|fBk?>`7$05A~ej{Yv}ho=UM@bKdkOlPq^bUb7q^bJgy=p%sL)2z~1z!yMd zpx?$&q499+lF>dD+sc97`I*omTUDk=Xd46n16g`xlmHUc*1GBb&(W`c)8z}04A7vy zLrRN;9MJlgLDs9mVemTcXT|(adKV^O)2e|6JqC6LA9fPN-vJvh^b;8JL4(jNkLusQ zRALLo2pJ;kgNTtSfnKp#XnjqFU^ujmn;{eB5{w6eycQeSFPbwTc47D#4d%Z|Tmsm0 z)Dr|PzlkvoO!rIJX3#tA1O#rf5PQ7oK*i>lBPB^2zKI-RJt{SO0&u{QlZpybB<1!A zgO7tq?!<(_Z%QE>v1EkZU%+4WOW4t|`-I*@14w5xr6$$7^RS1!1UgSeT0v(LSjc?i zM*c+N;aK3yR&J_ffKnRRD%e6+xTH;5AT5Xu7lZ>Igvs^bF&JcR?Vy{*2SeKZ<}7UK zd4abBzP1o954{}9#0904Pw#e82;9()kn)n&I~3X(etv?W(%&Ox#fS32zj}}dV0|10 zpbNoXa6ZkzXX?JddFTz_N}923I4l_@>5>>`%h_7M8_G*{^$lb+CX6R|8x1g^ICN`A zg?fyzXJd5DB!VXj>nbbZ568m3TnT-u8kBMv{z#DL&0uDvQZ)}@y>mDQU7oaB+_Ve+ zyo=V$0&%(zhmzb3N-WG#?d&`*Z4>=&#)nmH3CnHzhcWy#$OBMh9f5=7)LeSsq?#!O z#Yn5z1Xq_DdI(UM^dT_Z#%4F{Igzd4u|X-)fE=mmxcED;)7OZjaO{-x88M$><6wXh zYR%{nj>us?+lX1YX~2XP@D)l@EvRDwspDGE1FTqfTp+t=LKZ#`_-~((+xUU!b8|htyKXL$hEwM-PUjm6p*?P08isl2O7A-SW zxm9#;F8P0opmxjhm;^G7r>g3|s$sC`r}ct=mbR@xK$4C;`KJo7Z>vC#Q&PfTruT98 zFllRepZwi|uz?giB!8%?x(D$HU}A%$Ydu<4X0go1=Tg!Ih9#*C`Wuah_*WT(pPTdu zoIj9p1?h$8oatwT?}D&`#3ec@MSH+U`3M8U9-)+_AYSSszs=|w;J1O4Y6|fD8DN60 zkztwI;r|1;DRV(H?r|V@oTu{- zfKxN!D~3<=ksWg+b1MNvdVVTe8C)CwP>8ZiG&xKNW?LFR%%P=_p%Cb)@TWoD`kKfa ziw&+!^GB(GR)VsO`me2>QGosESUSLPg7$r&ZaJxegbft>BYFisp$?`-^1p#T=8*cx z2!1H?83f|_@^h%mXuXWoTFe(*7d!=P0gqKJ{QE#3pa+S2Abub40i3shlrz8`p**BZ zpm`sOv5@SqgJ+O!cKmo)&47n~0o5b-x` z1Y;Oj4^(^^NuUQ1zZ)m30`ipc96Lkoe=)fl;%k5>rFp>i0>ej23W6U54yaGY$lU~Q zd?k|`F>TbxxJWgSpj^=S7t=>f6;qJg!=z#CA>9mkNd9Y?Jpg$(F$ExH=9EI~ z0DSO4MrBYW!5i3)jek3#U@gG|lR%M&hlUaVMH>Ny0bAJ92?KU8EKJfZ%;V6db> zBfLlOh8+l&1EmGvm%Nt_1W*a9K=qMb7>k8YutfAZpEdwwQ;y;rmJuID>dUjC4HIpd z5Rc)-wh8i=<`1aGV*c?m!r>FD5I+G&z(}>k*`Nc<_yCN;X8@HxqYBE9gyV03m(q_w z%YrG%=WS4rUZVzZpOlQ}fC|2q&(ol;3@@;(>?l59F7o>x!hcX1RueP8SMqugyE%im z0U?p#LEp>s!>ZAEfM;0TR+$OXdO}yHy7DR@e+)j!{0GY`^AkK$#!Lu@{?Ib46+kYu z#ls&=1@F)tX?+z?p)t7^@=y_ZVgY~`@q`a3cqUh~P! zqF@m6cu*OC;%k~A+QZ}#%Ro2KW7taj)FWzO9x}e5FI2+&GJlY3%9Q~L^pEAG2n*Wf z3!x~y@K~7@@i&kOSRsLt*7FMepi|2KOEpnY+4<8is{N%N_+C( zs73jFXk1DI^fUUt@-jwBpMNa$`N#3o_tf5%?lG{R``4BJ01g1hhZm{74CrX1KWV>? zo+zK?Kjk-~<3&}h0?G*dqt&r*`|ss{KmY#=9*njvKPibZ<1GbwdFeya@z>2YGnBzD; zfvrPwb6lq-m^X9Cie`m1KE%Nzk8M;a#HYf(k5~jS>gGEsBOClHNDFuiT+PS5-oBwT zfwAm|r2b^1>4Zu>$9Zqwb%@X2%<0W0csupim<3@PD=927Sk5}l-Q#qz{sBF^nTItF z-0~C3(+7WKBocUaXY)U5Kl=Eik3ORDqG%;)JZ-mlY<_riycy><1dBUw+U&dpoNoh~ zJAZ+Q5=a;!fpue}&BLc4mZMepFGJY9^Jp8pZYDmQi#@m4jhg`2Uh<5?UjyzVb5kDq zSpS3mDY;lgvuLu(jE1eS_o4_PRaoFxj8T~j*g|0-F+4XTC)<>ky{OQ|UcaQ(4C9iK z{@(&XOpc!*n4lkw_~CqPHQ{ELx{ceCshd1JfGa#C3h;g*jq4<~%vqerk1bieCfj_` zl7RDs>x|f4Vy;FH1~!eQ+H9%G&VPG6T;!JPhZ30w>ErsoK6d8V-UHqi5u0tw=DI&qk|FA`hAa|oMx2F(YzNilIN>*nU+ zTlhx;QY2|OfaDnK{{Hvsk3L>yVc#(XCK-{OQVe0-E_QJHA%xZ( zYiU{d1D=pS6V6CL3cc~8p>5E1mTW|1#C;n68eGjF08-p2SoMDa_%IH)Kk-_i@O23L z%wvTijyD-#vBvN62~OtDYa1t+@JLvi<+AG8Is;=X^gP12c)`QF+5Hcs+~zLuPvW>0 znNC+8GueQycFdCSawF(D6q*5&Mo3wxLqt4_NFwiQa4{sU7T4oaQWzV8LFsX*L$5^w zfY*9>z+lN?#ls=dX=RszrMHJdx_vq^)Q)(Jpsx)N?|^mT2oRY3cNQ{nVO!V_CD;ne zd2PHACLnOASc*Sef^UE%+#mm5q1^b1( z+hJlg_#qC5VG8S!qBL6)f$#6sHJ_gUPoK1-;rQ|6$akal=~^YxurcBbiM~8wmk-&U zKe+&&Ac=^tSUw0cnT|zm!g6QeBGrKJJA~{#?vteB68zCEkN!twhkF9Sh!2-ES#kc` zs9UCE#+3$QnNuRKof=0Wo_ZvKuf{vNh1& zk-TaZ6<9wAnzbN`Srf689KnynmWYFj4)g(j8_S1Cgn=i-f)`m>2QUf?&TZo;wL(UXS215Ow5Ugy-+Lt2BGK=2U(;IQZkPLirqOwOef9&1b<8P z09<3bwGrH+#V|g@C$UDO)Bzr7m+5^CS|rYGB5@_Rk4Qt23qgP`kB2>*UyBz=j1vO! z$iI`CVAWgo30p>yAU2b~stFT^w=lH%0>z(&PFWJH3qeyH$VSg*0pUsjEa4iksYJjl z#N}u>wlfh-eGHxp!goN$v3NTd_=k9`1OR*{puP?0hIl@XV@oH1B#p3sX5qX(<4@d0nnTu@?t#}sZ~XTr5LLKt=^ru@6W8?a4`@J4hK5BHq!llMj{LF{}5}$ z(DT_;L}3OY1&Rj|8u;VpT%j0zJ?B$VCXCn&987VXP*0w>$E!EDYMm@=cM-5(Ix?{iL-_{ zD>JT-MN{|i2IJuv9Yg#WktXR-uVXs!r;_=E_ALF7aI6gRnFz8)^J_tLu!)m=q_S4R zLIOcxAL%%NM7MDAO#A3}m6a~$h=h?gfm6?+WwtdtBE*yNDEs%Cg-=q)4gO$V>q{*^FRhhGMk zv&;zg4t)X&<2F!44at@Hh<8v5xx6_E@t%l%6#>AN5Fb9osle7q(1L={4~s40BPvON zP=UFQ#=H7I9A~n_`BM(N2ky58m$dF0C5BdHxU6m$@khjdp}+_L%K+k)cy`KSwIn1Y zSoF(+1?;T&T3kfu`=Fa?yS2Wt?9A#joo70`@$Y#M0p??)m7NGKz48kGVG>3Nd}YAj zy-=?g$3tc<5W|G;@GP12`UK1Cmmo>tuQ8eUZ}ak8?XZd>J{JCNb0`4|d%px{p+H?lD`5|mzTK%#IrJM8WB zKK=`bk=>0;F(Tw2t35{aa(5|ZfYyVk*m`2Bz$Zke5IrHBM>cws-x~ag3lRZgLJV^u z{xjR;wz?n1vN??u-(-$~DitOee&co_zUz%R@a+yvAEh@=Y#KM>jjT_Alw;UF2J0go*g5eMFlZXr() zpXt*Ep$&1UiKE~=I+5{O1U!of*`hlTXt=P9k$()u#Xdx{=$(UC z7bnCJpCXF~p5vziZovPfu3+SN5TOl^Uyb(=FAnXG-QC~cxo9-j2|=4ws4Lm9iu??; zx`g1r00=Es@;@^65Rk}-IB@v;JUtL>CCQ+vBFJbrTVlu9Umvd(MXk>V{vO$9Hkq6z zi{8q?P>6Vdv4BMmcssFR4){Rmz|TNib>}Ibo0UMm^RgWW$N>BzGaTi}l$9=ukD&GF zBWDKuO{8C-Js zA`epHT!oU}J_e4#qAu9-W~4tI z8MOc}9N{+`5A5B5HI)~Z@@-J<)ih^hY^V04@R$1lJUQ+>xVahW;*cUvMe}c@HwX`K z;*^aBZ7#Lb-ZU=gL;Mh|?g)jWLVdUpo`~Dl;oXlQAy5eVfUblnqJ|!>r*kD$RoBx@ zU;!q7BJo2JvD}Q!B&PNd{MTShaM(&BmR|?>lH4O7nBpS=KFf}E;FgY!V;qf-UVuMI z_Oe3$kmQh)h}I&$%Z>Y7Iu~AzmMG{t5`e$`baQjk*MWY8USSM7>LiHnId1msQVR$1bT#KZ!~Vh zZ~mST);p>RLP_;{|5yq9#ADDzXvqYuPz~UP>g8mPX(dJ?=iie(jj|UK>qU-l*@q>2 zvA_?4izh<4U3b1S(68-x!+sY$;p^-Q1h!&eu=f*}t|nkhG%x^~pdYo~6MvTTA1fvd zIAs!HPvFJ^rTBGPyj+*g2EZHF5_lgY4VEHfQHW`h9|`h7l70jj;Txzzemb7WbG=oz zK&K@enQ`qU)KN2;6UmKGgahdHM-I@e`125fgCe8w)ox-$%wzsyYqj`xE#md5$U`K!Vg2NWWrT&l6Y)Ah1Bs|L z6LI{>5(9k}|6L8KjiI7IpQT@$h}LuP2mC9tK$Jtk;B%z-L&YN_BgJ3S*>T**u;5Fd zFX%%gJ@^TEp%vLFETwoXH_m&Y(1{$7jNFOrGgrvM%49I5(X81#BlvG;|27BYZYE*Z zrgn-4MEoEXc^yIDw5H&fGI`zT(s6!?MkuLpi;x^|1z5qKje?<4 zefg;}0pt%SFcN>G7$iZM#KI4e0ko0uYsC;Npn)|)_hJj^FSI9*L>@g6RysR9<2wK- zvKJW2-_a%0+dt&?WC&5bAPV9GP*4l{vp$XHhbSNvHbeXq}iaS#eSWR$6(f1fBVNrL(ihI#<~LAV>d z02o0mVWS|AUxpsyhESsXRc4_o z=!?2mA3ULP2w29nPO zDP919B`9#Qq9m|USX*ur4U>jW{#S)25NJENW+Dx= zD4p@}h1?5stlPcPuS0u`=n)1e`oWM7jE46a4OO;@V*Ci#L6IMmzCmap_a##`;`3z4 zB7_HJaVf2DzI!q8CM3O%fM4Sn3JUg$DA04TzcHKD*bOGUcnNre_+1%Kl&B7hx1@G7 zgrSSZU{x_cc`PJhNg^n#B{wGHfV}_08wz_O{zz5#kv-mN!XUt3$(pH{FTfU+cprH( zh!G*!S(GN__SaMZ9v~ihzl5+l9r&r3BfmY0Or$*EL&YSInW%>3qx==Zfl;F4QqC=^eJg6Jm{0M$y3%d!Fo#WU{TYvhqQUh}A$kgV@`MJfbOa#uxZQ%R)9dD$xI+#RjvlxX39!Kv6yra*RUvQ$S-vyf?@Q7?qCu*HiLd z7?02kStS6x711kx0fwvxeCa(i4kv0Ok~Tgvj-7+$;4FNC{ecUC2-a7z_sA9>43YYy zA|nilitsPA_7XW8$>bCQKKzsfW=~@9^YF$cw7KOs#e!aey~_4N#xwA9;bH7Ur2xfa z>Og-AZphe&xGRO9$i2yIj-HborT|_l(e)@D`A*p{$oxlUn6y4hZ3T?DPhCc6g{E$n zAB;!XBV_$LoX019;cW#~X&~S~(os2pW_AwZ@vI+2 z*H>v1_Q!mTJA^=bh)dQT<{m{g+sMMbHT zbP7F1_b;UO%lr^Ka63a3G7G1$E5g4Ke3ET$^f~&rvS2an-^$wx6uN#k|0q9- zG9*|yqvJs@D4P`gk|P6YFv|R@kzheC@1hG?e~Ah}>Voi9+2G;GP;~v}fkuD)xc(#K z!(l2aWdBErVK_ZOMfr%&N?$vaw~&udnTP^EFW@VKnT+cwa=b5%1|h3dqWJL>lu-6R za!9ef3s?AFw6&4>RV%g0<}Ty2%&?R_zdWAk>jWZN4`pwr>``dFr^~pc*cnJ)`N{Gr z_(~P!IL0VF%kWcV0>&2|oPsb|57}Qsv8zxJ94aF zl(tjkAtJs|wg#dTxgM>*GE;>E$wE$PQ64DSCltz3yb?11i7qJplIb;g!Y7pI9IxQ7B7t$RO2OYNA&Xbwqf}!f&y~$M zj0y^V<7sq^N`Lqe9Rl5_Cl^H{1ph*yhFp{(0RGBCQ?xP);1gXyWlls7Q3RAL(0VDG zVei48!-r-r^PeaR#xoqyoH|Q@454mqTf)m9DWxnh*0_M(7@RzN?`>puW<66n~ZWu@=#3AirMpi83v;{}X$MGM^~;WetGF6UBH!FQvaI zZzIm8tVeXl;ED7Zrj+P-uZ&;m41LS|;74+zd~|X_u)bWP_` z|L7F4&-rQh^7{(3=)6R<{b+jt5B7Hiwg*^ zOg7H{I0hj`gbPSi?7`-1XX|`bt8iI{_tIkcUk*N7dK9NGELx;)cm(k&bH_^YCboQ(cj+h# z@@M6+>hCQYQ^ouTq>@Q~_-jk|7W5UBB?3Hd5bM{?5(A4Npu>dkFqQOR2$-nJw8q*UM)nyeD zgT37nsI4fgF5P=>(Np^;3RYxQ>acZW`X4srBnNYCZuZIf{qyhmaEfvJ6aU)n_mKX{ z*8kUp`esX@TIyQuX8*d;&Sy=Qo<9~dC9pN5W@ZF@F<~<{y)aOuA^&6n&YNGzcH;>3 z7tOFw)gHr+lX(gYsw~J;>}CV{uv(?Q+cD&~>4LDYw@*hlhI`grGs7QTn&g0gNT<&T zEjAP~AJ=5sK=y3p9;hzcciJn=ItzcXkc&P4zA;FZUQ;U|{|3VKJ-aWkG|kqYn%GP? z)2^9pTGr{?xTV+ECc&T6Oj}QEq6O>_3+BSU&RHWno4{f`qfz!-AXC-aq2>+Tf&b?WHn-J`3p?F zmc^8CW7b-+f9T29&U8rOw8+EzF(1=RUe_Da8$6+X!j>3}eQP!BK5+d(Yp)&+ebtNY zA0pzb{vD9dhhmol6C+z+W!X|4cop_d9M*-$8nT(Kt%SYh8UZJ-13R!7-iq<`WUjdZ`Xr*FVBd#TVl$lV*e_7DU78#DR<0>7cog>S8O=oe@jjKwiwNoj#ywmA$R_o(+N2U+4Qs-BAJ2Kd zUKP|VsgGaIJn*-vHjiJQ!}O!$>eLIl1om$oCpa{}Sz|B>T3oZmklTOay0Vuax_r3e ziJM_x&52<}xQ}LbbJLmR0<@nN|8lXqr9?Z{H%2$hg!vqJw&Hhpe^`0=BQINX3G=Oc z^y!L6t}T;%uU1vWBh6p4_tF}-ch!RxwXgsEV?5We@~WzB)9)9EtzkXl>f$Tdt}!L7 z@;iGQ?hmk3EPMJAtMKnw^BPXTuf5dPutdWY8nFOV;Q13H)w#u3ow6yRX7mZ3k|AK;2v9TH!3P<{YZ zL5};7MEhI%+!K*3dA%3_>PzqkfeN~Tnz#^x@!gn@Mdk4#3B81@`@li@d^d+JrUtKr zA>)1Iza$eN>IW<;eD?Mm1bi!-bCmZ%2lNIV8O3v6AdQfW9`b$<9ZH}j{9>S7MgJag zc~ZV|s)c_1CoCn5Jd_yn!P#-T6^Pu^esD0G;(e4xb z1dE^cHy9oL!)w&OQm6)X68((1+2t}Gh(ST$xEU7lCdUv7dGA-rvtrA)jb=GG?^A~` z3tc%ynUpice_JiZ=7%F)KEp62KvS_e=53L4uk5Ww-W5H&Dkv^yh|E%@iEP}}@MmF| z4?O)~*_p>~vQm69T{9pezUjZ<2x<13>1?PRqgxVRhXj8rhAe5@t1A#ua0cd;S`|BY z2(pc`3s6QB#7lluy=q%El3x(v5=Ih)m6T|#JPw~AXyPHMV;~_BqRHXD3@s1WzU;K2 zcuc^m!YwsFh+_?(J@SQ$^&wQ3Vx3Z9sIEo+MqIK0i|($EUa9oQiUm+iSQZ%-+r)%-uth=SrS^i?!5nLoHx<(6MUh5q1Z8rf1%X? zd|CZ;*{P4`-}=!D-EMYw+UMdh@^Rz0A%}Fn%YmCVO#;g}9`d)3 z@9uIK9Y6ztb*pA+B#kQEc7AkEY@3SBz3<7d-{njRUyts5s${H_6?L)hguZ=d!`=Fo zGbO~JCy2p=HU8loRj2mE(*lcMRg*S@7jyeeHunA~fer1kZ_egSZT>yH-|iCFdc$M6 zk4~7*4c|BXX7_H6b#4ioA4u4qKj-_&To~=$f0KFk+=G_`e*gDVj%Oo+J(8koQ#q=A zpYP97NI@9SF80GAI~#}TRv?O*w|EKcl+5^qiLb^!G#vEhgp8X zIQD+?;Fv=m&G!O(e|&~rmobjX+Jw{hy~zD#^e-!8DtsKX%*vj`)pQnK$g${Zu-}=Gb^4Z7k81mi!gK0p)`0#i z`f=>xfU)7(!s}*DHnaGbZoPZ>t#9YOxUVCxZ;im}OorvQ!e=~I%@HQpA)97!j@Auv znaRfa8cV#M-MDkjD(}W8ioT5DHV9@Xt1r%+@}f1;)6;J8FF(w&u~PpK3%0PsCeD&= zQnBreQa^d`yZ>ZQzGdg#z5i6P{B=Duf3sG#D5rJBlJ#aiTlTBG@`ny}wKo<_eIYg( z`Pki_q5auc-<1By=ifdL8SA?cbQz^!WN{cbVij`Cl$c-Alj(2ow=<3KvA~X+>f8Co z{Y@q&%>?BmP(Cd*UzgNqvSsEZpI5PzH~)UdzI^p{_VF8=1MhO|#DQ~ZbBj~>X?czd z7yZcp_-RbnipCSaxccCc&QAfatHk>c)xR|7!;W1Zv&#W7zIEK_%*lJg=HZiHExMg! zEe?%qAVD=d*Cw3z_*Cr1W$zn)=Sh7aHgKAcJ1elF1zpyf^C#2Jne9oTg(~*q#Nocv zcRl@tYjoO-Ygee)bDCG)SUFgJN!81he|{42v8%rR&9&zyrEV%2bJu}9J^SmOx2!yS zhjV55-N93PogAy_t6jhi*F_W*GhncG2{(c5g6YpbXvpwiL^=1 zo!(A?ec0n#(7F@37lZ8`5zvQ|>-7tdtGQYFlfFYZCO1W=b!5-f>f;jx`^@=B4rfqF z#qXclk>8^&bRnuED47-s=7Hjrff&CLM<$Q7+Qo3Ltw@(tXxNeley;1~k`J5+#iXQP zXg;Q5Q-6KkaNwQ#KUt?wzfDM0vHcH9&+qr$cZVUH`)Yi?z#Kk*YquEA+j+4+tl8~k zjg~-eH)r$gZ#^IBMjqbDMoFdC4kN$iY<2i1f%)TAnL87*L-BjUu24W=X`u!)%MOM! zHn$1Mdj;mqZrj^#<3io3+Oa*LuX|&?o&T6x3?UY&P0Qez-yEn99`92-oQ0~d29R$) zZTQXfmBYV{%L)nV8G1J1Xky{?xVxuLR6T0?E>2*nsRrkaZ~Pl}&rWUC;|xw~=aAnp zZJf&{9zcxH&15HT~&WMDy+w$|(t=f+^Y61aFR zTM!)Zbi_syIHtGwC;9|!mL@NoyE8#xjmZ&isgT?dzR+yh!gATmD+bl`Ga44p*c9tC z9sxegNxXAO$)qMP%TAxwiuLMX1M%XunF|_t-Q);9mD^6STFno?42Ju3F))y~MJ z3)eXM3Kp+!d0SwuM|`GGc=wX2Mg*A~ad%V7WXrHB>}hCFi_e2!$5`7O(&5jySI77x zyDvk&1WUSgM{uM#A?%Fr;+Uh;)Tiz2*B+|Lw!1kqOB?=bvSmi{uEvvT9v!D*v(J_% zRQ~nLhUVvDUduhEXYX6S&L4Zr!=2-&zE-hf9>)^&>TYew5gyBFJNm)jv`IaNg|?a9 ztFuIY5D`YLTfT4kr1N7b-j%4cgMY=wum0kRe@@?C>F=_&At^v+g1$GsJ<#{LH8Ig_ z5FpG)ns2mI=QYuA3L5ql$NdOPZ5}HOs;fR@`oCQAt!P^uq78Wg^-WstHKR^;z7uxC*1pv z7bR>D9!qsEPQhm!t1O8P8VW`?>x5k3Z}Q=fyG4iZ*4ziKO91~AjddrBJKe=TU2%Pb zT3`!Xx~-WWzwg@~F`SL{)<#sVLgxI%!S2?D{oofnRlN~@SNq9On?GdKa?F-xu)9r* zZ(aJ0-5%=50U51JE2`=@V*^>lj<~=X7yU*7D zb`H*W^`Cj<-jrK?(s7@P*Fc|eWOZPI8h6DcMl>U zSy(#PoR^!Jre}d)U-eSw?E2^0EvY*e?KHEt{oRdw8)9~spW}MhUn?-A`_~phkFp29 zX;ilf?DkCC0^4L`IrIDKw2K8cp+wiwrk9$QdmUm7>~pE3zvh3?JbLGFZt%LBPhtGp zwD~7YyK+nH!62RIwFmxOYx9099J|??uwSKOCnN5Yji$|it2{RO@ZPULud~L6O_BcY zz$byebN^7WSGL%9Oesj|-}#@x@1_GjPP27o+Qgot-r6{7_W9%BJWF|n4M0hJ7PVNHm2bNu5FKN{-b&ptY3NV*7RMO9dDe! zd%1x_nh1O9tqD$3+lg~0c6`mT!(!x6T@WgS!D2(kdlrgy91n1@&`fuBoCkizVMQ7`Q883+E?#YX2D`4o>==kNucNbk^ST9C9Srp?l( zem_>l*1h=4_?D+<<)y{Woq77tX7*L5>tQLmC(V}q$doq#-^_IU>Up3+S z&#%uuvak^R$hRn=vjNv~NCWn)0OaF|x}pBrLVnKDbeE9g7TEO>%VU0rCuH7vJ|O^p zSCkcF4jkV!xaGxHn*aF;$2?s(&RyEU9S>QIJ)N*TL^^C-y-=9C#uh3*Q>s(XEgo|IbCpr<5xq4MFp?Dr&^w?{0I^+Wgk3aX?DPzkvKJmoam3lVgf^m7ntHr;$ z@B5>w7t%Sl&~?9S@Z;|f9vj)ZZ;qJPUYpGCa!qFMgFb%$>OFJL4DRSppOro}^I9j1 z$@J+qcQ<6&f`ZZZDEI@Dn)H#(__*BzS_8(nT@sAD2hVAJeV-q4IrZ%7V8U9qYt|jE z?1jQDxWA#G^$)`6p5Uyry?0Cv?Z;8hWU1SGJSgx(k%e<7>DfT){DnJ@7Pr10TN`(I zYc6Y=l{{mfDY-D?vA*6%22^a#lSWs5mSZ4Rtl6Q5{@ItjW3I6-yZWHi$pRm&4mRkak8C^|(9RD0>84t>yBqkT6HVF^lFstM!CcACVg0Rz zg1G3h!RKeZv3pFbioG#>;$5>~Shwt}b<)Hb4(8WjWUT&7s@Oj~(*k^qBm{lddy=|) z2lg1ULQb43E}oIm=2HcNXJ>{zNQv`At7oQ9uQMARLjvz)g$+GDvu2*Tlr_J8RH~U{ zeTU4VI4gfznPBd2?E=4#X+7U&&Yn_NtmTVCxolwm=9GCmuf5~FYllldIqPK78_o%A zLB_MUh;OM|bM@@(n6%?vvqP4YRmq2LKz^8wy0bYi83x3kx{ksrl~yG1|OqQ(VZJ(wf}e8w{l-wYBAIx8$-*yus9;xGPnU^x^J!+~)-$ zJSF*qj*~HFM@WrKC%$~o?tzpOkw07x=T3w?)me4tgScA5rw!kfC$NUS247mM=qPMs z?IQFoRraPMVXnBIZws(CCa`eFfXd|Ceb#0ar-+_hrgj(tPy8y9W1rknbmki~Q#~7K zYvkHX`cfuuxq$P$wTBKQ7U-v0^Lo3Dyc6;|-_5zCwpK$uJ7flaxl`+YlUkVFp?@d- zl<8RBjU?XO0hXMnC*nm(9(FZ`)t&*qS2o_Jw+8Xo|UP>vIhzuilwHIuZQ) za_xpi;TV<2P*+pHe2mI|j>YOZ`d3b;Khd9hEYP&OD(zE^h zHmbPSyM`|GG&c3_*0ZeHsUOVAJml3qvF47*Miu)$BQD+Mc&gy3`?kH;g8g^xnZdgF z0J2J3v`PJXC-Xmd`7P%>ZB}c7XKbbjc$q$NH?pei8w1}C`=^55(l*7X-uv=*^F}f0 zHjyrD{BNnVw8`ezx5X4s4+?Bk)*AbI z+@Q2L)r2dk08eY_Q)0)1cYXKHk&KcRPBw8*yZwl6V6kn@a6)&7nPoX@=1eKdTrm8I zBee+o4JppwmUg|T=<(35`I-_Dm7PjCIr#YZ#^dUkSOLuygy zv-aDcT)bcy@+3WhS?5mb(DZo>9PPh`k9jp-pXN|!^Wg!LTF?IE{7;;AY?tj%;iZr6 z27k|M9vtjDcdTncp5AhxC6|SL`|YOWw9cN8W$Lc;dNyxM=LfuTk}a!#e@aFl$IPan z$>!^vWE~sS)?xqbIMR{I^Mk68UUgy@;`ZC1TkZ1r z2H^u8FZ$QF$L;bAI0UtB9rh2uoSU2duiLZ#o|HEbaCDyG&vJs=dM|KYPg zbUiR?r;~lSd!T<`d(NX8pUP+N*0cJfe{{zg>@}HRJ+@=EIhXnTZQ*UaY4DqDkD+Iu zi^Zg98rgQ&zs%p8G%MRc|K4JL&oR!d(m9-hThA8U<4AS(p9$p89LNYl-dOun%`YFj z>CRF2pT1$#emx7%FG|`Qqq|03nw_-!eH9z9Jat#Xw5)LxKk1q|a8AWmTzjM2nRDD8 zIl+BDVKv}q2uZ5YWDm|ShjO7Gh$g+q`5%9_TUWG$`SdJtaOsqRwo6OKs9yc*#1cJg ztmopA+l%6}SC2~m!;5;B;~n4IFn&=@B(gQg!#@1^05`zdCcEd{T>9j!Smbl*swL1#_Ev^eKwt2Jz*spgPTFwo67v`%vhb?;O$Gg8C_HKS~E}!&ZMUFlfF0?G$ zn>kZ`=RH|wd!~xL@cn1i2QL5az1Ug0mWh{hS>{yVUR9Ru<=zh#F{xc(ZE34Tee&c$ zXp%Hg)T(EXciPN1JT`$F{gsri$<(u=w84|b&lLVJ>X}^UvKAG*t={!@2ND;jKTzR5 z1^uXPX6VL?L(-V6UT@;lz@L})#jdN#-Nbj#EQvVU^~{`G8xybz*$ejg6IbK&r$tXK z=#t|7i5ITko%0Xy56e{5!-h6bV+Z51fj@(~MZ9|Z58ZVW*~LE4OZaNlC$U*4#WA-& zJLMhN$0xZ|8{Xk|otDo0?)q8vdiKRZQ|oPony%v8gBi~?3QSWXimk212BYySk@DHZ zJ;{sPKYRDNif->a-&Mk1AzCEP?g@2={B?VA-_6a__8&;g%-7sBD?OMBdxa^>TVhYj zvLu_X7kwc;>u6t~6}0)+oS5tTN5>`DuS1C;tLy2f-S^#<_yO|yjeA7%As^c1A84A8 z>jb>#eEHC%qr>q>KAP-Kd18`^h4r5p0_84aR?xHQso7Z14LAm#XzS_A8uuQJ@8gK$ ziC=Hu@o`3BaBK?f&1!4Wv#VA=bjk9tr{C1#VmStXj?{48KK+d2kEOR`zhdwpVNa|< zOtXt=T`IOT|BaTx%f~)_1zzeJ(1%u7VpA9PkKu~0=N#wq*qbTOYzAH5$#OssI)@>Hdu-UZ zcpg8f$-sUVsd7S#2khDFvwhtrJlE;`37cZQX$wYw5idxtG*S6Z&MKyTEux|6$eB$ID_TtCo@6xl(>qC#+5Xo%gzcFR5f&6;vDb0D0)~%Y;dPsW3s=_rN zd5O;aj@F3j(@+12`zd`BZ@`VwNqk{f@3WdgGgH^~<_zyXyC}%98`KUnOWvr?oT+2k zcXSUFN|=8}(Zxq67TGgv)ka$o_8&2HVVH9i5A{wpdrWVt*qxdbxiMJkY4>?THtm6b zR(|&Pf5SgC7MIG+vedav>gvkMzan4Kj@%aWf?pQZ-rKilU#|=KQjY0=x$5(ib*4TO zhy4x?S~Znq6^^-R&$>+etpmZgk9+?Wc`BtMY|^}d@pss=4UUU;$9yie;bH*-Q=ia~``YnvQ4=o_B)$UxAP;m|dd+~8Q~WXXkH=j~ZDY)0MdXFZ~Y zUFbiV@u%7A^G~e!LA4F2{ZvVsDY+NgF|h>gPJ}Sd-m2zaXh#9sTJ3X(;V2J)Sny(r8yV& z2V#d#3N}646cfMqW>s>dzbJXkM4Ooz9EGW{Ynr7j`xMTI_^PKLeDTZLzw~`{(5{Mb zdUkDn?m^>Flgrn>-0zB*u{+{F)Z5p`2zHUvs&Id2Nm1s)NB;Qj+O7L`ee=4W;nI_o z#Z%kEdQoQ>7_DN51Mk)Tt2%P*%yUn>@2cR~9QTEg#A`L)uJjaKl*|6Mz49*wW&b|+ z<=;;X_YZ;JbWUvHcyrxd_xEYTpf8(k-te`Ya75T)ThX>h#qwNFUg`>%JgtZR{@vSm z;J#AdfWs#79)DYCd8$W+d|eNyZfZT)`ElWcH|JL%zscV@ck=B?J@&$y2ZA>2AC9P) zg9{f1yQQ~IVtwDX-2c@5a|#Ah{Ct-t2z&gNG+Y0Y?0UYa&0P1PnKh}5twPa^MUz?9 z@o%#9%;XzB(zCG9!M9Cn^T)W@jD5&#mX%fXP@rg#?CCsz?URYPa@lG6SJh{~*E5x7 zK=W;6vDTt87GCYZ{hGqSjMNWipFTVAQu>r`6>Is|X9v^oDl2=k^tNfL|3llG$2E24 zeZ$uYpjAlDIZ4n~AtxK!sgf0RrWyhv!D$si0iCIP6`XEXt3s!9SKCf!rprwCd+O4T zwaZum>sqZW3aDKqKmyoKB?(DDJEJ5yNpJ;{=No!I@8@~nf8M{Iw6z9Ka;|e-zw3AX z*6$bgBPH5VHB-)xhV1S?Y+Hu)D6iY~+XdCGJHqSvYRpIOid}x?rKl!5P%IbXyooht z8T<~0NE$j{KZN;~wTy}Qz;sEOvFzf-V(bUe_e|#4Ur_x7d;M7fdG#T20pn@txZ`>- zIo$&OP}SH=7i(Rw-6?GO&JKO*FBYCLjNFL!o9D07XC&|ehkN7TuTH zS6Q}>W%qw@`NtpsCLp^~a#P}Qxs8d?BXPHYe1TA{p6R*#?XSv}hH~J|{r#_YGh^09 z;`*%oLIIg^^v7!pb8EJhFCPlhOT;8?@GMyu?UCdKU-WmQpZ#CqABLN5{qp%geB8<$ zn}PF5b9gjn)xZ4uS9th5B}rSf>9HeUN*u=zB=f~SoJYyOZ4oqI(RTfPQ1;|61jN%9 zP&50R4oMA4ML|3Ap^wdxjt1_!Ba{2GEAV{=){?t^rlCO8LB>c8@;5zWB4xaLw7H@v zCl~h0tEVsTtu0`;+P1%X@L7z%YIn!=v+k1Q@&(H~Qt^A;+@(5ARn5hQkk&XyX&|PB z553#d8{ND@tQQxgiHXpuU6om&G`GE%*X8X-{O$4W%d<}GZTRYuf}4$Y=%XE84L6z; zKffe9pb&yDaK7mM8_r(E?;jY*U&SQ!xqWDPxjeaYE6)p{Pdme}ylVejO+|BD8w*ke zB+om*A1HZ)_67k3-^{pI*TJ3?EeRnn}=jX{LIF^ryC!sL_YV&oZRIT z%+6p_8fTS4f3RdnjXX9s>ML5V&li)7WAS0OKse@(X6($!hdj8N!<2H_wmtt%qkM{g=;;QK<4xGCnCb|*0*9`xB_px|> zlsAy#>tvJ{`nYd)&NAdJMt+}VJzw;VHk_%b4Dqky`|B^-RLKgp)EhhF@)*eZ?j&#U z{7Fi=`0z<(0QR;|Y|(G9d}=pU%z1P@{Pj>)7DcbyXw9i?kJU=ZOJ%<_%`e^=kG=d; z??2!_7OM(Bcui{Hv~qzyN2w%&`5H>ZoiO|@Cwstm-a_&ppjIy&xxt_F|JVG#tU|rQ z(qli`E3JW08rSN1GGWii(ym5j%ut&;J3Nf@pA!4x$3i=*REE0RgYaK+^pyuLK4Lr7 zam}5q5ReVa-MpTZNtm_O-_(H*JWa`IyJgu9QoK4OT_+~{BO`+8qF&uivF2S=RS=gw zxT0o5*`e!S)mI#=Q<4)UB@g#ge&2~?9sn-}asWi8`3s8!`9A6gW8(YkS8WiemMk9# zc*72?-|L^(f7e{+s@_;r0seCdDSAh-Xoc$=O;`81V)-lq z8y$7qhng=pFQ}=8{>hn>yP>Fj`!{`O0$RoZ`|`QzZb$cQQCdse5h?J08SxvU?t+Zo zX&co`+Wc63=ZY9igHZQdo#u&SdZ4IFFTt3XG!Q%uk0Pvf6y$D0AFv| zeGSMi7Mu^*ZeC!FVxN*>N!X&|$Uvo)dbsr{#X zU7vLOlE|69m}lQtdzR!{7i|CY9^hY|5{sXrV;au|hWYK7fQ)+LD7{drFZq1?+iNYD zuhPCRhr7>-pB&4Mgn>6``tQNK>6e}t`i@H4uzo)GNm-oF_E;8iS4y*i_cS4Vp;+F1 z-hJ-rzrA}C_TR&)vG29{w7~hYxVgtdvbC{C+YO7x#MyFDB3?@Q7|_Q>DQ~n}LEnRV zp*=;wrY|%UUToi^q`)T+N0=Y*bAsZ9FZ=-g)mT|nv7pk{12tWdWrV-v39YeKZ7eFP zJUP@PKra*WqC;+=%0?EeoDd|tM7Z*V!?c?)JyA{>gFxAwKX<2 z)4;P`nb9p+@_@(>D@;pU$WxCk%6`i>-QTOBPZdLcZve>}MMKW6P*?Rb8}^slU}@ZQ zu)gl%!{S27d)3Cuf1W;ga9MT5fySn00SN@IXDFCQi>k_phWhb&r9xg|+b|)bARCue z!T;8JbwCG8;RGRBJRCC+QCi8bwUD6M`c)e1h3NXNVO}cGu=C8f$x6vgOqwx z{_oB1Xl5p!$Frb%Sx(uP&Gma49>U-Ag(``DS$Qx9{low z7i4!wu%DJ!rttAi70U`G>QRr!0)N;fe5Ow;4uLd>d4BK9+uwqB*|>_|7Y* zhr??hh_AT|yh7ABk&p=Jk2`CsHlA#UKff2u*-hIkmz{5RwLc90H7Z&fysEf%F3A@% z&u#_3=%QJt_WzWbY@$=Y3HPF!E6Nu8vvsq7$C}1i=yy-1oY`D{i^%mou2h2kUd9Pd z@7a6W8#0PJ)1c4f^jg!4w|1s3T|7Gq`Q|Q37E@2oelT`var0jW2Ap^PE}P? z6NP?zW%_HcI6N00mJElqg)d8hL#>%0)6dI@&Gh!NUnz<5vv*RujL%Q|D23tNDX=%? zi$se?jvoIyJkY~-3CNy{b<>F4)yOD9fx3%0-vHLOtdb}V24fCSW54&g+$#$QLPzRq z>t^RD3EfK~BF5X3

n4--18)eNZJlJ9dI9mW2o4kCgn1{6)ozS8%HrhN1 z@cis9ZMP)tL)ZSj_!yrffpX%GK~&@ zoRXK_O_}>qkI&?Nn5$XjJtyY=@|iRP{u1ZyVZCBx^y!mP#8W}e&AbRvM$VU+0C|xZ zHg2q}YpD3%?rO>u8;Hg9&QEfYj_(ocwF$oG5=JhcPN&i7eB3{gfcaR;vq^YoD(90$7_AKJ33w4^-Lj3#o7&qO z>)H)u>>Ih_rK^tkXMzDk$!a6n@ocgTgV6lu0oUruGCbe0VVM(y=fd3gA|fWK6gNh= z)r4NBqMc9{b-Tk@@_- z$WDk6rNQ|FzT^FB#Plf;Rs-M99rk$M*R2{F;6_F|WtcxNzxg3kWI#Y2zvA#4kiY7s zia$E2L#Ei4q8YqAfEy#mSuutv0xyRg+6L5v;x6zp>eRI z1DHn^G5|nylcppCz zU76w~MHQG|{bb3 z)1KyE^eVJz+`qh0=-bNgg8&oZ`#r&6brtY{^JSE>B`L@s5a%-zJ8f!&)jE`|BjxFXiTQ19ZVy%dmY_QRjX{+ayN585%(t2dCRH7`ww9RT2#QwsdQM7##zpkTCWud1+B ze2MjGvRYjouAryTT3PXR64`Yx(x!@6cHQP%`&>rc&l^!>(+h#)qX6hI;=?p2htM(L zaoCJ%A;O^5GL@s{||@C}vPAjZxkJ(AKVluYP`T&vU3B z1%DkKKa1UtmlEd{^bRNI6)yk~Toeah{Q%*76nTu$TP8C!>Y){HU5$bKGw~8=Etp?A z{Nh9~zyc5s9MIYp)al|#kY*cHDW!EjbxRW+?EsFIDA_?AN$cj{wZMJ8Gd?NYt&E6)(9n} zl~1QKUf^}$dcI8}3E@F=Oahc;mf_K=aryna-30`QiYutu$9ZFgJK!LnEgS{3H8Ftv!I7jaKd;~-x+F~+AGXtW9Y0suUjEEty)cs^i= zgYeN6gCfVZXN6VbQ}HX*VG^Pl*F0)O5i8s`FwzW!}_g4KN8?6Z6J^ZH^6fMr5c~FS!i$?Z;%q=`NCA9K~C|z zgox{8iT;8FTO&9g(KWE90`eaIy_e-^UpSH=nSs7>L%q&|oTEpp&T+@d6mGBODy9o zrGXT1l4oP5ZYP>9$7p8#al|UCIc4NB5ocC7vfYYMJ2GL!T*Qv zNxUq{kAPSp?4`&fzzd|m1b*e46S!0Hw^R5>cp$pLzs5|GG+sV_xyVAe7UYH#2%0PD z{VDJl2)}MJiZ;NNf`g8NJhG^sSK3|UaOhXD9wY@Df=L3LC^L!ZNoh8qv;et}zC$Gg z*EfL|lwcnG8LtQa62`&%Nhne<3K($<5$o)39W18X-EP;>0{UIOI0c@i57@NKS*EVFWA}%%)36=6Co#mBodtF{sich@e>w&g24t` zL4zd0&q2SVD-Oaij`vYjRRn&cmu|+!`gi`~$PHkQnQ?z_LN&+})JlY`Mf^(j6qO`q zlf-7`2P5M7s9$6!kuS-TkV@vG@D{UnN&GDI)%fO5P;i!2q$?mlT2RBt zMGAuZGt?I7r?V5tB5=*03?EHl6Z;7ymrOnb^yWmbj{)nqL>Ebi>Qxz^G5oiJJVknW zKLu*41XDHYm7$Nuxsns}m5}sTUilpT7jh-&h|%GsBHA}5ZoUsPbOAImy`+%5Iki8! zc@j9TOyoSt$NY6CaN84PrU~R40TH6_1iB)jHqbW;ymF;Npgej5X6W~d#mFb7KwUXW zK5|~0#Z(6BHvzQ^NLuQVV83?Mp(M#&!^okWqB#&InGoNCH8ms(?rVGMwl7S6iTB4s zbgUc0e<=9;WXLb>pUFA{v1@kudbX;H9f;riTRzPR|$=IG-Ur z3Xt6B@Q;&$e?xH^E<(?z4&Zou7W?G@h~;}I4M?|&Ie5Xat28A1tUtlhO>=nP6XpmK zK=^-zdl>ceDLCE}joAXsJjR2gDS`d6dpFwHe9(_)yUnTIaDJwO^-Ef!@Q-gB%Jh6A+1rMZ7FcPMy8cY9Rh|rP-}1;iFNlFzQW8^zY!T#Fa2No~gn5K2=py z1C4e(TlJTC*&CB>nx?y&`$fD+CtC!(zsI_q4(NXvi5we*H)d2BjL`4$xHc@OpVf~I zXc(Hakn=~S@B7}F^8VXX!kV3_7GfIkisuiMrmf_>(F1t}qy;UF<}W6Ec~naL@~#5% zUtuOh4V=x&3}<^I=b>NMfSlkXFW9f-WCZpwaNN0HcXg_c~r9fAP{Uc|$c&;W5{?t21v2rj)K@u_Yo(KxW!UK<#W>Dw{GgcL;#el${vTg?I zk&IO}2Y}pI3&i{mJMK66`q?p;Xvp!4KOK2Tf5$+6d3N&3uReBoKIVmQ`e#GTMw5jt zI`3u*xg>|fLc}}oE}9?Se248y)+FqwU+tLmk}Z|<3m)E?+Ga&QX3_gNF~u@Q6!vTW zDIh22NfeffDws3h3w23K*czIiMF+xf)zs#SG$#}JakP8Oy-=b_bQ%ExG=vA{&zB?i3sHEPpi$t z6QatSB ze)nu+{j~6#!2iEze7-W4T#*%|Z}~w?^7C6;*YaywUkfi^hkC#{D&;ewFKVxyY3u{- zGWbA7OPbJrwRRTsPStg_9wjfdmn!L$@)VOFGlDNrEnc^*a>mln8tN{0x==qZJkprj zlE$EmI8Ne_hl#1er1VrlYI1VsWaP8`ehx)+E54*mC>7G6-ZvhvMQ3rGL07XK`VE24 z=5tJ1NUrJJ+IIqca`jmqTkrNrHf0OWNlVc`P@op;RU`dC0B>#|HIT-4+VZY3ona^c z(BG+t4deuz-N#Rgi!UwyUiBR6Z()v76rGeNMRUU2IL~P6b`li}(}L8g^zZlMe0G;! zH^jQdtKG@oEvb6amSonnly^wYd0v$qr~CRT`ZAmJa>pp`?QBB4W6`v;J)!rtzZv?= z!)eD7`bHq2Cm>IrIYq$SJFx)gsflnJ@}IphYjk^z8 z875(boDd>*~u*2Y?U<9N#=@?rGEv{ZT`&$RG5 z<|6@l(0Du*4duO0RZUrv{)`y>n_gH$VoD&Ic+X7K8wwhWexsOyl0}ccl+?5IYV{OORs!gU<(c*y{C5w2}`(9M3y1&9iB;_Sc@(wb8Uz$GE*A zAo(rG!}v;B=u_p&wL*z>m4Z>2sGp7-$a1YhA$~@;0`LGX4bS2%%)L=?foh*IXK>pJ6GJn|L zujvSGVhBG7|=?6LC(|4<;>@m+JMb>ZUs9Z?~|2ljY=|oe98n9T6M> z|5Xp^aU`98Yl@%laSDv&f#>gN>2EChl6P-9h))S-JaAoku?xPYnMbf2*ex}Ppsi0Z zR{rUgU4`f;-Z^RCEk8}S3~ss81Cb?1k}nXAS>zBS?qwlx7We~Xg;~U-&I{{HzV}ND zvLtm%`Kgxtp@y$wIFlyx$16AJg&iR83HoB)T<7k-5P)ax+AJHdVf84J2C z@SEcY@FkCcAb17s2OkFueujg!z()YiE$s7f9(_KctGUNH8FgNTcr)zRUWVcx1E0hZ z*sDHDj-K*+5eSf@_?C#zUSZ>FVZu5EXMqkunwsB@-PievHzPLLa}4o#7s$xQIg5Zk z5ykjHT{v=7b@m2O(@!P%8evXrfRr7&!kfla)$01`%%s)*nB3OAdeBVli;r>c-Tf3y;K46 z(c^fu7hQ)M!5r5Bev%zwayzc2`P=j`|FjgL{}KKL5h&?y!6`#MNy0{hlqADNA`>Yo zNlp+xi#6LF;L3wM3^hi zA+9&h&v7t;DB;?@`#(}}%h2RanCOYPP7(vh1)-Vt@!%E$X64{R@Jpzhz>ZY^7Z^Ch z>99@|Vy0BgN+f!*K0`|6n_0zecC(b_5{Zfbx&QyrQ@I;$q&43aKhAUrG$BoA1bF6W z+#epXzYK`~*qeUWd%zn3^NR~x1PIMI{1f;-2=64|Rak%y7{KR&F9ko#O$!j=pJWi- z1e`mgi&M|C+$s7+De{#RWnn2bdz6CPjQ(&d2n>OCfDj!*j_F9I9gcCg`KRgl7#B|g zz6-*TF87DKcke=)9r%@i(MUje#>O}3;sjoe5-v1efRh+UBZ6Wz8W9pwDh*a^I!RJM zvrT1>>aihNeHMY=Mx7j>1ZJ9}9>e}Cc;OZ(4!j|-M#p3EbRvx335CP)ofLB$K70b- zHz5d!7LEs0ld&yP9E#AY+YMkA;JGAj1Trp`#UBYi2poX&1iY@|fABjKQOLxrsv2}* z<9OB8;9r4s0HC6(dK_Oq0fl`KaM=i;B=DmVXmgLFSHc0p^Fwt)gTh!%ypjpd>hV`% z{Rsm+7uZILkthX;AK?CqL~si)Sav^;5BmX$hVd{NW+;I-g1;yJ2Q%pZ!D~*;AYQlaGpyrz{3u>ze)8__`@EVHSoP{(Tr z`(km%l1wT$TJhdff3Q79m_od-wyDA)cipQ8hj7DmqRJf$b)^OW)3;JDRVrtYEZJXv zJ(P4-@ZfWW4-NiT5jmbNx>rD*=L6@eOk-~)6NRO+%Bm#&O?wWyVoVm{ha*R+g_(Xw zm>Dd{wt>dBySG1ybNY|=-qI|#k)dHhI7H?BA;5Ph4dmsLQPHq%inToNOW)ZuM*=ot z9=$?!$0AfX6p^KPZN%StgNmG$%u{$6#|=R`S(j`r%e~M)GSulh9gt*^f*8`&eImo_4|WC73MT(7kFtdAaW(wfCH$gNdQ!Mf}}-5ZbnIq%v{ z8yWWM=TYJ4gQk!q@b1^?L@X^V*ig1z=Ds5eb_SA3gDVhc4CNI$C{6Y>0sr0wATNs* z?~&93o?7Cm6@?X;MLyI~mrmT6Heyv7drbUT;tQ8y z84~ZC8eCWiLuFO27=nQJyBo$5OIx!H3)@jd=-AfzW35)FVrV6*dXh==o?RC)qNUXv zbMb}Aq_b{(B@LC!(3us=C8BGiqhr2J^4#PsC2`t_hBnKeOv?)^y0nCA10S(xI;+T% z=PWh$hXURiWGE81TwoVnx^iXGk-toVe0S_==!+4I*rrBzGU@aL)+j8X0m%(tKP;L- z8c)TI1*`V253l|9#R*T~_pT2)y-Jmzv-A?z@3E0{el2%i(Vw5^Wgl=#lEGKkwELo) zmK!TK<|@NB@=waqd(){9r7!Y!42NvwhuPUrDp8F#AUNK6iL#O2m}X&Cre<+mOx@@6 zr<1PQS3kGvjRi#uYR)>^(@E@d!@+V%j%8a}WqU`v4gOz$c*duGMzMn&D^jTmMH_^I9APbX+256s4R%tr*Xr2^4WTV~0< zB7YVsIdrMND9Rq4DA50l>C7UflVrjze(6VZ#jH!1X(Owr$Ax7)_}j&8UA_x8qU`)M zlCyfYXWe{%r_W#`3ulpxTLRIs@1>F3&MWEUGV1>=+qPGutnrI_j5jkombqZIs7)gH z(#`PTw-M2yl!#mwWd+wG#D?+n3+Fz-W^xz4|93L%tuGpW{JhFm`Akkt&B5B&v&fg4 zG%{1JGYyN|K0HTdk$3yGzb_+#P2BMN(?lZJYeCoL#*9hXL}gH@m?HAcywM$&eG`8# zy7bwk3+FK2RPFQp>M=&HeTm|AStN8c`h3rTcr|B!+tDq?dVcraEQ3+KA*bSk(=D`- zw7dtd{`X*5&M58(OlBJqc?Qy*er8SfllRly0OYBuS(-aTX)7xf)^&8Ilg7SDhTNRB zm6chZ=0hzu@aHW)8DZi|ZhyCA1)WZ=eS4olm1MEzo^o~tg4qA<_cew>OZ6LbmI|nm zbkf(j(rTMfUbZ#pK4kw#I{EQn^3+Pj*6Qv3XPP^k(n-^+2A$DR{mg>ES%<@(ORj(W zMV}{6J7}~%W3PC&9{ZtmGOMCcnU%Z!J^P`_4b2~9 zkp;1v<{#ANT^|n|ZE^HV5e_XZE3YgHc{)7p2S=sEtdv;FD{eXhW1f!s2d!WMr#Omw z8!f!T;Q1Ui(T2N|<%^s7xO^u7E)(8HcC@mX4LQ*xhXl3}Jy3Vg7lvMcWPCeS0ywiaf=Hrr|XTX5wx zPsgzuNilw3v2j~4T+V+a;B_%No}-O*t4T5? zxQ3)@h?_mNxLaW*5k-Fp{l>lg^MY(zYbAckKZc21=#wFB;S=L|#CK^DqB2OjZ&0gK z6Mx%uS={I3)+90hsKiRNE+Q}qozAI5o2d(1Nq3Nyk(Q|G>k~UgS6KAIejcY$qLF!ajOab;$c-0ej)HVzuREwRmgg_nYHMge1bsE@ z;Zntu9Z5Z9-yWB&dN7Lwd^4j_vvN)RvTXm~k7p6l)B4q|Nk1vNMiq}wdOM3y>z%53 zJ57VP$&vR5gz4lLIqOyH7HmAx(0;d}@l`9iF%YKpnUPs^cTbpKlLgnZr=TZm-@;!N zyBsYGvQQ$=T@^KnS)xZ5(-Mv?2L8i z;w<1cHOlnQP0O6WVIBwhv|49`f~P)$;uEl#uAocAPAy);{18yPwiq3`Q|4a!W~Vc~(W~@@-!q zs&7Bka8OECS6A6KZeQMbXx9h3Ub|OKjC4fAN}H(kW3)!Xu?q`22ti1N*zBwt!2i*V;gd|Pf(f1JA3 z^i`+@`^Vlfy>O|e&{o;zZXQeUE5G`W_R&T1$~oJzau8qW{LoVy8W9&*EjfxFhZ?Yc zc|3_yf)PGU39%oRoRajfW=sCo#_9qht&|uGm#!@@y4uk>)KPy?n?rt_JSr`lSv1E; zWi*V|<&ds^v9e-Kj@%z}O>@P-S8U8F?Cf+o-S%09OViav$tQJ0V(h4oxrqo+4w+G1 zR5T|)?a=ElG`cgP&+^_Zmg&=k8QbIJ;fNuN_`e#Y+hl>QO4HkO^JPY&lk>j*u3*16 ztem=ishaeAyFOnQ zdPdabhA*nf*S3pze^6F3K}xD@+qPAIymwE-o?Uk;su17O5P>I56lpxU!N??JX-@f# zm`4cjTd7d!t-zyrDa5XE`{D~kah+F9Bp;6=#OEg47So7qSjn*<&7yR0D>F6_MJfXN zgzxs!rI5p@KrtEdFO;c;N-55#)KaO8l$<`gch9ej%9FP*udZu6js5Z6Yrm`BKDWB2 zrmp^#zp06+e@luYHbbuX%6IHI>YEOMxmIPg+31)DrLt=Be2VYWTN3@hxX?L1wnIuP z%ctblR5#b|adw=^P}|7YP6MZWC54>m=Qn>JkrHXyhHYL?^O2_JYZAQ#`hXMn2|`I} zjy1ZxcnRzcn^G+p2%NclRuos|Kp(v-5q%R2?YyCVdwqO3i=1AR8olT2=DC_bWl3%} zqO_Rh%CCb+vj-Zva zbu-Uv9c@VhW=Kv`D%AVHx%G*6!lOeiq?*LG>N!R%-ak#0Ju31Pl5`O63dk{z*Nuf~ zDfC+$bS;6Q6H>Sw47@i_K(L9T7-vhCkA z){T7kTH^xjZ$_)Txi8<%gZt~nwh9y=m;85X0e+*%RzsEMrKJGy095HT2H zcfDaX37u^#ikhF$$zPfs=P;hv8!9U14CGoJ_Ku+2E+rW`R<+eu*x|N&Y8#88-;zX9 z=9nnN3gci0l@d$7ZCP)y3+L2beSz3YbQ&tv=l`rHTo5L4eGb`TTPvnKO@BS>^6IS6 z_aDn-dDAmxQ@Ds$AaNny*Li6~xcTY$dke&hU9flPZdq_CYKO$yW7#2mpHRk6;F>Wqy>#Sr60y;_BHlbfJ=F>vsD^(@d4Z|KcJ=C14SK7$2 z6!Y=>M1TLu<=YlboR12vLnzW_W;$l_0s&zcNi~aDMl9{h9Zu1%3%?gOvK8IzWi)PO}uEUn|F2<__39o9r5d zD{N|#^zw)^&*hLUAk^B`YgEJ)_W5P8QZB*^1U@0^@es&I7?cGLnJ2M+-7VoAHu{iDYS`YEqICJ*OT_`6|K#=5#$y#0`ZTs(xy(AFbWfSGi0| ziU9R{_~0x5*tP4)s_Kn}h@UuP=)@+n8;nMS_^|wg0oL1kbX!Ezb#t%{=VAM3>l2=Xr6nZ!Aa@hi6(bRNWV z$cL|7OI2;0S)-P8Hhy~u_WH!i>Sd>E?HB*L`_YZaT5H0Qel!@#L~G=6?-e5{U$)+= zkDvKqUvogFw-%!QH5}&Y5;SxJ=o#^HSG)b-o-*6ACDq&8_S8bZef~{Gp)I{EXA1a+ zn{wd)|Ks{})1%wwY?oXL)l11Vkg1nf&KL^K?r6TqLLT^Hzr@Wud*jY^T4E(}Wu9Xe zYOUj8sn1WTQEJiQV_JOibzyy`INwS-y=-2P(Im&am7L-pD>3subjYfa6`RO@hPRTW zwD6i9s^vyFdiR{6%SOImG}UZ+?1JiZKl}Gsi<&H9DPOYg9WF*?&dbL6@SCnBI^P}P z;6LfHZEcZ+KO=aD50|8Rcd!vJ?8^vyo#sNka|UD9h+!{G-IyO4J^f>?t23pn$Z916 zIt0hk<75?KuSc}7Z-U)R_yOb#KSCo(nw5A|;d(Lb6GrjVEjOtH_$` zS_Hms2z*Hb!F^sUiF)}^kvFcv%c-Ntv5Dd( z(C>vamQ_z_|L#uXtJf-*BY(++6^Yt&paLNOswU#3##q;dPUq$c=DejQHTmPLF=n*0 zbfW2anYsHe8q)B~DG+GL9}Lz5D#cA4k6EX`i2ACcbvWO|>7Dcm3-gYIUxy(!I* zBemH+i($RQB3^crJEqFh-pf9zMsPY?#h6#{sy?6pIQF}^wqPUF5UB5k$+^F4UIFL1Hp7lua-s1cWvSZA#6!)YpUlC|ts2=+JY z;I&r-@nm1`h+j8PO~$w>j5A56nH)2Fh{j4(-tZbxZ<-AFkF*-{J|ax1%(1BlTlS6B zAHn_1BS6o&?v_b!-^8-$0*pG5`=;y{pgFd;u za!w%RZva2D{fI}%DRc5A0&lSXxDj}tibo&SsYriEL)WMb`c=U8QQ$NPt5`}4{SfUN z3%wU}TBAve4b&G_vbf~{$t=@y3n_6uh4GvhQtKkz6Y(2w@R5P}65=X0SdHb@fsT%| zP3^NK3BFD12G~IE=JpI$1^YlLdC9|YX=6KXM8XCu;f_9tXu(*SUgWsJyo>Y4!KP(C zpPPLlrJ(;#rj@L=_iReh_N(dh*P@&5LjFpo3O}BrJ(foANDY}W-?p1I+jHyd{vTscdIbo&?N1uw6||ebsoQ|AF~pdcvY>M+mpYjt2KUrioy zJI%Si9u@1o#?6QPiL_IlZ7M20iCxj}RH#W&_(ZNgbWN5y>d4E5K0KE$IX;p%&+X-A zgnJAL{dtGejET6Z7aU0f*e4?bb0%#{(S)Lr@B^@~M%yp;6)>IUn1|=KdmEfC63`WhdGUTEL<|c#w1dLU`%u1;s-*DMV(uf zg!sV6y`yKhsx;i!b3b%{l0|;X_(kbWR`$K*&CK|5C-#r=9BW!^I>Tmn6WW6J)M9Uv zJ1;(%DJl~xvdHgFmVedi{dC1WHQw`UBQ~P`oL&5G@|KKxv0o6JwXL^$;%WA#yZS7$c;E8Hj{D|6asBA#tZe+g%+hMhv+ot^eNt=?d$*ot&Z7xuGv0?X^Pux=LyyTdue z;pw)ldo*7xcYV?B8BOG)!`(E)ofVsbd5roKYwmiLICLiU@=%O5Ud8-gx9@%JS2^WN zDz^95H$xt2dc8)Zm76c-X?!8nXGmDNhN0ZPFdg#2e`F&PUG(2P9gLYKh9fwyA}1a& znKLq1Cv!9zMEt0|-QM)wv*pVwH!S-n>{;T7$2YL~Pp;0Z3&qdDKcuA`$1(D0U@y;> z!JkzbD{HDo+b1{GPAV%yd@OgtH%%A&+G~Hiv7&Mt;#F_*te1~(ljSX9s6N>5A`;@I za!wAvKFni2cfDfFN~zemAYi{{$N4?2(3Z8K=xRe_-`=L*R44qY>gxY)dJQDyc@@=L zVIRxyjD^}9Ez2oJmsB7nL#~ddgI|er(>GL2?dX6#aJeDXR+O`$>hB*kHNl^2FfFgy zT2<3pyXW=SUW2@g!;0jAfAifzS>{yAuO=z_chY>E)2meEi3Xxl(h~}_be_pq84-c$ z@4)k;oHm6Gvn)M5M2Qe@{=?MyZ%cna~Xf#`kD zWp>L}Ohq@iAct(&L=AL)G14CL=C040f%r6&AzDKX$l2A+5t$GAS(Hv|SIP!PQ9(j; zS)@qQKSh3I*1T1&;8qFZ5oR)cIBoVPB3Z?nyg&59pRB!9Fe~6+F8tUg@=nVo)sJ?( zdAs(<*(DohK9llgI{EJOjxB{t7u4*nyVmgG9xIuW?N!iiCXqboU+YcS7u3Kp5g*PJ z8MK6ns>wixCOgy$Me5UYzD&q}EZAWx$Y<5+0lORVx!IZPnYfnK-g)xzj!y^G)|_j?dDI)yJ}S$T`LEnVnFH8AtFMN8Rl2ujLf_}xw7lYMQ%7_A zzqXabA6{M|{i5!4UG0OmxmDY1tYjZYe8+rK{j6+IejCrPaXYm$@lbV1O3AXh+bUCFo&c#H$08C_ZqSc z<=eYE&*l)ndv8mJPr0n9P(`kPr6%T^NsP|?2JiJ{3d?bx=(1Ij+t_|_Z93ymg8x3T zDmN)sA82gRF4?wPN;*9D#u2M6t9--vb@lMS>gw~#tF~>ZK3i{Z`24h$-0F_7?Awlk zNQkTr!~a@Zk?-&FhTJX%dJI)r7`}K*6K@V`|VAQtB`NfE!6w&o30J5 zdSI@$4f~b6L!pM>Td3vDtTu~$#jd#aiE1!~Q|bSz^JJ0Fobicz=Njh3FEp}InlIW3P~+e^o?iw7?Z<`nNwMf{=kwHLx@f0P zBWELBv2Jf1{zbAvlq~pTsAnMP&DG-hNRn?T%*5iGS*?juSjlAyd5ruh!?V;2ENvw{ zaqnD_Pe2_*O_*0^C50w>6-V6->q6XW{a8A&l;<9A51eWG@tI2XGh1zBUu(|=Ut1BE zDc5KITaZPxN9X_HM!rlL?sWR!Ii61TmK&ApDkqM#2d{moSLKpp>N4AoNMz*n{gY-b zw%G{#%j}f_=BclH;@>Fu(TIn#qgo)r$f=i{QNd^y@jlA<&1PmagNkLZ)#j26dc!kH z3-?je>9e!4b8SR^R%VU|BU8O6^u9mEkS`ZxXRb5FwY`Gdy-Bew;@ByV6jRD;rj?_u zN=Z7gC6~{=P}hFO^__96bp!Gzf9&!fchZHWrul^{zD533lIFC$_k(8OLfTk5p0{CS zNrBp0aolZh`L4Mf@n*ij^vBHEzJBT%QB;9^C3~AuoDr<`!{#seH|{46#FqFYk!0Ol zKt#?ZcR%H>e#E=AH@J9`xjDl3a+$YsNRYFvpJLO;J8FmSj4E4h-ST_okeG?oGB$hda?Gv1!}x#H0I{u{ z16^+%agE9gCKM*lUtb86h8*h@@&xkj)>;eo>MVpAn(d2WKg+ZI;(Hjmkb8>7L5F0>UpPxlmdP2o>99M@+8~VMyIDbk8{b5%75l{Ji-BWy7L}v+qeE(4Q ze(y4Ux77@Nfvq}u_U5l6th#No2l-X|E02^f+fq|qd+^$xS8X6Unm0ece<-9F@NU+| zp!GmE^!p?>PbasL`oXBv8=mOT)PZ`?ZzG=$_-}cS-J;wkesEzZofzg!s7$}m z(b3t^2*ro^ZuBL8{3PLT2di#+{!TzNNh@lcyiy=p#XBl_!TOAYCJdx$u4CY(%IzIpe>F{ZG3xrFcE zYa>r9M6@n%C?x3S*{fV)zg8V}U+=7IHh>1-#XkB7(u>&Sl6ck@N)4bHf{)hjQyqW*EpxrnFEn`bP4;p zpI0a-rQ&G9>wG^C|u5ox_BGw3Z&FUYhgKA_Ucp+kF48@4Np zYL0(#y?$>N(WmRvl5gyd%RSDg)nep}E=~A0lmCb>%`tnYK%X>v`Jia^)d8^~|MMQ? z+w1pCDl06k+7=6ane`j(ul9ZQ>Wb~#x91Q`MX5nI)I54^OrtMl~#{dZSuaHxYVOh$at+%hlLD|2L(^eYAZ zke8v4Tz$r+0_*yovC)rEAG5n5K8Y0=YmvGO;O1$c|^lXmbVF z??F}YvZ;5YlRr$ByroC^C2X35_KI80QpL@0&el^4YYSWRf zov+RH9Jr)lw)$R||3JZ=&guE(o@ZYR!H-5=6rOUSsfH()pV@xv7sOgnOReyIN7Hw&6nz?A`R5fkd zC+GA$-~3d|S;NOIxpb`kG`^&GLs%mGrf`@;aI2>Pj$vIcgWW9dEs70l5=gi-9re4`sCH7&S;H6X9_tLVb z+e3R-XuGc+hdpNKQXJ7?AJxHj<*8USJ8Y(!DNO~dT?)H3EEcg@3(xkYpx5>t zZEmxon#=L%OOoDO0liZ6;bR$p82Ukvl-z+$nu)<9^q*?h_GybMjZCsx=o{z+qUNXD zJgQvITP>7_`}+J+GvH0N@IVk-MRW6wuHx9T_fo!E!s-~l_6i6^z~E}<2QiVL02Muq?R7n^rN@j z!An;!!G7#mOLKmB*78d#IygbarGE5OTc-S4dO)hVeow;1mW}V$6s8g&o36gtr3zCW z%?*cXv(ckUr{6YO|h@8&n&-YZegmO z`3QTc&o^eA-W9s+Y#-xkmvTN{8vm8^f}V6beaNNeV71rWc+3;5r~wh!?=`l0%!+H~ zP)SSyX^-Hd)zirH;eV#5(x0!JstfD&lwUJj+9TO?mDePE>GfqhJ@&X( zHadUFNT-+lAv!VimHcgcTg-1skNK{)|Fv&m6Dya=pBu^C^wtdxg4hd-;jMs>&^EbN zdaRq}GEWLT0$Y$Iq@jP}~ap{`C4;onDgkr?)Tb^!iygw(unMVi_n?Rw}9O zo!K@ZS?W^hZ=}LkI(=2JEd5jZY6wTyScB^PgJIBBWAy4`X0mE7EIJWid$q z)2OvDQsI;S0;;5j(uhg1ht57LPa;2Q>|00^dePXVwiN&->9g1on{jR5ZMB3T5Tud* zBm`1l8&e>JQt6L6AIU`!PI`?gjr>$5GlcRmDd$U@sE39qF?)}Txq3h@b6*>`>+J|6 zk@Nv82{!Ng{8|01&V?uK^JjGu761SLJo_)R@BiQZKIC*#$PjlCU zuc$QQ_$R(&s?qWEb(KcTBXv{RG1$IC`1#UGMv!@hV$0qv_UTdUqR>kFe6d%Ti_P#L z;!EY8zF6sVD5L4VB}MTqYvwpR$dGu;kFG09HQly2uXW2B9e)^st?9NoJ`(BpBUapz z+=2*2`bU^4G~G7a0aQf}#^u}R_Nk7JEj-h5$85@rH(g%DEz&gI zP|1F-jx7uYEjKd8?BEanb+do@<48JG6T?Qk{MCQ(=O%v<2-`tNve}^+c-oi3bDI(Q z+ONQ$CG1x!2r`m8Dsgm?YJsnlE~&YFaT>N0VEYI2q5HfxG>V*^cCn#d+k{LbT(?sgAh7PkvWW zJ*h*J42i&lyimgMOFMQvavlEC;#+Q;nrDVIh&5`^CZ_Sr8_6lHv zKcnNOso+M$-`RqnW&mxGM!2F7{I_3GXb3_(1gT3UKkz9ZM;!X0e7;jHAX`4|PE|Ki z5D8YNrkmDGbt?HzH{CXk&s3wyscxHXwD8aVIH8x@ry4KcPMQ^vl~c+0b)a0(KF8ry z%sCzHRIlZRqExfsn+o`nTd2~FAjGLIhuHK5qy35^!8e4tgDwH^EwgFgIM8;?N4lVX zOADP!wa<3Mo2buxYAW*HGi%1ge>B(e@|vRfkLS7+e@3_dc&^bTKhwD1be*%~_Nl2Y zOB{P|D`LEVbdK@zWzN<+CZ>Q7`C>(0>m5^p!Lj%9ytq?cMEgQ&ca7P*W?KA5w6DGWuU#tsd^s0&iTxm(R**3*!rWnLf^Ck5T5TylIB9Enil`@IjJAdO;g@8*E#WL zd%bJtZ6qJlar?y9JDh52{Kq7j7JqbZnzvK&wPkMlnYlgB%ym37*Qxk&H0ifoaH_e+ zGu)n&-ZG6h_<3|{{LzW=JIJf3^~TZQ_lCT^mpfbLI$Gu!P10V^OikTc6u)tF>lJw& z-y}bMJ^f5k{HD?IE1Zgpjuu+0C}qW+9djhk{iS&w^CZ38)DiyDA8sBUpYQ0P0p%j) z&2zLArIzJ!k>5OZcQSc*vhTe<`RzlVIH&dY(L0y){A60|ZO-2;L4Lnu%--uA|2VDn zcIV4;(MJ$n;rPem)*GC^IVXOHOO590BV+bnX8hyC*2`#*XC}5@?tFP#{6_udQu1?? zvqir4PD38#|J3+W=gSl0S2$jtNPF^A)H=^-8r}MJqsc}6rlyuTI*L;Bjh4}I7qaHd z&eo#Tg^rHV@$=yiNk_^D4}*wy+%^^ZOzwd93O|F1ErGsZYtnb_K&*tJLElNbsnM|m z3A+91+2D^}Y2D+_V(8gu-vayMqJwVf)cuRJ9Xo`P-AJ6v^i<(-s^cc&^fb-tWL~fX zdLWtRcXnKO9r1XxOgfgi6#eLtHPZ|@oN#U8@O2nn%YB-!AukIXl3Q~fFnkyMrR7KZ zmi_}ixJ7r;O#Sw$^ar>RT?-~oAEiIQF!kR;{YT_sW^Ys<{O||)iN32@p=RXlzY*-* zNQI<=%`PQ%gZHGGze%NULf*GPr!!M$%A>mTWWtUx4~L;VhAKpJ#<%wKwY+|zOqRfs zM3JW?+76%Zkm!ZTyF`)Alhb`FGhO7h+$iu#G)0fqDWH#b5cH$EqWGgQv8FZP#{oXT z*>y4~0uShzBPfzfdCnCaq>G9zajc?)LJFXX;3cgyPl@3_8ur%`5Z0?34!4_Br z-|%L+2|sUIg8V7`T*2&wZ5tiTKlz5+#o^5`8No-hU=WICeKK;@Bnew2I^YsTzTKM!S!5utGQJ(zcBUktcegwBN{S{1Vd|m)%L1%Khc&h+) z)-5Ky7f61|Dd18rl41({G#g2AI1=p>V(3H0#>`k5K)Nwx| z5R!{uAvh5Hb%3_%hD2LH zsnjniR~)J7T+#Rezg(Q6lev_2M=B?8sju}HqMV}ZV^FvLH~7nmr39XV3k4kRi2sTd zZz>Ia3O#4Q61Xu~m{oq{e`;wbVESSppSN&ki-XF^+d*8aN=@bc=i>HF(vhBsTOJC? z-~ZF(NZBzuti1TmgvKup8b$0RO7n zqIx^4hHo3^$*{qmscML9C=vf}#3(URQ%ix}oM#~gMy_2=d1UEet_v_yx}M;&e}7T! z#)if=bY%h;{u(G-Kp`1m;eTxpvtP3!a&6tc^)GQg2g(iEep$ESsYa5OQy;t{K8TOe z6mBAzUxXuzq$EI+Y8$|3LsTu0bKYzpC2>cv)#qV)qay$S9)lH}ue9jbjd&y$wF7^7 zUf-d%8o%W--}N%3|DG<9`cwesy$`(vEI}X!{tZtF#2Uu>m+;?4%|dMNyjNewzk9`^ zYwMn3mnp@C@o!J<_Z&mjBK2*+zbhG$Mw(ABB>18&Er^1lY774w;o<7nqcQxGbSra~ zph`gSI#mT_JtU|Re6Dt?&2n8TS@R-z#YwCuZ3Y7C--~-PX@&m+lm8iNl|Ok&2^4;|vTLJ?N$KXJRUl{8FqO)}*{f24EM zPqD|;rB>hjPcOyXs=T^Zif}2bxGX&EceYCg1}>T7XEo&vKM-g=XFwe2^m2Tk~AU= zfGWv-I)l|;NGSJZbF@w>RhIiYsi0&v2{pZevU;gK30?G`9PrS03RGkz#aqwQ0i?@U z*ZiDLC;G>n`>fYoyP2BU_eEcN(tRo4?m%?Umqgz#YPm(%!Y*~6<-TOAAF8$ClkQGc zb+6O)*RWU@rT#ep!2bu*F1l9Zk9_I5bbnHMSwE=evQ_}X9)*4>j1p>|6fVj5On)K) zwH0>qYrTIxCjsLP%KvN*g0E{qSAP+@z5}^QdrQvJJ>eJ{9$j>Q8B!i#NET8a{m=dn ze-V~00BD6kehy9$Umi#=rIL--ckAImj}n4!pnt)N2+wyZ zi<>bTgd-Xra>_>dEBe=p>Ie@=3A(3*|{kVoN=W(8wJ1}xsdd~BrO4W}@ zds`)1U}=2tOhvU-3iw6=9DW@sfegf72ZF-u4A7Dt$-uvr&=M%XKP8EH!9&$;QvYl$ zLM#J+9k?fVs2^}hN`b;l{uFKJFC#4cNtdl}k`RA0BESeJ0*JJ?Ri1!+uv^NLn_Aex zO$_v6McMuKt8D8&^|fKKvXk^&!1I+ZO+Wu%j7#yk>2u=(ZEhU*L{?nGAH-b| z;7j%F`2L(YvWNwc&Oq9Wcz*`q3o+B|%1W8izQpm?Wfh>?n|K#-3x8IE5D!R(a$mB{ zCeU%o!A@Ys2NGCuU4+iTj+4NG!H(-J1%_-qn;Z@+?RC(iMzA+ z4KD>dqKmKbHRdd;KKjz^OyhK;Ac90QW!kn z`m*{A<>_7Gnu=2@Bxrgp#gUTb4$CX_Vg(kP^!+%_mD*#y(C!Rw;6IE0b+sDh^~4=3PZ&2$ zNwfAGR&VAVAC0GP^Z4lCh3cEwzrC>Q51#W=t&Ww$*0}4QKv}lXbJEhLbj;9F&3o&*N3&gKXG2=ok{J3gW-j zAx}GJO^5HRG!3H+hfxIKd`>V?XskDc&!PRBy_Szno#5m*6P#JKinx zz2~g_tNr=-zy0wBX`H$xzy8sNhw5jkD<>Ct{A%8U5m%0Ebspe6ZMSll;p=TV$3xD9 z99is5`Nd(R@RbDqXh(YgP{{coq4n?eC5EF{89G#LQWvW#yzd!XeQMWoHhYaM8`0sZ zQfd|R_ut^hFSY@faVIisIlpI&_w-e`gES1Exv!J;D7?OXj3>KT3?GB_z|*zWHR~&> z$A>H*o-WJxfZs972zovHyk{uaE6g7;`aOQs7-ufHV95Sq$9*mb z^>?lFI0LGCSE@?6FAk}@K9Bn@&lvwOdt}#hANpr8UECZu*TfHQ-Z_L*cJSxh(!4eQ zSl?ZrU;j!XGu)~EI)W#<4d18>-}*_$Ojjs1A(PmCd1=a;(-zaf9X}}t!e?ziG~d~oUd{IWL`mX<}*dT2TP|MP=`XwD0uwC z;6?czJ%)PGe>wg-E@`>XpxyIda6*-6g3X-s5rk)Eju z#~SPK@1DNm!2EQ{wvx0uxFWq-`hyvZ)<^Nj8|J4_ZN9wg@0B zjFiRW-sNh^F7F8Ti7BHW0teM@2gXR}*T$~A`Z>oLc(WhQwzC5Mw?j_f* z4zW|#pI*UML@yfBH2h^$(%>X2#pwtmPikZs2K zo>7&m>83wE*wsBe`fBg%hJk)$)s%`G%Zy+!*y+4w`qiBOG}>#NpE;Z+R{oRft=%t)lMQAP_9-y#u*w3lj)-ApGv6kB#}g?jvB!8ZRBG zJb&H2v8I8Z)<$F*KLk3sxT zhGs>sS&U;$&cBeeCSs!kBJ#@lLbx-%Vs<6WMULp9eOo)1pVjF+Zk8JJ3NkA>uVgFL z8@0;l^sK{gxTLS!Sib`FMWg0bwGQ?Bm}zh#%F5cG*WqP=(=HQmO6jt_$SwoOFuU3^{S|A1?PFzeA{@K-;zu?3y z|BUlH7c>uXD3z$0DkDW*!wvd8N;x=TrfYd^qY68>j;~Ok8;3#{?K+~qb+&Km6gTH{ z%z1rFL%5COk(!Scm~x)v(zL(zlC#Z!`1r-r>gPt;}{>f)lVmZh^7 z)x<*ZO!^NK-38m^?1Zp5yr=w-%G9XVQ459y_RIO%&aHADNJ5!4(MU~({=0kn`6Zjx zn$0WHyN&zv)GA}$@>b49Sl6+G}P54BJk6?y2gk8`Ea*;*U}%HtJBv- z!Zq&~?Cb5wf4nAw|LaIpEt`1B#4h)8a0UMjH=K9t#@jYWL$$GmL!4^W=HXSUF{DuVqaDNYMVk@e-+qr;<4JM;jqvoa`oSp96_WSP&Tcs`45zWN*j;sYfL?XJ`ioq~ z>GPIqYu{aLg|G^hzk=^#K;wP?!D59sbzDiuGxvJ>*f@p0odEB!g7mj+cvj$^WR>@W zKVgNApW0)bN3_vobdYzC>18x=!nzD|l?_E$1D2;8ygp=+xU|oncZ4}{-x_9 zUmBs4^!(w8xxr95bAodCn;esA1(};<=NEd0z*ipEhRTQ{?|@<8U)~oC;1Bu1casuy z{j#;=cNXI#{=!q{BQ?_-*T{;9l#I! zJ7T#0h;<*r1!;Std$iayGKI0iO3zr%%Vj3R>)Q!VZA70V_{iQDd$9ic<4-vZ|I~EwK~ z4cBW_%2$mp-@?C6V9^@~(`AEN|6WHjq2IrSL@V*<#OZ{db8WN$&z)IXm*dik#J4#| zFsW517?!+@=iJTa#Zhf?zMvIOXe6_qM_AjRh3Gr7K?u&q=Yuwt)~>S zXAg9@TcPhnJM<;((ZJwn1);AsaT!h^uhLPL{zh_YwX)Uu_d_r1<=Nt5I)gC&5i2U5 z7-o$po>U=HI4UJMeS(wrgx)N4WS`=BTqoM$j)D5)l3fSQAF{yJz1h!iHf;DCeP554 z9FUd`W7Ig>0$2SR&;1E{P*`c+apee(ACx!xAFIOhO{#u>>Ka>Ce_%djhs}&{XU3C# ziDdRuq4@<05Gy>F!=I-Jk>RLG!Z;s8f_#u4tdxAGDKASed+inxeq_PC2|AqgIF2nO*!{P^ee+b6T-IR~2~W|V20Hr>Hf*R=4BHZJK21MU zDc|pfimJ7@{P68Eep=xHJ?xEB8y?CHtWWd@X%O~9NcdTnL>SNPk)2$s*mKOnvu=n# z6zU-JD<+tBm>$M|v>m>K3v$7SGzEyWLpAd|JFNN&LpA3Yu#zn&oq!YGl`3?-ZYD^+*=Aa-1Uaf`xvY zD-vf`nGG92O358kh{QxXmBuo$UxqkB1y{ytI@BB|e zue>>L<>mdzHSm-~_h#8oVk6paQShbiABN{XrsW>a=fnSw9lkYQ=%An8ZLED+oc=2Z z1onsi{%hBX{xC3JfT^7EPVn|rPJh)9X8E5+na0;i%@-2*Avs1*|NUd&>66sy8DWjt z5B@=|aMx!VQ91lxGrDX%+kU#evk`tHmOPIr--%OM595`cpZz2F{y|^7KjiRtog#-m zPZ7T^M;@^Fu0`J#EeOD;qF1ZMv+1qh>^JrSI_O@`m)+m9Q9It>JOljIZXcn4OA^e= zg9F4U|19~ppTtFDe#lU-b<420R3?c4QoH7gnwW>!P(n# zCfe${BF;a&9GwHoBV=Huik0`MGG;F8^d4P)_5$}iC&||v9=Z>C_31rtz16tqtv!uP zf0ypf887s(lpQZ(04(RI$#^F#YdV;w*uRCtHDiE9xLI{%j@rHOZ13{;!u(kUm)v}F z3H~q&$_i%YFUT+RuNvj_&O*n>1jg^>*X!LmhV?iXYP0?@M|EEQuzJiD!O3 zR_$1~XYWt$*|96StGV^jgZL{=XVMvCC3yB=zpeYP4UYU2D~@(Nc+e}aH`O8U7&Xxx zT~QP)3QY|a7fvfIRO`or7Ev1OPq8{@!tX!&aADUaB@2(d`_U!n-|KSa-7xa!Ta8aY z)3{^aADO4(QTy%oU*G}znf;akHcmzN?0F639Bexnd#C#y@Fi&KN0E##TBk~L;b=56 zR*eacx#Vi_Rs5Z4h3A|zMim9Il$cdiQCvN>aNMQnqm=14ysQre?jP4<$gt-1-#-k6 z0{>FRM0GCeR8*=Lj4B;f+HH&as_?S6r!lA&yL$g>N`~6j&!KwKF_LeDi$T;pj zWaRmeu-<3MIyRE>ScW5b*7jI(d$D@#XexEj@l@vfj(d(D*g00=itz2OZmld$bYcJb zRKrVh79(uGmzBAJ@d26!tg+}z%moXt3SJcq1#3fpJ6n0mJ;A^Xf6%-9>~cT;hdE{y z0p19%7o`g8sXP`iMU}t)J*cQyt!yl8Jd%vxlm6|5dyMCc)uuO?{<-_^NxznSVdoo_ z>ZPZz_@_=i7p6Vg|D*|91|exR?2`tFTUxA=^#>o0`=Fs&FO2hn?}d~###ek5Em(N< zIdhch8|%NSLwrKhyq7^xM#mCM67EYHN$tf_;W@=%{hX zEXsc;a1?!DVPR1?d^1QvpA{@to}Yyuf1SL3>W>Lu0TTf>*i`Kza`Lvf*JeV$aY`hDt-9pN)mo*D8v zcapm6eRpE*hFjm~c**4QPsgZ?pL0Ds{G9EHpGuZyEl!#hNi{vm)Xu`uBH%HNquxX2 zQS(snsH2$kKYeE4QwP1wt61|It3uO4(<;7Gcux47%DL5Z(f312<_buouOr@vKC(I1 zVu>i-ZSB9rKDY6ur(d!~Z2oC{?TKpHT8O&8zggy_n!d%HS3nY=M7|MD3Co6K*Vc|z zV_B4fk_!Je3=FpxEB8AyJ?|`ZUsCYS!i)V^VGrSO`egM>R-5{I&s0C{W`U~zRjlqg z{{5cUHBWeL>znvj^LRRWrMy4)sKD1tv-mm5z7L#RL==V}nI;^`R~)<=f)3&D%{0Ga ztr3{0>H@`TJWB;VZ#Xm+nTP0a4xdAGb~-bewffd-*r9%*QTM7FSihjT1e!jr z^zAEFUx7QyiW8p$JO40N*_;{%RPsTIUaw=?B&uW`PBp%2^F`goA~K$t2N&iqA9YFo ztdiwZt{SaQV~>>y7sBzp5%3nPf|+jP(9D7b`3v(8ITl*$7Zy#*_rQ27vn^ZyXHkpX z1|NbInv;71&;UQAqF_YE*M8)nnxMZ=pyEe)84E_uq<<}(V)$kiyc6Vg^^~i%e%6{_ zH)39)>sw!c-$U6kExZ2rkY+P7pKPcH9n#%dR?+hf;ITTld}rawN*vaF#cm#W+-RWm zngMjWrWr6#WBlC?Tq1KAM;9>avmQ|UWVXLxAPT>GFu(gNu`{T1=llEGC&Cu#`+?0K zMsqb|g3yO`dMfKD5XnjW4<$*K|H+aPkz@P0FO_+^87$Ji|mfu;|a^_poEVPNtca9~FVJTvTEI#y_Hw3H8*WFnF5`OGm zFkxoC*L!rqs98XQeJNsC+RK^h>E~SPa+i9zUqAK`&BA$!8|wMP>Ab@BAC~bEGg>*r zRv(&c(Ff~+rz&65hh`^i`qSvBiy$oo3cQG+XxGih#?4U$2TkvS{Fzg{us{=h4CenH z&pp47cXAqdDs^D*?}a`Dl=p>L@qp9MKWq!({`za8l=^3JFQZ-^`i@CjfhLn>842gm zQw|vpMke#B{NN${Q-lwI+5UjPVNC#?sjEJtimB{;kOb(svigYxwpRoaWXl(Ngk5O) zGmrnmI}mEZ@UziWQ;*S?z*!le&4p8DdVKE$7nZ!^d)KKp+W2p1_<$_4@~w163E6l| z+CLU+xA>K2Sh@vr=n-m^`swd9DxY>YHgAlA1Z zlTCv43dwjNu%F$cH30~|B<1U>cUwi~)R+2;J-1!3RqUc-H3~w5M|v4181jpdB_Cvx zlQs}5RTt}bwKl%3AM{xd*Ea}_3z@%^)lWr*_h3_wDP2_KIs0X|x82+}y;*}N1h*P` z4-}-$XFoCgEOwg($RoNRtj4BIhg|*HQx7+=KTry`r5h}a2k6@>F{i$E`)V|2n-~40 zF1Bux5F3I0Mb_j?i+qFia}V3)4V39ppX+hQ`V#w>lY)g`i?k(s_7iDcsYrTIo24(Z zOBBLdjeao~zm#X=R?tfj`m!u1tP%&GKMj5bxBUVE{n^T%%%wz&-*d@k`Jzw87_UE^ zgik2QqF>n^KtM=5)%dE*6XMnQvpbHi%_PdRFAU`tG`DxS&iB(4+YT663X8&0Z{%HJ`Y}~HD z-CF(Qo!v)u+$J^)NV~uEw~$4z+1wI+S$t+Q9DvVSbIPHoY<(ka$CRLJX+J+Qi?PA= zwdh~>Xx+~Q9;*Xb*K+7X1C@m*OXRum2t=ZPv|yFi$ZZcxgV3*kK4x{8EPUDeeG$Ga zx(4uN#=WQ4Ump3?` zgCC7p>O=EbIm-9o^#9Z23aKhL46!;Omq7U0BjH~@b#PB6Fe2zXk{VU{7XGh-hV$a+ z^SftWw4lnnz^M+*AA0ld*Wdora?i3ii?LtmI2ymQu>I2JcE^IH(+%wHQ$v>r%C?re z-?(-`5BBTxE_&mYc(5rMxN!W5o;G$xG`{k3a-ySc>*V05=U!(eBRqe`mD{3yWs|*y zv;O2zodxH==DcWTmn&Y>_~LGd%0FtnQMSvN+I+!~_SdohZc27n`QwWY7JayNsXOUV z->T|by6DxGF135<`J-ELgOeIEYRGc`Dt7m}&9lDeI^Wav+tRepr*3L` z@zp=%yLK)Zd2ssLvz^M-@X3a6R5S&y|Djs&bLx+lSduN5hC!A_ui@D-w z+cI}veV}((EB9YLd-wddnMcbP>@M3ip^E(qulWwV>e&9=t&N2f3gSLhb++eQ#-?`z z6H*h#oyC5#f}!&~-Jb;hdTUqJD2I#rePl}5h$W`-LI3bsM`OZRo^iA;t9xVV zMCK!z)MJfxqf)WNu;tURuef8$ccY=hO<44=+T6hU^t?r;;<({VU2Fa=iuudPDpx~8 z$^KEHx*Jyy1D@K@%Qr4=tT}ek-(gui5`TUrnNaitciap|hisjUeMY9?_|83Nhg8G( zS0;2yJT|tx&=HN*Y;By3|G!`&rkeRSEOt|@BwA^ho$atv9}TGp8K&L1-I%GX`$Uw>}4R{W^BY}lDU z{r-9DT>GV8r!5sQQ^pz4y-c4c%MM49xx8{O+G1@BeOZ@qv}CbH8!# z#_vp6eY#5x>8bnGvSD}p{BJ(pGU^lNH#coB{`l{28`bv7Id`p|{BJH?=Wl%Kn)FD= zu#v-_AHw{;WYl+7dnWznx)+yiHb<1W)bsCml->Bi6X!lV+40nKoi6qMUE!@uA3HDD z;=Sxw3VHv=kB-+q`ck6xe>uvh&H4cUZ5>-H=Z#!8vulC+)-TU>NQrj zy40wl`$jn%KI`&cHNRGkLEpXfg?Y!qyNz9O)8F(t<+=Aqc9|y{d)o4its`BkHN7|a ztpNQnyen#8f0T6ZIzK#8RUD{19=aHLrll>G@szx3dV__&@Cp1+WRhx1DAwh^ATOLo zU&@ZJ@T<{TuSa6}VRsqq>Y#G8nq}3Y_TI27&U#YifoNBYYVTELPLucr;UgEAm}RtG zm{D=`VXiW>T2;B!!B}`o1yGtq=z+>*x&w~r34cP(JQ#M7zoZYx4)lg1 zqZIQ4oOYRsUKT|gc8ZVXtW=QL`hcxiHHNCvn;MgrOPTL|H7^rZg%^nypDu7PCTxXrQW!s!0YjLdXZNg>4y} z<8DtNM0!VQml2fdgy`+uLzH{uF|{kMM!NBVwu}2LJaw{!Ws)v+5>f?1}d`WE0QER5aUmE=?+gLmfBjxK!0WZb-{RTgwl+@Q_fRI2> zP;uX%#@_?^fJpfm9`uC?Uhza+$qqh+{O{0n!+6N80wp2l+W`bFW{X|OJshB@<&U@{ z4#x>F_Q-sk2SOzYS|k)mNZifeI5Z|t5|6v%WJTU=zHRBbhQ>ty*MPhujTnVLr7|^R zm}5Cwo1#mHz-{t9eFy!9G2LD2_CEV}PUgoiupO@|QnRhG?z;CXl=Dd3vD@cc(ZPaJ z!6^1ec~oqAxZx}oA}PeFLGNY$-mW-K<2zCFw+c~1*{x6<($&zoa>k8asNY58 zlKl;lq7a&ia2QK2p7*WE^c|@3Kk;!_Rl$Dz`*M|`F08gTjyDzZ44QGb8WD%uIg1N> zbf}^|wz!5#*N(=ovmg93dbl$VL3^Pa55%!s&7%jDBpqud*Y*&la-^~zwbjY z@>?pi6UE)qns-1*iv5al!PPKhxEf8r*fmSJ;>Inft5Sbjtshp!czw@?4bfer0$OG)U1-)WLFY~Ifx~#W%bAZ)Yhwu8n-WO!P&AtM5go;w=Ud}u1TYFYe z!Yi)SmHF&HVk2^jrS4#PF!;UqGPBiBhq*ob-SMgso{gQ$hZm~pyYC;i+3zhaNXL5& z_V+wEab!B<&3H=PUw_enpKf56Yi94Ab?ko)zlQv)9dyJmiiDnTWLquxVOuIHUUr-% z3TCo@QZ1-q?uuY4ZW5}fk0Y*5q=dX3yOf*teKjtnHhDl1M2kGOvr)DiEB0Vz{3i8P z(VL_y+Z$`Ah5wfI82VE@!5YGCbr8v}q2^gp4H zUT~64zV-Nj!j85PzBf_>^^p#Yb{w?$Ogsi{IwmfF$G1=!L z>v0S$dcX*U_Cv3X?_o(TyXvCokEw4X`;?`-XvMh;{W}4!wbfp~(WI;CojoMTM;Uf$ zCn6D6DMT~Ge2wz3jYGwVj!5cD=4dv_2C$?)kP8cWV3DBpPo&ep$f^+OHJwTIW84t< z*sv~}5pd9A$B&5Ro~71DKV&IqsaBjNQPF2$KQ}>xY~?3ZYcD#Ae@q!HEeBq(sAcY4D%uD-A)tQrl1$>oqZUc_qkPJM5D3q%2&25cnp4 zVP{IrV&DiGe*haMI}n|?0Q?o2v?j;_>#`mt@|ND~$CuXs5KqZPf~b(jW3y29l*;E!RWm#H zg?-O9y-;F*O;SJnT=p?%KWD1OJ=W{3Ej##!e;+I0t3bk+59xK1>_g@N+z&8M0&FtL#1Tp_z_ypboXa)Y<*jrmPK5bk|9saxU zpQ&w|xTiYJx?^YRKq73IFd zJnm2BH9N8>qJQOnc{v}JMQ$4^M-OSXX@7Mk2HOj_vB%*m<>{R8RrSo%FXaS#Kd*QM=tw5Fqu8-q&i)x3A9wQoL#RURumX#`WZyXT z3g)f@VVtu5&tn*raMJ!I;{E&2^>~+HytYj-?4LgyM?jZw(*7apKR9mfU$Q$NKmF^k z_N6})*RaZ0*4`s~;pDizzO7NM-AP#Too)&1OUrvA585FskDWU1`!DUM;8`sq$vE_v zj>qat#;<<Ft@axLI5$06ZX2t1+VYMi^<>-IM&}ps+5f!2L>OG3?DN?Y z|2({>rkCvVR_={oDF4s4rDm&O& z+rS5pSA*k!`S{gu8lk!9zsm=WSQ7tA>p#YQjs0T$vj2&-==Q+i@hT^- zTWDNXp0*nJ>jV4eH)Dkt^o>(jF3Jt~o6!Md{maB<&;MKFml*X*7;Nu1IBtzkcIV^g zl>2c9ZfEajhpYe3$FF{S4_#vJ{wNCsJ^$^8CK|QlBeQd;|L*wp=k{+&{<|oJ`80#2 ztSK;Cj)z9d{66Tz*Rw2-O!2Y)6mqAKrZQztHKQYdW4~JV-P-6x(r47)|5M$_)ZW-9Yv zf34JWXvJFAOIJF2Tg!~h^5=S8uX{(J&vR{U%yhW}wT<8XL?OR)wcJ?Kxck^eby4$` zk=TFsl)l=KXw5H=)^0lMi4W*sk!Rmxwf3nqo(m=3=e)SPCPl(q_uQ1JuP>}PfIOv4 z9I}}z^6T5{5;dF@G-Oj;q3G(?7aeh7plEtcU%6#gc(t9jHpoOCw^P~)kD6ECwp(6x9br7u{IKIcx}h$;;t@( z{(Sxsf5Gp~f4J`RS$l^}Ig96OW2&S3?oY0qdZ6i!CG5|Nh8kjxA#~T9@rZ|g!lE(h zy(0sTR2{CaSl?HnsEVNfO^_r+yrH%(qb7B|e8+W9UcT+H1GH`*Z$397UeZ(gd}BTG zVJ61XbuD_xhD1I3_mLymROUWi4coMQ*RCr$Z||;gxRBhsCt8CWNcfi-N<>F`AG~b# zuZF8K;b)DtwK%G~`#SOa*+_j7s5tAt>hR9IW}3*$H4O=KUw3={;|(}U#2@ABCpwl* z4Aq2UnJb5Ia6I)7H)xM_bq&$b)%ah`Us$8+8irSiY7~EBD_5U`r_N2&{4m9B_ z^}BCIx!>@1Q)6g9xBQV_%&NI=tF+Uoq%*VeOiTsL1 z00lm)BaJt5AbTQwB!PThq1Nn;ces1w9(QMJ`o;CYOP(mVYo?0D9sc@0NT3j8t--{> zVr6)iT64?kD}tl^tG+Vb-?h(Iq2~GBhxW0|-SdXwyNrd$Mzq-Yieyd2G;7`gJ~C6d zTO1*Z^hTdr&I-#IuXof|P8L=}@$YLc^XJD?+Z}l;Ixhzrc_%(G8@MX|;-n!n;78^O==V2yz{yrMq&3y*aiNIC*Q(axGL*IP{4#t~GZ>=s z6hHp>qs{F1`p-*lHXZ-)(QCk~U7vn)`FLM&e(GQ+`7L0X_%znDIRZlaQ%ZZ!Upao= z(9H@H?*QXdT^;RsY0ZWWV1|an78Py$gY}oyL?ZQn&HBsh(M$#prS!2H(XXTLz7i17 zu`h!rw7v3q;ajOa3k`c1zb4>48|E#G3A8dzOs{sLy%+s^g!4GasEZO37eG$3-XIf6 zGCB}z1_7d@QsNK1Im zgMR!su$g!U5E0UawD;(gO5xE``Eey$p<+K|{cBSf?$=I3p=fCTFZapQH!ntR$Ww(Z zrv^`>u(<3r4PpL4mg3a+G6R8A_S+J~Lq;^(*f_pW{n7NOPMJk+#w%dJk{5r~(C~1* z2#yhf->V*w?Si5j--^HVzC_2$GXk;=2?s@{8V+=E*@y-KNqex27u~^=;X=US`G925 zD))x5bAk0)EhmXIDEmk_@2Sk=^O_4wad$PWGBR4RtI6=D>6&0+n0KZWI+~|TY3`KC z{+@il=Yb)tA_n@r1)h#_l|)`_Wvk-&Qnsfu9TqKu<$Q%l**T_$$ObkopfFg8K1Hru zE)B6oNB#`n5ABD@=hWCFz0DUgYtEvy?vHSb>8v;tB?DRWB+jf zSPTE?e(u{q3)I%mWa${`{y~KM^i{*tY+Lgx7C-G!hW}eixg-!S3>V%`B|Oz?xvY(@ z8jkl#wWbI77$_u&(f`BsnQF*Id2TtKwp=&PlTOhdpUM8ewYbky*bZuck>cu2R@d;k z4C^C@A+5SV&^C+b1Dck$`~7SK$A3FW@;)#O+Dc!@8EZm&I_T!c|KjMpypV5AUoV>< zPqZ^0+$U?f1l?@NKc9)Cy|sq;ko!|6Gg@`GB>vbPYiqf=J@Kb@)|1Od;ycIh#&>o~ zJ(((3tMUtv7m0(%auYRINcrOqI*2-7PlGl`lq=v1_ij$AiD}&8m&ST&qJklu%Jk@Q>dWty4` zXiDKftX)a~3l2pu(+z~PJ%Z>HE!~f$|Eo~>ClVPnnj^crTROUz!CzU?O{qU-Wclr4 z{6n*`)SBURB)@fr&?n*awfcuIp?3v;5Z|0P(0^y)t$WqyL^*@OOZyc9>-DAwxL_?n zO8%!?yFnSmD2n~lZx-V0sp9#e9#2On_MXXhVGb~3NQ6TI)MeJr_UV4{TM0crr@`s_ zl~i95|5_rJh_$!8(km5Dl&kR@>E>SVakh8`OtVmZXE+3uHr9`kEkKN)4@g79ALK{Q zZ)?9-5UBiXr-TYah1geG{&t?mRV_jINq`TyU+yI%&5tb9F{0GW#$Prs2VdM=mtuU> zeGB4~L@R0qpK0!`qtfi51KzW`H%-mr1S~@5=*SoD?XnpDLpWkAVBB!Tzs?`mfnG9?3*C<_sHwT#OoK z^oc(?ILU^(xvA`K-{n zt{wx#81=`)tjxcC`_qkY(+Dpaj8`0MAPhms-YimGrKvQz-Tx8RUJRE5eotm-1`KeXVP@@FY0wU_v4iw$#XiCB9Cd8daY%hz(=)HMw~hMaOd+Cec#_;Kqo&s%QqO)Yph^&g zfv;oDEiK2Q&3FB=hb^4&4{06c->ho{{`AT|1_Ux3)YME=p1{hUC|x=?ctbvy&Dr!^e3*7WNB`f^$T)Hv1Wh)PNFiQ4s2vb5KSLmpJhX*G z;@i3$8b(;pr@C{|Zun0|+9SEN#@%(-jmE!6C`f+`;3Jvg&8LRTaHMyDXHpl_{}MJd zmEk-xM?RJQ!dW%<)#4>T#3dV<#YM`K&HPbQF$g)9Y!^&12p+DkwJ2X(YrqTL$*1=B*_K9l>p?ns?nYq)Nr`lPI& zD_HPi4o?~ z0rvkp#zHaykPhPE7wS`buZ*rTU~m|&cddmp!COkf59qzfbHeRX-j{_Qgq}BS0GV{k zBF@i+9)%`5mG@a!p?I-F?i2Q$PHm@u-YC)$i6ItLbw25Y@(~utC{!V2FEFk;n-#WT z2>Ahii);rF-ZiOoNZ(9r>V@X%AFIxg(TKaA2R~LA!KHC-;o;MlycO4~|2E`eEzl#j zfIl%?AwbmPSeyIa2R_j~D#dqdq8j4K^rida`}`gI_W9|5u=5T7j=I9C6ZUX;Ml}|W zmob#LmIs5u!1{N4q3Cw#kL1Gm?k01dk^bnl`s8U+?=kA|%iV|D8)JtL?>Kb$AE9T# zC-e)|wJ#I@{L-+RH49o<^DN(N^LW`T^v z+MljrJkAyEQibyPOKI0F22V7oWCpQeiK0bpv0u{|_oOnZVJVq!3ZKu&*xAvMcwXl( z4-D0O2F+3E{z(B(+Tq`K9Bx!1@@{GqM~0Az5T)otgnF(gx7LK;#HUuxaKw%A4w zhS0`D{Fh8o1!?#CkBnqbfq#tnV{K0)yV){$_pan#@rR4^7V%GxgI4c?v1;7n8sDNZ zi$=R5E?4!8BIv=7GKNO>GF|bCKTf!%$!JK>@SBF0CiSWZMS>ZlQi-IecT}b$)4Q{; z`~70|rz2;dcism*XB{s2%@YNWRjBsOt4fOS-ihC680AED#5u$5^jvOsI*gJ|uhiEH ze)Y|?jNd|U=W3UB;&-!B&C4YF+V{4<+|srq+PDLL$bASz$cAycg4Xfij=-In{G=Dm z&Q@k=$#Wku_bO?1xYrlWQP2MJ$a%=KXFZ!ftEuxhqmggu*YQk>&AD2Ce!U)i!a{A} zrdXYD?scB6_8#Br^mx881^*NO7W>E8Pn(lToRuagLhTay;|(oku2YIx;yq`8O5nMee?nr@J#9I;_SZ>>_FA4gb{G$EsfBVM@0M)_PY#EPyVPgGdvqg<5av>^$#4dBp?Wxgas^EdXwUvEB5 zRIVue-!!@7Q*h40zBvAil;Ym^9$A0 z^p{VmGP8Vx({M^X*vl>~tq4_gx@e*vFZn6QxgE#an!n$g?6~+0KlJ3!pbyA!-Vou} z^w*TS#Y@i`iZpDTM*Bj0iy1Ug56FO}KID`L-x^5_0qXfp%1Awqo<@Wu_-*u|2cPL^O(c607mP0`fPZ>rtlZ_n5-uR) zlTWz-z>o+~+TZxO?afeAbL>yi=Fw^x7LpltS`ukutzx`OW|AYZH)~n@ zS4bMr??EQlD%Ar;8+ZUO5;xpyN??Y80+IYU|3Q!MQ-n?Kbj3MuulaCuw5fSV)4dZi z9pB2ul#3z1ejZhu>Kw6t1$VJgF`aeh+ zLILB*V0!&*o{VwUC@b?bSp-@>>m-*vg|KuDd z|2mGI+LITIKu(~4MMdvo1&-}-Dxtn?SW;80jJqZ~T44jmKIbi8#U5MkZI?+M3S8TM z7JZALL$1wHJCDyew&`d*e)i6u%z4`1{wwihYrHRc#@&DAmHEj6@)F^Xf!| zN|d_g3_3y@cqfT)U~?l=@FDmi2+`HdMxRSBlLXd*B$do}G(p5-KNCDld!jwnjU(l0 z`WOllslirS0R1mPSQKH3&lLM&g3cU>Sf7hQ$?U)fWC^-Ggg(HQ;2r%p>nIxE`a%7? z?hrf;Rfv5#9YjOO2`d&opzdZp1PqG(Fda+hY}tkD{557E2hwpG5n=7QqP^q=yf!WZ zWJ!rqo&YQCMhddlXZuAa4L{k-BtpW|oJWm8y%iuYi@ucFz+Xa2?4_;HexYQEMjP72 zK3oT~>9;?!orc)<>beHf>@MbS5^&a_f1&@wnjU2Ph?Spk(XdUS*SVCOQYOKN{$J>a zFY0B%qv4}N*vJP(dOy3|B$@nxFHE@!8iHl?SBEfIj{m&ngi4okpOUWA+*pc*e^u-9 zrF%;K`|XYGUMBUiHBd5xebn|3BjeWw>136vm$O7RdetZ84B`SWSTEMBN7~=e_S9Ur za?j0QxiI zNqPZH2j;;xeGBbrhr8MKw45^7`vJ1R+xU^K4|do^c>{%EquozwB#=A(^uw-5fy|EE z4QSb`32504X4Oxq-l}y!e(X|sK8qdswd*T66F{8MgS2Bd474X^?gULQIXT#IN8HN6 z_D7I?`pfOv2J+__5OY$TB2QIbE_?&{vViJOZ-X#+Jtv#LRhxeM1zk6*eq#7P5C%X0 ze|lWTq1;gBdQ+wyc?j709n_;#+E~W|0<#=A!vL;Ap%UgN@6a_I!GJgo<#(p25BzJ^ zh}SWO4^O#5GO*v@@7v7zV0Vf$+dMJm_Gma6Z#-AN1X;)p2cStc4N&w3~baiBc* zdL9^qLP@2{6=u@S-$(YdUUSrswE47{P|K@KO<*RG=&+6`yK+R3(L@ALh$nr^8GbcU zO$_5dbyh{VLjRVl%1AZu*C(0i&mkjS-vNMi{j92KJd~@)KgyOZzcbYup)+}>)J9}r z3x67kf|6pDV=U*(gV)(3r|rP3Se?bfm%y9UzgqvmI3)^H3B~GNv9l}3Tdw}Ko|lnK zhua47v(gK`t{NdKG~Vk!K_6gU&wiIHbDn4vI4|Y?*P3H(6V<;b<#4U+CtH5D0Qyr_ zc`Scc=(T<%gX{?$Ql7p3tV=Fdw=TZ+nt@ljs%EvI(*C+LXv34u9{ZgBzx=rk`xAjQ z>%*_UesKA;KVkZR$dNBni?Ql~XX>9Ccnz-aBz5PcN+7TR2v>4Hx4`;I>Ia`TgNN)# z!;?i8zo&R!?rK=&&*1xl4>_DBb<1$$0hoWW8fT(B@cHt}^)L0&)|m3O>ErYb6V<<< zI7D|YdwBRQR(lY>-s=D4oVTbYq~by6Us?BmYAu<opcywPE{7s+q2np<yD;a$o)ohAy~ou9q6i;!|K#`q)`uwQ%ku-WOy;ruEu1I$F9Vww_*bm{ zb;ph!hu`8$hjKk%`K88>e2rDYg{|;G!_y5_(2rkj6A{?!Z*?mCBM+;@8I|~ahG~V* zRM&o2zpkxaT+2VZK7S~Z6uj{t8=1Z6Z!hIb-ix3|C~d2Yv!ujhgoPmaY}H~~|p zS$uNaEA!ZE?ZDU4E^iyevFeGJ8ggDXee~m${oi<=)a*;IGLKPjTaK^oZ#%uvuErw# z5VzI(C^r3jrot?@wx<7sy%oG};Ec&h>TR6o9&3R^>2Ki&qA$@40_e(%)zIARf=oP6 zN9gxZIRMee{#z@oXT!aLe)f{F*VYWYv^}_Odo2?lv5(2!q0nzHt_9x4M(i;toh1S5 z`}*1H&8FtVu=}<>`gfArAajlW59iCLtk)vzEIrrRi&ON6Xsw}3?62Z@P$vg6n@jnY? zM)WA9{QP``N2s5x4ZJd;Ji0c6^)Gr{gg;LJ*_vYqYcFOp)xYj*L=xelkBk<^mlc=z zv+MQ80GQ}cpXk4i`Xa9NS{E+wU#|oFuc$BDKvr?7)@_iFMnN^Fucil=EyA&2!Gbb3 zW_tKyK^F&_Q0g?vK+pdPc8Lh8U%}C2I{XR431GEP_}gP!(#XjAYvC1wL$jaY^Jx~0 z9>*>8{vM|1j=UgXLBYErRYMVNygvrN>yQ4=rGNNc+;r7cKySa@^|NukGQ6|#VDn1k zfV>)=zt|iG{NS!&G86l=39#=)*t_=mk%sSrc%I?9_R{&A<|O|-5Yt7N>Z7lhVK)F} zMyc5idoB|-o<0Y0M3bT7=vOP3NWVJ0-tE&%2L=+$dtmov{uBD2!gnQ+ z=1&v3btJGp0|NPK773ks18J{teQf!z9TVGM^d)@wCL9rZk5CYsf8!aBhqje-z!B!F z#@^w7q{&v`IV)a!t$okc^q+g7s*C=!=RGx*nV&_ZP!?^8^fZ^wcgLhBFYIYLI!5U8 zYS1q{S?o50L;EyPj8<4l@LwpekyBnhxT+XNmuaNwI?5J=d&Tgz{wjI3!kp;N=L}#6{jr_bnte%A@ag+MBh<^} z_pAjP^I<&b&O6*1|JjhH3xNP#xINJxYw(KevVJ{=Gg^Cf_QGtYJmw>4=Uc3pTeOUh z+Wm1Hc5?n2t54&v_y1?$g!VTMe;$n$t)I!59gZ1vGus^-J;489)xxpqoR&9|?MGtA zQwRXVj#9uA0te&1>n>tV$M6YWn|Lu7Q4$IeKRb4;8Dafl*vgRhZoFMZS2F)Fyx4vm z;Yk@geEm*+* z89&vUhtb1pN`$|ojT(!eL2pj-yxqVUri&n@fYwBLH~Yf+542#`869_ATub`-fxa*wDpM zY;nz?a5J5PB1PY#%(f}(_=yXQC*X*lhe4Kj4hxrdL}r;wuZOQ_672wXZ7@+Ub|juN z|Hb$t@jRkO)T~0#7biY1@f;KwH+wN(+X<&}=kWeT8xxbP0M)GMrUH}vd z5R&7|vqdlbJ%I&`gEyyu1;ZcdJr!yh{NckCGGVT%4{r#4@TFatkE7g4qcEL5V*0?i>zbA{5nr|PvErJ)cBDpDA$uOwLsHF5e4>30 z42{H;+xQN|#(t_uXyhV0x==vZK;IgBVgKHFE#KDKSq3vSrjhdKn%2bF zeqWt`Y4{t(_PBiV+HaTA4}%Y*$C?b*!rhQ3pMt?2!fN;*^t~~SwRDY+bra|Vb%LJa zsJ*EP@X`>i;VWHAo=Yu%d9D3}J$sP9=!4eRvHopuXLu07l2S$|5&pqFq4STp_M<+= zf6#&!!e#gy#9hQ!{rVcPNKgZ0MCiXD+cBG}{_miwjH(Qs8?zq9)04R4jCxH>pLPUZ zF5VbDGHPV}@iQTwAj9|LPW_lmSFqgx5T5jZjh-T%!{^nsvH+f4EV>X=|6#)1R?`U= z*g#->n0>^a$LhP`*IBgrck#!B>l^y|ucmEYU2~~xFd01c!-4%-%pZ9nWEr#gQkE{% zu>BU{;}O=MSiBMa*YG(62wmxxcwO%=>+U>F$e2spHDT)Yy^yHzhW$#o^#a;{kX3)V z`tdM*-#GKXjX&bfwFV_VKmMH65LaHSCJZ0_;o@h|$32o`iTjK6U+cWqzWAqmW8?iI zqS4`C{fTJ{nY=M+c^w$x^05*>y%58%b;YbhbEzhNd-`3G_Gq1jMl`;E?6>fFv`R*w z@FUaZ3CO_M-wy4z_NOh}qwQnL1{`kWuu-@7c2=dR?y(?a}pt*sp{6TQtw3 z?>!KIX#1tYTlSekB8!^iqS3qt|Ff8`VEm~uOBQ`k+vgv7n@6>Odue?!eMPK2Is`z! z8Xv^FylN(K(04#Gj+hHA1xc{KF~`uzp~VM?EvMSyAEt5RyMs)R#`YU9-M)Uj2`_8- zCr>yM&$avOr?W*Ja}R=k1osAhdL@?Z+N*%{Pxf9_Tu^W|)xpXZljr6};xR0^Ui*VT zEe@s*(%G6G&mh)>3D3v6F)rSL7LR4Zb=(&29~NA2<5bxGoAyI{UN8T5FdepF!~XMF z-T1a*LVW<*WzWMfOt>HWQ76==okw8LJ}+AzuCXek_lIvvED!o%p!^irUJN{W->ih^q=Ju4IeB14X(D)=ZAkxJO{sk{>L{T#vdF%y^#1k zJzmm%QJT%6nqe%XB{r>N2FLt3-L6}fbPH1suHmY7tq!;h|4C@mV@uRqXq&FzJTajC z!Jkh%cC%&q)8|I!(%mo(YPL0IUsLDN2W-6>J($0BJ${$7^hNnK^v?lZcYt@~t&h|f ze&X&d#M6pD=Fu%TMLUBpk@y%`{c0M8Yv@(f6030@*0*f2962KOLm$h;e)BbS2X=jF ze-<}fdgf8tw<_Zvpx0j)QBNS=*NSj)?WTfxv?{LjU3XbX_%<{Zj94_9xrS<;4)~wy ze=ZC2uZ}w|u^(Oo{Vp1d8lcwq4!z5KJAZn6!Em!%oRPN z@vNSz)gHs1_PXbM6+!kT_4}QyFibcI6^gJSPpT*Tq;@ME-Q_iJg=e0YvcbI zeh?5tj|M~1T$-YdGaGC6AAY{(@AEv|bhGG}^$^g|Iny3WL4&62kPLQ4cFWkv`hnbC^7QH}*Tr3ivJ$e^^}( zVjEhrfU9BpV!Q8DUrlxa##a;@82RWAnDBhY1@LI*x5x^B6+r%DPpyp5bo)elExH)r zA7&qkv@bFxOuVk~Bdx;7TckYBwOb}Bk7q|pYwICMkh%m)@I{Ebej1;oBh9%4 zUhqH10TUHFsKuo1@6tiIlxYPtcxei<^BTY=JP*y%=F7Di8ni_4orLQK z$s)h>7tzU)0XS~Fv?(0&@BdWaYHd3v>S87&tQX}7?~m|*ZQ^-MU)Nq1UJPqN_3i}y zH^u{8SEn72P(#EXs0XEH`|~224E-~X-!QH-(SC?2C2~KTYrI~j4`%YL$fC(;$3%Y( zccQ;X;(QJNtGL-4>;C9`8~%C1b^NBEGYx@$a5SJdCHy|2eds{6zOa+qAzqm=jzZL(j0pZA29 zJlets`+X5_L`PSq$eE3F*f;$X{87`(fCxc|k`QfwSf!fK9^|u~sJ~(7@d_m1v!*|SfohHHSFqk; z#(a&W4|~g}VE;(C;)(S~E1j(V=tX?N_4vmjU-P8(g@4q_$KO6_`@;pad*bTTeokx; z`lso$wt<$}%ME`$9DP|V_yROHiRHEPxE3?`uvjsX*Ie3m-vwZJqWpU6-3 z^HHrT?Jb^v4@^?O-oMN9qr~<{&%+#S5`H+rz!_~X+RAzmy*~^woCAEZTcYJr7R~*J z`9`0y;`@j43w3^}!-~Df*N0Qrj5z=7<-kPXhr%2|VSZZ<^pGL1Macm)F-7B-JR-1w zSF(9IhF`i_2lyAoRfch~>+I~C_)${#+Ida1(yzuw|y^sjag#R$u&pN5?PxNbPxX1@!(x-qI`% zymU29VaB(phavP2QQllLmF4Hr3G4pwf@uLkJ@8;{#^xs?_czBlg($5sy|3BE%AF1w6WH7o3ZGM%oDYNwcv${n+>F!yHcN7KaQ=v(4B z^n;@KS;QJ~X?g8Bim+qxyd%Ri=r0r2XV<_#D(uX{KG}YI#Fe|C-+=eT=S?_=0VOeB z?Pbn&V-R{V;r>g{`z~{TRA$%rW5PJ3n?s8>OP?LB)@jP5UY~5kwy&>uw4#0k*!B2l+iv;)q{>)W9dfY;f-Gl{c^cCM; zHj3X2pJ@C4XLyD?jrK;{%6__qHcS|Q*50Un3RW+DziiA#a@lypVFi!2zdE9y!RO@X zVP=Jo%hnezlAPdQnNVM48JmyUiz&(9H;LC!9egeEcm18%1_ZB5rli1uup{sl6r|Ym z^adx?*N*y_y(el9nWpO>*Bcm@1FK6cpLjk8^c=6hbdzog2)}QRJzwu2@L__#_gq>N z@6N;G`>+E#4edCecz^VKGWbIa7rh>Zr}ibHj$`&8%r(%AVVYeIi9U;d7fWV=Zwr_{ zZ^gg)Ml@^C2-{yDIkbu<$Imdb;Uk`27}8HxUboz$FvDWKzpS%5ooMtM<7e!;wmAdB z8#9u|=u0yaUqY{O_^xrW#QLwH>%XNL7|NiA$3LUL@YA%%#I;|eu|)o%(Z5bnS_9(B zYx?ua`iovh0W_gJ?zl|(u!{at__6*lKt&)l;eHK16UaRRFCAFWFvg9iHm+{kKFA!T zBUVY&ANi^w>Rfsv>TAXjHH@G2#KM_?L=x-6`Gf>VfEUx;>#zB5OjcgUuZj2?J`3Y> z6niJ*FVUjfmkI5^PlP-=@p|NZXILj6Iggam_)Gna`m4hB+iU1w5AVg_VQfxhpBjBU zn-Qjtz{rID$sgHS{1c{V51S{j9%p{%(buWD^eCKqVk94ir=Zz10MkTx#`7omG=rZ- z$Wi~)&$ZTTP@4R@R#q#N*x%T>4&?u*_oIF5PO|_-f>1vf4l%L%bco_SI(qDv#OrLl z7Dw=-x%WgCZHOey(LObJ$GZG%V*7OX*IcLh!@w~H?Wqe!CJ3xN5}ybD2mgR$%)*L4 zNr~}WW>gYl?GHaQ41Zk!Ud_LhBpX@wZ8NOV;zq&mruKvs5Pke3K_(jK)wI>3fdIZvAJ`dMme_wd_z+NwQ5+sb@ z3O8NEW(oGfpUm&RjK7Me3t*E%(k1#A_G|Qr__u%~;($ISv3%q_qEJP=8XQq}JkkD% zFfc5BcJzP_B65F3ffGFs7mCDhh-iHi&yy1Txuf@oD~z4%N~Og66VF-unOvzIP@-QB zei}SbC|rNEONsS``x{oUu>6pbp6H*6=M(J30OL2DGeARMfFzf3$K>q|_b1lg2)Ln7 zi(SJvy}uae$Pt5|eoL%8f(at3{5P~uLjwIeiiRsptS|9A5gv)>@%Y19lz9K;&h^n} z@jBw+AD>@J$63%bN!uX&hGpPe=%>1ST6h(?V8Z&x{fHi4Cx-8d=L>Xsmkrg6y;uk& zUQaxamDgR?=F!s=?0L-JBmP)`^#ug6@M_KNo=59$VWL;$m`9Os`lr@@ZRM*ENH~91 z6ocZ=nH-7sMb|>CQ2cdvz95P}nt1nO)}c4!~)J*+L( zs-IAQ_<3>Er9CgqJ9HKiYrp<8YcGRWQ@7_{a4p(t+sn!Y#t{BC@&G1nqvkH`;rwKicU;{6ZJw zo~O}4Lis2>!{ZY@k353)FWfh+8wvOKEkJ$@9MMHZ3_hrF5`GcwpMJ|v6Y7i7V+@`v z9XSOq{J~a;cb$JR2B5n{$MsL^LIge$cqU%Q^I2P?Bb<0WnI^D{OW<*7`7q@4kD|o> zB%Jr*{s?F%8$a~_Ii}W&9trnjJuKW7wLsy^T)Hys*##XV^o$Jt(avFD65ES-jB>@W zg{eTaBeDK4K1aZ>@#A<79G*Wg0Q#ysvHsZj0)ZzP|M>C@(#aGVDII|E*V!-nd2CD) z+Z%VTRWSMedfj?`6TlzEHyz;XievrLe~GIt)X`ihCanL_tnhi1hqL%&@e{^` z_-2?m;`9;P1Trz7S0tWT^zpFg;(K^TU>}p0*9)*2BN8tyQp4ouu?oMbK4wNuyr0E0 z*0R}zhdlz12$Uw22Ouu3KhiS&Gh9B}pcuULSvC${8j?(U&NycHh}<8Mf+OwInu}4I z(7s9FrS~u9FB5ma*2T&0k2W4(wCC$hzqGv=@mRb#c30jd@Qv0Vt~3#TiRW5Sgl7MTOvKJi4$AJsdwT3XsdtXB7Xn_-^60vya5*vHEq%Mr+_;;&m2} zFzy&gq~C8kX9N|wKa8H=z)y6rMB~wg;gxtE?tjF6%940J@mwpXte)fq=}yE%^$p6{o~MK9O&_+@4Gt+mHSjt51hh6h0cj7{3A-BG`u! znD{)49v?JrfYJUig6KcS_lNaYdtPKP@ELwyq*0UA7p`5eHj!R+_{AtX{(0<1oU_M9 z4?I7rPGS9;*q?B`Q|*cREiw4$jSjar3ctkmu<~*2m-R2sTPHfdN7%!}=L7ZP+s|5X zdHnds_JwI$jmGbcKq$WcFnriYc+4(K&l*JH$|syhZ%2D0PtuF%^d3W?%RFE24BjW= z-zE5Q$@~!+p9suxe}utkpD});gM8w6PaJ=CgVw%iYbL-Szve~wS+v9?;J;zf8h}if&Z`Z0hZ+nhbMV8m%HNrLyP9+_C3{}%!_0klizFO*P3W7pPJJ|lBXuI zFMP)S3fgG2v!s~$f%jLpNVdEXb{taUCwmXQ9cbm6XYL4eVSjV_Kf<56lp!KA201$KKS=VC-x;D z7&;^n+1irHXoJf@37?rH(v>B)`XqjA$mYw>Fd5LFe4#BZGvgIo$9iL?VyENkj%j^` z$8sMTe(Zx!5ux8?L|$l0<+*X2(3S;{(=O4u=-7RgR901eZooqZe_ycYj(@JbVsI#! zhw_`829wC|ui=`rGR%*9=*xmY@7V)OeBb}_*E6;q4~T)6zp(mSUG$dq zr2OWP)N5FhT{Y_^7k#>WvFYVOp*Z`~C8P59J#?+_;a%3z6?}Kjb5GPEwXmtGZK*Fa zl*KocH0Oul&)wQSyP@%l1{ImoJ|6a9kI}9bxkJy)dFT4v8<%5vjossXNqbqPT5(ly z;69OPO4hszTSEzDT1=xhj?Ro&9ils>=9V)n$&GwpSze)n<1=acy*{5)@lbKOB8?}l z-%}#JA}QG}B>jA6)s=U@AqU_6K}P3Cb}Bk3{U0+6C6c?VZkx@HxGX+VsT&Lysjhc^ zobIBB-@AAJou);C({Ofa@G*=>H(A7tl-aiKZe#|v)6Tk~H};i{d5lHNt;^9I92?w&I7yf8wbBU%J$;#>OxI{K;_M3&2OWH(==g(|6o2I}Ujr$j?26 zYdAmPRu{E5TK&a85UH)kn3h1_rM@bwWJ9Dy_lRjOz zxT4%e8*87>x}Sb%IJ5HX-=zSa-4*^Sn=g|$%rMU~)rb__B(K{cm#|#)!t_~o>h*tr zKHvP4FC9ofRm9n8_dRt|UR8V9@C~Euz8nHR99Ztpd#hY7`DI$oFCKT%*GIa?j$*6z zALeJYJ;S-^Uk#Z@ntNs+nLpqgyaDhQyJq{}`Tace3s-LK65TFZ{U*)t&3?mtWWOzQ z1p5h`O|?R!yD8LI-Tc81tC8<^2UT>W7?BezYqkOO=BAo&kMdRDec)R;cT|%d`!&}b zllr*jyPI!1d{?HOHl8v|`I1z0Z1wJ6&Gn09FwxV13CYg38fxaKFhGGaM;S=5QZ!UK z$=Z`=HNI2O9vtyyt3Er(Z1urD>rYQCn?2Hb(k>!>cAJk+zcYTR4l^!3U7 zV6gmPap3{%*P*7`eaM+~%g#@J`qg?Dg$4^kkByhv?rs}-dDcbnpA??Pkyj}_jlNDs zJpLa4cV21BIb52#=^#R$qrCNm=N~ICaDt6DbISoAgNftK3}4bR444n5o00|uPp9Ck zk(;TegtCv8?VYyJI$+5U{ibBlPD6!-2dxLW!o_*Y&fsDf83UVbcMtz|zU1}sR^gDB0~rM)z*i0o?0eHOp6h~!sVR6=ipjuDx6xucSa=LvP1<^v zs1Nbno4leo(CQ^`W7F7Cj!uUbzWkjOv`mgdMmcRP6Z`{H4X~X{&!VZxyodUf zhc+wa2bR3TJy9fM>o4Y5)AT86NPUaM*#_JvmLc>(8T|IC)=}ocZo9moR1`e0gz{R7 zpKy{eYFTn>iJ?fz^>bFZGPMTmfp!WkDsyjsjndOP~L6?eA`y z?F;&BxqzpwA$6)gk2`pL~Z+_eZ}i+9XlCevJDN>;jIx_JuAhLxUW$}-NF z;;(Y~mw6iO^y81;tbF~dqd)qj*HSXu#~~hhM#pS_pj{1Aif-fwgglZumZ7FXAW=uj zL(4bwPxM(AW^ez=g|_Sa?R4i@!zqK55256ae}4Reoj4|!(BPV&f;4G;9xC5V4{d&E zFmGqxpj4!ARFW(xE3e=0E&B0CkDLm5Xld@BQwN92ch(&rEGpm0;K?mnE(O=lfA`a& z$EMn;`Rrq@XFbC~XK^+iTw$j{lHOBuwkojO=HPO$H;n&r%=x) zT?mHGj$`-C=Po+(>K<*O9z* zytfzpj;pWXrW#TV09Ixa5B?&$M+dU)2B(Hm9=dDR@}U*mii#BEi7bcwQn9zp3zA#+ zu|@9bGob&wet*5ZJC6#bL1pWYQQyWAath5gNSBmsOm&i$h7CJ=6nnZjhWQCxpLlLc z&|oB!A;UxKr#Anj^{VkzpBRF z{QQo*;IUDzB+qhxvGfu6(NsRGO?LXpW>cyP?HK>Q!N)E_2w0=+9IC%1(@YIjBWpV;`MfdJU`#>QxS{*6>Ma_w_IN8% zKk}6mpD9#ycp&U>yfM>53ZGZRSADQQdF_{+g868?Q@-|*E4R-%En4X|Y>PxQk&pY> z-b4N4TKm+pF1MrL&Ii6%`9QVm06s_x4R!qJ;-<&9Bokx zwm4qcb1{G4SiS?}eY|epvEnxO*s|Qg181x*l8;;N%ahm5e)H72`S(Js>O}1Sek9}_ z4tTp%a*=fFeg7@KGa53^Jn`Fmx{>d>n}4LeY_^nZ+_liQi6aKOE=Pz&4R9{1zRtWf zUm8lXao<02%FX7BOhcx{0x6)x*~yiFUWdvLaNU-LfgqRP2mbVSawcRKj1-IdL9a4an|lP}?YZFaKA;E%arcI(>D1SQ5Fo2GXBI|$N{FHiFtq-&(2XlAj*+M;| zLF9YEZUASFLscDRXO?aUe^Qc@rsg|4DCP4rN!_5w|J>V7qPqQef5{*R@&OnJDlWco zJh1JyK>lfY1o9^Wex=yVqFPy9eGc%v&S|;NMm{NIj_gaOpZz|WV+K7Yv0Zxechzf1}(M++tFRvo| zr4;lRmLY4tlz03ggk!L|2yv9mIWK)Rl$384JozrV@08=MT5npi^Q&iW8OQts$#QQp zS$E}Oo8-13j`l1*(C-y1Rq2jOu@?Bhw8)oSbkGL(6XVSOVHXw7G<|1m_`WZc|8x4; z=3bE&Vn!5f$Xu6UfY1i{$_??}BDz(Q-#ygtqLzpARew-<|Gs6}eaU$)df!p{Qr2ks zkg_UwVUk&-W%VS?omwOM1X71N{Fgg#$Opctwzew3Q(9Bin$jz6lkKLwTpRdV zk2lbD9m_Kn=>3N{dGOYEo~^f>HqI1jq(&xg%FoVNyvPoT3o~z5810QAO77( z9{*Lgvn9b%!&fCG)KNCO5DB~Gq9O9Ve9lb9wHam`Ev{ZwBxwHp|&Ik%_ZI9=(_wlzR+Ce9Z@z8xgO4$rpZqc0d}Jzw<~suCF=9+fB;y znv2}xp^hK8DA)B+H)q1`VOxIjr_d)nc%9Q+;PI5?l)mx9wocS1%2MBsA7x0}t7qOe zWT0Q(T1J&uz0n@3{YtP0AP-&-N0hQ3f8?$GiYKfGQufUi&X%m(K6f;E^!RcYy=*MT zWHaliI{cRH=qUI-O9k29dhNchc6Hzc_|Z_1uNW^^{9N8HL3WZM|0L%S7Iq9_8GQRE zF3gQcb{TkO>w--Fx)WsuLmU*NInOxy&QF=((S+oJZx>+q3hm12r>2?`s~NwOn3VZhNAS z>oE{S2%DqJyZ5zL<^urw54Oo^nMYUBMOz1-hV{(vUu{K-z(7;$1+|^JvEHO4xdwA2 zaV8@{{t_|~-f#vl^pNBf1Q)IN$W`#t7|ySCp@=*)N)M{j%LySOtAZ=!Wt?_O^{LNzeAH@@&XsB^^Y*3c&=rOtrYE z{E>D1j+5MGN#I2+ z!kT4+X(a&^lyV;HgH|`PIk}NX-AgveGYF29SyNY ztpt1__qg4ETV3hSab$@cz0~S}yyCv=AQB1pVE*XKv+=@cPtvar4xhew6#4-EVp@kh zmV&2du4nYsxCMzzPk0+K5w>2mlhP5{2NO&2F_H&S**)E$ThyTI^*@U z;N~53)WTKpr}sX;rFBtRnb@$U^{(TH$Gr~;o&qBZNl>jmcHm&lGo!(6z`qkqZR?%whNK(KdU+X>bPRDP$A1)F!#tV$2=n)xhh=(y-#P9iXBO-p z;s=#wv&uzY@R@gt1p1p`?nt%zR+L(^~}7B&=Yh#c@Y zxv%+;9)W&4@J>5YB^A63LU|MGH_$oBDze5pFd0{y>1j%@5x(6ZSQ?}mHQ--G$ZRsS z)Cl$3pvPF}qQ`7yYoE#L^&7G~g6Kzh)%jbpV0o6AkkDJC- zj(*qF-tTRi%JMcNGY{tHj-<_8o-Hw*brlweg+PT!1$kklLt zT=XPypY6-auNyWF2RbB_=S#Q@8*&m;1;_Mjm4W*!-FH@s_5yJ*fc2A4xi$0j@R5NN zk4&Av5P|1>0p#(;SUBEjSDPU(AXBc;6HtjN5B7T>anZF8Rb1z=J2`WnT}lG{E#hOv zD}$CZk1btW>bhT~62amZX3^(Kl+?lWE0S1wunRm6jw;o^ZxU&|n@=MwF48vgN(Jym z*>Jowq+UDRIpFIZAqNd^m{GX?Vz49WAPsGp;i6r=$=9vSESLD}*3TMDc2Sba zdo5IvcRTRoKu=Op$!w`6queG*JtY*(N6M1D1>4UlP9x9w*_cnZ@sQfmWcs><@#l|0 zh>cSqn6<0mr=UaH#G4Q<0*nvs@%M1Yn$I3U96K(_V*|Z%g18aurX)W>r3?INOF^vs z=G2z9>4QR^NL8X#&sot`O76sDgc$67n^MbpX=&zQ+u4I6ZBP|C*+~~{3?3Dr?=Ao3 zMXp&)mm>l9BFqnk9Z52$RAmI)l6_c@Ni`Sqx2MR#-OVX^&TO=g$R?PHtKu^4p98-J zk$pFimGTT z5E1fo~o z^wF(cJA67Y{3aoQC)TreabNPg?+wV-ag!?2MQE>y&!>7hS-yzzIemKg zr8d85*ry(7_%wj^-?oh(uFNoq#n3Mj=6g|Pfp~YdrF16^t%QE(X>J;KMGc2Ztsby; z(HZl(Gt(D2=PY{i>9bGGV;mk+Wrbkmj6LMT8DFo~(AP4z5|BI3JE%qC>u2nw+^UR@ zq+_hK4@)u*@C4K{$U~bulLst2X@$+yowOPAn>$IcI}pI*L8OUxKZE(WgU4{e!U#-H z!*$V)g^nl&Lh9OTM*;Yys#alRV^>$xFSh)pi;d@v>(YtVNJBihBu93~p)Lg0Jfk`n zA$cpti-X*c!AJ8%GJvLedqxK(vB?(78O9Ish1)16fnrUdXUqb9R9zuRUbeLIV{hvoqz(T! z@XImvXczPm0dFp*a>yrCXfpDll5Cf42?u#h=4vkb4Rw-LXlZ8l8A|35@Ji(Dh&KDI z)kU}QCLfK+I}yIe%+}K@vloh^mSVef!@#dP_99>B&-q)2j}D9+=?;#ix}fEfQRRJSUll$KhzezlP96?O(s_9XjVG-q_q zy?m~z*OPZe@37Z{+=lyUzmj+K4DrrP(BnuZ@{0HtX4jv2fo{>kK^SmGu2oG@d_bJu&%QAh|65$8oElfCp+epy4*keHt2W9nYF*% z)%|?i(E%3Tdc-pF$4?*(CtmxaZh>M^mwDm~7Yr4f_Ve2cd%?pYSZz^pr)EdH2Yh z3on+WOU9Kme+7A}g6av;w^6Mb)0Kx1$drXF6;)@pPekAf96%I|66|noNgmpC&Svs) zp0S=$k%tgQekgSAsuV@6?#0?#mM@3h!ZHQ7w^N4)JVh?abQ+se7iKRsjR2olg8OC) z9xzOQz-=d`m_Lr~&$&>92piHsUnzKA28D>P;be^OOB%1}1v|}zoW$k>$OzzR&_fCa z^j{K8LaO+*iNN_&(FY@RKj#(J)nc64Z8u%@C+^>^6 z!2}lN-ru#i{Sd=9Ou>K~%u19B{=4H`)pdp(m%Y@LbJe0!upAP0P$v6e^?L{kLv9dJ z&$l7)?DEy%5C8pQ@A&B>Bg1d7merDZeW-f?U#b04e9Le7ajJyWylLnU#lvfCPIHCGJLXO zq6X6>`MMDwzZ?AJAz@**jAW=RhX?ehEKfyQ8FR8^jI;9EgEDeH7Y#?C<;x)C1 z%L)1Wg!Qx5&#H%g<2^=eWp^C{J9?nnN&cj^(NK?UH5o@jp<>VzQ9Zw&qp6ThQZP;~ ztU4~TL4#opN_j%;r;enG*~lSh<#u6wILT*lX4|Z7DavjM>&u*_=Fb+4Ucbya`s|9Q zPnXhd@)}c*Z{2y~2MqGvX7~>vF!ty2dahY+ma)IOsj0CMd_eRd`b{$6)g8pX`uEHW zMFzGitRH2AXc1>3{Ms_4egnTf2Xx;scMVpxGPl%4R=Z97{d>4JQnm3`YUxDf!F+ewtD~UhEtdyt_5)$#3XZajycC{XPAwErM8|~>7n$t6loAtv5oVYUraMo2gG7NC}#=vK+iTdzllJS*491HV%+fn_*o#} zhW`-{WkzY)G#AYf&X}hctbF?B*&B{qPh1eNa$ z+z-LGgFJ$89#eS;mOzd^@jjxnS>#(0?Jm&oR|St^h;kMf=G^K7|K2P^i6gz3^=Jlq z%H)SYpcu@yv9YPC73&@T2Cw(@-=A+dIs6Cc{uzFCQwI;!un5_L&qZ8S?%*`8m_KG- zb^G89Gr2@Ior$Z=`+$Jp_ii^E-5wAfP`%nkPv3s1{Bu6EaS(Bu_o019yIybo2Piyo zG&Nld{T%e#ioA;kx%xDhQOloLC+z}TtI8Zfc1@a&>U~^|i0AO;tDIzaIXTO+f$4ek z$4^*KyJ$nl=}!({c>8CeBfh;I!;Jr9b_1_&!*KB_kk6;cHDVhzv)qa%;BQTO$FzJN z+$K*z{Ut=s5SIu3rzkFCTMG6Ei}rS`>1K(gWp}mi5$QP-@f-Q2GLXx_I2qG#r-2?b zplj)7{PZA{4-GS*6J`h2YY04T7wpB{sho#7@b5xP&NFGMUxDoeQ!VTXmDP=npNE!~ z-9@5%Q3;-mWbwYD9vfCp7uYhn0>9cZvEC z*n*Ssv&KdTv;}**x&*PzPQvzcP6*pvE_V=7_gz$yYo6w)aQfF+FRDh>LvL|_jsYT@ z6<}Y6K|+>i#sCQ#m9jG~O1lBsvhSO+eGmavcc{o0+p+h^;kLj&vhp?7m*MXNOtT4! zSXJagkUHS+TUj;I0#D9W?lV`{^*|oM;s(TVZ)Igb6|o)wV7HD2MtbGkkQ9=^Kj86T zCBz2gXi#|q560xmKx@L{uN%XH0vsmG4%VU+$fHxZ8qO{XRzc$YoU4T1g`5I`zuayF zL&`<|(TcH})0mSK1jVY#Kz^zg?lzWMlla}zHsGTl>IH_0kAH?WU@d$83YZ2fGL>Sb z7-&Lv7!ax$hOgz41C-r74lUXZOPIB*cM8uW3=i@EJq44 z`8kb>i^|pAj^32cB=tS>y)yJarjBIHj!RfF?LlrQ=!+*`NNt6HJ{t-J(AN;RaKl?C z#y&YQ=KUw+3wUv8xI1Jry`%iTv+J^`~Q=gh;b~(#o67L275l)qQ3f_EV?@^HPF!+yPMZ7SmH-er_ z@IBNo0ErD)9TwQZ51BkxR+iib3prDgf_}h7`6O_?8bC^Dwo^*YOhn!q<$Fe}#<)Dl zs}{F;fotjAJ)wMK_n?9HboDp2KJ<8PSs4_qtyu4KoP|3NGQ{eKFy5|AXNVr+MHAF` zMX0l^?5*}pmPVJ!cL5I_l=G3{2dG`7?9k+WNNvkRdjp?=84vn>*=e%p3q7N9bKgD{ z{DB+cXcwPLDz|jDbfAC&N4wY0M7GLa2sU&b@F^AO2~ta^8o`hGQJikSLUf9lPp%Z4 zB3r7#^e4bOf%ps_^t*m0znG3m;IxHkGn;QUN^fASAsyqB`Q;1NsdCV%2%Hikk`| zsVG5tUMW(ZgaroH|9mqk9kQGX4vprXmpclmE%)mXWzn>^s0;eTv9aMm=5TudaQ}(lV!gF-1`ih?-j9)#n0;qo3VrH=@VNw3 zU1PO@KXCbc_b4|gyoia^LzKr?j6r@8uxb_#0DtOPlYLx0&m$~wDH^pL{I@Nc-wCsR zkB5R_t!p!=TIQAKloj)PzZm$r)G+FaqHQU-S&r60p;mK3oN6D-hT<{WJ9G?$5&j0W6n12 zXtI0|^i}`iKqj<5HZtE1Uu_H=n~s(-C|Sf#P-SR46q4Rn=z|IXd5kQ^B;|8dYvS8@ ztXEh}w+@(`F&UE)=inFw#Tlj&j0K*rSj0TYeBwv$(W@Y3;y0{kpfZDk(@5-oXMISa z?J|f0H^N>;XV@1=;Ur82zkGBi_;8^gTm=Jw5~!awcnZIe&2ezaBQVKEc(3q{jdjrS*a^eII4s$$Fr+WTdZ)nS zSmd!>1Yfj;1|EeqbA!)LbFN>fA-pmWP6XRn1w*LVGX*HnjsEpB$s4pTi0cA@ zCg9&YBi)*awD z;HQ{p{TzhI&0w7pgI5UMTs~DBUq*R=ShW{#2-OFty-espN#ck!~3H z=@jnn6UZO`7Wli<$gLrpN<$LFhbo58fe@k-)Yjfob{BA&sswPryO~hqC&~oBL7=w! zz9Vlk^qxNg`?N1G9(Xj}AaI+`1jeDS03(41LKoCU@JB7B9-uaP=F1ED$I`jg;170a zJfH&+s-{9y%GMLm=2ii;JRk#o2lJNeg0ZX#-7bT?jpfTAqfVd-G6aC0`lfSt6c>~@ z2Il~wAWxXVZ0wtauc?HiHuSfiL*5+ls}441z+bP|;dnutKiuxOa2)`#VSa-m6ujC6 zeS*ztvpYqD!-jRXi?*fC9?2h9j+VBr3lLLvX|AmEisZ3GoJ7P&P7 z+Mmzp7*_t1$l7SJgNywO)|suX1fxtpwq^tVa;~Y^*l=)U_!a6jgR0H&X{8Q{PltpH zep&taS1K{tvh~T@S~K){c1}!(#TNpA0Q}*$ax5_ej8r5u{l(VSR_Im&x<(56718nZ z7I3qd+S`HXwJ5(4V~Gdguz@UCctt~stkPRt2#1$SmsF4|iqa19&fWH3`uJ{xao zVADf8nf@1K_q(Q7+Yil#X@IpSlPzoDOUQAG;3E4&{D@y(Ki}~%)gM1$#@ZDY z_7UPonPwgN;yIy3s=t~_W>c}$mSTsX_*%*!2wmEE4hkMKbqYeVC`UsNI78WBlio7W{qlRpZKap-?wJ0Q;eOfP+u4 zu>Bc+fEDEmYRCg9;IoM_~r6gPqK z!{5X`XTg(;;r#P#-;n@Y|D-UyXy74M;BX-GII<;l8z3Vo zZ@({JDm1N}ZC+@gXBw-k;Th$0Bb_>%A7Ox&k!k9eqsdfd7z;7|+B|rqh>l4h=bt@(`cD zU&vRX$^gB1y;vu=FnvOQ>z+llWL&AbC>+4z3?v8q2RmUY#zG6(q5}AZ1ZJXSu!&3L zRk_UGs9<&nx7eD(n)O5hef-1EX+R0>7o=Q%dmq+oFulDenpYq%x70BTc%^VMG33E9 z9q@Aj-Qg4~K==~43_DZLl=ILT*i4uOAP-no-!Y-#?08-#BvGz_?o~Gq4L^D%m#5HY zo>K5XVKE7-8TYjD`7P)##;21LzGM(#{CS!RX>VwJZ1}`m(z`>*cl)QK{m=MDj99`9 zo5o8C=mqJSt?#mAhY|Oq*F1C+oY(=_V!qtd{&02mw->>#RcmjAKGwt{U10^(J@RM< z_{}SO+Mx{$1deujhXasb;Vz45N?XxQ6FcGG$RrT74$~h}dlt|EanmW2H3Xi-?|vqY z=|`~)=pPP1Sqm1y=ARlklM+qCh;cp#ofXtN;W~jo28*sR;y{ahL2ftP8X?nD1@wVj zmMEkPHa6RzFTr|~-I+=COef8!6rjG7_Rh1n8czp!qv;^iA06tt4r@-gdlb_Whw^1u z?m!C-Yl>P4{)0&h7zLDR%Ot?t6xwZH<}7&Tx!%IhM?x$g_+lyz2&r4#; zHXktxG_di=Wy}T4tRYo+-+^`eVwRu;`6JIisthrGD$ps!f==v(6|)q>v5d`{+@vPRP3lz8)K}W;RaV<-1LQd!H?d=A-kxh_p z=zANtV3N)u(WI1&4+hx)C@YXJx2DkXOiBNJUn^EwRxgTFx>OoxF6`g$3#f`2&KI1F`11!3Nm9W=^gnx+FBEWk^R=wAuc zI|3|kwU`wh1*L>`cUq_|g{o%4Y;MA+f^ zEp34C8>h>=f&Cn`FDaa13b-@$x}bmbMS#`~7vLiZeFLLYux3hMkBQYypY=aFmD_iQ zZXF)J!1C*9E9XVvLQq>nMZ&Ni^{YYHDWt;XXfL#QDqCh(GJj0?EjfZPK8*$ zVR$ArejZR?)R-XXSE7>+dz}g+Z_+e}C5aYoY1)H0!^}&rxfS@c_fS_kTjQ4<_j>PV z`72j2ZY{jU34r>QZhHU7&jBfU)3K4;XOTaL?7yncP&&;#gu43aXz!PM55I&Snc!cg z4r0Ba3@TiqqO?FUEOjKK{qU{cF%7PkG~diuxoOJN+d93^A9=n1<3mf%tFZYK74bj@ zP`|Vp{h=E}ft{RK=!bM1dW+3RB&^RZ2+n~{!EA8@zE2pWqqn}^|3TN2{|S7kKRNz9 zG?QK4pJR_2u#GLyUu|s%F@R;T{$N^^cY{en6Cg<}KmAuoPYfMLW#t1bo~XALqd}XR zn!v-_kRO>r1B0`xz5SoT>K4%XPsWdo6pxeBD&DaW@jcBVGmP!PY{|4EiwM#e1Y8J1 zLSB6qc#9S}2${YyxJOM-0!q4 zq9JG+Nmx;bNl!Q+SqK|8&~#uNWpSJp`4(Tey&oFz6aOU1pW5w7koUHrCosicr2w#)EzJBW> zJgDZ%eE8rUoyJzxcs>Z)EJ7-Hnx@JapIHZU^J3^s3w3ruidHJ}E$r0K;c-HX7!Plp)yI@E=Dj{lnwK15lH0 zMLw8UFMt$O9)cW9OTdQ@9Xa=j!dXX7i8-eZv5B3qOx?u@4EAt-7cC%j-H6G@s|dLcEz~o~ z6oP|6WhEL5y@VF;3}8Ur^^IHp1IxN(YJ{~H?uduR(IpnYXBZ;{g(jpSW+q_qzIulP zx8GHHR#hRREr2~0OX@dQW)7c7@9)y`^RaoKxqLIhJQ@CzT`-9=XagVBIrs`;5|#se zsmVb5pyzQc;RHdj<-z3^&)))BF`aBd)DelF4(AEnjKcu>WAn0v?`RZ@Hx#PI7bcG% z!h%+0@o-%3oKk!+e(%uZ*Fn*45=0hOEg8!VcHk?fy#ULXfd%L=`yPP(o6)xh>b_wX zamPHELRvI3{D@~5Y^V%zhZp^= zqYLqeqG#+2MDPj6|J&!_EE5hxG8;yAu%ElSETlqD2xALK3>>;DhqbT}$gvoLzXzQbR%-mqv< zj)TkL+!pB8eapuV1ap@J*6uq4dl}M|nN|!+>kl6*rO`b8r0Lh6$!{eW8&~!hkfFC&*f&R9YO@9JEgZK{?qPXX_kKml zwJ95qDM|EFe@FTkU+%YVH#wDGH5;gEy7T{%_vUd?mizzkb>H`ZDYM+Opef7_m;$q+ zmM{x2SjjG8CaY#vu2`8%Zmo7s=aku^ovd(A(_~Y=iNdgH$u=9rY;(@H^FSOrGl6 zS(Rh#Qd=P3rOqLies3@Fh(1*e2iQ0owx*8?R!Vct3pO&9pbtNb<<4So5BG%?d!^D; z9K}>%^oRzoC_O$}bg~skWpR^ouWZhl+WFH*u7govPdBAvoOETRNZZiZ<6zSaBtM3Hn1@y*9Au?i9(uqQNdjHa7dq#Gn2C!{j#j#FbO!9 z@_xE?reo#B_Q-|uDWk8kP_fEWTonBUZKGS#*s>o-xBmWk*KBX)Cyw>vnKPx}Z=Nkq z*3gab-G-T}y23<3IaV5t(KR*gz0D-}d-z$|+zm@7jrTfA{=uLdoy+5s9ZO ze{$joZ;E)T#=~n)D_ZOyf%?W{v^Vwdy^wsw?`VAB6e6F8lr&$d^|j&Ho8!>_Je_cn z)k&JtaFGHmS|ro-UXjnzdknsUK|G3ySP4u?A}t#QZJI1q5escJum74gB`mt7{feq? z1H?a73#C*|f_X^2ox5`l_*?r7*5!GFKCY(7C`iDOQEWIBF)=GgN8#l2@y{f&yX+l>lz%^|EI-@QUVEEqP=S&E~i=VoqNkuLi2rn4LF)xyz&>sN=zd&Zj? z7EyY7Y8p}%xdqKxi=H4`y{y$i1+sqXsP( zmKTJ5m&q7#aXpIWgR_-|bsY+H*zXZ$dqx&6A0Aa?kqRVDK?EsYy034-K&J9Ha>g@?4ANgTBnpRX+W6@b5SO;o7>%qEVoD1HU-RD)h(_ zEJh6=V3@%P%OuRUk0$`1AJ}tP(6_pLcCf{3A4;Te#L?WO^fnVvE_Du$L(E43>W37r zvwOa5sY_&V$iNt<-u(3?djf_&b)HpSe3+z)&p%U_~ME%agia~T=SFI|H1EC!fG$3PS#I3DPP zpk#D^fR_ByG0T`T&HOjm4|I#?tx~L*u;`Y^WJ_Q_5lAr4MD^FF1`9WJO)x<|kY%FV zx^}TYC{LOT=atPpez71!l)gzhZYD)6bW7`1RTVT_Z8lL>Fi(NFDeHc>j!2TNL;6Wx zp91-J%~>v$nWvlya_4vR}_m$MGaq_{?((``f!L{`dp;u z_4>wF6luCXh!J|krJF`%{rv8p?Q_KN{?Wmt7ftsJfw`=wV{XF!7^^4!PYC#>&K4cN zn~g@i+Mlc6&3IqXmMG5wJb*<*Lp}lfiPABzFKmVanftw5uhE~_`za;CsRTTp5nMH@ zgkB*=Px1A$5o`X|v2CsulDA&!-Qs@0LMbYyCyVYvZ5z3IbuEEDy83fbQg-+HO(*X4 z{vGs@-tgXip5?M8LY-iuzQsKp*uq-wPRTW_( z2=C7XQ(u7S@@8Pcve+m18Rj77ZZWoo(4D9TPBREQl9J*?8 z(G~XeF#3c?gmw+ut&B)jLnLcqPGU9-ag9!gmvGxs=|qlt2FQPekE{ykNjY?^HmJeD ze?RVCoL(5&Yx?2Gr{rm9;QGj$&-53Yu2~djTm|_HtX^w)?(}R(CS`HQ2{aLhI4?ZC zaSwwP_L2bPs~{th^!O$M*3$#jfqa;+D85iKE(m&m4)>SDEbRw9lDB1B7Cd#I_Yu_8s#%0?$@{AKJdt<6Es zE${1c77FmyGt{JJ9gq1eUBo5OosMLB#ER!=SqHxR+pj<3i0fvtT;_mRBJ{qPeh){c ztVN0?tE>{KTQVm@EM|f}jTUN8HEUi!nz8@nj>)OGCr8sSCC9o5WHNqn2r4BjN1u z#5}qUE14Z|4^xz=$66zM$|OiIX!uX=gY!GsJ7~!h6JDQ40MRKW?TFwP6|W&H7O57@^+LtmXdnyyV*Bh7YOz}j8jtS5#i+N{-y-ENnxT>~HbepPhDAR@@S;^tBoG953eu5^C-`=% zMUVxD%WdUL-Ph`r zOX2?K@(D(t&F5_?<}aF^!UjAMkL@{3(m|*2l2ZtD;rqeUMK3)!Xr>Dw%|eW9kT~SU z&P(39<12G9(-F6?I$V-qW$y1~!4hC2?_0en6=O`{Yl5VQu0;CFQVV3sfj=U+C^?lt z7dye7y>{`E!Ons7q=FbUOe}=pi?rvmzpk%;gA%C#AVjJX>*cK!%xz;M3!5PrW%hvc zpFu|O#3s(c)0tzZva>#jibKz@? zUwUKm&z&65-%3)Z2w(?P^UB8j;LkZqN#uDPR=cIR*>RkM_;sXIwvcbjCugOr!Cx?^ zP`NT~WaH)qdk%bX&q+ZhfPiYK9i~pR#`*OCB(`~!e>?DDflfOlR=j(Gg1qkQZ8 zF1067ZJ^EKpgTOZYT5SL%`K?mUlq-t3V-c^+aD^JTNr+s@rs^X$h~ zrt~oa?jr@T_R5TvW48vgS1uBxu~LT7Pv0En%YxFT>aFvtmZyWkG1X`>fzwaj)HX9Xw@ZE*rV?xpufnk6|$S zJ-r}rwj7D)3KQ6yJXHkO%|;?BtA9i@5y3Pv=_L?$?Q8qxZ`O+q9Ul&`ob4sxA89s? zN4>G?IZpBvDW`x9{v@9S;|d(W$#~ulF?p7eoUa$$W(Cy&FDIIV3SNFJFM>?2P2b|3 z9<>bSzer!Vaz=BVFE63@2!##tXR8P2c?8A!itOyO6f{)lESS4Fj=%AQcl-wrtWWg7 zg8O-NZIG#=!hC!)$QQo*;a9gNuAct#FJlzc6&wsyRe$^wNb(RpTZyV8eI;&7OKZtk z{RVnA2lTZHS>Lfq_b7ub)j%Y`4ywn_{Ej8dI1hLRt&f8kpXKY9e@@=N%J!!`|2PHR zMWw2ln>H1;JBP|-;BV^v==)SMdq-+|s-ZKYnSy@#uJgyPAHB`4K0aG{7vu$(1!P-i zT|qHNE3QKRkX5m4b;Jn2^y^6$Q33=}Nu4z8xx9&~)GGeoKYpX2lCYOz$~a~As))x6 z3SNZvPnZQkZnZ|4_j=sC6BJ~=*_*C3(35gaSHGS@!g!OOh#r5QD>9%*CKCk-tygdT z@b$&vpM~-+7pw=?uoLlcPxClybcFj5laE?5maLPFIig?tv!K z2mULhw=`cEJ9F0cz1c=OAW|((^#v1s9Swg!IC&7hPqX5IvmJvP9`dQIhbZV|$4PI{ z-&5sez5d$VNx;O?3vo!?#?azMCIjZH(XGFADX>^vV3A4%zry?77aQHln3&E$eloPb zBuRX(9^-CJBc2X5g4EKp}=+8g|!$#tkfJ~Ic#FP}2*5B3W$$vpvz)4zU z2>HWubupxy!By+xRw!XTD}BRGYUdBPZVg@%qrhQ45mUngPnrDIxH;HM2mc{>b%T%&9&`o+!|?q8uq8DO znw%^^Gd}VkcDaWW5~-cLF{q1Zfg0bp*&uzzw@2 zlq>`XvkW5O>AJxIC%8*JKo%i_^$7mc!C@0P#2{#d6i|M;QU_fYGJ$f)VtI6*0<=&g z7PG;tON8EM1OTE6n48d@CLw<$%Vu)9z~u=zQQ%7tk&A-|GX$FWmp^hC{FMKG;QQcb z{L?!b`hV~l+^?X{hJ2Xtgvx`@A%86VmVCs&{(rxXI{$1>Fert0L*M^ze`DzTkA4He zsZa@__WoG~{BJ?kVv3Ck=?%;+Qf~#dnsXWK z8`2B;RAfGrCnQO)7_m}FRKE~HEE^|FJyl@Ogm{XABf+!$AmGwP$h0DYpTKk}X#8Xd z_=TiG8u0aJwnN@0nC5AS?1`1Q2_!QUKwQ`^MyvbPSF(KB%Y5xAKHd%RFHsqkeH+dq zfwYPr@=s~Lg@1aaZYkZ};|6^(hyG2Id#B-J*Un0}#tQl+t_h?LvgzLZMBbqVaQ+H8 z0cn$m5bj_W;a6}!p2HMiS7Hz|2NMYR z*otVxth}>ahbH_bOs~KuElvFRzNa<=)%9}kCMN&V)Meh=b;cEoxMS`o+}*tfdwl#v zkRFM;`^*9)pDtcGEkmLmiA9s}dXX=me?#m|*IwboBAgPlj;WLhDcW3;WsgN{);4MH zhzw=BjE?NgSVXhCCyIyPYfX%5>Wczn^5<_4Kk?(GZ+G_${627NZyYila!z&)nkCzQ z*`wUrnSpABQMML~f5>0v8A>mUMf5wbCbIK2EU`%IS2E*J23#z^TsBEj|3;C0PGTG? z|6~&5vm0wW6;I%;&m4>ed$+okTiL?+!8T!HjK!gk&we}pSSJK{2X^;Pe>M{Rc;;Sb zH+L}TvdM#DK?XABxGakjb|u@{cnp3&7AbIrkc~%{=V*bsOc)3HaQgC!+;D~ReB64` z)>zah*AJ*th5Q6Eo)%Awas#DPw;p5MH)D+5VQLZ^sIAJzCYZ9}(X(a7>_ZLX995i?vh3($v+yQx1%|LW{L>#k|t6$|%| zGle8engf9NA*Lb&35dBeVve$0yl8p(a(Nt@maa@nPgj=j$}8V~TNH=x!%+!$u`%&L zST<>nLnGCK-C@Ldv}MeC*7i{x^17xtyG?^#`vwOpJ3j$=nml>>LHEGmKvy^ICVbxa z32&A&BFg0ss2}s}n~F+it0RE^j&D60r#>yt8 zg-?1-mL7-F(v<0GJN=}*T!0ZL+u})~I z(|P9HvSIbh<8jE5h0!?7x+~%#&<8d~BJrj4r8<9 z>D{WJrONL?nQ@>`@7mTyVd}5tws3@&Sk&*Z6JAGNFh9{D!yIvFg;+Xw*LuNRLHPRc z`{FpHysuP1A|3qQAXeC6KIho($Cj1Z7V)lMPm?S__a`Y7x7h^};Wa#sOU^_ae%s~w zZT&x8?+tgq|3`5gy63V#nS7Jqn0VKE)Am~&vi^`T@vCPrV%DI^H3;o>jX(O~Fz=Sj zdzTl7nkKxr{DA>~Q!SqS=I>J>+ly8_cq^It? z(~rWYB9F%HvN>s=Aex@>JM1^(Ib#9;Gx`a2BR~1>;W$*oi;6m%_`_lAj0khYw;->B zQBKg#LVpr(nHQjo)c@(+9re@r!=IxM(te(b&N%NreE4uIvjcUuGZPn}#_}gK8Tz(9 z)Fx*ZGZ!H4l~hsU9P9njx$AfBERRFBMP+4W{5$f|+q;-6q;ZJ#Kn|J(WIf13|HdKF zdbu~rIJIxNaGmv9EVSQV6~w3b z?)jf-;#bzd{2#H!&u;Y2c0~ndn4=aT*x3GN=;#8xz{Rf4iKz&#h5!1%^e=xqdFCHK z+|VkY`++ADYGojq})o`J5Ms0U|aZz`9Qv#z+bWF2>?d;wZ7 zz5lBE{)1NqlhR(5y|w^=p|9trzot!B#_H0QLvd)TsDvf5MzTtU!V+Q0RJ5pYYGekJ zqNB)0k~ErWsN`C^uCuo0uyFcxNO@U+^s=h9+L7dhgyggR!@f8aU!|tcZ}P=A`MSNq z0DKNKdC&R+0e8369du2FOy|(^!}q7YKXvx|volfIGrzdj{Z#hP_gwGq_~-l6(5%@P zYLeTs2Kq}(s{;2!_5-L4ptJ%2=*3l=7ohx-#F6sEe8~{&_9Fg@1&GhSFS;*!RV@;~ zpcac4AaS??WS`@hs|Y<)QU2n>#ih_Y-!t9Lfw)A8Q@Q-Hb z#3JUSxVk4e6^&PU#+rhS?t4GDesJB4gS>l2Pfgl*axfWyx7Y&Y`XQuU{@?|Lf5PdS zi9-K1nO*O{7npFlp#2apKXmfsp;NQJk8eo62>1&?U7mylRxqIe7XkeV!Iwg+y$HIb zn~IqE%tbq)^NigDE3^4K^I5aw>XFne@N@K>Oc-XZ`IZ@RSx7PD6#) z;QrEnoh~IQDWw+dmr$x7a8450n7?0hxSsKNSo`2(c5Is6XwE zkn!;!dnUAVLQZJtSzHM1d-Q~IWJ6DA{Qvj!@8AFVz7FiG(6>VmbO@f1eFKGupj?lh zNA|;?KZeW*{eSG0kUao)*ni*87#`~X|9bxQnfk9H{y*CX|E(_zDHuOkL(z5-{i;N} z=2t9t{kIuEkY-Mk54;gX}Hi2oFy3Jz^xm=ZP%(P+N`sJPH4 zkpS8xA)cX3Qx;M@oA-Y}2y}5QxUU0{N;G5v@=;G^c@^Nj8+QGoaMcj*D>YM*H5K@? zf%-iR_^o8zEgK4D>Dtj|`!TP>ye$V3JDxMl4xG}kebU;WNu^BRb^)PDHE2QMyo z?=MRWJ1;QPkc98crK$NRcF$}zva&F=9A;@rFgHd746p7+A;L-C3A7ASi@fKzPc$=; z1?Z5c5L6EiiSic6C%^zEy4ACB`YYuTUUiU(Ah+9-r>El-DyBzxD)QJZYUDSK$o{_h zVU`g1L>N7mIz&EOdevcPz9K@$z1S;xRMollU&lDymzk)goR-~;(>&o8MDqZiU;|8* z-t$M$vx{*y2>9b4nm_8K0!j|#VPIgeA^0VvvjGV);IpT&ED(gCg`r}-0HB;2FlLE%@=Uh9fxfIY)v^H0by)C({@9P%5o+9eneRQBRFbiQX)0eRv8?A{RwluVJS^IqU1wEL$9QyQ{ut(eUCB)qR@ zafD|p;DNke8)mq&^wsG+n@|GrDZy^G4KQ?+YO(?#i=Om)B>A@lX+T3PNRZKsj+4N4)rS<||H{5$o0KyoHjV*M7>Pk%1A_6bqWGjiz~=&ut{&Rc4%=?gh8da2Tk7m<5uuSIgvaV@M||LQY7;^} z;FgLhuK?4VAebfKc_E&QD4!sifRXKCTWJiizZv~fhKgT4E3>@A0r<>bk3a^@V4omZ z1~fz<$a7g$99zh6f3U3m-XMX#FS@3z`cNnT^p7B%Y=HmDb+UMswgrJ!r6Nf zt&&lW!t|I0(3R7sP$}UN0&I#F0jZk^!A@vnx-lPkJyS00p}no-1}Z?``E7cNlmU2Z zQP|e=EjP+sqga*;;$sBD)Ik4VT^0VYwt~h+wg zzY5}Kxi@R9C3i88!RYBljC7Q_@o}_wNoK;9{rVe{@^m!j+X@K|d=J2$y}>jwqHQ_P zU`Z0COZ$BmriTC`8OnxL_k*iK2Tfifln=RrKt$fBLXu;L?kiDQl>hDf`(J^i(pDM0 zT8u`8T$@+5A!17z)vj-a`>Vg(IGtJaQQO_|t9t}esNdovk26;D%TL{|v-=3-(Sf%Q zkcsMaT~17z62N(e#TVgvjO)g*A(ja0_Xan^nCFi&wK1XlUN^&aCkRv@H~;~osD%6_ z4Z^-~3>}S0#@!G2Fz=QZe z2PBXLH=jZ}bZSNj?^+Sz)l(|NAf8+iVXK7xc|7^vst`FlgGEO%2r?b`6*Ha(^dTg* z)yDuwsMYr}b|qT=LnMP=3Gt-P&dqn|p=)q=JP3k5GDisnGRq)69C#$qZWvF@!yqgG zO-D71<63Bc*TFA4U7Z(`TMz!K>We|KKV%elz8k)Q+E+!fOTnMM>WeP{{|m`b<*VXs z(EnA(m$e7V3OC;vRER-e0mmf{>|-LJuQv}eV<3~kZd+YHf8VAHlr~j8=rgVmfOw9w z3rL8SpdYH0NKb{V_n9KG2-kzYHkb`w>#Bx$CbMfC{MmbzqAx?P=0m6EE1Atn$Zt%@ zjV70NyCB=lY@whtQ+EGucG3Y@Bl_~^NB-i@frf8pi%zwSo0T#O62s8z;0$$@icU2^ z`&ONTfG1>f-{%)@ypgCwF6YV_4p#}BZX3pZ{3Nhn;AFocm+yPvW}EkXD z6S}rxm-2;GAsRkF_QU*uFC5wccwtyN0w@ny+aX1&Il)M4P$Gz5NJOk^$Yq2s`hw6N z6pT{<6#z&n3@eleIq5*YYI6a>5VHaF&04rdKnVkA?UXa9m4^JA#vZAA?6hr zF9pyEFhz$?4F;X23HUyXc}mm*)SH9K&`5wjJQ0LakVRM15Wfn3U)TWb`YsrA80ygH z2(Y+NRTP$27;76=!|yGF3*h(zcHX`QGsG`kC~p+}|1d#0kkVRGURPhjU zhvca+3_(VfYp`SvXokoI_h&F6!jK2~DOqh0@1Z~l9_AO`G7Lih^TH6b7uIpZ7$05> z#it>GF$;=DNXvnEQn0fSGI5lBgX#j{NriY-5F@+B5nW)+rs$(7sJS!5pzDLV3?a^8 zyg?Q_sRpjMGgul1LADtNfof`;TX(h z$WZv#gHsfrEuKAH!#j9_^jBwtzBFlCy@A?zaY}Y(EadhbBl8SrFp2|FEF;H%< z2d0c@^R%ji{LdY-KX$DEyzA61f~Z|NX{YFlpzj(TN0BQp)!&HetVoz*zo)FcA)R6zqT)#YN8`5xnOAe~k(79m74WX}%B%F|?hRuV z<5gK-;3y~Tr56**PFLoiPp-5h991M>UcQHFC8#nA`~rEiwm_($S;vN;|&?kD4Ijq zNs|GVWb)~4%O9VtocvV9FJU8~|92xD#-;fc(sYWypF^Pge_6aZ6=pCf&kD|54!;4=Vo&@?A{vaks`O z)Qc9ptIbQG9FD7QF;{ZYH=)q#G9WGZ#1nzXj$IoySA4zdrz#5id0qLWic9>BhblKw z|8fiDh1ipKXL~%IT4rvw*20WQ|(a@{0FU|w`4x>0x zs(=9JAGmJdd;m%)xJbu+t+>y(|xqKp8-(4o4~7cMur44>htb3oqiTFuo-nY6?h zdYI+NYdAW;D7BvAn|m*O$M>hZFdXqq+GE4MT|;v}m~&|r;IVGS)6cKTjD))N%TWuC zw3T=NRX5bU5=YU^7N}ZPTxpx%lu(JIsOZ2tIZ<4hEHse|Bfx%ZE7Yty-QhAEA?LWV zA;P6YKO8CEShXRuJh{h%qw+-08qW^R`fJ8}z=ew=Ot68B%c?Y0j0%d{0q@uxI87Ee zJdCem-kPhg!r{J%g%PczWf$mY$XJI7N2!@wQJ!T^@`>|{c&|f!6yJ+|IpOQh%^&OH zgP?T?_um(A_U2_IbW z{d~Y`$m{uHGOt z)WzG#IvB44&9$qHBeK|gfmEdd`dezLeW567D78GZ%KIXaH{^ty7R*KoVMeIbza=a2>0{H~pg_*;;%SGD2mm5URHZIh z*fDH?{*&qH_pIEM6dEsvj&g8Rv!aO0)3@fgmae}z5e@ia?KR)ouyxX`SV>+71^UCl zRAaogd8Hj~m~af{C%dZoht@>Aq1ifCB8L?;KxtsvxRlnUbK{L#D9AdRT8Lj~hlK8rd@>!7pHJNMy_Q3}+eGyyU$6nlehlTpWf5k*4TVR+!(bzmNAIcP34pU60 zSYkRC72&9z>qyO`64AM_-Rdxq?~IFm<6S&0Om;tK1m5o$^0>lcN*5;%N7M5<;eD`L zJlFCCG-Av~wnKaJe8oCv0hh+(PgsO3^ybp|?M_NlYOhLznXa%9_5K7wJj>eVS-Y2k z%L(+PU{l8Xy^WtNd3kK!+zI%;B_`qAHAewTZ7D5H0r})|O1Ut68QqQ+6P!P&u(LlM z=%_ql+%Thzg)Sj~x-_a36+N54nh$tf6;(0gC|0+EZo0CTpTI)Z`(qB(EBEr4xz960 zd>1zNgX%m_j`L{T?m4Qrpgd}fizrPQrgL(*L=l1X6Q9-pOze5S#!pthPq5Hv!kbl> z$tvaB9%VNZ&Yz=uk|LI3n@a0{?SAalcX8x4wYcOtmX`KtP9A{S(dQeSd-yh{W1Xkj zePt&LHHCNk->abJZJnsP-U9L3>Pc(5m@R56`_0bw-rImTWn!JZ-tkf)K1d`$`CWpg z)3wL$3~m1KS9MGEAL*1{qLWY0UyL|rT?z{oy-i7xx};v#+GmGvM6HYfd|%aKx3JkrlF(@_K(OB#33cKyosq7rqPSuI zQgL|NEEYX4r?=IePl5cPojsTJeqW24l+;`=CeUxse49)hIAr{1!qGi3H1LlI;4;IB zMuCFLXmv3`|3DyjSpxtM2BCxX6Jt_4O8XMJOmq*Xb^spR-;^D2h1s(0%_b}SzO!Ee zgveoZ3Kr@X$nRn_(x&2)&8X{=xf${+hJ~sGtDKr1>>ps%zH&7I&=CE0o$^~U#bv1o3OI|e2Q z@@o}cf|sXjH{BVkKLB|?1jNI1zx486`8;gZj*4gML4NhETD7Hhf(A)VRj_}!v^JHa znM>D)Cd7-QFgi(V9mnz)YrWYiYS71e`TTKY)c3}lnfd_&8UMLGt@6#?&-PK@UNU(x zfwtWl`ieH^)$3i)w<{2wU!BqFFsdV`G+~&=ktG3sx{2ZvWOD}SZ7M4^gd;6UgMqP< z?T;oL#v9gRFHlBwG3Z7L>=zTE{)8mX%`p=2*)69U{kfg=8i9!FhW*FR!?eX5bxNws zA|8PCF5^`NGSn`tEi~}3A9>4$)0&&bjWQ$a-Dwd7`t-!{n@!}0e|d_0JFDs$xMg8! z{+;@hP8ga+Pp|xiK)v0cEP0-~c12$W+VSo)uwS%s9kRR>J*EqZFbkER!SSU_S~J5@%T6GYeD~!p`4b4_O_6Kw#z^s9<|#wCnTUF80a$& zIpdbYT?!skE7lf+BdZq~THVcIe+nhECtL50?}4fHj&nqYQ2P~Kp_x4uYX1Sq-+@&w z*ra=(>x_P-5cX3gMo1DeqA3L_(K-{9@9HzU=~_&!WBaSJLEhbAG1=+euoOB~JO&Bn z4>yP94zMT8kf|4v|A}0q-YOSNn1>s+EiN3js_hoh0L4J08gp`5aTIWRWDalOM+}nn0Ql>D;9CURHos;g(1rV+v+e#mRt6#A3((Et- z&HHBmi8p3riJE^J>LwjvA>;KN50zr_U&l<>srL!g2m27Vc-tKq92gonAM!wqK!`Hh z^>K4)EL3|G-9U_E*FL+Z<{!NV`B?c)=VUz(%ytavM=1o_wew`$%_h_92Yx4}&;C1s zx@YPt*X$0p7lh`~dU9<3FjRDC)K?PdQQ6OT>f@mG@6KYOlA0q0Q;wI-vMK{?1p1UC zbljPe9K9yGwiE1`M}uYgbCuEaS?JHvnlvNwS-oE{P5>p?o4X$k)=Aj%bimCKaUcfP z@}oXE0MGods{icM^RU0F<<>uCte@*ZIJzb?V6X>La{3aw-H?A#A=SUJ<^{_Z>cFm} zFQov#0;bJUVI<5KjTpDGP}(as4(I+8sYbI;IP3>Tq&E-z9&yr{w)zR$(mbG6tN(R( z-b!o!StP)rfz#Zv`f7U=hw53cEbOe zh6Gn=pue@aI5|o)Y#}C0$AbOi=Ag-Zj@?SduiZTNzGW-52lSCgbMNGaCSSyc{fZTAmvG<;xCn6~tQ6R9z1eMj>i*E+ zbh&8+`io}o+gat$&<^B1y=TfC(4Ps0g3e>p2}@f-dp`?lYp%Vg92{|s=N~FRd!9hQ z-^6A`)@)jrT&_X`jEDY^hTk-ta3TgRfh-djd19Ue%#-w{x z4fLNt6xgs*Ve~i?kS}41uzmK9@TaG%-cO<}oeX&Yr|ZA#p7ZL;LEh(KYge#Pe4pj^ z>UZ|bYki?^JUXu_uB0y8IIIt#Do5%6Rz)0kt`_z~Op9pIy&1W7u4lpk``_=$kMB#U z-D}((dGzV}UqS!KTRC`ovEa3etuxm>zZFM5N2`lrP4YWS`_g%XaK6C|B|?f}F045^ z4*ls9Y-ZT^Uq5zeVxOrN_UpfXCOUBI=clXI?0rhn1>^wx3SnP*Dd*Atkp=sQ_UVG( zdU?x^wj1L5${`+<+cam(bY;BlGqK45{(`ofn+)5JKl4@3Elj%ZB?9?&-{^U8Wb^4n zbZTKUf_pl)*XHg1C+*d34`e@VUQM7|A!E1cH-kKU)?Y_15D5QH_)T{zv1adlJ2fBn z!_;&4N3NW7YM<0D_?!phnSV5_WX?L(r>imaYKfje3x8Z*p78OciPz@cLMOHXmBmyD zqfLsQRGYW=w>|-ZUS0j5!nW=B`I1LQHGvL-Ssgz6@Hew74`=IT1Zqi59ouzNt6l8P zrxcb$f7c(GzyCzS_56w{VY4^D{(7l&(%|Q(ou0fazNSe8+Ht+;%)N>W;gDRBHLC*r zBX>^htPTu!3Di&`mq4}0*rr40 z_r|5t8#~Ss=-JcesB)=c(<2uF$h&#xhQ6X3#(4UP)r&D`zem4OuiY!5)xROwxuldp zQTIf{S>~Gn4s?uTQXV%h6(&%tLSdPqxDodeC z{Eb=)>|f(l^YyS>yUu}^Y6+Cjn0b^@KS|JdzWsHuDPcUO@QSPMJ?y|QG&h>35=f~p zsYuhZ zv0wv?M>zH9L;TfE$W~mGb-9@h>`n33I`Q2T;gf~j7?7{JC}TB6TkOBq5dd?Nf>vbg zi5{4-<^2;)^X82sD6e+Snp;Du=e6;9*N36~rpxm$b@3v#8I;wQC9RO()d|)st$ZC` zg?#SqtK;kPV zfcAmegcA)?`tr3XwkLy)wyb%JlHfZ`NOxQmZlA$Ml&1DN{}n5(C9k=5KgdJ2rm%zF z$P%}>`AT~Zfk2zMr3<4P?dP0yD>!ZMJT~^(ykF6tEzeJD56f|L|@>efU?Se@OM4*Z3?18VGZj z#hB(uoD-;hARY2cNXF;s1L8(yLi4mD;0M{d<>qnb?6+lma%MeleVT&GpM-Pn>H6RP zw(%Xy!Otj&x&#lO_vC>$%lCi&>J`Wz(eHoe_tOv=U%NT}zU2V?zGug-GcR3fnN6$s z^2AEmFX@;zw6#1klHh&I{vDXeMA+)yzQwEE3;x}F?cJTfXjuiO;;V@(puVT-!;xU_ zwjB$5ZXxjR^B*k*%Xc?C0}tk&AyATIkO6ACgM*@bAR{iLL zc*f-X^t*woS{f)9;BTX#^TpW}+Nce+Ifmzw$1j0Dnx6+t+4JLdj}ts@(4IK(xRswe zUx3lP;p>3xiCXy(UtWY_*Pm-!3I1-xBKy z+Ew}))|@&Et(M^0weUXpD;l^%=)5c{FLoI6qbv(fU+T(ZlxB~d4FiAuf|kYA(RoE# zy;zwcj|BfZ(E!$JR#;?DMwYVx%GX=Gy<>1y6eVgrzK4RiSckznRtDyBRRRo=MZVM$lZJp&XdpTRb#^GsgtU>|Ui!TTX$a|!h0R34vQ|Ha=nT zgjgvd-_2s-S{TS_@zA9tv@d|WAi`&1(n+g`6u|eL0o02~7Bk@SSb(1H|8OrOCZ$U_#IjeNEn9%QruzvzUlm`bsDtaz`2nn%YVo|`1*aCFOKgfgK9{7Hw)|H-mz zzIK=#;qTI10r{5w=Y{@z7I9|p=OS5_pWA6dL{A_T2 zGx#UgWWPN9dMs7mvvJ!WiW1;A&EFS0J92Pce0wJ4<+njUOPS~Dx5H~KvvjnVGS zNM%c#vI6k_;jNC(m=aWLH(;Y{++eTt+%ehU#_Y^ovtupzAC3`T2`MQ-*#TdYQwjS! znR&i@SNR`DcXjVr^DPVIjvpDK8FFcKBq`Ro6{zO&_9;Gzx@x3vB!}r>A!|Ru%SmXV zP;oPb3;Zx~QQoV;`qu2n)-7$iv51W#svcy$f8vg+IX0U%dX9~huew#myzPnGB)<#a ziiLP?1ph6}KWHGH;jXi8W1}g?7go<-#~_JT|TU5z85low` zH~}xQveCNYY-?UAxsHz4Bi+kvWVV;ldPcTW*EY4&3~*kt9uv%OB%@hWBU`f8B}T3H z-&SjET&# zf=sd$)4=%fBK^|Zyzv&@!iZxy?!@WYdW|qwDq1=V>lD#}`miW#}utZ;xN{RkB($e?o{u{;Q4xZAw3i8AC z%2G{cyxdi1-!VMOMnwjmW^zA|9#nPN_qDT;^(cAGO(baGt}D`I1H6yr8X**sDmni% zrcVy?;`BL+BWy=T66yRv3do14O()-vEWm)EQbY<;f>z)&lV|OZNz7nY5pbe@o zmN(kpuYccqW!9u`lqhUjASICU?8W!oAJxqG1;(qE!ex~tTaBa5_lYPr;A4a&kF}nc zSFDORY5`ASQSg#|ZVMhY(!>ag;k@SOzOtIurrBnynEz%vN}k`BYiTAnJR@-XYWG7J zEjjT1BvaBXHotq?*5^6s&9&c7m#q8AwzT$_{$V=@y?Z_41mU)q-Ci~^trX_pYn!@; zSE_1TRI)6Zv_rm3g10!p?>qX$>aa%~x^IEy}yfOcbFkZrkI@ zUk>$41+v7vEa19E9-D}RiXOWWZ@}2&hlj?n#823$*3VVZRMcuxkN`F*2iYg$tfga| z)ZXm#$CofUC^N?xH}nFUB1(2xEk^*4RPKCdDC?8fp||P6i53p3bQPPMJ?_u;U^h05 zpW~n)C-vLHZS!}igwjQaW2YG{A3 zzpZXWg#?+=BQC!V9geIPcaN%g27mRQ_P$8e`f>ZG5@yh0_Lj}hwQ`UjFX{208-lAt z^^3Ehy`+d~^x>`7_<{h>3h~`Jp=P)Hf<=Xq@Y^^ z0k@CIlZ#T*bp@C?+idaRmx>vOXK z18>bBCB!cPSPnkINkS-0ibQL3{l*N9M(&N20pKAL<(qwdJ_AzI0SIZsr=ll!2UET~ zfA8aCckfDEGa`|y+U~`;zEYD26IdLPsD)x9a3Pk;&&%br+3Lt$8i1HcDJ|Q=G|Jx96 z7~mV%zoDG_+5!`>p2Ph^PG?_f2HasT!r&%r5mI@C$>%EzJJ`IooPZJVtQ;P*%zM@| z;`{vzQma5&FMBDu?`5c5jQ^{&;EQ89581fwKuLjy_z45MQFK^_*JU?RgriA#h5V&Vd0z|ch2t$P4X*w$XV5hCk) z*A6gyxcWZ)%|F5ZaLKpdG<~@H_Vf9E?)`bauhCIeCf73T6^UX&ZT!|8xH$|St4ACk zulwf7(YBh!_V-K44QI3*zIB^h7XV6IBa=&ih{Zx9tCaVbYXtmMnO$-AZgp!>b|R@t zEDs*kfx~A!JDYs%fk5=AOj1%_SQ5STJ2$Ac1Ua`$WG=F+FPt)0c&?TSJUOYyMpKro zC1OBUTw);!#X5vJ*Pj>0N-fEx2ELCTKPlE;kQNDZ%;Eiha(p4p!t=ZK>+=@3AV z3Dl_6t6L|va%3e%gr*laQr9+mcRq47Jh)#(+D50QA}Q@X6fGkxcqu7fGs%uwE>)Y< z!)fqIY+f6;&L?=ZwB3Tw~z|#H)a8RZgYjqCRdq$3YKDYCTnZL+Tetu6LbJMEu00xGc=AE^`d3`irasjp1(} zm=L4wJf)t?q)JKkR7n(@J}{z7ytK%pJwH7&oTiRc+OrGbPC!PKRBt45{ors2g8AEi z!iF=%G#6!w(|KU_S_e$i6sY%22~!LX!%cqtKXn0QT8(P%f^O@r2oMcY~=MR|u0EaGrb4yz5Mt#o!gEIhl zyQ!8F$Fs28HE4|7R3FEGxUN%qK-iv4H2-nCc# zt$w6ih`XFST={OYg7sD|tsg#Tr@h>|8Z*HyI*7943{>P`n4=z077HH#p%vzk^W@~v zO43NJ(b#?`ttHn3b!Y6aRp7`OmW74dM(~z!sx-pp=cyjw;yxDpfn}<`nZ4T2c|%%i z0{5q%rx=E3eKx^lF?d>o&36*wh8f!zmh#~o>;orn+KTcE;T^|~jSdaq`l?VW6jKFF z=cmJrk>7=@YWu9AshTVIs~OZ^I!ScTysEOYptWgmw#1XaINoa4KiUcNa>hQNCvMH^ zZJ+UZ&HQktM-wtg;DwjpePt-e{%QSJ6aIXjl-fRanJ!gKnxJ57^F(2!?idqt6LEIemz$ohv=l|}-ZDMwfDgs%Q7NzGrR*?oi!pApYGN=R zO7^{Zcu#QLNh80YdL1B~YKqaNjs1uq4ME)VG3wwWBRx3^{hD}>Ib0spq$9OfVBLutC@Cfvw4 z^mQRVs);ivnz7;}fXfr8pC~^z4|Yv6X`kDnqP}23?7TcM=tP@{DHKHf4$gt|rR*=_ zsf3K{b*Q-&4d3Wy;#i_WKMFNyoaQj^7;{2*O~!O=Nmj1pGyr-HzAwh-=N|JA?1Xdypi3LX1A~WP%n_*S?Ej8UghT+> gb;|$FV+g?=}_Qyx#@W10B{HMP24k|asetQFEENtz_hwvr@SNs?r3 zNs_E2NwQX&Bw3Xt$=Z@6S(PNY@AnyVH7t`?`MmGOIrn?+`FqcqGjpEj`JLaHdr_~h zr>3Rn#>B6o+G1gVq(lG6*D(>K*`ez#&@n66SK+E z5;1KJkBIrsFg|9oVO&ft!{|EEPY%;Z5YLe$a1^B|O(I8AhGQs8Im&Y^6*!KH98V=q zpfV>?g_Eet$s}MvAd@p`#91`vY_d3) zrkqDJ&ZjvS(1HtT$wjo{Vp?+vZMc-STt+)Cr#)BTw-9qB9l45ZuBHoJ>Bcp5=UQ^O zjvic3Pi~+WH`1G%=)=wAatnRAm44hte{Ls_I~mAb4B~DEa}Ptfm!aIpF#g1F?q>uK zFp>uu#Y2qdVaD(XV|kQuJkA83U?NX4iKm#%(@fzRrt(*&@hsDMju|}9OkQ9XFEX2# zn8VA=YO979>k zQJ!O|z;RUMcq(xMl{t|roJ3VlCW%u>rUo^sMQ!Sk!l|Tk8flzPI(4Z>eHxHKL(U+R zGik(GH0ErwIG3iJM>Ec+ITz4^3u(zkwBlk~a|vy@l(t+(J1(a^SI~hg>Bv=Nb2VM) zN;j^dJJ*uKb@bqRdU6B3xRKu6L?3P@ms{w|t@PtI`g1#Z+{r-hVi0#Tn0pw)y$t0( zhVdtcb3Y?^fRQ}NC>~-o4>N{G7|WxK<8dbN1QU6ZNj$}5o@NTqFqOYDjc1w8bIjm* zX7U2Fc#+w>#2j8`F0ZhFSIOr!7V4O{t^ZG6XezGnv!V$`um5YLe$a1^B| zO(I8AhGQs8Im&Y^6*!KH98V=qpfV>?g_Eet$s}37dO(Io9M&Ml{kUQoJbW;qADkoM0Jv>No`Wepdn|F$(c0bEE;n*Su~{ut+|YL zTuytgpaWOZk*mn23pw=SW^%cOzT8SbZlgc9lgB^?F@)iSm~<55n7~9PGmQ{G&SVy| znZsNbkk2BP65_t)tY$6i*uZ8&jJB0;*~WKlX9qFb^dS}taaf4GLYx(1tPod)m@33m zA(l#_8r2E0REVQO3>D(15IcpqDa1@6UJ9{Nh?7E$6yl>08)XqEOs1x#*Nvn{(jxWirl&=stL?GU9+N*| zn}|I{cHfu73$HqbqA}%(r{I1629+UC9SS!R9j3gAUw#Q0)O#XyzV(lq*_kG!aIE2Etf+Hz1BR00g zu33JN5~17q77jWbcEQReV@p~VSE7W~i}v~X`Tg?qC+a6J!es95la@4F)Xe&iVOX`PupTE%Mvx!&Ko66Gdk^^3dfK4EyDG(-*GXIc%rvr_a@g z_q_Z(zo&>-wENG=oy)?nGZJ0xKUdo0BMy92P6?K^Fw(b zE$5?U{JzTe_SOw$dly1^oOo%X!vR3|01R;^m8$u(-#sFhqjxyHVx z>(q&OS(Qk#ezzGlPF1k(J}bFaku_|b5=k+%c_bmBRD8m2-QK5nmOuXd3W}j!1ybRR zqNP{itP+Lq;Vn*4wd)mIa0&0%K>5(yir%vGo8`AE7~3`Dhcctctgvq3q_RyB;x!<@ zpM%)XZs)bR^QtSj;)>2Fa;5sX!X?r7os-dFB+sSu2kzjHgtqIQ4CF4tHF>b}X@9nl z@%)9*{(YPYJV7V}4t9R+Z}u^dzcZg#Siq}^yQjhvHV1MI`-&nT^zvUdxr77nT>WuSg&IPpKLRxYW zp+32o)?7jxE~PE|8?*e*x}Eqvow=GWbS2bL*U+77$>BPBa6O^^x`AHYNN;YU5BnSc z{K2|+@J9x4Cj+^QLEOz??qLY`GL-um#-A9@{fyuNM)Dw|c!*GsKFk;%VJwdl>efFq zp1<%Ik28TMn8=e%;wdKcG*ftnsr;2`Jj-;RV+PMNlNXr9i-ef%B|_ZxGIRMG^Y}aS zd4&bMNhy53S^uPS=i$>>%#}Bv8jwLl&LERBX~bDH=4`TP!f!c; zb7{(XG~;}la{(>5kd|CTD_V02ZMc-STt+)Cr#)BDfh+0ARb%i|@;ZxngT=hb5|;87Z}Sezc$ek8#|qwOB_FVg z4_VC`{>4Xp%vwHS9iOtE&)C4{Y~%|zv6(OVim%ziH*Doww(%X?`JNp_2=@m{5J$Kt z5bg(*B9StbC7PqIXuJ{!nx_u=i;$nrBE;h%77uZFh{2l^^3*M9MKoU>p37}V$Zd2W zJcpZ2h@(U79C94d=WKf!FZx_<$ZOOn=#r}yzSpW=*d^;~)JU$Dk(QB>R@BcnSwF3Q z{j?txzoX2I%*>38%tjd*jS8eSYLxba%(TqRv>z1zQG4HS^iU3D+aFF)aVO_L_Z#+E zH!ZDheXorA8JYDXVOWs(51I3fFlLxu_lJe@>ltQBz=!m@hX_Ify0~svqW$p5K|KTE zcMyFLAo}|#oR0GxK;gphh=B2gSWh;@JD!C7$g;JYs2|mCM7v?_hRCZQo;(O=W9saJ zL}&D|g=p3@q$qcFJ;PX@`ve7V1BMig<^EW{vSi7liuOnCHdXTQ(xT_%r<{#~--E2K zZs&0KvFPlATya5uc<0#R{I4d?-5>eGM>8gd4ioJk|jqA_Qa#cycBZ#jo^Y07yt<9wQP0WG+YmRv+D zE~Yh?(1uHC%Vo6Va@unR9k`N?Ttzm&qZ7ZUGgs4vu5{xXx^pc#Tt^SCrzbbiiyP_9 zP4wYra=C@R+)6)gqd&Kk#~-+ZKQe$j8OU7>;%)|W4@0<@q1?wX{={(ZX9N#0k_Q>Z zLyYEO#_$MZd6aSdneqIE$9S9xJi$bsWD-v?nWve;Gfd^LOygOm^BgmHo|(MBEM8p9gnzJNJ}dcvReZ>5 z{>d8t#YcS1T0UVNpR%6M*udv({Ou3DjOXE> zxab);ocBGn&%mLY_dNV_?>qxqfT0PvD+iotybNV2Pc#=?#dydECsB=PPB_JQDruw> z%?&p)9`eIkG$EQJZecv+iCfW{Xs)<}@sKahrW4VeagOnjH||LIb~Ll1h=i$3(FAN|SW4hAxqAq-_0!x_nF#xRy~jAsIqn9LNWGL0F` zVm5P_%RCmakVPzJ2}@bV3Rbd;)vRGH>)F6YHnEv4Y-2k+2!H$`_6fPIz2&t+PHS)Z ztYq_RlS0w|w`^cKlaR-X{-0&YWi@4QIjoSs+B0|6$-FLfC*-f9|8p75U-dOF#g~w7ZJYuZzUhGf$-gYq5j!UoU$*G3RI*D)k&cq4alSs=g@*Sbf7ai z^rj#E8Nd)mFowsN%yecjoB1r_ZC3IT8~BP)E^jA1XOKwvpZ6+MCzP?#|6>p3YIrW7 z1>yhII}mdHp_~l=zZmlSp=``JDBZy+AVNm%=kG!S=;!bJ=@hgV8r4arCbdZ+jdbeKfQCe`hsMUUXu>%(r5Vj>K`UC*hPJe$Jss#sHl659 z7rN1%9D34=-t-}tzVsuHI~c$~1~Hf+3}ZMW7|AF`Glp@D=P@QQkx5KuD$|(G3}!Nm z+011g^I1SX3t7Yxmhv{sSk4MovYIt~#9G#|o(*hbGheZVt!!gEF|M}~;hH0!1WJ)e zS;|v^P(DT8r4arCbdZ+jdbb}%8-Uc%aJVOO*n_9G^05!Xhmz<(3W0PK5Sl zS3(;zv@fG=%kZ6pp?w+Jmh}$Z?%)*k{}rE_pVu>gRE|eI3f?B}J{g_8kEOe9y2q9W zVcC%Ui36tpjDw%Qx;$u~SkNf`83&;{C_`D2NF|LdTGEPadeDN)oAr`lB&fv?SCE9m%E#p}g-;9-(|6Kq%t}5z6=>3}qOh ztRF!r??(~J`!S4V9HHEQj8OJZB$WM=nZi^;9Wb3x56mRg1GAaKTta;?pHL^{6Y7LT zEM^IzZg`teKP)HI4=Y*4YC=8n5uvVFN2n_{u#ruKI^!!sy|I;0Z)|4=p%E79k2vBv zn9t>gdL}%Z8|sza!eF&$T?%OQCHa z>ZVA&NYPh46Z`DR&|JA!k*owZ2 zzoj3ZcMEI(AHC2OU&zgN0Sm2!!R#~xd{bougS l%9JTruH4>+Vdj4SK^Ki`N6j_WuHaj + + + + + + +

+ + + + + + + + + + + diff --git a/psn00bsdk-setup.mk b/psn00bsdk-setup.mk new file mode 100644 index 0000000..273409b --- /dev/null +++ b/psn00bsdk-setup.mk @@ -0,0 +1,68 @@ +# PSn00bSDK project setup file +# Part of the PSn00bSDK Project +# 2019 - 2020 Lameguy64 / Meido-Tek Productions +# +# This file may be copied for use with your projects, see the template +# directory for a makefile template + +ifndef PREFIX + +PREFIX = mipsel-unknown-elf + +endif # PREFIX + +ifndef GCC_VERSION + +GCC_VERSION = 7.4.0 + +endif # GCC_VERSION + +# PSn00bSDK library/include path setup +ifndef PSN00BSDK_LIBS + +# Default assumes PSn00bSDK is in the same parent dir as this project + +LIBDIRS = -L../psn00bsdk/libpsn00b +INCLUDE = -I../psn00bsdk/libpsn00b/include + +else + +LIBDIRS = -L$(PSN00BSDK_LIBS) +INCLUDE = -I$(PSN00BSDK_LIBS)/include + +endif # PSN00BSDK_LIBS + +# PSn00bSDK toolchain path setup +ifndef GCC_BASE + +ifndef PSN00BSDK_TC + +# Default assumes GCC toolchain is in root of C drive or /usr/local + +ifeq "$(OS)" "Windows_NT" + +GCC_BASE = /c/mipsel-unknown-elf +GCC_BIN = + +else + +GCC_BASE = /usr/local/mipsel-unknown-elf +GCC_BIN = + +endif + +else + +GCC_BASE = $(PSN00BSDK_TC) +GCC_BIN = $(PSN00BSDK_TC)/bin/ + +endif # PSN00BSDK_TC + +endif # GCC_BASE + +CC = $(GCC_BIN)$(PREFIX)-gcc +CXX = $(GCC_BIN)$(PREFIX)-g++ +AS = $(GCC_BIN)$(PREFIX)-as +AR = $(GCC_BIN)$(PREFIX)-ar +LD = $(GCC_BIN)$(PREFIX)-ld +RANLIB = $(GCC_BIN)$(PREFIX)-ranlib diff --git a/src/cd.c b/src/cd.c new file mode 100644 index 0000000..2c9511e --- /dev/null +++ b/src/cd.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "cd.h" +#include "util.h" + +// TEMPORARY CD FILE READING API WITH BUFFERS AND SHIT +// copied straight from d2d-psx and converted to only use one static handle + +#define SECSIZE 2048 +#define BUFSECS 4 +#define BUFSIZE (BUFSECS * SECSIZE) +#define MAX_FHANDLES 1 + +static const u32 cdmode = CdlModeSpeed; + +struct cd_file_s { + char fname[64]; + CdlFILE cdf; + s32 secstart, secend, seccur; + s32 fp, bufp; + s32 bufleft; + unsigned char buf[BUFSIZE]; +}; + +// lmao 1handle +static cd_file_t fhandle; +static s32 num_fhandles = 0; + +void cd_init(void) { + CdInit(); + // look alive + CdControl(CdlNop, 0, 0); + CdStatus(); + // set hispeed mode + CdControlB(CdlSetmode, (u8 *)&cdmode, 0); + VSync(3); // have to do this to not explode the drive apparently +} + +cd_file_t *cd_fopen(const char *fname, const int reopen) { + // check if the same file was just open and return it if allowed + if (reopen && !strncmp(fhandle.fname, fname, sizeof(fhandle.fname))) { + num_fhandles++; + return &fhandle; + } + + if (num_fhandles >= MAX_FHANDLES) { + printf("cd_fopen(%s): too many file handles\n", fname); + return NULL; + } + + cd_file_t *f = &fhandle; + memset(f, 0, sizeof(*f)); + + if (CdSearchFile(&f->cdf, fname) == NULL) { + printf("cd_fopen(%s): file not found\n", fname); + return NULL; + } + + // read first sector of the file + CdControl(CdlSetloc, (u8 *)&f->cdf.pos, 0); + CdRead(BUFSECS, (u_long *)f->buf, CdlModeSpeed); + CdReadSync(0, NULL); + + // set fp and shit + f->secstart = CdPosToInt(&f->cdf.pos); + f->seccur = f->secstart; + f->secend = f->secstart + (f->cdf.size + SECSIZE-1) / SECSIZE; + f->fp = 0; + f->bufp = 0; + f->bufleft = (f->cdf.size >= BUFSIZE) ? BUFSIZE : f->cdf.size; + strncpy(fhandle.fname, fname, sizeof(fhandle.fname) - 1); + + num_fhandles++; + printf("cd_fopen(%s): size %u bufleft %d secs %d %d\n", fname, f->cdf.size, f->bufleft, f->secstart, f->secend); + + return f; +} + +int cd_fexists(const char *fname) { + CdlFILE cdf; + if (CdSearchFile(&cdf, (char *)fname) == NULL) { + printf("cd_fexists(%s): file not found\n", fname); + return 0; + } + return 1; +} + +void cd_fclose(cd_file_t *f) { + if (!f) return; + num_fhandles--; +} + +s32 cd_fread(void *ptr, s32 size, s32 num, cd_file_t *f) { + s32 rx, rdbuf; + s32 fleft; + CdlLOC pos; + + if (!f || !ptr) return -1; + if (!size) return 0; + + size *= num; + rx = 0; + + while (size) { + // first empty the buffer + rdbuf = (size > f->bufleft) ? f->bufleft : size; + memcpy(ptr, f->buf + f->bufp, rdbuf); + rx += rdbuf; + ptr += rdbuf; + f->fp += rdbuf; + f->bufp += rdbuf; + f->bufleft -= rdbuf; + size -= rdbuf; + + // if we went over, load next sector + if (f->bufleft == 0) { + f->seccur += BUFSECS; + // check if we have reached the end + if (f->seccur >= f->secend) + return rx; + // looks like you need to seek every time when you use CdRead + CdIntToPos(f->seccur, &pos); + CdControl(CdlSetloc, (u8 *)&pos, 0); + CdRead(BUFSECS, (u_long *)f->buf, CdlModeSpeed); + CdReadSync(0, 0); + fleft = f->cdf.size - f->fp; + f->bufleft = (fleft >= BUFSIZE) ? BUFSIZE: fleft; + f->bufp = 0; + } + } + + return rx; +} + +void cd_freadordie(void *ptr, s32 size, s32 num, cd_file_t *f) { + if (cd_fread(ptr, size, num, f) < 0) + panic("cd_freadordie(%.16s, %d, %d): fucking died", f->cdf.name, size, num); +} + +s32 cd_fseek(cd_file_t *f, s32 ofs, s32 whence) { + s32 fsec, bofs; + CdlLOC pos; + + if (!f) return -1; + + if (whence == SEEK_CUR) + ofs = f->fp + ofs; + + if (f->fp == ofs) return 0; + + fsec = f->secstart + (ofs / BUFSIZE) * BUFSECS; + bofs = ofs % BUFSIZE; + + // fuck SEEK_END, it's only used to get file length here + + if (fsec != f->seccur) { + // sector changed; seek to new one and buffer it + CdIntToPos(fsec, &pos); + CdControl(CdlSetloc, (u8 *)&pos, 0); + CdRead(BUFSECS, (u_long *)f->buf, CdlModeSpeed); + CdReadSync(0, 0); + f->seccur = fsec; + f->bufp = -1; // hack: see below + } + + if (bofs != f->bufp) { + // buffer offset changed (or new sector loaded); reset pointers + f->bufp = bofs; + f->bufleft = BUFSIZE - bofs; + if (f->bufleft < 0) f->bufleft = 0; + } + + f->fp = ofs; + + return 0; +} + +s32 cd_ftell(cd_file_t *f) { + if (!f) return -1; + return f->fp; +} + +s32 cd_fsize(cd_file_t *f) { + if (!f) return -1; + return f->cdf.size; +} + +int cd_feof(cd_file_t *f) { + if (!f) return -1; + return (f->seccur >= f->secend); +} + +u8 cd_fread_u8(cd_file_t *f) { + u8 res = 0; + cd_freadordie(&res, 1, 1, f); + return res; +} + +u16 cd_fread_u16le(cd_file_t *f) { + u16 res = 0; + cd_freadordie(&res, 2, 1, f); + return res; +} + +u32 cd_fread_u32le(cd_file_t *f) { + u32 res = 0; + cd_freadordie(&res, 4, 1, f); + return res; +} + +int cd_scandir(const char *dir, char out[][CD_MAX_FILENAME], const char *filter) { + CdlFILE cdf; + CdlDIR *cddir = CdOpenDir(dir); + if (!cddir) return -1; + int n = 0; + while (CdReadDir(cddir, &cdf)) { + if (cdf.name[0] && cdf.name[0] != '.' && (!filter || strstr(cdf.name, filter))) { + strncpy(out[n], cdf.name, CD_MAX_FILENAME); + ++n; + } + } + return n; +} diff --git a/src/cd.h b/src/cd.h new file mode 100644 index 0000000..2626c96 --- /dev/null +++ b/src/cd.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "types.h" + +#define CD_MAX_FILENAME 16 +#define CD_MAX_PATH (128 + CD_MAX_FILENAME) + +typedef struct cd_file_s cd_file_t; + +void cd_init(void); +cd_file_t *cd_fopen(const char *fname, const int reopen); +int cd_fexists(const char *fname); +void cd_fclose(cd_file_t *f); +s32 cd_fread(void *ptr, s32 size, s32 num, cd_file_t *f); +void cd_freadordie(void *ptr, s32 size, s32 num, cd_file_t *f); +s32 cd_fseek(cd_file_t *f, s32 ofs, int whence); +s32 cd_ftell(cd_file_t *f); +s32 cd_fsize(cd_file_t *f); +int cd_feof(cd_file_t *f); +int cd_scandir(const char *dir, char out[][CD_MAX_FILENAME], const char *filter); + +u8 cd_fread_u8(cd_file_t *f); +u16 cd_fread_u16le(cd_file_t *f); +u32 cd_fread_u32le(cd_file_t *f); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3180c0c --- /dev/null +++ b/src/main.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "spu.h" +#include "cd.h" +#include "util.h" +#include "org.h" + +#define MAX_MENU_FILES 128 +#define MENU_DISP_FILES 20 + +static char padbuf[2][34]; +static DISPENV disp[2]; +static DRAWENV draw[2]; +static int db; + +static struct sfx_bank *bnk_sfx; + +static u16 pad_btn = 0xFFFF; +static u16 pad_btn_old = 0xFFFF; + +static void init(void) { + ResetGraph(0); + + SetDefDispEnv(&disp[0], 0, 0, 320, 240); + SetDefDispEnv(&disp[1], 0, 240, 320, 240); + SetDefDrawEnv(&draw[0], 0, 240, 320, 240); + SetDefDrawEnv(&draw[1], 0, 0, 320, 240); + + setRGB0(&draw[0], 0, 0, 32); + setRGB0(&draw[1], 0, 0, 32); + draw[0].isbg = 1; + draw[1].isbg = 1; + + db = 0; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + cd_init(); + spu_init(); + + InitPAD(&padbuf[0][0], 34, &padbuf[1][0], 34); + StartPAD(); + ChangeClearPAD(0); + + FntLoad(960, 0); + FntOpen(0, 8, 320, 224, 0, 1024); +} + +static void display(void) { + db = !db; + DrawSync(0); + VSync(0); + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + SetDispMask(1); +} + +static inline int btn_pressed(const u32 m) { + return !(pad_btn & m) && (pad_btn_old & m); +} + +static inline int btn_released(const u32 m) { + return (pad_btn & m) && !(pad_btn_old & m); +} + +static inline void btn_scan(void) { + pad_btn_old = pad_btn; + pad_btn = ((PADTYPE *)padbuf[0])->btn; +} + +static void draw_tracks(void) { + FntPrint(-1, " TRACKS\n\n"); + FntPrint(-1, " 000 001 002 003 004 005 006 007 008"); + + org_note_t dummy = { 0 }; + org_note_t *n[ORG_MAX_TRACKS]; + for (int i = 0; i <= 8; ++i) { + n[i] = org_get_track_pos(i); + if (!n[i]) n[i] = &dummy; + } + + FntPrint(-1, "\n\n "); + for (int i = 0; i <= 8; ++i) + FntPrint(-1, "%03x ", n[i]->pos); + FntPrint(-1, "\n\n "); + for (int i = 0; i <= 8; ++i) + FntPrint(-1, "K%02x ", n[i]->key); + FntPrint(-1, "\n\n "); + for (int i = 0; i <= 8; ++i) + FntPrint(-1, "L%02x ", n[i]->len); + FntPrint(-1, "\n\n "); + for (int i = 0; i <= 8; ++i) + FntPrint(-1, "V%02x ", n[i]->vol); + FntPrint(-1, "\n\n "); + for (int i = 0; i <= 8; ++i) + FntPrint(-1, "P%02x ", n[i]->pan); +} + +static volatile u32 play_org = 0; + +static void mus_callback(void) { + if (play_org) + org_tick(); +} + +static void timer_start(const u32 rate) { + EnterCriticalSection(); + const u32 tick = 15625 * rate / 1000; + SetRCnt(RCntCNT1, tick, RCntMdINTR); + InterruptCallback(5, mus_callback); // IRQ5 is RCNT1 + StartRCnt(RCntCNT1); + ChangeClearRCnt(1, 0); + ExitCriticalSection(); +} + +static void timer_stop(void) { + EnterCriticalSection(); + StopRCnt(RCntCNT1); + ExitCriticalSection(); +} + +static void run_player(const char *orgname) { + org_load(orgname); + + timer_start(org_get_wait()); + + u32 sfx = 1; + u16 mute_cur = 0; + u16 mute_mask = 0; + char mute_chans[17] = "................"; + + spu_set_voice_volume(0, SPU_MAX_VOLUME); + + while (1) { + btn_scan(); + + if (btn_pressed(PAD_LEFT)) { + if (sfx == 1) sfx = bnk_sfx->num_sfx - 1; + else --sfx; + } else if (btn_pressed(PAD_RIGHT)) { + if (sfx == bnk_sfx->num_sfx - 1) sfx = 1; + else ++sfx; + } + + if (btn_pressed(PAD_UP)) { + if (mute_cur == 0) mute_cur = 15; + else --mute_cur; + } else if (btn_pressed(PAD_DOWN)) { + if (mute_cur == 15) mute_cur = 0; + else ++mute_cur; + } + + if (btn_pressed(PAD_CROSS) && bnk_sfx->sfx_addr[sfx]) + spu_play_sample(0, bnk_sfx->sfx_addr[sfx], 22050); + else if (btn_released(PAD_CROSS)) + spu_key_off(SPU_VOICECH(0)); + + if (btn_pressed(PAD_CIRCLE)) { + play_org = !play_org; + if (!play_org) + spu_key_off(0xFFFFFFFF); + } + + if (btn_pressed(PAD_TRIANGLE)) { + mute_mask ^= (1 << mute_cur); + mute_chans[mute_cur] = (mute_chans[mute_cur] == 'm') ? '.' : 'm'; + org_set_mute_mask(mute_mask); + } + + if (btn_pressed(PAD_START)) + break; + + // HACK + const char old = mute_chans[mute_cur]; + mute_chans[mute_cur] = (old == 'm') ? 'X' : ','; + + FntPrint(-1, "\n X, O: PLAY\n DPAD: CHANGE\n START: BACK\n\n"); + FntPrint(-1, " SFX: %03d / %03d\n\n", sfx, bnk_sfx->num_sfx - 1); + FntPrint(-1, " ORG: %4d\n", org_get_pos()); + FntPrint(-1, " CHN: %s\n\n", mute_chans); + draw_tracks(); + FntPrint(-1, "\n\n\n %s.ORG", orgname); + FntFlush(-1); + display(); + + mute_chans[mute_cur] = old; + } + + play_org = 0; + timer_stop(); + spu_clear_all_voices(); + org_free(); +} + +static const char *run_menu(void) { + static char files[MAX_MENU_FILES][CD_MAX_FILENAME]; + int numfiles = 0; + + // scan root CD directory if needed + numfiles = cd_scandir("\\ORG", files, ".ORG"); + if (numfiles < 0) + panic("could not scan ORG directory"); + + int filepos = 0; + + while (1) { + btn_scan(); + + if (btn_pressed(PAD_DOWN)) { + if (filepos == numfiles - 1) filepos = 0; + else ++filepos; + } else if (btn_pressed(PAD_UP)) { + if (filepos == 0) filepos = numfiles - 1; + else --filepos; + } + + if (btn_pressed(PAD_CROSS) && numfiles) { + // nuke everything after the extension + char *dot = strrchr(files[filepos], '.'); + if (dot) *dot = '\0'; + return files[filepos]; + } + + if (btn_pressed(PAD_START)) + break; + + FntPrint(-1, "\n SELECT FILE AND PRESS X\n"); + FntPrint(-1, " OR SWAP CD AND PRESS START\n\n"); + + if (!numfiles) { + FntPrint(-1, " SORRY NOTHING\n"); + } else { + const int startfile = filepos / MENU_DISP_FILES * MENU_DISP_FILES; + int endfile = startfile + MENU_DISP_FILES; + if (endfile > numfiles) endfile = numfiles; + for (int i = startfile; i < endfile; ++i) + FntPrint(-1, " %c %s\n", (i == filepos) ? '*' : ' ', files[i]); + } + + FntFlush(-1); + display(); + } + + return NULL; +} + +int main(int argc, char **argv) { + init(); + + bnk_sfx = load_sfx_bank("\\BNK\\SFX.BNK;1"); + org_init(bnk_sfx); + + while (1) { + const char *org = NULL; + while (!org) + org = run_menu(); + run_player(org); + } + + return 0; +} diff --git a/src/org.c b/src/org.c new file mode 100644 index 0000000..50eb45a --- /dev/null +++ b/src/org.c @@ -0,0 +1,381 @@ +#include +#include +#include +#include + +#include "types.h" +#include "util.h" +#include "spu.h" +#include "org.h" +#include "cd.h" + +#define ORG_MAGIC "Org-0" +#define ORG_MAGICLEN 5 + +#define MAX_TRACKS ORG_MAX_TRACKS +#define MAX_MELODY_TRACKS 8 +#define MAX_DRUM_TRACKS 8 + +#define NUM_OCTS 8 +#define NUM_ALTS 2 + +#define DRUM_BANK_BASE 150 + +#define PANDUMMY 0xFF +#define VOLDUMMY 0xFF +#define KEYDUMMY 0xFF + +#define ALLOCNOTE 4096 + +#define DEFVOLUME 200 +#define DEFPAN 6 + +#pragma pack(push, 1) + +typedef struct { + u16 freq; // frequency modifier (default = 1000) + u8 wave_no; // waveform index in the wavetable + u8 pipi; // loop flag? + u16 note_num; // number of notes in track +} org_trackhdr_t; + +typedef struct { + u16 wait; + u8 line; + u8 dot; + s32 repeat_x; + s32 end_x; + org_trackhdr_t tdata[MAX_TRACKS]; +} org_hdr_t; + +#pragma pack(pop) + +typedef struct { + org_note_t *notes; + org_note_t *cur_note; + s32 vol; + u32 sustain; + s8 mute; + u8 old_key; +} org_trackstate_t; + +typedef struct { + org_hdr_t info; + org_trackstate_t tracks[MAX_TRACKS]; + s32 vol; + s32 pos; + u8 fadeout; + s8 track; + u8 def_pan; + u8 def_vol; +} org_state_t; + +static u32 key_on_mask; // all the keys that got keyed on this tick +static u32 key_off_mask; // all the keys that got keyed off this tick +static org_state_t org; +static struct sfx_bank *inst_bank; +static struct sfx_bank *drum_bank; + +s32 org_freqshift = 0; + +static const struct { + s16 wave_size; + s16 oct_par; + s16 oct_size; +} oct_wave[NUM_OCTS] = { + { 256, 1, 4 }, // 0 Oct + { 256, 2, 8 }, // 1 Oct + { 128, 4, 12 }, // 2 Oct + { 128, 8, 16 }, // 3 Oct + { 64, 16, 20 }, // 4 Oct + { 32, 32, 24 }, // 5 Oct + { 16, 64, 28 }, // 6 Oct + { 8, 128, 32 }, // 7 Oct +}; + +static const s16 freq_tbl[12] = { 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494 }; +static const s16 pan_tbl[13] = { 0, 43, 86, 129, 172, 215, 256, 297, 340, 383, 426, 469, 512 }; + +static void org_read_track(cd_file_t *f, const int track) { + const org_trackhdr_t *hdr = &org.info.tdata[track]; + org_trackstate_t *dst = &org.tracks[track]; + // hope there's enough stack space + u8 notedata[sizeof(s32) * hdr->note_num]; + // read positions ("x coordinate") + cd_freadordie(notedata, sizeof(s32) * hdr->note_num, 1, f); + for (u16 i = 0; i < hdr->note_num; ++i) dst->notes[i].pos = ((s32 *)notedata)[i]; + // read keys ("y coordinate") + cd_freadordie(notedata, hdr->note_num, 1, f); + for (u16 i = 0; i < hdr->note_num; ++i) dst->notes[i].key = notedata[i]; + // read lengths + cd_freadordie(notedata, hdr->note_num, 1, f); + for (u16 i = 0; i < hdr->note_num; ++i) dst->notes[i].len = notedata[i]; + // read volumes + cd_freadordie(notedata, hdr->note_num, 1, f); + for (u16 i = 0; i < hdr->note_num; ++i) dst->notes[i].vol = notedata[i]; + // read pans + cd_freadordie(notedata, hdr->note_num, 1, f); + for (u16 i = 0; i < hdr->note_num; ++i) dst->notes[i].pan = notedata[i]; +} + +void org_init(struct sfx_bank *sample_bank) { + org.info.dot = 4; + org.info.line = 4; + org.info.wait = 128; + org.info.repeat_x = 0; + org.info.end_x = org.info.line * 255; + org.def_pan = DEFPAN; + org.def_vol = DEFVOLUME; + for (int i = 0; i < MAX_TRACKS; ++i) { + org.info.tdata[i].freq = 1000; + org.info.tdata[i].wave_no = 0; + org.info.tdata[i].pipi = 0; + } + drum_bank = sample_bank; +} + +int org_load(const char *name) { + char tmp[256]; + cd_file_t *f = NULL; + + snprintf(tmp, sizeof(tmp), "\\BNK\\%s.BNK;1", name); + inst_bank = load_sfx_bank(tmp); + if (!inst_bank) goto _error; + if (inst_bank->num_sfx != MAX_MELODY_TRACKS * NUM_OCTS) { + printf("org_load(%s): expected %d instruments in bank, got %d\n", + name, MAX_MELODY_TRACKS * NUM_OCTS, inst_bank->num_sfx); + goto _error; + } + + snprintf(tmp, sizeof(tmp), "\\ORG\\%s.ORG;1", name); + f = cd_fopen(tmp, 0); + if (!f) goto _error; + + char magic[ORG_MAGICLEN + 1] = { 0 }; // +1 for version + cd_freadordie(magic, ORG_MAGICLEN + 1, 1, f); + + if (memcmp(magic, ORG_MAGIC, ORG_MAGICLEN)) { + printf("org_load(%s): invalid Org magic\n", name); + goto _error; + } + + const int ver = magic[ORG_MAGICLEN] - '0'; + if (ver != 1 && ver != 2) { + printf("org_load(%s): expected version 1 or 2, got %d\n", name, ver); + goto _error; + } + + cd_freadordie(&org.info, sizeof(org.info), 1, f); + + for (int i = 0; i < MAX_TRACKS; ++i) { + if (ver == 1) + org.info.tdata[i].pipi = 0; + if (org.info.tdata[i].note_num) { + org.tracks[i].notes = malloc(org.info.tdata[i].note_num * sizeof(org_note_t)); + ASSERT(org.tracks[i].notes); + org_read_track(f, i); + } else { + org.tracks[i].notes = NULL; + } + } + + cd_fclose(f); + + // dump eet + /* + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) { + printf("\nTRACK %02d\n", i); + for (u32 j = 0; j < org.info.tdata[i].note_num; ++j) { + const org_note_t *n = org.tracks[i].notes + j; + printf("%04d | %02x %02x %02x %02x\n", j, n->key, n->len, n->vol, n->pan); + } + } + */ + + org.vol = 100; + + org_restart_from(0); + + return 1; + +_error: + if (f) cd_fclose(f); + org_free(); + return 0; +} + +void org_free(void) { + if (inst_bank) { + free_sfx_bank(inst_bank); + inst_bank = NULL; + } + for (int i = 0; i < MAX_TRACKS; ++i) { + if (org.tracks[i].notes) { + free(org.tracks[i].notes); + org.tracks[i].notes = NULL; + } + } +} + +void org_restart_from(const s32 pos) { + org.pos = pos; + for (int i = 0; i < MAX_TRACKS; ++i) { + org.tracks[i].cur_note = NULL; + for (int j = 0; j < org.info.tdata[i].note_num; ++j) { + if (org.tracks[i].notes[j].pos >= pos) { + org.tracks[i].cur_note = &org.tracks[i].notes[j]; + break; + } + } + } +} + +static inline void org_play_melodic(const int trk, int key, int freq, int mode) { + const u32 ch = ORG_START_CH + trk; + const int oct = key / 12; + const int inst = trk * NUM_OCTS + oct; + switch (mode) { + case 0: // also stop? + case 2: // stop + if (org.tracks[trk].old_key != KEYDUMMY) { + key_off_mask |= SPU_VOICECH(ch); + org.tracks[trk].old_key = KEYDUMMY; + } + break; + case -1: // key on? + org.tracks[trk].old_key = key; + freq = ((oct_wave[oct].wave_size * freq_tbl[key % 12]) * oct_wave[oct].oct_par) / 8 + (freq - 1000); + spu_set_voice_addr(ch, inst_bank->sfx_addr[inst]); + spu_set_voice_freq(ch, freq + org_freqshift); + key_on_mask |= SPU_VOICECH(ch); + break; + default: + break; + } +} + +static inline void org_play_drum(const int trk, int key, int mode) { + const u32 ch = ORG_START_CH + trk; + const int inst = trk - MAX_MELODY_TRACKS + DRUM_BANK_BASE; + switch (mode) { + case 0: // stop + key_off_mask |= SPU_VOICECH(ch); + break; + case 1: // play + spu_set_voice_addr(ch, drum_bank->sfx_addr[inst]); + spu_set_voice_freq(ch, key * 800 + 100); + key_on_mask |= SPU_VOICECH(ch); + break; + default: + break; + } +} + +static inline void org_set_vol(const int trk, int vol) { + spu_set_voice_volume(ORG_START_CH + trk, vol << 5); +} + +static inline void org_set_pan(const int trk, int pan) { + spu_set_voice_pan(ORG_START_CH + trk, pan_tbl[pan] - 256); +} + +void org_tick(void) { + if (org.fadeout && org.vol) + org.vol -= 2; + if (org.vol < 0) + org.vol = 0; + + key_off_mask = 0; + key_on_mask = 0; + + // waves + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) { + const org_note_t *note = org.tracks[i].cur_note; + if (note && org.pos == note->pos) { + if (!org.tracks[i].mute && note->key != KEYDUMMY) { + org_play_melodic(i, note->key, org.info.tdata[i].freq, -1); + org.tracks[i].sustain = note->len; + } + if (note->pan != PANDUMMY) + org_set_pan(i, note->pan); + if (note->vol != VOLDUMMY) + org.tracks[i].vol = note->vol; + ++org.tracks[i].cur_note; + if (org.tracks[i].cur_note >= org.tracks[i].notes + org.info.tdata[i].note_num) + org.tracks[i].cur_note = NULL; + } + + if (org.tracks[i].sustain == 0) + org_play_melodic(i, 0, org.info.tdata[i].freq, 2); + else + --org.tracks[i].sustain; + + if (org.tracks[i].cur_note) + org_set_vol(i, org.tracks[i].vol * org.vol / 0x7F); + } + + // drums + for (int i = MAX_MELODY_TRACKS; i < MAX_TRACKS; ++i) { + const org_note_t *note = org.tracks[i].cur_note; + if (note && org.pos == note->pos) { + if (!org.tracks[i].mute && note->key != KEYDUMMY) + org_play_drum(i, note->key, 1); + if (note->pan != PANDUMMY) + org_set_pan(i, note->pan); + if (note->vol != VOLDUMMY) + org.tracks[i].vol = note->vol; + ++org.tracks[i].cur_note; + if (org.tracks[i].cur_note >= org.tracks[i].notes + org.info.tdata[i].note_num) + org.tracks[i].cur_note = NULL; + } + + if (org.tracks[i].cur_note) + org_set_vol(i, org.tracks[i].vol * org.vol / 0x7F); + } + + spu_flush_voices(); + spu_key_off(key_off_mask); + spu_key_on(key_on_mask); + + ++org.pos; + if (org.pos >= org.info.end_x) + org_restart_from(org.info.repeat_x); +} + +int org_get_wait(void) { + return org.info.wait; +} + +int org_get_pos(void) { + return org.pos; +} + +u16 org_get_mute_mask(void) { + register u16 mask = 0; + for (u16 i = 0; i < MAX_TRACKS; ++i) { + if (org.tracks[i].mute) + mask |= (1 << i); + } + return mask; +} + +u16 org_set_mute_mask(const u16 mask) { + register u16 oldmask = 0; + for (u16 i = 0; i < MAX_TRACKS; ++i) { + if (org.tracks[i].mute) + oldmask |= (1 << i); + org.tracks[i].mute = !!(mask & (1 << i)); + } + spu_key_off((u32)mask << ORG_START_CH); + return oldmask; +} + +org_note_t *org_get_track(const int tracknum, u32 *numnotes) { + if (numnotes) + *numnotes = org.info.tdata[tracknum].note_num; + return org.tracks[tracknum].notes; +} + +org_note_t *org_get_track_pos(const int tracknum) { + return org.tracks[tracknum].cur_note; +} diff --git a/src/org.h b/src/org.h new file mode 100644 index 0000000..6ebc5b3 --- /dev/null +++ b/src/org.h @@ -0,0 +1,31 @@ +#pragma once + +#include "types.h" +#include "util.h" + +#define ORG_START_CH 8 +#define ORG_MAX_TRACKS 16 + +typedef struct org_note { + s32 pos; + u8 len; + u8 key; + u8 vol; + u8 pan; +} org_note_t; + +extern s32 org_freqshift; + +void org_init(struct sfx_bank *drum_bank); +int org_load(const char *name); +void org_free(void); +void org_restart_from(const s32 pos); +void org_tick(void); + +int org_get_wait(void); +int org_get_pos(void); +u16 org_get_mute_mask(void); +u16 org_set_mute_mask(const u16 mask); + +org_note_t *org_get_track(const int tracknum, u32 *numnotes); +org_note_t *org_get_track_pos(const int tracknum); diff --git a/src/spu.c b/src/spu.c new file mode 100644 index 0000000..6896e14 --- /dev/null +++ b/src/spu.c @@ -0,0 +1,146 @@ +#include + +#include "types.h" +#include "spu.h" + +#define SPU_VOICE_BASE ((volatile u16 *)(0x1F801C00)) +#define SPU_KEY_ON_LO ((volatile u16 *)(0x1F801D88)) +#define SPU_KEY_ON_HI ((volatile u16 *)(0x1F801D8A)) +#define SPU_KEY_OFF_LO ((volatile u16 *)(0x1F801D8C)) +#define SPU_KEY_OFF_HI ((volatile u16 *)(0x1F801D8E)) +#define DMA_BASE ((volatile u32 *)(0x1F801080)) + +struct spu_voice { + volatile s16 vol_left; + volatile s16 vol_right; + volatile u16 sample_rate; + volatile u16 sample_startaddr; + volatile u16 attack_decay; + volatile u16 sustain_release; + volatile u16 vol_current; + volatile u16 sample_repeataddr; +}; +#define SPU_VOICE(x) (((volatile struct spu_voice *)SPU_VOICE_BASE) + (x)) + +struct dma_regs { + volatile u32 madr; + volatile u32 bcr; + volatile u32 chcr; + volatile u32 pad; +}; +#define DMA_CTRL(x) (((volatile struct dma_regs *)DMA_BASE) + (x)) +#define DMA_CTRL_SPU 4 + +#define PAN_SHIFT 8 + +u32 spuram_ptr = SPU_RAM_START; + +// saved state for stop/play +static struct { + u32 addr; + s16 vol; // 0 to SPU_MAX_VOLUME + s16 pan; // -255 to 255 + u16 freq; + u16 dirty; +} voice_state[SPU_NUM_VOICES]; + +void spu_init(void) { + SpuInit(); + spu_clear_all_voices(); + spuram_ptr = SPU_RAM_START; +} + +void spu_key_on(const u32 mask) { + *SPU_KEY_ON_LO = mask; + *SPU_KEY_ON_HI = mask >> 16; +} + +void spu_key_off(const u32 mask) { + *SPU_KEY_OFF_LO = mask; + *SPU_KEY_OFF_HI = mask >> 16; +} + +void spu_clear_voice(const u32 v) { + SPU_VOICE(v)->vol_left = 0; + SPU_VOICE(v)->vol_right = 0; + SPU_VOICE(v)->sample_rate = 0; + SPU_VOICE(v)->sample_startaddr = 0; + SPU_VOICE(v)->sample_repeataddr = 0; + SPU_VOICE(v)->attack_decay = 0x000F; + SPU_VOICE(v)->sustain_release = 0x0000; + SPU_VOICE(v)->vol_current = 0; + voice_state[v].vol = 0; + voice_state[v].pan = 0; + voice_state[v].addr = 0; + voice_state[v].freq = 0; + voice_state[v].dirty = 0; +} + +void spu_clear_all_voices(void) { + spu_key_off(0xFFFFFFFF); + for (u32 i = 0; i < 24; ++i) + spu_clear_voice(i); +} + +void spu_set_voice_volume(const u32 v, const s16 vol) { + voice_state[v].vol = vol; + voice_state[v].dirty = 1; +} + +void spu_set_voice_pan(const u32 v, const s16 pan) { + voice_state[v].pan = pan; + voice_state[v].dirty = 1; +} + +void spu_set_voice_freq(const u32 v, const u32 hz) { + voice_state[v].freq = freq2pitch(hz); + voice_state[v].dirty = 1; +} + +void spu_set_voice_pitch(const u32 v, const u32 pitch) { + voice_state[v].freq = pitch; + voice_state[v].dirty = 1; +} + +void spu_set_voice_addr(const u32 v, const u32 addr) { + voice_state[v].addr = (addr >> 3); + voice_state[v].dirty = 1; +} + +static inline void spu_update_voice_volume(const u32 v) { + s32 vol_left = voice_state[v].vol; + s32 vol_right = vol_left; + const s32 pan = voice_state[v].pan; + if (pan < 0) + vol_right = (vol_right * -pan) >> PAN_SHIFT; + else if (pan > 0) + vol_left = (vol_left * pan) >> PAN_SHIFT; + SPU_VOICE(v)->vol_left = vol_left; + SPU_VOICE(v)->vol_right = vol_right; +} + +void spu_flush_voices(void) { + SpuWait(); + for (int v = 0; v < SPU_NUM_VOICES; ++v) { + if (voice_state[v].dirty) { + voice_state[v].dirty = 0; + spu_update_voice_volume(v); + SPU_VOICE(v)->sample_rate = voice_state[v].freq; + SPU_VOICE(v)->sample_startaddr = voice_state[v].addr; + } + } +} + +void spu_play_sample(const u32 ch, const u32 addr, const u32 freq) { + spu_update_voice_volume(ch); // restore volume + voice_state[ch].freq = freq2pitch(freq); + voice_state[ch].addr = (addr >> 3); + SPU_VOICE(ch)->sample_rate = voice_state[ch].freq; + SPU_VOICE(ch)->sample_startaddr = voice_state[ch].addr; + spu_key_on(SPU_VOICECH(ch)); // this restarts the channel on the new address +} + +void spu_wait_for_transfer(void) { + while ((DMA_CTRL(DMA_CTRL_SPU)->chcr & 0x01000000) != 0) { } + SpuWait(); +} diff --git a/src/spu.h b/src/spu.h new file mode 100644 index 0000000..be24598 --- /dev/null +++ b/src/spu.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "types.h" +#include "util.h" + +#define SPU_NUM_VOICES 24 +#define SPU_MAX_VOLUME 0x3FFF +#define SPU_RAM_START 0x1100 + +extern u32 spuram_ptr; + +void spu_init(void); +void spu_key_on(const u32 mask); +void spu_key_off(const u32 mask); +void spu_clear_voice(const u32 v) ; +void spu_set_voice_volume(const u32 v, const s16 vol); +void spu_set_voice_pan(const u32 v, const s16 pan); +void spu_set_voice_freq(const u32 v, const u32 hz); +void spu_set_voice_pitch(const u32 v, const u32 pitch); +void spu_set_voice_addr(const u32 v, const u32 addr); +void spu_flush_voices(void); +void spu_play_sample(const u32 ch, const u32 addr, const u32 srate); +void spu_wait_for_transfer(void); +void spu_clear_all_voices(void); + +static inline u16 freq2pitch(const u32 hz) { + return (hz << 12) / 44100; +} + +// unfortunately the psn00bsdk function for this is bugged: +// it checks against 0x1000..0xffff instead of 0x1000..0x7ffff +// fortunately, the address is stored in a global variable +// unfortunately, reading it from C requires GP-relative addressing +// so we have to implement the function in assembly (see spu.s) +extern u32 spu_set_transfer_addr(const u32 addr); diff --git a/src/spu_a.s b/src/spu_a.s new file mode 100644 index 0000000..e51dfde --- /dev/null +++ b/src/spu_a.s @@ -0,0 +1,28 @@ +.set noreorder + +.include "hwregs_a.h" + +.section .text + +# this is a copy of the function from psn00b that fixes a bug +# where the function tests for 0xffff instead of 0x7ffff +.global spu_set_transfer_addr +.type spu_set_transfer_addr, @function +spu_set_transfer_addr: + li $v0, 0x1000 # Check if value is valid + blt $a0, $v0, .Lbad_value + nop + li $v0, 0x7ffff + bgt $a0, $v0, .Lbad_value + nop + + la $v1, _spu_transfer_addr + srl $v0, $a0, 3 # Set transfer destination address + sh $v0, 0($v1) + + jr $ra + move $v0, $a0 + +.Lbad_value: + jr $ra + move $v0, $0 diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..ba782ef --- /dev/null +++ b/src/types.h @@ -0,0 +1,8 @@ +#pragma once + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..a06456f --- /dev/null +++ b/src/util.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include + +#include "cd.h" +#include "spu.h" +#include "util.h" + +static char errmsg[512]; + +static void __attribute__((noreturn)) fatal(void) { + DISPENV disp; + DRAWENV draw; + SetDefDispEnv(&disp, 0, 0, 320, 240); + SetDefDrawEnv(&draw, 0, 0, 320, 240); + setRGB0(&draw, 0x40, 0x00, 0x00); + draw.isbg = 1; + PutDispEnv(&disp); + PutDrawEnv(&draw); + while (1) { + FntPrint(-1, "FATAL ERROR:\n%s", errmsg); + FntFlush(-1); + DrawSync(0); + VSync(0); + } +} + +// while our libc has a declaration for assert(), it does not actually provide it + +void do_assert(const int expr, const char *strexpr, const char *file, const int line) { + if (!expr) { + snprintf(errmsg, sizeof(errmsg), "ASSERTION FAILED:\n`%s` at %s:%d", strexpr, file, line); + printf("%s\n", errmsg); + fatal(); + } +} + +void panic(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vsnprintf(errmsg, sizeof(errmsg), fmt, args); + va_end(args); + printf("FATAL ERROR: %s\n", errmsg); + fatal(); +} + +struct sfx_bank *load_sfx_bank(const char *fname) { + cd_file_t *f = cd_fopen(fname, 0); + if (!f) panic("could not open bank file '%s'", fname); + + printf("loading bank '%s' at addr %u\n", fname, spuram_ptr); + + const u32 buflen = cd_fread_u32le(f); + const u32 num_sfx = cd_fread_u32le(f); + + struct sfx_bank *bank = malloc(sizeof(*bank) + sizeof(u32) * num_sfx); + ASSERT(bank); + bank->data_len = buflen; + bank->num_sfx = num_sfx; + cd_freadordie(&bank->sfx_addr[0], sizeof(u32) * num_sfx, 1, f); + + u8 *buf = malloc(buflen); + ASSERT(buf); + cd_freadordie(buf, buflen, 1, f); + + cd_fclose(f); + + ASSERT(spuram_ptr == bank->sfx_addr[0] || spuram_ptr == bank->sfx_addr[1]); + + SpuSetTransferMode(SPU_TRANSFER_BY_DMA); + spu_set_transfer_addr(spuram_ptr); + SpuWrite((void *)buf, buflen); + spu_wait_for_transfer(); + + spuram_ptr += buflen; + + printf("bank '%s': read %u bytes of sample data (%u samples), spuram_ptr=%u\n", fname, buflen, num_sfx, spuram_ptr); + printf("bank ident: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); + /* + for (u32 i = 0; i < bank->num_sfx; ++i) + printf("* (%03d) 0x%06x\n", i, bank->sfx_addr[i]); + */ + + free(buf); + + return bank; +} + +int free_sfx_bank(struct sfx_bank *bank) { + const u32 prevaddr = spuram_ptr - bank->data_len; + if (prevaddr == bank->sfx_addr[0] || prevaddr == bank->sfx_addr[1]) + spuram_ptr = prevaddr; // free SPU RAM if this is the last loaded bank + free(bank); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..6d3333b --- /dev/null +++ b/src/util.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" + +#define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) +#define ASSERT(x) do_assert((int)(x), #x, __FILE__, __LINE__) + +void panic(const char *fmt, ...) __attribute__((noreturn)); +void do_assert(const int, const char *, const char *, const int); + +struct sfx_bank { + u32 data_len; + u32 num_sfx; + u32 sfx_addr[]; // [num_sfx]; +}; + +struct sfx_bank *load_sfx_bank(const char *fname); +int free_sfx_bank(struct sfx_bank *bank); diff --git a/system.cnf b/system.cnf new file mode 100644 index 0000000..6e03647 --- /dev/null +++ b/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\orgplay.exe;1 +TCB=4 +EVENT=10 +STACK=801FFFF0 diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..85d30e6 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,15 @@ +CC ?= gcc +LIBPSXAV_SRC := $(wildcard src/libpsxav/*.c) + +all: orgconv.exe sfxconv.exe + +orgconv.exe: src/orgconv.c $(LIBPSXAV_SRC) + $(CC) -g -Og -o $@ $^ + +sfxconv.exe: src/sfxconv.c $(LIBPSXAV_SRC) + $(CC) -g -Og -o $@ $^ + +clean: + rm -f *.exe + +.PHONY: clean diff --git a/tools/make_banks.sh b/tools/make_banks.sh new file mode 100644 index 0000000..425982d --- /dev/null +++ b/tools/make_banks.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +if [[ $# -eq 0 ]] ; then + echo 'usage: ./make_banks.sh []' + exit 0 +fi + +for fn in `ls "$1"`; do + ./orgconv "$1/$fn" "$2" "$3/${fn%%.*}.bnk" $4 +done diff --git a/tools/src/common.h b/tools/src/common.h new file mode 100644 index 0000000..97db6d0 --- /dev/null +++ b/tools/src/common.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#define NUM_INST 100 +#define INST_LEN 256 +#define MAX_SFX 160 + +#define SPURAM_ALIGN 8 +#define SPURAM_START 0x1100 +#define SPURAM_SIZE (512 * 1024) +#define SPURAM_AVAIL (SPURAM_SIZE - SPURAM_START) + +#define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) + +#pragma pack(push, 1) + +struct bank_hdr { + uint32_t data_size; // size of raw SPU data at the end + uint32_t num_sfx; // number of samples in bank, including #0 (dummy) and all the unused samples + uint32_t sfx_addr[1]; // address in SPU RAM of each sample, first one is always 0, others may be 0 (means it's unused) + // after the last sfx_addr, raw SPU data follows +}; + +struct sfx { + int16_t *data; + uint32_t len; // in samples + uint32_t freq; + uint32_t addr; +}; + +#pragma pack(pop) diff --git a/tools/src/dr_wav.h b/tools/src/dr_wav.h new file mode 100644 index 0000000..7e50e4b --- /dev/null +++ b/tools/src/dr_wav.h @@ -0,0 +1,7932 @@ +/* +WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_wav - v0.13.0 - 2021-07-01 + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs +*/ + +/* +Introduction +============ +This is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_WAV_IMPLEMENTATION + #include "dr_wav.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header file. Do something like the following to read audio data: + + ```c + drwav wav; + if (!drwav_init_file(&wav, "my_song.wav", NULL)) { + // Error opening WAV file. + } + + drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32)); + size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); + + ... + + drwav_uninit(&wav); + ``` + +If you just want to quickly open and read the audio data in a single operation you can do something like this: + + ```c + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalPCMFrameCount; + float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount, NULL); + if (pSampleData == NULL) { + // Error opening and reading WAV file. + } + + ... + + drwav_free(pSampleData, NULL); + ``` + +The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in this case), but you can still output the +audio data in its internal format (see notes below for supported formats): + + ```c + size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); + ``` + +You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for a particular data format: + + ```c + size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer); + ``` + +dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`, +`drwav_init_file_write()`, etc. Use `drwav_write_pcm_frames()` to write samples, or `drwav_write_raw()` to write raw data in the "data" chunk. + + ```c + drwav_data_format format; + format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. + format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. + format.channels = 2; + format.sampleRate = 44100; + format.bitsPerSample = 16; + drwav_init_file_write(&wav, "data/recording.wav", &format, NULL); + + ... + + drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples); + ``` + +dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work without any manual intervention. + + +Build Options +============= +#define these options before including this file. + +#define DR_WAV_NO_CONVERSION_API + Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`. + +#define DR_WAV_NO_STDIO + Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc. + + + +Notes +===== +- Samples are always interleaved. +- The default read function does not do any data conversion. Use `drwav_read_pcm_frames_f32()`, `drwav_read_pcm_frames_s32()` and `drwav_read_pcm_frames_s16()` + to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively. Tested and supported internal + formats include the following: + - Unsigned 8-bit PCM + - Signed 12-bit PCM + - Signed 16-bit PCM + - Signed 24-bit PCM + - Signed 32-bit PCM + - IEEE 32-bit floating point + - IEEE 64-bit floating point + - A-law and u-law + - Microsoft ADPCM + - IMA ADPCM (DVI, format code 0x11) +- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format. +*/ + +#ifndef dr_wav_h +#define dr_wav_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRWAV_STRINGIFY(x) #x +#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) + +#define DRWAV_VERSION_MAJOR 0 +#define DRWAV_VERSION_MINOR 13 +#define DRWAV_VERSION_REVISION 0 +#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) + +#include /* For size_t. */ + +/* Sized types. */ +typedef signed char drwav_int8; +typedef unsigned char drwav_uint8; +typedef signed short drwav_int16; +typedef unsigned short drwav_uint16; +typedef signed int drwav_int32; +typedef unsigned int drwav_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drwav_int64; + typedef unsigned __int64 drwav_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drwav_int64; + typedef unsigned long long drwav_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + typedef drwav_uint64 drwav_uintptr; +#else + typedef drwav_uint32 drwav_uintptr; +#endif +typedef drwav_uint8 drwav_bool8; +typedef drwav_uint32 drwav_bool32; +#define DRWAV_TRUE 1 +#define DRWAV_FALSE 0 + +#if !defined(DRWAV_API) + #if defined(DRWAV_DLL) + #if defined(_WIN32) + #define DRWAV_DLL_IMPORT __declspec(dllimport) + #define DRWAV_DLL_EXPORT __declspec(dllexport) + #define DRWAV_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) + #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) + #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRWAV_DLL_IMPORT + #define DRWAV_DLL_EXPORT + #define DRWAV_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) + #define DRWAV_API DRWAV_DLL_EXPORT + #else + #define DRWAV_API DRWAV_DLL_IMPORT + #endif + #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE + #else + #define DRWAV_API extern + #define DRWAV_PRIVATE static + #endif +#endif + +typedef drwav_int32 drwav_result; +#define DRWAV_SUCCESS 0 +#define DRWAV_ERROR -1 /* A generic error. */ +#define DRWAV_INVALID_ARGS -2 +#define DRWAV_INVALID_OPERATION -3 +#define DRWAV_OUT_OF_MEMORY -4 +#define DRWAV_OUT_OF_RANGE -5 +#define DRWAV_ACCESS_DENIED -6 +#define DRWAV_DOES_NOT_EXIST -7 +#define DRWAV_ALREADY_EXISTS -8 +#define DRWAV_TOO_MANY_OPEN_FILES -9 +#define DRWAV_INVALID_FILE -10 +#define DRWAV_TOO_BIG -11 +#define DRWAV_PATH_TOO_LONG -12 +#define DRWAV_NAME_TOO_LONG -13 +#define DRWAV_NOT_DIRECTORY -14 +#define DRWAV_IS_DIRECTORY -15 +#define DRWAV_DIRECTORY_NOT_EMPTY -16 +#define DRWAV_END_OF_FILE -17 +#define DRWAV_NO_SPACE -18 +#define DRWAV_BUSY -19 +#define DRWAV_IO_ERROR -20 +#define DRWAV_INTERRUPT -21 +#define DRWAV_UNAVAILABLE -22 +#define DRWAV_ALREADY_IN_USE -23 +#define DRWAV_BAD_ADDRESS -24 +#define DRWAV_BAD_SEEK -25 +#define DRWAV_BAD_PIPE -26 +#define DRWAV_DEADLOCK -27 +#define DRWAV_TOO_MANY_LINKS -28 +#define DRWAV_NOT_IMPLEMENTED -29 +#define DRWAV_NO_MESSAGE -30 +#define DRWAV_BAD_MESSAGE -31 +#define DRWAV_NO_DATA_AVAILABLE -32 +#define DRWAV_INVALID_DATA -33 +#define DRWAV_TIMEOUT -34 +#define DRWAV_NO_NETWORK -35 +#define DRWAV_NOT_UNIQUE -36 +#define DRWAV_NOT_SOCKET -37 +#define DRWAV_NO_ADDRESS -38 +#define DRWAV_BAD_PROTOCOL -39 +#define DRWAV_PROTOCOL_UNAVAILABLE -40 +#define DRWAV_PROTOCOL_NOT_SUPPORTED -41 +#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRWAV_SOCKET_NOT_SUPPORTED -44 +#define DRWAV_CONNECTION_RESET -45 +#define DRWAV_ALREADY_CONNECTED -46 +#define DRWAV_NOT_CONNECTED -47 +#define DRWAV_CONNECTION_REFUSED -48 +#define DRWAV_NO_HOST -49 +#define DRWAV_IN_PROGRESS -50 +#define DRWAV_CANCELLED -51 +#define DRWAV_MEMORY_ALREADY_MAPPED -52 +#define DRWAV_AT_END -53 + +/* Common data formats. */ +#define DR_WAVE_FORMAT_PCM 0x1 +#define DR_WAVE_FORMAT_ADPCM 0x2 +#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define DR_WAVE_FORMAT_ALAW 0x6 +#define DR_WAVE_FORMAT_MULAW 0x7 +#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE + +/* Flags to pass into drwav_init_ex(), etc. */ +#define DRWAV_SEQUENTIAL 0x00000001 + +DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); +DRWAV_API const char* drwav_version_string(void); + +typedef enum +{ + drwav_seek_origin_start, + drwav_seek_origin_current +} drwav_seek_origin; + +typedef enum +{ + drwav_container_riff, + drwav_container_w64, + drwav_container_rf64 +} drwav_container; + +typedef struct +{ + union + { + drwav_uint8 fourcc[4]; + drwav_uint8 guid[16]; + } id; + + /* The size in bytes of the chunk. */ + drwav_uint64 sizeInBytes; + + /* + RIFF = 2 byte alignment. + W64 = 8 byte alignment. + */ + unsigned int paddingSize; +} drwav_chunk_header; + +typedef struct +{ + /* + The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications + that require support for data formats not natively supported by dr_wav. + */ + drwav_uint16 formatTag; + + /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */ + drwav_uint16 channels; + + /* The sample rate. Usually set to something like 44100. */ + drwav_uint32 sampleRate; + + /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */ + drwav_uint32 avgBytesPerSec; + + /* Block align. This is equal to the number of channels * bytes per sample. */ + drwav_uint16 blockAlign; + + /* Bits per sample. */ + drwav_uint16 bitsPerSample; + + /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */ + drwav_uint16 extendedSize; + + /* + The number of valid bits per sample. When is equal to WAVE_FORMAT_EXTENSIBLE, + is always rounded up to the nearest multiple of 8. This variable contains information about exactly how + many bits are valid per sample. Mainly used for informational purposes. + */ + drwav_uint16 validBitsPerSample; + + /* The channel mask. Not used at the moment. */ + drwav_uint32 channelMask; + + /* The sub-format, exactly as specified by the wave file. */ + drwav_uint8 subFormat[16]; +} drwav_fmt; + +DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); + + +/* +Callback for when data is read. Return value is the number of bytes actually read. + +pUserData [in] The user data that was passed to drwav_init() and family. +pBufferOut [out] The output buffer. +bytesToRead [in] The number of bytes to read. + +Returns the number of bytes actually read. + +A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +either the entire bytesToRead is filled or you have reached the end of the stream. +*/ +typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* +Callback for when data is written. Returns value is the number of bytes actually written. + +pUserData [in] The user data that was passed to drwav_init_write() and family. +pData [out] A pointer to the data to write. +bytesToWrite [in] The number of bytes to write. + +Returns the number of bytes actually written. + +If the return value differs from bytesToWrite, it indicates an error. +*/ +typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); + +/* +Callback for when data needs to be seeked. + +pUserData [in] The user data that was passed to drwav_init() and family. +offset [in] The number of bytes to move, relative to the origin. Will never be negative. +origin [in] The origin of the seek - the current position or the start of the stream. + +Returns whether or not the seek was successful. + +Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or +drwav_seek_origin_current. +*/ +typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); + +/* +Callback for when drwav_init_ex() finds a chunk. + +pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family. +onRead [in] A pointer to the function to call when reading. +onSeek [in] A pointer to the function to call when seeking. +pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family. +pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk. +container [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF. +pFMT [in] A pointer to the object containing the contents of the "fmt" chunk. + +Returns the number of bytes read + seeked. + +To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same for seeking with onSeek(). The return value must +be the total number of bytes you have read _plus_ seeked. + +Use the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should +use `id.fourcc`, otherwise you should use `id.guid`. + +The `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the +`DR_WAVE_FORMAT_*` identifiers. + +The read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk. +*/ +typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); + +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drwav_allocation_callbacks; + +/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */ +typedef struct +{ + const drwav_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drwav__memory_stream; + +/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */ +typedef struct +{ + void** ppData; + size_t* pDataSize; + size_t dataSize; + size_t dataCapacity; + size_t currentWritePos; +} drwav__memory_stream_write; + +typedef struct +{ + drwav_container container; /* RIFF, W64. */ + drwav_uint32 format; /* DR_WAVE_FORMAT_* */ + drwav_uint32 channels; + drwav_uint32 sampleRate; + drwav_uint32 bitsPerSample; +} drwav_data_format; + +typedef enum +{ + drwav_metadata_type_none = 0, + + /* + Unknown simply means a chunk that drwav does not handle specifically. You can still ask to + receive these chunks as metadata objects. It is then up to you to interpret the chunk's data. + You can also write unknown metadata to a wav file. Be careful writing unknown chunks if you + have also edited the audio data. The unknown chunks could represent offsets/sizes that no + longer correctly correspond to the audio data. + */ + drwav_metadata_type_unknown = 1 << 0, + + /* Only 1 of each of these metadata items are allowed in a wav file. */ + drwav_metadata_type_smpl = 1 << 1, + drwav_metadata_type_inst = 1 << 2, + drwav_metadata_type_cue = 1 << 3, + drwav_metadata_type_acid = 1 << 4, + drwav_metadata_type_bext = 1 << 5, + + /* + Wav files often have a LIST chunk. This is a chunk that contains a set of subchunks. For this + higher-level metadata API, we don't make a distinction between a regular chunk and a LIST + subchunk. Instead, they are all just 'metadata' items. + + There can be multiple of these metadata items in a wav file. + */ + drwav_metadata_type_list_label = 1 << 6, + drwav_metadata_type_list_note = 1 << 7, + drwav_metadata_type_list_labelled_cue_region = 1 << 8, + + drwav_metadata_type_list_info_software = 1 << 9, + drwav_metadata_type_list_info_copyright = 1 << 10, + drwav_metadata_type_list_info_title = 1 << 11, + drwav_metadata_type_list_info_artist = 1 << 12, + drwav_metadata_type_list_info_comment = 1 << 13, + drwav_metadata_type_list_info_date = 1 << 14, + drwav_metadata_type_list_info_genre = 1 << 15, + drwav_metadata_type_list_info_album = 1 << 16, + drwav_metadata_type_list_info_tracknumber = 1 << 17, + + /* Other type constants for convenience. */ + drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software + | drwav_metadata_type_list_info_copyright + | drwav_metadata_type_list_info_title + | drwav_metadata_type_list_info_artist + | drwav_metadata_type_list_info_comment + | drwav_metadata_type_list_info_date + | drwav_metadata_type_list_info_genre + | drwav_metadata_type_list_info_album + | drwav_metadata_type_list_info_tracknumber, + + drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label + | drwav_metadata_type_list_note + | drwav_metadata_type_list_labelled_cue_region, + + drwav_metadata_type_all = -2, /*0xFFFFFFFF & ~drwav_metadata_type_unknown,*/ + drwav_metadata_type_all_including_unknown = -1 /*0xFFFFFFFF,*/ +} drwav_metadata_type; + +/* +Sampler Metadata + +The sampler chunk contains information about how a sound should be played in the context of a whole +audio production, and when used in a sampler. See https://en.wikipedia.org/wiki/Sample-based_synthesis. +*/ +typedef enum +{ + drwav_smpl_loop_type_forward = 0, + drwav_smpl_loop_type_pingpong = 1, + drwav_smpl_loop_type_backward = 2 +} drwav_smpl_loop_type; + +typedef struct +{ + /* The ID of the associated cue point, see drwav_cue and drwav_cue_point. As with all cue point IDs, this can correspond to a label chunk to give this loop a name, see drwav_list_label_or_note. */ + drwav_uint32 cuePointId; + + /* See drwav_smpl_loop_type. */ + drwav_uint32 type; + + /* The byte offset of the first sample to be played in the loop. */ + drwav_uint32 firstSampleByteOffset; + + /* The byte offset into the audio data of the last sample to be played in the loop. */ + drwav_uint32 lastSampleByteOffset; + + /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */ + drwav_uint32 sampleFraction; + + /* Number of times to play the loop. 0 means loop infinitely. */ + drwav_uint32 playCount; +} drwav_smpl_loop; + +typedef struct +{ + /* IDs for a particular MIDI manufacturer. 0 if not used. */ + drwav_uint32 manufacturerId; + drwav_uint32 productId; + + /* The period of 1 sample in nanoseconds. */ + drwav_uint32 samplePeriodNanoseconds; + + /* The MIDI root note of this file. 0 to 127. */ + drwav_uint32 midiUnityNote; + + /* The fraction of a semitone up from the given MIDI note. This is a value from 0 to UINT32_MAX, where 0 means no change and (UINT32_MAX / 2) is half a semitone (AKA 50 cents). */ + drwav_uint32 midiPitchFraction; + + /* Data relating to SMPTE standards which are used for syncing audio and video. 0 if not used. */ + drwav_uint32 smpteFormat; + drwav_uint32 smpteOffset; + + /* drwav_smpl_loop loops. */ + drwav_uint32 sampleLoopCount; + + /* Optional sampler-specific data. */ + drwav_uint32 samplerSpecificDataSizeInBytes; + + drwav_smpl_loop* pLoops; + drwav_uint8* pSamplerSpecificData; +} drwav_smpl; + +/* +Instrument Metadata + +The inst metadata contains data about how a sound should be played as part of an instrument. This +commonly read by samplers. See https://en.wikipedia.org/wiki/Sample-based_synthesis. +*/ +typedef struct +{ + drwav_int8 midiUnityNote; /* The root note of the audio as a MIDI note number. 0 to 127. */ + drwav_int8 fineTuneCents; /* -50 to +50 */ + drwav_int8 gainDecibels; /* -64 to +64 */ + drwav_int8 lowNote; /* 0 to 127 */ + drwav_int8 highNote; /* 0 to 127 */ + drwav_int8 lowVelocity; /* 1 to 127 */ + drwav_int8 highVelocity; /* 1 to 127 */ +} drwav_inst; + +/* +Cue Metadata + +Cue points are markers at specific points in the audio. They often come with an associated piece of +drwav_list_label_or_note metadata which contains the text for the marker. +*/ +typedef struct +{ + /* Unique identification value. */ + drwav_uint32 id; + + /* Set to 0. This is only relevant if there is a 'playlist' chunk - which is not supported by dr_wav. */ + drwav_uint32 playOrderPosition; + + /* Should always be "data". This represents the fourcc value of the chunk that this cue point corresponds to. dr_wav only supports a single data chunk so this should always be "data". */ + drwav_uint8 dataChunkId[4]; + + /* Set to 0. This is only relevant if there is a wave list chunk. dr_wav, like lots of readers/writers, do not support this. */ + drwav_uint32 chunkStart; + + /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */ + drwav_uint32 blockStart; + + /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */ + drwav_uint32 sampleByteOffset; +} drwav_cue_point; + +typedef struct +{ + drwav_uint32 cuePointCount; + drwav_cue_point *pCuePoints; +} drwav_cue; + +/* +Acid Metadata + +This chunk contains some information about the time signature and the tempo of the audio. +*/ +typedef enum +{ + drwav_acid_flag_one_shot = 1, /* If this is not set, then it is a loop instead of a one-shot. */ + drwav_acid_flag_root_note_set = 2, + drwav_acid_flag_stretch = 4, + drwav_acid_flag_disk_based = 8, + drwav_acid_flag_acidizer = 16 /* Not sure what this means. */ +} drwav_acid_flag; + +typedef struct +{ + /* A bit-field, see drwav_acid_flag. */ + drwav_uint32 flags; + + /* Valid if flags contains drwav_acid_flag_root_note_set. It represents the MIDI root note the file - a value from 0 to 127. */ + drwav_uint16 midiUnityNote; + + /* Reserved values that should probably be ignored. reserved1 seems to often be 128 and reserved2 is 0. */ + drwav_uint16 reserved1; + float reserved2; + + /* Number of beats. */ + drwav_uint32 numBeats; + + /* The time signature of the audio. */ + drwav_uint16 meterDenominator; + drwav_uint16 meterNumerator; + + /* Beats per minute of the track. Setting a value of 0 suggests that there is no tempo. */ + float tempo; +} drwav_acid; + +/* +Cue Label or Note metadata + +These are 2 different types of metadata, but they have the exact same format. Labels tend to be the +more common and represent a short name for a cue point. Notes might be used to represent a longer +comment. +*/ +typedef struct +{ + /* The ID of a cue point that this label or note corresponds to. */ + drwav_uint32 cuePointId; + + /* Size of the string not including any null terminator. */ + drwav_uint32 stringLength; + + /* The string. The *init_with_metadata functions null terminate this for convenience. */ + char* pString; +} drwav_list_label_or_note; + +/* +BEXT metadata, also known as Broadcast Wave Format (BWF) + +This metadata adds some extra description to an audio file. You must check the version field to +determine if the UMID or the loudness fields are valid. +*/ +typedef struct +{ + /* + These top 3 fields, and the umid field are actually defined in the standard as a statically + sized buffers. In order to reduce the size of this struct (and therefore the union in the + metadata struct), we instead store these as pointers. + */ + char* pDescription; /* Can be NULL or a null-terminated string, must be <= 256 characters. */ + char* pOriginatorName; /* Can be NULL or a null-terminated string, must be <= 32 characters. */ + char* pOriginatorReference; /* Can be NULL or a null-terminated string, must be <= 32 characters. */ + char pOriginationDate[10]; /* ASCII "yyyy:mm:dd". */ + char pOriginationTime[8]; /* ASCII "hh:mm:ss". */ + drwav_uint64 timeReference; /* First sample count since midnight. */ + drwav_uint16 version; /* Version of the BWF, check this to see if the fields below are valid. */ + + /* + Unrestricted ASCII characters containing a collection of strings terminated by CR/LF. Each + string shall contain a description of a coding process applied to the audio data. + */ + char* pCodingHistory; + drwav_uint32 codingHistorySize; + + /* Fields below this point are only valid if the version is 1 or above. */ + drwav_uint8* pUMID; /* Exactly 64 bytes of SMPTE UMID */ + + /* Fields below this point are only valid if the version is 2 or above. */ + drwav_uint16 loudnessValue; /* Integrated Loudness Value of the file in LUFS (multiplied by 100). */ + drwav_uint16 loudnessRange; /* Loudness Range of the file in LU (multiplied by 100). */ + drwav_uint16 maxTruePeakLevel; /* Maximum True Peak Level of the file expressed as dBTP (multiplied by 100). */ + drwav_uint16 maxMomentaryLoudness; /* Highest value of the Momentary Loudness Level of the file in LUFS (multiplied by 100). */ + drwav_uint16 maxShortTermLoudness; /* Highest value of the Short-Term Loudness Level of the file in LUFS (multiplied by 100). */ +} drwav_bext; + +/* +Info Text Metadata + +There a many different types of information text that can be saved in this format. This is where +things like the album name, the artists, the year it was produced, etc are saved. See +drwav_metadata_type for the full list of types that dr_wav supports. +*/ +typedef struct +{ + /* Size of the string not including any null terminator. */ + drwav_uint32 stringLength; + + /* The string. The *init_with_metadata functions null terminate this for convenience. */ + char* pString; +} drwav_list_info_text; + +/* +Labelled Cue Region Metadata + +The labelled cue region metadata is used to associate some region of audio with text. The region +starts at a cue point, and extends for the given number of samples. +*/ +typedef struct +{ + /* The ID of a cue point that this object corresponds to. */ + drwav_uint32 cuePointId; + + /* The number of samples from the cue point forwards that should be considered this region */ + drwav_uint32 sampleLength; + + /* Four characters used to say what the purpose of this region is. */ + drwav_uint8 purposeId[4]; + + /* Unsure of the exact meanings of these. It appears to be acceptable to set them all to 0. */ + drwav_uint16 country; + drwav_uint16 language; + drwav_uint16 dialect; + drwav_uint16 codePage; + + /* Size of the string not including any null terminator. */ + drwav_uint32 stringLength; + + /* The string. The *init_with_metadata functions null terminate this for convenience. */ + char* pString; +} drwav_list_labelled_cue_region; + +/* +Unknown Metadata + +This chunk just represents a type of chunk that dr_wav does not understand. + +Unknown metadata has a location attached to it. This is because wav files can have a LIST chunk +that contains subchunks. These LIST chunks can be one of two types. An adtl list, or an INFO +list. This enum is used to specify the location of a chunk that dr_wav currently doesn't support. +*/ +typedef enum +{ + drwav_metadata_location_invalid, + drwav_metadata_location_top_level, + drwav_metadata_location_inside_info_list, + drwav_metadata_location_inside_adtl_list +} drwav_metadata_location; + +typedef struct +{ + drwav_uint8 id[4]; + drwav_metadata_location chunkLocation; + drwav_uint32 dataSizeInBytes; + drwav_uint8* pData; +} drwav_unknown_metadata; + +/* +Metadata is saved as a union of all the supported types. +*/ +typedef struct +{ + /* Determines which item in the union is valid. */ + drwav_metadata_type type; + + union + { + drwav_cue cue; + drwav_smpl smpl; + drwav_acid acid; + drwav_inst inst; + drwav_bext bext; + drwav_list_label_or_note labelOrNote; /* List label or list note. */ + drwav_list_labelled_cue_region labelledCueRegion; + drwav_list_info_text infoText; /* Any of the list info types. */ + drwav_unknown_metadata unknown; + } data; +} drwav_metadata; + +typedef struct +{ + /* A pointer to the function to call when more data is needed. */ + drwav_read_proc onRead; + + /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */ + drwav_write_proc onWrite; + + /* A pointer to the function to call when the wav file needs to be seeked. */ + drwav_seek_proc onSeek; + + /* The user data to pass to callbacks. */ + void* pUserData; + + /* Allocation callbacks. */ + drwav_allocation_callbacks allocationCallbacks; + + + /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */ + drwav_container container; + + + /* Structure containing format information exactly as specified by the wav file. */ + drwav_fmt fmt; + + /* The sample rate. Will be set to something like 44100. */ + drwav_uint32 sampleRate; + + /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */ + drwav_uint16 channels; + + /* The bits per sample. Will be set to something like 16, 24, etc. */ + drwav_uint16 bitsPerSample; + + /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */ + drwav_uint16 translatedFormatTag; + + /* The total number of PCM frames making up the audio data. */ + drwav_uint64 totalPCMFrameCount; + + + /* The size in bytes of the data chunk. */ + drwav_uint64 dataChunkDataSize; + + /* The position in the stream of the first data byte of the data chunk. This is used for seeking. */ + drwav_uint64 dataChunkDataPos; + + /* The number of bytes remaining in the data chunk. */ + drwav_uint64 bytesRemaining; + + /* The current read position in PCM frames. */ + drwav_uint64 readCursorInPCMFrames; + + + /* + Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always + set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation. + */ + drwav_uint64 dataChunkDataSizeTargetWrite; + + /* Keeps track of whether or not the wav writer was initialized in sequential mode. */ + drwav_bool32 isSequentialWrite; + + + /* A bit-field of drwav_metadata_type values, only bits set in this variable are parsed and saved */ + drwav_metadata_type allowedMetadataTypes; + + /* A array of metadata. This is valid after the *init_with_metadata call returns. It will be valid until drwav_uninit() is called. You can take ownership of this data with drwav_take_ownership_of_metadata(). */ + drwav_metadata* pMetadata; + drwav_uint32 metadataCount; + + + /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */ + drwav__memory_stream memoryStream; + drwav__memory_stream_write memoryStreamWrite; + + + /* Microsoft ADPCM specific data. */ + struct + { + drwav_uint32 bytesRemainingInBlock; + drwav_uint16 predictor[2]; + drwav_int32 delta[2]; + drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */ + drwav_uint32 cachedFrameCount; + drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */ + } msadpcm; + + /* IMA ADPCM specific data. */ + struct + { + drwav_uint32 bytesRemainingInBlock; + drwav_int32 predictor[2]; + drwav_int32 stepIndex[2]; + drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */ + drwav_uint32 cachedFrameCount; + } ima; +} drwav; + + +/* +Initializes a pre-allocated drwav object for reading. + +pWav [out] A pointer to the drwav object being initialized. +onRead [in] The function to call when data needs to be read from the client. +onSeek [in] The function to call when the read position of the client data needs to move. +onChunk [in, optional] The function to call when a chunk is enumerated at initialized time. +pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk. +flags [in, optional] A set of flags for controlling how things are loaded. + +Returns true if successful; false otherwise. + +Close the loader with drwav_uninit(). + +This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() +to open the stream from a file or from a block of memory respectively. + +Possible values for flags: + DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function + to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored. + +drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);". + +The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt +after the function returns. + +See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() +*/ +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* +Initializes a pre-allocated drwav object for writing. + +onWrite [in] The function to call when data needs to be written. +onSeek [in] The function to call when the write position needs to move. +pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek. +metadata, numMetadata [in, optional] An array of metadata objects that should be written to the file. The array is not edited. You are responsible for this metadata memory and it must maintain valid until drwav_uninit() is called. + +Returns true if successful; false otherwise. + +Close the writer with drwav_uninit(). + +This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write() +to open the stream from a file or from a block of memory respectively. + +If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform +a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek. + +See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit() +*/ +DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); + +/* +Utility function to determine the target size of the entire data to be written (including all headers and chunks). + +Returns the target size in bytes. + +The metadata argument can be NULL meaning no metadata exists. + +Useful if the application needs to know the size to allocate. + +Only writing to the RIFF chunk and one data chunk is currently supported. + +See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write() +*/ +DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); + +/* +Take ownership of the metadata objects that were allocated via one of the init_with_metadata() function calls. The init_with_metdata functions perform a single heap allocation for this metadata. + +Useful if you want the data to persist beyond the lifetime of the drwav object. + +You must free the data returned from this function using drwav_free(). +*/ +DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); + +/* +Uninitializes the given drwav object. + +Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()). +*/ +DRWAV_API drwav_result drwav_uninit(drwav* pWav); + + +/* +Reads raw audio data. + +This is the lowest level function for reading audio data. It simply reads the given number of +bytes of the raw internal sample data. + +Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for +reading sample data in a consistent format. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of bytes actually read. +*/ +DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); + +/* +Reads up to the specified number of PCM frames from the WAV file. + +The output data will be in the file's internal format, converted to native-endian byte order. Use +drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format. + +If the return value is less than it means the end of the file has been reached or +you have requested more PCM frames than can possibly fit in the output buffer. + +This function will only work when sample data is of a fixed size and uncompressed. If you are +using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32(). + +pBufferOut can be NULL in which case a seek will be performed. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); + +/* +Seeks to the given PCM frame. + +Returns true if successful; false otherwise. +*/ +DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); + +/* +Retrieves the current read position in pcm frames. +*/ +DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); + +/* +Retrieves the length of the file. +*/ +DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); + + +/* +Writes raw audio data. + +Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error. +*/ +DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); + +/* +Writes PCM frames. + +Returns the number of PCM frames written. + +Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to +little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion. +*/ +DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); + +/* Conversion Utilities */ +#ifndef DR_WAV_NO_CONVERSION_API + +/* +Reads a chunk of audio data and converts it to signed 16-bit PCM samples. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of PCM frames actually read. + +If the return value is less than it means the end of the file has been reached. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); + +/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); + +/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + + +/* +Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of PCM frames actually read. + +If the return value is less than it means the end of the file has been reached. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); + +/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); + +/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); + +/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + + +/* +Reads a chunk of audio data and converts it to signed 32-bit PCM samples. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of PCM frames actually read. + +If the return value is less than it means the end of the file has been reached. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); + +/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); + +/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); + +/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +#endif /* DR_WAV_NO_CONVERSION_API */ + + +/* High-Level Convenience Helpers */ + +#ifndef DR_WAV_NO_STDIO +/* +Helper for initializing a wave file for reading using stdio. + +This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav +objects because the operating system may restrict the number of file handles an application can have open at +any given time. +*/ +DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); + + +/* +Helper for initializing a wave file for writing using stdio. + +This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav +objects because the operating system may restrict the number of file handles an application can have open at +any given time. +*/ +DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif /* DR_WAV_NO_STDIO */ + +/* +Helper for initializing a loader from a pre-allocated memory buffer. + +This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +the lifetime of the drwav object. + +The buffer should contain the contents of the entire wave file, not just the sample data. +*/ +DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* +Helper for initializing a writer which outputs data to a memory buffer. + +dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free(). + +The buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid +until after drwav_uninit() has been called. +*/ +DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); + + +#ifndef DR_WAV_NO_CONVERSION_API +/* +Opens and reads an entire wav file in a single operation. + +The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. +*/ +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_WAV_NO_STDIO +/* +Opens and decodes an entire wav file in a single operation. + +The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. +*/ +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif +/* +Opens and decodes an entire wav file from a block of memory in a single operation. + +The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. +*/ +DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif + +/* Frees data that was allocated internally by dr_wav. */ +DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* Converts bytes from a wav stream to a sized type of native endian. */ +DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); +DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); +DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); +DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); +DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); +DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); +DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); + +/* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */ +DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); + +/* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */ +DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); + +#ifdef __cplusplus +} +#endif +#endif /* dr_wav_h */ + + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) +#ifndef dr_wav_c +#define dr_wav_c + +#include +#include /* For memcpy(), memset() */ +#include /* For INT_MAX */ + +#ifndef DR_WAV_NO_STDIO +#include +#include +#endif + +/* Standard library stuff. */ +#ifndef DRWAV_ASSERT +#include +#define DRWAV_ASSERT(expression) assert(expression) +#endif +#ifndef DRWAV_MALLOC +#define DRWAV_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRWAV_REALLOC +#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRWAV_FREE +#define DRWAV_FREE(p) free((p)) +#endif +#ifndef DRWAV_COPY_MEMORY +#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRWAV_ZERO_MEMORY +#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef DRWAV_ZERO_OBJECT +#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) +#endif + +#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) +#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) + +#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ + +/* CPU architecture. */ +#if defined(__x86_64__) || defined(_M_X64) + #define DRWAV_X64 +#elif defined(__i386) || defined(_M_IX86) + #define DRWAV_X86 +#elif defined(__arm__) || defined(_M_ARM) + #define DRWAV_ARM +#endif + +#ifdef _MSC_VER + #define DRWAV_INLINE __forceinline +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define DRWAV_INLINE __inline__ __attribute__((always_inline)) + #else + #define DRWAV_INLINE inline __attribute__((always_inline)) + #endif +#elif defined(__WATCOMC__) + #define DRWAV_INLINE __inline +#else + #define DRWAV_INLINE +#endif + +#if defined(SIZE_MAX) + #define DRWAV_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRWAV_SIZE_MAX 0xFFFFFFFF + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #endif +#endif + +DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRWAV_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRWAV_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRWAV_VERSION_REVISION; + } +} + +DRWAV_API const char* drwav_version_string(void) +{ + return DRWAV_VERSION_STRING; +} + +/* +These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are +you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation. +*/ +#ifndef DRWAV_MAX_SAMPLE_RATE +#define DRWAV_MAX_SAMPLE_RATE 384000 +#endif +#ifndef DRWAV_MAX_CHANNELS +#define DRWAV_MAX_CHANNELS 256 +#endif +#ifndef DRWAV_MAX_BITS_PER_SAMPLE +#define DRWAV_MAX_BITS_PER_SAMPLE 64 +#endif + +static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */ +static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */ +/*static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */ +/*static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */ + + +static DRWAV_INLINE int drwav__is_little_endian(void) +{ +#if defined(DRWAV_X86) || defined(DRWAV_X64) + return DRWAV_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return DRWAV_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + + +static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) +{ + int i; + for (i = 0; i < 16; ++i) { + guid[i] = data[i]; + } +} + + +static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) +{ +#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) +{ +#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ + /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ + drwav_uint32 r; + __asm__ __volatile__ ( + #if defined(DRWAV_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) +{ +#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ + return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | + ((n & ((drwav_uint64)0xFF000000 )) << 8) | + ((n & ((drwav_uint64)0x00FF0000 )) << 24) | + ((n & ((drwav_uint64)0x0000FF00 )) << 40) | + ((n & ((drwav_uint64)0x000000FF )) << 56); +#endif +} + + +static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) +{ + return (drwav_int16)drwav__bswap16((drwav_uint16)n); +} + +static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); + } +} + + +static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) +{ + drwav_uint8 t; + t = p[0]; + p[0] = p[2]; + p[2] = t; +} + +static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + drwav_uint8* pSample = pSamples + (iSample*3); + drwav__bswap_s24(pSample); + } +} + + +static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) +{ + return (drwav_int32)drwav__bswap32((drwav_uint32)n); +} + +static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); + } +} + + +static DRWAV_INLINE float drwav__bswap_f32(float n) +{ + union { + drwav_uint32 i; + float f; + } x; + x.f = n; + x.i = drwav__bswap32(x.i); + + return x.f; +} + +static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); + } +} + + +static DRWAV_INLINE double drwav__bswap_f64(double n) +{ + union { + drwav_uint64 i; + double f; + } x; + x.f = n; + x.i = drwav__bswap64(x.i); + + return x.f; +} + +static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); + } +} + + +static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) +{ + /* Assumes integer PCM. Floating point PCM is done in drwav__bswap_samples_ieee(). */ + switch (bytesPerSample) + { + case 2: /* s16, s12 (loosely packed) */ + { + drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + } break; + case 3: /* s24 */ + { + drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); + } break; + case 4: /* s32 */ + { + drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); + } break; + default: + { + /* Unsupported format. */ + DRWAV_ASSERT(DRWAV_FALSE); + } break; + } +} + +static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) +{ + switch (bytesPerSample) + { + #if 0 /* Contributions welcome for f16 support. */ + case 2: /* f16 */ + { + drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); + } break; + #endif + case 4: /* f32 */ + { + drwav__bswap_samples_f32((float*)pSamples, sampleCount); + } break; + case 8: /* f64 */ + { + drwav__bswap_samples_f64((double*)pSamples, sampleCount); + } break; + default: + { + /* Unsupported format. */ + DRWAV_ASSERT(DRWAV_FALSE); + } break; + } +} + +static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) +{ + switch (format) + { + case DR_WAVE_FORMAT_PCM: + { + drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); + } break; + + case DR_WAVE_FORMAT_IEEE_FLOAT: + { + drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); + } break; + + case DR_WAVE_FORMAT_ALAW: + case DR_WAVE_FORMAT_MULAW: + { + drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + } break; + + case DR_WAVE_FORMAT_ADPCM: + case DR_WAVE_FORMAT_DVI_ADPCM: + default: + { + /* Unsupported format. */ + DRWAV_ASSERT(DRWAV_FALSE); + } break; + } +} + + +DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRWAV_MALLOC(sz); +} + +DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRWAV_REALLOC(p, sz); +} + +DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRWAV_FREE(p); +} + + +DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + + if (p != NULL) { + DRWAV_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + + +DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + /* Copy. */ + return *pAllocationCallbacks; + } else { + /* Defaults. */ + drwav_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drwav__malloc_default; + allocationCallbacks.onRealloc = drwav__realloc_default; + allocationCallbacks.onFree = drwav__free_default; + return allocationCallbacks; + } +} + + +static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) +{ + return + formatTag == DR_WAVE_FORMAT_ADPCM || + formatTag == DR_WAVE_FORMAT_DVI_ADPCM; +} + +DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 2); +} + +DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 8); +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); + +DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) +{ + if (container == drwav_container_riff || container == drwav_container_rf64) { + drwav_uint8 sizeInBytes[4]; + + if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { + return DRWAV_AT_END; + } + + if (onRead(pUserData, sizeInBytes, 4) != 4) { + return DRWAV_INVALID_FILE; + } + + pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes); + pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 8; + } else { + drwav_uint8 sizeInBytes[8]; + + if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { + return DRWAV_AT_END; + } + + if (onRead(pUserData, sizeInBytes, 8) != 8) { + return DRWAV_INVALID_FILE; + } + + pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */ + pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 24; + } + + return DRWAV_SUCCESS; +} + +DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +{ + drwav_uint64 bytesRemainingToSeek = offset; + while (bytesRemainingToSeek > 0) { + if (bytesRemainingToSeek > 0x7FFFFFFF) { + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + bytesRemainingToSeek -= 0x7FFFFFFF; + } else { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + bytesRemainingToSeek = 0; + } + } + + return DRWAV_TRUE; +} + +DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +{ + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, drwav_seek_origin_start); + } + + /* Larger than 32-bit seek. */ + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { + return DRWAV_FALSE; + } + offset -= 0x7FFFFFFF; + + for (;;) { + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, drwav_seek_origin_current); + } + + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + offset -= 0x7FFFFFFF; + } + + /* Should never get here. */ + /*return DRWAV_TRUE; */ +} + + +DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) +{ + drwav_chunk_header header; + drwav_uint8 fmt[16]; + + if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + + /* Skip non-fmt chunks. */ + while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { + if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; + + /* Try the next header. */ + if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + } + + + /* Validation. */ + if (container == drwav_container_riff || container == drwav_container_rf64) { + if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) { + return DRWAV_FALSE; + } + } else { + if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) { + return DRWAV_FALSE; + } + } + + + if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += sizeof(fmt); + + fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0); + fmtOut->channels = drwav_bytes_to_u16(fmt + 2); + fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4); + fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8); + fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12); + fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14); + + fmtOut->extendedSize = 0; + fmtOut->validBitsPerSample = 0; + fmtOut->channelMask = 0; + memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); + + if (header.sizeInBytes > 16) { + drwav_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + + if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return DRWAV_FALSE; /* Expecting more data. */ + } + *pRunningBytesReadOut += sizeof(fmt_cbSize); + + bytesReadSoFar = 18; + + fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize); + if (fmtOut->extendedSize > 0) { + /* Simple validation. */ + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmtOut->extendedSize != 22) { + return DRWAV_FALSE; + } + } + + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + drwav_uint8 fmtext[22]; + if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { + return DRWAV_FALSE; /* Expecting more data. */ + } + + fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0); + fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2); + drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat); + } else { + if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + } + *pRunningBytesReadOut += fmtOut->extendedSize; + + bytesReadSoFar += fmtOut->extendedSize; + } + + /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */ + if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); + } + + if (header.paddingSize > 0) { + if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.paddingSize; + } + + return DRWAV_TRUE; +} + + +DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +{ + size_t bytesRead; + + DRWAV_ASSERT(onRead != NULL); + DRWAV_ASSERT(pCursor != NULL); + + bytesRead = onRead(pUserData, pBufferOut, bytesToRead); + *pCursor += bytesRead; + return bytesRead; +} + +#if 0 +DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) +{ + DRWAV_ASSERT(onSeek != NULL); + DRWAV_ASSERT(pCursor != NULL); + + if (!onSeek(pUserData, offset, origin)) { + return DRWAV_FALSE; + } + + if (origin == drwav_seek_origin_start) { + *pCursor = offset; + } else { + *pCursor += offset; + } + + return DRWAV_TRUE; +} +#endif + + +#define DRWAV_SMPL_BYTES 36 +#define DRWAV_SMPL_LOOP_BYTES 24 +#define DRWAV_INST_BYTES 7 +#define DRWAV_ACID_BYTES 24 +#define DRWAV_CUE_BYTES 4 +#define DRWAV_BEXT_BYTES 602 +#define DRWAV_BEXT_DESCRIPTION_BYTES 256 +#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 +#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 +#define DRWAV_BEXT_RESERVED_BYTES 180 +#define DRWAV_BEXT_UMID_BYTES 64 +#define DRWAV_CUE_POINT_BYTES 24 +#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 +#define DRWAV_LIST_LABELLED_TEXT_BYTES 20 + +#define DRWAV_METADATA_ALIGNMENT 8 + +typedef enum +{ + drwav__metadata_parser_stage_count, + drwav__metadata_parser_stage_read +} drwav__metadata_parser_stage; + +typedef struct +{ + drwav_read_proc onRead; + drwav_seek_proc onSeek; + void *pReadSeekUserData; + drwav__metadata_parser_stage stage; + drwav_metadata *pMetadata; + drwav_uint32 metadataCount; + drwav_uint8 *pData; + drwav_uint8 *pDataCursor; + drwav_uint64 metadataCursor; + drwav_uint64 extraCapacity; +} drwav__metadata_parser; + +DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) +{ + drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; + if (cap > DRWAV_SIZE_MAX) { + return 0; /* Too big. */ + } + + return (size_t)cap; /* Safe cast thanks to the check above. */ +} + +DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) +{ + drwav_uint8* pResult; + + if (align) { + drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; + if (modulo != 0) { + pParser->pDataCursor += align - modulo; + } + } + + pResult = pParser->pDataCursor; + + /* + Getting to the point where this function is called means there should always be memory + available. Out of memory checks should have been done at an earlier stage. + */ + DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); + + pParser->pDataCursor += size; + return pResult; +} + +DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) +{ + size_t extra = bytes + (align ? (align - 1) : 0); + pParser->extraCapacity += extra; +} + +DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { + free(pParser->pData); + + pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); + pParser->pDataCursor = pParser->pData; + + if (pParser->pData == NULL) { + return DRWAV_OUT_OF_MEMORY; + } + + /* + We don't need to worry about specifying an alignment here because malloc always returns something + of suitable alignment. This also means than pParser->pMetadata is all that we need to store in order + for us to free when we are done. + */ + pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); + pParser->metadataCursor = 0; + } + + return DRWAV_SUCCESS; +} + +DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +{ + if (pCursor != NULL) { + return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); + } else { + return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); + } +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +{ + drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; + drwav_uint64 totalBytesRead = 0; + size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesJustRead == sizeof(smplHeaderData)) { + drwav_uint32 iSampleLoop; + + pMetadata->type = drwav_metadata_type_smpl; + pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); + pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); + pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); + pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); + pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); + pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); + pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); + pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); + pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); + pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); + + for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { + drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; + bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + + if (bytesJustRead == sizeof(smplLoopData)) { + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); + } else { + break; + } + } + + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + + bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); + } + } + + return totalBytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +{ + drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; + drwav_uint64 totalBytesRead = 0; + size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesJustRead == sizeof(cueHeaderSectionData)) { + pMetadata->type = drwav_metadata_type_cue; + pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); + pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); + DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + + if (pMetadata->data.cue.cuePointCount > 0) { + drwav_uint32 iCuePoint; + + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; + bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + + if (bytesJustRead == sizeof(cuePointData)) { + pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; + pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); + } else { + break; + } + } + } + } + + return totalBytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +{ + drwav_uint8 instData[DRWAV_INST_BYTES]; + drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesRead == sizeof(instData)) { + pMetadata->type = drwav_metadata_type_inst; + pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; + pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; + pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; + pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; + pMetadata->data.inst.highNote = (drwav_int8)instData[4]; + pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; + pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; + } + + return bytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +{ + drwav_uint8 acidData[DRWAV_ACID_BYTES]; + drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesRead == sizeof(acidData)) { + pMetadata->type = drwav_metadata_type_acid; + pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); + pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); + pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); + pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); + pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); + pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); + pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); + pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); + } + + return bytesRead; +} + +DRWAV_PRIVATE size_t drwav__strlen_clamped(char* str, size_t maxToRead) +{ + size_t result = 0; + + while (*str++ && result < maxToRead) { + result += 1; + } + + return result; +} + +DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, char* str, size_t maxToRead) +{ + size_t len = drwav__strlen_clamped(str, maxToRead); + + if (len) { + char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); + DRWAV_ASSERT(result != NULL); + + memcpy(result, str, len); + result[len] = '\0'; + + return result; + } else { + return NULL; + } +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) +{ + drwav_uint8 bextData[DRWAV_BEXT_BYTES]; + drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesRead == sizeof(bextData)) { + drwav_uint8* pReadPointer; + drwav_uint32 timeReferenceLow; + drwav_uint32 timeReferenceHigh; + size_t extraBytes; + + pMetadata->type = drwav_metadata_type_bext; + + pReadPointer = bextData; + pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_DESCRIPTION_BYTES); + pReadPointer += DRWAV_BEXT_DESCRIPTION_BYTES; + + pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + pReadPointer += DRWAV_BEXT_ORIGINATOR_NAME_BYTES; + + pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_ORIGINATOR_REF_BYTES); + pReadPointer += DRWAV_BEXT_ORIGINATOR_REF_BYTES; + + memcpy(pReadPointer, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + pReadPointer += sizeof(pMetadata->data.bext.pOriginationDate); + + memcpy(pReadPointer, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + pReadPointer += sizeof(pMetadata->data.bext.pOriginationTime); + + timeReferenceLow = drwav_bytes_to_u32(pReadPointer); + pReadPointer += sizeof(drwav_uint32); + timeReferenceHigh = drwav_bytes_to_u32(pReadPointer); + pReadPointer += sizeof(drwav_uint32); + pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; + + pMetadata->data.bext.version = drwav_bytes_to_u16(pReadPointer); + pReadPointer += sizeof(drwav_uint16); + + pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); + memcpy(pMetadata->data.bext.pUMID, pReadPointer, DRWAV_BEXT_UMID_BYTES); + pReadPointer += DRWAV_BEXT_UMID_BYTES; + + pMetadata->data.bext.loudnessValue = drwav_bytes_to_u16(pReadPointer); + pReadPointer += sizeof(drwav_uint16); + + pMetadata->data.bext.loudnessRange = drwav_bytes_to_u16(pReadPointer); + pReadPointer += sizeof(drwav_uint16); + + pMetadata->data.bext.maxTruePeakLevel = drwav_bytes_to_u16(pReadPointer); + pReadPointer += sizeof(drwav_uint16); + + pMetadata->data.bext.maxMomentaryLoudness = drwav_bytes_to_u16(pReadPointer); + pReadPointer += sizeof(drwav_uint16); + + pMetadata->data.bext.maxShortTermLoudness = drwav_bytes_to_u16(pReadPointer); + pReadPointer += sizeof(drwav_uint16); + + DRWAV_ASSERT((pReadPointer + DRWAV_BEXT_RESERVED_BYTES) == (bextData + DRWAV_BEXT_BYTES)); + + extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); + if (extraBytes > 0) { + pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); + DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + + bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory); + } else { + pMetadata->data.bext.pCodingHistory = NULL; + pMetadata->data.bext.codingHistorySize = 0; + } + } + + return bytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) +{ + drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; + drwav_uint64 totalBytesRead = 0; + size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesJustRead == sizeof(cueIDBuffer)) { + drwav_uint32 sizeIncludingNullTerminator; + + pMetadata->type = type; + pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); + + sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; + if (sizeIncludingNullTerminator > 0) { + pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; + pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + + bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + } else { + pMetadata->data.labelOrNote.stringLength = 0; + pMetadata->data.labelOrNote.pString = NULL; + } + } + + return totalBytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) +{ + drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; + drwav_uint64 totalBytesRead = 0; + size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); + + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + + if (bytesJustRead == sizeof(buffer)) { + drwav_uint32 sizeIncludingNullTerminator; + + pMetadata->type = drwav_metadata_type_list_labelled_cue_region; + pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); + pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); + pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; + pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; + pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; + pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; + pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); + pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); + pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); + pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); + + sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; + if (sizeIncludingNullTerminator > 0) { + pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; + pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + + bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + } else { + pMetadata->data.labelledCueRegion.stringLength = 0; + pMetadata->data.labelledCueRegion.pString = NULL; + } + } + + return totalBytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) +{ + drwav_uint64 bytesRead = 0; + drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; + + if (pParser->stage == drwav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); + } else { + drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = type; + if (stringSizeWithNullTerminator > 0) { + pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; + pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); + DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); + + bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); + if (bytesRead == chunkSize) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } else { + pMetadata->data.infoText.stringLength = 0; + pMetadata->data.infoText.pString = NULL; + pParser->metadataCursor += 1; + } + } + + return bytesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) +{ + drwav_uint64 bytesRead = 0; + + if (location == drwav_metadata_location_invalid) { + return 0; + } + + if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) { + return 0; + } + + if (pParser->stage == drwav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); + } else { + drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = drwav_metadata_type_unknown; + pMetadata->data.unknown.chunkLocation = location; + pMetadata->data.unknown.id[0] = pChunkId[0]; + pMetadata->data.unknown.id[1] = pChunkId[1]; + pMetadata->data.unknown.id[2] = pChunkId[2]; + pMetadata->data.unknown.id[3] = pChunkId[3]; + pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; + pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); + DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); + + bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); + if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { + pParser->metadataCursor += 1; + } else { + /* Failed to read. */ + } + } + + return bytesRead; +} + +DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_uint64 allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) +{ + return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); +} + +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_uint64 allowedMetadataTypes) +{ + const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; + drwav_uint64 bytesRead = 0; + + if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { + if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + drwav_uint8 buffer[4]; + size_t bytesJustRead; + + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { + return bytesRead; + } + bytesRead += 28; + + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); + + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); + + pParser->metadataCount += 1; + drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); + drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + } + } + } else { + bytesRead = drwav__read_smpl_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { + if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + } else { + bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { + if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + } else { + bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { + if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + size_t cueCount; + + pParser->metadataCount += 1; + cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; + drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); + } else { + bytesRead = drwav__read_cue_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { + if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + /* The description field is the largest one in a bext chunk, so that is the max size of this temporary buffer. */ + char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; + size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; /* We know we will need SMPTE umid size. */ + size_t bytesJustRead; + + buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); + if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { + return bytesRead; + } + allocSizeNeeded += strlen(buffer) + 1; + + buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); + if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { + return bytesRead; + } + allocSizeNeeded += strlen(buffer) + 1; + + buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); + if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { + return bytesRead; + } + allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */ + + drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); + + pParser->metadataCount += 1; + } else { + bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { + drwav_metadata_location listType = drwav_metadata_location_invalid; + while (bytesRead < pChunkHeader->sizeInBytes) { + drwav_uint8 subchunkId[4]; + drwav_uint8 subchunkSizeBuffer[4]; + drwav_uint64 subchunkDataSize; + drwav_uint64 subchunkBytesRead = 0; + drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); + if (bytesJustRead != sizeof(subchunkId)) { + break; + } + + /* + The first thing in a list chunk should be "adtl" or "INFO". + + - adtl means this list is a Associated Data List Chunk and will contain labels, notes + or labelled cue regions. + - INFO means this list is an Info List Chunk containing info text chunks such as IPRD + which would specifies the album of this wav file. + + No data follows the adtl or INFO id so we just make note of what type this list is and + continue. + */ + if (drwav_fourcc_equal(subchunkId, "adtl")) { + listType = drwav_metadata_location_inside_adtl_list; + continue; + } else if (drwav_fourcc_equal(subchunkId, "INFO")) { + listType = drwav_metadata_location_inside_info_list; + continue; + } + + bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); + if (bytesJustRead != sizeof(subchunkSizeBuffer)) { + break; + } + subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); + + if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { + if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { + drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; + if (pParser->stage == drwav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); + } else { + subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); + if (subchunkBytesRead == subchunkDataSize) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { + if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { + drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; + if (pParser->stage == drwav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); + } else { + subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); + if (subchunkBytesRead == subchunkDataSize) { + pParser->metadataCursor += 1; + } else { + /* Failed to parse. */ + } + } + } else { + /* Incorrectly formed chunk. */ + } + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); + } else if (allowedMetadataTypes & drwav_metadata_type_unknown) { + subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); + } + + bytesRead += subchunkBytesRead; + DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); + + if (subchunkBytesRead < subchunkDataSize) { + drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; + + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { + break; + } + bytesRead += bytesToSeek; + } + + if ((subchunkDataSize % 2) == 1) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { + break; + } + bytesRead += 1; + } + } + } else if (allowedMetadataTypes & drwav_metadata_type_unknown) { + bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); + } + + return bytesRead; +} + + +DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) +{ + /* + The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here + is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align. + */ + if ((pWav->bitsPerSample & 0x7) == 0) { + /* Bits per sample is a multiple of 8. */ + return (pWav->bitsPerSample * pWav->fmt.channels) >> 3; + } else { + return pWav->fmt.blockAlign; + } +} + +DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) +{ + if (pFMT == NULL) { + return 0; + } + + if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { + return pFMT->formatTag; + } else { + return drwav_bytes_to_u16(pFMT->subFormat); /* Only the first two bytes are required. */ + } +} + +DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onRead == NULL || onSeek == NULL) { + return DRWAV_FALSE; + } + + DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->pUserData = pReadSeekUserData; + pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return DRWAV_FALSE; /* Invalid allocation callbacks. */ + } + + return DRWAV_TRUE; +} + +DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +{ + /* This function assumes drwav_preinit() has been called beforehand. */ + + drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */ + drwav_bool32 sequential; + drwav_uint8 riff[4]; + drwav_fmt fmt; + unsigned short translatedFormatTag; + drwav_bool32 foundDataChunk; + drwav_uint64 dataChunkSize = 0; /* <-- Important! Don't explicitly set this to 0 anywhere else. Calculation of the size of the data chunk is performed in different paths depending on the container. */ + drwav_uint64 sampleCountFromFactChunk = 0; /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */ + drwav_uint64 chunkSize; + drwav__metadata_parser metadataParser; + + cursor = 0; + sequential = (flags & DRWAV_SEQUENTIAL) != 0; + + /* The first 4 bytes should be the RIFF identifier. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return DRWAV_FALSE; + } + + /* + The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for + w64 it will start with "riff". + */ + if (drwav_fourcc_equal(riff, "RIFF")) { + pWav->container = drwav_container_riff; + } else if (drwav_fourcc_equal(riff, "riff")) { + int i; + drwav_uint8 riff2[12]; + + pWav->container = drwav_container_w64; + + /* Check the rest of the GUID for validity. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return DRWAV_FALSE; + } + + for (i = 0; i < 12; ++i) { + if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { + return DRWAV_FALSE; + } + } + } else if (drwav_fourcc_equal(riff, "RF64")) { + pWav->container = drwav_container_rf64; + } else { + return DRWAV_FALSE; /* Unknown or unsupported container. */ + } + + + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + drwav_uint8 chunkSizeBytes[4]; + drwav_uint8 wave[4]; + + /* RIFF/WAVE */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; + } + + if (pWav->container == drwav_container_riff) { + if (drwav_bytes_to_u32(chunkSizeBytes) < 36) { + return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */ + } + } else { + if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { + return DRWAV_FALSE; /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */ + } + } + + if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav_fourcc_equal(wave, "WAVE")) { + return DRWAV_FALSE; /* Expecting "WAVE". */ + } + } else { + drwav_uint8 chunkSizeBytes[8]; + drwav_uint8 wave[16]; + + /* W64 */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; + } + + if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { + return DRWAV_FALSE; + } + + if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { + return DRWAV_FALSE; + } + } + + + /* For RF64, the "ds64" chunk must come next, before the "fmt " chunk. */ + if (pWav->container == drwav_container_rf64) { + drwav_uint8 sizeBytes[8]; + drwav_uint64 bytesRemainingInChunk; + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { + return DRWAV_FALSE; /* Expecting "ds64". */ + } + + bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; + + /* We don't care about the size of the RIFF chunk - skip it. */ + if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return DRWAV_FALSE; + } + bytesRemainingInChunk -= 8; + cursor += 8; + + + /* Next 8 bytes is the size of the "data" chunk. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return DRWAV_FALSE; + } + bytesRemainingInChunk -= 8; + dataChunkSize = drwav_bytes_to_u64(sizeBytes); + + + /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return DRWAV_FALSE; + } + bytesRemainingInChunk -= 8; + sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); + + + /* Skip over everything else. */ + if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return DRWAV_FALSE; + } + cursor += bytesRemainingInChunk; + } + + + /* The next bytes should be the "fmt " chunk. */ + if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { + return DRWAV_FALSE; /* Failed to read the "fmt " chunk. */ + } + + /* Basic validation. */ + if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || + (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return DRWAV_FALSE; /* Probably an invalid WAV file. */ + } + + + /* Translate the internal format. */ + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); + } + + memset(&metadataParser, 0, sizeof(metadataParser)); + + /* Not tested on W64. */ + if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { + drwav_uint64 cursorForMetadata = cursor; + + metadataParser.onRead = pWav->onRead; + metadataParser.onSeek = pWav->onSeek; + metadataParser.pReadSeekUserData = pWav->pUserData; + metadataParser.stage = drwav__metadata_parser_stage_count; + + for (;;) { + drwav_result result; + drwav_uint64 bytesRead; + drwav_uint64 remainingBytes; + drwav_chunk_header header; + + result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header); + if (result != DRWAV_SUCCESS) { + break; + } + + bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); + DRWAV_ASSERT(bytesRead <= header.sizeInBytes); + + remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize; + if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) { + break; + } + cursorForMetadata += remainingBytes; + } + + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; + } + + drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); + metadataParser.stage = drwav__metadata_parser_stage_read; + } + + /* + We need to enumerate over each chunk for two reasons: + 1) The "data" chunk may not be the next one + 2) We may want to report each chunk back to the client + + In order to correctly report each chunk back to the client we will need to keep looping until the end of the file. + */ + foundDataChunk = DRWAV_FALSE; + + /* The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. */ + for (;;) { + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + if (!foundDataChunk) { + return DRWAV_FALSE; + } else { + break; /* Probably at the end of the file. Get out of the loop. */ + } + } + + /* Tell the client about this chunk. */ + if (!sequential && onChunk != NULL) { + drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + + /* + dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before + we called the callback. + */ + if (callbackBytesRead > 0) { + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; + } + } + } + + if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { + drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); + + if (bytesRead > 0) { + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; + } + } + } + + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + + chunkSize = header.sizeInBytes; + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + if (drwav_fourcc_equal(header.id.fourcc, "data")) { + foundDataChunk = DRWAV_TRUE; + if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */ + dataChunkSize = chunkSize; + } + } + } else { + if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) { + foundDataChunk = DRWAV_TRUE; + dataChunkSize = chunkSize; + } + } + + /* + If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for + this is that we would otherwise require a backwards seek which sequential mode forbids. + */ + if (foundDataChunk && sequential) { + break; + } + + /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */ + if (pWav->container == drwav_container_riff) { + if (drwav_fourcc_equal(header.id.fourcc, "fact")) { + drwav_uint32 sampleCount; + if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return DRWAV_FALSE; + } + chunkSize -= 4; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + + /* + The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this + for Microsoft ADPCM formats. + */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = sampleCount; + } else { + sampleCountFromFactChunk = 0; + } + } + } else if (pWav->container == drwav_container_w64) { + if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) { + if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return DRWAV_FALSE; + } + chunkSize -= 8; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + } + } else if (pWav->container == drwav_container_rf64) { + /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */ + } + + /* Make sure we seek past the padding. */ + chunkSize += header.paddingSize; + if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { + break; + } + cursor += chunkSize; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + } + + pWav->pMetadata = metadataParser.pMetadata; + pWav->metadataCount = metadataParser.metadataCount; + + /* If we haven't found a data chunk, return an error. */ + if (!foundDataChunk) { + return DRWAV_FALSE; + } + + /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */ + if (!sequential) { + if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return DRWAV_FALSE; + } + cursor = pWav->dataChunkDataPos; + } + + + /* At this point we should be sitting on the first byte of the raw audio data. */ + + pWav->fmt = fmt; + pWav->sampleRate = fmt.sampleRate; + pWav->channels = fmt.channels; + pWav->bitsPerSample = fmt.bitsPerSample; + pWav->bytesRemaining = dataChunkSize; + pWav->translatedFormatTag = translatedFormatTag; + pWav->dataChunkDataSize = dataChunkSize; + + if (sampleCountFromFactChunk != 0) { + pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else { + pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav); + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 totalBlockHeaderSizeInBytes; + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + + /* Make sure any trailing partial block is accounted for. */ + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + + /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ + totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 totalBlockHeaderSizeInBytes; + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + + /* Make sure any trailing partial block is accounted for. */ + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + + /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ + totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + + /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */ + pWav->totalPCMFrameCount += blockCount; + } + } + + /* Some formats only support a certain number of channels. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->channels > 2) { + return DRWAV_FALSE; + } + } + +#ifdef DR_WAV_LIBSNDFILE_COMPAT + /* + I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website), + it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count + from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct + way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should + always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my + correctness tests against libsndfile, and is disabled by default. + */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */ + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; + } +#endif + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); +} + +DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; /* <-- Needs to be set to tell drwav_init_ex() that we need to process metadata. */ + return drwav_init__internal(pWav, NULL, NULL, flags); +} + +DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) +{ + drwav_metadata *result = pWav->pMetadata; + + pWav->pMetadata = NULL; + pWav->metadataCount = 0; + + return result; +} + + +DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + /* Generic write. Assumes no byte reordering required. */ + return pWav->onWrite(pWav->pUserData, pData, dataSize); +} + +DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + return pWav->onWrite(pWav->pUserData, &byte, 1); +} + +DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + if (!drwav__is_little_endian()) { + value = drwav__bswap16(value); + } + + return drwav__write(pWav, &value, 2); +} + +DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + if (!drwav__is_little_endian()) { + value = drwav__bswap32(value); + } + + return drwav__write(pWav, &value, 4); +} + +DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + if (!drwav__is_little_endian()) { + value = drwav__bswap64(value); + } + + return drwav__write(pWav, &value, 8); +} + +DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) +{ + union { + drwav_uint32 u32; + float f32; + } u; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + u.f32 = value; + + if (!drwav__is_little_endian()) { + u.u32 = drwav__bswap32(u.u32); + } + + return drwav__write(pWav, &u.u32, 4); +} + +DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) +{ + if (pWav == NULL) { + return dataSize; + } + + return drwav__write(pWav, pData, dataSize); +} + +DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) +{ + if (pWav == NULL) { + return 1; + } + + return drwav__write_byte(pWav, byte); +} + +DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) +{ + if (pWav == NULL) { + return 2; + } + + return drwav__write_u16ne_to_le(pWav, value); +} + +DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) +{ + if (pWav == NULL) { + return 4; + } + + return drwav__write_u32ne_to_le(pWav, value); +} + +#if 0 /* Unused for now. */ +DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) +{ + if (pWav == NULL) { + return 8; + } + + return drwav__write_u64ne_to_le(pWav, value); +} +#endif + +DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) +{ + if (pWav == NULL) { + return 4; + } + + return drwav__write_f32ne_to_le(pWav, value); +} + +DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) +{ + size_t len; + + if (pWav == NULL) { + return bufFixedSize; + } + + len = drwav__strlen_clamped(str, bufFixedSize); + drwav__write_or_count(pWav, str, len); + + if (len < bufFixedSize) { + size_t i; + for (i = 0; i < bufFixedSize - len; ++i) { + drwav__write_byte(pWav, 0); + } + } + + return bufFixedSize; +} + + +/* pWav can be NULL meaning just count the bytes that would be written. */ +DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) +{ + size_t bytesWritten = 0; + drwav_bool32 hasListAdtl = DRWAV_FALSE; + drwav_bool32 hasListInfo = DRWAV_FALSE; + drwav_uint32 iMetadata; + + if (pMetadatas == NULL || metadataCount == 0) { + return 0; + } + + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_uint32 chunkSize = 0; + + if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { + hasListInfo = DRWAV_TRUE; + } + + if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { + hasListAdtl = DRWAV_TRUE; + } + + switch (pMetadata->type) { + case drwav_metadata_type_smpl: + { + drwav_uint32 iLoop; + + chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; + + bytesWritten += drwav__write_or_count(pWav, "smpl", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + + for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); + } + + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + bytesWritten += drwav__write(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + } + } break; + + case drwav_metadata_type_inst: + { + chunkSize = DRWAV_INST_BYTES; + + bytesWritten += drwav__write_or_count(pWav, "inst", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); + } break; + + case drwav_metadata_type_cue: + { + drwav_uint32 iCuePoint; + + chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; + + bytesWritten += drwav__write_or_count(pWav, "cue ", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + } + } break; + + case drwav_metadata_type_acid: + { + chunkSize = DRWAV_ACID_BYTES; + + bytesWritten += drwav__write_or_count(pWav, "acid", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); + bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); + bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); + } break; + + case drwav_metadata_type_bext: + { + char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; + drwav_uint32 timeReferenceLow; + drwav_uint32 timeReferenceHigh; + + chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; + + bytesWritten += drwav__write_or_count(pWav, "bext", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + + bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); + bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + + timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); + timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); + + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); + + memset(reservedBuf, 0, sizeof(reservedBuf)); + bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); + + if (pMetadata->data.bext.codingHistorySize > 0) { + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); + } + } break; + + case drwav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { + chunkSize = pMetadata->data.unknown.dataSizeInBytes; + + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); + } + } break; + + default: break; + } + if ((chunkSize % 2) != 0) { + bytesWritten += drwav__write_or_count_byte(pWav, 0); + } + } + + if (hasListInfo) { + drwav_uint32 chunkSize = 4; /* Start with 4 bytes for "INFO". */ + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + + if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { + chunkSize += 8; /* For id and string size. */ + chunkSize += pMetadata->data.infoText.stringLength + 1; /* Include null terminator. */ + } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { + chunkSize += 8; /* For id string size. */ + chunkSize += pMetadata->data.unknown.dataSizeInBytes; + } + + if ((chunkSize % 2) != 0) { + chunkSize += 1; + } + } + + bytesWritten += drwav__write_or_count(pWav, "LIST", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, "INFO", 4); + + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_uint32 subchunkSize = 0; + + if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { + const char* pID = NULL; + + switch (pMetadata->type) { + case drwav_metadata_type_list_info_software: pID = "ISFT"; break; + case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case drwav_metadata_type_list_info_title: pID = "INAM"; break; + case drwav_metadata_type_list_info_artist: pID = "IART"; break; + case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; + case drwav_metadata_type_list_info_date: pID = "ICRD"; break; + case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; + case drwav_metadata_type_list_info_album: pID = "IPRD"; break; + case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + default: break; + } + + DRWAV_ASSERT(pID != NULL); + + if (pMetadata->data.infoText.stringLength) { + subchunkSize = pMetadata->data.infoText.stringLength + 1; + bytesWritten += drwav__write_or_count(pWav, pID, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); + bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + } + } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { + if (pMetadata->data.unknown.dataSizeInBytes) { + subchunkSize = pMetadata->data.unknown.dataSizeInBytes; + + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + } + } + + if ((subchunkSize % 2) != 0) { + bytesWritten += drwav__write_or_count_byte(pWav, 0); + } + } + } + + if (hasListAdtl) { + drwav_uint32 chunkSize = 4; /* start with 4 bytes for "adtl" */ + + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + + switch (pMetadata->type) + { + case drwav_metadata_type_list_label: + case drwav_metadata_type_list_note: + { + chunkSize += 8; /* for id and chunk size */ + chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; + + if (pMetadata->data.labelOrNote.stringLength > 0) { + chunkSize += pMetadata->data.labelOrNote.stringLength + 1; + } + } break; + + case drwav_metadata_type_list_labelled_cue_region: + { + chunkSize += 8; /* for id and chunk size */ + chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; + + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; + } + } break; + + case drwav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { + chunkSize += 8; /* for id and chunk size */ + chunkSize += pMetadata->data.unknown.dataSizeInBytes; + } + } break; + + default: break; + } + + if ((chunkSize % 2) != 0) { + chunkSize += 1; + } + } + + bytesWritten += drwav__write_or_count(pWav, "LIST", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, "adtl", 4); + + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_uint32 subchunkSize = 0; + + switch (pMetadata->type) + { + case drwav_metadata_type_list_label: + case drwav_metadata_type_list_note: + { + if (pMetadata->data.labelOrNote.stringLength > 0) { + const char *pID = NULL; + + if (pMetadata->type == drwav_metadata_type_list_label) { + pID = "labl"; + } + else if (pMetadata->type == drwav_metadata_type_list_note) { + pID = "note"; + } + + DRWAV_ASSERT(pID != NULL); + DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + + subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; + + bytesWritten += drwav__write_or_count(pWav, pID, 4); + subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); + bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + } + } break; + + case drwav_metadata_type_list_labelled_cue_region: + { + subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; + + bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; + } + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); + + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); + bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + } + } break; + + case drwav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { + subchunkSize = pMetadata->data.unknown.dataSizeInBytes; + + DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + } + } break; + + default: break; + } + + if ((subchunkSize % 2) != 0) { + bytesWritten += drwav__write_or_count_byte(pWav, 0); + } + } + } + + DRWAV_ASSERT((bytesWritten % 2) == 0); + + return bytesWritten; +} + +DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +{ + drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 24 = "fmt " chunk. 8 = "data" + u32 data size. */ + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + + return (drwav_uint32)chunkSize; /* Safe cast due to the clamp above. */ +} + +DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) +{ + if (dataChunkSize <= 0xFFFFFFFFUL) { + return (drwav_uint32)dataChunkSize; + } else { + return 0xFFFFFFFFUL; + } +} + +DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) +{ + drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); + + return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */ +} + +DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) +{ + return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ +} + +DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) +{ + drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 36 = "ds64" chunk. 24 = "fmt " chunk. 8 = "data" + u32 data size. */ + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + + return chunkSize; +} + +DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) +{ + return dataChunkSize; +} + + + +DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onWrite == NULL) { + return DRWAV_FALSE; + } + + if (!isSequential && onSeek == NULL) { + return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */ + } + + /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */ + if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { + return DRWAV_FALSE; + } + if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { + return DRWAV_FALSE; + } + + DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onWrite = onWrite; + pWav->onSeek = onSeek; + pWav->pUserData = pUserData; + pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return DRWAV_FALSE; /* Invalid allocation callbacks. */ + } + + pWav->fmt.formatTag = (drwav_uint16)pFormat->format; + pWav->fmt.channels = (drwav_uint16)pFormat->channels; + pWav->fmt.sampleRate = pFormat->sampleRate; + pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->fmt.extendedSize = 0; + pWav->isSequentialWrite = isSequential; + + return DRWAV_TRUE; +} + + +DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + /* The function assumes drwav_preinit_write() was called beforehand. */ + + size_t runningPos = 0; + drwav_uint64 initialDataChunkSize = 0; + drwav_uint64 chunkSizeFMT; + + /* + The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In + sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non- + sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek. + */ + if (pWav->isSequentialWrite) { + initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; + + /* + The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64 + so for the sake of simplicity I'm not doing any validation for that. + */ + if (pFormat->container == drwav_container_riff) { + if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { + return DRWAV_FALSE; /* Not enough room to store every sample. */ + } + } + } + + pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; + + + /* "RIFF" chunk. */ + if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; /* +28 = "WAVE" + [sizeof "fmt " chunk] */ + runningPos += drwav__write(pWav, "RIFF", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, "WAVE", 4); + } else if (pFormat->container == drwav_container_w64) { + drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ + runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); + } else if (pFormat->container == drwav_container_rf64) { + runningPos += drwav__write(pWav, "RF64", 4); + runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always 0xFFFFFFFF for RF64. Set to a proper value in the "ds64" chunk. */ + runningPos += drwav__write(pWav, "WAVE", 4); + } + + + /* "ds64" chunk (RF64 only). */ + if (pFormat->container == drwav_container_rf64) { + drwav_uint32 initialds64ChunkSize = 28; /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */ + drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; /* +8 for the ds64 header. */ + + runningPos += drwav__write(pWav, "ds64", 4); + runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); /* Size of ds64. */ + runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); /* Size of RIFF. Set to true value at the end. */ + runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); /* Size of DATA. Set to true value at the end. */ + runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); /* Sample count. */ + runningPos += drwav__write_u32ne_to_le(pWav, 0); /* Table length. Always set to zero in our case since we're not doing any other chunks than "DATA". */ + } + + + /* "fmt " chunk. */ + if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { + chunkSizeFMT = 16; + runningPos += drwav__write(pWav, "fmt ", 4); + runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); + } else if (pFormat->container == drwav_container_w64) { + chunkSizeFMT = 40; + runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); + } + + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + + /* TODO: is a 'fact' chunk required for DR_WAVE_FORMAT_IEEE_FLOAT? */ + + if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { + runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); + } + + pWav->dataChunkDataPos = runningPos; + + /* "data" chunk. */ + if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == drwav_container_w64) { + drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ + runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == drwav_container_rf64) { + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */ + } + + /* Set some properties for the client's convenience. */ + pWav->container = pFormat->container; + pWav->channels = (drwav_uint16)pFormat->channels; + pWav->sampleRate = pFormat->sampleRate; + pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (drwav_uint16)pFormat->format; + pWav->dataChunkDataPos = runningPos; + + return DRWAV_TRUE; +} + + +DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */ +} + +DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */ +} + +DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +{ + if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->pMetadata = pMetadata; + pWav->metadataCount = metadataCount; + + return drwav_init_write__internal(pWav, pFormat, 0); +} + + +DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +{ + /* Casting totalFrameCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */ + drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); + drwav_uint64 riffChunkSizeBytes; + drwav_uint64 fileSizeBytes = 0; + + if (pFormat->container == drwav_container_riff) { + riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); + fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ + } else if (pFormat->container == drwav_container_w64) { + riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); + fileSizeBytes = riffChunkSizeBytes; + } else if (pFormat->container == drwav_container_rf64) { + riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); + fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ + } + + return fileSizeBytes; +} + + +#ifndef DR_WAV_NO_STDIO + +/* drwav_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ +#include +DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRWAV_SUCCESS; + #ifdef EPERM + case EPERM: return DRWAV_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRWAV_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRWAV_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRWAV_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRWAV_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRWAV_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRWAV_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRWAV_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRWAV_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRWAV_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRWAV_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRWAV_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRWAV_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRWAV_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRWAV_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRWAV_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRWAV_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRWAV_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRWAV_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRWAV_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRWAV_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRWAV_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRWAV_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRWAV_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRWAV_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRWAV_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRWAV_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRWAV_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRWAV_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRWAV_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRWAV_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRWAV_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRWAV_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRWAV_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRWAV_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRWAV_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRWAV_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRWAV_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRWAV_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRWAV_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRWAV_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRWAV_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRWAV_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRWAV_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRWAV_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRWAV_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRWAV_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRWAV_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRWAV_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRWAV_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRWAV_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRWAV_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRWAV_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRWAV_ERROR; + #endif + #ifdef EADV + case EADV: return DRWAV_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRWAV_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRWAV_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRWAV_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRWAV_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRWAV_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRWAV_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRWAV_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRWAV_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRWAV_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRWAV_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRWAV_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRWAV_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRWAV_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRWAV_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRWAV_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRWAV_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRWAV_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRWAV_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRWAV_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRWAV_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRWAV_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRWAV_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRWAV_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRWAV_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRWAV_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRWAV_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRWAV_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRWAV_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRWAV_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRWAV_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRWAV_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRWAV_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRWAV_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRWAV_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRWAV_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRWAV_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRWAV_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRWAV_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRWAV_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRWAV_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRWAV_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRWAV_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRWAV_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRWAV_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRWAV_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRWAV_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRWAV_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRWAV_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRWAV_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRWAV_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRWAV_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRWAV_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRWAV_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRWAV_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRWAV_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRWAV_ERROR; + #endif + default: return DRWAV_ERROR; + } +} + +DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRWAV_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drwav_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drwav_result result = drwav_result_from_errno(errno); + if (result == DRWAV_SUCCESS) { + result = DRWAV_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRWAV_SUCCESS; +} + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRWAV_HAS_WFOPEN + #endif +#endif + +DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRWAV_INVALID_ARGS; + } + +#if defined(DRWAV_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drwav_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drwav_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRWAV_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drwav_result_from_errno(errno); + } + + pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRWAV_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRWAV_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + + if (*ppFile == NULL) { + return DRWAV_ERROR; + } +#endif + + return DRWAV_SUCCESS; +} + + +DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +{ + return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); +} + +DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} + + +DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav_bool32 result; + + result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + pWav->allowedMetadataTypes = allowedMetadataTypes; + + result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); +} + + +DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav_bool32 result; + + result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + return DRWAV_TRUE; +} + +DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} + +DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +#endif /* DR_WAV_NO_STDIO */ + + +DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drwav* pWav = (drwav*)pUserData; + size_t bytesRemaining; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + + bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + pWav->memoryStream.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) +{ + drwav* pWav = (drwav*)pUserData; + DRWAV_ASSERT(pWav != NULL); + + if (origin == drwav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { + return DRWAV_FALSE; /* Trying to seek too far forward. */ + } + } else { + if (pWav->memoryStream.currentReadPos < (size_t)-offset) { + return DRWAV_FALSE; /* Trying to seek too far backwards. */ + } + } + + /* This will never underflow thanks to the clamps above. */ + pWav->memoryStream.currentReadPos += offset; + } else { + if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { + pWav->memoryStream.currentReadPos = offset; + } else { + return DRWAV_FALSE; /* Trying to seek too far forward. */ + } + } + + return DRWAV_TRUE; +} + +DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +{ + drwav* pWav = (drwav*)pUserData; + size_t bytesRemaining; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + + bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; + if (bytesRemaining < bytesToWrite) { + /* Need to reallocate. */ + void* pNewData; + size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; + + /* If doubling wasn't enough, just make it the minimum required size to write the data. */ + if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { + newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; + } + + pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + + *pWav->memoryStreamWrite.ppData = pNewData; + pWav->memoryStreamWrite.dataCapacity = newDataCapacity; + } + + DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + + pWav->memoryStreamWrite.currentWritePos += bytesToWrite; + if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { + pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; + } + + *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; + + return bytesToWrite; +} + +DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) +{ + drwav* pWav = (drwav*)pUserData; + DRWAV_ASSERT(pWav != NULL); + + if (origin == drwav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { + offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */ + } + } else { + if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { + offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */ + } + } + + /* This will never underflow thanks to the clamps above. */ + pWav->memoryStreamWrite.currentWritePos += offset; + } else { + if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + pWav->memoryStreamWrite.currentWritePos = offset; + } else { + pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */ + } + } + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return DRWAV_FALSE; + } + + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + + return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); +} + +DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return DRWAV_FALSE; + } + + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + + pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; + + return drwav_init__internal(pWav, NULL, NULL, flags); +} + + +DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (ppData == NULL || pDataSize == NULL) { + return DRWAV_FALSE; + } + + *ppData = NULL; /* Important because we're using realloc()! */ + *pDataSize = 0; + + if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->memoryStreamWrite.ppData = ppData; + pWav->memoryStreamWrite.pDataSize = pDataSize; + pWav->memoryStreamWrite.dataSize = 0; + pWav->memoryStreamWrite.dataCapacity = 0; + pWav->memoryStreamWrite.currentWritePos = 0; + + return drwav_init_write__internal(pWav, pFormat, totalSampleCount); +} + +DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} + + + +DRWAV_API drwav_result drwav_uninit(drwav* pWav) +{ + drwav_result result = DRWAV_SUCCESS; + + if (pWav == NULL) { + return DRWAV_INVALID_ARGS; + } + + /* + If the drwav object was opened in write mode we'll need to finalize a few things: + - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers. + - Set the size of the "data" chunk. + */ + if (pWav->onWrite != NULL) { + drwav_uint32 paddingSize = 0; + + /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */ + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); + } else { + paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); + } + + if (paddingSize > 0) { + drwav_uint64 paddingData = 0; + drwav__write(pWav, &paddingData, paddingSize); /* Byte order does not matter for this. */ + } + + /* + Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need + to do this when using non-sequential mode. + */ + if (pWav->onSeek && !pWav->isSequentialWrite) { + if (pWav->container == drwav_container_riff) { + /* The "RIFF" chunk size. */ + if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { + drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + drwav__write_u32ne_to_le(pWav, riffChunkSize); + } + + /* The "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { + drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); + drwav__write_u32ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == drwav_container_w64) { + /* The "RIFF" chunk size. */ + if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, riffChunkSize); + } + + /* The "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == drwav_container_rf64) { + /* We only need to update the ds64 chunk. The "RIFF" and "data" chunks always have their sizes set to 0xFFFFFFFF for RF64. */ + int ds64BodyPos = 12 + 8; + + /* The "RIFF" chunk size. */ + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + drwav__write_u64ne_to_le(pWav, riffChunkSize); + } + + /* The "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, dataChunkSize); + } + } + } + + /* Validation for sequential mode. */ + if (pWav->isSequentialWrite) { + if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { + result = DRWAV_INVALID_FILE; + } + } + } else { + if (pWav->pMetadata != NULL) { + pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData); + } + } + +#ifndef DR_WAV_NO_STDIO + /* + If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() + was used by looking at the onRead and onSeek callbacks. + */ + if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { + fclose((FILE*)pWav->pUserData); + } +#endif + + return result; +} + + + +DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) +{ + size_t bytesRead; + + if (pWav == NULL || bytesToRead == 0) { + return 0; /* Invalid args. */ + } + + if (bytesToRead > pWav->bytesRemaining) { + bytesToRead = (size_t)pWav->bytesRemaining; + } + + if (bytesToRead == 0) { + return 0; /* At end. */ + } + + if (pBufferOut != NULL) { + bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); + } else { + /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */ + bytesRead = 0; + while (bytesRead < bytesToRead) { + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > 0x7FFFFFFF) { + bytesToSeek = 0x7FFFFFFF; + } + + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { + break; + } + + bytesRead += bytesToSeek; + } + + /* When we get here we may need to read-and-discard some data. */ + while (bytesRead < bytesToRead) { + drwav_uint8 buffer[4096]; + size_t bytesSeeked; + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > sizeof(buffer)) { + bytesToSeek = sizeof(buffer); + } + + bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); + bytesRead += bytesSeeked; + + if (bytesSeeked < bytesToSeek) { + break; /* Reached the end. */ + } + } + } + + pWav->readCursorInPCMFrames += bytesRead / drwav_get_bytes_per_pcm_frame(pWav); + + pWav->bytesRemaining -= bytesRead; + return bytesRead; +} + + + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + drwav_uint32 bytesPerFrame; + drwav_uint64 bytesToRead; /* Intentionally uint64 instead of size_t so we can do a check that we're not reading too much on 32-bit builds. */ + + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + /* Cannot use this function for compressed formats. */ + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + return 0; + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + bytesToRead = framesToRead * bytesPerFrame; + if (bytesToRead > DRWAV_SIZE_MAX) { + bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; /* Round the number of bytes to read to a clean frame boundary. */ + } + + /* + Doing an explicit check here just to make it clear that we don't want to be attempt to read anything if there's no bytes to read. There + *could* be a time where it evaluates to 0 due to overflowing. + */ + if (bytesToRead == 0) { + return 0; + } + + return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + + if (pBufferOut != NULL) { + drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + if (drwav__is_little_endian()) { + return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } else { + return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } +} + + + +DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) +{ + if (pWav->onWrite != NULL) { + return DRWAV_FALSE; /* No seeking in write mode. */ + } + + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { + return DRWAV_FALSE; + } + + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + /* Cached data needs to be cleared for compressed formats. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->ima); + } else { + DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ + } + } + + pWav->readCursorInPCMFrames = 0; + pWav->bytesRemaining = pWav->dataChunkDataSize; + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) +{ + /* Seeking should be compatible with wave files > 2GB. */ + + if (pWav == NULL || pWav->onSeek == NULL) { + return DRWAV_FALSE; + } + + /* No seeking in write mode. */ + if (pWav->onWrite != NULL) { + return DRWAV_FALSE; + } + + /* If there are no samples, just return DRWAV_TRUE without doing anything. */ + if (pWav->totalPCMFrameCount == 0) { + return DRWAV_TRUE; + } + + /* Make sure the sample is clamped. */ + if (targetFrameIndex >= pWav->totalPCMFrameCount) { + targetFrameIndex = pWav->totalPCMFrameCount - 1; + } + + /* + For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need + to seek back to the start. + */ + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + /* TODO: This can be optimized. */ + + /* + If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards, + we first need to seek back to the start and then just do the same thing as a forward seek. + */ + if (targetFrameIndex < pWav->readCursorInPCMFrames) { + if (!drwav_seek_to_first_pcm_frame(pWav)) { + return DRWAV_FALSE; + } + } + + if (targetFrameIndex > pWav->readCursorInPCMFrames) { + drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; + + drwav_int16 devnull[2048]; + while (offsetInFrames > 0) { + drwav_uint64 framesRead = 0; + drwav_uint64 framesToRead = offsetInFrames; + if (framesToRead > drwav_countof(devnull)/pWav->channels) { + framesToRead = drwav_countof(devnull)/pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + } else { + DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ + } + + if (framesRead != framesToRead) { + return DRWAV_FALSE; + } + + offsetInFrames -= framesRead; + } + } + } else { + drwav_uint64 totalSizeInBytes; + drwav_uint64 currentBytePos; + drwav_uint64 targetBytePos; + drwav_uint64 offset; + + totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav); + DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); + + currentBytePos = totalSizeInBytes - pWav->bytesRemaining; + targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav); + + if (currentBytePos < targetBytePos) { + /* Offset forwards. */ + offset = (targetBytePos - currentBytePos); + } else { + /* Offset backwards. */ + if (!drwav_seek_to_first_pcm_frame(pWav)) { + return DRWAV_FALSE; + } + offset = targetBytePos; + } + + while (offset > 0) { + int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); + if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + + pWav->readCursorInPCMFrames += offset32 / drwav_get_bytes_per_pcm_frame(pWav); + pWav->bytesRemaining -= offset32; + offset -= offset32; + } + } + + return DRWAV_TRUE; +} + +DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) +{ + if (pCursor == NULL) { + return DRWAV_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pWav == NULL) { + return DRWAV_INVALID_ARGS; + } + + *pCursor = pWav->readCursorInPCMFrames; + + return DRWAV_SUCCESS; +} + +DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) +{ + if (pLength == NULL) { + return DRWAV_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pWav == NULL) { + return DRWAV_INVALID_ARGS; + } + + *pLength = pWav->totalPCMFrameCount; + + return DRWAV_SUCCESS; +} + + +DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) +{ + size_t bytesWritten; + + if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { + return 0; + } + + bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); + pWav->dataChunkDataSize += bytesWritten; + + return bytesWritten; +} + +DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + drwav_uint64 bytesToWrite; + drwav_uint64 bytesWritten; + const drwav_uint8* pRunningData; + + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > DRWAV_SIZE_MAX) { + return 0; + } + + bytesWritten = 0; + pRunningData = (const drwav_uint8*)pData; + + while (bytesToWrite > 0) { + size_t bytesJustWritten; + drwav_uint64 bytesToWriteThisIteration; + + bytesToWriteThisIteration = bytesToWrite; + DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ + + bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + if (bytesJustWritten == 0) { + break; + } + + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} + +DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + drwav_uint64 bytesToWrite; + drwav_uint64 bytesWritten; + drwav_uint32 bytesPerSample; + const drwav_uint8* pRunningData; + + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > DRWAV_SIZE_MAX) { + return 0; + } + + bytesWritten = 0; + pRunningData = (const drwav_uint8*)pData; + + bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + + while (bytesToWrite > 0) { + drwav_uint8 temp[4096]; + drwav_uint32 sampleCount; + size_t bytesJustWritten; + drwav_uint64 bytesToWriteThisIteration; + + bytesToWriteThisIteration = bytesToWrite; + DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ + + /* + WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need + to use an intermediary buffer for the conversion. + */ + sampleCount = sizeof(temp)/bytesPerSample; + + if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; + } + + DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); + + bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + if (bytesJustWritten == 0) { + break; + } + + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} + +DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + if (drwav__is_little_endian()) { + return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); + } else { + return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); + } +} + + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead = 0; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(framesToRead > 0); + + /* TODO: Lots of room for optimization here. */ + + while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */ + + /* If there are no cached frames we need to load a new block. */ + if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + /* Mono. */ + drwav_uint8 header[7]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrameCount = 2; + } else { + /* Stereo. */ + drwav_uint8 header[14]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.predictor[1] = header[1]; + pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); + + pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.cachedFrameCount = 2; + } + } + + /* Output anything that's cached. */ + while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + drwav_uint32 iSample = 0; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + } + + pBufferOut += pWav->channels; + } + + framesToRead -= 1; + totalFramesRead += 1; + pWav->readCursorInPCMFrames += 1; + pWav->msadpcm.cachedFrameCount -= 1; + } + + if (framesToRead == 0) { + break; + } + + + /* + If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next + loop iteration which will trigger the loading of a new block. + */ + if (pWav->msadpcm.cachedFrameCount == 0) { + if (pWav->msadpcm.bytesRemainingInBlock == 0) { + continue; + } else { + static drwav_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + + drwav_uint8 nibbles; + drwav_int32 nibble0; + drwav_int32 nibble1; + + if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock -= 1; + + /* TODO: Optimize away these if statements. */ + nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } + nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } + + if (pWav->channels == 1) { + /* Mono. */ + drwav_int32 newSample0; + drwav_int32 newSample1; + + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = drwav_clamp(newSample0, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + + + newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[0]; + newSample1 = drwav_clamp(newSample1, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample1; + + + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 2; + } else { + /* Stereo. */ + drwav_int32 newSample0; + drwav_int32 newSample1; + + /* Left. */ + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = drwav_clamp(newSample0, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + + + /* Right. */ + newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[1]; + newSample1 = drwav_clamp(newSample1, -32768, 32767); + + pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; + if (pWav->msadpcm.delta[1] < 16) { + pWav->msadpcm.delta[1] = 16; + } + + pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.prevFrames[1][1] = newSample1; + + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 1; + } + } + } + } + + return totalFramesRead; +} + + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead = 0; + drwav_uint32 iChannel; + + static drwav_int32 indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + static drwav_int32 stepTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(framesToRead > 0); + + /* TODO: Lots of room for optimization here. */ + + while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */ + + /* If there are no cached samples we need to load a new block. */ + if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + /* Mono. */ + drwav_uint8 header[4]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + if (header[2] >= drwav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; /* Invalid data. */ + } + + pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = header[2]; + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.cachedFrameCount = 1; + } else { + /* Stereo. */ + drwav_uint8 header[8]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; /* Invalid data. */ + } + + pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = header[2]; + pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = header[6]; + + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.cachedFrameCount = 1; + } + } + + /* Output anything that's cached. */ + while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + drwav_uint32 iSample; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + } + pBufferOut += pWav->channels; + } + + framesToRead -= 1; + totalFramesRead += 1; + pWav->readCursorInPCMFrames += 1; + pWav->ima.cachedFrameCount -= 1; + } + + if (framesToRead == 0) { + break; + } + + /* + If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next + loop iteration which will trigger the loading of a new block. + */ + if (pWav->ima.cachedFrameCount == 0) { + if (pWav->ima.bytesRemainingInBlock == 0) { + continue; + } else { + /* + From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the + left channel, 4 bytes for the right channel. + */ + pWav->ima.cachedFrameCount = 8; + for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { + drwav_uint32 iByte; + drwav_uint8 nibbles[4]; + if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { + pWav->ima.cachedFrameCount = 0; + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock -= 4; + + for (iByte = 0; iByte < 4; ++iByte) { + drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + + drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + drwav_int32 predictor = pWav->ima.predictor[iChannel]; + + drwav_int32 diff = step >> 3; + if (nibble0 & 1) diff += step >> 2; + if (nibble0 & 2) diff += step >> 1; + if (nibble0 & 4) diff += step; + if (nibble0 & 8) diff = -diff; + + predictor = drwav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + + + step = stepTable[pWav->ima.stepIndex[iChannel]]; + predictor = pWav->ima.predictor[iChannel]; + + diff = step >> 3; + if (nibble1 & 1) diff += step >> 2; + if (nibble1 & 2) diff += step >> 1; + if (nibble1 & 4) diff += step; + if (nibble1 & 8) diff = -diff; + + predictor = drwav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + } + } + } + } + } + + return totalFramesRead; +} + + +#ifndef DR_WAV_NO_CONVERSION_API +static unsigned short g_drwavAlawTable[256] = { + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 +}; + +static unsigned short g_drwavMulawTable[256] = { + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; + +static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavAlawTable[sampleIn]; +} + +static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavMulawTable[sampleIn]; +} + + + +DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + + /* Special case for 8-bit sample data because it's treated as unsigned. */ + if (bytesPerSample == 1) { + drwav_u8_to_s16(pOut, pIn, totalSampleCount); + return; + } + + + /* Slightly more optimal implementation for common formats. */ + if (bytesPerSample == 2) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const drwav_int16*)pIn)[i]; + } + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); + return; + } + + + /* Anything more than 64 bits per sample is not supported. */ + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + + + /* Generic, slow converter. */ + for (i = 0; i < totalSampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); + } +} + +DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + /* Fast path. */ + if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + } + + return 0; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + + +DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x << 8; + r = r - 32768; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 8; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x >> 16; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5f); + r = r - 32768; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + double x = pIn[i]; + double c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5); + r = r - 32768; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = drwav__alaw_to_s16(pIn[i]); + } +} + +DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = drwav__mulaw_to_s16(pIn[i]); + } +} + + + +DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + + /* Special case for 8-bit sample data because it's treated as unsigned. */ + if (bytesPerSample == 1) { + drwav_u8_to_f32(pOut, pIn, sampleCount); + return; + } + + /* Slightly more optimal implementation for common formats. */ + if (bytesPerSample == 2) { + drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); + return; + } + + + /* Anything more than 64 bits per sample is not supported. */ + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } + + + /* Generic, slow converter. */ + for (i = 0; i < sampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); + } +} + +DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + unsigned int i; + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((const float*)pIn)[i]; + } + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + return; + } else { + /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ + DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } +} + + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + /* Fast path. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut); + } + + return 0; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + + +DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + +#ifdef DR_WAV_LIBSNDFILE_COMPAT + /* + It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears + libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note + the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated + correctness testing. This is disabled by default. + */ + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 256.0f) * 2 - 1; + } +#else + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ + + *pOut++ = x; + } +#endif +} + +DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] * 0.000030517578125f; + } +} + +DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + double x; + drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); + drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); + drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); + + x = (double)((drwav_int32)(a | b | c) >> 8); + *pOut++ = (float)(x * 0.00000011920928955078125); + } +} + +DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)(pIn[i] / 2147483648.0); + } +} + +DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)pIn[i]; + } +} + +DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; + } +} + +DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; + } +} + + + +DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + + /* Special case for 8-bit sample data because it's treated as unsigned. */ + if (bytesPerSample == 1) { + drwav_u8_to_s32(pOut, pIn, totalSampleCount); + return; + } + + /* Slightly more optimal implementation for common formats. */ + if (bytesPerSample == 2) { + drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const drwav_int32*)pIn)[i]; + } + return; + } + + + /* Anything more than 64 bits per sample is not supported. */ + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + + + /* Generic, slow converter. */ + for (i = 0; i < totalSampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); + } +} + +DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} + + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + /* Fast path. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut); + } + + return 0; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + + +DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((int)pIn[i] - 128) << 24; + } +} + +DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] << 16; + } +} + +DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + unsigned int s0 = pIn[i*3 + 0]; + unsigned int s1 = pIn[i*3 + 1]; + unsigned int s2 = pIn[i*3 + 2]; + + drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + *pOut++ = sample32; + } +} + +DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + } +} + +DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + } +} + +DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; + } +} + +DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i= 0; i < sampleCount; ++i) { + *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; + } +} + + + +DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +{ + drwav_uint64 sampleDataSize; + drwav_int16* pSampleData; + drwav_uint64 framesRead; + + DRWAV_ASSERT(pWav != NULL); + + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); + return NULL; /* File's too big. */ + } + + pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; /* Failed to allocate memory. */ + } + + framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); + return NULL; /* There was an error reading the samples. */ + } + + drwav_uninit(pWav); + + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + + return pSampleData; +} + +DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +{ + drwav_uint64 sampleDataSize; + float* pSampleData; + drwav_uint64 framesRead; + + DRWAV_ASSERT(pWav != NULL); + + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); + return NULL; /* File's too big. */ + } + + pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; /* Failed to allocate memory. */ + } + + framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); + return NULL; /* There was an error reading the samples. */ + } + + drwav_uninit(pWav); + + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + + return pSampleData; +} + +DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +{ + drwav_uint64 sampleDataSize; + drwav_int32* pSampleData; + drwav_uint64 framesRead; + + DRWAV_ASSERT(pWav != NULL); + + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); + return NULL; /* File's too big. */ + } + + pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; /* Failed to allocate memory. */ + } + + framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); + return NULL; /* There was an error reading the samples. */ + } + + drwav_uninit(pWav); + + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + + return pSampleData; +} + + + +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +#ifndef DR_WAV_NO_STDIO +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + + +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif + +DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif /* DR_WAV_NO_CONVERSION_API */ + + +DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + drwav__free_from_callbacks(p, pAllocationCallbacks); + } else { + drwav__free_default(p, NULL); + } +} + +DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) +{ + return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); +} + +DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) +{ + return (drwav_int16)drwav_bytes_to_u16(data); +} + +DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) +{ + return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); +} + +DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) +{ + union { + drwav_uint32 u32; + float f32; + } value; + + value.u32 = drwav_bytes_to_u32(data); + return value.f32; +} + +DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) +{ + return (drwav_int32)drwav_bytes_to_u32(data); +} + +DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) +{ + return + ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | + ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); +} + +DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) +{ + return (drwav_int64)drwav_bytes_to_u64(data); +} + + +DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) +{ + int i; + for (i = 0; i < 16; i += 1) { + if (a[i] != b[i]) { + return DRWAV_FALSE; + } + } + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) +{ + return + a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3]; +} + +#endif /* dr_wav_c */ +#endif /* DR_WAV_IMPLEMENTATION */ + +/* +REVISION HISTORY +================ +v0.13.0 - 2021-07-01 + - Improve support for reading and writing metadata. Use the `_with_metadata()` APIs to initialize + a WAV decoder and store the metadata within the `drwav` object. Use the `pMetadata` and + `metadataCount` members of the `drwav` object to read the data. The old way of handling metadata + via a callback is still usable and valid. + - API CHANGE: drwav_target_write_size_bytes() now takes extra parameters for calculating the + required write size when writing metadata. + - Add drwav_get_cursor_in_pcm_frames() + - Add drwav_get_length_in_pcm_frames() + - Fix a bug where drwav_read_raw() can call the read callback with a byte count of zero. + +v0.12.20 - 2021-06-11 + - Fix some undefined behavior. + +v0.12.19 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + - Minor improvements to the management of some internal state concerning the data chunk cursor. + +v0.12.18 - 2021-01-31 + - Clean up some static analysis warnings. + +v0.12.17 - 2021-01-17 + - Minor fix to sample code in documentation. + - Correctly qualify a private API as private rather than public. + - Code cleanup. + +v0.12.16 - 2020-12-02 + - Fix a bug when trying to read more bytes than can fit in a size_t. + +v0.12.15 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.12.14 - 2020-11-13 + - Minor code clean up. + +v0.12.13 - 2020-11-01 + - Improve compiler support for older versions of GCC. + +v0.12.12 - 2020-09-28 + - Add support for RF64. + - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section. + +v0.12.11 - 2020-09-08 + - Fix a compilation error on older compilers. + +v0.12.10 - 2020-08-24 + - Fix a bug when seeking with ADPCM formats. + +v0.12.9 - 2020-08-02 + - Simplify sized types. + +v0.12.8 - 2020-07-25 + - Fix a compilation warning. + +v0.12.7 - 2020-07-15 + - Fix some bugs on big-endian architectures. + - Fix an error in s24 to f32 conversion. + +v0.12.6 - 2020-06-23 + - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek. + - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files. + - Add include guard for the implementation section. + +v0.12.5 - 2020-05-27 + - Minor documentation fix. + +v0.12.4 - 2020-05-16 + - Replace assert() with DRWAV_ASSERT(). + - Add compile-time and run-time version querying. + - DRWAV_VERSION_MINOR + - DRWAV_VERSION_MAJOR + - DRWAV_VERSION_REVISION + - DRWAV_VERSION_STRING + - drwav_version() + - drwav_version_string() + +v0.12.3 - 2020-04-30 + - Fix compilation errors with VC6. + +v0.12.2 - 2020-04-21 + - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file. + +v0.12.1 - 2020-04-13 + - Fix some pedantic warnings. + +v0.12.0 - 2020-04-04 + - API CHANGE: Add container and format parameters to the chunk callback. + - Minor documentation updates. + +v0.11.5 - 2020-03-07 + - Fix compilation error with Visual Studio .NET 2003. + +v0.11.4 - 2020-01-29 + - Fix some static analysis warnings. + - Fix a bug when reading f32 samples from an A-law encoded stream. + +v0.11.3 - 2020-01-12 + - Minor changes to some f32 format conversion routines. + - Minor bug fix for ADPCM conversion when end of file is reached. + +v0.11.2 - 2019-12-02 + - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. + - Fix an integer overflow bug. + - Fix a null pointer dereference bug. + - Add limits to sample rate, channels and bits per sample to tighten up some validation. + +v0.11.1 - 2019-10-07 + - Internal code clean up. + +v0.11.0 - 2019-10-06 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation + routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: + - drwav_init() + - drwav_init_ex() + - drwav_init_file() + - drwav_init_file_ex() + - drwav_init_file_w() + - drwav_init_file_w_ex() + - drwav_init_memory() + - drwav_init_memory_ex() + - drwav_init_write() + - drwav_init_write_sequential() + - drwav_init_write_sequential_pcm_frames() + - drwav_init_file_write() + - drwav_init_file_write_sequential() + - drwav_init_file_write_sequential_pcm_frames() + - drwav_init_file_write_w() + - drwav_init_file_write_sequential_w() + - drwav_init_file_write_sequential_pcm_frames_w() + - drwav_init_memory_write() + - drwav_init_memory_write_sequential() + - drwav_init_memory_write_sequential_pcm_frames() + - drwav_open_and_read_pcm_frames_s16() + - drwav_open_and_read_pcm_frames_f32() + - drwav_open_and_read_pcm_frames_s32() + - drwav_open_file_and_read_pcm_frames_s16() + - drwav_open_file_and_read_pcm_frames_f32() + - drwav_open_file_and_read_pcm_frames_s32() + - drwav_open_file_and_read_pcm_frames_s16_w() + - drwav_open_file_and_read_pcm_frames_f32_w() + - drwav_open_file_and_read_pcm_frames_s32_w() + - drwav_open_memory_and_read_pcm_frames_s16() + - drwav_open_memory_and_read_pcm_frames_f32() + - drwav_open_memory_and_read_pcm_frames_s32() + Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use + DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE. + - Add support for reading and writing PCM frames in an explicit endianness. New APIs: + - drwav_read_pcm_frames_le() + - drwav_read_pcm_frames_be() + - drwav_read_pcm_frames_s16le() + - drwav_read_pcm_frames_s16be() + - drwav_read_pcm_frames_f32le() + - drwav_read_pcm_frames_f32be() + - drwav_read_pcm_frames_s32le() + - drwav_read_pcm_frames_s32be() + - drwav_write_pcm_frames_le() + - drwav_write_pcm_frames_be() + - Remove deprecated APIs. + - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data. + - drwav_read_pcm_frames() + - drwav_read_pcm_frames_s16() + - drwav_read_pcm_frames_s32() + - drwav_read_pcm_frames_f32() + - drwav_open_and_read_pcm_frames_s16() + - drwav_open_and_read_pcm_frames_s32() + - drwav_open_and_read_pcm_frames_f32() + - drwav_open_file_and_read_pcm_frames_s16() + - drwav_open_file_and_read_pcm_frames_s32() + - drwav_open_file_and_read_pcm_frames_f32() + - drwav_open_file_and_read_pcm_frames_s16_w() + - drwav_open_file_and_read_pcm_frames_s32_w() + - drwav_open_file_and_read_pcm_frames_f32_w() + - drwav_open_memory_and_read_pcm_frames_s16() + - drwav_open_memory_and_read_pcm_frames_s32() + - drwav_open_memory_and_read_pcm_frames_f32() + +v0.10.1 - 2019-08-31 + - Correctly handle partial trailing ADPCM blocks. + +v0.10.0 - 2019-08-04 + - Remove deprecated APIs. + - Add wchar_t variants for file loading APIs: + drwav_init_file_w() + drwav_init_file_ex_w() + drwav_init_file_write_w() + drwav_init_file_write_sequential_w() + - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count. + - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode: + drwav_init_write_sequential_pcm_frames() + drwav_init_file_write_sequential_pcm_frames() + drwav_init_file_write_sequential_pcm_frames_w() + drwav_init_memory_write_sequential_pcm_frames() + - Deprecate drwav_open*() and drwav_close(): + drwav_open() + drwav_open_ex() + drwav_open_write() + drwav_open_write_sequential() + drwav_open_file() + drwav_open_file_ex() + drwav_open_file_write() + drwav_open_file_write_sequential() + drwav_open_memory() + drwav_open_memory_ex() + drwav_open_memory_write() + drwav_open_memory_write_sequential() + drwav_close() + - Minor documentation updates. + +v0.9.2 - 2019-05-21 + - Fix warnings. + +v0.9.1 - 2019-05-05 + - Add support for C89. + - Change license to choice of public domain or MIT-0. + +v0.9.0 - 2018-12-16 + - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and + will be removed in v0.10.0. Deprecated APIs and their replacements: + drwav_read() -> drwav_read_pcm_frames() + drwav_read_s16() -> drwav_read_pcm_frames_s16() + drwav_read_f32() -> drwav_read_pcm_frames_f32() + drwav_read_s32() -> drwav_read_pcm_frames_s32() + drwav_seek_to_sample() -> drwav_seek_to_pcm_frame() + drwav_write() -> drwav_write_pcm_frames() + drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16() + drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32() + drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32() + drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16() + drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32() + drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32() + drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16() + drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32() + drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32() + drwav::totalSampleCount -> drwav::totalPCMFrameCount + - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*(). + - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*(). + - Add built-in support for smpl chunks. + - Add support for firing a callback for each chunk in the file at initialization time. + - This is enabled through the drwav_init_ex(), etc. family of APIs. + - Handle invalid FMT chunks more robustly. + +v0.8.5 - 2018-09-11 + - Const correctness. + - Fix a potential stack overflow. + +v0.8.4 - 2018-08-07 + - Improve 64-bit detection. + +v0.8.3 - 2018-08-05 + - Fix C++ build on older versions of GCC. + +v0.8.2 - 2018-08-02 + - Fix some big-endian bugs. + +v0.8.1 - 2018-06-29 + - Add support for sequential writing APIs. + - Disable seeking in write mode. + - Fix bugs with Wave64. + - Fix typos. + +v0.8 - 2018-04-27 + - Bug fix. + - Start using major.minor.revision versioning. + +v0.7f - 2018-02-05 + - Restrict ADPCM formats to a maximum of 2 channels. + +v0.7e - 2018-02-02 + - Fix a crash. + +v0.7d - 2018-02-01 + - Fix a crash. + +v0.7c - 2018-02-01 + - Set drwav.bytesPerSample to 0 for all compressed formats. + - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for + all format conversion reading APIs (*_s16, *_s32, *_f32 APIs). + - Fix some divide-by-zero errors. + +v0.7b - 2018-01-22 + - Fix errors with seeking of compressed formats. + - Fix compilation error when DR_WAV_NO_CONVERSION_API + +v0.7a - 2017-11-17 + - Fix some GCC warnings. + +v0.7 - 2017-11-04 + - Add writing APIs. + +v0.6 - 2017-08-16 + - API CHANGE: Rename dr_* types to drwav_*. + - Add support for custom implementations of malloc(), realloc(), etc. + - Add support for Microsoft ADPCM. + - Add support for IMA ADPCM (DVI, format code 0x11). + - Optimizations to drwav_read_s16(). + - Bug fixes. + +v0.5g - 2017-07-16 + - Change underlying type for booleans to unsigned. + +v0.5f - 2017-04-04 + - Fix a minor bug with drwav_open_and_read_s16() and family. + +v0.5e - 2016-12-29 + - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this. + - Minor fixes to documentation. + +v0.5d - 2016-12-28 + - Use drwav_int* and drwav_uint* sized types to improve compiler support. + +v0.5c - 2016-11-11 + - Properly handle JUNK chunks that come before the FMT chunk. + +v0.5b - 2016-10-23 + - A minor change to drwav_bool8 and drwav_bool32 types. + +v0.5a - 2016-10-11 + - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering. + - Improve A-law and mu-law efficiency. + +v0.5 - 2016-09-29 + - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to + keep it consistent with dr_audio and dr_flac. + +v0.4b - 2016-09-18 + - Fixed a typo in documentation. + +v0.4a - 2016-09-18 + - Fixed a typo. + - Change date format to ISO 8601 (YYYY-MM-DD) + +v0.4 - 2016-07-13 + - API CHANGE. Make onSeek consistent with dr_flac. + - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac. + - Added support for Sony Wave64. + +v0.3a - 2016-05-28 + - API CHANGE. Return drwav_bool32 instead of int in onSeek callback. + - Fixed a memory leak. + +v0.3 - 2016-05-22 + - Lots of API changes for consistency. + +v0.2a - 2016-05-16 + - Fixed Linux/GCC build. + +v0.2 - 2016-05-11 + - Added support for reading data as signed 32-bit PCM for consistency with dr_flac. + +v0.1a - 2016-05-07 + - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize. + +v0.1 - 2016-05-04 + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2020 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/tools/src/libpsxav/adpcm.c b/tools/src/libpsxav/adpcm.c new file mode 100644 index 0000000..2007ca7 --- /dev/null +++ b/tools/src/libpsxav/adpcm.c @@ -0,0 +1,328 @@ +/* +libpsxav: MDEC video + SPU/XA-ADPCM audio library + +Copyright (c) 2019, 2020 Adrian "asie" Siekierka +Copyright (c) 2019 Ben "GreaseMonkey" Russell + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include "libpsxav.h" + +#define ADPCM_FILTER_COUNT 5 +#define XA_ADPCM_FILTER_COUNT 4 +#define SPU_ADPCM_FILTER_COUNT 5 + +static const int16_t filter_k1[ADPCM_FILTER_COUNT] = {0, 60, 115, 98, 122}; +static const int16_t filter_k2[ADPCM_FILTER_COUNT] = {0, 0, -52, -55, -60}; + +static int find_min_shift(const psx_audio_encoder_channel_state_t *state, int16_t *samples, int pitch, int filter) { + // Assumption made: + // + // There is value in shifting right one step further to allow the nibbles to clip. + // However, given a possible shift value, there is no value in shifting one step less. + // + // Having said that, this is not a completely accurate model of the encoder, + // so maybe we will need to shift one step less. + // + int prev1 = state->prev1; + int prev2 = state->prev2; + int k1 = filter_k1[filter]; + int k2 = filter_k2[filter]; + + int right_shift = 0; + + int32_t s_min = 0; + int32_t s_max = 0; + for (int i = 0; i < 28; i++) { + int32_t raw_sample = samples[i * pitch]; + int32_t previous_values = (k1*prev1 + k2*prev2 + (1<<5))>>6; + int32_t sample = raw_sample - previous_values; + if (sample < s_min) { s_min = sample; } + if (sample > s_max) { s_max = sample; } + prev2 = prev1; + prev1 = raw_sample; + } + while(right_shift < 12 && (s_max>>right_shift) > +0x7) { right_shift += 1; }; + while(right_shift < 12 && (s_min>>right_shift) < -0x8) { right_shift += 1; }; + + int min_shift = 12 - right_shift; + assert(0 <= min_shift && min_shift <= 12); + return min_shift; +} + +static uint8_t attempt_to_encode_nibbles(psx_audio_encoder_channel_state_t *outstate, const psx_audio_encoder_channel_state_t *instate, int16_t *samples, int sample_limit, int pitch, uint8_t *data, int data_shift, int data_pitch, int filter, int sample_shift) { + uint8_t nondata_mask = ~(0x0F << data_shift); + int min_shift = sample_shift; + int k1 = filter_k1[filter]; + int k2 = filter_k2[filter]; + + uint8_t hdr = (min_shift & 0x0F) | (filter << 4); + + if (outstate != instate) { + memcpy(outstate, instate, sizeof(psx_audio_encoder_channel_state_t)); + } + + outstate->mse = 0; + + for (int i = 0; i < 28; i++) { + int32_t sample = ((i * pitch) >= sample_limit ? 0 : samples[i * pitch]) + outstate->qerr; + int32_t previous_values = (k1*outstate->prev1 + k2*outstate->prev2 + (1<<5))>>6; + int32_t sample_enc = sample - previous_values; + sample_enc <<= min_shift; + sample_enc += (1<<(12-1)); + sample_enc >>= 12; + if(sample_enc < -8) { sample_enc = -8; } + if(sample_enc > +7) { sample_enc = +7; } + sample_enc &= 0xF; + + int32_t sample_dec = (int16_t) ((sample_enc&0xF) << 12); + sample_dec >>= min_shift; + sample_dec += previous_values; + if (sample_dec > +0x7FFF) { sample_dec = +0x7FFF; } + if (sample_dec < -0x8000) { sample_dec = -0x8000; } + int64_t sample_error = sample_dec - sample; + + assert(sample_error < (1<<30)); + assert(sample_error > -(1<<30)); + + data[i * data_pitch] = (data[i * data_pitch] & nondata_mask) | (sample_enc << data_shift); + // FIXME: dithering is hard to predict + //outstate->qerr += sample_error; + outstate->mse += ((uint64_t)sample_error) * (uint64_t)sample_error; + + outstate->prev2 = outstate->prev1; + outstate->prev1 = sample_dec; + } + + return hdr; +} + +static uint8_t encode_nibbles(psx_audio_encoder_channel_state_t *state, int16_t *samples, int sample_limit, int pitch, uint8_t *data, int data_shift, int data_pitch, int filter_count) { + psx_audio_encoder_channel_state_t proposed; + int64_t best_mse = ((int64_t)1<<(int64_t)50); + int best_filter = 0; + int best_sample_shift = 0; + + for (int filter = 0; filter < filter_count; filter++) { + int true_min_shift = find_min_shift(state, samples, pitch, filter); + + // Testing has shown that the optimal shift can be off the true minimum shift + // by 1 in *either* direction. + // This is NOT the case when dither is used. + int min_shift = true_min_shift - 1; + int max_shift = true_min_shift + 1; + if (min_shift < 0) { min_shift = 0; } + if (max_shift > 12) { max_shift = 12; } + + for (int sample_shift = min_shift; sample_shift <= max_shift; sample_shift++) { + // ignore header here + attempt_to_encode_nibbles( + &proposed, state, + samples, sample_limit, pitch, + data, data_shift, data_pitch, + filter, sample_shift); + + if (best_mse > proposed.mse) { + best_mse = proposed.mse; + best_filter = filter; + best_sample_shift = sample_shift; + } + } + } + + // now go with the encoder + return attempt_to_encode_nibbles( + state, state, + samples, sample_limit, pitch, + data, data_shift, data_pitch, + best_filter, best_sample_shift); +} + +static void encode_block_xa(int16_t *audio_samples, int audio_samples_limit, uint8_t *data, psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state) { + if (settings.bits_per_sample == 4) { + if (settings.stereo) { + data[0] = encode_nibbles(&(state->left), audio_samples, audio_samples_limit, 2, data + 0x10, 0, 4, XA_ADPCM_FILTER_COUNT); + data[1] = encode_nibbles(&(state->right), audio_samples + 1, audio_samples_limit - 1, 2, data + 0x10, 4, 4, XA_ADPCM_FILTER_COUNT); + data[2] = encode_nibbles(&(state->left), audio_samples + 56, audio_samples_limit - 56, 2, data + 0x11, 0, 4, XA_ADPCM_FILTER_COUNT); + data[3] = encode_nibbles(&(state->right), audio_samples + 56 + 1, audio_samples_limit - 56 - 1, 2, data + 0x11, 4, 4, XA_ADPCM_FILTER_COUNT); + data[8] = encode_nibbles(&(state->left), audio_samples + 56*2, audio_samples_limit - 56*2, 2, data + 0x12, 0, 4, XA_ADPCM_FILTER_COUNT); + data[9] = encode_nibbles(&(state->right), audio_samples + 56*2 + 1, audio_samples_limit - 56*2 - 1, 2, data + 0x12, 4, 4, XA_ADPCM_FILTER_COUNT); + data[10] = encode_nibbles(&(state->left), audio_samples + 56*3, audio_samples_limit - 56*3, 2, data + 0x13, 0, 4, XA_ADPCM_FILTER_COUNT); + data[11] = encode_nibbles(&(state->right), audio_samples + 56*3 + 1, audio_samples_limit - 56*3 - 1, 2, data + 0x13, 4, 4, XA_ADPCM_FILTER_COUNT); + } else { + data[0] = encode_nibbles(&(state->left), audio_samples, audio_samples_limit, 1, data + 0x10, 0, 4, XA_ADPCM_FILTER_COUNT); + data[1] = encode_nibbles(&(state->right), audio_samples + 28, audio_samples_limit - 28, 1, data + 0x10, 4, 4, XA_ADPCM_FILTER_COUNT); + data[2] = encode_nibbles(&(state->left), audio_samples + 28*2, audio_samples_limit - 28*2, 1, data + 0x11, 0, 4, XA_ADPCM_FILTER_COUNT); + data[3] = encode_nibbles(&(state->right), audio_samples + 28*3, audio_samples_limit - 28*3, 1, data + 0x11, 4, 4, XA_ADPCM_FILTER_COUNT); + data[8] = encode_nibbles(&(state->left), audio_samples + 28*4, audio_samples_limit - 28*4, 1, data + 0x12, 0, 4, XA_ADPCM_FILTER_COUNT); + data[9] = encode_nibbles(&(state->right), audio_samples + 28*5, audio_samples_limit - 28*5, 1, data + 0x12, 4, 4, XA_ADPCM_FILTER_COUNT); + data[10] = encode_nibbles(&(state->left), audio_samples + 28*6, audio_samples_limit - 28*6, 1, data + 0x13, 0, 4, XA_ADPCM_FILTER_COUNT); + data[11] = encode_nibbles(&(state->right), audio_samples + 28*7, audio_samples_limit - 28*7, 1, data + 0x13, 4, 4, XA_ADPCM_FILTER_COUNT); + } + } else { +/* if (settings->stereo) { + data[0] = encode_bytes(audio_samples, 2, data + 0x10); + data[1] = encode_bytes(audio_samples + 1, 2, data + 0x11); + data[2] = encode_bytes(audio_samples + 56, 2, data + 0x12); + data[3] = encode_bytes(audio_samples + 57, 2, data + 0x13); + } else { + data[0] = encode_bytes(audio_samples, 1, data + 0x10); + data[1] = encode_bytes(audio_samples + 28, 1, data + 0x11); + data[2] = encode_bytes(audio_samples + 56, 1, data + 0x12); + data[3] = encode_bytes(audio_samples + 84, 1, data + 0x13); + } */ + } +} + +uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count) { + int sample_pitch = psx_audio_xa_get_samples_per_sector(settings); + int xa_sectors = ((sample_count + sample_pitch - 1) / sample_pitch); + int xa_sector_size = psx_audio_xa_get_buffer_size_per_sector(settings); + return xa_sectors * xa_sector_size; +} + +uint32_t psx_audio_spu_get_buffer_size(int sample_count) { + return ((sample_count + 27) / 28) << 4; +} + +uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings) { + return settings.format == PSX_AUDIO_XA_FORMAT_XA ? 2336 : 2352; +} + +uint32_t psx_audio_spu_get_buffer_size_per_block(void) { + return 16; +} + +uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings) { + return (((settings.bits_per_sample == 8) ? 112 : 224) >> (settings.stereo ? 1 : 0)) * 18; +} + +uint32_t psx_audio_spu_get_samples_per_block(void) { + return 28; +} + +static void psx_audio_xa_encode_init_sector(uint8_t *buffer, psx_audio_xa_settings_t settings) { + if (settings.format == PSX_AUDIO_XA_FORMAT_XACD) { + memset(buffer, 0, 2352); + memset(buffer+0x001, 0xFF, 10); + buffer[0x00F] = 0x02; + } else { + memset(buffer + 0x10, 0, 2336); + } + + buffer[0x010] = settings.file_number; + buffer[0x011] = settings.channel_number & 0x1F; + buffer[0x012] = 0x24 | 0x40; + buffer[0x013] = + (settings.stereo ? 1 : 0) + | (settings.frequency >= PSX_AUDIO_XA_FREQ_DOUBLE ? 0 : 4) + | (settings.bits_per_sample >= 8 ? 16 : 0); + memcpy(buffer + 0x014, buffer + 0x010, 4); +} + +int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) { + int sample_jump = (settings.bits_per_sample == 8) ? 112 : 224; + int i, j; + int xa_sector_size = settings.format == PSX_AUDIO_XA_FORMAT_XA ? 2336 : 2352; + int xa_offset = 2352 - xa_sector_size; + uint8_t init_sector = 1; + + if (settings.stereo) { sample_count <<= 1; } + + for (i = 0, j = 0; i < sample_count || ((j % 18) != 0); i += sample_jump, j++) { + uint8_t *sector_data = output + ((j/18) * xa_sector_size) - xa_offset; + uint8_t *block_data = sector_data + 0x18 + ((j%18) * 0x80); + + if (init_sector) { + psx_audio_xa_encode_init_sector(sector_data, settings); + init_sector = 0; + } + + encode_block_xa(samples + i, sample_count - i, block_data, settings, state); + + memcpy(block_data + 4, block_data, 4); + memcpy(block_data + 12, block_data + 8, 4); + + if ((j+1)%18 == 0) { + psx_cdrom_calculate_checksums(sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2); + init_sector = 1; + } + } + + return (((j + 17) / 18) * xa_sector_size); +} + +int psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length) { + if (output_length >= 2336) { + output[output_length - 2352 + 0x12] |= 0x80; + output[output_length - 2352 + 0x18] |= 0x80; + } + return 0; +} + +int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output) { + psx_audio_encoder_state_t state; + memset(&state, 0, sizeof(psx_audio_encoder_state_t)); + int length = psx_audio_xa_encode(settings, &state, samples, sample_count, output); + psx_audio_xa_encode_finalize(settings, output, length); + return length; +} + +int psx_audio_spu_encode(psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) { + uint8_t prebuf[28]; + uint8_t *buffer = output; + uint8_t *data; + + for (int i = 0; i < sample_count; i += 28, buffer += 16) { + buffer[0] = encode_nibbles(&(state->left), samples + i, sample_count - i, 1, prebuf, 0, 1, SPU_ADPCM_FILTER_COUNT); + buffer[1] = 0; + + for (int j = 0; j < 28; j+=2) { + buffer[2 + (j>>1)] = (prebuf[j] & 0x0F) | (prebuf[j+1] << 4); + } + } + + return buffer - output; +} + +int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start) { + psx_audio_encoder_state_t state; + memset(&state, 0, sizeof(psx_audio_encoder_state_t)); + int length = psx_audio_spu_encode(&state, samples, sample_count, output); + + if (length >= 32) { + if (loop_start < 0) { + output[1] = 4; + output[length - 16 + 1] = 1; + } else { + psx_audio_spu_set_flag_at_sample(output, loop_start, 4); + output[length - 16 + 1] = 3; + } + } else if (length >= 16) { + output[1] = loop_start >= 0 ? 7 : 5; + } + + return length; +} + +void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag) { + int buffer_pos = (sample_pos / 28) << 4; + spu_data[buffer_pos + 1] = flag; +} diff --git a/tools/src/libpsxav/cdrom.c b/tools/src/libpsxav/cdrom.c new file mode 100644 index 0000000..f6b0144 --- /dev/null +++ b/tools/src/libpsxav/cdrom.c @@ -0,0 +1,69 @@ +/* +libpsxav: MDEC video + SPU/XA-ADPCM audio library + +Copyright (c) 2019, 2020 Adrian "asie" Siekierka +Copyright (c) 2019 Ben "GreaseMonkey" Russell + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include "libpsxav.h" + +static uint32_t psx_cdrom_calculate_edc(uint8_t *sector, uint32_t offset, uint32_t size) +{ + uint32_t edc = 0; + for (int i = offset; i < offset+size; i++) { + edc ^= 0xFF&(uint32_t)sector[i]; + for (int ibit = 0; ibit < 8; ibit++) { + edc = (edc>>1)^(0xD8018001*(edc&0x1)); + } + } + return edc; +} + +void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type) +{ + switch (type) { + case PSX_CDROM_SECTOR_TYPE_MODE1: { + uint32_t edc = psx_cdrom_calculate_edc(sector, 0x0, 0x810); + sector[0x810] = (uint8_t)(edc); + sector[0x811] = (uint8_t)(edc >> 8); + sector[0x812] = (uint8_t)(edc >> 16); + sector[0x813] = (uint8_t)(edc >> 24); + + memset(sector + 0x814, 0, 8); + // TODO: ECC + } break; + case PSX_CDROM_SECTOR_TYPE_MODE2_FORM1: { + uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x808); + sector[0x818] = (uint8_t)(edc); + sector[0x819] = (uint8_t)(edc >> 8); + sector[0x81A] = (uint8_t)(edc >> 16); + sector[0x81B] = (uint8_t)(edc >> 24); + + // TODO: ECC + } break; + case PSX_CDROM_SECTOR_TYPE_MODE2_FORM2: { + uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x91C); + sector[0x92C] = (uint8_t)(edc); + sector[0x92D] = (uint8_t)(edc >> 8); + sector[0x92E] = (uint8_t)(edc >> 16); + sector[0x92F] = (uint8_t)(edc >> 24); + } break; + } +} \ No newline at end of file diff --git a/tools/src/libpsxav/libpsxav.h b/tools/src/libpsxav/libpsxav.h new file mode 100644 index 0000000..828d8f8 --- /dev/null +++ b/tools/src/libpsxav/libpsxav.h @@ -0,0 +1,89 @@ +/* +libpsxav: MDEC video + SPU/XA-ADPCM audio library + +Copyright (c) 2019, 2020 Adrian "asie" Siekierka +Copyright (c) 2019 Ben "GreaseMonkey" Russell + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef __LIBPSXAV_H__ +#define __LIBPSXAV_H__ + +#include +#include + +// audio.c + +#define PSX_AUDIO_XA_FREQ_SINGLE 18900 +#define PSX_AUDIO_XA_FREQ_DOUBLE 37800 + +typedef enum { + PSX_AUDIO_XA_FORMAT_XA, // .xa file + PSX_AUDIO_XA_FORMAT_XACD // 2352-byte sector +} psx_audio_xa_format_t; + +typedef struct { + psx_audio_xa_format_t format; + bool stereo; // false or true + int frequency; // 18900 or 37800 Hz + int bits_per_sample; // 4 or 8 + int file_number; // 00-FF + int channel_number; // 00-1F +} psx_audio_xa_settings_t; + +typedef struct { + int qerr; // quanitisation error + uint64_t mse; // mean square error + int prev1, prev2; +} psx_audio_encoder_channel_state_t; + +typedef struct { + psx_audio_encoder_channel_state_t left; + psx_audio_encoder_channel_state_t right; +} psx_audio_encoder_state_t; + +#define PSX_AUDIO_SPU_LOOP_END 1 +#define PSX_AUDIO_SPU_LOOP_REPEAT 3 +#define PSX_AUDIO_SPU_LOOP_START 4 + +uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count); +uint32_t psx_audio_spu_get_buffer_size(int sample_count); +uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings); +uint32_t psx_audio_spu_get_buffer_size_per_block(void); +uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings); +uint32_t psx_audio_spu_get_samples_per_block(void); +int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output); +int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output); +int psx_audio_spu_encode(psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output); +int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start); +int psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length); +void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag); + +// cdrom.c + +#define PSX_CDROM_SECTOR_SIZE 2352 + +typedef enum { + PSX_CDROM_SECTOR_TYPE_MODE1, + PSX_CDROM_SECTOR_TYPE_MODE2_FORM1, + PSX_CDROM_SECTOR_TYPE_MODE2_FORM2 +} psx_cdrom_sector_type_t; + +void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type); + +#endif /* __LIBPSXAV_H__ */ \ No newline at end of file diff --git a/tools/src/orgconv.c b/tools/src/orgconv.c new file mode 100644 index 0000000..d2e4efa --- /dev/null +++ b/tools/src/orgconv.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include + +// #define SAVE_WAVS 1 + +#ifdef SAVE_WAVS +#define DR_WAV_IMPLEMENTATION +#include "dr_wav.h" +#endif + +#include "libpsxav/libpsxav.h" +#include "common.h" + +#define MAX_TRACKS 16 +#define MAX_MELODY_TRACKS 8 +#define NUM_WAVEFORMS 100 +#define WAVEFORM_LEN 256 +#define NUM_OCT 8 + +#define ORG_MAGIC "Org-0" +#define ORG_MAGICLEN 6 // +1 char for version + +#pragma pack(push, 1) + +typedef struct { + uint16_t freq; // frequency modifier (default = 1000) + uint8_t wave_no; // waveform index in the wavetable + uint8_t pipi; // loop flag? + uint16_t note_num; // number of notes in track +} org_trackhdr_t; + +typedef struct { + uint16_t wait; + uint8_t line; + uint8_t dot; + int32_t repeat_x; + int32_t end_x; + org_trackhdr_t tdata[MAX_TRACKS]; +} org_hdr_t; + +#pragma pack(pop) + +// organya header +static org_hdr_t org_data; + +// output PSX SPURAM +static uint8_t spuram[SPURAM_SIZE + 1024]; // 1kb of grace zone +static int spuram_ptr = SPURAM_START; +static int spuram_start = SPURAM_START; + +// wave.dat +static int8_t wavetable[NUM_WAVEFORMS][WAVEFORM_LEN]; + +// output instrument samples +static struct sfx inst[MAX_MELODY_TRACKS][NUM_OCT]; + +static const struct { + short wave_size; + short oct_par; + short oct_size; +} oct_wave[NUM_OCT] = { + { 256, 1, 4 }, // 0 Oct + { 256, 2, 8 }, // 1 Oct + { 128, 4, 12 }, // 2 Oct + { 128, 8, 16 }, // 3 Oct + { 64, 16, 20 }, // 4 Oct + { 32, 32, 24 }, // 5 Oct + { 16, 64, 28 }, // 6 Oct + { 8, 128, 32 }, // 7 Oct +}; + +static struct bank_hdr bank_hdr; + +#ifdef SAVE_WAVS +static void save_track_sample(const int i, const int j, const char *fmask) { + char path[2048]; + snprintf(path, sizeof(path), "%s%d.wav", fmask, i * NUM_OCT + j); + drwav wav; + drwav_data_format format; + format.container = drwav_container_riff; + format.format = DR_WAVE_FORMAT_PCM; + format.channels = 1; + format.sampleRate = 22050; + format.bitsPerSample = 16; + if (drwav_init_file_write(&wav, path, &format, NULL)) { + drwav_write_pcm_frames(&wav, inst[i][j].len, inst[i][j].data); + drwav_uninit(&wav); + } +} +#endif + +static inline int lcm(const int a, const int b) { + register int step, div, x; + if (a > b) { + step = b; + div = a; + } else { + step = a; + div = b; + } + x = step; + while (x % div) + x += step; + return x; +} + +// this is basically MakeSoundObject8 from Organya.cpp +static void build_track_samples(const int idx, const int8_t *wavep, const bool pipi) { + for (int j = 0; j < NUM_OCT; ++j) { + // generate unsigned 8-bit PCM sample + const uint32_t wave_size = oct_wave[j].wave_size; + const uint32_t num_samples = lcm(wave_size, 28); + int16_t *wp = malloc(num_samples * sizeof(int16_t)); + assert(wp); + uint32_t wav_tp = 0; + int16_t *wp_sub = wp; + for (uint32_t i = 0; i < num_samples; ++i) { + int work = *(wavep + wav_tp); + work *= 256; + *wp_sub = (int16_t)work; + wav_tp += 0x100 / wave_size; + if (wav_tp > 0xFF) + wav_tp -= 0x100; + wp_sub++; + } + inst[idx][j].data = wp; + inst[idx][j].len = num_samples; +#ifdef SAVE_WAVS + save_track_sample(idx, j, "orgwave/"); +#endif + } +} + +static bool load_wavetable(const char *fname) { + FILE *f = fopen(fname, "rb"); + if (!f) return false; + const size_t rx = fread(wavetable, sizeof(wavetable), 1, f); + fclose(f); + return (rx > 0); +} + +static bool load_org_tracks(const char *fname) { + FILE *f = fopen(fname, "rb"); + if (!f) return false; + + char magic[ORG_MAGICLEN + 1] = { 0 }; + fread(magic, ORG_MAGICLEN, 1, f); + if (memcmp(magic, ORG_MAGIC, ORG_MAGICLEN - 1)) { + fclose(f); + fprintf(stderr, "error: '%s' is not a valid Org file\n", fname); + return false; + } + + const int ver = magic[ORG_MAGICLEN - 1] - '0'; + if (ver != 1 && ver != 2) { + fclose(f); + fprintf(stderr, "error: '%s' has version %d, expected 1 or 2\n", fname, ver); + return false; + } + + fread(&org_data, sizeof(org_data), 1, f); + fclose(f); + + // don't know if this is required + if (ver == 1) { + for (int i = 0; i < MAX_TRACKS; ++i) + org_data.tdata[i].pipi = 0; + } + + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) + build_track_samples(i, wavetable[org_data.tdata[i].wave_no], org_data.tdata[i].pipi); + + return true; +} + +static void cleanup(void) { + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) { + for (int j = 0; j < NUM_OCT; ++j) { + if (inst[i][j].data) + free(inst[i][j].data); + } + } +} + +int main(int argc, char **argv) { + if (argc < 4) { + printf("usage: orgconv []\n"); + return -1; + } + + atexit(cleanup); + + const char *orgfname = argv[1]; + const char *datfname = argv[2]; + const char *outfname = argv[3]; + if (argc > 4) { + int addr = atoi(argv[4]); + if (addr > SPURAM_START) + spuram_ptr = spuram_start = addr; + } + + if (!load_wavetable(datfname)) { + fprintf(stderr, "error: could not load wavetable from '%s'\n", datfname); + return -2; + } + +#ifdef SAVE_WAVS + mkdir("orgwave"); +#endif + + if (!load_org_tracks(orgfname)) { + fprintf(stderr, "error: could not load track data from '%s'\n", orgfname); + return -3; + } + + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) { + for (int j = 0; j < NUM_OCT; ++j) { + const int adpcm_len = psx_audio_spu_encode_simple(inst[i][j].data, inst[i][j].len, spuram + spuram_ptr, 0); + if (adpcm_len <= 0) { + fprintf(stderr, "error: could not encode instrument sample %d/%d\n", i, j); + return -4; + } + inst[i][j].addr = spuram_ptr; + spuram_ptr += ALIGN(adpcm_len, 8); + if (spuram_ptr >= SPURAM_SIZE) { + fprintf(stderr, "error: ran out of SPU RAM packing instrument sample %d/%d\n", i, j); + return -5; + } + // printf(" * instrument [%d][%d]: addr=%05x pcmlen=%06u adpcmlen=%06d\n", i, j, inst[i][j].addr, inst[i][j].len, adpcm_len); + } + } + + bank_hdr.num_sfx = MAX_MELODY_TRACKS * NUM_OCT; + bank_hdr.data_size = spuram_ptr - spuram_start; + + /* + printf("bank addr:\n"); + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) + for (int j = 0; j < NUM_OCT; ++j) + printf("* (%03d) 0x%06x\n", i*NUM_OCT + j, inst[i][j].addr); + */ + + printf("SPU RAM total usage: %u/%u bytes\n", spuram_ptr, SPURAM_SIZE); + printf("SPU RAM start address: %u\n", SPURAM_START); + printf("bank size: %u bytes\n", bank_hdr.data_size); + printf("bank start: %u\n", spuram_start); + printf("bank ident: %02x %02x %02x %02x\n", + spuram[spuram_start+0], spuram[spuram_start+1], spuram[spuram_start+2], spuram[spuram_start+3]); + + FILE *f = fopen(outfname, "wb"); + if (!f) { + fprintf(stderr, "error: could not open '%s' for writing\n", outfname); + return -5; + } + + // write header without the first address because fuck this shit + fwrite(&bank_hdr, sizeof(bank_hdr) - sizeof(uint32_t), 1, f); + // write sample addresses starting with 0, 0 + for (int i = 0; i < MAX_MELODY_TRACKS; ++i) + for (int j = 0; j < NUM_OCT; ++j) + fwrite(&inst[i][j].addr, sizeof(uint32_t), 1, f); + // write sample data + fwrite(spuram + spuram_start, bank_hdr.data_size, 1, f); + + fclose(f); + + return 0; +} diff --git a/tools/src/sfxconv.c b/tools/src/sfxconv.c new file mode 100644 index 0000000..3b61db7 --- /dev/null +++ b/tools/src/sfxconv.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include + +#define DR_WAV_IMPLEMENTATION +#include "dr_wav.h" +#include "libpsxav/libpsxav.h" +#include "common.h" + +// output PSX SPURAM +static uint8_t spuram[SPURAM_SIZE + 1024]; // 1kb of grace zone +static int spuram_ptr = SPURAM_START; + +static struct sfx sfx[MAX_SFX]; // 0 is dummy +static int max_sfx = 0; + +static struct bank_hdr bank_hdr; + +static bool load_wav(struct sfx *sfx, const char *fname) { + drwav wav; + if (!drwav_init_file(&wav, fname, NULL)) + return false; + + if (wav.bitsPerSample != 8 || wav.channels != 1 || wav.sampleRate != 22050) { + fprintf(stderr, "error: '%s' is not a mono unsigned 8-bit 22khz PCM WAV file!\n", fname); + drwav_uninit(&wav); + return false; + } + + int16_t *pcm = malloc(sizeof(int16_t) * wav.totalPCMFrameCount); + assert(pcm); + + const uint32_t total_read = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, pcm); + assert(total_read); + + sfx->data = pcm; + sfx->len = total_read; + sfx->freq = wav.sampleRate; + + drwav_uninit(&wav); + + return true; +} + +static bool load_all_sfx(const char *path) { + char fname[2048]; + int i = 1; + for (; i < MAX_SFX; ++i) { + snprintf(fname, sizeof(fname), "%s/%d.wav", path, i); + if (load_wav(&sfx[i], fname)) + max_sfx = i; + } + return (i > 1); +} + +static void cleanup(void) { + for (int i = 0; i < max_sfx; ++i) { + if (sfx[i].data) + free(sfx[i].data); + } +} + +static inline bool is_sfx_looping(const int sfx) { + return (sfx == 40 || sfx == 41 || sfx == 58 || sfx == 7); +} + +int main(int argc, char **argv) { + if (argc != 3) { + printf("usage: sfxconv \n"); + return -1; + } + + atexit(cleanup); + + const char *wavpath = argv[1]; + const char *outfname = argv[2]; + + if (!load_all_sfx(wavpath)) { + fprintf(stderr, "error: could not load samples from '%s'\n", wavpath); + return -2; + } + + printf("%d samples loaded\n", max_sfx); + + // convert samples + + for (int i = 1; i <= max_sfx; ++i) { + if (sfx[i].data == NULL) + continue; + const int loop_start = is_sfx_looping(i) ? 0 : -1; + const int adpcm_len = psx_audio_spu_encode_simple(sfx[i].data, sfx[i].len, spuram + spuram_ptr, loop_start); + if (adpcm_len <= 0) { + fprintf(stderr, "error: could not encode sfx %d\n", i); + return -3; + } + sfx[i].addr = spuram_ptr; + spuram_ptr += ALIGN(adpcm_len, 8); + if (spuram_ptr >= SPURAM_SIZE) { + fprintf(stderr, "error: ran out of SPU RAM packing sfx %d\n", i); + return -4; + } + } + + bank_hdr.num_sfx = max_sfx + 1; + bank_hdr.data_size = spuram_ptr - SPURAM_START; + bank_hdr.sfx_addr[0] = 0; + + printf("SPU RAM total usage: %u/%u bytes\n", spuram_ptr, SPURAM_SIZE); + printf("SPU RAM start address: %u\n", SPURAM_START); + printf("bank size: %u bytes\n", bank_hdr.data_size); + printf("bank start: %u\n", SPURAM_START); + printf("bank ident: %02x %02x %02x %02x\n", + spuram[SPURAM_START+0], spuram[SPURAM_START+1], spuram[SPURAM_START+2], spuram[SPURAM_START+3]); + + FILE *f = fopen(outfname, "wb"); + if (!f) { + fprintf(stderr, "error: could not open '%s' for writing\n", outfname); + return -5; + } + + // write header + fwrite(&bank_hdr, sizeof(bank_hdr), 1, f); + // write sample addresses + for (int i = 1; i <= max_sfx; ++i) + fwrite(&sfx[i].addr, sizeof(uint32_t), 1, f); + // write sample data + fwrite(spuram + SPURAM_START, spuram_ptr - SPURAM_START, 1, f); + + fclose(f); + + return 0; +}