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(vsi_l_offset 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, static_cast<vsi_l_offset>(nDataOffset),
171 258 : SEEK_SET) != 0)
172 0 : return FALSE;
173 : }
174 :
175 18375 : if (EQUAL(szBoxType, "uuid"))
176 : {
177 2774 : if (VSIFReadL(abyUUID, 16, 1, fpVSIL) != 1)
178 0 : return FALSE;
179 2774 : nDataOffset += 16;
180 : }
181 :
182 18375 : if (m_bAllowGetFileSize && GetDataLength() < 0)
183 : {
184 0 : CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
185 0 : return FALSE;
186 : }
187 :
188 18375 : return TRUE;
189 : }
190 :
191 : /************************************************************************/
192 : /* IsSuperBox() */
193 : /************************************************************************/
194 :
195 2698 : int GDALJP2Box::IsSuperBox()
196 :
197 : {
198 4589 : if (EQUAL(GetType(), "asoc") || EQUAL(GetType(), "jp2h") ||
199 4589 : EQUAL(GetType(), "res ") || EQUAL(GetType(), "jumb"))
200 2377 : return TRUE;
201 :
202 321 : return FALSE;
203 : }
204 :
205 : /************************************************************************/
206 : /* ReadBoxData() */
207 : /************************************************************************/
208 :
209 2805 : GByte *GDALJP2Box::ReadBoxData()
210 :
211 : {
212 2805 : GIntBig nDataLength = GetDataLength();
213 2805 : if (nDataLength > 100 * 1024 * 1024)
214 : {
215 0 : CPLError(CE_Failure, CPLE_AppDefined,
216 : "Too big box : " CPL_FRMT_GIB " bytes", nDataLength);
217 0 : return nullptr;
218 : }
219 :
220 2805 : if (VSIFSeekL(fpVSIL, static_cast<vsi_l_offset>(nDataOffset), SEEK_SET) !=
221 : 0)
222 0 : return nullptr;
223 :
224 : char *pszData = static_cast<char *>(
225 2805 : VSI_MALLOC_VERBOSE(static_cast<int>(nDataLength) + 1));
226 2805 : if (pszData == nullptr)
227 0 : return nullptr;
228 :
229 2805 : if (static_cast<GIntBig>(VSIFReadL(
230 2805 : pszData, 1, static_cast<int>(nDataLength), fpVSIL)) != nDataLength)
231 : {
232 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
233 0 : CPLFree(pszData);
234 0 : return nullptr;
235 : }
236 :
237 2805 : pszData[nDataLength] = '\0';
238 :
239 2805 : return reinterpret_cast<GByte *>(pszData);
240 : }
241 :
242 : /************************************************************************/
243 : /* GetDataLength() */
244 : /************************************************************************/
245 :
246 29466 : GIntBig GDALJP2Box::GetDataLength() const
247 : {
248 29466 : return nBoxLength - (nDataOffset - nBoxOffset);
249 : }
250 :
251 : /************************************************************************/
252 : /* DumpReadable() */
253 : /************************************************************************/
254 :
255 0 : int GDALJP2Box::DumpReadable(FILE *fpOut, int nIndentLevel)
256 :
257 : {
258 0 : if (fpOut == nullptr)
259 0 : fpOut = stdout;
260 :
261 0 : for (int i = 0; i < nIndentLevel; ++i)
262 0 : fprintf(fpOut, " ");
263 :
264 : char szBuffer[128];
265 0 : CPLsnprintf(szBuffer, sizeof(szBuffer),
266 : " Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
267 : ", Data Size=" CPL_FRMT_GIB,
268 0 : szBoxType, nBoxOffset, nDataOffset, GetDataLength());
269 0 : fprintf(fpOut, "%s", szBuffer);
270 :
271 0 : if (IsSuperBox())
272 : {
273 0 : fprintf(fpOut, " (super)");
274 : }
275 :
276 0 : fprintf(fpOut, "\n");
277 :
278 0 : if (IsSuperBox())
279 : {
280 0 : GDALJP2Box oSubBox(GetFILE());
281 :
282 0 : for (oSubBox.ReadFirstChild(this); strlen(oSubBox.GetType()) > 0;
283 0 : oSubBox.ReadNextChild(this))
284 : {
285 0 : oSubBox.DumpReadable(fpOut, nIndentLevel + 1);
286 : }
287 : }
288 :
289 0 : if (EQUAL(GetType(), "uuid"))
290 : {
291 0 : char *pszHex = CPLBinaryToHex(16, GetUUID());
292 0 : for (int i = 0; i < nIndentLevel; ++i)
293 0 : fprintf(fpOut, " ");
294 :
295 0 : fprintf(fpOut, " UUID=%s", pszHex);
296 :
297 0 : if (EQUAL(pszHex, "B14BF8BD083D4B43A5AE8CD7D5A6CE03"))
298 0 : fprintf(fpOut, " (GeoTIFF)");
299 0 : if (EQUAL(pszHex, "96A9F1F1DC98402DA7AED68E34451809"))
300 0 : fprintf(fpOut, " (MSI Worldfile)");
301 0 : if (EQUAL(pszHex, "BE7ACFCB97A942E89C71999491E3AFAC"))
302 0 : fprintf(fpOut, " (XMP)");
303 0 : CPLFree(pszHex);
304 :
305 0 : fprintf(fpOut, "\n");
306 : }
307 :
308 0 : return 0;
309 : }
310 :
311 : /************************************************************************/
312 : /* SetType() */
313 : /************************************************************************/
314 :
315 2009 : void GDALJP2Box::SetType(const char *pszType)
316 :
317 : {
318 2009 : CPLAssert(strlen(pszType) == 4);
319 :
320 2009 : memcpy(szBoxType, pszType, 4);
321 2009 : szBoxType[4] = '\0';
322 2009 : }
323 :
324 : /************************************************************************/
325 : /* GetWritableBoxData() */
326 : /************************************************************************/
327 :
328 32 : GByte *GDALJP2Box::GetWritableBoxData() const
329 : {
330 32 : CPLAssert(static_cast<GUInt32>(nBoxLength) == 8 + abyData.size());
331 : GByte *pabyRet =
332 32 : static_cast<GByte *>(CPLMalloc(static_cast<GUInt32>(nBoxLength)));
333 32 : const GUInt32 nLBox = CPL_MSBWORD32(static_cast<GUInt32>(nBoxLength));
334 32 : memcpy(pabyRet, &nLBox, sizeof(GUInt32));
335 32 : memcpy(pabyRet + 4, szBoxType, 4);
336 32 : memcpy(pabyRet + 8, abyData.data(), abyData.size());
337 32 : return pabyRet;
338 : }
339 :
340 : /************************************************************************/
341 : /* SetWritableData() */
342 : /************************************************************************/
343 :
344 775 : void GDALJP2Box::SetWritableData(int nLength, const GByte *pabyDataIn)
345 :
346 : {
347 775 : abyData.assign(pabyDataIn, pabyDataIn + nLength);
348 :
349 775 : nBoxOffset = -9; // Virtual offsets for data length computation.
350 775 : nDataOffset = -1;
351 :
352 775 : nBoxLength = 8 + nLength;
353 775 : }
354 :
355 : /************************************************************************/
356 : /* AppendWritableData() */
357 : /************************************************************************/
358 :
359 4750 : void GDALJP2Box::AppendWritableData(int nLength, const void *pabyDataIn)
360 :
361 : {
362 4750 : if (abyData.empty())
363 : {
364 1229 : nBoxOffset = -9; // Virtual offsets for data length computation.
365 1229 : nDataOffset = -1;
366 1229 : nBoxLength = 8;
367 : }
368 :
369 9500 : abyData.insert(abyData.end(), static_cast<const GByte *>(pabyDataIn),
370 4750 : static_cast<const GByte *>(pabyDataIn) + nLength);
371 :
372 4750 : nBoxLength += nLength;
373 4750 : }
374 :
375 : /************************************************************************/
376 : /* AppendUInt32() */
377 : /************************************************************************/
378 :
379 872 : void GDALJP2Box::AppendUInt32(GUInt32 nVal)
380 : {
381 872 : CPL_MSBPTR32(&nVal);
382 872 : AppendWritableData(4, &nVal);
383 872 : }
384 :
385 : /************************************************************************/
386 : /* AppendUInt16() */
387 : /************************************************************************/
388 :
389 619 : void GDALJP2Box::AppendUInt16(GUInt16 nVal)
390 : {
391 619 : CPL_MSBPTR16(&nVal);
392 619 : AppendWritableData(2, &nVal);
393 619 : }
394 :
395 : /************************************************************************/
396 : /* AppendUInt8() */
397 : /************************************************************************/
398 :
399 2010 : void GDALJP2Box::AppendUInt8(GByte nVal)
400 : {
401 2010 : AppendWritableData(1, &nVal);
402 2010 : }
403 :
404 : /************************************************************************/
405 : /* CreateUUIDBox() */
406 : /************************************************************************/
407 :
408 229 : GDALJP2Box *GDALJP2Box::CreateUUIDBox(const GByte *pabyUUID, int nDataSize,
409 : const GByte *pabyDataIn)
410 :
411 : {
412 229 : GDALJP2Box *const poBox = new GDALJP2Box();
413 229 : poBox->SetType("uuid");
414 :
415 229 : poBox->AppendWritableData(16, pabyUUID);
416 229 : poBox->AppendWritableData(nDataSize, pabyDataIn);
417 :
418 229 : return poBox;
419 : }
420 :
421 : /************************************************************************/
422 : /* CreateAsocBox() */
423 : /************************************************************************/
424 :
425 200 : GDALJP2Box *GDALJP2Box::CreateAsocBox(int nCount,
426 : const GDALJP2Box *const *papoBoxes)
427 : {
428 200 : return CreateSuperBox("asoc", nCount, papoBoxes);
429 : }
430 :
431 : /************************************************************************/
432 : /* CreateAsocBox() */
433 : /************************************************************************/
434 :
435 458 : GDALJP2Box *GDALJP2Box::CreateSuperBox(const char *pszType, int nCount,
436 : const GDALJP2Box *const *papoBoxes)
437 : {
438 458 : int nDataSize = 0;
439 :
440 : /* -------------------------------------------------------------------- */
441 : /* Compute size of data area of asoc box. */
442 : /* -------------------------------------------------------------------- */
443 1421 : for (int iBox = 0; iBox < nCount; ++iBox)
444 963 : nDataSize += 8 + static_cast<int>(papoBoxes[iBox]->GetDataLength());
445 :
446 458 : GByte *pabyNext = static_cast<GByte *>(CPLMalloc(nDataSize));
447 458 : GByte *pabyCompositeData = pabyNext;
448 :
449 : /* -------------------------------------------------------------------- */
450 : /* Copy subboxes headers and data into buffer. */
451 : /* -------------------------------------------------------------------- */
452 1421 : for (int iBox = 0; iBox < nCount; ++iBox)
453 : {
454 963 : GUInt32 nLBox =
455 963 : CPL_MSBWORD32(static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
456 963 : memcpy(pabyNext, &nLBox, 4);
457 963 : pabyNext += 4;
458 :
459 963 : memcpy(pabyNext, papoBoxes[iBox]->szBoxType, 4);
460 963 : pabyNext += 4;
461 :
462 963 : memcpy(pabyNext, papoBoxes[iBox]->abyData.data(),
463 963 : papoBoxes[iBox]->abyData.size());
464 963 : pabyNext += papoBoxes[iBox]->GetDataLength();
465 : }
466 :
467 : /* -------------------------------------------------------------------- */
468 : /* Create asoc box. */
469 : /* -------------------------------------------------------------------- */
470 458 : GDALJP2Box *const poAsoc = new GDALJP2Box();
471 :
472 458 : poAsoc->SetType(pszType);
473 458 : poAsoc->SetWritableData(nDataSize, pabyCompositeData);
474 :
475 458 : CPLFree(pabyCompositeData);
476 :
477 458 : return poAsoc;
478 : }
479 :
480 : /************************************************************************/
481 : /* CreateLblBox() */
482 : /************************************************************************/
483 :
484 87 : GDALJP2Box *GDALJP2Box::CreateLblBox(const char *pszLabel)
485 :
486 : {
487 87 : GDALJP2Box *const poBox = new GDALJP2Box();
488 87 : poBox->SetType("lbl ");
489 87 : poBox->SetWritableData(static_cast<int>(strlen(pszLabel)),
490 : reinterpret_cast<const GByte *>(pszLabel));
491 :
492 87 : return poBox;
493 : }
494 :
495 : /************************************************************************/
496 : /* CreateLabelledXMLAssoc() */
497 : /************************************************************************/
498 :
499 108 : GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc(const char *pszLabel,
500 : const char *pszXML)
501 :
502 : {
503 216 : GDALJP2Box oLabel;
504 108 : oLabel.SetType("lbl ");
505 108 : oLabel.SetWritableData(static_cast<int>(strlen(pszLabel)),
506 : reinterpret_cast<const GByte *>(pszLabel));
507 :
508 216 : GDALJP2Box oXML;
509 108 : oXML.SetType("xml ");
510 108 : oXML.SetWritableData(static_cast<int>(strlen(pszXML)),
511 : reinterpret_cast<const GByte *>(pszXML));
512 :
513 108 : GDALJP2Box *aoList[2] = {&oLabel, &oXML};
514 :
515 216 : return CreateAsocBox(2, aoList);
516 : }
517 :
518 : /************************************************************************/
519 : /* CreateJUMBFDescriptionBox() */
520 : /************************************************************************/
521 :
522 40 : GDALJP2Box *GDALJP2Box::CreateJUMBFDescriptionBox(const GByte *pabyUUIDType,
523 : const char *pszLabel)
524 :
525 : {
526 40 : GDALJP2Box *const poBox = new GDALJP2Box();
527 40 : poBox->SetType("jumd");
528 :
529 40 : poBox->AppendWritableData(16, pabyUUIDType);
530 40 : poBox->AppendUInt8(3); // requestable field
531 : // +1 since NUL terminated byte required in the JUMBF spec
532 : // 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
533 40 : const size_t nLabelLen = strlen(pszLabel) + 1;
534 40 : poBox->AppendWritableData(static_cast<int>(nLabelLen), pszLabel);
535 :
536 40 : return poBox;
537 : }
538 :
539 : /************************************************************************/
540 : /* CreateJUMBFBox() */
541 : /************************************************************************/
542 :
543 40 : GDALJP2Box *GDALJP2Box::CreateJUMBFBox(const GDALJP2Box *poJUMBFDescriptionBox,
544 : int nCount,
545 : const GDALJP2Box *const *papoBoxes)
546 : {
547 80 : std::vector<const GDALJP2Box *> apoBoxes;
548 40 : apoBoxes.push_back(poJUMBFDescriptionBox);
549 40 : apoBoxes.insert(apoBoxes.end(), papoBoxes, papoBoxes + nCount);
550 40 : return CreateSuperBox("jumb", static_cast<int>(apoBoxes.size()),
551 120 : apoBoxes.data());
552 : }
553 :
554 : /*! @endcond */
|