1 /******************************************************************************
2  *
3  * Project:  GDAL
4  * Purpose:  GDALJP2Box Implementation - Low level JP2 box reader.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "gdaljp2metadata.h"
32 
33 #include <cstddef>
34 #include <cstdio>
35 #include <cstring>
36 #if HAVE_FCNTL_H
37 #  include <fcntl.h>
38 #endif
39 
40 #include <algorithm>
41 
42 #include "cpl_conv.h"
43 #include "cpl_error.h"
44 #include "cpl_string.h"
45 #include "cpl_vsi.h"
46 
47 CPL_CVSID("$Id: gdaljp2box.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
48 
49 /*! @cond Doxygen_Suppress */
50 
51 /************************************************************************/
52 /*                             GDALJP2Box()                             */
53 /************************************************************************/
54 
55 // GDALJP2Box does *not* take ownership of fpIn
GDALJP2Box(VSILFILE * fpIn)56 GDALJP2Box::GDALJP2Box( VSILFILE *fpIn ) :
57     fpVSIL(fpIn),
58     szBoxType{'\0', '\0', '\0', '\0', '\0'},
59     nBoxOffset(-1),
60     nBoxLength(0),
61     nDataOffset(-1),
62     pabyData(nullptr)
63 {
64 }
65 
66 /************************************************************************/
67 /*                            ~GDALJP2Box()                             */
68 /************************************************************************/
69 
~GDALJP2Box()70 GDALJP2Box::~GDALJP2Box()
71 
72 {
73     // Do not close fpVSIL. Ownership remains to the caller of GDALJP2Box
74     // constructor
75     CPLFree( pabyData );
76 }
77 
78 /************************************************************************/
79 /*                             SetOffset()                              */
80 /************************************************************************/
81 
SetOffset(GIntBig nNewOffset)82 int GDALJP2Box::SetOffset( GIntBig nNewOffset )
83 
84 {
85     szBoxType[0] = '\0';
86     return VSIFSeekL( fpVSIL, nNewOffset, SEEK_SET ) == 0;
87 }
88 
89 /************************************************************************/
90 /*                             ReadFirst()                              */
91 /************************************************************************/
92 
ReadFirst()93 int GDALJP2Box::ReadFirst()
94 
95 {
96     return SetOffset(0) && ReadBox();
97 }
98 
99 /************************************************************************/
100 /*                              ReadNext()                              */
101 /************************************************************************/
102 
ReadNext()103 int GDALJP2Box::ReadNext()
104 
105 {
106     return SetOffset( nBoxOffset + nBoxLength ) && ReadBox();
107 }
108 
109 /************************************************************************/
110 /*                           ReadFirstChild()                           */
111 /************************************************************************/
112 
ReadFirstChild(GDALJP2Box * poSuperBox)113 int GDALJP2Box::ReadFirstChild( GDALJP2Box *poSuperBox )
114 
115 {
116     if( poSuperBox == nullptr )
117         return ReadFirst();
118 
119     szBoxType[0] = '\0';
120     if( !poSuperBox->IsSuperBox() )
121         return FALSE;
122 
123     return SetOffset( poSuperBox->nDataOffset ) && ReadBox();
124 }
125 
126 /************************************************************************/
127 /*                           ReadNextChild()                            */
128 /************************************************************************/
129 
ReadNextChild(GDALJP2Box * poSuperBox)130 int GDALJP2Box::ReadNextChild( GDALJP2Box *poSuperBox )
131 
132 {
133     if( poSuperBox == nullptr )
134         return ReadNext();
135 
136     if( !ReadNext() )
137         return FALSE;
138 
139     if( nBoxOffset >= poSuperBox->nBoxOffset + poSuperBox->nBoxLength )
140     {
141         szBoxType[0] = '\0';
142         return FALSE;
143     }
144 
145     return TRUE;
146 }
147 
148 /************************************************************************/
149 /*                              ReadBox()                               */
150 /************************************************************************/
151 
ReadBox()152 int GDALJP2Box::ReadBox()
153 
154 {
155     GUInt32 nLBox = 0;
156     GUInt32 nTBox = 0;
157 
158     nBoxOffset = VSIFTellL( fpVSIL );
159 
160     if( VSIFReadL( &nLBox, 4, 1, fpVSIL ) != 1
161         || VSIFReadL( &nTBox, 4, 1, fpVSIL ) != 1 )
162     {
163         return FALSE;
164     }
165 
166     memcpy( szBoxType, &nTBox, 4 );
167     szBoxType[4] = '\0';
168 
169     nLBox = CPL_MSBWORD32( nLBox );
170 
171     if( nLBox != 1 )
172     {
173         nBoxLength = nLBox;
174         nDataOffset = nBoxOffset + 8;
175     }
176     else
177     {
178         GByte abyXLBox[8] = { 0 };
179         if( VSIFReadL( abyXLBox, 8, 1, fpVSIL ) != 1 )
180             return FALSE;
181 
182 #ifdef CPL_HAS_GINT64
183         CPL_MSBPTR64( abyXLBox );
184         memcpy( &nBoxLength, abyXLBox, 8 );
185 #else
186         // In case we lack a 64 bit integer type
187         if( abyXLBox[0] != 0 || abyXLBox[1] != 0 || abyXLBox[2] != 0 ||
188             abyXLBox[3] != 0 )
189         {
190             CPLError(CE_Failure, CPLE_AppDefined,
191                         "Box size requires a 64 bit integer type");
192             return FALSE;
193         }
194         CPL_MSBPTR32( abyXLBox+4 );
195         memcpy( &nBoxLength, abyXLBox+4, 4 );
196 #endif
197         if( nBoxLength < 0 )
198         {
199             CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
200             return FALSE;
201         }
202         nDataOffset = nBoxOffset + 16;
203     }
204 
205     if( nBoxLength == 0 )
206     {
207         if( VSIFSeekL( fpVSIL, 0, SEEK_END ) != 0 )
208             return FALSE;
209         nBoxLength = VSIFTellL( fpVSIL ) - nBoxOffset;
210         if( VSIFSeekL( fpVSIL, nDataOffset, SEEK_SET ) != 0 )
211             return FALSE;
212     }
213 
214     if( EQUAL(szBoxType,"uuid") )
215     {
216         if( VSIFReadL( abyUUID, 16, 1, fpVSIL ) != 1 )
217             return FALSE;
218         nDataOffset += 16;
219     }
220 
221     if( GetDataLength() < 0 )
222     {
223         CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
224         return FALSE;
225     }
226 
227     return TRUE;
228 }
229 
230 /************************************************************************/
231 /*                             IsSuperBox()                             */
232 /************************************************************************/
233 
IsSuperBox()234 int GDALJP2Box::IsSuperBox()
235 
236 {
237     if( EQUAL(GetType(),"asoc") || EQUAL(GetType(),"jp2h")
238         || EQUAL(GetType(),"res ") )
239         return TRUE;
240 
241     return FALSE;
242 }
243 
244 /************************************************************************/
245 /*                            ReadBoxData()                             */
246 /************************************************************************/
247 
ReadBoxData()248 GByte *GDALJP2Box::ReadBoxData()
249 
250 {
251     GIntBig nDataLength = GetDataLength();
252     if( nDataLength > 100 * 1024 * 1024 )
253     {
254         CPLError( CE_Failure, CPLE_AppDefined,
255                   "Too big box : " CPL_FRMT_GIB " bytes",
256                   nDataLength );
257         return nullptr;
258     }
259 
260     if( VSIFSeekL( fpVSIL, nDataOffset, SEEK_SET ) != 0 )
261         return nullptr;
262 
263     char *pszData = static_cast<char *>(
264         VSI_MALLOC_VERBOSE( static_cast<int>(nDataLength) + 1) );
265     if( pszData == nullptr )
266         return nullptr;
267 
268     if( static_cast<GIntBig>( VSIFReadL(
269            pszData, 1, static_cast<int>(nDataLength), fpVSIL ) )
270         != nDataLength )
271     {
272         CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
273         CPLFree( pszData );
274         return nullptr;
275     }
276 
277     pszData[nDataLength] = '\0';
278 
279     return reinterpret_cast<GByte *>( pszData );
280 }
281 
282 /************************************************************************/
283 /*                           GetDataLength()                            */
284 /************************************************************************/
285 
GetDataLength()286 GIntBig GDALJP2Box::GetDataLength()
287 {
288     return nBoxLength - (nDataOffset - nBoxOffset);
289 }
290 
291 /************************************************************************/
292 /*                            DumpReadable()                            */
293 /************************************************************************/
294 
DumpReadable(FILE * fpOut,int nIndentLevel)295 int GDALJP2Box::DumpReadable( FILE *fpOut, int nIndentLevel )
296 
297 {
298     if( fpOut == nullptr )
299         fpOut = stdout;
300 
301     for( int i=0; i < nIndentLevel; ++i)
302         fprintf( fpOut, "  " );
303 
304     char szBuffer[128];
305     CPLsnprintf( szBuffer, sizeof(szBuffer),
306              "  Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
307              ", Data Size=" CPL_FRMT_GIB,
308              szBoxType, nBoxOffset, nDataOffset,
309              GetDataLength() );
310     fprintf( fpOut, "%s", szBuffer );
311 
312     if( IsSuperBox() )
313     {
314         fprintf( fpOut, " (super)" );
315     }
316 
317     fprintf( fpOut, "\n" );
318 
319     if( IsSuperBox() )
320     {
321         GDALJP2Box oSubBox( GetFILE() );
322 
323         for( oSubBox.ReadFirstChild( this );
324              strlen(oSubBox.GetType()) > 0;
325              oSubBox.ReadNextChild( this ) )
326         {
327             oSubBox.DumpReadable( fpOut, nIndentLevel + 1 );
328         }
329     }
330 
331     if( EQUAL(GetType(),"uuid") )
332     {
333         char *pszHex = CPLBinaryToHex( 16, GetUUID() );
334         for( int i = 0; i < nIndentLevel; ++i )
335             fprintf( fpOut, "  " );
336 
337         fprintf( fpOut, "    UUID=%s", pszHex );
338 
339         if( EQUAL(pszHex,"B14BF8BD083D4B43A5AE8CD7D5A6CE03") )
340             fprintf( fpOut, " (GeoTIFF)" );
341         if( EQUAL(pszHex,"96A9F1F1DC98402DA7AED68E34451809") )
342             fprintf( fpOut, " (MSI Worldfile)" );
343         if( EQUAL(pszHex,"BE7ACFCB97A942E89C71999491E3AFAC") )
344             fprintf( fpOut, " (XMP)" );
345         CPLFree( pszHex );
346 
347         fprintf( fpOut, "\n" );
348     }
349 
350     return 0;
351 }
352 
353 /************************************************************************/
354 /*                              SetType()                               */
355 /************************************************************************/
356 
SetType(const char * pszType)357 void GDALJP2Box::SetType( const char *pszType )
358 
359 {
360     CPLAssert( strlen(pszType) == 4 );
361 
362     memcpy(szBoxType, pszType, 4);
363     szBoxType[4] = '\0';
364 }
365 
366 /************************************************************************/
367 /*                          SetWritableData()                           */
368 /************************************************************************/
369 
SetWritableData(int nLength,const GByte * pabyDataIn)370 void GDALJP2Box::SetWritableData( int nLength, const GByte *pabyDataIn )
371 
372 {
373     CPLFree( pabyData );
374 
375     pabyData = static_cast<GByte *>( CPLMalloc(nLength) );
376     memcpy( pabyData, pabyDataIn, nLength );
377 
378     nBoxOffset = -9; // Virtual offsets for data length computation.
379     nDataOffset = -1;
380 
381     nBoxLength = 8 + nLength;
382 }
383 
384 /************************************************************************/
385 /*                          AppendWritableData()                        */
386 /************************************************************************/
387 
AppendWritableData(int nLength,const void * pabyDataIn)388 void GDALJP2Box::AppendWritableData( int nLength, const void *pabyDataIn )
389 
390 {
391     if( pabyData == nullptr )
392     {
393         nBoxOffset = -9; // Virtual offsets for data length computation.
394         nDataOffset = -1;
395         nBoxLength = 8;
396     }
397 
398     pabyData = static_cast<GByte *>(
399         CPLRealloc(pabyData, static_cast<size_t>(GetDataLength() + nLength)) );
400     memcpy( pabyData + GetDataLength(), pabyDataIn, nLength );
401 
402     nBoxLength += nLength;
403 }
404 
405 /************************************************************************/
406 /*                              AppendUInt32()                          */
407 /************************************************************************/
408 
AppendUInt32(GUInt32 nVal)409 void GDALJP2Box::AppendUInt32( GUInt32 nVal )
410 {
411     CPL_MSBPTR32(&nVal);
412     AppendWritableData(4, &nVal);
413 }
414 
415 /************************************************************************/
416 /*                              AppendUInt16()                          */
417 /************************************************************************/
418 
AppendUInt16(GUInt16 nVal)419 void GDALJP2Box::AppendUInt16( GUInt16 nVal )
420 {
421     CPL_MSBPTR16(&nVal);
422     AppendWritableData(2, &nVal);
423 }
424 /************************************************************************/
425 /*                              AppendUInt8()                           */
426 /************************************************************************/
427 
AppendUInt8(GByte nVal)428 void GDALJP2Box::AppendUInt8( GByte nVal )
429 {
430     AppendWritableData(1, &nVal);
431 }
432 
433 /************************************************************************/
434 /*                           CreateUUIDBox()                            */
435 /************************************************************************/
436 
CreateUUIDBox(const GByte * pabyUUID,int nDataSize,const GByte * pabyDataIn)437 GDALJP2Box *GDALJP2Box::CreateUUIDBox(
438     const GByte *pabyUUID, int nDataSize, const GByte *pabyDataIn )
439 
440 {
441     GDALJP2Box * const poBox = new GDALJP2Box();
442     poBox->SetType( "uuid" );
443 
444     poBox->AppendWritableData( 16, pabyUUID );
445     poBox->AppendWritableData( nDataSize, pabyDataIn );
446 
447     return poBox;
448 }
449 
450 /************************************************************************/
451 /*                           CreateAsocBox()                            */
452 /************************************************************************/
453 
CreateAsocBox(int nCount,GDALJP2Box ** papoBoxes)454 GDALJP2Box *GDALJP2Box::CreateAsocBox( int nCount, GDALJP2Box **papoBoxes )
455 {
456     return CreateSuperBox("asoc", nCount, papoBoxes);
457 }
458 
459 /************************************************************************/
460 /*                           CreateAsocBox()                            */
461 /************************************************************************/
462 
CreateSuperBox(const char * pszType,int nCount,GDALJP2Box ** papoBoxes)463 GDALJP2Box *GDALJP2Box::CreateSuperBox( const char* pszType,
464                                         int nCount, GDALJP2Box **papoBoxes )
465 {
466     int nDataSize = 0;
467 
468 /* -------------------------------------------------------------------- */
469 /*      Compute size of data area of asoc box.                          */
470 /* -------------------------------------------------------------------- */
471     for( int iBox = 0; iBox < nCount; ++iBox )
472         nDataSize += 8 + static_cast<int>( papoBoxes[iBox]->GetDataLength() );
473 
474     GByte *pabyNext = static_cast<GByte *>( CPLMalloc(nDataSize) );
475     GByte *pabyCompositeData = pabyNext;
476 
477 /* -------------------------------------------------------------------- */
478 /*      Copy subboxes headers and data into buffer.                     */
479 /* -------------------------------------------------------------------- */
480     for( int iBox = 0; iBox < nCount; ++iBox )
481     {
482         GUInt32 nLBox = CPL_MSBWORD32(
483             static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
484         memcpy( pabyNext, &nLBox, 4 );
485         pabyNext += 4;
486 
487         memcpy( pabyNext, papoBoxes[iBox]->szBoxType, 4 );
488         pabyNext += 4;
489 
490         memcpy( pabyNext, papoBoxes[iBox]->pabyData,
491                 static_cast<int>(papoBoxes[iBox]->GetDataLength()) );
492         pabyNext += papoBoxes[iBox]->GetDataLength();
493     }
494 
495 /* -------------------------------------------------------------------- */
496 /*      Create asoc box.                                                */
497 /* -------------------------------------------------------------------- */
498     GDALJP2Box * const poAsoc = new GDALJP2Box();
499 
500     poAsoc->SetType( pszType );
501     poAsoc->SetWritableData( nDataSize, pabyCompositeData );
502 
503     CPLFree( pabyCompositeData );
504 
505     return poAsoc;
506 }
507 
508 /************************************************************************/
509 /*                            CreateLblBox()                            */
510 /************************************************************************/
511 
CreateLblBox(const char * pszLabel)512 GDALJP2Box *GDALJP2Box::CreateLblBox( const char *pszLabel )
513 
514 {
515     GDALJP2Box * const poBox = new GDALJP2Box();
516     poBox->SetType( "lbl " );
517     poBox->SetWritableData( static_cast<int>(strlen(pszLabel)+1),
518                             reinterpret_cast<const GByte *>( pszLabel ) );
519 
520     return poBox;
521 }
522 
523 /************************************************************************/
524 /*                       CreateLabelledXMLAssoc()                       */
525 /************************************************************************/
526 
CreateLabelledXMLAssoc(const char * pszLabel,const char * pszXML)527 GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc( const char *pszLabel,
528                                                 const char *pszXML )
529 
530 {
531     GDALJP2Box oLabel;
532     oLabel.SetType( "lbl " );
533     oLabel.SetWritableData( static_cast<int>(strlen(pszLabel)+1),
534                             reinterpret_cast<const GByte *>(pszLabel) );
535 
536     GDALJP2Box oXML;
537     oXML.SetType( "xml " );
538     oXML.SetWritableData( static_cast<int>(strlen(pszXML)+1),
539                           reinterpret_cast<const GByte *>(pszXML) );
540 
541     GDALJP2Box *aoList[2] = { &oLabel, &oXML };
542 
543     return CreateAsocBox( 2, aoList );
544 }
545 
546 /*! @endcond */
547