1 : /**********************************************************************
2 : * $Id: mitab_maptoolblock.cpp,v 1.8 2010-07-07 19:00:15 aboudreault Exp $
3 : *
4 : * Name: mitab_maptoollock.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABMAPToolBlock class used to handle
8 : * reading/writing of the .MAP files' drawing tool blocks
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999, 2000, Daniel Morissette
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : **********************************************************************
32 : *
33 : * $Log: mitab_maptoolblock.cpp,v $
34 : * Revision 1.8 2010-07-07 19:00:15 aboudreault
35 : * Cleanup Win32 Compile Warnings (GDAL bug #2930)
36 : *
37 : * Revision 1.7 2006-11-28 18:49:08 dmorissette
38 : * Completed changes to split TABMAPObjectBlocks properly and produce an
39 : * optimal spatial index (bug 1585)
40 : *
41 : * Revision 1.6 2004/06/30 20:29:04 dmorissette
42 : * Fixed refs to old address danmo@videotron.ca
43 : *
44 : * Revision 1.5 2000/11/15 04:13:50 daniel
45 : * Fixed writing of TABMAPToolBlock to allocate a new block when full
46 : *
47 : * Revision 1.4 2000/02/28 17:03:30 daniel
48 : * Changed TABMAPBlockManager to TABBinBlockManager
49 : *
50 : * Revision 1.3 2000/01/15 22:30:44 daniel
51 : * Switch to MIT/X-Consortium OpenSource license
52 : *
53 : * Revision 1.2 1999/09/26 14:59:37 daniel
54 : * Implemented write support
55 : *
56 : * Revision 1.1 1999/09/16 02:39:17 daniel
57 : * Completed read support for most feature types
58 : *
59 : **********************************************************************/
60 :
61 : #include "mitab.h"
62 :
63 : /*=====================================================================
64 : * class TABMAPToolBlock
65 : *====================================================================*/
66 :
67 : #define MAP_TOOL_HEADER_SIZE 8
68 :
69 : /**********************************************************************
70 : * TABMAPToolBlock::TABMAPToolBlock()
71 : *
72 : * Constructor.
73 : **********************************************************************/
74 8 : TABMAPToolBlock::TABMAPToolBlock(TABAccess eAccessMode /*= TABRead*/):
75 8 : TABRawBinBlock(eAccessMode, TRUE)
76 : {
77 8 : m_nNextToolBlock = m_numDataBytes = 0;
78 :
79 8 : m_numBlocksInChain = 1; // Current block counts as 1
80 :
81 8 : m_poBlockManagerRef = NULL;
82 8 : }
83 :
84 : /**********************************************************************
85 : * TABMAPToolBlock::~TABMAPToolBlock()
86 : *
87 : * Destructor.
88 : **********************************************************************/
89 8 : TABMAPToolBlock::~TABMAPToolBlock()
90 : {
91 :
92 8 : }
93 :
94 :
95 : /**********************************************************************
96 : * TABMAPToolBlock::EndOfChain()
97 : *
98 : * Return TRUE if we reached the end of the last block in the chain
99 : * TABMAPToolBlocks, or FALSE if there is still data to be read from
100 : * this chain.
101 : **********************************************************************/
102 12 : GBool TABMAPToolBlock::EndOfChain()
103 : {
104 12 : if (m_pabyBuf &&
105 : (m_nCurPos < (m_numDataBytes+MAP_TOOL_HEADER_SIZE) ||
106 : m_nNextToolBlock > 0 ) )
107 : {
108 8 : return FALSE; // There is still data to be read.
109 : }
110 :
111 4 : return TRUE;
112 : }
113 :
114 : /**********************************************************************
115 : * TABMAPToolBlock::InitBlockFromData()
116 : *
117 : * Perform some initialization on the block after its binary data has
118 : * been set or changed (or loaded from a file).
119 : *
120 : * Returns 0 if succesful or -1 if an error happened, in which case
121 : * CPLError() will have been called.
122 : **********************************************************************/
123 4 : int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf,
124 : int nBlockSize, int nSizeUsed,
125 : GBool bMakeCopy /* = TRUE */,
126 : FILE *fpSrc /* = NULL */,
127 : int nOffset /* = 0 */)
128 : {
129 : int nStatus;
130 :
131 : /*-----------------------------------------------------------------
132 : * First of all, we must call the base class' InitBlockFromData()
133 : *----------------------------------------------------------------*/
134 : nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, nBlockSize, nSizeUsed,
135 4 : bMakeCopy, fpSrc, nOffset);
136 4 : if (nStatus != 0)
137 0 : return nStatus;
138 :
139 : /*-----------------------------------------------------------------
140 : * Validate block type
141 : *----------------------------------------------------------------*/
142 4 : if (m_nBlockType != TABMAP_TOOL_BLOCK)
143 : {
144 : CPLError(CE_Failure, CPLE_FileIO,
145 : "InitBlockFromData(): Invalid Block Type: got %d expected %d",
146 0 : m_nBlockType, TABMAP_TOOL_BLOCK);
147 0 : CPLFree(m_pabyBuf);
148 0 : m_pabyBuf = NULL;
149 0 : return -1;
150 : }
151 :
152 : /*-----------------------------------------------------------------
153 : * Init member variables
154 : *----------------------------------------------------------------*/
155 4 : GotoByteInBlock(0x002);
156 4 : m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
157 :
158 4 : m_nNextToolBlock = ReadInt32();
159 :
160 : /*-----------------------------------------------------------------
161 : * The read ptr is now located at the beginning of the data part.
162 : *----------------------------------------------------------------*/
163 4 : GotoByteInBlock(MAP_TOOL_HEADER_SIZE);
164 :
165 4 : return 0;
166 : }
167 :
168 : /**********************************************************************
169 : * TABMAPToolBlock::CommitToFile()
170 : *
171 : * Commit the current state of the binary block to the file to which
172 : * it has been previously attached.
173 : *
174 : * This method makes sure all values are properly set in the map object
175 : * block header and then calls TABRawBinBlock::CommitToFile() to do
176 : * the actual writing to disk.
177 : *
178 : * Returns 0 if succesful or -1 if an error happened, in which case
179 : * CPLError() will have been called.
180 : **********************************************************************/
181 4 : int TABMAPToolBlock::CommitToFile()
182 : {
183 4 : int nStatus = 0;
184 :
185 4 : if ( m_pabyBuf == NULL )
186 : {
187 : CPLError(CE_Failure, CPLE_AssertionFailed,
188 0 : "CommitToFile(): Block has not been initialized yet!");
189 0 : return -1;
190 : }
191 :
192 : /*-----------------------------------------------------------------
193 : * Make sure 8 bytes block header is up to date.
194 : *----------------------------------------------------------------*/
195 4 : GotoByteInBlock(0x000);
196 :
197 4 : WriteInt16(TABMAP_TOOL_BLOCK); // Block type code
198 4 : WriteInt16((GInt16)(m_nSizeUsed - MAP_TOOL_HEADER_SIZE)); // num. bytes used
199 4 : WriteInt32(m_nNextToolBlock);
200 :
201 4 : nStatus = CPLGetLastErrorNo();
202 :
203 : /*-----------------------------------------------------------------
204 : * OK, call the base class to write the block to disk.
205 : *----------------------------------------------------------------*/
206 4 : if (nStatus == 0)
207 4 : nStatus = TABRawBinBlock::CommitToFile();
208 :
209 4 : return nStatus;
210 : }
211 :
212 : /**********************************************************************
213 : * TABMAPToolBlock::InitNewBlock()
214 : *
215 : * Initialize a newly created block so that it knows to which file it
216 : * is attached, its block size, etc . and then perform any specific
217 : * initialization for this block type, including writing a default
218 : * block header, etc. and leave the block ready to receive data.
219 : *
220 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
221 : * that puts the block in a stable state without loading any initial
222 : * data in it.
223 : *
224 : * Returns 0 if succesful or -1 if an error happened, in which case
225 : * CPLError() will have been called.
226 : **********************************************************************/
227 8 : int TABMAPToolBlock::InitNewBlock(FILE *fpSrc, int nBlockSize,
228 : int nFileOffset /* = 0*/)
229 : {
230 : /*-----------------------------------------------------------------
231 : * Start with the default initialisation
232 : *----------------------------------------------------------------*/
233 8 : if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
234 0 : return -1;
235 :
236 : /*-----------------------------------------------------------------
237 : * And then set default values for the block header.
238 : *----------------------------------------------------------------*/
239 8 : m_nNextToolBlock = 0;
240 :
241 8 : m_numDataBytes = 0;
242 :
243 8 : GotoByteInBlock(0x000);
244 :
245 8 : if (m_eAccess != TABRead)
246 : {
247 4 : WriteInt16(TABMAP_TOOL_BLOCK); // Block type code
248 4 : WriteInt16(0); // num. bytes used, excluding header
249 4 : WriteInt32(0); // Pointer to next tool block
250 : }
251 :
252 8 : if (CPLGetLastErrorNo() != 0)
253 0 : return -1;
254 :
255 8 : return 0;
256 : }
257 :
258 : /**********************************************************************
259 : * TABMAPToolBlock::SetNextToolBlock()
260 : *
261 : * Set the address (offset from beginning of file) of the drawing tool block
262 : * that follows the current one.
263 : **********************************************************************/
264 0 : void TABMAPToolBlock::SetNextToolBlock(GInt32 nNextToolBlockAddress)
265 : {
266 0 : m_nNextToolBlock = nNextToolBlockAddress;
267 0 : }
268 :
269 : /**********************************************************************
270 : * TABMAPToolBlock::SetMAPBlockManagerRef()
271 : *
272 : * Pass a reference to the block manager object for the file this
273 : * block belongs to. The block manager will be used by this object
274 : * when it needs to automatically allocate a new block.
275 : **********************************************************************/
276 4 : void TABMAPToolBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
277 : {
278 4 : m_poBlockManagerRef = poBlockMgr;
279 4 : };
280 :
281 :
282 : /**********************************************************************
283 : * TABMAPToolBlock::ReadBytes()
284 : *
285 : * Cover function for TABRawBinBlock::ReadBytes() that will automagically
286 : * load the next coordinate block in the chain before reading the
287 : * requested bytes if we are at the end of the current block and if
288 : * m_nNextToolBlock is a valid block.
289 : *
290 : * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
291 : * work:
292 : * Copy the number of bytes from the data block's internal buffer to
293 : * the user's buffer pointed by pabyDstBuf.
294 : *
295 : * Passing pabyDstBuf = NULL will only move the read pointer by the
296 : * specified number of bytes as if the copy had happened... but it
297 : * won't crash.
298 : *
299 : * Returns 0 if succesful or -1 if an error happened, in which case
300 : * CPLError() will have been called.
301 : **********************************************************************/
302 80 : int TABMAPToolBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
303 : {
304 : int nStatus;
305 :
306 80 : if (m_pabyBuf &&
307 : m_nCurPos >= (m_numDataBytes+MAP_TOOL_HEADER_SIZE) &&
308 : m_nNextToolBlock > 0)
309 : {
310 0 : if ( (nStatus=GotoByteInFile(m_nNextToolBlock)) != 0)
311 : {
312 : // Failed.... an error has already been reported.
313 0 : return nStatus;
314 : }
315 :
316 0 : GotoByteInBlock(MAP_TOOL_HEADER_SIZE); // Move pointer past header
317 0 : m_numBlocksInChain++;
318 : }
319 :
320 80 : return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
321 : }
322 :
323 : /**********************************************************************
324 : * TABMAPToolBlock::WriteBytes()
325 : *
326 : * Cover function for TABRawBinBlock::WriteBytes() that will automagically
327 : * CommitToFile() the current block and create a new one if we are at
328 : * the end of the current block.
329 : *
330 : * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
331 : * work.
332 : *
333 : * Passing pabySrcBuf = NULL will only move the write pointer by the
334 : * specified number of bytes as if the copy had happened... but it
335 : * won't crash.
336 : *
337 : * Returns 0 if succesful or -1 if an error happened, in which case
338 : * CPLError() will have been called.
339 : **********************************************************************/
340 96 : int TABMAPToolBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf)
341 : {
342 96 : if (m_eAccess == TABWrite && m_poBlockManagerRef &&
343 : (m_nBlockSize - m_nCurPos) < nBytesToWrite)
344 : {
345 0 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock();
346 0 : SetNextToolBlock(nNewBlockOffset);
347 :
348 0 : if (CommitToFile() != 0 ||
349 0 : InitNewBlock(m_fp, 512, nNewBlockOffset) != 0)
350 : {
351 : // An error message should have already been reported.
352 0 : return -1;
353 : }
354 :
355 0 : m_numBlocksInChain ++;
356 : }
357 :
358 96 : return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
359 : }
360 :
361 : /**********************************************************************
362 : * TABMAPToolBlock::CheckAvailableSpace()
363 : *
364 : * Check if an object of the specified type can fit in
365 : * current block. If it can't fit then force committing current block
366 : * and allocating a new one.
367 : *
368 : * Returns 0 if succesful or -1 if an error happened, in which case
369 : * CPLError() will have been called.
370 : **********************************************************************/
371 8 : int TABMAPToolBlock::CheckAvailableSpace(int nToolType)
372 : {
373 8 : int nBytesToWrite = 0;
374 :
375 8 : switch(nToolType)
376 : {
377 : case TABMAP_TOOL_PEN:
378 4 : nBytesToWrite = 11;
379 4 : break;
380 : case TABMAP_TOOL_BRUSH:
381 4 : nBytesToWrite = 13;
382 4 : break;
383 : case TABMAP_TOOL_FONT:
384 0 : nBytesToWrite = 37;
385 0 : break;
386 : case TABMAP_TOOL_SYMBOL:
387 0 : nBytesToWrite = 13;
388 0 : break;
389 : default:
390 0 : CPLAssert(FALSE);
391 : }
392 :
393 8 : if (GetNumUnusedBytes() < nBytesToWrite)
394 : {
395 0 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock();
396 0 : SetNextToolBlock(nNewBlockOffset);
397 :
398 0 : if (CommitToFile() != 0 ||
399 0 : InitNewBlock(m_fp, 512, nNewBlockOffset) != 0)
400 : {
401 : // An error message should have already been reported.
402 0 : return -1;
403 : }
404 :
405 0 : m_numBlocksInChain ++;
406 : }
407 :
408 8 : return 0;
409 : }
410 :
411 :
412 :
413 :
414 : /**********************************************************************
415 : * TABMAPToolBlock::Dump()
416 : *
417 : * Dump block contents... available only in DEBUG mode.
418 : **********************************************************************/
419 : #ifdef DEBUG
420 :
421 0 : void TABMAPToolBlock::Dump(FILE *fpOut /*=NULL*/)
422 : {
423 0 : if (fpOut == NULL)
424 0 : fpOut = stdout;
425 :
426 0 : fprintf(fpOut, "----- TABMAPToolBlock::Dump() -----\n");
427 0 : if (m_pabyBuf == NULL)
428 : {
429 0 : fprintf(fpOut, "Block has not been initialized yet.");
430 : }
431 : else
432 : {
433 : fprintf(fpOut,"Coordinate Block (type %d) at offset %d.\n",
434 0 : m_nBlockType, m_nFileOffset);
435 0 : fprintf(fpOut," m_numDataBytes = %d\n", m_numDataBytes);
436 0 : fprintf(fpOut," m_nNextToolBlock = %d\n", m_nNextToolBlock);
437 : }
438 :
439 0 : fflush(fpOut);
440 0 : }
441 :
442 : #endif // DEBUG
443 :
444 :
445 :
|