1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKFile class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada
8 : *
9 : * Permission is hereby granted, free of charge, to any person obtaining a
10 : * copy of this software and associated documentation files (the "Software"),
11 : * to deal in the Software without restriction, including without limitation
12 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : * and/or sell copies of the Software, and to permit persons to whom the
14 : * Software is furnished to do so, subject to the following conditions:
15 : *
16 : * The above copyright notice and this permission notice shall be included
17 : * in all copies or substantial portions of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 : * DEALINGS IN THE SOFTWARE.
26 : ****************************************************************************/
27 : #include "pcidsk_file.h"
28 : #include "pcidsk_exception.h"
29 : #include "pcidsk_channel.h"
30 : #include "pcidsk_segment.h"
31 : #include "core/mutexholder.h"
32 : #include "core/pcidsk_utils.h"
33 : #include "core/cpcidskfile.h"
34 :
35 : // Channel types
36 : #include "channel/cbandinterleavedchannel.h"
37 : #include "channel/cpixelinterleavedchannel.h"
38 : #include "channel/ctiledchannel.h"
39 : #include "channel/cexternalchannel.h"
40 :
41 : // Segment types
42 : #include "segment/cpcidskgeoref.h"
43 : #include "segment/cpcidskpct.h"
44 : #include "segment/cpcidskvectorsegment.h"
45 : #include "segment/metadatasegment.h"
46 : #include "segment/sysblockmap.h"
47 : #include "segment/cpcidskrpcmodel.h"
48 : #include "segment/cpcidskgcp2segment.h"
49 : #include "segment/cpcidskbitmap.h"
50 : #include "segment/cpcidsk_tex.h"
51 : #include "segment/cpcidskapmodel.h"
52 :
53 : #include <cassert>
54 : #include <cstdlib>
55 : #include <cstring>
56 : #include <cstdio>
57 : #include <string>
58 :
59 : #include <iostream>
60 :
61 : using namespace PCIDSK;
62 :
63 : /************************************************************************/
64 : /* CPCIDSKFile() */
65 : /************************************************************************/
66 :
67 124 : CPCIDSKFile::CPCIDSKFile()
68 :
69 : {
70 124 : io_handle = NULL;
71 124 : io_mutex = NULL;
72 124 : updatable = false;
73 :
74 : /* -------------------------------------------------------------------- */
75 : /* Initialize the metadata object, but do not try to load till */
76 : /* needed. */
77 : /* -------------------------------------------------------------------- */
78 124 : metadata.Initialize( this, "FIL", 0 );
79 124 : }
80 :
81 : /************************************************************************/
82 : /* ~CPCIDSKFile() */
83 : /************************************************************************/
84 :
85 124 : CPCIDSKFile::~CPCIDSKFile()
86 :
87 : {
88 124 : Synchronize();
89 :
90 : /* -------------------------------------------------------------------- */
91 : /* Cleanup last block buffer. */
92 : /* -------------------------------------------------------------------- */
93 124 : if( last_block_data != NULL )
94 : {
95 0 : last_block_index = -1;
96 0 : free( last_block_data );
97 0 : last_block_data = NULL;
98 0 : delete last_block_mutex;
99 : }
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Cleanup channels and segments. */
103 : /* -------------------------------------------------------------------- */
104 : size_t i;
105 363 : for( i = 0; i < channels.size(); i++ )
106 : {
107 239 : delete channels[i];
108 239 : channels[i] = NULL;
109 : }
110 :
111 127224 : for( i = 0; i < segments.size(); i++ )
112 : {
113 127100 : delete segments[i];
114 127100 : segments[i] = NULL;
115 : }
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Close and cleanup IO stuff. */
119 : /* -------------------------------------------------------------------- */
120 : {
121 124 : MutexHolder oHolder( io_mutex );
122 :
123 124 : if( io_handle )
124 : {
125 124 : interfaces.io->Close( io_handle );
126 124 : io_handle = NULL;
127 124 : }
128 : }
129 :
130 : size_t i_file;
131 :
132 124 : for( i_file=0; i_file < file_list.size(); i_file++ )
133 : {
134 0 : delete file_list[i_file].io_mutex;
135 0 : file_list[i_file].io_mutex = NULL;
136 :
137 0 : interfaces.io->Close( file_list[i_file].io_handle );
138 0 : file_list[i_file].io_handle = NULL;
139 : }
140 :
141 124 : for( i_file=0; i_file < edb_file_list.size(); i_file++ )
142 : {
143 0 : delete edb_file_list[i_file].io_mutex;
144 0 : edb_file_list[i_file].io_mutex = NULL;
145 :
146 0 : delete edb_file_list[i_file].file;
147 0 : edb_file_list[i_file].file = NULL;
148 : }
149 :
150 124 : delete io_mutex;
151 124 : }
152 :
153 : /************************************************************************/
154 : /* Synchronize() */
155 : /************************************************************************/
156 :
157 204 : void CPCIDSKFile::Synchronize()
158 :
159 : {
160 204 : if( !GetUpdatable() )
161 66 : return;
162 :
163 : /* -------------------------------------------------------------------- */
164 : /* Flush out last line caching stuff for pixel interleaved data. */
165 : /* -------------------------------------------------------------------- */
166 138 : FlushBlock();
167 :
168 : /* -------------------------------------------------------------------- */
169 : /* Synchronize all channels. */
170 : /* -------------------------------------------------------------------- */
171 : size_t i;
172 399 : for( i = 0; i < channels.size(); i++ )
173 261 : channels[i]->Synchronize();
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Synchronize all segments we have instantiated. */
177 : /* -------------------------------------------------------------------- */
178 141588 : for( i = 0; i < segments.size(); i++ )
179 : {
180 141450 : if( segments[i] != NULL )
181 170 : segments[i]->Synchronize();
182 : }
183 :
184 : /* -------------------------------------------------------------------- */
185 : /* Ensure the file is synhronized to disk. */
186 : /* -------------------------------------------------------------------- */
187 138 : MutexHolder oHolder( io_mutex );
188 :
189 138 : interfaces.io->Flush( io_handle );
190 : }
191 :
192 : /************************************************************************/
193 : /* GetChannel() */
194 : /************************************************************************/
195 :
196 154 : PCIDSKChannel *CPCIDSKFile::GetChannel( int band )
197 :
198 : {
199 154 : if( band < 1 || band > channel_count )
200 : ThrowPCIDSKException( "Out of range band (%d) requested.",
201 0 : band );
202 :
203 154 : return channels[band-1];
204 : }
205 :
206 : /************************************************************************/
207 : /* GetSegment() */
208 : /************************************************************************/
209 :
210 195 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int segment )
211 :
212 : {
213 : /* -------------------------------------------------------------------- */
214 : /* Is this a valid segment? */
215 : /* -------------------------------------------------------------------- */
216 195 : if( segment < 1 || segment > segment_count )
217 0 : return NULL;
218 :
219 195 : const char *segment_pointer = segment_pointers.buffer + (segment-1) * 32;
220 :
221 195 : if( segment_pointer[0] != 'A' && segment_pointer[0] != 'L' )
222 0 : return NULL;
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Do we already have a corresponding object? */
226 : /* -------------------------------------------------------------------- */
227 195 : if( segments[segment] != NULL )
228 74 : return segments[segment];
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Instantiate per the type. */
232 : /* -------------------------------------------------------------------- */
233 121 : int segment_type = segment_pointers.GetInt((segment-1)*32+1,3);
234 121 : PCIDSKSegment *segobj = NULL;
235 :
236 121 : switch( segment_type )
237 : {
238 : case SEG_GEO:
239 88 : segobj = new CPCIDSKGeoref( this, segment, segment_pointer );
240 88 : break;
241 :
242 : case SEG_PCT:
243 2 : segobj = new CPCIDSK_PCT( this, segment, segment_pointer );
244 2 : break;
245 :
246 : case SEG_VEC:
247 0 : segobj = new CPCIDSKVectorSegment( this, segment, segment_pointer );
248 0 : break;
249 :
250 : case SEG_BIT:
251 0 : segobj = new CPCIDSKBitmap( this, segment, segment_pointer );
252 0 : break;
253 :
254 : case SEG_TEX:
255 0 : segobj = new CPCIDSK_TEX( this, segment, segment_pointer );
256 0 : break;
257 :
258 : case SEG_SYS:
259 31 : if( strncmp(segment_pointer + 4, "SysBMDir",8) == 0 )
260 3 : segobj = new SysBlockMap( this, segment, segment_pointer );
261 28 : else if( strncmp(segment_pointer + 4, "METADATA",8) == 0 )
262 25 : segobj = new MetadataSegment( this, segment, segment_pointer );
263 : else
264 3 : segobj = new CPCIDSKSegment( this, segment, segment_pointer );
265 :
266 31 : break;
267 :
268 : case SEG_GCP2:
269 0 : segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer);
270 :
271 : case SEG_BIN:
272 0 : if (!strncmp(segment_pointer + 4, "RFMODEL ", 8))
273 0 : segobj = new CPCIDSKRPCModelSegment( this, segment, segment_pointer );
274 0 : else if (!strncmp(segment_pointer + 4, "APMODEL ", 8)) {
275 0 : segobj = new CPCIDSKAPModelSegment(this, segment, segment_pointer);
276 : }
277 : break;
278 :
279 : }
280 :
281 121 : if (segobj == NULL)
282 0 : segobj = new CPCIDSKSegment( this, segment, segment_pointer );
283 :
284 121 : segments[segment] = segobj;
285 :
286 121 : return segobj;
287 : }
288 :
289 : /************************************************************************/
290 : /* GetSegment() */
291 : /* */
292 : /* Find segment by type/name. */
293 : /************************************************************************/
294 :
295 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int type, std::string name,
296 311 : int previous )
297 :
298 : {
299 : int i;
300 : char type_str[4];
301 :
302 311 : name += " "; // white space pad name.
303 :
304 311 : sprintf( type_str, "%03d", type );
305 :
306 318731 : for( i = previous; i < segment_count; i++ )
307 : {
308 318458 : if( type != SEG_UNKNOWN
309 : && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 )
310 318412 : continue;
311 :
312 46 : if( name != " "
313 : && strncmp(segment_pointers.buffer+i*32+4,name.c_str(),8) != 0 )
314 8 : continue;
315 :
316 38 : return GetSegment(i+1);
317 : }
318 :
319 273 : return NULL;
320 : }
321 :
322 :
323 : /************************************************************************/
324 : /* GetSegments() */
325 : /************************************************************************/
326 :
327 0 : std::vector<PCIDSK::PCIDSKSegment *> CPCIDSKFile::GetSegments()
328 :
329 : {
330 0 : PCIDSK::ThrowPCIDSKException( "Objects list access not implemented yet." );
331 :
332 0 : std::vector<PCIDSK::PCIDSKSegment *> list;
333 : return list;
334 : }
335 :
336 : /************************************************************************/
337 : /* InitializeFromHeader() */
338 : /************************************************************************/
339 :
340 124 : void CPCIDSKFile::InitializeFromHeader()
341 :
342 : {
343 : /* -------------------------------------------------------------------- */
344 : /* Process the file header. */
345 : /* -------------------------------------------------------------------- */
346 124 : PCIDSKBuffer fh(512);
347 :
348 124 : ReadFromFile( fh.buffer, 0, 512 );
349 :
350 124 : width = atoi(fh.Get(384,8));
351 124 : height = atoi(fh.Get(392,8));
352 124 : channel_count = atoi(fh.Get(376,8));
353 124 : file_size = fh.GetUInt64(16,16);
354 :
355 124 : uint64 ih_start_block = atouint64(fh.Get(336,16));
356 124 : uint64 image_start_block = atouint64(fh.Get(304,16));
357 124 : fh.Get(360,8,interleaving);
358 :
359 124 : uint64 image_offset = (image_start_block-1) * 512;
360 :
361 124 : block_size = 0;
362 124 : last_block_index = -1;
363 124 : last_block_dirty = 0;
364 124 : last_block_data = NULL;
365 124 : last_block_mutex = NULL;
366 :
367 : /* -------------------------------------------------------------------- */
368 : /* Load the segment pointers into a PCIDSKBuffer. For now we */
369 : /* try to avoid doing too much other processing on them. */
370 : /* -------------------------------------------------------------------- */
371 124 : int segment_block_count = atoi(fh.Get(456,8));
372 :
373 124 : segment_count = (segment_block_count * 512) / 32;
374 124 : segment_pointers.SetSize( segment_block_count * 512 );
375 124 : segment_pointers_offset = atouint64(fh.Get(440,16)) * 512 - 512;
376 : ReadFromFile( segment_pointers.buffer, segment_pointers_offset,
377 124 : segment_block_count * 512 );
378 :
379 124 : segments.resize( segment_count + 1 );
380 :
381 : /* -------------------------------------------------------------------- */
382 : /* Get the number of each channel type - only used for some */
383 : /* interleaving cases. */
384 : /* -------------------------------------------------------------------- */
385 124 : int count_8u = atoi(fh.Get(464,4));
386 124 : int count_16s = atoi(fh.Get(468,4));
387 124 : int count_16u = atoi(fh.Get(472,4));
388 124 : int count_32r = atoi(fh.Get(476,4));
389 124 : int count_c16u = atoi(fh.Get(480,4));
390 124 : int count_c16s = atoi(fh.Get(484,4));
391 124 : int count_c32r = atoi(fh.Get(488,4));
392 :
393 : /* -------------------------------------------------------------------- */
394 : /* for pixel interleaved files we need to compute the length of */
395 : /* a scanline padded out to a 512 byte boundary. */
396 : /* -------------------------------------------------------------------- */
397 124 : if( interleaving == "PIXEL" )
398 : {
399 0 : first_line_offset = image_offset;
400 0 : pixel_group_size = count_8u + count_16s*2 + count_16u*2 + count_32r*4;
401 :
402 0 : block_size = pixel_group_size * width;
403 0 : if( block_size % 512 != 0 )
404 0 : block_size += 512 - (block_size % 512);
405 :
406 0 : last_block_data = malloc((size_t) block_size);
407 0 : if( last_block_data == NULL )
408 : ThrowPCIDSKException( "Allocating %d bytes for scanline buffer failed.",
409 0 : (int) block_size );
410 :
411 0 : last_block_mutex = interfaces.CreateMutex();
412 0 : image_offset = 0;
413 : }
414 :
415 : /* -------------------------------------------------------------------- */
416 : /* Initialize the list of channels. */
417 : /* -------------------------------------------------------------------- */
418 : int channelnum;
419 :
420 363 : for( channelnum = 1; channelnum <= channel_count; channelnum++ )
421 : {
422 239 : PCIDSKBuffer ih(1024);
423 239 : PCIDSKChannel *channel = NULL;
424 239 : uint64 ih_offset = (ih_start_block-1)*512 + (channelnum-1)*1024;
425 :
426 239 : ReadFromFile( ih.buffer, ih_offset, 1024 );
427 :
428 : // fetch the filename, if there is one.
429 239 : std::string filename;
430 239 : ih.Get(64,64,filename);
431 :
432 : // work out channel type from header
433 : eChanType pixel_type;
434 239 : const char *pixel_type_string = ih.Get( 160, 8 );
435 :
436 239 : pixel_type = GetDataTypeFromName(pixel_type_string);
437 :
438 : // if we didn't get channel type in header, work out from counts (old).
439 : // Check this only if we don't have complex channels:
440 :
441 464 : if (count_c32r == 0 && count_c16u == 0 && count_c16s == 0) {
442 211 : if( channelnum <= count_8u )
443 157 : pixel_type = CHN_8U;
444 54 : else if( channelnum <= count_8u + count_16s )
445 14 : pixel_type = CHN_16S;
446 40 : else if( channelnum <= count_8u + count_16s + count_16u )
447 26 : pixel_type = CHN_16U;
448 : else
449 14 : pixel_type = CHN_32R;
450 : }
451 :
452 239 : if( interleaving == "BAND" )
453 : {
454 : channel = new CBandInterleavedChannel( ih, ih_offset, fh,
455 : channelnum, this,
456 239 : image_offset, pixel_type );
457 :
458 :
459 : image_offset += (int64)DataTypeSize(channel->GetType())
460 478 : * (int64)width * (int64)height;
461 : }
462 :
463 0 : else if( interleaving == "PIXEL" )
464 : {
465 : channel = new CPixelInterleavedChannel( ih, ih_offset, fh,
466 : channelnum, this,
467 : (int) image_offset,
468 0 : pixel_type );
469 0 : image_offset += DataTypeSize(pixel_type);
470 : }
471 :
472 0 : else if( interleaving == "FILE"
473 : && strncmp(filename.c_str(),"/SIS=",5) == 0 )
474 : {
475 : channel = new CTiledChannel( ih, ih_offset, fh,
476 0 : channelnum, this, pixel_type );
477 : }
478 :
479 0 : else if( interleaving == "FILE"
480 : && strncmp(((const char*)ih.buffer)+250, " ", 8 ) != 0 )
481 : {
482 : channel = new CExternalChannel( ih, ih_offset, fh,
483 0 : channelnum, this, pixel_type );
484 : }
485 :
486 0 : else if( interleaving == "FILE" )
487 : {
488 : channel = new CBandInterleavedChannel( ih, ih_offset, fh,
489 : channelnum, this,
490 0 : 0, pixel_type );
491 : }
492 :
493 : else
494 : ThrowPCIDSKException( "Unsupported interleaving:%s",
495 0 : interleaving.c_str() );
496 :
497 239 : channels.push_back( channel );
498 124 : }
499 124 : }
500 :
501 : /************************************************************************/
502 : /* ReadFromFile() */
503 : /************************************************************************/
504 :
505 1056 : void CPCIDSKFile::ReadFromFile( void *buffer, uint64 offset, uint64 size )
506 :
507 : {
508 1056 : MutexHolder oHolder( io_mutex );
509 :
510 1056 : interfaces.io->Seek( io_handle, offset, SEEK_SET );
511 1056 : if( interfaces.io->Read( buffer, 1, size, io_handle ) != size )
512 : ThrowPCIDSKException( "PCIDSKFile:Failed to read %d bytes at %d.",
513 0 : (int) size, (int) offset );
514 1056 : }
515 :
516 : /************************************************************************/
517 : /* WriteToFile() */
518 : /************************************************************************/
519 :
520 491 : void CPCIDSKFile::WriteToFile( const void *buffer, uint64 offset, uint64 size )
521 :
522 : {
523 491 : if( !GetUpdatable() )
524 0 : throw PCIDSKException( "File not open for update in WriteToFile()" );
525 :
526 491 : MutexHolder oHolder( io_mutex );
527 :
528 491 : interfaces.io->Seek( io_handle, offset, SEEK_SET );
529 491 : if( interfaces.io->Write( buffer, 1, size, io_handle ) != size )
530 : ThrowPCIDSKException( "PCIDSKFile:Failed to write %d bytes at %d.",
531 0 : (int) size, (int) offset );
532 491 : }
533 :
534 : /************************************************************************/
535 : /* ReadAndLockBlock() */
536 : /************************************************************************/
537 :
538 : void *CPCIDSKFile::ReadAndLockBlock( int block_index,
539 0 : int win_xoff, int win_xsize )
540 :
541 : {
542 0 : if( last_block_data == NULL )
543 0 : ThrowPCIDSKException( "ReadAndLockBlock() called on a file that is not pixel interleaved." );
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Default, and validate windowing. */
547 : /* -------------------------------------------------------------------- */
548 0 : if( win_xoff == -1 && win_xsize == -1 )
549 : {
550 0 : win_xoff = 0;
551 0 : win_xsize = GetWidth();
552 : }
553 :
554 0 : if( win_xoff < 0 || win_xoff+win_xsize > GetWidth() )
555 : {
556 : ThrowPCIDSKException( "CPCIDSKFile::ReadAndLockBlock(): Illegal window - xoff=%d, xsize=%d",
557 0 : win_xoff, win_xsize );
558 : }
559 :
560 0 : if( block_index == last_block_index
561 : && win_xoff == last_block_xoff
562 : && win_xsize == last_block_xsize )
563 : {
564 0 : last_block_mutex->Acquire();
565 0 : return last_block_data;
566 : }
567 :
568 : /* -------------------------------------------------------------------- */
569 : /* Flush any dirty writable data. */
570 : /* -------------------------------------------------------------------- */
571 0 : FlushBlock();
572 :
573 : /* -------------------------------------------------------------------- */
574 : /* Read the requested window. */
575 : /* -------------------------------------------------------------------- */
576 0 : last_block_mutex->Acquire();
577 :
578 : ReadFromFile( last_block_data,
579 : first_line_offset + block_index*block_size
580 : + win_xoff * pixel_group_size,
581 0 : pixel_group_size * win_xsize );
582 0 : last_block_index = block_index;
583 0 : last_block_xoff = win_xoff;
584 0 : last_block_xsize = win_xsize;
585 :
586 0 : return last_block_data;
587 : }
588 :
589 : /************************************************************************/
590 : /* UnlockBlock() */
591 : /************************************************************************/
592 :
593 0 : void CPCIDSKFile::UnlockBlock( bool mark_dirty )
594 :
595 : {
596 0 : if( last_block_mutex == NULL )
597 0 : return;
598 :
599 0 : last_block_dirty |= mark_dirty;
600 0 : last_block_mutex->Release();
601 : }
602 :
603 : /************************************************************************/
604 : /* WriteBlock() */
605 : /************************************************************************/
606 :
607 0 : void CPCIDSKFile::WriteBlock( int block_index, void *buffer )
608 :
609 : {
610 0 : if( !GetUpdatable() )
611 0 : throw PCIDSKException( "File not open for update in WriteBlock()" );
612 :
613 0 : if( last_block_data == NULL )
614 0 : ThrowPCIDSKException( "WriteBlock() called on a file that is not pixel interleaved." );
615 :
616 : WriteToFile( buffer,
617 : first_line_offset + block_index*block_size,
618 0 : block_size );
619 0 : }
620 :
621 : /************************************************************************/
622 : /* FlushBlock() */
623 : /************************************************************************/
624 :
625 138 : void CPCIDSKFile::FlushBlock()
626 :
627 : {
628 138 : if( last_block_dirty )
629 : {
630 0 : last_block_mutex->Acquire();
631 0 : if( last_block_dirty ) // is it still dirty?
632 : {
633 0 : WriteBlock( last_block_index, last_block_data );
634 0 : last_block_dirty = 0;
635 : }
636 0 : last_block_mutex->Release();
637 : }
638 138 : }
639 :
640 : /************************************************************************/
641 : /* GetEDBFileDetails() */
642 : /************************************************************************/
643 :
644 : bool CPCIDSKFile::GetEDBFileDetails( EDBFile** file_p,
645 : Mutex **io_mutex_p,
646 0 : std::string filename )
647 :
648 : {
649 0 : *file_p = NULL;
650 0 : *io_mutex_p = NULL;
651 :
652 : /* -------------------------------------------------------------------- */
653 : /* Does the file exist already in our file list? */
654 : /* -------------------------------------------------------------------- */
655 : unsigned int i;
656 :
657 0 : for( i = 0; i < edb_file_list.size(); i++ )
658 : {
659 0 : if( edb_file_list[i].filename == filename )
660 : {
661 0 : *file_p = edb_file_list[i].file;
662 0 : *io_mutex_p = edb_file_list[i].io_mutex;
663 0 : return edb_file_list[i].writable;
664 : }
665 : }
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* If not, we need to try and open the file. Eventually we */
669 : /* will need better rules about read or update access. */
670 : /* -------------------------------------------------------------------- */
671 0 : ProtectedEDBFile new_file;
672 :
673 0 : new_file.file = NULL;
674 0 : new_file.writable = false;
675 :
676 0 : if( GetUpdatable() )
677 : {
678 : try {
679 0 : new_file.file = interfaces.OpenEDB( filename, "r+" );
680 0 : new_file.writable = true;
681 0 : } catch( PCIDSK::PCIDSKException ex ) {}
682 : }
683 :
684 0 : if( new_file.file == NULL )
685 0 : new_file.file = interfaces.OpenEDB( filename, "r" );
686 :
687 0 : if( new_file.file == NULL )
688 : ThrowPCIDSKException( "Unable to open file '%s'.",
689 0 : filename.c_str() );
690 :
691 : /* -------------------------------------------------------------------- */
692 : /* Push the new file into the list of files managed for this */
693 : /* PCIDSK file. */
694 : /* -------------------------------------------------------------------- */
695 0 : new_file.io_mutex = interfaces.CreateMutex();
696 0 : new_file.filename = filename;
697 :
698 0 : edb_file_list.push_back( new_file );
699 :
700 0 : *file_p = edb_file_list[edb_file_list.size()-1].file;
701 0 : *io_mutex_p = edb_file_list[edb_file_list.size()-1].io_mutex;
702 :
703 0 : return new_file.writable;
704 : }
705 :
706 : /************************************************************************/
707 : /* GetIODetails() */
708 : /************************************************************************/
709 :
710 : void CPCIDSKFile::GetIODetails( void ***io_handle_pp,
711 : Mutex ***io_mutex_pp,
712 239 : std::string filename )
713 :
714 : {
715 239 : *io_handle_pp = NULL;
716 239 : *io_mutex_pp = NULL;
717 :
718 : /* -------------------------------------------------------------------- */
719 : /* Does this reference the PCIDSK file itself? */
720 : /* -------------------------------------------------------------------- */
721 239 : if( filename.size() == 0 )
722 : {
723 239 : *io_handle_pp = &io_handle;
724 239 : *io_mutex_pp = &io_mutex;
725 239 : return;
726 : }
727 :
728 : /* -------------------------------------------------------------------- */
729 : /* Does the file exist already in our file list? */
730 : /* -------------------------------------------------------------------- */
731 : unsigned int i;
732 :
733 0 : for( i = 0; i < file_list.size(); i++ )
734 : {
735 0 : if( file_list[i].filename == filename )
736 : {
737 0 : *io_handle_pp = &(file_list[i].io_handle);
738 0 : *io_mutex_pp = &(file_list[i].io_mutex);
739 0 : return;
740 : }
741 : }
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* If not, we need to try and open the file. Eventually we */
745 : /* will need better rules about read or update access. */
746 : /* -------------------------------------------------------------------- */
747 0 : ProtectedFile new_file;
748 :
749 0 : new_file.io_handle = interfaces.io->Open( filename, "r" );
750 0 : if( new_file.io_handle == NULL )
751 : ThrowPCIDSKException( "Unable to open file '%s'.",
752 0 : filename.c_str() );
753 :
754 : /* -------------------------------------------------------------------- */
755 : /* Push the new file into the list of files managed for this */
756 : /* PCIDSK file. */
757 : /* -------------------------------------------------------------------- */
758 0 : new_file.io_mutex = interfaces.CreateMutex();
759 0 : new_file.filename = filename;
760 :
761 0 : file_list.push_back( new_file );
762 :
763 0 : *io_handle_pp = &(file_list[file_list.size()-1].io_handle);
764 0 : *io_mutex_pp = &(file_list[file_list.size()-1].io_mutex);
765 : }
766 :
767 : /************************************************************************/
768 : /* DeleteSegment() */
769 : /************************************************************************/
770 :
771 1 : void CPCIDSKFile::DeleteSegment( int segment )
772 :
773 : {
774 : /* -------------------------------------------------------------------- */
775 : /* Is this an existing segment? */
776 : /* -------------------------------------------------------------------- */
777 1 : PCIDSKSegment *poSeg = GetSegment( segment );
778 :
779 1 : if( poSeg == NULL )
780 0 : ThrowPCIDSKException( "DeleteSegment(%d) failed, segment does not exist.", segment );
781 :
782 : /* -------------------------------------------------------------------- */
783 : /* Wipe associated metadata. */
784 : /* -------------------------------------------------------------------- */
785 1 : std::vector<std::string> md_keys = poSeg->GetMetadataKeys();
786 : unsigned int i;
787 :
788 2 : for( i = 0; i < md_keys.size(); i++ )
789 0 : poSeg->SetMetadataValue( md_keys[i], "" );
790 :
791 : /* -------------------------------------------------------------------- */
792 : /* Remove the segment object from the segment object cache. I */
793 : /* hope the application is not retaining any references to this */
794 : /* segment! */
795 : /* -------------------------------------------------------------------- */
796 1 : segments[segment] = NULL;
797 1 : delete poSeg;
798 :
799 : /* -------------------------------------------------------------------- */
800 : /* Mark the segment pointer as deleted. */
801 : /* -------------------------------------------------------------------- */
802 1 : segment_pointers.buffer[(segment-1)*32] = 'D';
803 :
804 : // write the updated segment pointer back to the file.
805 : WriteToFile( segment_pointers.buffer + (segment-1)*32,
806 : segment_pointers_offset + (segment-1)*32,
807 1 : 32 );
808 1 : }
809 :
810 : /************************************************************************/
811 : /* CreateSegment() */
812 : /************************************************************************/
813 :
814 : int CPCIDSKFile::CreateSegment( std::string name, std::string description,
815 62 : eSegType seg_type, int data_blocks )
816 :
817 : {
818 : /* -------------------------------------------------------------------- */
819 : /* Set the size of fixed length segments. */
820 : /* -------------------------------------------------------------------- */
821 62 : int expected_data_blocks = 0;
822 62 : bool prezero = false;
823 :
824 62 : switch( seg_type )
825 : {
826 : case SEG_LUT:
827 0 : expected_data_blocks = 2;
828 0 : break;
829 :
830 : case SEG_PCT:
831 1 : expected_data_blocks = 6;
832 1 : break;
833 :
834 : case SEG_SIG:
835 0 : expected_data_blocks = 12;
836 0 : break;
837 :
838 : case SEG_GCP2:
839 : // expected_data_blocks = 67;
840 : // Change seg type to new GCP segment type
841 0 : expected_data_blocks = 129;
842 0 : break;
843 :
844 : case SEG_GEO:
845 44 : expected_data_blocks = 6;
846 44 : break;
847 :
848 : case SEG_TEX:
849 0 : expected_data_blocks = 64;
850 0 : prezero = true;
851 0 : break;
852 :
853 : case SEG_BIT:
854 : {
855 0 : uint64 bytes = ((width * (uint64) height) + 7) / 8;
856 0 : expected_data_blocks = (int) ((bytes + 511) / 512);
857 0 : prezero = true;
858 : }
859 : break;
860 :
861 : default:
862 : break;
863 : }
864 :
865 62 : if( data_blocks == 0 && expected_data_blocks != 0 )
866 1 : data_blocks = expected_data_blocks;
867 :
868 : /* -------------------------------------------------------------------- */
869 : /* Find an empty Segment Pointer. For System segments we start */
870 : /* at the end, instead of the beginning to avoid using up */
871 : /* segment numbers that the user would notice. */
872 : /* -------------------------------------------------------------------- */
873 62 : int segment = 1;
874 62 : int64 seg_start = -1;
875 62 : PCIDSKBuffer segptr( 32 );
876 :
877 62 : if( seg_type == SEG_SYS )
878 : {
879 17 : for( segment=segment_count; segment >= 1; segment-- )
880 : {
881 17 : memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
882 :
883 17 : uint64 this_seg_size = segptr.GetUInt64(23,9);
884 17 : char flag = (char) segptr.buffer[0];
885 :
886 17 : if( flag == 'D'
887 : && (uint64) data_blocks+2 == this_seg_size
888 : && this_seg_size > 0 )
889 0 : seg_start = segptr.GetUInt64(12,11) - 1;
890 17 : else if( flag == ' ' )
891 17 : seg_start = 0;
892 0 : else if( flag && this_seg_size == 0 )
893 0 : seg_start = 0;
894 :
895 17 : if( seg_start != -1 )
896 17 : break;
897 : }
898 : }
899 : else
900 : {
901 46 : for( segment=1; segment <= segment_count; segment++ )
902 : {
903 46 : memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
904 :
905 46 : uint64 this_seg_size = segptr.GetUInt64(23,9);
906 46 : char flag = (char) segptr.buffer[0];
907 :
908 46 : if( flag == 'D'
909 : && (uint64) data_blocks+2 == this_seg_size
910 : && this_seg_size > 0 )
911 0 : seg_start = segptr.GetUInt64(12,11) - 1;
912 46 : else if( flag == ' ' )
913 45 : seg_start = 0;
914 1 : else if( flag && this_seg_size == 0 )
915 0 : seg_start = 0;
916 :
917 46 : if( seg_start != -1 )
918 45 : break;
919 : }
920 : }
921 :
922 62 : if( segment > segment_count )
923 0 : ThrowPCIDSKException( "All %d segment pointers in use.", segment_count);
924 :
925 : /* -------------------------------------------------------------------- */
926 : /* If the segment does not have a data area already, identify */
927 : /* it's location at the end of the file, and extend the file to */
928 : /* the desired length. */
929 : /* -------------------------------------------------------------------- */
930 62 : if( seg_start == 0 )
931 : {
932 62 : seg_start = GetFileSize();
933 62 : ExtendFile( data_blocks + 2, prezero );
934 : }
935 :
936 : /* -------------------------------------------------------------------- */
937 : /* Update the segment pointer information. */
938 : /* -------------------------------------------------------------------- */
939 : // SP1.1 - Flag
940 62 : segptr.Put( "A", 0, 1 );
941 :
942 : // SP1.2 - Type
943 62 : segptr.Put( (int) seg_type, 1, 3 );
944 :
945 : // SP1.3 - Name
946 62 : segptr.Put( name.c_str(), 4, 8 );
947 :
948 : // SP1.4 - start block
949 62 : segptr.Put( (uint64) (seg_start + 1), 12, 11 );
950 :
951 : // SP1.5 - data blocks.
952 62 : segptr.Put( data_blocks+2, 23, 9 );
953 :
954 : // Update in memory copy of segment pointers.
955 62 : memcpy( segment_pointers.buffer+(segment-1)*32, segptr.buffer, 32);
956 :
957 : // Update on disk.
958 : WriteToFile( segptr.buffer,
959 62 : segment_pointers_offset + (segment-1)*32, 32 );
960 :
961 : /* -------------------------------------------------------------------- */
962 : /* Prepare segment header. */
963 : /* -------------------------------------------------------------------- */
964 62 : PCIDSKBuffer sh(1024);
965 :
966 : char current_time[17];
967 :
968 62 : GetCurrentDateTime( current_time );
969 :
970 62 : sh.Put( " ", 0, 1024 );
971 :
972 : // SH1 - segment content description
973 62 : sh.Put( description.c_str(), 0, 64 );
974 :
975 : // SH3 - Creation time/date
976 62 : sh.Put( current_time, 128, 16 );
977 :
978 : // SH4 - Last Update time/date
979 62 : sh.Put( current_time, 144, 16 );
980 :
981 : /* -------------------------------------------------------------------- */
982 : /* Write segment header. */
983 : /* -------------------------------------------------------------------- */
984 62 : WriteToFile( sh.buffer, seg_start * 512, 1024 );
985 :
986 : /* -------------------------------------------------------------------- */
987 : /* Initialize the newly created segment. */
988 : /* -------------------------------------------------------------------- */
989 62 : PCIDSKSegment *seg_obj = GetSegment( segment );
990 :
991 62 : seg_obj->Initialize();
992 :
993 62 : return segment;
994 : }
995 :
996 : /************************************************************************/
997 : /* ExtendFile() */
998 : /************************************************************************/
999 :
1000 79 : void CPCIDSKFile::ExtendFile( uint64 blocks_requested, bool prezero )
1001 :
1002 : {
1003 79 : if( prezero )
1004 : {
1005 0 : std::vector<uint8> zeros;
1006 0 : uint64 blocks_to_zero = blocks_requested;
1007 :
1008 0 : zeros.resize( 512 * 32 );
1009 :
1010 0 : while( blocks_to_zero > 0 )
1011 : {
1012 0 : uint64 this_time = blocks_to_zero;
1013 0 : if( this_time > 32 )
1014 0 : this_time = 32;
1015 :
1016 0 : WriteToFile( &(zeros[0]), file_size * 512, this_time*512 );
1017 0 : blocks_to_zero -= this_time;
1018 0 : file_size += this_time;
1019 0 : }
1020 : }
1021 : else
1022 : {
1023 79 : WriteToFile( "\0", (file_size + blocks_requested) * 512 - 1, 1 );
1024 79 : file_size += blocks_requested;
1025 : }
1026 :
1027 79 : PCIDSKBuffer fh3( 16 );
1028 79 : fh3.Put( file_size, 0, 16 );
1029 79 : WriteToFile( fh3.buffer, 16, 16 );
1030 79 : }
1031 :
1032 : /************************************************************************/
1033 : /* ExtendSegment() */
1034 : /************************************************************************/
1035 :
1036 : void CPCIDSKFile::ExtendSegment( int segment, uint64 blocks_requested,
1037 17 : bool prezero )
1038 :
1039 : {
1040 : // for now we take it for granted that the segment is valid and at th
1041 : // end of the file - later we should support moving it.
1042 :
1043 17 : ExtendFile( blocks_requested, prezero );
1044 :
1045 : // Update the block count.
1046 : segment_pointers.Put(
1047 : segment_pointers.GetUInt64((segment-1)*32+23,9) + blocks_requested,
1048 17 : (segment-1)*32+23, 9 );
1049 :
1050 : // write the updated segment pointer back to the file.
1051 : WriteToFile( segment_pointers.buffer + (segment-1)*32,
1052 : segment_pointers_offset + (segment-1)*32,
1053 17 : 32 );
1054 17 : }
1055 :
1056 : /************************************************************************/
1057 : /* MoveSegmentToEOF() */
1058 : /************************************************************************/
1059 :
1060 0 : void CPCIDSKFile::MoveSegmentToEOF( int segment )
1061 :
1062 : {
1063 0 : int segptr_off = (segment - 1) * 32;
1064 : uint64 seg_start, seg_size;
1065 : uint64 new_seg_start;
1066 :
1067 0 : seg_start = segment_pointers.GetUInt64( segptr_off + 12, 11 );
1068 0 : seg_size = segment_pointers.GetUInt64( segptr_off + 23, 9 );
1069 :
1070 : // Are we already at the end of the file?
1071 0 : if( (seg_start + seg_size - 1) == file_size )
1072 0 : return;
1073 :
1074 0 : new_seg_start = file_size + 1;
1075 :
1076 : // Grow the file to hold the segment at the end.
1077 0 : ExtendFile( seg_size, false );
1078 :
1079 : // Move the segment data to the new location.
1080 : uint8 copy_buf[16384];
1081 : uint64 srcoff, dstoff, bytes_to_go;
1082 :
1083 0 : bytes_to_go = seg_size * 512;
1084 0 : srcoff = (seg_start - 1) * 512;
1085 0 : dstoff = (new_seg_start - 1) * 512;
1086 :
1087 0 : while( bytes_to_go > 0 )
1088 : {
1089 0 : uint64 bytes_this_chunk = sizeof(copy_buf);
1090 0 : if( bytes_to_go < bytes_this_chunk )
1091 0 : bytes_this_chunk = bytes_to_go;
1092 :
1093 0 : ReadFromFile( copy_buf, srcoff, bytes_this_chunk );
1094 0 : WriteToFile( copy_buf, dstoff, bytes_this_chunk );
1095 :
1096 0 : srcoff += bytes_this_chunk;
1097 0 : dstoff += bytes_this_chunk;
1098 0 : bytes_to_go -= bytes_this_chunk;
1099 : }
1100 :
1101 : // Update segment pointer in memory and on disk.
1102 0 : segment_pointers.Put( new_seg_start, segptr_off + 12, 11 );
1103 :
1104 : WriteToFile( segment_pointers.buffer + segptr_off,
1105 : segment_pointers_offset + segptr_off,
1106 0 : 32 );
1107 :
1108 : // Update the segments own information.
1109 0 : if( segments[segment] != NULL )
1110 : {
1111 : CPCIDSKSegment *seg =
1112 0 : dynamic_cast<CPCIDSKSegment *>( segments[segment] );
1113 :
1114 0 : seg->LoadSegmentPointer( segment_pointers.buffer + segptr_off );
1115 : }
1116 : }
1117 :
1118 : /************************************************************************/
1119 : /* CreateOverviews() */
1120 : /************************************************************************/
1121 : /*
1122 : const char *pszResampling;
1123 : Can be "NEAREST" for Nearest Neighbour resampling (the fastest),
1124 : "AVERAGE" for block averaging or "MODE" for block mode. This
1125 : establishing the type of resampling to be applied when preparing
1126 : the decimated overviews. Other methods can be set as well, but
1127 : not all applications might support a given overview generation
1128 : method.
1129 : */
1130 :
1131 : void CPCIDSKFile::CreateOverviews( int chan_count, int *chan_list,
1132 0 : int factor, std::string resampling )
1133 :
1134 : {
1135 0 : std::vector<int> default_chan_list;
1136 :
1137 : /* -------------------------------------------------------------------- */
1138 : /* Default to processing all bands. */
1139 : /* -------------------------------------------------------------------- */
1140 0 : if( chan_count == 0 )
1141 : {
1142 0 : chan_count = channel_count;
1143 0 : default_chan_list.resize( chan_count );
1144 :
1145 0 : for( int i = 0; i < chan_count; i++ )
1146 0 : default_chan_list[i] = i+1;
1147 :
1148 0 : chan_list = &(default_chan_list[0]);
1149 : }
1150 :
1151 : /* -------------------------------------------------------------------- */
1152 : /* Work out the creation options that should apply for the */
1153 : /* overview. */
1154 : /* -------------------------------------------------------------------- */
1155 0 : std::string layout = GetMetadataValue( "_DBLayout" );
1156 0 : int blocksize = 127;
1157 0 : std::string compression = "NONE";
1158 :
1159 0 : if( strncmp( layout.c_str(), "TILED", 5 ) == 0 )
1160 : {
1161 0 : ParseTileFormat( layout, blocksize, compression );
1162 : }
1163 :
1164 : /* -------------------------------------------------------------------- */
1165 : /* Make sure we have a blockmap segment for managing the tiled */
1166 : /* layers. */
1167 : /* -------------------------------------------------------------------- */
1168 0 : PCIDSKSegment *bm_seg = GetSegment( SEG_SYS, "SysBMDir" );
1169 : SysBlockMap *bm;
1170 :
1171 0 : if( bm_seg == NULL )
1172 : {
1173 : CreateSegment( "SysBMDir",
1174 : "System Block Map Directory - Do not modify.",
1175 0 : SEG_SYS, 0 );
1176 0 : bm_seg = GetSegment( SEG_SYS, "SysBMDir" );
1177 0 : bm = dynamic_cast<SysBlockMap *>(bm_seg);
1178 0 : bm->Initialize();
1179 : }
1180 : else
1181 0 : bm = dynamic_cast<SysBlockMap *>(bm_seg);
1182 :
1183 : /* ==================================================================== */
1184 : /* Loop over the channels. */
1185 : /* ==================================================================== */
1186 0 : for( int chan_index = 0; chan_index < chan_count; chan_index++ )
1187 : {
1188 0 : int channel_number = chan_list[chan_index];
1189 0 : PCIDSKChannel *channel = GetChannel( channel_number );
1190 :
1191 : /* -------------------------------------------------------------------- */
1192 : /* Figure out if the given overview level already exists */
1193 : /* for a given channel; if it does, skip creating it. */
1194 : /* -------------------------------------------------------------------- */
1195 0 : bool overview_exists = false;
1196 0 : for( int i = channel->GetOverviewCount()-1; i >= 0; i-- )
1197 : {
1198 0 : PCIDSKChannel *overview = channel->GetOverview( i );
1199 :
1200 0 : if( overview->GetWidth() == channel->GetWidth() / factor
1201 : && overview->GetHeight() == channel->GetHeight() / factor )
1202 : {
1203 0 : overview_exists = true;
1204 : }
1205 : }
1206 :
1207 0 : if (overview_exists == false)
1208 : {
1209 : /* -------------------------------------------------------------------- */
1210 : /* Create the overview as a tiled image layer. */
1211 : /* -------------------------------------------------------------------- */
1212 : int virtual_image =
1213 : bm->CreateVirtualImageFile( channel->GetWidth() / factor,
1214 : channel->GetHeight() / factor,
1215 : blocksize, blocksize,
1216 0 : channel->GetType(), compression );
1217 :
1218 : /* -------------------------------------------------------------------- */
1219 : /* Attach reference to this overview as metadata. */
1220 : /* -------------------------------------------------------------------- */
1221 : char overview_md_value[128];
1222 : char overview_md_key[128];
1223 :
1224 0 : sprintf( overview_md_key, "_Overview_%d", factor );
1225 0 : sprintf( overview_md_value, "%d 0 %s",virtual_image,resampling.c_str());
1226 :
1227 0 : channel->SetMetadataValue( overview_md_key, overview_md_value );
1228 : }
1229 :
1230 : /* -------------------------------------------------------------------- */
1231 : /* Force channel to invalidate it's loaded overview list. */
1232 : /* -------------------------------------------------------------------- */
1233 0 : dynamic_cast<CPCIDSKChannel *>(channel)->InvalidateOverviewInfo();
1234 0 : }
1235 896 : }
1236 448 :
|