Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_maptoollock.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABMAPToolBlock class used to handle
7 : * reading/writing of the .MAP files' drawing tool blocks
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999, 2000, Daniel Morissette
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
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 : #include "cpl_port.h"
34 : #include "mitab.h"
35 :
36 : #include <cstddef>
37 :
38 : #include "cpl_conv.h"
39 : #include "cpl_error.h"
40 : #include "cpl_vsi.h"
41 : #include "mitab_priv.h"
42 :
43 : /*=====================================================================
44 : * class TABMAPToolBlock
45 : *====================================================================*/
46 :
47 : constexpr int MAP_TOOL_HEADER_SIZE = 8;
48 :
49 : /**********************************************************************
50 : * TABMAPToolBlock::TABMAPToolBlock()
51 : *
52 : * Constructor.
53 : **********************************************************************/
54 2369 : TABMAPToolBlock::TABMAPToolBlock(TABAccess eAccessMode /*= TABRead*/)
55 : : TABRawBinBlock(eAccessMode, TRUE), m_numDataBytes(0), m_nNextToolBlock(0),
56 : m_numBlocksInChain(1), // Current block counts as 1
57 2369 : m_poBlockManagerRef(nullptr)
58 : {
59 2369 : }
60 :
61 : /**********************************************************************
62 : * TABMAPToolBlock::~TABMAPToolBlock()
63 : *
64 : * Destructor.
65 : **********************************************************************/
66 4738 : TABMAPToolBlock::~TABMAPToolBlock()
67 : {
68 4738 : }
69 :
70 : /**********************************************************************
71 : * TABMAPToolBlock::EndOfChain()
72 : *
73 : * Return TRUE if we reached the end of the last block in the chain
74 : * TABMAPToolBlocks, or FALSE if there is still data to be read from
75 : * this chain.
76 : **********************************************************************/
77 2489 : GBool TABMAPToolBlock::EndOfChain()
78 : {
79 2489 : if (m_pabyBuf && (m_nCurPos < (m_numDataBytes + MAP_TOOL_HEADER_SIZE) ||
80 1208 : m_nNextToolBlock > 0))
81 : {
82 1281 : return FALSE; // There is still data to be read.
83 : }
84 :
85 1208 : return TRUE;
86 : }
87 :
88 : /**********************************************************************
89 : * TABMAPToolBlock::InitBlockFromData()
90 : *
91 : * Perform some initialization on the block after its binary data has
92 : * been set or changed (or loaded from a file).
93 : *
94 : * Returns 0 if successful or -1 if an error happened, in which case
95 : * CPLError() will have been called.
96 : **********************************************************************/
97 1208 : int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
98 : int nSizeUsed,
99 : GBool bMakeCopy /* = TRUE */,
100 : VSILFILE *fpSrc /* = NULL */,
101 : int nOffset /* = 0 */)
102 : {
103 : /*-----------------------------------------------------------------
104 : * First of all, we must call the base class' InitBlockFromData()
105 : *----------------------------------------------------------------*/
106 1208 : const int nStatus = TABRawBinBlock::InitBlockFromData(
107 : pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
108 1208 : if (nStatus != 0)
109 0 : return nStatus;
110 :
111 : /*-----------------------------------------------------------------
112 : * Validate block type
113 : *----------------------------------------------------------------*/
114 1208 : if (m_nBlockType != TABMAP_TOOL_BLOCK)
115 : {
116 0 : CPLError(CE_Failure, CPLE_FileIO,
117 : "InitBlockFromData(): Invalid Block Type: got %d expected %d",
118 : m_nBlockType, TABMAP_TOOL_BLOCK);
119 0 : CPLFree(m_pabyBuf);
120 0 : m_pabyBuf = nullptr;
121 0 : return -1;
122 : }
123 :
124 : /*-----------------------------------------------------------------
125 : * Init member variables
126 : *----------------------------------------------------------------*/
127 1208 : GotoByteInBlock(0x002);
128 1208 : m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
129 1208 : if (m_numDataBytes < 0 ||
130 1208 : m_numDataBytes + MAP_TOOL_HEADER_SIZE > nBlockSize)
131 : {
132 0 : CPLError(CE_Failure, CPLE_FileIO,
133 : "TABMAPToolBlock::InitBlockFromData(): m_numDataBytes=%d "
134 : "incompatible with block size %d",
135 : m_numDataBytes, nBlockSize);
136 0 : CPLFree(m_pabyBuf);
137 0 : m_pabyBuf = nullptr;
138 0 : return -1;
139 : }
140 :
141 1208 : m_nNextToolBlock = ReadInt32();
142 1208 : if (m_nNextToolBlock != 0 &&
143 0 : (m_nNextToolBlock / m_nBlockSize) * m_nBlockSize == nOffset)
144 : {
145 0 : CPLError(CE_Failure, CPLE_FileIO,
146 : "InitBlockFromData(): self referencing block");
147 0 : CPLFree(m_pabyBuf);
148 0 : m_pabyBuf = nullptr;
149 0 : return -1;
150 : }
151 :
152 : /*-----------------------------------------------------------------
153 : * The read ptr is now located at the beginning of the data part.
154 : *----------------------------------------------------------------*/
155 1208 : GotoByteInBlock(MAP_TOOL_HEADER_SIZE);
156 :
157 1208 : return 0;
158 : }
159 :
160 : /**********************************************************************
161 : * TABMAPToolBlock::CommitToFile()
162 : *
163 : * Commit the current state of the binary block to the file to which
164 : * it has been previously attached.
165 : *
166 : * This method makes sure all values are properly set in the map object
167 : * block header and then calls TABRawBinBlock::CommitToFile() to do
168 : * the actual writing to disk.
169 : *
170 : * Returns 0 if successful or -1 if an error happened, in which case
171 : * CPLError() will have been called.
172 : **********************************************************************/
173 1161 : int TABMAPToolBlock::CommitToFile()
174 : {
175 1161 : if (m_pabyBuf == nullptr)
176 : {
177 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
178 : "CommitToFile(): Block has not been initialized yet!");
179 0 : return -1;
180 : }
181 :
182 : /*-----------------------------------------------------------------
183 : * Nothing to do here if block has not been modified
184 : *----------------------------------------------------------------*/
185 1161 : if (!m_bModified)
186 0 : return 0;
187 :
188 : /*-----------------------------------------------------------------
189 : * Make sure 8 bytes block header is up to date.
190 : *----------------------------------------------------------------*/
191 1161 : GotoByteInBlock(0x000);
192 :
193 1161 : WriteInt16(TABMAP_TOOL_BLOCK); // Block type code
194 1161 : CPLAssert(m_nSizeUsed >= MAP_TOOL_HEADER_SIZE &&
195 : m_nSizeUsed < MAP_TOOL_HEADER_SIZE + 32768);
196 1161 : WriteInt16(static_cast<GInt16>(m_nSizeUsed -
197 : MAP_TOOL_HEADER_SIZE)); // num. bytes used
198 1161 : WriteInt32(m_nNextToolBlock);
199 :
200 1161 : int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
201 :
202 : /*-----------------------------------------------------------------
203 : * OK, call the base class to write the block to disk.
204 : *----------------------------------------------------------------*/
205 1161 : if (nStatus == 0)
206 : {
207 : #ifdef DEBUG_VERBOSE
208 : CPLDebug("MITAB", "Committing TOOL block to offset %d", m_nFileOffset);
209 : #endif
210 1161 : nStatus = TABRawBinBlock::CommitToFile();
211 : }
212 :
213 1161 : return nStatus;
214 : }
215 :
216 : /**********************************************************************
217 : * TABMAPToolBlock::InitNewBlock()
218 : *
219 : * Initialize a newly created block so that it knows to which file it
220 : * is attached, its block size, etc . and then perform any specific
221 : * initialization for this block type, including writing a default
222 : * block header, etc. and leave the block ready to receive data.
223 : *
224 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
225 : * that puts the block in a stable state without loading any initial
226 : * data in it.
227 : *
228 : * Returns 0 if successful or -1 if an error happened, in which case
229 : * CPLError() will have been called.
230 : **********************************************************************/
231 2369 : int TABMAPToolBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
232 : int nFileOffset /* = 0*/)
233 : {
234 : #ifdef DEBUG_VERBOSE
235 : CPLDebug("MITAB", "Instantiating new TOOL block at offset %d", nFileOffset);
236 : #endif
237 :
238 : /*-----------------------------------------------------------------
239 : * Start with the default initialization
240 : *----------------------------------------------------------------*/
241 2369 : if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
242 0 : return -1;
243 :
244 : /*-----------------------------------------------------------------
245 : * And then set default values for the block header.
246 : *----------------------------------------------------------------*/
247 2369 : m_nNextToolBlock = 0;
248 :
249 2369 : m_numDataBytes = 0;
250 :
251 2369 : GotoByteInBlock(0x000);
252 :
253 2369 : if (m_eAccess != TABRead)
254 : {
255 1161 : WriteInt16(TABMAP_TOOL_BLOCK); // Block type code
256 1161 : WriteInt16(0); // num. bytes used, excluding header
257 1161 : WriteInt32(0); // Pointer to next tool block
258 : }
259 :
260 2369 : if (CPLGetLastErrorType() == CE_Failure)
261 0 : return -1;
262 :
263 2369 : return 0;
264 : }
265 :
266 : /**********************************************************************
267 : * TABMAPToolBlock::SetNextToolBlock()
268 : *
269 : * Set the address (offset from beginning of file) of the drawing tool block
270 : * that follows the current one.
271 : **********************************************************************/
272 0 : void TABMAPToolBlock::SetNextToolBlock(GInt32 nNextToolBlockAddress)
273 : {
274 0 : m_nNextToolBlock = nNextToolBlockAddress;
275 0 : }
276 :
277 : /**********************************************************************
278 : * TABMAPToolBlock::SetMAPBlockManagerRef()
279 : *
280 : * Pass a reference to the block manager object for the file this
281 : * block belongs to. The block manager will be used by this object
282 : * when it needs to automatically allocate a new block.
283 : **********************************************************************/
284 1161 : void TABMAPToolBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
285 : {
286 1161 : m_poBlockManagerRef = poBlockMgr;
287 1161 : }
288 :
289 : /**********************************************************************
290 : * TABMAPToolBlock::ReadBytes()
291 : *
292 : * Cover function for TABRawBinBlock::ReadBytes() that will automagically
293 : * load the next coordinate block in the chain before reading the
294 : * requested bytes if we are at the end of the current block and if
295 : * m_nNextToolBlock is a valid block.
296 : *
297 : * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
298 : * work:
299 : * Copy the number of bytes from the data block's internal buffer to
300 : * the user's buffer pointed by pabyDstBuf.
301 : *
302 : * Passing pabyDstBuf = NULL will only move the read pointer by the
303 : * specified number of bytes as if the copy had happened... but it
304 : * won't crash.
305 : *
306 : * Returns 0 if successful or -1 if an error happened, in which case
307 : * CPLError() will have been called.
308 : **********************************************************************/
309 12663 : int TABMAPToolBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
310 : {
311 12663 : if (m_pabyBuf && m_nCurPos >= (m_numDataBytes + MAP_TOOL_HEADER_SIZE) &&
312 0 : m_nNextToolBlock > 0)
313 : {
314 0 : int nStatus = GotoByteInFile(m_nNextToolBlock);
315 0 : if (nStatus != 0)
316 : {
317 : // Failed.... an error has already been reported.
318 0 : return nStatus;
319 : }
320 :
321 0 : GotoByteInBlock(MAP_TOOL_HEADER_SIZE); // Move pointer past header
322 :
323 : // FIXME ? what about a corrupted tab file that would have more than
324 : // 255 blocks
325 0 : m_numBlocksInChain++;
326 : }
327 :
328 12663 : return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
329 : }
330 :
331 : /**********************************************************************
332 : * TABMAPToolBlock::WriteBytes()
333 : *
334 : * Cover function for TABRawBinBlock::WriteBytes() that will automagically
335 : * CommitToFile() the current block and create a new one if we are at
336 : * the end of the current block.
337 : *
338 : * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
339 : * work.
340 : *
341 : * Passing pabySrcBuf = NULL will only move the write pointer by the
342 : * specified number of bytes as if the copy had happened... but it
343 : * won't crash.
344 : *
345 : * Returns 0 if successful or -1 if an error happened, in which case
346 : * CPLError() will have been called.
347 : **********************************************************************/
348 16732 : int TABMAPToolBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
349 : {
350 16732 : if (m_eAccess == TABWrite && m_poBlockManagerRef &&
351 1679 : (m_nBlockSize - m_nCurPos) < nBytesToWrite)
352 : {
353 0 : if (m_numBlocksInChain >= 255)
354 : {
355 0 : CPLError(CE_Failure, CPLE_NotSupported,
356 : "Maximum number of 255 tool blocks reached");
357 0 : return -1;
358 : }
359 :
360 0 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL");
361 0 : SetNextToolBlock(nNewBlockOffset);
362 :
363 0 : if (CommitToFile() != 0 ||
364 0 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
365 : {
366 : // An error message should have already been reported.
367 0 : return -1;
368 : }
369 :
370 0 : m_numBlocksInChain++;
371 : }
372 :
373 16732 : return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
374 : }
375 :
376 : /**********************************************************************
377 : * TABMAPToolBlock::CheckAvailableSpace()
378 : *
379 : * Check if an object of the specified type can fit in
380 : * current block. If it can't fit then force committing current block
381 : * and allocating a new one.
382 : *
383 : * Returns 0 if successful or -1 if an error happened, in which case
384 : * CPLError() will have been called.
385 : **********************************************************************/
386 1217 : int TABMAPToolBlock::CheckAvailableSpace(int nToolType)
387 : {
388 1217 : int nBytesToWrite = 0;
389 :
390 1217 : switch (nToolType)
391 : {
392 45 : case TABMAP_TOOL_PEN:
393 45 : nBytesToWrite = 11;
394 45 : break;
395 30 : case TABMAP_TOOL_BRUSH:
396 30 : nBytesToWrite = 13;
397 30 : break;
398 6 : case TABMAP_TOOL_FONT:
399 6 : nBytesToWrite = 37;
400 6 : break;
401 1136 : case TABMAP_TOOL_SYMBOL:
402 1136 : nBytesToWrite = 13;
403 1136 : break;
404 0 : default:
405 0 : CPLAssert(false);
406 : }
407 :
408 1217 : if (GetNumUnusedBytes() < nBytesToWrite)
409 : {
410 0 : if (m_numBlocksInChain >= 255)
411 : {
412 0 : CPLError(CE_Failure, CPLE_NotSupported,
413 : "Maximum number of 255 tool blocks reached");
414 0 : return -1;
415 : }
416 0 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL");
417 0 : SetNextToolBlock(nNewBlockOffset);
418 :
419 0 : if (CommitToFile() != 0 ||
420 0 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
421 : {
422 : // An error message should have already been reported.
423 0 : return -1;
424 : }
425 :
426 0 : m_numBlocksInChain++;
427 : }
428 :
429 1217 : return 0;
430 : }
431 :
432 : /**********************************************************************
433 : * TABMAPToolBlock::Dump()
434 : *
435 : * Dump block contents... available only in DEBUG mode.
436 : **********************************************************************/
437 : #ifdef DEBUG
438 :
439 0 : void TABMAPToolBlock::Dump(FILE *fpOut /*=NULL*/)
440 : {
441 0 : if (fpOut == nullptr)
442 0 : fpOut = stdout;
443 :
444 0 : fprintf(fpOut, "----- TABMAPToolBlock::Dump() -----\n");
445 0 : if (m_pabyBuf == nullptr)
446 : {
447 0 : fprintf(fpOut, "Block has not been initialized yet.");
448 : }
449 : else
450 : {
451 0 : fprintf(fpOut, "Tool Block (type %d) at offset %d.\n", m_nBlockType,
452 : m_nFileOffset);
453 0 : fprintf(fpOut, " m_numDataBytes = %d\n", m_numDataBytes);
454 0 : fprintf(fpOut, " m_nNextToolBlock = %d\n", m_nNextToolBlock);
455 : }
456 :
457 0 : fflush(fpOut);
458 0 : }
459 :
460 : #endif // DEBUG
|