1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2011, Industrial Light & Magic, a division of Lucas
4 // Digital Ltd. LLC
5 //
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
10 // met:
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 // * Neither the name of Industrial Light & Magic nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34
35 #include "ImfMultiPartInputFile.h"
36
37 #include "ImfTimeCodeAttribute.h"
38 #include "ImfChromaticitiesAttribute.h"
39 #include "ImfBoxAttribute.h"
40 #include "ImfFloatAttribute.h"
41 #include "ImfStdIO.h"
42 #include "ImfTileOffsets.h"
43 #include "ImfMisc.h"
44 #include "ImfTiledMisc.h"
45 #include "ImfInputStreamMutex.h"
46 #include "ImfInputPartData.h"
47 #include "ImfPartType.h"
48 #include "ImfInputFile.h"
49 #include "ImfScanLineInputFile.h"
50 #include "ImfTiledInputFile.h"
51 #include "ImfDeepScanLineInputFile.h"
52 #include "ImfDeepTiledInputFile.h"
53 #include "ImfVersion.h"
54
55 #include <OpenEXRConfig.h>
56 #include <IlmThread.h>
57 #include <IlmThreadMutex.h>
58
59 #include <Iex.h>
60 #include <map>
61 #include <set>
62
63 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
64
65 using ILMTHREAD_NAMESPACE::Mutex;
66 using ILMTHREAD_NAMESPACE::Lock;
67 using IMATH_NAMESPACE::Box2i;
68
69 using std::vector;
70 using std::map;
71 using std::set;
72 using std::string;
73
74 namespace
75 {
76 // Controls whether we error out in the event of shared attribute
77 // inconsistency in the input file
78 static const bool strictSharedAttribute = true;
79 }
80
81 struct MultiPartInputFile::Data: public InputStreamMutex
82 {
83 int version; // Version of this file.
84 bool deleteStream; // If we should delete the stream during destruction.
85 vector<InputPartData*> parts; // Data to initialize Output files.
86 int numThreads; // Number of threads
87 bool reconstructChunkOffsetTable; // If we should reconstruct
88 // the offset table if it's broken.
89 std::map<int,GenericInputFile*> _inputFiles;
90 std::vector<Header> _headers;
91
92
93 void chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, const std::vector<InputPartData*>& parts);
94
95 void readChunkOffsetTables(bool reconstructChunkOffsetTable);
96
97 bool checkSharedAttributesValues(const Header & src,
98 const Header & dst,
99 std::vector<std::string> & conflictingAttributes) const;
100
101 TileOffsets* createTileOffsets(const Header& header);
102
103 InputPartData* getPart(int partNumber);
104
DataMultiPartInputFile::Data105 Data (bool deleteStream, int numThreads, bool reconstructChunkOffsetTable):
106 InputStreamMutex(),
107 deleteStream (deleteStream),
108 numThreads (numThreads),
109 reconstructChunkOffsetTable(reconstructChunkOffsetTable)
110 {
111 }
112
~DataMultiPartInputFile::Data113 ~Data()
114 {
115 if (deleteStream) delete is;
116
117 for (size_t i = 0; i < parts.size(); i++)
118 delete parts[i];
119 }
120
121 template <class T>
createInputPartTMultiPartInputFile::Data122 T* createInputPartT(int partNumber)
123 {
124
125 }
126 };
127
MultiPartInputFile(const char fileName[],int numThreads,bool reconstructChunkOffsetTable)128 MultiPartInputFile::MultiPartInputFile(const char fileName[],
129 int numThreads,
130 bool reconstructChunkOffsetTable):
131 _data(new Data(true, numThreads, reconstructChunkOffsetTable))
132 {
133 try
134 {
135 _data->is = new StdIFStream (fileName);
136 initialize();
137 }
138 catch (IEX_NAMESPACE::BaseExc &e)
139 {
140 delete _data;
141
142 REPLACE_EXC (e, "Cannot read image file "
143 "\"" << fileName << "\". " << e);
144 throw;
145 }
146 catch (...)
147 {
148 delete _data;
149 throw;
150 }
151 }
152
MultiPartInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads,bool reconstructChunkOffsetTable)153 MultiPartInputFile::MultiPartInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is,
154 int numThreads,
155 bool reconstructChunkOffsetTable):
156 _data(new Data(false, numThreads, reconstructChunkOffsetTable))
157 {
158 try
159 {
160 _data->is = &is;
161 initialize();
162 }
163 catch (IEX_NAMESPACE::BaseExc &e)
164 {
165 delete _data;
166
167 REPLACE_EXC (e, "Cannot read image file "
168 "\"" << is.fileName() << "\". " << e);
169 throw;
170 }
171 catch (...)
172 {
173 delete _data;
174 throw;
175 }
176 }
177
178 template<class T>
179 T*
getInputPart(int partNumber)180 MultiPartInputFile::getInputPart(int partNumber)
181 {
182 Lock lock(*_data);
183 if (_data->_inputFiles.find(partNumber) == _data->_inputFiles.end())
184 {
185 T* file = new T(_data->getPart(partNumber));
186 _data->_inputFiles.insert(std::make_pair(partNumber, (GenericInputFile*) file));
187 return file;
188 }
189 else return (T*) _data->_inputFiles[partNumber];
190 }
191
192
193 template InputFile* MultiPartInputFile::getInputPart<InputFile>(int);
194 template TiledInputFile* MultiPartInputFile::getInputPart<TiledInputFile>(int);
195 template DeepScanLineInputFile* MultiPartInputFile::getInputPart<DeepScanLineInputFile>(int);
196 template DeepTiledInputFile* MultiPartInputFile::getInputPart<DeepTiledInputFile>(int);
197
198 InputPartData*
getPart(int partNumber)199 MultiPartInputFile::getPart(int partNumber)
200 {
201 return _data->getPart(partNumber);
202 }
203
204
205
206 const Header &
header(int n) const207 MultiPartInputFile::header(int n) const
208 {
209 return _data->_headers[n];
210 }
211
212
213
~MultiPartInputFile()214 MultiPartInputFile::~MultiPartInputFile()
215 {
216 for (map<int, GenericInputFile*>::iterator it = _data->_inputFiles.begin();
217 it != _data->_inputFiles.end(); it++)
218 {
219 delete it->second;
220 }
221
222 delete _data;
223 }
224
225
226 bool
checkSharedAttributesValues(const Header & src,const Header & dst,vector<string> & conflictingAttributes) const227 MultiPartInputFile::Data::checkSharedAttributesValues(const Header & src,
228 const Header & dst,
229 vector<string> & conflictingAttributes) const
230 {
231 conflictingAttributes.clear();
232
233 bool conflict = false;
234
235 //
236 // Display Window
237 //
238 if (src.displayWindow() != dst.displayWindow())
239 {
240 conflict = true;
241 conflictingAttributes.push_back ("displayWindow");
242 }
243
244
245 //
246 // Pixel Aspect Ratio
247 //
248 if (src.pixelAspectRatio() != dst.pixelAspectRatio())
249 {
250 conflict = true;
251 conflictingAttributes.push_back ("pixelAspectRatio");
252 }
253
254
255 //
256 // Timecode
257 //
258 const TimeCodeAttribute * srcTimeCode = src.findTypedAttribute<
259 TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
260 const TimeCodeAttribute * dstTimeCode = dst.findTypedAttribute<
261 TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
262
263 if (dstTimeCode)
264 {
265 if ( (srcTimeCode && (srcTimeCode->value() != dstTimeCode->value())) ||
266 (!srcTimeCode))
267 {
268 conflict = true;
269 conflictingAttributes.push_back (TimeCodeAttribute::staticTypeName());
270 }
271 }
272
273 //
274 // Chromaticities
275 //
276 const ChromaticitiesAttribute * srcChrom = src.findTypedAttribute<
277 ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
278 const ChromaticitiesAttribute * dstChrom = dst.findTypedAttribute<
279 ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
280
281 if (dstChrom)
282 {
283 if ( (srcChrom && (srcChrom->value() != dstChrom->value())) ||
284 (!srcChrom))
285 {
286 conflict = true;
287 conflictingAttributes.push_back (ChromaticitiesAttribute::staticTypeName());
288 }
289 }
290
291
292 return conflict;
293 }
294
295
296 void
initialize()297 MultiPartInputFile::initialize()
298 {
299 readMagicNumberAndVersionField(*_data->is, _data->version);
300
301 bool multipart = isMultiPart(_data->version);
302 bool tiled = isTiled(_data->version);
303
304 //
305 // Multipart files don't have and shouldn't have the tiled bit set.
306 //
307
308 if (tiled && multipart)
309 throw IEX_NAMESPACE::InputExc ("Multipart files cannot have the tiled bit set");
310
311
312 int pos = 0;
313 while (true)
314 {
315 Header header;
316 header.readFrom(*_data->is, _data->version);
317
318 //
319 // If we read nothing then we stop reading.
320 //
321
322 if (header.readsNothing())
323 {
324 pos++;
325 break;
326 }
327
328 _data->_headers.push_back(header);
329
330 if(multipart == false)
331 break;
332 }
333
334 //
335 // Perform usual check on headers.
336 //
337
338 for (size_t i = 0; i < _data->_headers.size(); i++)
339 {
340 //
341 // Silently invent a type if the file is a single part regular image.
342 //
343
344 if( _data->_headers[i].hasType() == false )
345 {
346 if(multipart)
347
348 throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a type");
349
350 _data->_headers[i].setType(tiled ? TILEDIMAGE : SCANLINEIMAGE);
351 }
352 else
353 {
354
355 //
356 // Silently fix the header type if it's wrong
357 // (happens when a regular Image file written by EXR_2.0 is rewritten by an older library,
358 // so doesn't effect deep image types)
359 //
360
361 if(!multipart && !isNonImage(_data->version))
362 {
363 _data->_headers[i].setType(tiled ? TILEDIMAGE : SCANLINEIMAGE);
364 }
365 }
366
367
368
369 if( _data->_headers[i].hasName() == false )
370 {
371 if(multipart)
372 throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a name");
373 }
374
375 if (isTiled(_data->_headers[i].type()))
376 _data->_headers[i].sanityCheck(true, multipart);
377 else
378 _data->_headers[i].sanityCheck(false, multipart);
379 }
380
381 //
382 // Check name uniqueness.
383 //
384
385 if (multipart)
386 {
387 set<string> names;
388 for (size_t i = 0; i < _data->_headers.size(); i++)
389 {
390
391 if (names.find(_data->_headers[i].name()) != names.end())
392 {
393 throw IEX_NAMESPACE::InputExc ("Header name " + _data->_headers[i].name() +
394 " is not a unique name.");
395 }
396 names.insert(_data->_headers[i].name());
397 }
398 }
399
400 //
401 // Check shared attributes compliance.
402 //
403
404 if (multipart && strictSharedAttribute)
405 {
406 for (size_t i = 1; i < _data->_headers.size(); i++)
407 {
408 vector <string> attrs;
409 if (_data->checkSharedAttributesValues (_data->_headers[0], _data->_headers[i], attrs))
410 {
411 string attrNames;
412 for (size_t j=0; j<attrs.size(); j++)
413 attrNames += " " + attrs[j];
414 throw IEX_NAMESPACE::InputExc ("Header name " + _data->_headers[i].name() +
415 " has non-conforming shared attributes: "+
416 attrNames);
417 }
418 }
419 }
420
421 //
422 // Create InputParts and read chunk offset tables.
423 //
424
425 for (size_t i = 0; i < _data->_headers.size(); i++)
426 _data->parts.push_back(
427 new InputPartData(_data, _data->_headers[i], i, _data->numThreads, _data->version));
428
429 _data->readChunkOffsetTables(_data->reconstructChunkOffsetTable);
430 }
431
432 TileOffsets*
createTileOffsets(const Header & header)433 MultiPartInputFile::Data::createTileOffsets(const Header& header)
434 {
435 //
436 // Get the dataWindow information
437 //
438
439 const Box2i &dataWindow = header.dataWindow();
440 int minX = dataWindow.min.x;
441 int maxX = dataWindow.max.x;
442 int minY = dataWindow.min.y;
443 int maxY = dataWindow.max.y;
444
445 //
446 // Precompute level and tile information
447 //
448
449 int* numXTiles;
450 int* numYTiles;
451 int numXLevels, numYLevels;
452 TileDescription tileDesc = header.tileDescription();
453 precalculateTileInfo (tileDesc,
454 minX, maxX,
455 minY, maxY,
456 numXTiles, numYTiles,
457 numXLevels, numYLevels);
458
459 TileOffsets* tileOffsets = new TileOffsets (tileDesc.mode,
460 numXLevels,
461 numYLevels,
462 numXTiles,
463 numYTiles);
464 delete [] numXTiles;
465 delete [] numYTiles;
466
467 return tileOffsets;
468 }
469
470
471 void
chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,const vector<InputPartData * > & parts)472 MultiPartInputFile::Data::chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, const vector<InputPartData*>& parts)
473 {
474 //
475 // Reconstruct broken chunk offset tables. Stop once we received any exception.
476 //
477
478 Int64 position = is.tellg();
479
480
481 //
482 // check we understand all the parts available: if not, we cannot continue
483 // exceptions thrown here should trickle back up to the constructor
484 //
485
486 for (size_t i = 0; i < parts.size(); i++)
487 {
488 Header& header=parts[i]->header;
489
490 //
491 // do we have a valid type entry?
492 // we only need them for true multipart files or single part non-image (deep) files
493 //
494 if(!header.hasType() && (isMultiPart(version) || isNonImage(version)))
495 {
496 throw IEX_NAMESPACE::ArgExc("cannot reconstruct incomplete file: part with missing type");
497 }
498 if(!isSupportedType(header.type()))
499 {
500 throw IEX_NAMESPACE::ArgExc("cannot reconstruct incomplete file: part with unknown type "+header.type());
501 }
502 }
503
504
505 // how many chunks should we read? We should stop when we reach the end
506 size_t total_chunks = 0;
507
508 // for tiled-based parts, array of (pointers to) tileOffsets objects
509 // to create mapping between tile coordinates and chunk table indices
510
511
512 vector<TileOffsets*> tileOffsets(parts.size());
513
514 // for scanline-based parts, number of scanlines in each part
515 vector<int> rowsizes(parts.size());
516
517 for(size_t i = 0 ; i < parts.size() ; i++)
518 {
519 total_chunks += parts[i]->chunkOffsets.size();
520 if (isTiled(parts[i]->header.type()))
521 {
522 tileOffsets[i] = createTileOffsets(parts[i]->header);
523 }else{
524 tileOffsets[i] = NULL;
525 // (TODO) fix this so that it doesn't need to be revised for future compression types.
526 switch(parts[i]->header.compression())
527 {
528 case DWAB_COMPRESSION :
529 rowsizes[i] = 256;
530 break;
531 case PIZ_COMPRESSION :
532 case B44_COMPRESSION :
533 case B44A_COMPRESSION :
534 case DWAA_COMPRESSION :
535 rowsizes[i]=32;
536 break;
537 case ZIP_COMPRESSION :
538 case PXR24_COMPRESSION :
539 rowsizes[i]=16;
540 break;
541 case ZIPS_COMPRESSION :
542 case RLE_COMPRESSION :
543 case NO_COMPRESSION :
544 rowsizes[i]=1;
545 break;
546 default :
547 throw(IEX_NAMESPACE::ArgExc("Unknown compression method in chunk offset reconstruction"));
548 }
549 }
550 }
551
552 try
553 {
554
555 //
556 //
557 //
558
559 Int64 chunk_start = position;
560 for (size_t i = 0; i < total_chunks ; i++)
561 {
562 //
563 // do we have a part number?
564 //
565
566 int partNumber = 0;
567 if(isMultiPart(version))
568 {
569 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, partNumber);
570 }
571
572
573
574 if(partNumber<0 || partNumber>int(parts.size()))
575 {
576 // bail here - bad part number
577 throw int();
578 }
579
580 Header& header = parts[partNumber]->header;
581
582 // size of chunk NOT including multipart field
583
584 Int64 size_of_chunk=0;
585
586 if (isTiled(header.type()))
587 {
588 //
589 //
590 //
591 int tilex,tiley,levelx,levely;
592 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tilex);
593 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tiley);
594 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelx);
595 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levely);
596
597 //std::cout << "chunk_start for " << tilex <<',' << tiley << ',' << levelx << ' ' << levely << ':' << chunk_start << std::endl;
598
599
600 if(!tileOffsets[partNumber])
601 {
602 // this shouldn't actually happen - we should have allocated a valid
603 // tileOffsets for any part which isTiled
604 throw int();
605
606 }
607
608 if(!tileOffsets[partNumber]->isValidTile(tilex,tiley,levelx,levely))
609 {
610 //std::cout << "invalid tile : aborting\n";
611 throw int();
612 }
613
614 (*tileOffsets[partNumber])(tilex,tiley,levelx,levely)=chunk_start;
615
616 // compute chunk sizes - different procedure for deep tiles and regular
617 // ones
618 if(header.type()==DEEPTILE)
619 {
620 Int64 packed_offset;
621 Int64 packed_sample;
622 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset);
623 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample);
624
625 //add 40 byte header to packed sizes (tile coordinates, packed sizes, unpacked size)
626 size_of_chunk=packed_offset+packed_sample+40;
627 }
628 else
629 {
630
631 // regular image has 20 bytes of header, 4 byte chunksize;
632 int chunksize;
633 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, chunksize);
634 size_of_chunk=chunksize+20;
635 }
636 }
637 else
638 {
639 int y_coordinate;
640 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, y_coordinate);
641
642 y_coordinate -= header.dataWindow().min.y;
643 y_coordinate /= rowsizes[partNumber];
644
645 if(y_coordinate < 0 || y_coordinate >= int(parts[partNumber]->chunkOffsets.size()))
646 {
647 //std::cout << "aborting reconstruction: bad data " << y_coordinate << endl;
648 //bail to exception catcher: broken scanline
649 throw int();
650 }
651
652 parts[partNumber]->chunkOffsets[y_coordinate]=chunk_start;
653 //std::cout << "chunk_start for " << y_coordinate << ':' << chunk_start << std::endl;
654
655 if(header.type()==DEEPSCANLINE)
656 {
657 Int64 packed_offset;
658 Int64 packed_sample;
659 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset);
660 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample);
661
662
663 size_of_chunk=packed_offset+packed_sample+28;
664 }
665 else
666 {
667 int chunksize;
668 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, chunksize);
669 size_of_chunk=chunksize+8;
670 }
671
672 }
673
674 if(isMultiPart(version))
675 {
676 chunk_start+=4;
677 }
678
679 chunk_start+=size_of_chunk;
680
681 //std::cout << " next chunk +"<<size_of_chunk << " = " << chunk_start << std::endl;
682
683 is.seekg(chunk_start);
684
685 }
686
687 }
688 catch (...)
689 {
690 //
691 // Suppress all exceptions. This functions is
692 // called only to reconstruct the line offset
693 // table for incomplete files, and exceptions
694 // are likely.
695 //
696 }
697
698 // copy tiled part data back to chunk offsets
699
700 for(size_t partNumber=0;partNumber<parts.size();partNumber++)
701 {
702 if(tileOffsets[partNumber])
703 {
704 size_t pos=0;
705 vector<vector<vector <Int64> > > offsets = tileOffsets[partNumber]->getOffsets();
706 for (size_t l = 0; l < offsets.size(); l++)
707 for (size_t y = 0; y < offsets[l].size(); y++)
708 for (size_t x = 0; x < offsets[l][y].size(); x++)
709 {
710 parts[ partNumber ]->chunkOffsets[pos] = offsets[l][y][x];
711 pos++;
712 }
713 delete tileOffsets[partNumber];
714 }
715 }
716
717 is.clear();
718 is.seekg (position);
719 }
720
721 InputPartData*
getPart(int partNumber)722 MultiPartInputFile::Data::getPart(int partNumber)
723 {
724 if (partNumber < 0 || partNumber >= (int) parts.size())
725 throw IEX_NAMESPACE::ArgExc ("Part number is not in valid range.");
726 return parts[partNumber];
727 }
728
729
730
731 void
readChunkOffsetTables(bool reconstructChunkOffsetTable)732 MultiPartInputFile::Data::readChunkOffsetTables(bool reconstructChunkOffsetTable)
733 {
734 bool brokenPartsExist = false;
735
736 for (size_t i = 0; i < parts.size(); i++)
737 {
738 int chunkOffsetTableSize = getChunkOffsetTableSize(parts[i]->header,false);
739 parts[i]->chunkOffsets.resize(chunkOffsetTableSize);
740
741 for (int j = 0; j < chunkOffsetTableSize; j++)
742 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*is, parts[i]->chunkOffsets[j]);
743
744 //
745 // Check chunk offsets, reconstruct if broken.
746 // At first we assume the table is complete.
747 //
748 parts[i]->completed = true;
749 for (int j = 0; j < chunkOffsetTableSize; j++)
750 {
751 if (parts[i]->chunkOffsets[j] <= 0)
752 {
753 brokenPartsExist = true;
754 parts[i]->completed = false;
755 break;
756 }
757 }
758 }
759
760 if (brokenPartsExist && reconstructChunkOffsetTable)
761 chunkOffsetReconstruction(*is, parts);
762 }
763
764 int
version() const765 MultiPartInputFile::version() const
766 {
767 return _data->version;
768 }
769
770 bool
partComplete(int part) const771 MultiPartInputFile::partComplete(int part) const
772 {
773 return _data->parts[part]->completed;
774 }
775
776 int
parts() const777 MultiPartInputFile::parts() const
778 {
779 return int(_data->_headers.size());
780 }
781
782
783 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
784