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