github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/jxr/jxrlib/jxrgluelib/JXRMeta.c (about)

     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  
    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  
    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  
    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  
    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  
    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  
    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  
   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  
   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  
   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  
   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  
   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  
   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  
   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  
   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
   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
   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