1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKSegment 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 :
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 <vector>
38 : #include <string>
39 :
40 : using namespace PCIDSK;
41 :
42 : /************************************************************************/
43 : /* PCIDSKSegment() */
44 : /************************************************************************/
45 :
46 326 : CPCIDSKSegment::CPCIDSKSegment( PCIDSKFile *file, int segment,
47 326 : const char *segment_pointer )
48 :
49 : {
50 326 : this->file = file;
51 326 : this->segment = segment;
52 :
53 326 : LoadSegmentPointer( segment_pointer );
54 326 : LoadSegmentHeader(); // eventually we might want to defer this.
55 :
56 : /* -------------------------------------------------------------------- */
57 : /* Initialize the metadata object, but do not try to load till */
58 : /* needed. */
59 : /* -------------------------------------------------------------------- */
60 326 : metadata = new MetadataSet;
61 652 : metadata->Initialize( file, SegmentTypeName(segment_type), segment );
62 326 : }
63 :
64 : /************************************************************************/
65 : /* ~PCIDSKSegment() */
66 : /************************************************************************/
67 :
68 326 : CPCIDSKSegment::~CPCIDSKSegment()
69 :
70 : {
71 326 : delete metadata;
72 326 : }
73 :
74 : /************************************************************************/
75 : /* GetMetadataValue() */
76 : /************************************************************************/
77 54 : std::string CPCIDSKSegment::GetMetadataValue( const std::string &key ) const
78 : {
79 54 : return metadata->GetMetadataValue(key);
80 : }
81 :
82 : /************************************************************************/
83 : /* SetMetadataValue() */
84 : /************************************************************************/
85 12 : void CPCIDSKSegment::SetMetadataValue( const std::string &key, const std::string &value )
86 : {
87 12 : metadata->SetMetadataValue(key,value);
88 12 : }
89 :
90 : /************************************************************************/
91 : /* GetMetdataKeys() */
92 : /************************************************************************/
93 2 : std::vector<std::string> CPCIDSKSegment::GetMetadataKeys() const
94 : {
95 2 : return metadata->GetMetadataKeys();
96 : }
97 :
98 : /************************************************************************/
99 : /* LoadSegmentPointer() */
100 : /************************************************************************/
101 :
102 340 : void CPCIDSKSegment::LoadSegmentPointer( const char *segment_pointer )
103 :
104 : {
105 340 : PCIDSKBuffer segptr( segment_pointer, 32 );
106 :
107 340 : segment_flag = segptr.buffer[0];
108 340 : segment_type = (eSegType) (atoi(segptr.Get(1,3)));
109 340 : data_offset = (atouint64(segptr.Get(12,11))-1) * 512;
110 340 : data_size = atouint64(segptr.Get(23,9)) * 512;
111 :
112 340 : segptr.Get(4,8,segment_name);
113 340 : }
114 :
115 : /************************************************************************/
116 : /* LoadSegmentHeader() */
117 : /************************************************************************/
118 : #include <iostream>
119 326 : void CPCIDSKSegment::LoadSegmentHeader()
120 :
121 : {
122 326 : header.SetSize(1024);
123 :
124 326 : file->ReadFromFile( header.buffer, data_offset, 1024 );
125 :
126 : // Read the history from the segment header. PCIDSK supports
127 : // 8 history entries per segment.
128 326 : std::string hist_msg;
129 326 : history_.clear();
130 2934 : for (unsigned int i = 0; i < 8; i++)
131 : {
132 2608 : header.Get(384 + i * 80, 80, hist_msg);
133 :
134 : // Some programs seem to push history records with a trailing '\0'
135 : // so do some extra processing to cleanup. FUN records on segment
136 : // 3 of eltoro.pix are an example of this.
137 2608 : size_t size = hist_msg.size();
138 5342 : while( size > 0
139 : && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
140 126 : size--;
141 :
142 2608 : hist_msg.resize(size);
143 :
144 2608 : history_.push_back(hist_msg);
145 326 : }
146 326 : }
147 :
148 : /************************************************************************/
149 : /* FlushHeader() */
150 : /* */
151 : /* This is used primarily after this class or subclasses have */
152 : /* modified the header buffer and need it pushed back to disk. */
153 : /************************************************************************/
154 :
155 30 : void CPCIDSKSegment::FlushHeader()
156 :
157 : {
158 30 : file->WriteToFile( header.buffer, data_offset, 1024 );
159 30 : }
160 :
161 : /************************************************************************/
162 : /* ReadFromFile() */
163 : /************************************************************************/
164 :
165 2488 : void CPCIDSKSegment::ReadFromFile( void *buffer, uint64 offset, uint64 size )
166 :
167 : {
168 2488 : if( offset+size+1024 > data_size )
169 : ThrowPCIDSKException(
170 : "Attempt to read past end of segment %d (%d bytes at offset %d)",
171 0 : segment, (int) offset, (int) size );
172 2488 : file->ReadFromFile( buffer, offset + data_offset + 1024, size );
173 2488 : }
174 :
175 : /************************************************************************/
176 : /* WriteToFile() */
177 : /************************************************************************/
178 :
179 640 : void CPCIDSKSegment::WriteToFile( const void *buffer, uint64 offset, uint64 size )
180 : {
181 640 : if( offset+size > data_size-1024 )
182 : {
183 84 : CPCIDSKFile *poFile = dynamic_cast<CPCIDSKFile *>(file);
184 :
185 84 : if (poFile == NULL) {
186 : ThrowPCIDSKException("Attempt to dynamic_cast the file interface "
187 : "to a CPCIDSKFile failed. This is a programmer error, and should "
188 0 : "be reported to your software provider.");
189 : }
190 :
191 84 : if( !IsAtEOF() )
192 14 : poFile->MoveSegmentToEOF( segment );
193 :
194 : uint64 blocks_to_add =
195 84 : ((offset+size+511) - (data_size - 1024)) / 512;
196 :
197 : // prezero if we aren't directly writing all the new blocks.
198 : poFile->ExtendSegment( segment, blocks_to_add,
199 : !(offset == data_size - 1024
200 84 : && size == blocks_to_add * 512) );
201 84 : data_size += blocks_to_add * 512;
202 : }
203 :
204 640 : file->WriteToFile( buffer, offset + data_offset + 1024, size );
205 640 : }
206 :
207 : /************************************************************************/
208 : /* GetDescription() */
209 : /************************************************************************/
210 :
211 32 : std::string CPCIDSKSegment::GetDescription()
212 : {
213 32 : std::string target;
214 :
215 32 : header.Get( 0, 64, target );
216 :
217 0 : return target;
218 : }
219 :
220 : /************************************************************************/
221 : /* SetDescription() */
222 : /************************************************************************/
223 :
224 0 : void CPCIDSKSegment::SetDescription( const std::string &description )
225 : {
226 0 : header.Put( description.c_str(), 0, 64);
227 :
228 0 : file->WriteToFile( header.buffer, data_offset, 1024 );
229 0 : }
230 :
231 : /************************************************************************/
232 : /* IsAtEOF() */
233 : /************************************************************************/
234 :
235 84 : bool CPCIDSKSegment::IsAtEOF()
236 : {
237 84 : if( 512 * file->GetFileSize() == data_offset + data_size )
238 70 : return true;
239 : else
240 14 : return false;
241 : }
242 :
243 : /************************************************************************/
244 : /* GetHistoryEntries() */
245 : /************************************************************************/
246 :
247 0 : std::vector<std::string> CPCIDSKSegment::GetHistoryEntries() const
248 : {
249 0 : return history_;
250 : }
251 :
252 : /************************************************************************/
253 : /* SetHistoryEntries() */
254 : /************************************************************************/
255 :
256 0 : void CPCIDSKSegment::SetHistoryEntries(const std::vector<std::string> &entries)
257 :
258 : {
259 0 : for( unsigned int i = 0; i < 8; i++ )
260 : {
261 0 : const char *msg = "";
262 0 : if( entries.size() > i )
263 0 : msg = entries[i].c_str();
264 :
265 0 : header.Put( msg, 384 + i * 80, 80 );
266 : }
267 :
268 0 : FlushHeader();
269 :
270 : // Force reloading of history_
271 0 : LoadSegmentHeader();
272 0 : }
273 :
274 : /************************************************************************/
275 : /* PushHistory() */
276 : /************************************************************************/
277 :
278 0 : void CPCIDSKSegment::PushHistory( const std::string &app,
279 : const std::string &message )
280 :
281 : {
282 : #define MY_MIN(a,b) ((a<b) ? a : b)
283 :
284 : char current_time[17];
285 : char history[81];
286 :
287 0 : GetCurrentDateTime( current_time );
288 :
289 0 : memset( history, ' ', 80 );
290 0 : history[80] = '\0';
291 :
292 0 : memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
293 0 : history[7] = ':';
294 :
295 0 : memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
296 0 : memcpy( history + 64, current_time, 16 );
297 :
298 0 : std::vector<std::string> history_entries = GetHistoryEntries();
299 :
300 0 : history_entries.insert( history_entries.begin(), history );
301 0 : history_entries.resize(8);
302 :
303 0 : SetHistoryEntries( history_entries );
304 0 : }
305 :
306 :
307 : /************************************************************************/
308 : /* MoveData() */
309 : /* */
310 : /* Move a chunk of data within a segment. Overlapping source */
311 : /* and destination are permitted. */
312 : /************************************************************************/
313 :
314 26 : void CPCIDSKSegment::MoveData( uint64 src_offset, uint64 dst_offset,
315 : uint64 size_in_bytes )
316 :
317 : {
318 26 : bool copy_backwards = false;
319 :
320 : // We move things backwards if the areas overlap and the destination
321 : // is further on in the segment.
322 26 : if( dst_offset > src_offset
323 : && src_offset + size_in_bytes > dst_offset )
324 12 : copy_backwards = true;
325 :
326 :
327 : // Move the segment data to the new location.
328 : uint8 copy_buf[16384];
329 : uint64 bytes_to_go;
330 :
331 26 : bytes_to_go = size_in_bytes;
332 :
333 78 : while( bytes_to_go > 0 )
334 : {
335 26 : uint64 bytes_this_chunk = sizeof(copy_buf);
336 26 : if( bytes_to_go < bytes_this_chunk )
337 26 : bytes_this_chunk = bytes_to_go;
338 :
339 26 : if( copy_backwards )
340 : {
341 : ReadFromFile( copy_buf,
342 : src_offset + bytes_to_go - bytes_this_chunk,
343 12 : bytes_this_chunk );
344 : WriteToFile( copy_buf,
345 : dst_offset + bytes_to_go - bytes_this_chunk,
346 12 : bytes_this_chunk );
347 : }
348 : else
349 : {
350 14 : ReadFromFile( copy_buf, src_offset, bytes_this_chunk );
351 14 : WriteToFile( copy_buf, dst_offset, bytes_this_chunk );
352 :
353 14 : src_offset += bytes_this_chunk;
354 14 : dst_offset += bytes_this_chunk;
355 : }
356 :
357 26 : bytes_to_go -= bytes_this_chunk;
358 : }
359 4055 : }
|