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