Line data Source code
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 : /*! @cond Doxygen_Suppress */
48 :
49 : /************************************************************************/
50 : /* GDALJP2Box() */
51 : /************************************************************************/
52 :
53 : // GDALJP2Box does *not* take ownership of fpIn
54 7555 : GDALJP2Box::GDALJP2Box(VSILFILE *fpIn) : fpVSIL(fpIn)
55 : {
56 7555 : }
57 :
58 : /************************************************************************/
59 : /* ~GDALJP2Box() */
60 : /************************************************************************/
61 :
62 15110 : GDALJP2Box::~GDALJP2Box()
63 :
64 : {
65 : // Do not close fpVSIL. Ownership remains to the caller of GDALJP2Box
66 : // constructor
67 7555 : CPLFree(pabyData);
68 7555 : }
69 :
70 : /************************************************************************/
71 : /* SetOffset() */
72 : /************************************************************************/
73 :
74 19892 : int GDALJP2Box::SetOffset(GIntBig nNewOffset)
75 :
76 : {
77 19892 : szBoxType[0] = '\0';
78 19892 : return VSIFSeekL(fpVSIL, nNewOffset, SEEK_SET) == 0;
79 : }
80 :
81 : /************************************************************************/
82 : /* ReadFirst() */
83 : /************************************************************************/
84 :
85 2443 : int GDALJP2Box::ReadFirst()
86 :
87 : {
88 2443 : return SetOffset(0) && ReadBox();
89 : }
90 :
91 : /************************************************************************/
92 : /* ReadNext() */
93 : /************************************************************************/
94 :
95 15165 : int GDALJP2Box::ReadNext()
96 :
97 : {
98 15165 : return SetOffset(nBoxOffset + nBoxLength) && ReadBox();
99 : }
100 :
101 : /************************************************************************/
102 : /* ReadFirstChild() */
103 : /************************************************************************/
104 :
105 2325 : int GDALJP2Box::ReadFirstChild(GDALJP2Box *poSuperBox)
106 :
107 : {
108 2325 : if (poSuperBox == nullptr)
109 41 : return ReadFirst();
110 :
111 2284 : szBoxType[0] = '\0';
112 2284 : if (!poSuperBox->IsSuperBox())
113 0 : return FALSE;
114 :
115 2284 : return SetOffset(poSuperBox->nDataOffset) && ReadBox();
116 : }
117 :
118 : /************************************************************************/
119 : /* ReadNextChild() */
120 : /************************************************************************/
121 :
122 4628 : int GDALJP2Box::ReadNextChild(GDALJP2Box *poSuperBox)
123 :
124 : {
125 4628 : if (poSuperBox == nullptr)
126 210 : return ReadNext();
127 :
128 4418 : if (!ReadNext())
129 0 : return FALSE;
130 :
131 4418 : if (nBoxOffset >= poSuperBox->nBoxOffset + poSuperBox->nBoxLength)
132 : {
133 2030 : szBoxType[0] = '\0';
134 2030 : return FALSE;
135 : }
136 :
137 2388 : return TRUE;
138 : }
139 :
140 : /************************************************************************/
141 : /* ReadBox() */
142 : /************************************************************************/
143 :
144 19892 : int GDALJP2Box::ReadBox()
145 :
146 : {
147 19892 : GUInt32 nLBox = 0;
148 19892 : GUInt32 nTBox = 0;
149 :
150 19892 : nBoxOffset = VSIFTellL(fpVSIL);
151 :
152 38171 : if (VSIFReadL(&nLBox, 4, 1, fpVSIL) != 1 ||
153 18279 : VSIFReadL(&nTBox, 4, 1, fpVSIL) != 1)
154 : {
155 1614 : return FALSE;
156 : }
157 :
158 18278 : memcpy(szBoxType, &nTBox, 4);
159 18278 : szBoxType[4] = '\0';
160 :
161 18278 : nLBox = CPL_MSBWORD32(nLBox);
162 :
163 18278 : if (nLBox != 1)
164 : {
165 18273 : nBoxLength = nLBox;
166 18273 : nDataOffset = nBoxOffset + 8;
167 : }
168 : else
169 : {
170 5 : GByte abyXLBox[8] = {0};
171 5 : if (VSIFReadL(abyXLBox, 8, 1, fpVSIL) != 1)
172 0 : return FALSE;
173 :
174 : #ifdef CPL_HAS_GINT64
175 5 : CPL_MSBPTR64(abyXLBox);
176 5 : memcpy(&nBoxLength, abyXLBox, 8);
177 : #else
178 : // In case we lack a 64 bit integer type
179 : if (abyXLBox[0] != 0 || abyXLBox[1] != 0 || abyXLBox[2] != 0 ||
180 : abyXLBox[3] != 0)
181 : {
182 : CPLError(CE_Failure, CPLE_AppDefined,
183 : "Box size requires a 64 bit integer type");
184 : return FALSE;
185 : }
186 : CPL_MSBPTR32(abyXLBox + 4);
187 : memcpy(&nBoxLength, abyXLBox + 4, 4);
188 : #endif
189 5 : if (nBoxLength < 0)
190 : {
191 0 : CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
192 0 : return FALSE;
193 : }
194 5 : nDataOffset = nBoxOffset + 16;
195 : }
196 :
197 18278 : if (nBoxLength == 0 && m_bAllowGetFileSize)
198 : {
199 258 : if (VSIFSeekL(fpVSIL, 0, SEEK_END) != 0)
200 0 : return FALSE;
201 258 : nBoxLength = VSIFTellL(fpVSIL) - nBoxOffset;
202 258 : if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
203 0 : return FALSE;
204 : }
205 :
206 18278 : if (EQUAL(szBoxType, "uuid"))
207 : {
208 2763 : if (VSIFReadL(abyUUID, 16, 1, fpVSIL) != 1)
209 0 : return FALSE;
210 2763 : nDataOffset += 16;
211 : }
212 :
213 18278 : if (m_bAllowGetFileSize && GetDataLength() < 0)
214 : {
215 0 : CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
216 0 : return FALSE;
217 : }
218 :
219 18278 : return TRUE;
220 : }
221 :
222 : /************************************************************************/
223 : /* IsSuperBox() */
224 : /************************************************************************/
225 :
226 2685 : int GDALJP2Box::IsSuperBox()
227 :
228 : {
229 4572 : if (EQUAL(GetType(), "asoc") || EQUAL(GetType(), "jp2h") ||
230 4572 : EQUAL(GetType(), "res ") || EQUAL(GetType(), "jumb"))
231 2364 : return TRUE;
232 :
233 321 : return FALSE;
234 : }
235 :
236 : /************************************************************************/
237 : /* ReadBoxData() */
238 : /************************************************************************/
239 :
240 2792 : GByte *GDALJP2Box::ReadBoxData()
241 :
242 : {
243 2792 : GIntBig nDataLength = GetDataLength();
244 2792 : if (nDataLength > 100 * 1024 * 1024)
245 : {
246 0 : CPLError(CE_Failure, CPLE_AppDefined,
247 : "Too big box : " CPL_FRMT_GIB " bytes", nDataLength);
248 0 : return nullptr;
249 : }
250 :
251 2792 : if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
252 0 : return nullptr;
253 :
254 : char *pszData = static_cast<char *>(
255 2792 : VSI_MALLOC_VERBOSE(static_cast<int>(nDataLength) + 1));
256 2792 : if (pszData == nullptr)
257 0 : return nullptr;
258 :
259 2792 : if (static_cast<GIntBig>(VSIFReadL(
260 2792 : pszData, 1, static_cast<int>(nDataLength), fpVSIL)) != nDataLength)
261 : {
262 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
263 0 : CPLFree(pszData);
264 0 : return nullptr;
265 : }
266 :
267 2792 : pszData[nDataLength] = '\0';
268 :
269 2792 : return reinterpret_cast<GByte *>(pszData);
270 : }
271 :
272 : /************************************************************************/
273 : /* GetDataLength() */
274 : /************************************************************************/
275 :
276 39688 : GIntBig GDALJP2Box::GetDataLength() const
277 : {
278 39688 : return nBoxLength - (nDataOffset - nBoxOffset);
279 : }
280 :
281 : /************************************************************************/
282 : /* DumpReadable() */
283 : /************************************************************************/
284 :
285 0 : int GDALJP2Box::DumpReadable(FILE *fpOut, int nIndentLevel)
286 :
287 : {
288 0 : if (fpOut == nullptr)
289 0 : fpOut = stdout;
290 :
291 0 : for (int i = 0; i < nIndentLevel; ++i)
292 0 : fprintf(fpOut, " ");
293 :
294 : char szBuffer[128];
295 0 : CPLsnprintf(szBuffer, sizeof(szBuffer),
296 : " Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
297 : ", Data Size=" CPL_FRMT_GIB,
298 0 : szBoxType, nBoxOffset, nDataOffset, GetDataLength());
299 0 : fprintf(fpOut, "%s", szBuffer);
300 :
301 0 : if (IsSuperBox())
302 : {
303 0 : fprintf(fpOut, " (super)");
304 : }
305 :
306 0 : fprintf(fpOut, "\n");
307 :
308 0 : if (IsSuperBox())
309 : {
310 0 : GDALJP2Box oSubBox(GetFILE());
311 :
312 0 : for (oSubBox.ReadFirstChild(this); strlen(oSubBox.GetType()) > 0;
313 0 : oSubBox.ReadNextChild(this))
314 : {
315 0 : oSubBox.DumpReadable(fpOut, nIndentLevel + 1);
316 : }
317 : }
318 :
319 0 : if (EQUAL(GetType(), "uuid"))
320 : {
321 0 : char *pszHex = CPLBinaryToHex(16, GetUUID());
322 0 : for (int i = 0; i < nIndentLevel; ++i)
323 0 : fprintf(fpOut, " ");
324 :
325 0 : fprintf(fpOut, " UUID=%s", pszHex);
326 :
327 0 : if (EQUAL(pszHex, "B14BF8BD083D4B43A5AE8CD7D5A6CE03"))
328 0 : fprintf(fpOut, " (GeoTIFF)");
329 0 : if (EQUAL(pszHex, "96A9F1F1DC98402DA7AED68E34451809"))
330 0 : fprintf(fpOut, " (MSI Worldfile)");
331 0 : if (EQUAL(pszHex, "BE7ACFCB97A942E89C71999491E3AFAC"))
332 0 : fprintf(fpOut, " (XMP)");
333 0 : CPLFree(pszHex);
334 :
335 0 : fprintf(fpOut, "\n");
336 : }
337 :
338 0 : return 0;
339 : }
340 :
341 : /************************************************************************/
342 : /* SetType() */
343 : /************************************************************************/
344 :
345 1991 : void GDALJP2Box::SetType(const char *pszType)
346 :
347 : {
348 1991 : CPLAssert(strlen(pszType) == 4);
349 :
350 1991 : memcpy(szBoxType, pszType, 4);
351 1991 : szBoxType[4] = '\0';
352 1991 : }
353 :
354 : /************************************************************************/
355 : /* GetWritableBoxData() */
356 : /************************************************************************/
357 :
358 30 : GByte *GDALJP2Box::GetWritableBoxData() const
359 : {
360 : GByte *pabyRet =
361 30 : static_cast<GByte *>(CPLMalloc(static_cast<GUInt32>(nBoxLength)));
362 30 : const GUInt32 nLBox = CPL_MSBWORD32(static_cast<GUInt32>(nBoxLength));
363 30 : memcpy(pabyRet, &nLBox, sizeof(GUInt32));
364 30 : memcpy(pabyRet + 4, szBoxType, 4);
365 30 : memcpy(pabyRet + 8, pabyData, static_cast<GUInt32>(nBoxLength) - 8);
366 30 : return pabyRet;
367 : }
368 :
369 : /************************************************************************/
370 : /* SetWritableData() */
371 : /************************************************************************/
372 :
373 767 : void GDALJP2Box::SetWritableData(int nLength, const GByte *pabyDataIn)
374 :
375 : {
376 767 : CPLFree(pabyData);
377 :
378 767 : pabyData = static_cast<GByte *>(CPLMalloc(nLength));
379 767 : memcpy(pabyData, pabyDataIn, nLength);
380 :
381 767 : nBoxOffset = -9; // Virtual offsets for data length computation.
382 767 : nDataOffset = -1;
383 :
384 767 : nBoxLength = 8 + nLength;
385 767 : }
386 :
387 : /************************************************************************/
388 : /* AppendWritableData() */
389 : /************************************************************************/
390 :
391 4713 : void GDALJP2Box::AppendWritableData(int nLength, const void *pabyDataIn)
392 :
393 : {
394 4713 : if (pabyData == nullptr)
395 : {
396 1219 : nBoxOffset = -9; // Virtual offsets for data length computation.
397 1219 : nDataOffset = -1;
398 1219 : nBoxLength = 8;
399 : }
400 :
401 4713 : pabyData = static_cast<GByte *>(
402 4713 : CPLRealloc(pabyData, static_cast<size_t>(GetDataLength() + nLength)));
403 4713 : memcpy(pabyData + GetDataLength(), pabyDataIn, nLength);
404 :
405 4713 : nBoxLength += nLength;
406 4713 : }
407 :
408 : /************************************************************************/
409 : /* AppendUInt32() */
410 : /************************************************************************/
411 :
412 868 : void GDALJP2Box::AppendUInt32(GUInt32 nVal)
413 : {
414 868 : CPL_MSBPTR32(&nVal);
415 868 : AppendWritableData(4, &nVal);
416 868 : }
417 :
418 : /************************************************************************/
419 : /* AppendUInt16() */
420 : /************************************************************************/
421 :
422 614 : void GDALJP2Box::AppendUInt16(GUInt16 nVal)
423 : {
424 614 : CPL_MSBPTR16(&nVal);
425 614 : AppendWritableData(2, &nVal);
426 614 : }
427 :
428 : /************************************************************************/
429 : /* AppendUInt8() */
430 : /************************************************************************/
431 :
432 1996 : void GDALJP2Box::AppendUInt8(GByte nVal)
433 : {
434 1996 : AppendWritableData(1, &nVal);
435 1996 : }
436 :
437 : /************************************************************************/
438 : /* CreateUUIDBox() */
439 : /************************************************************************/
440 :
441 226 : GDALJP2Box *GDALJP2Box::CreateUUIDBox(const GByte *pabyUUID, int nDataSize,
442 : const GByte *pabyDataIn)
443 :
444 : {
445 226 : GDALJP2Box *const poBox = new GDALJP2Box();
446 226 : poBox->SetType("uuid");
447 :
448 226 : poBox->AppendWritableData(16, pabyUUID);
449 226 : poBox->AppendWritableData(nDataSize, pabyDataIn);
450 :
451 226 : return poBox;
452 : }
453 :
454 : /************************************************************************/
455 : /* CreateAsocBox() */
456 : /************************************************************************/
457 :
458 198 : GDALJP2Box *GDALJP2Box::CreateAsocBox(int nCount,
459 : const GDALJP2Box *const *papoBoxes)
460 : {
461 198 : return CreateSuperBox("asoc", nCount, papoBoxes);
462 : }
463 :
464 : /************************************************************************/
465 : /* CreateAsocBox() */
466 : /************************************************************************/
467 :
468 453 : GDALJP2Box *GDALJP2Box::CreateSuperBox(const char *pszType, int nCount,
469 : const GDALJP2Box *const *papoBoxes)
470 : {
471 453 : int nDataSize = 0;
472 :
473 : /* -------------------------------------------------------------------- */
474 : /* Compute size of data area of asoc box. */
475 : /* -------------------------------------------------------------------- */
476 1406 : for (int iBox = 0; iBox < nCount; ++iBox)
477 953 : nDataSize += 8 + static_cast<int>(papoBoxes[iBox]->GetDataLength());
478 :
479 453 : GByte *pabyNext = static_cast<GByte *>(CPLMalloc(nDataSize));
480 453 : GByte *pabyCompositeData = pabyNext;
481 :
482 : /* -------------------------------------------------------------------- */
483 : /* Copy subboxes headers and data into buffer. */
484 : /* -------------------------------------------------------------------- */
485 1406 : for (int iBox = 0; iBox < nCount; ++iBox)
486 : {
487 953 : GUInt32 nLBox =
488 953 : CPL_MSBWORD32(static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
489 953 : memcpy(pabyNext, &nLBox, 4);
490 953 : pabyNext += 4;
491 :
492 953 : memcpy(pabyNext, papoBoxes[iBox]->szBoxType, 4);
493 953 : pabyNext += 4;
494 :
495 953 : memcpy(pabyNext, papoBoxes[iBox]->pabyData,
496 953 : static_cast<int>(papoBoxes[iBox]->GetDataLength()));
497 953 : pabyNext += papoBoxes[iBox]->GetDataLength();
498 : }
499 :
500 : /* -------------------------------------------------------------------- */
501 : /* Create asoc box. */
502 : /* -------------------------------------------------------------------- */
503 453 : GDALJP2Box *const poAsoc = new GDALJP2Box();
504 :
505 453 : poAsoc->SetType(pszType);
506 453 : poAsoc->SetWritableData(nDataSize, pabyCompositeData);
507 :
508 453 : CPLFree(pabyCompositeData);
509 :
510 453 : return poAsoc;
511 : }
512 :
513 : /************************************************************************/
514 : /* CreateLblBox() */
515 : /************************************************************************/
516 :
517 86 : GDALJP2Box *GDALJP2Box::CreateLblBox(const char *pszLabel)
518 :
519 : {
520 86 : GDALJP2Box *const poBox = new GDALJP2Box();
521 86 : poBox->SetType("lbl ");
522 86 : poBox->SetWritableData(static_cast<int>(strlen(pszLabel) + 1),
523 : reinterpret_cast<const GByte *>(pszLabel));
524 :
525 86 : return poBox;
526 : }
527 :
528 : /************************************************************************/
529 : /* CreateLabelledXMLAssoc() */
530 : /************************************************************************/
531 :
532 107 : GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc(const char *pszLabel,
533 : const char *pszXML)
534 :
535 : {
536 214 : GDALJP2Box oLabel;
537 107 : oLabel.SetType("lbl ");
538 107 : oLabel.SetWritableData(static_cast<int>(strlen(pszLabel) + 1),
539 : reinterpret_cast<const GByte *>(pszLabel));
540 :
541 214 : GDALJP2Box oXML;
542 107 : oXML.SetType("xml ");
543 107 : oXML.SetWritableData(static_cast<int>(strlen(pszXML) + 1),
544 : reinterpret_cast<const GByte *>(pszXML));
545 :
546 107 : GDALJP2Box *aoList[2] = {&oLabel, &oXML};
547 :
548 214 : return CreateAsocBox(2, aoList);
549 : }
550 :
551 : /************************************************************************/
552 : /* CreateJUMBFDescriptionBox() */
553 : /************************************************************************/
554 :
555 38 : GDALJP2Box *GDALJP2Box::CreateJUMBFDescriptionBox(const GByte *pabyUUIDType,
556 : const char *pszLabel)
557 :
558 : {
559 38 : GDALJP2Box *const poBox = new GDALJP2Box();
560 38 : poBox->SetType("jumd");
561 :
562 38 : poBox->AppendWritableData(16, pabyUUIDType);
563 38 : poBox->AppendUInt8(3); // requestable field
564 38 : const size_t nLabelLen = strlen(pszLabel) + 1;
565 38 : poBox->AppendWritableData(static_cast<int>(nLabelLen), pszLabel);
566 :
567 38 : return poBox;
568 : }
569 :
570 : /************************************************************************/
571 : /* CreateJUMBFBox() */
572 : /************************************************************************/
573 :
574 38 : GDALJP2Box *GDALJP2Box::CreateJUMBFBox(const GDALJP2Box *poJUMBFDescriptionBox,
575 : int nCount,
576 : const GDALJP2Box *const *papoBoxes)
577 : {
578 76 : std::vector<const GDALJP2Box *> apoBoxes;
579 38 : apoBoxes.push_back(poJUMBFDescriptionBox);
580 38 : apoBoxes.insert(apoBoxes.end(), papoBoxes, papoBoxes + nCount);
581 38 : return CreateSuperBox("jumb", static_cast<int>(apoBoxes.size()),
582 114 : apoBoxes.data());
583 : }
584 :
585 : /*! @endcond */
|