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