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