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