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