Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Shapelib
4 : * Purpose: Implementation of core Shapefile read/write functions.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, 2001, Frank Warmerdam
9 : * Copyright (c) 2011-2024, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
12 : ******************************************************************************/
13 :
14 : #include "shapefil_private.h"
15 :
16 : #include <assert.h>
17 : #include <errno.h>
18 : #include <limits.h>
19 : #include <math.h>
20 : #include <stdbool.h>
21 : #include <stdint.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 :
26 : #ifndef FALSE
27 : #define FALSE 0
28 : #define TRUE 1
29 : #endif
30 :
31 : #define ByteCopy(a, b, c) memcpy(b, a, c)
32 : #ifndef MAX
33 : #define MIN(a, b) ((a < b) ? a : b)
34 : #define MAX(a, b) ((a > b) ? a : b)
35 : #endif
36 :
37 : #ifndef USE_CPL
38 : #if defined(_MSC_VER)
39 : #if _MSC_VER < 1900
40 : #define snprintf _snprintf
41 : #endif
42 : #elif defined(_WIN32)
43 : #ifndef snprintf
44 : #define snprintf _snprintf
45 : #endif
46 : #endif
47 : #endif
48 :
49 : /* Allows customization of the message in vendored builds (such as GDAL) */
50 : #ifndef SHP_RESTORE_SHX_HINT_MESSAGE
51 : #define SHP_RESTORE_SHX_HINT_MESSAGE \
52 : " Use SHPRestoreSHX() to restore or create it."
53 : #endif
54 :
55 : /************************************************************************/
56 : /* SHPWriteHeader() */
57 : /* */
58 : /* Write out a header for the .shp and .shx files as well as the */
59 : /* contents of the index (.shx) file. */
60 : /************************************************************************/
61 :
62 1831 : void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
63 : {
64 1831 : if (psSHP->fpSHX == SHPLIB_NULLPTR)
65 : {
66 0 : psSHP->sHooks.Error("SHPWriteHeader failed : SHX file is closed");
67 0 : return;
68 : }
69 :
70 : /* -------------------------------------------------------------------- */
71 : /* Prepare header block for .shp file. */
72 : /* -------------------------------------------------------------------- */
73 :
74 1831 : unsigned char abyHeader[100] = {0};
75 1831 : abyHeader[2] = 0x27; /* magic cookie */
76 1831 : abyHeader[3] = 0x0a;
77 :
78 1831 : uint32_t i32 = psSHP->nFileSize / 2; /* file size */
79 1831 : ByteCopy(&i32, abyHeader + 24, 4);
80 : #if !defined(SHP_BIG_ENDIAN)
81 1831 : SHP_SWAP32(abyHeader + 24);
82 : #endif
83 :
84 1831 : i32 = 1000; /* version */
85 1831 : ByteCopy(&i32, abyHeader + 28, 4);
86 : #if defined(SHP_BIG_ENDIAN)
87 : SHP_SWAP32(abyHeader + 28);
88 : #endif
89 :
90 1831 : i32 = psSHP->nShapeType; /* shape type */
91 1831 : ByteCopy(&i32, abyHeader + 32, 4);
92 : #if defined(SHP_BIG_ENDIAN)
93 : SHP_SWAP32(abyHeader + 32);
94 : #endif
95 :
96 1831 : double dValue = psSHP->adBoundsMin[0]; /* set bounds */
97 1831 : ByteCopy(&dValue, abyHeader + 36, 8);
98 : #if defined(SHP_BIG_ENDIAN)
99 : SHP_SWAP64(abyHeader + 36);
100 : #endif
101 1831 : dValue = psSHP->adBoundsMin[1];
102 1831 : ByteCopy(&dValue, abyHeader + 44, 8);
103 : #if defined(SHP_BIG_ENDIAN)
104 : SHP_SWAP64(abyHeader + 44);
105 : #endif
106 1831 : dValue = psSHP->adBoundsMax[0];
107 1831 : ByteCopy(&dValue, abyHeader + 52, 8);
108 : #if defined(SHP_BIG_ENDIAN)
109 : SHP_SWAP64(abyHeader + 52);
110 : #endif
111 :
112 1831 : dValue = psSHP->adBoundsMax[1];
113 1831 : ByteCopy(&dValue, abyHeader + 60, 8);
114 : #if defined(SHP_BIG_ENDIAN)
115 : SHP_SWAP64(abyHeader + 60);
116 : #endif
117 :
118 1831 : dValue = psSHP->adBoundsMin[2]; /* z */
119 1831 : ByteCopy(&dValue, abyHeader + 68, 8);
120 : #if defined(SHP_BIG_ENDIAN)
121 : SHP_SWAP64(abyHeader + 68);
122 : #endif
123 :
124 1831 : dValue = psSHP->adBoundsMax[2];
125 1831 : ByteCopy(&dValue, abyHeader + 76, 8);
126 : #if defined(SHP_BIG_ENDIAN)
127 : SHP_SWAP64(abyHeader + 76);
128 : #endif
129 :
130 1831 : dValue = psSHP->adBoundsMin[3]; /* m */
131 1831 : ByteCopy(&dValue, abyHeader + 84, 8);
132 : #if defined(SHP_BIG_ENDIAN)
133 : SHP_SWAP64(abyHeader + 84);
134 : #endif
135 :
136 1831 : dValue = psSHP->adBoundsMax[3];
137 1831 : ByteCopy(&dValue, abyHeader + 92, 8);
138 : #if defined(SHP_BIG_ENDIAN)
139 : SHP_SWAP64(abyHeader + 92);
140 : #endif
141 :
142 : /* -------------------------------------------------------------------- */
143 : /* Write .shp file header. */
144 : /* -------------------------------------------------------------------- */
145 3662 : if (psSHP->sHooks.FSeek(psSHP->fpSHP, 0, 0) != 0 ||
146 1831 : psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHP) != 1)
147 : {
148 : char szErrorMsg[200];
149 :
150 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
151 0 : "Failure writing .shp header: %s", strerror(errno));
152 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
153 0 : psSHP->sHooks.Error(szErrorMsg);
154 0 : return;
155 : }
156 :
157 : /* -------------------------------------------------------------------- */
158 : /* Prepare, and write .shx file header. */
159 : /* -------------------------------------------------------------------- */
160 1831 : i32 = (psSHP->nRecords * 2 * sizeof(uint32_t) + 100) / 2; /* file size */
161 1831 : ByteCopy(&i32, abyHeader + 24, 4);
162 : #if !defined(SHP_BIG_ENDIAN)
163 1831 : SHP_SWAP32(abyHeader + 24);
164 : #endif
165 :
166 3662 : if (psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 0) != 0 ||
167 1831 : psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHX) != 1)
168 : {
169 : char szErrorMsg[200];
170 :
171 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
172 0 : "Failure writing .shx header: %s", strerror(errno));
173 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
174 0 : psSHP->sHooks.Error(szErrorMsg);
175 :
176 0 : return;
177 : }
178 :
179 : /* -------------------------------------------------------------------- */
180 : /* Write out the .shx contents. */
181 : /* -------------------------------------------------------------------- */
182 : uint32_t *panSHX =
183 1831 : STATIC_CAST(uint32_t *, malloc(sizeof(uint32_t) * 2 * psSHP->nRecords));
184 1831 : if (panSHX == SHPLIB_NULLPTR)
185 : {
186 0 : psSHP->sHooks.Error("Failure allocatin panSHX");
187 0 : return;
188 : }
189 :
190 124549 : for (int i = 0; i < psSHP->nRecords; i++)
191 : {
192 122718 : panSHX[i * 2] = psSHP->panRecOffset[i] / 2;
193 122718 : panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2;
194 : #if !defined(SHP_BIG_ENDIAN)
195 122718 : SHP_SWAP32(panSHX + i * 2);
196 122718 : SHP_SWAP32(panSHX + i * 2 + 1);
197 : #endif
198 : }
199 :
200 1831 : if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(uint32_t) * 2,
201 1831 : psSHP->nRecords, psSHP->fpSHX)) !=
202 1831 : psSHP->nRecords)
203 : {
204 : char szErrorMsg[200];
205 :
206 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
207 0 : "Failure writing .shx contents: %s", strerror(errno));
208 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
209 0 : psSHP->sHooks.Error(szErrorMsg);
210 : }
211 :
212 1831 : free(panSHX);
213 :
214 : /* -------------------------------------------------------------------- */
215 : /* Flush to disk. */
216 : /* -------------------------------------------------------------------- */
217 1831 : psSHP->sHooks.FFlush(psSHP->fpSHP);
218 1831 : psSHP->sHooks.FFlush(psSHP->fpSHX);
219 : }
220 :
221 : /************************************************************************/
222 : /* SHPOpen() */
223 : /************************************************************************/
224 :
225 0 : SHPHandle SHPAPI_CALL SHPOpen(const char *pszLayer, const char *pszAccess)
226 : {
227 : SAHooks sHooks;
228 :
229 0 : SASetupDefaultHooks(&sHooks);
230 :
231 0 : return SHPOpenLL(pszLayer, pszAccess, &sHooks);
232 : }
233 :
234 : /************************************************************************/
235 : /* SHPGetLenWithoutExtension() */
236 : /************************************************************************/
237 :
238 5577 : static int SHPGetLenWithoutExtension(const char *pszBasename)
239 : {
240 5577 : const int nLen = STATIC_CAST(int, strlen(pszBasename));
241 22308 : for (int i = nLen - 1;
242 22308 : i > 0 && pszBasename[i] != '/' && pszBasename[i] != '\\'; i--)
243 : {
244 22308 : if (pszBasename[i] == '.')
245 : {
246 5577 : return i;
247 : }
248 : }
249 0 : return nLen;
250 : }
251 :
252 : /************************************************************************/
253 : /* SHPOpen() */
254 : /* */
255 : /* Open the .shp and .shx files based on the basename of the */
256 : /* files or either file name. */
257 : /************************************************************************/
258 :
259 3920 : SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
260 : const SAHooks *psHooks)
261 : {
262 : /* -------------------------------------------------------------------- */
263 : /* Ensure the access string is one of the legal ones. We */
264 : /* ensure the result string indicates binary to avoid common */
265 : /* problems on Windows. */
266 : /* -------------------------------------------------------------------- */
267 3920 : bool bLazySHXLoading = false;
268 3920 : if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 ||
269 3920 : strcmp(pszAccess, "r+") == 0)
270 : {
271 1684 : pszAccess = "r+b";
272 : }
273 : else
274 : {
275 2236 : bLazySHXLoading = strchr(pszAccess, 'l') != SHPLIB_NULLPTR;
276 2236 : pszAccess = "rb";
277 : }
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* Initialize the info structure. */
281 : /* -------------------------------------------------------------------- */
282 3920 : SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo)));
283 3920 : if (!psSHP)
284 0 : return SHPLIB_NULLPTR;
285 :
286 3920 : psSHP->bUpdated = FALSE;
287 3920 : memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks));
288 :
289 : /* -------------------------------------------------------------------- */
290 : /* Open the .shp and .shx files. Note that files pulled from */
291 : /* a PC to Unix with upper case filenames won't work! */
292 : /* -------------------------------------------------------------------- */
293 3920 : const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
294 3920 : char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
295 3920 : if (!pszFullname)
296 : {
297 0 : free(psSHP);
298 0 : return SHPLIB_NULLPTR;
299 : }
300 3920 : memcpy(pszFullname, pszLayer, nLenWithoutExtension);
301 3920 : memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
302 3920 : psSHP->fpSHP =
303 3920 : psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData);
304 3920 : if (psSHP->fpSHP == SHPLIB_NULLPTR)
305 : {
306 334 : memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
307 334 : psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess,
308 : psSHP->sHooks.pvUserData);
309 : }
310 :
311 3920 : if (psSHP->fpSHP == SHPLIB_NULLPTR)
312 : {
313 332 : const size_t nMessageLen = strlen(pszFullname) * 2 + 256;
314 332 : char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
315 332 : if (pszMessage)
316 : {
317 332 : pszFullname[nLenWithoutExtension] = 0;
318 332 : snprintf(pszMessage, nMessageLen,
319 : "Unable to open %s.shp or %s.SHP in %s mode.", pszFullname,
320 : pszFullname, pszAccess);
321 332 : psHooks->Error(pszMessage);
322 332 : free(pszMessage);
323 : }
324 :
325 332 : free(psSHP);
326 332 : free(pszFullname);
327 :
328 332 : return SHPLIB_NULLPTR;
329 : }
330 :
331 3588 : memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
332 3588 : psSHP->fpSHX =
333 3588 : psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData);
334 3588 : if (psSHP->fpSHX == SHPLIB_NULLPTR)
335 : {
336 2 : memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5);
337 2 : psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess,
338 : psSHP->sHooks.pvUserData);
339 : }
340 :
341 3588 : if (psSHP->fpSHX == SHPLIB_NULLPTR)
342 : {
343 0 : const size_t nMessageLen =
344 0 : 64 + strlen(pszFullname) * 2 + strlen(SHP_RESTORE_SHX_HINT_MESSAGE);
345 0 : char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
346 0 : if (pszMessage)
347 : {
348 0 : pszFullname[nLenWithoutExtension] = 0;
349 0 : snprintf(
350 : pszMessage, nMessageLen,
351 : "Unable to open %s.shx or %s.SHX." SHP_RESTORE_SHX_HINT_MESSAGE,
352 : pszFullname, pszFullname);
353 0 : psHooks->Error(pszMessage);
354 0 : free(pszMessage);
355 : }
356 :
357 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
358 0 : free(psSHP);
359 0 : free(pszFullname);
360 0 : return SHPLIB_NULLPTR;
361 : }
362 :
363 3588 : free(pszFullname);
364 :
365 : /* -------------------------------------------------------------------- */
366 : /* Read the file size from the SHP file. */
367 : /* -------------------------------------------------------------------- */
368 3588 : unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100));
369 3588 : if (!pabyBuf || psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHP) != 1)
370 : {
371 0 : psSHP->sHooks.Error(".shp file is unreadable, or corrupt.");
372 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
373 0 : psSHP->sHooks.FClose(psSHP->fpSHX);
374 0 : free(pabyBuf);
375 0 : free(psSHP);
376 :
377 0 : return SHPLIB_NULLPTR;
378 : }
379 :
380 3588 : psSHP->nFileSize = (STATIC_CAST(unsigned int, pabyBuf[24]) << 24) |
381 3588 : (pabyBuf[25] << 16) | (pabyBuf[26] << 8) | pabyBuf[27];
382 3588 : if (psSHP->nFileSize < UINT_MAX / 2)
383 3588 : psSHP->nFileSize *= 2;
384 : else
385 0 : psSHP->nFileSize = (UINT_MAX / 2) * 2;
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Read SHX file Header info */
389 : /* -------------------------------------------------------------------- */
390 3588 : if (psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHX) != 1 ||
391 7176 : pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27 ||
392 3588 : (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d))
393 : {
394 0 : psSHP->sHooks.Error(".shx file is unreadable, or corrupt.");
395 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
396 0 : psSHP->sHooks.FClose(psSHP->fpSHX);
397 0 : free(pabyBuf);
398 0 : free(psSHP);
399 :
400 0 : return SHPLIB_NULLPTR;
401 : }
402 :
403 3588 : psSHP->nRecords = pabyBuf[27] | (pabyBuf[26] << 8) | (pabyBuf[25] << 16) |
404 3588 : ((pabyBuf[24] & 0x7F) << 24);
405 3588 : psSHP->nRecords = (psSHP->nRecords - 50) / 4;
406 :
407 3588 : psSHP->nShapeType = pabyBuf[32];
408 :
409 3588 : if (psSHP->nRecords < 0 || psSHP->nRecords > 256000000)
410 : {
411 : char szErrorMsg[200];
412 :
413 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
414 : "Record count in .shx header is %d, which seems\n"
415 : "unreasonable. Assuming header is corrupt.",
416 : psSHP->nRecords);
417 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
418 0 : psSHP->sHooks.Error(szErrorMsg);
419 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
420 0 : psSHP->sHooks.FClose(psSHP->fpSHX);
421 0 : free(psSHP);
422 0 : free(pabyBuf);
423 :
424 0 : return SHPLIB_NULLPTR;
425 : }
426 :
427 : /* If a lot of records are advertized, check that the file is big enough */
428 : /* to hold them */
429 3588 : if (psSHP->nRecords >= 1024 * 1024)
430 : {
431 2 : psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 2);
432 2 : const SAOffset nFileSize = psSHP->sHooks.FTell(psSHP->fpSHX);
433 2 : if (nFileSize > 100 &&
434 2 : nFileSize / 2 < STATIC_CAST(SAOffset, psSHP->nRecords * 4 + 50))
435 : {
436 1 : psSHP->nRecords = STATIC_CAST(int, (nFileSize - 100) / 8);
437 : }
438 2 : psSHP->sHooks.FSeek(psSHP->fpSHX, 100, 0);
439 : }
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* Read the bounds. */
443 : /* -------------------------------------------------------------------- */
444 : double dValue;
445 :
446 : #if defined(SHP_BIG_ENDIAN)
447 : SHP_SWAP64(pabyBuf + 36);
448 : #endif
449 3588 : memcpy(&dValue, pabyBuf + 36, 8);
450 3588 : psSHP->adBoundsMin[0] = dValue;
451 :
452 : #if defined(SHP_BIG_ENDIAN)
453 : SHP_SWAP64(pabyBuf + 44);
454 : #endif
455 3588 : memcpy(&dValue, pabyBuf + 44, 8);
456 3588 : psSHP->adBoundsMin[1] = dValue;
457 :
458 : #if defined(SHP_BIG_ENDIAN)
459 : SHP_SWAP64(pabyBuf + 52);
460 : #endif
461 3588 : memcpy(&dValue, pabyBuf + 52, 8);
462 3588 : psSHP->adBoundsMax[0] = dValue;
463 :
464 : #if defined(SHP_BIG_ENDIAN)
465 : SHP_SWAP64(pabyBuf + 60);
466 : #endif
467 3588 : memcpy(&dValue, pabyBuf + 60, 8);
468 3588 : psSHP->adBoundsMax[1] = dValue;
469 :
470 : #if defined(SHP_BIG_ENDIAN)
471 : SHP_SWAP64(pabyBuf + 68); /* z */
472 : #endif
473 3588 : memcpy(&dValue, pabyBuf + 68, 8);
474 3588 : psSHP->adBoundsMin[2] = dValue;
475 :
476 : #if defined(SHP_BIG_ENDIAN)
477 : SHP_SWAP64(pabyBuf + 76);
478 : #endif
479 3588 : memcpy(&dValue, pabyBuf + 76, 8);
480 3588 : psSHP->adBoundsMax[2] = dValue;
481 :
482 : #if defined(SHP_BIG_ENDIAN)
483 : SHP_SWAP64(pabyBuf + 84); /* z */
484 : #endif
485 3588 : memcpy(&dValue, pabyBuf + 84, 8);
486 3588 : psSHP->adBoundsMin[3] = dValue;
487 :
488 : #if defined(SHP_BIG_ENDIAN)
489 : SHP_SWAP64(pabyBuf + 92);
490 : #endif
491 3588 : memcpy(&dValue, pabyBuf + 92, 8);
492 3588 : psSHP->adBoundsMax[3] = dValue;
493 :
494 3588 : free(pabyBuf);
495 :
496 : /* -------------------------------------------------------------------- */
497 : /* Read the .shx file to get the offsets to each record in */
498 : /* the .shp file. */
499 : /* -------------------------------------------------------------------- */
500 3588 : psSHP->nMaxRecords = psSHP->nRecords;
501 :
502 3588 : psSHP->panRecOffset =
503 3588 : STATIC_CAST(unsigned int *,
504 : malloc(sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords)));
505 3588 : psSHP->panRecSize =
506 3588 : STATIC_CAST(unsigned int *,
507 : malloc(sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords)));
508 3588 : if (bLazySHXLoading)
509 0 : pabyBuf = SHPLIB_NULLPTR;
510 : else
511 : pabyBuf =
512 3588 : STATIC_CAST(unsigned char *, malloc(8 * MAX(1, psSHP->nRecords)));
513 :
514 3588 : if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
515 3588 : psSHP->panRecSize == SHPLIB_NULLPTR ||
516 3588 : (!bLazySHXLoading && pabyBuf == SHPLIB_NULLPTR))
517 : {
518 : char szErrorMsg[200];
519 :
520 0 : snprintf(
521 : szErrorMsg, sizeof(szErrorMsg),
522 : "Not enough memory to allocate requested memory (nRecords=%d).\n"
523 : "Probably broken SHP file",
524 : psSHP->nRecords);
525 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
526 0 : psSHP->sHooks.Error(szErrorMsg);
527 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
528 0 : psSHP->sHooks.FClose(psSHP->fpSHX);
529 0 : if (psSHP->panRecOffset)
530 0 : free(psSHP->panRecOffset);
531 0 : if (psSHP->panRecSize)
532 0 : free(psSHP->panRecSize);
533 0 : if (pabyBuf)
534 0 : free(pabyBuf);
535 0 : free(psSHP);
536 0 : return SHPLIB_NULLPTR;
537 : }
538 :
539 3588 : if (bLazySHXLoading)
540 : {
541 0 : memset(psSHP->panRecOffset, 0,
542 0 : sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords));
543 0 : memset(psSHP->panRecSize, 0,
544 0 : sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords));
545 0 : free(pabyBuf); // sometimes make cppcheck happy, but
546 0 : return (psSHP);
547 : }
548 :
549 3588 : if (STATIC_CAST(int, psSHP->sHooks.FRead(pabyBuf, 8, psSHP->nRecords,
550 3588 : psSHP->fpSHX)) != psSHP->nRecords)
551 : {
552 : char szErrorMsg[200];
553 :
554 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
555 : "Failed to read all values for %d records in .shx file: %s.",
556 0 : psSHP->nRecords, strerror(errno));
557 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
558 0 : psSHP->sHooks.Error(szErrorMsg);
559 :
560 : /* SHX is short or unreadable for some reason. */
561 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
562 0 : psSHP->sHooks.FClose(psSHP->fpSHX);
563 0 : free(psSHP->panRecOffset);
564 0 : free(psSHP->panRecSize);
565 0 : free(pabyBuf);
566 0 : free(psSHP);
567 :
568 0 : return SHPLIB_NULLPTR;
569 : }
570 :
571 : /* In read-only mode, we can close the SHX now */
572 3588 : if (strcmp(pszAccess, "rb") == 0)
573 : {
574 1995 : psSHP->sHooks.FClose(psSHP->fpSHX);
575 1995 : psSHP->fpSHX = SHPLIB_NULLPTR;
576 : }
577 :
578 1391860 : for (int i = 0; i < psSHP->nRecords; i++)
579 : {
580 : unsigned int nOffset;
581 1388270 : memcpy(&nOffset, pabyBuf + i * 8, 4);
582 : #if !defined(SHP_BIG_ENDIAN)
583 1388270 : SHP_SWAP32(&nOffset);
584 : #endif
585 :
586 : unsigned int nLength;
587 1388270 : memcpy(&nLength, pabyBuf + i * 8 + 4, 4);
588 : #if !defined(SHP_BIG_ENDIAN)
589 1388270 : SHP_SWAP32(&nLength);
590 : #endif
591 :
592 1388270 : if (nOffset > STATIC_CAST(unsigned int, INT_MAX))
593 : {
594 : char str[128];
595 0 : snprintf(str, sizeof(str), "Invalid offset for entity %d", i);
596 0 : str[sizeof(str) - 1] = '\0';
597 :
598 0 : psSHP->sHooks.Error(str);
599 0 : SHPClose(psSHP);
600 0 : free(pabyBuf);
601 0 : return SHPLIB_NULLPTR;
602 : }
603 1388270 : if (nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4))
604 : {
605 : char str[128];
606 0 : snprintf(str, sizeof(str), "Invalid length for entity %d", i);
607 0 : str[sizeof(str) - 1] = '\0';
608 :
609 0 : psSHP->sHooks.Error(str);
610 0 : SHPClose(psSHP);
611 0 : free(pabyBuf);
612 0 : return SHPLIB_NULLPTR;
613 : }
614 1388270 : psSHP->panRecOffset[i] = nOffset * 2;
615 1388270 : psSHP->panRecSize[i] = nLength * 2;
616 : }
617 3588 : free(pabyBuf);
618 :
619 3588 : return (psSHP);
620 : }
621 :
622 : /************************************************************************/
623 : /* SHPOpenLLEx() */
624 : /* */
625 : /* Open the .shp and .shx files based on the basename of the */
626 : /* files or either file name. It generally invokes SHPRestoreSHX() */
627 : /* in case when bRestoreSHX equals true. */
628 : /************************************************************************/
629 :
630 3921 : SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess,
631 : const SAHooks *psHooks, int bRestoreSHX)
632 : {
633 3921 : if (!bRestoreSHX)
634 3900 : return SHPOpenLL(pszLayer, pszAccess, psHooks);
635 : else
636 : {
637 21 : if (SHPRestoreSHX(pszLayer, pszAccess, psHooks))
638 : {
639 20 : return SHPOpenLL(pszLayer, pszAccess, psHooks);
640 : }
641 : }
642 :
643 1 : return SHPLIB_NULLPTR;
644 : }
645 :
646 : /************************************************************************/
647 : /* SHPRestoreSHX() */
648 : /* */
649 : /* Restore .SHX file using associated .SHP file. */
650 : /* */
651 : /************************************************************************/
652 :
653 21 : int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
654 : const SAHooks *psHooks)
655 : {
656 : /* -------------------------------------------------------------------- */
657 : /* Ensure the access string is one of the legal ones. We */
658 : /* ensure the result string indicates binary to avoid common */
659 : /* problems on Windows. */
660 : /* -------------------------------------------------------------------- */
661 21 : if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 ||
662 21 : strcmp(pszAccess, "r+") == 0)
663 : {
664 20 : pszAccess = "r+b";
665 : }
666 : else
667 : {
668 1 : pszAccess = "rb";
669 : }
670 :
671 : /* -------------------------------------------------------------------- */
672 : /* Open the .shp file. Note that files pulled from */
673 : /* a PC to Unix with upper case filenames won't work! */
674 : /* -------------------------------------------------------------------- */
675 21 : const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
676 21 : char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
677 21 : if (!pszFullname)
678 0 : return 0;
679 21 : memcpy(pszFullname, pszLayer, nLenWithoutExtension);
680 21 : memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
681 21 : SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
682 21 : if (fpSHP == SHPLIB_NULLPTR)
683 : {
684 0 : memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
685 0 : fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
686 : }
687 :
688 21 : if (fpSHP == SHPLIB_NULLPTR)
689 : {
690 0 : const size_t nMessageLen = strlen(pszFullname) * 2 + 256;
691 0 : char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
692 0 : if (pszMessage)
693 : {
694 0 : pszFullname[nLenWithoutExtension] = 0;
695 0 : snprintf(pszMessage, nMessageLen,
696 : "Unable to open %s.shp or %s.SHP.", pszFullname,
697 : pszFullname);
698 0 : psHooks->Error(pszMessage);
699 0 : free(pszMessage);
700 : }
701 :
702 0 : free(pszFullname);
703 :
704 0 : return (0);
705 : }
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* Read the file size from the SHP file. */
709 : /* -------------------------------------------------------------------- */
710 21 : unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100));
711 21 : if (psHooks->FRead(pabyBuf, 100, 1, fpSHP) != 1)
712 : {
713 1 : psHooks->Error(".shp file is unreadable, or corrupt.");
714 1 : psHooks->FClose(fpSHP);
715 :
716 1 : free(pabyBuf);
717 1 : free(pszFullname);
718 :
719 1 : return (0);
720 : }
721 :
722 20 : unsigned int nSHPFilesize = (STATIC_CAST(unsigned int, pabyBuf[24]) << 24) |
723 20 : (pabyBuf[25] << 16) | (pabyBuf[26] << 8) |
724 20 : pabyBuf[27];
725 20 : if (nSHPFilesize < UINT_MAX / 2)
726 20 : nSHPFilesize *= 2;
727 : else
728 0 : nSHPFilesize = (UINT_MAX / 2) * 2;
729 :
730 20 : memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
731 20 : const char pszSHXAccess[] = "w+b";
732 : SAFile fpSHX =
733 20 : psHooks->FOpen(pszFullname, pszSHXAccess, psHooks->pvUserData);
734 20 : if (fpSHX == SHPLIB_NULLPTR)
735 : {
736 0 : size_t nMessageLen = strlen(pszFullname) * 2 + 256;
737 0 : char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
738 0 : if (pszMessage)
739 : {
740 0 : pszFullname[nLenWithoutExtension] = 0;
741 0 : snprintf(pszMessage, nMessageLen,
742 : "Error opening file %s.shx for writing", pszFullname);
743 0 : psHooks->Error(pszMessage);
744 0 : free(pszMessage);
745 : }
746 :
747 0 : psHooks->FClose(fpSHP);
748 :
749 0 : free(pabyBuf);
750 0 : free(pszFullname);
751 :
752 0 : return (0);
753 : }
754 :
755 : /* -------------------------------------------------------------------- */
756 : /* Open SHX and create it using SHP file content. */
757 : /* -------------------------------------------------------------------- */
758 20 : psHooks->FSeek(fpSHP, 100, 0);
759 20 : char *pabySHXHeader = STATIC_CAST(char *, malloc(100));
760 20 : if (!pabySHXHeader)
761 : {
762 0 : psHooks->FClose(fpSHP);
763 :
764 0 : free(pabyBuf);
765 0 : free(pszFullname);
766 :
767 0 : return (0);
768 : }
769 20 : memcpy(pabySHXHeader, pabyBuf, 100);
770 20 : psHooks->FWrite(pabySHXHeader, 100, 1, fpSHX);
771 20 : free(pabyBuf);
772 :
773 : // unsigned int nCurrentRecordOffset = 0;
774 20 : unsigned int nCurrentSHPOffset = 100;
775 20 : unsigned int nRealSHXContentSize = 100;
776 20 : int nRetCode = TRUE;
777 20 : unsigned int nRecordOffset = 50;
778 :
779 52 : while (nCurrentSHPOffset < nSHPFilesize)
780 : {
781 32 : unsigned int niRecord = 0;
782 32 : unsigned int nRecordLength = 0;
783 : int nSHPType;
784 :
785 32 : if (psHooks->FRead(&niRecord, 4, 1, fpSHP) == 1 &&
786 64 : psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1 &&
787 32 : psHooks->FRead(&nSHPType, 4, 1, fpSHP) == 1)
788 : {
789 : char abyReadRecord[8];
790 32 : unsigned int nRecordOffsetBE = nRecordOffset;
791 :
792 : #if !defined(SHP_BIG_ENDIAN)
793 32 : SHP_SWAP32(&nRecordOffsetBE);
794 : #endif
795 32 : memcpy(abyReadRecord, &nRecordOffsetBE, 4);
796 32 : memcpy(abyReadRecord + 4, &nRecordLength, 4);
797 :
798 : #if !defined(SHP_BIG_ENDIAN)
799 32 : SHP_SWAP32(&nRecordLength);
800 : #endif
801 : #if defined(SHP_BIG_ENDIAN)
802 : SHP_SWAP32(&nSHPType);
803 : #endif
804 :
805 : // Sanity check on record length
806 32 : if (nRecordLength < 1 ||
807 32 : nRecordLength > (nSHPFilesize - (nCurrentSHPOffset + 8)) / 2)
808 : {
809 : char szErrorMsg[200];
810 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
811 : "Error parsing .shp to restore .shx. "
812 : "Invalid record length = %u at record starting at "
813 : "offset %u",
814 : nRecordLength, nCurrentSHPOffset);
815 0 : psHooks->Error(szErrorMsg);
816 :
817 0 : nRetCode = FALSE;
818 0 : break;
819 : }
820 :
821 : // Sanity check on record type
822 32 : if (nSHPType != SHPT_NULL && nSHPType != SHPT_POINT &&
823 18 : nSHPType != SHPT_ARC && nSHPType != SHPT_POLYGON &&
824 14 : nSHPType != SHPT_MULTIPOINT && nSHPType != SHPT_POINTZ &&
825 11 : nSHPType != SHPT_ARCZ && nSHPType != SHPT_POLYGONZ &&
826 7 : nSHPType != SHPT_MULTIPOINTZ && nSHPType != SHPT_POINTM &&
827 4 : nSHPType != SHPT_ARCM && nSHPType != SHPT_POLYGONM &&
828 2 : nSHPType != SHPT_MULTIPOINTM && nSHPType != SHPT_MULTIPATCH)
829 : {
830 : char szErrorMsg[200];
831 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
832 : "Error parsing .shp to restore .shx. "
833 : "Invalid shape type = %d at record starting at "
834 : "offset %u",
835 : nSHPType, nCurrentSHPOffset);
836 0 : psHooks->Error(szErrorMsg);
837 :
838 0 : nRetCode = FALSE;
839 0 : break;
840 : }
841 :
842 32 : psHooks->FWrite(abyReadRecord, 8, 1, fpSHX);
843 :
844 32 : nRecordOffset += nRecordLength + 4;
845 : // nCurrentRecordOffset += 8;
846 32 : nCurrentSHPOffset += 8 + nRecordLength * 2;
847 :
848 32 : psHooks->FSeek(fpSHP, nCurrentSHPOffset, 0);
849 32 : nRealSHXContentSize += 8;
850 : }
851 : else
852 : {
853 : char szErrorMsg[200];
854 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
855 : "Error parsing .shp to restore .shx. "
856 : "Cannot read first bytes of record starting at "
857 : "offset %u",
858 : nCurrentSHPOffset);
859 0 : psHooks->Error(szErrorMsg);
860 :
861 0 : nRetCode = FALSE;
862 0 : break;
863 : }
864 : }
865 20 : if (nRetCode && nCurrentSHPOffset != nSHPFilesize)
866 : {
867 0 : psHooks->Error("Error parsing .shp to restore .shx. "
868 : "Not expected number of bytes");
869 :
870 0 : nRetCode = FALSE;
871 : }
872 :
873 20 : nRealSHXContentSize /= 2; // Bytes counted -> WORDs
874 : #if !defined(SHP_BIG_ENDIAN)
875 20 : SHP_SWAP32(&nRealSHXContentSize);
876 : #endif
877 :
878 20 : psHooks->FSeek(fpSHX, 24, 0);
879 20 : psHooks->FWrite(&nRealSHXContentSize, 4, 1, fpSHX);
880 :
881 20 : psHooks->FClose(fpSHP);
882 20 : psHooks->FClose(fpSHX);
883 :
884 20 : free(pszFullname);
885 20 : free(pabySHXHeader);
886 :
887 20 : return nRetCode;
888 : }
889 :
890 : /************************************************************************/
891 : /* SHPClose() */
892 : /* */
893 : /* Close the .shp and .shx files. */
894 : /************************************************************************/
895 :
896 5214 : void SHPAPI_CALL SHPClose(SHPHandle psSHP)
897 : {
898 5214 : if (psSHP == SHPLIB_NULLPTR)
899 1 : return;
900 :
901 : /* -------------------------------------------------------------------- */
902 : /* Update the header if we have modified anything. */
903 : /* -------------------------------------------------------------------- */
904 5213 : if (psSHP->bUpdated)
905 1624 : SHPWriteHeader(psSHP);
906 :
907 : /* -------------------------------------------------------------------- */
908 : /* Free all resources, and close files. */
909 : /* -------------------------------------------------------------------- */
910 5213 : free(psSHP->panRecOffset);
911 5213 : free(psSHP->panRecSize);
912 :
913 5213 : if (psSHP->fpSHX != SHPLIB_NULLPTR)
914 3223 : psSHP->sHooks.FClose(psSHP->fpSHX);
915 5213 : psSHP->sHooks.FClose(psSHP->fpSHP);
916 :
917 5213 : if (psSHP->pabyRec != SHPLIB_NULLPTR)
918 : {
919 2250 : free(psSHP->pabyRec);
920 : }
921 :
922 5213 : if (psSHP->pabyObjectBuf != SHPLIB_NULLPTR)
923 : {
924 936 : free(psSHP->pabyObjectBuf);
925 : }
926 5213 : if (psSHP->psCachedObject != SHPLIB_NULLPTR)
927 : {
928 5190 : free(psSHP->psCachedObject);
929 : }
930 :
931 5213 : free(psSHP);
932 : }
933 :
934 : /************************************************************************/
935 : /* SHPSetFastModeReadObject() */
936 : /************************************************************************/
937 :
938 : /* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */
939 : /* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */
940 : /* The SHPObject padfZ and padfM members may be NULL depending on the geometry */
941 : /* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */
942 5197 : void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode)
943 : {
944 5197 : if (bFastMode)
945 : {
946 5197 : if (hSHP->psCachedObject == SHPLIB_NULLPTR)
947 : {
948 5197 : hSHP->psCachedObject =
949 5197 : STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject)));
950 5197 : assert(hSHP->psCachedObject != SHPLIB_NULLPTR);
951 : }
952 : }
953 :
954 5197 : hSHP->bFastModeReadObject = bFastMode;
955 5197 : }
956 :
957 : /************************************************************************/
958 : /* SHPGetInfo() */
959 : /* */
960 : /* Fetch general information about the shape file. */
961 : /************************************************************************/
962 :
963 13200 : void SHPAPI_CALL SHPGetInfo(const SHPHandle psSHP, int *pnEntities,
964 : int *pnShapeType, double *padfMinBound,
965 : double *padfMaxBound)
966 : {
967 13200 : if (psSHP == SHPLIB_NULLPTR)
968 0 : return;
969 :
970 13200 : if (pnEntities != SHPLIB_NULLPTR)
971 34 : *pnEntities = psSHP->nRecords;
972 :
973 13200 : if (pnShapeType != SHPLIB_NULLPTR)
974 0 : *pnShapeType = psSHP->nShapeType;
975 :
976 66000 : for (int i = 0; i < 4; i++)
977 : {
978 52800 : if (padfMinBound != SHPLIB_NULLPTR)
979 52664 : padfMinBound[i] = psSHP->adBoundsMin[i];
980 52800 : if (padfMaxBound != SHPLIB_NULLPTR)
981 52664 : padfMaxBound[i] = psSHP->adBoundsMax[i];
982 : }
983 : }
984 :
985 : /************************************************************************/
986 : /* SHPCreate() */
987 : /* */
988 : /* Create a new shape file and return a handle to the open */
989 : /* shape file with read/write access. */
990 : /************************************************************************/
991 :
992 23 : SHPHandle SHPAPI_CALL SHPCreate(const char *pszLayer, int nShapeType)
993 : {
994 : SAHooks sHooks;
995 :
996 23 : SASetupDefaultHooks(&sHooks);
997 :
998 46 : return SHPCreateLL(pszLayer, nShapeType, &sHooks);
999 : }
1000 :
1001 : /************************************************************************/
1002 : /* SHPCreate() */
1003 : /* */
1004 : /* Create a new shape file and return a handle to the open */
1005 : /* shape file with read/write access. */
1006 : /************************************************************************/
1007 :
1008 1636 : SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
1009 : const SAHooks *psHooks)
1010 : {
1011 :
1012 1636 : SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo)));
1013 1636 : if (!psSHP)
1014 0 : return SHPLIB_NULLPTR;
1015 :
1016 : /* -------------------------------------------------------------------- */
1017 : /* Open the two files so we can write their headers. */
1018 : /* -------------------------------------------------------------------- */
1019 1636 : const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
1020 1636 : char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
1021 1636 : if (!pszFullname)
1022 : {
1023 0 : free(psSHP);
1024 0 : return SHPLIB_NULLPTR;
1025 : }
1026 1636 : memcpy(pszFullname, pszLayer, nLenWithoutExtension);
1027 1636 : memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
1028 1636 : SAFile fpSHP = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData);
1029 1636 : if (fpSHP == SHPLIB_NULLPTR)
1030 : {
1031 4 : const size_t nMessageLen = strlen(pszFullname) + 256;
1032 4 : char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
1033 4 : if (pszMessage)
1034 : {
1035 8 : snprintf(pszMessage, nMessageLen, "Failed to create file %s: %s",
1036 4 : pszFullname, strerror(errno));
1037 4 : psHooks->Error(pszMessage);
1038 4 : free(pszMessage);
1039 : }
1040 4 : free(pszFullname);
1041 4 : free(psSHP);
1042 4 : return SHPLIB_NULLPTR;
1043 : }
1044 :
1045 1632 : memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
1046 1632 : SAFile fpSHX = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData);
1047 1632 : if (fpSHX == SHPLIB_NULLPTR)
1048 : {
1049 0 : const size_t nMessageLen = strlen(pszFullname) + 256;
1050 0 : char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
1051 0 : if (pszMessage)
1052 : {
1053 0 : snprintf(pszMessage, nMessageLen, "Failed to create file %s: %s",
1054 0 : pszFullname, strerror(errno));
1055 0 : psHooks->Error(pszMessage);
1056 0 : free(pszMessage);
1057 : }
1058 :
1059 0 : free(pszFullname);
1060 0 : psHooks->FClose(fpSHP);
1061 0 : free(psSHP);
1062 0 : return SHPLIB_NULLPTR;
1063 : }
1064 :
1065 1632 : free(pszFullname);
1066 1632 : pszFullname = SHPLIB_NULLPTR;
1067 :
1068 : /* -------------------------------------------------------------------- */
1069 : /* Prepare header block for .shp file. */
1070 : /* -------------------------------------------------------------------- */
1071 : unsigned char abyHeader[100];
1072 1632 : memset(abyHeader, 0, sizeof(abyHeader));
1073 :
1074 1632 : abyHeader[2] = 0x27; /* magic cookie */
1075 1632 : abyHeader[3] = 0x0a;
1076 :
1077 1632 : uint32_t i32 = 50; /* file size */
1078 1632 : ByteCopy(&i32, abyHeader + 24, 4);
1079 : #if !defined(SHP_BIG_ENDIAN)
1080 1632 : SHP_SWAP32(abyHeader + 24);
1081 : #endif
1082 :
1083 1632 : i32 = 1000; /* version */
1084 1632 : ByteCopy(&i32, abyHeader + 28, 4);
1085 : #if defined(SHP_BIG_ENDIAN)
1086 : SHP_SWAP32(abyHeader + 28);
1087 : #endif
1088 :
1089 1632 : i32 = nShapeType; /* shape type */
1090 1632 : ByteCopy(&i32, abyHeader + 32, 4);
1091 : #if defined(SHP_BIG_ENDIAN)
1092 : SHP_SWAP32(abyHeader + 32);
1093 : #endif
1094 :
1095 1632 : double dValue = 0.0; /* set bounds */
1096 1632 : ByteCopy(&dValue, abyHeader + 36, 8);
1097 1632 : ByteCopy(&dValue, abyHeader + 44, 8);
1098 1632 : ByteCopy(&dValue, abyHeader + 52, 8);
1099 1632 : ByteCopy(&dValue, abyHeader + 60, 8);
1100 :
1101 : /* -------------------------------------------------------------------- */
1102 : /* Write .shp file header. */
1103 : /* -------------------------------------------------------------------- */
1104 1632 : if (psHooks->FWrite(abyHeader, 100, 1, fpSHP) != 1)
1105 : {
1106 : char szErrorMsg[200];
1107 :
1108 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
1109 0 : "Failed to write .shp header: %s", strerror(errno));
1110 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1111 0 : psHooks->Error(szErrorMsg);
1112 :
1113 0 : free(pszFullname);
1114 0 : psHooks->FClose(fpSHP);
1115 0 : psHooks->FClose(fpSHX);
1116 0 : free(psSHP);
1117 0 : return SHPLIB_NULLPTR;
1118 : }
1119 :
1120 : /* -------------------------------------------------------------------- */
1121 : /* Prepare, and write .shx file header. */
1122 : /* -------------------------------------------------------------------- */
1123 1632 : i32 = 50; /* file size */
1124 1632 : ByteCopy(&i32, abyHeader + 24, 4);
1125 : #if !defined(SHP_BIG_ENDIAN)
1126 1632 : SHP_SWAP32(abyHeader + 24);
1127 : #endif
1128 :
1129 1632 : if (psHooks->FWrite(abyHeader, 100, 1, fpSHX) != 1)
1130 : {
1131 : char szErrorMsg[200];
1132 :
1133 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
1134 0 : "Failure writing .shx header: %s", strerror(errno));
1135 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1136 0 : psHooks->Error(szErrorMsg);
1137 :
1138 0 : free(pszFullname);
1139 0 : psHooks->FClose(fpSHP);
1140 0 : psHooks->FClose(fpSHX);
1141 0 : free(psSHP);
1142 0 : return SHPLIB_NULLPTR;
1143 : }
1144 :
1145 1632 : psSHP->bUpdated = FALSE;
1146 1632 : memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks));
1147 :
1148 1632 : psSHP->fpSHP = fpSHP;
1149 1632 : psSHP->fpSHX = fpSHX;
1150 1632 : psSHP->nShapeType = nShapeType;
1151 1632 : psSHP->nFileSize = 100;
1152 1632 : psSHP->panRecOffset =
1153 1632 : STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int)));
1154 1632 : psSHP->panRecSize =
1155 1632 : STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int)));
1156 :
1157 1632 : if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
1158 1632 : psSHP->panRecSize == SHPLIB_NULLPTR)
1159 : {
1160 0 : psSHP->sHooks.Error("Not enough memory to allocate requested memory");
1161 0 : psSHP->sHooks.FClose(psSHP->fpSHP);
1162 0 : psSHP->sHooks.FClose(psSHP->fpSHX);
1163 0 : if (psSHP->panRecOffset)
1164 0 : free(psSHP->panRecOffset);
1165 0 : if (psSHP->panRecSize)
1166 0 : free(psSHP->panRecSize);
1167 0 : free(psSHP);
1168 0 : return SHPLIB_NULLPTR;
1169 : }
1170 :
1171 1632 : return psSHP;
1172 : }
1173 :
1174 : /************************************************************************/
1175 : /* _SHPSetBounds() */
1176 : /* */
1177 : /* Compute a bounds rectangle for a shape, and set it into the */
1178 : /* indicated location in the record. */
1179 : /************************************************************************/
1180 :
1181 19034 : static void _SHPSetBounds(unsigned char *pabyRec, const SHPObject *psShape)
1182 : {
1183 19034 : ByteCopy(&(psShape->dfXMin), pabyRec + 0, 8);
1184 19034 : ByteCopy(&(psShape->dfYMin), pabyRec + 8, 8);
1185 19034 : ByteCopy(&(psShape->dfXMax), pabyRec + 16, 8);
1186 19034 : ByteCopy(&(psShape->dfYMax), pabyRec + 24, 8);
1187 :
1188 : #if defined(SHP_BIG_ENDIAN)
1189 : SHP_SWAP64(pabyRec + 0);
1190 : SHP_SWAP64(pabyRec + 8);
1191 : SHP_SWAP64(pabyRec + 16);
1192 : SHP_SWAP64(pabyRec + 24);
1193 : #endif
1194 19034 : }
1195 :
1196 : /************************************************************************/
1197 : /* SHPComputeExtents() */
1198 : /* */
1199 : /* Recompute the extents of a shape. Automatically done by */
1200 : /* SHPCreateObject(). */
1201 : /************************************************************************/
1202 :
1203 64258 : void SHPAPI_CALL SHPComputeExtents(SHPObject *psObject)
1204 : {
1205 : /* -------------------------------------------------------------------- */
1206 : /* Build extents for this object. */
1207 : /* -------------------------------------------------------------------- */
1208 64258 : if (psObject->nVertices > 0)
1209 : {
1210 63641 : psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
1211 63641 : psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
1212 63641 : psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
1213 63641 : psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
1214 : }
1215 :
1216 317672 : for (int i = 0; i < psObject->nVertices; i++)
1217 : {
1218 253414 : psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
1219 253414 : psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
1220 253414 : psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
1221 253414 : psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
1222 :
1223 253414 : psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
1224 253414 : psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
1225 253414 : psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
1226 253414 : psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
1227 : }
1228 64258 : }
1229 :
1230 : /************************************************************************/
1231 : /* SHPCreateObject() */
1232 : /* */
1233 : /* Create a shape object. It should be freed with */
1234 : /* SHPDestroyObject(). */
1235 : /************************************************************************/
1236 :
1237 : SHPObject SHPAPI_CALL1(*)
1238 64258 : SHPCreateObject(int nSHPType, int nShapeId, int nParts,
1239 : const int *panPartStart, const int *panPartType,
1240 : int nVertices, const double *padfX, const double *padfY,
1241 : const double *padfZ, const double *padfM)
1242 : {
1243 : SHPObject *psObject =
1244 64258 : STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject)));
1245 64258 : if (!psObject)
1246 0 : return SHPLIB_NULLPTR;
1247 64258 : psObject->nSHPType = nSHPType;
1248 64258 : psObject->nShapeId = nShapeId;
1249 64258 : psObject->bMeasureIsUsed = FALSE;
1250 :
1251 : /* -------------------------------------------------------------------- */
1252 : /* Establish whether this shape type has M, and Z values. */
1253 : /* -------------------------------------------------------------------- */
1254 : bool bHasM;
1255 : bool bHasZ;
1256 :
1257 64258 : if (nSHPType == SHPT_ARCM || nSHPType == SHPT_POINTM ||
1258 64243 : nSHPType == SHPT_POLYGONM || nSHPType == SHPT_MULTIPOINTM)
1259 : {
1260 18 : bHasM = true;
1261 18 : bHasZ = false;
1262 : }
1263 64240 : else if (nSHPType == SHPT_ARCZ || nSHPType == SHPT_POINTZ ||
1264 18342 : nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_MULTIPOINTZ ||
1265 : nSHPType == SHPT_MULTIPATCH)
1266 : {
1267 45921 : bHasM = true;
1268 45921 : bHasZ = true;
1269 : }
1270 : else
1271 : {
1272 18319 : bHasM = false;
1273 18319 : bHasZ = false;
1274 : }
1275 :
1276 : /* -------------------------------------------------------------------- */
1277 : /* Capture parts. Note that part type is optional, and */
1278 : /* defaults to ring. */
1279 : /* -------------------------------------------------------------------- */
1280 64258 : if (nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON ||
1281 47316 : nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM ||
1282 45426 : nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ ||
1283 : nSHPType == SHPT_MULTIPATCH)
1284 : {
1285 18918 : psObject->nParts = MAX(1, nParts);
1286 :
1287 18918 : psObject->panPartStart =
1288 18918 : STATIC_CAST(int *, calloc(psObject->nParts, sizeof(int)));
1289 18918 : psObject->panPartType =
1290 18918 : STATIC_CAST(int *, malloc(sizeof(int) * psObject->nParts));
1291 18918 : if (!psObject->panPartStart || !psObject->panPartType)
1292 : {
1293 0 : free(psObject->panPartStart);
1294 0 : free(psObject->panPartType);
1295 0 : free(psObject);
1296 0 : return SHPLIB_NULLPTR;
1297 : }
1298 :
1299 18918 : psObject->panPartStart[0] = 0;
1300 18918 : psObject->panPartType[0] = SHPP_RING;
1301 :
1302 38094 : for (int i = 0; i < nParts; i++)
1303 : {
1304 19176 : if (panPartStart != SHPLIB_NULLPTR)
1305 19176 : psObject->panPartStart[i] = panPartStart[i];
1306 :
1307 19176 : if (panPartType != SHPLIB_NULLPTR)
1308 15 : psObject->panPartType[i] = panPartType[i];
1309 : else
1310 19161 : psObject->panPartType[i] = SHPP_RING;
1311 : }
1312 :
1313 18918 : psObject->panPartStart[0] = 0;
1314 : }
1315 :
1316 : /* -------------------------------------------------------------------- */
1317 : /* Capture vertices. Note that X, Y, Z and M are optional. */
1318 : /* -------------------------------------------------------------------- */
1319 64258 : if (nVertices > 0)
1320 : {
1321 63641 : const size_t nSize = sizeof(double) * nVertices;
1322 63641 : psObject->padfX =
1323 63641 : STATIC_CAST(double *, padfX ? malloc(nSize)
1324 : : calloc(nVertices, sizeof(double)));
1325 63641 : psObject->padfY =
1326 63641 : STATIC_CAST(double *, padfY ? malloc(nSize)
1327 : : calloc(nVertices, sizeof(double)));
1328 63641 : psObject->padfZ = STATIC_CAST(
1329 : double *,
1330 : padfZ &&bHasZ ? malloc(nSize) : calloc(nVertices, sizeof(double)));
1331 63641 : psObject->padfM = STATIC_CAST(
1332 : double *,
1333 : padfM &&bHasM ? malloc(nSize) : calloc(nVertices, sizeof(double)));
1334 63641 : if (!psObject->padfX || !psObject->padfY || !psObject->padfZ ||
1335 63641 : !psObject->padfM)
1336 : {
1337 0 : free(psObject->panPartStart);
1338 0 : free(psObject->panPartType);
1339 0 : free(psObject->padfX);
1340 0 : free(psObject->padfY);
1341 0 : free(psObject->padfZ);
1342 0 : free(psObject->padfM);
1343 0 : free(psObject);
1344 0 : return SHPLIB_NULLPTR;
1345 : }
1346 63641 : if (padfX != SHPLIB_NULLPTR)
1347 63641 : memcpy(psObject->padfX, padfX, nSize);
1348 63641 : if (padfY != SHPLIB_NULLPTR)
1349 63641 : memcpy(psObject->padfY, padfY, nSize);
1350 63641 : if (padfZ != SHPLIB_NULLPTR && bHasZ)
1351 45921 : memcpy(psObject->padfZ, padfZ, nSize);
1352 63641 : if (padfM != SHPLIB_NULLPTR && bHasM)
1353 : {
1354 36 : memcpy(psObject->padfM, padfM, nSize);
1355 36 : psObject->bMeasureIsUsed = TRUE;
1356 : }
1357 : }
1358 :
1359 : /* -------------------------------------------------------------------- */
1360 : /* Compute the extents. */
1361 : /* -------------------------------------------------------------------- */
1362 64258 : psObject->nVertices = nVertices;
1363 64258 : SHPComputeExtents(psObject);
1364 :
1365 64258 : return (psObject);
1366 : }
1367 :
1368 : /************************************************************************/
1369 : /* SHPCreateSimpleObject() */
1370 : /* */
1371 : /* Create a simple (common) shape object. Destroy with */
1372 : /* SHPDestroyObject(). */
1373 : /************************************************************************/
1374 :
1375 : SHPObject SHPAPI_CALL1(*)
1376 0 : SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX,
1377 : const double *padfY, const double *padfZ)
1378 : {
1379 0 : return (SHPCreateObject(nSHPType, -1, 0, SHPLIB_NULLPTR, SHPLIB_NULLPTR,
1380 0 : nVertices, padfX, padfY, padfZ, SHPLIB_NULLPTR));
1381 : }
1382 :
1383 : /************************************************************************/
1384 : /* SHPWriteObject() */
1385 : /* */
1386 : /* Write out the vertices of a new structure. Note that it is */
1387 : /* only possible to write vertices at the end of the file. */
1388 : /************************************************************************/
1389 :
1390 64409 : int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
1391 : const SHPObject *psObject)
1392 : {
1393 64409 : psSHP->bUpdated = TRUE;
1394 :
1395 : /* -------------------------------------------------------------------- */
1396 : /* Ensure that shape object matches the type of the file it is */
1397 : /* being written to. */
1398 : /* -------------------------------------------------------------------- */
1399 64409 : assert(psObject->nSHPType == psSHP->nShapeType ||
1400 : psObject->nSHPType == SHPT_NULL);
1401 :
1402 : /* -------------------------------------------------------------------- */
1403 : /* Ensure that -1 is used for appends. Either blow an */
1404 : /* assertion, or if they are disabled, set the shapeid to -1 */
1405 : /* for appends. */
1406 : /* -------------------------------------------------------------------- */
1407 64409 : assert(nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords));
1408 :
1409 64409 : if (nShapeId != -1 && nShapeId >= psSHP->nRecords)
1410 0 : nShapeId = -1;
1411 :
1412 : /* -------------------------------------------------------------------- */
1413 : /* Add the new entity to the in memory index. */
1414 : /* -------------------------------------------------------------------- */
1415 64409 : if (nShapeId == -1 && psSHP->nRecords + 1 > psSHP->nMaxRecords)
1416 : {
1417 : /* This cannot overflow given that we check that the file size does
1418 : * not grow over 4 GB, and the minimum size of a record is 12 bytes,
1419 : * hence the maximm value for nMaxRecords is 357,913,941
1420 : */
1421 1689 : int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100;
1422 : unsigned int *panRecOffsetNew;
1423 : unsigned int *panRecSizeNew;
1424 :
1425 1689 : panRecOffsetNew = STATIC_CAST(
1426 : unsigned int *, realloc(psSHP->panRecOffset,
1427 : sizeof(unsigned int) * nNewMaxRecords));
1428 1689 : if (panRecOffsetNew == SHPLIB_NULLPTR)
1429 : {
1430 0 : psSHP->sHooks.Error("Failed to write shape object. "
1431 : "Memory allocation error.");
1432 0 : return -1;
1433 : }
1434 1689 : psSHP->panRecOffset = panRecOffsetNew;
1435 :
1436 1689 : panRecSizeNew = STATIC_CAST(
1437 : unsigned int *,
1438 : realloc(psSHP->panRecSize, sizeof(unsigned int) * nNewMaxRecords));
1439 1689 : if (panRecSizeNew == SHPLIB_NULLPTR)
1440 : {
1441 0 : psSHP->sHooks.Error("Failed to write shape object. "
1442 : "Memory allocation error.");
1443 0 : return -1;
1444 : }
1445 1689 : psSHP->panRecSize = panRecSizeNew;
1446 :
1447 1689 : psSHP->nMaxRecords = nNewMaxRecords;
1448 : }
1449 :
1450 : /* -------------------------------------------------------------------- */
1451 : /* Initialize record. */
1452 : /* -------------------------------------------------------------------- */
1453 :
1454 : /* The following computation cannot overflow on 32-bit platforms given that
1455 : * the user had to allocate arrays of at least that size. */
1456 64409 : size_t nRecMaxSize =
1457 64409 : psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8;
1458 : /* But the following test could trigger on 64-bit platforms on huge
1459 : * geometries. */
1460 64409 : const unsigned nExtraSpaceForGeomHeader = 128;
1461 64409 : if (nRecMaxSize > UINT_MAX - nExtraSpaceForGeomHeader)
1462 : {
1463 0 : psSHP->sHooks.Error("Failed to write shape object. Too big geometry.");
1464 0 : return -1;
1465 : }
1466 64409 : nRecMaxSize += nExtraSpaceForGeomHeader;
1467 64409 : unsigned char *pabyRec = STATIC_CAST(unsigned char *, malloc(nRecMaxSize));
1468 64409 : if (pabyRec == SHPLIB_NULLPTR)
1469 : {
1470 0 : psSHP->sHooks.Error("Failed to write shape object. "
1471 : "Memory allocation error.");
1472 0 : return -1;
1473 : }
1474 :
1475 : /* -------------------------------------------------------------------- */
1476 : /* Extract vertices for a Polygon or Arc. */
1477 : /* -------------------------------------------------------------------- */
1478 64409 : unsigned int nRecordSize = 0;
1479 64409 : const bool bFirstFeature = psSHP->nRecords == 0;
1480 :
1481 64409 : if (psObject->nSHPType == SHPT_POLYGON ||
1482 49900 : psObject->nSHPType == SHPT_POLYGONZ ||
1483 49826 : psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARC ||
1484 47300 : psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM ||
1485 45410 : psObject->nSHPType == SHPT_MULTIPATCH)
1486 : {
1487 19011 : uint32_t nPoints = psObject->nVertices;
1488 19011 : uint32_t nParts = psObject->nParts;
1489 :
1490 19011 : _SHPSetBounds(pabyRec + 12, psObject);
1491 :
1492 : #if defined(SHP_BIG_ENDIAN)
1493 : SHP_SWAP32(&nPoints);
1494 : SHP_SWAP32(&nParts);
1495 : #endif
1496 :
1497 19011 : ByteCopy(&nPoints, pabyRec + 40 + 8, 4);
1498 19011 : ByteCopy(&nParts, pabyRec + 36 + 8, 4);
1499 :
1500 19011 : nRecordSize = 52;
1501 :
1502 : /*
1503 : * Write part start positions.
1504 : */
1505 19011 : ByteCopy(psObject->panPartStart, pabyRec + 44 + 8,
1506 : 4 * psObject->nParts);
1507 38280 : for (int i = 0; i < psObject->nParts; i++)
1508 : {
1509 : #if defined(SHP_BIG_ENDIAN)
1510 : SHP_SWAP32(pabyRec + 44 + 8 + 4 * i);
1511 : #endif
1512 19269 : nRecordSize += 4;
1513 : }
1514 :
1515 : /*
1516 : * Write multipatch part types if needed.
1517 : */
1518 19011 : if (psObject->nSHPType == SHPT_MULTIPATCH)
1519 : {
1520 12 : memcpy(pabyRec + nRecordSize, psObject->panPartType,
1521 12 : 4 * psObject->nParts);
1522 27 : for (int i = 0; i < psObject->nParts; i++)
1523 : {
1524 : #if defined(SHP_BIG_ENDIAN)
1525 : SHP_SWAP32(pabyRec + nRecordSize);
1526 : #endif
1527 15 : nRecordSize += 4;
1528 : }
1529 : }
1530 :
1531 : /*
1532 : * Write the (x,y) vertex values.
1533 : */
1534 229796 : for (int i = 0; i < psObject->nVertices; i++)
1535 : {
1536 210785 : ByteCopy(psObject->padfX + i, pabyRec + nRecordSize, 8);
1537 210785 : ByteCopy(psObject->padfY + i, pabyRec + nRecordSize + 8, 8);
1538 :
1539 : #if defined(SHP_BIG_ENDIAN)
1540 : SHP_SWAP64(pabyRec + nRecordSize);
1541 : SHP_SWAP64(pabyRec + nRecordSize + 8);
1542 : #endif
1543 :
1544 210785 : nRecordSize += 2 * 8;
1545 : }
1546 :
1547 : /*
1548 : * Write the Z coordinates (if any).
1549 : */
1550 19011 : if (psObject->nSHPType == SHPT_POLYGONZ ||
1551 18937 : psObject->nSHPType == SHPT_ARCZ ||
1552 17052 : psObject->nSHPType == SHPT_MULTIPATCH)
1553 : {
1554 1971 : ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
1555 : #if defined(SHP_BIG_ENDIAN)
1556 : SHP_SWAP64(pabyRec + nRecordSize);
1557 : #endif
1558 1971 : nRecordSize += 8;
1559 :
1560 1971 : ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
1561 : #if defined(SHP_BIG_ENDIAN)
1562 : SHP_SWAP64(pabyRec + nRecordSize);
1563 : #endif
1564 1971 : nRecordSize += 8;
1565 :
1566 53271 : for (int i = 0; i < psObject->nVertices; i++)
1567 : {
1568 51300 : ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
1569 : #if defined(SHP_BIG_ENDIAN)
1570 : SHP_SWAP64(pabyRec + nRecordSize);
1571 : #endif
1572 51300 : nRecordSize += 8;
1573 : }
1574 : }
1575 :
1576 : /*
1577 : * Write the M values, if any.
1578 : */
1579 19011 : if (psObject->bMeasureIsUsed &&
1580 20 : (psObject->nSHPType == SHPT_POLYGONM ||
1581 15 : psObject->nSHPType == SHPT_ARCM
1582 : #ifndef DISABLE_MULTIPATCH_MEASURE
1583 : || psObject->nSHPType == SHPT_MULTIPATCH
1584 : #endif
1585 10 : || psObject->nSHPType == SHPT_POLYGONZ ||
1586 5 : psObject->nSHPType == SHPT_ARCZ))
1587 : {
1588 20 : ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
1589 : #if defined(SHP_BIG_ENDIAN)
1590 : SHP_SWAP64(pabyRec + nRecordSize);
1591 : #endif
1592 20 : nRecordSize += 8;
1593 :
1594 20 : ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
1595 : #if defined(SHP_BIG_ENDIAN)
1596 : SHP_SWAP64(pabyRec + nRecordSize);
1597 : #endif
1598 20 : nRecordSize += 8;
1599 :
1600 104 : for (int i = 0; i < psObject->nVertices; i++)
1601 : {
1602 84 : ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
1603 : #if defined(SHP_BIG_ENDIAN)
1604 : SHP_SWAP64(pabyRec + nRecordSize);
1605 : #endif
1606 84 : nRecordSize += 8;
1607 : }
1608 19011 : }
1609 : }
1610 :
1611 : /* -------------------------------------------------------------------- */
1612 : /* Extract vertices for a MultiPoint. */
1613 : /* -------------------------------------------------------------------- */
1614 45398 : else if (psObject->nSHPType == SHPT_MULTIPOINT ||
1615 45389 : psObject->nSHPType == SHPT_MULTIPOINTZ ||
1616 45378 : psObject->nSHPType == SHPT_MULTIPOINTM)
1617 : {
1618 23 : uint32_t nPoints = psObject->nVertices;
1619 :
1620 23 : _SHPSetBounds(pabyRec + 12, psObject);
1621 :
1622 : #if defined(SHP_BIG_ENDIAN)
1623 : SHP_SWAP32(&nPoints);
1624 : #endif
1625 23 : ByteCopy(&nPoints, pabyRec + 44, 4);
1626 :
1627 56 : for (int i = 0; i < psObject->nVertices; i++)
1628 : {
1629 33 : ByteCopy(psObject->padfX + i, pabyRec + 48 + i * 16, 8);
1630 33 : ByteCopy(psObject->padfY + i, pabyRec + 48 + i * 16 + 8, 8);
1631 :
1632 : #if defined(SHP_BIG_ENDIAN)
1633 : SHP_SWAP64(pabyRec + 48 + i * 16);
1634 : SHP_SWAP64(pabyRec + 48 + i * 16 + 8);
1635 : #endif
1636 : }
1637 :
1638 23 : nRecordSize = 48 + 16 * psObject->nVertices;
1639 :
1640 23 : if (psObject->nSHPType == SHPT_MULTIPOINTZ)
1641 : {
1642 11 : ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
1643 : #if defined(SHP_BIG_ENDIAN)
1644 : SHP_SWAP64(pabyRec + nRecordSize);
1645 : #endif
1646 11 : nRecordSize += 8;
1647 :
1648 11 : ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
1649 : #if defined(SHP_BIG_ENDIAN)
1650 : SHP_SWAP64(pabyRec + nRecordSize);
1651 : #endif
1652 11 : nRecordSize += 8;
1653 :
1654 27 : for (int i = 0; i < psObject->nVertices; i++)
1655 : {
1656 16 : ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
1657 : #if defined(SHP_BIG_ENDIAN)
1658 : SHP_SWAP64(pabyRec + nRecordSize);
1659 : #endif
1660 16 : nRecordSize += 8;
1661 : }
1662 : }
1663 :
1664 23 : if (psObject->bMeasureIsUsed &&
1665 6 : (psObject->nSHPType == SHPT_MULTIPOINTZ ||
1666 3 : psObject->nSHPType == SHPT_MULTIPOINTM))
1667 : {
1668 6 : ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
1669 : #if defined(SHP_BIG_ENDIAN)
1670 : SHP_SWAP64(pabyRec + nRecordSize);
1671 : #endif
1672 6 : nRecordSize += 8;
1673 :
1674 6 : ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
1675 : #if defined(SHP_BIG_ENDIAN)
1676 : SHP_SWAP64(pabyRec + nRecordSize);
1677 : #endif
1678 6 : nRecordSize += 8;
1679 :
1680 14 : for (int i = 0; i < psObject->nVertices; i++)
1681 : {
1682 8 : ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
1683 : #if defined(SHP_BIG_ENDIAN)
1684 : SHP_SWAP64(pabyRec + nRecordSize);
1685 : #endif
1686 8 : nRecordSize += 8;
1687 : }
1688 23 : }
1689 : }
1690 :
1691 : /* -------------------------------------------------------------------- */
1692 : /* Write point. */
1693 : /* -------------------------------------------------------------------- */
1694 45375 : else if (psObject->nSHPType == SHPT_POINT ||
1695 44568 : psObject->nSHPType == SHPT_POINTZ ||
1696 629 : psObject->nSHPType == SHPT_POINTM)
1697 : {
1698 44751 : ByteCopy(psObject->padfX, pabyRec + 12, 8);
1699 44751 : ByteCopy(psObject->padfY, pabyRec + 20, 8);
1700 :
1701 : #if defined(SHP_BIG_ENDIAN)
1702 : SHP_SWAP64(pabyRec + 12);
1703 : SHP_SWAP64(pabyRec + 20);
1704 : #endif
1705 :
1706 44751 : nRecordSize = 28;
1707 :
1708 44751 : if (psObject->nSHPType == SHPT_POINTZ)
1709 : {
1710 43939 : ByteCopy(psObject->padfZ, pabyRec + nRecordSize, 8);
1711 : #if defined(SHP_BIG_ENDIAN)
1712 : SHP_SWAP64(pabyRec + nRecordSize);
1713 : #endif
1714 43939 : nRecordSize += 8;
1715 : }
1716 :
1717 44751 : if (psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ ||
1718 5 : psObject->nSHPType == SHPT_POINTM))
1719 : {
1720 10 : ByteCopy(psObject->padfM, pabyRec + nRecordSize, 8);
1721 : #if defined(SHP_BIG_ENDIAN)
1722 : SHP_SWAP64(pabyRec + nRecordSize);
1723 : #endif
1724 10 : nRecordSize += 8;
1725 : }
1726 : }
1727 :
1728 : /* -------------------------------------------------------------------- */
1729 : /* Not much to do for null geometries. */
1730 : /* -------------------------------------------------------------------- */
1731 624 : else if (psObject->nSHPType == SHPT_NULL)
1732 : {
1733 624 : nRecordSize = 12;
1734 : }
1735 : else
1736 : {
1737 : /* unknown type */
1738 0 : assert(false);
1739 : }
1740 :
1741 : /* -------------------------------------------------------------------- */
1742 : /* Establish where we are going to put this record. If we are */
1743 : /* rewriting the last record of the file, then we can update it in */
1744 : /* place. Otherwise if rewriting an existing record, and it will */
1745 : /* fit, then put it back where the original came from. Otherwise */
1746 : /* write at the end. */
1747 : /* -------------------------------------------------------------------- */
1748 : SAOffset nRecordOffset;
1749 64409 : bool bAppendToLastRecord = false;
1750 64409 : bool bAppendToFile = false;
1751 64409 : if (nShapeId != -1 &&
1752 51 : psSHP->panRecOffset[nShapeId] + psSHP->panRecSize[nShapeId] + 8 ==
1753 51 : psSHP->nFileSize)
1754 : {
1755 19 : nRecordOffset = psSHP->panRecOffset[nShapeId];
1756 19 : bAppendToLastRecord = true;
1757 : }
1758 64390 : else if (nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize - 8)
1759 : {
1760 64364 : if (psSHP->nFileSize > UINT_MAX - nRecordSize)
1761 : {
1762 : char str[255];
1763 0 : snprintf(str, sizeof(str),
1764 : "Failed to write shape object. "
1765 : "The maximum file size of %u has been reached. "
1766 : "The current record of size %u cannot be added.",
1767 : psSHP->nFileSize, nRecordSize);
1768 0 : str[sizeof(str) - 1] = '\0';
1769 0 : psSHP->sHooks.Error(str);
1770 0 : free(pabyRec);
1771 0 : return -1;
1772 : }
1773 :
1774 64364 : bAppendToFile = true;
1775 64364 : nRecordOffset = psSHP->nFileSize;
1776 : }
1777 : else
1778 : {
1779 26 : nRecordOffset = psSHP->panRecOffset[nShapeId];
1780 : }
1781 :
1782 : /* -------------------------------------------------------------------- */
1783 : /* Set the shape type, record number, and record size. */
1784 : /* -------------------------------------------------------------------- */
1785 64409 : uint32_t i32 =
1786 64409 : (nShapeId < 0) ? psSHP->nRecords + 1 : nShapeId + 1; /* record # */
1787 : #if !defined(SHP_BIG_ENDIAN)
1788 64409 : SHP_SWAP32(&i32);
1789 : #endif
1790 64409 : ByteCopy(&i32, pabyRec, 4);
1791 :
1792 64409 : i32 = (nRecordSize - 8) / 2; /* record size */
1793 : #if !defined(SHP_BIG_ENDIAN)
1794 64409 : SHP_SWAP32(&i32);
1795 : #endif
1796 64409 : ByteCopy(&i32, pabyRec + 4, 4);
1797 :
1798 64409 : i32 = psObject->nSHPType; /* shape type */
1799 : #if defined(SHP_BIG_ENDIAN)
1800 : SHP_SWAP32(&i32);
1801 : #endif
1802 64409 : ByteCopy(&i32, pabyRec + 8, 4);
1803 :
1804 : /* -------------------------------------------------------------------- */
1805 : /* Write out record. */
1806 : /* -------------------------------------------------------------------- */
1807 :
1808 : /* -------------------------------------------------------------------- */
1809 : /* Guard FSeek with check for whether we're already at position; */
1810 : /* no-op FSeeks defeat network filesystems' write buffering. */
1811 : /* -------------------------------------------------------------------- */
1812 64409 : if (psSHP->sHooks.FTell(psSHP->fpSHP) != nRecordOffset)
1813 : {
1814 73 : if (psSHP->sHooks.FSeek(psSHP->fpSHP, nRecordOffset, 0) != 0)
1815 : {
1816 : char szErrorMsg[200];
1817 :
1818 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
1819 : "Error in psSHP->sHooks.FSeek() while writing object to "
1820 : ".shp file: %s",
1821 0 : strerror(errno));
1822 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1823 0 : psSHP->sHooks.Error(szErrorMsg);
1824 :
1825 0 : free(pabyRec);
1826 0 : return -1;
1827 : }
1828 : }
1829 64409 : if (psSHP->sHooks.FWrite(pabyRec, nRecordSize, 1, psSHP->fpSHP) < 1)
1830 : {
1831 : char szErrorMsg[200];
1832 :
1833 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
1834 : "Error in psSHP->sHooks.FWrite() while writing object of %u "
1835 : "bytes to .shp file: %s",
1836 0 : nRecordSize, strerror(errno));
1837 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1838 0 : psSHP->sHooks.Error(szErrorMsg);
1839 :
1840 0 : free(pabyRec);
1841 0 : return -1;
1842 : }
1843 :
1844 64409 : free(pabyRec);
1845 :
1846 64409 : if (bAppendToLastRecord)
1847 : {
1848 19 : psSHP->nFileSize = psSHP->panRecOffset[nShapeId] + nRecordSize;
1849 : }
1850 64390 : else if (bAppendToFile)
1851 : {
1852 64364 : if (nShapeId == -1)
1853 64358 : nShapeId = psSHP->nRecords++;
1854 :
1855 64364 : psSHP->panRecOffset[nShapeId] = psSHP->nFileSize;
1856 64364 : psSHP->nFileSize += nRecordSize;
1857 : }
1858 64409 : psSHP->panRecSize[nShapeId] = nRecordSize - 8;
1859 :
1860 : /* -------------------------------------------------------------------- */
1861 : /* Expand file wide bounds based on this shape. */
1862 : /* -------------------------------------------------------------------- */
1863 64409 : if (bFirstFeature)
1864 : {
1865 1571 : if (psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0)
1866 : {
1867 563 : psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
1868 563 : psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
1869 563 : psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
1870 563 : psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
1871 : }
1872 : else
1873 : {
1874 1008 : psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
1875 1008 : psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
1876 1008 : psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] =
1877 1008 : psObject->padfZ ? psObject->padfZ[0] : 0.0;
1878 1008 : psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] =
1879 1008 : psObject->padfM ? psObject->padfM[0] : 0.0;
1880 : }
1881 : }
1882 :
1883 319978 : for (int i = 0; i < psObject->nVertices; i++)
1884 : {
1885 255569 : psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0], psObject->padfX[i]);
1886 255569 : psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1], psObject->padfY[i]);
1887 255569 : psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0], psObject->padfX[i]);
1888 255569 : psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1], psObject->padfY[i]);
1889 255569 : if (psObject->padfZ)
1890 : {
1891 253465 : psSHP->adBoundsMin[2] =
1892 253465 : MIN(psSHP->adBoundsMin[2], psObject->padfZ[i]);
1893 253465 : psSHP->adBoundsMax[2] =
1894 253465 : MAX(psSHP->adBoundsMax[2], psObject->padfZ[i]);
1895 : }
1896 255569 : if (psObject->padfM)
1897 : {
1898 253465 : psSHP->adBoundsMin[3] =
1899 253465 : MIN(psSHP->adBoundsMin[3], psObject->padfM[i]);
1900 253465 : psSHP->adBoundsMax[3] =
1901 253465 : MAX(psSHP->adBoundsMax[3], psObject->padfM[i]);
1902 : }
1903 : }
1904 :
1905 64409 : return (nShapeId);
1906 : }
1907 :
1908 : /************************************************************************/
1909 : /* SHPAllocBuffer() */
1910 : /************************************************************************/
1911 :
1912 860878 : static void *SHPAllocBuffer(unsigned char **pBuffer, int nSize)
1913 : {
1914 860878 : if (pBuffer == SHPLIB_NULLPTR)
1915 0 : return calloc(1, nSize);
1916 :
1917 860878 : unsigned char *pRet = *pBuffer;
1918 860878 : if (pRet == SHPLIB_NULLPTR)
1919 0 : return SHPLIB_NULLPTR;
1920 :
1921 860878 : (*pBuffer) += nSize;
1922 860878 : return pRet;
1923 : }
1924 :
1925 : /************************************************************************/
1926 : /* SHPReallocObjectBufIfNecessary() */
1927 : /************************************************************************/
1928 :
1929 143501 : static unsigned char *SHPReallocObjectBufIfNecessary(SHPHandle psSHP,
1930 : int nObjectBufSize)
1931 : {
1932 143501 : if (nObjectBufSize == 0)
1933 : {
1934 1 : nObjectBufSize = 4 * sizeof(double);
1935 : }
1936 :
1937 : unsigned char *pBuffer;
1938 143501 : if (nObjectBufSize > psSHP->nObjectBufSize)
1939 : {
1940 2257 : pBuffer = STATIC_CAST(unsigned char *,
1941 : realloc(psSHP->pabyObjectBuf, nObjectBufSize));
1942 2257 : if (pBuffer != SHPLIB_NULLPTR)
1943 : {
1944 2257 : psSHP->pabyObjectBuf = pBuffer;
1945 2257 : psSHP->nObjectBufSize = nObjectBufSize;
1946 : }
1947 : }
1948 : else
1949 : {
1950 141244 : pBuffer = psSHP->pabyObjectBuf;
1951 : }
1952 :
1953 143501 : return pBuffer;
1954 : }
1955 :
1956 : /************************************************************************/
1957 : /* SHPReadObject() */
1958 : /* */
1959 : /* Read the vertices, parts, and other non-attribute information */
1960 : /* for one shape. */
1961 : /************************************************************************/
1962 :
1963 240930 : SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle psSHP, int hEntity)
1964 : {
1965 : /* -------------------------------------------------------------------- */
1966 : /* Validate the record/entity number. */
1967 : /* -------------------------------------------------------------------- */
1968 240930 : if (hEntity < 0 || hEntity >= psSHP->nRecords)
1969 0 : return SHPLIB_NULLPTR;
1970 :
1971 : /* -------------------------------------------------------------------- */
1972 : /* Read offset/length from SHX loading if necessary. */
1973 : /* -------------------------------------------------------------------- */
1974 240930 : if (psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != SHPLIB_NULLPTR)
1975 : {
1976 : unsigned int nOffset;
1977 : unsigned int nLength;
1978 :
1979 0 : if (psSHP->sHooks.FSeek(psSHP->fpSHX, 100 + 8 * hEntity, 0) != 0 ||
1980 0 : psSHP->sHooks.FRead(&nOffset, 1, 4, psSHP->fpSHX) != 4 ||
1981 0 : psSHP->sHooks.FRead(&nLength, 1, 4, psSHP->fpSHX) != 4)
1982 : {
1983 : char str[128];
1984 0 : snprintf(str, sizeof(str),
1985 : "Error in fseek()/fread() reading object from .shx file "
1986 : "at offset %d",
1987 0 : 100 + 8 * hEntity);
1988 0 : str[sizeof(str) - 1] = '\0';
1989 :
1990 0 : psSHP->sHooks.Error(str);
1991 0 : return SHPLIB_NULLPTR;
1992 : }
1993 : #if !defined(SHP_BIG_ENDIAN)
1994 0 : SHP_SWAP32(&nOffset);
1995 0 : SHP_SWAP32(&nLength);
1996 : #endif
1997 :
1998 0 : if (nOffset > STATIC_CAST(unsigned int, INT_MAX))
1999 : {
2000 : char str[128];
2001 0 : snprintf(str, sizeof(str), "Invalid offset for entity %d", hEntity);
2002 0 : str[sizeof(str) - 1] = '\0';
2003 :
2004 0 : psSHP->sHooks.Error(str);
2005 0 : return SHPLIB_NULLPTR;
2006 : }
2007 0 : if (nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4))
2008 : {
2009 : char str[128];
2010 0 : snprintf(str, sizeof(str), "Invalid length for entity %d", hEntity);
2011 0 : str[sizeof(str) - 1] = '\0';
2012 :
2013 0 : psSHP->sHooks.Error(str);
2014 0 : return SHPLIB_NULLPTR;
2015 : }
2016 :
2017 0 : psSHP->panRecOffset[hEntity] = nOffset * 2;
2018 0 : psSHP->panRecSize[hEntity] = nLength * 2;
2019 : }
2020 :
2021 : /* -------------------------------------------------------------------- */
2022 : /* Ensure our record buffer is large enough. */
2023 : /* -------------------------------------------------------------------- */
2024 240930 : const int nEntitySize = psSHP->panRecSize[hEntity] + 8;
2025 240930 : if (nEntitySize > psSHP->nBufSize)
2026 : {
2027 3120 : int nNewBufSize = nEntitySize;
2028 3120 : if (nNewBufSize < INT_MAX - nNewBufSize / 3)
2029 3120 : nNewBufSize += nNewBufSize / 3;
2030 : else
2031 0 : nNewBufSize = INT_MAX;
2032 :
2033 : /* Before allocating too much memory, check that the file is big enough */
2034 : /* and do not trust the file size in the header the first time we */
2035 : /* need to allocate more than 10 MB */
2036 3120 : if (nNewBufSize >= 10 * 1024 * 1024)
2037 : {
2038 2 : if (psSHP->nBufSize < 10 * 1024 * 1024)
2039 : {
2040 : SAOffset nFileSize;
2041 2 : psSHP->sHooks.FSeek(psSHP->fpSHP, 0, 2);
2042 2 : nFileSize = psSHP->sHooks.FTell(psSHP->fpSHP);
2043 2 : if (nFileSize >= UINT_MAX)
2044 0 : psSHP->nFileSize = UINT_MAX;
2045 : else
2046 2 : psSHP->nFileSize = STATIC_CAST(unsigned int, nFileSize);
2047 : }
2048 :
2049 2 : if (psSHP->panRecOffset[hEntity] >= psSHP->nFileSize ||
2050 : /* We should normally use nEntitySize instead of*/
2051 : /* psSHP->panRecSize[hEntity] in the below test, but because of */
2052 : /* the case of non conformant .shx files detailed a bit below, */
2053 : /* let be more tolerant */
2054 2 : psSHP->panRecSize[hEntity] >
2055 2 : psSHP->nFileSize - psSHP->panRecOffset[hEntity])
2056 : {
2057 : char str[128];
2058 1 : snprintf(str, sizeof(str),
2059 : "Error in fread() reading object of size %d at offset "
2060 : "%u from .shp file",
2061 1 : nEntitySize, psSHP->panRecOffset[hEntity]);
2062 1 : str[sizeof(str) - 1] = '\0';
2063 :
2064 1 : psSHP->sHooks.Error(str);
2065 1 : return SHPLIB_NULLPTR;
2066 : }
2067 : }
2068 :
2069 : unsigned char *pabyRecNew =
2070 3119 : STATIC_CAST(unsigned char *, realloc(psSHP->pabyRec, nNewBufSize));
2071 3119 : if (pabyRecNew == SHPLIB_NULLPTR)
2072 : {
2073 : char szErrorMsg[160];
2074 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2075 : "Not enough memory to allocate requested memory "
2076 : "(nNewBufSize=%d). "
2077 : "Probably broken SHP file",
2078 : nNewBufSize);
2079 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2080 0 : psSHP->sHooks.Error(szErrorMsg);
2081 0 : return SHPLIB_NULLPTR;
2082 : }
2083 :
2084 : /* Only set new buffer size after successful alloc */
2085 3119 : psSHP->pabyRec = pabyRecNew;
2086 3119 : psSHP->nBufSize = nNewBufSize;
2087 : }
2088 :
2089 : /* In case we were not able to reallocate the buffer on a previous step */
2090 240929 : if (psSHP->pabyRec == SHPLIB_NULLPTR)
2091 : {
2092 0 : return SHPLIB_NULLPTR;
2093 : }
2094 :
2095 : /* -------------------------------------------------------------------- */
2096 : /* Read the record. */
2097 : /* -------------------------------------------------------------------- */
2098 240929 : if (psSHP->sHooks.FSeek(psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0) != 0)
2099 : {
2100 : /*
2101 : * TODO - mloskot: Consider detailed diagnostics of shape file,
2102 : * for example to detect if file is truncated.
2103 : */
2104 : char str[128];
2105 0 : snprintf(str, sizeof(str),
2106 : "Error in fseek() reading object from .shp file at offset %u",
2107 0 : psSHP->panRecOffset[hEntity]);
2108 0 : str[sizeof(str) - 1] = '\0';
2109 :
2110 0 : psSHP->sHooks.Error(str);
2111 0 : return SHPLIB_NULLPTR;
2112 : }
2113 :
2114 240929 : const int nBytesRead = STATIC_CAST(
2115 : int, psSHP->sHooks.FRead(psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP));
2116 :
2117 : /* Special case for a shapefile whose .shx content length field is not equal */
2118 : /* to the content length field of the .shp, which is a violation of "The */
2119 : /* content length stored in the index record is the same as the value stored in the main */
2120 : /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */
2121 : /* Actually in that case the .shx content length is equal to the .shp content length + */
2122 : /* 4 (16 bit words), representing the 8 bytes of the record header... */
2123 240929 : if (nBytesRead >= 8 && nBytesRead == nEntitySize - 8)
2124 : {
2125 : /* Do a sanity check */
2126 : int nSHPContentLength;
2127 1 : memcpy(&nSHPContentLength, psSHP->pabyRec + 4, 4);
2128 : #if !defined(SHP_BIG_ENDIAN)
2129 1 : SHP_SWAP32(&(nSHPContentLength));
2130 : #endif
2131 1 : if (nSHPContentLength < 0 || nSHPContentLength > INT_MAX / 2 - 4 ||
2132 1 : 2 * nSHPContentLength + 8 != nBytesRead)
2133 : {
2134 : char str[128];
2135 0 : snprintf(str, sizeof(str),
2136 : "Sanity check failed when trying to recover from "
2137 : "inconsistent .shx/.shp with shape %d",
2138 : hEntity);
2139 0 : str[sizeof(str) - 1] = '\0';
2140 :
2141 0 : psSHP->sHooks.Error(str);
2142 0 : return SHPLIB_NULLPTR;
2143 1 : }
2144 : }
2145 240928 : else if (nBytesRead != nEntitySize)
2146 : {
2147 : /*
2148 : * TODO - mloskot: Consider detailed diagnostics of shape file,
2149 : * for example to detect if file is truncated.
2150 : */
2151 : char str[128];
2152 0 : snprintf(str, sizeof(str),
2153 : "Error in fread() reading object of size %d at offset %u from "
2154 : ".shp file",
2155 0 : nEntitySize, psSHP->panRecOffset[hEntity]);
2156 0 : str[sizeof(str) - 1] = '\0';
2157 :
2158 0 : psSHP->sHooks.Error(str);
2159 0 : return SHPLIB_NULLPTR;
2160 : }
2161 :
2162 240929 : if (8 + 4 > nEntitySize)
2163 : {
2164 : char szErrorMsg[160];
2165 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2166 : "Corrupted .shp file : shape %d : nEntitySize = %d", hEntity,
2167 : nEntitySize);
2168 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2169 0 : psSHP->sHooks.Error(szErrorMsg);
2170 0 : return SHPLIB_NULLPTR;
2171 : }
2172 : int nSHPType;
2173 240929 : memcpy(&nSHPType, psSHP->pabyRec + 8, 4);
2174 :
2175 : #if defined(SHP_BIG_ENDIAN)
2176 : SHP_SWAP32(&(nSHPType));
2177 : #endif
2178 :
2179 : /* -------------------------------------------------------------------- */
2180 : /* Allocate and minimally initialize the object. */
2181 : /* -------------------------------------------------------------------- */
2182 : SHPObject *psShape;
2183 240929 : if (psSHP->bFastModeReadObject)
2184 : {
2185 240929 : if (psSHP->psCachedObject->bFastModeReadObject)
2186 : {
2187 0 : psSHP->sHooks.Error("Invalid read pattern in fast read mode. "
2188 : "SHPDestroyObject() should be called.");
2189 0 : return SHPLIB_NULLPTR;
2190 : }
2191 :
2192 240929 : psShape = psSHP->psCachedObject;
2193 240929 : memset(psShape, 0, sizeof(SHPObject));
2194 : }
2195 : else
2196 : {
2197 0 : psShape = STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject)));
2198 0 : if (!psShape)
2199 : {
2200 0 : psSHP->sHooks.Error("Out of memory.");
2201 0 : return SHPLIB_NULLPTR;
2202 : }
2203 : }
2204 240929 : psShape->nShapeId = hEntity;
2205 240929 : psShape->nSHPType = nSHPType;
2206 240929 : psShape->bMeasureIsUsed = FALSE;
2207 240929 : psShape->bFastModeReadObject = psSHP->bFastModeReadObject;
2208 :
2209 : /* ==================================================================== */
2210 : /* Extract vertices for a Polygon or Arc. */
2211 : /* ==================================================================== */
2212 240929 : if (psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC ||
2213 99551 : psShape->nSHPType == SHPT_POLYGONZ ||
2214 99480 : psShape->nSHPType == SHPT_POLYGONM || psShape->nSHPType == SHPT_ARCZ ||
2215 97515 : psShape->nSHPType == SHPT_ARCM || psShape->nSHPType == SHPT_MULTIPATCH)
2216 : {
2217 143443 : if (40 + 8 + 4 > nEntitySize)
2218 : {
2219 : char szErrorMsg[160];
2220 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2221 : "Corrupted .shp file : shape %d : nEntitySize = %d",
2222 : hEntity, nEntitySize);
2223 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2224 0 : psSHP->sHooks.Error(szErrorMsg);
2225 0 : SHPDestroyObject(psShape);
2226 0 : return SHPLIB_NULLPTR;
2227 : }
2228 : /* -------------------------------------------------------------------- */
2229 : /* Get the X/Y bounds. */
2230 : /* -------------------------------------------------------------------- */
2231 : #if defined(SHP_BIG_ENDIAN)
2232 : SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4);
2233 : SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12);
2234 : SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20);
2235 : SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28);
2236 : #else
2237 143443 : memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8);
2238 143443 : memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8);
2239 143443 : memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8);
2240 143443 : memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8);
2241 : #endif
2242 :
2243 : /* -------------------------------------------------------------------- */
2244 : /* Extract part/point count, and build vertex and part arrays */
2245 : /* to proper size. */
2246 : /* -------------------------------------------------------------------- */
2247 : uint32_t nPoints;
2248 143443 : memcpy(&nPoints, psSHP->pabyRec + 40 + 8, 4);
2249 : uint32_t nParts;
2250 143443 : memcpy(&nParts, psSHP->pabyRec + 36 + 8, 4);
2251 :
2252 : #if defined(SHP_BIG_ENDIAN)
2253 : SHP_SWAP32(&nPoints);
2254 : SHP_SWAP32(&nParts);
2255 : #endif
2256 :
2257 : /* nPoints and nParts are unsigned */
2258 143443 : if (/* nPoints < 0 || nParts < 0 || */
2259 143443 : nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
2260 : {
2261 : char szErrorMsg[160];
2262 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2263 : "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u.",
2264 : hEntity, nPoints, nParts);
2265 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2266 0 : psSHP->sHooks.Error(szErrorMsg);
2267 0 : SHPDestroyObject(psShape);
2268 0 : return SHPLIB_NULLPTR;
2269 : }
2270 :
2271 : /* With the previous checks on nPoints and nParts, */
2272 : /* we should not overflow here and after */
2273 : /* since 50 M * (16 + 8 + 8) = 1 600 MB */
2274 143443 : int nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
2275 143443 : if (psShape->nSHPType == SHPT_POLYGONZ ||
2276 143372 : psShape->nSHPType == SHPT_ARCZ ||
2277 141424 : psShape->nSHPType == SHPT_MULTIPATCH)
2278 : {
2279 2031 : nRequiredSize += 16 + 8 * nPoints;
2280 : }
2281 143443 : if (psShape->nSHPType == SHPT_MULTIPATCH)
2282 : {
2283 12 : nRequiredSize += 4 * nParts;
2284 : }
2285 143443 : if (nRequiredSize > nEntitySize)
2286 : {
2287 : char szErrorMsg[160];
2288 6 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2289 : "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u, "
2290 : "nEntitySize=%d.",
2291 : hEntity, nPoints, nParts, nEntitySize);
2292 6 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2293 6 : psSHP->sHooks.Error(szErrorMsg);
2294 6 : SHPDestroyObject(psShape);
2295 6 : return SHPLIB_NULLPTR;
2296 : }
2297 :
2298 143437 : unsigned char *pBuffer = SHPLIB_NULLPTR;
2299 143437 : unsigned char **ppBuffer = SHPLIB_NULLPTR;
2300 :
2301 143437 : if (psShape->bFastModeReadObject)
2302 : {
2303 143437 : const int nObjectBufSize =
2304 143437 : 4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts;
2305 143437 : pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize);
2306 143437 : ppBuffer = &pBuffer;
2307 : }
2308 :
2309 143437 : psShape->nVertices = nPoints;
2310 143437 : psShape->padfX = STATIC_CAST(
2311 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2312 143437 : psShape->padfY = STATIC_CAST(
2313 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2314 143437 : psShape->padfZ = STATIC_CAST(
2315 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2316 143437 : psShape->padfM = STATIC_CAST(
2317 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2318 :
2319 143437 : psShape->nParts = nParts;
2320 143437 : psShape->panPartStart =
2321 143437 : STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int)));
2322 143437 : psShape->panPartType =
2323 143437 : STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int)));
2324 :
2325 143437 : if (psShape->padfX == SHPLIB_NULLPTR ||
2326 143437 : psShape->padfY == SHPLIB_NULLPTR ||
2327 143437 : psShape->padfZ == SHPLIB_NULLPTR ||
2328 143437 : psShape->padfM == SHPLIB_NULLPTR ||
2329 143437 : psShape->panPartStart == SHPLIB_NULLPTR ||
2330 143437 : psShape->panPartType == SHPLIB_NULLPTR)
2331 : {
2332 : char szErrorMsg[160];
2333 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2334 : "Not enough memory to allocate requested memory "
2335 : "(nPoints=%u, nParts=%u) for shape %d. "
2336 : "Probably broken SHP file",
2337 : nPoints, nParts, hEntity);
2338 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2339 0 : psSHP->sHooks.Error(szErrorMsg);
2340 0 : SHPDestroyObject(psShape);
2341 0 : return SHPLIB_NULLPTR;
2342 : }
2343 :
2344 287499 : for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
2345 144062 : psShape->panPartType[i] = SHPP_RING;
2346 :
2347 : /* -------------------------------------------------------------------- */
2348 : /* Copy out the part array from the record. */
2349 : /* -------------------------------------------------------------------- */
2350 143437 : memcpy(psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts);
2351 287493 : for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
2352 : {
2353 : #if defined(SHP_BIG_ENDIAN)
2354 : SHP_SWAP32(psShape->panPartStart + i);
2355 : #endif
2356 :
2357 : /* We check that the offset is inside the vertex array */
2358 144059 : if (psShape->panPartStart[i] < 0 ||
2359 144059 : (psShape->panPartStart[i] >= psShape->nVertices &&
2360 0 : psShape->nVertices > 0) ||
2361 144059 : (psShape->panPartStart[i] > 0 && psShape->nVertices == 0))
2362 : {
2363 : char szErrorMsg[160];
2364 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2365 : "Corrupted .shp file : shape %d : panPartStart[%d] = "
2366 : "%d, nVertices = %d",
2367 0 : hEntity, i, psShape->panPartStart[i],
2368 : psShape->nVertices);
2369 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2370 0 : psSHP->sHooks.Error(szErrorMsg);
2371 0 : SHPDestroyObject(psShape);
2372 0 : return SHPLIB_NULLPTR;
2373 : }
2374 144059 : if (i > 0 &&
2375 624 : psShape->panPartStart[i] <= psShape->panPartStart[i - 1])
2376 : {
2377 : char szErrorMsg[160];
2378 3 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2379 : "Corrupted .shp file : shape %d : panPartStart[%d] = "
2380 : "%d, panPartStart[%d] = %d",
2381 3 : hEntity, i, psShape->panPartStart[i], i - 1,
2382 3 : psShape->panPartStart[i - 1]);
2383 3 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2384 3 : psSHP->sHooks.Error(szErrorMsg);
2385 3 : SHPDestroyObject(psShape);
2386 3 : return SHPLIB_NULLPTR;
2387 : }
2388 : }
2389 :
2390 143434 : int nOffset = 44 + 8 + 4 * nParts;
2391 :
2392 : /* -------------------------------------------------------------------- */
2393 : /* If this is a multipatch, we will also have parts types. */
2394 : /* -------------------------------------------------------------------- */
2395 143434 : if (psShape->nSHPType == SHPT_MULTIPATCH)
2396 : {
2397 12 : memcpy(psShape->panPartType, psSHP->pabyRec + nOffset, 4 * nParts);
2398 31 : for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
2399 : {
2400 : #if defined(SHP_BIG_ENDIAN)
2401 : SHP_SWAP32(psShape->panPartType + i);
2402 : #endif
2403 : }
2404 :
2405 12 : nOffset += 4 * nParts;
2406 : }
2407 :
2408 : /* -------------------------------------------------------------------- */
2409 : /* Copy out the vertices from the record. */
2410 : /* -------------------------------------------------------------------- */
2411 1173110 : for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2412 : {
2413 : #if defined(SHP_BIG_ENDIAN)
2414 : SHP_SWAPDOUBLE_CPY(psShape->padfX + i,
2415 : psSHP->pabyRec + nOffset + i * 16);
2416 : SHP_SWAPDOUBLE_CPY(psShape->padfY + i,
2417 : psSHP->pabyRec + nOffset + i * 16 + 8);
2418 : #else
2419 1029680 : memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8);
2420 1029680 : memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8,
2421 : 8);
2422 : #endif
2423 : }
2424 :
2425 143434 : nOffset += 16 * nPoints;
2426 :
2427 : /* -------------------------------------------------------------------- */
2428 : /* If we have a Z coordinate, collect that now. */
2429 : /* -------------------------------------------------------------------- */
2430 143434 : if (psShape->nSHPType == SHPT_POLYGONZ ||
2431 143363 : psShape->nSHPType == SHPT_ARCZ ||
2432 141415 : psShape->nSHPType == SHPT_MULTIPATCH)
2433 : {
2434 : #if defined(SHP_BIG_ENDIAN)
2435 : SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset);
2436 : SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8);
2437 : #else
2438 2031 : memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8);
2439 2031 : memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8);
2440 :
2441 : #endif
2442 :
2443 54301 : for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2444 : {
2445 : #if defined(SHP_BIG_ENDIAN)
2446 : SHP_SWAPDOUBLE_CPY(psShape->padfZ + i,
2447 : psSHP->pabyRec + nOffset + 16 + i * 8);
2448 : #else
2449 52270 : memcpy(psShape->padfZ + i,
2450 52270 : psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2451 : #endif
2452 : }
2453 :
2454 2031 : nOffset += 16 + 8 * nPoints;
2455 : }
2456 141403 : else if (psShape->bFastModeReadObject)
2457 : {
2458 141403 : psShape->padfZ = SHPLIB_NULLPTR;
2459 : }
2460 :
2461 : /* -------------------------------------------------------------------- */
2462 : /* If we have a M measure value, then read it now. We assume */
2463 : /* that the measure can be present for any shape if the size is */
2464 : /* big enough, but really it will only occur for the Z shapes */
2465 : /* (options), and the M shapes. */
2466 : /* -------------------------------------------------------------------- */
2467 143434 : if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints))
2468 : {
2469 : #if defined(SHP_BIG_ENDIAN)
2470 : SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset);
2471 : SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8);
2472 : #else
2473 45 : memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8);
2474 45 : memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8);
2475 : #endif
2476 :
2477 233 : for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2478 : {
2479 : #if defined(SHP_BIG_ENDIAN)
2480 : SHP_SWAPDOUBLE_CPY(psShape->padfM + i,
2481 : psSHP->pabyRec + nOffset + 16 + i * 8);
2482 : #else
2483 188 : memcpy(psShape->padfM + i,
2484 188 : psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2485 : #endif
2486 : }
2487 45 : psShape->bMeasureIsUsed = TRUE;
2488 : }
2489 143389 : else if (psShape->bFastModeReadObject)
2490 : {
2491 143389 : psShape->padfM = SHPLIB_NULLPTR;
2492 143434 : }
2493 : }
2494 :
2495 : /* ==================================================================== */
2496 : /* Extract vertices for a MultiPoint. */
2497 : /* ==================================================================== */
2498 97486 : else if (psShape->nSHPType == SHPT_MULTIPOINT ||
2499 97471 : psShape->nSHPType == SHPT_MULTIPOINTM ||
2500 97466 : psShape->nSHPType == SHPT_MULTIPOINTZ)
2501 : {
2502 67 : if (44 + 4 > nEntitySize)
2503 : {
2504 : char szErrorMsg[160];
2505 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2506 : "Corrupted .shp file : shape %d : nEntitySize = %d",
2507 : hEntity, nEntitySize);
2508 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2509 0 : psSHP->sHooks.Error(szErrorMsg);
2510 0 : SHPDestroyObject(psShape);
2511 0 : return SHPLIB_NULLPTR;
2512 : }
2513 : uint32_t nPoints;
2514 67 : memcpy(&nPoints, psSHP->pabyRec + 44, 4);
2515 :
2516 : #if defined(SHP_BIG_ENDIAN)
2517 : SHP_SWAP32(&nPoints);
2518 : #endif
2519 :
2520 : /* nPoints is unsigned */
2521 67 : if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000)
2522 : {
2523 : char szErrorMsg[160];
2524 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2525 : "Corrupted .shp file : shape %d : nPoints = %u", hEntity,
2526 : nPoints);
2527 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2528 0 : psSHP->sHooks.Error(szErrorMsg);
2529 0 : SHPDestroyObject(psShape);
2530 0 : return SHPLIB_NULLPTR;
2531 : }
2532 :
2533 67 : int nRequiredSize = 48 + nPoints * 16;
2534 67 : if (psShape->nSHPType == SHPT_MULTIPOINTZ)
2535 : {
2536 47 : nRequiredSize += 16 + nPoints * 8;
2537 : }
2538 67 : if (nRequiredSize > nEntitySize)
2539 : {
2540 : char szErrorMsg[160];
2541 3 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2542 : "Corrupted .shp file : shape %d : nPoints = %u, "
2543 : "nEntitySize = %d",
2544 : hEntity, nPoints, nEntitySize);
2545 3 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2546 3 : psSHP->sHooks.Error(szErrorMsg);
2547 3 : SHPDestroyObject(psShape);
2548 3 : return SHPLIB_NULLPTR;
2549 : }
2550 :
2551 64 : unsigned char *pBuffer = SHPLIB_NULLPTR;
2552 64 : unsigned char **ppBuffer = SHPLIB_NULLPTR;
2553 :
2554 64 : if (psShape->bFastModeReadObject)
2555 : {
2556 64 : const int nObjectBufSize = 4 * sizeof(double) * nPoints;
2557 64 : pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize);
2558 64 : ppBuffer = &pBuffer;
2559 : }
2560 :
2561 64 : psShape->nVertices = nPoints;
2562 :
2563 64 : psShape->padfX = STATIC_CAST(
2564 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2565 64 : psShape->padfY = STATIC_CAST(
2566 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2567 64 : psShape->padfZ = STATIC_CAST(
2568 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2569 64 : psShape->padfM = STATIC_CAST(
2570 : double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2571 :
2572 64 : if (psShape->padfX == SHPLIB_NULLPTR ||
2573 64 : psShape->padfY == SHPLIB_NULLPTR ||
2574 64 : psShape->padfZ == SHPLIB_NULLPTR ||
2575 64 : psShape->padfM == SHPLIB_NULLPTR)
2576 : {
2577 : char szErrorMsg[160];
2578 0 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2579 : "Not enough memory to allocate requested memory "
2580 : "(nPoints=%u) for shape %d. "
2581 : "Probably broken SHP file",
2582 : nPoints, hEntity);
2583 0 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2584 0 : psSHP->sHooks.Error(szErrorMsg);
2585 0 : SHPDestroyObject(psShape);
2586 0 : return SHPLIB_NULLPTR;
2587 : }
2588 :
2589 175 : for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2590 : {
2591 : #if defined(SHP_BIG_ENDIAN)
2592 : SHP_SWAPDOUBLE_CPY(psShape->padfX + i,
2593 : psSHP->pabyRec + 48 + 16 * i);
2594 : SHP_SWAPDOUBLE_CPY(psShape->padfY + i,
2595 : psSHP->pabyRec + 48 + 16 * i + 8);
2596 : #else
2597 111 : memcpy(psShape->padfX + i, psSHP->pabyRec + 48 + 16 * i, 8);
2598 111 : memcpy(psShape->padfY + i, psSHP->pabyRec + 48 + 16 * i + 8, 8);
2599 : #endif
2600 : }
2601 :
2602 64 : int nOffset = 48 + 16 * nPoints;
2603 :
2604 : /* -------------------------------------------------------------------- */
2605 : /* Get the X/Y bounds. */
2606 : /* -------------------------------------------------------------------- */
2607 : #if defined(SHP_BIG_ENDIAN)
2608 : SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4);
2609 : SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12);
2610 : SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20);
2611 : SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28);
2612 : #else
2613 64 : memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8);
2614 64 : memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8);
2615 64 : memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8);
2616 64 : memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8);
2617 : #endif
2618 :
2619 : /* -------------------------------------------------------------------- */
2620 : /* If we have a Z coordinate, collect that now. */
2621 : /* -------------------------------------------------------------------- */
2622 64 : if (psShape->nSHPType == SHPT_MULTIPOINTZ)
2623 : {
2624 : #if defined(SHP_BIG_ENDIAN)
2625 : SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset);
2626 : SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8);
2627 : #else
2628 47 : memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8);
2629 47 : memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8);
2630 : #endif
2631 :
2632 133 : for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2633 : {
2634 : #if defined(SHP_BIG_ENDIAN)
2635 : SHP_SWAPDOUBLE_CPY(psShape->padfZ + i,
2636 : psSHP->pabyRec + nOffset + 16 + i * 8);
2637 : #else
2638 86 : memcpy(psShape->padfZ + i,
2639 86 : psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2640 : #endif
2641 : }
2642 :
2643 47 : nOffset += 16 + 8 * nPoints;
2644 : }
2645 17 : else if (psShape->bFastModeReadObject)
2646 17 : psShape->padfZ = SHPLIB_NULLPTR;
2647 :
2648 : /* -------------------------------------------------------------------- */
2649 : /* If we have a M measure value, then read it now. We assume */
2650 : /* that the measure can be present for any shape if the size is */
2651 : /* big enough, but really it will only occur for the Z shapes */
2652 : /* (options), and the M shapes. */
2653 : /* -------------------------------------------------------------------- */
2654 64 : if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints))
2655 : {
2656 : #if defined(SHP_BIG_ENDIAN)
2657 : SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset);
2658 : SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8);
2659 : #else
2660 11 : memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8);
2661 11 : memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8);
2662 : #endif
2663 :
2664 23 : for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2665 : {
2666 : #if defined(SHP_BIG_ENDIAN)
2667 : SHP_SWAPDOUBLE_CPY(psShape->padfM + i,
2668 : psSHP->pabyRec + nOffset + 16 + i * 8);
2669 : #else
2670 12 : memcpy(psShape->padfM + i,
2671 12 : psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2672 : #endif
2673 : }
2674 11 : psShape->bMeasureIsUsed = TRUE;
2675 : }
2676 53 : else if (psShape->bFastModeReadObject)
2677 64 : psShape->padfM = SHPLIB_NULLPTR;
2678 : }
2679 :
2680 : /* ==================================================================== */
2681 : /* Extract vertices for a point. */
2682 : /* ==================================================================== */
2683 97419 : else if (psShape->nSHPType == SHPT_POINT ||
2684 88653 : psShape->nSHPType == SHPT_POINTM ||
2685 88641 : psShape->nSHPType == SHPT_POINTZ)
2686 : {
2687 96707 : psShape->nVertices = 1;
2688 96707 : if (psShape->bFastModeReadObject)
2689 : {
2690 96707 : psShape->padfX = &(psShape->dfXMin);
2691 96707 : psShape->padfY = &(psShape->dfYMin);
2692 96707 : psShape->padfZ = &(psShape->dfZMin);
2693 96707 : psShape->padfM = &(psShape->dfMMin);
2694 96707 : psShape->padfZ[0] = 0.0;
2695 96707 : psShape->padfM[0] = 0.0;
2696 : }
2697 : else
2698 : {
2699 0 : psShape->padfX = STATIC_CAST(double *, calloc(1, sizeof(double)));
2700 0 : psShape->padfY = STATIC_CAST(double *, calloc(1, sizeof(double)));
2701 0 : psShape->padfZ = STATIC_CAST(double *, calloc(1, sizeof(double)));
2702 0 : psShape->padfM = STATIC_CAST(double *, calloc(1, sizeof(double)));
2703 : }
2704 :
2705 96707 : if (20 + 8 + ((psShape->nSHPType == SHPT_POINTZ) ? 8 : 0) > nEntitySize)
2706 : {
2707 : char szErrorMsg[160];
2708 3 : snprintf(szErrorMsg, sizeof(szErrorMsg),
2709 : "Corrupted .shp file : shape %d : nEntitySize = %d",
2710 : hEntity, nEntitySize);
2711 3 : szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2712 3 : psSHP->sHooks.Error(szErrorMsg);
2713 3 : SHPDestroyObject(psShape);
2714 3 : return SHPLIB_NULLPTR;
2715 : }
2716 : #if defined(SHP_BIG_ENDIAN)
2717 : SHP_SWAPDOUBLE_CPY(psShape->padfX, psSHP->pabyRec + 12);
2718 : SHP_SWAPDOUBLE_CPY(psShape->padfY, psSHP->pabyRec + 20);
2719 : #else
2720 96704 : memcpy(psShape->padfX, psSHP->pabyRec + 12, 8);
2721 96704 : memcpy(psShape->padfY, psSHP->pabyRec + 20, 8);
2722 : #endif
2723 :
2724 96704 : int nOffset = 20 + 8;
2725 :
2726 : /* -------------------------------------------------------------------- */
2727 : /* If we have a Z coordinate, collect that now. */
2728 : /* -------------------------------------------------------------------- */
2729 96704 : if (psShape->nSHPType == SHPT_POINTZ)
2730 : {
2731 : #if defined(SHP_BIG_ENDIAN)
2732 : SHP_SWAPDOUBLE_CPY(psShape->padfZ, psSHP->pabyRec + nOffset);
2733 : #else
2734 87929 : memcpy(psShape->padfZ, psSHP->pabyRec + nOffset, 8);
2735 : #endif
2736 :
2737 87929 : nOffset += 8;
2738 : }
2739 :
2740 : /* -------------------------------------------------------------------- */
2741 : /* If we have a M measure value, then read it now. We assume */
2742 : /* that the measure can be present for any shape if the size is */
2743 : /* big enough, but really it will only occur for the Z shapes */
2744 : /* (options), and the M shapes. */
2745 : /* -------------------------------------------------------------------- */
2746 96704 : if (nEntitySize >= nOffset + 8)
2747 : {
2748 : #if defined(SHP_BIG_ENDIAN)
2749 : SHP_SWAPDOUBLE_CPY(psShape->padfM, psSHP->pabyRec + nOffset);
2750 : #else
2751 33 : memcpy(psShape->padfM, psSHP->pabyRec + nOffset, 8);
2752 : #endif
2753 33 : psShape->bMeasureIsUsed = TRUE;
2754 : }
2755 :
2756 : /* -------------------------------------------------------------------- */
2757 : /* Since no extents are supplied in the record, we will apply */
2758 : /* them from the single vertex. */
2759 : /* -------------------------------------------------------------------- */
2760 96704 : psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
2761 96704 : psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
2762 96704 : psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
2763 96704 : psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
2764 : }
2765 :
2766 240914 : return (psShape);
2767 : }
2768 :
2769 : /************************************************************************/
2770 : /* SHPTypeName() */
2771 : /************************************************************************/
2772 :
2773 0 : const char SHPAPI_CALL1(*) SHPTypeName(int nSHPType)
2774 : {
2775 0 : switch (nSHPType)
2776 : {
2777 0 : case SHPT_NULL:
2778 0 : return "NullShape";
2779 :
2780 0 : case SHPT_POINT:
2781 0 : return "Point";
2782 :
2783 0 : case SHPT_ARC:
2784 0 : return "Arc";
2785 :
2786 0 : case SHPT_POLYGON:
2787 0 : return "Polygon";
2788 :
2789 0 : case SHPT_MULTIPOINT:
2790 0 : return "MultiPoint";
2791 :
2792 0 : case SHPT_POINTZ:
2793 0 : return "PointZ";
2794 :
2795 0 : case SHPT_ARCZ:
2796 0 : return "ArcZ";
2797 :
2798 0 : case SHPT_POLYGONZ:
2799 0 : return "PolygonZ";
2800 :
2801 0 : case SHPT_MULTIPOINTZ:
2802 0 : return "MultiPointZ";
2803 :
2804 0 : case SHPT_POINTM:
2805 0 : return "PointM";
2806 :
2807 0 : case SHPT_ARCM:
2808 0 : return "ArcM";
2809 :
2810 0 : case SHPT_POLYGONM:
2811 0 : return "PolygonM";
2812 :
2813 0 : case SHPT_MULTIPOINTM:
2814 0 : return "MultiPointM";
2815 :
2816 0 : case SHPT_MULTIPATCH:
2817 0 : return "MultiPatch";
2818 :
2819 0 : default:
2820 0 : return "UnknownShapeType";
2821 : }
2822 : }
2823 :
2824 : /************************************************************************/
2825 : /* SHPPartTypeName() */
2826 : /************************************************************************/
2827 :
2828 0 : const char SHPAPI_CALL1(*) SHPPartTypeName(int nPartType)
2829 : {
2830 0 : switch (nPartType)
2831 : {
2832 0 : case SHPP_TRISTRIP:
2833 0 : return "TriangleStrip";
2834 :
2835 0 : case SHPP_TRIFAN:
2836 0 : return "TriangleFan";
2837 :
2838 0 : case SHPP_OUTERRING:
2839 0 : return "OuterRing";
2840 :
2841 0 : case SHPP_INNERRING:
2842 0 : return "InnerRing";
2843 :
2844 0 : case SHPP_FIRSTRING:
2845 0 : return "FirstRing";
2846 :
2847 0 : case SHPP_RING:
2848 0 : return "Ring";
2849 :
2850 0 : default:
2851 0 : return "UnknownPartType";
2852 : }
2853 : }
2854 :
2855 : /************************************************************************/
2856 : /* SHPDestroyObject() */
2857 : /************************************************************************/
2858 :
2859 305187 : void SHPAPI_CALL SHPDestroyObject(SHPObject *psShape)
2860 : {
2861 305187 : if (psShape == SHPLIB_NULLPTR)
2862 0 : return;
2863 :
2864 305187 : if (psShape->bFastModeReadObject)
2865 : {
2866 240929 : psShape->bFastModeReadObject = FALSE;
2867 240929 : return;
2868 : }
2869 :
2870 64258 : if (psShape->padfX != SHPLIB_NULLPTR)
2871 63641 : free(psShape->padfX);
2872 64258 : if (psShape->padfY != SHPLIB_NULLPTR)
2873 63641 : free(psShape->padfY);
2874 64258 : if (psShape->padfZ != SHPLIB_NULLPTR)
2875 63641 : free(psShape->padfZ);
2876 64258 : if (psShape->padfM != SHPLIB_NULLPTR)
2877 63641 : free(psShape->padfM);
2878 :
2879 64258 : if (psShape->panPartStart != SHPLIB_NULLPTR)
2880 18918 : free(psShape->panPartStart);
2881 64258 : if (psShape->panPartType != SHPLIB_NULLPTR)
2882 18918 : free(psShape->panPartType);
2883 :
2884 64258 : free(psShape);
2885 : }
2886 :
2887 : /************************************************************************/
2888 : /* SHPGetPartVertexCount() */
2889 : /************************************************************************/
2890 :
2891 0 : static int SHPGetPartVertexCount(const SHPObject *psObject, int iPart)
2892 : {
2893 0 : if (iPart == psObject->nParts - 1)
2894 0 : return psObject->nVertices - psObject->panPartStart[iPart];
2895 : else
2896 0 : return psObject->panPartStart[iPart + 1] -
2897 0 : psObject->panPartStart[iPart];
2898 : }
2899 :
2900 : /************************************************************************/
2901 : /* SHPRewindIsInnerRing() */
2902 : /************************************************************************/
2903 :
2904 : /* Return -1 in case of ambiguity */
2905 0 : static int SHPRewindIsInnerRing(const SHPObject *psObject, int iOpRing,
2906 : double dfTestX, double dfTestY,
2907 : double dfRelativeTolerance, int bSameZ,
2908 : double dfTestZ)
2909 : {
2910 : /* -------------------------------------------------------------------- */
2911 : /* Determine if this ring is an inner ring or an outer ring */
2912 : /* relative to all the other rings. For now we assume the */
2913 : /* first ring is outer and all others are inner, but eventually */
2914 : /* we need to fix this to handle multiple island polygons and */
2915 : /* unordered sets of rings. */
2916 : /* */
2917 : /* -------------------------------------------------------------------- */
2918 :
2919 0 : bool bInner = false;
2920 0 : for (int iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++)
2921 : {
2922 0 : if (iCheckRing == iOpRing)
2923 0 : continue;
2924 :
2925 0 : const int nVertStartCheck = psObject->panPartStart[iCheckRing];
2926 0 : const int nVertCountCheck = SHPGetPartVertexCount(psObject, iCheckRing);
2927 :
2928 : /* Ignore rings that don't have the same (constant) Z value as the
2929 : * point. */
2930 : /* As noted in SHPRewindObject(), this is a simplification */
2931 : /* of what we should ideally do. */
2932 0 : if (!bSameZ)
2933 : {
2934 0 : int bZTestOK = TRUE;
2935 0 : for (int iVert = nVertStartCheck + 1;
2936 0 : iVert < nVertStartCheck + nVertCountCheck; ++iVert)
2937 : {
2938 0 : if (psObject->padfZ[iVert] != dfTestZ)
2939 : {
2940 0 : bZTestOK = FALSE;
2941 0 : break;
2942 : }
2943 : }
2944 0 : if (!bZTestOK)
2945 0 : continue;
2946 : }
2947 :
2948 0 : for (int iEdge = 0; iEdge < nVertCountCheck; iEdge++)
2949 : {
2950 : int iNext;
2951 0 : if (iEdge < nVertCountCheck - 1)
2952 0 : iNext = iEdge + 1;
2953 : else
2954 0 : iNext = 0;
2955 :
2956 0 : const double y0 = psObject->padfY[iEdge + nVertStartCheck];
2957 0 : const double y1 = psObject->padfY[iNext + nVertStartCheck];
2958 : /* Rule #1:
2959 : * Test whether the edge 'straddles' the horizontal ray from
2960 : * the test point (dfTestY,dfTestY)
2961 : * The rule #1 also excludes edges colinear with the ray.
2962 : */
2963 0 : if ((y0 < dfTestY && dfTestY <= y1) ||
2964 0 : (y1 < dfTestY && dfTestY <= y0))
2965 : {
2966 : /* Rule #2:
2967 : * Test if edge-ray intersection is on the right from the
2968 : * test point (dfTestY,dfTestY)
2969 : */
2970 0 : const double x0 = psObject->padfX[iEdge + nVertStartCheck];
2971 0 : const double x1 = psObject->padfX[iNext + nVertStartCheck];
2972 0 : const double intersect_minus_testX =
2973 0 : (x0 - dfTestX) + (dfTestY - y0) / (y1 - y0) * (x1 - x0);
2974 :
2975 0 : if (fabs(intersect_minus_testX) <=
2976 0 : dfRelativeTolerance * fabs(dfTestX))
2977 : {
2978 : /* Potential shared edge, or slightly overlapping polygons
2979 : */
2980 0 : return -1;
2981 : }
2982 0 : else if (intersect_minus_testX < 0)
2983 : {
2984 0 : bInner = !bInner;
2985 : }
2986 : }
2987 : }
2988 : } /* for iCheckRing */
2989 0 : return bInner;
2990 : }
2991 :
2992 : /************************************************************************/
2993 : /* SHPRewindObject() */
2994 : /* */
2995 : /* Reset the winding of polygon objects to adhere to the */
2996 : /* specification. */
2997 : /************************************************************************/
2998 :
2999 5 : int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject)
3000 : {
3001 : (void)hSHP;
3002 : /* -------------------------------------------------------------------- */
3003 : /* Do nothing if this is not a polygon object. */
3004 : /* -------------------------------------------------------------------- */
3005 5 : if (psObject->nSHPType != SHPT_POLYGON &&
3006 5 : psObject->nSHPType != SHPT_POLYGONZ &&
3007 5 : psObject->nSHPType != SHPT_POLYGONM)
3008 5 : return 0;
3009 :
3010 0 : if (psObject->nVertices == 0 || psObject->nParts == 0)
3011 0 : return 0;
3012 :
3013 : /* -------------------------------------------------------------------- */
3014 : /* Test if all points have the same Z value. */
3015 : /* -------------------------------------------------------------------- */
3016 0 : int bSameZ = TRUE;
3017 0 : if (psObject->nSHPType == SHPT_POLYGONZ ||
3018 0 : psObject->nSHPType == SHPT_POLYGONM)
3019 : {
3020 0 : for (int iVert = 1; iVert < psObject->nVertices; ++iVert)
3021 : {
3022 0 : if (psObject->padfZ[iVert] != psObject->padfZ[0])
3023 : {
3024 0 : bSameZ = FALSE;
3025 0 : break;
3026 : }
3027 : }
3028 : }
3029 :
3030 : /* -------------------------------------------------------------------- */
3031 : /* Process each of the rings. */
3032 : /* -------------------------------------------------------------------- */
3033 0 : int bAltered = 0;
3034 0 : for (int iOpRing = 0; iOpRing < psObject->nParts; iOpRing++)
3035 : {
3036 0 : const int nVertStart = psObject->panPartStart[iOpRing];
3037 0 : const int nVertCount = SHPGetPartVertexCount(psObject, iOpRing);
3038 :
3039 0 : if (nVertCount < 2)
3040 0 : continue;
3041 :
3042 : /* If a ring has a non-constant Z value, then consider it as an outer */
3043 : /* ring. */
3044 : /* NOTE: this is a rough approximation. If we were smarter, */
3045 : /* we would check that all points of the ring are coplanar, and compare
3046 : */
3047 : /* that to other rings in the same (oblique) plane. */
3048 0 : int bDoIsInnerRingTest = TRUE;
3049 0 : if (!bSameZ)
3050 : {
3051 0 : int bPartSameZ = TRUE;
3052 0 : for (int iVert = nVertStart + 1; iVert < nVertStart + nVertCount;
3053 : ++iVert)
3054 : {
3055 0 : if (psObject->padfZ[iVert] != psObject->padfZ[nVertStart])
3056 : {
3057 0 : bPartSameZ = FALSE;
3058 0 : break;
3059 : }
3060 : }
3061 0 : if (!bPartSameZ)
3062 0 : bDoIsInnerRingTest = FALSE;
3063 : }
3064 :
3065 0 : int bInner = FALSE;
3066 0 : if (bDoIsInnerRingTest)
3067 : {
3068 0 : for (int iTolerance = 0; iTolerance < 2; iTolerance++)
3069 : {
3070 : /* In a first attempt, use a relaxed criterion to decide if a
3071 : * point */
3072 : /* is inside another ring. If all points of the current ring are
3073 : * in the */
3074 : /* "grey" zone w.r.t that criterion, which seems really
3075 : * unlikely, */
3076 : /* then use the strict criterion for another pass. */
3077 0 : const double dfRelativeTolerance = (iTolerance == 0) ? 1e-9 : 0;
3078 0 : for (int iVert = nVertStart;
3079 0 : iVert + 1 < nVertStart + nVertCount; ++iVert)
3080 : {
3081 : /* Use point in the middle of segment to avoid testing
3082 : * common points of rings.
3083 : */
3084 0 : const double dfTestX =
3085 0 : (psObject->padfX[iVert] + psObject->padfX[iVert + 1]) /
3086 : 2;
3087 0 : const double dfTestY =
3088 0 : (psObject->padfY[iVert] + psObject->padfY[iVert + 1]) /
3089 : 2;
3090 0 : const double dfTestZ =
3091 0 : !bSameZ ? psObject->padfZ[nVertStart] : 0;
3092 :
3093 0 : bInner = SHPRewindIsInnerRing(psObject, iOpRing, dfTestX,
3094 : dfTestY, dfRelativeTolerance,
3095 : bSameZ, dfTestZ);
3096 0 : if (bInner >= 0)
3097 0 : break;
3098 : }
3099 0 : if (bInner >= 0)
3100 0 : break;
3101 : }
3102 0 : if (bInner < 0)
3103 : {
3104 : /* Completely degenerate case. Do not bother touching order. */
3105 0 : continue;
3106 : }
3107 : }
3108 :
3109 : /* -------------------------------------------------------------------- */
3110 : /* Determine the current order of this ring so we will know if */
3111 : /* it has to be reversed. */
3112 : /* -------------------------------------------------------------------- */
3113 :
3114 0 : double dfSum = psObject->padfX[nVertStart] *
3115 0 : (psObject->padfY[nVertStart + 1] -
3116 0 : psObject->padfY[nVertStart + nVertCount - 1]);
3117 0 : int iVert = nVertStart + 1;
3118 0 : for (; iVert < nVertStart + nVertCount - 1; iVert++)
3119 : {
3120 0 : dfSum += psObject->padfX[iVert] *
3121 0 : (psObject->padfY[iVert + 1] - psObject->padfY[iVert - 1]);
3122 : }
3123 :
3124 0 : dfSum += psObject->padfX[iVert] *
3125 0 : (psObject->padfY[nVertStart] - psObject->padfY[iVert - 1]);
3126 :
3127 : /* -------------------------------------------------------------------- */
3128 : /* Reverse if necessary. */
3129 : /* -------------------------------------------------------------------- */
3130 0 : if ((dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner))
3131 : {
3132 0 : bAltered++;
3133 0 : for (int i = 0; i < nVertCount / 2; i++)
3134 : {
3135 : /* Swap X */
3136 0 : double dfSaved = psObject->padfX[nVertStart + i];
3137 0 : psObject->padfX[nVertStart + i] =
3138 0 : psObject->padfX[nVertStart + nVertCount - i - 1];
3139 0 : psObject->padfX[nVertStart + nVertCount - i - 1] = dfSaved;
3140 :
3141 : /* Swap Y */
3142 0 : dfSaved = psObject->padfY[nVertStart + i];
3143 0 : psObject->padfY[nVertStart + i] =
3144 0 : psObject->padfY[nVertStart + nVertCount - i - 1];
3145 0 : psObject->padfY[nVertStart + nVertCount - i - 1] = dfSaved;
3146 :
3147 : /* Swap Z */
3148 0 : if (psObject->padfZ)
3149 : {
3150 0 : dfSaved = psObject->padfZ[nVertStart + i];
3151 0 : psObject->padfZ[nVertStart + i] =
3152 0 : psObject->padfZ[nVertStart + nVertCount - i - 1];
3153 0 : psObject->padfZ[nVertStart + nVertCount - i - 1] = dfSaved;
3154 : }
3155 :
3156 : /* Swap M */
3157 0 : if (psObject->padfM)
3158 : {
3159 0 : dfSaved = psObject->padfM[nVertStart + i];
3160 0 : psObject->padfM[nVertStart + i] =
3161 0 : psObject->padfM[nVertStart + nVertCount - i - 1];
3162 0 : psObject->padfM[nVertStart + nVertCount - i - 1] = dfSaved;
3163 : }
3164 : }
3165 : }
3166 : }
3167 :
3168 0 : return bAltered;
3169 : }
|