1 //*@@@+++@@@@******************************************************************
2 //
3 // Copyright � Microsoft Corp.
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 //
9 // � Redistributions of source code must retain the above copyright notice,
10 //   this list of conditions and the following disclaimer.
11 // � Redistributions in binary form must reproduce the above copyright notice,
12 //   this list of conditions and the following disclaimer in the documentation
13 //   and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 // POSSIBILITY OF SUCH DAMAGE.
26 //
27 //*@@@---@@@@******************************************************************
28 #include "JXRMeta.h"
29 #include "JXRGlue.h"
30 
31 
32 
33 // read and write big and little endian words/dwords from a buffer on both big and little endian cpu's
34 // with full buffer overflow checking
35 
36 
37 
getbfcpy(U8 * pbdest,const U8 * pb,size_t cb,size_t ofs,U32 n)38 ERR getbfcpy(U8* pbdest, const U8* pb, size_t cb, size_t ofs, U32 n)
39 {
40     ERR err = WMP_errSuccess;
41     FailIf(ofs + n > cb, WMP_errBufferOverflow);
42     memcpy(pbdest, &pb[ofs], n);
43 Cleanup:
44     return err;
45 }
46 
47 
48 
getbfw(const U8 * pb,size_t cb,size_t ofs,U16 * pw)49 ERR getbfw(const U8* pb, size_t cb, size_t ofs, U16* pw)
50 {
51     ERR err = WMP_errSuccess;
52     FailIf(ofs + sizeof(U16) > cb, WMP_errBufferOverflow);
53     *pw = (U16)( pb[ofs] + ( pb[ofs + 1] << 8 ) );
54 Cleanup:
55     return err;
56 }
57 
58 
59 
getbfdw(const U8 * pb,size_t cb,size_t ofs,U32 * pdw)60 ERR getbfdw(const U8* pb, size_t cb, size_t ofs, U32* pdw)
61 {
62     ERR err = WMP_errSuccess;
63     FailIf(ofs + sizeof(U32) > cb, WMP_errBufferOverflow);
64     *pdw = pb[ofs] + ( pb[ofs + 1] << 8 ) + ( pb[ofs + 2] << 16UL ) + ( pb[ofs + 3] << 24UL );
65 Cleanup:
66     return err;
67 }
68 
69 
70 
getbfwbig(const U8 * pb,size_t cb,size_t ofs,U16 * pw)71 ERR getbfwbig(const U8* pb, size_t cb, size_t ofs, U16* pw)
72 {
73     ERR err = WMP_errSuccess;
74     FailIf(ofs + sizeof(U16) > cb, WMP_errBufferOverflow);
75     *pw = (U16)( pb[ofs + 1] + ( pb[ofs] << 8 ) );
76 Cleanup:
77     return err;
78 }
79 
80 
81 
getbfdwbig(const U8 * pb,size_t cb,size_t ofs,U32 * pdw)82 ERR getbfdwbig(const U8* pb, size_t cb, size_t ofs, U32* pdw)
83 {
84     ERR err = WMP_errSuccess;
85     FailIf(ofs + sizeof(U32) > cb, WMP_errBufferOverflow);
86     *pdw = pb[ofs + 3] + ( pb[ofs + 2] << 8 ) + ( pb[ofs + 1] << 16UL ) + ( pb[ofs] << 24UL );
87 Cleanup:
88     return err;
89 }
90 
91 
92 
getbfwe(const U8 * pb,size_t cb,size_t ofs,U16 * pw,U8 endian)93 ERR getbfwe(const U8* pb, size_t cb, size_t ofs, U16* pw, U8 endian)
94 {
95     if ( endian == WMP_INTEL_ENDIAN )
96         return ( getbfw(pb, cb, ofs, pw) );
97     else
98         return ( getbfwbig(pb, cb, ofs, pw) );
99 }
100 
101 
102 
getbfdwe(const U8 * pb,size_t cb,size_t ofs,U32 * pdw,U8 endian)103 ERR getbfdwe(const U8* pb, size_t cb, size_t ofs, U32* pdw, U8 endian)
104 {
105     if ( endian == WMP_INTEL_ENDIAN )
106         return ( getbfdw(pb, cb, ofs, pdw) );
107     else
108         return ( getbfdwbig(pb, cb, ofs, pdw) );
109 }
110 
111 
112 
setbfcpy(U8 * pb,size_t cb,size_t ofs,const U8 * pbset,size_t cbset)113 ERR setbfcpy(U8* pb, size_t cb, size_t ofs, const U8* pbset, size_t cbset)
114 {
115     ERR err = WMP_errSuccess;
116     FailIf(ofs + cbset > cb, WMP_errBufferOverflow);
117     memcpy(&pb[ofs], pbset, cbset);
118 Cleanup:
119     return err;
120 }
121 
122 
123 
setbfw(U8 * pb,size_t cb,size_t ofs,U16 dw)124 ERR setbfw(U8* pb, size_t cb, size_t ofs, U16 dw)
125 {
126     ERR err = WMP_errSuccess;
127     FailIf(ofs + sizeof(U16) > cb, WMP_errBufferOverflow);
128     pb[ofs] = (U8)dw;
129     pb[ofs + 1] = (U8)( dw >> 8 );
130 Cleanup:
131     return err;
132 }
133 
134 
135 
setbfdw(U8 * pb,size_t cb,size_t ofs,U32 dw)136 ERR setbfdw(U8* pb, size_t cb, size_t ofs, U32 dw)
137 {
138     ERR err = WMP_errSuccess;
139     FailIf(ofs + sizeof(U32) > cb, WMP_errBufferOverflow);
140     pb[ofs] = (U8)dw;
141     pb[ofs + 1] = (U8)( dw >> 8 );
142     pb[ofs + 2] = (U8)( dw >> 16 );
143     pb[ofs + 3] = (U8)( dw >> 24 );
144 Cleanup:
145     return err;
146 }
147 
148 
149 
setbfwbig(U8 * pb,size_t cb,size_t ofs,U16 dw)150 ERR setbfwbig(U8* pb, size_t cb, size_t ofs, U16 dw)
151 {
152     ERR err = WMP_errSuccess;
153     FailIf(ofs + sizeof(U16) > cb, WMP_errBufferOverflow);
154     pb[ofs + 1] = (U8)dw;
155     pb[ofs] = (U8)( dw >> 8 );
156 Cleanup:
157     return err;
158 }
159 
160 
161 
setbfdwbig(U8 * pb,size_t cb,size_t ofs,U32 dw)162 ERR setbfdwbig(U8* pb, size_t cb, size_t ofs, U32 dw)
163 {
164     ERR err = WMP_errSuccess;
165     FailIf(ofs + sizeof(U32) > cb, WMP_errBufferOverflow);
166     pb[ofs + 3] = (U8)dw;
167     pb[ofs + 2] = (U8)( dw >> 8 );
168     pb[ofs + 1] = (U8)( dw >> 16 );
169     pb[ofs] = (U8)( dw >> 24 );
170 Cleanup:
171     return err;
172 }
173 
174 
175 
176 //================================================================
177 // BufferCalcIFDSize (arbitrary endian)
178 // StreamCalcIFDSize (little endian)
179 //
180 // count up the number of bytes needed to store the IFD and all
181 // associated data including a subordinate interoperability IFD if any
182 //================================================================
183 
184 
185 
BufferCalcIFDSize(const U8 * pbdata,size_t cbdata,U32 ofsifd,U8 endian,U32 * pcbifd)186 ERR BufferCalcIFDSize(const U8* pbdata, size_t cbdata, U32 ofsifd, U8 endian, U32* pcbifd)
187 {
188     ERR err = WMP_errSuccess;
189     U16 cDir;
190     U16 i;
191     U32 ofsdir;
192     U32 cbifd = 0;
193     U32 cbEXIFIFD = 0;
194     U32 cbGPSInfoIFD = 0;
195     U32 cbInteroperabilityIFD = 0;
196 
197     *pcbifd = 0;
198     Call(getbfwe(pbdata, cbdata, ofsifd, &cDir, endian));
199 
200     cbifd = sizeof(U16) + cDir * SizeofIFDEntry + sizeof(U32);
201     ofsdir = ofsifd + sizeof(U16);
202     for ( i = 0; i < cDir; i++ )
203     {
204         U16 tag;
205         U16 type;
206         U32 count;
207         U32 value;
208         U32 datasize;
209 
210         Call(getbfwe(pbdata, cbdata, ofsdir, &tag, endian));
211         Call(getbfwe(pbdata, cbdata, ofsdir + sizeof(U16), &type, endian));
212         Call(getbfdwe(pbdata, cbdata, ofsdir + 2 * sizeof(U16), &count, endian));
213         Call(getbfdwe(pbdata, cbdata, ofsdir + 2 * sizeof(U16) + sizeof(U32), &value, endian));
214         FailIf(type == 0 || type >= sizeof(IFDEntryTypeSizes) / sizeof(IFDEntryTypeSizes[0]), WMP_errFail);
215         if ( tag == WMP_tagEXIFMetadata )
216         {
217             Call(BufferCalcIFDSize(pbdata, cbdata, value, endian, &cbEXIFIFD));
218         }
219         else if ( tag == WMP_tagGPSInfoMetadata )
220         {
221             Call(BufferCalcIFDSize(pbdata, cbdata, value, endian, &cbGPSInfoIFD));
222         }
223         else if ( tag == WMP_tagInteroperabilityIFD )
224         {
225             Call(BufferCalcIFDSize(pbdata, cbdata, value, endian, &cbInteroperabilityIFD));
226         }
227         else
228         {
229             datasize = IFDEntryTypeSizes[type] * count;
230             if ( datasize > 4 )
231                 cbifd += datasize;
232         }
233         ofsdir += SizeofIFDEntry;
234     }
235     if ( cbEXIFIFD != 0 )
236         cbifd += ( cbifd & 1 ) + cbEXIFIFD;
237     if ( cbGPSInfoIFD != 0 )
238         cbifd += ( cbifd & 1 ) + cbGPSInfoIFD;
239     if ( cbInteroperabilityIFD != 0 )
240         cbifd += ( cbifd & 1 ) + cbInteroperabilityIFD;
241 
242     *pcbifd = cbifd;
243 
244 Cleanup:
245     return err;
246 }
247 
248 
StreamCalcIFDSize(struct WMPStream * pWS,U32 uIFDOfs,U32 * pcbifd)249 ERR StreamCalcIFDSize(struct WMPStream* pWS, U32 uIFDOfs, U32 *pcbifd)
250 {
251     ERR err = WMP_errSuccess;
252     size_t offCurPos = 0;
253     Bool GetPosOK = FALSE;
254     U16 cDir;
255     U32 i;
256     U32 ofsdir;
257     U32 cbifd = 0;
258     U32 cbEXIFIFD = 0;
259     U32 cbGPSInfoIFD = 0;
260     U32 cbInteroperabilityIFD = 0;
261 
262     *pcbifd = 0;
263     Call(pWS->GetPos(pWS, &offCurPos));
264     GetPosOK = TRUE;
265 
266     Call(GetUShort(pWS, uIFDOfs, &cDir));
267     cbifd = sizeof(U16) + cDir * SizeofIFDEntry + sizeof(U32);
268     ofsdir = uIFDOfs + sizeof(U16);
269     for ( i = 0; i < cDir; i++ )
270     {
271         U16 tag;
272         U16 type;
273         U32 count;
274         U32 value;
275         U32 datasize;
276 
277         Call(GetUShort(pWS, ofsdir, &tag));
278         Call(GetUShort(pWS, ofsdir + sizeof(U16), &type));
279         Call(GetULong(pWS, ofsdir + 2 * sizeof(U16), &count));
280         Call(GetULong(pWS, ofsdir + 2 * sizeof(U16) + sizeof(U32), &value));
281         FailIf(type == 0 || type >= sizeof(IFDEntryTypeSizes) / sizeof(IFDEntryTypeSizes[0]), WMP_errUnsupportedFormat);
282         if ( tag == WMP_tagEXIFMetadata )
283         {
284             Call(StreamCalcIFDSize(pWS, value, &cbEXIFIFD));
285         }
286         else if ( tag == WMP_tagGPSInfoMetadata )
287         {
288             Call(StreamCalcIFDSize(pWS, value, &cbGPSInfoIFD));
289         }
290         else if ( tag == WMP_tagInteroperabilityIFD )
291         {
292             Call(StreamCalcIFDSize(pWS, value, &cbInteroperabilityIFD));
293         }
294         else
295         {
296             datasize = IFDEntryTypeSizes[type] * count;
297             if ( datasize > 4 )
298                 cbifd += datasize;
299         }
300         ofsdir += SizeofIFDEntry;
301     }
302     if ( cbEXIFIFD != 0 )
303         cbifd += ( cbifd & 1 ) + cbEXIFIFD;
304     if ( cbGPSInfoIFD != 0 )
305         cbifd += ( cbifd & 1 ) + cbGPSInfoIFD;
306     if ( cbInteroperabilityIFD != 0 )
307         cbifd += ( cbifd & 1 ) + cbInteroperabilityIFD;
308     *pcbifd = cbifd;
309 
310 Cleanup:
311     if ( GetPosOK )
312         Call(pWS->SetPos(pWS, offCurPos));
313     return ( err );
314 }
315 
316 
317 
318 // src IFD copied to dst IFD with any nested IFD's
319 // src IFD is arbitrary endian, arbitrary data arrangement
320 // dst IFD is little endian, data arranged in tag order
321 // dst IFD tags are ordered the same as src IFD so src IFD tags must be in order
BufferCopyIFD(const U8 * pbsrc,U32 cbsrc,U32 ofssrc,U8 endian,U8 * pbdst,U32 cbdst,U32 * pofsdst)322 ERR BufferCopyIFD(const U8* pbsrc, U32 cbsrc, U32 ofssrc, U8 endian, U8* pbdst, U32 cbdst, U32* pofsdst)
323 {
324     ERR err = WMP_errSuccess;
325     U16 cDir;
326     U16 i;
327     U16 ofsEXIFIFDEntry = 0;
328     U16 ofsGPSInfoIFDEntry = 0;
329     U16 ofsInteroperabilityIFDEntry = 0;
330     U32 ofsEXIFIFD = 0;
331     U32 ofsGPSInfoIFD = 0;
332     U32 ofsInteroperabilityIFD = 0;
333     U32 ofsdstnextdata;
334     U32 ofsdst = *pofsdst;
335     U32 ofssrcdir;
336     U32 ofsdstdir;
337     U32 ofsnextifd;
338 
339     Call(getbfwe(pbsrc, cbsrc, ofssrc, &cDir, endian));
340     Call(setbfw(pbdst, cbdst, ofsdst, cDir));
341     ofsnextifd = ofsdst + sizeof(U16) + SizeofIFDEntry * cDir;
342     ofsdstnextdata = ofsnextifd + sizeof(U32);
343 
344     ofssrcdir = ofssrc + sizeof(U16);
345     ofsdstdir = ofsdst + sizeof(U16);
346     for ( i = 0; i < cDir; i++ )
347     {
348         U16 tag;
349         U16 type;
350         U32 count;
351         U32 value;
352         U32 size;
353 
354         Call(getbfwe(pbsrc, cbsrc, ofssrcdir, &tag, endian));
355         Call(setbfw(pbdst, cbdst, ofsdstdir, tag));
356 
357         Call(getbfwe(pbsrc, cbsrc, ofssrcdir + sizeof(U16), &type, endian));
358         Call(setbfw(pbdst, cbdst, ofsdstdir + sizeof(U16), type));
359 
360         Call(getbfdwe(pbsrc, cbsrc, ofssrcdir + 2 * sizeof(U16), &count, endian));
361         Call(setbfdw(pbdst, cbdst, ofsdstdir + 2 * sizeof(U16), count));
362 
363         Call(getbfdwe(pbsrc, cbsrc, ofssrcdir + 2 * sizeof(U16) + sizeof(U32), &value, endian));
364         Call(setbfdw(pbdst, cbdst, ofsdstdir + 2 * sizeof(U16) + sizeof(U32), 0));
365 
366         FailIf(type == 0 || type >= sizeof(IFDEntryTypeSizes) / sizeof(IFDEntryTypeSizes[0]), WMP_errFail);
367         if ( tag == WMP_tagEXIFMetadata )
368         {
369             ofsEXIFIFDEntry = (U16) ofsdstdir;
370             ofsEXIFIFD = value;
371         }
372         else if ( tag == WMP_tagGPSInfoMetadata )
373         {
374             ofsGPSInfoIFDEntry = (U16) ofsdstdir;
375             ofsGPSInfoIFD = value;
376         }
377         else if ( tag == WMP_tagInteroperabilityIFD )
378         {
379             ofsInteroperabilityIFDEntry = (U16) ofsdstdir;
380             ofsInteroperabilityIFD = value;
381         }
382         else
383         {
384             U32 ofsdstdata = ofsdstdir + 2 * sizeof(U16) + sizeof(U32);
385             U32 ofssrcdata = ofssrcdir + 2 * sizeof(U16) + sizeof(U32);
386             size = count * IFDEntryTypeSizes[type];
387             if ( size > 4 )
388             {
389                 ofssrcdata = value;
390                 Call(setbfdw(pbdst, cbdst, ofsdstdata, ofsdstnextdata));
391                 ofsdstdata = ofsdstnextdata;
392                 ofsdstnextdata += size;
393             }
394             FailIf(ofssrcdata + size > cbsrc || ofsdstdata + size > cbdst, WMP_errBufferOverflow);
395             if ( size == count || endian == WMP_INTEL_ENDIAN )
396                 // size == count means 8-bit data means endian doesn't matter
397                 memcpy(&pbdst[ofsdstdata], &pbsrc[ofssrcdata], size);
398             else
399             {   // big endian source and endian matters
400                 U32 j;
401 
402                 switch ( IFDEntryTypeSizes[type] )
403                 {
404                 case 2:
405                     for ( j = 0; j < count; j++ )
406                     {
407                         U16 w;
408                         getbfwbig(pbsrc, cbsrc, ofssrcdata + j * sizeof(U16), &w);
409                         setbfw(pbdst, cbdst, ofsdstdata + j * sizeof(U16), w);
410                     }
411                     break;
412                 case 8:
413                     if ( type == WMP_typDOUBLE )
414                     {
415                         for ( j = 0; j < count; j++ )
416                         {
417                             U32 dwlo;
418                             U32 dwhi;
419                             getbfdwbig(pbsrc, cbsrc, ofssrcdata + j * 8, &dwhi);
420                             getbfdwbig(pbsrc, cbsrc, ofssrcdata + j * 8 + sizeof(U32), &dwlo);
421                             setbfdw(pbdst, cbdst, ofsdstdata + j * 8, dwlo);
422                             setbfdw(pbdst, cbdst, ofsdstdata + j * 8 + sizeof(U32), dwhi);
423                         }
424                         break;
425                     }
426                     count *= 2;
427                     // RATIONAL's fall through to be handled as LONG's
428                 case 4:
429                     for ( j = 0; j < count; j++ )
430                     {
431                         U32 dw;
432                         getbfdwbig(pbsrc, cbsrc, ofssrcdata + j * sizeof(U32), &dw);
433                         setbfdw(pbdst, cbdst, ofsdstdata + j * sizeof(U32), dw);
434                     }
435                     break;
436                 }
437             }
438         }
439         ofssrcdir += SizeofIFDEntry;
440         ofsdstdir += SizeofIFDEntry;
441     }
442     Call(setbfdw(pbdst, cbdst, ofsnextifd, 0));    // no nextIFD
443 
444     if ( ofsEXIFIFDEntry != 0 )
445     {
446         ofsdstnextdata += ( ofsdstnextdata & 1 );
447         Call(setbfdw(pbdst, cbdst, ofsEXIFIFDEntry + 2 * sizeof(U16) + sizeof(U32), ofsdstnextdata));
448         Call(BufferCopyIFD(pbsrc, cbsrc, ofsEXIFIFD, endian, pbdst, cbdst, &ofsdstnextdata));
449     }
450     if ( ofsGPSInfoIFDEntry != 0 )
451     {
452         ofsdstnextdata += ( ofsdstnextdata & 1 );
453         Call(setbfdw(pbdst, cbdst, ofsGPSInfoIFDEntry + 2 * sizeof(U16) + sizeof(U32), ofsdstnextdata));
454         Call(BufferCopyIFD(pbsrc, cbsrc, ofsGPSInfoIFD, endian, pbdst, cbdst, &ofsdstnextdata));
455     }
456     if ( ofsInteroperabilityIFDEntry != 0 )
457     {
458         ofsdstnextdata += ( ofsdstnextdata & 1 );
459         Call(setbfdw(pbdst, cbdst, ofsInteroperabilityIFDEntry + 2 * sizeof(U16) + sizeof(U32), ofsdstnextdata));
460         Call(BufferCopyIFD(pbsrc, cbsrc, ofsInteroperabilityIFD, endian, pbdst, cbdst, &ofsdstnextdata));
461     }
462     *pofsdst = ofsdstnextdata;
463 
464 Cleanup:
465     return err;
466 }
467 
468 
469 
470 // src IFD copied to dst IFD with any nested IFD's
471 // src IFD is little endian, arbitrary data arrangement
472 // dst IFD is little endian, data arranged in tag order
473 // dst IFD tags are ordered the same as src IFD so src IFD tags must be in order
StreamCopyIFD(struct WMPStream * pWS,U32 ofssrc,U8 * pbdst,U32 cbdst,U32 * pofsdst)474 ERR StreamCopyIFD(struct WMPStream* pWS, U32 ofssrc, U8* pbdst, U32 cbdst, U32* pofsdst)
475 {
476     ERR err = WMP_errSuccess;
477     size_t offCurPos = 0;
478     Bool GetPosOK = FALSE;
479     U16 cDir;
480     U16 i;
481     U16 ofsEXIFIFDEntry = 0;
482     U16 ofsGPSInfoIFDEntry = 0;
483     U16 ofsInteroperabilityIFDEntry = 0;
484     U32 ofsEXIFIFD = 0;
485     U32 ofsGPSInfoIFD = 0;
486     U32 ofsInteroperabilityIFD = 0;
487     U32 ofsdstnextdata;
488     U32 ofsdst = *pofsdst;
489     U32 ofssrcdir;
490     U32 ofsdstdir;
491     U32 ofsnextifd;
492 
493     Call(pWS->GetPos(pWS, &offCurPos));
494     GetPosOK = TRUE;
495 
496     Call(GetUShort(pWS, ofssrc, &cDir));
497     Call(setbfw(pbdst, cbdst, ofsdst, cDir));
498 
499     ofsnextifd = ofsdst + sizeof(U16) + SizeofIFDEntry * cDir;
500     ofsdstnextdata = ofsnextifd + sizeof(U32);
501 
502     ofssrcdir = ofssrc + sizeof(U16);
503     ofsdstdir = ofsdst + sizeof(U16);
504     for ( i = 0; i < cDir; i++ )
505     {
506         U16 tag;
507         U16 type;
508         U32 count;
509         U32 value;
510         U32 size;
511 
512         Call(GetUShort(pWS, ofssrcdir, &tag));
513         Call(setbfw(pbdst, cbdst, ofsdstdir, tag));
514 
515         Call(GetUShort(pWS, ofssrcdir + sizeof(U16), &type));
516         Call(setbfw(pbdst, cbdst, ofsdstdir + sizeof(U16), type));
517 
518         Call(GetULong(pWS, ofssrcdir + 2 * sizeof(U16), &count));
519         Call(setbfdw(pbdst, cbdst, ofsdstdir + 2 * sizeof(U16), count));
520 
521         Call(GetULong(pWS, ofssrcdir + 2 * sizeof(U16) + sizeof(U32), &value));
522         Call(setbfdw(pbdst, cbdst, ofsdstdir + 2 * sizeof(U16) + sizeof(U32), 0));
523 
524         FailIf(type == 0 || type >= sizeof(IFDEntryTypeSizes) / sizeof(IFDEntryTypeSizes[0]), WMP_errFail);
525         if ( tag == WMP_tagEXIFMetadata )
526         {
527             ofsEXIFIFDEntry = (U16) ofsdstdir;
528             ofsEXIFIFD = value;
529         }
530         else if ( tag == WMP_tagGPSInfoMetadata )
531         {
532             ofsGPSInfoIFDEntry = (U16) ofsdstdir;
533             ofsGPSInfoIFD = value;
534         }
535         else if ( tag == WMP_tagInteroperabilityIFD )
536         {
537             ofsInteroperabilityIFDEntry = (U16) ofsdstdir;
538             ofsInteroperabilityIFD = value;
539         }
540         else
541         {
542             U32 ofsdstdata = ofsdstdir + 2 * sizeof(U16) + sizeof(U32);
543             U32 ofssrcdata = ofssrcdir + 2 * sizeof(U16) + sizeof(U32);
544             size = count * IFDEntryTypeSizes[type];
545             if ( size > 4 )
546             {
547                 ofssrcdata = value;
548                 Call(setbfdw(pbdst, cbdst, ofsdstdata, ofsdstnextdata));
549                 ofsdstdata = ofsdstnextdata;
550                 ofsdstnextdata += size;
551             }
552             FailIf(ofsdstdata + size > cbdst, WMP_errBufferOverflow);
553             Call(pWS->SetPos(pWS, ofssrcdata));
554             Call(pWS->Read(pWS, &pbdst[ofsdstdata], size));
555         }
556         ofssrcdir += SizeofIFDEntry;
557         ofsdstdir += SizeofIFDEntry;
558     }
559     Call(setbfdw(pbdst, cbdst, ofsnextifd, 0));    // no nextIFD
560 
561     if ( ofsEXIFIFDEntry != 0 )
562     {
563         ofsdstnextdata += ( ofsdstnextdata & 1 );
564         Call(setbfdw(pbdst, cbdst, ofsEXIFIFDEntry + 2 * sizeof(U16) + sizeof(U32), ofsdstnextdata));
565         Call(StreamCopyIFD(pWS, ofsEXIFIFD, pbdst, cbdst, &ofsdstnextdata));
566     }
567     if ( ofsGPSInfoIFDEntry != 0 )
568     {
569         ofsdstnextdata += ( ofsdstnextdata & 1 );
570         Call(setbfdw(pbdst, cbdst, ofsGPSInfoIFDEntry + 2 * sizeof(U16) + sizeof(U32), ofsdstnextdata));
571         Call(StreamCopyIFD(pWS, ofsGPSInfoIFD, pbdst, cbdst, &ofsdstnextdata));
572     }
573     if ( ofsInteroperabilityIFDEntry != 0 )
574     {
575         ofsdstnextdata += ( ofsdstnextdata & 1 );
576         Call(setbfdw(pbdst, cbdst, ofsInteroperabilityIFDEntry + 2 * sizeof(U16) + sizeof(U32), ofsdstnextdata));
577         Call(StreamCopyIFD(pWS, ofsInteroperabilityIFD, pbdst, cbdst, &ofsdstnextdata));
578     }
579     *pofsdst = ofsdstnextdata;
580 
581 Cleanup:
582     if ( GetPosOK )
583         Call(pWS->SetPos(pWS, offCurPos));
584     return err;
585 }
586 
587 
588 
589 //================================================================
590 ERR GetUShort(
591     __in_ecount(1) struct WMPStream* pWS,
592     size_t offPos,
593     __out_ecount(1) U16* puValue)
594 {
595     ERR err = WMP_errSuccess;
596     U8  cVal;
597 
598     Call(pWS->SetPos(pWS, offPos));
599     Call(pWS->Read(pWS, &cVal, sizeof(cVal)));
600     puValue[0] = (U16) cVal;
601     Call(pWS->Read(pWS, &cVal, sizeof(cVal)));
602     puValue[0] += ((U16) cVal) << 8;
603 
604 Cleanup:
605     return err;
606 }
607 
608 ERR PutUShort(
609     __in_ecount(1) struct WMPStream* pWS,
610     size_t offPos,
611     U16 uValue)
612 {
613     ERR err = WMP_errSuccess;
614     U8  cVal = (U8) uValue;
615 
616     Call(pWS->SetPos(pWS, offPos));
617     Call(pWS->Write(pWS, &cVal, sizeof(cVal)));
618     cVal = (U8) (uValue >> 8);
619     Call(pWS->Write(pWS, &cVal, sizeof(cVal)));
620 
621 Cleanup:
622     return err;
623 }
624 
625 ERR GetULong(
626     __in_ecount(1) struct WMPStream* pWS,
627     size_t offPos,
628     __out_ecount(1) U32* puValue)
629 {
630     ERR err = WMP_errSuccess;
631     U8  cVal;
632 
633     Call(pWS->SetPos(pWS, offPos));
634     Call(pWS->Read(pWS, &cVal, sizeof(cVal)));
635     puValue[0] = (U32) cVal;
636     Call(pWS->Read(pWS, &cVal, sizeof(cVal)));
637     puValue[0] += ((U32) cVal) << 8;
638     Call(pWS->Read(pWS, &cVal, sizeof(cVal)));
639     puValue[0] += ((U32) cVal) << 16;
640     Call(pWS->Read(pWS, &cVal, sizeof(cVal)));
641     puValue[0] += ((U32) cVal) << 24;
642 
643 Cleanup:
644     return err;
645 }
646 
647 ERR PutULong(
648     __in_ecount(1) struct WMPStream* pWS,
649     size_t offPos,
650     U32 uValue)
651 {
652     ERR err = WMP_errSuccess;
653     U8  cVal = (U8) uValue;
654 
655     Call(pWS->SetPos(pWS, offPos));
656     Call(pWS->Write(pWS, &cVal, sizeof(cVal)));
657     cVal = (U8) (uValue >> 8);
658     Call(pWS->Write(pWS, &cVal, sizeof(cVal)));
659     cVal = (U8) (uValue >> 16);
660     Call(pWS->Write(pWS, &cVal, sizeof(cVal)));
661     cVal = (U8) (uValue >> 24);
662     Call(pWS->Write(pWS, &cVal, sizeof(cVal)));
663 
664 Cleanup:
665     return err;
666 }
667 
668 
669 ERR ReadBinaryData(__in_ecount(1) struct WMPStream* pWS,
670                    const __in_win U32 uCount,
671                    const __in_win U32 uValue,
672                    U8 **ppbData)
673 {
674     ERR err = WMP_errSuccess;
675     U8 *pbData = NULL;
676 
677     Call(PKAlloc((void **) &pbData, uCount + 2)); // Allocate buffer to store data with space for an added ascii or unicode null
678     if (uCount <= 4)
679     {
680         unsigned int i;
681         for (i = 0; i < uCount; i++)
682             pbData[i] = ((U8*)&uValue)[i]; // Copy least sig bytes - we assume 'II' type TIFF files
683     }
684     else
685     {
686         size_t offPosPrev;
687 
688         Call(pWS->GetPos(pWS, &offPosPrev));
689         Call(pWS->SetPos(pWS, uValue));
690         Call(pWS->Read(pWS, pbData, uCount));
691         Call(pWS->SetPos(pWS, offPosPrev));
692     }
693 
694     *ppbData = pbData;
695 
696 Cleanup:
697     if (Failed(err))
698     {
699         if (pbData)
700             PKFree((void **) &pbData);
701     }
702     return err;
703 }
704 
705 
706 ERR ReadPropvar(__in_ecount(1) struct WMPStream* pWS,
707                 const __in_win U16 uType,
708                 const __in_win U32 uCount,
709                 const __in_win U32 uValue,
710                 __out_win DPKPROPVARIANT *pvar)
711 {
712     ERR err = WMP_errSuccess;
713     // U8 *pbData = NULL;
714 
715     memset(pvar, 0, sizeof(*pvar));
716     if (uCount == 0)
717         goto Cleanup; // Nothing to read in here
718 
719     switch (uType)
720     {
721         case WMP_typASCII:
722             pvar->vt = DPKVT_LPSTR;
723             Call(ReadBinaryData(pWS, uCount, uValue, (U8 **) &pvar->VT.pszVal));
724             assert(0 == pvar->VT.pszVal[uCount - 1]); // Check that it's null-terminated
725             // make sure (ReadBinaryData allocated uCount + 2 so this and unicode can have forced nulls)
726             pvar->VT.pszVal[uCount] = 0;
727             break;
728 
729         case WMP_typBYTE:
730         case WMP_typUNDEFINED:
731             // Return as regular C array rather than safearray, as this type is sometimes
732             // used to convey unicode (which does not require a count field). Caller knows
733             // uCount and can convert to safearray if necessary.
734             pvar->vt = (DPKVT_BYREF | DPKVT_UI1);
735             Call(ReadBinaryData(pWS, uCount, uValue, &pvar->VT.pbVal));
736             break;
737 
738         case WMP_typSHORT:
739             if (1 == uCount)
740             {
741                 pvar->vt = DPKVT_UI2;
742                 pvar->VT.uiVal = (U16)(uValue & 0x0000FFFF);
743             }
744             else if (2 == uCount)
745             {
746                 pvar->vt = DPKVT_UI4;
747                 pvar->VT.ulVal = uValue;
748             }
749             else
750             {
751                 assert(FALSE); // NYI
752                 FailIf(TRUE, WMP_errNotYetImplemented);
753             }
754             break;
755 
756         default:
757             assert(FALSE); // Unhandled type
758             FailIf(TRUE, WMP_errNotYetImplemented);
759             break;
760     }
761 
762 Cleanup:
763     return err;
764 }
765 
766 
767 ERR WriteWmpDE(
768     __in_ecount(1) struct WMPStream* pWS,
769     size_t *pOffPos,
770     const __in_ecount(1) WmpDE* pDE,
771     const U8 *pbData,
772     U32 *pcbDataWrittenToOffset)
773 {
774     ERR err = WMP_errSuccess;
775     size_t offPos = *pOffPos;
776 
777     assert(-1 != pDE->uCount);
778     assert(-1 != pDE->uValueOrOffset);
779 
780     if (pcbDataWrittenToOffset)
781     {
782         assert(pbData); // Makes no sense to provide this arg without pbData
783         *pcbDataWrittenToOffset = 0;
784     }
785 
786     Call(PutUShort(pWS, offPos, pDE->uTag)); offPos += 2;
787     Call(PutUShort(pWS, offPos, pDE->uType)); offPos += 2;
788     Call(PutULong(pWS, offPos, pDE->uCount)); offPos += 4;
789 
790     switch (pDE->uType)
791     {
792 
793         case WMP_typASCII:
794         case WMP_typUNDEFINED:
795         case WMP_typBYTE:
796             if (pDE->uCount <= 4)
797             {
798                 U8 pad[4] = {0};
799                 Call(pWS->SetPos(pWS, offPos));
800 
801                 if (NULL == pbData)
802                     pbData = (U8*)&pDE->uValueOrOffset;
803 
804                 Call(pWS->Write(pWS, pbData, pDE->uCount));
805                 Call(pWS->Write(pWS, pad, 4 - pDE->uCount)); offPos += 4;
806             }
807             else
808             {
809                 Call(PutULong(pWS, offPos, pDE->uValueOrOffset)); offPos += 4;
810 
811                 // Write the data if requested to do so
812                 if (pbData)
813                 {
814                     Call(pWS->SetPos(pWS, pDE->uValueOrOffset));
815                     Call(pWS->Write(pWS, pbData, pDE->uCount));
816                     Call(pWS->SetPos(pWS, offPos));
817                     *pcbDataWrittenToOffset = pDE->uCount;
818                 }
819             }
820             break;
821 
822         case WMP_typSHORT:
823             if (pDE->uCount <= 2)
824             {
825                 U16 uiShrt1 = 0;
826                 U16 uiShrt2 = 0;
827 
828                 if (NULL == pbData)
829                     pbData = (U8*)&pDE->uValueOrOffset;
830 
831                 if (pDE->uCount > 0)
832                     uiShrt1 = *((U16*)pbData);
833 
834                 if (pDE->uCount > 1)
835                 {
836                     assert(FALSE); // Untested - remove this assert after this has been tested
837                     uiShrt2 = *(U16*)(pbData + 2);
838                 }
839 
840                 Call(PutUShort(pWS, offPos, uiShrt1)); offPos += 2;
841                 Call(PutUShort(pWS, offPos, uiShrt2)); offPos += 2;
842             }
843             else
844             {
845                 assert(FALSE); // Untested - remove this assert after this has been tested
846                 Call(PutULong(pWS, offPos, pDE->uValueOrOffset)); offPos += 4;
847 
848                 // Write the data if requested to do so
849                 if (pbData)
850                 {
851                     U32 i;
852                     Call(pWS->SetPos(pWS, pDE->uValueOrOffset));
853                     for (i = 0; i < pDE->uCount; i++)
854                     {
855                         const U16 uiShort = *(U16*)(pbData + i*sizeof(U16));
856                         Call(PutUShort(pWS, offPos, uiShort)); // Write one at a time for endian purposes - but inefficient
857                     }
858                     Call(pWS->SetPos(pWS, offPos));
859                     *pcbDataWrittenToOffset = pDE->uCount * sizeof(U16);
860                 }
861 
862             }
863             break;
864 
865         case WMP_typFLOAT:
866         case WMP_typLONG:
867             if (pDE->uCount <= 1)
868             {
869                 if (NULL == pbData)
870                     pbData = (U8*)&pDE->uValueOrOffset;
871 
872                 Call(PutULong(pWS, offPos, *(U32*)pbData)); offPos += 4;
873             }
874             else
875             {
876                 assert(FALSE); // Untested - remove this assert after this has been tested
877                 Call(PutULong(pWS, offPos, pDE->uValueOrOffset)); offPos += 4;
878 
879                 // Write the data if requested to do so
880                 if (pbData)
881                 {
882                     U32 i;
883                     Call(pWS->SetPos(pWS, pDE->uValueOrOffset));
884                     for (i = 0; i < pDE->uCount; i++)
885                     {
886                         const U32 uLong = *(U32*)(pbData + i*sizeof(U32));
887                         Call(PutULong(pWS, offPos, uLong)); // Write one at a time for endian purposes - but inefficient
888                     }
889                     Call(pWS->SetPos(pWS, offPos));
890                     *pcbDataWrittenToOffset = pDE->uCount * sizeof(U32);
891                 }
892             }
893             break;
894 
895         default:
896             assert(FALSE); // Alert the programmer
897             Call(WMP_errInvalidParameter);
898             break;
899     }
900 
901 Cleanup:
902     *pOffPos = offPos;
903     return err;
904 }
905 
906