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