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