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