Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKFile class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8 : *
9 : * Permission is hereby granted, free of charge, to any person obtaining a
10 : * copy of this software and associated documentation files (the "Software"),
11 : * to deal in the Software without restriction, including without limitation
12 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : * and/or sell copies of the Software, and to permit persons to whom the
14 : * Software is furnished to do so, subject to the following conditions:
15 : *
16 : * The above copyright notice and this permission notice shall be included
17 : * in all copies or substantial portions of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 : * DEALINGS IN THE SOFTWARE.
26 : ****************************************************************************/
27 : #include "pcidsk_file.h"
28 : #include "pcidsk_exception.h"
29 : #include "pcidsk_channel.h"
30 : #include "pcidsk_segment.h"
31 : #include "core/mutexholder.h"
32 : #include "core/pcidsk_utils.h"
33 : #include "core/cpcidskfile.h"
34 : #include "core/cpcidskblockfile.h"
35 :
36 : // Channel types
37 : #include "channel/cbandinterleavedchannel.h"
38 : #include "channel/cpixelinterleavedchannel.h"
39 : #include "channel/ctiledchannel.h"
40 : #include "channel/cexternalchannel.h"
41 :
42 : // Segment types
43 : #include "segment/cpcidskgeoref.h"
44 : #include "segment/cpcidskpct.h"
45 : #include "segment/cpcidskbpct.h"
46 : #include "segment/cpcidsklut.h"
47 : #include "segment/cpcidskblut.h"
48 : #include "segment/cpcidskvectorsegment.h"
49 : #include "segment/metadatasegment.h"
50 : #include "segment/systiledir.h"
51 : #include "segment/cpcidskrpcmodel.h"
52 : #include "segment/cpcidskgcp2segment.h"
53 : #include "segment/cpcidskbitmap.h"
54 : #include "segment/cpcidsk_tex.h"
55 : #include "segment/cpcidsk_array.h"
56 : #include "segment/cpcidsktoutinmodel.h"
57 : #include "segment/cpcidskpolymodel.h"
58 : #include "segment/cpcidskbinarysegment.h"
59 : #include "core/clinksegment.h"
60 :
61 : #ifdef PCIDSK_IMP_PARAM_SUPPORT
62 : #define __INT16DEF__
63 : #define __INT32DEF__
64 : #define __INT64DEF__
65 :
66 : #define __CINT16TDEF__
67 : #define __CINT32TDEF__
68 : #define __CINT64TDEF__
69 : #define __CFLOAT32TDEF__
70 : #define __CFLOAT64TDEF__
71 :
72 : #define __CINT16DEF__
73 : #define __CINT32DEF__
74 : #define __CINT64DEF__
75 : #define __CFLOAT32DEF__
76 : #define __CFLOAT64DEF__
77 :
78 : #include "core/impparam/impparam.h"
79 : #endif
80 :
81 : #include <cassert>
82 : #include <cstdlib>
83 : #include <cstring>
84 : #include <cstdio>
85 : #include <limits>
86 : #include <string>
87 : #include <fstream>
88 : #include <sstream>
89 : #include <iostream>
90 : #include <algorithm>
91 :
92 : using namespace PCIDSK;
93 :
94 : /************************************************************************/
95 : /* CPCIDSKFile() */
96 : /************************************************************************/
97 :
98 255 : CPCIDSKFile::CPCIDSKFile( const std::string& filename )
99 :
100 : {
101 255 : io_handle = nullptr;
102 255 : io_mutex = nullptr;
103 255 : updatable = false;
104 255 : base_filename = filename;
105 255 : width = 0;
106 255 : height = 0;
107 255 : channel_count = 0;
108 255 : segment_count = 0;
109 255 : segment_pointers_offset = 0;
110 255 : block_size = 0;
111 255 : pixel_group_size = 0;
112 255 : segment_count = 0;
113 255 : segment_pointers_offset = 0;
114 255 : block_size = 0;
115 255 : pixel_group_size = 0;
116 255 : first_line_offset = 0;
117 255 : last_block_index = 0;
118 255 : last_block_dirty = false;
119 255 : last_block_xoff = 0;
120 255 : last_block_xsize = 0;
121 255 : last_block_data = nullptr;
122 255 : last_block_mutex = nullptr;
123 255 : file_size = 0;
124 :
125 255 : file_list.reserve(1024);
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Initialize the metadata object, but do not try to load till */
129 : /* needed. */
130 : /* -------------------------------------------------------------------- */
131 255 : metadata.Initialize( this, "FIL", 0 );
132 255 : }
133 :
134 : /************************************************************************/
135 : /* ~CPCIDSKFile() */
136 : /************************************************************************/
137 :
138 510 : CPCIDSKFile::~CPCIDSKFile()
139 :
140 : {
141 : try
142 : {
143 255 : Synchronize();
144 : }
145 0 : catch( const PCIDSKException& e )
146 : {
147 0 : fprintf(stderr, "Exception in ~CPCIDSKFile(): %s", e.what()); // ok
148 : }
149 :
150 : /* -------------------------------------------------------------------- */
151 : /* Cleanup last block buffer. */
152 : /* -------------------------------------------------------------------- */
153 255 : if( last_block_data != nullptr )
154 : {
155 0 : last_block_index = -1;
156 0 : free( last_block_data );
157 0 : last_block_data = nullptr;
158 0 : delete last_block_mutex;
159 : }
160 :
161 : /* -------------------------------------------------------------------- */
162 : /* Cleanup channels and segments. */
163 : /* -------------------------------------------------------------------- */
164 : size_t i;
165 495 : for( i = 0; i < channels.size(); i++ )
166 : {
167 240 : delete channels[i];
168 240 : channels[i] = nullptr;
169 : }
170 :
171 256637 : for( i = 0; i < segments.size(); i++ )
172 : {
173 256382 : delete segments[i];
174 256382 : segments[i] = nullptr;
175 : }
176 :
177 : /* -------------------------------------------------------------------- */
178 : /* Close and cleanup IO stuff. */
179 : /* -------------------------------------------------------------------- */
180 : {
181 510 : MutexHolder oHolder( io_mutex );
182 :
183 255 : if( io_handle )
184 : {
185 255 : interfaces.io->Close( io_handle );
186 255 : io_handle = nullptr;
187 : }
188 : }
189 :
190 : size_t i_file;
191 :
192 257 : for( i_file=0; i_file < file_list.size(); i_file++ )
193 : {
194 2 : delete file_list[i_file].io_mutex;
195 2 : file_list[i_file].io_mutex = nullptr;
196 :
197 2 : interfaces.io->Close( file_list[i_file].io_handle );
198 2 : file_list[i_file].io_handle = nullptr;
199 : }
200 :
201 255 : for( i_file=0; i_file < edb_file_list.size(); i_file++ )
202 : {
203 0 : delete edb_file_list[i_file].io_mutex;
204 0 : edb_file_list[i_file].io_mutex = nullptr;
205 :
206 0 : delete edb_file_list[i_file].file;
207 0 : edb_file_list[i_file].file = nullptr;
208 : }
209 :
210 255 : delete io_mutex;
211 510 : }
212 :
213 : /************************************************************************/
214 : /* Synchronize() */
215 : /************************************************************************/
216 :
217 509 : void CPCIDSKFile::Synchronize()
218 :
219 : {
220 509 : if( !GetUpdatable() )
221 267 : return;
222 :
223 : /* -------------------------------------------------------------------- */
224 : /* Flush out last line caching stuff for pixel interleaved data. */
225 : /* -------------------------------------------------------------------- */
226 242 : FlushBlock();
227 :
228 : /* -------------------------------------------------------------------- */
229 : /* Synchronize all channels. */
230 : /* -------------------------------------------------------------------- */
231 : size_t i;
232 490 : for( i = 0; i < channels.size(); i++ )
233 248 : channels[i]->Synchronize();
234 :
235 : /* -------------------------------------------------------------------- */
236 : /* Synchronize all segments we have instantiated. */
237 : /* -------------------------------------------------------------------- */
238 248292 : for( i = 0; i < segments.size(); i++ )
239 : {
240 248050 : if( segments[i] != nullptr )
241 2526 : segments[i]->Synchronize();
242 : }
243 :
244 : /* -------------------------------------------------------------------- */
245 : /* Ensure the file is synchronized to disk. */
246 : /* -------------------------------------------------------------------- */
247 484 : MutexHolder oHolder( io_mutex );
248 :
249 242 : interfaces.io->Flush( io_handle );
250 : }
251 :
252 : /************************************************************************/
253 : /* GetChannel() */
254 : /************************************************************************/
255 :
256 273 : PCIDSKChannel *CPCIDSKFile::GetChannel( int band )
257 :
258 : {
259 273 : if( band < 1 || band > channel_count )
260 0 : return (PCIDSKChannel*)ThrowPCIDSKExceptionPtr( "Out of range band (%d) requested.",
261 0 : band );
262 :
263 273 : return channels[band-1];
264 : }
265 :
266 : /************************************************************************/
267 : /* GetSegment() */
268 : /************************************************************************/
269 :
270 4238 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int segment )
271 :
272 : {
273 : /* -------------------------------------------------------------------- */
274 : /* Is this a valid segment? */
275 : /* -------------------------------------------------------------------- */
276 4238 : if( segment < 1 || segment > segment_count )
277 0 : return nullptr;
278 :
279 4238 : const char *segment_pointer = segment_pointers.buffer + (segment-1) * 32;
280 4238 : if( segment_pointer[0] != 'A' && segment_pointer[0] != 'L' )
281 0 : return nullptr;
282 :
283 : /* -------------------------------------------------------------------- */
284 : /* Do we already have a corresponding object? */
285 : /* -------------------------------------------------------------------- */
286 4238 : if( segments[segment] != nullptr )
287 2775 : return segments[segment];
288 :
289 : /* -------------------------------------------------------------------- */
290 : /* Instantiate per the type. */
291 : /* -------------------------------------------------------------------- */
292 1463 : int segment_type = segment_pointers.GetInt((segment-1)*32+1,3);
293 1463 : PCIDSKSegment *segobj = nullptr;
294 :
295 1463 : switch( segment_type )
296 : {
297 119 : case SEG_GEO:
298 119 : segobj = new CPCIDSKGeoref( this, segment, segment_pointer );
299 119 : break;
300 :
301 2 : case SEG_PCT:
302 2 : segobj = new CPCIDSK_PCT( this, segment, segment_pointer );
303 2 : break;
304 :
305 0 : case SEG_BPCT:
306 0 : segobj = new CPCIDSK_BPCT( this, segment, segment_pointer );
307 0 : break;
308 :
309 0 : case SEG_LUT:
310 0 : segobj = new CPCIDSK_LUT( this, segment, segment_pointer );
311 0 : break;
312 :
313 0 : case SEG_BLUT:
314 0 : segobj = new CPCIDSK_BLUT( this, segment, segment_pointer );
315 0 : break;
316 :
317 1185 : case SEG_VEC:
318 1185 : segobj = new CPCIDSKVectorSegment( this, segment, segment_pointer );
319 1185 : break;
320 :
321 0 : case SEG_BIT:
322 0 : segobj = new CPCIDSKBitmap( this, segment, segment_pointer );
323 0 : break;
324 :
325 0 : case SEG_TEX:
326 0 : segobj = new CPCIDSK_TEX( this, segment, segment_pointer );
327 0 : break;
328 :
329 157 : case SEG_SYS:
330 157 : if (STARTS_WITH(segment_pointer + 4, "SysBMDir") ||
331 144 : STARTS_WITH(segment_pointer + 4, "TileDir"))
332 : {
333 32 : segobj = new SysTileDir( this, segment, segment_pointer );
334 : }
335 125 : else if( STARTS_WITH(segment_pointer + 4, "METADATA") )
336 97 : segobj = new MetadataSegment( this, segment, segment_pointer );
337 28 : else if (STARTS_WITH(segment_pointer + 4, "Link ") )
338 0 : segobj = new CLinkSegment(this, segment, segment_pointer);
339 : else
340 28 : segobj = new CPCIDSKSegment( this, segment, segment_pointer );
341 :
342 157 : break;
343 :
344 0 : case SEG_GCP2:
345 0 : segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer);
346 0 : break;
347 :
348 0 : case SEG_ORB:
349 0 : segobj = new CPCIDSKEphemerisSegment(this, segment, segment_pointer);
350 0 : break;
351 :
352 0 : case SEG_ARR:
353 0 : segobj = new CPCIDSK_ARRAY(this, segment, segment_pointer);
354 0 : break;
355 :
356 0 : case SEG_BIN:
357 0 : if (STARTS_WITH(segment_pointer + 4, "RFMODEL "))
358 : {
359 0 : segobj = new CPCIDSKRPCModelSegment( this, segment, segment_pointer );
360 : }
361 0 : else if (STARTS_WITH(segment_pointer + 4, "APMODEL "))
362 : {
363 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
364 : }
365 0 : else if (STARTS_WITH(segment_pointer + 4, "POLYMDL "))
366 : {
367 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
368 : }
369 0 : else if (STARTS_WITH(segment_pointer + 4, "TPSMODEL"))
370 : {
371 0 : segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer);
372 : }
373 0 : else if (STARTS_WITH(segment_pointer + 4, "RTCSMDL "))
374 : {
375 0 : segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer);
376 : }
377 0 : else if (STARTS_WITH(segment_pointer + 4, "MMRTCS "))
378 : {
379 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
380 : }
381 0 : else if (STARTS_WITH(segment_pointer + 4, "MODEL "))
382 : {
383 0 : segobj = new CPCIDSKToutinModelSegment(this, segment, segment_pointer);
384 : }
385 0 : else if (STARTS_WITH(segment_pointer + 4, "MMSPB "))
386 : {
387 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
388 : }
389 0 : else if (STARTS_WITH(segment_pointer + 4, "MMADS "))
390 : {
391 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
392 : }
393 0 : else if (STARTS_WITH(segment_pointer + 4, "MMSRS "))
394 : {
395 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
396 : }
397 0 : else if (STARTS_WITH(segment_pointer + 4, "MMSGS "))
398 : {
399 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
400 : }
401 0 : else if (STARTS_WITH(segment_pointer + 4, "LRSMODEL"))
402 : {
403 0 : segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer);
404 : }
405 0 : else if (STARTS_WITH(segment_pointer + 4, "MMLRS "))
406 : {
407 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
408 : }
409 0 : else if (STARTS_WITH(segment_pointer + 4, "EPIPOLAR"))
410 : {
411 0 : segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer);
412 : }
413 0 : break;
414 : }
415 :
416 1463 : if (segobj == nullptr)
417 0 : segobj = new CPCIDSKSegment( this, segment, segment_pointer );
418 :
419 1463 : segments[segment] = segobj;
420 :
421 1463 : return segobj;
422 : }
423 :
424 : /************************************************************************/
425 : /* GetSegment() */
426 : /* */
427 : /* Find segment by type/name. */
428 : /************************************************************************/
429 :
430 2509 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int type, const std::string & name,
431 : int previous)
432 : {
433 : int i;
434 : char type_str[16];
435 :
436 : //we want the 3 less significant digit only in case type is too big
437 : // Note : that happen with SEG_VEC_TABLE that is equal to 65652 in GDB.
438 : //see function BuildChildrenLayer in jtfile.cpp, the call on GDBSegNext
439 : //in the loop on gasTypeTable can create issue in PCIDSKSegNext
440 : //(in pcic/gdbfrtms/pcidskopen.cpp)
441 2509 : CPLsnprintf( type_str, sizeof(type_str), "%03d", (type % 1000) );
442 2440270 : for( i = previous; i < segment_count; i++ )
443 : {
444 2438100 : if( type != SEG_UNKNOWN
445 2438100 : && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 )
446 2437530 : continue;
447 :
448 575 : auto pszName = segment_pointers.buffer + i*32 + 4;
449 575 : if(!CheckSegNamesEqual(pszName, 8, name.data(), (unsigned)name.size()))
450 232 : continue;
451 :
452 : // Ignore deleted segments.
453 343 : if (*(segment_pointers.buffer + i * 32 + 0) == 'D') continue;
454 :
455 343 : return GetSegment(i+1);
456 : }
457 :
458 2166 : return nullptr;
459 : }
460 :
461 0 : unsigned int CPCIDSKFile::GetSegmentID(int type, const std::string &name,
462 : unsigned previous) const
463 : {
464 : //we want the 3 less significant digit only in case type is too big
465 : // Note : that happen with SEG_VEC_TABLE that is equal to 65652 in GDB.
466 : //see function BuildChildrenLayer in jtfile.cpp, the call on GDBSegNext
467 : //in the loop on gasTypeTable can create issue in PCIDSKSegNext
468 : //(in pcic/gdbfrtms/pcidskopen.cpp)
469 : char type_str[16];
470 0 : CPLsnprintf(type_str, sizeof(type_str), "%03d", (type % 1000) );
471 :
472 0 : for(int i = previous; i < segment_count; i++ )
473 : {
474 0 : if( type != SEG_UNKNOWN
475 0 : && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 )
476 0 : continue;
477 :
478 0 : auto pszName = segment_pointers.buffer + i*32 + 4;
479 0 : if(!CheckSegNamesEqual(pszName, 8, name.data(), (unsigned)name.size()))
480 0 : continue;
481 :
482 : // Ignore deleted segments.
483 0 : if (*(segment_pointers.buffer + i * 32 + 0) == 'D') continue;
484 :
485 0 : return i+1;
486 : }
487 0 : return 0;
488 : }
489 :
490 0 : std::vector<unsigned> CPCIDSKFile::GetSegmentIDs(int type,
491 : const std::function<bool(const char *,unsigned)> & oFilter) const
492 : {
493 0 : std::vector<unsigned> vnSegments;
494 : char type_str[16];
495 0 : CPLsnprintf(type_str, sizeof(type_str), "%03d", (type % 1000) );
496 :
497 0 : for(int i = 0; i < segment_count; i++)
498 : {
499 0 : if( type != SEG_UNKNOWN
500 0 : && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 )
501 0 : continue;
502 :
503 0 : auto pszName = segment_pointers.buffer + i*32 + 4;
504 0 : if(!oFilter(pszName, 8))
505 0 : continue;
506 :
507 : // Ignore deleted segments.
508 0 : if (*(segment_pointers.buffer + i * 32 + 0) == 'D') continue;
509 :
510 0 : vnSegments.push_back(i + 1);
511 : }
512 0 : return vnSegments;
513 : }
514 :
515 : /************************************************************************/
516 : /* InitializeFromHeader() */
517 : /************************************************************************/
518 :
519 255 : void CPCIDSKFile::InitializeFromHeader(int max_channel_count_allowed)
520 :
521 : {
522 : /* -------------------------------------------------------------------- */
523 : /* Process the file header. */
524 : /* -------------------------------------------------------------------- */
525 256 : PCIDSKBuffer fh(512);
526 :
527 255 : ReadFromFile( fh.buffer, 0, 512 );
528 :
529 255 : width = atoi(fh.Get(384,8));
530 255 : height = atoi(fh.Get(392,8));
531 255 : channel_count = atoi(fh.Get(376,8));
532 255 : if( width < 0 || height < 0 || channel_count < 0 )
533 : {
534 0 : return ThrowPCIDSKException(
535 0 : "Invalid width, height and/or channel_count" );
536 : }
537 255 : if( max_channel_count_allowed >= 0 && channel_count > max_channel_count_allowed )
538 : {
539 0 : return ThrowPCIDSKException(
540 : "channel_count = %d exceeds max_channel_count_allowed = %d",
541 : channel_count,
542 0 : max_channel_count_allowed );
543 : }
544 255 : file_size = fh.GetUInt64(16,16);
545 :
546 255 : if (file_size > std::numeric_limits<uint64>::max() / 512)
547 : {
548 0 : return ThrowPCIDSKException("Invalid file_size: " PCIDSK_FRMT_UINT64,
549 0 : file_size);
550 : }
551 :
552 255 : uint64 ih_start_block = atouint64(fh.Get(336,16));
553 255 : uint64 image_start_block = atouint64(fh.Get(304,16));
554 255 : fh.Get(360,8,interleaving);
555 :
556 510 : if( image_start_block == 0 ||
557 255 : image_start_block-1 > std::numeric_limits<uint64>::max() / 512 )
558 : {
559 0 : return ThrowPCIDSKException(
560 0 : "Invalid image_start_block: " PCIDSK_FRMT_UINT64, image_start_block );
561 : }
562 255 : uint64 image_offset = (image_start_block-1) * 512;
563 :
564 255 : block_size = 0;
565 255 : last_block_index = -1;
566 255 : last_block_dirty = false;
567 255 : last_block_data = nullptr;
568 255 : last_block_mutex = nullptr;
569 :
570 : /* -------------------------------------------------------------------- */
571 : /* Load the segment pointers into a PCIDSKBuffer. For now we */
572 : /* try to avoid doing too much other processing on them. */
573 : /* -------------------------------------------------------------------- */
574 255 : int segment_block_count = atoi(fh.Get(456,8));
575 510 : if( segment_block_count < 0 ||
576 255 : segment_block_count > std::numeric_limits<int>::max() / 512 )
577 0 : return ThrowPCIDSKException( "Invalid segment_block_count: %d",
578 0 : segment_block_count );
579 :
580 255 : segment_pointers_offset = atouint64(fh.Get(440,16));
581 255 : if( segment_pointers_offset == 0 ||
582 255 : segment_pointers_offset-1 > file_size )
583 : {
584 0 : return ThrowPCIDSKException(
585 0 : "Invalid segment_pointers_offset: " PCIDSK_FRMT_UINT64, segment_pointers_offset );
586 : }
587 255 : segment_pointers_offset = segment_pointers_offset * 512 - 512;
588 :
589 : // Sanity check to avoid allocating too much memory
590 255 : if( segment_block_count * 512 > 100 * 1024 * 1024 )
591 : {
592 2 : MutexHolder oHolder( io_mutex );
593 :
594 1 : interfaces.io->Seek( io_handle, 0, SEEK_END );
595 1 : const auto nRealFileSize = interfaces.io->Tell( io_handle );
596 1 : if( segment_pointers_offset > nRealFileSize )
597 : {
598 1 : return ThrowPCIDSKException(
599 0 : "Invalid segment_pointers_offset: " PCIDSK_FRMT_UINT64, segment_pointers_offset );
600 : }
601 0 : if( static_cast<unsigned>(segment_block_count * 512) > nRealFileSize - segment_pointers_offset )
602 : {
603 : // I guess we could also decide to error out
604 0 : segment_block_count = static_cast<int>((nRealFileSize - segment_pointers_offset) / 512);
605 : }
606 : }
607 :
608 254 : segment_count = (segment_block_count * 512) / 32;
609 254 : segment_pointers.SetSize( segment_block_count * 512 );
610 :
611 254 : ReadFromFile( segment_pointers.buffer, segment_pointers_offset,
612 254 : segment_block_count * 512 );
613 :
614 254 : segments.resize( segment_count + 1 );
615 :
616 : /* -------------------------------------------------------------------- */
617 : /* Get the number of each channel type - only used for some */
618 : /* interleaving cases. */
619 : /* -------------------------------------------------------------------- */
620 254 : int count_8u = 0;
621 254 : int count_16s = 0;
622 254 : int count_16u = 0;
623 254 : int count_32r = 0;
624 254 : int count_c16u = 0;
625 254 : int count_c16s = 0;
626 254 : int count_c32r = 0;
627 :
628 254 : int16 count_32s = 0;
629 254 : int16 count_32u = 0;
630 254 : int16 count_64s = 0;
631 254 : int16 count_64u = 0;
632 254 : int16 count_64r = 0;
633 254 : int16 count_c32s = 0;
634 254 : int16 count_c32u = 0;
635 :
636 254 : if (strcmp(fh.Get(464,4), " ") == 0)
637 : {
638 0 : count_8u = channel_count;
639 : }
640 : else
641 : {
642 254 : count_8u = atoi(fh.Get(464,4));
643 254 : count_16s = atoi(fh.Get(468,4));
644 254 : count_16u = atoi(fh.Get(472,4));
645 254 : count_32r = atoi(fh.Get(476,4));
646 254 : count_c16u = atoi(fh.Get(480,4));
647 254 : count_c16s = atoi(fh.Get(484,4));
648 254 : count_c32r = atoi(fh.Get(488,4));
649 :
650 254 : count_32s = *(int16 *) fh.Get(492,2);
651 254 : count_32u = *(int16 *) fh.Get(494,2);
652 254 : count_64s = *(int16 *) fh.Get(496,2);
653 254 : count_64u = *(int16 *) fh.Get(498,2);
654 254 : count_64r = *(int16 *) fh.Get(500,2);
655 254 : count_c32s = *(int16 *) fh.Get(502,2);
656 254 : count_c32u = *(int16 *) fh.Get(504,2);
657 :
658 254 : if (!BigEndianSystem())
659 : {
660 254 : SwapData(&count_32s, 2, 1);
661 254 : SwapData(&count_32u, 2, 1);
662 254 : SwapData(&count_64s, 2, 1);
663 254 : SwapData(&count_64u, 2, 1);
664 254 : SwapData(&count_64r, 2, 1);
665 254 : SwapData(&count_c32s, 2, 1);
666 254 : SwapData(&count_c32u, 2, 1);
667 : }
668 :
669 : // 8224 is two space characters.
670 254 : if (count_32s == 8224)
671 5 : count_32s = 0;
672 254 : if (count_32u == 8224)
673 5 : count_32u = 0;
674 254 : if (count_64s == 8224)
675 5 : count_64s = 0;
676 254 : if (count_64u == 8224)
677 5 : count_64u = 0;
678 254 : if (count_64r == 8224)
679 5 : count_64r = 0;
680 254 : if (count_c32s == 8224)
681 5 : count_c32s = 0;
682 254 : if (count_c32u == 8224)
683 5 : count_c32u = 0;
684 : }
685 :
686 254 : if (channel_count !=
687 254 : count_8u +
688 254 : count_16s +
689 254 : count_16u +
690 254 : count_32s +
691 254 : count_32u +
692 254 : count_32r +
693 254 : count_64s +
694 254 : count_64u +
695 254 : count_64r +
696 254 : count_c16s +
697 254 : count_c16u +
698 254 : count_c32s +
699 254 : count_c32u +
700 : count_c32r)
701 : {
702 0 : return ThrowPCIDSKException("The file seems to contain an unsupported data type.");
703 : }
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* for pixel interleaved files we need to compute the length of */
707 : /* a scanline padded out to a 512 byte boundary. */
708 : /* -------------------------------------------------------------------- */
709 254 : if( interleaving == "PIXEL" )
710 : {
711 0 : first_line_offset = image_offset;
712 0 : pixel_group_size =
713 0 : count_8u +
714 0 : count_16s*2 +
715 0 : count_16u*2 +
716 0 : count_32s*4 +
717 0 : count_32u*4 +
718 0 : count_32r*4 +
719 0 : count_64s*8 +
720 0 : count_64u*8 +
721 0 : count_64r*8 +
722 0 : count_c16u*4 +
723 0 : count_c16s*4 +
724 0 : count_c32u*8 +
725 0 : count_c32s*8 +
726 0 : count_c32r*8;
727 :
728 0 : block_size = static_cast<PCIDSK::uint64>(pixel_group_size) * width;
729 0 : if( block_size % 512 != 0 )
730 0 : block_size += 512 - (block_size % 512);
731 : if( block_size != static_cast<size_t>(block_size) )
732 : {
733 : return ThrowPCIDSKException(
734 : "Allocating " PCIDSK_FRMT_UINT64 " bytes for scanline "
735 : "buffer failed.", block_size );
736 : }
737 :
738 0 : last_block_data = calloc(1, static_cast<size_t>(block_size));
739 0 : if( last_block_data == nullptr )
740 : {
741 0 : return ThrowPCIDSKException(
742 : "Allocating " PCIDSK_FRMT_UINT64 " bytes for scanline "
743 0 : "buffer failed.", block_size );
744 : }
745 :
746 0 : last_block_mutex = interfaces.CreateMutex();
747 0 : image_offset = 0;
748 : }
749 :
750 : /* -------------------------------------------------------------------- */
751 : /* Initialize the list of channels. */
752 : /* -------------------------------------------------------------------- */
753 : int channelnum;
754 :
755 494 : for( channelnum = 1; channelnum <= channel_count; channelnum++ )
756 : {
757 240 : PCIDSKBuffer ih(1024);
758 240 : PCIDSKChannel *channel = nullptr;
759 480 : if( ih_start_block == 0 ||
760 480 : ih_start_block-1 > std::numeric_limits<uint64>::max() / 512 ||
761 240 : (ih_start_block-1)*512 > std::numeric_limits<uint64>::max() - (channelnum-1)*1024 )
762 : {
763 0 : return ThrowPCIDSKException( "Integer overflow when computing ih_offset");
764 : }
765 240 : uint64 ih_offset = (ih_start_block-1)*512 + (channelnum-1)*1024;
766 :
767 240 : ReadFromFile( ih.buffer, ih_offset, 1024 );
768 :
769 : // fetch the filename, if there is one.
770 240 : std::string filename;
771 240 : ih.Get(64,64,filename);
772 240 : filename.resize(strlen(filename.c_str()));
773 :
774 : // Check for an extended link file
775 240 : bool bLinked = false;
776 240 : if (STARTS_WITH(filename.c_str(), "LNK"))
777 : {
778 0 : std::string seg_str(filename, 4, 4);
779 0 : unsigned int seg_num = std::atoi(seg_str.c_str());
780 :
781 0 : if (seg_num == 0)
782 : {
783 : throw PCIDSKException("Unable to find link segment. Link name:%s",
784 0 : filename.c_str());
785 : }
786 :
787 : CLinkSegment* link_seg =
788 0 : dynamic_cast<CLinkSegment*>(GetSegment(seg_num));
789 0 : if (link_seg == nullptr)
790 : {
791 0 : throw PCIDSKException("Failed to get Link Information Segment.");
792 : }
793 :
794 0 : filename = link_seg->GetPath();
795 0 : bLinked = true;
796 : }
797 28 : else if (!filename.empty() &&
798 268 : filename != "<uninitialized>" &&
799 267 : filename.substr(0,5) != "/SIS=")
800 : {
801 : // adjust it relative to the path of the pcidsk file.
802 4 : std::string oTmp = interfaces.MergeRelativePath(interfaces.io,base_filename,filename);
803 :
804 6 : if(std::ifstream( filename.c_str() ).is_open() ||
805 4 : std::ifstream( oTmp.c_str() ).is_open() )
806 : {
807 2 : bLinked = true;
808 : }
809 :
810 : try
811 : {
812 10 : PCIDSK::EDBFile* poEDBFile = interfaces.OpenEDB(oTmp.c_str(),"r");
813 0 : delete poEDBFile;
814 0 : bLinked = true;
815 : }
816 2 : catch(...)
817 : {
818 2 : bLinked = false;
819 : }
820 : }
821 240 : if (bLinked)
822 : {
823 : // adjust it relative to the path of the pcidsk file.
824 0 : filename = interfaces.MergeRelativePath(interfaces.io,base_filename,filename);
825 : }
826 : // work out channel type from header
827 : eChanType pixel_type;
828 240 : const char *pixel_type_string = ih.Get( 160, 8 );
829 :
830 240 : pixel_type = GetDataTypeFromName(pixel_type_string);
831 :
832 : // For file interleaved channels, we expect a valid channel type.
833 240 : if (interleaving == "FILE" && pixel_type == CHN_UNKNOWN)
834 : {
835 0 : return ThrowPCIDSKException("Invalid or unsupported channel type: %s",
836 0 : pixel_type_string);
837 : }
838 :
839 : // if we didn't get channel type in header, work out from counts (old).
840 : // Check this only if we don't have complex channels:
841 :
842 240 : if (STARTS_WITH(pixel_type_string," "))
843 : {
844 0 : if( !( count_c32r == 0 && count_c16u == 0 && count_c16s == 0 ) )
845 0 : return ThrowPCIDSKException("Assertion 'count_c32r == 0 && count_c16u == 0 && count_c16s == 0' failed");
846 :
847 0 : if( channelnum <= count_8u )
848 0 : pixel_type = CHN_8U;
849 0 : else if( channelnum <= count_8u + count_16s )
850 0 : pixel_type = CHN_16S;
851 0 : else if( channelnum <= count_8u + count_16s + count_16u )
852 0 : pixel_type = CHN_16U;
853 : else
854 0 : pixel_type = CHN_32R;
855 : }
856 :
857 240 : if( interleaving == "BAND" )
858 : {
859 212 : channel = new CBandInterleavedChannel( ih, ih_offset, fh,
860 : channelnum, this,
861 212 : image_offset, pixel_type );
862 :
863 :
864 212 : image_offset += (int64)DataTypeSize(channel->GetType())
865 212 : * (int64)width * (int64)height;
866 : }
867 :
868 28 : else if( interleaving == "PIXEL" )
869 : {
870 0 : channel = new CPixelInterleavedChannel( ih, ih_offset, fh,
871 : channelnum, this,
872 : (int) image_offset,
873 0 : pixel_type );
874 0 : image_offset += DataTypeSize(pixel_type);
875 : }
876 :
877 28 : else if( interleaving == "FILE"
878 28 : && STARTS_WITH(filename.c_str(), "/SIS=") )
879 : {
880 25 : channel = new CTiledChannel( ih, ih_offset, fh,
881 25 : channelnum, this, pixel_type );
882 : }
883 :
884 9 : else if( bLinked ||
885 3 : (interleaving == "FILE"
886 3 : && filename != ""
887 3 : && !STARTS_WITH(((const char*)ih.buffer)+282, " ")) )
888 : {
889 0 : channel = new CExternalChannel( ih, ih_offset, fh, filename,
890 0 : channelnum, this, pixel_type );
891 : }
892 :
893 3 : else if( interleaving == "FILE" )
894 : {
895 3 : channel = new CBandInterleavedChannel( ih, ih_offset, fh,
896 : channelnum, this,
897 3 : 0, pixel_type );
898 : }
899 :
900 : else
901 0 : return ThrowPCIDSKException( "Unsupported interleaving:%s",
902 0 : interleaving.c_str() );
903 :
904 240 : channels.push_back( channel );
905 : }
906 : }
907 :
908 : /************************************************************************/
909 : /* ReadFromFile() */
910 : /************************************************************************/
911 :
912 4494 : void CPCIDSKFile::ReadFromFile( void *buffer, uint64 offset, uint64 size )
913 :
914 : {
915 4494 : MutexHolder oHolder( io_mutex );
916 :
917 4494 : interfaces.io->Seek( io_handle, offset, SEEK_SET );
918 :
919 4494 : uint64 nReadSize = interfaces.io->Read(buffer, 1, size, io_handle);
920 :
921 4494 : if (nReadSize != size)
922 : {
923 : // Only throw an exception if the sum of the offset and size is
924 : // greater than the internal file size.
925 8 : if (offset + size > file_size * 512)
926 : {
927 0 : std::stringstream oOffsetStream;
928 0 : std::stringstream oSizeStream;
929 :
930 0 : oOffsetStream << offset;
931 0 : oSizeStream << size;
932 :
933 0 : return ThrowPCIDSKException("Failed to read %s bytes at offset %s in file: %s",
934 0 : oSizeStream.str().c_str(),
935 0 : oOffsetStream.str().c_str(),
936 0 : base_filename.c_str());
937 : }
938 :
939 : // If we were not able to read the requested size, initialize
940 : // the remaining bytes to 0.
941 8 : std::memset((char *) buffer + nReadSize, 0,
942 8 : static_cast<size_t>(size - nReadSize));
943 : }
944 : }
945 :
946 : /************************************************************************/
947 : /* WriteToFile() */
948 : /************************************************************************/
949 :
950 11738 : void CPCIDSKFile::WriteToFile( const void *buffer, uint64 offset, uint64 size )
951 :
952 : {
953 11738 : if( !GetUpdatable() )
954 0 : throw PCIDSKException( "File not open for update in WriteToFile()" );
955 :
956 23476 : MutexHolder oHolder( io_mutex );
957 :
958 11738 : interfaces.io->Seek( io_handle, offset, SEEK_SET );
959 :
960 11738 : uint64 nWriteSize = interfaces.io->Write(buffer, 1, size, io_handle);
961 :
962 11738 : if (nWriteSize != size)
963 : {
964 2 : std::stringstream oOffsetStream;
965 2 : std::stringstream oSizeStream;
966 :
967 1 : oOffsetStream << offset;
968 1 : oSizeStream << size;
969 :
970 3 : ThrowPCIDSKException("Failed to write %s bytes at offset %s in file: %s",
971 3 : oSizeStream.str().c_str(),
972 3 : oOffsetStream.str().c_str(),
973 : base_filename.c_str());
974 : }
975 11737 : }
976 :
977 : /************************************************************************/
978 : /* ReadAndLockBlock() */
979 : /************************************************************************/
980 :
981 0 : void *CPCIDSKFile::ReadAndLockBlock( int block_index,
982 : int win_xoff, int win_xsize )
983 :
984 : {
985 0 : if( last_block_data == nullptr )
986 0 : return ThrowPCIDSKExceptionPtr( "ReadAndLockBlock() called on a file that is not pixel interleaved." );
987 :
988 : /* -------------------------------------------------------------------- */
989 : /* Default, and validate windowing. */
990 : /* -------------------------------------------------------------------- */
991 0 : if( win_xoff == -1 && win_xsize == -1 )
992 : {
993 0 : win_xoff = 0;
994 0 : win_xsize = GetWidth();
995 : }
996 :
997 0 : if( win_xoff < 0 || win_xoff+win_xsize > GetWidth() )
998 : {
999 0 : return ThrowPCIDSKExceptionPtr( "CPCIDSKFile::ReadAndLockBlock(): Illegal window - xoff=%d, xsize=%d",
1000 0 : win_xoff, win_xsize );
1001 : }
1002 :
1003 0 : if( block_index == last_block_index
1004 0 : && win_xoff == last_block_xoff
1005 0 : && win_xsize == last_block_xsize )
1006 : {
1007 0 : last_block_mutex->Acquire();
1008 0 : return last_block_data;
1009 : }
1010 :
1011 : /* -------------------------------------------------------------------- */
1012 : /* Flush any dirty writable data. */
1013 : /* -------------------------------------------------------------------- */
1014 : // We cannot use FlushBlock() because this method needs to acquire the
1015 : // last_block_mutex but we must do everything at once with last_block_data
1016 : // to prevent race conditions.
1017 : //
1018 : // --------------------------------------------------------------------
1019 : // Process 1 Process 2
1020 : // FlushBlock()
1021 : // FlushBlock()
1022 : // last_block_mutex->Acquire();
1023 : // ReadFromFile(last_block_data);
1024 : // last_block_mutex->Release();
1025 : // last_block_mutex->Acquire();
1026 : // ReadFromFile(last_block_data);
1027 : // last_block_mutex->Release();
1028 : // --------------------------------------------------------------------
1029 : //
1030 : // Now the data in last_block_data set by process 1 will never be flushed
1031 : // because process 2 overwrites it without calling FlushBlock.
1032 :
1033 0 : last_block_mutex->Acquire();
1034 :
1035 0 : if( last_block_dirty )
1036 : {
1037 0 : WriteBlock( last_block_index, last_block_data );
1038 0 : last_block_dirty = 0;
1039 : }
1040 : /* -------------------------------------------------------------------- */
1041 : /* Read the requested window. */
1042 : /* -------------------------------------------------------------------- */
1043 :
1044 0 : ReadFromFile( last_block_data,
1045 0 : first_line_offset + block_index*block_size
1046 0 : + static_cast<uint64_t>(win_xoff) * pixel_group_size,
1047 0 : static_cast<uint64_t>(pixel_group_size) * win_xsize );
1048 0 : last_block_index = block_index;
1049 0 : last_block_xoff = win_xoff;
1050 0 : last_block_xsize = win_xsize;
1051 :
1052 0 : return last_block_data;
1053 : }
1054 :
1055 : /************************************************************************/
1056 : /* UnlockBlock() */
1057 : /************************************************************************/
1058 :
1059 0 : void CPCIDSKFile::UnlockBlock( bool mark_dirty )
1060 :
1061 : {
1062 0 : if( last_block_mutex == nullptr )
1063 0 : return;
1064 :
1065 0 : last_block_dirty |= mark_dirty;
1066 0 : last_block_mutex->Release();
1067 : }
1068 :
1069 : /************************************************************************/
1070 : /* WriteBlock() */
1071 : /************************************************************************/
1072 :
1073 0 : void CPCIDSKFile::WriteBlock( int block_index, void *buffer )
1074 :
1075 : {
1076 0 : if( !GetUpdatable() )
1077 0 : return ThrowPCIDSKException( "File not open for update in WriteBlock()" );
1078 :
1079 0 : if( last_block_data == nullptr )
1080 0 : return ThrowPCIDSKException( "WriteBlock() called on a file that is not pixel interleaved." );
1081 :
1082 0 : WriteToFile( buffer,
1083 0 : first_line_offset + block_index*block_size,
1084 : block_size );
1085 : }
1086 :
1087 : /************************************************************************/
1088 : /* FlushBlock() */
1089 : /************************************************************************/
1090 :
1091 242 : void CPCIDSKFile::FlushBlock()
1092 :
1093 : {
1094 242 : if( last_block_dirty )
1095 : {
1096 0 : last_block_mutex->Acquire();
1097 0 : if( last_block_dirty ) // is it still dirty?
1098 : {
1099 0 : WriteBlock( last_block_index, last_block_data );
1100 0 : last_block_dirty = false;
1101 : }
1102 0 : last_block_mutex->Release();
1103 : }
1104 242 : }
1105 :
1106 : /************************************************************************/
1107 : /* GetEDBFileDetails() */
1108 : /************************************************************************/
1109 :
1110 0 : bool CPCIDSKFile::GetEDBFileDetails( EDBFile** file_p,
1111 : Mutex **io_mutex_p,
1112 : const std::string& filename )
1113 :
1114 : {
1115 0 : *file_p = nullptr;
1116 0 : *io_mutex_p = nullptr;
1117 :
1118 : /* -------------------------------------------------------------------- */
1119 : /* Does the file exist already in our file list? */
1120 : /* -------------------------------------------------------------------- */
1121 : unsigned int i;
1122 :
1123 0 : for( i = 0; i < edb_file_list.size(); i++ )
1124 : {
1125 0 : if( edb_file_list[i].filename == filename )
1126 : {
1127 0 : *file_p = edb_file_list[i].file;
1128 0 : *io_mutex_p = edb_file_list[i].io_mutex;
1129 0 : return edb_file_list[i].writable;
1130 : }
1131 : }
1132 :
1133 : /* -------------------------------------------------------------------- */
1134 : /* If not, we need to try and open the file. Eventually we */
1135 : /* will need better rules about read or update access. */
1136 : /* -------------------------------------------------------------------- */
1137 0 : ProtectedEDBFile new_file;
1138 :
1139 0 : new_file.file = nullptr;
1140 0 : new_file.writable = false;
1141 :
1142 0 : if( GetUpdatable() )
1143 : {
1144 : try
1145 : {
1146 0 : new_file.file = interfaces.OpenEDB( filename, "r+" );
1147 0 : new_file.writable = true;
1148 : }
1149 0 : catch (PCIDSK::PCIDSKException &)
1150 : {
1151 : }
1152 0 : catch (std::exception &)
1153 : {
1154 : }
1155 : }
1156 :
1157 0 : if( new_file.file == nullptr )
1158 0 : new_file.file = interfaces.OpenEDB( filename, "r" );
1159 :
1160 0 : if( new_file.file == nullptr )
1161 0 : return ThrowPCIDSKException( 0, "Unable to open file '%s'.",
1162 0 : filename.c_str() ) != 0;
1163 :
1164 : /* -------------------------------------------------------------------- */
1165 : /* Push the new file into the list of files managed for this */
1166 : /* PCIDSK file. */
1167 : /* -------------------------------------------------------------------- */
1168 0 : new_file.io_mutex = interfaces.CreateMutex();
1169 0 : new_file.filename = filename;
1170 :
1171 0 : edb_file_list.push_back( new_file );
1172 :
1173 0 : *file_p = edb_file_list.back().file;
1174 0 : *io_mutex_p = edb_file_list.back().io_mutex;
1175 :
1176 0 : return new_file.writable;
1177 : }
1178 :
1179 : /************************************************************************/
1180 : /* GetUniqueEDBFilename() */
1181 : /************************************************************************/
1182 : /**
1183 : * If the PIX file is a link pix where all channels are linked
1184 : * to the same file, then this filename is given to be able to
1185 : * access it directly without going through PCIDSK code.
1186 : */
1187 0 : std::string CPCIDSKFile::GetUniqueEDBFilename()
1188 : {
1189 0 : bool bAllSameFile = true;
1190 0 : bool bAllExternal = true;
1191 :
1192 0 : std::string oEDBName;
1193 :
1194 0 : for(int iChan=1 ; iChan <= this->GetChannels() ; iChan++)
1195 : {
1196 0 : PCIDSK::PCIDSKChannel* poChannel = this->GetChannel(iChan);
1197 :
1198 : PCIDSK::CExternalChannel* poExt =
1199 0 : dynamic_cast<PCIDSK::CExternalChannel*>(poChannel);
1200 :
1201 0 : if(!poExt)
1202 : {
1203 0 : bAllExternal = false;
1204 0 : break;
1205 : }
1206 :
1207 : //trigger call to AccessDB()
1208 0 : poChannel->GetBlockWidth();
1209 :
1210 0 : const std::string oFilename = poExt->GetExternalFilename();
1211 :
1212 0 : if(oEDBName.size() == 0)
1213 : {
1214 0 : oEDBName = oFilename;
1215 : }
1216 0 : else if(oEDBName != oFilename)
1217 : {
1218 0 : bAllSameFile = false;
1219 0 : break;
1220 : }
1221 : }
1222 :
1223 0 : if(bAllExternal && bAllSameFile)
1224 : {
1225 0 : return oEDBName;
1226 : }
1227 0 : return "";
1228 : }
1229 :
1230 : /************************************************************************/
1231 : /* GetEDBChannelMap() */
1232 : /************************************************************************/
1233 : /**
1234 : * Gets the mapping between channels in this CPCIDSKFile and the channels
1235 : * they are linked to in the external file.
1236 : *
1237 : * Current File External File
1238 : * 2 -----------------> 1
1239 : * 3 -----------------> 3
1240 : * 4 -----------------> 6
1241 : *
1242 : * This would return this map:
1243 : * (2,1), (3,3), (4,6)
1244 : *
1245 : * @param oExtFilename The external file we want a mapping for
1246 : *
1247 : * @return A map with the mapping between the channels in the current file
1248 : * and the channels the refer to in the external file.
1249 : */
1250 0 : std::map<int,int> CPCIDSKFile::GetEDBChannelMap(std::string oExtFilename)
1251 : {
1252 0 : std::map<int,int> vnChanMap;
1253 :
1254 0 : for(int iChan=1 ; iChan <= this->GetChannels() ; iChan++)
1255 : {
1256 0 : PCIDSK::PCIDSKChannel* poChannel = this->GetChannel(iChan);
1257 :
1258 : PCIDSK::CExternalChannel* poExt =
1259 0 : dynamic_cast<PCIDSK::CExternalChannel*>(poChannel);
1260 :
1261 0 : if(poExt)
1262 : {
1263 0 : std::string oFilename = poExt->GetExternalFilename();
1264 :
1265 0 : if(oExtFilename == oFilename)
1266 0 : vnChanMap[iChan] = poExt->GetExternalChanNum();
1267 : }
1268 : }
1269 :
1270 0 : return vnChanMap;
1271 : }
1272 :
1273 : /************************************************************************/
1274 : /* GetIODetails() */
1275 : /************************************************************************/
1276 :
1277 214 : void CPCIDSKFile::GetIODetails( void ***io_handle_pp,
1278 : Mutex ***io_mutex_pp,
1279 : const std::string& filename,
1280 : bool writable )
1281 :
1282 : {
1283 214 : *io_handle_pp = nullptr;
1284 214 : *io_mutex_pp = nullptr;
1285 :
1286 : /* -------------------------------------------------------------------- */
1287 : /* Does this reference the PCIDSK file itself? */
1288 : /* -------------------------------------------------------------------- */
1289 214 : if( filename.empty() )
1290 : {
1291 212 : *io_handle_pp = &io_handle;
1292 212 : *io_mutex_pp = &io_mutex;
1293 212 : return;
1294 : }
1295 :
1296 : /* -------------------------------------------------------------------- */
1297 : /* Does the file exist already in our file list? */
1298 : /* -------------------------------------------------------------------- */
1299 : unsigned int i;
1300 :
1301 2 : for( i = 0; i < file_list.size(); i++ )
1302 : {
1303 0 : if( file_list[i].filename == filename
1304 0 : && (!writable || file_list[i].writable) )
1305 : {
1306 0 : *io_handle_pp = &(file_list[i].io_handle);
1307 0 : *io_mutex_pp = &(file_list[i].io_mutex);
1308 0 : return;
1309 : }
1310 : }
1311 :
1312 : /* -------------------------------------------------------------------- */
1313 : /* If not, we need to try and open the file. Eventually we */
1314 : /* will need better rules about read or update access. */
1315 : /* -------------------------------------------------------------------- */
1316 2 : ProtectedFile new_file;
1317 :
1318 2 : if( writable )
1319 1 : new_file.io_handle = interfaces.io->Open( filename, "r+" );
1320 : else
1321 1 : new_file.io_handle = interfaces.io->Open( filename, "r" );
1322 :
1323 2 : if( new_file.io_handle == nullptr )
1324 0 : return ThrowPCIDSKException( "Unable to open file '%s'.",
1325 0 : filename.c_str() );
1326 :
1327 : /* -------------------------------------------------------------------- */
1328 : /* Push the new file into the list of files managed for this */
1329 : /* PCIDSK file. */
1330 : /* -------------------------------------------------------------------- */
1331 2 : new_file.io_mutex = interfaces.CreateMutex();
1332 2 : new_file.filename = filename;
1333 2 : new_file.writable = writable;
1334 :
1335 2 : file_list.push_back( new_file );
1336 :
1337 2 : *io_handle_pp = &(file_list.back().io_handle);
1338 2 : *io_mutex_pp = &(file_list.back().io_mutex);
1339 : }
1340 :
1341 : /************************************************************************/
1342 : /* DeleteSegment() */
1343 : /************************************************************************/
1344 :
1345 1 : void CPCIDSKFile::DeleteSegment( int segment )
1346 :
1347 : {
1348 : /* -------------------------------------------------------------------- */
1349 : /* Is this an existing segment? */
1350 : /* -------------------------------------------------------------------- */
1351 1 : PCIDSKSegment *poSeg = GetSegment( segment );
1352 :
1353 1 : if( poSeg == nullptr )
1354 0 : return ThrowPCIDSKException( "DeleteSegment(%d) failed, segment does not exist.", segment );
1355 :
1356 : /* -------------------------------------------------------------------- */
1357 : /* Wipe associated metadata. */
1358 : /* -------------------------------------------------------------------- */
1359 2 : std::vector<std::string> md_keys = poSeg->GetMetadataKeys();
1360 : unsigned int i;
1361 :
1362 1 : for( i = 0; i < md_keys.size(); i++ )
1363 0 : poSeg->SetMetadataValue( md_keys[i], "" );
1364 :
1365 : /* -------------------------------------------------------------------- */
1366 : /* Remove the segment object from the segment object cache. I */
1367 : /* hope the application is not retaining any references to this */
1368 : /* segment! */
1369 : /* -------------------------------------------------------------------- */
1370 1 : segments[segment] = nullptr;
1371 1 : delete poSeg;
1372 :
1373 : /* -------------------------------------------------------------------- */
1374 : /* Mark the segment pointer as deleted. */
1375 : /* -------------------------------------------------------------------- */
1376 1 : segment_pointers.buffer[(segment-1)*32] = 'D';
1377 :
1378 : // write the updated segment pointer back to the file.
1379 1 : WriteToFile( segment_pointers.buffer + (segment-1)*32,
1380 1 : segment_pointers_offset + (segment-1)*32,
1381 : 32 );
1382 : }
1383 :
1384 : /************************************************************************/
1385 : /* CreateSegment() */
1386 : /************************************************************************/
1387 :
1388 1256 : int CPCIDSKFile::CreateSegment( std::string name, std::string description,
1389 : eSegType seg_type, int data_blocks )
1390 :
1391 : {
1392 : /* -------------------------------------------------------------------- */
1393 : /* Set the size of fixed length segments. */
1394 : /* -------------------------------------------------------------------- */
1395 1256 : int expected_data_blocks = 0;
1396 1256 : bool prezero = false;
1397 :
1398 1256 : switch( seg_type )
1399 : {
1400 1 : case SEG_PCT:
1401 1 : expected_data_blocks = 6;
1402 1 : break;
1403 :
1404 0 : case SEG_BPCT:
1405 0 : expected_data_blocks = 12;
1406 0 : break;
1407 :
1408 0 : case SEG_LUT:
1409 0 : expected_data_blocks = 2;
1410 0 : break;
1411 :
1412 0 : case SEG_BLUT:
1413 0 : expected_data_blocks = 6;
1414 0 : break;
1415 :
1416 0 : case SEG_SIG:
1417 0 : expected_data_blocks = 12;
1418 0 : break;
1419 :
1420 0 : case SEG_GCP2:
1421 : // expected_data_blocks = 67;
1422 : // Change seg type to new GCP segment type
1423 0 : expected_data_blocks = 129;
1424 0 : break;
1425 :
1426 118 : case SEG_GEO:
1427 118 : expected_data_blocks = 6;
1428 118 : break;
1429 :
1430 0 : case SEG_TEX:
1431 0 : expected_data_blocks = 64;
1432 0 : prezero = true;
1433 0 : break;
1434 :
1435 0 : case SEG_BIT:
1436 : {
1437 0 : uint64 bytes = ((width * (uint64) height) + 7) / 8;
1438 0 : expected_data_blocks = (int) ((bytes + 511) / 512);
1439 0 : prezero = true;
1440 : }
1441 0 : break;
1442 :
1443 1137 : default:
1444 1137 : break;
1445 : }
1446 :
1447 1256 : if( data_blocks == 0 && expected_data_blocks != 0 )
1448 1 : data_blocks = expected_data_blocks;
1449 :
1450 : /* -------------------------------------------------------------------- */
1451 : /* Find an empty Segment Pointer. For System segments we start */
1452 : /* at the end, instead of the beginning to avoid using up */
1453 : /* segment numbers that the user would notice. */
1454 : /* -------------------------------------------------------------------- */
1455 1256 : int segment = 1;
1456 1256 : int64 seg_start = -1;
1457 2512 : PCIDSKBuffer segptr( 32 );
1458 :
1459 1256 : if( seg_type == SEG_SYS )
1460 : {
1461 72 : for( segment=segment_count; segment >= 1; segment-- )
1462 : {
1463 72 : memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
1464 :
1465 72 : uint64 this_seg_size = segptr.GetUInt64(23,9);
1466 72 : char flag = (char) segptr.buffer[0];
1467 :
1468 72 : if( flag == 'D'
1469 0 : && (uint64) data_blocks+2 == this_seg_size
1470 0 : && this_seg_size > 0 )
1471 0 : seg_start = segptr.GetUInt64(12,11) - 1;
1472 72 : else if( flag == ' ' )
1473 48 : seg_start = 0;
1474 24 : else if( flag && this_seg_size == 0 )
1475 0 : seg_start = 0;
1476 :
1477 72 : if( seg_start != -1 )
1478 48 : break;
1479 : }
1480 : }
1481 : else
1482 : {
1483 534312 : for( segment=1; segment <= segment_count; segment++ )
1484 : {
1485 534303 : memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
1486 :
1487 534303 : uint64 this_seg_size = segptr.GetUInt64(23,9);
1488 534303 : char flag = (char) segptr.buffer[0];
1489 :
1490 534303 : if( flag == 'D'
1491 0 : && (uint64) data_blocks+2 == this_seg_size
1492 0 : && this_seg_size > 0 )
1493 0 : seg_start = segptr.GetUInt64(12,11) - 1;
1494 534303 : else if( flag == ' ' )
1495 1199 : seg_start = 0;
1496 533104 : else if( flag && this_seg_size == 0 )
1497 0 : seg_start = 0;
1498 :
1499 534303 : if( seg_start != -1 )
1500 1199 : break;
1501 : }
1502 : }
1503 :
1504 1256 : if( segment <= 0 || segment > segment_count )
1505 9 : return ThrowPCIDSKException(0, "All %d segment pointers in use.", segment_count);
1506 :
1507 : /* -------------------------------------------------------------------- */
1508 : /* If the segment does not have a data area already, identify */
1509 : /* its location at the end of the file, and extend the file to */
1510 : /* the desired length. */
1511 : /* -------------------------------------------------------------------- */
1512 1247 : if( seg_start == 0 )
1513 : {
1514 1247 : seg_start = GetFileSize();
1515 1247 : ExtendFile( data_blocks + 2, prezero );
1516 : }
1517 : else
1518 : {
1519 0 : std::vector<uint8> zeros;
1520 0 : uint64 blocks_to_zero = data_blocks + 2;
1521 0 : uint64 segiter = seg_start;
1522 :
1523 0 : zeros.resize( 512 * 32 );
1524 :
1525 0 : while( blocks_to_zero > 0 )
1526 : {
1527 0 : uint64 this_time = blocks_to_zero;
1528 0 : if( this_time > 32 )
1529 0 : this_time = 32;
1530 :
1531 0 : WriteToFile( &(zeros[0]), segiter * 512, this_time*512 );
1532 0 : blocks_to_zero -= this_time;
1533 0 : segiter += this_time;
1534 : }
1535 : }
1536 :
1537 : /* -------------------------------------------------------------------- */
1538 : /* Update the segment pointer information. */
1539 : /* -------------------------------------------------------------------- */
1540 : // SP1.1 - Flag
1541 1246 : segptr.Put( "A", 0, 1 );
1542 :
1543 : // SP1.2 - Type
1544 1246 : segptr.Put( (int) seg_type, 1, 3 );
1545 :
1546 : // SP1.3 - Name
1547 1246 : segptr.Put( name.c_str(), 4, 8 );
1548 :
1549 : // SP1.4 - start block
1550 1246 : segptr.Put( (uint64) (seg_start + 1), 12, 11 );
1551 :
1552 : // SP1.5 - data blocks.
1553 1246 : segptr.Put( data_blocks+2, 23, 9 );
1554 :
1555 : // Update in memory copy of segment pointers.
1556 1246 : memcpy( segment_pointers.buffer+(segment-1)*32, segptr.buffer, 32);
1557 :
1558 : // Update on disk.
1559 1246 : WriteToFile( segptr.buffer,
1560 1246 : segment_pointers_offset + (segment-1)*32, 32 );
1561 :
1562 : /* -------------------------------------------------------------------- */
1563 : /* Prepare segment header. */
1564 : /* -------------------------------------------------------------------- */
1565 1246 : PCIDSKBuffer sh(1024);
1566 :
1567 : char current_time[17];
1568 :
1569 1246 : GetCurrentDateTime( current_time );
1570 :
1571 1246 : sh.Put( " ", 0, 1024 );
1572 :
1573 : // SH1 - segment content description
1574 1246 : sh.Put( description.c_str(), 0, 64 );
1575 :
1576 : // SH3 - Creation time/date
1577 1246 : sh.Put( current_time, 128, 16 );
1578 :
1579 : // SH4 - Last Update time/date
1580 1246 : sh.Put( current_time, 144, 16 );
1581 :
1582 : /* -------------------------------------------------------------------- */
1583 : /* Write segment header. */
1584 : /* -------------------------------------------------------------------- */
1585 1246 : WriteToFile( sh.buffer, seg_start * 512, 1024 );
1586 :
1587 : /* -------------------------------------------------------------------- */
1588 : /* Initialize the newly created segment. */
1589 : /* -------------------------------------------------------------------- */
1590 1246 : PCIDSKSegment *seg_obj = GetSegment( segment );
1591 :
1592 1246 : seg_obj->Initialize();
1593 :
1594 : #ifdef PCIDSK_IMP_PARAM_SUPPORT
1595 : // Update the Last Segment Number Created parameter.
1596 : if (seg_type != SEG_SYS)
1597 : {
1598 : float fLASC = (float) segment;
1599 : IMPPutReal("LASC;", &fLASC, 1);
1600 : }
1601 : #endif
1602 :
1603 1246 : return segment;
1604 : }// CreateSegment
1605 :
1606 : /************************************************************************/
1607 : /* ExtendFile() */
1608 : /************************************************************************/
1609 :
1610 2432 : void CPCIDSKFile::ExtendFile( uint64 blocks_requested,
1611 : bool prezero, bool writedata )
1612 :
1613 : {
1614 2432 : if( prezero )
1615 : {
1616 0 : const int nBufferSize = 64 * 1024 * 1024;
1617 0 : const int nBufferBlocks = nBufferSize / 512;
1618 :
1619 0 : PCIDSKBuffer oZero(nBufferSize);
1620 :
1621 0 : std::memset(oZero.buffer, 0, nBufferSize);
1622 :
1623 0 : uint64 nBlockCount = blocks_requested;
1624 :
1625 0 : while (nBlockCount > 0)
1626 : {
1627 0 : uint64 nCurrentBlocks = nBlockCount;
1628 0 : if (nCurrentBlocks > nBufferBlocks)
1629 0 : nCurrentBlocks = nBufferBlocks;
1630 :
1631 0 : WriteToFile(oZero.buffer, file_size * 512, nCurrentBlocks * 512);
1632 :
1633 0 : nBlockCount -= nCurrentBlocks;
1634 :
1635 0 : file_size += nCurrentBlocks;
1636 : }
1637 : }
1638 : else
1639 : {
1640 2432 : if ( writedata )
1641 2415 : WriteToFile( "\0", (file_size + blocks_requested) * 512 - 1, 1 );
1642 :
1643 2431 : file_size += blocks_requested;
1644 : }
1645 :
1646 4862 : PCIDSKBuffer fh3( 16 );
1647 2431 : fh3.Put( file_size, 0, 16 );
1648 2431 : WriteToFile( fh3.buffer, 16, 16 );
1649 2431 : }
1650 :
1651 : /************************************************************************/
1652 : /* ExtendSegment() */
1653 : /************************************************************************/
1654 :
1655 1176 : void CPCIDSKFile::ExtendSegment( int segment, uint64 blocks_requested,
1656 : bool prezero, bool writedata )
1657 :
1658 : {
1659 1176 : PCIDSKSegment * poSegment = GetSegment(segment);
1660 :
1661 1176 : if (!poSegment)
1662 : {
1663 0 : return ThrowPCIDSKException("ExtendSegment(%d) failed, "
1664 0 : "segment does not exist.", segment);
1665 : }
1666 :
1667 : // Move the segment at the end of file if necessary.
1668 1176 : if (!poSegment->IsAtEOF())
1669 9 : MoveSegmentToEOF(segment);
1670 :
1671 : // Extend the file.
1672 1176 : ExtendFile( blocks_requested, prezero, writedata );
1673 :
1674 : // Update the segment pointer in memory and on disk.
1675 1176 : int segptr_off = (segment - 1) * 32;
1676 :
1677 2352 : segment_pointers.Put(
1678 1176 : segment_pointers.GetUInt64(segptr_off+23,9) + blocks_requested,
1679 : segptr_off+23, 9 );
1680 :
1681 1176 : WriteToFile( segment_pointers.buffer + segptr_off,
1682 1176 : segment_pointers_offset + segptr_off,
1683 : 32 );
1684 :
1685 : // Update the segment information.
1686 1176 : poSegment->LoadSegmentPointer(segment_pointers.buffer + segptr_off);
1687 : }
1688 :
1689 : /************************************************************************/
1690 : /* MoveSegmentToEOF() */
1691 : /************************************************************************/
1692 :
1693 9 : void CPCIDSKFile::MoveSegmentToEOF( int segment )
1694 :
1695 : {
1696 9 : PCIDSKSegment * poSegment = GetSegment(segment);
1697 :
1698 9 : if (!poSegment)
1699 : {
1700 0 : return ThrowPCIDSKException("MoveSegmentToEOF(%d) failed, "
1701 0 : "segment does not exist.", segment);
1702 : }
1703 :
1704 9 : int segptr_off = (segment - 1) * 32;
1705 : uint64 seg_start, seg_size;
1706 : uint64 new_seg_start;
1707 :
1708 9 : seg_start = segment_pointers.GetUInt64( segptr_off + 12, 11 );
1709 9 : seg_size = segment_pointers.GetUInt64( segptr_off + 23, 9 );
1710 :
1711 : // Are we already at the end of the file?
1712 9 : if( (seg_start + seg_size - 1) == file_size )
1713 0 : return;
1714 :
1715 9 : new_seg_start = file_size + 1;
1716 :
1717 : // Grow the file to hold the segment at the end.
1718 9 : ExtendFile( seg_size, false, false );
1719 :
1720 : // Move the segment data to the new location.
1721 : uint8 copy_buf[16384];
1722 : uint64 srcoff, dstoff, bytes_to_go;
1723 :
1724 9 : bytes_to_go = seg_size * 512;
1725 9 : srcoff = (seg_start - 1) * 512;
1726 9 : dstoff = (new_seg_start - 1) * 512;
1727 :
1728 18 : while( bytes_to_go > 0 )
1729 : {
1730 9 : uint64 bytes_this_chunk = sizeof(copy_buf);
1731 9 : if( bytes_to_go < bytes_this_chunk )
1732 9 : bytes_this_chunk = bytes_to_go;
1733 :
1734 9 : ReadFromFile( copy_buf, srcoff, bytes_this_chunk );
1735 9 : WriteToFile( copy_buf, dstoff, bytes_this_chunk );
1736 :
1737 9 : srcoff += bytes_this_chunk;
1738 9 : dstoff += bytes_this_chunk;
1739 9 : bytes_to_go -= bytes_this_chunk;
1740 : }
1741 :
1742 : // Update the segment pointer in memory and on disk.
1743 9 : segment_pointers.Put( new_seg_start, segptr_off + 12, 11 );
1744 :
1745 9 : WriteToFile( segment_pointers.buffer + segptr_off,
1746 9 : segment_pointers_offset + segptr_off,
1747 : 32 );
1748 :
1749 : // Update the segment information.
1750 9 : poSegment->LoadSegmentPointer(segment_pointers.buffer + segptr_off);
1751 : }
1752 :
1753 : /************************************************************************/
1754 : /* CreateOverviews() */
1755 : /************************************************************************/
1756 : /*
1757 : const char *pszResampling;
1758 : Can be "NEAREST" for Nearest Neighbour resampling (the fastest),
1759 : "AVERAGE" for block averaging or "MODE" for block mode. This
1760 : establishing the type of resampling to be applied when preparing
1761 : the decimated overviews. Other methods can be set as well, but
1762 : not all applications might support a given overview generation
1763 : method.
1764 : */
1765 :
1766 1 : void CPCIDSKFile::CreateOverviews( int chan_count, const int *chan_list,
1767 : int factor, std::string resampling )
1768 :
1769 : {
1770 2 : std::vector<int> default_chan_list;
1771 :
1772 : /* -------------------------------------------------------------------- */
1773 : /* Default to processing all bands. */
1774 : /* -------------------------------------------------------------------- */
1775 1 : if( chan_count == 0 )
1776 : {
1777 0 : chan_count = channel_count;
1778 0 : default_chan_list.resize( chan_count );
1779 :
1780 0 : for( int i = 0; i < chan_count; i++ )
1781 0 : default_chan_list[i] = i+1;
1782 :
1783 0 : chan_list = &(default_chan_list[0]);
1784 : }
1785 :
1786 : /* -------------------------------------------------------------------- */
1787 : /* Work out the creation options that should apply for the */
1788 : /* overview. */
1789 : /* -------------------------------------------------------------------- */
1790 3 : std::string layout = GetMetadataValue( "_DBLayout" );
1791 1 : int tilesize = PCIDSK_DEFAULT_TILE_SIZE;
1792 2 : std::string compression = "NONE";
1793 :
1794 1 : if( STARTS_WITH( layout.c_str(), "TILED") )
1795 : {
1796 0 : ParseTileFormat(layout, tilesize, compression);
1797 : }
1798 :
1799 : /* -------------------------------------------------------------------- */
1800 : /* Make sure we have a block tile directory for managing the */
1801 : /* tile layers. */
1802 : /* -------------------------------------------------------------------- */
1803 2 : CPCIDSKBlockFile oBlockFile(this);
1804 :
1805 1 : SysTileDir * poTileDir = oBlockFile.GetTileDir();
1806 :
1807 1 : if (!poTileDir)
1808 1 : poTileDir = oBlockFile.CreateTileDir();
1809 :
1810 : /* ==================================================================== */
1811 : /* Loop over the channels. */
1812 : /* ==================================================================== */
1813 2 : for( int chan_index = 0; chan_index < chan_count; chan_index++ )
1814 : {
1815 1 : int channel_number = chan_list[chan_index];
1816 1 : PCIDSKChannel *channel = GetChannel( channel_number );
1817 :
1818 : /* -------------------------------------------------------------------- */
1819 : /* Figure out if the given overview level already exists */
1820 : /* for a given channel; if it does, skip creating it. */
1821 : /* -------------------------------------------------------------------- */
1822 1 : bool overview_exists = false;
1823 1 : for( int i = channel->GetOverviewCount()-1; i >= 0; i-- )
1824 : {
1825 0 : PCIDSKChannel *overview = channel->GetOverview( i );
1826 :
1827 0 : if( overview->GetWidth() == channel->GetWidth() / factor
1828 0 : && overview->GetHeight() == channel->GetHeight() / factor )
1829 : {
1830 0 : overview_exists = true;
1831 : }
1832 : }
1833 :
1834 1 : if (overview_exists == false && poTileDir != nullptr)
1835 : {
1836 : /* -------------------------------------------------------------------- */
1837 : /* Create the overview as a tiled image layer. */
1838 : /* -------------------------------------------------------------------- */
1839 : int virtual_image =
1840 2 : poTileDir->CreateTileLayer(channel->GetWidth() / factor,
1841 1 : channel->GetHeight() / factor,
1842 : tilesize, tilesize,
1843 1 : channel->GetType(),
1844 1 : compression);
1845 :
1846 : /* -------------------------------------------------------------------- */
1847 : /* Attach reference to this overview as metadata. */
1848 : /* -------------------------------------------------------------------- */
1849 : char overview_md_key[128];
1850 : char overview_md_value[128];
1851 :
1852 1 : snprintf( overview_md_key, sizeof(overview_md_key), "_Overview_%d", factor );
1853 1 : snprintf( overview_md_value, sizeof(overview_md_value), "%d 0 %s",virtual_image,resampling.c_str());
1854 :
1855 1 : channel->SetMetadataValue( overview_md_key, overview_md_value );
1856 :
1857 : /* -------------------------------------------------------------------- */
1858 : /* Update the internal overview lists */
1859 : /* -------------------------------------------------------------------- */
1860 1 : CPCIDSKChannel* cpcidskchannel = dynamic_cast<CPCIDSKChannel *>(channel);
1861 1 : if( cpcidskchannel )
1862 1 : cpcidskchannel->UpdateOverviewInfo(overview_md_value, factor);
1863 : }
1864 : }
1865 1 : }
1866 :
|