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