1 /******************************************************************************
2  *
3  * Purpose:  Implementation of the CPCIDSKSegment class.
4  *
5  ******************************************************************************
6  * Copyright (c) 2009
7  * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  ****************************************************************************/
27 
28 #include "segment/cpcidsksegment.h"
29 #include "core/metadataset.h"
30 #include "core/cpcidskfile.h"
31 #include "core/pcidsk_utils.h"
32 #include "pcidsk_buffer.h"
33 #include "pcidsk_exception.h"
34 #include <cassert>
35 #include <cstdlib>
36 #include <cstring>
37 #include <limits>
38 #include <vector>
39 #include <string>
40 
41 using namespace PCIDSK;
42 
43 /************************************************************************/
44 /*                           PCIDSKSegment()                            */
45 /************************************************************************/
46 
CPCIDSKSegment(PCIDSKFile * fileIn,int segmentIn,const char * segment_pointer)47 CPCIDSKSegment::CPCIDSKSegment( PCIDSKFile *fileIn, int segmentIn,
48                               const char *segment_pointer )
49 
50 {
51     this->file = fileIn;
52     this->segment = segmentIn;
53 
54     LoadSegmentPointer( segment_pointer );
55     LoadSegmentHeader(); // eventually we might want to defer this.
56 
57 /* -------------------------------------------------------------------- */
58 /*      Initialize the metadata object, but do not try to load till     */
59 /*      needed.                                                         */
60 /* -------------------------------------------------------------------- */
61     metadata = new MetadataSet;
62     metadata->Initialize( file, SegmentTypeName(segment_type), segment );
63 }
64 
65 /************************************************************************/
66 /*                           ~PCIDSKSegment()                           */
67 /************************************************************************/
68 
~CPCIDSKSegment()69 CPCIDSKSegment::~CPCIDSKSegment()
70 
71 {
72     delete metadata;
73 }
74 
75 /************************************************************************/
76 /*                          GetMetadataValue()                          */
77 /************************************************************************/
GetMetadataValue(const std::string & key) const78 std::string CPCIDSKSegment::GetMetadataValue( const std::string &key ) const
79 {
80     return metadata->GetMetadataValue(key);
81 }
82 
83 /************************************************************************/
84 /*                          SetMetadataValue()                          */
85 /************************************************************************/
SetMetadataValue(const std::string & key,const std::string & value)86 void CPCIDSKSegment::SetMetadataValue( const std::string &key, const std::string &value )
87 {
88     metadata->SetMetadataValue(key,value);
89 }
90 
91 /************************************************************************/
92 /*                           GetMetadataKeys()                           */
93 /************************************************************************/
GetMetadataKeys() const94 std::vector<std::string> CPCIDSKSegment::GetMetadataKeys() const
95 {
96     return metadata->GetMetadataKeys();
97 }
98 
99 /************************************************************************/
100 /*                         LoadSegmentPointer()                         */
101 /************************************************************************/
102 
LoadSegmentPointer(const char * segment_pointer)103 void CPCIDSKSegment::LoadSegmentPointer( const char *segment_pointer )
104 
105 {
106     PCIDSKBuffer segptr( segment_pointer, 32 );
107 
108     segment_flag = segptr.buffer[0];
109     const int segment_type_int = atoi(segptr.Get(1,3));
110     segment_type = EQUAL(SegmentTypeName(segment_type_int), "UNKNOWN") ?
111         SEG_UNKNOWN : static_cast<eSegType>(segment_type_int);
112     data_offset = atouint64(segptr.Get(12,11));
113     if( data_offset == 0 )
114         data_offset = 0; // throw exception maybe ?
115     else
116     {
117         if( data_offset-1 > std::numeric_limits<uint64>::max() / 512 )
118         {
119             return ThrowPCIDSKException("too large data_offset");
120         }
121         data_offset = (data_offset-1) * 512;
122     }
123     data_size = atouint64(segptr.Get(23,9));
124     data_size_limit = 999999999ULL * 512;
125 
126     if( data_size > 999999999ULL )
127     {
128         return ThrowPCIDSKException("too large data_size");
129     }
130     data_size *= 512;
131 
132     segptr.Get(4,8,segment_name);
133 }
134 
135 /************************************************************************/
136 /*                         LoadSegmentHeader()                          */
137 /************************************************************************/
138 #include <iostream>
LoadSegmentHeader()139 void CPCIDSKSegment::LoadSegmentHeader()
140 
141 {
142     header.SetSize(1024);
143 
144     file->ReadFromFile( header.buffer, data_offset, 1024 );
145 
146     // Read the history from the segment header. PCIDSK supports
147     // 8 history entries per segment.
148     std::string hist_msg;
149     history_.clear();
150     for (unsigned int i = 0; i < 8; i++)
151     {
152         header.Get(384 + i * 80, 80, hist_msg);
153 
154         // Some programs seem to push history records with a trailing '\0'
155         // so do some extra processing to cleanup.  FUN records on segment
156         // 3 of eltoro.pix are an example of this.
157         size_t size = hist_msg.size();
158         while( size > 0
159                && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
160             size--;
161 
162         hist_msg.resize(size);
163 
164         history_.push_back(hist_msg);
165     }
166 }
167 
168 /************************************************************************/
169 /*                            FlushHeader()                             */
170 /*                                                                      */
171 /*      This is used primarily after this class or subclasses have      */
172 /*      modified the header buffer and need it pushed back to disk.     */
173 /************************************************************************/
174 
FlushHeader()175 void CPCIDSKSegment::FlushHeader()
176 
177 {
178     file->WriteToFile( header.buffer, data_offset, 1024 );
179 }
180 
181 /************************************************************************/
182 /*                            ReadFromFile()                            */
183 /************************************************************************/
184 
ReadFromFile(void * buffer,uint64 offset,uint64 size)185 void CPCIDSKSegment::ReadFromFile( void *buffer, uint64 offset, uint64 size )
186 
187 {
188     if( offset+size+1024 > data_size )
189         return ThrowPCIDSKException("Attempt to read past end of segment %d: "
190                                     "Segment Size: " PCIDSK_FRMT_UINT64 ", "
191                                     "Read Offset: " PCIDSK_FRMT_UINT64 ", "
192                                     "Read Size: " PCIDSK_FRMT_UINT64,
193                                     segment, data_size, offset, size);
194 
195     file->ReadFromFile( buffer, offset + data_offset + 1024, size );
196 }
197 
198 /************************************************************************/
199 /*                            WriteToFile()                             */
200 /************************************************************************/
201 
WriteToFile(const void * buffer,uint64 offset,uint64 size)202 void CPCIDSKSegment::WriteToFile( const void *buffer, uint64 offset, uint64 size )
203 {
204     if( offset+size > data_size-1024 )
205     {
206         CPCIDSKFile *poFile = dynamic_cast<CPCIDSKFile *>(file);
207 
208         if (poFile == nullptr) {
209             return ThrowPCIDSKException("Attempt to dynamic_cast the file interface "
210                 "to a CPCIDSKFile failed. This is a programmer error, and should "
211                 "be reported to your software provider.");
212         }
213 
214         uint64 blocks_to_add =
215             ((offset+size+511) - (data_size - 1024)) / 512;
216 
217         // prezero if we aren't directly writing all the new blocks.
218         poFile->ExtendSegment( segment, blocks_to_add,
219                              !(offset == data_size - 1024
220                                && size == blocks_to_add * 512) );
221         // ExtendSegment() will call LoadSegmentPointer() to update data_size.
222     }
223 
224     assert(file); // avoid CLang Static Analyzer false positive
225     file->WriteToFile( buffer, offset + data_offset + 1024, size );
226 }
227 
228 /************************************************************************/
229 /*                           GetDescription()                           */
230 /************************************************************************/
231 
GetDescription()232 std::string CPCIDSKSegment::GetDescription()
233 {
234     std::string target;
235 
236     header.Get( 0, 64, target );
237 
238     return target;
239 }
240 
241 /************************************************************************/
242 /*                           SetDescription()                           */
243 /************************************************************************/
244 
SetDescription(const std::string & description)245 void CPCIDSKSegment::SetDescription( const std::string &description )
246 {
247     header.Put( description.c_str(), 0, 64);
248 
249     file->WriteToFile( header.buffer, data_offset, 1024 );
250 }
251 
252 /************************************************************************/
253 /*                              IsAtEOF()                               */
254 /************************************************************************/
255 
IsAtEOF()256 bool CPCIDSKSegment::IsAtEOF()
257 {
258     return data_offset + data_size == file->GetFileSize() * 512;
259 }
260 
261 /************************************************************************/
262 /*                               CanExtend()                            */
263 /************************************************************************/
CanExtend(uint64 size) const264 bool CPCIDSKSegment::CanExtend(uint64 size) const
265 {
266     return data_size + size <= data_size_limit;
267 }
268 
269 /************************************************************************/
270 /*                         GetHistoryEntries()                          */
271 /************************************************************************/
272 
GetHistoryEntries() const273 std::vector<std::string> CPCIDSKSegment::GetHistoryEntries() const
274 {
275     return history_;
276 }
277 
278 /************************************************************************/
279 /*                         SetHistoryEntries()                          */
280 /************************************************************************/
281 
SetHistoryEntries(const std::vector<std::string> & entries)282 void CPCIDSKSegment::SetHistoryEntries(const std::vector<std::string> &entries)
283 
284 {
285     for( unsigned int i = 0; i < 8; i++ )
286     {
287         const char *msg = "";
288         if( entries.size() > i )
289             msg = entries[i].c_str();
290 
291         header.Put( msg, 384 + i * 80, 80 );
292     }
293 
294     FlushHeader();
295 
296     // Force reloading of history_
297     LoadSegmentHeader();
298 }
299 
300 /************************************************************************/
301 /*                            PushHistory()                             */
302 /************************************************************************/
303 
PushHistory(const std::string & app,const std::string & message)304 void CPCIDSKSegment::PushHistory( const std::string &app,
305                                   const std::string &message )
306 
307 {
308 #define MY_MIN(a,b)      ((a<b) ? a : b)
309 
310     char current_time[17];
311     char history[81];
312 
313     GetCurrentDateTime( current_time );
314 
315     memset( history, ' ', 80 );
316     history[80] = '\0';
317 
318     memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
319     history[7] = ':';
320 
321     memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
322     memcpy( history + 64, current_time, 16 );
323 
324     std::vector<std::string> history_entries = GetHistoryEntries();
325 
326     history_entries.insert( history_entries.begin(), history );
327     history_entries.resize(8);
328 
329     SetHistoryEntries( history_entries );
330 }
331 
332 
333 /************************************************************************/
334 /*                              MoveData()                              */
335 /*                                                                      */
336 /*      Move a chunk of data within a segment. Overlapping source       */
337 /*      and destination are permitted.                                  */
338 /************************************************************************/
339 
MoveData(uint64 src_offset,uint64 dst_offset,uint64 size_in_bytes)340 void CPCIDSKSegment::MoveData( uint64 src_offset, uint64 dst_offset,
341                                uint64 size_in_bytes )
342 
343 {
344     bool copy_backwards = false;
345 
346     // We move things backwards if the areas overlap and the destination
347     // is further on in the segment.
348     if( dst_offset > src_offset
349         && src_offset + size_in_bytes > dst_offset )
350         copy_backwards = true;
351 
352 
353     // Move the segment data to the new location.
354     uint8 copy_buf[16384];
355     uint64 bytes_to_go;
356 
357     bytes_to_go = size_in_bytes;
358 
359     while( bytes_to_go > 0 )
360     {
361         uint64 bytes_this_chunk = sizeof(copy_buf);
362         if( bytes_to_go < bytes_this_chunk )
363             bytes_this_chunk = bytes_to_go;
364 
365         if( copy_backwards )
366         {
367             ReadFromFile( copy_buf,
368                           src_offset + bytes_to_go - bytes_this_chunk,
369                           bytes_this_chunk );
370             WriteToFile( copy_buf,
371                          dst_offset + bytes_to_go - bytes_this_chunk,
372                          bytes_this_chunk );
373         }
374         else
375         {
376             ReadFromFile( copy_buf, src_offset, bytes_this_chunk );
377             WriteToFile( copy_buf, dst_offset, bytes_this_chunk );
378 
379             src_offset += bytes_this_chunk;
380             dst_offset += bytes_this_chunk;
381         }
382 
383         bytes_to_go -= bytes_this_chunk;
384     }
385 }
386