openmcdf-1.5.4/0000755000175000017500000000000012130475036013167 5ustar mathieumathieuopenmcdf-1.5.4/help/0000755000175000017500000000000012027631352014117 5ustar mathieumathieuopenmcdf-1.5.4/help/OpenMCDF.chm0000644000175000017500000046322612061100176016151 0ustar mathieumathieuITSF`u@|{ "|{ "`xT00fITSPT  j].!"TPMGLX//#IDXHDR/#ITBITS /#STRINGSO7/#SYSTEML/#TOCIDX`/#TOPICSup/#URLSTRYv/#URLTBLet /#WINDOWS~L /$FIftiMainDQ /$OBJINST?/$WWAssociativeLinks//$WWAssociativeLinks/Property/$WWKeywordLinks//$WWKeywordLinks/BTreeJL/$WWKeywordLinks/Data!/$WWKeywordLinks/Map7*/$WWKeywordLinks/Propertya /html/./html/01842334-005a-e659-34e1-209a996972d7.htmE ./html/01bb365a-f37c-d568-af3c-dea802c3485e.htmP./html/0327ba37-8f88-ba90-a47e-4600eef3533c.htmU5./html/083dbc21-c68b-a0c3-15b3-b72ae1f13552.htm y./html/0e0cfbf6-54e0-0d4f-cab0-e37a0e777e7a.htmk./html/0f34a1a8-7a44-7c1f-30ed-5cea06c1c0ba.htmnN./html/1c4cac35-a55a-2775-8c75-b7f68623df6e.htm./html/70bef6ab-3346-c69c-eb03-eb15c847a938.htmu./html/72235ea0-8da6-f048-3ec2-ccc06829744c.htm{8./html/723a8da0-a8e2-9cf2-5a0d-51ee0024fa27.htm3F./html/7469f364-52a7-59b8-b213-dd8debfb8967.htmy]./html/7640c425-ac1a-501f-e8d5-89651d20e950.htmVe./html/7776793a-8666-2c2e-a749-ca103385a093.htm;F./html/7828b074-d983-af76-10a8-bd1e64d27bf3.htm./html/786401ef-ba80-13b1-db89-018683bdfeb4.htma./html/7aae6559-804c-23af-5618-8f6c4651a320.htmfh./html/7c065c12-8d96-b709-a6f5-1ee4aaffeaf0.htmN./html/7efbf5b4-d2ad-1ca3-c649-19d89b2c3339.htmg ./html/80b24b4c-8304-d440-0c4a-2377f9c16d93.htms./html/84a82a4e-fde9-fd35-548c-e44438102411.htmp./html/853dfe88-7013-b4ae-08c9-92360b3da8b0.htm~ ./html/8b235cdd-2b8e-c7f6-ea54-cde0201a54a2.htmg./html/90af6466-77bc-c1e2-0e93-2b5047d3f83c.htm./html/90b46897-8d53-eca8-b80f-e04319de49f6.htm$r./html/951b0969-2b48-2b2d-7776-3c37b618e0fb.htmd./html/9c888a48-5226-d66d-6988-92fe52ccbf9e.htmzB./html/9da7b4fa-11b8-9c6e-9128-443e9d67e910.htm<`./html/a679b475-a542-a08b-8988-5dc12e0d4955.htm ./html/a8242421-983c-2e21-ccfe-4f233943a708.htm%./html/a8d80da5-46bf-3b95-eae2-dccd75366438.htmA"./html/a8ef6e37-3136-e094-ff31-26b300ac66b1.htmc./html/ab085398-18b2-c088-96b1-b4e779b2a28a.htm;./html/adb717a1-ee01-82c5-dc19-4a6d25240ac2.htm;j./html/aeca6508-963f-2a26-b18d-2c7e5005344e.htm%U./html/b33118ca-57bc-d02a-51a1-2ef77218ceef.htmzY./html/b8a04620-ea24-6e09-e1fb-fd7ec93aed60.htmSE./html/c4681ddb-cfbf-f29a-fce3-614267f9a959.htm:./html/c4e7ed66-896b-5364-1fd0-58a261adc2f3.htmRA z>XPMGL./html/c4ee3868-614a-315d-9687-46278ff19371.htmD./html/c6136813-53f8-0c1b-67f6-cff7bb7bee99.htmW ./html/c94801cc-184b-4630-89b7-624fe735c698.htm`4./html/ca7ff989-3ff0-e0e1-b827-5857c539a757.htm9./html/cb6161dd-ad12-0bd9-3240-e8df796e37c1.htmMq./html/cd09e020-65d7-46ee-51d2-250877a48c66.htm>w./html/d4648875-d41a-783b-d5f4-638df39ee413.htm5i./html/d674016a-e54f-0de3-f712-e9f135958d4c.htmA./html/dcc587bc-d981-4b17-0c5c-15d7cdc98124.htm_./html/dd017eba-527f-8ddb-5cd6-0ef0c43ddc79.htmdZ./html/dd1d65e7-fb78-9a3f-3ef2-85bedcd1a234.htm>p./html/dd84a01d-8bd8-9ff3-456f-ae0a6686d98a.htm. ./html/e07f1d7c-fdcd-dc3b-7dd3-5af9ddce627f.htm8./html/e0a4470d-442e-f95b-5c94-695879367539.htmT2./html/e5d6ede8-0b1c-d579-a681-1f7468fdd867.htm|./html/e5e99a64-48e0-d637-4d99-8fb6283f84dd.htmg./html/eadaa1db-676d-751f-4c06-7aa051cb6a2d.htmi)./html/eddd415c-8266-6b39-3f06-6e84bb917668.htm)./html/f0b3a9a7-de73-5947-440b-70d15a641f0b.htm;C./html/f1b1235a-b1f3-9851-51eb-b96b2eac3647.htm~./html/f3b76b65-6d40-054a-1973-734e37cae547.htmd./html/f68f6600-cada-b8d4-2efe-9847ad19613b.htmrq./html/fd37e35b-8853-00cf-7631-a576c9704433.htmcW./html/fe082e8b-de1b-de41-c244-d1febfe5e716.htm:./html/fe70fa83-6cfa-8694-9bce-18c757c1ab27.htmU3/icons//icons/alert_caution.gifj/icons/alert_note.gifx{/icons/alert_security.gifs7/icons/Caution.gif*/icons/CFW.gif8L/icons/collapse_all.gifE/icons/copycode.gifIB/icons/CopyCode_h.gif B/icons/expand_all.gifMH/icons/LastChild.gifC/icons/NotLastChild.gifXF/icons/privclass.gifm/icons/privdelegate.gif /icons/privenum.gif U/icons/privevent.gifuD/icons/privfield.gif9>/icons/privinterface.gifwI/icons/privmethod.gif@[/icons/privproperty.gif/icons/privstructure.gif9v/icons/protclass.gif/X/icons/protdelegate.gif/icons/protenum.gifG/icons/protevent.gif_4/icons/protfield.gif:/icons/protfield.pngM/icons/protinterface.gifP2/icons/protmethod.gif7/icons/protoperator.gif9#/icons/protproperty.gif\/icons/protstructure.gifkk/icons/pubclass.gifVp/icons/pubdelegate.gifF/icons/pubenum.gifWS/icons/pubevent.gif*:/icons/pubfield.gifd6/icons/pubinterface.gif:/icons/pubmethod.gifTI/icons/puboperator.gif6/icons/pubproperty.gifSa/icons/pubstructure.gif4S/icons/security.gif7/icons/static.gif> /OpenMCDF.hhc' /OpenMCDF.hhk/O /scripts//scripts/CollapsibleSection.js F/scripts/CookieDataStore.jsfg/scripts/ElementCollection.jsM=/scripts/EventUtilities.js -/scripts/highlight.js7{/scripts/LanguageFilter.js2%/scripts/MemberFilter.jsWL/scripts/script_prototype.js#I/scripts/SplitScreen.jsly/scripts/StyleUtilities.jse/styles//styles/highlight.cssv/styles/presentation.css ::DataSpace/NameList<(::DataSpace/Storage/MSCompressed/Contentj`,::DataSpace/Storage/MSCompressed/ControlDataj)::DataSpace/Storage/MSCompressed/SpanInfob/::DataSpace/Storage/MSCompressed/Transform/List<&_::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/i::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable-a542-a08b-8988-5dc12e0d4955.htm ./html/a8242421-983c-2e21-ccfe-4f233943a708.htm%./html/a8d80da5-46bf-3b95-eae2-dccd75366438.htmA"./html/a8ef6e37-3136-e094-ff31-26b300ac66b1.htmc./html/ab085398-18b2-c088-96b1-b4e779b2a28a.htm;./html/adb717a1-ee01-82c5-dc19-4a6d25240ac2.htm;j./html/aeca6508-963f-2a26-b18d-2c7e5005344e.htm%U./html/b33118ca-57bc-d02a-51a1-2ef77218ceef.htmzY./html/b8a04620-ea24-6e09-e1fb-fd7ec93aed60.htmSE./html/c4681ddb-cfbf-f29a-fce3-614267f9a959.htm:./html/c4e7ed66-896b-5364-1fd0-58a261adc2f3.htmRA - ~ gK2/% \PMGI/./html/c4ee3868-614a-315d-9687-46278ff19371.htm Uncompressed MSCompressed{7FC28940-9D31-11D0LZXC(`#bSz<Z(b>N D$N28bKPR0gm2zHn  {P HHA Version 4.74.8702$ ʅ.html/d4648875-d41a-783b-d5f4-638df39ee413.htm Open MCDF openmcdf MsdnHelp( (  T#SM(=_3CF5^VtgDn샩Hb!Sʱ`fdh51pKBQI GD,4mudkYeBY;/$^w,YNk@gPk/!9Xh5'Q6RH$i@hd {ݳgU+qYwv֜%[ #m-dgIX%A#[D\JIOK*^GKv^TnѢiR6tK%'Aa~ֶ3:uv.ߟ;U̺c%Vՠ剫=R]5Vb.5|؁֊_V=5u%\0ڽ w)r|UTb@Q d$<ä0 Cmgjv_p?Zݍee]u§&tԝQ"ĀJdH9"*cs|`Wʲ!JR8 @Q/yhZ[]^mPI 9%{f1rtfLHzafP:܆p?:O3fH&w=SJ ҢsQh3Q# J,ܧ[K$&#B;'>i(HAΕ:e4= I1ŐP 1(@6˾5NA60 d1G`(0ns'R':@-)1iRqDٛn4rZȒ11$F"5"`3hr;j,PUiI TB=&AQo $!!5 a)oA%Ԏ1Is<Ȱh]-$zZ0'YÒCH0IVrj7dBI<Fg# ('VBfQ{I4Еq_;jCZ{hF޵aUAQ,L) 2Ia(X`(7 `6l( Xi.( R Ry=NHU@fLvaD#bXct4_Zj1M^ês⹎ce|9@I)iJ7nx] ?\2\^ӏF7÷;,3ZpBƁ<%U0AJƑi ?ty\Vux:f %\nkP1$S`Dщ lSvϭ9٧|,!yRF#څ9w"΍KIqe5e'b['Q$L`AyG"(pRXpRbM'y;DFƩba&479bMH$Yk9S)`jm/Tj72,bEiZ$M fē|5N cDm63\$X.v5"Sr3 3@*Y$6έd9r<)S_II,Ơ4xV4DnA(A>U9p06 1`*(E@=4Z]ci2qU":؆6-Tңum3faG6ַH7QoI#=I8(^37jA#L+Jg]2@,MSP%mI5wr뮛ebw|ٗuIT$pX/SN6Ir dDZY>I.uyFM`ff˩ַɓ5ILېETXǩ Fm)m1cE3r xD&;F铎exӇ c֩tV"E98IO(19'(13n_ BbNfJLve.t똔W') JTsBxf Ngi*iIIx̥ћ.M{'Qh㗎uEJA&_\8Nߘ ?,y{v!9&8aC*N8C f(/iDiF7A4OW{:& 06χd1I˭Gi\.|o឴ "=35a9k37:(8S @8k2piVzSG!́gŶ$V KQ(zmjT/ARW O_]& dD>"$ GoC_*JzQ9$Z=%;ulJ/k-Q=)A9 U`Fvi /O{:HEj>8Ӏ)@V4-,DB4WP'T_=Us|M,Ȏ!mwdijaI=OrR?gbIy1Mm_:,퓅9dYbf͑@Q^Я;fs ]'vok#GXpԯ }i9W |bWPf7lU#wEFsy}$QB|6 zKhIO$(}Q.u`bן#h%d#0S :74bYQϙaOdDOBZޏ9ה}9@؂YxmGMT\ւLْUdF yd2A ]*jRH.%| $O<6E3k<ܢ$ eތ xLzr&LOL>KqMOYUiԜV+ـ%f"ysٔsEn^iv nE͖e 5)N$eK%G 7'әKOs?YLy).TcB w Ƶph X$G 5JSmpYaz,ǨiErH~[c1L1o0y\;$5YtU";=ذ+IIj)I>z]\}~?h[ K5svĹt ˒z%y X-6R5j$L1PwVI$;pT:c cA0Qx(snnj_>J((d> [ 1#YwALbM#@ - ܢBt'tehD,^KhkY -dCH#n$H],Y;0Eo/! y1 bd4YA` .r۠ -  b{ТU\?kP;sZ ƠjY"$b< P-BAFx- -CP ^%j`vnXS5+P!?6m, DZƬЫAvԘ- 2bUp@dŀS ј }+Au]"Y[LPxm#R%i%~V ߖ1A̠rf83,sI3g:!,qX9o6lSCyτwPv ̨*ႈ+d_aSam&a;qV'QW975+ u8 2IjEX^09WσG|'ƕw:yJ} ܦUO;\? |io̒\rV 9`<"t:.@MḚ"^LNڋ0ˡ)hʎ`l]H`m aOL剰O5qDR-/GMg} ObX7K>Z`9g>)= lL k\ȟX_0in-:nVɍԾj=ŕt:^]g*C9Cot!>HМV:>Pu[^[rҘ x !;_鑍z#ޜ\ C@1 $=Bed!wL|"})H1a[wv3""ѕ Y|M玲25*U6To3)gU[v=鿥먋W⮯Ӌ3t+1ÛjE- 1 OM`0z8 >bp*Ja;AGw^v05=BuujϪab@u!kװýj۶_V[Wr{I?;?ڏuokY_dn {th j/zMoG.94g4 /xꔡ)ޓ_z HBJo/F(B5b94I8?nK]$Cie4(wXyf)GV)D|EKbA4%)/M OmE^YcG gTz?^S(fED(>~b!t7k;^Xa\@ؐC}\YϟZgyC:+ȏ"V'vW rx+ D j?o)U\hl# 廈.`qePr5W6O|s+dvLD2};B@Z(@gLۇPEg=629obX; ][q]8>Q "O+"`wOki"c@ 12gl7/]Аٺ1Rr俎K|6( +N1UjS ly99P%g0ŒP"3a5v*At.=lQE2y!&@k?nX̰lr!19E/?GL9i1% _ R n-t)ګ w -QSe1kT8bSj0 e˿2u$yr/15bU]XFLm~m^ai<_՟ f-WF&vVPuEv1]n4rm1ɦ:t1Ϳh7kgvGu*GwdUzE:;[Q""cr]ҍ+z<Fý߯3PxOŬ]:pI ׼0?NoNnoN.խڹ#/bZd)}ý+SYjmzC/om~aάGfvsÛ|opD L31{n_[yBsf_+  =Ey_?{;`]>cxd C?ڣ C[5;e}|fvjN;C6QКW>?HpǮpyzI>.^m)4]l!Ȕ +xm6[;JnQ_sζAFZ3wP?&#NGɥR<\zdKq__d)*^| EZM|.UP2_E({u~Hh!_~n$l )&$nu~{~gk};=`skj2^$7Oeۗ&u3y ME3Ad*r(鿠b6J*_'fîLIAH/`]:Њ_>׸vnx!?2)y4S9^I-/1$؝YvX*.%B}'NJအ櫟;@:އT:.[SM3dcum9x>k`?O g{t.6pLs]mrsٺD9oׁScI~we@ɱe^`'vJrXe='fk ]@:}ʍH^Ͷ1ܕvδmƏYY q/Wʻ5ojTRoQl}.w剉=@O^vuSƬP{p7A$?ejG mP+)Hvhʰ}sDB8sZv’-r@"Sw4=`o=ߟ(nzeOHHQ=RUܕK=Bgģ XF$q!BZcx++$Q_w|,я?yD~N`?n#L;=M/s^kXW^ kZ #!g^ PL.ޠ|cd-(_ mte3  XYb^_t >?iu^db#aOt?^;J@nr/ZV1P 3] N s_0aZ7nݠBd tg|-_1twǿ'};!┡, {Ut"f`B`һ辶lvۂ ^HA4j@7ɽ~σӯo&ٸl ՌdŽl_9&8ULomfư7^YH(n6o7Yy(. w ]J>2AÀBwNŋX>^k]ka"Mjw$ϸ0~w` 7^l>*_;tOC"-V~;̘^e{(GS7}6`5{3B܀hۍW7>7;#Xb]>ZI*btq0Xg]cRts=wz|i7N[j1HS21Ity8[%kJu}zJp;ݬd,xs-LkV{wdس<Gc6Y,쐐P:R2GebDd~677:;4 hpS_2M z^6J;DB}co}ۧi \ץ*|gvV_;*U~dMʥmj { .xMUuݮtnx07<>gs-T1y0CkȚz1a{fm^D !,NjlJ 6ϲ|2r޿0xzϴ2;nqoW{-#?CzԄ!_ӝ tvBFE= m&~N_YG.N u׬T,衫 OG\˦:v;YnX(/)pt?^+꯼ڈ?J!LoUUve8_~t65kR,PQ 2>!۶pOD> IAL=^)X-w גƴaZ;v,_wD>";o|a_ ꃹn p/`+}X(.p68a[#¶C7;h\Ȥ W sexEga&mv/Mm:y-{q :O(~8ɴD= ݏ+G}?,_wjNp US/Nsdn%~[{?ۿ~b߅OBCMޕ_u y XR(@s*E)y{j:ipkRӂy e7By$RMfDc"p+WԂafdĝY:_fDj󩧲 }7OykY_Hu![cnc_gQq|>'G6,1{}JK<$S%R~|HY:=H'o,}A$)!tܧtw5~$!]⽾aؿx\'ߔx.Ƹ}h[e3u[PJ̯7}?~/8C>hNX ^mcM|o& "Js T0KHO~/^Ou 1uqP&/Jÿ{ * zv0uc>U}:6]b_}SF̉A, \'mn27k=&Z|ܞ [cpxDd;Q}}J3}gn1gtƓ1Ƽtǫ{QH͸z=nGmSᷧWNC>E,s?ߨ!E F(2Zl8uC.?~%A%x,PB Un7/͟҂ֺ?\\?O{|h]í{r{_?S|q^!BrW_Dpio?T`DlZ!R%7B!)u\{߭!b ?t@lu=;t [ !YH bA'iѱYgM#ptt {|gn7]_I>r%)E?V"{r~:o ~?xP6s?{?ͽj{ۦ]fn P@?qh?- o^Ch=2x/V,7k5aO8xDHyBnwa{2ЄYqsä{ Cdz1<0׷WV^G0AvG#M+AAK_CkKm(#j^i7qK?4xvƗ'IӆZ(<BQ o-僨ZJ|JW͑;y?&DFO'h=F?,FL 3~Ihb^qGBβo(>7Es+eH~98@IWO*+-|7QJYW o4ϵ嵚e1T&K3ı\ _B)^OQ&fF Lp#wH,`uMAjX)#kW|GNGdKԭ4SqlgRppju:mGenM!mfҴOi6Ža'r $8iCFe*<6A%*55, -ELQK sR1urU9SE'./L FJDwb9U.%ȁrc tu>#(U $ Zax:| ʥCs9iKm>~o lFZ˝4,{xgr"wM0Fڥ;דm_yiIv.rGӖQd2-e\JAuQ{umlN1^^3EE9Wd:c"Ne)s]A9Y4SO/̔ []׬۸)8tĕy洈"4Oy|FHdr3sD9Q771u2œް,ʱo5kQ01,a[Yʵx0TyX QvU+V.};uX!W9NU_*k8<`EꍮgdK)a)¾ld%g۳:>fTYVeVJuQ۪m^ի0 X^n krV0׍: -+4ph E~D25].F: `Oi:Mam!w66]VFJQ}%W#$j9|DZhLlfst`-Rnޙ.:^1Xȋm2Zu])%T뭘h"*C#+CD%Fa019=I Ar5cSlO;7K*4\zJ"gƢD-%T"'GuϬeE 7Xqav~kឣbnZVFPTSC@74I¡ @崳;)R$l.$_t$*(ՔEFBː|R鶛Kթj5"p5{ 5grr3[ٜ 'KǍctW#f|d2rJɴ}a[Rtb=Art5+Rdv$v.覦11- dlYIc#T<ő߶ݹmm:>Z'֊ ^_0윸Juۦun њu,`ZVuj &onPt"UigHW֌SfWKݲW*y][_8.k7nu뵌}Gל8!305|ÝZsk/[Ͱֶ;MAH楣 Kf>Ast5{ŽNG;7#vcеjKT6]fTr{lLf5d։M۫/ڛuvm*n0cK}d'aW&56S_$]l:cT2X/d1eF[<-@4*֥J$1!6ZM妹P%e8ai TTi--@S3-OfR!n4=48ȕ L*ED3C( H)‰ Zx>,Y'HYJO7ROZEL,F)Y d>p೶c{$梩"#TOXTQP4F0֝|Uhj^e5yu;WH83F;U'N''ʦ1I@'óJkXPX l qG*5Rj r}"{5bOsFؠkhO.%Y_Ѽ@J\7DrdFSR~R3.viRmPQ_Đ˶=F b A(+OQuڣ% Wgkɸ1nOikU)]]LY3epmop_E5jy#k͙yżӤWZʌ8-MADoh:1X+-cnܨMqd{'c~HXf׵\{tkfaUk3Sl5ӈr q@dOsT%z&R湣'T̻[ߜAv< 6fIWdU5D|9=9bFEئ).\0͚ \g8nEܮt9|uim56cϳ>v)d D#~̔j6mySYi0uA1#F\+ʴ/'F1¶ ̕ɍKOD/ʲ7ydXL 72>ULF L'+3<$*g%/3K' Z+%0ⰠlZ@"`_hLvnФKJԝ 夙Q@߸)p&̥F[]"ƪ9c &\NBA41F$S0!Ab6yb&_JdrScnljȼd v,Ѻ ohOmz>`#e. lڿwnO]tBi+RY nqB]$7aW²w Hvq z.wd~`7j+w;GM;9]qYɞ /D0® 3Sr^Czƪc+V8.ΙZs;Ҏ[0| .;#w?;ulѸ>k[V•s\v^e1ukwvZugˆ1Wr^y]0U~^afKn|NzdgN+ίrW9ZΚ+r}уc .{N=gS|UdLQy)zRU'0ɪ6#KKzTsTjY/]/î2mpXTy:R:N,HAm8Səh08]@yi]̥ -Q|E[2z\?cC ,=v>)X(~X,GQlmZv;Sj12~$Ld,s*"RIhk 0M E2*T.Ka %!eà֚~6ql!۳\s1T fJ|"\ygaqc Y;1T5 ]#CIclb4yNQ5-:El5M$.uS,DrjXS{hIPX˯uÄC0(uN.)[~NI48Knho{W="Ds95ezsïdkSK jއk"ˊ?\>"NìCr;r?HDjo#"yX'lm<K.?^9y%'sguʖ Gz.. -D^.oI#2H"at6T cRɤg-4 i[괎|W>Sh}+r`)TΏ'œ ]xʑHiEY)eP$)|h.ܵc'sN7p{B Z'"&` 'jRn(6XsQNbKW*"K4E FDG5H2Њm5\Y**<&N,0(%PFN %KEIK &Qdy,[)5Ȧ  ۻ3 1Ri$A3娤,1@PE&8(Z&9ꊬvh SӨA+e|+#G]Igٚz5 UlΖX)[Bُ1W;=u3Uts֗N Yݶא+ aCʴ2Q|kqu 4exHp"mbQiB>Iӯ^C?*@BC w×rc)NoL3#l>ZҷbB_O/*|%E*>Tm]ƜtJ eq\p[6Kםc8\PB].fu j5P\B ~MZ\^0=u V /"ʭiYmk#p޻ š^rFئxh Qu-,\S;2RN I鴜¸A6k&.&( c )&(r4,qƌ*FԙR`th8iɚ%iS$pʴ(PͦHEdDL@T~<&V$V'KF'ӓji$SR#t<9O_*Ťo+&IH[E0B%LE|$H:룥赚KVjO)y^iеi)^?{:B݅*Yft^3eMjmttgzk|:r۶v'aPjM?epqXn}S#{lvQc)L~>V{ǻn\G^JMֻյT s^CAZP4r؎m3B2d`Լ#wkN1 k~\.u託o]^-NmEŞIrCoR^λFMrtQ5f6*?;F1k/ UeB #AЄd!yHvi&Yi:lx̨d]nA( Q6#L%ҥAӥ.JD- J`Md .#M ڎgRTMY2oVJc⬒suכHex]au g[ێ]]̘հ%B Tc/8wN׬l1M7e3ɖcs6`GU#Ukj5,m_a~EL;WndNyT[Eq[4Lk;0ۋySYA7sk njXO4V3QmbxHYV2_ݴz8wAoBQy&Wԟ do~,L/Haψq.>/Cq {N[乺3΃N+-|M;͘&Q6-\߱\oGRkuBkMw@Uvޅnl~!+?hʙt9ooYܖFvm1%o׽InwxtX05x&Fև=8aN4۹٧rONABUm3ѧ0 iB`$9hÒ X8kґ˜ikvfQw**5)BW[YV)Fuĥhh&!Y@+1+i1j%px_K$g>Ŕg"cMɪ"jPSc՜#@6/)ߣ1kQ63Dn^ O-Jf<{@ ͸r=2_jhQ )BŻ|)Tndqz2y wF=97T|gsȎ넋_Y'!o5qd oV:4ծ;M"hlE;ˮIwh՚+^872h^ltJN8(^CÜCw,M>C]z\|Od]z[mF]fڬwqYY)"27+Ip] 7fb x#K;#LJeYEAص cĜ5&$q֛EL˴cKdl·^(ZFN`ZߘRK؁Sc9a˿*7*Jx#"Nw&6†~iK!f$@0aKk[6h}`P$ 0aCf6VaHKqྷsN@ @a`\ЈUo0`8@@ H6\ T$;W[@H . hˉO"@ Tm_ʀ!xj۽c01;F0a!:@v `*+p-@!@& ":nPhHQH~]>!=g%sRb߈Thoy0plL)) [WV.v3|JHztr*97sLP)a $N?+Ra1%aD}, c͉B ƦD cp cZ@\ljRES"2aYbiii)Lteub1)H(Տ)NsPr I_ ` uNsk(d،s窯" +ޢp h842Cl>#S8I:Ml7D"kInٿJ56|}Z@>bc@r6 @^ȭmXzHD ``wVgD)2 i@9. Vʚu( Fyc\ȥ#㜓DQ{ y2"\piX3Sژ-PLʖ3]>GO M-*YZ T6ɺ|pLME]drBCQdBDeQ"eH>(ZH }=A%HO ߩII#cxDY)<6~&lx7+|/b`B +^m~ք&ƒP }`6W!-؅I#̠@B WH|X#_eO j Cz^-\8j`@svjOf08`B5o(G):6@%ȚIT$`N !.]H\Lb2 Ir `MީQL"# $$xQ> 9I4Em5(7%SlO$REJ&7EvLVR\=VP`6eR3u6\+f#,F$̘-H)Ix?$)gY1 Ax f͵,WCdM#]ͧ"9H\u@’Z& = .xm6KotJk/" , `o Q5OW)Rh綄66 5ăcr hA4-cInLHҏIJ"*tnyQ9TG$. J1A|r˅Q N,&&RKHcDy xaDHy2Z%aɢ ÚB.LJ\.$;--i)\qtMBIkjH=J1!Mqn qXʶC!e5aZhdhb=%0j>cMY+q*$1IS0_^va|%S^43KbURǰbӅ1kmIr0JP7[_.yFVycCw7]<.\8r ѥԬavtc\3jS^b:ă$< BJZ$+}oA3d҅_mL F1׫l5rLD$)FЄI 4߫\3;fh)^4L: XTeL6N2F锞heI0{yp6C-=`Ol[YcVihզ<?ĊTZ !VmޡpM 9ܮ6zCᮚp :֞S';\–SL-!/K+`"Cༀ@Hȹ@1a*~@Y@Q98(_#>Y5"PbmNA_DH"bry^RZcPOJ<ʐ I2sd#l<^a25Jy$̜5*+btOH%K9>ԙx"Q`s72:h'$tQs+Yګ]rƠ:LnNzUmXFk~=K\.<5$Dr#TϯJ~ `;s"2q$xa !V'W梄4yI%B #X)W)`eqL1HҲ..,4MK߿O +.㹢Qb1,AZ_!/R+FAp ͛P ,cejA]g(#6zZO?D,jR̴: aV>R3p5i}xvڹQV_0S.U^ma$!'lNl{ :sEok5D!FQeѴu5k(NX 'A"e1-"IeXYQ%HDQ ̤$ jBƒS"dṐ24æz3OR(61]:hiyR3_Ѐ`6i Te@Z`Aci4 %,^(M?O@@c@MG`P`d`CP@@ـB|;<O,--$@98V Ngab<emdb7Uuh`po`ew ~{bo6dbЦ@\ܪdy7-82=1( X]= \ {My:c+7(geynI؜fYɨ Y'S =(kӬmӚg.{(BrZqWLJ2\9TfZnf4JZJ5;q 6pqALwӹ~LCMW 6vcdc⥤+켥{YCq,|dQBblls'3 Meuk?O8ofXPȤvs/5\MuXfx"4*@`GG@g[U !43 #Bs-fE$h~5gbZeFED H䘎330 Cl h%fJ`X >}$!i+]`R cYGpMnV+8Klxp N34%Qys·+w[w֥Ԭ700%ge;neG Ϗ:el.(7912wʅܡh be0;12u[Ӡ;w=O5ϻu-7O7&7؞GY3UP ͳ%S:98ŭ2!M8'A6[ޫ%LDɗctt_bU^S< ̱vy;|aQ$8ᮼv\.^a+xr}jM`#5A;g\-B|dt|sZL@:`D3@7+CvJv+Pڳ/ Hz#82d߀s%P<#kϭ>+kth+(tX h V$p"J>^urp}nm lƶs tig6m L ̈ـwVZ]ѢpfQmlLDq0"@00(.*5C^22nCFty+ȰV7+]t5rVW'@269@{!D}lQLR8) ayU2!E9QŐ&U3VDu2U4L:3!zAc;97LMKNUIkLbxJgzQ(:eqg"`="Sb1B/o]WNsRah3,,hse1Ab8 0ob}٘i\h^961,9SJ*1tJpr*s6O7$U2wm4Q{@Z@@73L?GK95g,(" 15.@W@X@o@@@q:VmCX!ٳ;O=@F@D@JI`^eQjkJZ@6?{(vǘO  yHP;qšXUNJʏX_6iuY4$RX3O'\34A6b*YXJdR4v_@-aK3%DK"e-ɨtOQe+ױ7Z3H@:^L8Dp2jbX\j"v;M@3@]6Q`0nґ!zn .8C! t<ޖ#Sվ*2/.fj3̓/Yυ\ MUE[;켙iPcXux ~˖_1rIӄ ) =MIg߫QU 7(`r9_[/ q80\qz96ѠA/lo+\#$ ޑ>(@5K9ӨKn1Pdh7s5V2 5# kL\ .&TbspW6̲#P $J;45'/!E)u}f[Bb%+1]q|E/⢟/Q6}J]Ů"_^E__%_EG||{hb{//0w_\ _E_\.__LE*gg :ॲ//vlkI__E $.__O_qJ2Z.E٤ߍZ(^vNMNc+crJE?,$ʳ$z`$j\i18ɜˠKOGUTʢ߁.P/$htc"iezj{WAhXa)Ey^ dz?TvY\MacW#5+n/N*ɐ4; PEOMe ďxWÆo3P Sg~uf 5qΠf@3TLkS)Rؐ mj"YYJD\~)S9N1(huI|Di;53!GJR#Rq뺄13@ 701Ze)'%r99m2$<> Ɩdrlhؚ@6\_DkʷB|-&Xf,m-QIT(}կj6rv͉Ȩ%s|>b/2;3kJMJc&_3; i !; u:ϊ APH:ȣt8 vb72o$tl zYUKL6aAcgʽԁW`~ _ hhU\\ؠ.uD&730&?[!=A沽}*( Jԍxs=7Xꗝ=v'&sQ,N?op}>$"" Z>FG:t dNH'YCH9:t3{ #[ tDs1/S }P환&plLF`ɉIET̬.͝u Eζ7Xq-/-o_Y鎉# $X@s=Gub>{ֳpӧ*Cr͊#|4я}̨)Lpq H&.+|t;buz"uEeE;ر̪τL;kZ@чC.cpkY"گH]?Bн,7$dQOeI {zwz20@ cS~8z2 Z{b3,գ#4m! ɋ(|z'?D)kpB%5k@ rkt=9|G0|5/C"k֓Jo^ܝ;h YGDOovm}ض~l?pǔz٫ $^|&Hcd~b ~GWw3 Y~W17|ѭ.^C~-O:d= _^wRgzs~Kڀ8ψoAuk-Ƃ$"Ad |fhcaMՓ 0<8hwu>C;N+.*ƣ,QƇpd  CYD/S" "G"c 3c gwB#DCT moU [;>caG{w*BAkC߷ryT0FF xjYG|Ogd?Ŭv~eR+oӾay UҺ_;xKYY#qʈ67ҚM=KZoL&?nRS_ƒ}7`NÊK(.c+)$A+ <"Շ,h/Gy~֫փm6֫;r6O,{CMJw19N3Z<ӑO8MֆJZhYM2˘8&dkOC)e`~ j~Vf :h>j8u$K}=ysqd~z?}|K ^E=*&^&4&|J˜'?JD32>e|]3iC߿n~O6ֵN#S+OkfK a>Bc٠}Ab*Z"¤$2sRQ:V N'J!C4[gQ~x>H=zB#߀&F8jtNp$lj%ެp $&cT>ٝXSK'_J/뉕 mOL; |Uݟ "pdA YPm{LBBߡww`Z HY Oƃ=hV 3xsVZ~~~FO o@\ȏ!Оญۛ7 ;ع< <:mE h:I1bh JTDe?^?#8O]@y:W@*5/T\j 8 GkbP-fq{hN^"N1ti (@j8`ֵbgx'US<χ7 rT4yy4yQVEBJ񞨔t1HhKHo|1ph3E@3nFC.F٦nGωC6gB0LSv%Cfܙu`/9ga"((!75M6̔JfnL&Bp{T_pP^몶[ծ94E&zXnL2I?0{vݶvs[o[l *E |"VAH2J  d>+!,H XzNlc 5o y}S%uK ЗI'igQkG(CJSvsdv9rΨ5á4`a;i1T3SV\@Wmf7 ˧Sj5 ٥ddnQb1^j4gs?1zR=.UkrңJ՝\KظlyZhAeC ;fepR o3`Heoe#t1|h5 ke%%d0i]ƃ[~-i0mhm Gx^9E>ғu9A1M8蚇8美։x EGP\ND"*0J*)b2ݗ*2&JkaU*mD6!JSUP%Eu!U"W IGj(<20(O#- #lnA8i8hY0ԥV䳵iiYZ7s h^դk zSuۗxm#/R_a=)T~c?!,y3 e[]<Ƶy`KN^LRXeZ>^f^5%7HuDWL|@@?^qc1$ޞ[Fm.0w܀.2wMlLb?6A޸Zo2 r0@6\.[]ѕ/SIڶyػ[G"tvpj?1g1`K%ֻX6"q+I"<ܺUbY䪵kQU,m۩y_mQGO+0 buɭ<$1DFVHou7a7 e|,K koc ˫|ݕKkI|uy5&aЧu^6 ?|Q%yt7W!-Ё:Mzrs 6sD )9# $ @ʴ"J OK1\:{3 Ԙ~=1Th~<*RA0QFx\h) VYox\+X.F ˺\E(Cb_% 6[M(  85fJ/]cBQ 3Ixfn$@?m_2{ =!…0̷5wN;:OJ?BNãO7V (N{;hC.e<ĝo VI 2QoJR>܁r}<^[Ɗ-"8؈",Dʤqh*{ʸ.cS_2h7*2 | ףD]!1hEMH+Lcj@; ,^#4OLqz UO_T6 }: RvL۱8],aH"EKc*UѢY&qn iePGua:PF%(<'4V2Z#Dl 6bD)>=Zb7 dDbhś BfFPQ EIuR4pR$=Թ=NX.G;ȧ+ݥ$쀘vsC{;3|v7ko0V hv{mn5x@9@4ٚ;Rv@ض;;d!+X:d 4B<+yV"e}mDi*6_%1x[2(@W|K"ǁ=Jycbp'ofTv>'z 0߉x]zAcG73ɸS$'hY:%A@*ro:rBS ̥b7u9|2RB%:cl]@[j>6x.ܨ:` Úϝk7wq#Br4g͜ewjA/˘|O=5?fy޲eN]f~˳ 11w=xE.__{W[.ce!x//t$KKz 4|!O]* \z8Z{޳gNnL g]hЭh{8a$t`*#Dq,U2E5OOﰏ{޽.oH^[Wg1 y*=Q )DrEP  P;BP8RMMxb9'T4̑imKE;IoMYEW} ,0g90)@N!F`J)S&:3؎"[!?'tv!w!^9_KG u>iIũ6>|N^j9Жu}V]sKDM[|Zg@^gE$ќM4ܔd)@ !UB-`$*H=t-=}&7IOI-dM}W+6ErN m3&j+ռ97O+> X l}ٗU]OTeX72L-DסrGG=& V\LԒgJhdVПSε㹑})qmY|bd'+D%G^"vga[?eyAb'z}'^D~+=_h4M6d0T~洢_.ȒwQ5g=vy#/ʕ%2 _ )w̆T{ql}Rt`\9V m$tZ]f,QcyqĖlv3Gw?"D|ф)G8q(A h5:%K!&-BⰕ)JrzSG7TiH;Ƌ2 RٓNhExCܕ8v=O9ߌŇ8ĂXTϘ Xmn7rx6PƁ<~;D8*fa}uO>%8?y=W7 iQǖҗANA NJp0 ^4{L⡁SEǍ K.wHG5>tR)š{i1KV+̺;@>nSe1ѿD}JdTȒ**ͅvcM|))OIGa^\b( #q^'qNb9 $}6)&rȐMUK3{='2sa":ruj ^[LǭArq`ƅo=P(5fj5531 Aq;eMP8+\|^ӟǃ-X\6[)`ݳPq4Ow)@ Bx8Ј*71_[-RIWq<;#W(+F.O6w(M4nƒA "t֌"fyM+ dI,XVJώ?P!h3A,1SV/NyGm` >ڇ=D%*EԠ6BK%Þz5+V햆]h^;@>o(,߭ӹ ;*5~dtP:hh&oɼ>$ >d.<-z0|W:$K9Z˷KS:8JLK\{YrEp"4?a˟@KO **.Ƃ$*ƙmHjXr xқsS*$ms@zrK pr_VhQnNѶj'3J9P5ψ' ̨1Pĥj#^`ħf6Lrͮ'^O0aI^+}'kϖ}!M*IŔGrBw/Ec?EIxoRZK4M8F:ouZuR..HD[X3<<7PԚWBĩ'8vE= NY ܣi6)]O|(]DRXM a~*ùpb`BV2P7%z6>- 2T2q沆Ջ,\Z!j*G3LDgx[H%̨PC@x "4:ShP( )#&af?kjm*Eb(*W*}(޸enjȧ|oeӔEZkpIQ5M!ݠGZC1=O4p(aT\3_W6:Rqհ|T*¨jitY@͖;wwH 6i(A-XDs~yą,@BS$3Y/׃ij)2r"c,DQ8 9 P,&~+LSSi$bgGKS7?".gz-I\t;Rct:V龃Dq`?__fցR [W &O,.o|NL4M7dn8K kssCۏ{{*Gqht,^v@>Ө|AFPKj9=- tr 9-)/Ԍ>i{;ZsB^ŁbotJ3-9&`d7bЧV螔~Si 4ށeָ9K9[zgտ״.F#51bj0ҳ6\} T̿Xh&#Z-B(55dWmodv63͞S(SRGȧ$OąbD#03Q *5QY8{vY^gߚdv q<5)eӘf4y]ԂJDB_1ofS!ڞ&'rѕQqR$HI>ͥ 'vdju+ӲH2*SޭszpeWt!/9v-f\o fA/P44@3oV 30qF;Qz͍l0++>$ B"jadHkIu$9;(\{_Vwݷ[ދeVm4onnڷm/ܸŌ+H!8@ChHH >۷nnmڙn̆J2c`b$B0u0^D&`NA*r ͪ\7{ingyN :P!3Ӣ-},gv[@˗ [qdmquͷˑWh /U沵[&YRP `NXq mF7jlUYO!Xm ?ƻOY<NL={YoMхȞjǖZ]0:DꚲiiϙՔ@ )o]eumͤ~^KЬnolLwsEXu5x} uL@j;E\F=%-[Z,lQ=͐^ Q,J fu%ύ>oE+Vtz&|p8e͌Ol63jf д*fxbSpUHAз?]Hb-W@2QǍT=+>[ZKsD^xVbW4b1n⊄@׫41{l:nyOD?1*dNa-֊0 #׍ҦIe* "&KI*ןףed<-{BM8d곡 Jm' wzåa V^X5!nxcM5i OğA̰͂vJQ"żSDM8.F:Oj3iC) ׌{R:!? oW,N$|~Nw2KϬ88S:L%gT,.l e;ȼK>}ĚF _OwP81sZ1i= IӀ9 e OsG3nU1ΡN{QZ S¡D6 n$arϘ~eFҊ̜ud 46rT;On2,I,{ByqFYU{x'Eh R1TX5?ORSC(7AXgW@:邊W ^.?Ht@(ƏB%+fi)+䕠vMUlzwx].["4ȫ>7g7j.luB<v o+E{{3B|a0HS8F |=?dwFޱBP vEjC%d׏/ۤ]n=F\/ޝ~t;,r(n՘{,F QS(]~4҈.L0]"%n5Uz%<ٛ1tC*BS]s."v 0ޟ_lI;7<\1l2=xxԔ4A-JkVmRi_9˗|CV,;XJ oNF8էKq.A ьׅǹ`xr)fi8עbќaVaDȈr*YlX|F_Z6S,Wenպ>K$)KWvkI nY)\GڲKAX0D5t^`q$sE2C? z A {19̽C{񶺹qGՃ+!SKM*u˼LTWʝjH&mMe8<%w$93ZO%8k_cq,._ vo4'#Bn9-6sj*ss\MDuT{:95}7#?۶*N43w`q)$ߛu[&ixyz;la}K7I a%ڕ0㵆)FQ5•1Ҝa%.``g 9gƫr `ۮOo'sK$}iaJ3!Ńzd_-֫ybLdԴeҏ@>NP\ϴ  *ݧBZ hZ'H$-.5ՌJ=gc}${LeXRy"+Bѡ:Jnl|Xbk&8&_ (}Ѵ^ηA[6R X-BrqP*T}tyiŮė(莘 !'!9Qu%*ȩm&@}GޤLXmy*?ɍUE4 eCdq x -a@W=7a*\)i0,$P9:(q+݊w)2ƾO_WH̃ %aɞWG^Amɮ+vO&q;+6AU 5nmm/_<, Pm֧դ +R%5ӵõV O5-y yT#Y x΄wv$VnQ$* ΑC>>0Hxᰝ20cdny[9XtMEgԪCP/K涫2 <1}OʲreT!f!- 8źB\'P8قZeg-?KMܟyY'M/gxci\%g]>\K}94SrUf鈂@|`^֊gOyہ&P"ET1< v4FV8ƫa,)Nm!8gu_(F!Wx-a=m8 %'uI6qV锅'FK|Vq3} Q`0Ń.!a@,wnVҌv֮XZyxˤ1A NJPʕG^Fe yh6ؐ(WKG,2]_RDs̔2Jj|7u&6|n'p͒/qʀθs*˯y.sbu ̂d)nqŬ݂$֏&^!X0[ːh) oTlmU|Ê}33EG3~GChg{bf~lOZG8aJvܲlƯ3f` 猬303E#p5/$i_Gڰ`Iqm%gX-h`w{ۉXvC "/%l9-o[lȐBTV$KI4~@hf nڶ;-jcۮڮk2e R(W!@PPV"BHPH OIK|?+|bGHC遙;p2c DR]\wQQ d}zvvfg ((TɧWb糔5+쭃)H6c& C4[#fgz hkɏإo!3VkQRT;xE;hTQtޭm C :LkWcLKRˣn[bXfS[-wڊ^@ͻ;^UjlbD[C2` SB-RČ4[r݉~ vlq5HVb}2MN!niyZ$ܷA;}CRnBZ.ZHZr] ̓U,u8f|./jEQ\tN{>J:39/sk <v7?/ \JfGY^l7gIl Kx-h O%oGaLbM:҇]w{;3"BRf_ pAʍ;ՊcU;%~o[2Ӏ9Ihlo6²;OyRRHpى^;=:?o lnbE΍QoGNVe|̣UYFUA_~'Cg"m|֖*Y}m+Zg'339ϯm-0`S:HՏyH>;ȁ;Ue߷m( OH;=(&ϥJMaݺ2{G<Ԡ}ZBd#ԫV#`ƟcN-Mp~0!K+MXۘ0y963vά.Rp ͢n;Ι-K%M3w KN-θU<E eFTo5Nb \kPIFi Qc&'_G݆Y}`ҿyRci :cO󍳱wZ>r=}%3 rP^_l*Swe1.~'X+|BT AJ+߅P<3+IO= }vYal`0=S!l?c[:x7X5*ymq+uԆNͭK3CԄ֑9? 3xjtmx~mEj }P)8XbƍcWt7K s|rg16˫2':K.J®+hhZJp??E/psEg[<; fm=/4hp 4b~XTMhβ,q%{T7iQ=ݚ PK+(ce*.3;Q ;ŦRxn34Àf9_ HGb$^e:]T)uk!Hr9Π\ ,|S$`b0 $$y-;..Rh\ yehxUSiXPT }͆!pB4p< ռմB-pYMumb=;ZWO+n/ÎJZ@nB-m̨͹dzʣmOҤ!- K<ٳŋ pPVt-h||]{C{`6mKt9+%}h S%FGi''sߨΜK(Ef‡OZ5S*Wб(]I~F톾qBP Ђ %O4EH)CAju;kIogxΒv$ ճzNpˡy; ʵB(T`td'w Ͽx5Uʺg5*.MmKCnhg6-~_w5Ƈ-m3R a>P[{_8SXgn%ȍ<( b#ȁFٷ'_k̻B/؁aOT+s=tpt9!<~.i\*rj\ T S?ZU(]a[VWzpMRS6ܞ*h/.B%I~oCH̊F_(ݷQufD$N'C="f³[l2m)iNz\-@(AU|34ޒ8 N}ǁliPN]q^ dڣ 3蓬.y#F_$KpWKh!F ۗ%Bd[38ITs9CMm b\,~K%T4KmfHySG.%>fKI ̰:Or#aI$Ҕ>sI FW >Gxe=BJbbiZA2V("Ixʊg^sA0FAmOjKK5cPƶTFA&Ms2p5fݼ\=41c7a|~wE;^0%:x"QHDcH wRaO=70[ʞ>l7tn챼M]}ׂhKĶ~@&ߧ Kay+ir×E 5w%#G zց| [?Sk/_GxN3`v^X`e@C7+ñYEӉCxiS<3RI,F0H #iȭ(4oT̖s~%?Y6M˃_Hw?f#Iyɬn5 _^p=Dl+d0DP˶f4!GpR6ɗo(\7~|TGT]ѩ:{wtcn`EgSуiC(8&6GDŽk{ rk-%l#~QXǢJ'Q z}Yu6`6 4Lߊ)0Q\FI:9}ðOOV5"f{^<nP̒8T(ZBI2` TEZW|YTJR1s68?33rz 5[9^n^)sx#b*UɑziR"Z^@*8 ƶ+W ֔N )i/ lHPݩ3B8"ԎZ2c4\e߽%vfWDu[\'܂q"5G8﫬/YBrl|UCy _S" l$ Ҕ"RXᵴdlv߻̪1 z8@j! 3 tHQrNufٽ9'q'QtdW3H#[s##>|R@;/§G!aFWldvb9{4.|@k8՗'3f?u ȕt$ P{dy\aJ^>Ӿh;*E 8UVgrN{*n8 O?cNh $I|'RIBn9!vClfUaZzTUV bkZ%P)vCbIJ`4<iw޳r+?22|Z0V]P_}k[yW~Z#[B.&IxGx) ( z(喉~ V}.vCGɀ 4Djr4eGy&,-8rJLnUQ>]dn `zAK~$S(CTЙ+ GzT A4>::5SvzOXlj-r X9e0S%9Y&eQt|k{+Iy&C2<:͗hya7 ʛx*FOLJ- LIg Ր[MP/ܓ׾׳H.ٍFU%fbݬUTÆcPCˡ{SƆꡝKJLRl ? jWf&8@m@S^H hX7ńUxw2^b{: UlG&?i <:u$l['f%ɲFb|sȹll̺8c?=h4۱>ZjID`+.&SVX.r,N.K`jAq%dPӉ iާ[D1r]tu T{ۥcCڽ{f'0^;Db!ziV"nd?N +2e]ڵ/QoˑP7j¾J>(-oFM2G'^=]M/No8H'J>^A;d{8\lE辚0q, ˺0 q,A%"caR?3q!lH7 !bH P(T^& HZ\6X#X&"-r}Q)aI P&Qڐ*RэV@/ vsO/ҩ\:Otӏӻ~*eVLEXbaVѤ(HeygDﺒjKʉt`\, AL@R`^Vb42ŊOf4oGj%PUȑC rRc=Rq[zF$D[,.IVUdTRY QƆVǤ{jbj)_'rZUmMی\Q>;P|CB[HhLD]~ N'L9\nmªq]zA_5d[JG˴,cac?G*f% ө2{'l.˼;OT6р`o&⤟6n''z@r|M$E$<0vOP O!@;{`I k/cZ*SKs + o/{ua %k6{D 0I>^I"=RD/5H=`!:#O#`@44F4^FPA>HWrs{v7n nݖ}V瘕$Bb+b] @\ ̜AIVriXF{)o{"vkohTrCR˗Xmy>+FZu9G84 3X4wwZ\wwnF 6+ T# DAj(@kH O ( $ 9'=@Anpmffr#j|@[_f!nAnت9*!G;:vA؂ U,_L#B-Aδ$Վ'EG~O,mA&CڦCۡf}Hk';"Hyl4Gglخ Bc4"72/ &FMǝzRզT,ű:k&!2-yJTNWGSKj`j9b~c;v)kd[)k1UզQMmY[]Zk} D@`hFМȮ3K]^VsMRs7X]vړJ,-5Uevv?J8DV`0+0IQR^ Mv0A6MbG@N0]ホw+ugH9=(Z*z4zvds$+э9k'ÌoDvß~Xdq6 4QE&$ZĠp.Nёc4WG7 kX$X$;ERhw&=t9E9 qe8ݡj^4,|!A"S& E#?:LxQM6#Ϗ|?Mw uƘETHܔ'DV{oD8`JT,8SB~=!ػBG~zlR DR }=U 4ɪhI ^xJ*fݚp, dl4^I|yGÑ)n$faVQqF'n WL*/5T+a7QȣZINj8zmWGMN'ˍl AHݨ?OV],\?i*=jRH};5 eG?O>7fpfH~fCdڥb?r0śIonų8 R.P&t;"Wki ؈s{_bzH͑)J 8OˆrtH%-:= EGH\oh#Z@_C= &Pش >t>淞zD~v kKq2O[F:ZX#SW8E;B']8!ÏCgx;u]<4՘\,7i8'0|@^bE'"1@)@$N- P9z~ j jWUjAڳ 4ۢ4:Ⱦ?W,TR8ghT\4 "w}e<}V3?ڨ˚9jqeh$W af2fpbJU(8<|YWb(mּ(i|lQu_=F 7O:>i{M/ǐI[:6ֱ>I-r|_Nk_/# !vk-Xﳸj.9bgAQV! Ӫk?S[%e&&$10)%&cX$rbK`+$<q/u>l_,Ԋ(ˈ* 8!,'/ Gr@Q1 G{D!HjK%U\Yp|UjbCA4B;S'.ة{>A5 ~^*X&@?3k&~~̲GVh{.؎7ڔ><%s2E?]F{o[Ndr+f؄/ GM|eQ^: JS?;62,U/fфƦrΒ%&h䆴Uܳ}!ת>?t3PΎC>*@/]n24jj]+iKeD31=w[iq!+8B;G=iEgakq,4zSݺpȧzfӏ6=5tiP RrWD~\,@p djKgƖZ94 Q+PkR}Ϸbݙ`S4$?'Z:]OnsuwTpY'W}G5Y ФrӚ-Y&ĵW0Gt%NzqH:2 pepy4WuocfIUB+muJQÔ}QѶPmEN 30fۜ8n-mVY+Q-bU™{~]G[ kϼG# vYmKrƹ*nNS; 垏;!Z|Ă{_3kݼqWcuC{HG+=T]`4 $7xe*40JOg u)*AfUMi&Jh3M8/7R1;]S6=sv$9:;3,4"hh":w=5?-˦'W"b( (I-L`Ig[m.{$Q$ ,X`+,9 cֺJ-Acj$i^ V0a|~@ |=n0VzzLjq\QQ@E1%:tvmv:Y#k[QEs)M(QNdIka021Bg'Ƚ<(OE;G۟$*j̤r9 q2vLSk{%PMJ*a +ZM cu"[!b9dAuxr⡙ Or#PRop`V 6Y0w,IR$S@1bJ)_=1 ƓU(!jCYkT*;2rRdYC0 aCֶ-0f,w+?@p>V(׿fYόan-/ṽ@ !@augcr8%ZDŽt2?zSP,sn@*C0nVtyOYmAtPˣcH~hﶤ#'I˵(SVE,elE:sRouPgV]܅͒={6<.?RI^%&#jPXE$$0MJAe`zQR DCS0!>G/Y>ç38&}L&s܋$bM+L$-x0QW©;v7XP\~ 1bzH]Ok;M۹[TBTyY% 7#'u#*%qE~lILgo"t4 ZK;l?\pi|63zM:4j.f"ba55K>%x(RHŴ^P*N~#Wtcy4y)Ғ^,~Ge < Un`H؂)YR Ra]/}dwwZ:5 q#srݑ(i~W8aVS]y{"֘gKJzɕ͢6ˮ?WΐlM/gY@xM[o>V>c;vw~ۥH~c,I2q%C>7J}?߁ ?m˧QCK43!:-~OrX9OP3sHZp}unvz')qhQK#A)ءA`t[?cʍNLYfU2cĤCJ`TmϬEnT.c N]ugn F"C A& C8ωr::oŪ*K>̛+>`s phnz}^g#64@P{@&0lm ܛ98gmqQf82}?ى Μ)<[U;-#ӰY2* C0ﱓ/H:]#!ow35i*$i:DMPjсըSKq_8Sl 9N]+;rQ;0Cq£)E#]5)Ye ^dZlȝ|34@4oV 3r,Y{l%7LMH7ҷ{sv bH /`XHd 9mQ 3~fXfsgf5-4vjw#.APghsQPNv:}C5R݊e e4.%&0Ib߽vv\ҤKn*a %a#DEUJH@(( '4HAmD "$F~|Rdu2 Gdcq vκIܒYnTg"4: pT ;O#g=Uϗ$J'yU! ?H˨"uޮ]>s.d ZʮGD}d>D_boE]2S7HBk4nyV ˥'θӒx䌭DE3[ɦwl~.y/c)Y~z̓ EHlt2 "tD]ʹN7)|j'Ι1@ޫ;JϦ<TjP*pF)z_o xޅdw.Ϙ̡OxP*֊_X][=-!:Tʹ|J["5oUvFE9o:D+%|m dba72@>UjfU)qㆣX]U"`uRׅߙrynckjkrP @:3pR8e\`Lm*$S; ( m%yfE۶X> ҂lPRsTB\κ<ǗNfC \OʲdU5|k't< ֩4䒷%KV8M8O(N ׻€iQyB ;[EBǩTVr+twQcT*:nY|u3뱷a$%HV "&B FQY&J}6˴H#oQnZP<"t˙܊ѻrVS#ĝCU39|R͂rk"/Q2jU(&WN^C2mBTK Lڸ`b/y?QtLPKNNN3Rу{la"v= 9n9'bD`UuX9eWRB'a56Ec!&ޓ!6'ְ;}rpy=s~ju0F}0g]ؔuvGvׅہ\OZcQý\p3ς;*GGRo㾅]°% \3z!_ *ݖjNxTUPpM߽%>$qpyၛѱu/~uS~u ZVw-[z ;쥄NT}; /ڔ[R7q 7V_^͑oUzW 08|d#n阱F<9gKrAhsoл< 6Nojlf ut < g'ř14a1k{6%Ex}wQ.Ɯ4/ˁ\-}6(VOcxןAs}|6ղ^Att["'tz?+iNC"'u./`ǒuq5JpUk Am8# YSpv.ޡ1u|Կq>j:QSbc=yߔo-yAD[C0%6hw9HweG;# 4(-cLf,!,Y&N`" F0l1$,E BI44/XŞ6 Ng^kMx4}ۍVb%XQqcqrE}g6I!Ѕh.֟Q HR[`[ ~x]x3R!'\觿3o[E9lZ -;92TL_{OC ʗ71MN1f`ёfuЮ[8dVK] 6ZF }Uxս*6b5۾u!V|=ed-B88Q,EkHa(X5+l)%ͤкنB{S,Vr4LhJB/{@b4,Hql|tFE6Cq1HKA,a`,bJ KIN,=?ܢ=4qn` Y1t@0"!F%J=M-*qp-FN}Z(ꚓO{έ̖ϻFZ W;0bZɺe##!΄u}vj3pΣCv3̝78Eqw>m}Pֽ!XP7X~w<]Ij ( Vo?w)⇲>[f_-rLN p=N;{z8pX0rwZ\jgX\YZZƔ%2|Nx[sܧZ>db/wˠ7jkTVJ ^UpL( |~{JPύ+7 nϥS17փN# |*U#YN&vrgngXS}a~ \9:gjYuW VH\;noNkwAUΦ%9?7UelܗS1cdiwo0%.|W'́ 1`6ΪKاԉ5^=fRGlOn +=I K=jsB#6'K%\?WAsHCHb6g{[n=OC7Zvzw֕~~jl y>|+OnF "*qWJgc8kmĤ Wɑ_5NVyY yi{{8ꂟ?'nB Ʃ7t}(2Jϩ$w&t@zY\@$ݳ? C}l}%#R M|WTu t HeGPfҕ3bpyVs: z&Лƒ.7wtW}+/?,gjY(o"!Ģb7'^)<߄6C(϶5o3o.E\C-ҋ*}5~RrxM#sD| \6L*Q/|좿h?- S8QrE5xq5Ń߮on.fncYq*AQ+FwsmE .–UGԤGjQA>2aupQ,m&9>W)WFMfP}nbQCk/}w6\l qwgşkƵY橎dR2` QBj<|'}/KDv4oA'JBagm}]#B ͑:lNgi"Z3pIٟrUD]$~ۜqA)0WK!-YIv}AA`ԹЯ16+UseRc5/b̨!Q"8248 }NR,2&Z Nm+Z7zs@^v v/5C[}y+*RpG]͹㣭o0dq S?ыDw1T.L67Фpthe+=a Jǧ$VJV q!at(C =Ks,A9G/=)_xw#+_oclA q~w;)_%e_Xa%" a/GmzK//WAy=mlo<ჟUf{FؖW옇Xkւ˃~aq 8uGڟ)PPvnH/8_+\w:?61} ZH11AERYش/F "AxTQöPUr1b0A UD4@3^E A]Ď2MGxwMaݍUY9n+!b\B`Xfdj |Is$; Vo8-:n[ 6!K R@$ڕ&VXcN'#E3`{{{wwmmonn6mIm *PHjcF"$ ((?E?`_VBFc IhSW3i,*AEIbYOVXkLd)FkĭVbǎ/܉)R=6ڔ SmM`[4T,4%5 90R↶,OvB=;Ԉٚl^{鬌jxs7t~uгHy5Lc y"ǾȎӚEv|ژ'|IReYtR/ab_Bˌ5DYm;=jgߋu;f73Nj&nbR[F3hn&Ej˥.AW2lnZ=51@__@ ̰7Y͡Y|Ϣ DU\om;"8~qKM>ߦWfjՓP9]Ng38_D>feyZ{~ t2] !tHj9C8Bo,td px;nԬ]g!s# l0>B((ka sh;gO$J3x ]492. z0#dӘMn-2R}NF+a¨T! K"`>3<Ϩ'0+hrƠj-2A%KJ%jn5Kn9kAZ#uXj˭(n~H@ Sg}#t"yuSac|UX5-4T.DwT=Y*Y A.e)v (CƂ-tгe(I"4 2olO# )EA!)0JP@ hxiL/j*Go{{ckIJl|LF6G{mc FI}>`?NvXHxp(> *:yqvH` 0gTuNCv_LHNatv3{bsi;W+]3OjCXia Thi2j[1K\ZJY218DxI,cg`&zH0{xfl[.i| C4\۾MLE <Rֹޑ9qAMPvZCmЗ\#eOX!Om4lfJ2 ƽWLVsk1sl%zO.(&[(gxfIFZ E ] -Xⶠ{ID(XŽQCG:%f+AŲ)^,қ5.3j(lgm}-)hт2)Ma6#Dq$B @(ɮ]:0Ŗع'IĤGM~p ^hBh!#]3\]쀉tp/+sj-.9nSWAn/( erk|>mY_ ' r C+l;ZVпw *Ocmf9Oi@eM3dkY6VwvnvI:_zTM!?Kmw\|_:/=CB27./tM<$Gt#$P$zFw.GDa##߉"/|2)/]5NLvYCjs9z6dDqǽaku#}vK^sGgd9QD;+zzmm%kߪXI$~1CFJm0{=3zoF@@b\sc&O_q?멙=Eg"x|@Uk<'о=W<+lMԽ{ALSOѴfpn;jf+ j-.䩮wyaH_KL,5sq GFa#puhO}~>V}"43?Sv-Ȱ?%}i&DJ<=D3 'ARmHg P 'VElglɞ$$ *}^ᔆJ'xIk sỷGi7349O F^_beZE {WB%BМ9Ey@|@PDH_ Jw֑r*J/_(O?…XLA8Iת bnlw9+ԓT#eIpvk+fT{=kr24Iʙ27VW%X`)UW 7㯼Ɲi^EWʆ-n#u&Ma&')e#yU1`@61Qv6=f0_W%QYn6eIsywJfYs@|V ӰAVF$wƠ@NhËajQPB6fXHk/tr7AʖsGs\2Zu MT\T(q<`1[ƻ: caCjDgDDs%"F }!o2{kC,̑@z92*8\ b@G8u-眨8X׮f6sbY|N~w91we@R9zp8ra`NYwȧ;i_ k97v¾0P z,"p ? rQ/=&n*< gСXc'xT1fܮӼŐx<4pqHA$CDQB *5y- ;r>b l4ix i8EV:J>%pr3h4F̄:#Iz$D)UY,çb [7B oKsQVvH"%>-VqG[A>pM|Z<)h "ug|as)VB%^-3^CP΃0 : E: Rbd"ۛe:Dr@* Z߉^ h.nsqRC."+#G_ 6Uwȝش&>)ԭtH@Ɏhׄ踜+9xo;qܰiҊYm7_i d$i ٞǮʹ2n]x\]V' G* N] 6U.b2)) @mn~$QBv6=B0[a[竑x_r!,9Y9:{EJˌSwlv =IcSZR/X?_, 9-332te.n[).#<Ju^fU܁)%#NuK$#9'+׳('Vڞ6N-9U,SL\hpsq,(H-pIwNA$SAȻ8M6}98et]s8;{9 \p|=p^䚚Hrr.#9z{Nr=uvmnhrKjֺ:+^]v0WMMmaWņBH5jST T!OPA ,ww^>Ĝ^t:_ٓ=f ,[4s|- 5C^"KU ::~ҩEXF2vOQED̸|0_ߒ^PTD+`(Lb1dA)2D޾;Co]H^%4yx/u'@~癈"fϘ>WSVĘ 51Iiv{Nk`_KO]`sc a]KP&>~W\BWhcK'uro$nhq6ح$p/6D.c?/Mp]`&/p9`=c|mXB_iEW<Џm6˶xO/FC^($q ?@\E's=&\QtFrmVlۺ\y0{ Rܔ-EmUU8=d;}nkgHFiV Ί-_~!vILϦKꦼZ\ip]D[NLj y1=vRSOEh"Dd$EY"F$Xuc5XEBu OSVI.7{.J*dgkP_jvE;=d:nvS[ǜXu=0L1wu C& /Sl= ?$.CD0HsYG~^(/INQ)Ɔk$lg CiiJbMQ1]#nRkg0 qͥIv 'WCF=ss/ZK8[oDu&O5k[[mU4T uJ|]%mōǛ^kL{S_M 2T9f;Y駾/ /ZLLpܼ>{uV$}:Y%^~u]: *'- 9M먨##^pZ,8M*`ꎱe xJN6+o.v¯+vI2tpp^$}VW1Mȯ85 _yHHF-I%.`@Y[P\X!ECD >sǮ{.Zͻ;jQCoW[M+(V*pQ>(׺+z0xzڻJ $n8t;D?zvגf].܁H:G''-Mwq hfGUݫ'^G2؍a3 K# 0X@>RqrmUi/x,xw+O`r1jq4P-L`_Ǔ 2HEk%~yJ34H3|MJ0+Kk60zPBc%"gA[LyDPxqW%/҉${'Z~8-ptJZ=X۪Q16uq#wNܭ-UYbI_T;kbAfnLtU.i*խ)B @9V6po`V; %*dz|4r|@:~e][_v= UOӰ}Q5{,{g:(5pSh)7bAfjTۛjJ-޵@SH︬XU) 7ŒdJ͎@`ٱ-QԣJ%+Y+Vd+i^EoJphȱҮU3¸ ʜn$qؿ}vCۭ!8g)o|"';z>)Ϙ ~B֦A#gmYMy)$}&wSU]M+څWo/`τ(X'$6t _:[K }! KrY^[&!@UC~6kVUdmqLW& pN2 b3ehZ _(BpP2]41DGnY*8MI̬.

?eIfIu"ڹYTƄU ,,y^gX3p8/l-ſjr>dM4+ G5Qb[KaXlTC2rJ-%[?sj·?λ BB W"0>c1xI,uF#M~)¾fF%>tA6o}rGc}-rnK# DA*,SmcxٺQhMRxnC-=P`z+z×Sr-?[! JJzlD[Ru:..iR_w+.1#>gЍ]ikqӽ-t'fM,LoMŷIpy77\d|yhOsVb ܊AouwYrV?AGD]\؏#[Rտ˂c4\}N4_H`1ca E`ظ^LbSqkH'i4^Cu  |mBYy$،쁪a摺7* a =R `7\0|l9 }򭼦#L-, -"ƕ_'eO.ЗUpKr|c3d"7}g+n/*`z1fqA!8H&ft jzfl g~ɩ+`pGSUyO=|Il AQ=DŽ!\ϒ?Ja7]p8ﱱ'HZ1-3W==kw՛wL5#PYNO0',meoSmvy]O5r&N"oB{Hcn.ِK-WnNMw)‚tw18J& C X 4I 2C-HZ)8O5DtC<yj:"Ftʝ.>wDd3k #=%O+gHq`zfYl_3ew/yn,߅ͯΛH""rbS)L9X{ 28X1IVID Hk[R+^ePiʛtb|N,h=}n?t_MW5kB*"`w`辗q UdI F)~3)s$QtkiCH\꧃&`h2jZ$DQhۑ*x]vP4r`(菛O8ݗ>Z׸6z@wP?AG yƏzleHK۰^3ߡjg opmG% {@]I*Wiֳ}Z'0JH,_2 -3$skXWE}Sȋ2OG~}b#:Gy@"p%}GzTD뉋ҵ8hwDnz:~UzU']EҬeSc aʗ,ykK1F@\i3kDq ն574A9 8BrQ)Lf)=Z[fHs@8 R`/b(a9 FyfljTKycz'~z"6˦-EL9w2UY"QM 2m.֩c\^Q\`;ms*`C]-w\ssJR:@73-߱ӃaO ȵu<.^=TctLR]P#+QxWJɛ} SF?{O߾AvZ~ŊZ$Ǎn5SQ yr/S<.HLĉ4uD̂9M$h45UX\6F7W K}mNTor&9r c]FxSHN; =u4콐o?{[u Zo:j,UVP_~ͮC1OҖ&Ǯd&\_{4 א2uTNT lI!UD *FE]4EBJ9!p#8/QS80ݶ^brTX Ɔg1^E$"pD_`_M=UQ).6IW5͡"mt篍>;#b.Ѹ6vPFNRIX(Ndž |*%F$!'HgZ<^p؊z*vN>X{pQh-5^{&E#Z 3M(}j+U5ڂ'C&Q ?e Q[qQSh&S,d 4tn?~d'nfޛe`։8Q8T} X1q04?JFa˪tcA ?Z 7҂4T"/Jn2Ġ+.ǔA`Gvw h8&a,mԗp^rk=v!zW:.,-6< Im~; geV3"z j@!$CQ &wdUa!P7}ןC- Čmsſ&3+U k{<>@ʀ`FCY9,rX781$08?aWk"~ighqX mD@Сy?WF~v_=pXg`ï d!PwLOZpI/Rb͒`vytVen0su ~oD?VHYg70lR Kwp ȟa(b(bfe吃ŠS얋Zz/,dwFb%18?8\ޝ:Mgp.tg+ᇛX=%G|NO/ٍ?>Aa}=1 ݀|$$X^j{ְ0r<4lH0TNtڐ?P]q}Rΰ2[2QDUJVN:lzAޝTVBgSLmB>#ȶnkiѧ M>z1KDxL}piȫ #面Z"޵V$p逾ͳij Bfq/$ȜN=Ϟ`:'ѦD0Zzz'ʓ ;]K"tW튠?:L_ڝ/]zg<UƳ7]AM'!ƝF1ZWW9@`|rS%> %qT+՜ni D9h. |E|%!eL4o68Cl_U=eYbh]ʳS"eK̬P 4,I=so`k*K0` !űCG[1+;qVnlg߬D\^HeVǃi.XatwWeWA$wiԌiDה(`}+4]ڊ>KǢ}dJN95%cG}|9[==“4 8_t{)+lGUHm^ Q5R=%6I7eVnx*38tV*8l" 1Ne}p4y6vՑt:aݣnl9)0P @@)v"jhڲݰ9 ̡59(im=OLTR8A:RP;0Ep ? .m- `R~+M2Xpv@@{lr1 0rmq3Y-vr._x8*c"&p Nyv4AprBLYlXr10ɘь(!Y,qlU$ET Àj7BdDPHOoqv;Dn@P@@ eG'*QZ:cGBd+}#\H\ F>{ɢD Sn?C :oZRyc(!4|ť!C>.}F?]JWW:0f:n)hbE s" DPAQj懄Xҧ ` mI5tۄ1IhID &hפE$җjRB4LMe!z2,OeQGѓz=m_O۷mҌD@3EG3nGC.H`'٦4dqExoGD( 2]2 >sJZe1Ÿ\Mfg?'Lzˀ d"FEnbzEo,/Z,PNe { 4bCU,K(dB: Ɵ2X̣w֡oZ3gmqkUlX$ qoCo>`=Y{2f̭ vDtjU+b%*ۡV:@eVjhE.%*91 }uY 3Ʉ6K6U0z*Kgf.ɶ31d~ʥim:8 x)~MYFIMYA01JSa)-&mS)%"% qFcr6]2)J;IDd>%I9Uj 3+Pg:%31* J`P%J*O%uJ,Q j&QL/J#A<)LD xBE*[* )B ^Mޖn_XC [e3ӊJXKNDuvUg7yֽ1s7[,ܲD #}^CgN(TY)q([Dd* a6wuqxkˠ2OaYkHD4ь~4"$r$+䢗6V)BRnɄOHpY-0L&d.^pۋ!]˕kv{ɚixT^/ .ˡb/|ksSUovNb]e/NIH颹7ӂcˊ8KyEnu .ěl#%$HU*'^%G',FGi lNVCQq"F5]B6^^-?]ߞg%qXSzD G#ӄ6~3Rd3Z.,&M,aiV']}*Ȍ^q<ٯ dXNT-,-7@PQV̲v0VC(Y>8qi=W:N8LqP–E[J* ^리A~naޖ%CYW+Y ViBk;JN9z27T|49qU8#18/w )rVy9ZjK>V}~wփ|Qq{ݶdž\Fr7l oE a|d񿪡7tlmp|&tdll—Y]IT7j&fgă4)VmEtaee*ӒɮtʇeBJ ~TaXFPB*>[w~a3pU~ #IuR$uf_FnӾpӵSCi_+ë"z1?'3#4>r Kr2knhVjnLTjC֎5msLț^zxO}pwn@- "//p9#2WjY)xGT'Fp~ưWyy.2:B VڶWfރh ài5j `,X99q q@X+=]kja^`Wfz:Ꟑ.!hP{g6|A( 跲 v-ѷ#%}lHJbYqXny|aweRR/ G+SmGh,"aEмT> gnJǎkΦf7PYͪ+gJQN~ 淹ɺj}T_\, kE/}~ew3eVM)4БB"df/$ "AVh~VI >9>n*H|Od2(~D$C*̗ȝ[xW.쓚IL6JmS|&O PGCo06`䦐pHfibG0ϴ%6e)!O*{W9b[nY7gS>=mbNCEI}GJx1$X>h%"kæ/%L>o &L&Gɓ@,E#2>_[yki0ڻfᇼؤDmd'$BvTtON^|`V^?C#G)7L|s+<=02hEϚ4 <3:D;Ͷ',q5W9k>϶wY~'=נP`,9@)ᶱMq?7ZlƼ~r(2?09_ Vv/.| l< )F,R2l6Ԡƙ׶g9-Ӣsi²nϩو]rtByD$:kz y܌;Bc'ZǹwIJux\8԰bH^)J'8_ܑcG뎸6-3˖Gyȍ9j )Ԏ=r~2 >.O[љC.iNvݑZYvRjQxsc+ 8 KhZ i7uHJg*oTx֟GH h~:M~4 8|񪷞k9sU_w%$֒Sӫɶs'uЭʗ&$?w{Gm Swn'oDi36~!v}m \3D!/V h@Y, {I뼦7t9567pA]]{ԯ|e~oz Bm_}-xVA|uAwl" 8 Lڡ70!oJcxx2ZrW3o+W@,`sp"7&gu+ s_Y6\ݮN|h#@hxy͇E@7$2r6 > f3SʥLCmd)mxm}s]/;6'nk@,Yy0g^櫺~k>ղ_z.A_Ai}s94s_zMyַ<%fX}ߐpj\IR14f6 Y8 ?#RKš.ҫ#W57AQ7?zb|HUv{6+Uqg}twyf~yT[TGʖw+_X'~$MI0pw?h7:vJen+SA 'n[=PI%" DMT9[v$B[\H*g/Γ{,LdmNI/w9XǤ0D'd( N,;tvug玢Gto;l)Ԓ@;;qX?΁E"p\9b;Xb4? %5 (" T!"V2*mxL^bgt%tpuR-ڢEuՠã]>_Ʉ7 ~jiv26!N#Ap0 V0.6;J¦linc7owItJyNrE6Q sDo>V񑐕kmKt'@ǵ\"_,N/|AqIO'Z? '; n RRcU+%, 8$dMoUT)Ӗ_LF3csKh 7!V;{6Nsnbj#8 D9f8ыx^`\Fphܮ!goxb {ќge\3BVp1=BJ,z"~ς&v̺w/ے=~ļ3zg^rF?XffaBy3D~8iIZد}~oXp@U?StL1t舚9,“rh6la԰Ĭ12׾^&fx@rPˉ=k/ ]<ǃ7IRA]"P}\4 #x;%8DiƉup,%2)Vl$SO Ox aR#U$C(NFJ*JPCeWy^JgKft!\}Q՞ҥ;CW8 &;cAyLcJp. L@ ١.`$~tX@-yp%~cSMZjܹ㏴6%Gw(f-A?GIYAOBwI闞Lb?8S q)t&|j eiR<(Vi<:~gº($.sInz]Ԟ<Bdm y$L?.K;'xF Nat՚c\ƭR?*h܀ R"2RTD)HQIRE)1NJ@n#7(ShuL@'~ 6}@|W'1֥IϝLO2 Bb4 |(In hWL;TEjvAt}=~N ,ɠØΩ'Cp$K>R6].]9.33qfe~u'Illb2`$=6 3'lAZ\%yG?`~hfB1B6 >)_j:?ED X43W4nG0A}mbY9GK !|?uv!. ?8ú#CP3j`4>CoO |C χFFK>:6R7d (X9qU+J VH#Y idk\orFf{zwn׸%nzҵktsDZ-FiR2 *2FA!(@AHDA1~؀(BHŸ`i-J?{|]&t(&Z6@J۫"ڮN2g#($-nc jX"X\=A5"F*+j4Kw!=]DlSXpIWt ,] 7G29URhZ8 ~ u u=(_RuTvpCChl LEx^B ΧXR1@샂Q_@{쳮#1ν dPG8 6hA'(h~7] ٥c0+Xaw&9:.w `ݢ޷EX{5\f6 H UӔh'vlkf4^,Ta i{l`\OG IE%'~~ ᵽd/N}eR^ Ezt$Wc}ߔ]B4V Z6j t+CՕVa͵"Ð~u~FKNRP-f@n5ĭ+,!Hj{ѬU`p="O`QzX4$H 9E9WwfoRޖ{jE.q+ g8lĆqUW](9FbWhUGFp0MJ6&ts'>[<>bax;xwHOwk au/gutEuA h~+dP&O)kU _ן <)MnX 6Jhחj&=]̚>K⠞*Uwh 8 (D҇~[ 7$x 9JR㱩v/ S!ŦDED ,L2$nx2e4 _{nÿd/JƦ*ūW:{ҳ1BUi8 ޠ v,u7iyڹ{nT}+wqߧ5=*(?6hmE|ar @=+ö&HQ~.rmrϏHN{ϸV詆V`TOFߑdfEn*>:`x ,ٮc)5.) mW^SUP{(Qei`:z-ǧ- w_Tsv;h]:Ԕ5wV+R5:C,7ߔ \}^n&=whh}kjVLguhk"AaY =>)qnMK>h>&Br,H~羟Wa\-t Zk qvIuv_j?r0niI$Ni0+A_3m4Νs~_bX.}H;q@f0v ^M!biHjDd,5l1UlmêvpW$M|r 4Aj/vjPKRzܥG]`e&BmN/N 86=Ǝ3+ Bϫ܎o>4맧{}ZүDoq$lO1\}bsBzL}{wFxݝ1.-{i ESҬDYCu:>ނ5*po{F3n0 4ԏ'éF^ѷ,l߿bY_Tliapg4^r6:>T=OOF\ 4jǦDFקQॽ šcY\F:ĭ9NJq;z{G1-1LqAe %l޹6H`TqigjzWhUIŒ8m%n4![e4JR,)k_vVه 7*+⽒o`vW((m?MAc }/-.=EOR1~YV6<ԯk/]~mWQM($$ji8(I 99 &᧹MNe;lYiGjk5h6TusTj9TlyV>%WV0,MBr%+7rKƎ#6.xێNTj[W,6fφ~&os%❑D,`{ 3rɝa(, Щg<EgY<RrId<ґp$! Fvh Ѧ4e-g}|QY0d@pU(kZY+[r1h`hA!1,aH)=յ2I!9VN!ZDƑ6Q:Ov"&jM aR0pAPZ2(R΂Pzƿ\"[)N2N[7=%0-[G(Q*Bw `$;= 7d}a{|j %K" )މ] 7xW?.qvMq%[Zdj uÆ|ӖccQ8Z!ݴ?bĦ=Ow#4h FmCc9Q4m[t1ٻq@O!Elas*8Z@}z39T# y Ő1ď!Ir`G1QV|hsDS3!Z[.'=KOkb1ML 2p6c'LM4VAk+PB&hcrռڨ=/[.UEV̦0'؛1Īk+'`5 Vn1< |@p>r ̗jxnOA  s~OBLCO>1}6·x ЕJ)cTAbGꅨ{l ma/s5p<i.U~{ե3\W՝TE=HB+I1O2Otӡ;5/7y& ~I0J2K/x\]ui?f=a<3(8~&XD8|K_ z]zŻp.Zw_n˟i~ș ^W0Mb?NUL&>.iK7VOc&Lf&O!MhX"i{D,$-i p^buNQ2Skdt^]7r>UNf/_$7&tL+[ ײ 0xgvO&>&rK(+GlE6|Z) k^kRb _icFUr =/l8r`I<%O$&L „)zׄG4"j eWHX >a؀e( S|O7)g=PT[m^ļ1(z+g8%JV H*98$dARQF+ qsMݟ-d!4RAI-Ձ=h<'@Ġ})p%)ҐGF%wljYG[C& y5!`*a㔣!RVrB4 bdd+9ȣY5$AxSS(a85^͵Ŝf]~y+*0VSSg8Ł)٭zO =Jz_هsu TS 4G}[(*K m8GBkc'?;nl8hEJ Q &DfH恞4v ȼe^#X :S7YIѫTzi/FDj`ݿuD7H 4iCI=:`5I€WɎOÞ"' 扽(ݚZ"aׇ8D9QXۢXqh?aY EB!ޜI\l~Ffaߡ.z3lf[;͚/>F?%7XL/:2BgR!Fi]#0H|{9r{_wwȽ͠5ڃZ`XXI4mCe'H@nRҥ тh&X Va1plf[Ыm>jAPw=.5кtIz;B*5z6,SȇuHJpTnVv$[|sTkX0*ٛU -"O:(7cɸ}5 TVs[3N&4tgr2bo|cԌ:܄) '`(G=`vrb_iNPuQmfOߡCdYL>K! QQ#!k}䑆}i鶙t2-5ЫV< ܭ9Cc"6YUe6ِ:*U @&ejS Aqn\1\;mW*|XZ.Z4[+ }o_38-J%BGDY|g `]@y\_oPs"3>o}_Dp*Y" nMy[${`xP?~˻/h~Q~Uܱc;T1j~rQ xd1O]lF*ͧa~]|- fnyzٶn[ɪ:fY@Mly;a[E쪩bMMdň cLT95Ib F/B@]Ծt4]bޱɥ>R&1;ƥz_A~VmGjL=}}!b9 5<-2ebeR%}REoa6g /<+T3],8†/A%mΒפB0"юqsq2(Yӡg}Δ;H!۹L=3E'YL"CF[͓[W +tRpX:ROB G템RN unFZ%o{!WD1*HCYYM7s%2Agl:aC{NKY\ ÕܓETQm5:R}̲WӾV'v 4 }[bFVKb* j (1][h?[8LW{̥^ϊt0'sxO ~M?"Н+5c<( ٢I<ۊY[F"fiP:GisY!]º}K 6ꈭ+.W~nHYv|v1P9#}$ˋ6_ṷ?VZQ U;,j҈z.n)h Ko*z0mou'8&e ivjvZ?^ߺMj^o,/eh>{̲g#ح \7x:he%H`Yi@ H(&7)xP2zMmJ~1mӬΓf׼쉂2n>AM G#Dkk0cmvY.ju'y4i'xQ2PyR5z?ؾlſ9G@MT~ ۰}ko >x^|zԴ,XOi_т?鮅L-6Rg?#!s?,ZIc&HCEX߬l[Tu18fLֺ+;ibi跪x7[.nu޸XlB2VdNwV o8g(xHC6C0v5*>~XK .!8?[/"v^N6׎hF؈=coz1Uu' )'N[XSmak"xń{˓urLI!w;kaIO'(H*Y7cH<+moV0uQ%Q+'F>c 'K'|j%?.jDX `=Bqr¢G4$O  Ho{?]+)1rTj؞&!˫`ke9jO =rEwj]*D"YH(K:#ïm(Jj@i" L5"Vi4ӱ*'k83uwlk)ǓZ-kV+aPtI⋷y`$_A뾻88֜ꘂ/kWBZ%]ӚN -7,ħ3'TIc >^ vE3RXX=h**Qnv9ypKy!+' |VC6aɪu=\v7ş)\0Wiy_o%~85zU.r"`6P_̐^FFGxMzpO#]czXP ɓgDCd͓TWqOO>Z4o7V}g2='"xfQri3M0Ɍ_pkWh/uY8"DM - +n!b$t~6]wlrJ91Le]h'X""&'~¯8—;x[[J,Hxf-tex"tPWVQdyX+iQ,_[m> $>҂`,EÀ0 PZ(oj}I'|S_:t^PDau'7wD B}hڌJ ̺f(9fNle-lYJ\ypG0,B Ѓ}Os=湔鷃aS?#B99 ^^Ѹn8!)"6WcM^}VEm"%v ]C Ϸn9|~J?twCNCʎ6fyYG#D39m+9!͸:nNYYG+0e S~}, @W\"dZH5 ҫ:Aq n(_9E7MCm}^ @}Ŷ~2WHD&bM79}$-BhoY" 9`P#$hĒy 7I= 2gFYZ,A;K\BmE%|ͽm .;`Rnw_M-pXK}~,Zɚi۸fXe6n$,RI'3hfkTM(I]_}D{tM{n'WGQKp_G22oI ޤJMLPt92:`TEh*0T;1y{X kԀƬowF:vNFʵy%C}u`̙?|]S~!_ЩꫡF1e3r"ڌ`I֗ bwo0x?2bo.wy0_9["\yɰOh=UnaQV¼ԈYg4r40P#@v(^%4|5DAI#Ġ LD' (gNP u)U:dpDEH w3{˜#8G@Kc 1y^j̈g`XoP ^naI%Sz$Ȏ,`&N8'9`pJsFgsr}}O֮D2VnL X" .]*aa?Ageʱho iSdM?DIW^%U=Td UU2اP. )oTv3!kaؒhXqH=44ȉ'5-a"*K 1!Y SCu˓9g^c[٘:2`/mgWڲ W 0B`F_׾"w֞u%o7[۩B{uYe*BJMI ) YTP(bI sn{JNQ $/ c7J Z61u_^Xυ Ջl+L`tScMsƀ"ᶚ^TCfv{{W;mޭwv{]32.-Dk"!H4gM֤Z/Ki q.$o$C`K.=u13LFY衋@WfZ޿Rg#y=;ݼg'ӮԲwS_T6S34]>uӟéye%3_=;>9dyΦ#6?{/4i3I|9iJW:S}9^猎r!aC.YWY& bI aYjL`[jrx: o f'qQoU0J JD9 ?n>Q֟g̞N\e91PhYe8 9Ъj@ujKpSgvJ,Ĺċ0#z1BjkJ5/W;:tuy44MSw8 YVN#LMSLQ&`d $6W:XI'1^Ċ40^cݒD7%α Nǃ*kNu.1s95J ב}jSl` ix _#Ḵ.%v`VD1) p]`R A%Y0;@+?5a8[#Ml8iY/\ɂ1ީe'pN(4WӴD@LUn1|Op PZ}$Q˙ @THk-34 ȞA%J5I;< V|Υ \uWMuLQ!T(1 J +R%qVc"<.Ƃ0,(e*]N.z"P@/ 'wB+„@ / YBW1qܮ>d; NAQ|͋K+MMJDa$ < *(IxZMӾ 'VRjp )PQ51Z m S5?c&R.EiS|?S@gUr%'} $)`YV2S2\nz0 ΡFN't 88*W֑u!lхmW;쇓Qoc~qkϝ"GG nw]7)~'e|F=z9EJBY M@\\0| YȞrƉASVQPƈ[χ-Ʈb{+R 3tl2S(ŊbR B2)X^!yM(\*w)_~DrTgB+4g86wNA)-7Uz-Nt)ǒdO9<D@)` ! XuʢN5GGExs-VRt@ծ ݅/VGN$LBHS`<BɎN˫MxC,\9D;T *XƥC7?I#,(7<ŶB%J Lp] b Z$}VA8Mԑ)XJ#4dUf<0nBz P9(p |^S6NqUQ 0yҠdȹzUKB@MU0T2G$hQV갶QlYzcN==f,hZb# YK xOp"݁MK)&Vg | 鈱[3B4ۻCT@Ca8 >3v8X+S DKUJVÔFa =,K7zե -8pJ5 `.\@KO賹Y&+6e?,i0!1%RUU fh,%;HcXJM_йTE!*鬯Ɔ!G\QKJæ_E1=]R9ZiGPZȔ|nApTij皕Řש6c;jB5HT8DVAopC8/fup29aXvUuRв؀0%z>i T2*$]5KQPjTB/$E팛y.)C·p7Ɋ}̸WĘVT@FLb_5I( ffBD6=TlJ2}uFn}EѪ4 H?ZBʷ7 >sE0>ϣ۲v6ކڏbϸ, c9Eib~QU<ś^0Sˀ > (%f#J0p|#e BX!VaO/AxQl`4dXlBj pQ!!,,E,P2p&J' <(E)Bo[0~Ӧ7Ym)v닧wQ jk[3z!u1FCf Cbblq˨8]b:܆2ixjd1ʡҨ%5,s`p3ιw{x,J1<4;= ]xg.KyGqL\웲MSWәҴatJTO8RXN@D׆Cp0.7J JMPƁCATq`5oZ=]湵&P\Hd6SRz5)AJJjPA?kl;"bLYH V8Τ-a( l+"-/^e:?# |[%jE/+W ף9h 0?_] g;/ xrr==LDؑ{dN6L3p,TktA\g|С7,OV,8Yn4&+L)LqLI)JbO37}F$/`9DX]#J|Of2GKA͔a23q񍩘F;NrHHsgZE ,لJ}I;ač)RPSND^4 i˙DcHWFi\nv 60T(m^*:kua~aaP})kN:U2:HijdGv \B&ϟ}?ꞝs*̼Pe|vNˑl}wo6drI,Eb麉ƓokU,+1$-VqBXM5Nudġ18]5D.0LNeŨbh3GIM ݾDsHs^?PX3Ϣ?9^ȫ{U=~}iHUsb>Ρ!.-d ZYÂ!-s';[OhA9L TXC#,~ Of*A!i]y789,?MwMhfHpT|ɲ8p%s9K9Qł.Hk9S 9̶v {̱J>4u%'r2`GOC$g)€g߼YoHay&_`ff]7B{m<|] (%ihga|Rp/NB0f̽V,4HPl׳˒y/Dzr 'ac»Vq9JiV`r/(i"1 hQh7+$?(n$d14_Hf$V8+Rv暻jG\6.WGlW=/ۙAi4jSZ?"z\ tBg~b9>Bq3fBr6!lY^PXcr.WnV '{Wr L:Aojcuّ:3PƘ:> Jޠ~Kߵ~)S#XOP㢚͛V=~W8x]йٯ̎c.[NcY.9?@ PP Pslk*! -%]l5P P @B~(6bb($:D挌74 G>]L[-ÿdkXef=ˮr,\͍6.KuKMdiJvO3ft3e4YХxA,3Ijk[Gl&-vi5(F7ىjE..4lf^F]Mz{-w5i(eLґzIRg~/դ(v5Q &*vܟ ۹NT444F֨Zj#; ݘLCI&X j7.A,cIIp2ˤƒJZ12#I&,[ q2cI ѻb}&p5Q;yFt(xj4۾V eEwgl,yY|,#Z~TEE|嵹G)/wlY1mYQoY7pYWrY1:z,YeѐuҜd4, i, ,4$,\5-CmR(Vligh~[Y2ɸK1F [9Bчi?D&5IMjT$&I4MhRT&5IRMjDդ&I5MjT$&4k,v -#*>2XTkY2 > ۬u|p-ӟ6nڟQ>1ڭ|S[{f|mA}CmTx6rCkd=(9Zb}dz[F~' ~8~ !k_\nOABn[-oPr!|qZޡXc\Gr_qo}ʃ-s}{bu岧Ȅt8OpVt5M=UHz3\"ڃ?pPTEEFۃ?qP6TUE5+1Ƹgq{ :Jl("8Zk|9( uqyMaAkʽ^イ'pG;e;/7쯋#Z.Jj0gZ=gصٷLf>#׬Fi]r3x9kH;l\6wc:6G2<1CKKϸoN-~3nׯ޼v7W`qG3+>*@f@ LҢJUce,߻thfh9/|κ2h Lk-Fc$ b ADk!* 41X#"*y S]WP2lWy~cl&[w̸%̙Y& 8yY#i<]Oy7\PToڔWO<`k6Ͻ堘w=$_`{3lQ`U( BP4EB“ CDoHͮll}1\aN:k6ƍ3nc+@#?96mCmwCGϧ[mlCW64N(-XW&4J6&@BB#x': {NL5dƒ9 mBT'~8QW\? &ŝcX?JUk8}Q5'al@nhȸ4D9;,;eC ;1 $ d+t39fqm{9dFMvu9G*@'|c[k8 SwJkYgA\>tOr=؊W82 3Fc't >MN`=!`QBd`a)H/oqeCLS (Đ6^@@&a$#@k:xc:DO]!NV'zxPӆNN g[xX_+lپfξ嘁(UýADf8eK/+$>آ?'/QL][/ R/B{"&ﵧ~R)8Vw!laB!SHK+/w"7r^f{(ovCgS[AD:*L|З?>iu6?۟IC??C_rv?!v^fԾ[EWsY)]]U_Au8 xyּsSU˘ӊFnou9z-(7 Y@< g>am9g׵'6e,=Pxݵ Tz'?[{5t_2썇5]ƭZ ?gwE ~o?0o'ϣ+p [*^5 `w|QE^J89Ju㑳)el7Ψ5}B>N>;\ X +w=Zy괚!hc~<_ B;=%KEp %d\CSXa|[a1u6-7kH5ja uFwWfo`Z1䕢uPVbՑ|Al|1b "#/`+psVR5<&5nm*_5ni|˹184F`&ʝ%G "@v=}]f9ߣrYfp"{YD83^h]Ƭ`P(v1q N떐hEՓ&dbFmB&L,V^`Vo3%DZphɡ eoiu癋8F0=aEwQhˑCQ U=hj%כgG c0sbE^1'燂E>":a?5VL><7yexW; 52阙̮:xx&[(nkf/Xsh[  8(LQ%r/\(H$z# o-*D,}R{~]/:%g֕u@0z}8 kv98ĉBS̨!p\FB5ne.5Y䖓C7|*ۄ{B|O\ %C\@QɻueO)Ro@nF}yy\z##ý7-(kM - 0rLD-gȸ;󨍵_kcϨwsCѤ} !Q5F.d\'jk>NߑtlI3ѴHFUh>.>pzCp_,]XPjt)-)=VP樲:cN}Z[F-)ץFWe6k|ojA1;rRR AHV+VW)!- C؅'FoIhAKWԓ'MWޘ`7Nʤ RekWv]k.!// ATk%=+@UO7d=l*60=sMN48 $GSd="zCяNڥih宀m a dCJ^Z2)X̖֑A!5dMHD'=t c%yѓp:=yPXnb4;֪6n뙣sUCgy@τO"2Dux k=tqa˘U`.r֟{%PW=D1߼ȃ17pT]Yyb=m=[uOu<k׋fz#]>ւ~obkmU(-|yc!fh<:p+pJ x+T9,c&|_W81ښ@S oP_խfLu(BC4 a LOy}h\ػ߰i.Pp",F>*"p.L…rGswq>hbw .֥pP1xmfXO0xw");9ԅ<v8Q}/ήY3̯+K%uMNv-4ܻQqU'QvT*>90D^zpo,БV0Z˾d>λTsPT8֕W^q2eSE_01(X}9r-I`}?=a43ѼMk YuāǛ 3;ia<|.K3^~Q =ƺ5g+7h/ijHٚd~렞~O!J!Wݣ`Skv&GGG>Vj=׏cЭ &FQC"6`+'I,Wػ"X U} ؘA 1ѸΗ {-8|ű e ꢃ.cD"x҄G`aB5 )'7˓^̦d#N8:,=M <вENUAUH'i/P-~q@6e\H[y2 V髌G X]fpH{Z@s\7sg*XMY+_uiOFx<_ω&kVk7UyFm]4| CErs OhSœ|yU[gduXi9Q KRaW\@7&Тd1;5)q)KxWZ]9Riax^V7g1`:ߡ3G) qu glEwK ˍP@qcK ̭.BAJ$ Q {bƺ+?4CdᅃBda^Qf:]) ;(|XR6zpu; f &NC?PG3SOoi  W6:0>apz{5X؏{m*5bḦo.^,gxocv#;P:ւ0<3En:)_@ːL!w)WKWr Y1i,!F\ VAjC`CNy"kx)Q 8I̜ϒoҠ4͎҂F̤dI1uLwѣ-'G -4$p[fvOl3N87kZ_aMK6!6fpMhFmiq5'|'_8jhXɏp*Y+ RT^+o5b۔R ixZŎ%t4$eavpoږG8aX$Y6G(tNvmB<="oDx̵d픩uL_?f?_ûj*&c?Ӷq޴9,q"LYt c3Ӳ 4;@Z&ikč &ߟx:q2ws8eL+yte:%e)+9y9Qꃰ{Ы=X%Ѧq˭D#]dO(u56JoY<먵dŌ}5@PvYWAG8 ڷTX|pZHJa-hjy &tzh]p>aruV8$wҺ@!=<81X ?j=C]:\ f+ 8{A)o_#^{e)+r+UZƄ+^/!^]MQϗ=lB2ᩄly%e.Qx'Uќ.fe/gU}^0ԃ'Wc- wĢ0_MS\#k&#* 4U[ZB$vvvmNo=s"iPs(4JԨ2m5SSg6? xA4G ٳ 6"4U=[Tsx298o(8gA@XYP.DA4lq<%F%xofϞcRJ;&(+:8RL)l5v!PHPEj'} s ~r{?T7x_ÐsR6))#%u|SBC: <{2* UܔJhn`ŰLCp*duw*) *3E8yoziNJץ'eLD,!䗐4/BD:`  M =@* T7oiFGսWK.0eOU5].' `H̾0ͨs#Vve_cIICd-:g/~]IpsQie`r 77`/_ 6fgT[RI{Rߟ WgbԽx1 >EGII:[X{ynѩG6jZEqjQjaGcǹ0rL;iXUcUxv|I^`1|LJ|#-$^dacacN5**+ hVU QǙYTh soGCmÈ$[PQ1U֧m\bc1:bElcET; ㅦ?{2_A?$Z]*-J5C.^ULNvzoEhlA g>#?+;Vd@!w.*ok :9 M|˪{lY-hVJΥu?;@$}uy%"ռ_6)\ea id:Cm?3}IQU*?8qupO³s=_RSz@G4JsfR+qoԕ![x֥mZ֍SNfb73m-ϏjꅼP2>-p0}iX'{gޏ|fgOҍ͇~} }+I; ~WYٍ{^y9Ȗyy̬ym+S4)_!rM/OvenӲ}U0) y)j}%\XW-68/Jp4s6qڋE q"bY\27l4UZv WV9ھm#QE%E(NďJ椨RU8T|E݈W+ʝP:BC有Du:{RkåquSaHgŬxql[?Zm'AMIf\ gH҅*t.YV]TZ$lH@<ޗ(,#! *EaïOQK~?}g^+C,4>H9E& Xp_>^ٛk&ePu`8n '{z~sǨĎ!k9E D,^J_5Bg=Obn$aHFbؾ!ǎeXYxL}15D|D+d^ez:7%T&/]@{K|}^>'Tƛ3q9E0;k%ñҨsّL)ɎR61c(dOu3ӖGWÌdtdJ:;`"mZݬ@ސAW؎!}DY?yIwja{:yek b@nԌc蜶;M7ڻcDvDwՌ؏=ݮ͒`}bJZ%F&[Bߐ{,kq _OrY;a-evu{X\e8v?`yh# c3v0{ %1> AflFzMjI琶oC KD%`bϙ?rZ;YG9.Jy&ΕE2'.i7QP@{n˂Jcv1PL vw\H_Ǩ4DxHr/Ы)w?$9. &ᶰp>6ea+!$0D_MҖP聓BOl$0w$BD`,kb)j zX/"p6X̉t%9!~s!=s-9[wD!aV,Tgӌ NYS5i(A_O_hJw94(oxz]Ħ[x`\:$h = 34p F3}>jUs<[h rZw*Ur{5=+?Sz Ü4VKu|Mv@uz;QZRuZכUg+F7>-{+#c0]8 ǃѣ^blI<^u+d-B6HohNQM6$w1y߱bThFH&~`Vz Β/Ƃh d\g|IE\I<3{Fh{;;G%j+a%0Uن&)+JיǸ86XWrU6 661p8~3|țx5n`b!@qlmhpˇJ-kpW#ו&dI`wI[NM2sOr'ܗRg`r@EHaG0r91hUO,S97-XƁX}ӈǻgjREzT:1" yk9Bb6ʹp?%DPÂ\CL7 E\l2,kEfրZx^p{3R{<>Zĵh 67]buQ6YLQ.3(JwMj<5=WDU`Х8pafゖM*TSݞخd&f}O\ 9(.}`N')Dbk۪|>BӅnh}n ,e|5UvX5ZAdm{_7}|hsX|bhҝ$ߙe;L+r5ScDD߳(㥋L’}k!VdnmG^{t!|:T_#[Q:xī{(tXTrf򯽅)+e Y5e5z 0Iscg ,}{۫s5ar|,4M+.S5aTFXRL9 yZjzMTc*8)9gDf?,p֘9H݂b rH8Mӛ;M(='Z3&$.kχ" mCZ{k2B C809.F~H<Jg&Hs39B)P29AƱrYp=OL)mw^~avvʻ]]bV_^'CC 2 hךz#,,v#Vz%kA벁GpOw}Z\FճӨ4hk{ŤԶ҈X2x-~ *G#Jx8UWu0ۯMp+=`E>a=9f-:,P7 .{D7+}V-࿁=[ ^1)/_u + )AvD! ;A *Ȏ ,^fMY,*l#lsإ;Vl]fb;uՌwi0:;+(3ȠGd84xfX^׹f?OcF3=d+$/ϼ'ː!?*g5d-3n) "_4js]+IkWm ++U+21zNc}^^-a댗|M HU # C$4fZ Ǖba34HL3dLX2C gQ }˧MAJ#/U }+fMsUЛ^5g8xm\b!&A#́&&猐 Egi%v-=9}^ߓHE${6 4@K\覩ނe*cNsZsImm+qchtkV<$m6YM K4fAkP[{-W j Ѫ191i~Xk}Db*hG "yw9QLjS[FB+;f[ nj65QX'fZ.-WUrg8= -_(stx97?NbXu_qA}_'˙~WzSԝkѭGRNIgD4y7OxT=}KDwYZ?&'^fD[1oVoO[qK%^odk+Pb9Zrrnm,~t G5dv͕^j?O՞7.ю>JGBSꔌ&rc)ɯvboҩ~Z8mjHEVp}Y(LR哊umQsSVcM茩*u fOؿaV8;K~b*=^!T;VcUma4^dyFYɩ$Yx9}!S~޵'0Ig-i?Z{pq}9ݿ=Kw!r<5UW[ĭ)S_&}. WRf7r/KD _źיOQ|r`iak|o_B%3B5WQsnux $Ҽ;J Z8'VZvYk;#BK!noaEO'&gY˯feϡo͏qT´o 43о_^mRZ@]Ht;%NeQ!'>ePb "M-9 J tRѹh%fwFMaE!K1;5 L6Kx 79֍ &}Tʅww Lg)+XL әJhTƜ#$^>zqƤ4 =tɽ1'%%Yv7d Wͥ>7\e%)+ᵱY3Y[ù~!OvMQj`ߴrq. +Iq:B Y'ب~?RT=tf|#}R20e PX.cK21 "VJ?ZTcLF={Miփ4I#o{5U=B&$ 5񆴐%2핗%26# MƼ%g UAt6SҐ E9JB!H‚~\jK8&7wj<-%xd/ǚZceܷj,h]o$8{AŲrj;P~]տ@9kk8:ۊuLm#zg CY.zJk@;vVG#{3ؿw `ȸvm˵.[r k`]tWܔC˦։{&w~fzm5͜H{Nn+j~'_Z{[?d\!t{#W^tW͏dxcoT;R.zIPJT읷/b>rEV͓/q3CHbFi%ɥRvò1r7A%/~ jDJCɘ^"eB2cmh0(lT|Eh"+(ل{A(\~ 2I<0S1AK0a*b<6hI*|}qg"e x@ª)I 1+(iǀ0 /xue"yv%Pf.^GIX:*6n`FRZ̀łb Μ I|m78Wۤ3?v2Ix9=0_K>`FSUMObVnpը26}b 7!gW06_H9 G|TK^,4ka⯩޻f7GL̮WX2/&)/#Z̭nMv!) 8/|$?ϰ5-^) 롻\[ {P 5CF-uxә0m~Ŝ%6162;b=_ڼfZ6F%B8^ @\^HiUjxmΒZnv~ci>A$d͊!61&k{zĦFz3)ߦ` +MZ!.eR,c`lԴsrNཟ pR' Q Z2Gy_Ef(IDL]K@4D"N̈~emi)h)7|H+Ԑi2Tm=q<. pX-sYFB D|Z*c sƹ1_W3J,t=o XO O Ig4b#08! >Mn6almMb|1a@΅aO47WJ[8oBz;UWQ}>Ov`SF=n"b|~˸DLGgo8v>Q:p:B T,("}c>}T/@ a0ZoFáj^&n&lwBdr[%|2^4-! ʏ&p2])My*Y37v܈J, tNʂh{mkgVZaa"8# Cauּ.|e* % mov`#!`;[Oͫ3bO>v '5h35>%-8]n؜#UZ0)۶ݸ'II!u kaCHc.X w /t$'pj&H+;t=66D 66S}`в~-swGD^2#㐀η# @oDvCOFm|M7C̄vQ@"۠9ŴZCGqJ{`۵DCx|sbjsMj2g7`x1\!%yc(Hͺ3~;NIZܸK+3!OLs o_c L|%q> ρ-5+H4I#)L-jRTKePR/ N)"$w+YmHAJ`4BC8k>삁۔)9EA7tTr(?8dR6F)DǓiH q S*hG$|5INS2LnSg$ M1OluӐyr02ǚM*#!o^ Iڑ\.Xu *𹡯&j(OVUcMR%, (D|CRpTC*uo & tr#l~ l@EpPDfV&x^)ĐB}g,9׳暖n<@>>ZSTn#6Bߪg?>]p$&d`i=r*](BG?Sch$eꜘ> ; ߯TH;qp=2Ũi3A,!1k_k[U_T n(u$AA p~7Kcpp$r^w"0+*, GOzӾ!5:]KG,pviZds[/pM|V#qbjZfKZ(0|uեk< 5>y8r0wҞ@M'u}ߕ&}v\RkPMd`Vb\g0Et,"X-Q&mG1|/_J-3Fժ-{tm#Z/k6f\ s}BmZƙ«Sj2GQb.<h H[R?}j[fMye;M t6ī%N?>J>``8e6z= ݸg[武诹uNJ_T:B^ -7_pD W9KR1GńO{s {ުŧG!W8l+oX\jv.sY"\)-~U6x@|?p=M ~J{XĶd^ɭ{Z'dzǧF\𺪚d__v'`Wmc]ܰDv\ޣpMQ1[5[  _ri+ljߥJ10\u;9|VCܖc)y$[xh7i#PiJ@oF9]M `3貏6@^9F5p6}&y?dNhP\#ښ-':zrʼns(vKR8w;"꛼| a=]dWD7b?+REǟmVMxeÕ*'@V ckp:QmcBu҃눞RrLkK-"1-Ѷ9z[]3[UC]9`d/?"y'uV+=ڔ؄@tfU˭ji!="uVuSPuQe_aC+!VʘU0QkU jkajʬ5 )%%>j8k<_k]^U#4Vh^Ҁ#>2U!g@ ^nTT0Vpj{x %*U*[*"T6@WꯁCa?3ڕnJOR+H Jc6"aAE]=/{bzv *BQ}XTW- L]7q?Vw*r(73fot%\{&#T]UTZUj'pͩޚpYxI؜mϵ",{ |*KJ6Ms  IZ-!qy|X V]{ieOtYzחDO=MAjI}Yo]Y?a_^nm.z]bΡg)]kNjxM.Wa`<5A,3koa0 - +DD<\]B '(** pXp?:~Ao0àE8ЕqEpQpaU }.WL^YAq,kLbQ}/\cK/Z&z}-9"t G갸g.\w*k9%3 &t(JbՍ)WFQj+6O],1qY悪)|eҹr!csqb Ios}o]V!ZsBL[z㯂ZqSW4 m(] xJ']IC ߰zap^ò^rn>J0ɇ\ GD.7TCRdsܬ |ހ61NjSǦ7sXȑ S\X:xZH5{؆܂'Y鼤57lmR>;\`]5Hs`ԩ/{CtjCuSO2WoyXfۭaKp6$A1Uڿ)uW&ڲ{7#Q +"XhTT9]R S;.cp6ewqrNO 76ߍge CVEtԁo堺\mfYinN:Z͓sBW7)=f~PgᘂwXZo$k? AOݛ-tO]wLݧ8 YUbC; }yG5C [SMzK^ucw~fmqZWOw [J2 ԒxS~aYOMj૶{QɧTucy{M\+{!M뮏ֲUb]SiFލ7/n6e=(Z㌟8d?d |1A@>)$,~J@й}{/io'} dF[VEط ,830 ?ҁT`M UӠՆӞʌ\uc\OxHQN3Rl-<6ټ$LV3VQ㣋 [ \ZR“)Yb#jK(d:JSPʓKj&Rk[/2WL,0]a Wpj*Qnp iS*}!}Ph.!P)kY0dwqrThX"I$5{t&!0™ҕD!m܄2[cxX!փQ-^pRJc;+i* y o)Dtg*vv |a!luX$xi b< ]Ds"K~q2f).5op%Slt3'"] SνmD2(|DlIcbb8^Ȫ苆 4Jng)L>R9F^L%?&=>wr!Ɔ(6JdlD7|sb 32HR%G.?;uXI5S KN r5 ^T16Uu&2v MfCd힚$8{NV0 .JZ3$&%szI%/ݕ@܃ݯ]9l͑~dOHv.>}=↸+_=] ;qC#9H*0?Goٓس*4M!~r'9㚓b^م3<6o`fM)! 4^7"L9͇BPk@aU|] 7xNh2%MQ5!ĎGǀl39c'E٨TG.V+y :0ԷmDu0MD5ym>tP"Zp #+֨UFP^@xhGdsH4[+Eb4~q.siΔJըg#tchy$KhD2jR,7 CfV:F9Cm 5;X#EsdxWp(*e.#F5E_I: $ ; b9e@D{ $xfϢb%Oj?I[M Pf䣠ʪCЧa i4G GQ|D0x}Lܾ6Tb$WrqloJ@Hįa-W"ghG >+aIW }5k%"&'֘b OpenMcdf

Structured Storage signature This integer field contains the starting sector number for the mini FAT Provides the set of values by which a binary search tree can be enumerated. Action to implement when transaction support - sector has to be written to the underlying stream (see specs). Ad-hoc Heap Friendly sector collection to avoid using large array that may create some problem to GC collection (see http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/ ) Abstract base class for Structured Storage entities. const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); // CFItem represents both storage and stream items VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); Get entity name Size in bytes of the item. It has a valid value only if entity is a stream, otherwise it is setted to zero. Return true if item is Storage This check doesn't use reflection or runtime type information and doesn't suffer related performance penalties. Return true if item is a Stream This check doesn't use reflection or runtime type information and doesn't suffer related performance penalties. Return true if item is the Root Storage This check doesn't use reflection or runtime type information and doesn't suffer related performance penalties. Get/Set the Creation Date of the current item Get/Set the Modify Date of the current item Get/Set Object class Guid for Root and Storage entries. Represents a collection of Node<T> instances. The type of data held in the Node instances referenced by this class. Searches the NodeList for a Node containing a particular value. The value to search for. The Node in the NodeList, if it exists; null otherwise. Represents a binary search tree. A binary search tree is a binary tree whose nodes are arranged such that for any given node k, all nodes in k's left subtree have a value less than k, and all nodes in k's right subtree have a value greater than k. The type of data stored in the binary tree nodes. Removes the contents of the BST Copies the contents of the BST to an appropriately-sized array of type T, using the Inorder traversal method. Copies the contents of the BST to an appropriately-sized array of type T, using a specified traversal method. Adds a new value to the BST. The data to insert into the BST. Adding a value already in the BST has no effect; that is, the SkipList is not altered, the Add() method simply exits. Returns a Boolean, indicating if a specified value is contained within the BST. The data to search for. True if data is found in the BST; false otherwise. Returns a Boolean, indicating if a specified value is contained within the BST. The data to search for. True if data is found in the BST; false otherwise. Attempts to remove the specified data element from the BST. The data to remove from the BST. True if the element is found in the tree, and removed; false if the element is not found in the tree. Enumerates the BST's contents using inorder traversal. An enumerator that provides inorder access to the BST's elements. Enumerates the BST's contents using a specified traversal method. The type of traversal to perform. An enumerator that provides access to the BST's elements using a specified traversal technique. Provides enumeration through the BST using preorder traversal. Provides enumeration through the BST using inorder traversal. Provides enumeration through the BST using postorder traversal. Returns the number of elements in the BST. Binary File Format Version. Sector size is 512 byte for version 3, 4096 for version 4 Compound file version 3 - The default and most common version available. Sector size 512 bytes, 2GB max file size. Compound file version 4 - Sector size is 4096 bytes. Using this version could bring some compatibility problem with existing applications. Update mode of the compound file. Default is ReadOnly. ReadOnly update mode prevents overwriting of the opened file. Data changes are allowed but they have to be persisted on a different file when required. Update mode allows subsequent data changing operations to be persisted directly on the opened file or stream using the Commit method when required. Warning: this option may cause existing data loss if misused. Standard Microsoft© Compound File implementation. It is also known as OLE/COM structured storage and contains a hierarchy of storage and stream objects providing efficent storage of multiple kinds of documents in a single file. Version 3 and 4 of specifications are supported. Number of DIFAT entries in the header Sector ID Size (int) Initial capacity of the flushing queue used to optimize commit writing operations Maximum size of the flushing buffer used to optimize commit writing operations Returns the size of standard sectors switching on CFS version (3 or 4) Standard sector size Number of FAT entries in a DIFAT Sector Sectors ID entries in a FAT Sector Flag for sector recycling. Flag for unallocated sector zeroing out. CompoundFile header Compound underlying stream. Null when new CF has been created. Create a blank, version 3 compound file. Sector recycle is turned off to achieve the best reading/writing performance in most common scenarios. byte[] b = new byte[10000]; for (int i = 0; i < 10000; i++) { b[i % 120] = (byte)i; } CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); myStream.SetData(b); cf.Save("MyCompoundFile.cfs"); cf.Close(); Create a new, blank, compound file. Use a specific Compound File Version to set 512 or 4096 bytes sectors If true, recycle unused sectors If true, unallocated sectors will be overwritten with zeros byte[] b = new byte[10000]; for (int i = 0; i < 10000; i++) { b[i % 120] = (byte)i; } CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, true, true); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); myStream.SetData(b); cf.Save("MyCompoundFile.cfs"); cf.Close(); Sector recycling reduces data writing performances but avoids space wasting in scenarios with frequently data manipulation of the same streams. The new compound file is open in Update mode. Load an existing compound file. Compound file to read from //A xls file should have a Workbook stream String filename = "report.xls"; CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); File will be open in read-only mode: it has to be saved with a different filename. A wrapping implementation has to be provided in order to remove/substitute an existing file. Version will be automatically recognized from the file. Sector recycle is turned off to achieve the best reading/writing performance in most common scenarios. Load an existing compound file. Compound file to read from If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors String srcFilename = "data_YOU_CAN_CHANGE.xls"; CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true); Random r = new Random(); byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); cf.RootStorage.AddStream("MyStream").SetData(buffer); //This will persist data to the underlying media. cf.Commit(); cf.Close(); Load an existing compound file. Compound file to read from If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors If true, no CFCorruptedFileException will be thrown even if corrupted file is loaded. Please note that this option is could pose a potential security threat String srcFilename = "data_YOU_CAN_CHANGE.xls"; CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true, true); Random r = new Random(); byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); cf.RootStorage.AddStream("MyStream").SetData(buffer); //This will persist data to the underlying media. cf.Commit(); cf.Close(); Load an existing compound file. A stream containing a compound file to read If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors String filename = "reportREAD.xls"; FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); Raised when trying to open a non-seekable stream Raised stream is null Load an existing compound file. A stream containing a compound file to read If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors If true, openMcdf will try to ignore invalid references or format in order to load a possibly corrupted file anyway The 'noValidationEcxception' parameter could possibly lead to security issues so it's recommanded to use it only on trusted sources String filename = "reportREAD.xls"; FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false, false, true); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); //if 'reportRead.xls' is corrupted, openMcdf will try to lad it anyway [noValidationException set true] Assert.IsNotNull(temp); cf.Close(); Raised when trying to open a non-seekable stream Raised stream is null Load an existing compound file from a stream. Streamed compound file String filename = "reportREAD.xls"; FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); Raised when trying to open a non-seekable stream Raised stream is null Commit data changes since the previously commit operation to the underlying supporting stream or file on the disk. This method can be used only if the supporting stream has been opened in Update mode. Commit data changes since the previously commit operation to the underlying supporting stream or file on the disk. If true, release loaded sectors to limit memory usage but reduces following read operations performance This method can be used only if the supporting stream has been opened in Update mode. Load compound file from an existing stream. Stream to load compound file from Allocate space, setup sectors id and refresh header for the new or updated mini sector chain. The new MINI sector chain Allocate space, setup sectors id and refresh header for the new or updated sector chain. The new or updated generic sector chain Check for transaction lock sector addition and mark it in the FAT. Allocate space, setup sectors id and refresh header for the new or updated FAT sector chain. The new or updated generic sector chain Setup the DIFAT sector chain A FAT sector chain Get the DIFAT Sector chain A list of DIFAT sectors Get the FAT sector chain List of FAT sectors Get a standard sector chain First SecID of the required chain A list of sectors Get a mini sector chain First SecID of the required chain A list of mini sectors (64 bytes) Get a sector chain from a compound file given the first sector ID and the required sector type. First chain sector's id Type of Sectors in the required chain (mini sectors, normal sectors or FAT) A list of Sectors as the result of their concatenation Reset a directory entry setting it to StgInvalid in the Directory. Sid of the directory to invalidate Load directory entries from compound file. Header and FAT MUST be already loaded. Commit directory entries change on the Current Source stream Saves the in-memory image of Compound File to a file. File name to write the compound file to Raised if destination file is not seekable Saves the in-memory image of Compound File to a stream. Destination Stream must be seekable. The stream to save compound File to Raised if destination stream is not seekable Raised if Compound File Storage has been already disposed MemoryStream ms = new MemoryStream(size); CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = new byte[]{0x00,0x01,0x02,0x03}; sm.SetData(b); cf.Save(ms); cf.Close(); Scan FAT o miniFAT for free sectors to reuse. Type of sector to look for A stack of available sectors or minisectors already allocated Check file size limit ( 2GB for version 3 ) Close the Compound File object CompoundFile and free all associated resources (e.g. open file handle and allocated memory). When the Close method is called, all the associated stream and storage objects are invalidated: any operation invoked on them will produce a CFDisposedException. const String FILENAME = "CompoundFile.cfs"; CompoundFile cf = new CompoundFile(FILENAME); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); cf.Close(); try { byte[] temp = st.GetStream("MyStream").GetData(); // The following line will fail because back-end object has been closed Assert.Fail("Stream without media"); } catch (Exception ex) { Assert.IsTrue(ex is CFDisposedException); } When called from user code, release all resources, otherwise, in the case runtime called it, only unmanagd resources are released. If true, method has been called from User code, if false it's been called from .net runtime Get a list of all entries with a given name contained in the document. Name of entries to retrive A list of name-matching entries This function is aimed to speed up entity lookup in flat-structure files (only one or little more known entries) without the performance penalty related to entities hierarchy constraints. There is no implied hierarchy in the returned list. Compress free space by removing unallocated sectors from compound file effectively reducing stream or file size. Current implementation supports compression only for ver. 3 compound files. //This code has been extracted from unit test String FILENAME = "MultipleStorage3.cfs"; FileInfo srcFile = new FileInfo(FILENAME); File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); //17Kb cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsTrue(srcFile.Length > dstFile.Length); Remove unallocated sectors from compound file in order to reduce its size. Current implementation supports compression only for ver. 3 compound files. //This code has been extracted from unit test String FILENAME = "MultipleStorage3.cfs"; FileInfo srcFile = new FileInfo(FILENAME); File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); //17Kb cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsTrue(srcFile.Length > dstFile.Length); Recursively clones valid structures, avoiding to copy free sectors. Current source storage to clone Current cloned destination storage Return true if this compound file has been loaded from an existing file or stream The entry point object that represents the root of the structures tree to get or set storage or stream data. //Create a compound file string FILENAME = "MyFileName.cfs"; CompoundFile ncf = new CompoundFile(); CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1"); l1.AddStream("l1ns1"); l1.AddStream("l1ns2"); l1.AddStream("l1ns3"); CFStorage l2 = l1.AddStorage("Storage Level 2"); l2.AddStream("l2ns1"); l2.AddStream("l2ns2"); ncf.Save(FILENAME); ncf.Close(); OLE structured storage stream Object It is contained inside a Storage object in a file-directory relationship and indexed by its name. Set the data associated with the stream object. byte[] b = new byte[]{0x0,0x1,0x2,0x3}; CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); myStream.SetData(b); Data bytes to write to this stream Append the provided data to stream data. byte[] b = new byte[]{0x0,0x1,0x2,0x3}; byte[] b2 = new byte[]{0x4,0x5,0x6,0x7}; CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); myStream.SetData(b); // here we could also have invoked .AppendData myStream.AppendData(b2); cf.Save("MyLargeStreamsFile.cfs); cf.Close(); Data bytes to append to this stream This method allows user to create stream with more than 2GB of data, appending data to the end of existing ones. Large streams (>2GB) are only supported by CFS version 4. Append data can also be invoked on streams with no data in order to simplify its use inside loops. Get the data associated with the stream object. CompoundFile cf2 = new CompoundFile("AFileName.cfs"); CFStream st = cf2.RootStorage.GetStream("MyStream"); byte[] buffer = st.GetData(); Array of byte containing stream data Raised when the owner compound file has been closed. Get bytes associated with the stream object, starting from a provided . When method returns, count will contain the effective count of bytes read. CompoundFile cf = new CompoundFile("AFileName.cfs"); CFStream st = cf.RootStorage.GetStream("MyStream"); int count = 8; // The stream is supposed to have a length greater than offset + count byte[] data = st.GetData(20, ref count); cf.Close(); Array of byte containing stream data Raised when the owner compound file has been closed. Copy data from an existing stream. A stream to read from Input stream is NOT closed after method invocation. Action to apply to visited items in the OLE structured storage Currently visited item //We assume that xls file should be a valid OLE compound file const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); Storage entity that acts like a logic container for streams or substorages in a compound file. Create a new CFStorage The Storage Owner - CompoundFile Create a CFStorage using an existing directory (previously loaded). The Storage Owner - CompoundFile An existing Directory Entry Create a new child stream inside the current storage The new stream name The new stream reference Raised when adding an item with the same name of an existing one Raised when adding a stream to a closed compound file Raised when adding a stream with null or empty name String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(220, 0x0A); sm.SetData(b); cf.Save(filename); Get a named stream contained in the current storage if existing. Name of the stream to look for A stream reference if existing Raised if trying to delete item from a closed compound file Raised if item to delete is not found String filename = "report.xls"; CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); Get a named storage contained in the current one if existing. Name of the storage to look for A storage reference if existing. Raised if trying to delete item from a closed compound file Raised if item to delete is not found String FILENAME = "MultipleStorage2.cfs"; CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); Assert.IsNotNull(st); cf.Close(); Create new child storage directory inside the current storage. The new storage name Reference to the new storage Raised when adding an item with the same name of an existing one Raised when adding a storage to a closed compound file Raised when adding a storage with null or empty name String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(220, 0x0A); sm.SetData(b); cf.Save(filename); Visit all entities contained in the storage applying a user provided action Raised when visiting items of a closed compound file User action to apply to visited entities Visiting recursion level. True means substorages are visited recursively, false indicates that only the direct children of this storage are visited const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); Remove an entry from the current storage and compound file. The name of the entry in the current storage to delete cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false); cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist. cf.Commit(true); cf.Close(); Raised if trying to delete item from a closed compound file Raised if item to delete is not found Raised if trying to delete root storage FNV hash, short for Fowler/Noll/Vo (not warranted) unique hash for byte array The BinaryTreeNode class represents a node in a binary tree, or a binary search tree. It has precisely two neighbors, which can be accessed via the Left and Right properties. The type of data stored in the binary tree node. The Node<T> class represents the base concept of a Node for a tree or graph. It contains a data item of type T, and a list of neighbors. The type of data contained in the Node. None of the classes in the SkmDataStructures2 namespace use the Node class directly; they all derive from this class, adding necessary functionality specific to each data structure. When called from user code, release all resources, otherwise, in the case runtime called it, only unmanagd resources are released. If true, method has been called from User code, if false it's been called from .net runtime A red-black tree must satisfy these properties: 1. The root is black. 2. All leaves are black. 3. Red nodes can only have black children. 4. All paths from a node to its leaves contain the same number of black nodes. Add args: ByVal key As IComparable, ByVal data As Object key is object that implements IComparable interface performance tip: change to use use int type (such as the hashcode) RestoreAfterInsert Additions to red-black trees usually destroy the red-black properties. Examine the tree and restore. Rotations are normally required to restore it RotateLeft Rebalance the tree by rotating the nodes to the left RotateRight Rebalance the tree by rotating the nodes to the right RestoreAfterDelete Deletions from red-black trees may destroy the red-black properties. Examine the tree and restore. Rotations are normally required to restore it Used as internal template object for binary tree searches. The RedBlackNode class encapsulates a node in the tree Key Data Color Left Right OpenMCDF base exception. Raised when a data setter/getter method is invoked on a stream or storage object after the disposal of the owner compound file object. Raised when opening a file with invalid header or not supported COM/OLE Structured storage version. Raised when a named stream or a storage object are not found in a parent storage. Raised when a method call is invalid for the current object state Raised when trying to add a duplicated CFItem Items are compared by name as indicated by specs. Two items with the same name CANNOT be added within the same storage or sub-storage. Raised when trying to load a Compound File with invalid, corrupted or mismatched fields (4.1 - specifications) This exception is NOT raised when Compound file has been opened with NO_VALIDATION_EXCEPTION option. Represents a binary tree. This class provides access to the Root of the tree. The developer must manually create the binary tree by adding descendents to the root. The type of data stored in the binary tree's nodes. Clears out the contents of the binary tree. Stream decorator for a Sector or miniSector chain The RedBlackEnumerator class returns the keys or data objects of the treap in sorted order. Determine order, walk the tree and push the nodes onto the stack HasMoreElements NextElement MoveNext For .NET compatibility Key Data The RedBlackException class distinguishes read black tree exceptions from .NET exceptions. openmcdf-1.5.4/help/sample structured storage eXplorer/Readme.txt0000644000175000017500000000032711550043716024734 0ustar mathieumathieuSample Structured Storage explorer source code is located in main src directory of the distribution as TESTOpenMCDF subdirectory. -- This application has to be considered ONLY a sample and NOT a production tool.openmcdf-1.5.4/help/sample structured storage eXplorer/StucturedStorageExplorer.exe.config0000644000175000017500000000156311546170036032001 0ustar mathieumathieu
True openmcdf-1.5.4/License.txt0000644000175000017500000006316011461307260015317 0ustar mathieumathieu MOZILLA PUBLIC LICENSE Version 1.1 --------------- 1. Definitions. 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. "Executable" means Covered Code in any form other than Source Code. 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. "License" means this document. 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code. 6. Versions of the License. 6.1. New Versions. Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. 6.3. Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL", "NPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) 7. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 8. TERMINATION. 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that: (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant. 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. 9. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 10. U.S. GOVERNMENT END USERS. The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. 11. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. 12. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. 13. MULTIPLE-LICENSED CODE. Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A. EXHIBIT A -Mozilla Public License. ``The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved. Contributor(s): ______________________________________. Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License." [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.] openmcdf-1.5.4/OpenMcdf.xml0000644000175000017500000017365412061073514015422 0ustar mathieumathieu OpenMcdf Structured Storage signature This integer field contains the starting sector number for the mini FAT Provides the set of values by which a binary search tree can be enumerated. Action to implement when transaction support - sector has to be written to the underlying stream (see specs). Ad-hoc Heap Friendly sector collection to avoid using large array that may create some problem to GC collection (see http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/ ) Abstract base class for Structured Storage entities. const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); // CFItem represents both storage and stream items VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); Get entity name Size in bytes of the item. It has a valid value only if entity is a stream, otherwise it is setted to zero. Return true if item is Storage This check doesn't use reflection or runtime type information and doesn't suffer related performance penalties. Return true if item is a Stream This check doesn't use reflection or runtime type information and doesn't suffer related performance penalties. Return true if item is the Root Storage This check doesn't use reflection or runtime type information and doesn't suffer related performance penalties. Get/Set the Creation Date of the current item Get/Set the Modify Date of the current item Get/Set Object class Guid for Root and Storage entries. Represents a collection of Node<T> instances. The type of data held in the Node instances referenced by this class. Searches the NodeList for a Node containing a particular value. The value to search for. The Node in the NodeList, if it exists; null otherwise. Represents a binary search tree. A binary search tree is a binary tree whose nodes are arranged such that for any given node k, all nodes in k's left subtree have a value less than k, and all nodes in k's right subtree have a value greater than k. The type of data stored in the binary tree nodes. Removes the contents of the BST Copies the contents of the BST to an appropriately-sized array of type T, using the Inorder traversal method. Copies the contents of the BST to an appropriately-sized array of type T, using a specified traversal method. Adds a new value to the BST. The data to insert into the BST. Adding a value already in the BST has no effect; that is, the SkipList is not altered, the Add() method simply exits. Returns a Boolean, indicating if a specified value is contained within the BST. The data to search for. True if data is found in the BST; false otherwise. Returns a Boolean, indicating if a specified value is contained within the BST. The data to search for. True if data is found in the BST; false otherwise. Attempts to remove the specified data element from the BST. The data to remove from the BST. True if the element is found in the tree, and removed; false if the element is not found in the tree. Enumerates the BST's contents using inorder traversal. An enumerator that provides inorder access to the BST's elements. Enumerates the BST's contents using a specified traversal method. The type of traversal to perform. An enumerator that provides access to the BST's elements using a specified traversal technique. Provides enumeration through the BST using preorder traversal. Provides enumeration through the BST using inorder traversal. Provides enumeration through the BST using postorder traversal. Returns the number of elements in the BST. Binary File Format Version. Sector size is 512 byte for version 3, 4096 for version 4 Compound file version 3 - The default and most common version available. Sector size 512 bytes, 2GB max file size. Compound file version 4 - Sector size is 4096 bytes. Using this version could bring some compatibility problem with existing applications. Update mode of the compound file. Default is ReadOnly. ReadOnly update mode prevents overwriting of the opened file. Data changes are allowed but they have to be persisted on a different file when required. Update mode allows subsequent data changing operations to be persisted directly on the opened file or stream using the Commit method when required. Warning: this option may cause existing data loss if misused. Standard Microsoft© Compound File implementation. It is also known as OLE/COM structured storage and contains a hierarchy of storage and stream objects providing efficent storage of multiple kinds of documents in a single file. Version 3 and 4 of specifications are supported. Number of DIFAT entries in the header Sector ID Size (int) Initial capacity of the flushing queue used to optimize commit writing operations Maximum size of the flushing buffer used to optimize commit writing operations Returns the size of standard sectors switching on CFS version (3 or 4) Standard sector size Number of FAT entries in a DIFAT Sector Sectors ID entries in a FAT Sector Flag for sector recycling. Flag for unallocated sector zeroing out. CompoundFile header Compound underlying stream. Null when new CF has been created. Create a blank, version 3 compound file. Sector recycle is turned off to achieve the best reading/writing performance in most common scenarios. byte[] b = new byte[10000]; for (int i = 0; i < 10000; i++) { b[i % 120] = (byte)i; } CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); myStream.SetData(b); cf.Save("MyCompoundFile.cfs"); cf.Close(); Create a new, blank, compound file. Use a specific Compound File Version to set 512 or 4096 bytes sectors If true, recycle unused sectors If true, unallocated sectors will be overwritten with zeros byte[] b = new byte[10000]; for (int i = 0; i < 10000; i++) { b[i % 120] = (byte)i; } CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, true, true); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); myStream.SetData(b); cf.Save("MyCompoundFile.cfs"); cf.Close(); Sector recycling reduces data writing performances but avoids space wasting in scenarios with frequently data manipulation of the same streams. The new compound file is open in Update mode. Load an existing compound file. Compound file to read from //A xls file should have a Workbook stream String filename = "report.xls"; CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); File will be open in read-only mode: it has to be saved with a different filename. A wrapping implementation has to be provided in order to remove/substitute an existing file. Version will be automatically recognized from the file. Sector recycle is turned off to achieve the best reading/writing performance in most common scenarios. Load an existing compound file. Compound file to read from If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors String srcFilename = "data_YOU_CAN_CHANGE.xls"; CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true); Random r = new Random(); byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); cf.RootStorage.AddStream("MyStream").SetData(buffer); //This will persist data to the underlying media. cf.Commit(); cf.Close(); Load an existing compound file. Compound file to read from If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors If true, no CFCorruptedFileException will be thrown even if corrupted file is loaded. Please note that this option is could pose a potential security threat String srcFilename = "data_YOU_CAN_CHANGE.xls"; CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true, true); Random r = new Random(); byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); cf.RootStorage.AddStream("MyStream").SetData(buffer); //This will persist data to the underlying media. cf.Commit(); cf.Close(); Load an existing compound file. A stream containing a compound file to read If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors String filename = "reportREAD.xls"; FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); Raised when trying to open a non-seekable stream Raised stream is null Load an existing compound file. A stream containing a compound file to read If true, recycle unused sectors Select the update mode of the underlying data file If true, overwrite with zeros unallocated sectors If true, openMcdf will try to ignore invalid references or format in order to load a possibly corrupted file anyway The 'noValidationEcxception' parameter could possibly lead to security issues so it's recommanded to use it only on trusted sources String filename = "reportREAD.xls"; FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false, false, true); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); //if 'reportRead.xls' is corrupted, openMcdf will try to lad it anyway [noValidationException set true] Assert.IsNotNull(temp); cf.Close(); Raised when trying to open a non-seekable stream Raised stream is null Load an existing compound file from a stream. Streamed compound file String filename = "reportREAD.xls"; FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); Raised when trying to open a non-seekable stream Raised stream is null Commit data changes since the previously commit operation to the underlying supporting stream or file on the disk. This method can be used only if the supporting stream has been opened in Update mode. Commit data changes since the previously commit operation to the underlying supporting stream or file on the disk. If true, release loaded sectors to limit memory usage but reduces following read operations performance This method can be used only if the supporting stream has been opened in Update mode. Load compound file from an existing stream. Stream to load compound file from Allocate space, setup sectors id and refresh header for the new or updated mini sector chain. The new MINI sector chain Allocate space, setup sectors id and refresh header for the new or updated sector chain. The new or updated generic sector chain Check for transaction lock sector addition and mark it in the FAT. Allocate space, setup sectors id and refresh header for the new or updated FAT sector chain. The new or updated generic sector chain Setup the DIFAT sector chain A FAT sector chain Get the DIFAT Sector chain A list of DIFAT sectors Get the FAT sector chain List of FAT sectors Get a standard sector chain First SecID of the required chain A list of sectors Get a mini sector chain First SecID of the required chain A list of mini sectors (64 bytes) Get a sector chain from a compound file given the first sector ID and the required sector type. First chain sector's id Type of Sectors in the required chain (mini sectors, normal sectors or FAT) A list of Sectors as the result of their concatenation Reset a directory entry setting it to StgInvalid in the Directory. Sid of the directory to invalidate Load directory entries from compound file. Header and FAT MUST be already loaded. Commit directory entries change on the Current Source stream Saves the in-memory image of Compound File to a file. File name to write the compound file to Raised if destination file is not seekable Saves the in-memory image of Compound File to a stream. Destination Stream must be seekable. The stream to save compound File to Raised if destination stream is not seekable Raised if Compound File Storage has been already disposed MemoryStream ms = new MemoryStream(size); CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = new byte[]{0x00,0x01,0x02,0x03}; sm.SetData(b); cf.Save(ms); cf.Close(); Scan FAT o miniFAT for free sectors to reuse. Type of sector to look for A stack of available sectors or minisectors already allocated Check file size limit ( 2GB for version 3 ) Close the Compound File object CompoundFile and free all associated resources (e.g. open file handle and allocated memory). When the Close method is called, all the associated stream and storage objects are invalidated: any operation invoked on them will produce a CFDisposedException. const String FILENAME = "CompoundFile.cfs"; CompoundFile cf = new CompoundFile(FILENAME); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); cf.Close(); try { byte[] temp = st.GetStream("MyStream").GetData(); // The following line will fail because back-end object has been closed Assert.Fail("Stream without media"); } catch (Exception ex) { Assert.IsTrue(ex is CFDisposedException); } When called from user code, release all resources, otherwise, in the case runtime called it, only unmanagd resources are released. If true, method has been called from User code, if false it's been called from .net runtime Get a list of all entries with a given name contained in the document. Name of entries to retrive A list of name-matching entries This function is aimed to speed up entity lookup in flat-structure files (only one or little more known entries) without the performance penalty related to entities hierarchy constraints. There is no implied hierarchy in the returned list. Compress free space by removing unallocated sectors from compound file effectively reducing stream or file size. Current implementation supports compression only for ver. 3 compound files. //This code has been extracted from unit test String FILENAME = "MultipleStorage3.cfs"; FileInfo srcFile = new FileInfo(FILENAME); File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); //17Kb cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsTrue(srcFile.Length > dstFile.Length); Remove unallocated sectors from compound file in order to reduce its size. Current implementation supports compression only for ver. 3 compound files. //This code has been extracted from unit test String FILENAME = "MultipleStorage3.cfs"; FileInfo srcFile = new FileInfo(FILENAME); File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); //17Kb cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsTrue(srcFile.Length > dstFile.Length); Recursively clones valid structures, avoiding to copy free sectors. Current source storage to clone Current cloned destination storage Return true if this compound file has been loaded from an existing file or stream The entry point object that represents the root of the structures tree to get or set storage or stream data. //Create a compound file string FILENAME = "MyFileName.cfs"; CompoundFile ncf = new CompoundFile(); CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1"); l1.AddStream("l1ns1"); l1.AddStream("l1ns2"); l1.AddStream("l1ns3"); CFStorage l2 = l1.AddStorage("Storage Level 2"); l2.AddStream("l2ns1"); l2.AddStream("l2ns2"); ncf.Save(FILENAME); ncf.Close(); OLE structured storage stream Object It is contained inside a Storage object in a file-directory relationship and indexed by its name. Set the data associated with the stream object. byte[] b = new byte[]{0x0,0x1,0x2,0x3}; CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); myStream.SetData(b); Data bytes to write to this stream Append the provided data to stream data. byte[] b = new byte[]{0x0,0x1,0x2,0x3}; byte[] b2 = new byte[]{0x4,0x5,0x6,0x7}; CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); myStream.SetData(b); // here we could also have invoked .AppendData myStream.AppendData(b2); cf.Save("MyLargeStreamsFile.cfs); cf.Close(); Data bytes to append to this stream This method allows user to create stream with more than 2GB of data, appending data to the end of existing ones. Large streams (>2GB) are only supported by CFS version 4. Append data can also be invoked on streams with no data in order to simplify its use inside loops. Get the data associated with the stream object. CompoundFile cf2 = new CompoundFile("AFileName.cfs"); CFStream st = cf2.RootStorage.GetStream("MyStream"); byte[] buffer = st.GetData(); Array of byte containing stream data Raised when the owner compound file has been closed. Get bytes associated with the stream object, starting from a provided . When method returns, count will contain the effective count of bytes read. CompoundFile cf = new CompoundFile("AFileName.cfs"); CFStream st = cf.RootStorage.GetStream("MyStream"); int count = 8; // The stream is supposed to have a length greater than offset + count byte[] data = st.GetData(20, ref count); cf.Close(); Array of byte containing stream data Raised when the owner compound file has been closed. Copy data from an existing stream. A stream to read from Input stream is NOT closed after method invocation. Action to apply to visited items in the OLE structured storage Currently visited item //We assume that xls file should be a valid OLE compound file const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); Storage entity that acts like a logic container for streams or substorages in a compound file. Create a new CFStorage The Storage Owner - CompoundFile Create a CFStorage using an existing directory (previously loaded). The Storage Owner - CompoundFile An existing Directory Entry Create a new child stream inside the current storage The new stream name The new stream reference Raised when adding an item with the same name of an existing one Raised when adding a stream to a closed compound file Raised when adding a stream with null or empty name String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(220, 0x0A); sm.SetData(b); cf.Save(filename); Get a named stream contained in the current storage if existing. Name of the stream to look for A stream reference if existing Raised if trying to delete item from a closed compound file Raised if item to delete is not found String filename = "report.xls"; CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); Get a named storage contained in the current one if existing. Name of the storage to look for A storage reference if existing. Raised if trying to delete item from a closed compound file Raised if item to delete is not found String FILENAME = "MultipleStorage2.cfs"; CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); Assert.IsNotNull(st); cf.Close(); Create new child storage directory inside the current storage. The new storage name Reference to the new storage Raised when adding an item with the same name of an existing one Raised when adding a storage to a closed compound file Raised when adding a storage with null or empty name String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(220, 0x0A); sm.SetData(b); cf.Save(filename); Visit all entities contained in the storage applying a user provided action Raised when visiting items of a closed compound file User action to apply to visited entities Visiting recursion level. True means substorages are visited recursively, false indicates that only the direct children of this storage are visited const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); Remove an entry from the current storage and compound file. The name of the entry in the current storage to delete cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false); cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist. cf.Commit(true); cf.Close(); Raised if trying to delete item from a closed compound file Raised if item to delete is not found Raised if trying to delete root storage FNV hash, short for Fowler/Noll/Vo (not warranted) unique hash for byte array The BinaryTreeNode class represents a node in a binary tree, or a binary search tree. It has precisely two neighbors, which can be accessed via the Left and Right properties. The type of data stored in the binary tree node. The Node<T> class represents the base concept of a Node for a tree or graph. It contains a data item of type T, and a list of neighbors. The type of data contained in the Node. None of the classes in the SkmDataStructures2 namespace use the Node class directly; they all derive from this class, adding necessary functionality specific to each data structure. When called from user code, release all resources, otherwise, in the case runtime called it, only unmanagd resources are released. If true, method has been called from User code, if false it's been called from .net runtime A red-black tree must satisfy these properties: 1. The root is black. 2. All leaves are black. 3. Red nodes can only have black children. 4. All paths from a node to its leaves contain the same number of black nodes. Add args: ByVal key As IComparable, ByVal data As Object key is object that implements IComparable interface performance tip: change to use use int type (such as the hashcode) RestoreAfterInsert Additions to red-black trees usually destroy the red-black properties. Examine the tree and restore. Rotations are normally required to restore it RotateLeft Rebalance the tree by rotating the nodes to the left RotateRight Rebalance the tree by rotating the nodes to the right RestoreAfterDelete Deletions from red-black trees may destroy the red-black properties. Examine the tree and restore. Rotations are normally required to restore it Used as internal template object for binary tree searches. The RedBlackNode class encapsulates a node in the tree Key Data Color Left Right OpenMCDF base exception. Raised when a data setter/getter method is invoked on a stream or storage object after the disposal of the owner compound file object. Raised when opening a file with invalid header or not supported COM/OLE Structured storage version. Raised when a named stream or a storage object are not found in a parent storage. Raised when a method call is invalid for the current object state Raised when trying to add a duplicated CFItem Items are compared by name as indicated by specs. Two items with the same name CANNOT be added within the same storage or sub-storage. Raised when trying to load a Compound File with invalid, corrupted or mismatched fields (4.1 - specifications) This exception is NOT raised when Compound file has been opened with NO_VALIDATION_EXCEPTION option. Represents a binary tree. This class provides access to the Root of the tree. The developer must manually create the binary tree by adding descendents to the root. The type of data stored in the binary tree's nodes. Clears out the contents of the binary tree. Stream decorator for a Sector or miniSector chain The RedBlackEnumerator class returns the keys or data objects of the treap in sorted order. Determine order, walk the tree and push the nodes onto the stack HasMoreElements NextElement MoveNext For .NET compatibility Key Data The RedBlackException class distinguishes read black tree exceptions from .NET exceptions. openmcdf-1.5.4/Release notes.txt0000644000175000017500000000444412061101616016420 0ustar mathieumathieuver 1.5.4 FIXED: In particular conditions, an opened file could be left opened after a loading exception FIXED: Circular references of corrupted files could lead to stack overflows FIXED: Enanched standard compliance: corrupted file loading defaults to abort operation. ADD: Version property ADD: New overloaded constructors to force the load of possibly corrrupted files. --------- ver 1.5.3 ADD: 'GetAllNamedEntries' Method to access structured files without tree-loading performance penalties ADD: New hex editor for stuctured storage explorer sample application --------- ver 1.5.2 FIXED: Math error in sector number recognition caused exception when reading some streams FIXED: Saving twice caused OutOfMemoryException FIXED: Error when using names of exactly 31 characters for streams or storages --------- ver: 1.5.1. FIXED: Casting error when removing uncommitted-added Stream. ADDED: CFDuplicatedItem exception thrown when trying to add duplicated items (previously item addition was silently failing). --------- ver: 1.5.0 FIXED: Exception thrown when removing a stream of length equals to zero. --------- ver: 1.5.0 - RC1 ADD: New Update mode to commit changes to the underlying stream ADD: Sector recycle to reuse unallocated sectors ADD: File shrinking to compact compound files ADD: Support for version 4 of specs (4096 bytes sectors) ADD: Partial stream data reading to read data from a specified offset ADD: Advanced lazy loading to reduce memory footprint of application !! FIXED: CHANGED NAMESPACE to OpenMcdf !! -------- ver: 1.4.1 FIXED: ERROR, internal modifier applied to Delete method FIXED: Redundant method call for DIFAT chain ADD: 'Delete' feature for sample project -------- ver. 1.4.0 ADD: 'Remove' feature for storage and stream objects. FIXED: ERROR in manipulation of streams with a length of 4096 bytes (cutoff bug) (Thx to meddingt) FIXED: ERROR in zero sized streams -------- ver. 1.3.1 FIXED: Error in DIFAT sectors manipulation -------- ver. 1.3 FIXED: Null pointer in traversal with empty storages; -------- ver. 1.2 FIXED: Fixed ministream (<4096 bytes) bug; -------- ver. 1.1 ADD: Added traversal of Compound file method (VisitEntries); FIXED: Fixed bug when multiple storage added; -------- ver. 1.0 Initial releaseopenmcdf-1.5.4/src/0000755000175000017500000000000012061101754013752 5ustar mathieumathieuopenmcdf-1.5.4/src/Clean.cmd0000644000175000017500000000143411546167614015501 0ustar mathieumathieurd /s /Q "OleCfsMemoryTest\bin\Debug" rd /s /Q OleCfsMemoryTest\bin\Release rd /s /Q OleCfsMemoryTest\obj\Debug rd /s /Q OleCfsMemoryTest\obj\Release rd /s /Q OLECFSTest\bin\Debug rd /s /Q OLECFSTest\bin\Release rd /s /Q OLECFSTest\obj\Debug rd /s /Q OLECFSTest\obj\Release rd /s /Q OLECompoundFileStorage\bin\Debug rd /s /Q OLECompoundFileStorage\bin\Release rd /s /Q OLECompoundFileStorage\obj\Debug rd /s /Q OLECompoundFileStorage\obj\Release rd /s /Q OleCfsMemoryTest\bin\Debug rd /s /Q OleCfsMemoryTest\bin\Release rd /s /Q OleCfsMemoryTest\obj\Debug rd /s /Q OleCfsMemoryTest\obj\Release rd /s /Q TESTOpenMCDF\bin\Debug rd /s /Q TESTOpenMCDF\bin\Release rd /s /Q TESTOpenMCDF\obj\Debug rd /s /Q TESTOpenMCDF\obj\Release rd /s /Q TestResultsopenmcdf-1.5.4/src/Html Help/0000755000175000017500000000000012061101736015527 5ustar mathieumathieuopenmcdf-1.5.4/src/Html Help/Help/0000755000175000017500000000000012061101736016417 5ustar mathieumathieuopenmcdf-1.5.4/src/Html Help/OpenMcdfHelp.shfbproj0000644000175000017500000000653512042056250021602 0ustar mathieumathieu Debug AnyCPU 2.0 {6b2e0fe5-8246-4f87-9663-d72ebadfc53f} 1.8.0.0 Documentation Documentation Documentation .\Help\ OpenMCDF Copyright 2010 - 2012 &#169%3b Federico Blaseotto Open MCDF Open MCDF Open MCDF Summary, AutoDocumentCtors ironfede%40users.sourceforge.net Prototype en-US InheritedMembers, InheritedFrameworkMembers 1.5.3.0 Hierarchical openmcdf-1.5.4/src/LocalTestRun.testrunconfig0000644000175000017500000000063611560547704021166 0ustar mathieumathieu This is a default test run configuration for a local test run. openmcdf-1.5.4/src/OleCfsMemoryTest/0000755000175000017500000000000012061101740017151 5ustar mathieumathieuopenmcdf-1.5.4/src/OleCfsMemoryTest/OpenMcdfMemTest.csproj0000644000175000017500000000457111546167634023421 0ustar mathieumathieu Debug AnyCPU 9.0.30729 2.0 {E2BAD82D-3040-462B-BAA2-6E608A9054F4} Exe Properties OpenMcdfMemTest OpenMcdfMemTest v2.0 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1} OpenMcdf openmcdf-1.5.4/src/OleCfsMemoryTest/Program.cs0000644000175000017500000002461011546413610021123 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using OpenMcdf; using System.IO; using System.Diagnostics; using System.Threading; //This project is used for profiling memory and performances of OpenMCDF . namespace OpenMcdfMemTest { class Program { static void Main(string[] args) { //TestMultipleStreamCommit(); TestCode(); //StressMemory(); //DummyFile(); //Console.WriteLine("CLOSED"); //Console.ReadKey(); } private static void TestCode() { const int N_FACTOR = 1000; byte[] bA = GetBuffer(20 * 1024 * N_FACTOR, 0x0A); byte[] bB = GetBuffer(5 * 1024, 0x0B); byte[] bC = GetBuffer(5 * 1024, 0x0C); byte[] bD = GetBuffer(5 * 1024, 0x0D); byte[] bE = GetBuffer(8 * 1024 * N_FACTOR + 1, 0x1A); byte[] bF = GetBuffer(16 * 1024 * N_FACTOR, 0x1B); byte[] bG = GetBuffer(14 * 1024 * N_FACTOR, 0x1C); byte[] bH = GetBuffer(12 * 1024 * N_FACTOR, 0x1D); byte[] bE2 = GetBuffer(8 * 1024 * N_FACTOR, 0x2A); byte[] bMini = GetBuffer(1027, 0xEE); Stopwatch sw = new Stopwatch(); sw.Start(); var cf = new CompoundFile(CFSVersion.Ver_3, true, false); cf.RootStorage.AddStream("A").SetData(bA); cf.Save("OneStream.cfs"); cf.Close(); cf = new CompoundFile("OneStream.cfs", UpdateMode.ReadOnly, true, false); cf.RootStorage.AddStream("B").SetData(bB); cf.RootStorage.AddStream("C").SetData(bC); cf.RootStorage.AddStream("D").SetData(bD); cf.RootStorage.AddStream("E").SetData(bE); cf.RootStorage.AddStream("F").SetData(bF); cf.RootStorage.AddStream("G").SetData(bG); cf.RootStorage.AddStream("H").SetData(bH); cf.Save("8_Streams.cfs"); cf.Close(); File.Copy("8_Streams.cfs", "6_Streams.cfs", true); cf = new CompoundFile("6_Streams.cfs", UpdateMode.Update, true, true); cf.RootStorage.Delete("D"); cf.RootStorage.Delete("G"); cf.Commit(); cf.Close(); File.Copy("6_Streams.cfs", "6_Streams_Shrinked.cfs", true); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStream("ZZZ").SetData(bF); cf.RootStorage.GetStream("E").AppendData(bE2); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.CLSID = new Guid("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStorage("MyStorage").AddStream("ANS").AppendData(bE); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStorage("AnotherStorage").AddStream("ANS").AppendData(bE); cf.RootStorage.Delete("MyStorage"); cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("6_Streams_Shrinked.cfs"); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStorage("MiniStorage").AddStream("miniSt").AppendData(bMini); cf.RootStorage.GetStorage("MiniStorage").AddStream("miniSt2").AppendData(bMini); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.GetStorage("MiniStorage").Delete("miniSt"); cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").AppendData(bE); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.ReadOnly, true, false); var myStream = cf.RootStorage.GetStream("C"); var data = myStream.GetData(); Console.WriteLine(data[0] + " : " + data[data.Length - 1]); myStream = cf.RootStorage.GetStream("B"); data = myStream.GetData(); Console.WriteLine(data[0] + " : " + data[data.Length - 1]); cf.Close(); sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); Console.ReadKey(); } private static void StressMemory() { const int N_LOOP = 20; const int MB_SIZE = 10; byte[] b = GetBuffer(1024 * 1024 * MB_SIZE); //2GB buffer byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, false, false); CFStream st = cf.RootStorage.AddStream("MySuperLargeStream"); cf.Save("LARGE.cfs"); cf.Close(); //Console.WriteLine("Closed save"); //Console.ReadKey(); cf = new CompoundFile("LARGE.cfs", UpdateMode.Update, false, false); CFStream cfst = cf.RootStorage.GetStream("MySuperLargeStream"); Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < N_LOOP; i++) { cfst.AppendData(b); cf.Commit(true); Console.WriteLine(" Updated " + i.ToString()); //Console.ReadKey(); } cfst.AppendData(cmp); cf.Commit(true); sw.Stop(); cf.Close(); Console.WriteLine(sw.Elapsed.TotalMilliseconds); sw.Reset(); //Console.WriteLine(sw.Elapsed.TotalMilliseconds); //Console.WriteLine("Closed Transacted"); //Console.ReadKey(); cf = new CompoundFile("LARGE.cfs"); int count = 8; sw.Reset(); sw.Start(); byte[] data = cf.RootStorage.GetStream("MySuperLargeStream").GetData(b.Length * (long)N_LOOP, ref count); sw.Stop(); Console.Write(data.Length); cf.Close(); Console.WriteLine("Closed Final " + sw.ElapsedMilliseconds); Console.ReadKey(); } private static void DummyFile() { Console.WriteLine("Start"); FileStream fs = new FileStream("myDummyFile", FileMode.Create); fs.Close(); Stopwatch sw = new Stopwatch(); byte[] b = GetBuffer(1024 * 1024 * 50); //2GB buffer fs = new FileStream("myDummyFile", FileMode.Open); sw.Start(); for (int i = 0; i < 42; i++) { fs.Seek(b.Length * i, SeekOrigin.Begin); fs.Write(b, 0, b.Length); } fs.Close(); sw.Stop(); Console.WriteLine("Stop - " + sw.ElapsedMilliseconds); sw.Reset(); Console.ReadKey(); } private static void AddNodes(String depth, CFStorage cfs) { VisitedEntryAction va = delegate(CFItem target) { String temp = target.Name + (target is CFStorage ? "" : " (" + target.Size + " bytes )"); //Stream Console.WriteLine(depth + temp); if (target is CFStorage) { //Storage String newDepth = depth + " "; //Recursion into the storage AddNodes(newDepth, (CFStorage)target); } }; //Visit NON-recursively (first level only) cfs.VisitEntries(va, false); } public static void TestMultipleStreamCommit() { String srcFilename = Directory.GetCurrentDirectory() + @"\testfile\report.xls"; String dstFilename = Directory.GetCurrentDirectory() + @"\testfile\reportOverwriteMultiple.xls"; //Console.WriteLine(Directory.GetCurrentDirectory()); //Console.ReadKey(); File.Copy(srcFilename, dstFilename, true); CompoundFile cf = new CompoundFile(dstFilename, UpdateMode.Update, true, false); Random r = new Random(); DateTime start = DateTime.Now; for (int i = 0; i < 1000; i++) { byte[] buffer = GetBuffer(r.Next(100, 3500), 0x0A); if (i > 0) { if (r.Next(0, 100) > 50) { cf.RootStorage.Delete("MyNewStream" + (i - 1).ToString()); } } CFStream addedStream = cf.RootStorage.AddStream("MyNewStream" + i.ToString()); addedStream.SetData(buffer); // Random commit, not on single addition if (r.Next(0, 100) > 50) cf.Commit(); } cf.Close(); TimeSpan sp = (DateTime.Now - start); Console.WriteLine(sp.TotalMilliseconds); } private static byte[] GetBuffer(int count) { Random r = new Random(); byte[] b = new byte[count]; r.NextBytes(b); return b; } private static byte[] GetBuffer(int count, byte c) { byte[] b = new byte[count]; for (int i = 0; i < b.Length; i++) { b[i] = c; } return b; } private static bool CompareBuffer(byte[] b, byte[] p) { if (b == null && p == null) throw new Exception("Null buffers"); if (b == null && p != null) return false; if (b != null && p == null) return false; if (b.Length != p.Length) return false; for (int i = 0; i < b.Length; i++) { if (b[i] != p[i]) return false; } return true; } } } openmcdf-1.5.4/src/OleCfsMemoryTest/Properties/0000755000175000017500000000000012061101740021305 5ustar mathieumathieuopenmcdf-1.5.4/src/OleCfsMemoryTest/Properties/AssemblyInfo.cs0000644000175000017500000000264011546167646024261 0ustar mathieumathieuusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("OpenMcdfMemTest")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Federico Blaseotto")] [assembly: AssemblyProduct("OpenMcdfMemTest")] [assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("6be95c7d-5e29-4e84-b1d0-ac5a0de6bfe2")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.5.0.*")] openmcdf-1.5.4/src/OleCfsMemoryTest/testfile/0000755000175000017500000000000012061101742020772 5ustar mathieumathieuopenmcdf-1.5.4/src/OleCfsMemoryTest/testfile/report.xls0000644000175000017500000004100011546167624023052 0ustar mathieumathieuࡱ>  Root Entry F;~EWorkbook3SummaryInformation(DocumentSummaryInformation8 \pBLASEOTTO FEDERICO Ba==F8X@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri3" "\ #,##0;\-" "\ #,##0=" "\ #,##0;[Red]\-" "\ #,##0?" "\ #,##0.00;\-" "\ #,##0.00I"" "\ #,##0.00;[Red]\-" "\ #,##0.00q*6_-" "\ * #,##0_-;\-" "\ * #,##0_-;_-" "\ * "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-,>_-" "\ * #,##0.00_-;\-" "\ * #,##0.00_-;_-" "\ * "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                                                                       ff + ) , *     P  P        `            a>  ||>gK}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}+ _-;_-* "}-}, _-;_-* "}-}- _-;_-* "}-}. _-;_-* "}-}: _-;_-* "}-}; _-;_-* "}A}1 _-;_-* " * #}A}2 _-;_-* "? * #}A}3 _-;_-* "23 * #}-}4 _-;_-* "}A}0 a_-;_-* " * #}A}( _-;_-* " * #}A}7 e_-;_-* " * #}}5 ??v_-;_-* "̙ * #0_-; * " ;_-@ }}9 ???_-;_-* " * #???0_-;??? * " ???;_-@ ???}}) }_-;_-* " * #0_-; * " ;_-@ }A}6 }_-;_-* " * #}}* _-;_-* " * #???0_-;??? * " ???;_-@ ???}-}= _-;_-* "}}8 _-;_-* " * #0_-; * " ;_-@ }-}/ _-;_-* "}U}< _-;_-* " * #0_-;}A}" _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}# _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}$ _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}% _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}& _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}  _-;_-* "23 * #}A}' _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}! _-;_-* "23 * # 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L湸 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ٗ % 60% - Accent3M( 60% - Accent3 23֚ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleLight16`%/Sheet10Sheet2a2Sheet3'hello . ccB  ;0u0  dMbP?_*+%,&ffffff?'ffffff?(?)?",B333333?333333?t&<3U, ">@ggD  1  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  s3  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  Oh+'0@Hd BLASEOTTO FEDERICOBLASEOTTO FEDERICOMicrosoft Excel@f`@rq՜.+,0 PXd lt| - Sheet1Sheet2Sheet3  Worksheets F&Microsoft Office Excel 2003 WorksheetBiff8Excel.Sheet.89qCompObj ropenmcdf-1.5.4/src/OleCfsMemoryTest/testfile/reportOverwriteMultiple.xls0000644000175000017500000677400011546167632026517 0ustar mathieumathieuࡱ>       2 >!"#$%&'()*+,-./013E456789:;<=?@AVBCDFSGHIJKLMNOPQRTiUW~XYZ[\]^_`abcdefghjvklmnopqrstuwxyz{|}Root Entry F;~EWorkbook3SummaryInformation(DocumentSummaryInformation8 \pBLASEOTTO FEDERICO Ba==F8X@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri3" "\ #,##0;\-" "\ #,##0=" "\ #,##0;[Red]\-" "\ #,##0?" "\ #,##0.00;\-" "\ #,##0.00I"" "\ #,##0.00;[Red]\-" "\ #,##0.00q*6_-" "\ * #,##0_-;\-" "\ * #,##0_-;_-" "\ * "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-,>_-" "\ * #,##0.00_-;\-" "\ * #,##0.00_-;_-" "\ * "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                                                                       ff + ) , *     P  P        `            a>  ||>gK}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}+ _-;_-* "}-}, _-;_-* "}-}- _-;_-* "}-}. _-;_-* "}-}: _-;_-* "}-}; _-;_-* "}A}1 _-;_-* " * #}A}2 _-;_-* "? * #}A}3 _-;_-* "23 * #}-}4 _-;_-* "}A}0 a_-;_-* " * #}A}( _-;_-* " * #}A}7 e_-;_-* " * #}}5 ??v_-;_-* "̙ * #0_-; * " ;_-@ }}9 ???_-;_-* " * #???0_-;??? * " ???;_-@ ???}}) }_-;_-* " * #0_-; * " ;_-@ }A}6 }_-;_-* " * #}}* _-;_-* " * #???0_-;??? * " ???;_-@ ???}-}= _-;_-* "}}8 _-;_-* " * #0_-; * " ;_-@ }-}/ _-;_-* "}U}< _-;_-* " * #0_-;}A}" _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}# _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}$ _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}% _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}& _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}  _-;_-* "23 * #}A}' _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}! _-;_-* "23 * # 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L湸 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ٗ % 60% - Accent3M( 60% - Accent3 23֚ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleLight16`%/Sheet10Sheet2a2Sheet3'hello . ccB  ;0u0  dMbP?_*+%,&ffffff?'ffffff?(?)?",B333333?333333?t&<3U, ">@ggD  1  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  s3  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnpqrstuvwxyz{|}~Oh+'0@Hd BLASEOTTO FEDERICOBLASEOTTO FEDERICOMicrosoft Excel@f`@rq՜.+,0 PXd lt| - Sheet1Sheet2Sheet3  Worksheets F&Microsoft Office Excel 2003 WorksheetBiff8Excel.Sheet.89q CompObj rMyNewStream0MyNewStream1A MyNewStream5 =  MyNewStream6 |MyNewStream7 MyNewStream8 MyNewStream9 MyNewStream10 MyNewStream11 MyNewStream12 MyNewStream14q      !"#$%&'()*+,-./0123456789:;<=>? @ABCDEFGHIJKLMN PQRSTUVWXYZ[\]^` `abcdefghijkln rstuvwxyz{|}~ /   MyNewStream16 MyNewStream17 MyNewStream20" MyNewStream22_1 R      #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMjNPQRSTUVWXYZ[\]^`abcdefhijklmngpqrstuvwxyz{|}~  MyNewStream23uMyNewStream24,MyNewStream25 MyNewStream26ot       !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHJKLMNOPQRSTUVWXYZ[\]^`cdefghijklmnopqrstuvwxyz{|}~ MyNewStream27 * MyNewStream28I1MyNewStream30b MyNewStream31  MyNewStream32 MyNewStream33 MyNewStream34dMyNewStream35        !"!#$%&'()*+,-.!0123456789:;>?@ABCDEFG&"IJKLMN"PQRSTUVWYZ[\]^S#`abcdefghijklmn#pqrstuvwxyz{|}~ MyNewStream37!MyNewStream41"MyNewStream43#$MyNewStream44$~ $$%%&&R'      !"#$%&'()*+,-./012345789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcefghijklmnopqrstuvwxyz{|}~ MyNewStream45%w MyNewStream46&6Z MyNewStream47'd MyNewStream49( , --..J// MyNewStream52)Y MyNewStream54*e^ MyNewStream55+s MyNewStream57, 0      !"#$%&'()*+,-./0123456789:;<=>02ABCDEGHIJKLMN2PQRSTUVWXYZ[\]^3`abcfghijklmnopqrstuvwxyz{|}~      .( !"#$%&')U*+,-/=0123456789:;<>L?@ABCDEFGHIJKM`NOPQRSTVWXYZ[\]^_arbcdefghijklmnopqstuvwxyz{|}~  6677 8     "#$%&')*+,-./0123456789:;<=>?@&:ABCDEFGHIJKLMN:PQRSTVWXYZ[\]^.;`abcdefghijklmn;pqrstuvwxyz{|}~ MyNewStream59-aMyNewStream60. MyNewStream61/!MyNewStream620(> MyNewStream731DMyNewStream742TMyNewStream763OMyNewStream774w %<<2=='>>0?? 2@     @A !"#$&'()*+,-.A0123456789:;<=>B@ABEFGHIJKLMNOPQRSUVWXYZ[\]^_`abcdefghijklmn CCpqrstuvwxyz{|}~ DEE4FG MyNewStream795 MyNewStream8265 MyNewStream837F MyNewStream878  H          H               5I ! " # $ % & ' ( ) * + , - . I0 1 2 3 6 7 8 9 : ; < = > ? @ A B C D E G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u Kv w x y z { } ~ L L M M (N O O lP                  P      8Q ! " # $ % & ' ( ) * + - . Q0 1 2 3 4 5 6 7 8 9 : ; < = > 0R@ A B C D E F G H I J K L M N RP Q R S T U V W X Y Z [ \ ] ^ S` a b c d e f g h i j k l m n q r s t u v w x y z { | } ~   MyNewStream929OcMyNewStream95:p MyNewStream96;_ MyNewStream99<  4V V )W W         2X  X               VY " # $ % & ' ( ) * + , - . Y0 1 2 3 4 5 6 7 8 9 : ; < = > +Z@ A B D E G H I J K L M N ZP Q R S T U V W X Y Z [ \ ] ^ a b c d e f g h i j k l m n o p q r s t u v w x y z { | } [~ MyNewStream100=ouMyNewStream106>m MyNewStream109?pMyNewStream114@: u\ \ ] ] (^ ^ _ _ &`          `               a ! " # $ % & ' ( ) * + , - 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ B C D E F G H I J K L bM N O P Q R S T U V W X Y Z [ \ ] ^ Zc` a b d e f g h i j k l m n cp q r s t u v w x y z { | } ~ MyNewStream115AA MyNewStream119B MyNewStream120CMyNewStream125D                   ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; o p q r s t u v w x y z { | } ~  MyNewStream127En MyNewStream128F MyNewStream129G MyNewStream132H       ". !#6$%&'()*+,-/H0123457C89:;<=>?@ABDXEFGIYJKLMNOPQRSTUVWZg[\]^_`abcdefhyijklmnopqrstuvwxz{|}~   !"#$%&'()*+,-./0123456789:;<=>?@ABDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnpqrstuvwxyz{|}~ MyNewStream133I MyNewStream134JN MyNewStream135KC MyNewStream136LoE  MyNewStream137MMyNewStream138N MyNewStream140O MyNewStream141P!       "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM  MyNewStream147QMyNewStream149R MyNewStream150SMyNewStream151T      STUVWXYZ[\]^_`abcdefghijklmnopqMyNewStream158U MyNewStream161VMyNewStream162W MyNewStream163X1  opqrstuvwxyz{|}~ MyNewStream165YMyNewStream167Z!hMyNewStream170[nC MyNewStream171\]   klmnopqrstuvwxyz{|}~ MyNewStream177]OMMyNewStream182^_ MyNewStream187_oMyNewStream189`?         MyNewStream190aMyNewStream191b MyNewStream192c$ MyNewStream193d       !"#$%&'()*+,-./012356789:;<=>?@ABCDEFG MyNewStream194e4MyNewStream201fMyNewStream202gMyNewStream203h       +* !"#$%&'(),=>-./0123456789:;<?PL@ABCDEFGHIJKMdNOQ`RSTUVWXYZ[\]^_aqbcefghijklmnoprstuvwxyz{|}~      ?@ABCD MyNewStream205i>MyNewStream208jiMyNewStream210kMyNewStream211l`        !"#$%&'()*+,-./012345679:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmopqrstuvwxyz{|}~MyNewStream212m< MyNewStream213nMyNewStream214o0MyNewStream215p8 MyNewStream216qnMyNewStream218r_ MyNewStream219slMyNewStream221t MyNewStream222uDMyNewStream223vMyNewStream224wMyNewStream227xf       !"#$%&'()*+ghijklmnopqrstuvwxyz{|}~  MyNewStream231yMyNewStream233zMyNewStream235{?MyNewStream241|      @ABCDEFGHIJKLMNOPQRSTUV     9:;<=>?@ABCDEFGHIJKLMNOPQ{| MyNewStream243}8QMyNewStream245~zMyNewStream248: MyNewStream249        !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJpqrstuvwxyz{|}~ MyNewStream250MyNewStream251 MyNewStream254o MyNewStream256_  MyNewStream257MyNewStream260aMyNewStream265 MyNewStream267  !"#$%&'()*+       MyNewStream268MyNewStream277MyNewStream280G MyNewStream2829     " !#3$%&'()*+,-./0124F5678:P;<=>?@ABCDEGVHIJKLMNOQdRSTUWkXYZ[\]^_`abcefghijl}mnopqrstuvwxyz{|~   HIJKLMNOPQRSTUVW  MyNewStream283 MyNewStream285p MyNewStream286WMyNewStream288       !"#$%&'(*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ MyNewStream289)6 MyNewStream290Z: MyNewStream291t MyNewStream292  MyNewStream293o MyNewStream297* MyNewStream299\MyNewStream3010 +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRST]^_`abcdefghijklmnopqr   MyNewStream304MyNewStream310 MyNewStream313  MyNewStream314/=    ! !!!!!!!! ! ! ! ! !!!!!!!!!!!!!!!!!! !!!"!#!$!%!&!'!(!)!*!+!,!-!.!0!1!2!3!4!5!6!7!8!9!:!;!"?"A"B"C"D"E"F"G"H"I"J"K"L"M"N"O"P"Q"R"S"T"U"V"W"X"Y"Z"["\"]"^"_"`"a"b"c"e"f"g"h"i"j"k"l"m"n"o"p"q"r"s"t"u"v"w"x"y"z"{"|"~"""MyNewStream329OMyNewStream331HMyNewStream332@"MyNewStream333d"7 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""#MyNewStream334}"MyNewStream335_MyNewStream336X!MyNewStream337" ######## # # # # ##############T#U#V#W#X#MyNewStream338"MyNewStream340"MyNewStream345oUMyNewStream347     0% !"#$&I'()*+,-./1@23456789:;<=>?AQBCDEFGHJeKLMNOPRaSTUVWXYZ[\]^_`bvcdfoghijklmnpqrstuwxyz{|}~ ############################################################################################################# $$$$$$$$$$$$$$$$$$ $!$"$#$$$%$&$'$($)$*$+$,$-$.$/$0$1$2$3$4$5$6$7$8$9$:$;$<$~$$$MyNewStream348#} MyNewStream349# MyNewStream351SMyNewStream352I MyNewStream356}$MyNewStream357x MyNewStream358$ MyNewStream359$6 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% %%%%%%%% % % % % %%%%%%%%%%%%%%%%%P%Q%R%S%T%U%V%W%y%z%{%|%}%~%%% MyNewStream360MyNewStream362O%MyNewStream364x%MyNewStream365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%& &&&&&&& & & & & &&&&&&&&&&&&&&&&&&S&T&U&V&W&X&Y&Z&[&\&]&^&_&`&a&b&c&d&e&&& MyNewStream366%MyNewStream367% MyNewStream368%T MyNewStream369 MyNewStream371R&MyNewStream374~&$MyNewStream375MyNewStream384 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& " S'T'U'V'W'X'Y'Z'['\'^'_'`'a'b'c'd'e'f'g'h'i'j'k'l'm'n'o'p'q'r's't'u'v'w'x'y'z'{'|'}'~''' '''''''''''''''''''''''''''''''''''''''''''''''( MyNewStream385]'MyNewStream389'MyNewStream390'h MyNewStream391 (P (((((((( ( ( ( ((((((((((((((((((( (!("(#($(%( ((((((((((((((((((((((((((((((((((((((((() )))))))) ) ) ){)|)})~))) MyNewStream397({ MyNewStream402z) MyNewStream403)MyNewStream404) )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) MyNewStream4082* MyNewStream409Z* MyNewStream413* MyNewStream414* 3*4*5*6*7*8*9*:*;*<*=*>*?*@*A*B*C*D*E*F*G*H*I*J*K*L*M*N*O*P*Q*R*S*T*U*V*W*X*Y*[*\*]*^*_*`*a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*{*|*}*~***      ) !#1$%&'(*8+,-./02H345679G:;<=>?@ABCDEFI[LJKM`NOPQRSTUVWXYZ\l]^_apbcdefghijkm~noqrstuvwxyz{|}*************************************************************+ ++++++++ + + + + ++++++++++++++++++ +!+"+#+$+%+&+'+(+)+*+++,+.+/+0+1+2+3+4+5+6+7+8+9+:+;+<+=+>+?+@+A+B+C+D+E+F+G+H+I+J+K+L+M+O+ MyNewStream415+MyNewStream416-+MyNewStream417N+|MyNewStream420+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, MyNewStream421+MyNewStream422+MyNewStream423+ MyNewStream424,% ,,,,,,,, , , , , ,,,,,,,,,,,,,,,,,, ,!,",#,$,%,&,',(,),*,+,,,-,.,/,0,1,2,3,4,5,6,7,8,9,:,;,<,=,>,?,@,A,B,C,D,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,[,],^,_,`,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,{,|,},~,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,-MyNewStream425E,MyNewStream426\,Q MyNewStream427MyNewStream429A MyNewStream431,MyNewStream434,MyNewStream435,MyNewStream436d -------- - - - ------------------ -!-"-#-$-%-&-'-(-)-*-,---.-/-0-1-2-3-4-5-6-7-8-9-:-;-<-=->-?-@-A-B-C-D-E-`-a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-{-|-}-~--- MyNewStream437VMyNewStream438-MyNewStream439+-MyNewStream441_- ------------------------------------ MyNewStream442 MyNewStream444-MyNewStream446 MyNewStream448#. ...... . . . . ................... .$.%.&.'.(.).*.+.,.-.../.0.1.2.3.4.5.6.7.8.9.:.;.<.=.>.?.@.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.  MyNewStream453 MyNewStream454.2MyNewStream457. MyNewStream462B ......................................../ //////// / / / / /////////////////// /!/"/#/$/%/&/'/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/[/]/^/_/ /////////////////////////////////////////////////////////////////0MyNewStream463\/MyNewStream4656MyNewStream466MyNewStream467/k MyNewStream469/MyNewStream4703 MyNewStream4730b MyNewStream4740 00000000 0 0 0 0000000000000000000 0!0"0#0$0%0&0'0(0)0*0+0,0-0.0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 MyNewStream4770MyNewStream4780,MyNewStream4790SMyNewStream4801 11111111 1 1 1 1 111111111111111111 1!1"1#1$1%1&1'1(1)1*1+1,1 MyNewStream484?DMyNewStream4861MyNewStream4891MyNewStream490O$ 1111111111111111111111111111111112      "# !$1:%&'()*+,-./02H3456789;V<=>?@ABCDEFGITJKLMNOPQRSUjWoXYZ[\]^_`abcdefghikzlmnpqrstuvwxy{|}~ 22222222 2 2 2 2 22122232425262728292:2;2<2=2>2?2@2A2B2C2D2E2F2G2H2I2J2K2L2M2N2O2P2Q2R2S2U2V2W2X2Y2Z2[2\2]2^2_2`2a2b2c2d2e2f2g2h2j2k2l2n2o2p2q2r2s2t2u2v2w2x2y2z2{2|2}2~222 MyNewStream493FUMyNewStream495@ MyNewStream496T2;MyNewStream497i2 22222222222222222222222222222222222222223MyNewStream498m2 MyNewStream499_MyNewStream5022MyNewStream503dd 3333333 3 3 3 3 3333333333333333333 3"3#3$3%3&3'3(3)3*3+3,3-3.3/303132333435363738393:3;3<3=3>3?3@3A3B3C3E3F3G3H3I3J3K3L3M3N3O3P3Q3R3S3T3U3V3W3X3Y3Z3[3\3]3^3_3`3a3b3c3d3e3f3g3h3i3j3k3l3m3n3o3p3q3r3s3t3u3v3w3x3y3{3|3}3~333 MyNewStream504!3MyNewStream505D3g MyNewStream506z3 MyNewStream5073E 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 4444444444444444444 4!4"4#4$4%4&4'4(4)4*4+4,4-4.4/404142434445464748494:4;4<4=4?4@4A4B4C4D4E4F4G4H4I4J4K4L4M4N4P4Q4k4l4m4n4o4p4q4r4s4t4u4v4w4x4y4z4{4|4}4~444 MyNewStream510  4c MyNewStream511 >4=MyNewStream512 O4MyNewStream514 j4 44444444444444444444444444444444 MyNewStream519 !5 MyNewStream521v5MyNewStream5235MyNewStream5255 "5#5$5%5&5'5(5)5*5+5,5-5.5/505152535455565758595:5;5<5=5>5?5@5A5B5C5D5E5F5G5H5I5J5K5L5M5N5O5P5Q5R5S5w5x5y5z5{5|5}5~555   555555555555555555555555555555555555555555555555556 66666666 6 6 6 666666666666666666 6!6"6#6$6%6&6'6(6)6*6+6,6-6.6/606162636465666768696:6;6<6=6>6?6@6A6B6C6D6E6F6G6H6I6J6K6L6M6N6O6P6Q6R6S6T6V6W6X6Y6Z6[6\6]6^6_6`6a6b6c6d6e6f6g6h6i6j6k6l6m6n6o6p6q6r6s6t6u6v6w6x6y6z6MyNewStream5265[MyNewStream527MyNewStream5286O MyNewStream529U6E 666666MyNewStream534MyNewStream5376 MyNewStream53817 MyNewStream539b7 777777777777777 7!7"7#7$7%7&7'7(7)7*7+7,7-7.7/7072737475767778797:7;7<7=7>7?7@7A7B7C7D7E7F7G7H7I7J7K7L7M7N7O7P7Q7R7S7T7U7V7W7X7Y7Z7[7\7]7^7_7`7a7c7d7e7f7g7h7i7j7k7l7n7o7p7 7777777777777777777MyNewStream540m7MyNewStream542MyNewStream5434MyNewStream546 888888888 8!8"8#8$8%8&8'8(8)8*8+8,8-8.8/808182838485868788898:8;8<8=8>8?8@8A8B8C8D8M8N8O8P8Q8R8S8T8U8W8X8Y8Z8[8\8]8^8_8`8a8b8c8d8e8f8g8h8i8j8k8l8m8n8o8p8q8r8s8t8u8v8w8x8y8z8{8|8}8~888 888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888MyNewStream548L8KMyNewStream549V8 MyNewStream5518w MyNewStream552 8      +!2"#$%&'()*,>-./013?456789:;<=@OYABCDEFGHIJKLMNPaQRSTUVWXZp[\]^_`bvcdefghijklmnoqrstuwxyz{|}~MyNewStream553!8MyNewStream555"9 MyNewStream556#>9%MyNewStream559$9b 9999999999999 9!9"9#9$9%9&9'9(9)9*9+9,9-9.9/909192939495969798999:9;9<9=9?9@9A9B9C9D9E9F9G9H9I9J9K9L9M9N9O9P9Q9R9S9T9U9V9W9X9Y9Z9[9\9]9^9 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 MyNewStream560%9[MyNewStream561&9 MyNewStream565'O`MyNewStream566(1: ':(:):*:+:,:-:.:/:0:2:3:4:5:6:7:8:::;:<:>:?:@:A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:V:W:X:Y:Z:[:\:]:^:_:`:a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:w:x:y:z:{:|:}:~::: MyNewStream567)9:MyNewStream568*=:MyNewStream569+U:MyNewStream571,v: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;MyNewStream573-_kMyNewStream574.U MyNewStream577/:S MyNewStream5800o  ;;;;;;;; ; ; ; ; ;;;/;0;1;2;3;4;5;6;8;9;:;;;<;=;>;?;@;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;V;W;X;Y; MyNewStream58117;vMyNewStream5822U;MyNewStream5843MyNewStream5864;~ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MyNewStream5875;qMyNewStream5886;MyNewStream5897;MyNewStream5918@ &<'<(<)<*<+<,<-<.</<0<1<2<3<4<5<6<7<8<9<:<;<<<=<><?<@<A<B<C<D<E<o<p<q<r<s<t<u<v<w<x<y<z<{<|<}<~<<<    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=MyNewStream5939n<{ MyNewStream594:MyNewStream596;) MyNewStream600<< ======== = = = = =================== =!="=#=$=%=&='=(=)=*=+=,=-=.=3=4=5=6=7=8=9=:=;=<===>=?=@=A=B=C=D=E=F=G=H=I=J=K=M=N=O=P=Q=R=S=T=U=V=W=X=Y=Z=[=\=]=^=_=`=a=b=c=d=e=f=g=h=i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=z={=|=}=~=== MyNewStream601=MyNewStream603>eMyNewStream604?L=+ MyNewStream605@y=O ======================================================== MyNewStream606A MyNewStream609B=MyNewStream612C;MyNewStream613D (>)>*>+>,>->.>/>0>1>2>3>4>5>6>7>8>9>:>;><>=>>>?>@>A>B>C>E>F>G>H>I>J>K>L>M>N>O>P>Q>R>S>T>U>V>W> MyNewStream614ED>MyNewStream618FO MyNewStream619G> MyNewStream622H >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>? 1?2?4?5?6?7?8?9?:?;????@?A?B?C?D?E?F?G?H?I?J?K?L?M?N?O?P?Q?R?S?T?U?V?W?r?s?t?u?v?w? ??????????????????????????????????????????????????????               %      0     ! " # $ & 8 ' ( ) * + , - . / 1 G 2 3 4 5 6 7 9 J : ; < = > ? @ A B C D E F H q I K [ L M N O P Q R S T U V W X Y Z \ l ] ^ _ ` a b c d e f g h i j k m n o p r s t u v w x y z { | } ~  3@4@5@6@7@8@9@:@;@<@=@>@?@@@A@B@C@MyNewStream623I3?3 MyNewStream625Jq?MyNewStream627KwMyNewStream628L?MyNewStream629M? MyNewStream632NMyNewStream634OMyNewStream638PB @@@@@@@@@@@@@@@@@@@@@@@@@@@@ MyNewStream641Q@MyNewStream643R/BMyNewStream645S%QMyNewStream646T2A A A!A"A#A$A%A&A'A(A)A*A+A,A-A.A/A0A1A3A4A6A7A8A9A:A;AA?A@AAABACADAEAFAGAHAIAJAKALAMANAOAPAQARASA MyNewStream647U5AMyNewStream650V?MyNewStream652WA MyNewStream653XAb AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA MyNewStream654YAMyNewStream656ZC/ MyNewStream658[cBN MyNewStream661\B BBBBBBBBBBBBBBBB B!B"B#B$B%B&B'B(B)B*B+B,B-B.B/B0B1B2B3BdBeBfBgBhBiBjBkBlBmBnBoBpBqBrBsBtBuBvBwBxByBzB{B|B}B~BBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)CMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCoCpCqCrCsCtCuCvCwCxCyCzC MyNewStream664]oHMyNewStream666^LC MyNewStream668_MyNewStream669`C6  CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCD MyNewStream672aCB MyNewStream673b MyNewStream674c?D{MyNewStream675d]D DDDDDDDD D D D D DDDDDDDDDDDDDDDDDD D!D"D#D$D%D&D'D(D)D*D+D,D-D.D/D0D1D2D3D4D5D6D7D8D9D:D;DD@DADBDCDDDEDFDGDHDIDJDKDLDMDNDODPDQDRDSDTDUDVDWDXDYDZD[D\D^D_D`DaDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDuDvDwDxDyDzD{D|D}D~DD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD MyNewStream676eDMyNewStream678fD MyNewStream680gI MyNewStream681h+E EEEE E E E E EEEEEEEEEEEEEEEEEEE E!E"E#E$E%E&E'E(E)E*E,E-E.E/E0E1E2E3E4E5E6E7E8E9E:E;EE?E@EAEBECEDEEEFEGEHEIEJEKELEMENEOEPEQEhEiEjEkElEmEnEoEpEqErEsEtEuEwExEyEzE{E|E}E~EEE MyNewStream683igEMyNewStream684jvEMyNewStream687kT MyNewStream688lE^ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE MyNewStream689mEgMyNewStream692nMyNewStream693oVF MyNewStream696pF 5F6F7F8F9F:F;FF?F@FAFBFCFDFEFFFGFHFIFJFKFLFMFNFOFPFQFRFSFTFUFWFXFYFZF[F\F]F^F_F`FaFbFcFdFeFfFgFhFiFjFkFlFmFnFoFpFqFrFsFtFuFvFwFxFyFzF{F|F}F~FFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFG GGGGGGGG G G G G GGGGGGGGGGGGGGGGGGG G!G"G#G$G%G&G'G(G)G*G+G,G-G.G/G0GKGLGhGiGjGkGlGmGnGoGpGqGrGsGtGuGvGwGxGyGzG{G|G}G~GGG             $             ! 2 " # % = & ' ( ) * + , - . / 0 1 3 B 4 5 6 7 8 9 : ; < > [ ? @ A C W D E F G H I J K L M N O P Q R S T U V X f Y Z \ n ] ^ _ ` a b c d e g z h i j k l m o p q r s t u v w x y { | } ~  MyNewStream697qF MyNewStream699rJGMyNewStream701sgG8MyNewStream702t GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG MyNewStream704uMyNewStream705vG?MyNewStream706wG MyNewStream708x ~ H+H,H-H.H{H|H}H~HHH MyNewStream710y*HMyNewStream713zzH[MyNewStream714{ MyNewStream716|H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHI MyNewStream717}HMyNewStream718~HdMyNewStream721/ MyNewStream723LIs IIIIIIMINIOIPIQIRISITIUIVIWIXIYIZI[I\I]I^I_I`IaIbIcIdIeIfIgIhIiIjIkIlImIoIpIqIrIsItIuIvIwIxIyIzI{I|I}I~III IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII MyNewStream724nI MyNewStream7254 MyNewStream726I MyNewStream729J JJJJJJJJJJJJJJJJ J!J"JFJGJHJIJJJKJLJMJNJOJPJQJRJSJTJUJVJWJXJYJZJ[J\J]J^J_J`JaJbJcJdJeJfJgJhJiJjJkJlJmJnJoJpJqJrJsJtJuJvJwJxJyJ MyNewStream731EJ: MyNewStream733JK MyNewStream734JMyNewStream737KN JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ   KKKK K K K K KKKKKKKKKKKKKKKKKKK K!K"K#K$K%K'K(K)K*K+K,K-K.K/K0K1K2K3K4K5K6K7K8K:K;KK?K@KAKBKCKDKEKFKGKHKIKJKKKLKMKNKOKPKQKSKTKUKVKWKXKYKZK[K\K]K^K_K`KaKbKcKdK MyNewStream738&KMyNewStream7399KMyNewStream740RKMyNewStream742 KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKL MyNewStream743| 'MyNewStream744Kx MyNewStream747KMyNewStream749  LLLLLLLLLLLLLLL L!L"L#L$L&L'L(L)L*L+L,L-L.L/L0L1L2L3L4L5L6L7L8L9L:L;LL?L@LALBLCLDLELFLGLHLILJLKLLLMLNLOLPL MyNewStream750%L MyNewStream752 MyNewStream753L MyNewStream755L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLM MMMMMMMM M M M M MMMMMMMMMMMMMMMMMMWMXMYMZM[M\M]M^M_M`MaMbMcMdMeMfMgMhMiMjMkMlMmMnMoMpMqMrMsMtMuMvMwMxMyMzM{M|M}MMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN MyNewStream756 MyNewStream758VM MyNewStream759~MMyNewStream760 MyNewStream761MMyNewStream762MMyNewStream763M MyNewStream764M NNNN)N*N+N,N-N.N/N0N1N2N3N4N5N6N7N8N9N:N;NN?N@NANBNCNDNENFNGNHNINJNKNLNMNNNONPNQNRNSNTNUNVNWNXNYN MyNewStream766 MyNewStream768N0MyNewStream770NMyNewStream771N               0        /   ! " # $ % & ' ( ) * + , - . 1 A [ 2 3 4 5 6 7 8 9 : ; < = > ? @ B M C D E F G H I J K L N b O P Q R S T U V W X Y Z \ o ] ^ _ ` a c t d e f g h i j k l m n p q r s u v w x y z { | } ~  NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNO MyNewStream773NMyNewStream775N! MyNewStream776 MyNewStream7779O OOOOOOOO O O O O OOOOOOOOOOOOOOOOOO O!O"O#O$O%O&O'O(O)O*O+O,O-O.O/O0O1O2O3O4O5O6O7O8O:O;OO?O@OAOBOCODOEOFOGOHOIOJOKOLOMONOOOPOQOROSOTOUOVOWOXO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOPMyNewStream780 & MyNewStream782O, MyNewStream785 MyNewStream788 z PPPPPPPP P P P P PPPPPPPPPPPPPPPPPPP P!P"P#P$P%P&P'P(P)P*PmPnPoPpPqPrPsPtPuPvPwPxPyPzP{P|P}P~PPP PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP MyNewStream789PMyNewStream790PJ MyNewStream791PMyNewStream795/ 9Q:Q;QQ?Q@QAQBQCQSQTQUQVQWQXQYQZQ[Q\Q]Q^Q_Q`QaQbQcQdQeQfQgQhQiQjQkQlQmQnQoQpQqQrQsQtQuQvQwQxQyQ MyNewStream796, MyNewStream798RQ MyNewStream800? MyNewStream804Q QQQQQQQQQQQQQQQQQQQQQQQQQQR  RRRRRRRR R R R R RRRRRRRRRRRRRRRRRRR R!R"R#R$R%R&R'R(R)R*R+R,R-R.R/R1R2R3R4R5R6R7R8R9R:R;RR?R@RARBRCRDRERFRGRHRIRJRKRLRMRNRORPRQRRRtRuRvRwRxRyRzR{R|R}R~RRR RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRS MyNewStream805O MyNewStream807sR`MyNewStream808_ MyNewStream809Re MyNewStream810RMyNewStream812RMyNewStream813RMyNewStream814R SSSSSSSSSSSSSS S!S"S#S$S%S&S'S(S)S*S+S,S-S.S/S0S1S2S3S4S5S7S8S9S:S;SS?S@SASBSCSDSESFSGSHSISJSKSLSMSNSOSPSQSRSSSTSUSVSWSXSYSZS[S\S]S_S`SaSbScSdSeSfSgShSiSjSkSlSmSnSoSpSqSrSsStSuSvSwS MyNewStream816o MyNewStream8176S MyNewStream818^S|MyNewStream822 T T T T TTTTTTTTTTTTTTTTT T!T"T#T$T%T&T'T(T)T*T+T,T-T.T/T0T1T2T3T4T5T6T7T8T9T:T;TT?T@TATBTCTDTETGTHTITJTKTLTMTNTOTPTQTRTSTTTUTVTWTXTYTZT[T\T]T^T_T`TaTbTcTdTeTfTgThTiTjTkTlTmTnToTpTqTrTsTtTuTvTwTyTzT{T|T}T~TTT MyNewStream823TMyNewStream824T MyNewStream825FT_ MyNewStream826xT TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTU MyNewStream827T MyNewStream831TMyNewStream832U!MyNewStream834EU[ UUUUUUUU U U U U UUUUUUUUUUUFUGUHUIUJUKULUMUNUOUPUQURUSUTUUUVUWUXUYUZU         2      #            ! " $ : % & ' ( ) * + , - . / 0 1 3 K 4 5 6 7 8 9 ; I < = > ? @ A B C D E F G H J \ L V M N O P Q R S T U W | X Y Z [ ] m ^ _ ` a b c d e f g h i j k l n { o p q r s t u v w x y z } ~  MyNewStream841 MyNewStream843 .MyNewStream8447Vz MyNewStream850 N 5V6V8V9V:V;VV?V@VAVBVCVDVEVFVGVHVIVJVKVLVMVNVOVPVQVRVSVTVUVVVWVXVYVZV[V\V]V^V_V`VaVbVcVdVeVfVgVhV VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV MyNewStream851V MyNewStream853 MyNewStream855lWMyNewStream856wW *W+W,W-W.W/W0W1W2W3W4W5W6W7W8W9W:W;WX?X@XAXBXCXDXEXFXGXHXIXJXKXLXMXOXPXQXRXSXTXUXVXWXXXYXnXoXpXqXrXsXtXuXvXwXxXyXzX{X|X}X~XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX WYXYYYZY[Y\Y]Y^Y_Y`YaYbYcYdYeYgYhYiYjYkYlYmYnYoYpYqYrYsYtYuYvYwYxYyYzY{Y|Y}YMyNewStream871 MyNewStream878/ LMyNewStream879! \MyNewStream880fY  Z MyNewStream886? LMyNewStream887O 2MyNewStream888F MyNewStream890C & ,Z-Z.Z/Z0Z1Z2Z3Z4Z5Z6Z7Z8Z9Z:Z;ZZ?ZpZqZrZsZtZuZvZwZxZyZzZ{Z}Z~ZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[MyNewStream892oZ?MyNewStream893|ZMyNewStream896_ MyNewStream897Zf [[[[[[[ [ [ [ [ [[[[[[[[[[[[[[[[[[[ [:[;[<[=[>[?[@[A[B[C[D[E[F[G[H[I[J[K[L[MyNewStream899Z. MyNewStream900[MyNewStream9029[MyNewStream906  [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\ MyNewStream908[MyNewStream916 MyNewStream919 MyNewStream921\ \\\\\\\\ \ \ \ \ \\\\\\\\\\v\w\x\y\z\{\|\}\~\\\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\        !           3        " 2 # $ % & ' ( ) * + , - . / 0 1 4 E ? 5 6 7 8 9 : ; < = > @ S A B C D F R G H I J K L M N O P Q T d g U V W X Y Z [ \ ] ^ _ ` a b c e z f h i j k l m n o p q r s t u v w x y { | } ~  MyNewStream922\1MyNewStream923\[MyNewStream925 g MyNewStream926#] ] ] ] ]]]]]]]]]]]]]]]]]]] ]!]"]$]%]&]'](])]*]+],]-].]/]0]1]2]3]4]5]6]7]8]9]:];]<]=]>]?]@]A]B]C]D]E]|]}]~]]] MyNewStream929{] MyNewStream931 rMyNewStream932]CMyNewStream934] ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^ ^^^^^^^^ ^ ^ ^ ^ ^^^^^^^^^^^^^^^^^^^ ^!^"^#^$^%^&^'^)^*^+^,^-^.^/^1^2^3^4^5^6^7^8^9^F^G^H^I^J^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_^`^a^b^c^d^e^f^g^h^i^j^k^l^m^n^o^p^q^r^s^t^u^v^x^y^z^~^^^MyNewStream935] MyNewStream936 MyNewStream9370^rMyNewStream939E^] MyNewStream940w^MyNewStream942}^: MyNewStream943 MyNewStream944^2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _ _ _ _ _ ___________________ _!_"_#_$_%_&_'_(_)_+_,_-_._/_0_1_2_3_4_5_6_7_8_9_:_;_<_=_>_?_@_A_B_C_D_E_F_G_H_I_J_K_L_M_N_O_P_Q_R_S_T_V_W_X_Y_Z_[_\_]_^___`_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_{_|_}_~___MyNewStream946 MyNewStream947*_ MyNewStream948U_ MyNewStream950 0 _____________________________________________________________________ MyNewStream951_MyNewStream952_ MyNewStream955 MyNewStream9579` '`(`)`*`+`,`-`.`/`0`1`:`;`<`=`>`?`@`A`B`C`D`E`F`G`H`I`J`K`L`M`N`O`P`Q`R`S`T`U`V`W`X`Y`Z`[`\`]`^`_```a`b`c`d`e`g`h`i`j`k`l`m`n`o`p`q`r`s`t`u`v`w`x`y` ````````````````````````````````````````````````````` MyNewStream958f`MyNewStream961 IMyNewStream963` MyNewStream965.  a aaaaaaaaaaaaaaaaaa a!a"a#a$a%a&a'a(a)a+a,a-a.a/a0a1a2a3a4a5a6a7a8a9a:a;aa?a@aAaBaCaDaEaFaGaHaIaJaKaLaMaNaOaPaQaRaSaTaUaVaWaXaYaZa[a\a]a^a_a MyNewStream966ayMyNewStream967*ao MyNewStream970a MyNewStream971a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab MyNewStream972 a MyNewStream973 a4MyNewStream974 a-MyNewStream981 _ bbbbbbbb b b b b bbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb [c\c]c^c_c`cacbcccdcecfcgchcicjckclcmcncocpcqcrcsctcucvcwcxcyczc{c|c}c~cccMyNewStream983 bMyNewStream989o MyNewStream990c MyNewStream993  ccccccccccccccccc openmcdf-1.5.4/src/OLECFSTest/0000755000175000017500000000000012061101736015565 5ustar mathieumathieuopenmcdf-1.5.4/src/OLECFSTest/AuthoringTests.txt0000644000175000017500000001511711546163204021323 0ustar mathieumathieu========================================================================== Visual Studio Team System: Overview of Authoring and Running Tests ========================================================================== This overview describes the features for authoring and running tests in Visual Studio Team System and Visual Studio Team Edition for Software Testers. Opening Tests ------------- To open a test, open a test project or a test metadata file (a file with extension .vsmdi) that contains the definition of the test. You can find test projects and metadata files in Solution Explorer. Viewing Tests ------------- To see which tests are available to you, open the Test View window. Or, if you have installed Team Edition for Software Testers, you can also open the Test List Editor window to view tests. To open the Test View window, click the Test menu, point to Windows, and then click Test View. To open the Test List Editor window (if you have installed Team Edition for Software Testers), click Test, point to Windows, and then click Test List Editor. Running Tests ------------- You can run tests from the Test View window and the Test List Editor window. See Viewing Tests to learn how to open these windows. To run one or more tests displayed in the Test View window, first select the tests in that window; to select multiple tests, hold either the Shift or CTRL key while clicking tests. Then click the Run Tests button in the Test View window toolbar. If you have installed Visual Studio Team Edition for Software Testers, you can also use the Test List Editor window to run tests. To run tests in Test List Editor, select the check box next to each test that you want to run. Then click the Run Tests button in the Test List Editor window toolbar. Viewing Test Results -------------------- When you run a test or a series of tests, the results of the test run will be shown in the Test Results window. Each individual test in the run is shown on a separate line so that you can see its status. The window contains an embedded status bar in the top half of the window that provides you with summary details of the complete test run. To see more detailed results for a particular test result, double-click it in the Test Results window. This opens a window that provides more information about the particular test result, such as any specific error messages returned by the test. Changing the way that tests are run ----------------------------------- Each time you run one or more tests, a collection of settings is used to determine how those tests are run. These settings are contained in a “test run configuration” file. Here is a partial list of the changes you can make with a test run configuration file: - Change the naming scheme for each test run. - Change the test controller that the tests are run on so that you can run tests remotely. - Gather code coverage data for the code being tested so that you can see which lines of code are covered by your tests. - Enable and disable test deployment. - Specify additional files to deploy before tests are run. - Select a different host, ASP.NET, for running ASP.NET unit tests. - Select a different host, the smart device test host, for running smart device unit tests. - Set various properties for the test agents that run your tests. - Run custom scripts at the start and end of each test run so that you can set up the test environment exactly as required each time tests are run. - Set time limits for tests and test runs. - Set the browser mix and the number of times to repeat Web tests in the test run. By default, a test run configuration file is created whenever you create a new test project. You make changes to this file by double-clicking it in Solution Explorer and then changing its settings. (Test run configuration files have the extension .testrunconfig.) A solution can contain multiple test run configuration files. Only one of those files, known as the “Active” test run configuration file, is used to determine the settings that are currently used for test runs. You select the active test run configuration by clicking Select Active Test Run Configuration on the Test menu. ------------------------------------------------------------------------------- Test Types ---------- Using Visual Studio Team Edition for Software Testers, you can create a number of different test types: Unit test: Use a unit test to create a programmatic test in C++, Visual C# or Visual Basic that exercises source code. A unit test calls the methods of a class, passing suitable parameters, and verifies that the returned value is what you expect. There are three specialized variants of unit tests: - Data-driven unit tests are created when you configure a unit test to be called repeatedly for each row of a data source. The data from each row is used by the unit test as input data. - ASP.NET unit tests are unit tests that exercise code in an ASP.NET Web application. - Smart device unit tests are unit tests that are deployed to a smart device or emulator and then executed by the smart device test host. Web Test: Web tests consist of an ordered series of HTTP requests that you record in a browser session using Microsoft Internet Explorer. You can have the test report specific details about the pages or sites it requests, such as whether a particular page contains a specified string. Load Test: You use a load test to encapsulate non-manual tests, such as unit, Web, and generic tests, and then run them simultaneously by using virtual users. Running these tests under load generates test results, including performance and other counters, in tables and in graphs. Generic test: A generic test is an existing program wrapped to function as a test in Visual Studio. The following are examples of tests or programs that you can turn into generic tests: - An existing test that uses process exit codes to communicate whether the test passed or failed. 0 indicates passing and any other value indicates a failure. - A general program to obtain specific functionality during a test scenario. - A test or program that uses a special XML file (called a “summary results file”), to communicate detailed results. Manual test: The manual test type is used when the test tasks are to be completed by a test engineer as opposed to an automated script. Ordered test: Use an ordered test to execute a set of tests in an order you specify. ------------------------------------------------------------------------------- openmcdf-1.5.4/src/OLECFSTest/CFSStreamTest.cs0000644000175000017500000007201712042056202020546 0ustar mathieumathieuusing System; using System.Text; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenMcdf; using System.IO; namespace OpenMcdfTest { /// /// Summary description for UnitTest1 /// [TestClass] public class CFSStreamTest { //const String TestContext.TestDir = "C:\\TestOutputFiles\\"; public CFSStreamTest() { // // TODO: Add constructor logic here // } private TestContext testContextInstance; /// ///Gets or sets the test context which provides ///information about and functionality for the current test run. /// public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region Additional test attributes // // You can use the following additional attributes as you write your tests: // // Use ClassInitialize to run code before running the first test in the class // [ClassInitialize()] // public static void MyClassInitialize(TestContext testContext) { } // // Use ClassCleanup to run code after all tests in a class have run // [ClassCleanup()] // public static void MyClassCleanup() { } // // Use TestInitialize to run code before running each test // [TestInitialize()] // public void MyTestInitialize() { } // // Use TestCleanup to run code after each test has run // [TestCleanup()] // public void MyTestCleanup() { } // #endregion [TestMethod] public void Test_READ_STREAM() { String filename = "report.xls"; CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); Assert.IsTrue(temp.Length > 0); cf.Close(); } [TestMethod] public void Test_WRITE_STREAM() { const int BUFFER_LENGTH = 10000; byte[] b = Helpers.GetBuffer(BUFFER_LENGTH); CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); Assert.IsTrue(myStream.Size == 0); myStream.SetData(b); Assert.IsTrue(myStream.Size == BUFFER_LENGTH, "Stream size differs from buffer size"); cf.Close(); } [TestMethod] public void Test_WRITE_MINI_STREAM() { const int BUFFER_LENGTH = 1023; // < 4096 byte[] b = Helpers.GetBuffer(BUFFER_LENGTH); CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyMiniStream"); Assert.IsNotNull(myStream); Assert.IsTrue(myStream.Size == 0); myStream.SetData(b); Assert.IsTrue(myStream.Size == BUFFER_LENGTH, "Mini Stream size differs from buffer size"); cf.Close(); } [TestMethod] public void Test_ZERO_LENGTH_WRITE_STREAM() { byte[] b = new byte[0]; CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); try { myStream.SetData(b); cf.Save("ZERO_LENGTH_STREAM.cfs"); } catch { Assert.Fail("Failed setting zero length stream"); } finally { if (cf != null) cf.Close(); } if (File.Exists("ZERO_LENGTH_STREAM.cfs")) File.Delete("ZERO_LENGTH_STREAM.cfs"); } [TestMethod] public void Test_ZERO_LENGTH_RE_WRITE_STREAM() { byte[] b = new byte[0]; CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); try { myStream.SetData(b); } catch { Assert.Fail("Failed setting zero length stream"); } cf.Save("ZERO_LENGTH_STREAM_RE.cfs"); cf.Close(); CompoundFile cfo = new CompoundFile("ZERO_LENGTH_STREAM_RE.cfs"); CFStream oStream = cfo.RootStorage.GetStream("MyStream"); Assert.IsNotNull(oStream); Assert.IsTrue(oStream.Size == 0); try { oStream.SetData(Helpers.GetBuffer(30)); cfo.Save("ZERO_LENGTH_STREAM_RE2.cfs"); } catch { Assert.Fail("Failed re-writing zero length stream"); } finally { cfo.Close(); } if (File.Exists("ZERO_LENGTH_STREAM_RE.cfs")) File.Delete("ZERO_LENGTH_STREAM_RE.cfs"); if (File.Exists("ZERO_LENGTH_STREAM_RE2.cfs")) File.Delete("ZERO_LENGTH_STREAM_RE2.cfs"); } [TestMethod] public void Test_WRITE_STREAM_WITH_DIFAT() { //const int SIZE = 15388609; //Incredible condition of 'resonance' between FAT and DIFAT sec number const int SIZE = 15345665; // 64 -> 65 NOT working (in the past ;-) ) byte[] b = Helpers.GetBuffer(SIZE, 0); CompoundFile cf = new CompoundFile(); CFStream myStream = cf.RootStorage.AddStream("MyStream"); Assert.IsNotNull(myStream); myStream.SetData(b); cf.Save("WRITE_STREAM_WITH_DIFAT.cfs"); cf.Close(); CompoundFile cf2 = new CompoundFile("WRITE_STREAM_WITH_DIFAT.cfs"); CFStream st = cf2.RootStorage.GetStream("MyStream"); Assert.IsNotNull(cf2); Assert.IsTrue(st.Size == SIZE); Assert.IsTrue(Helpers.CompareBuffer(b, st.GetData())); cf2.Close(); if (File.Exists("WRITE_STREAM_WITH_DIFAT.cfs")) File.Delete("WRITE_STREAM_WITH_DIFAT.cfs"); } [TestMethod] public void Test_WRITE_MINISTREAM_READ_REWRITE_STREAM() { const int BIGGER_SIZE = 350; //const int SMALLER_SIZE = 290; const int MEGA_SIZE = 18000000; byte[] ba1 = Helpers.GetBuffer(BIGGER_SIZE, 1); byte[] ba2 = Helpers.GetBuffer(BIGGER_SIZE, 2); byte[] ba3 = Helpers.GetBuffer(BIGGER_SIZE, 3); byte[] ba4 = Helpers.GetBuffer(BIGGER_SIZE, 4); byte[] ba5 = Helpers.GetBuffer(BIGGER_SIZE, 5); //WRITE 5 (mini)streams in a compound file -- CompoundFile cfa = new CompoundFile(); CFStream myStream = cfa.RootStorage.AddStream("MyFirstStream"); Assert.IsNotNull(myStream); myStream.SetData(ba1); Assert.IsTrue(myStream.Size == BIGGER_SIZE); CFStream myStream2 = cfa.RootStorage.AddStream("MySecondStream"); Assert.IsNotNull(myStream2); myStream2.SetData(ba2); Assert.IsTrue(myStream2.Size == BIGGER_SIZE); CFStream myStream3 = cfa.RootStorage.AddStream("MyThirdStream"); Assert.IsNotNull(myStream3); myStream3.SetData(ba3); Assert.IsTrue(myStream3.Size == BIGGER_SIZE); CFStream myStream4 = cfa.RootStorage.AddStream("MyFourthStream"); Assert.IsNotNull(myStream4); myStream4.SetData(ba4); Assert.IsTrue(myStream4.Size == BIGGER_SIZE); CFStream myStream5 = cfa.RootStorage.AddStream("MyFifthStream"); Assert.IsNotNull(myStream5); myStream5.SetData(ba5); Assert.IsTrue(myStream5.Size == BIGGER_SIZE); cfa.Save("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs"); cfa.Close(); // Now get the second stream and rewrite it smaller byte[] bb = Helpers.GetBuffer(MEGA_SIZE); CompoundFile cfb = new CompoundFile("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs"); CFStream myStreamB = cfb.RootStorage.GetStream("MySecondStream"); Assert.IsNotNull(myStreamB); myStreamB.SetData(bb); Assert.IsTrue(myStreamB.Size == MEGA_SIZE); byte[] bufferB = myStreamB.GetData(); cfb.Save("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs"); cfb.Close(); CompoundFile cfc = new CompoundFile("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs"); CFStream myStreamC = cfc.RootStorage.GetStream("MySecondStream"); Assert.IsTrue(myStreamC.Size == MEGA_SIZE, "DATA SIZE FAILED"); byte[] bufferC = myStreamC.GetData(); Assert.IsTrue(Helpers.CompareBuffer(bufferB, bufferC), "DATA INTEGRITY FAILED"); cfc.Close(); if (File.Exists("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs")) File.Delete("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs"); if (File.Exists("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs")) File.Delete("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs"); } [TestMethod] public void Test_RE_WRITE_SMALLER_STREAM() { const int BUFFER_LENGTH = 8000; String filename = "report.xls"; byte[] b = Helpers.GetBuffer(BUFFER_LENGTH); CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); foundStream.SetData(b); cf.Save("reportRW_SMALL.xls"); cf.Close(); cf = new CompoundFile("reportRW_SMALL.xls"); byte[] c = cf.RootStorage.GetStream("Workbook").GetData(); Assert.IsTrue(c.Length == BUFFER_LENGTH); cf.Close(); if (File.Exists("reportRW_SMALL.xls")) File.Delete("reportRW_SMALL.xls"); } [TestMethod] public void Test_RE_WRITE_SMALLER_MINI_STREAM() { String filename = "report.xls"; CompoundFile cf = new CompoundFile(filename); CFStream foundStream = cf.RootStorage.GetStream("\x05SummaryInformation"); int TEST_LENGTH = (int)foundStream.Size - 20; byte[] b = Helpers.GetBuffer(TEST_LENGTH); foundStream.SetData(b); cf.Save("RE_WRITE_SMALLER_MINI_STREAM.xls"); cf.Close(); cf = new CompoundFile("RE_WRITE_SMALLER_MINI_STREAM.xls"); byte[] c = cf.RootStorage.GetStream("\x05SummaryInformation").GetData(); Assert.IsTrue(c.Length == TEST_LENGTH); cf.Close(); if (File.Exists("RE_WRITE_SMALLER_MINI_STREAM.xls")) File.Delete("RE_WRITE_SMALLER_MINI_STREAM.xls"); } [TestMethod] public void Test_TRANSACTED_ADD_STREAM_TO_EXISTING_FILE() { String srcFilename = "report.xls"; String dstFilename = "reportOverwrite.xls"; File.Copy(srcFilename, dstFilename, true); CompoundFile cf = new CompoundFile(dstFilename, UpdateMode.Update, false, false); byte[] buffer = Helpers.GetBuffer(5000); CFStream addedStream = cf.RootStorage.AddStream("MyNewStream"); addedStream.SetData(buffer); cf.Commit(); cf.Close(); if (File.Exists("reportOverwrite.xls")) File.Delete("reportOverwrite.xls"); } [TestMethod] public void Test_TRANSACTED_ADD_REMOVE_MULTIPLE_STREAM_TO_EXISTING_FILE() { String srcFilename = "report.xls"; String dstFilename = "reportOverwriteMultiple.xls"; File.Copy(srcFilename, dstFilename, true); CompoundFile cf = new CompoundFile(dstFilename, UpdateMode.ReadOnly, true, false); //CompoundFile cf = new CompoundFile(); Random r = new Random(); for (int i = 0; i < 254; i++) { //byte[] buffer = Helpers.GetBuffer(r.Next(100, 3500), (byte)i); byte[] buffer = Helpers.GetBuffer(1995, 1); //if (i > 0) //{ // if (r.Next(0, 100) > 50) // { // cf.RootStorage.Delete("MyNewStream" + (i - 1).ToString()); // } //} CFStream addedStream = cf.RootStorage.AddStream("MyNewStream" + i.ToString()); Assert.IsNotNull(addedStream, "Stream not found"); addedStream.SetData(buffer); Assert.IsTrue(Helpers.CompareBuffer(addedStream.GetData(), buffer), "Data buffer corrupted"); // Random commit, not on single addition //if (r.Next(0, 100) > 50) // cf.UpdateFile(); } cf.Save(dstFilename + "PP"); cf.Close(); if (File.Exists("reportOverwriteMultiple.xls")) File.Delete("reportOverwriteMultiple.xls"); if (File.Exists("reportOverwriteMultiple.xlsPP")) File.Delete("reportOverwriteMultiple.xlsPP"); } [TestMethod] public void Test_TRANSACTED_ADD_MINISTREAM_TO_EXISTING_FILE() { String srcFilename = "report.xls"; String dstFilename = "reportOverwriteMultiple.xls"; File.Copy(srcFilename, dstFilename, true); CompoundFile cf = new CompoundFile(dstFilename, UpdateMode.Update, true, true); Random r = new Random(); byte[] buffer = Helpers.GetBuffer(31, 0x0A); cf.RootStorage.AddStream("MyStream").SetData(buffer); cf.Commit(); cf.Close(); FileStream larger = new FileStream(dstFilename, FileMode.Open); FileStream smaller = new FileStream(srcFilename, FileMode.Open); // Equal condition if minisector can be "allocated" // within the existing standard sector border Assert.IsTrue(larger.Length >= smaller.Length); larger.Close(); smaller.Close(); if (File.Exists("reportOverwriteMultiple.xlsPP")) File.Delete("reportOverwriteMultiple.xlsPP"); } [TestMethod] public void Test_TRANSACTED_REMOVE_MINI_STREAM_ADD_MINISTREAM_TO_EXISTING_FILE() { String srcFilename = "report.xls"; String dstFilename = "reportOverwrite2.xls"; File.Copy(srcFilename, dstFilename, true); CompoundFile cf = new CompoundFile(dstFilename, UpdateMode.Update, true, true); cf.RootStorage.Delete("\x05SummaryInformation"); byte[] buffer = Helpers.GetBuffer(2000); CFStream addedStream = cf.RootStorage.AddStream("MyNewStream"); addedStream.SetData(buffer); cf.Commit(); cf.Close(); if (File.Exists("reportOverwrite2.xlsPP")) File.Delete("reportOverwrite2.xlsPP"); } //[TestMethod] //public void Test_DELETE_STREAM_1() //{ // String filename = "MultipleStorage.cfs"; // CompoundFile cf = new CompoundFile(filename); // CFStorage cfs = cf.RootStorage.GetStorage("MyStorage"); // cfs.DeleteStream("MySecondStream"); // cf.Save(TestContext.TestDir + "MultipleStorage_REMOVED_STREAM_1.cfs"); // cf.Close(); //} //[TestMethod] //public void Test_DELETE_STREAM_2() //{ // String filename = "MultipleStorage.cfs"; // CompoundFile cf = new CompoundFile(filename); // CFStorage cfs = cf.RootStorage.GetStorage("MyStorage").GetStorage("AnotherStorage"); // cfs.DeleteStream("AnotherStream"); // cf.Save(TestContext.TestDir + "MultipleStorage_REMOVED_STREAM_2.cfs"); // cf.Close(); //} [TestMethod] public void Test_WRITE_AND_READ_CFS() { String filename = "WRITE_AND_READ_CFS.cfs"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(220, 0x0A); sm.SetData(b); cf.Save(filename); cf.Close(); CompoundFile cf2 = new CompoundFile(filename); CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); CFStream sm2 = st2.GetStream("MyStream"); cf2.Close(); Assert.IsNotNull(sm2); Assert.IsTrue(sm2.Size == 220); if (File.Exists(filename)) File.Delete(filename); } [TestMethod] public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS() { Random r = new Random(); for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) { SingleWriteReadMatching(i + r.Next(0, 3)); } } [TestMethod] public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS_STREAM() { Random r = new Random(); for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) { SingleWriteReadMatchingSTREAMED(i + r.Next(0, 3)); } } [TestMethod] public void Test_DELETE_ZERO_LENGTH_STREAM() { byte[] b = new byte[0]; CompoundFile cf = new CompoundFile(); string zeroLengthName = "MyZeroStream"; CFStream myStream = cf.RootStorage.AddStream(zeroLengthName); Assert.IsNotNull(myStream); try { myStream.SetData(b); } catch { Assert.Fail("Failed setting zero length stream"); } string filename = "DeleteZeroLengthStream.cfs"; cf.Save(filename); cf.Close(); CompoundFile cf2 = new CompoundFile(filename); // Execption in next line! cf2.RootStorage.Delete(zeroLengthName); CFStream zeroStream2 = null; try { zeroStream2 = cf2.RootStorage.GetStream(zeroLengthName); } catch (Exception ex) { Assert.IsNull(zeroStream2); Assert.IsInstanceOfType(ex, typeof(CFItemNotFound)); } cf2.Save("MultipleDeleteMiniStream.cfs"); cf2.Close(); } //[TestMethod] //public void Test_INCREMENTAL_TRANSACTED_CHANGE_CFS() //{ // Random r = new Random(); // for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) // { // SingleTransactedChange(i + r.Next(0, 3)); // } //} private void SingleTransactedChange(int size) { String filename = "INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS.cfs"; if (File.Exists(filename)) File.Delete(filename); CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(size); sm.SetData(b); cf.Save(filename); cf.Close(); CompoundFile cf2 = new CompoundFile(filename); CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); CFStream sm2 = st2.GetStream("MyStream"); Assert.IsNotNull(sm2); Assert.IsTrue(sm2.Size == size); Assert.IsTrue(Helpers.CompareBuffer(sm2.GetData(), b)); cf2.Close(); } private void SingleWriteReadMatching(int size) { String filename = "INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS.cfs"; if (File.Exists(filename)) File.Delete(filename); CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(size); sm.SetData(b); cf.Save(filename); cf.Close(); CompoundFile cf2 = new CompoundFile(filename); CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); CFStream sm2 = st2.GetStream("MyStream"); Assert.IsNotNull(sm2); Assert.IsTrue(sm2.Size == size); Assert.IsTrue(Helpers.CompareBuffer(sm2.GetData(), b)); cf2.Close(); } private void SingleWriteReadMatchingSTREAMED(int size) { MemoryStream ms = new MemoryStream(size); CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(size); sm.SetData(b); cf.Save(ms); cf.Close(); CompoundFile cf2 = new CompoundFile(ms); CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); CFStream sm2 = st2.GetStream("MyStream"); Assert.IsNotNull(sm2); Assert.IsTrue(sm2.Size == size); Assert.IsTrue(Helpers.CompareBuffer(sm2.GetData(), b)); cf2.Close(); } [TestMethod] public void Test_APPEND_DATA_TO_STREAM() { MemoryStream ms = new MemoryStream(); byte[] b = new byte[] { 0x0, 0x1, 0x2, 0x3 }; byte[] b2 = new byte[] { 0x4, 0x5, 0x6, 0x7 }; CompoundFile cf = new CompoundFile(); CFStream st = cf.RootStorage.AddStream("MyLargeStream"); st.SetData(b); st.AppendData(b2); cf.Save(ms); cf.Close(); byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; cf = new CompoundFile(ms); byte[] data = cf.RootStorage.GetStream("MyLargeStream").GetData(); Assert.IsTrue(Helpers.CompareBuffer(cmp, data)); } [TestMethod] public void Test_COPY_FROM_STREAM() { byte[] b = Helpers.GetBuffer(100); MemoryStream ms = new MemoryStream(b); CompoundFile cf = new CompoundFile(); CFStream st = cf.RootStorage.AddStream("MyImportedStream"); st.CopyFrom(ms); ms.Close(); cf.Save("COPY_FROM_STREAM.cfs"); cf.Close(); cf = new CompoundFile("COPY_FROM_STREAM.cfs"); byte[] data = cf.RootStorage.GetStream("MyImportedStream").GetData(); Assert.IsTrue(Helpers.CompareBuffer(b, data)); } #if LARGETEST [TestMethod] public void Test_APPEND_DATA_TO_CREATE_LARGE_STREAM() { byte[] b = Helpers.GetBuffer(1024 * 1024 * 50); //2GB buffer byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, false, false); CFStream st = cf.RootStorage.AddStream("MySuperLargeStream"); cf.Save("MEGALARGESSIMUSFILE.cfs"); cf.Close(); cf = new CompoundFile("MEGALARGESSIMUSFILE.cfs", UpdateMode.Update, false, false); CFStream cfst = cf.RootStorage.GetStream("MySuperLargeStream"); for (int i = 0; i < 42; i++) { cfst.AppendData(b); cf.Commit(true); } cfst.AppendData(cmp); cf.Commit(true); cf.Close(); cf = new CompoundFile("MEGALARGESSIMUSFILE.cfs"); int count = 8; byte[] data = cf.RootStorage.GetStream("MySuperLargeStream").GetData((long)b.Length * 42L, ref count); Assert.IsTrue(Helpers.CompareBuffer(cmp, data)); cf.Close(); } #endif [TestMethod] public void Test_DELETE_STREAM_SECTOR_REUSE() { CompoundFile cf = null; CFStream st = null; byte[] b = Helpers.GetBuffer(1024 * 1024 * 2); //2MB buffer //byte[] b = Helpers.GetBuffer(5000); byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; cf = new CompoundFile(CFSVersion.Ver_4, false, false); st = cf.RootStorage.AddStream("AStream"); st.AppendData(b); cf.Save("SectorRecycle.cfs"); cf.Close(); cf = new CompoundFile("SectorRecycle.cfs", UpdateMode.Update, true, false); cf.RootStorage.Delete("AStream"); cf.Commit(true); cf.Close(); cf = new CompoundFile("SectorRecycle.cfs", UpdateMode.ReadOnly, false, false); //No sector recycle st = cf.RootStorage.AddStream("BStream"); st.AppendData(Helpers.GetBuffer(1024 * 1024 * 1)); cf.Save("SectorRecycleLarger.cfs"); cf.Close(); Assert.IsFalse((new FileInfo("SectorRecycle.cfs").Length) >= (new FileInfo("SectorRecycleLarger.cfs").Length)); cf = new CompoundFile("SectorRecycle.cfs", UpdateMode.ReadOnly, true, false); st = cf.RootStorage.AddStream("BStream"); st.AppendData(Helpers.GetBuffer(1024 * 1024 * 1)); cf.Save("SectorRecycleSmaller.cfs"); cf.Close(); Assert.IsTrue((new FileInfo("SectorRecycle.cfs").Length) >= (new FileInfo("SectorRecycleSmaller.cfs").Length)); } [TestMethod] public void TEST_STREAM_VIEW() { Stream a = null; List temp = new List(); Sector s = new Sector(512); Buffer.BlockCopy(BitConverter.GetBytes((int)1), 0, s.GetData(), 0, 4); temp.Add(s); StreamView sv = new StreamView(temp, 512, 0, a); BinaryReader br = new BinaryReader(sv); Int32 t = br.ReadInt32(); Assert.IsTrue(t == 1); } [TestMethod] public void Test_STREAM_VIEW_2() { Stream b = null; List temp = new List(); StreamView sv = new StreamView(temp, 512, b); sv.Write(BitConverter.GetBytes(1), 0, 4); sv.Seek(0, SeekOrigin.Begin); BinaryReader br = new BinaryReader(sv); Int32 t = br.ReadInt32(); Assert.IsTrue(t == 1); } /// /// Write a sequence of Int32 greater than sector size, /// read and compare. /// [TestMethod] public void Test_STREAM_VIEW_3() { Stream b = null; List temp = new List(); StreamView sv = new StreamView(temp, 512, b); for (int i = 0; i < 200; i++) { sv.Write(BitConverter.GetBytes(i), 0, 4); } sv.Seek(0, SeekOrigin.Begin); BinaryReader br = new BinaryReader(sv); for (int i = 0; i < 200; i++) { Assert.IsTrue(i == br.ReadInt32(), "Failed with " + i.ToString()); } } /// /// Write a sequence of Int32 greater than sector size, /// read and compare. /// [TestMethod] public void Test_STREAM_VIEW_LARGE_DATA() { Stream b = null; List temp = new List(); StreamView sv = new StreamView(temp, 512, b); for (int i = 0; i < 200; i++) { sv.Write(BitConverter.GetBytes(i), 0, 4); } sv.Seek(0, SeekOrigin.Begin); BinaryReader br = new BinaryReader(sv); for (int i = 0; i < 200; i++) { Assert.IsTrue(i == br.ReadInt32(), "Failed with " + i.ToString()); } } } } openmcdf-1.5.4/src/OLECFSTest/CFSTorageTest.cs0000644000175000017500000002435612042056110020535 0ustar mathieumathieuusing System; using System.Text; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenMcdf; using System.IO; namespace OpenMcdfTest { /// /// Summary description for CFTorageTest /// [TestClass] public class CFSTorageTest { //const String OUTPUT_DIR = "C:\\TestOutputFiles\\"; public CFSTorageTest() { // // TODO: Add constructor logic here // } private TestContext testContextInstance; /// ///Gets or sets the test context which provides ///information about and functionality for the current test run. /// public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region Additional test attributes // // You can use the following additional attributes as you write your tests: // // Use ClassInitialize to run code before running the first test in the class // [ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) // // Use ClassCleanup to run code after all tests in a class have run // [ClassCleanup()] // public static void MyClassCleanup() { } // // Use TestInitialize to run code before running each test // [TestInitialize()] // public void MyTestInitialize() { } // // Use TestCleanup to run code after each test has run // [TestCleanup()] // public void MyTestCleanup() { } // #endregion [TestMethod] public void Test_CREATE_STORAGE() { const String STORAGE_NAME = "NewStorage"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage(STORAGE_NAME); Assert.IsNotNull(st); Assert.AreEqual(STORAGE_NAME, st.Name, false); } [TestMethod] public void Test_CREATE_STORAGE_WITH_CREATION_DATE() { const String STORAGE_NAME = "NewStorage1"; CompoundFile cf = new CompoundFile(); CFStorage st = cf.RootStorage.AddStorage(STORAGE_NAME); st.CreationDate = DateTime.Now; Assert.IsNotNull(st); Assert.AreEqual(STORAGE_NAME, st.Name, false); cf.Save("ProvaData.cfs"); cf.Close(); } [TestMethod] public void Test_VISIT_ENTRIES() { const String STORAGE_NAME = "report.xls"; CompoundFile cf = new CompoundFile(STORAGE_NAME); FileStream output = new FileStream("LogEntries.txt", FileMode.Create); TextWriter tw = new StreamWriter(output); VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; cf.RootStorage.VisitEntries(va, true); tw.Close(); } [TestMethod] public void Test_VISIT_ENTRIES_CORRUPTED_FILE_VALIDATION_ON() { CompoundFile f = null; try { f = new CompoundFile("CorruptedDoc_bug3547815.doc", UpdateMode.ReadOnly, false, false, false); } catch (Exception ex) { Assert.Fail("No exception has to be fired on creation due to lazy loading"); } FileStream output = null; try { output = new FileStream("LogEntriesCorrupted_1.txt", FileMode.Create); using (TextWriter tw = new StreamWriter(output)) { VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; f.RootStorage.VisitEntries(va, true); tw.Flush(); } } catch (Exception ex) { Assert.IsTrue(ex is CFCorruptedFileException); Assert.IsTrue(f != null && f.IsClosed); } finally { if (output != null) output.Close(); } } [TestMethod] public void Test_VISIT_ENTRIES_CORRUPTED_FILE_VALIDATION_OFF_BUT_CAN_LOAD() { CompoundFile f = null; try { //Corrupted file has invalid children item sid reference f = new CompoundFile("CorruptedDoc_bug3547815_B.doc", UpdateMode.ReadOnly, false, false, true); } catch (Exception ex) { Assert.Fail("No exception has to be fired on creation due to lazy loading"); } FileStream output = null; try { output = new FileStream("LogEntriesCorrupted_2.txt", FileMode.Create); using (TextWriter tw = new StreamWriter(output)) { VisitedEntryAction va = delegate(CFItem item) { tw.WriteLine(item.Name); }; f.RootStorage.VisitEntries(va, true); tw.Flush(); } } catch (Exception ex) { Assert.Fail("Fail is corrupted but it has to be loaded anyway by test design"); } finally { if (output != null) output.Close(); } } [TestMethod] public void Test_VISIT_STORAGE() { String FILENAME = "testVisiting.xls"; // Remove... if (File.Exists(FILENAME)) File.Delete(FILENAME); //Create... CompoundFile ncf = new CompoundFile(); CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1"); l1.AddStream("l1ns1"); l1.AddStream("l1ns2"); l1.AddStream("l1ns3"); CFStorage l2 = l1.AddStorage("Storage Level 2"); l2.AddStream("l2ns1"); l2.AddStream("l2ns2"); ncf.Save(FILENAME); ncf.Close(); // Read... CompoundFile cf = new CompoundFile(FILENAME); FileStream output = new FileStream("reportVisit.txt", FileMode.Create); TextWriter sw = new StreamWriter(output); Console.SetOut(sw); VisitedEntryAction va = delegate(CFItem target) { sw.WriteLine(target.Name); }; cf.RootStorage.VisitEntries(va, true); cf.Close(); sw.Close(); } [TestMethod] public void Test_DELETE_DIRECTORY() { String FILENAME = "MultipleStorage2.cfs"; CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); Assert.IsNotNull(st); st.Delete("AnotherStorage"); cf.Save("MultipleStorage_Delete.cfs"); cf.Close(); } [TestMethod] public void Test_DELETE_MINISTREAM_STREAM() { String FILENAME = "MultipleStorage2.cfs"; CompoundFile cf = new CompoundFile(FILENAME); CFStorage found = null; VisitedEntryAction action = delegate(CFItem item) { if (item.Name == "AnotherStorage") found = item as CFStorage; }; cf.RootStorage.VisitEntries(action, true); Assert.IsNotNull(found); found.Delete("AnotherStream"); cf.Save("MultipleDeleteMiniStream"); cf.Close(); } [TestMethod] public void Test_DELETE_STREAM() { String FILENAME = "MultipleStorage3.cfs"; CompoundFile cf = new CompoundFile(FILENAME); CFStorage found = null; VisitedEntryAction action = delegate(CFItem item) { if (item.Name == "AnotherStorage") found = item as CFStorage; }; cf.RootStorage.VisitEntries(action, true); Assert.IsNotNull(found); found.Delete("Another2Stream"); cf.Save("MultipleDeleteStream"); cf.Close(); } [TestMethod] public void Test_CHECK_DISPOSED_() { const String FILENAME = "MultipleStorage.cfs"; CompoundFile cf = new CompoundFile(FILENAME); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); cf.Close(); try { byte[] temp = st.GetStream("MyStream").GetData(); Assert.Fail("Stream without media"); } catch (Exception ex) { Assert.IsTrue(ex is CFDisposedException); } } [TestMethod] public void Test_LAZY_LOAD_CHILDREN_() { CompoundFile cf = new CompoundFile(); cf.RootStorage.AddStorage("Level_1") .AddStorage("Level_2") .AddStream("Level2Stream") .SetData(Helpers.GetBuffer(100)); cf.Save("$Hel1"); cf.Close(); cf = new CompoundFile("$Hel1"); IList i = cf.GetAllNamedEntries("Level2Stream"); Assert.IsNotNull(i[0]); Assert.IsTrue(i[0] is CFStream); Assert.IsTrue((i[0] as CFStream).GetData().Length == 100); cf.Save("$Hel2"); cf.Close(); if (File.Exists("$Hel1")) { File.Delete("$Hel1"); } if (File.Exists("$Hel2")) { File.Delete("$Hel2"); } } } } openmcdf-1.5.4/src/OLECFSTest/CompoundFileTest.cs0000644000175000017500000005350112042056176021352 0ustar mathieumathieuusing System; using System.Text; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenMcdf; using System.IO; using System.Diagnostics; namespace OpenMcdfTest { /// /// Summary description for CompoundFileTest /// [TestClass] public class CompoundFileTest { public CompoundFileTest() { // // TODO: Add constructor logic here // } private TestContext testContextInstance; /// ///Gets or sets the test context which provides ///information about and functionality for the current test run. /// public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region Additional test attributes // // You can use the following additional attributes as you write your tests: // // Use ClassInitialize to run code before running the first test in the class // [ClassInitialize()] // public static void MyClassInitialize(TestContext testContext) { } // // Use ClassCleanup to run code after all tests in a class have run // [ClassCleanup()] // public static void MyClassCleanup() { } // // Use TestInitialize to run code before running each test // [TestInitialize()] // public void MyTestInitialize() { } // // Use TestCleanup to run code after each test has run // [TestCleanup()] // public void MyTestCleanup() { } // #endregion [TestMethod] public void Test_COMPRESS_SPACE() { String FILENAME = "MultipleStorage3.cfs"; // 22Kb FileInfo srcFile = new FileInfo(FILENAME); File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); // -> 7Kb FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsTrue(srcFile.Length > dstFile.Length); } [TestMethod] public void Test_ENTRY_NAME_LENGTH() { //Thanks to Mark Bosold for bug fix and unit CompoundFile cf = new CompoundFile(); // Cannot be equal. string maxCharactersStreamName = "1234567890123456789A12345678901"; // 31 chars string maxCharactersStorageName = "1234567890123456789012345678901"; // 31 chars // Try Storage entry name with max characters. Assert.IsNotNull(cf.RootStorage.AddStorage(maxCharactersStorageName)); CFStorage strg = cf.RootStorage.GetStorage(maxCharactersStorageName); Assert.IsNotNull(strg); Assert.IsTrue(strg.Name == maxCharactersStorageName); // Try Stream entry name with max characters. Assert.IsNotNull(cf.RootStorage.AddStream(maxCharactersStreamName)); CFStream strm = cf.RootStorage.GetStream(maxCharactersStreamName); Assert.IsNotNull(strm); Assert.IsTrue(strm.Name == maxCharactersStreamName); string tooManyCharactersEntryName = "12345678901234567890123456789012"; // 32 chars try { // Try Storage entry name with too many characters. cf.RootStorage.AddStorage(tooManyCharactersEntryName); Assert.Fail(); } catch (Exception ex) { Assert.IsTrue(ex is CFException); } try { // Try Stream entry name with too many characters. cf.RootStorage.AddStream(tooManyCharactersEntryName); Assert.Fail(); } catch (Exception ex) { Assert.IsTrue(ex is CFException); } cf.Save("EntryNameLength"); cf.Close(); } [TestMethod] public void Test_DELETE_WITHOUT_COMPRESSION() { String FILENAME = "MultipleStorage3.cfs"; FileInfo srcFile = new FileInfo(FILENAME); CompoundFile cf = new CompoundFile(FILENAME); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); //17Kb //cf.CompressFreeSpace(); cf.Save("MultipleStorage_Deleted_Compress.cfs"); cf.Close(); FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsFalse(srcFile.Length > dstFile.Length); } [TestMethod] public void Test_WRITE_AND_READ_CFS_VERSION_4() { String filename = "WRITE_AND_READ_CFS_V4.cfs"; CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, true, true); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = new byte[220]; sm.SetData(b); cf.Save(filename); cf.Close(); CompoundFile cf2 = new CompoundFile(filename); CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); CFStream sm2 = st2.GetStream("MyStream"); Assert.IsNotNull(sm2); Assert.IsTrue(sm2.Size == 220); cf2.Close(); } [TestMethod] public void Test_WRITE_READ_CFS_VERSION_4_STREAM() { String filename = "WRITE_COMMIT_READ_CFS_V4.cfs"; CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, true, true); CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream"); byte[] b = Helpers.GetBuffer(227); sm.SetData(b); cf.Save(filename); cf.Close(); CompoundFile cf2 = new CompoundFile(filename); CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); CFStream sm2 = st2.GetStream("MyStream"); Assert.IsNotNull(sm2); Assert.IsTrue(sm2.Size == b.Length); cf2.Close(); } [TestMethod] public void Test_OPEN_FROM_STREAM() { String filename = "reportREAD.xls"; File.Copy(filename, "reportOPENFROMSTREAM.xls"); FileStream fs = new FileStream(filename, FileMode.Open); CompoundFile cf = new CompoundFile(fs); CFStream foundStream = cf.RootStorage.GetStream("Workbook"); byte[] temp = foundStream.GetData(); Assert.IsNotNull(temp); cf.Close(); } [TestMethod] public void Test_MULTIPLE_SAVE() { var file = new CompoundFile(); file.Save("test.mdf"); var meta = file. RootStorage. AddStream("meta"); meta.AppendData(BitConverter.GetBytes(DateTime.Now.ToBinary())); meta.AppendData(BitConverter.GetBytes(DateTime.Now.ToBinary())); file.Save("test.mdf"); } [TestMethod] public void Test_OPEN_COMPOUND_BUG_FIX_133() { var f = new CompoundFile("testbad.ole"); CFStream cfs = f.RootStorage.GetStream("\x01Ole10Native"); byte[] data = cfs.GetData(); Assert.IsTrue(data.Length == 18140); } [TestMethod] public void Test_COMPARE_DIR_ENTRY_NAME_BUG_FIX_ID_3487353() { var f = new CompoundFile("report_name_fix.xls", UpdateMode.Update, true, true); CFStream cfs = f.RootStorage.AddStream("Poorbook"); cfs.AppendData(Helpers.GetBuffer(20)); f.Commit(); f.Close(); f = new CompoundFile("report_name_fix.xls", UpdateMode.Update, true, true); cfs = f.RootStorage.GetStream("Workbook"); Assert.IsTrue(cfs.Name == "Workbook"); f.RootStorage.Delete("PoorBook"); f.Commit(); f.Close(); } [TestMethod] public void Test_GET_COMPOUND_VERSION() { var f = new CompoundFile("report_name_fix.xls"); CFSVersion ver = f.Version; Assert.IsTrue(ver == CFSVersion.Ver_3); f.Close(); } [TestMethod] public void Test_FUNCTIONAL_BEHAVIOUR() { const int N_FACTOR = 1; byte[] bA = Helpers.GetBuffer(20 * 1024 * N_FACTOR, 0x0A); byte[] bB = Helpers.GetBuffer(5 * 1024, 0x0B); byte[] bC = Helpers.GetBuffer(5 * 1024, 0x0C); byte[] bD = Helpers.GetBuffer(5 * 1024, 0x0D); byte[] bE = Helpers.GetBuffer(8 * 1024 * N_FACTOR + 1, 0x1A); byte[] bF = Helpers.GetBuffer(16 * 1024 * N_FACTOR, 0x1B); byte[] bG = Helpers.GetBuffer(14 * 1024 * N_FACTOR, 0x1C); byte[] bH = Helpers.GetBuffer(12 * 1024 * N_FACTOR, 0x1D); byte[] bE2 = Helpers.GetBuffer(8 * 1024 * N_FACTOR, 0x2A); byte[] bMini = Helpers.GetBuffer(1027, 0xEE); Stopwatch sw = new Stopwatch(); sw.Start(); //############ // Phase 1 var cf = new CompoundFile(CFSVersion.Ver_3, true, false); cf.RootStorage.AddStream("A").SetData(bA); cf.Save("OneStream.cfs"); cf.Close(); // Test Phase 1 var cfTest = new CompoundFile("OneStream.cfs"); CFStream testSt = cfTest.RootStorage.GetStream("A"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bA.Length); Assert.IsTrue(Helpers.CompareBuffer(bA, testSt.GetData())); cfTest.Close(); //########### //Phase 2 cf = new CompoundFile("OneStream.cfs", UpdateMode.ReadOnly, true, false); cf.RootStorage.AddStream("B").SetData(bB); cf.RootStorage.AddStream("C").SetData(bC); cf.RootStorage.AddStream("D").SetData(bD); cf.RootStorage.AddStream("E").SetData(bE); cf.RootStorage.AddStream("F").SetData(bF); cf.RootStorage.AddStream("G").SetData(bG); cf.RootStorage.AddStream("H").SetData(bH); cf.Save("8_Streams.cfs"); cf.Close(); // Test Phase 2 cfTest = new CompoundFile("8_Streams.cfs"); testSt = cfTest.RootStorage.GetStream("B"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bB.Length); Assert.IsTrue(Helpers.CompareBuffer(bB, testSt.GetData())); testSt = cfTest.RootStorage.GetStream("C"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bC.Length); Assert.IsTrue(Helpers.CompareBuffer(bC, testSt.GetData())); testSt = cfTest.RootStorage.GetStream("D"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bD.Length); Assert.IsTrue(Helpers.CompareBuffer(bD, testSt.GetData())); testSt = cfTest.RootStorage.GetStream("E"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bE.Length); Assert.IsTrue(Helpers.CompareBuffer(bE, testSt.GetData())); testSt = cfTest.RootStorage.GetStream("F"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bF.Length); Assert.IsTrue(Helpers.CompareBuffer(bF, testSt.GetData())); testSt = cfTest.RootStorage.GetStream("G"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bG.Length); Assert.IsTrue(Helpers.CompareBuffer(bG, testSt.GetData())); testSt = cfTest.RootStorage.GetStream("H"); Assert.IsNotNull(testSt); Assert.IsTrue(testSt.Size == bH.Length); Assert.IsTrue(Helpers.CompareBuffer(bH, testSt.GetData())); cfTest.Close(); File.Copy("8_Streams.cfs", "6_Streams.cfs", true); File.Delete("8_Streams.cfs"); //########### // Phase 3 cf = new CompoundFile("6_Streams.cfs", UpdateMode.Update, true, true); cf.RootStorage.Delete("D"); cf.RootStorage.Delete("G"); cf.Commit(); cf.Close(); //Test Phase 3 cfTest = new CompoundFile("6_Streams.cfs"); bool catched = false; try { testSt = cfTest.RootStorage.GetStream("D"); } catch (Exception ex) { if (ex is CFItemNotFound) catched = true; } Assert.IsTrue(catched); catched = false; try { testSt = cfTest.RootStorage.GetStream("G"); } catch (Exception ex) { if (ex is CFItemNotFound) catched = true; } Assert.IsTrue(catched); cfTest.Close(); //########## // Phase 4 File.Copy("6_Streams.cfs", "6_Streams_Shrinked.cfs", true); CompoundFile.ShrinkCompoundFile("6_Streams_Shrinked.cfs"); // Test Phase 4 Assert.IsTrue(new FileInfo("6_Streams_Shrinked.cfs").Length < new FileInfo("6_Streams.cfs").Length); cfTest = new CompoundFile("6_Streams_Shrinked.cfs"); VisitedEntryAction va = delegate(CFItem item) { if (item.IsStream) { CFStream ia = item as CFStream; Assert.IsNotNull(ia); Assert.IsTrue(ia.Size > 0); byte[] d = ia.GetData(); Assert.IsNotNull(d); Assert.IsTrue(d.Length > 0); Assert.IsTrue(d.Length == ia.Size); } }; cfTest.RootStorage.VisitEntries(va, true); cfTest.Close(); //########## //Phase 5 cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStream("ZZZ").SetData(bF); cf.RootStorage.GetStream("E").AppendData(bE2); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.CLSID = new Guid("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStorage("MyStorage").AddStream("ANS").AppendData(bE); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStorage("AnotherStorage").AddStream("ANS").AppendData(bE); cf.RootStorage.Delete("MyStorage"); cf.Commit(); cf.Close(); //Test Phase 5 //##### //Phase 6 cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.AddStorage("MiniStorage").AddStream("miniSt").AppendData(bMini); cf.RootStorage.GetStorage("MiniStorage").AddStream("miniSt2").AppendData(bMini); cf.Commit(); cf.Close(); cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.GetStorage("MiniStorage").Delete("miniSt"); cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").AppendData(bE); cf.Commit(); cf.Close(); //Test Phase 6 cfTest = new CompoundFile("6_Streams_Shrinked.cfs"); byte[] d2 = cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").GetData(); Assert.IsTrue(d2.Length == (bE.Length + bMini.Length)); int cnt = 1; d2 = cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").GetData(bMini.Length, ref cnt); Assert.IsTrue(cnt == 1); Assert.IsTrue(d2.Length == 1); Assert.IsTrue(d2[0] == 0x1A); cnt = 1; d2 = cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").GetData(bMini.Length - 1, ref cnt); Assert.IsTrue(cnt == 1); Assert.IsTrue(d2.Length == 1); Assert.IsTrue(d2[0] == 0xEE); try { cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt"); } catch (Exception ex) { Assert.IsTrue(ex is CFItemNotFound); } cfTest.Close(); //############## //Phase 7 cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").SetData(bA); cf.Commit(); cf.Close(); //Test Phase 7 cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.Update, true, false); d2 = cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").GetData(); Assert.IsNotNull(d2); Assert.IsTrue(d2.Length == bA.Length); cf.Close(); //############## cf = new CompoundFile("6_Streams_Shrinked.cfs", UpdateMode.ReadOnly, true, false); var myStream = cf.RootStorage.GetStream("C"); var data = myStream.GetData(); Console.WriteLine(data[0] + " : " + data[data.Length - 1]); myStream = cf.RootStorage.GetStream("B"); data = myStream.GetData(); Console.WriteLine(data[0] + " : " + data[data.Length - 1]); cf.Close(); sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); } [TestMethod] public void Test_RETRIVE_ALL_NAMED_ENTRIES() { var f = new CompoundFile("MultipleStorage4.cfs"); IList result = f.GetAllNamedEntries("MyStream"); Assert.IsTrue(result.Count == 3); } [TestMethod] public void Test_CORRUPTED_CYCLIC_FAT_CHECK() { CompoundFile f = null; try { f = new CompoundFile("CyclicFAT.cfs"); } catch (Exception ex) { Assert.IsTrue(ex is CFCorruptedFileException); } finally { if (f != null) f.Close(); } } [TestMethod] public void Test_DIFAT_CHECK() { CompoundFile f = null; try { f = new CompoundFile(); CFStream st = f.RootStorage.AddStream("LargeStream"); st.AppendData(Helpers.GetBuffer(20000000, 0x0A)); //Forcing creation of two DIFAT sectors byte[] b1 = Helpers.GetBuffer(3, 0x0B); st.AppendData(b1); //Forcing creation of two DIFAT sectors f.Save("$OpenMcdf$LargeFile.cfs"); f.Close(); int cnt = 3; f = new CompoundFile("$OpenMcdf$LargeFile.cfs"); byte[] b2 = f.RootStorage.GetStream("LargeStream").GetData(20000000, ref cnt); f.Close(); Assert.IsTrue(Helpers.CompareBuffer(b1, b2)); } finally { if (f != null) f.Close(); if (File.Exists("$OpenMcdf$LargeFile.cfs")) File.Delete("$OpenMcdf$LargeFile.cfs"); } } //[TestMethod] //public void Test_CORRUPTED_CYCLIC_DIFAT_VALIDATION_CHECK() //{ // CompoundFile cf = null; // try // { // cf = new CompoundFile("CiclycDFAT.cfs"); // CFStorage s = cf.RootStorage.GetStorage("MyStorage"); // CFStream st = s.GetStream("MyStream"); // Assert.IsTrue(st.Size > 0); // } // catch (Exception ex) // { // Assert.IsTrue(ex is CFCorruptedFileException); // } // finally // { // if (cf != null) // { // cf.Close(); // } // } //} //[TestMethod] //public void Test_REM() //{ // var f = new CompoundFile(); // byte[] bB = Helpers.GetBuffer(5 * 1024, 0x0B); // f.RootStorage.AddStream("Test").AppendData(bB); // f.Save("Astorage.cfs"); //} } } openmcdf-1.5.4/src/OLECFSTest/Helpers.cs0000644000175000017500000000227311546170014017524 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OpenMcdfTest { public static class Helpers { public static byte[] GetBuffer(int count) { Random r = new Random(); byte[] b = new byte[count]; r.NextBytes(b); return b; } public static byte[] GetBuffer(int count, byte c) { byte[] b = new byte[count]; for (int i = 0; i < b.Length; i++) { b[i] = c; } return b; } public static bool CompareBuffer(byte[] b, byte[] p) { if (b == null && p == null) throw new Exception("Null buffers"); if (b == null && p != null) return false; if (b != null && p == null) return false; if (b.Length != p.Length) return false; for (int i = 0; i < b.Length; i++) { if (b[i] != p[i]) return false; } return true; } } } openmcdf-1.5.4/src/OLECFSTest/OpenMcdfTest.csproj0000644000175000017500000000565211546170016021356 0ustar mathieumathieu Debug AnyCPU 9.0.30729 2.0 {FD339266-8842-40B4-9230-F8E84FC42AC2} Library Properties OpenMcdfTest OpenMcdfTest v3.5 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 3.5 {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1} OpenMcdf openmcdf-1.5.4/src/OLECFSTest/Properties/0000755000175000017500000000000012061101736017721 5ustar mathieumathieuopenmcdf-1.5.4/src/OLECFSTest/Properties/AssemblyInfo.cs0000644000175000017500000000254111546415640022656 0ustar mathieumathieuusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("OpenMcdfTest")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("-")] [assembly: AssemblyProduct("OpenMcdfTest")] [assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM componenets. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("7b918910-a8be-4e22-85d6-d927eae56003")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.5.1.*")] openmcdf-1.5.4/src/OLECFSTest/SectorCollectionTest.cs0000644000175000017500000001205611546170006022236 0ustar mathieumathieuusing OpenMcdf; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System; namespace OpenMcdfTest { /// ///This is a test class for SectorCollectionTest and is intended ///to contain all SectorCollectionTest Unit Tests /// [TestClass()] public class SectorCollectionTest { private TestContext testContextInstance; /// ///Gets or sets the test context which provides ///information about and functionality for the current test run. /// public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region Additional test attributes // //You can use the following additional attributes as you write your tests: // //Use ClassInitialize to run code before running the first test in the class //[ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) //{ //} // //Use ClassCleanup to run code after all tests in a class have run //[ClassCleanup()] //public static void MyClassCleanup() //{ //} // //Use TestInitialize to run code before running each test //[TestInitialize()] //public void MyTestInitialize() //{ //} // //Use TestCleanup to run code after each test has run //[TestCleanup()] //public void MyTestCleanup() //{ //} // #endregion /// ///A test for Count /// [TestMethod()] public void CountTest() { int count = 0; SectorCollection target = new SectorCollection(); // TODO: Initialize to an appropriate value int actual; actual = target.Count; Assert.IsTrue(actual == count); Sector s = new Sector(4096); target.Add(s); Assert.IsTrue(target.Count == actual + 1); for (int i = 0; i < 5000; i++) target.Add(s); Assert.IsTrue(target.Count == actual + 1 + 5000); } /// ///A test for Item /// [TestMethod()] public void ItemTest() { int count = 37; SectorCollection target = new SectorCollection(); int index = 0; Sector expected = new Sector(4096); target.Add(null); Sector actual; target[index] = expected; actual = target[index]; Assert.AreEqual(expected, actual); Assert.IsNotNull(actual); Assert.IsTrue(actual.Id == expected.Id); actual = null; try { actual = target[count + 100]; } catch (Exception ex) { Assert.IsTrue(ex is ArgumentOutOfRangeException); } try { actual = target[-1]; } catch (Exception ex) { Assert.IsTrue(ex is ArgumentOutOfRangeException); } } /// ///A test for SectorCollection Constructor /// [TestMethod()] public void SectorCollectionConstructorTest() { SectorCollection target = new SectorCollection(); Assert.IsNotNull(target); Assert.IsTrue(target.Count == 0); Sector s = new Sector(4096); target.Add(s); Assert.IsTrue(target.Count == 1); } /// ///A test for Add /// [TestMethod()] public void AddTest() { SectorCollection target = new SectorCollection(); for (int i = 0; i < 579; i++) { target.Add(null); } Sector item = new Sector(4096); target.Add(item); Assert.IsTrue(target.Count == 580); } /// ///A test for GetEnumerator /// [TestMethod()] public void GetEnumeratorTest() { SectorCollection target = new SectorCollection(); for (int i = 0; i < 579; i++) { target.Add(null); } Sector item = new Sector(4096); target.Add(item); Assert.IsTrue(target.Count == 580); int cnt = 0; foreach (Sector s in target) { cnt++; } Assert.IsTrue(cnt == target.Count); } } } openmcdf-1.5.4/src/OLECompoundFileStorage/0000755000175000017500000000000012061101736020223 5ustar mathieumathieuopenmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/0000755000175000017500000000000012061101736022267 5ustar mathieumathieuopenmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/BinarySearchTree.cs0000644000175000017500000004667511546414024026036 0ustar mathieumathieu#region Using directives using System; using System.Collections; using System.Collections.Generic; using System.Text; #endregion namespace BinaryTrees { public delegate void NodeAction(BinaryTreeNode node); /// /// Represents a binary search tree. A binary search tree is a binary tree whose nodes are arranged /// such that for any given node k, all nodes in k's left subtree have a value less than k, and all /// nodes in k's right subtree have a value greater than k. /// /// The type of data stored in the binary tree nodes. public class BinarySearchTree : ICollection, IEnumerable { #region Private Member Variables private BinaryTreeNode root = null; private int count = 0; private IComparer comparer = Comparer.Default; // used to compare node values when percolating down the tree #endregion #region Constructors public BinarySearchTree() { } public BinarySearchTree(IComparer comparer) { this.comparer = comparer; } #endregion #region Public Methods #region Clear /// /// Removes the contents of the BST /// public void Clear() { root = null; count = 0; } #endregion #region CopyTo /// /// Copies the contents of the BST to an appropriately-sized array of type T, using the Inorder /// traversal method. /// public void CopyTo(T[] array, int index) { CopyTo(array, index, TraversalMethod.Inorder); } /// /// Copies the contents of the BST to an appropriately-sized array of type T, using a specified /// traversal method. /// public void CopyTo(T[] array, int index, TraversalMethod TraversalMethod) { IEnumerable enumProp = null; // Determine which Enumerator-returning property to use, based on the TraversalMethod input parameter switch (TraversalMethod) { case TraversalMethod.Preorder: enumProp = Preorder; break; case TraversalMethod.Inorder: enumProp = Inorder; break; case TraversalMethod.Postorder: default: enumProp = Postorder; break; } // dump the contents of the tree into the passed-in array int i = 0; foreach (T value in enumProp) { array[i + index] = value; i++; } } #endregion #region Add /// /// Adds a new value to the BST. /// /// The data to insert into the BST. /// Adding a value already in the BST has no effect; that is, the SkipList is not /// altered, the Add() method simply exits. public virtual void Add(T data) { // create a new Node instance BinaryTreeNode n = new BinaryTreeNode(data); int result; // now, insert n into the tree // trace down the tree until we hit a NULL BinaryTreeNode current = root, parent = null; while (current != null) { result = comparer.Compare(current.Value, data); if (result == 0) // they are equal - attempting to enter a duplicate - do nothing //return; throw new BSTDuplicatedException(); else if (result < 0) { // current.Value < data, must add n to current's right subtree parent = current; current = current.Right; } else if (result > 0) { // current.Value > data, must add n to current's left subtree parent = current; current = current.Left; } } // We're ready to add the node! count++; if (parent == null) // the tree was empty, make n the root root = n; else { result = comparer.Compare(parent.Value, data); if (result > 0) // parent.Value > data , therefore n must be added to the left subtree parent.Left = n; else // parent.Value < data , therefore n must be added to the right subtree parent.Right = n; } } #endregion #region Contains /// /// Returns a Boolean, indicating if a specified value is contained within the BST. /// /// The data to search for. /// True if data is found in the BST; false otherwise. public bool Contains(T data) { // search the tree for a node that contains data BinaryTreeNode current = root; int result; while (current != null) { result = comparer.Compare(current.Value, data); if (result == 0) // we found data return true; else if (result > 0) // current.Value > data, search current's left subtree current = current.Left; else if (result < 0) // current.Value < data, search current's right subtree current = current.Right; } return false; // didn't find data } #endregion /// /// Returns a Boolean, indicating if a specified value is contained within the BST. /// /// The data to search for. /// True if data is found in the BST; false otherwise. public bool TryFind(T data, out T foundObject) { foundObject = default(T); // search the tree for a node that contains data BinaryTreeNode current = root; int result; while (current != null) { result = comparer.Compare(current.Value, data); if (result == 0) { // we found data foundObject = current.Value; return true; } else if (result > 0) // current.Value > data, search current's right subtree current = current.Left; else if (result < 0) // current.Value < data, search current's left subtree current = current.Right; } return false; // didn't find data } #region Remove /// /// Attempts to remove the specified data element from the BST. /// /// The data to remove from the BST. /// True if the element is found in the tree, and removed; false if the element is not /// found in the tree. public bool Remove(T data) { // first make sure there exist some items in this tree if (root == null) return false; // no items to remove // Now, try to find data in the tree BinaryTreeNode current = root, parent = null; int result = comparer.Compare(current.Value, data); while (result != 0) { if (result > 0) { // current.Value > data, if data exists it's in the left subtree parent = current; current = current.Left; } else if (result < 0) { // current.Value < data, if data exists it's in the right subtree parent = current; current = current.Right; } // If current == null, then we didn't find the item to remove if (current == null) return false; else result = comparer.Compare(current.Value, data); } // At this point, we've found the node to remove count--; // We now need to "rethread" the tree // CASE 1: If current has no right child, then current's left child becomes // the node pointed to by the parent if (current.Right == null) { if (parent == null) root = current.Left; else { result = comparer.Compare(parent.Value, current.Value); if (result > 0) // parent.Value > current.Value, so make current's left child a left child of parent parent.Left = current.Left; else if (result < 0) // parent.Value < current.Value, so make current's left child a right child of parent parent.Right = current.Left; } } // CASE 2: If current's right child has no left child, then current's right child // replaces current in the tree else if (current.Right.Left == null) { current.Right.Left = current.Left; if (parent == null) root = current.Right; else { result = comparer.Compare(parent.Value, current.Value); if (result > 0) // parent.Value > current.Value, so make current's right child a left child of parent parent.Left = current.Right; else if (result < 0) // parent.Value < current.Value, so make current's right child a right child of parent parent.Right = current.Right; } } // CASE 3: If current's right child has a left child, replace current with current's // right child's left-most descendent else { // We first need to find the right node's left-most child BinaryTreeNode leftmost = current.Right.Left, lmParent = current.Right; while (leftmost.Left != null) { lmParent = leftmost; leftmost = leftmost.Left; } // the parent's left subtree becomes the leftmost's right subtree lmParent.Left = leftmost.Right; // assign leftmost's left and right to current's left and right children leftmost.Left = current.Left; leftmost.Right = current.Right; if (parent == null) root = leftmost; else { result = comparer.Compare(parent.Value, current.Value); if (result > 0) // parent.Value > current.Value, so make leftmost a left child of parent parent.Left = leftmost; else if (result < 0) // parent.Value < current.Value, so make leftmost a right child of parent parent.Right = leftmost; } } // Clear out the values from current current.Left = current.Right = null; current = null; return true; } #endregion #region GetEnumerator /// /// Enumerates the BST's contents using inorder traversal. /// /// An enumerator that provides inorder access to the BST's elements. public virtual IEnumerator GetEnumerator() { return GetEnumerator(TraversalMethod.Inorder); } /// /// Enumerates the BST's contents using a specified traversal method. /// /// The type of traversal to perform. /// An enumerator that provides access to the BST's elements using a specified traversal technique. public virtual IEnumerator GetEnumerator(TraversalMethod TraversalMethod) { // The traversal approaches are defined as public properties in the BST class... // This method simply returns the appropriate property. switch (TraversalMethod) { case TraversalMethod.Preorder: return Preorder.GetEnumerator(); case TraversalMethod.Inorder: return Inorder.GetEnumerator(); case TraversalMethod.Postorder: default: return Postorder.GetEnumerator(); } } #endregion public void VisitTreeInOrder(NodeAction nodeCallback) { if (root != null) DoVisitInOrder(root, nodeCallback); } public void VisitTreeInOrder(NodeAction nodeCallback, bool recursive) { if (root != null) DoVisitInOrder(root, nodeCallback); } private void DoVisitInOrder(BinaryTreeNode node, NodeAction nodeCallback) { if (node.Left != null) DoVisitInOrder(node.Left, nodeCallback); if (nodeCallback != null) { nodeCallback(node); } if (node.Right != null) DoVisitInOrder(node.Right, nodeCallback); } #endregion #region Public Properties public BinaryTreeNode Root { get { return root; } } #region Enumerable Properties /// /// Provides enumeration through the BST using preorder traversal. /// public IEnumerable Preorder { get { // A single stack is sufficient here - it simply maintains the correct // order with which to process the children. Stack> toVisit = new Stack>(Count); BinaryTreeNode current = root; if (current != null) toVisit.Push(current); while (toVisit.Count != 0) { // take the top item from the stack current = toVisit.Pop(); // add the right and left children, if not null if (current.Right != null) toVisit.Push(current.Right); if (current.Left != null) toVisit.Push(current.Left); // return the current node yield return current.Value; } } } /// /// Provides enumeration through the BST using inorder traversal. /// public IEnumerable Inorder { get { // A single stack is sufficient - this code was made available by Grant Richins: // http://blogs.msdn.com/grantri/archive/2004/04/08/110165.aspx Stack> toVisit = new Stack>(Count); for (BinaryTreeNode current = root; current != null || toVisit.Count != 0; current = current.Right) { // Get the left-most item in the subtree, remembering the path taken while (current != null) { toVisit.Push(current); current = current.Left; } current = toVisit.Pop(); yield return current.Value; } } } /// /// Provides enumeration through the BST using postorder traversal. /// public IEnumerable Postorder { get { // maintain two stacks, one of a list of nodes to visit, // and one of booleans, indicating if the note has been processed // or not. Stack> toVisit = new Stack>(Count); Stack hasBeenProcessed = new Stack(Count); BinaryTreeNode current = root; if (current != null) { toVisit.Push(current); hasBeenProcessed.Push(false); current = current.Left; } while (toVisit.Count != 0) { if (current != null) { // add this node to the stack with a false processed value toVisit.Push(current); hasBeenProcessed.Push(false); current = current.Left; } else { // see if the node on the stack has been processed bool processed = hasBeenProcessed.Pop(); BinaryTreeNode node = toVisit.Pop(); if (!processed) { // if it's not been processed, "recurse" down the right subtree toVisit.Push(node); hasBeenProcessed.Push(true); // it's now been processed current = node.Right; } else yield return node.Value; } } } } #endregion /// /// Returns the number of elements in the BST. /// public int Count { get { return count; } } public bool IsReadOnly { get { return false; } } #endregion IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } public class BSTDuplicatedException : ApplicationException { public BSTDuplicatedException() : base("Duplicated item already present in BSTree") { } } } openmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/BinaryTree.cs0000644000175000017500000000214611546163204024672 0ustar mathieumathieu#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace BinaryTrees { /// /// Represents a binary tree. This class provides access to the Root of the tree. The developer /// must manually create the binary tree by adding descendents to the root. /// /// The type of data stored in the binary tree's nodes. public class BinaryTree { #region Private Member Variables private BinaryTreeNode root = null; #endregion #region Public Methods /// /// Clears out the contents of the binary tree. /// public void Clear() { root = null; } #endregion #region Public Properties public BinaryTreeNode Root { get { return root; } set { root = value; } } #endregion } } openmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/BinaryTreeNode.cs0000644000175000017500000000374611546163204025507 0ustar mathieumathieu#region Using directives using System; using System.Collections; using System.Collections.Generic; using System.Text; #endregion namespace BinaryTrees { /// /// The BinaryTreeNode class represents a node in a binary tree, or a binary search tree. /// It has precisely two neighbors, which can be accessed via the Left and Right properties. /// /// The type of data stored in the binary tree node. public class BinaryTreeNode : Node { #region Constructors public BinaryTreeNode() : base() {} public BinaryTreeNode(T data) : base(data, null) {} public BinaryTreeNode(T data, BinaryTreeNode left, BinaryTreeNode right) { base.Value = data; NodeList children = new NodeList(2); children[0] = left; children[1] = right; base.Neighbors = children; } #endregion #region Public Properties public BinaryTreeNode Left { get { if (base.Neighbors == null) return null; else return (BinaryTreeNode) base.Neighbors[0]; } set { if (base.Neighbors == null) base.Neighbors = new NodeList(2); base.Neighbors[0] = value; } } public BinaryTreeNode Right { get { if (base.Neighbors == null) return null; else return (BinaryTreeNode) base.Neighbors[1]; } set { if (base.Neighbors == null) base.Neighbors = new NodeList(2); base.Neighbors[1] = value; } } #endregion } } openmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/Node.cs0000644000175000017500000000305111546163204023507 0ustar mathieumathieu#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace BinaryTrees { /// /// The Node<T> class represents the base concept of a Node for a tree or graph. It contains /// a data item of type T, and a list of neighbors. /// /// The type of data contained in the Node. /// None of the classes in the SkmDataStructures2 namespace use the Node class directly; /// they all derive from this class, adding necessary functionality specific to each data structure. public class Node { #region Private Member Variables private T data; private NodeList neighbors = null; #endregion #region Constructors public Node() {} public Node(T data) : this(data, null) {} public Node(T data, NodeList neighbors) { this.data = data; this.neighbors = neighbors; } #endregion #region Properties public T Value { get { return data; } set { data = value; } } protected NodeList Neighbors { get { return neighbors; } set { neighbors = value; } } #endregion } } openmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/NodeList.cs0000644000175000017500000000262511546163204024351 0ustar mathieumathieu#region Using directives using System; using System.Collections.Generic; using System.Text; using System.Collections.ObjectModel; #endregion namespace BinaryTrees { /// /// Represents a collection of Node<T> instances. /// /// The type of data held in the Node instances referenced by this class. public class NodeList : Collection> { #region Constructors public NodeList() : base() { } public NodeList(int initialSize) { // Add the specified number of items for (int i = 0; i < initialSize; i++) base.Items.Add(default(Node)); } #endregion #region Methods /// /// Searches the NodeList for a Node containing a particular value. /// /// The value to search for. /// The Node in the NodeList, if it exists; null otherwise. public Node FindByValue(T value) { // search the list for the value foreach (Node node in Items) if (node.Value.Equals(value)) return node; // if we reached here, we didn't find a matching node return null; } #endregion } } openmcdf-1.5.4/src/OLECompoundFileStorage/BinaryTree/TraversalMethods.cs0000644000175000017500000000056511546163204026120 0ustar mathieumathieu#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace BinaryTrees { /// /// Provides the set of values by which a binary search tree can be enumerated. /// public enum TraversalMethod { Preorder, Inorder, Postorder } } openmcdf-1.5.4/src/OLECompoundFileStorage/CFException.cs0000644000175000017500000001403712042056102022721 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.Runtime.Serialization; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { /// /// OpenMCDF base exception. /// [Serializable] public class CFException : Exception { public CFException() : base() { } protected CFException(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFException(string message) : base(message, null) { } public CFException(string message, Exception innerException) : base(message, innerException) { } } /// /// Raised when a data setter/getter method is invoked /// on a stream or storage object after the disposal of the owner /// compound file object. /// [Serializable] public class CFDisposedException : CFException { public CFDisposedException() : base() { } protected CFDisposedException(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFDisposedException(string message) : base(message, null) { } public CFDisposedException(string message, Exception innerException) : base(message, innerException) { } } /// /// Raised when opening a file with invalid header /// or not supported COM/OLE Structured storage version. /// [Serializable] public class CFFileFormatException : CFException { public CFFileFormatException() : base() { } protected CFFileFormatException(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFFileFormatException(string message) : base(message, null) { } public CFFileFormatException(string message, Exception innerException) : base(message, innerException) { } } /// /// Raised when a named stream or a storage object /// are not found in a parent storage. /// [Serializable] public class CFItemNotFound : CFException { protected CFItemNotFound(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFItemNotFound() : base("Entry not found") { } public CFItemNotFound(string message) : base(message, null) { } public CFItemNotFound(string message, Exception innerException) : base(message, innerException) { } } /// /// Raised when a method call is invalid for the current object state /// [Serializable] public class CFInvalidOperation : CFException { public CFInvalidOperation() : base() { } protected CFInvalidOperation(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFInvalidOperation(string message) : base(message, null) { } public CFInvalidOperation(string message, Exception innerException) : base(message, innerException) { } } /// /// Raised when trying to add a duplicated CFItem /// /// /// Items are compared by name as indicated by specs. /// Two items with the same name CANNOT be added within /// the same storage or sub-storage. /// [Serializable] public class CFDuplicatedItemException : CFException { public CFDuplicatedItemException() : base() { } protected CFDuplicatedItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFDuplicatedItemException(string message) : base(message, null) { } public CFDuplicatedItemException(string message, Exception innerException) : base(message, innerException) { } } /// /// Raised when trying to load a Compound File with invalid, corrupted or mismatched fields (4.1 - specifications) /// /// /// This exception is NOT raised when Compound file has been opened with NO_VALIDATION_EXCEPTION option. /// [Serializable] public class CFCorruptedFileException : CFException { public CFCorruptedFileException() : base() { } protected CFCorruptedFileException(SerializationInfo info, StreamingContext context) : base(info, context) { } public CFCorruptedFileException(string message) : base(message, null) { } public CFCorruptedFileException(string message, Exception innerException) : base(message, innerException) { } } } openmcdf-1.5.4/src/OLECompoundFileStorage/CFItem.cs0000644000175000017500000001715711546414244021704 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using BinaryTrees; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { /// /// Abstract base class for Structured Storage entities. /// /// /// /// /// const String STORAGE_NAME = "report.xls"; /// CompoundFile cf = new CompoundFile(STORAGE_NAME); /// /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create); /// TextWriter tw = new StreamWriter(output); /// /// // CFItem represents both storage and stream items /// VisitedEntryAction va = delegate(CFItem item) /// { /// tw.WriteLine(item.Name); /// }; /// /// cf.RootStorage.VisitEntries(va, true); /// /// tw.Close(); /// /// /// public abstract class CFItem : IComparable { private CompoundFile compoundFile; protected CompoundFile CompoundFile { get { return compoundFile; } } protected void CheckDisposed() { if (compoundFile.IsClosed) throw new CFDisposedException("Owner Compound file has been closed and owned items have been invalidated"); } protected CFItem() { } protected CFItem(CompoundFile compoundFile) { this.compoundFile = compoundFile; } #region IDirectoryEntry Members private IDirectoryEntry dirEntry; internal IDirectoryEntry DirEntry { get { return dirEntry; } set { dirEntry = value; } } internal int CompareTo(CFItem other) { return this.dirEntry.CompareTo(other.DirEntry); } #endregion #region IComparable Members public int CompareTo(object obj) { return this.dirEntry.CompareTo(((CFItem)obj).DirEntry); } #endregion public static bool operator ==(CFItem leftItem, CFItem rightItem) { // If both are null, or both are same instance, return true. if (System.Object.ReferenceEquals(leftItem, rightItem)) { return true; } // If one is null, but not both, return false. if (((object)leftItem == null) || ((object)rightItem == null)) { return false; } // Return true if the fields match: return leftItem.CompareTo(rightItem) == 0; } public static bool operator !=(CFItem leftItem, CFItem rightItem) { return !(leftItem == rightItem); } public override bool Equals(object obj) { return this.CompareTo(obj) == 0; } public override int GetHashCode() { return this.dirEntry.GetEntryName().GetHashCode(); } /// /// Get entity name /// public String Name { get { String n = this.dirEntry.GetEntryName(); if (n != null && n.Length > 0) { return n.TrimEnd('\0'); } else return String.Empty; } } /// /// Size in bytes of the item. It has a valid value /// only if entity is a stream, otherwise it is setted to zero. /// public long Size { get { return this.dirEntry.Size; } } /// /// Return true if item is Storage /// /// /// This check doesn't use reflection or runtime type information /// and doesn't suffer related performance penalties. /// public bool IsStorage { get { return this.dirEntry.StgType == StgType.StgStorage; } } /// /// Return true if item is a Stream /// /// /// This check doesn't use reflection or runtime type information /// and doesn't suffer related performance penalties. /// public bool IsStream { get { return this.dirEntry.StgType == StgType.StgStream; } } /// /// Return true if item is the Root Storage /// /// /// This check doesn't use reflection or runtime type information /// and doesn't suffer related performance penalties. /// public bool IsRoot { get { return this.dirEntry.StgType == StgType.StgRoot; } } /// /// Get/Set the Creation Date of the current item /// public DateTime CreationDate { get { return DateTime.FromFileTime(BitConverter.ToInt64(this.dirEntry.CreationDate, 0)); } set { if (this.dirEntry.StgType != StgType.StgStream && this.dirEntry.StgType != StgType.StgRoot) this.dirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime())); else throw new CFException("Creation Date can only be set on storage entries"); } } /// /// Get/Set the Modify Date of the current item /// public DateTime ModifyDate { get { return DateTime.FromFileTime(BitConverter.ToInt64(this.dirEntry.ModifyDate, 0)); } set { if (this.dirEntry.StgType != StgType.StgStream && this.dirEntry.StgType != StgType.StgRoot) this.dirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime())); else throw new CFException("Modify Date can only be set on storage entries"); } } /// /// Get/Set Object class Guid for Root and Storage entries. /// public Guid CLSID { get { return this.dirEntry.StorageCLSID; } set { if (this.dirEntry.StgType != StgType.StgStream) { this.dirEntry.StorageCLSID = value; } else throw new CFException("Object class GUID can only be set on Root and Storage entries"); } } } } openmcdf-1.5.4/src/OLECompoundFileStorage/CFMock.cs0000644000175000017500000000213711553623772021675 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { /// /// Used as internal template object for binary tree searches. /// internal class CFMock: CFItem { internal CFMock(String dirName, StgType dirType): base() { this.DirEntry = new DirectoryEntry(dirType); this.DirEntry.SetEntryName(dirName); } } } openmcdf-1.5.4/src/OLECompoundFileStorage/CFStorage.cs0000644000175000017500000004371712042056042022401 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using BinaryTrees; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { /// /// Action to apply to visited items in the OLE structured storage /// /// Currently visited item /// /// /// /// //We assume that xls file should be a valid OLE compound file /// const String STORAGE_NAME = "report.xls"; /// CompoundFile cf = new CompoundFile(STORAGE_NAME); /// /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create); /// TextWriter tw = new StreamWriter(output); /// /// VisitedEntryAction va = delegate(CFItem item) /// { /// tw.WriteLine(item.Name); /// }; /// /// cf.RootStorage.VisitEntries(va, true); /// /// tw.Close(); /// /// /// public delegate void VisitedEntryAction(CFItem item); /// /// Storage entity that acts like a logic container for streams /// or substorages in a compound file. /// public class CFStorage : CFItem { private BinarySearchTree children; internal BinarySearchTree Children { get { // Lazy loading of children tree. if (children == null) { if (this.CompoundFile.HasSourceStream) { children = LoadChildren(this.DirEntry.SID); } else { children = new BinarySearchTree(); } } return children; } } /// /// Create a new CFStorage /// /// The Storage Owner - CompoundFile internal CFStorage(CompoundFile compFile) : base(compFile) { this.DirEntry = new DirectoryEntry(StgType.StgStorage); this.DirEntry.StgColor = StgColor.Black; compFile.InsertNewDirectoryEntry(this.DirEntry); } /// /// Create a CFStorage using an existing directory (previously loaded). /// /// The Storage Owner - CompoundFile /// An existing Directory Entry internal CFStorage(CompoundFile compFile, IDirectoryEntry dirEntry) : base(compFile) { if (dirEntry == null || dirEntry.SID < 0) throw new CFException("Attempting to create a CFStorage using an unitialized directory"); this.DirEntry = dirEntry; } private BinarySearchTree LoadChildren(int SID) { return this.CompoundFile.GetChildrenTree(SID); } /// /// Create a new child stream inside the current storage /// /// The new stream name /// The new stream reference /// Raised when adding an item with the same name of an existing one /// Raised when adding a stream to a closed compound file /// Raised when adding a stream with null or empty name /// /// /// /// String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; /// /// CompoundFile cf = new CompoundFile(); /// /// CFStorage st = cf.RootStorage.AddStorage("MyStorage"); /// CFStream sm = st.AddStream("MyStream"); /// byte[] b = Helpers.GetBuffer(220, 0x0A); /// sm.SetData(b); /// /// cf.Save(filename); /// /// /// public CFStream AddStream(String streamName) { CheckDisposed(); if (String.IsNullOrEmpty(streamName)) throw new CFException("Stream name cannot be null or empty"); CFStream cfo = null; // Add new Stream directory entry cfo = new CFStream(this.CompoundFile); cfo.DirEntry.SetEntryName(streamName); try { // Add object to Siblings tree this.Children.Add(cfo); //Rethread children tree... CompoundFile.RefreshIterative(Children.Root); //... and set the root of the tree as new child of the current item directory entry this.DirEntry.Child = Children.Root.Value.DirEntry.SID; } catch (BSTDuplicatedException) { CompoundFile.ResetDirectoryEntry(cfo.DirEntry.SID); cfo = null; throw new CFDuplicatedItemException("An entry with name '" + streamName + "' is already present in storage '" + this.Name + "' "); } return cfo as CFStream; } /// /// Get a named stream contained in the current storage if existing. /// /// Name of the stream to look for /// A stream reference if existing /// Raised if trying to delete item from a closed compound file /// Raised if item to delete is not found /// /// /// String filename = "report.xls"; /// /// CompoundFile cf = new CompoundFile(filename); /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); /// /// byte[] temp = foundStream.GetData(); /// /// Assert.IsNotNull(temp); /// /// cf.Close(); /// /// public CFStream GetStream(String streamName) { CheckDisposed(); CFMock tmp = new CFMock(streamName, StgType.StgStream); //if (children == null) //{ // children = compoundFile.GetChildrenTree(SID); //} CFItem outDe = null; if (Children.TryFind(tmp, out outDe) && outDe.DirEntry.StgType == StgType.StgStream) { return outDe as CFStream; } else { throw new CFItemNotFound("Cannot find item [" + streamName + "] within the current storage"); } } /// /// Get a named storage contained in the current one if existing. /// /// Name of the storage to look for /// A storage reference if existing. /// Raised if trying to delete item from a closed compound file /// Raised if item to delete is not found /// /// /// /// String FILENAME = "MultipleStorage2.cfs"; /// CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false); /// /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); /// /// Assert.IsNotNull(st); /// cf.Close(); /// /// public CFStorage GetStorage(String storageName) { CheckDisposed(); CFMock tmp = new CFMock(storageName, StgType.StgStorage); CFItem outDe = null; if (Children.TryFind(tmp, out outDe) && outDe.DirEntry.StgType == StgType.StgStorage) { return outDe as CFStorage; } else { throw new CFItemNotFound("Cannot find item [" + storageName + "] within the current storage"); } } /// /// Create new child storage directory inside the current storage. /// /// The new storage name /// Reference to the new storage /// Raised when adding an item with the same name of an existing one /// Raised when adding a storage to a closed compound file /// Raised when adding a storage with null or empty name /// /// /// /// String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; /// /// CompoundFile cf = new CompoundFile(); /// /// CFStorage st = cf.RootStorage.AddStorage("MyStorage"); /// CFStream sm = st.AddStream("MyStream"); /// byte[] b = Helpers.GetBuffer(220, 0x0A); /// sm.SetData(b); /// /// cf.Save(filename); /// /// /// public CFStorage AddStorage(String storageName) { CheckDisposed(); if (String.IsNullOrEmpty(storageName)) throw new CFException("Stream name cannot be null or empty"); // Add new Storage directory entry CFStorage cfo = null; cfo = new CFStorage(this.CompoundFile); cfo.DirEntry.SetEntryName(storageName); try { // Add object to Siblings tree Children.Add(cfo); } catch (BSTDuplicatedException) { CompoundFile.ResetDirectoryEntry(cfo.DirEntry.SID); cfo = null; throw new CFDuplicatedItemException("An entry with name '" + storageName + "' is already present in storage '" + this.Name + "' "); } CompoundFile.RefreshIterative(Children.Root); this.DirEntry.Child = Children.Root.Value.DirEntry.SID; return cfo; } //public List GetSubTreeObjects() //{ // List result = new List(); // children.VisitTree(TraversalMethod.Inorder, // delegate(BinaryTreeNode node) // { // result.Add(node.Value); // }); // return result; //} private NodeAction internalAction; /// /// Visit all entities contained in the storage applying a user provided action /// /// Raised when visiting items of a closed compound file /// User action to apply to visited entities /// Visiting recursion level. True means substorages are visited recursively, false indicates that only the direct children of this storage are visited /// /// /// const String STORAGE_NAME = "report.xls"; /// CompoundFile cf = new CompoundFile(STORAGE_NAME); /// /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create); /// TextWriter tw = new StreamWriter(output); /// /// VisitedEntryAction va = delegate(CFItem item) /// { /// tw.WriteLine(item.Name); /// }; /// /// cf.RootStorage.VisitEntries(va, true); /// /// tw.Close(); /// /// public void VisitEntries(VisitedEntryAction action, bool recursive) { CheckDisposed(); if (action != null) { List> subStorages = new List>(); internalAction = delegate(BinaryTreeNode targetNode) { action(targetNode.Value as CFItem); if (targetNode.Value.DirEntry.Child != DirectoryEntry.NOSTREAM) subStorages.Add(targetNode); return; }; this.Children.VisitTreeInOrder(internalAction); if (recursive && subStorages.Count > 0) foreach (BinaryTreeNode n in subStorages) { ((CFStorage)n.Value).VisitEntries(action, recursive); } } } //public void DeleteStream(String name) //{ // Delete(name, typeof(CFStream)); //} //public void DeleteStorage(String name) //{ // Delete(name, typeof(CFStorage)); //} /// /// Remove an entry from the current storage and compound file. /// /// The name of the entry in the current storage to delete /// /// /// cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false); /// cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist. /// cf.Commit(true); /// cf.Close(); /// /// /// Raised if trying to delete item from a closed compound file /// Raised if item to delete is not found /// Raised if trying to delete root storage public void Delete(String entryName) { CheckDisposed(); // Find entry to delete CFMock tmp = new CFMock(entryName, StgType.StgInvalid); CFItem foundObj = null; this.Children.TryFind(tmp, out foundObj); if (foundObj == null) throw new CFItemNotFound("Entry named [" + entryName + "] was not found"); //if (foundObj.GetType() != typeCheck) // throw new CFException("Entry named [" + entryName + "] has not the correct type"); if (foundObj.DirEntry.StgType == StgType.StgRoot) throw new CFException("Root storage cannot be removed"); switch (foundObj.DirEntry.StgType) { case StgType.StgStorage: CFStorage temp = (CFStorage)foundObj; foreach (CFItem de in temp.Children) { temp.Delete(de.Name); } // Remove item from children tree this.Children.Remove(foundObj); // Synchronize tree with directory entries this.CompoundFile.RefreshIterative(this.Children.Root); // Rethread the root of siblings tree... if (this.Children.Root != null) this.DirEntry.Child = this.Children.Root.Value.DirEntry.SID; else this.DirEntry.Child = DirectoryEntry.NOSTREAM; // ...and now remove directory (storage) entry this.CompoundFile.RemoveDirectoryEntry(foundObj.DirEntry.SID); break; case StgType.StgStream: // Remove item from children tree this.Children.Remove(foundObj); // Synchronize tree with directory entries this.CompoundFile.RefreshIterative(this.Children.Root); // Rethread the root of siblings tree... if (this.Children.Root != null) this.DirEntry.Child = this.Children.Root.Value.DirEntry.SID; else this.DirEntry.Child = DirectoryEntry.NOSTREAM; // Remove directory entry this.CompoundFile.RemoveDirectoryEntry(foundObj.DirEntry.SID); break; } //// Refresh recursively all SIDs (invariant for tree sorting) //VisitedEntryAction action = delegate(CFSItem target) //{ // if( ((IDirectoryEntry)target).SID>foundObj.SID ) // { // ((IDirectoryEntry)target).SID--; // } // ((IDirectoryEntry)target).LeftSibling--; //}; } } } openmcdf-1.5.4/src/OLECompoundFileStorage/CFStream.cs0000644000175000017500000001422711546414064022234 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.IO; using BinaryTrees; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { /// /// OLE structured storage stream Object /// It is contained inside a Storage object in a file-directory /// relationship and indexed by its name. /// public class CFStream : CFItem { internal CFStream(CompoundFile sectorManager) : base(sectorManager) { this.DirEntry = new DirectoryEntry(StgType.StgStream); this.DirEntry.StgColor = StgColor.Black; sectorManager.InsertNewDirectoryEntry(this.DirEntry); } internal CFStream(CompoundFile sectorManager, IDirectoryEntry dirEntry) : base(sectorManager) { if (dirEntry == null || dirEntry.SID < 0) throw new CFException("Attempting to add a CFStream using an unitialized directory"); this.DirEntry = dirEntry; } /// /// Set the data associated with the stream object. /// /// /// /// byte[] b = new byte[]{0x0,0x1,0x2,0x3}; /// CompoundFile cf = new CompoundFile(); /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); /// myStream.SetData(b); /// /// /// Data bytes to write to this stream public void SetData(Byte[] data) { CheckDisposed(); this.CompoundFile.SetData(this, data); } /// /// Append the provided data to stream data. /// /// /// /// byte[] b = new byte[]{0x0,0x1,0x2,0x3}; /// byte[] b2 = new byte[]{0x4,0x5,0x6,0x7}; /// CompoundFile cf = new CompoundFile(); /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); /// myStream.SetData(b); // here we could also have invoked .AppendData /// myStream.AppendData(b2); /// cf.Save("MyLargeStreamsFile.cfs); /// cf.Close(); /// /// /// Data bytes to append to this stream /// /// This method allows user to create stream with more than 2GB of data, /// appending data to the end of existing ones. /// Large streams (>2GB) are only supported by CFS version 4. /// Append data can also be invoked on streams with no data in order /// to simplify its use inside loops. /// public void AppendData(Byte[] data) { CheckDisposed(); if (this.Size > 0) { this.CompoundFile.AppendData(this, data); } else { this.CompoundFile.SetData(this, data); } } /// /// Get the data associated with the stream object. /// /// /// /// CompoundFile cf2 = new CompoundFile("AFileName.cfs"); /// CFStream st = cf2.RootStorage.GetStream("MyStream"); /// byte[] buffer = st.GetData(); /// /// /// Array of byte containing stream data /// /// Raised when the owner compound file has been closed. /// public Byte[] GetData() { CheckDisposed(); return this.CompoundFile.GetData(this); } /// /// Get bytes associated with the stream object, starting from /// a provided . When method returns, count will contain the /// effective count of bytes read. /// /// /// /// CompoundFile cf = new CompoundFile("AFileName.cfs"); /// CFStream st = cf.RootStorage.GetStream("MyStream"); /// int count = 8; /// // The stream is supposed to have a length greater than offset + count /// byte[] data = st.GetData(20, ref count); /// cf.Close(); /// /// /// Array of byte containing stream data /// /// Raised when the owner compound file has been closed. /// public Byte[] GetData(long offset, ref int count) { CheckDisposed(); return this.CompoundFile.GetData(this, offset, ref count); } /// /// Copy data from an existing stream. /// /// A stream to read from /// /// Input stream is NOT closed after method invocation. /// public void CopyFrom(Stream input) { CheckDisposed(); byte[] buffer = new byte[input.Length]; if (input.CanSeek) { input.Seek(0, SeekOrigin.Begin); } input.Read(buffer, 0, (int)input.Length); this.SetData(buffer); } } } openmcdf-1.5.4/src/OLECompoundFileStorage/CompoundFile.cs0000644000175000017500000026626212061073176023162 0ustar mathieumathieu/* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ #define FLAT_WRITE // No optimization on the number of write operations using System; using System.Collections.Generic; using System.Text; using System.IO; using BinaryTrees; using System.Collections; using System.Security.AccessControl; using System.Threading; namespace OpenMcdf { internal class CFItemComparer : IComparer { public int Compare(CFItem x, CFItem y) { // X CompareTo Y : X > Y --> 1 ; X < Y --> -1 return (x.DirEntry.CompareTo(y.DirEntry)); //Compare X < Y --> -1 } } /// /// Binary File Format Version. Sector size is 512 byte for version 3, /// 4096 for version 4 /// public enum CFSVersion : int { /// /// Compound file version 3 - The default and most common version available. Sector size 512 bytes, 2GB max file size. /// Ver_3 = 3, /// /// Compound file version 4 - Sector size is 4096 bytes. Using this version could bring some compatibility problem with existing applications. /// Ver_4 = 4 } /// /// Update mode of the compound file. /// Default is ReadOnly. /// public enum UpdateMode { /// /// ReadOnly update mode prevents overwriting /// of the opened file. /// Data changes are allowed but they have to be /// persisted on a different file when required. /// ReadOnly, /// /// Update mode allows subsequent data changing operations /// to be persisted directly on the opened file or stream /// using the Commit /// method when required. Warning: this option may cause existing data loss if misused. /// Update } /// /// Standard Microsoft© Compound File implementation. /// It is also known as OLE/COM structured storage /// and contains a hierarchy of storage and stream objects providing /// efficent storage of multiple kinds of documents in a single file. /// Version 3 and 4 of specifications are supported. /// public class CompoundFile : IDisposable { /// /// Returns the size of standard sectors switching on CFS version (3 or 4) /// /// Standard sector size internal int GetSectorSize() { return 2 << (header.SectorShift - 1); } /// /// Number of DIFAT entries in the header /// private const int HEADER_DIFAT_ENTRIES_COUNT = 109; /// /// Number of FAT entries in a DIFAT Sector /// private readonly int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127; /// /// Sectors ID entries in a FAT Sector /// private readonly int FAT_SECTOR_ENTRIES_COUNT = 128; /// /// Sector ID Size (int) /// private const int SIZE_OF_SID = 4; /// /// Flag for sector recycling. /// private bool sectorRecycle = false; /// /// Flag for unallocated sector zeroing out. /// private bool eraseFreeSectors = false; /// /// Initial capacity of the flushing queue used /// to optimize commit writing operations /// private const int FLUSHING_QUEUE_SIZE = 6000; /// /// Maximum size of the flushing buffer used /// to optimize commit writing operations /// private const int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16; private SectorCollection sectors = new SectorCollection(); //private ArrayList sectors = new ArrayList(); /// /// CompoundFile header /// private Header header; /// /// Compound underlying stream. Null when new CF has been created. /// internal Stream sourceStream = null; /// /// Create a blank, version 3 compound file. /// Sector recycle is turned off to achieve the best reading/writing /// performance in most common scenarios. /// /// /// /// /// byte[] b = new byte[10000]; /// for (int i = 0; i < 10000; i++) /// { /// b[i % 120] = (byte)i; /// } /// /// CompoundFile cf = new CompoundFile(); /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); /// /// Assert.IsNotNull(myStream); /// myStream.SetData(b); /// cf.Save("MyCompoundFile.cfs"); /// cf.Close(); /// /// /// public CompoundFile() { this.header = new Header(); this.sectorRecycle = false; this.sectors.OnVer3SizeLimitReached += new Ver3SizeLimitReached(OnSizeLimitReached); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); //Root -- rootStorage = new CFStorage(this); rootStorage.DirEntry.SetEntryName("Root Entry"); rootStorage.DirEntry.StgType = StgType.StgRoot; rootStorage.DirEntry.StgColor = StgColor.Black; //this.InsertNewDirectoryEntry(rootStorage.DirEntry); } void OnSizeLimitReached() { Sector rangeLockSector = new Sector(GetSectorSize(), sourceStream); sectors.Add(rangeLockSector); rangeLockSector.Type = SectorType.RangeLockSector; _transactionLockAdded = true; _lockSectorId = rangeLockSector.Id; } /// /// Create a new, blank, compound file. /// /// Use a specific Compound File Version to set 512 or 4096 bytes sectors /// If true, recycle unused sectors /// If true, unallocated sectors will be overwritten with zeros /// /// /// /// byte[] b = new byte[10000]; /// for (int i = 0; i < 10000; i++) /// { /// b[i % 120] = (byte)i; /// } /// /// CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, true, true); /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); /// /// Assert.IsNotNull(myStream); /// myStream.SetData(b); /// cf.Save("MyCompoundFile.cfs"); /// cf.Close(); /// /// /// /// /// Sector recycling reduces data writing performances but avoids space wasting in scenarios with frequently /// data manipulation of the same streams. The new compound file is open in Update mode. /// public CompoundFile(CFSVersion cfsVersion, bool sectorRecycle, bool eraseFreeSectors) { this.header = new Header((ushort)cfsVersion); this.sectorRecycle = sectorRecycle; DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); //Root -- rootStorage = new CFStorage(this); rootStorage.DirEntry.SetEntryName("Root Entry"); rootStorage.DirEntry.StgType = StgType.StgRoot; rootStorage.DirEntry.StgColor = StgColor.Black; //this.InsertNewDirectoryEntry(rootStorage.DirEntry); } /// /// Load an existing compound file. /// /// Compound file to read from /// /// /// //A xls file should have a Workbook stream /// String filename = "report.xls"; /// /// CompoundFile cf = new CompoundFile(filename); /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); /// /// byte[] temp = foundStream.GetData(); /// /// Assert.IsNotNull(temp); /// /// cf.Close(); /// /// /// /// File will be open in read-only mode: it has to be saved /// with a different filename. A wrapping implementation has to be provided /// in order to remove/substitute an existing file. Version will be /// automatically recognized from the file. Sector recycle is turned off /// to achieve the best reading/writing performance in most common scenarios. /// public CompoundFile(String fileName) { this.sectorRecycle = false; this.updateMode = UpdateMode.ReadOnly; this.eraseFreeSectors = false; LoadFile(fileName); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } /// /// Load an existing compound file. /// /// Compound file to read from /// If true, recycle unused sectors /// Select the update mode of the underlying data file /// If true, overwrite with zeros unallocated sectors /// /// /// String srcFilename = "data_YOU_CAN_CHANGE.xls"; /// /// CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true); /// /// Random r = new Random(); /// /// byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); /// /// cf.RootStorage.AddStream("MyStream").SetData(buffer); /// /// //This will persist data to the underlying media. /// cf.Commit(); /// cf.Close(); /// /// /// public CompoundFile(String fileName, UpdateMode updateMode, bool sectorRecycle, bool eraseFreeSectors) { this.sectorRecycle = sectorRecycle; this.updateMode = updateMode; this.eraseFreeSectors = eraseFreeSectors; LoadFile(fileName); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } /// /// Load an existing compound file. /// /// Compound file to read from /// If true, recycle unused sectors /// Select the update mode of the underlying data file /// If true, overwrite with zeros unallocated sectors /// If true, no CFCorruptedFileException /// will be thrown even if corrupted file is loaded. Please note that this option is could pose a potential security threat /// /// /// String srcFilename = "data_YOU_CAN_CHANGE.xls"; /// /// CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true, true); /// /// /// Random r = new Random(); /// /// byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); /// /// cf.RootStorage.AddStream("MyStream").SetData(buffer); /// /// //This will persist data to the underlying media. /// cf.Commit(); /// cf.Close(); /// /// /// public CompoundFile(String fileName, UpdateMode updateMode, bool sectorRecycle, bool eraseFreeSectors, bool noValidationException) { this.validationExceptionEnabled = !noValidationException; this.sectorRecycle = sectorRecycle; this.updateMode = updateMode; this.eraseFreeSectors = eraseFreeSectors; LoadFile(fileName); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } private bool validationExceptionEnabled = true; public bool ValidationExceptionEnabled { get { return validationExceptionEnabled; } } /// /// Load an existing compound file. /// /// A stream containing a compound file to read /// If true, recycle unused sectors /// Select the update mode of the underlying data file /// If true, overwrite with zeros unallocated sectors /// /// /// /// String filename = "reportREAD.xls"; /// /// FileStream fs = new FileStream(filename, FileMode.Open); /// CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false); /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); /// /// byte[] temp = foundStream.GetData(); /// /// Assert.IsNotNull(temp); /// /// cf.Close(); /// /// /// /// Raised when trying to open a non-seekable stream /// Raised stream is null public CompoundFile(Stream stream, UpdateMode updateMode, bool sectorRecycle, bool eraseFreeSectors) { this.sectorRecycle = sectorRecycle; this.updateMode = updateMode; this.eraseFreeSectors = eraseFreeSectors; LoadStream(stream); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } /// /// Load an existing compound file. /// /// A stream containing a compound file to read /// If true, recycle unused sectors /// Select the update mode of the underlying data file /// If true, overwrite with zeros unallocated sectors /// If true, openMcdf will try to ignore invalid references or format in order to load a possibly corrupted file anyway /// The 'noValidationEcxception' parameter could possibly lead to security issues so it's recommanded to use it only on trusted sources /// /// /// /// String filename = "reportREAD.xls"; /// /// FileStream fs = new FileStream(filename, FileMode.Open); /// CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false, false, true); /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); /// /// byte[] temp = foundStream.GetData(); //if 'reportRead.xls' is corrupted, openMcdf will try to lad it anyway [noValidationException set true] /// /// Assert.IsNotNull(temp); /// /// cf.Close(); /// /// /// /// Raised when trying to open a non-seekable stream /// Raised stream is null public CompoundFile(Stream stream, UpdateMode updateMode, bool sectorRecycle, bool eraseFreeSectors, bool noValidationException) { this.validationExceptionEnabled = !noValidationException; this.sectorRecycle = sectorRecycle; this.updateMode = updateMode; this.eraseFreeSectors = eraseFreeSectors; LoadStream(stream); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } /// /// Load an existing compound file from a stream. /// /// Streamed compound file /// /// /// /// String filename = "reportREAD.xls"; /// /// FileStream fs = new FileStream(filename, FileMode.Open); /// CompoundFile cf = new CompoundFile(fs); /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); /// /// byte[] temp = foundStream.GetData(); /// /// Assert.IsNotNull(temp); /// /// cf.Close(); /// /// /// /// Raised when trying to open a non-seekable stream /// Raised stream is null public CompoundFile(Stream stream) { LoadStream(stream); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } private UpdateMode updateMode = UpdateMode.ReadOnly; private String fileName = String.Empty; /// /// Commit data changes since the previously commit operation /// to the underlying supporting stream or file on the disk. /// /// /// This method can be used /// only if the supporting stream has been opened in /// Update mode. /// public void Commit() { Commit(false); } #if !FLAT_WRITE private byte[] buffer = new byte[FLUSHING_BUFFER_MAX_SIZE]; private Queue flushingQueue = new Queue(FLUSHING_QUEUE_SIZE); #endif /// /// Commit data changes since the previously commit operation /// to the underlying supporting stream or file on the disk. /// /// If true, release loaded sectors to limit memory usage but reduces following read operations performance /// /// This method can be used only if /// the supporting stream has been opened in /// Update mode. /// public void Commit(bool releaseMemory) { if (_disposed) throw new CFDisposedException("Compound File closed: cannot commit data"); if (updateMode != UpdateMode.Update) throw new CFInvalidOperation("Cannot commit data in Read-Only update mode"); //try //{ #if !FLAT_WRITE int sId = -1; int sCount = 0; int bufOffset = 0; #endif int sSize = GetSectorSize(); if (header.MajorVersion != (ushort)CFSVersion.Ver_3) CheckForLockSector(); sourceStream.Seek(0, SeekOrigin.Begin); sourceStream.Write((byte[])Array.CreateInstance(typeof(byte), GetSectorSize()), 0, sSize); CommitDirectory(); bool gap = true; for (int i = 0; i < sectors.Count; i++) { #if FLAT_WRITE //Note: //Here sectors should not be loaded dynamically because //if they are null it means that no change has involved them; Sector s = (Sector)sectors[i]; if (s != null && s.DirtyFlag) { if (gap) sourceStream.Seek((long)((long)(sSize) + (long)i * (long)sSize), SeekOrigin.Begin); sourceStream.Write(s.GetData(), 0, sSize); sourceStream.Flush(); s.DirtyFlag = false; gap = false; } else { gap = true; continue; } if (releaseMemory) { s.ReleaseData(); s = null; sectors[i] = null; //GC.Collect(); } #else Sector s = sectors[i] as Sector; if (s != null && s.DirtyFlag && flushingQueue.Count < (int)(buffer.Length / sSize)) { //First of a block of contiguous sectors, mark id, start enqueuing if (gap) { sId = s.Id; gap = false; } flushingQueue.Enqueue(s); } else { //Found a gap, stop enqueuing, flush a write operation gap = true; sCount = flushingQueue.Count; if (sCount == 0) continue; bufOffset = 0; while (flushingQueue.Count > 0) { Sector r = flushingQueue.Dequeue(); Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize); r.DirtyFlag = false; if (releaseMemory) { r.ReleaseData(); } bufOffset += sSize; } sourceStream.Seek(((long)sSize + (long)sId * (long)sSize), SeekOrigin.Begin); sourceStream.Write(buffer, 0, sCount * sSize); //Console.WriteLine("W - " + (int)(sCount * sSize )); } #endif } #if !FLAT_WRITE sCount = flushingQueue.Count; bufOffset = 0; while (flushingQueue.Count > 0) { Sector r = flushingQueue.Dequeue(); Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize); r.DirtyFlag = false; if (releaseMemory) { r.ReleaseData(); r = null; } bufOffset += sSize; } if (sCount != 0) { sourceStream.Seek((long)sSize + (long)sId * (long)sSize, SeekOrigin.Begin); sourceStream.Write(buffer, 0, sCount * sSize); //Console.WriteLine("W - " + (int)(sCount * sSize)); } #endif // Seek to beginning position and save header (first 512 or 4096 bytes) sourceStream.Seek(0, SeekOrigin.Begin); header.Write(sourceStream); sourceStream.Flush(); //} //catch (Exception ex) //{ // throw new CFException("Internal error while committing data", ex); //} } /// /// Load compound file from an existing stream. /// /// Stream to load compound file from private void Load(Stream stream) { try { this.header = new Header(); this.directoryEntries = new List(); this.sourceStream = stream; header.Read(stream); int n_sector = Ceiling(((double)(stream.Length - GetSectorSize()) / (double)GetSectorSize())); if (stream.Length > 0x7FFFFF0) this._transactionLockAllocated = true; sectors = new SectorCollection(); //sectors = new ArrayList(); for (int i = 0; i < n_sector; i++) { sectors.Add(null); } LoadDirectories(); this.rootStorage = new CFStorage(this, directoryEntries[0]); } catch (Exception) { if (stream != null) stream.Close(); throw; } } private void LoadFile(String fileName) { this.fileName = fileName; FileStream fs = null; try { if (this.updateMode == UpdateMode.ReadOnly) { fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); } else { fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); } Load(fs); } catch (Exception ex) { if (fs != null) fs.Close(); throw; } } private void LoadStream(Stream stream) { if (stream == null) throw new CFException("Stream parameter cannot be null"); if (!stream.CanSeek) throw new CFException("Cannot load a non-seekable Stream"); stream.Seek(0, SeekOrigin.Begin); Load(stream); } /// /// Return true if this compound file has been /// loaded from an existing file or stream /// public bool HasSourceStream { get { return sourceStream != null; } } /// /// Allocate space, setup sectors id and refresh header /// for the new or updated mini sector chain. /// /// The new MINI sector chain private void SetMiniSectorChain(List sectorChain) { List miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal); List miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); StreamView miniFATView = new StreamView( miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, this.sourceStream ); StreamView miniStreamView = new StreamView( miniStream, GetSectorSize(), this.rootStorage.Size, sourceStream); // Set updated/new sectors within the ministream for (int i = 0; i < sectorChain.Count; i++) { Sector s = sectorChain[i]; if (s.Id != -1) { // Overwrite miniStreamView.Seek(Sector.MINISECTOR_SIZE * s.Id, SeekOrigin.Begin); miniStreamView.Write(s.GetData(), 0, Sector.MINISECTOR_SIZE); } else { // Allocate, position ministream at the end of already allocated // ministream's sectors miniStreamView.Seek(this.rootStorage.Size, SeekOrigin.Begin); miniStreamView.Write(s.GetData(), 0, Sector.MINISECTOR_SIZE); s.Id = (int)(miniStreamView.Position - Sector.MINISECTOR_SIZE) / Sector.MINISECTOR_SIZE; this.rootStorage.DirEntry.Size = miniStreamView.Length; } } // Update miniFAT for (int i = 0; i < sectorChain.Count - 1; i++) { Int32 currentId = sectorChain[i].Id; Int32 nextId = sectorChain[i + 1].Id; //AssureLength(miniFATView, Math.Max(currentId * SIZE_OF_SID, nextId * SIZE_OF_SID)); miniFATView.Seek(currentId * 4, SeekOrigin.Begin); miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4); } //AssureLength(miniFATView, sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID); // Write End of Chain in MiniFAT miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin); miniFATView.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); // Update sector chains SetNormalSectorChain(miniStreamView.BaseSectorChain); SetNormalSectorChain(miniFATView.BaseSectorChain); //Update HEADER and root storage when ministream changes if (miniFAT.Count > 0) { this.rootStorage.DirEntry.StartSetc = miniStream[0].Id; header.MiniFATSectorsNumber = (uint)miniFAT.Count; header.FirstMiniFATSectorID = miniFAT[0].Id; } } private void FreeChain(List sectorChain, bool zeroSector) { byte[] ZEROED_SECTOR = new byte[GetSectorSize()]; List FAT = GetSectorChain(-1, SectorType.FAT); StreamView FATView = new StreamView(FAT, GetSectorSize(), FAT.Count * GetSectorSize(), sourceStream); // Zeroes out sector data (if requested) if (zeroSector) { for (int i = 0; i < sectorChain.Count; i++) { Sector s = sectorChain[i]; s.ZeroData(); } } // Update FAT marking unallocated sectors for (int i = 0; i < sectorChain.Count - 1; i++) { Int32 currentId = sectorChain[i].Id; Int32 nextId = sectorChain[i + 1].Id; //AssureLength(FATView, Math.Max(currentId * SIZE_OF_SID, nextId * SIZE_OF_SID)); FATView.Seek(currentId * 4, SeekOrigin.Begin); FATView.Write(BitConverter.GetBytes(Sector.FREESECT), 0, 4); } } private void FreeMiniChain(List sectorChain, bool zeroSector) { byte[] ZEROED_MINI_SECTOR = new byte[Sector.MINISECTOR_SIZE]; List miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal); List miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, sourceStream); StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), this.rootStorage.Size, sourceStream); // Set updated/new sectors within the ministream if (zeroSector) { for (int i = 0; i < sectorChain.Count; i++) { Sector s = sectorChain[i]; if (s.Id != -1) { // Overwrite miniStreamView.Seek(Sector.MINISECTOR_SIZE * s.Id, SeekOrigin.Begin); miniStreamView.Write(ZEROED_MINI_SECTOR, 0, Sector.MINISECTOR_SIZE); } } } // Update miniFAT for (int i = 0; i < sectorChain.Count - 1; i++) { Int32 currentId = sectorChain[i].Id; Int32 nextId = sectorChain[i + 1].Id; // AssureLength(miniFATView, Math.Max(currentId * SIZE_OF_SID, nextId * SIZE_OF_SID)); miniFATView.Seek(currentId * 4, SeekOrigin.Begin); miniFATView.Write(BitConverter.GetBytes(Sector.FREESECT), 0, 4); } //AssureLength(miniFATView, sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID); // Write End of Chain in MiniFAT miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin); miniFATView.Write(BitConverter.GetBytes(Sector.FREESECT), 0, 4); // Update sector chains SetNormalSectorChain(miniStreamView.BaseSectorChain); SetNormalSectorChain(miniFATView.BaseSectorChain); //Update HEADER and root storage when ministream changes if (miniFAT.Count > 0) { this.rootStorage.DirEntry.StartSetc = miniStream[0].Id; header.MiniFATSectorsNumber = (uint)miniFAT.Count; header.FirstMiniFATSectorID = miniFAT[0].Id; } } /// /// Allocate space, setup sectors id and refresh header /// for the new or updated sector chain. /// /// The new or updated generic sector chain private void SetNormalSectorChain(List sectorChain) { foreach (Sector s in sectorChain) { if (s.Id == -1) { sectors.Add(s); s.Id = sectors.Count - 1; } } SetFATSectorChain(sectorChain); } internal bool _transactionLockAdded = false; internal int _lockSectorId = -1; internal bool _transactionLockAllocated = false; /// /// Check for transaction lock sector addition and mark it in the FAT. /// private void CheckForLockSector() { //If transaction lock has been added and not yet allocated in the FAT... if (_transactionLockAdded && !_transactionLockAllocated) { StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream); fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin); fatStream.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); _transactionLockAllocated = true; } } /// /// Allocate space, setup sectors id and refresh header /// for the new or updated FAT sector chain. /// /// The new or updated generic sector chain private void SetFATSectorChain(List sectorChain) { List fatSectors = GetSectorChain(-1, SectorType.FAT); StreamView fatStream = new StreamView( fatSectors, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), sourceStream ); // Write FAT chain values -- for (int i = 0; i < sectorChain.Count - 1; i++) { Sector sN = sectorChain[i + 1]; Sector sC = sectorChain[i]; fatStream.Seek(sC.Id * 4, SeekOrigin.Begin); fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4); } fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin); fatStream.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); // Merge chain to CFS SetDIFATSectorChain(fatStream.BaseSectorChain); } /// /// Setup the DIFAT sector chain /// /// A FAT sector chain private void SetDIFATSectorChain(List FATsectorChain) { // Get initial sector's count header.FATSectorsNumber = FATsectorChain.Count; // Allocate Sectors foreach (Sector s in FATsectorChain) { if (s.Id == -1) { sectors.Add(s); s.Id = sectors.Count - 1; s.Type = SectorType.FAT; } } // Sector count... int nCurrentSectors = sectors.Count; // Temp DIFAT count int nDIFATSectors = (int)header.DIFATSectorsNumber; if (FATsectorChain.Count > HEADER_DIFAT_ENTRIES_COUNT) { nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HEADER_DIFAT_ENTRIES_COUNT) / DIFAT_SECTOR_FAT_ENTRIES_COUNT); nDIFATSectors = LowSaturation(nDIFATSectors - (int)header.DIFATSectorsNumber); //required DIFAT } // ...sum with new required DIFAT sectors count nCurrentSectors += nDIFATSectors; // ReCheck FAT bias while (header.FATSectorsNumber * FAT_SECTOR_ENTRIES_COUNT < nCurrentSectors) { Sector extraFATSector = new Sector(GetSectorSize(), sourceStream); sectors.Add(extraFATSector); extraFATSector.Id = sectors.Count - 1; extraFATSector.Type = SectorType.FAT; FATsectorChain.Add(extraFATSector); header.FATSectorsNumber++; nCurrentSectors++; //... so, adding a FAT sector may induce DIFAT sectors to increase by one // and consequently this may induce ANOTHER FAT sector (TO-THINK: May this condition occure ?) if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT < (header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ? header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT : 0)) { nDIFATSectors++; nCurrentSectors++; } } List difatSectors = GetSectorChain(-1, SectorType.DIFAT); StreamView difatStream = new StreamView(difatSectors, GetSectorSize(), sourceStream); // Write DIFAT Sectors (if required) // Save room for the following chaining for (int i = 0; i < FATsectorChain.Count; i++) { if (i < HEADER_DIFAT_ENTRIES_COUNT) { header.DIFAT[i] = FATsectorChain[i].Id; } else { // room for DIFAT chaining at the end of any DIFAT sector (4 bytes) if (i != HEADER_DIFAT_ENTRIES_COUNT && (i - HEADER_DIFAT_ENTRIES_COUNT) % DIFAT_SECTOR_FAT_ENTRIES_COUNT == 0) { byte[] temp = new byte[sizeof(int)]; difatStream.Write(temp, 0, sizeof(int)); } difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int)); } } // Allocate room for DIFAT sectors for (int i = 0; i < difatStream.BaseSectorChain.Count; i++) { if (difatStream.BaseSectorChain[i].Id == -1) { sectors.Add(difatStream.BaseSectorChain[i]); difatStream.BaseSectorChain[i].Id = sectors.Count - 1; difatStream.BaseSectorChain[i].Type = SectorType.DIFAT; } } header.DIFATSectorsNumber = (uint)nDIFATSectors; // Chain first sector if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0) { header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id; // Update header information header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count; // Write chaining information at the end of DIFAT Sectors for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++) { Buffer.BlockCopy( BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id), 0, difatStream.BaseSectorChain[i].GetData(), GetSectorSize() - sizeof(int), 4); } Buffer.BlockCopy( BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(), GetSectorSize() - sizeof(int), sizeof(int) ); } else header.FirstDIFATSectorID = Sector.ENDOFCHAIN; // Mark DIFAT Sectors in FAT StreamView fatSv = new StreamView(FATsectorChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), sourceStream); for (int i = 0; i < header.DIFATSectorsNumber; i++) { fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin); fatSv.Write(BitConverter.GetBytes(Sector.DIFSECT), 0, 4); } for (int i = 0; i < header.FATSectorsNumber; i++) { fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin); fatSv.Write(BitConverter.GetBytes(Sector.FATSECT), 0, 4); } //fatSv.Seek(fatSv.BaseSectorChain[fatSv.BaseSectorChain.Count - 1].Id * 4, SeekOrigin.Begin); //fatSv.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); header.FATSectorsNumber = fatSv.BaseSectorChain.Count; } /// /// Get the DIFAT Sector chain /// /// A list of DIFAT sectors private List GetDifatSectorChain() { int validationCount = 0; List result = new List(); int nextSecID = Sector.ENDOFCHAIN; if (header.DIFATSectorsNumber != 0) { validationCount = (int)header.DIFATSectorsNumber; Sector s = sectors[header.FirstDIFATSectorID] as Sector; if (s == null) //Lazy loading { s = new Sector(GetSectorSize(), sourceStream); s.Type = SectorType.DIFAT; s.Id = header.FirstDIFATSectorID; sectors[header.FirstDIFATSectorID] = s; } result.Add(s); while (true && validationCount >= 0) { nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4); // Strictly speaking, the following condition is not correct from // a specification point of view: // only ENDOFCHAIN should break DIFAT chain but // a lot of existing compound files use FREESECT as DIFAT chain termination if (nextSecID == Sector.FREESECT || nextSecID == Sector.ENDOFCHAIN) break; validationCount--; if (validationCount < 0) { this.Close(); throw new CFCorruptedFileException("DIFAT sectors count mismatched. Corrupted compound file"); } s = sectors[nextSecID] as Sector; if (s == null) { s = new Sector(GetSectorSize(), sourceStream); s.Id = nextSecID; sectors[nextSecID] = s; } result.Add(s); } } return result; } /// /// Get the FAT sector chain /// /// List of FAT sectors private List GetFatSectorChain() { int N_HEADER_FAT_ENTRY = 109; //Number of FAT sectors id in the header List result = new List(); int nextSecID = Sector.ENDOFCHAIN; List difatSectors = GetDifatSectorChain(); int idx = 0; // Read FAT entries from the header Fat entry array (max 109 entries) while (idx < header.FATSectorsNumber && idx < N_HEADER_FAT_ENTRY) { nextSecID = header.DIFAT[idx]; Sector s = sectors[nextSecID] as Sector; if (s == null) { s = new Sector(GetSectorSize(), sourceStream); s.Id = nextSecID; s.Type = SectorType.FAT; sectors[nextSecID] = s; } result.Add(s); idx++; } //Is there any DIFAT sector containing other FAT entries ? if (difatSectors.Count > 0) { StreamView difatStream = new StreamView ( difatSectors, GetSectorSize(), header.FATSectorsNumber > N_HEADER_FAT_ENTRY ? (header.FATSectorsNumber - N_HEADER_FAT_ENTRY) * 4 : 0, sourceStream ); byte[] nextDIFATSectorBuffer = new byte[4]; difatStream.Read(nextDIFATSectorBuffer, 0, 4); nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0); int i = 0; int nFat = N_HEADER_FAT_ENTRY; while (nFat < header.FATSectorsNumber) { if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize())) { difatStream.Seek(4, SeekOrigin.Current); i++; continue; } Sector s = sectors[nextSecID] as Sector; if (s == null) { s = new Sector(GetSectorSize(), sourceStream); s.Type = SectorType.FAT; s.Id = nextSecID; sectors[nextSecID] = s;//UUU } result.Add(s); difatStream.Read(nextDIFATSectorBuffer, 0, 4); nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0); nFat++; } } return result; } /// /// Get a standard sector chain /// /// First SecID of the required chain /// A list of sectors private List GetNormalSectorChain(int secID) { List result = new List(); int nextSecID = secID; List fatSectors = GetFatSectorChain(); StreamView fatStream = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count * GetSectorSize(), sourceStream); while (true) { if (nextSecID == Sector.ENDOFCHAIN) break; if (nextSecID >= sectors.Count) throw new CFCorruptedFileException(String.Format("Next Sector ID reference an out of range sector. NextID : {0} while sector count {1}", nextSecID, sectors.Count)); Sector s = sectors[nextSecID] as Sector; if (s == null) { s = new Sector(GetSectorSize(), sourceStream); s.Id = nextSecID; s.Type = SectorType.Normal; sectors[nextSecID] = s; } result.Add(s); fatStream.Seek(nextSecID * 4, SeekOrigin.Begin); int next = fatStream.ReadInt32(); if (next != nextSecID) nextSecID = next; else throw new CFCorruptedFileException("Cyclic sector chain found. File is corrupted"); } return result; } /// /// Get a mini sector chain /// /// First SecID of the required chain /// A list of mini sectors (64 bytes) private List GetMiniSectorChain(int secID) { List result = new List(); if (secID != Sector.ENDOFCHAIN) { int nextSecID = secID; List miniFAT = GetNormalSectorChain(header.FirstMiniFATSectorID); List miniStream = GetNormalSectorChain(RootEntry.StartSetc); StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, sourceStream); StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), rootStorage.Size, sourceStream); BinaryReader miniFATReader = new BinaryReader(miniFATView); nextSecID = secID; while (true) { if (nextSecID == Sector.ENDOFCHAIN) break; Sector ms = new Sector(Sector.MINISECTOR_SIZE, sourceStream); byte[] temp = new byte[Sector.MINISECTOR_SIZE]; ms.Id = nextSecID; ms.Type = SectorType.Mini; miniStreamView.Seek(nextSecID * Sector.MINISECTOR_SIZE, SeekOrigin.Begin); miniStreamView.Read(ms.GetData(), 0, Sector.MINISECTOR_SIZE); result.Add(ms); miniFATView.Seek(nextSecID * 4, SeekOrigin.Begin); nextSecID = miniFATReader.ReadInt32(); } } return result; } /// /// Get a sector chain from a compound file given the first sector ID /// and the required sector type. /// /// First chain sector's id /// Type of Sectors in the required chain (mini sectors, normal sectors or FAT) /// A list of Sectors as the result of their concatenation internal List GetSectorChain(int secID, SectorType chainType) { switch (chainType) { case SectorType.DIFAT: return GetDifatSectorChain(); case SectorType.FAT: return GetFatSectorChain(); case SectorType.Normal: return GetNormalSectorChain(secID); case SectorType.Mini: return GetMiniSectorChain(secID); default: throw new CFException("Unsupproted chain type"); } } private CFStorage rootStorage; /// /// The entry point object that represents the /// root of the structures tree to get or set storage or /// stream data. /// /// /// /// /// //Create a compound file /// string FILENAME = "MyFileName.cfs"; /// CompoundFile ncf = new CompoundFile(); /// /// CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1"); /// /// l1.AddStream("l1ns1"); /// l1.AddStream("l1ns2"); /// l1.AddStream("l1ns3"); /// CFStorage l2 = l1.AddStorage("Storage Level 2"); /// l2.AddStream("l2ns1"); /// l2.AddStream("l2ns2"); /// /// ncf.Save(FILENAME); /// ncf.Close(); /// /// public CFStorage RootStorage { get { return rootStorage as CFStorage; } } public CFSVersion Version { get { return (CFSVersion)this.header.MajorVersion; } } internal void InsertNewDirectoryEntry(IDirectoryEntry de) { // If we are not adding an invalid dirEntry as // in a normal loading from file (invalid dirs MAY pad a sector) if (de != null) { // Find first available invalid slot (if any) to reuse it for (int i = 0; i < directoryEntries.Count; i++) { if (directoryEntries[i].StgType == StgType.StgInvalid) { directoryEntries[i] = de; de.SID = i; return; } } } // No invalid directory entry found directoryEntries.Add(de); de.SID = directoryEntries.Count - 1; } /// /// Reset a directory entry setting it to StgInvalid in the Directory. /// /// Sid of the directory to invalidate internal void ResetDirectoryEntry(int sid) { directoryEntries[sid] = new DirectoryEntry(StgType.StgInvalid); } internal BinarySearchTree GetChildrenTree(int sid) { BinarySearchTree bst = new BinarySearchTree(new CFItemComparer()); // Load children from their original tree. DoLoadChildren(bst, directoryEntries[sid]); // Rebuild of (Red)-Black tree of entry children. bst.VisitTreeInOrder(RefreshSIDs); return bst; } private void DoLoadChildren(BinarySearchTree bst, IDirectoryEntry de) { if (de.Child != DirectoryEntry.NOSTREAM ) { if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return; if (directoryEntries[de.Child].StgType == StgType.StgStream) bst.Add(new CFStream(this, directoryEntries[de.Child])); else bst.Add(new CFStorage(this, directoryEntries[de.Child])); LoadSiblings(bst, directoryEntries[de.Child]); } } // Doubling methods allows iterative behavior while avoiding // to insert duplicate items private void LoadSiblings(BinarySearchTree bst, IDirectoryEntry de) { if (de.LeftSibling != DirectoryEntry.NOSTREAM) { // If there're more left siblings load them... DoLoadSiblings(bst, directoryEntries[de.LeftSibling]); } if (de.RightSibling != DirectoryEntry.NOSTREAM) { // If there're more right siblings load them... DoLoadSiblings(bst, directoryEntries[de.RightSibling]); } } private void DoLoadSiblings(BinarySearchTree bst, IDirectoryEntry de) { if (ValidateSibling(de.LeftSibling)) { // If there're more left siblings load them... DoLoadSiblings(bst, directoryEntries[de.LeftSibling]); } if (directoryEntries[de.SID].StgType == StgType.StgStream) bst.Add(new CFStream(this, directoryEntries[de.SID])); else if (directoryEntries[de.SID].StgType == StgType.StgStorage) bst.Add(new CFStorage(this, directoryEntries[de.SID])); if (ValidateSibling(de.RightSibling)) { // If there're more right siblings load them... DoLoadSiblings(bst, directoryEntries[de.RightSibling]); } } private bool ValidateSibling(int sid) { if (sid != DirectoryEntry.NOSTREAM) { // if this siblings id does not overflow current list if (sid >= directoryEntries.Count) { if (this.validationExceptionEnabled) { this.Close(); throw new CFCorruptedFileException("A Directory Entry references the non-existent sid number " + sid.ToString()); } else return false; } //if this sibling is valid... if (directoryEntries[sid].StgType == StgType.StgInvalid) { if (this.validationExceptionEnabled) { this.Close(); throw new CFCorruptedFileException("A Directory Entry has a valid reference to an Invalid Storage Type directory"); } else return false; } if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType)) { if (this.validationExceptionEnabled) { this.Close(); throw new CFCorruptedFileException("A Directory Entry has an invalid Storage Type"); } else return false; } return true; //No fault condition encountered for sid being validated } return false; } /// /// Load directory entries from compound file. Header and FAT MUST be already loaded. /// private void LoadDirectories() { List directoryChain = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal); if (header.FirstDirectorySectorID == Sector.ENDOFCHAIN) header.FirstDirectorySectorID = directoryChain[0].Id; StreamView dirReader = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), sourceStream); while (dirReader.Position < directoryChain.Count * GetSectorSize()) { DirectoryEntry de = new DirectoryEntry(StgType.StgInvalid); //We are not inserting dirs. Do not use 'InsertNewDirectoryEntry' de.Read(dirReader); directoryEntries.Add(de); de.SID = directoryEntries.Count - 1; } } internal void RefreshSIDs(BinaryTreeNode Node) { if (Node.Value != null) { if (Node.Left != null && (Node.Left.Value.DirEntry.StgType != StgType.StgInvalid)) { Node.Value.DirEntry.LeftSibling = Node.Left.Value.DirEntry.SID; } else { Node.Value.DirEntry.LeftSibling = DirectoryEntry.NOSTREAM; } if (Node.Right != null && (Node.Right.Value.DirEntry.StgType != StgType.StgInvalid)) { Node.Value.DirEntry.RightSibling = Node.Right.Value.DirEntry.SID; } else { Node.Value.DirEntry.RightSibling = DirectoryEntry.NOSTREAM; } } } internal void RefreshIterative(BinaryTreeNode node) { if (node == null) return; RefreshSIDs(node); RefreshIterative(node.Left); RefreshIterative(node.Right); } /// /// Commit directory entries change on the Current Source stream /// private void CommitDirectory() { const int DIRECTORY_SIZE = 128; List directorySectors = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal); StreamView sv = new StreamView(directorySectors, GetSectorSize(), 0, sourceStream); foreach (IDirectoryEntry di in directoryEntries) { di.Write(sv); } int delta = directoryEntries.Count; while (delta % (GetSectorSize() / DIRECTORY_SIZE) != 0) { DirectoryEntry dummy = new DirectoryEntry(StgType.StgInvalid); dummy.Write(sv); delta++; } foreach (Sector s in directorySectors) { s.Type = SectorType.Directory; } SetNormalSectorChain(directorySectors); header.FirstDirectorySectorID = directorySectors[0].Id; //Version 4 supports directory sectors count if (header.MajorVersion == 3) { header.DirectorySectorsNumber = 0; } else { header.DirectorySectorsNumber = directorySectors.Count; } } /// /// Saves the in-memory image of Compound File to a file. /// /// File name to write the compound file to /// Raised if destination file is not seekable public void Save(String fileName) { if (_disposed) throw new CFException("Compound File closed: cannot save data"); FileStream fs = null; try { fs = new FileStream(fileName, FileMode.Create); Save(fs); } catch (Exception ex) { throw new CFException("Error saving file [" + fileName + "]", ex); } finally { if (fs != null) fs.Flush(); if (fs != null) fs.Close(); } } /// /// Saves the in-memory image of Compound File to a stream. /// /// /// Destination Stream must be seekable. /// /// The stream to save compound File to /// Raised if destination stream is not seekable /// Raised if Compound File Storage has been already disposed /// /// /// MemoryStream ms = new MemoryStream(size); /// /// CompoundFile cf = new CompoundFile(); /// CFStorage st = cf.RootStorage.AddStorage("MyStorage"); /// CFStream sm = st.AddStream("MyStream"); /// /// byte[] b = new byte[]{0x00,0x01,0x02,0x03}; /// /// sm.SetData(b); /// cf.Save(ms); /// cf.Close(); /// /// public void Save(Stream stream) { if (_disposed) throw new CFDisposedException("Compound File closed: cannot save data"); if (!stream.CanSeek) throw new CFException("Cannot save on a non-seekable stream"); CheckForLockSector(); int sSize = GetSectorSize(); try { stream.Write((byte[])Array.CreateInstance(typeof(byte), sSize), 0, sSize); CommitDirectory(); for (int i = 0; i < sectors.Count; i++) { Sector s = sectors[i] as Sector; if (s == null) { // Load source (unmodified) sectors // Here we have to ignore "Dirty flag" of // sectors because we are NOT modifying the source // in a differential way but ALL sectors need to be // persisted on the destination stream s = new Sector(sSize, sourceStream); s.Id = i; //sectors[i] = s; } stream.Write(s.GetData(), 0, sSize); //s.ReleaseData(); } stream.Seek(0, SeekOrigin.Begin); header.Write(stream); } catch (Exception ex) { throw new CFException("Internal error while saving compound file to stream ", ex); } } /// /// Scan FAT o miniFAT for free sectors to reuse. /// /// Type of sector to look for /// A stack of available sectors or minisectors already allocated internal Queue FindFreeSectors(SectorType sType) { Queue freeList = new Queue(); if (sType == SectorType.Normal) { List FatChain = GetSectorChain(-1, SectorType.FAT); StreamView fatStream = new StreamView(FatChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), sourceStream); int idx = 0; while (idx < sectors.Count) { int id = fatStream.ReadInt32(); if (id == Sector.FREESECT) { if (sectors[idx] == null) { Sector s = new Sector(GetSectorSize(), sourceStream); s.Id = idx; sectors[idx] = s; } freeList.Enqueue(sectors[idx] as Sector); } idx++; } } else { List miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal); StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, sourceStream); List miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), rootStorage.Size, sourceStream); long ptr = 0; int nMinisectors = (int)(miniStreamView.Length / Sector.MINISECTOR_SIZE); while (ptr < nMinisectors) { //AssureLength(miniStreamView, (int)miniFATView.Length); int id = miniFATView.ReadInt32(); ptr += 4; if (id == Sector.FREESECT) { Sector ms = new Sector(Sector.MINISECTOR_SIZE, sourceStream); byte[] temp = new byte[Sector.MINISECTOR_SIZE]; ms.Id = (int)((ptr - 4) / 4); ms.Type = SectorType.Mini; miniStreamView.Seek(ms.Id * Sector.MINISECTOR_SIZE, SeekOrigin.Begin); miniStreamView.Read(ms.GetData(), 0, Sector.MINISECTOR_SIZE); freeList.Enqueue(ms); } } } return freeList; } internal void SetData(CFItem cfItem, Byte[] buffer) { SetStreamData(cfItem, buffer); } /// /// INTERNAL DEVELOPMENT. DO NOT CALL. /// /// internal void AppendData(CFItem cfItem, Byte[] buffer) { //CheckFileLength(); if (buffer == null) throw new CFException("Parameter [buffer] cannot be null"); //Quick and dirty :-) if (buffer.Length == 0) return; IDirectoryEntry directoryEntry = cfItem.DirEntry; SectorType _st = SectorType.Normal; int _sectorSize = GetSectorSize(); if (buffer.Length + directoryEntry.Size < header.MinSizeStandardStream) { _st = SectorType.Mini; _sectorSize = Sector.MINISECTOR_SIZE; } // Check for transition ministream -> stream: // Only in this case we need to free old sectors, // otherwise they will be overwritten. byte[] tempMini = null; if (directoryEntry.StartSetc != Sector.ENDOFCHAIN) { if ((directoryEntry.Size + buffer.Length) > header.MinSizeStandardStream && directoryEntry.Size < header.MinSizeStandardStream) { tempMini = new byte[directoryEntry.Size]; StreamView miniData = new StreamView(GetMiniSectorChain(directoryEntry.StartSetc), Sector.MINISECTOR_SIZE, sourceStream); miniData.Read(tempMini, 0, (int)directoryEntry.Size); FreeMiniChain(GetMiniSectorChain(directoryEntry.StartSetc), this.eraseFreeSectors); directoryEntry.StartSetc = Sector.ENDOFCHAIN; directoryEntry.Size = 0; } } List sectorChain = GetSectorChain(directoryEntry.StartSetc, _st); Queue freeList = FindFreeSectors(_st); // Collect available free sectors StreamView sv = new StreamView(sectorChain, _sectorSize, tempMini != null ? buffer.Length + tempMini.Length : buffer.Length, freeList, sourceStream); // If stream was a ministream, copy ministream data // in the new stream if (tempMini != null) { sv.Seek(0, SeekOrigin.Begin); sv.Write(tempMini, 0, tempMini.Length); } else { sv.Seek(directoryEntry.Size, SeekOrigin.Begin); } // Write appended data sv.Write(buffer, 0, buffer.Length); switch (_st) { case SectorType.Normal: SetNormalSectorChain(sv.BaseSectorChain); break; case SectorType.Mini: SetMiniSectorChain(sv.BaseSectorChain); break; } if (sv.BaseSectorChain.Count > 0) { directoryEntry.StartSetc = sv.BaseSectorChain[0].Id; directoryEntry.Size = (long)(buffer.Length) + directoryEntry.Size; if (tempMini != null) directoryEntry.Size += tempMini.Length; } else { directoryEntry.StartSetc = Sector.ENDOFCHAIN; directoryEntry.Size = 0; } } private void SetStreamData(CFItem cfItem, Byte[] buffer) { //CheckFileLength(); if (buffer == null) throw new CFException("Parameter [buffer] cannot be null"); //CheckFileLength(); if (buffer == null) throw new CFException("Parameter [buffer] cannot be null"); //Quick and dirty :-) if (buffer.Length == 0) return; IDirectoryEntry directoryEntry = cfItem.DirEntry; SectorType _st = SectorType.Normal; int _sectorSize = GetSectorSize(); if (buffer.Length < header.MinSizeStandardStream) { _st = SectorType.Mini; _sectorSize = Sector.MINISECTOR_SIZE; } // Check for transition ministream -> stream: // Only in this case we need to free old sectors, // otherwise they will be overwritten. if (directoryEntry.StartSetc != Sector.ENDOFCHAIN) { if ( (buffer.Length < header.MinSizeStandardStream && directoryEntry.Size > header.MinSizeStandardStream) || (buffer.Length > header.MinSizeStandardStream && directoryEntry.Size < header.MinSizeStandardStream) ) { if (directoryEntry.Size < header.MinSizeStandardStream) { FreeMiniChain(GetMiniSectorChain(directoryEntry.StartSetc), this.eraseFreeSectors); } else { FreeChain(GetNormalSectorChain(directoryEntry.StartSetc), this.eraseFreeSectors); } directoryEntry.Size = 0; directoryEntry.StartSetc = Sector.ENDOFCHAIN; } } List sectorChain = GetSectorChain(directoryEntry.StartSetc, _st); Queue freeList = null; if (this.sectorRecycle) freeList = FindFreeSectors(_st); // Collect available free sectors StreamView sv = new StreamView(sectorChain, _sectorSize, buffer.Length, freeList, sourceStream); sv.Write(buffer, 0, buffer.Length); switch (_st) { case SectorType.Normal: SetNormalSectorChain(sv.BaseSectorChain); break; case SectorType.Mini: SetMiniSectorChain(sv.BaseSectorChain); break; } if (sv.BaseSectorChain.Count > 0) { directoryEntry.StartSetc = sv.BaseSectorChain[0].Id; directoryEntry.Size = buffer.Length; } else { directoryEntry.StartSetc = Sector.ENDOFCHAIN; directoryEntry.Size = 0; } } /// /// Check file size limit ( 2GB for version 3 ) /// private void CheckFileLength() { throw new NotImplementedException(); } internal byte[] GetData(CFStream cFStream, long offset, ref int count) { byte[] result = null; IDirectoryEntry de = cFStream.DirEntry; count = (int)Math.Min((long)(de.Size - offset), (long)count); StreamView sView = null; if (de.Size < header.MinSizeStandardStream) { sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MINISECTOR_SIZE, de.Size, sourceStream); } else { sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, sourceStream); } result = new byte[(int)(count)]; sView.Seek(offset, SeekOrigin.Begin); sView.Read(result, 0, result.Length); return result; } internal byte[] GetData(CFStream cFStream) { if (_disposed) throw new CFDisposedException("Compound File closed: cannot access data"); byte[] result = null; IDirectoryEntry de = cFStream.DirEntry; //IDirectoryEntry root = directoryEntries[0]; if (de.Size < header.MinSizeStandardStream) { StreamView miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MINISECTOR_SIZE, de.Size, sourceStream); BinaryReader br = new BinaryReader(miniView); result = br.ReadBytes((int)de.Size); br.Close(); } else { StreamView sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, sourceStream); result = new byte[(int)de.Size]; sView.Read(result, 0, result.Length); } return result; } private static int Ceiling(double d) { return (int)Math.Ceiling(d); } private static int LowSaturation(int i) { return i > 0 ? i : 0; } internal void RemoveDirectoryEntry(int sid) { if (sid >= directoryEntries.Count) throw new CFException("Invalid SID of the directory entry to remove"); if (directoryEntries[sid].StgType == StgType.StgStream) { // Clear the associated stream (or ministream) if required if (directoryEntries[sid].Size > 0) //thanks to Mark Bosold for this ! { if (directoryEntries[sid].Size < header.MinSizeStandardStream) { List miniChain = GetSectorChain(directoryEntries[sid].StartSetc, SectorType.Mini); FreeMiniChain(miniChain, this.eraseFreeSectors); } else { List chain = GetSectorChain(directoryEntries[sid].StartSetc, SectorType.Normal); FreeChain(chain, this.eraseFreeSectors); } } } Random r = new Random(); directoryEntries[sid].SetEntryName("_DELETED_NAME_" + r.Next(short.MaxValue).ToString()); directoryEntries[sid].StgType = StgType.StgInvalid; } /// /// Close the Compound File object CompoundFile and /// free all associated resources (e.g. open file handle and allocated memory). /// /// When the Close method is called, /// all the associated stream and storage objects are invalidated: /// any operation invoked on them will produce a CFDisposedException. /// /// /// /// /// const String FILENAME = "CompoundFile.cfs"; /// CompoundFile cf = new CompoundFile(FILENAME); /// /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); /// cf.Close(); /// /// try /// { /// byte[] temp = st.GetStream("MyStream").GetData(); /// /// // The following line will fail because back-end object has been closed /// Assert.Fail("Stream without media"); /// } /// catch (Exception ex) /// { /// Assert.IsTrue(ex is CFDisposedException); /// } /// /// public void Close() { ((IDisposable)this).Dispose(); } private bool closeStream = true; internal void Close(bool closeStream) { ((IDisposable)this).Dispose(); } #region IDisposable Members private bool _disposed;//false void IDisposable.Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion private object lockObject = new Object(); /// /// When called from user code, release all resources, otherwise, in the case runtime called it, /// only unmanagd resources are released. /// /// If true, method has been called from User code, if false it's been called from .net runtime protected virtual void Dispose(bool disposing) { try { if (!_disposed) { lock (lockObject) { if (disposing) { // Call from user code... if (sectors != null) { sectors.Clear(); sectors = null; } this.rootStorage = null; // Some problem releasing resources... this.header = null; this.directoryEntries.Clear(); this.directoryEntries = null; this.fileName = null; this.lockObject = null; #if !FLAT_WRITE this.buffer = null; #endif } if (this.sourceStream != null && closeStream) this.sourceStream.Close(); } } } finally { _disposed = true; } } internal bool IsClosed { get { return _disposed; } } private List directoryEntries = new List(); //internal List DirectoryEntries //{ // get { return directoryEntries; } //} internal IDirectoryEntry RootEntry { get { return directoryEntries[0]; } } private IList FindDirectoryEntries(String entryName) { List result = new List(); foreach (IDirectoryEntry d in directoryEntries) { if (d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid) result.Add(d); } return result; } /// /// Get a list of all entries with a given name contained in the document. /// /// Name of entries to retrive /// A list of name-matching entries /// This function is aimed to speed up entity lookup in /// flat-structure files (only one or little more known entries) /// without the performance penalty related to entities hierarchy constraints. /// There is no implied hierarchy in the returned list. /// public IList GetAllNamedEntries(String entryName) { IList r = FindDirectoryEntries(entryName); List result = new List(); foreach (IDirectoryEntry id in r) { if (id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid) { CFItem i = id.StgType == StgType.StgStorage ? (CFItem)new CFStorage(this, id) : (CFItem)new CFStream(this, id); result.Add(i); } } return result; } /// /// Compress free space by removing unallocated sectors from compound file /// effectively reducing stream or file size. /// /// /// Current implementation supports compression only for ver. 3 compound files. /// /// /// /// /// //This code has been extracted from unit test /// /// String FILENAME = "MultipleStorage3.cfs"; /// /// FileInfo srcFile = new FileInfo(FILENAME); /// /// File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); /// /// CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); /// /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); /// st = st.GetStorage("AnotherStorage"); /// /// Assert.IsNotNull(st); /// st.Delete("Another2Stream"); //17Kb /// cf.Commit(); /// cf.Close(); /// /// CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); /// /// FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); /// /// Assert.IsTrue(srcFile.Length > dstFile.Length); /// /// /// public static void ShrinkCompoundFile(Stream s) { CompoundFile cf = new CompoundFile(s); if (cf.header.MajorVersion != (ushort)CFSVersion.Ver_3) throw new CFException("Current implementation of free space compression does not support version 4 of Compound File Format"); using (CompoundFile tempCF = new CompoundFile((CFSVersion)cf.header.MajorVersion, cf.sectorRecycle, cf.eraseFreeSectors)) { //Copy Root CLSID tempCF.RootStorage.CLSID = new Guid(cf.RootStorage.CLSID.ToByteArray()); DoCompression(cf.RootStorage, tempCF.RootStorage); MemoryStream tmpMS = new MemoryStream((int)cf.sourceStream.Length); //This could be a problem for v4 tempCF.Save(tmpMS); tempCF.Close(); // If we were based on a writable stream, we update // the stream and do reload from the compressed one... s.Seek(0, SeekOrigin.Begin); tmpMS.WriteTo(s); s.Seek(0, SeekOrigin.Begin); s.SetLength(tmpMS.Length); tmpMS.Close(); cf.Close(false); } } /// /// Remove unallocated sectors from compound file in order to reduce its size. /// /// /// Current implementation supports compression only for ver. 3 compound files. /// /// /// /// /// //This code has been extracted from unit test /// /// String FILENAME = "MultipleStorage3.cfs"; /// /// FileInfo srcFile = new FileInfo(FILENAME); /// /// File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); /// /// CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); /// /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); /// st = st.GetStorage("AnotherStorage"); /// /// Assert.IsNotNull(st); /// st.Delete("Another2Stream"); //17Kb /// cf.Commit(); /// cf.Close(); /// /// CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); /// /// FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); /// /// Assert.IsTrue(srcFile.Length > dstFile.Length); /// /// /// public static void ShrinkCompoundFile(String fileName) { FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite); ShrinkCompoundFile(fs); fs.Close(); } /// /// Recursively clones valid structures, avoiding to copy free sectors. /// /// Current source storage to clone /// Current cloned destination storage private static void DoCompression(CFStorage currSrcStorage, CFStorage currDstStorage) { VisitedEntryAction va = delegate(CFItem item) { if (item.IsStream) { CFStream itemAsStream = item as CFStream; CFStream st = ((CFStorage)currDstStorage).AddStream(itemAsStream.Name); st.SetData(itemAsStream.GetData()); } else if (item.IsStorage) { CFStorage itemAsStorage = item as CFStorage; CFStorage strg = ((CFStorage)currDstStorage).AddStorage(itemAsStorage.Name); strg.CLSID = new Guid(itemAsStorage.CLSID.ToByteArray()); DoCompression(itemAsStorage, strg); // recursion, one level deeper } }; currSrcStorage.VisitEntries(va, false); } } }openmcdf-1.5.4/src/OLECompoundFileStorage/Directory.cs0000644000175000017500000000517411546163204022532 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OLECompoundFileStorage { public class DirectoryEntryCollection:ICollection { private List directoryEntries; internal DirectoryEntryCollection(List directoryEntries) { this.directoryEntries = directoryEntries; } public void Add(IDirectoryEntry item) { throw new NotSupportedException("Read Only Collection"); } public void Clear() { throw new NotSupportedException("Read Only Collection"); } public bool Contains(IDirectoryEntry item) { throw new NotImplementedException(); } public void CopyTo(IDirectoryEntry[] array, int arrayIndex) { throw new NotImplementedException(); } public int Count { get { throw new NotImplementedException(); } } public bool IsReadOnly { get { throw new NotImplementedException(); } } public bool Remove(IDirectoryEntry item) { throw new NotImplementedException(); } #region IEnumerable Members public IEnumerator GetEnumerator() { throw new NotImplementedException(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion } public class Directory { private bool isCompoundFileDisposed = false; internal bool IsCompoundFileDisposed { get { return isCompoundFileDisposed; } set { isCompoundFileDisposed = value; } } } } openmcdf-1.5.4/src/OLECompoundFileStorage/DirectoryEntry.cs0000644000175000017500000002716711752022672023564 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.IO; using System.Globalization; using BinaryTrees; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { public enum StgType : int { StgInvalid = 0, StgStorage = 1, StgStream = 2, StgLockbytes = 3, StgProperty = 4, StgRoot = 5 } public enum StgColor : int { Red = 0, Black = 1 } internal class DirectoryEntry : IComparable, IDirectoryEntry { private int sid = -1; public int SID { get { return sid; } set { sid = value; } } internal static Int32 NOSTREAM = unchecked((int)0xFFFFFFFF); public DirectoryEntry(StgType stgType) { this.stgType = stgType; switch (stgType) { case StgType.StgStream: this.storageCLSID = new Guid("00000000000000000000000000000000"); this.creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; this.modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; break; case StgType.StgStorage: this.creationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime())); break; case StgType.StgRoot: this.creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; this.modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; break; } } private byte[] entryName = new byte[64]; public byte[] EntryName { get { return entryName; } //set //{ // entryName = value; //} } public String GetEntryName() { if (entryName != null && entryName.Length > 0) { return Encoding.Unicode.GetString(entryName).Remove((this.nameLength - 1) / 2); } else return String.Empty; } public void SetEntryName(String entryName) { if ( entryName.Contains(@"\") || entryName.Contains(@"/") || entryName.Contains(@":") || entryName.Contains(@"!") ) throw new CFException("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name"); if (entryName.Length > 31) throw new CFException("Entry name MUST be smaller than 31 characters"); byte[] newName = null; byte[] temp = Encoding.Unicode.GetBytes(entryName); newName = new byte[64]; Buffer.BlockCopy(temp, 0, newName, 0, temp.Length); newName[temp.Length] = 0x00; newName[temp.Length + 1] = 0x00; this.entryName = newName; this.nameLength = (ushort)(temp.Length + 2); } private ushort nameLength; public ushort NameLength { get { return nameLength; } set { throw new NotImplementedException(); } } private StgType stgType = StgType.StgInvalid; public StgType StgType { get { return stgType; } set { stgType = value; } } private StgColor stgColor = StgColor.Black; public StgColor StgColor { get { return stgColor; } set { stgColor = value; } } private Int32 leftSibling = NOSTREAM; public Int32 LeftSibling { get { return leftSibling; } set { leftSibling = value; } } private Int32 rightSibling = NOSTREAM; public Int32 RightSibling { get { return rightSibling; } set { rightSibling = value; } } private Int32 child = NOSTREAM; public Int32 Child { get { return child; } set { child = value; } } private Guid storageCLSID = Guid.NewGuid(); public Guid StorageCLSID { get { return storageCLSID; } set { this.storageCLSID = value; } } private Int32 stateBits; public Int32 StateBits { get { return stateBits; } set { stateBits = value; } } private byte[] creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public byte[] CreationDate { get { return creationDate; } set { creationDate = value; } } private byte[] modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public byte[] ModifyDate { get { return modifyDate; } set { modifyDate = value; } } private Int32 startSetc = Sector.ENDOFCHAIN; public Int32 StartSetc { get { return startSetc; } set { startSetc = value; } } private long size; public long Size { get { return size; } set { size = value; } } public int CompareTo(object obj) { const int THIS_IS_GREATER = 1; const int OTHER_IS_GREATER = -1; IDirectoryEntry otherDir = obj as IDirectoryEntry; if (otherDir == null) throw new CFException("Invalid casting: compared object does not implement IDirectorEntry interface"); if (this.NameLength > otherDir.NameLength) { return THIS_IS_GREATER; } else if (this.NameLength < otherDir.NameLength) { return OTHER_IS_GREATER; } else { String thisName = Encoding.Unicode.GetString(this.EntryName, 0, this.NameLength).ToUpper(CultureInfo.InvariantCulture); String otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength).ToUpper(CultureInfo.InvariantCulture); for (int z = 0; z < thisName.Length; z++) { if (BitConverter.ToInt16(BitConverter.GetBytes(thisName[z]), 0) > BitConverter.ToInt16(BitConverter.GetBytes(otherName[z]), 0)) return THIS_IS_GREATER; else if (BitConverter.ToInt16(BitConverter.GetBytes(thisName[z]), 0) < BitConverter.ToInt16(BitConverter.GetBytes(otherName[z]), 0)) return OTHER_IS_GREATER; } return 0; } // return String.Compare(Encoding.Unicode.GetString(this.EntryName).ToUpper(), Encoding.Unicode.GetString(other.EntryName).ToUpper()); } public override bool Equals(object obj) { return this.CompareTo(obj) == 0; } /// /// FNV hash, short for Fowler/Noll/Vo /// /// /// (not warranted) unique hash for byte array private static ulong fnv_hash(byte[] buffer) { ulong h = 2166136261; int i; for (i = 0; i < buffer.Length; i++) h = (h * 16777619) ^ buffer[i]; return h; } public override int GetHashCode() { return (int)fnv_hash(this.entryName); } public void Write(Stream stream) { StreamRW rw = new StreamRW(stream); rw.Write(entryName); rw.Write(nameLength); rw.Write((byte)stgType); rw.Write((byte)stgColor); rw.Write(leftSibling); rw.Write(rightSibling); rw.Write(child); rw.Write(storageCLSID.ToByteArray()); rw.Write(stateBits); rw.Write(creationDate); rw.Write(modifyDate); rw.Write(startSetc); rw.Write(size); rw.Close(); } //public Byte[] ToByteArray() //{ // MemoryStream ms // = new MemoryStream(128); // BinaryWriter bw = new BinaryWriter(ms); // byte[] paddedName = new byte[64]; // Array.Copy(entryName, paddedName, entryName.Length); // bw.Write(paddedName); // bw.Write(nameLength); // bw.Write((byte)stgType); // bw.Write((byte)stgColor); // bw.Write(leftSibling); // bw.Write(rightSibling); // bw.Write(child); // bw.Write(storageCLSID.ToByteArray()); // bw.Write(stateBits); // bw.Write(creationDate); // bw.Write(modifyDate); // bw.Write(startSetc); // bw.Write(size); // return ms.ToArray(); //} public void Read(Stream stream) { StreamRW rw = new StreamRW(stream); entryName = rw.ReadBytes(64); nameLength = rw.ReadUInt16(); stgType = (StgType)rw.ReadByte(); rw.ReadByte();//Ignore color, only black tree //stgColor = (StgColor)br.ReadByte(); leftSibling = rw.ReadInt32(); rightSibling = rw.ReadInt32(); child = rw.ReadInt32(); // Thank you to bugaccount (BugTrack id 3519554) if (stgType == StgType.StgInvalid) { leftSibling = NOSTREAM; rightSibling = NOSTREAM; child = NOSTREAM; } storageCLSID = new Guid(rw.ReadBytes(16)); stateBits = rw.ReadInt32(); creationDate = rw.ReadBytes(8); modifyDate = rw.ReadBytes(8); startSetc = rw.ReadInt32(); size = rw.ReadInt64(); } public string Name { get { return GetEntryName(); } } } } openmcdf-1.5.4/src/OLECompoundFileStorage/Header.cs0000644000175000017500000002351112017100166021741 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.IO; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { internal class Header { //0 8 Compound document file identifier: D0H CFH 11H E0H A1H B1H 1AH E1H private byte[] headerSignature = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; public byte[] HeaderSignature { get { return headerSignature; } } //8 16 Unique identifier (UID) of this file (not of interest in the following, may be all 0) private byte[] clsid = new byte[16]; public byte[] CLSID { get { return clsid; } set { clsid = value; } } //24 2 Revision number of the file format (most used is 003EH) private ushort minorVersion = 0x003E; public ushort MinorVersion { get { return minorVersion; } } //26 2 Version number of the file format (most used is 0003H) private ushort majorVersion = 0x0003; public ushort MajorVersion { get { return majorVersion; } } //28 2 Byte order identifier (➜4.2): FEH FFH = Little-Endian FFH FEH = Big-Endian private ushort byteOrder = 0xFFFE; public ushort ByteOrder { get { return byteOrder; } } //30 2 Size of a sector in the compound document file (➜3.1) in power-of-two (ssz), real sector //size is sec_size = 2ssz bytes (minimum value is 7 which means 128 bytes, most used //value is 9 which means 512 bytes) private ushort sectorShift = 9; public ushort SectorShift { get { return sectorShift; } } //32 2 Size of a short-sector in the short-stream container stream (➜6.1) in power-of-two (sssz), //real short-sector size is short_sec_size = 2sssz bytes (maximum value is sector size //ssz, see above, most used value is 6 which means 64 bytes) private ushort miniSectorShift = 6; public ushort MiniSectorShift { get { return miniSectorShift; } } //34 10 Not used private byte[] unUsed = new byte[6]; public byte[] UnUsed { get { return unUsed; } } //44 4 Total number of sectors used Directory (➜5.2) private int directorySectorsNumber; public int DirectorySectorsNumber { get { return directorySectorsNumber; } set { directorySectorsNumber = value; } } //44 4 Total number of sectors used for the sector allocation table (➜5.2) private int fatSectorsNumber; public int FATSectorsNumber { get { return fatSectorsNumber; } set { fatSectorsNumber = value; } } //48 4 SecID of first sector of the directory stream (➜7) private int firstDirectorySectorID = Sector.ENDOFCHAIN; public int FirstDirectorySectorID { get { return firstDirectorySectorID; } set { firstDirectorySectorID = value; } } //52 4 Not used private uint unUsed2; public uint UnUsed2 { get { return unUsed2; } } //56 4 Minimum size of a standard stream (in bytes, minimum allowed and most used size is 4096 //bytes), streams with an actual size smaller than (and not equal to) this value are stored as //short-streams (➜6) private uint minSizeStandardStream = 4096; public uint MinSizeStandardStream { get { return minSizeStandardStream; } set { minSizeStandardStream = value; } } //60 4 SecID of first sector of the short-sector allocation table (➜6.2), or –2 (End Of Chain //SecID, ➜3.1) if not extant private int firstMiniFATSectorID = unchecked((int)0xFFFFFFFE); /// /// This integer field contains the starting sector number for the mini FAT /// public int FirstMiniFATSectorID { get { return firstMiniFATSectorID; } set { firstMiniFATSectorID = value; } } //64 4 Total number of sectors used for the short-sector allocation table (➜6.2) private uint miniFATSectorsNumber; public uint MiniFATSectorsNumber { get { return miniFATSectorsNumber; } set { miniFATSectorsNumber = value; } } //68 4 SecID of first sector of the master sector allocation table (➜5.1), or –2 (End Of Chain //SecID, ➜3.1) if no additional sectors used private int firstDIFATSectorID = Sector.ENDOFCHAIN; public int FirstDIFATSectorID { get { return firstDIFATSectorID; } set { firstDIFATSectorID = value; } } //72 4 Total number of sectors used for the master sector allocation table (➜5.1) private uint difatSectorsNumber; public uint DIFATSectorsNumber { get { return difatSectorsNumber; } set { difatSectorsNumber = value; } } //76 436 First part of the master sector allocation table (➜5.1) containing 109 SecIDs private int[] difat = new int[109]; public int[] DIFAT { get { return difat; } } public Header() : this(3) { } public Header(ushort version) { switch (version) { case 3: this.majorVersion = 3; this.sectorShift = 0x0009; break; case 4: this.majorVersion = 4; this.sectorShift = 0x000C; break; default: throw new CFException("Invalid Compound File Format version"); } for (int i = 0; i < 109; i++) { difat[i] = Sector.FREESECT; } } public void Write(Stream stream) { StreamRW rw = new StreamRW(stream); rw.Write(headerSignature); rw.Write(clsid); rw.Write(minorVersion); rw.Write(majorVersion); rw.Write(byteOrder); rw.Write(sectorShift); rw.Write(miniSectorShift); rw.Write(unUsed); rw.Write(directorySectorsNumber); rw.Write(fatSectorsNumber); rw.Write(firstDirectorySectorID); rw.Write(unUsed2); rw.Write(minSizeStandardStream); rw.Write(firstMiniFATSectorID); rw.Write(miniFATSectorsNumber); rw.Write(firstDIFATSectorID); rw.Write(difatSectorsNumber); foreach (int i in difat) { rw.Write(i); } if (majorVersion == 4) { byte[] zeroHead = new byte[3584]; rw.Write(zeroHead); } rw.Close(); } public void Read(Stream stream) { StreamRW rw = new StreamRW(stream); headerSignature = rw.ReadBytes(8); CheckSignature(); clsid = rw.ReadBytes(16); minorVersion = rw.ReadUInt16(); majorVersion = rw.ReadUInt16(); CheckVersion(); byteOrder = rw.ReadUInt16(); sectorShift = rw.ReadUInt16(); miniSectorShift = rw.ReadUInt16(); unUsed = rw.ReadBytes(6); directorySectorsNumber = rw.ReadInt32(); fatSectorsNumber = rw.ReadInt32(); firstDirectorySectorID = rw.ReadInt32(); unUsed2 = rw.ReadUInt32(); minSizeStandardStream = rw.ReadUInt32(); firstMiniFATSectorID = rw.ReadInt32(); miniFATSectorsNumber = rw.ReadUInt32(); firstDIFATSectorID = rw.ReadInt32(); difatSectorsNumber = rw.ReadUInt32(); for (int i = 0; i < 109; i++) { this.DIFAT[i] = rw.ReadInt32(); } rw.Close(); } private void CheckVersion() { if (this.majorVersion != 3 && this.majorVersion != 4) throw new CFFileFormatException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 "); } /// /// Structured Storage signature /// private byte[] OLE_CFS_SIGNATURE = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; private void CheckSignature() { for (int i = 0; i < headerSignature.Length; i++) { if (headerSignature[i] != OLE_CFS_SIGNATURE[i]) throw new CFFileFormatException("Invalid OLE structured storage file"); } } } } openmcdf-1.5.4/src/OLECompoundFileStorage/IDirectoryEntry.cs0000644000175000017500000000277011546167756023704 0ustar mathieumathieuusing System; using BinaryTrees; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { internal interface IDirectoryEntry : IComparable { int Child { get; set; } byte[] CreationDate { get; set; } byte[] EntryName { get; } string GetEntryName(); int LeftSibling { get; set; } byte[] ModifyDate { get; set; } string Name { get; } ushort NameLength { get; set; } void Read(System.IO.Stream stream); int RightSibling { get; set; } void SetEntryName(string entryName); int SID { get; set; } long Size { get; set; } int StartSetc { get; set; } int StateBits { get; set; } StgColor StgColor { get; set; } StgType StgType { get; set; } Guid StorageCLSID { get; set; } void Write(System.IO.Stream stream); } } openmcdf-1.5.4/src/OLECompoundFileStorage/OpenMcdf.csproj0000644000175000017500000000657411546416162023165 0ustar mathieumathieu Debug AnyCPU 9.0.30729 2.0 {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1} Library Properties OpenMcdf OpenMcdf v2.0 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 bin\Debug\OpenMcdf.xml 1591,1592,1573,1571,1570,1572 pdbonly true bin\Release\ TRACE prompt 4 bin\Release\OpenMcdf.xml 1591,1592,1573,1571,1570,1572 openmcdf-1.5.4/src/OLECompoundFileStorage/OpenMcdfClassDiagram.cd0000644000175000017500000001274211553623772024525 0ustar mathieumathieu gAAEAAAQIBQIAgEUgEIBYgDg0GEAwUAAABAAAAGQgCA= Header.cs AAAAAAAAAAAAAEYAAAQEIBAAAAEABAAAAAACAAAAAAA= CFStorage.cs AAAAAAACAAAAQAAgGkoAIABgAAAAAAQACAAAAAGIACA= StreamView.cs QIQAwEAAkAAAgMQAgIDAgAQkEADAAKwgAAAsIAHEAAA= DirectoryEntry.cs ATUGgNQFUmMEQBB0AARISIAAEAEEAmRARs0BQeeQBCg= CompoundFile.cs QAAEAAAABAAAgAUggAAAgAQAAAAAAIAAAQACCAAAAiQ= CFItem.cs AAAAAAABEAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAA= CFStream.cs AGIAAEAAAAAAABAQDAAABAQAAAQAAAQAGEAAggBSIAA= SectorCollection.cs QAAAQAAAEAAAgMAAAAAAgAQkAAAAACwAAAAAIAHEAAA= IDirectoryEntry.cs openmcdf-1.5.4/src/OLECompoundFileStorage/Properties/0000755000175000017500000000000012061101736022357 5ustar mathieumathieuopenmcdf-1.5.4/src/OLECompoundFileStorage/Properties/AssemblyInfo.cs0000644000175000017500000000301612042056100025272 0ustar mathieumathieuusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("OpenMcdf")] [assembly: AssemblyDescription("MS Compound File Storage .NET Implementation")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Federico Blaseotto")] [assembly: AssemblyProduct("OpenMcdf")] [assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("ffc13791-ddf0-4d14-bd64-575aba119190")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.5.4.*")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("OpenMcdfTest")]openmcdf-1.5.4/src/OLECompoundFileStorage/RBTree/0000755000175000017500000000000012061101736021346 5ustar mathieumathieuopenmcdf-1.5.4/src/OLECompoundFileStorage/RBTree/RedBlack.cs0000644000175000017500000004451011546167720023364 0ustar mathieumathieu using System.Collections; using System.Text; using System; using System.Reflection; namespace RBTree { /// ///A red-black tree must satisfy these properties: /// ///1. The root is black. ///2. All leaves are black. ///3. Red nodes can only have black children. ///4. All paths from a node to its leaves contain the same number of black nodes. /// public class RedBlack : object { // the number of nodes contained in the tree private int intCount; // a simple randomized hash code. The hash code could be used as a key // if it is "unique" enough. Note: The IComparable interface would need to // be replaced with int. private int intHashCode; // identifies the owner of the tree private string strIdentifier; // the tree private RedBlackNode rbTree; // sentinelNode is convenient way of indicating a leaf node. public static RedBlackNode sentinelNode; // the node that was last found; used to optimize searches private RedBlackNode lastNodeFound; private Random rand = new Random(); public RedBlack() { strIdentifier = base.ToString() + rand.Next(); intHashCode = rand.Next(); // set up the sentinel node. the sentinel node is the key to a successfull // implementation and for understanding the red-black tree properties. sentinelNode = new RedBlackNode(); sentinelNode.Left = null; sentinelNode.Right = null; sentinelNode.Parent = null; sentinelNode.Color = RedBlackNode.BLACK; rbTree = sentinelNode; lastNodeFound = sentinelNode; } public RedBlack(string strIdentifier) { intHashCode = rand.Next(); this.strIdentifier = strIdentifier; } /// /// Add /// args: ByVal key As IComparable, ByVal data As Object /// key is object that implements IComparable interface /// performance tip: change to use use int type (such as the hashcode) /// public void Add(IComparable key, object data) { if(key == null || data == null) throw(new RedBlackException("RedBlackNode key and data must not be null")); // traverse tree - find where node belongs int result = 0; // create new node RedBlackNode node = new RedBlackNode(); RedBlackNode temp = rbTree; // grab the rbTree node of the tree while(temp != sentinelNode) { // find Parent node.Parent = temp; result = key.CompareTo(temp.Key); if(result == 0) throw(new RedBlackException("A Node with the same key already exists")); if(result > 0) temp = temp.Right; else temp = temp.Left; } // setup node node.Key = key; node.Data = data; node.Left = sentinelNode; node.Right = sentinelNode; // insert node into tree starting at parent's location if(node.Parent != null) { result = node.Key.CompareTo(node.Parent.Key); if(result > 0) node.Parent.Right = node; else node.Parent.Left = node; } else rbTree = node; // first node added RestoreAfterInsert(node); // restore red-black properities lastNodeFound = node; intCount = intCount + 1; } /// /// RestoreAfterInsert /// Additions to red-black trees usually destroy the red-black /// properties. Examine the tree and restore. Rotations are normally /// required to restore it /// private void RestoreAfterInsert(RedBlackNode x) { // x and y are used as variable names for brevity, in a more formal // implementation, you should probably change the names RedBlackNode y; // maintain red-black tree properties after adding x while(x != rbTree && x.Parent.Color == RedBlackNode.RED) { // Parent node is .Colored red; if(x.Parent == x.Parent.Parent.Left) // determine traversal path { // is it on the Left or Right subtree? y = x.Parent.Parent.Right; // get uncle if(y!= null && y.Color == RedBlackNode.RED) { // uncle is red; change x's Parent and uncle to black x.Parent.Color = RedBlackNode.BLACK; y.Color = RedBlackNode.BLACK; // grandparent must be red. Why? Every red node that is not // a leaf has only black children x.Parent.Parent.Color = RedBlackNode.RED; x = x.Parent.Parent; // continue loop with grandparent } else { // uncle is black; determine if x is greater than Parent if(x == x.Parent.Right) { // yes, x is greater than Parent; rotate Left // make x a Left child x = x.Parent; RotateLeft(x); } // no, x is less than Parent x.Parent.Color = RedBlackNode.BLACK; // make Parent black x.Parent.Parent.Color = RedBlackNode.RED; // make grandparent black RotateRight(x.Parent.Parent); // rotate right } } else { // x's Parent is on the Right subtree // this code is the same as above with "Left" and "Right" swapped y = x.Parent.Parent.Left; if(y!= null && y.Color == RedBlackNode.RED) { x.Parent.Color = RedBlackNode.BLACK; y.Color = RedBlackNode.BLACK; x.Parent.Parent.Color = RedBlackNode.RED; x = x.Parent.Parent; } else { if(x == x.Parent.Left) { x = x.Parent; RotateRight(x); } x.Parent.Color = RedBlackNode.BLACK; x.Parent.Parent.Color = RedBlackNode.RED; RotateLeft(x.Parent.Parent); } } } rbTree.Color = RedBlackNode.BLACK; // rbTree should always be black } /// /// RotateLeft /// Rebalance the tree by rotating the nodes to the left /// public void RotateLeft(RedBlackNode x) { // pushing node x down and to the Left to balance the tree. x's Right child (y) // replaces x (since y > x), and y's Left child becomes x's Right child // (since it's < y but > x). RedBlackNode y = x.Right; // get x's Right node, this becomes y // set x's Right link x.Right = y.Left; // y's Left child's becomes x's Right child // modify parents if(y.Left != sentinelNode) y.Left.Parent = x; // sets y's Left Parent to x if(y != sentinelNode) y.Parent = x.Parent; // set y's Parent to x's Parent if(x.Parent != null) { // determine which side of it's Parent x was on if(x == x.Parent.Left) x.Parent.Left = y; // set Left Parent to y else x.Parent.Right = y; // set Right Parent to y } else rbTree = y; // at rbTree, set it to y // link x and y y.Left = x; // put x on y's Left if(x != sentinelNode) // set y as x's Parent x.Parent = y; } /// /// RotateRight /// Rebalance the tree by rotating the nodes to the right /// public void RotateRight(RedBlackNode x) { // pushing node x down and to the Right to balance the tree. x's Left child (y) // replaces x (since x < y), and y's Right child becomes x's Left child // (since it's < x but > y). RedBlackNode y = x.Left; // get x's Left node, this becomes y // set x's Right link x.Left = y.Right; // y's Right child becomes x's Left child // modify parents if(y.Right != sentinelNode) y.Right.Parent = x; // sets y's Right Parent to x if(y != sentinelNode) y.Parent = x.Parent; // set y's Parent to x's Parent if(x.Parent != null) // null=rbTree, could also have used rbTree { // determine which side of it's Parent x was on if(x == x.Parent.Right) x.Parent.Right = y; // set Right Parent to y else x.Parent.Left = y; // set Left Parent to y } else rbTree = y; // at rbTree, set it to y // link x and y y.Right = x; // put x on y's Right if(x != sentinelNode) // set y as x's Parent x.Parent = y; } /// /// GetData /// Gets the data object associated with the specified key /// public object GetData(IComparable key) { int result; RedBlackNode treeNode = rbTree; // begin at root // traverse tree until node is found while(treeNode != sentinelNode) { result = key.CompareTo(treeNode.Key); if(result == 0) { lastNodeFound = treeNode; return treeNode.Data; } if(result < 0) treeNode = treeNode.Left; else treeNode = treeNode.Right; } throw(new RedBlackException("RedBlackNode key was not found")); } /// /// GetMinKey /// Returns the minimum key value /// public IComparable GetMinKey() { RedBlackNode treeNode = rbTree; if(treeNode == null || treeNode == sentinelNode) throw(new RedBlackException("RedBlack tree is empty")); // traverse to the extreme left to find the smallest key while(treeNode.Left != sentinelNode) treeNode = treeNode.Left; lastNodeFound = treeNode; return treeNode.Key; } /// /// GetMaxKey /// Returns the maximum key value /// public IComparable GetMaxKey() { RedBlackNode treeNode = rbTree; if(treeNode == null || treeNode == sentinelNode) throw(new RedBlackException("RedBlack tree is empty")); // traverse to the extreme right to find the largest key while(treeNode.Right != sentinelNode) treeNode = treeNode.Right; lastNodeFound = treeNode; return treeNode.Key; } /// /// GetMinValue /// Returns the object having the minimum key value /// public object GetMinValue() { return GetData(GetMinKey()); } /// /// GetMaxValue /// Returns the object having the maximum key /// public object GetMaxValue() { return GetData(GetMaxKey()); } /// /// GetEnumerator /// return an enumerator that returns the tree nodes in order /// public RedBlackEnumerator GetEnumerator() { // elements is simply a generic name to refer to the // data objects the nodes contain return Elements(true); } /// /// Keys /// if(ascending is true, the keys will be returned in ascending order, else /// the keys will be returned in descending order. /// public RedBlackEnumerator Keys() { return Keys(true); } public RedBlackEnumerator Keys(bool ascending) { return new RedBlackEnumerator(rbTree, true, ascending); } /// /// Values /// Provided for .NET compatibility. /// public RedBlackEnumerator Values() { return Elements(true); } /// /// Elements /// Returns an enumeration of the data objects. /// if(ascending is true, the objects will be returned in ascending order, /// else the objects will be returned in descending order. /// public RedBlackEnumerator Elements() { return Elements(true); } public RedBlackEnumerator Elements(bool ascending) { return new RedBlackEnumerator(rbTree, false, ascending); } /// /// IsEmpty /// Is the tree empty? /// public bool IsEmpty() { return (rbTree == null); } /// /// Remove /// removes the key and data object (delete) /// public void Remove(IComparable key) { if(key == null) throw(new RedBlackException("RedBlackNode key is null")); // find node int result; RedBlackNode node; // see if node to be deleted was the last one found result = key.CompareTo(lastNodeFound.Key); if(result == 0) node = lastNodeFound; else { // not found, must search node = rbTree; while(node != sentinelNode) { result = key.CompareTo(node.Key); if(result == 0) break; if(result < 0) node = node.Left; else node = node.Right; } if(node == sentinelNode) return; // key not found } Delete(node); intCount = intCount - 1; } /// /// Delete /// Delete a node from the tree and restore red black properties /// private void Delete(RedBlackNode z) { // A node to be deleted will be: // 1. a leaf with no children // 2. have one child // 3. have two children // If the deleted node is red, the red black properties still hold. // If the deleted node is black, the tree needs rebalancing RedBlackNode x = new RedBlackNode(); // work node to contain the replacement node RedBlackNode y; // work node // find the replacement node (the successor to x) - the node one with // at *most* one child. if(z.Left == sentinelNode || z.Right == sentinelNode) y = z; // node has sentinel as a child else { // z has two children, find replacement node which will // be the leftmost node greater than z y = z.Right; // traverse right subtree while(y.Left != sentinelNode) // to find next node in sequence y = y.Left; } // at this point, y contains the replacement node. it's content will be copied // to the valules in the node to be deleted // x (y's only child) is the node that will be linked to y's old parent. if(y.Left != sentinelNode) x = y.Left; else x = y.Right; // replace x's parent with y's parent and // link x to proper subtree in parent // this removes y from the chain x.Parent = y.Parent; if(y.Parent != null) if(y == y.Parent.Left) y.Parent.Left = x; else y.Parent.Right = x; else rbTree = x; // make x the root node // copy the values from y (the replacement node) to the node being deleted. // note: this effectively deletes the node. if(y != z) { z.Key = y.Key; z.Data = y.Data; } if(y.Color == RedBlackNode.BLACK) RestoreAfterDelete(x); lastNodeFound = sentinelNode; } /// /// RestoreAfterDelete /// Deletions from red-black trees may destroy the red-black /// properties. Examine the tree and restore. Rotations are normally /// required to restore it /// private void RestoreAfterDelete(RedBlackNode x) { // maintain Red-Black tree balance after deleting node RedBlackNode y; while(x != rbTree && x.Color == RedBlackNode.BLACK) { if(x == x.Parent.Left) // determine sub tree from parent { y = x.Parent.Right; // y is x's sibling if(y.Color == RedBlackNode.RED) { // x is black, y is red - make both black and rotate y.Color = RedBlackNode.BLACK; x.Parent.Color = RedBlackNode.RED; RotateLeft(x.Parent); y = x.Parent.Right; } if(y.Left.Color == RedBlackNode.BLACK && y.Right.Color == RedBlackNode.BLACK) { // children are both black y.Color = RedBlackNode.RED; // change parent to red x = x.Parent; // move up the tree } else { if(y.Right.Color == RedBlackNode.BLACK) { y.Left.Color = RedBlackNode.BLACK; y.Color = RedBlackNode.RED; RotateRight(y); y = x.Parent.Right; } y.Color = x.Parent.Color; x.Parent.Color = RedBlackNode.BLACK; y.Right.Color = RedBlackNode.BLACK; RotateLeft(x.Parent); x = rbTree; } } else { // right subtree - same as code above with right and left swapped y = x.Parent.Left; if(y.Color == RedBlackNode.RED) { y.Color = RedBlackNode.BLACK; x.Parent.Color = RedBlackNode.RED; RotateRight (x.Parent); y = x.Parent.Left; } if(y.Right.Color == RedBlackNode.BLACK && y.Left.Color == RedBlackNode.BLACK) { y.Color = RedBlackNode.RED; x = x.Parent; } else { if(y.Left.Color == RedBlackNode.BLACK) { y.Right.Color = RedBlackNode.BLACK; y.Color = RedBlackNode.RED; RotateLeft(y); y = x.Parent.Left; } y.Color = x.Parent.Color; x.Parent.Color = RedBlackNode.BLACK; y.Left.Color = RedBlackNode.BLACK; RotateRight(x.Parent); x = rbTree; } } } x.Color = RedBlackNode.BLACK; } /// /// RemoveMin /// removes the node with the minimum key /// public void RemoveMin() { if(rbTree == null) throw(new RedBlackException("RedBlackNode is null")); Remove(GetMinKey()); } /// /// RemoveMax /// removes the node with the maximum key /// public void RemoveMax() { if(rbTree == null) throw(new RedBlackException("RedBlackNode is null")); Remove(GetMaxKey()); } /// /// Clear /// Empties or clears the tree /// public void Clear () { rbTree = sentinelNode; intCount = 0; } /// /// Size /// returns the size (number of nodes) in the tree /// public int Size() { // number of keys return intCount; } /// /// Equals /// public override bool Equals(object obj) { if(obj == null) return false; if(!(obj is RedBlackNode )) return false; if(this == obj) return true; return (ToString().Equals(((RedBlackNode)(obj)).ToString())); } /// /// HashCode /// public override int GetHashCode() { return intHashCode; } /// /// ToString /// public override string ToString() { return strIdentifier.ToString(); } } } openmcdf-1.5.4/src/OLECompoundFileStorage/RBTree/RedBlackEnumerator.cs0000644000175000017500000001245611546163204025423 0ustar mathieumathieu using System; using System.Collections; namespace RBTree { /// /// The RedBlackEnumerator class returns the keys or data objects of the treap in /// sorted order. /// public class RedBlackEnumerator { // the treap uses the stack to order the nodes private Stack stack; // return the keys private bool keys; // return in ascending order (true) or descending (false) private bool ascending; // key private IComparable ordKey; // the data or value associated with the key private object objValue; public string Color; // testing only, don't use in live system public IComparable parentKey; // testing only, don't use in live system /// ///Key /// public IComparable Key { get { return ordKey; } set { ordKey = value; } } /// ///Data /// public object Value { get { return objValue; } set { objValue = value; } } public RedBlackEnumerator() { } /// /// Determine order, walk the tree and push the nodes onto the stack /// public RedBlackEnumerator(RedBlackNode tnode, bool keys, bool ascending) { stack = new Stack(); this.keys = keys; this.ascending = ascending; // use depth-first traversal to push nodes into stack // the lowest node will be at the top of the stack if(ascending) { // find the lowest node while(tnode != RedBlack.sentinelNode) { stack.Push(tnode); tnode = tnode.Left; } } else { // the highest node will be at top of stack while(tnode != RedBlack.sentinelNode) { stack.Push(tnode); tnode = tnode.Right; } } } /// /// HasMoreElements /// public bool HasMoreElements() { return (stack.Count > 0); } /// /// NextElement /// public object NextElement() { if(stack.Count == 0) throw(new RedBlackException("Element not found")); // the top of stack will always have the next item // get top of stack but don't remove it as the next nodes in sequence // may be pushed onto the top // the stack will be popped after all the nodes have been returned RedBlackNode node = (RedBlackNode) stack.Peek(); //next node in sequence if(ascending) { if(node.Right == RedBlack.sentinelNode) { // yes, top node is lowest node in subtree - pop node off stack RedBlackNode tn = (RedBlackNode) stack.Pop(); // peek at right node's parent // get rid of it if it has already been used while(HasMoreElements()&& ((RedBlackNode) stack.Peek()).Right == tn) tn = (RedBlackNode) stack.Pop(); } else { // find the next items in the sequence // traverse to left; find lowest and push onto stack RedBlackNode tn = node.Right; while(tn != RedBlack.sentinelNode) { stack.Push(tn); tn = tn.Left; } } } else // descending, same comments as above apply { if(node.Left == RedBlack.sentinelNode) { // walk the tree RedBlackNode tn = (RedBlackNode) stack.Pop(); while(HasMoreElements() && ((RedBlackNode)stack.Peek()).Left == tn) tn = (RedBlackNode) stack.Pop(); } else { // determine next node in sequence // traverse to left subtree and find greatest node - push onto stack RedBlackNode tn = node.Left; while(tn != RedBlack.sentinelNode) { stack.Push(tn); tn = tn.Right; } } } // the following is for .NET compatibility (see MoveNext()) Key = node.Key; Value = node.Data; // ******** testing only ******** try { parentKey = node.Parent.Key; // testing only } catch(Exception e) { object o = e; // stop compiler from complaining parentKey = 0; } if(node.Color == 0) // testing only Color = "Red"; else Color = "Black"; // ******** testing only ******** return keys == true ? node.Key : node.Data; } /// /// MoveNext /// For .NET compatibility /// public bool MoveNext() { if(HasMoreElements()) { NextElement(); return true; } return false; } } } openmcdf-1.5.4/src/OLECompoundFileStorage/RBTree/RedBlackException.cs0000644000175000017500000000062311546167714025243 0ustar mathieumathieuusing System; namespace RBTree { /// /// The RedBlackException class distinguishes read black tree exceptions from .NET /// exceptions. /// public class RedBlackException : Exception { public RedBlackException() { } public RedBlackException(string msg) : base(msg) { } } } openmcdf-1.5.4/src/OLECompoundFileStorage/RBTree/RedBlackNode.cs0000644000175000017500000000346011546167710024170 0ustar mathieumathieuusing System; using System.Text; namespace RBTree { /// /// The RedBlackNode class encapsulates a node in the tree /// public class RedBlackNode { // tree node colors public static int RED = 0; public static int BLACK = 1; // key provided by the calling class private IComparable ordKey; // the data or value associated with the key private object objData; // color - used to balance the tree private int intColor; // left node private RedBlackNode rbnLeft; // right node private RedBlackNode rbnRight; // parent node private RedBlackNode rbnParent; /// ///Key /// public IComparable Key { get { return ordKey; } set { ordKey = value; } } /// ///Data /// public object Data { get { return objData; } set { objData = value; } } /// ///Color /// public int Color { get { return intColor; } set { intColor = value; } } /// ///Left /// public RedBlackNode Left { get { return rbnLeft; } set { rbnLeft = value; } } /// /// Right /// public RedBlackNode Right { get { return rbnRight; } set { rbnRight = value; } } public RedBlackNode Parent { get { return rbnParent; } set { rbnParent = value; } } public RedBlackNode() { Color = RED; } } } openmcdf-1.5.4/src/OLECompoundFileStorage/Sector.cs0000644000175000017500000001167411546167762022044 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.Collections; using System.IO; using System.Collections.Specialized; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { internal enum SectorType { Normal, Mini, FAT, DIFAT, RangeLockSector, Directory } internal class Sector : IDisposable { public static int MINISECTOR_SIZE = 64; public const int FREESECT = unchecked((int)0xFFFFFFFF); public const int ENDOFCHAIN = unchecked((int)0xFFFFFFFE); public const int FATSECT = unchecked((int)0xFFFFFFFD); public const int DIFSECT = unchecked((int)0xFFFFFFFC); private bool dirtyFlag = false; public bool DirtyFlag { get { return dirtyFlag; } set { dirtyFlag = value; } } public bool IsStreamed { get { return (stream != null && size != MINISECTOR_SIZE) ? (this.id * size) + size < stream.Length : false; } } private int size = 0; private Stream stream; public Sector(int size, Stream stream) { this.size = size; this.stream = stream; } public Sector(int size, byte[] data) { this.size = size; this.data = data; this.stream = null; } public Sector(int size) { this.size = size; this.data = null; this.stream = null; } private SectorType type; internal SectorType Type { get { return type; } set { type = value; } } private int id = -1; public int Id { get { return id; } set { id = value; } } public int Size { get { return size; } } private byte[] data; public byte[] GetData() { if (this.data == null) { data = new byte[size]; if (IsStreamed) { stream.Seek((long)size + (long)this.id * (long)size, SeekOrigin.Begin); stream.Read(data, 0, size); } } return data; } //public void SetSectorData(byte[] b) //{ // this.data = b; //} //public void FillData(byte b) //{ // if (data != null) // { // for (int i = 0; i < data.Length; i++) // { // data[i] = b; // } // } //} public void ZeroData() { data = new byte[size]; dirtyFlag = true; } internal void ReleaseData() { this.data = null; } private object lockObject = new Object(); /// /// When called from user code, release all resources, otherwise, in the case runtime called it, /// only unmanagd resources are released. /// /// If true, method has been called from User code, if false it's been called from .net runtime protected virtual void Dispose(bool disposing) { try { if (!_disposed) { lock (lockObject) { if (disposing) { // Call from user code... } this.data = null; this.dirtyFlag = false; this.id = Sector.ENDOFCHAIN; this.size = 0; } } } finally { _disposed = true; } } #region IDisposable Members private bool _disposed;//false void IDisposable.Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion } } openmcdf-1.5.4/src/OLECompoundFileStorage/SectorCollection.cs0000644000175000017500000001210611546167676024053 0ustar mathieumathieuusing System; using System.Text; using System.Collections; using System.Collections.Generic; namespace OpenMcdf { /// /// Action to implement when transaction support - sector /// has to be written to the underlying stream (see specs). /// public delegate void Ver3SizeLimitReached(); /// /// Ad-hoc Heap Friendly sector collection to avoid using /// large array that may create some problem to GC collection /// (see http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/ ) /// internal class SectorCollection : IList { private const int MAX_SECTOR_V4_COUNT_LOCK_RANGE = 524287; //0x7FFFFF00 for Version 4 private const int SLICE_SIZE = 4096; private int count = 0; public event Ver3SizeLimitReached OnVer3SizeLimitReached; private List largeArraySlices = new List(); public SectorCollection() { } private bool sizeLimitReached = false; private void DoCheckSizeLimitReached() { if (!sizeLimitReached && (count - 1 > MAX_SECTOR_V4_COUNT_LOCK_RANGE)) { if (OnVer3SizeLimitReached != null) OnVer3SizeLimitReached(); sizeLimitReached = true; } } #region IList Members public int IndexOf(Sector item) { throw new NotImplementedException(); } public void Insert(int index, Sector item) { throw new NotImplementedException(); } public void RemoveAt(int index) { throw new NotImplementedException(); } public Sector this[int index] { get { int itemIndex = index / SLICE_SIZE; int itemOffset = index % SLICE_SIZE; if ((index > -1) && (index < count)) { return (Sector)largeArraySlices[itemIndex][itemOffset]; } else throw new ArgumentOutOfRangeException("index", index, "Argument out of range"); } set { int itemIndex = index / SLICE_SIZE; int itemOffset = index % SLICE_SIZE; if (index > -1 && index < count) { largeArraySlices[itemIndex][itemOffset] = value; } else throw new ArgumentOutOfRangeException("index", index, "Argument out of range"); } } #endregion #region ICollection Members private int add(Sector item) { int itemIndex = count / SLICE_SIZE; if (itemIndex < largeArraySlices.Count) { largeArraySlices[itemIndex].Add(item); count++; } else { ArrayList ar = new ArrayList(SLICE_SIZE); ar.Add(item); largeArraySlices.Add(ar); count++; } return count - 1; } public void Add(Sector item) { DoCheckSizeLimitReached(); add(item); } public void Clear() { foreach (ArrayList slice in largeArraySlices) { slice.Clear(); } largeArraySlices.Clear(); count = 0; } public bool Contains(Sector item) { throw new NotImplementedException(); } public void CopyTo(Sector[] array, int arrayIndex) { throw new NotImplementedException(); } public int Count { get { return count; } } public bool IsReadOnly { get { return false; } } public bool Remove(Sector item) { throw new NotImplementedException(); } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { for (int i = 0; i < largeArraySlices.Count; i++) { for (int j = 0; j < largeArraySlices[i].Count; j++) { yield return (Sector)largeArraySlices[i][j]; } } } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { for (int i = 0; i < largeArraySlices.Count; i++) { for (int j = 0; j < largeArraySlices[i].Count; j++) { yield return largeArraySlices[i][j]; } } } #endregion } } openmcdf-1.5.4/src/OLECompoundFileStorage/StreamRW.cs0000644000175000017500000000720611546167762022305 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.IO; namespace OpenMcdf { internal class StreamRW { private byte[] buffer = new byte[8]; private Stream stream; public StreamRW(Stream stream) { this.stream = stream; } public long Seek(long offset) { return stream.Seek(offset, SeekOrigin.Begin); } public byte ReadByte() { return (byte)this.stream.ReadByte(); } public ushort ReadUInt16() { this.stream.Read(buffer, 0, 2); return (ushort)(buffer[0] | (buffer[1] << 8)); } public int ReadInt32() { this.stream.Read(buffer, 0, 4); return (int)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); } public uint ReadUInt32() { this.stream.Read(buffer, 0, 4); return (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); } public long ReadInt64() { this.stream.Read(buffer, 0, 8); uint ls = (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); uint ms = (uint)((buffer[4]) | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24)); return (long)((ms << 32) | ls); } public ulong ReadUInt64() { this.stream.Read(buffer, 0, 8); return (ulong)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24) | (buffer[4] << 32) | (buffer[5] << 40) | (buffer[6] << 48) | (buffer[7] << 56)); } public byte[] ReadBytes(int count) { byte[] result = new byte[count]; this.stream.Read(result, 0, count); return result; } public byte[] ReadBytes(int count, out int r_count) { byte[] result = new byte[count]; r_count = this.stream.Read(result, 0, count); return result; } public void Write(byte b) { this.stream.WriteByte(b); } public void Write(ushort value) { buffer[0] = (byte)value; buffer[1] = (byte)(value >> 8); this.stream.Write(buffer, 0, 2); } public void Write(int value) { buffer[0] = (byte)value; buffer[1] = (byte)(value >> 8); buffer[2] = (byte)(value >> 16); buffer[3] = (byte)(value >> 24); this.stream.Write(buffer, 0, 4); } public void Write(long value) { buffer[0] = (byte)value; buffer[1] = (byte)(value >> 8); buffer[2] = (byte)(value >> 16); buffer[3] = (byte)(value >> 24); buffer[4] = (byte)(value >> 32); buffer[5] = (byte)(value >> 40); buffer[6] = (byte)(value >> 48); buffer[7] = (byte)(value >> 56); this.stream.Write(buffer, 0, 8); } public void Write(uint value) { buffer[0] = (byte)value; buffer[1] = (byte)(value >> 8); buffer[2] = (byte)(value >> 16); buffer[3] = (byte)(value >> 24); this.stream.Write(buffer, 0, 4); } public void Write(byte[] value) { this.stream.Write(value, 0, value.Length); } public void Close() { //Nothing to do ;-) } } } openmcdf-1.5.4/src/OLECompoundFileStorage/StreamView.cs0000644000175000017500000002435211546167732022665 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.Collections; using System.IO; using System.Collections.Specialized; using System.Diagnostics; /* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is OpenMCDF - Compound Document Format library. The Initial Developer of the Original Code is Federico Blaseotto. */ namespace OpenMcdf { /// /// Stream decorator for a Sector or miniSector chain /// internal class StreamView : Stream { private int sectorSize; private long position; private List sectorChain; private Stream stream; public StreamView(List sectorChain, int sectorSize, Stream stream) { if (sectorChain == null) throw new CFException("Sector Chain cannot be null"); if (sectorSize <= 0) throw new CFException("Sector size must be greater than zero"); this.sectorChain = sectorChain; this.sectorSize = sectorSize; this.stream = stream; } public StreamView(List sectorChain, int sectorSize, long length, Queue availableSectors, Stream stream) : this(sectorChain, sectorSize, stream) { adjustLength(length, availableSectors); } public StreamView(List sectorChain, int sectorSize, long length, Stream stream) : this(sectorChain, sectorSize, stream) { adjustLength(length); } public List BaseSectorChain { get { return sectorChain; } } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override bool CanWrite { get { return true; } } public override void Flush() { } private long length; public override long Length { get { return length; } } public override long Position { get { return position; } set { if (position > length - 1) throw new ArgumentOutOfRangeException("value"); position = value; } } public override void Close() { base.Close(); } private byte[] buf = new byte[4]; public int ReadInt32() { this.Read(buf, 0, 4); return (((this.buf[0] | (this.buf[1] << 8)) | (this.buf[2] << 16)) | (this.buf[3] << 24)); } public override int Read(byte[] buffer, int offset, int count) { int nRead = 0; int nToRead = 0; if (sectorChain != null && sectorChain.Count > 0) { // First sector int secIndex = (int)(position / (long)sectorSize); // Bytes to read count is the min between request count // and sector border nToRead = Math.Min( sectorChain[0].Size - ((int)position % sectorSize), count); if (secIndex < sectorChain.Count) { Buffer.BlockCopy( sectorChain[secIndex].GetData(), (int)(position % sectorSize), buffer, offset, nToRead ); } nRead += nToRead; secIndex++; // Central sectors while (nRead < (count - sectorSize)) { nToRead = sectorSize; Buffer.BlockCopy( sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead ); nRead += nToRead; secIndex++; } // Last sector nToRead = count - nRead; if (nToRead != 0) { Buffer.BlockCopy( sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead ); nRead += nToRead; } position += nRead; return nRead; } else return 0; } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: position = offset; break; case SeekOrigin.Current: position += offset; break; case SeekOrigin.End: position = Length - offset; break; } adjustLength(position); return position; } private void adjustLength(long value) { adjustLength(value, null); } private void adjustLength(long value, Queue availableSectors) { this.length = value; long delta = value - ((long)this.sectorChain.Count * (long)sectorSize); if (delta > 0) { // enlargment required int nSec = (int)Math.Ceiling(((double)delta / sectorSize)); while (nSec > 0) { Sector t = null; if (availableSectors == null || availableSectors.Count == 0) { t = new Sector(sectorSize, stream); } else { t = availableSectors.Dequeue(); } sectorChain.Add(t); nSec--; } //if (((int)delta % sectorSize) != 0) //{ // Sector t = new Sector(sectorSize); // sectorChain.Add(t); //} } else { // TODO: Freeing sector to avoid wasting space. // FREE Sectors //delta = Math.Abs(delta); //int nSec = (int)(length - delta) / sectorSize; //if (((int)(length - delta) % sectorSize) != 0) //{ // nSec++; //} //while (sectorChain.Count > nSec) //{ // sectorChain.RemoveAt(sectorChain.Count - 1); //} } } public override void SetLength(long value) { adjustLength(value); } public override void Write(byte[] buffer, int offset, int count) { int byteWritten = 0; int roundByteWritten = 0; // Assure length if ((position + count) > length) adjustLength((position + count)); if (sectorChain != null) { // First sector int secOffset = (int)(position / (long)sectorSize); int secShift = (int)position % sectorSize; roundByteWritten = (int)Math.Min(sectorSize - (int)(position % (long)sectorSize), count); if (secOffset < sectorChain.Count) { Buffer.BlockCopy( buffer, offset, sectorChain[secOffset].GetData(), secShift, roundByteWritten ); sectorChain[secOffset].DirtyFlag = true; } byteWritten += roundByteWritten; offset += roundByteWritten; secOffset++; // Central sectors while (byteWritten < (count - sectorSize)) { roundByteWritten = sectorSize; Buffer.BlockCopy( buffer, offset, sectorChain[secOffset].GetData(), 0, roundByteWritten ); sectorChain[secOffset].DirtyFlag = true; byteWritten += roundByteWritten; offset += roundByteWritten; secOffset++; } // Last sector roundByteWritten = count - byteWritten; if (roundByteWritten != 0) { Buffer.BlockCopy( buffer, offset, sectorChain[secOffset].GetData(), 0, roundByteWritten ); sectorChain[secOffset].DirtyFlag = true; offset += roundByteWritten; byteWritten += roundByteWritten; } position += count; } } } }openmcdf-1.5.4/src/OpenMcdf.sln0000644000175000017500000001030012042056030016150 0ustar mathieumathieu Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf", "OLECompoundFileStorage\OpenMcdf.csproj", "{56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructuredStorageExplorer", "TESTOpenMCDF\StructuredStorageExplorer.csproj", "{4F6323A8-9C06-4D94-808F-EBD69B8370D7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6C619B4F-100F-4D60-BEF1-60B770D24E5E}" ProjectSection(SolutionItems) = preProject LocalTestRun.testrunconfig = LocalTestRun.testrunconfig OpenMcdf.vsmdi = OpenMcdf.vsmdi EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestFiles", "TestFiles", "{AC082D72-85BB-4FE8-AE0A-DB12A5729317}" ProjectSection(SolutionItems) = preProject TestFiles\CorruptedDoc_bug3547815.doc = TestFiles\CorruptedDoc_bug3547815.doc TestFiles\CorruptedDoc_bug3547815_B.doc = TestFiles\CorruptedDoc_bug3547815_B.doc TestFiles\CyclicFAT.cfs = TestFiles\CyclicFAT.cfs TestFiles\MultipleStorage.cfs = TestFiles\MultipleStorage.cfs TestFiles\MultipleStorage2.cfs = TestFiles\MultipleStorage2.cfs TestFiles\MultipleStorage3.cfs = TestFiles\MultipleStorage3.cfs TestFiles\MultipleStorage4.cfs = TestFiles\MultipleStorage4.cfs TestFiles\report.xls = TestFiles\report.xls TestFiles\report_name_fix.xls = TestFiles\report_name_fix.xls EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfTest", "OLECFSTest\OpenMcdfTest.csproj", "{FD339266-8842-40B4-9230-F8E84FC42AC2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfMemTest", "OleCfsMemoryTest\OpenMcdfMemTest.csproj", "{E2BAD82D-3040-462B-BAA2-6E608A9054F4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfPerfTest", "OpenMcdfPerfTest\OpenMcdfPerfTest.csproj", "{7077508F-B313-4DF6-8855-4764911BE161}" EndProject Global GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = OpenMcdf.vsmdi EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Release|Any CPU.Build.0 = Release|Any CPU {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Release|Any CPU.Build.0 = Release|Any CPU {FD339266-8842-40B4-9230-F8E84FC42AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD339266-8842-40B4-9230-F8E84FC42AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.Build.0 = Release|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Release|Any CPU.Build.0 = Release|Any CPU {7077508F-B313-4DF6-8855-4764911BE161}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7077508F-B313-4DF6-8855-4764911BE161}.Debug|Any CPU.Build.0 = Debug|Any CPU {7077508F-B313-4DF6-8855-4764911BE161}.Release|Any CPU.ActiveCfg = Release|Any CPU {7077508F-B313-4DF6-8855-4764911BE161}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {AC082D72-85BB-4FE8-AE0A-DB12A5729317} = {6C619B4F-100F-4D60-BEF1-60B770D24E5E} EndGlobalSection EndGlobal openmcdf-1.5.4/src/OpenMcdf.vsmdi0000644000175000017500000000076312027630262016522 0ustar mathieumathieu openmcdf-1.5.4/src/OpenMcdfPerfTest/0000755000175000017500000000000012061101742017117 5ustar mathieumathieuopenmcdf-1.5.4/src/OpenMcdfPerfTest/Helpers.cs0000644000175000017500000000225312042056032021052 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; namespace OpenMcdfPerfTest { public static class Helpers { public static byte[] GetBuffer(int count) { Random r = new Random(); byte[] b = new byte[count]; r.NextBytes(b); return b; } public static byte[] GetBuffer(int count, byte c) { byte[] b = new byte[count]; for (int i = 0; i < b.Length; i++) { b[i] = c; } return b; } public static bool CompareBuffer(byte[] b, byte[] p) { if (b == null && p == null) throw new Exception("Null buffers"); if (b == null && p != null) return false; if (b != null && p == null) return false; if (b.Length != p.Length) return false; for (int i = 0; i < b.Length; i++) { if (b[i] != p[i]) return false; } return true; } } } openmcdf-1.5.4/src/OpenMcdfPerfTest/OpenMcdfPerfTest.csproj0000644000175000017500000000452612042056034023522 0ustar mathieumathieu Debug AnyCPU 9.0.30729 2.0 {7077508F-B313-4DF6-8855-4764911BE161} Exe Properties OpenMcdfPerfTest OpenMcdfPerfTest v2.0 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1} OpenMcdf openmcdf-1.5.4/src/OpenMcdfPerfTest/Program.cs0000644000175000017500000000221112042056034021053 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using OpenMcdf; using System.IO; namespace OpenMcdfPerfTest { class Program { static int MAX_STREAM_COUNT = 1000; static String fileName = "PerfLoad.cfs"; static void Main(string[] args) { File.Delete(fileName); if (!File.Exists(fileName)) { CreateFile(fileName); } CompoundFile cf = new CompoundFile(fileName); DateTime dt = DateTime.Now; CFStream s = cf.RootStorage.GetStream("Test1"); TimeSpan ts = DateTime.Now.Subtract(dt); Console.WriteLine(ts.TotalMilliseconds.ToString()); Console.Read(); } private static void CreateFile(String fn) { CompoundFile cf = new CompoundFile(); for (int i = 0; i < MAX_STREAM_COUNT; i++) { cf.RootStorage.AddStream("Test" + i.ToString()).SetData(Helpers.GetBuffer(300)); } cf.Save(fileName); cf.Close(); } } } openmcdf-1.5.4/src/OpenMcdfPerfTest/Properties/0000755000175000017500000000000012061101742021253 5ustar mathieumathieuopenmcdf-1.5.4/src/OpenMcdfPerfTest/Properties/AssemblyInfo.cs0000644000175000017500000000264612042056036024211 0ustar mathieumathieuusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("OpenMcdfPerfTest")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("-")] [assembly: AssemblyProduct("OpenMcdfPerfTest")] [assembly: AssemblyCopyright("Copyright © - 2012")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("c3a84d86-d1be-4fdc-8294-169e1f6747d8")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] openmcdf-1.5.4/src/TestFiles/0000755000175000017500000000000012061101742015651 5ustar mathieumathieuopenmcdf-1.5.4/src/TestFiles/CorruptedDoc_bug3547815.doc0000644000175000017500000005300012042056020022346 0ustar mathieumathieuࡱ> &(%bjbj 4xx  0:<<<<<<$.``u::NP &0 `` :TESTTESTTEST for OpenMCDF hwhPj21h:pw. A!n"n#$n% j 666666666vvvvvvvvv666666>6666666666666666666666666666666666666666666666666hH6666666666666666666666666666666666666666666666666666666666666666662 0@P`p2( 0@P`p 0@P`p 0@P`p 0@P`p 0@P`p 0@P`p8XV~ OJPJQJ_HmHnHsHtHJ`J wNormal dCJ_HaJmHsHtH DA D Default Paragraph FontRiR 0 Table Normal4 l4a (k ( 0No List PK![Content_Types].xmlj0Eжr(΢Iw},-j4 wP-t#bΙ{UTU^hd}㨫)*1P' ^W0)T9<l#$yi};~@(Hu* Dנz/0ǰ $ X3aZ,D0j~3߶b~i>3\`?/[G\!-Rk.sԻ..a濭?PK!֧6 _rels/.relsj0 }Q%v/C/}(h"O = C?hv=Ʌ%[xp{۵_Pѣ<1H0ORBdJE4b$q_6LR7`0̞O,En7Lib/SeеPK!kytheme/theme/themeManager.xml M @}w7c(EbˮCAǠҟ7՛K Y, e.|,H,lxɴIsQ}#Ր ֵ+!,^$j=GW)E+& 8PK!Ptheme/theme/theme1.xmlYOo6w toc'vuر-MniP@I}úama[إ4:lЯGRX^6؊>$ !)O^rC$y@/yH*񄴽)޵߻UDb`}"qۋJחX^)I`nEp)liV[]1M<OP6r=zgbIguSebORD۫qu gZo~ٺlAplxpT0+[}`jzAV2Fi@qv֬5\|ʜ̭NleXdsjcs7f W+Ն7`g ȘJj|h(KD- dXiJ؇(x$( :;˹! I_TS 1?E??ZBΪmU/?~xY'y5g&΋/ɋ>GMGeD3Vq%'#q$8K)fw9:ĵ x}rxwr:\TZaG*y8IjbRc|XŻǿI u3KGnD1NIBs RuK>V.EL+M2#'fi ~V vl{u8zH *:(W☕ ~JTe\O*tHGHY}KNP*ݾ˦TѼ9/#A7qZ$*c?qUnwN%Oi4 =3ڗP 1Pm \\9Mؓ2aD];Yt\[x]}Wr|]g- eW )6-rCSj id DЇAΜIqbJ#x꺃 6k#ASh&ʌt(Q%p%m&]caSl=X\P1Mh9MVdDAaVB[݈fJíP|8 քAV^f Hn- "d>znNJ ة>b&2vKyϼD:,AGm\nziÙ.uχYC6OMf3or$5NHT[XF64T,ќM0E)`#5XY`פ;%1U٥m;R>QD DcpU'&LE/pm%]8firS4d 7y\`JnίI R3U~7+׸#m qBiDi*L69mY&iHE=(K&N!V.KeLDĕ{D vEꦚdeNƟe(MN9ߜR6&3(a/DUz<{ˊYȳV)9Z[4^n5!J?Q3eBoCM m<.vpIYfZY_p[=al-Y}Nc͙ŋ4vfavl'SA8|*u{-ߟ0%M07%<ҍPK! ѐ'theme/theme/_rels/themeManager.xml.relsM 0wooӺ&݈Э5 6?$Q ,.aic21h:qm@RN;d`o7gK(M&$R(.1r'JЊT8V"AȻHu}|$b{P8g/]QAsم(#L[PK-![Content_Types].xmlPK-!֧6 +_rels/.relsPK-!kytheme/theme/themeManager.xmlPK-!Ptheme/theme/theme1.xmlPK-! ѐ' theme/theme/_rels/themeManager.xml.relsPK]  8@0(  B S  ?Pjw@eX@UnknownG* Times New Roman5Symbol3. * Arial7.{ @CalibriACambria Math"1) ) !nr02HX $PPj2!xx--Oh+'0d   , 8DLT\- Normal.dotm-1Microsoft Office Word@@r@r՜.+,0 hp|  -  Title  !"#$'Root Entry F0*t)1TableWordDocument8SummaryInformation(DocumentSummaryInformation8CompObjy  F'Microsoft Office Word 97-2003 Document MSWordDocWord.Document.89qopenmcdf-1.5.4/src/TestFiles/CorruptedDoc_bug3547815_B.doc0000644000175000017500000005300012042056022022611 0ustar mathieumathieuࡱ> &(%bjbj 4xx  0:<<<<<<$.``u::NP &0 `` :TESTTESTTEST for OpenMCDF hwhPj21h:pw. A!n"n#$n% j 666666666vvvvvvvvv666666>6666666666666666666666666666666666666666666666666hH6666666666666666666666666666666666666666666666666666666666666666662 0@P`p2( 0@P`p 0@P`p 0@P`p 0@P`p 0@P`p 0@P`p8XV~ OJPJQJ_HmHnHsHtHJ`J wNormal dCJ_HaJmHsHtH DA D Default Paragraph FontRiR 0 Table Normal4 l4a (k ( 0No List PK![Content_Types].xmlj0Eжr(΢Iw},-j4 wP-t#bΙ{UTU^hd}㨫)*1P' ^W0)T9<l#$yi};~@(Hu* Dנz/0ǰ $ X3aZ,D0j~3߶b~i>3\`?/[G\!-Rk.sԻ..a濭?PK!֧6 _rels/.relsj0 }Q%v/C/}(h"O = C?hv=Ʌ%[xp{۵_Pѣ<1H0ORBdJE4b$q_6LR7`0̞O,En7Lib/SeеPK!kytheme/theme/themeManager.xml M @}w7c(EbˮCAǠҟ7՛K Y, e.|,H,lxɴIsQ}#Ր ֵ+!,^$j=GW)E+& 8PK!Ptheme/theme/theme1.xmlYOo6w toc'vuر-MniP@I}úama[إ4:lЯGRX^6؊>$ !)O^rC$y@/yH*񄴽)޵߻UDb`}"qۋJחX^)I`nEp)liV[]1M<OP6r=zgbIguSebORD۫qu gZo~ٺlAplxpT0+[}`jzAV2Fi@qv֬5\|ʜ̭NleXdsjcs7f W+Ն7`g ȘJj|h(KD- dXiJ؇(x$( :;˹! I_TS 1?E??ZBΪmU/?~xY'y5g&΋/ɋ>GMGeD3Vq%'#q$8K)fw9:ĵ x}rxwr:\TZaG*y8IjbRc|XŻǿI u3KGnD1NIBs RuK>V.EL+M2#'fi ~V vl{u8zH *:(W☕ ~JTe\O*tHGHY}KNP*ݾ˦TѼ9/#A7qZ$*c?qUnwN%Oi4 =3ڗP 1Pm \\9Mؓ2aD];Yt\[x]}Wr|]g- eW )6-rCSj id DЇAΜIqbJ#x꺃 6k#ASh&ʌt(Q%p%m&]caSl=X\P1Mh9MVdDAaVB[݈fJíP|8 քAV^f Hn- "d>znNJ ة>b&2vKyϼD:,AGm\nziÙ.uχYC6OMf3or$5NHT[XF64T,ќM0E)`#5XY`פ;%1U٥m;R>QD DcpU'&LE/pm%]8firS4d 7y\`JnίI R3U~7+׸#m qBiDi*L69mY&iHE=(K&N!V.KeLDĕ{D vEꦚdeNƟe(MN9ߜR6&3(a/DUz<{ˊYȳV)9Z[4^n5!J?Q3eBoCM m<.vpIYfZY_p[=al-Y}Nc͙ŋ4vfavl'SA8|*u{-ߟ0%M07%<ҍPK! ѐ'theme/theme/_rels/themeManager.xml.relsM 0wooӺ&݈Э5 6?$Q ,.aic21h:qm@RN;d`o7gK(M&$R(.1r'JЊT8V"AȻHu}|$b{P8g/]QAsم(#L[PK-![Content_Types].xmlPK-!֧6 +_rels/.relsPK-!kytheme/theme/themeManager.xmlPK-!Ptheme/theme/theme1.xmlPK-! ѐ' theme/theme/_rels/themeManager.xml.relsPK]  8@0(  B S  ?Pjw@eX@UnknownG* Times New Roman5Symbol3. * Arial7.{ @CalibriACambria Math"1) ) !nr02HX $PPj2!xx--Oh+'0d   , 8DLT\- Normal.dotm-1Microsoft Office Word@@r@r՜.+,0 hp|  -  Title  !"#$'Root Entry F0*t)1TableWordDocumentWSummaryInformation(DocumentSummaryInformation8CompObjy  F'Microsoft Office Word 97-2003 Document MSWordDocWord.Document.89qopenmcdf-1.5.4/src/TestFiles/CyclicFAT.cfs0000644000175000017500000015000012042056026020106 0ustar mathieumathieuࡱ>   !"#$%&'()+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefRoot Entry0~MyStorageaC0~AnotherStorageaC0~MyStream AnotherStream MySecondStreamPAnother2StreamCAnother2Storage  @1f@1fUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPAnother3StreamMyStream*yMyStreamg~7IarqlPNG  IHDRz^gAMA7tEXtSoftwareAdobe ImageReadyqe<yIDATxl[s֮6.2Ȱqׅ< sY^ wJ:yHn ^lhGqki+Lюr3_ILMcliɫ`6P×kCj }c!%EJt~9<|VL 2 y>ST 6,˖ei8HEQ?5>>W7aQ,+t:f[᐀H˗((jPAւTf ui05MD?BP|QQrbA1RnfppN E!FQ˩l6ljud2lby+_YbDb|Ո )L&'`YczE-XD I-QW+F__b\R)f\!i~Gv̢(f+ 1ZQJ$2V(K2zbբm0'\04((buU__FqG~IZhQ(Ju`N.c.aECB+dЩZ˫ZѲU&bDqVF߱]njnE-iP_,d̢~t((%T+ _hHQQTtKFSď;F^B݉ {L"|btOWFG-hHzE51XЪ@:ݨiyӣNLE:ݨwk:QI!w?xLe(섮dQ/T 1͍w#teo,1:::5eO((#Fƣ˛P(Őb> 1jx%[34@*`'bzgbKͬâ(;nHz}D* Eՠ:J+0=Z.=C|V͛P(1#o߮:Xt֛U^|\< (:wkRB$acƸE5Rqvڥr˙ŸE5R|e˞9C1q= 1jjM:-/F!6fGFF"xj/q}:ƆO((; ڗGK(pH'MZ:E9^q=k8-8ǥas((ar%{Y|wYFq&p`=ZG#fQZ r=9MK<E5@Xer_<#(+(6 1ZQt =|PEmڀzbհpeddUMM]^p:::ۙGǸE-*g5xA%xA{R΄B 0X8Q|Umrq|>?((~+ʣڢBK c0qXB%_d=&5M xiQv˞PQԢ!Bڦ@ 000KŸQ^q=C!FQFpdDe )cU$T2a\BB CKA.^6J|hq=bE N:G,F^bq%>ܡ%:E+BҖ6ʐKWnQb(7ܱ')G"zt((5) NI E-$ˇB\d+6pX\_G>((:dԲ4rӐb\BaGc-xCfHKfHqE!FQ;ʹ!-j$]L'Fc'T#w_S=._ܼpm`YN]8###@5JFW!mm7(JB{{ LVYq= (Ot3hن:sK2C F퀓u"(t(9A@f$6@SRT'lѲ\âQFQm1p_/p`T"c Q'fd2zpM|w_ME5PU!,Nĉi@jce~kE9>ʶiUWpK( 1A@gwW!d2u-NJQEyFiNa /eq;iVM)8z6Bj܋#ɯΟ VDnlUQ sݼj> Yr3S K͂83%y PwlȣwfVVo (\G ۨB.br}QͧkBoIJH1Z)^PxW'0^XƹɘˎW\;1$p G~5ru!8ۭ_)8Zߐj WW|>s Ҋ<$I!u)!U ]Á,˪iy=/Ox OAQu 30q6w4hkupgP% WUC΍@Oi]`z&CY,EoT•. '!ݷok@/æs:g;[RsϛcG#{fho].vСC`3P( Quu~P/K|s60%@.ޕ.@gf=ypy P8:B7qsBׯn Srm  ?՛:¢ N3fdﮩz1ߦw` m|?jFQ4fOv47;+7F$_ 0ٹtO4NE,A-|bUY( Kb zTu HR`<]^&`͛bFS 9=;=:+r4⾏%'f޲/BaPW0_`WܸPp?? EST+j/6z`Ya)7PQ'UZDbKLl w{vw PTɾbkKA/w$Ų|bU'=b+09E2ԘzK*yz[OE!FQ1ʚ}a[δ˃ UO^9v>3sgD;,jd*b iUXFlq=:n~v|٫"(zMoH4JP‚'d4ؔze/ d}.=X_=3ױg×G}PQ R1jF:FR+Ns<Ϥr.2j.]TZLRȁ]$BRn‰~$) 1j F-˂h;Q$jN̝_mѶLt?=%W05MB+VJw];[ZU |+ E{ I|vR;3-A CC~$v;]w*;5n\Ru%_ɚ]%v4Q+N((j1c=E_| eZNd2vFQuOr/,&z !ʾLSرTq0E^~,khhh=ޗ*ӹsAvmWm$}l2$EVm2MgQ}M2.s݁ .cTi$,E.gEQgQw[1PݧGMNFtRtQ'm=Or2 l"vE!Fx@$ 5R0i.!a`43sǜh!24Up ˘ݚ14* 66=A7jcz&l9Y*A$Fd 0NjϏFI%HѸ:}_~"s ,^|5|]6v9P/Rb؏BĄ*Y@V۷aV˲"beI?pKׂ\w\^G'%N~A;T+0ح8vFR>eb\ LZ/Ώ?)8Z~X+FAG3YՎC:p|b2}EQ$Iu&"mf(k&EmJ-u_fMzy4ZY}!5?MNN$)޲ݚHo FimݛݝibNb2ArY%m?~`wPiՄ,(k|1܏θ,+P[1串 ZO&TUUpHXk;!$`ҷK*Ob='.zL =]s79d C_dggg>m0j?(Q08!<P-I9 m,[ 4ޓwHA9;oտ @[u^|"FQm˲ 1P0, ^.@zd{W;< FGin5T37"]_S?S 1_˗s=9CF#ѠXW5Ne&Cmi彎iQy:r$N/܌B!FQ|ܪ:SɃ9ԙxY 48577qî4BCY |9HUs Gߜ^[zӕR+=4ՄmXN1#FQl6}h4` #/d"x}bI@ҥhi F6D 촿#VaPk>L_ /;ozԳ4.b1j``zR*+oNo=5~/j/XӆTT#FEX~bdm `EтSMfxP͊DsszN77r`w>6)mݴD"s;`ߜQT{\}S!)hRd2(it&; tu^91dz.^Zm:EbMC{ UuQ%۴N=eAr-*cRbkt E#Wku]el1E |LU |SSČ$pMk٭"tv1BhϜhp5Ǐ9Mڅ!1E d2,w h, 4Z̳$ԍ@JO \Wi]J XQ(b2M3LtNJ c(RKcd2{nTR{)p|)#O ^_,Xl}@GMȸ:w'/1+sv5M.̆w@dzjRܭ=8]dr$,C!8\OMLg[vc. XoW EElF8gau* CCC$`;=;ߣtaRlA_)f!r/{~N;7cGZwGB7j5A`(S3+ʥiı:"C :QiUҵԟ]U_VB-$3sG;s5Jp(bF⭃Xhђh"Q|SIRR|MWI-B2 >겁PF >񮓡Q("Gl!OeJcJU߼pۗjX+Y/Sˣk}11-;?c2>N'KE-P%_W:m7xz T3Q{,k/6ph_cٚ(Rowfm@CE4MF!">`cc *Gִn i/Ɩx'068oVSE=_Q-\G ;B;u?\*xQ-#\G1~{ lUi&Tßx:5<׭HhʅܝMf/cp n0;Hoko>wt4*g1E=I0ps||L&J9Io_c| -JfY{S.ýc!:.aNg VFGIzXx1GVdYt1M]$3^1l2*Kr6%_0;]!u~T;(j)H0n$'` PSVy3F" u9&8ɜPEJdv|N+_ )E=&ŗg]J^$Zl1pҒv%*eD*Թ)Q߯rfv=zcߧ`\.+'_Fe"FQ"9;s:d=N&Xp81h h;L fذYOKy]OQ%ѣj@>e]@('0]Ŷozug}b&uwZRĄG˥YJn5F:D > }2$ V.( H5&[ǔSfHwCe^###؈1B5^rgH(v]N#S4V ׎b˗/+KanjHM|VynR<ߏ ed iV" KFQf "b}|145y'1]7f]ؘx\Ν3 Eַٔ14S9Z֝̎/-2C1MԣPKyպ'{>[=f}XM:-6]nˊEZDmtvwu}r /1$SUUEAls`]=\uy}lUڗ+ =:`fPT{LhldH4Z@.1"OB>qUCCA 5&E 922f5MC"FQ뇆mQk'jd'/;1?s``a`A= բeKzR5{,oTRL|os~?nG7B|BZSGkPnal F}hͧ1B$Ijb@=FXqcɉFtmGL73ӄ^|[,vR8\ftB :_}l: UT44((l6;<\<ع^7?9v^>6MLvsN|bLl5((\bBlp  |4:>N顿Y(*~ Ysf.iDRtIy(IUUmH3*T[! 3$|9w0 N;(\bƆd?;ډbtF4j5(͸2{ mA~|uX^;{U;sNMOܸ~]='O&c'23w0Ŝ)(2@O`s= &?D0ؑۜ_fk5L((j'R 6*٩Hs}BKaDQ.&( m9'3"1O4w ri`NS\3?tl@*UC=z}AhoMߣ[NfQ)bZ"NƎv+e%;BR@p |Ǣ>$=]E3EPK$0q>a#^zZTQ_*Wtz/ iKPWWnflzOOޮ7Nya(H .HO͆OȣMMκXY|o9v$zPFGG£RSt(ԢwMӀ##'{}θ[ezNA||9C:6͸\gqb~&dO}iRԋGr\$Pz_[٫[EvF"FQ(Y|g >3 !+F*USW Z^mZ)CZ*UH$0RS( vTTCF#tQ=Nߘ Ð:aRtءP)\FV{^iM*:B&hwXJGمu~ٷ+ lFQR*4t)Džԯ<<}1 bc]v}٪~`Ly&oigONCV*e?Y XT3 KuUsZt>V"FQ+9~gK1%Pv4dQØ'"_>KM"`MbV PgO=@O W0rE~(n(Q"}MvUK=W |zRĻEK4+ޠ!*Q400/1j3 &tmWd=;xPPLBcoL(:S(=;x"Tz`6UJ(ZnRv;L]E PŹD'=xK?Vr)z;v)1AN~p51j-R)82&szxUZ IRŔy6O̱[dǮTjEZBa'=òK9c2j䯞Ws.;[:{1j(>-u%0\ew a1Z<9#]ݭc?,ʅVسTUː~hgvD"1ZZJ (Ν[I$=t|4Uᜡ.Էi]vݕT QTSα7d<R`χ++酯vbg߼hLL34Pg? bM>b,v6I(Ui8'75Mc++c}9;ty ^{ti~O'IՑL&q=b5VJ_=,R|KR4lSTvS;&O^m)n[ рAdEL]d~/6̐6GSj҇-hAi)e P|I3e=_۩̙YMb(@EԇoGo[p^! v _+e(_kwd$ﴥç|,c(yONoN(=";7&pߪ XWlvҜOm)p"/>hT1A=?W{粢z"Ev?zLpcsJщ77OMr{(q3n6kq-HӄF"V2E %0L+{cQâ({]\|,~E(zyQ}q={U߽-;->jOEfx"ՕQl=*/5'QFQ K3Bں5"UsJe ї>ﱳtl>OQTF"wԝ6k &5"'sVEm%Ɂ& 鴗EqrB\mJ)sK jGz1tdd#FQͱuєto:W~c۟ؿ p1Z jA֬oƽ(D[W.! E5UuցksQR!RxZ uoBߑ-gڙ+h2(Zg:(X14n*Fz[ص -|sXwIaůZ.: Ƥ3_V/_: $=C|VC Ku [G%໎tzcB~L5%B֯䗣'x'!FQKW:*F^- פNpvkH*/ 7)!FQ#zǞ鷍5 0 7SˍguB+)G||-f5\W44@1jX1(mT[ Tu:) >krm8ϬH'4xͿeGټyWUj V?J߮Lhc1ʪ߿ϓ `E~+o d!L&!V‹O}j]!i^Nɟbs~>UO)VD_OOº"%Em;q.F %K o+e\ZqKn_ٹ{rkov/[ѨY(8 Y!u6gԿ  U>}D\A=&_F=yQe(zTC!+-þа16 zhR$8 4CA,i߃b1*/G&nb 1jo^kh/"-F۶^r(T^}˽,bu!I 1Max$Nk]|-ENY!FQ,0Y.Hꑽܐ~O΍ŎQbզie{-L&  ~) u)EMȽ⎴((j$Mf0`wWZ( ^ Q!cܭ|9}^jݜ-^sͽ>ص}Uj?hIKIĩiYQi/SzR("I/_,?=3UK1 TUfakv)^Z\#b[or0!'IL(1 #DЈF|qB60?}T-,"dbH5MJʎM$oXvq&H5UPS:e/;P'>r#=5kA-d ?kj˯HZ` "}+d%v g, 1GWkĬZP8L|P)c+W:UpB LSiE3*s;5^!E/{%Tqmmi>rgŐhWǏWBadN)?|c>("LAmEx)'/\ܨN [2HR˞\ pK2Nao`I=20@fᰙLZ%\?J$YNFvG xHgYٙ"}- Y18З^KBϟZEԣY z[xM7TR=ґch ;8 5bR3V`I]ś >ޒ "f |D*?IB xN*I){k:!t,~PnR߆u/tז(eې!060(Za*1O~K.*/.4s@KJIR7z@$DUa`gUvDBW 8$M7I\vn}ӽsURuA=j4Muɗ)^ ˒Թ)vzJaq޼g_G?MTAMz  1m `pw hiXV;(D?z2M~PX'YdO]d@n1=hmoP碷$~!XE޼d7$}$SwU6eq3Fw$͈QTaVrJ?l$8ܼ7*}'+?{_}q'5^F6l0i%KLhVlxH3(ߪ*5<Ų:' Q&Y{&c +~]<}ǏW"r9bamlT [)iB4H"aXF:IH뉔͸o޼=Ω)3F mBLb6^FOS]H;sř\>7A){HkeXduԤ*}$3K UȚ?+>ƣ㚏$Ik^s0mE"wDaNV_nTXx;`J-x6I/HLE1aօF( WXs詄?&=h>4C ,(?= =ްtN4N:iK= @'pn_#>AzpvRGNx UkӮV&ߛ/-H/$Ӗvvt OMi5>%izYCv/7Ij}]IB: Q<ĂOTz *wn;:XЄn CᾜUvl;I}{ĺG?MgKrIk3C[$_7K^Wޖ-N-Qz={KW"z |neO)?-HV`W\\cPdx`w_}|v` l7 |sC^pwYNJP(p/Bhq}5I䓤5 E,KЫ&(c(V9Ko柜&|AW H^:H<wB6Ɵ S2Y\YmtӈQtHAaᨴL<:\{`]HIeO73 bL-:ߤM+pavnERov2۷:|PUPLәClDmb_ o* ]-RUuxxTot`rzEX:JpfsDQڏK=Iy-?]':-6I밺o\$Zw>&|+ԓc䱭 ]FEOY)״*c i*EiK&hA=([E[|PszN;se!)R%@|c"d(1$6L7rS|l1C_ ׹*BW*v#Hu*yɛ{RvK0!F V&sO驅C<_WIzOD޼d}}?{39r |0emx6K9 LCC1DŽ]bۜYYhS 4LMץs#{ƎM! p$ET|p<{l>^-Zz1i?]a|}fCh_D˔{tgrcc>fwdY:6N?ࣳ9nDSDJzz u|v'NJO#}gb4Jǂ E@PsC7YHɉ}25^{ |`¬ d(C9qb%a")22wѸR$;4!7h ުv$(_M[ : kEe\7SLb^"?y<+io7y<7APnfk5."IW Xi0aXDӠ ԷRԾ}#>uJI^?G|مR:Cc)Qԏ \H9K] .1rݢ̝f |N m5\ӂǏ'i~,ovvdJ<%}H#Li]+3EQ 4OX\+ww}W' _%\oKv;.+/9vDyv[ sR؞92TL14G0@%i uz D}Lu]^P nb6;4rB6/en̒ɻ^L&sٓc{o*l\ 6NeһB={]ҷbןnrԘXX?X}hѲWVQ=DzzRw%NfSWD O| 'lx>>{džPE-GVLld*4XԷuHtuvp֑' $]Ųwu@;({hvZ#%n{;Ĩ>6_8ٿ[g{-L ߣLdEbk#t$@򃒶yUΙ[D$53~DEcS !_Ztg!ܚ]p ;{#Ki,)m/zWR0ra||\n6X(0:~G̍عHo[J#DPɄtLu\2t/Ոix<-P22 YoB‹sq4م\_~#|qn =;k}mQ Q9g߯Fђs$pbuPb}_S eB-)B煜(Po# Ůgb8"$PB3`Kݨٛ%bC6wǟ$_?Ya߼XROt /˷.Xmۿ裏;goGµ̝1yI}A"|8'3֓?0$~10f$OH۟ >*2ɩE<5}2E>5':*!'hW"OLO)aPڥ(ODBE6MB}&춨`]|;lZw"FQ ,+/I>.\hkigNOU5®ٻĘCi=2_E 2o5EE.d.")('Ii@"€PNڣ. oX يӁ=s֮b 50-8އDlʲNN<~06Rظ;\9@l_=P~Kg2 Bؠʡ|rqA}-)fۨfë́Y0p,oTVM.t $:).])]$u/'Ex覆T:x cGBEԋm-l_J3Б`Hu1-*3O/PFcIjQJ":ovk?I2dJ}[tt톈hzjV5ݎwc#oܤC+1G+x/5[PBH Cۋk|(D'JݮP&^2mLL߽"`w'Dym& X֬V_|,SU0ݢҝי|MEۙX(O= k 6NB,o펟z䳧YOdWnEx/1OOw,=LԕL`O}ݪ~jFYjH`ubh\h:b`ז-:OWRjNNOU kI(]gg_ħ~@-C8>Q^ < ԣr_WA:Un"ߧ[h' ).%!FQg@{Vա:3ɨInG&a$gW} _ger]Qa#;ֽyں)ьU( S6ea+n9ɛ;b~~!?m*"dԊs>ԉQn٣ A3(C}- d&|&d8׷V7tq7B4ŭQT=2*[uz)=û- )=I\pI !G2 SPt+ufFJSzdwn"Ya}@^K[kr/e])v6a'(HiZ:]$K"ϓ@a&tZr[J;&!۫$N.6<^|cS%UݟzD]_PB&hL9r3(CϜa$=6!\1.DW<1TySУgh8B27Љۧ=EO4[G]t(-{./p*R $uOW1Lit]C * )QKɆET?$Hְ {iX{v.7pЏ sMkj`2Kc{()<]%Y :j&+=E|w`z Xz@*5 hDjW'mT:w 5GH &]X,&b3ϻ/=EAB36]lYNgMN!ňM{-ҩ,xV~ݒ ã[W @eiu`*F]Gf6z_ JP"q1 -uG&d)OTi s̺N^gow%i:}`hE!@%ZW;9}Pv7$N. J*]D"eTd+pYa=Px&7'XEV"tcDֱ*: {(rv;htFQO'2P(jYC}(SC_3C[7=D;;S MzXiygݣ‚ flevմfSK(I{+EDYLݸ5k.N1Q@:]~ 2nRj)a|6S9yC` w72 UMDfrDIglm{j] uY8_i{;sc[r [pp8a(jźl/0aT|L03t¤\Oy.KX3A)x-+|t ֲV~]TR)`bn;MISUoW(E~8z6>ic63Ky;F-5?wd~YY&(}$RbIJi`$†cΒOTr3NgQ,Yg"`ebP(-4Dt2]?3{Q4^ R뷤ĄYT'dgUV@P#TvGk.Ӳ2Va.L(jOlrahȖ{Io~:@kҢ|JYK7Ц6fMa#г Q&ce&uQjiSh * ڿM?^X(zfIQ.$ܤTbILdS<\=e}Uc$MBsY1%{r"FQ!*X15)sI ڝDӵN|ݬbӴ #JwxO_C 2JZ3v1B `CUCIFu$)]jֵ;/ˊ5=p{dGR[q+Dp1b̩l6d2k-tGf@#O+7SWe+0 8s w1 bU`h~֣ٮl#K -lg-u W:՟foDE|־}ݮٝVd끉>O[W(=՚M¾ -ř((:S.а,Cݮ&4Q&Spw+QԣV ` ma&Y:Κ B:6vJ,9?&IayϗV+C1GEUW `N Dk@$%Ȍ%]MY 4' &ٓZ u14 IPQTE}KѳvP9KXd\pd F~LGCQQW+ }|LبZ:MӖBnA=27Σ07*/߹T.N'JDIEO^moNDl)8(1DA=iatcܗ(T5CIԓ‘^.:^,tM_)0ԶbHݛHFIIW4>Ka(3JQn}fSPVUVR"ȍ8+a:s 1Dn~D/{DOfrݟl,j&1D%XQF0[(l|z8%B@ %FIKĨ_dQMQXJ-DI]r$_/b)V/CaT^_Bζm\C9bQsPQR}_v{<Ư*R)hڍrJ>DnG/1 MEU4==76qx0Tˇ"08 v U mY~ }L&O"z\^7?_:FEP2F3z PDQ҆ l`ؘ(2y@Q<[b((/kbC|]V$FFy%% 04e5q\.(W&x]ގnTJ"ѐXޫ]QDh ewJ $()dqU4nD㊳Pؽ):1D% Ȋێ$7gJ_j}XVԱCIQ0%'NJ_-ԋCq$JU}U<''ߡ*PynD79dK~lLg9'FI]sIIU$ ̈&z5R"Q*R QaYp]Z#ef=?%xuX(NҼ?4_|+1F!O+&x=̥gckT"*Zt;XNz:1 tV7aqO KT|t*wѻWS$rBיfե'c^&iƌrCz[E/q@T=Lq//FyW‰b.E %FI >+Տ/XGf[ be:`4m[ 8PkDlb(%FIh& Pi??7=k5=47L](jƊL&]r uH)P.Cr,75{nHjϸWי~m}H%B*o؝u`%wN}z:sʴ0>`q+M )_0pCCl -jsHgp>C_T$=iPifY<{$TkE% W8?55 t|>~zuLC6?30#|Wv5k~>[Lkci|Mj*"ٺVl׮O6ES0;;9s7Lh]E\ ´b:n=VK}*E7zϩXT 7R$umplk&F8P9MS}1~iC_ Z7WnMWF%#"ƏޒS{,13q`GE ? fIC:ku<5#nUa n|́J,Wu6BZWo0(Mߞ."<3Y{6׌PҸ0tR܅Jk)Oʷ+ [D$yg1aޛDD}&Fƞa5+"$9Q,@KE[{ْڑ^iL 2YIψ â˱c. 3~EON&NrH?.9DJ*P݄c/ ~SFB^vEsO~R9|ş b^D&zO 89=Y>N-uIK\ꚑel )at )Q !΄..ҟڏS`ov%\egg. '749WW_ѐ=at &}8O=4&ty}6 8հuHUk7g{Q^~u`^|ӧtDk1:C3v PrU~MKOMz~ V֓=fsv|8{Ãt*dC/*{"7: XCY7#F oTSR 5m^4nGyy0Mң8?8mP5|/Db m1=t1+W5&BPAa_!缪1Vߘ&6˷$e6CǡAWHкzR;ߪ:vC#m(7ZG\NoM?iOqvt\~h4!kKmh6PTۮj}ղneZᵢxvFuS׋j¿OMO5vɶ_Pᵢ:~^ [I1Y~lǏ"G6FE'===M %Ljm踘xC Qm 5syŲm*FG4TiGϊ&h%:2TVgE2D P(j"FTz=(EV *c'. cRTG] kemq$x%*VZѶz` ]~es Xv5>55 P C .yEjdhւT\Fqq,aܩ$\.U3at :?Br*;wN:ztL^c)zcqgc11w^}hINlZn:wWjq(4772bbD%6Ľ\wI}-:eYh?0Z'y:0؊=|2F'iQKdH3fy"(c#;N F!}Okh.DHɖ9WH-wۢޭ׆qϮzm;-DcGl6{mO(S~`4ܺuS庮B׶~4X iщ[aԽlē'FIh{M^$R۽cxj`.: 524m"A|#5IAW$h (Kc-kug/Ţ* ET"*kGƨߌb:94M<2 e? D Vx¶ ]VtDT4XGJViun;1~Ұ;at'T-Tw}K3=;qE0vx|TU$R99P999))x'ty>#Z |[bi3hU 0E>?du~ݫ'EQR}[:}+(>fl'=c@o:\#ՠy3.fx9An"1%@h0JjZb 0鶅 T|{Pdp2 bViZdN$F~91dSm߱xQVZ mr KvKa4dKboDVd(oQnNe7#uϻ/@ʐJ" SBy*_0v+„^|2JTH$h8r'HHbta Oid$@/UvGG0Gf2UIc"((vZ% 2Y_)hvnضL&4DH/ y?IENDB`openmcdf-1.5.4/src/TestFiles/MultipleStorage.cfs0000644000175000017500000001000011546163204021465 0ustar mathieumathieuࡱ> Root EntryMCMyStorageaCMCAnotherStorageaCMCMyStream AnotherStreamMySecondStreamPopenmcdf-1.5.4/src/TestFiles/MultipleStorage2.cfs0000644000175000017500000001100011546163204021550 0ustar mathieumathieuࡱ> Root Entry05fMyStorageaC05fAnotherStorageaC05fMyStream AnotherStreamMySecondStreamPAnother2Stream.Another2Storage @1f@1fUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPAnother3Streamopenmcdf-1.5.4/src/TestFiles/MultipleStorage3.cfs0000644000175000017500000005300011546167612021566 0ustar mathieumathieuࡱ>   !"#$%&'()Root Entry0~MyStorageaC0~AnotherStorageaC0~MyStream AnotherStreamMySecondStreamPAnother2StreamCAnother2Storage @1f@1fUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPAnother3Streamopenmcdf-1.5.4/src/TestFiles/MultipleStorage4.cfs0000644000175000017500000015000011660052616021557 0ustar mathieumathieuࡱ>   !"#$%&'()+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefRoot Entry0~MyStorageaC0~AnotherStorageaC0~MyStream AnotherStream MySecondStreamPAnother2StreamCAnother2Storage  @1f@1fUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPAnother3StreamMyStream*yMyStreamg~7IarqlPNG  IHDRz^gAMA7tEXtSoftwareAdobe ImageReadyqe<yIDATxl[s֮6.2Ȱqׅ< sY^ wJ:yHn ^lhGqki+Lюr3_ILMcliɫ`6P×kCj }c!%EJt~9<|VL 2 y>ST 6,˖ei8HEQ?5>>W7aQ,+t:f[᐀H˗((jPAւTf ui05MD?BP|QQrbA1RnfppN E!FQ˩l6ljud2lby+_YbDb|Ո )L&'`YczE-XD I-QW+F__b\R)f\!i~Gv̢(f+ 1ZQJ$2V(K2zbբm0'\04((buU__FqG~IZhQ(Ju`N.c.aECB+dЩZ˫ZѲU&bDqVF߱]njnE-iP_,d̢~t((%T+ _hHQQTtKFSď;F^B݉ {L"|btOWFG-hHzE51XЪ@:ݨiyӣNLE:ݨwk:QI!w?xLe(섮dQ/T 1͍w#teo,1:::5eO((#Fƣ˛P(Őb> 1jx%[34@*`'bzgbKͬâ(;nHz}D* Eՠ:J+0=Z.=C|V͛P(1#o߮:Xt֛U^|\< (:wkRB$acƸE5Rqvڥr˙ŸE5R|e˞9C1q= 1jjM:-/F!6fGFF"xj/q}:ƆO((; ڗGK(pH'MZ:E9^q=k8-8ǥas((ar%{Y|wYFq&p`=ZG#fQZ r=9MK<E5@Xer_<#(+(6 1ZQt =|PEmڀzbհpeddUMM]^p:::ۙGǸE-*g5xA%xA{R΄B 0X8Q|Umrq|>?((~+ʣڢBK c0qXB%_d=&5M xiQv˞PQԢ!Bڦ@ 000KŸQ^q=C!FQFpdDe )cU$T2a\BB CKA.^6J|hq=bE N:G,F^bq%>ܡ%:E+BҖ6ʐKWnQb(7ܱ')G"zt((5) NI E-$ˇB\d+6pX\_G>((:dԲ4rӐb\BaGc-xCfHKfHqE!FQ;ʹ!-j$]L'Fc'T#w_S=._ܼpm`YN]8###@5JFW!mm7(JB{{ LVYq= (Ot3hن:sK2C F퀓u"(t(9A@f$6@SRT'lѲ\âQFQm1p_/p`T"c Q'fd2zpM|w_ME5PU!,Nĉi@jce~kE9>ʶiUWpK( 1A@gwW!d2u-NJQEyFiNa /eq;iVM)8z6Bj܋#ɯΟ VDnlUQ sݼj> Yr3S K͂83%y PwlȣwfVVo (\G ۨB.br}QͧkBoIJH1Z)^PxW'0^XƹɘˎW\;1$p G~5ru!8ۭ_)8Zߐj WW|>s Ҋ<$I!u)!U ]Á,˪iy=/Ox OAQu 30q6w4hkupgP% WUC΍@Oi]`z&CY,EoT•. '!ݷok@/æs:g;[RsϛcG#{fho].vСC`3P( Quu~P/K|s60%@.ޕ.@gf=ypy P8:B7qsBׯn Srm  ?՛:¢ N3fdﮩz1ߦw` m|?jFQ4fOv47;+7F$_ 0ٹtO4NE,A-|bUY( Kb zTu HR`<]^&`͛bFS 9=;=:+r4⾏%'f޲/BaPW0_`WܸPp?? EST+j/6z`Ya)7PQ'UZDbKLl w{vw PTɾbkKA/w$Ų|bU'=b+09E2ԘzK*yz[OE!FQ1ʚ}a[δ˃ UO^9v>3sgD;,jd*b iUXFlq=:n~v|٫"(zMoH4JP‚'d4ؔze/ d}.=X_=3ױg×G}PQ R1jF:FR+Ns<Ϥr.2j.]TZLRȁ]$BRn‰~$) 1j F-˂h;Q$jN̝_mѶLt?=%W05MB+VJw];[ZU |+ E{ I|vR;3-A CC~$v;]w*;5n\Ru%_ɚ]%v4Q+N((j1c=E_| eZNd2vFQuOr/,&z !ʾLSرTq0E^~,khhh=ޗ*ӹsAvmWm$}l2$EVm2MgQ}M2.s݁ .cTi$,E.gEQgQw[1PݧGMNFtRtQ'm=Or2 l"vE!Fx@$ 5R0i.!a`43sǜh!24Up ˘ݚ14* 66=A7jcz&l9Y*A$Fd 0NjϏFI%HѸ:}_~"s ,^|5|]6v9P/Rb؏BĄ*Y@V۷aV˲"beI?pKׂ\w\^G'%N~A;T+0ح8vFR>eb\ LZ/Ώ?)8Z~X+FAG3YՎC:p|b2}EQ$Iu&"mf(k&EmJ-u_fMzy4ZY}!5?MNN$)޲ݚHo FimݛݝibNb2ArY%m?~`wPiՄ,(k|1܏θ,+P[1串 ZO&TUUpHXk;!$`ҷK*Ob='.zL =]s79d C_dggg>m0j?(Q08!<P-I9 m,[ 4ޓwHA9;oտ @[u^|"FQm˲ 1P0, ^.@zd{W;< FGin5T37"]_S?S 1_˗s=9CF#ѠXW5Ne&Cmi彎iQy:r$N/܌B!FQ|ܪ:SɃ9ԙxY 48577qî4BCY |9HUs Gߜ^[zӕR+=4ՄmXN1#FQl6}h4` #/d"x}bI@ҥhi F6D 촿#VaPk>L_ /;ozԳ4.b1j``zR*+oNo=5~/j/XӆTT#FEX~bdm `EтSMfxP͊DsszN77r`w>6)mݴD"s;`ߜQT{\}S!)hRd2(it&; tu^91dz.^Zm:EbMC{ UuQ%۴N=eAr-*cRbkt E#Wku]el1E |LU |SSČ$pMk٭"tv1BhϜhp5Ǐ9Mڅ!1E d2,w h, 4Z̳$ԍ@JO \Wi]J XQ(b2M3LtNJ c(RKcd2{nTR{)p|)#O ^_,Xl}@GMȸ:w'/1+sv5M.̆w@dzjRܭ=8]dr$,C!8\OMLg[vc. XoW EElF8gau* CCC$`;=;ߣtaRlA_)f!r/{~N;7cGZwGB7j5A`(S3+ʥiı:"C :QiUҵԟ]U_VB-$3sG;s5Jp(bF⭃Xhђh"Q|SIRR|MWI-B2 >겁PF >񮓡Q("Gl!OeJcJU߼pۗjX+Y/Sˣk}11-;?c2>N'KE-P%_W:m7xz T3Q{,k/6ph_cٚ(Rowfm@CE4MF!">`cc *Gִn i/Ɩx'068oVSE=_Q-\G ;B;u?\*xQ-#\G1~{ lUi&Tßx:5<׭HhʅܝMf/cp n0;Hoko>wt4*g1E=I0ps||L&J9Io_c| -JfY{S.ýc!:.aNg VFGIzXx1GVdYt1M]$3^1l2*Kr6%_0;]!u~T;(j)H0n$'` PSVy3F" u9&8ɜPEJdv|N+_ )E=&ŗg]J^$Zl1pҒv%*eD*Թ)Q߯rfv=zcߧ`\.+'_Fe"FQ"9;s:d=N&Xp81h h;L fذYOKy]OQ%ѣj@>e]@('0]Ŷozug}b&uwZRĄG˥YJn5F:D > }2$ V.( H5&[ǔSfHwCe^###؈1B5^rgH(v]N#S4V ׎b˗/+KanjHM|VynR<ߏ ed iV" KFQf "b}|145y'1]7f]ؘx\Ν3 Eַٔ14S9Z֝̎/-2C1MԣPKyպ'{>[=f}XM:-6]nˊEZDmtvwu}r /1$SUUEAls`]=\uy}lUڗ+ =:`fPT{LhldH4Z@.1"OB>qUCCA 5&E 922f5MC"FQ뇆mQk'jd'/;1?s``a`A= բeKzR5{,oTRL|os~?nG7B|BZSGkPnal F}hͧ1B$Ijb@=FXqcɉFtmGL73ӄ^|[,vR8\ftB :_}l: UT44((l6;<\<ع^7?9v^>6MLvsN|bLl5((\bBlp  |4:>N顿Y(*~ Ysf.iDRtIy(IUUmH3*T[! 3$|9w0 N;(\bƆd?;ډbtF4j5(͸2{ mA~|uX^;{U;sNMOܸ~]='O&c'23w0Ŝ)(2@O`s= &?D0ؑۜ_fk5L((j'R 6*٩Hs}BKaDQ.&( m9'3"1O4w ri`NS\3?tl@*UC=z}AhoMߣ[NfQ)bZ"NƎv+e%;BR@p |Ǣ>$=]E3EPK$0q>a#^zZTQ_*Wtz/ iKPWWnflzOOޮ7Nya(H .HO͆OȣMMκXY|o9v$zPFGG£RSt(ԢwMӀ##'{}θ[ezNA||9C:6͸\gqb~&dO}iRԋGr\$Pz_[٫[EvF"FQ(Y|g >3 !+F*USW Z^mZ)CZ*UH$0RS( vTTCF#tQ=Nߘ Ð:aRtءP)\FV{^iM*:B&hwXJGمu~ٷ+ lFQR*4t)Džԯ<<}1 bc]v}٪~`Ly&oigONCV*e?Y XT3 KuUsZt>V"FQ+9~gK1%Pv4dQØ'"_>KM"`MbV PgO=@O W0rE~(n(Q"}MvUK=W |zRĻEK4+ޠ!*Q400/1j3 &tmWd=;xPPLBcoL(:S(=;x"Tz`6UJ(ZnRv;L]E PŹD'=xK?Vr)z;v)1AN~p51j-R)82&szxUZ IRŔy6O̱[dǮTjEZBa'=òK9c2j䯞Ws.;[:{1j(>-u%0\ew a1Z<9#]ݭc?,ʅVسTUː~hgvD"1ZZJ (Ν[I$=t|4Uᜡ.Էi]vݕT QTSα7d<R`χ++酯vbg߼hLL34Pg? bM>b,v6I(Ui8'75Mc++c}9;ty ^{ti~O'IՑL&q=b5VJ_=,R|KR4lSTvS;&O^m)n[ рAdEL]d~/6̐6GSj҇-hAi)e P|I3e=_۩̙YMb(@EԇoGo[p^! v _+e(_kwd$ﴥç|,c(yONoN(=";7&pߪ XWlvҜOm)p"/>hT1A=?W{粢z"Ev?zLpcsJщ77OMr{(q3n6kq-HӄF"V2E %0L+{cQâ({]\|,~E(zyQ}q={U߽-;->jOEfx"ՕQl=*/5'QFQ K3Bں5"UsJe ї>ﱳtl>OQTF"wԝ6k &5"'sVEm%Ɂ& 鴗EqrB\mJ)sK jGz1tdd#FQͱuєto:W~c۟ؿ p1Z jA֬oƽ(D[W.! E5UuցksQR!RxZ uoBߑ-gڙ+h2(Zg:(X14n*Fz[ص -|sXwIaůZ.: Ƥ3_V/_: $=C|VC Ku [G%໎tzcB~L5%B֯䗣'x'!FQKW:*F^- פNpvkH*/ 7)!FQ#zǞ鷍5 0 7SˍguB+)G||-f5\W44@1jX1(mT[ Tu:) >krm8ϬH'4xͿeGټyWUj V?J߮Lhc1ʪ߿ϓ `E~+o d!L&!V‹O}j]!i^Nɟbs~>UO)VD_OOº"%Em;q.F %K o+e\ZqKn_ٹ{rkov/[ѨY(8 Y!u6gԿ  U>}D\A=&_F=yQe(zTC!+-þа16 zhR$8 4CA,i߃b1*/G&nb 1jo^kh/"-F۶^r(T^}˽,bu!I 1Max$Nk]|-ENY!FQ,0Y.Hꑽܐ~O΍ŎQbզie{-L&  ~) u)EMȽ⎴((j$Mf0`wWZ( ^ Q!cܭ|9}^jݜ-^sͽ>ص}Uj?hIKIĩiYQi/SzR("I/_,?=3UK1 TUfakv)^Z\#b[or0!'IL(1 #DЈF|qB60?}T-,"dbH5MJʎM$oXvq&H5UPS:e/;P'>r#=5kA-d ?kj˯HZ` "}+d%v g, 1GWkĬZP8L|P)c+W:UpB LSiE3*s;5^!E/{%Tqmmi>rgŐhWǏWBadN)?|c>("LAmEx)'/\ܨN [2HR˞\ pK2Nao`I=20@fᰙLZ%\?J$YNFvG xHgYٙ"}- Y18З^KBϟZEԣY z[xM7TR=ґch ;8 5bR3V`I]ś >ޒ "f |D*?IB xN*I){k:!t,~PnR߆u/tז(eې!060(Za*1O~K.*/.4s@KJIR7z@$DUa`gUvDBW 8$M7I\vn}ӽsURuA=j4Muɗ)^ ˒Թ)vzJaq޼g_G?MTAMz  1m `pw hiXV;(D?z2M~PX'YdO]d@n1=hmoP碷$~!XE޼d7$}$SwU6eq3Fw$͈QTaVrJ?l$8ܼ7*}'+?{_}q'5^F6l0i%KLhVlxH3(ߪ*5<Ų:' Q&Y{&c +~]<}ǏW"r9bamlT [)iB4H"aXF:IH뉔͸o޼=Ω)3F mBLb6^FOS]H;sř\>7A){HkeXduԤ*}$3K UȚ?+>ƣ㚏$Ik^s0mE"wDaNV_nTXx;`J-x6I/HLE1aօF( WXs詄?&=h>4C ,(?= =ްtN4N:iK= @'pn_#>AzpvRGNx UkӮV&ߛ/-H/$Ӗvvt OMi5>%izYCv/7Ij}]IB: Q<ĂOTz *wn;:XЄn CᾜUvl;I}{ĺG?MgKrIk3C[$_7K^Wޖ-N-Qz={KW"z |neO)?-HV`W\\cPdx`w_}|v` l7 |sC^pwYNJP(p/Bhq}5I䓤5 E,KЫ&(c(V9Ko柜&|AW H^:H<wB6Ɵ S2Y\YmtӈQtHAaᨴL<:\{`]HIeO73 bL-:ߤM+pavnERov2۷:|PUPLәClDmb_ o* ]-RUuxxTot`rzEX:JpfsDQڏK=Iy-?]':-6I밺o\$Zw>&|+ԓc䱭 ]FEOY)״*c i*EiK&hA=([E[|PszN;se!)R%@|c"d(1$6L7rS|l1C_ ׹*BW*v#Hu*yɛ{RvK0!F V&sO驅C<_WIzOD޼d}}?{39r |0emx6K9 LCC1DŽ]bۜYYhS 4LMץs#{ƎM! p$ET|p<{l>^-Zz1i?]a|}fCh_D˔{tgrcc>fwdY:6N?ࣳ9nDSDJzz u|v'NJO#}gb4Jǂ E@PsC7YHɉ}25^{ |`¬ d(C9qb%a")22wѸR$;4!7h ުv$(_M[ : kEe\7SLb^"?y<+io7y<7APnfk5."IW Xi0aXDӠ ԷRԾ}#>uJI^?G|مR:Cc)Qԏ \H9K] .1rݢ̝f |N m5\ӂǏ'i~,ovvdJ<%}H#Li]+3EQ 4OX\+ww}W' _%\oKv;.+/9vDyv[ sR؞92TL14G0@%i uz D}Lu]^P nb6;4rB6/en̒ɻ^L&sٓc{o*l\ 6NeһB={]ҷbןnrԘXX?X}hѲWVQ=DzzRw%NfSWD O| 'lx>>{džPE-GVLld*4XԷuHtuvp֑' $]Ųwu@;({hvZ#%n{;Ĩ>6_8ٿ[g{-L ߣLdEbk#t$@򃒶yUΙ[D$53~DEcS !_Ztg!ܚ]p ;{#Ki,)m/zWR0ra||\n6X(0:~G̍عHo[J#DPɄtLu\2t/Ոix<-P22 YoB‹sq4م\_~#|qn =;k}mQ Q9g߯Fђs$pbuPb}_S eB-)B煜(Po# Ůgb8"$PB3`Kݨٛ%bC6wǟ$_?Ya߼XROt /˷.Xmۿ裏;goGµ̝1yI}A"|8'3֓?0$~10f$OH۟ >*2ɩE<5}2E>5':*!'hW"OLO)aPڥ(ODBE6MB}&춨`]|;lZw"FQ ,+/I>.\hkigNOU5®ٻĘCi=2_E 2o5EE.d.")('Ii@"€PNڣ. oX يӁ=s֮b 50-8އDlʲNN<~06Rظ;\9@l_=P~Kg2 Bؠʡ|rqA}-)fۨfë́Y0p,oTVM.t $:).])]$u/'Ex覆T:x cGBEԋm-l_J3Б`Hu1-*3O/PFcIjQJ":ovk?I2dJ}[tt톈hzjV5ݎwc#oܤC+1G+x/5[PBH Cۋk|(D'JݮP&^2mLL߽"`w'Dym& X֬V_|,SU0ݢҝי|MEۙX(O= k 6NB,o펟z䳧YOdWnEx/1OOw,=LԕL`O}ݪ~jFYjH`ubh\h:b`ז-:OWRjNNOU kI(]gg_ħ~@-C8>Q^ < ԣr_WA:Un"ߧ[h' ).%!FQg@{Vա:3ɨInG&a$gW} _ger]Qa#;ֽyں)ьU( S6ea+n9ɛ;b~~!?m*"dԊs>ԉQn٣ A3(C}- d&|&d8׷V7tq7B4ŭQT=2*[uz)=û- )=I\pI !G2 SPt+ufFJSzdwn"Ya}@^K[kr/e])v6a'(HiZ:]$K"ϓ@a&tZr[J;&!۫$N.6<^|cS%UݟzD]_PB&hL9r3(CϜa$=6!\1.DW<1TySУgh8B27Љۧ=EO4[G]t(-{./p*R $uOW1Lit]C * )QKɆET?$Hְ {iX{v.7pЏ sMkj`2Kc{()<]%Y :j&+=E|w`z Xz@*5 hDjW'mT:w 5GH &]X,&b3ϻ/=EAB36]lYNgMN!ňM{-ҩ,xV~ݒ ã[W @eiu`*F]Gf6z_ JP"q1 -uG&d)OTi s̺N^gow%i:}`hE!@%ZW;9}Pv7$N. J*]D"eTd+pYa=Px&7'XEV"tcDֱ*: {(rv;htFQO'2P(jYC}(SC_3C[7=D;;S MzXiygݣ‚ flevմfSK(I{+EDYLݸ5k.N1Q@:]~ 2nRj)a|6S9yC` w72 UMDfrDIglm{j] uY8_i{;sc[r [pp8a(jźl/0aT|L03t¤\Oy.KX3A)x-+|t ֲV~]TR)`bn;MISUoW(E~8z6>ic63Ky;F-5?wd~YY&(}$RbIJi`$†cΒOTr3NgQ,Yg"`ebP(-4Dt2]?3{Q4^ R뷤ĄYT'dgUV@P#TvGk.Ӳ2Va.L(jOlrahȖ{Io~:@kҢ|JYK7Ц6fMa#г Q&ce&uQjiSh * ڿM?^X(zfIQ.$ܤTbILdS<\=e}Uc$MBsY1%{r"FQ!*X15)sI ڝDӵN|ݬbӴ #JwxO_C 2JZ3v1B `CUCIFu$)]jֵ;/ˊ5=p{dGR[q+Dp1b̩l6d2k-tGf@#O+7SWe+0 8s w1 bU`h~֣ٮl#K -lg-u W:՟foDE|־}ݮٝVd끉>O[W(=՚M¾ -ř((:S.а,Cݮ&4Q&Spw+QԣV ` ma&Y:Κ B:6vJ,9?&IayϗV+C1GEUW `N Dk@$%Ȍ%]MY 4' &ٓZ u14 IPQTE}KѳvP9KXd\pd F~LGCQQW+ }|LبZ:MӖBnA=27Σ07*/߹T.N'JDIEO^moNDl)8(1DA=iatcܗ(T5CIԓ‘^.:^,tM_)0ԶbHݛHFIIW4>Ka(3JQn}fSPVUVR"ȍ8+a:s 1Dn~D/{DOfrݟl,j&1D%XQF0[(l|z8%B@ %FIKĨ_dQMQXJ-DI]r$_/b)V/CaT^_Bζm\C9bQsPQR}_v{<Ư*R)hڍrJ>DnG/1 MEU4==76qx0Tˇ"08 v U mY~ }L&O"z\^7?_:FEP2F3z PDQ҆ l`ؘ(2y@Q<[b((/kbC|]V$FFy%% 04e5q\.(W&x]ގnTJ"ѐXޫ]QDh ewJ $()dqU4nD㊳Pؽ):1D% Ȋێ$7gJ_j}XVԱCIQ0%'NJ_-ԋCq$JU}U<''ߡ*PynD79dK~lLg9'FI]sIIU$ ̈&z5R"Q*R QaYp]Z#ef=?%xuX(NҼ?4_|+1F!O+&x=̥gckT"*Zt;XNz:1 tV7aqO KT|t*wѻWS$rBיfե'c^&iƌrCz[E/q@T=Lq//FyW‰b.E %FI >+Տ/XGf[ be:`4m[ 8PkDlb(%FIh& Pi??7=k5=47L](jƊL&]r uH)P.Cr,75{nHjϸWי~m}H%B*o؝u`%wN}z:sʴ0>`q+M )_0pCCl -jsHgp>C_T$=iPifY<{$TkE% W8?55 t|>~zuLC6?30#|Wv5k~>[Lkci|Mj*"ٺVl׮O6ES0;;9s7Lh]E\ ´b:n=VK}*E7zϩXT 7R$umplk&F8P9MS}1~iC_ Z7WnMWF%#"ƏޒS{,13q`GE ? fIC:ku<5#nUa n|́J,Wu6BZWo0(Mߞ."<3Y{6׌PҸ0tR܅Jk)Oʷ+ [D$yg1aޛDD}&Fƞa5+"$9Q,@KE[{ْڑ^iL 2YIψ â˱c. 3~EON&NrH?.9DJ*P݄c/ ~SFB^vEsO~R9|ş b^D&zO 89=Y>N-uIK\ꚑel )at )Q !΄..ҟڏS`ov%\egg. '749WW_ѐ=at &}8O=4&ty}6 8հuHUk7g{Q^~u`^|ӧtDk1:C3v PrU~MKOMz~ V֓=fsv|8{Ãt*dC/*{"7: XCY7#F oTSR 5m^4nGyy0Mң8?8mP5|/Db m1=t1+W5&BPAa_!缪1Vߘ&6˷$e6CǡAWHкzR;ߪ:vC#m(7ZG\NoM?iOqvt\~h4!kKmh6PTۮj}ղneZᵢxvFuS׋j¿OMO5vɶ_Pᵢ:~^ [I1Y~lǏ"G6FE'===M %Ljm踘xC Qm 5syŲm*FG4TiGϊ&h%:2TVgE2D P(j"FTz=(EV *c'. cRTG] kemq$x%*VZѶz` ]~es Xv5>55 P C .yEjdhւT\Fqq,aܩ$\.U3at :?Br*;wN:ztL^c)zcqgc11w^}hINlZn:wWjq(4772bbD%6Ľ\wI}-:eYh?0Z'y:0؊=|2F'iQKdH3fy"(c#;N F!}Okh.DHɖ9WH-wۢޭ׆qϮzm;-DcGl6{mO(S~`4ܺuS庮B׶~4X iщ[aԽlē'FIh{M^$R۽cxj`.: 524m"A|#5IAW$h (Kc-kug/Ţ* ET"*kGƨߌb:94M<2 e? D Vx¶ ]VtDT4XGJViun;1~Ұ;at'T-Tw}K3=;qE0vx|TU$R99P999))x'ty>#Z |[bi3hU 0E>?du~ݫ'EQR}[:}+(>fl'=c@o:\#ՠy3.fx9An"1%@h0JjZb 0鶅 T|{Pdp2 bViZdN$F~91dSm߱xQVZ mr KvKa4dKboDVd(oQnNe7#uϻ/@ʐJ" SBy*_0v+„^|2JTH$h8r'HHbta Oid$@/UvGG0Gf2UIc"((vZ% 2Y_)hvnضL&4DH/ y?IENDB`openmcdf-1.5.4/src/TestFiles/report.xls0000644000175000017500000004100011546163204017717 0ustar mathieumathieuࡱ>  Root Entry F;~EWorkbook3SummaryInformation(DocumentSummaryInformation8 \pBLASEOTTO FEDERICO Ba==F8X@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri3" "\ #,##0;\-" "\ #,##0=" "\ #,##0;[Red]\-" "\ #,##0?" "\ #,##0.00;\-" "\ #,##0.00I"" "\ #,##0.00;[Red]\-" "\ #,##0.00q*6_-" "\ * #,##0_-;\-" "\ * #,##0_-;_-" "\ * "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-,>_-" "\ * #,##0.00_-;\-" "\ * #,##0.00_-;_-" "\ * "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                                                                       ff + ) , *     P  P        `            a>  ||>gK}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}+ _-;_-* "}-}, _-;_-* "}-}- _-;_-* "}-}. _-;_-* "}-}: _-;_-* "}-}; _-;_-* "}A}1 _-;_-* " * #}A}2 _-;_-* "? * #}A}3 _-;_-* "23 * #}-}4 _-;_-* "}A}0 a_-;_-* " * #}A}( _-;_-* " * #}A}7 e_-;_-* " * #}}5 ??v_-;_-* "̙ * #0_-; * " ;_-@ }}9 ???_-;_-* " * #???0_-;??? * " ???;_-@ ???}}) }_-;_-* " * #0_-; * " ;_-@ }A}6 }_-;_-* " * #}}* _-;_-* " * #???0_-;??? * " ???;_-@ ???}-}= _-;_-* "}}8 _-;_-* " * #0_-; * " ;_-@ }-}/ _-;_-* "}U}< _-;_-* " * #0_-;}A}" _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}# _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}$ _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}% _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}& _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}  _-;_-* "23 * #}A}' _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}! _-;_-* "23 * # 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L湸 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ٗ % 60% - Accent3M( 60% - Accent3 23֚ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleLight16`%/Sheet10Sheet2a2Sheet3'hello . ccB  ;0u0  dMbP?_*+%,&ffffff?'ffffff?(?)?",B333333?333333?t&<3U, ">@ggD  1  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  s3  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  Oh+'0@Hd BLASEOTTO FEDERICOBLASEOTTO FEDERICOMicrosoft Excel@f`@rq՜.+,0 PXd lt| - Sheet1Sheet2Sheet3  Worksheets F&Microsoft Office Excel 2003 WorksheetBiff8Excel.Sheet.89qCompObj ropenmcdf-1.5.4/src/TestFiles/reportREAD.xls0000644000175000017500000004100011546167610020360 0ustar mathieumathieuࡱ>  Root Entry F` amWorkbook3SummaryInformation(DocumentSummaryInformation8 \p-LASEOTTO FEDERICO Ba==F8X@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri3" "\ #,##0;\-" "\ #,##0=" "\ #,##0;[Red]\-" "\ #,##0?" "\ #,##0.00;\-" "\ #,##0.00I"" "\ #,##0.00;[Red]\-" "\ #,##0.00q*6_-" "\ * #,##0_-;\-" "\ * #,##0_-;_-" "\ * "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-,>_-" "\ * #,##0.00_-;\-" "\ * #,##0.00_-;_-" "\ * "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                                                                       ff + ) , *     P  P        `            a>  ||>gK}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}+ _-;_-* "}-}, _-;_-* "}-}- _-;_-* "}-}. _-;_-* "}-}: _-;_-* "}-}; _-;_-* "}A}1 _-;_-* " * #}A}2 _-;_-* "? * #}A}3 _-;_-* "23 * #}-}4 _-;_-* "}A}0 a_-;_-* " * #}A}( _-;_-* " * #}A}7 e_-;_-* " * #}}5 ??v_-;_-* "̙ * #0_-; * " ;_-@ }}9 ???_-;_-* " * #???0_-;??? * " ???;_-@ ???}}) }_-;_-* " * #0_-; * " ;_-@ }A}6 }_-;_-* " * #}}* _-;_-* " * #???0_-;??? * " ???;_-@ ???}-}= _-;_-* "}}8 _-;_-* " * #0_-; * " ;_-@ }-}/ _-;_-* "}U}< _-;_-* " * #0_-;}A}" _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}# _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}$ _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}% _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}& _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}  _-;_-* "23 * #}A}' _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}! _-;_-* "23 * # 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L湸 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ٗ % 60% - Accent3M( 60% - Accent3 23֚ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleLight16`%/Sheet10Sheet2a2Sheet3'hello . ccB  ;0u0  dMbP?_*+%,&ffffff?'ffffff?(?)?",B333333?333333?t&<3U, ">@ggD  1  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  s3  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  Oh+'0@Hd BLASEOTTO FEDERICOBLASEOTTO FEDERICOMicrosoft Excel@f`@rq՜.+,0 PXd lt| - Sheet1Sheet2Sheet3  Worksheets F&Microsoft Office Excel 2003 WorksheetBiff8Excel.Sheet.89qCompObj ropenmcdf-1.5.4/src/TestFiles/report_name_fix.xls0000644000175000017500000004100012061073174021564 0ustar mathieumathieuࡱ>  Root Entry F;~EWorkbook3SummaryInformation(DocumentSummaryInformation8 \pBLASEOTTO FEDERICU Ba==F8X@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri3" "\ #,##0;\-" "\ #,##0=" "\ #,##0;[Red]\-" "\ #,##0?" "\ #,##0.00;\-" "\ #,##0.00I"" "\ #,##0.00;[Red]\-" "\ #,##0.00q*6_-" "\ * #,##0_-;\-" "\ * #,##0_-;_-" "\ * "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-,>_-" "\ * #,##0.00_-;\-" "\ * #,##0.00_-;_-" "\ * "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                                                                       ff + ) , *     P  P        `            a>  ||>gK}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-}  _-;_-* "}-} _-;_-* "}-} _-;_-* "}-}+ _-;_-* "}-}, _-;_-* "}-}- _-;_-* "}-}. _-;_-* "}-}: _-;_-* "}-}; _-;_-* "}A}1 _-;_-* " * #}A}2 _-;_-* "? * #}A}3 _-;_-* "23 * #}-}4 _-;_-* "}A}0 a_-;_-* " * #}A}( _-;_-* " * #}A}7 e_-;_-* " * #}}5 ??v_-;_-* "̙ * #0_-; * " ;_-@ }}9 ???_-;_-* " * #???0_-;??? * " ???;_-@ ???}}) }_-;_-* " * #0_-; * " ;_-@ }A}6 }_-;_-* " * #}}* _-;_-* " * #???0_-;??? * " ???;_-@ ???}-}= _-;_-* "}}8 _-;_-* " * #0_-; * " ;_-@ }-}/ _-;_-* "}U}< _-;_-* " * #0_-;}A}" _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}# _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}$ _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}% _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A} _-;_-* "23 * #}A}& _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}  _-;_-* "23 * #}A}' _-;_-* " * #}A} _-;_-* "ef * #}A} _-;_-* "L * #}A}! _-;_-* "23 * # 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L湸 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ٗ % 60% - Accent3M( 60% - Accent3 23֚ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleLight16`%/Sheet10Sheet2a2Sheet3'hello . ccB  ;0u0  dMbP?_*+%,&ffffff?'ffffff?(?)?",B333333?333333?t&<3U, ">@ggD  1  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  s3  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD  Oh+'0@Hd BLASEOTTO FEDERICOBLASEOTTO FEDERICOMicrosoft Excel@f`@rq՜.+,0 PXd lt| - Sheet1Sheet2Sheet3  Worksheets F&Microsoft Office Excel 2003 WorksheetBiff8Excel.Sheet.89qCompObj ropenmcdf-1.5.4/src/TestFiles/testbad.ole0000644000175000017500000005033411560531162020013 0ustar mathieumathieuࡱ>   !"#$%&'Root Entry F@ Ole10NativeFCompObjL F OLE PackagePackage9qtestbad.jpgC:\tmp\testbad.jpg8C:\Users\ROBIN~1.GUE\AppData\Local\Temp\testbad (2).jpgEFtestbad.jpgC:\tmp\testbad.jpg8C:\Users\ROBIN~1.GUE\AppData\Local\Temp\testbad (2).jpgEGIF87a'^G,'^0I8ͻ`(dihlp,tm߸ܻ?Zp6cGXt5[OVt5UUSWT5uKߜxL.zn|N~~a#"! :<>@ҕBDFHJLNPR*0pBA $\xJC,D2KE02jȱǏ CIɓ(S\ɒK/I͛8sɳfL{ J E*]QPtjPUTe^ݪ4B`z Kزh\'ʝKݻx; LÈ ǐ#KL˘%3U MӨS^]zs+b˞M۸sZ Nȓ-УK<سk/PS_We^Ͼ{͜=ي^rh˭"ނ _E(a 5hqv{ ("dYg"},a0(l"h8!<.a@Nxs=9ݏA&iއ#6e%x-Vij/Ψ喹՘`"d>hbWeRiƩ&AOigdUHgW駋q)蠳!案 6V:AfJRwvdzNiП^rih>鬓+AʩjY*16j+iB g+}"k2.V[fI^re~VZ[&l\ J۰Bff'; F$J(/Ld>l1/s1Y%p)3,4,aG7݌: V> tMF7cP,3]tN=uVoun^nh1ݷj~no wVM7vߍlz3mD`Π+x.xT?^j䒧Jy^C풶M:~Ǫkh${f_zέ/z~/heMг߻AJ\}ǏK7/=#w@ѯ~ GA*Ri`vAP~$8AUЂ`]A.˃ /wn+'p7ٛm#Dm=[Ză6qUM{-| W}6rQ9y^79~9#c,=q(^bב2:j38:o{Ύ=/f*OI׿v#|IRGٟlu-}Orj޵3㹳}潋9j`%{t0^Ύ73#OKuCzkymG\?xwѓ+ur:CO߲qs_O&|De:|X3ߠV5}>mۗ }}ExL곺f|Δ~wCdzAq~{#Dd*&~1}%s4j7|KA,6!X~`vַԂၪDg㦃f#2_F}H7JrL8mNRPOu%gMq!x]8ucxQ<uReh@AҀ3zDu9Zfl(4hRH{ VpϦKȈlhᓅG7Ƿhpa(TxXHzXMw;8gi_ny3x**%4Xn腳YǨvPe u`8B^98oŇE_iHVȆIgT֎n Csx\7S(H((86[Hȉ'l TIF8g樒'sxn9&iYQDH.r`U!i#)(y>xsUI\EaG9ZI)xKP%)?)=SYXUy]WcYi[[e]%M&acFyQx0I:2?m_o%q K(ivg^w?C&9sّ&a5\x@ |BF[FIvvY9;U@Bk-m[q-ŽL$zדѕ|p5=I\KSցΘ֫פz6s}u͖͌-틢Τ=ΰӠڑ$ #fjXٺ<0" Lа3~C٧x֩Hπ|7LG]ە]R=y=Kƽ۲ۣ J)^y`i۾xݸ-I\ipi \iKjnႩN9pm=6
╉&/ j⢊㓩rD ,O䴪I޼䄉lˬMٌb f>0tml& )InLD.$l~>^.jJ(ɇ( k韻⭋N..Wc~ nzL뽹tD*M%n졋쀪p+J$m팎*Z~Pyn؇] 'S/{-&о=.Ȱ^-Σ^.>W6 &3NZ k7}|]IrjAJjw& *Jj#9G *JjKNJGj ^Y? +:I'kv P,^:j *+ ,KkZ7{X(Jj!~͗>_0ߝ/ŔbGJjފۢɦ߼ϲOޏ>x]Cm0^ |0 4O4UWuѐgow|^/|EBCı7>Fǹ7IJ ?MNOQҙTUV.XY[\^>AA]cEefxiNk%%BsϠiެܺ&]܂frcݼ8/]* _ƻ7ڝrIy{' }+N^wPڏ mF=|>ed:D?75ܰ1OѐsA"\mB _B2QKdBGuATqEZt11cllF%[rR*? 4A$SrItR-(|kJ.;j8NӸ#4=4_n s:8lcN>S*<}tHwO3 IMDjsѣy2@b*]U(\} N%B"RKTT7Ru3 uMDYWO5&Oy_ VXs-6c%L=I\=[0MqWks6[o-E};t/Uw]`G~:7uji^zMV.~  2 r#vi@",%4Qx(/%~KSe(BqXRQޖQ&|m6 gtu܁8hhN~駛YDz\X&9n[0fiy@;%f ŵK!Xů{oJap§+ݶ[ZRG/#r!'/$s%n}ĝe]Q}uvu bks܍0||^K ^ t;Gߤy=qޑ;tSԔ-}U!~ꗐk _ǷPO`}.~'$. js` BP \ Btχq %P>sCZj6EшHLb &#*pxg_. cƘ2才uI#"GpNPCPg82+} 74A|"KQG}$B̔va?Ng'$"69qV|*]?^c& 9ځs)Kq@L3Ԝ2 9G,4gjZs+r%EnΛi'y#rfƜCgV iy>`3f@zw&LKʶj'%:ADFeQubZH4R{DTD6_R 4b2Mmft7%5NiMR*-^ QB3QTWl]UXYV|5&) 6.Rk-)]J-۫+FUEb/B=vMEd;_vu]gYMuieʠVrMeX,* d$ȶn^~bvtk۱Eɥrܹ<\ISZQ 7y-n'i[޲pkBBɂİJ{o]xFLwFJucz4FQl_ ½ZWa^eІa|8&܈)VZwZ*vD5i:*pi<`/52l`|t <{,# Y͗d 4a蘭Uf1W/_vg=+r/L?: g``<)@OJgcf~8Wv3QF^+Lt;I)Slkjp6,)jNZP6P Uتv -Rv]Ӭ׾}mu[]m$}I}6=6՚'<68-n[J.1wm:" j4ѻ7}{ ^0)F>A:#{PKӓ2\yOZnG& j \uU9{2pYܮ#_չm1{EPχrK@Zo-mu\;?#-!OhWQ܋ImfHUG󒙼t"4Ch#<SԼF9?eϗn;ƚ1`:]!Ԗ?@8B ClDE05H|IdA;D1l2=*))*,D:+P"T;KдZТjУzзrO]OZOOPPQ"р4aCSbQqѵѶQʢ*PF B -5 + +!,"=-#.H6?-(u)*+ͭ,կ-.-/m01 23-4u75&E'ŵ:;<= >U?}@AB]CEDUEeFů5 JR@]qTFTo{TT!DUOScU sUU(U*UXUVV#3V:DV.UV4 ePRxV-VU|WvLy.z;{53| T,(muS=HoFpMz d۟D{Z#[4[Tt>շP4KEA]?->7]5N}J%ȝJ}΂5] UэGMݳƕUʽQf\,<<{]J ݭ}օ=Mŷ}em OUվ5:M6]ZYꕹ5C=M]-eۥЖ|ߗ_ޜ_XYX8߻IފߋZ m ;`___;ࠤ` s8N;^=Q>UvRA? ;9)*6:NKLu/XaW[ג=}L7܍%%&'>,!"J6ٕYՄ01^Dcb,`]sd{`xM:LOMJNn26c!`"S8F$&:;AO<ۤ宵e>TNU^V`=PfTTSܝebf[Xd! 4fgFh7 fMYt u>@vYweKjg$$ c/BY z>{/N}^\wxy>8@&DKKJΗfLNU>GfËFf>c~VSКՒ^ѓθjŸܠF\&vĤfFŐS\6CD\j7lj8[fJ5.kChyjtkAniЌi9={eXyjj"UlFfmj̽|iQdk@kJd\Zkj$1&c03vw4kUmlZmImS4eEcVl1ncnR$n\$mEnUnf~ZSFEVf9mfn tod,m4nhUfovo(}l״nnnUoH_GGg_Xl~gpœ~pppufd6gooqEgMe3r%$Dq kHur%%rUqe&s^6ErDtUra$lt moegpFo]Vteuus#v2 r-$4rZttcu u uVfu?t$J6lEWgvwMvĴv.s"FtuuEWbtQeYww@w%4wDw4O⩎`/nUwExTdBwxҽגgW]gbw]LOOQCQByGݔ_pgeЗΘwЙњћ7W6ܕ㖇ޠ?ϡТ/ң?>yw yhy)zz,z-Mz_{y{2%5!E{5U{6ew{/{{;{+{>K6uAF;'|6|DE U|4e|Gu|bLŶ|M|*|=|P{Qp뇁vHT}3e}FuY|Ԧfm}w}Obk}&}U6z}#Y'׺/oV7;?0( :#2\2'4:(:+6r/8|m 3:^7P;>? a!b"c#Nd%fJg' e褥ɜ*kk,mY.o/`0#r2!3tM)*5u'g6t5x+y5̭:;V/|[BvF$;8d -~b IQJUZw%rif]zi0&"I~Vf@&PqIu.u'v'U}* 0Anj49dJBu))i:z j6SBIaѭʪ[Q:Ek'&Ԋe[!A^i{hY$B`m㶊Ynf̴&' j/QK#C 0K BÝd[+c_6dz#AO0;&l8<Ϲ|ʒ5r J$s uOYVg5n}5 cRdOo c܍$L3-qҁG18ᶬ}+nd^띺}.y|:^RܦKKޑd_fnBFλG#+( 7{N3{q=Ro_Lޏ<7y .}S ڻ_f/ktӁ>u`}2 MpAlCx &lU8"3Y Y3Ë0ɡy7C&"kMaxD911P X1A̢[Q0bD]xP iT7 1ؕ;zqG5~T!HC='T:HoS$. Lzo~eL>)@QX){Jީ~+uni\p~i`ie(SsT3Iqd5MG"sW|clHUQp)s))&厩mv3pߌ_8نOj?fS}D;= BP=EUD5΁zf;h>jNm!+N u Ԣ4 Nilz0HJ)WӋTiDQT)]LmSը?(.*'YkeThGe5]r]cW[) 3m*Ij,(aYqկHl,+)J{ljFІ6{WiM[y֣gREm˫n0ruYnMڙNk<s]Ncu]!m0ms+jֳUloq[Nˈ T{VV:/һc1@iUaIG&a!o}#k*N<gÊj1xY>Ƭ1p"X1l[!ώ2򣐼%&BW|<)?VP,'-k2p(3ЬQ5Wq3,`EY5xw|Α{"s읩̺Nymt7{4,BG~t]=6+KmU ӊ5`iOQo~kaĝ]fZoߢ1hl4";c u?4gF>m^ۂV%mo/2撹mi;ژ[kl.e;@=o ϩ|{ͻpI//Sek+9V+~q95|p\;Sk -'=˸ #r<\=[ 0f2kߤGtnDoAOuoj\W h=chu;ݠmasgjwmw2}i-t#n.|Uޤ/rzM|2RϓS=Ak>eԅ3s*yn ;M;`~n;* Ǽʢ*s;.nOɍ>n ]ؾ-OOx=f/WaQ _tM_)O SП(`1x9`Uߦ]d)Y pa  AQ 6  ړ R Y6 RWƈzX~ 1^\ Z^SD z ["TZ~ ྕ!1muy_aVfa9"AI"Q" =p>!% Ĉ"ޙbѡb"pq)٢"ᢌb[|!"E!0")}1Q8c/b= ca9jrnycQu&F45J,z: ;ڠ;JW /Z4= &R. :_)[AAbRBBrRCCv`DJDRi dQe$/m*u&}+$1##$y$V3/d+ɤ05$ Z$d[8e4 %0e5:!%C*e1e9eeABI%9Q4Y%:a?$Dz垁ej%He>9e?D%Ie#$JV]$Y%W&j"9*C> W"_&eI`$<: b&-5?]j%HF&%uL%C>%D%JMVVf]&EfQHɦCѦINLfyV gM'IgN!St>tu&uv6vUl\zzޕs'dag*&=c@"&h$i"}*܁w^xxVyry.{U|2nn6 orX^քUzՅ*VFr%s|rE<8ωUv֊~wƨͨa֨.ܨhejR`FNbوVV"֎Vֆ.g~`fYZזfVn֗rW֘hZbٚW֛VלzW^›"^͟bWjנXW"Ꝣa.YFXפW^إXejܢ\ߣӨNXVةX^ݪݫf]2ܬnG&*jjE>i(vj7~**ժ)19jmQ;Zm~:6F'v^ݰX.ٻ~Y&#~bn \&ٿvYYڽ`fa]nnYľ^Ii¶aǺ]ȶ!.cn:dƪΊdǎ^YZɮZŶڦ^ba6N^Jgh"C˼Y~eՖhK.Ӫᔖbj_2۱":Bebfeb_ + e,Z#-j. 6"0HQ.Yn_b(jn1rsz~ -"K|*mW.}&~~r&햛 ަ"#ʨobb'銟'w6or&n .ob/$ml~cԾiڢ\"Jff &/%w/ϙ.. :00&/Am/IQQfJ&+2] &0fpeo=/XpnIpo11Ͱհʑ00 ";n'd3{o11{/o 1*&bCCs[[i_"gg]h& /Fnr v 2r#[\(o g);)*+Q8n8u,0홸g272o%/~ 3}=2Fs"su2"q3ysղ12r*p s>3%z1:: +;?3@!=(7'>+>o#C+,D!8gAqifrO24U,f44o w4ڴ4?tAS !,0\4*5VnN:uXB5!Ju+u'NVtBBkCGs_ 45bjrz\*8+5޲ vvkv/)v0ꪵ%55eeG'ff!ggC/hwh,SGigjkjbk;&c"Rddii3l\knnG#^oW#_pt[{lr;rds#s^tcpsZwqsqvlwGwsdxKxCuku}zSm{{e||Oc}~k~kclv me{c( hm[턿fO+$Au$Zul6*smCh牗e{xOԋGvOtOgd Tw7mfSyӔwvKhWeNwz;m3n6kFgvhֹ9y6sd82"h+.Ǯ 3U7Uk͇kls짗o;n30Ӌ /p.bhlo:{5_o:-{^6?{G{O{qn'13y;+4S2g]ql»Ͼ:϶;);+h=|q <|#Ǒ~'\>>>??'/?7??GO?W_?go?w???/;~QB$aֿ?9if7 2A ˱<c?"H0e1y.-p:]p[FJvsJ$U&!S'$c 1بK*϶z*bJ|@B1K7v# 3«0l0рKMxӄzê-jR!䲲lGYGRfH5MSLl*u-2M=TU]V]}XeZm\uݕ^}`b=d]f}vT;tOTjK.[k5[lr=t]v}x块z|ݗ~i";7C:\Users\ROBIN~1.GUE\AppData\Local\Temp\testbad (2).jpg testbad.jpgC:\tmp\testbad.jpgopenmcdf-1.5.4/src/TESTOpenMCDF/0000755000175000017500000000000012061101742016002 5ustar mathieumathieuopenmcdf-1.5.4/src/TESTOpenMCDF/app.config0000644000175000017500000000156311546170036017767 0ustar mathieumathieu
True openmcdf-1.5.4/src/TESTOpenMCDF/img/0000755000175000017500000000000012061101742016556 5ustar mathieumathieuopenmcdf-1.5.4/src/TESTOpenMCDF/img/disk.png0000644000175000017500000000115411615212602020221 0ustar mathieumathieuPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT=]Uе;C`DD Ve h*B"6VV"c vba"NB ޙ{~jo|x&,K*o=]߻_v<}tu.R[Ҝ9Xsy|o~ |u_Bk6Yf7bQڴ^Й<T::֊Ǧ7Uݕuwٞ ,6}4'kٞF%*T⏿NHZ'}&NGۯ_鋾ﴌ*UD:tHُZkƦ/F%8A#хJpFQ$Q5:H0' QEDDK$e]t4IJDUS*!BxKOƈ5Hup9Q pҫj'ɬ4nx93W3j7˜oO$*yHI$H=m?!|nmIENDB`openmcdf-1.5.4/src/TESTOpenMCDF/img/door_out.png0000644000175000017500000000126012042056242021120 0ustar mathieumathieuPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<BIDAT8˕kAfI Dh AP+-?NAXYW4>( dgc,VYltrÙsӗ.ƙw6XY)zƲglEOd ʺM6g^ Y}}}Z4;yL"9;"JLﭘfk\_B:fՄ`pM@ R }ʖ鱵8bWUqI& o+0:4kp'⊘p&u6)|ѡjK\*AI*"]?];O[iES;؛o={LJTTKDK"u\#&눩Ň#eTf0uL 7'1cILxKK{ Z#L#!qwTSSd-!$6qIP'vj@ =  L yzz֣gEI[ş5>y-w8u0a3r\9j~Mw{ͽIENDB`openmcdf-1.5.4/src/TESTOpenMCDF/img/folder.png0000644000175000017500000000103111615212602020534 0ustar mathieumathieuPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œA[EtQg7wALCWA0P017p2=:A3cb2'pܪ$Vme@ ݬ2OTO1W/`z8% ;O;9P#9B}^nO;Ǫo~ d~E dpɳ__j` @]Fmww   5 8]%~[)1 .4X:Z}& $;0iE^.6~`^dcbH_ bX-L@W^_IENDB`openmcdf-1.5.4/src/TESTOpenMCDF/InputBox.cs0000644000175000017500000000366311546170054020122 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; namespace StructuredStorageExplorer { class Utils { public static DialogResult InputBox(string title, string promptText, ref string value) { Form form = new Form(); Label label = new Label(); TextBox textBox = new TextBox(); Button buttonOk = new Button(); Button buttonCancel = new Button(); form.Text = title; label.Text = promptText; textBox.Text = value; buttonOk.Text = "OK"; buttonCancel.Text = "Cancel"; buttonOk.DialogResult = DialogResult.OK; buttonCancel.DialogResult = DialogResult.Cancel; label.SetBounds(9, 20, 372, 13); textBox.SetBounds(12, 36, 372, 20); buttonOk.SetBounds(228, 72, 75, 23); buttonCancel.SetBounds(309, 72, 75, 23); label.AutoSize = true; textBox.Anchor = textBox.Anchor | AnchorStyles.Right; buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; form.ClientSize = new Size(396, 107); form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); form.FormBorderStyle = FormBorderStyle.FixedDialog; form.StartPosition = FormStartPosition.CenterScreen; form.MinimizeBox = false; form.MaximizeBox = false; form.AcceptButton = buttonOk; form.CancelButton = buttonCancel; DialogResult dialogResult = form.ShowDialog(); value = textBox.Text; return dialogResult; } } } openmcdf-1.5.4/src/TESTOpenMCDF/lib/0000755000175000017500000000000012130475036016557 5ustar mathieumathieuopenmcdf-1.5.4/src/TESTOpenMCDF/MainForm.cs0000644000175000017500000003445312042056226020057 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using OpenMcdf; using System.IO; using System.Resources; using System.Globalization; using StructuredStorageExplorer.Properties; using Be.Windows.Forms; // Author Federico Blaseotto namespace StructuredStorageExplorer { /// /// Sample Structured Storage viewer to /// demonstrate use of OpenMCDF /// public partial class MainForm : Form { private CompoundFile cf; private FileStream fs; public MainForm() { InitializeComponent(); //Load images for icons from resx Image folderImage = (Image)Properties.Resources.ResourceManager.GetObject("storage"); Image streamImage = (Image)Properties.Resources.ResourceManager.GetObject("stream"); treeView1.ImageList = new ImageList(); treeView1.ImageList.Images.Add(folderImage); treeView1.ImageList.Images.Add(streamImage); saveAsToolStripMenuItem.Enabled = false; updateCurrentFileToolStripMenuItem.Enabled = false; } private void OpenFile() { if (!String.IsNullOrEmpty(openFileDialog1.FileName)) { CloseCurrentFile(); treeView1.Nodes.Clear(); fileNameLabel.Text = openFileDialog1.FileName; LoadFile(openFileDialog1.FileName, true); canUpdate = true; saveAsToolStripMenuItem.Enabled = true; updateCurrentFileToolStripMenuItem.Enabled = true; } } private void CloseCurrentFile() { if (cf != null) cf.Close(); if (fs != null) fs.Close(); treeView1.Nodes.Clear(); fileNameLabel.Text = String.Empty; saveAsToolStripMenuItem.Enabled = false ; updateCurrentFileToolStripMenuItem.Enabled = false; propertyGrid1.SelectedObject = null; hexEditor.ByteProvider = null; } private bool canUpdate = false; private void CreateNewFile() { CloseCurrentFile(); cf = new CompoundFile(); canUpdate = false; saveAsToolStripMenuItem.Enabled = true; updateCurrentFileToolStripMenuItem.Enabled = false; RefreshTree(); } private void RefreshTree() { treeView1.Nodes.Clear(); TreeNode root = null; root = treeView1.Nodes.Add("Root Entry", "Root"); root.ImageIndex = 0; root.Tag = cf.RootStorage; //Recursive function to get all storage and streams AddNodes(root, cf.RootStorage); } private void LoadFile(string fileName, bool enableCommit) { fs = new FileStream( fileName, FileMode.Open, enableCommit ? FileAccess.ReadWrite : FileAccess.Read ); try { if (cf != null) { cf.Close(); cf = null; } //Load file if (enableCommit) { cf = new CompoundFile(fs, UpdateMode.Update, true, true, false); } else { cf = new CompoundFile(fs); } RefreshTree(); } catch (Exception ex) { treeView1.Nodes.Clear(); fileNameLabel.Text = String.Empty; MessageBox.Show("Internal error: " + ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// Recursive addition of tree nodes foreach child of current item in the storage /// /// Current TreeNode /// Current storage associated with node private void AddNodes(TreeNode node, CFStorage cfs) { VisitedEntryAction va = delegate(CFItem target) { TreeNode temp = node.Nodes.Add( target.Name, target.Name + (target.IsStream ? " (" + target.Size + " bytes )" : "") ); temp.Tag = target; if (target.IsStream) { //Stream temp.ImageIndex = 1; temp.SelectedImageIndex = 1; } else { //Storage temp.ImageIndex = 0; temp.SelectedImageIndex = 0; //Recursion into the storage AddNodes(temp, (CFStorage)target); } }; //Visit NON-recursively (first level only) cfs.VisitEntries(va, false); } private void exportDataToolStripMenuItem_Click(object sender, EventArgs e) { //No export if storage if (treeView1.SelectedNode == null || !((CFItem)treeView1.SelectedNode.Tag).IsStream) { MessageBox.Show("Only stream data can be exported", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } CFStream target = (CFStream)treeView1.SelectedNode.Tag; // A lot of stream and storage have only non-printable characters. // We need to sanitize filename. String sanitizedFileName = String.Empty; foreach (char c in target.Name) { if ( Char.GetUnicodeCategory(c) == UnicodeCategory.LetterNumber || Char.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter || Char.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter ) sanitizedFileName += c; } if (String.IsNullOrEmpty(sanitizedFileName)) { sanitizedFileName = "tempFileName"; } saveFileDialog1.FileName = sanitizedFileName + ".bin"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { FileStream fs = null; try { fs = new FileStream(saveFileDialog1.FileName, FileMode.CreateNew, FileAccess.ReadWrite); fs.Write(target.GetData(), 0, (int)target.Size); } catch (Exception ex) { treeView1.Nodes.Clear(); MessageBox.Show("Internal error: " + ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { if (fs != null) { fs.Flush(); fs.Close(); fs = null; } } } } private void removeToolStripMenuItem_Click(object sender, EventArgs e) { TreeNode n = treeView1.SelectedNode; ((CFStorage)n.Parent.Tag).Delete(n.Name); RefreshTree(); } private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) { saveFileDialog1.FilterIndex = 2; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { cf.Save(saveFileDialog1.FileName); } } private void updateCurrentFileToolStripMenuItem_Click(object sender, EventArgs e) { if (canUpdate) { if (hexEditor.ByteProvider != null && hexEditor.ByteProvider.HasChanges()) hexEditor.ByteProvider.ApplyChanges(); cf.Commit(); } else MessageBox.Show("Cannot update a compound document that is not based on a stream or on a file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void addStreamToolStripMenuItem_Click(object sender, EventArgs e) { string streamName = String.Empty; if (Utils.InputBox("Add stream", "Insert stream name", ref streamName) == DialogResult.OK) { CFItem cfs = treeView1.SelectedNode.Tag as CFItem; if (cfs != null && (cfs.IsStorage || cfs.IsRoot)) { try { ((CFStorage)cfs).AddStream(streamName); } catch (CFDuplicatedItemException) { MessageBox.Show("Cannot insert a duplicated item", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } RefreshTree(); } } private void addStorageStripMenuItem1_Click(object sender, EventArgs e) { string storage = String.Empty; if (Utils.InputBox("Add storage", "Insert storage name", ref storage) == DialogResult.OK) { CFItem cfs = treeView1.SelectedNode.Tag as CFItem; if (cfs != null && (cfs.IsStorage || cfs.IsRoot)) { try { ((CFStorage)cfs).AddStorage(storage); } catch (CFDuplicatedItemException) { MessageBox.Show("Cannot insert a duplicated item", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } RefreshTree(); } } private void importDataStripMenuItem1_Click(object sender, EventArgs e) { string fileName = String.Empty; if (openDataFileDialog.ShowDialog() == DialogResult.OK) { CFStream s = treeView1.SelectedNode.Tag as CFStream; if (s != null) { FileStream f = new FileStream(openDataFileDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] data = new byte[f.Length]; f.Read(data, 0, (int)f.Length); f.Flush(); f.Close(); s.SetData(data); RefreshTree(); } } } private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (cf != null) cf.Close(); } private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { } private void newStripMenuItem1_Click(object sender, EventArgs e) { CreateNewFile(); } private void openFileMenuItem_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() == DialogResult.OK) { try { OpenFile(); } catch { } } } private void treeView1_MouseUp(object sender, MouseEventArgs e) { // Get the node under the mouse cursor. // We intercept both left and right mouse clicks // and set the selected treenode according. TreeNode n = treeView1.GetNodeAt(e.X, e.Y); if (n != null) { if (this.hexEditor.ByteProvider != null && this.hexEditor.ByteProvider.HasChanges()) { if (MessageBox.Show("Do you want to save pending changes ?", "Save changes", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { this.hexEditor.ByteProvider.ApplyChanges(); } } treeView1.SelectedNode = n; // The tag property contains the underlying CFItem. CFItem target = (CFItem)n.Tag; if (target.IsStream) { addStorageStripMenuItem1.Enabled = false; addStreamToolStripMenuItem.Enabled = false; importDataStripMenuItem1.Enabled = true; exportDataToolStripMenuItem.Enabled = true; } else { addStorageStripMenuItem1.Enabled = true; addStreamToolStripMenuItem.Enabled = true; importDataStripMenuItem1.Enabled = false; exportDataToolStripMenuItem.Enabled = false; } propertyGrid1.SelectedObject = n.Tag; CFStream targetStream = n.Tag as CFStream; if (targetStream != null) { this.hexEditor.ByteProvider = new StreamDataProvider(targetStream); } else { this.hexEditor.ByteProvider = null; } } } void hexEditor_ByteProviderChanged(object sender, EventArgs e) { } private void closeStripMenuItem1_Click(object sender, EventArgs e) { if (this.hexEditor.ByteProvider != null && this.hexEditor.ByteProvider.HasChanges()) { if (MessageBox.Show("Do you want to save pending changes ?", "Save changes", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { this.hexEditor.ByteProvider.ApplyChanges(); } } CloseCurrentFile(); } } } openmcdf-1.5.4/src/TESTOpenMCDF/MainForm.Designer.cs0000644000175000017500000004346312042056222021613 0ustar mathieumathieunamespace StructuredStorageExplorer { partial class MainForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); this.treeView1 = new System.Windows.Forms.TreeView(); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); this.importDataStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.exportDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addStorageStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.addStreamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); this.menuStrip1 = new System.Windows.Forms.MenuStrip(); this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openFileMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.newStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.closeStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.updateCurrentFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openDataFileDialog = new System.Windows.Forms.OpenFileDialog(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.fileNameLabel = new System.Windows.Forms.ToolStripStatusLabel(); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); this.splitContainer2 = new System.Windows.Forms.SplitContainer(); this.hexEditor = new Be.Windows.Forms.HexBox(); this.contextMenuStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout(); this.statusStrip1.SuspendLayout(); this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel2.SuspendLayout(); this.splitContainer1.SuspendLayout(); this.splitContainer2.Panel1.SuspendLayout(); this.splitContainer2.Panel2.SuspendLayout(); this.splitContainer2.SuspendLayout(); this.SuspendLayout(); // // openFileDialog1 // this.openFileDialog1.Filter = "Office files (*.xls *.doc *.ppt)|*.xls;*.doc;*.ppt|Thumbs db files (Thumbs.db)|*." + "db|MSI Setup files (*.msi)|*.msi|All files (*.*)|*.*"; this.openFileDialog1.Title = "Open OLE Structured Storae file"; // // treeView1 // this.treeView1.ContextMenuStrip = this.contextMenuStrip1; this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill; this.treeView1.HideSelection = false; this.treeView1.Location = new System.Drawing.Point(0, 0); this.treeView1.Name = "treeView1"; this.treeView1.Size = new System.Drawing.Size(241, 240); this.treeView1.TabIndex = 4; this.treeView1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeView1_MouseUp); // // contextMenuStrip1 // this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.importDataStripMenuItem1, this.exportDataToolStripMenuItem, this.addStorageStripMenuItem1, this.addStreamToolStripMenuItem, this.removeToolStripMenuItem}); this.contextMenuStrip1.Name = "contextMenuStrip1"; this.contextMenuStrip1.Size = new System.Drawing.Size(157, 114); this.contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening); // // importDataStripMenuItem1 // this.importDataStripMenuItem1.Name = "importDataStripMenuItem1"; this.importDataStripMenuItem1.Size = new System.Drawing.Size(156, 22); this.importDataStripMenuItem1.Text = "Import data..."; this.importDataStripMenuItem1.Click += new System.EventHandler(this.importDataStripMenuItem1_Click); // // exportDataToolStripMenuItem // this.exportDataToolStripMenuItem.Name = "exportDataToolStripMenuItem"; this.exportDataToolStripMenuItem.Size = new System.Drawing.Size(156, 22); this.exportDataToolStripMenuItem.Text = "Export data..."; this.exportDataToolStripMenuItem.Click += new System.EventHandler(this.exportDataToolStripMenuItem_Click); // // addStorageStripMenuItem1 // this.addStorageStripMenuItem1.Name = "addStorageStripMenuItem1"; this.addStorageStripMenuItem1.Size = new System.Drawing.Size(156, 22); this.addStorageStripMenuItem1.Text = "Add storage..."; this.addStorageStripMenuItem1.Click += new System.EventHandler(this.addStorageStripMenuItem1_Click); // // addStreamToolStripMenuItem // this.addStreamToolStripMenuItem.Name = "addStreamToolStripMenuItem"; this.addStreamToolStripMenuItem.Size = new System.Drawing.Size(156, 22); this.addStreamToolStripMenuItem.Text = "Add stream..."; this.addStreamToolStripMenuItem.Click += new System.EventHandler(this.addStreamToolStripMenuItem_Click); // // removeToolStripMenuItem // this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; this.removeToolStripMenuItem.Size = new System.Drawing.Size(156, 22); this.removeToolStripMenuItem.Text = "Remove"; this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); // // saveFileDialog1 // this.saveFileDialog1.DefaultExt = "*.bin"; this.saveFileDialog1.Filter = "Exported data files (*.bin)|*.bin|All files (*.*)|*.*"; // // menuStrip1 // this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.fileToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; this.menuStrip1.Size = new System.Drawing.Size(729, 24); this.menuStrip1.TabIndex = 5; this.menuStrip1.Text = "menuStrip1"; // // fileToolStripMenuItem // this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.openFileMenuItem, this.newStripMenuItem1, this.closeStripMenuItem1, this.toolStripSeparator2, this.updateCurrentFileToolStripMenuItem, this.saveAsToolStripMenuItem}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20); this.fileToolStripMenuItem.Text = "File"; // // openFileMenuItem // this.openFileMenuItem.Image = global::StructuredStorageExplorer.Properties.Resources.folder; this.openFileMenuItem.Name = "openFileMenuItem"; this.openFileMenuItem.Size = new System.Drawing.Size(179, 22); this.openFileMenuItem.Text = "Open..."; this.openFileMenuItem.Click += new System.EventHandler(this.openFileMenuItem_Click); // // newStripMenuItem1 // this.newStripMenuItem1.Image = global::StructuredStorageExplorer.Properties.Resources.page_white; this.newStripMenuItem1.Name = "newStripMenuItem1"; this.newStripMenuItem1.Size = new System.Drawing.Size(179, 22); this.newStripMenuItem1.Text = "New Compound File"; this.newStripMenuItem1.Click += new System.EventHandler(this.newStripMenuItem1_Click); // // closeStripMenuItem1 // this.closeStripMenuItem1.Name = "closeStripMenuItem1"; this.closeStripMenuItem1.Size = new System.Drawing.Size(179, 22); this.closeStripMenuItem1.Text = "Close file"; this.closeStripMenuItem1.Click += new System.EventHandler(this.closeStripMenuItem1_Click); // // toolStripSeparator2 // this.toolStripSeparator2.Name = "toolStripSeparator2"; this.toolStripSeparator2.Size = new System.Drawing.Size(176, 6); // // updateCurrentFileToolStripMenuItem // this.updateCurrentFileToolStripMenuItem.Image = global::StructuredStorageExplorer.Properties.Resources.disk; this.updateCurrentFileToolStripMenuItem.Name = "updateCurrentFileToolStripMenuItem"; this.updateCurrentFileToolStripMenuItem.Size = new System.Drawing.Size(179, 22); this.updateCurrentFileToolStripMenuItem.Text = "Save"; this.updateCurrentFileToolStripMenuItem.Click += new System.EventHandler(this.updateCurrentFileToolStripMenuItem_Click); // // saveAsToolStripMenuItem // this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem"; this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(179, 22); this.saveAsToolStripMenuItem.Text = "Save As..."; this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click); // // statusStrip1 // this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.fileNameLabel}); this.statusStrip1.Location = new System.Drawing.Point(0, 509); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Size = new System.Drawing.Size(729, 22); this.statusStrip1.TabIndex = 6; this.statusStrip1.Text = "statusStrip1"; // // fileNameLabel // this.fileNameLabel.Name = "fileNameLabel"; this.fileNameLabel.Size = new System.Drawing.Size(0, 17); // // splitContainer1 // this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer1.Location = new System.Drawing.Point(0, 0); this.splitContainer1.Name = "splitContainer1"; this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; // // splitContainer1.Panel1 // this.splitContainer1.Panel1.Controls.Add(this.treeView1); // // splitContainer1.Panel2 // this.splitContainer1.Panel2.Controls.Add(this.propertyGrid1); this.splitContainer1.Size = new System.Drawing.Size(243, 485); this.splitContainer1.SplitterDistance = 242; this.splitContainer1.TabIndex = 5; // // propertyGrid1 // this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; this.propertyGrid1.Location = new System.Drawing.Point(0, 0); this.propertyGrid1.Name = "propertyGrid1"; this.propertyGrid1.Size = new System.Drawing.Size(241, 237); this.propertyGrid1.TabIndex = 0; this.propertyGrid1.ToolbarVisible = false; // // splitContainer2 // this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer2.Location = new System.Drawing.Point(0, 24); this.splitContainer2.Name = "splitContainer2"; // // splitContainer2.Panel1 // this.splitContainer2.Panel1.Controls.Add(this.splitContainer1); // // splitContainer2.Panel2 // this.splitContainer2.Panel2.Controls.Add(this.hexEditor); this.splitContainer2.Size = new System.Drawing.Size(729, 485); this.splitContainer2.SplitterDistance = 243; this.splitContainer2.TabIndex = 7; // // hexEditor // this.hexEditor.BackColor = System.Drawing.Color.WhiteSmoke; this.hexEditor.Dock = System.Windows.Forms.DockStyle.Fill; this.hexEditor.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.hexEditor.LineInfoForeColor = System.Drawing.Color.Empty; this.hexEditor.LineInfoVisible = true; this.hexEditor.Location = new System.Drawing.Point(0, 0); this.hexEditor.Name = "hexEditor"; this.hexEditor.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255))))); this.hexEditor.Size = new System.Drawing.Size(482, 485); this.hexEditor.StringViewVisible = true; this.hexEditor.TabIndex = 0; this.hexEditor.UseFixedBytesPerLine = true; this.hexEditor.VScrollBarVisible = true; // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(729, 531); this.Controls.Add(this.splitContainer2); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.menuStrip1); this.MainMenuStrip = this.menuStrip1; this.Name = "MainForm"; this.Text = "Structured Storage eXplorer"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.contextMenuStrip1.ResumeLayout(false); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); this.splitContainer1.Panel1.ResumeLayout(false); this.splitContainer1.Panel2.ResumeLayout(false); this.splitContainer1.ResumeLayout(false); this.splitContainer2.Panel1.ResumeLayout(false); this.splitContainer2.Panel2.ResumeLayout(false); this.splitContainer2.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.OpenFileDialog openFileDialog1; private System.Windows.Forms.TreeView treeView1; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private System.Windows.Forms.ToolStripMenuItem exportDataToolStripMenuItem; private System.Windows.Forms.SaveFileDialog saveFileDialog1; private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem; private System.Windows.Forms.MenuStrip menuStrip1; private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem updateCurrentFileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addStreamToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem importDataStripMenuItem1; private System.Windows.Forms.OpenFileDialog openDataFileDialog; private System.Windows.Forms.ToolStripMenuItem addStorageStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem newStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem openFileMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.StatusStrip statusStrip1; private System.Windows.Forms.ToolStripStatusLabel fileNameLabel; private System.Windows.Forms.SplitContainer splitContainer1; private System.Windows.Forms.PropertyGrid propertyGrid1; private System.Windows.Forms.SplitContainer splitContainer2; private Be.Windows.Forms.HexBox hexEditor; private System.Windows.Forms.ToolStripMenuItem closeStripMenuItem1; } } openmcdf-1.5.4/src/TESTOpenMCDF/MainForm.resx0000644000175000017500000001604712027627034020436 0ustar mathieumathieu text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 147, 17 292, 17 420, 17 529, 17 676, 17 42 openmcdf-1.5.4/src/TESTOpenMCDF/Program.cs0000644000175000017500000000076411546163206017761 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Windows.Forms; namespace StructuredStorageExplorer { static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } } } openmcdf-1.5.4/src/TESTOpenMCDF/Properties/0000755000175000017500000000000012061101742020136 5ustar mathieumathieuopenmcdf-1.5.4/src/TESTOpenMCDF/Properties/AssemblyInfo.cs0000644000175000017500000000275511615212564023102 0ustar mathieumathieuusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("StucturedStorageExplorer")] [assembly: AssemblyDescription("COM/OLE Stuctured Storage Viewer - Sample application for OpenMCDF component.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("StucturedStorageExplorer")] [assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("aaf2d359-361e-47a3-883e-194bb63c7930")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.5.3.*")] openmcdf-1.5.4/src/TESTOpenMCDF/Properties/Resources.Designer.cs0000644000175000017500000001045112042056240024200 0ustar mathieumathieu//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:2.0.50727.3634 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace StructuredStorageExplorer.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("StructuredStorageExplorer.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } internal static System.Drawing.Bitmap disk { get { object obj = ResourceManager.GetObject("disk", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } internal static System.Drawing.Bitmap door_out { get { object obj = ResourceManager.GetObject("door_out", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } internal static System.Drawing.Bitmap folder { get { object obj = ResourceManager.GetObject("folder", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } internal static System.Drawing.Bitmap page_white { get { object obj = ResourceManager.GetObject("page_white", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } internal static System.Drawing.Bitmap storage { get { object obj = ResourceManager.GetObject("storage", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } internal static System.Drawing.Bitmap stream { get { object obj = ResourceManager.GetObject("stream", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } openmcdf-1.5.4/src/TESTOpenMCDF/Properties/Resources.resx0000644000175000017500000001632712042056234023030 0ustar mathieumathieu text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\img\disk.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\img\door_out.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\img\folder.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\img\page_white.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\img\storage.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\img\stream.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a openmcdf-1.5.4/src/TESTOpenMCDF/Properties/Settings.Designer.cs0000644000175000017500000000302011546170074024032 0ustar mathieumathieu//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:2.0.50727.3615 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace StructuredStorageExplorer.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] public bool CommitEnabled { get { return ((bool)(this["CommitEnabled"])); } set { this["CommitEnabled"] = value; } } } } openmcdf-1.5.4/src/TESTOpenMCDF/Properties/Settings.settings0000644000175000017500000000067011546170070023532 0ustar mathieumathieu True openmcdf-1.5.4/src/TESTOpenMCDF/StreamDataProvider.cs0000644000175000017500000001152311615212526022102 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using Be.Windows.Forms; using OpenMcdf; namespace StructuredStorageExplorer { public class StreamDataProvider : IByteProvider { /// /// Modifying stream /// CFStream _modifiedStream; /// /// Contains information about changes. /// bool _hasChanges; /// /// Contains a byte collection. /// ByteCollection _bytes; /// /// Initializes a new instance of the DynamicByteProvider class. /// /// public StreamDataProvider(CFStream modifiedStream) { _bytes = new ByteCollection(modifiedStream.GetData()); _modifiedStream = modifiedStream; } /// /// Raises the Changed event. /// void OnChanged(EventArgs e) { _hasChanges = true; if (Changed != null) Changed(this, e); } /// /// Raises the LengthChanged event. /// void OnLengthChanged(EventArgs e) { if (LengthChanged != null) LengthChanged(this, e); } /// /// Gets the byte collection. /// public ByteCollection Bytes { get { return _bytes; } } #region IByteProvider Members /// /// True, when changes are done. /// public bool HasChanges() { return _hasChanges; } /// /// Applies changes. /// public void ApplyChanges() { _hasChanges = false; _modifiedStream.SetData(this._bytes.ToArray()); } /// /// Occurs, when the write buffer contains new changes. /// public event EventHandler Changed; /// /// Occurs, when InsertBytes or DeleteBytes method is called. /// public event EventHandler LengthChanged; /// /// Reads a byte from the byte collection. /// /// the index of the byte to read /// the byte public byte ReadByte(long index) { return _bytes[(int)index]; } /// /// Write a byte into the byte collection. /// /// the index of the byte to write. /// the byte public void WriteByte(long index, byte value) { _bytes[(int)index] = value; OnChanged(EventArgs.Empty); } /// /// Deletes bytes from the byte collection. /// /// the start index of the bytes to delete. /// the length of bytes to delete. public void DeleteBytes(long index, long length) { int internal_index = (int)Math.Max(0, index); int internal_length = (int)Math.Min((int)Length, length); _bytes.RemoveRange(internal_index, internal_length); OnLengthChanged(EventArgs.Empty); OnChanged(EventArgs.Empty); } /// /// Inserts byte into the byte collection. /// /// the start index of the bytes in the byte collection /// the byte array to insert public void InsertBytes(long index, byte[] bs) { _bytes.InsertRange((int)index, bs); OnLengthChanged(EventArgs.Empty); OnChanged(EventArgs.Empty); } /// /// Gets the length of the bytes in the byte collection. /// public long Length { get { return _bytes.Count; } } /// /// Returns true /// public bool SupportsWriteByte() { return true; } /// /// Returns true /// public bool SupportsInsertBytes() { return true; } /// /// Returns true /// public bool SupportsDeleteBytes() { return true; } #endregion } } openmcdf-1.5.4/src/TESTOpenMCDF/StructuredStorageExplorer.csproj0000644000175000017500000001024412061073200024434 0ustar mathieumathieu Debug AnyCPU 9.0.30729 2.0 {4F6323A8-9C06-4D94-808F-EBD69B8370D7} WinExe Properties StructuredStorageExplorer StucturedStorageExplorer v2.0 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 Form MainForm.cs MainForm.cs ResXFileCodeGenerator Resources.Designer.cs Designer True Resources.resx True SettingsSingleFileGenerator Settings.Designer.cs True Settings.settings True {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1} OpenMcdf openmcdf-1.5.4/src/TESTOpenMCDF/Utils.cs0000644000175000017500000000412111615212530017431 0ustar mathieumathieuusing System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using OpenMcdf; namespace StructuredStorageExplorer { class Utils { public static DialogResult InputBox(string title, string promptText, ref string value) { Form form = new Form(); Label label = new Label(); TextBox textBox = new TextBox(); Button buttonOk = new Button(); Button buttonCancel = new Button(); form.Text = title; label.Text = promptText; textBox.Text = value; buttonOk.Text = "OK"; buttonCancel.Text = "Cancel"; buttonOk.DialogResult = DialogResult.OK; buttonCancel.DialogResult = DialogResult.Cancel; label.SetBounds(9, 20, 372, 13); textBox.SetBounds(12, 36, 372, 20); buttonOk.SetBounds(228, 72, 75, 23); buttonCancel.SetBounds(309, 72, 75, 23); label.AutoSize = true; textBox.Anchor = textBox.Anchor | AnchorStyles.Right; buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; form.ClientSize = new Size(396, 107); form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); form.FormBorderStyle = FormBorderStyle.FixedDialog; form.StartPosition = FormStartPosition.CenterScreen; form.MinimizeBox = false; form.MaximizeBox = false; form.AcceptButton = buttonOk; form.CancelButton = buttonCancel; DialogResult dialogResult = form.ShowDialog(); value = textBox.Text; return dialogResult; } public static bool IsStreamTreeNode(TreeNode node) { return ((CFItem)node.Tag).IsStream; } } } openmcdf-1.5.4/Support.txt0000644000175000017500000000030211461307640015400 0ustar mathieumathieuPlease, use sourceforge bug-tracker to get support at http://sourceforge.net/projects/openmcdf/ Thank You in advance for bug reporting and for You interest in OpenMCDF ! Federico Blaseotto