2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2021 Olly Betts
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 # define INT32_T int32_t
38 # define UINT32_T uint32_t
40 # include "filelist.h"
41 # include "filename.h"
44 # define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
48 # define INT32_T int32_t
49 # define UINT32_T uint32_t
52 # if INT_MAX >= 2147483647
54 # define UINT32_T unsigned
57 # define UINT32_T unsigned long
60 # define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
61 # define EXT_SVX_3D "3d"
62 # define EXT_SVX_POS "pos"
63 # define FNM_SEP_EXT '.'
64 # define METRES_PER_FOOT 0.3048 /* exact value */
65 # define xosmalloc(L) malloc((L))
66 # define xosrealloc(L,S) realloc((L),(S))
67 # define osfree(P) free((P))
68 # define osnew(T) (T*)malloc(sizeof(T))
70 /* in IMG_HOSTED mode, this tests if a filename refers to a directory */
71 # define fDirectory(X) 0
72 /* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
73 /* path isn't used in img.c, but EXT is */
74 # define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) \
75 ((*(X) = NULL), fopen(FNM,MODE))
77 # define PUTC(C, FH) putc(C, FH)
80 # define GETC(FH) getc(FH)
82 # define fputsnl(S, FH) (fputs((S), (FH)) == EOF ? EOF : putc('\n', (FH)))
83 # define SVX_ASSERT(X)
90 /* Return max/min of two numbers. */
91 /* May be defined already (e.g. by Borland C in stdlib.h) */
92 /* NB Bad news if X or Y has side-effects... */
94 # define max(X, Y) ((X) > (Y) ? (X) : (Y))
97 # define min(X, Y) ((X) < (Y) ? (X) : (Y))
104 UINT32_T w
= GETC(fh
);
105 w
|= (UINT32_T
)GETC(fh
) << 8l;
106 w
|= (UINT32_T
)GETC(fh
) << 16l;
107 w
|= (UINT32_T
)GETC(fh
) << 24l;
112 put32(UINT32_T w
, FILE *fh
)
115 PUTC((char)(w
>> 8l), fh
);
116 PUTC((char)(w
>> 16l), fh
);
117 PUTC((char)(w
>> 24l), fh
);
123 UINT32_T w
= GETC(fh
);
124 w
|= (UINT32_T
)GETC(fh
) << 8l;
129 put16(short word
, FILE *fh
)
131 unsigned short w
= (unsigned short)word
;
133 PUTC((char)(w
>> 8l), fh
);
137 baseleaf_from_fnm(const char *fnm
)
147 q
= strrchr(p
, '\\');
150 q
= strrchr(p
, FNM_SEP_EXT
);
151 if (q
) len
= (const char *)q
- p
; else len
= strlen(p
);
153 res
= (char *)xosmalloc(len
+ 1);
154 if (!res
) return NULL
;
161 static char * my_strdup(const char *str
);
164 mktime_with_tz(struct tm
* tm
, const char * tz
)
167 char * old_tz
= getenv("TZ");
170 old_tz
= my_strdup(old_tz
);
174 if (_putenv_s("TZ", tz
) != 0) {
178 #elif defined HAVE_SETENV
180 old_tz
= my_strdup(old_tz
);
184 if (setenv("TZ", tz
, 1) < 0) {
191 size_t len
= strlen(old_tz
) + 1;
192 p
= (char *)xosmalloc(len
+ 3);
196 memcpy(p
+ 3, tz
, len
);
199 p
= (char *)xosmalloc(strlen(tz
) + 4);
206 if (putenv(p
) != 0) {
211 #define CLEANUP() osfree(p)
217 _putenv_s("TZ", old_tz
);
218 #elif !defined HAVE_SETENV
221 setenv("TZ", old_tz
, 1);
227 #elif !defined HAVE_UNSETENV
240 static unsigned short
243 return (unsigned short)get16(fh
);
248 #if !defined HAVE_LROUND && !defined HAVE_DECL_LROUND
249 /* The autoconf tests are not in use, but C99 and C++11 both added lround(),
250 * so set HAVE_LROUND and HAVE_DECL_LROUND conservatively based on the language
251 * standard version the compiler claims to support. */
252 # if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
253 (defined __cplusplus && __cplusplus >= 201103L)
254 # define HAVE_LROUND 1
255 # define HAVE_DECL_LROUND 1
260 # if defined HAVE_DECL_LROUND && !HAVE_DECL_LROUND
261 /* On older systems, the prototype may be missing. */
262 extern long lround(double);
264 # define my_lround lround
267 my_lround(double x
) {
268 return (x
>= 0.0) ? (long)(x
+ 0.5) : -(long)(0.5 - x
);
272 /* portable case insensitive string compare */
273 #if defined(strcasecmp) || defined(HAVE_STRCASECMP)
274 # define my_strcasecmp strcasecmp
276 static int my_strcasecmp(const char *s1
, const char *s2
) {
277 unsigned char c1
, c2
;
281 } while (c1
&& toupper(c1
) == toupper(c2
));
282 /* now calculate real difference */
287 unsigned int img_output_version
= IMG_VERSION_MAX
;
289 static img_errcode img_errno
= IMG_NONE
;
291 #define FILEID "Survex 3D Image File"
293 #define EXT_PLT "plt"
294 #define EXT_PLF "plf"
296 /* Attempt to string paste to ensure we are passed a literal string */
297 #define LITLEN(S) (sizeof(S"") - 1)
299 /* Fake "version numbers" for non-3d formats we can read. */
300 #define VERSION_CMAP_SHOT -4
301 #define VERSION_CMAP_STATION -3
302 #define VERSION_COMPASS_PLT -2
303 #define VERSION_SURVEX_POS -1
306 my_strdup(const char *str
)
309 size_t len
= strlen(str
) + 1;
310 p
= (char *)xosmalloc(len
);
311 if (p
) memcpy(p
, str
, len
);
315 #define getline_alloc(FH) getline_alloc_len(FH, NULL)
318 getline_alloc_len(FILE *fh
, size_t * p_len
)
323 char *buf
= (char *)xosmalloc(len
);
324 if (!buf
) return NULL
;
327 while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
) {
332 p
= (char *)xosrealloc(buf
, len
);
341 if (ch
== '\n' || ch
== '\r') {
342 int otherone
= ch
^ ('\n' ^ '\r');
344 /* if it's not the other eol character, put it back */
345 if (ch
!= otherone
) ungetc(ch
, fh
);
348 if (p_len
) *p_len
= i
;
359 check_label_space(img
*pimg
, size_t len
)
361 if (len
> pimg
->buf_len
) {
362 char *b
= (char *)xosrealloc(pimg
->label_buf
, len
);
364 pimg
->label
= (pimg
->label
- pimg
->label_buf
) + b
;
371 /* Check if a station name should be included. */
373 stn_included(img
*pimg
)
375 if (!pimg
->survey_len
) return 1;
376 size_t l
= pimg
->survey_len
;
377 const char *s
= pimg
->label_buf
;
378 if (strncmp(pimg
->survey
, s
, l
+ 1) != 0) {
381 pimg
->label
+= l
+ 1;
385 /* Check if a survey name should be included. */
387 survey_included(img
*pimg
)
389 if (!pimg
->survey_len
) return 1;
390 size_t l
= pimg
->survey_len
;
391 const char *s
= pimg
->label_buf
;
392 if (strncmp(pimg
->survey
, s
, l
) != 0 ||
393 !(s
[l
] == '.' || s
[l
] == '\0')) {
397 /* skip the dot if there */
398 if (*pimg
->label
) pimg
->label
++;
402 /* Check if a survey name in a buffer should be included.
404 * For "foreign" formats which just have one level of surveys.
407 buf_included(img
*pimg
, const char *buf
, size_t len
)
409 return pimg
->survey_len
== len
&& strncmp(buf
, pimg
->survey
, len
) == 0;
412 #define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
413 (F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
414 my_strcasecmp((F) + (L) - LITLEN(E), E) == 0)
417 img_open_survey(const char *fnm
, const char *survey
)
421 char* filename_opened
= NULL
;
423 if (fDirectory(fnm
)) {
424 img_errno
= IMG_DIRECTORY
;
428 fh
= fopenWithPthAndExt("", fnm
, EXT_SVX_3D
, "rb", &filename_opened
);
429 pimg
= img_read_stream_survey(fh
, fclose
,
430 filename_opened
? filename_opened
: fnm
,
433 pimg
->filename_opened
= filename_opened
;
435 osfree(filename_opened
);
441 img_read_stream_survey(FILE *stream
, int (*close_func
)(FILE*),
447 char buf
[LITLEN(FILEID
) + 9];
450 if (stream
== NULL
) {
451 img_errno
= IMG_FILENOTFOUND
;
457 img_errno
= IMG_OUTOFMEMORY
;
458 if (close_func
) close_func(stream
);
463 pimg
->close_func
= close_func
;
466 pimg
->label_buf
= (char *)xosmalloc(pimg
->buf_len
);
467 if (!pimg
->label_buf
) {
468 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
470 img_errno
= IMG_OUTOFMEMORY
;
474 pimg
->fRead
= 1; /* reading from this file */
475 img_errno
= IMG_NONE
;
478 pimg
->filename_opened
= NULL
;
480 /* for version >= 3 we use label_buf to store the prefix for reuse */
481 /* for VERSION_COMPASS_PLT, 0 value indicates we haven't
482 * entered a survey yet */
483 /* for VERSION_CMAP_SHOT, we store the last station here
484 * to detect whether we MOVE or LINE */
486 pimg
->label_buf
[0] = '\0';
489 pimg
->survey_len
= 0;
490 pimg
->separator
= '.';
491 #if IMG_API_VERSION == 0
492 pimg
->date1
= pimg
->date2
= 0;
493 #else /* IMG_API_VERSION == 1 */
494 pimg
->days1
= pimg
->days2
= -1;
496 pimg
->is_extended_elevation
= 0;
498 pimg
->style
= pimg
->oldstyle
= img_STYLE_UNKNOWN
;
500 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
502 pimg
->title
= pimg
->datestamp
= pimg
->cs
= NULL
;
503 pimg
->datestamp_numeric
= (time_t)-1;
506 len
= strlen(survey
);
508 if (survey
[len
- 1] == '.') len
--;
511 pimg
->survey
= (char *)xosmalloc(len
+ 2);
513 img_errno
= IMG_OUTOFMEMORY
;
516 memcpy(pimg
->survey
, survey
, len
);
517 /* Set title to leaf survey name */
518 pimg
->survey
[len
] = '\0';
519 p
= strrchr(pimg
->survey
, '.');
520 if (p
) p
++; else p
= pimg
->survey
;
521 pimg
->title
= my_strdup(p
);
523 img_errno
= IMG_OUTOFMEMORY
;
526 pimg
->survey
[len
] = '.';
527 pimg
->survey
[len
+ 1] = '\0';
530 pimg
->survey_len
= len
;
533 /* [VERSION_COMPASS_PLT, VERSION_CMAP_STATION, VERSION_CMAP_SHOT] pending
534 * IMG_LINE or IMG_MOVE - both have 4 added.
535 * [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
536 * [version 0] not in the middle of a 'LINE' command
537 * [version >= 3] not in the middle of turning a LINE into a MOVE
542 if (has_ext(fnm
, len
, EXT_SVX_POS
)) {
544 pimg
->version
= VERSION_SURVEX_POS
;
545 if (!pimg
->survey
) pimg
->title
= baseleaf_from_fnm(fnm
);
546 pimg
->datestamp
= my_strdup(TIMENA
);
547 if (!pimg
->datestamp
) {
548 img_errno
= IMG_OUTOFMEMORY
;
555 if (has_ext(fnm
, len
, EXT_PLT
) || has_ext(fnm
, len
, EXT_PLF
)) {
558 pimg
->version
= VERSION_COMPASS_PLT
;
559 /* Spaces aren't legal in Compass station names, but dots are, so
560 * use space as the level separator */
561 pimg
->separator
= ' ';
563 if (!pimg
->survey
) pimg
->title
= baseleaf_from_fnm(fnm
);
564 pimg
->datestamp
= my_strdup(TIMENA
);
565 if (!pimg
->datestamp
) {
566 img_errno
= IMG_OUTOFMEMORY
;
573 fseek(pimg
->fh
, -1, SEEK_CUR
);
576 pimg
->start
= ftell(pimg
->fh
);
580 fpos
= ftell(pimg
->fh
) - 1;
582 /* FIXME : if there's only one survey in the file, it'd be nice
583 * to use its description as the title here...
585 ungetc('N', pimg
->fh
);
589 line
= getline_alloc(pimg
->fh
);
591 img_errno
= IMG_OUTOFMEMORY
;
595 while (line
[len
] > 32) ++len
;
596 if (!buf_included(pimg
, line
, len
)) {
600 q
= strchr(line
+ len
, 'C');
603 pimg
->title
= my_strdup(q
+ 1);
604 } else if (!pimg
->title
) {
605 pimg
->title
= my_strdup(pimg
->label
);
609 img_errno
= IMG_OUTOFMEMORY
;
612 if (!pimg
->start
) pimg
->start
= fpos
;
613 fseek(pimg
->fh
, pimg
->start
, SEEK_SET
);
617 pimg
->start
= ftell(pimg
->fh
) - 1;
620 while (ch
!= '\n' && ch
!= '\r') {
626 /* Although these are often referred to as "CMAP .XYZ files", it seems
627 * that actually, the extension .XYZ isn't used, rather .SHT (shot
628 * variant, produced by CMAP v16 and later), .UNA (unadjusted) and
629 * .ADJ (adjusted) extensions are. Since img has long checked for
630 * .XYZ, we continue to do so in case anyone is relying on it.
632 if (has_ext(fnm
, len
, "sht") ||
633 has_ext(fnm
, len
, "adj") ||
634 has_ext(fnm
, len
, "una") ||
635 has_ext(fnm
, len
, "xyz")) {
638 /* Spaces aren't legal in CMAP station names, but dots are, so
639 * use space as the level separator. */
640 pimg
->separator
= ' ';
641 line
= getline_alloc(pimg
->fh
);
643 img_errno
= IMG_OUTOFMEMORY
;
646 /* There doesn't seem to be a spec for what happens after 1999 with cmap
647 * files, so this code allows for:
648 * * 21xx -> xx (up to 2150)
649 * * 21xx -> 1xx (up to 2199)
650 * * full year being specified instead of 2 digits
654 /* Don't just truncate at column 59, allow for a > 2 digit year. */
655 char * p
= strstr(line
+ len
, "Page");
657 while (p
> line
&& p
[-1] == ' ')
670 pimg
->datestamp
= my_strdup(line
+ 45);
672 v
= strtoul(p
, &p
, 10);
674 /* In the absence of a spec for cmap files, assume <= 50 means 21st
677 } else if (v
< 200) {
678 /* Map 100-199 to 21st century. */
681 if (v
== ULONG_MAX
|| *p
++ != '/')
683 tm
.tm_year
= v
- 1900;
684 v
= strtoul(p
, &p
, 10);
685 if (v
< 1 || v
> 12 || *p
++ != '/')
688 v
= strtoul(p
, &p
, 10);
689 if (v
< 1 || v
> 31 || *p
++ != ' ')
692 v
= strtoul(p
, &p
, 10);
693 if (v
>= 24 || *p
++ != ':')
696 v
= strtoul(p
, &p
, 10);
701 v
= strtoul(p
+ 1, &p
, 10);
709 /* We have no indication of what timezone this timestamp is in. It's
710 * probably local time for whoever processed the data, so just assume
711 * UTC, which is at least fairly central in the possibilities.
713 pimg
->datestamp_numeric
= mktime_with_tz(&tm
, "");
715 pimg
->datestamp
= my_strdup(TIMENA
);
718 if (strncmp(line
, " Cave Survey Data Processed by CMAP ",
719 LITLEN(" Cave Survey Data Processed by CMAP ")) == 0) {
726 while (len
> 2 && line
[len
- 1] == ' ') --len
;
729 pimg
->title
= my_strdup(line
+ 2);
732 if (len
<= 2) pimg
->title
= baseleaf_from_fnm(fnm
);
734 if (!pimg
->datestamp
|| !pimg
->title
) {
735 img_errno
= IMG_OUTOFMEMORY
;
738 line
= getline_alloc(pimg
->fh
);
740 img_errno
= IMG_OUTOFMEMORY
;
743 if (line
[0] != ' ' || (line
[1] != 'S' && line
[1] != 'O')) {
744 img_errno
= IMG_BADFORMAT
;
747 if (line
[1] == 'S') {
748 pimg
->version
= VERSION_CMAP_STATION
;
750 pimg
->version
= VERSION_CMAP_SHOT
;
753 line
= getline_alloc(pimg
->fh
);
755 img_errno
= IMG_OUTOFMEMORY
;
758 if (line
[0] != ' ' || line
[1] != '-') {
759 img_errno
= IMG_BADFORMAT
;
763 pimg
->start
= ftell(pimg
->fh
);
767 if (fread(buf
, LITLEN(FILEID
) + 1, 1, pimg
->fh
) != 1 ||
768 memcmp(buf
, FILEID
"\n", LITLEN(FILEID
) + 1) != 0) {
769 if (fread(buf
+ LITLEN(FILEID
) + 1, 8, 1, pimg
->fh
) == 1 &&
770 memcmp(buf
, FILEID
"\r\nv0.01\r\n", LITLEN(FILEID
) + 9) == 0) {
771 /* v0 3d file with DOS EOLs */
778 /* Looks like a CMAP .xyz file ... */
780 } else if (strchr("ZSNF", buf
[0])) {
781 /* Looks like a Compass .plt file ... */
782 /* Almost certainly it'll start "Z " */
787 /* Looks like a Survex .pos file ... */
790 img_errno
= IMG_BADFORMAT
;
794 /* check file format version */
797 if (tolower(ch
) == 'b') {
798 /* binary file iff B/b prefix */
803 img_errno
= IMG_BADFORMAT
;
808 if (fread(buf
, 4, 1, pimg
->fh
) != 1 || memcmp(buf
, ".01\n", 4) != 0) {
809 img_errno
= IMG_BADFORMAT
;
812 /* nothing special to do */
813 } else if (pimg
->version
== 0) {
814 if (ch
< '2' || ch
> '0' + IMG_VERSION_MAX
|| GETC(pimg
->fh
) != '\n') {
815 img_errno
= IMG_TOONEW
;
818 pimg
->version
= ch
- '0';
820 img_errno
= IMG_BADFORMAT
;
827 char * title
= getline_alloc_len(pimg
->fh
, &title_len
);
828 if (pimg
->version
== 8 && title
) {
829 /* We sneak in an extra field after a zero byte here, containing the
830 * specified coordinate system (if any). Older readers will just
831 * not see it (which is fine), and this trick avoids us having to
832 * bump the 3d format version.
834 size_t real_len
= strlen(title
);
835 if (real_len
!= title_len
) {
836 char * cs
= title
+ real_len
+ 1;
837 if (memcmp(cs
, "+init=", 6) == 0) {
838 /* PROJ 5 and later don't handle +init=esri:<number> but
839 * that's what cavern used to put in .3d files for
840 * coordinate systems specified using ESRI codes. We parse
841 * and convert the strings cavern used to generate and
842 * convert to the form ESRI:<number> which is still
845 * PROJ 6 and later don't recognise +init=epsg:<number>
846 * by default and don't apply datum shift terms in some
847 * cases, so we also convert these to the form
851 if (p
[4] == ':' && isdigit((unsigned char)p
[5]) &&
852 ((memcmp(p
, "epsg", 4) == 0 || memcmp(p
, "esri", 4) == 0))) {
854 while (isdigit((unsigned char)*p
)) {
857 // Allow +no_defs to be omitted as it seems to not
858 // actually do anything with recent PROJ - cavern always
859 // included it, but other software generating 3d files
861 if (*p
== '\0' || strcmp(p
, " +no_defs") == 0) {
864 for (i
= 0; i
< 4; ++i
) {
865 cs
[i
] = toupper(cs
[i
]);
871 pimg
->cs
= my_strdup(cs
);
880 pimg
->datestamp
= getline_alloc(pimg
->fh
);
881 if (!pimg
->title
|| !pimg
->datestamp
) {
882 img_errno
= IMG_OUTOFMEMORY
;
886 osfree(pimg
->datestamp
);
887 osfree(pimg
->filename_opened
);
888 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
893 if (pimg
->version
>= 8) {
894 int flags
= GETC(pimg
->fh
);
895 if (flags
& img_FFLAG_EXTENDED
) pimg
->is_extended_elevation
= 1;
897 len
= strlen(pimg
->title
);
898 if (len
> 11 && strcmp(pimg
->title
+ len
- 11, " (extended)") == 0) {
899 pimg
->title
[len
- 11] = '\0';
900 pimg
->is_extended_elevation
= 1;
904 if (pimg
->datestamp
[0] == '@') {
908 v
= strtoul(pimg
->datestamp
+ 1, &p
, 10);
909 if (errno
== 0 && *p
== '\0')
910 pimg
->datestamp_numeric
= v
;
911 /* FIXME: We're assuming here that the C time_t epoch is 1970, which is
912 * true for Unix-like systems, macOS and Windows, but isn't guaranteed
916 /* %a,%Y.%m.%d %H:%M:%S %Z */
919 char * p
= pimg
->datestamp
;
920 while (isalpha((unsigned char)*p
)) ++p
;
922 while (isspace((unsigned char)*p
)) ++p
;
923 v
= strtoul(p
, &p
, 10);
924 if (v
== ULONG_MAX
|| *p
++ != '.')
926 tm
.tm_year
= v
- 1900;
927 v
= strtoul(p
, &p
, 10);
928 if (v
< 1 || v
> 12 || *p
++ != '.')
931 v
= strtoul(p
, &p
, 10);
932 if (v
< 1 || v
> 31 || *p
++ != ' ')
935 v
= strtoul(p
, &p
, 10);
936 if (v
>= 24 || *p
++ != ':')
939 v
= strtoul(p
, &p
, 10);
940 if (v
>= 60 || *p
++ != ':')
943 v
= strtoul(p
, &p
, 10);
948 while (isspace((unsigned char)*p
)) ++p
;
949 /* p now points to the timezone string.
951 * However, it's likely to be a string like "BST", and such strings can
952 * be ambiguous (BST could be UTC+1 or UTC+6), so it is impossible to
953 * reliably convert in all cases. Just pass what we have to tzset() - if
954 * it doesn't handle it, UTC will be used.
956 pimg
->datestamp_numeric
= mktime_with_tz(&tm
, p
);
960 pimg
->start
= ftell(pimg
->fh
);
966 img_rewind(img
*pimg
)
969 img_errno
= IMG_WRITEERROR
;
972 if (fseek(pimg
->fh
, pimg
->start
, SEEK_SET
) != 0) {
973 img_errno
= IMG_READERROR
;
977 /* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
978 * [version 0] not in the middle of a 'LINE' command
979 * [version >= 3] not in the middle of turning a LINE into a MOVE */
982 img_errno
= IMG_NONE
;
984 /* for version >= 3 we use label_buf to store the prefix for reuse */
985 /* for VERSION_COMPASS_PLT, 0 value indicates we haven't entered a survey
987 /* for VERSION_CMAP_SHOT, we store the last station here to detect whether
990 pimg
->style
= img_STYLE_UNKNOWN
;
995 img_open_write_cs(const char *fnm
, const char *title
, const char *cs
, int flags
)
997 if (fDirectory(fnm
)) {
998 img_errno
= IMG_DIRECTORY
;
1002 return img_write_stream(fopen(fnm
, "wb"), fclose
, title
, cs
, flags
);
1006 img_write_stream(FILE *stream
, int (*close_func
)(FILE*),
1007 const char *title
, const char *cs
, int flags
)
1012 if (stream
== NULL
) {
1013 img_errno
= IMG_FILENOTFOUND
;
1019 img_errno
= IMG_OUTOFMEMORY
;
1020 if (close_func
) close_func(stream
);
1025 pimg
->close_func
= close_func
;
1026 pimg
->buf_len
= 257;
1027 pimg
->label_buf
= (char *)xosmalloc(pimg
->buf_len
);
1028 if (!pimg
->label_buf
) {
1029 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
1031 img_errno
= IMG_OUTOFMEMORY
;
1035 pimg
->filename_opened
= NULL
;
1037 /* Output image file header */
1038 fputs("Survex 3D Image File\n", pimg
->fh
); /* file identifier string */
1039 if (img_output_version
< 2) {
1041 fputs("Bv0.01\n", pimg
->fh
); /* binary file format version number */
1043 pimg
->version
= (img_output_version
> IMG_VERSION_MAX
) ? IMG_VERSION_MAX
: img_output_version
;
1044 fprintf(pimg
->fh
, "v%d\n", pimg
->version
); /* file format version no. */
1047 fputs(title
, pimg
->fh
);
1048 if (pimg
->version
< 8 && (flags
& img_FFLAG_EXTENDED
)) {
1049 /* Older format versions append " (extended)" to the title to mark
1050 * extended elevations. */
1051 size_t len
= strlen(title
);
1052 if (len
< 11 || strcmp(title
+ len
- 11, " (extended)") != 0)
1053 fputs(" (extended)", pimg
->fh
);
1055 if (pimg
->version
== 8 && cs
&& *cs
) {
1056 /* We sneak in an extra field after a zero byte here, containing the
1057 * specified coordinate system (if any). Older readers will just not
1058 * see it (which is fine), and this trick avoids us having to bump the
1059 * 3d format version.
1061 PUTC('\0', pimg
->fh
);
1062 fputs(cs
, pimg
->fh
);
1064 PUTC('\n', pimg
->fh
);
1067 if (tm
== (time_t)-1) {
1068 fputsnl(TIMENA
, pimg
->fh
);
1069 } else if (pimg
->version
<= 7) {
1071 /* output current date and time in format specified */
1072 strftime(date
, 256, TIMEFMT
, localtime(&tm
));
1073 fputsnl(date
, pimg
->fh
);
1075 fprintf(pimg
->fh
, "@%ld\n", (long)tm
);
1078 if (pimg
->version
>= 8) {
1079 /* Clear bit one in case anyone has been passing true for fBinary. */
1081 PUTC(flags
, pimg
->fh
);
1085 if (img_output_version
>= 5) {
1086 static const unsigned char codelengths
[32] = {
1087 4, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1088 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1090 fwrite(codelengths
, 32, 1, pimg
->fh
);
1093 pimg
->fRead
= 0; /* writing to this file */
1094 img_errno
= IMG_NONE
;
1096 /* for version >= 3 we use label_buf to store the prefix for reuse */
1097 pimg
->label_buf
[0] = '\0';
1098 pimg
->label_len
= 0;
1100 #if IMG_API_VERSION == 0
1101 pimg
->date1
= pimg
->date2
= 0;
1102 pimg
->olddate1
= pimg
->olddate2
= 0;
1103 #else /* IMG_API_VERSION == 1 */
1104 pimg
->days1
= pimg
->days2
= -1;
1105 pimg
->olddays1
= pimg
->olddays2
= -1;
1107 pimg
->style
= pimg
->oldstyle
= img_STYLE_UNKNOWN
;
1109 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1113 pimg
->E
= pimg
->H
= pimg
->V
= 0.0;
1115 /* Don't check for write errors now - let img_close() report them... */
1120 read_xyz_station_coords(img_point
*pt
, const char *line
)
1123 memcpy(num
, line
+ 6, 9);
1125 pt
->x
= atof(num
) / METRES_PER_FOOT
;
1126 memcpy(num
, line
+ 15, 9);
1127 pt
->y
= atof(num
) / METRES_PER_FOOT
;
1128 memcpy(num
, line
+ 24, 8);
1130 pt
->z
= atof(num
) / METRES_PER_FOOT
;
1134 read_xyz_shot_coords(img_point
*pt
, const char *line
)
1137 memcpy(num
, line
+ 40, 10);
1139 pt
->x
= atof(num
) / METRES_PER_FOOT
;
1140 memcpy(num
, line
+ 50, 10);
1141 pt
->y
= atof(num
) / METRES_PER_FOOT
;
1142 memcpy(num
, line
+ 60, 9);
1144 pt
->z
= atof(num
) / METRES_PER_FOOT
;
1148 subtract_xyz_shot_deltas(img_point
*pt
, const char *line
)
1151 memcpy(num
, line
+ 15, 9);
1153 pt
->x
-= atof(num
) / METRES_PER_FOOT
;
1154 memcpy(num
, line
+ 24, 8);
1156 pt
->y
-= atof(num
) / METRES_PER_FOOT
;
1157 memcpy(num
, line
+ 32, 8);
1158 pt
->z
-= atof(num
) / METRES_PER_FOOT
;
1162 read_coord(FILE *fh
, img_point
*pt
)
1166 pt
->x
= get32(fh
) / 100.0;
1167 pt
->y
= get32(fh
) / 100.0;
1168 pt
->z
= get32(fh
) / 100.0;
1169 if (ferror(fh
) || feof(fh
)) {
1170 img_errno
= feof(fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1177 skip_coord(FILE *fh
)
1179 return (fseek(fh
, 12, SEEK_CUR
) == 0);
1183 read_v3label(img
*pimg
)
1186 long len
= GETC(pimg
->fh
);
1188 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1192 len
+= get16(pimg
->fh
);
1193 if (feof(pimg
->fh
)) {
1194 img_errno
= IMG_BADFORMAT
;
1197 if (ferror(pimg
->fh
)) {
1198 img_errno
= IMG_READERROR
;
1201 } else if (len
== 0xff) {
1202 len
= get32(pimg
->fh
);
1203 if (ferror(pimg
->fh
)) {
1204 img_errno
= IMG_READERROR
;
1207 if (feof(pimg
->fh
) || len
< 0xfe + 0xffff) {
1208 img_errno
= IMG_BADFORMAT
;
1213 if (!check_label_space(pimg
, pimg
->label_len
+ len
+ 1)) {
1214 img_errno
= IMG_OUTOFMEMORY
;
1217 q
= pimg
->label_buf
+ pimg
->label_len
;
1218 pimg
->label_len
+= len
;
1219 if (len
&& fread(q
, len
, 1, pimg
->fh
) != 1) {
1220 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1228 read_v8label(img
*pimg
, int common_flag
, size_t common_val
)
1233 if (common_val
== 0) return 0;
1234 add
= del
= common_val
;
1236 int ch
= GETC(pimg
->fh
);
1238 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1245 ch
= GETC(pimg
->fh
);
1247 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1253 del
= get32(pimg
->fh
);
1254 if (ferror(pimg
->fh
)) {
1255 img_errno
= IMG_READERROR
;
1259 ch
= GETC(pimg
->fh
);
1261 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1267 add
= get32(pimg
->fh
);
1268 if (ferror(pimg
->fh
)) {
1269 img_errno
= IMG_READERROR
;
1275 if (add
> del
&& !check_label_space(pimg
, pimg
->label_len
+ add
- del
+ 1)) {
1276 img_errno
= IMG_OUTOFMEMORY
;
1280 if (del
> pimg
->label_len
) {
1281 img_errno
= IMG_BADFORMAT
;
1284 pimg
->label_len
-= del
;
1285 q
= pimg
->label_buf
+ pimg
->label_len
;
1286 pimg
->label_len
+= add
;
1287 if (add
&& fread(q
, add
, 1, pimg
->fh
) != 1) {
1288 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1295 static int img_read_item_new(img
*pimg
, img_point
*p
);
1296 static int img_read_item_v3to7(img
*pimg
, img_point
*p
);
1297 static int img_read_item_ancient(img
*pimg
, img_point
*p
);
1298 static int img_read_item_ascii_wrapper(img
*pimg
, img_point
*p
);
1299 static int img_read_item_ascii(img
*pimg
, img_point
*p
);
1302 img_read_item(img
*pimg
, img_point
*p
)
1306 if (pimg
->version
>= 8) {
1307 return img_read_item_new(pimg
, p
);
1308 } else if (pimg
->version
>= 3) {
1309 return img_read_item_v3to7(pimg
, p
);
1310 } else if (pimg
->version
>= 1) {
1311 return img_read_item_ancient(pimg
, p
);
1313 return img_read_item_ascii_wrapper(pimg
, p
);
1318 img_read_item_new(img
*pimg
, img_point
*p
)
1322 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1323 if (pimg
->pending
>= 0x40) {
1324 if (pimg
->pending
== 256) {
1326 return img_XSECT_END
;
1329 pimg
->flags
= (int)(pimg
->pending
) & 0x3f;
1333 again3
: /* label to goto if we get a prefix, date, or lrud */
1334 pimg
->label
= pimg
->label_buf
;
1335 opt
= GETC(pimg
->fh
);
1337 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1340 if (opt
>> 6 == 0) {
1342 if (opt
== 0 && pimg
->style
== 0)
1343 return img_STOP
; /* end of data marker */
1350 case 0x10: { /* No date info */
1351 #if IMG_API_VERSION == 0
1352 pimg
->date1
= pimg
->date2
= 0;
1353 #else /* IMG_API_VERSION == 1 */
1354 pimg
->days1
= pimg
->days2
= -1;
1358 case 0x11: { /* Single date */
1359 int days1
= (int)getu16(pimg
->fh
);
1360 #if IMG_API_VERSION == 0
1361 pimg
->date2
= pimg
->date1
= (days1
- 25567) * 86400;
1362 #else /* IMG_API_VERSION == 1 */
1363 pimg
->days2
= pimg
->days1
= days1
;
1367 case 0x12: { /* Date range (short) */
1368 int days1
= (int)getu16(pimg
->fh
);
1369 int days2
= days1
+ GETC(pimg
->fh
) + 1;
1370 #if IMG_API_VERSION == 0
1371 pimg
->date1
= (days1
- 25567) * 86400;
1372 pimg
->date2
= (days2
- 25567) * 86400;
1373 #else /* IMG_API_VERSION == 1 */
1374 pimg
->days1
= days1
;
1375 pimg
->days2
= days2
;
1379 case 0x13: { /* Date range (long) */
1380 int days1
= (int)getu16(pimg
->fh
);
1381 int days2
= (int)getu16(pimg
->fh
);
1382 #if IMG_API_VERSION == 0
1383 pimg
->date1
= (days1
- 25567) * 86400;
1384 pimg
->date2
= (days2
- 25567) * 86400;
1385 #else /* IMG_API_VERSION == 1 */
1386 pimg
->days1
= days1
;
1387 pimg
->days2
= days2
;
1391 case 0x1f: /* Error info */
1392 pimg
->n_legs
= get32(pimg
->fh
);
1393 pimg
->length
= get32(pimg
->fh
) / 100.0;
1394 pimg
->E
= get32(pimg
->fh
) / 100.0;
1395 pimg
->H
= get32(pimg
->fh
) / 100.0;
1396 pimg
->V
= get32(pimg
->fh
) / 100.0;
1397 return img_ERROR_INFO
;
1398 case 0x30: case 0x31: /* LRUD */
1399 case 0x32: case 0x33: /* Big LRUD! */
1400 if (read_v8label(pimg
, 0, 0) == img_BAD
) return img_BAD
;
1401 pimg
->flags
= (int)opt
& 0x01;
1403 pimg
->l
= get16(pimg
->fh
) / 100.0;
1404 pimg
->r
= get16(pimg
->fh
) / 100.0;
1405 pimg
->u
= get16(pimg
->fh
) / 100.0;
1406 pimg
->d
= get16(pimg
->fh
) / 100.0;
1408 pimg
->l
= get32(pimg
->fh
) / 100.0;
1409 pimg
->r
= get32(pimg
->fh
) / 100.0;
1410 pimg
->u
= get32(pimg
->fh
) / 100.0;
1411 pimg
->d
= get32(pimg
->fh
) / 100.0;
1413 if (!stn_included(pimg
)) {
1414 return img_XSECT_END
;
1416 /* If this is the last cross-section in this passage, set
1417 * pending so we return img_XSECT_END next time. */
1418 if (pimg
->flags
& 0x01) {
1419 pimg
->pending
= 256;
1420 pimg
->flags
&= ~0x01;
1423 default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1424 img_errno
= IMG_BADFORMAT
;
1430 /* 1-14 and 16-31 reserved */
1431 img_errno
= IMG_BADFORMAT
;
1435 } else if (opt
>= 0x80) {
1436 if (read_v8label(pimg
, 0, 0) == img_BAD
) return img_BAD
;
1440 if (!stn_included(pimg
)) {
1441 if (!skip_coord(pimg
->fh
)) return img_BAD
;
1446 pimg
->flags
= (int)opt
& 0x7f;
1447 } else if ((opt
>> 6) == 1) {
1448 if (read_v8label(pimg
, opt
& 0x20, 0) == img_BAD
) return img_BAD
;
1452 if (!survey_included(pimg
)) {
1453 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1458 if (pimg
->pending
) {
1460 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1461 pimg
->pending
= opt
;
1464 pimg
->flags
= (int)opt
& 0x1f;
1466 img_errno
= IMG_BADFORMAT
;
1469 if (!read_coord(pimg
->fh
, p
)) return img_BAD
;
1475 img_read_item_v3to7(img
*pimg
, img_point
*p
)
1479 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1480 if (pimg
->pending
== 256) {
1482 return img_XSECT_END
;
1484 if (pimg
->pending
>= 0x80) {
1486 pimg
->flags
= (int)(pimg
->pending
) & 0x3f;
1490 again3
: /* label to goto if we get a prefix, date, or lrud */
1491 pimg
->label
= pimg
->label_buf
;
1492 opt
= GETC(pimg
->fh
);
1494 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1500 if (!pimg
->label_len
) return img_STOP
; /* end of data marker */
1501 pimg
->label_len
= 0;
1505 /* 1-14 mean trim that many levels from current prefix */
1507 if (pimg
->label_len
<= 17) {
1508 /* zero prefix using "0" */
1509 img_errno
= IMG_BADFORMAT
;
1512 /* extra - 1 because label_len points to one past the end */
1513 c
= pimg
->label_len
- 17 - 1;
1514 while (pimg
->label_buf
[c
] != '.' || --opt
> 0) {
1516 /* zero prefix using "0" */
1517 img_errno
= IMG_BADFORMAT
;
1522 pimg
->label_len
= c
;
1531 case 0x20: /* Single date */
1532 if (pimg
->version
< 7) {
1533 int date1
= get32(pimg
->fh
);
1534 #if IMG_API_VERSION == 0
1535 pimg
->date2
= pimg
->date1
= date1
;
1536 #else /* IMG_API_VERSION == 1 */
1538 pimg
->days2
= pimg
->days1
= (date1
/ 86400) + 25567;
1540 pimg
->days2
= pimg
->days1
= -1;
1544 int days1
= (int)getu16(pimg
->fh
);
1545 #if IMG_API_VERSION == 0
1546 pimg
->date2
= pimg
->date1
= (days1
- 25567) * 86400;
1547 #else /* IMG_API_VERSION == 1 */
1548 pimg
->days2
= pimg
->days1
= days1
;
1552 case 0x21: /* Date range (short for v7+) */
1553 if (pimg
->version
< 7) {
1554 INT32_T date1
= get32(pimg
->fh
);
1555 INT32_T date2
= get32(pimg
->fh
);
1556 #if IMG_API_VERSION == 0
1557 pimg
->date1
= date1
;
1558 pimg
->date2
= date2
;
1559 #else /* IMG_API_VERSION == 1 */
1560 pimg
->days1
= (date1
/ 86400) + 25567;
1561 pimg
->days2
= (date2
/ 86400) + 25567;
1564 int days1
= (int)getu16(pimg
->fh
);
1565 int days2
= days1
+ GETC(pimg
->fh
) + 1;
1566 #if IMG_API_VERSION == 0
1567 pimg
->date1
= (days1
- 25567) * 86400;
1568 pimg
->date2
= (days2
- 25567) * 86400;
1569 #else /* IMG_API_VERSION == 1 */
1570 pimg
->days1
= days1
;
1571 pimg
->days2
= days2
;
1575 case 0x22: /* Error info */
1576 pimg
->n_legs
= get32(pimg
->fh
);
1577 pimg
->length
= get32(pimg
->fh
) / 100.0;
1578 pimg
->E
= get32(pimg
->fh
) / 100.0;
1579 pimg
->H
= get32(pimg
->fh
) / 100.0;
1580 pimg
->V
= get32(pimg
->fh
) / 100.0;
1581 if (feof(pimg
->fh
)) {
1582 img_errno
= IMG_BADFORMAT
;
1585 if (ferror(pimg
->fh
)) {
1586 img_errno
= IMG_READERROR
;
1589 return img_ERROR_INFO
;
1590 case 0x23: { /* v7+: Date range (long) */
1591 if (pimg
->version
< 7) {
1592 img_errno
= IMG_BADFORMAT
;
1595 int days1
= (int)getu16(pimg
->fh
);
1596 int days2
= (int)getu16(pimg
->fh
);
1597 if (feof(pimg
->fh
)) {
1598 img_errno
= IMG_BADFORMAT
;
1601 if (ferror(pimg
->fh
)) {
1602 img_errno
= IMG_READERROR
;
1605 #if IMG_API_VERSION == 0
1606 pimg
->date1
= (days1
- 25567) * 86400;
1607 pimg
->date2
= (days2
- 25567) * 86400;
1608 #else /* IMG_API_VERSION == 1 */
1609 pimg
->days1
= days1
;
1610 pimg
->days2
= days2
;
1614 case 0x24: { /* v7+: No date info */
1615 #if IMG_API_VERSION == 0
1616 pimg
->date1
= pimg
->date2
= 0;
1617 #else /* IMG_API_VERSION == 1 */
1618 pimg
->days1
= pimg
->days2
= -1;
1622 case 0x30: case 0x31: /* LRUD */
1623 case 0x32: case 0x33: /* Big LRUD! */
1624 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1625 pimg
->flags
= (int)opt
& 0x01;
1627 pimg
->l
= get16(pimg
->fh
) / 100.0;
1628 pimg
->r
= get16(pimg
->fh
) / 100.0;
1629 pimg
->u
= get16(pimg
->fh
) / 100.0;
1630 pimg
->d
= get16(pimg
->fh
) / 100.0;
1632 pimg
->l
= get32(pimg
->fh
) / 100.0;
1633 pimg
->r
= get32(pimg
->fh
) / 100.0;
1634 pimg
->u
= get32(pimg
->fh
) / 100.0;
1635 pimg
->d
= get32(pimg
->fh
) / 100.0;
1637 if (feof(pimg
->fh
)) {
1638 img_errno
= IMG_BADFORMAT
;
1641 if (ferror(pimg
->fh
)) {
1642 img_errno
= IMG_READERROR
;
1645 if (!stn_included(pimg
)) {
1646 return img_XSECT_END
;
1648 /* If this is the last cross-section in this passage, set
1649 * pending so we return img_XSECT_END next time. */
1650 if (pimg
->flags
& 0x01) {
1651 pimg
->pending
= 256;
1652 pimg
->flags
&= ~0x01;
1655 default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1656 img_errno
= IMG_BADFORMAT
;
1659 if (feof(pimg
->fh
)) {
1660 img_errno
= IMG_BADFORMAT
;
1663 if (ferror(pimg
->fh
)) {
1664 img_errno
= IMG_READERROR
;
1669 /* 16-31 mean remove (n - 15) characters from the prefix */
1670 /* zero prefix using 0 */
1671 if (pimg
->label_len
<= (size_t)(opt
- 15)) {
1672 img_errno
= IMG_BADFORMAT
;
1675 pimg
->label_len
-= (opt
- 15);
1678 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1682 if (!stn_included(pimg
)) {
1683 if (!skip_coord(pimg
->fh
)) return img_BAD
;
1688 pimg
->flags
= (int)opt
& 0x3f;
1691 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1695 if (!survey_included(pimg
)) {
1696 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1701 if (pimg
->pending
) {
1703 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1704 pimg
->pending
= opt
;
1707 pimg
->flags
= (int)opt
& 0x3f;
1710 img_errno
= IMG_BADFORMAT
;
1713 if (!read_coord(pimg
->fh
, p
)) return img_BAD
;
1719 img_read_item_ancient(img
*pimg
, img_point
*p
)
1722 static long opt_lookahead
= 0;
1723 static img_point pt
= { 0.0, 0.0, 0.0 };
1726 again
: /* label to goto if we get a cross */
1727 pimg
->label
= pimg
->label_buf
;
1728 pimg
->label
[0] = '\0';
1730 if (pimg
->version
== 1) {
1731 if (opt_lookahead
) {
1732 opt
= opt_lookahead
;
1735 opt
= get32(pimg
->fh
);
1738 opt
= GETC(pimg
->fh
);
1741 if (feof(pimg
->fh
)) {
1742 img_errno
= IMG_BADFORMAT
;
1745 if (ferror(pimg
->fh
)) {
1746 img_errno
= IMG_READERROR
;
1752 return img_STOP
; /* end of data marker */
1754 /* skip coordinates */
1755 if (!skip_coord(pimg
->fh
)) {
1756 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1763 if (!fgets(pimg
->label_buf
, pimg
->buf_len
, pimg
->fh
)) {
1764 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1767 if (pimg
->label
[0] == '\\') pimg
->label
++;
1768 len
= strlen(pimg
->label
);
1769 if (len
== 0 || pimg
->label
[len
- 1] != '\n') {
1770 img_errno
= IMG_BADFORMAT
;
1773 /* Ignore empty labels in some .3d files (caused by a bug) */
1774 if (len
== 1) goto again
;
1775 pimg
->label
[len
- 1] = '\0';
1776 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* no flags given... */
1777 if (opt
== 2) goto done
;
1785 pimg
->flags
= GETC(pimg
->fh
);
1787 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* no flags given... */
1789 len
= get32(pimg
->fh
);
1791 if (feof(pimg
->fh
)) {
1792 img_errno
= IMG_BADFORMAT
;
1795 if (ferror(pimg
->fh
)) {
1796 img_errno
= IMG_READERROR
;
1800 /* Ignore empty labels in some .3d files (caused by a bug) */
1801 if (len
== 0) goto again
;
1802 if (!check_label_space(pimg
, len
+ 1)) {
1803 img_errno
= IMG_OUTOFMEMORY
;
1806 if (fread(pimg
->label_buf
, len
, 1, pimg
->fh
) != 1) {
1807 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1810 pimg
->label_buf
[len
] = '\0';
1820 switch ((int)opt
& 0xc0) {
1822 pimg
->flags
= (int)opt
& 0x3f;
1827 pimg
->flags
= (int)opt
& 0x3f;
1829 if (!fgets(pimg
->label_buf
, pimg
->buf_len
, pimg
->fh
)) {
1830 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1833 q
= pimg
->label_buf
+ strlen(pimg
->label_buf
) - 1;
1834 /* Ignore empty-labels in some .3d files (caused by a bug) */
1835 if (q
== pimg
->label_buf
) goto again
;
1837 img_errno
= IMG_BADFORMAT
;
1844 img_errno
= IMG_BADFORMAT
;
1850 if (!read_coord(pimg
->fh
, &pt
)) return img_BAD
;
1852 if (result
== img_LABEL
&& !stn_included(pimg
)) {
1859 if (result
== img_MOVE
&& pimg
->version
== 1) {
1860 /* peek at next code and see if it's an old-style label */
1861 opt_lookahead
= get32(pimg
->fh
);
1863 if (feof(pimg
->fh
)) {
1864 img_errno
= IMG_BADFORMAT
;
1867 if (ferror(pimg
->fh
)) {
1868 img_errno
= IMG_READERROR
;
1872 if (opt_lookahead
== 2) return img_read_item_ancient(pimg
, p
);
1879 img_read_item_ascii_wrapper(img
*pimg
, img_point
*p
)
1881 /* We need to set the default locale for fscanf() to work on
1882 * numbers with "." as decimal point. */
1884 char * current_locale
= my_strdup(setlocale(LC_NUMERIC
, NULL
));
1885 setlocale(LC_NUMERIC
, "C");
1886 result
= img_read_item_ascii(pimg
, p
);
1887 setlocale(LC_NUMERIC
, current_locale
);
1888 free(current_locale
);
1892 /* Handle all ASCII formats. */
1894 img_read_item_ascii(img
*pimg
, img_point
*p
)
1897 pimg
->label
= pimg
->label_buf
;
1898 if (pimg
->version
== 0) {
1900 pimg
->label
[0] = '\0';
1901 if (feof(pimg
->fh
)) return img_STOP
;
1902 if (pimg
->pending
) {
1907 /* Stop if nothing found */
1908 if (fscanf(pimg
->fh
, "%6s", cmd
) < 1) return img_STOP
;
1909 if (strcmp(cmd
, "move") == 0)
1911 else if (strcmp(cmd
, "draw") == 0)
1913 else if (strcmp(cmd
, "line") == 0) {
1914 /* set flag to indicate to process second triplet as LINE */
1917 } else if (strcmp(cmd
, "cross") == 0) {
1918 if (fscanf(pimg
->fh
, "%lf%lf%lf", &p
->x
, &p
->y
, &p
->z
) < 3) {
1919 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1923 } else if (strcmp(cmd
, "name") == 0) {
1925 int ch
= GETC(pimg
->fh
);
1926 if (ch
== ' ') ch
= GETC(pimg
->fh
);
1928 if (ch
== '\n' || ch
== EOF
) {
1929 img_errno
= ferror(pimg
->fh
) ? IMG_READERROR
: IMG_BADFORMAT
;
1932 if (off
== pimg
->buf_len
) {
1933 if (!check_label_space(pimg
, pimg
->buf_len
* 2)) {
1934 img_errno
= IMG_OUTOFMEMORY
;
1938 pimg
->label_buf
[off
++] = ch
;
1939 ch
= GETC(pimg
->fh
);
1941 pimg
->label_buf
[off
] = '\0';
1943 pimg
->label
= pimg
->label_buf
;
1944 if (pimg
->label
[0] == '\\') pimg
->label
++;
1946 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
1950 img_errno
= IMG_BADFORMAT
;
1951 return img_BAD
; /* unknown keyword */
1955 if (fscanf(pimg
->fh
, "%lf%lf%lf", &p
->x
, &p
->y
, &p
->z
) < 3) {
1956 img_errno
= ferror(pimg
->fh
) ? IMG_READERROR
: IMG_BADFORMAT
;
1960 if (result
== img_LABEL
&& !stn_included(pimg
)) {
1965 } else if (pimg
->version
== VERSION_SURVEX_POS
) {
1966 /* Survex .pos file */
1969 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
1971 while (fscanf(pimg
->fh
, "(%lf,%lf,%lf )", &p
->x
, &p
->y
, &p
->z
) != 3) {
1972 if (ferror(pimg
->fh
)) {
1973 img_errno
= IMG_READERROR
;
1976 if (feof(pimg
->fh
)) return img_STOP
;
1977 if (pimg
->pending
) {
1978 img_errno
= IMG_BADFORMAT
;
1982 /* ignore rest of line */
1984 ch
= GETC(pimg
->fh
);
1985 } while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
);
1988 pimg
->label_buf
[0] = '\0';
1990 ch
= GETC(pimg
->fh
);
1991 } while (ch
== ' ' || ch
== '\t');
1992 if (ch
== '\n' || ch
== EOF
) {
1993 /* If there's no label, set img_SFLAG_ANON. */
1994 pimg
->flags
|= img_SFLAG_ANON
;
1997 pimg
->label_buf
[0] = ch
;
1999 while (!feof(pimg
->fh
)) {
2000 if (!fgets(pimg
->label_buf
+ off
, pimg
->buf_len
- off
, pimg
->fh
)) {
2001 img_errno
= IMG_READERROR
;
2005 off
+= strlen(pimg
->label_buf
+ off
);
2006 if (off
&& pimg
->label_buf
[off
- 1] == '\n') {
2007 pimg
->label_buf
[off
- 1] = '\0';
2010 if (!check_label_space(pimg
, pimg
->buf_len
* 2)) {
2011 img_errno
= IMG_OUTOFMEMORY
;
2016 pimg
->label
= pimg
->label_buf
;
2018 if (pimg
->label
[0] == '\\') pimg
->label
++;
2020 if (!stn_included(pimg
)) goto againpos
;
2023 } else if (pimg
->version
== VERSION_COMPASS_PLT
) {
2024 /* Compass .plt file */
2025 if (pimg
->pending
> 0) {
2026 /* -1 signals we've entered the first survey we want to
2027 * read, and need to fudge lots if the first action is 'D'...
2029 /* pending MOVE or LINE */
2030 int r
= pimg
->pending
- 4;
2033 pimg
->label
[pimg
->label_len
] = '\0';
2041 int ch
= GETC(pimg
->fh
);
2044 case '\x1a': case EOF
: /* Don't insist on ^Z at end of file */
2046 case 'X': case 'F': case 'S':
2047 /* bounding boX (marks end of survey), Feature survey, or
2048 * new Section - skip to next survey */
2049 if (pimg
->survey
) return img_STOP
;
2053 ch
= GETC(pimg
->fh
);
2054 } while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
);
2055 while (ch
== '\n' || ch
== '\r') ch
= GETC(pimg
->fh
);
2056 if (ch
== 'N') break;
2057 if (ch
== '\x1a' || ch
== EOF
) return img_STOP
;
2061 line
= getline_alloc(pimg
->fh
);
2063 img_errno
= IMG_OUTOFMEMORY
;
2066 while (line
[len
] > 32) ++len
;
2067 if (pimg
->label_len
== 0) pimg
->pending
= -1;
2068 if (!check_label_space(pimg
, len
+ 1)) {
2070 img_errno
= IMG_OUTOFMEMORY
;
2073 pimg
->label_len
= len
;
2074 pimg
->label
= pimg
->label_buf
;
2075 memcpy(pimg
->label
, line
, len
);
2076 pimg
->label
[len
] = '\0';
2079 case 'M': case 'D': {
2082 if (pimg
->survey
&& pimg
->label_len
== 0) {
2083 /* We're only holding onto this line in case the first line
2084 * of the 'N' is a 'D', so skip it for now...
2088 if (ch
== 'D' && pimg
->pending
== -1) {
2090 fpos
= ftell(pimg
->fh
) - 1;
2091 fseek(pimg
->fh
, pimg
->start
, SEEK_SET
);
2092 ch
= GETC(pimg
->fh
);
2095 /* If a file actually has a 'D' before any 'M', then
2096 * pretend the 'D' is an 'M' - one of the examples
2097 * in the docs was like this! */
2101 line
= getline_alloc(pimg
->fh
);
2103 img_errno
= IMG_OUTOFMEMORY
;
2106 /* Compass stores coordinates as North, East, Up = (y,x,z)! */
2107 if (sscanf(line
, "%lf%lf%lf", &p
->y
, &p
->x
, &p
->z
) != 3) {
2109 if (ferror(pimg
->fh
)) {
2110 img_errno
= IMG_READERROR
;
2112 img_errno
= IMG_BADFORMAT
;
2116 p
->x
*= METRES_PER_FOOT
;
2117 p
->y
*= METRES_PER_FOOT
;
2118 p
->z
*= METRES_PER_FOOT
;
2119 q
= strchr(line
, 'S');
2122 img_errno
= IMG_BADFORMAT
;
2127 while (q
[len
] > ' ') ++len
;
2129 len
+= 2; /* ' ' and '\0' */
2130 if (!check_label_space(pimg
, pimg
->label_len
+ len
)) {
2131 img_errno
= IMG_OUTOFMEMORY
;
2134 pimg
->label
= pimg
->label_buf
;
2135 if (pimg
->label_len
) {
2136 pimg
->label
[pimg
->label_len
] = ' ';
2137 memcpy(pimg
->label
+ pimg
->label_len
+ 1, q
, len
- 1);
2139 memcpy(pimg
->label
, q
, len
- 1);
2142 /* Now read LRUD. Technically, this is optional but virtually
2143 * all PLT files have it (with dummy negative values if no LRUD
2144 * was measured) and some versions of Compass can't read PLT
2147 while (*q
&& *q
<= ' ') q
++;
2149 if (sscanf(q
+ 1, "%lf%lf%lf%lf",
2150 &pimg
->l
, &pimg
->r
, &pimg
->u
, &pimg
->d
) != 4) {
2152 if (ferror(pimg
->fh
)) {
2153 img_errno
= IMG_READERROR
;
2155 img_errno
= IMG_BADFORMAT
;
2159 pimg
->l
*= METRES_PER_FOOT
;
2160 pimg
->r
*= METRES_PER_FOOT
;
2161 pimg
->u
*= METRES_PER_FOOT
;
2162 pimg
->d
*= METRES_PER_FOOT
;
2164 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1;
2167 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
2169 fseek(pimg
->fh
, fpos
, SEEK_SET
);
2171 pimg
->pending
= (ch
== 'M' ? img_MOVE
: img_LINE
) + 4;
2176 img_errno
= IMG_BADFORMAT
;
2181 /* CMAP .xyz file */
2186 if (pimg
->pending
) {
2187 /* pending MOVE or LINE or LABEL or STOP */
2188 int r
= pimg
->pending
- 4;
2189 /* Set label to empty - don't use "" as we adjust label relative
2190 * to label_buf when label_buf is reallocated. */
2191 pimg
->label
= pimg
->label_buf
+ strlen(pimg
->label_buf
);
2193 if (r
== img_LABEL
) {
2195 read_xyz_shot_coords(p
, pimg
->label_buf
+ 16);
2196 subtract_xyz_shot_deltas(p
, pimg
->label_buf
+ 16);
2197 pimg
->pending
= img_STOP
+ 4;
2203 if (r
== img_STOP
) {
2205 read_xyz_shot_coords(p
, pimg
->label_buf
+ 16);
2212 pimg
->label
= pimg
->label_buf
;
2215 if (feof(pimg
->fh
)) return img_STOP
;
2216 line
= getline_alloc(pimg
->fh
);
2218 img_errno
= IMG_OUTOFMEMORY
;
2221 } while (line
[0] == ' ' || line
[0] == '\0');
2222 if (line
[0] == '\x1a') return img_STOP
;
2225 if (pimg
->version
== VERSION_CMAP_STATION
) {
2226 /* station variant */
2229 img_errno
= IMG_BADFORMAT
;
2232 memcpy(pimg
->label
, line
, 6);
2233 q
= (char *)memchr(pimg
->label
, ' ', 6);
2234 if (!q
) q
= pimg
->label
+ 6;
2237 read_xyz_station_coords(p
, line
);
2239 /* FIXME: look at prev for lines (line + 32, 5) */
2240 /* FIXME: duplicate stations... */
2243 /* Shot variant (VERSION_CMAP_SHOT) */
2244 char old
[8], new_
[8];
2247 img_errno
= IMG_BADFORMAT
;
2251 memcpy(old
, line
, 7);
2252 q
= (char *)memchr(old
, ' ', 7);
2253 if (!q
) q
= old
+ 7;
2256 memcpy(new_
, line
+ 7, 7);
2257 q
= (char *)memchr(new_
, ' ', 7);
2258 if (!q
) q
= new_
+ 7;
2261 pimg
->flags
= img_SFLAG_UNDERGROUND
;
2263 if (strcmp(old
, new_
) == 0) {
2264 pimg
->pending
= img_MOVE
+ 4;
2265 read_xyz_shot_coords(p
, line
);
2266 strcpy(pimg
->label
, new_
);
2271 if (strcmp(old
, pimg
->label
) == 0) {
2272 pimg
->pending
= img_LINE
+ 4;
2273 read_xyz_shot_coords(p
, line
);
2274 strcpy(pimg
->label
, new_
);
2279 pimg
->pending
= img_LABEL
+ 4;
2280 read_xyz_shot_coords(p
, line
);
2281 strcpy(pimg
->label
, new_
);
2282 memcpy(pimg
->label
+ 16, line
, 70);
2291 write_coord(FILE *fh
, double x
, double y
, double z
)
2295 static INT32_T X_
, Y_
, Z_
;
2296 INT32_T X
= my_lround(x
* 100.0);
2297 INT32_T Y
= my_lround(y
* 100.0);
2298 INT32_T Z
= my_lround(z
* 100.0);
2306 X_
= X
; Y_
= Y
; Z_
= Z
;
2310 write_v3label(img
*pimg
, int opt
, const char *s
)
2314 /* find length of common prefix */
2316 for (len
= 0; s
[len
] == pimg
->label_buf
[len
] && s
[len
] != '\0'; len
++) {
2317 if (s
[len
] == '.') dot
= len
+ 1;
2320 SVX_ASSERT(len
<= pimg
->label_len
);
2321 n
= pimg
->label_len
- len
;
2323 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2324 } else if (n
<= 16) {
2325 if (n
) PUTC(n
+ 15, pimg
->fh
);
2326 } else if (dot
== 0) {
2327 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2330 const char *p
= pimg
->label_buf
+ dot
;
2332 for (len
= pimg
->label_len
- dot
- 17; len
; len
--) {
2333 if (*p
++ == '.') n
++;
2339 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2344 n
= strlen(s
+ len
);
2345 PUTC(opt
, pimg
->fh
);
2348 } else if (n
< 0xffff + 0xfe) {
2349 PUTC(0xfe, pimg
->fh
);
2350 put16((short)(n
- 0xfe), pimg
->fh
);
2352 PUTC(0xff, pimg
->fh
);
2355 fwrite(s
+ len
, n
, 1, pimg
->fh
);
2358 pimg
->label_len
= n
;
2359 if (!check_label_space(pimg
, n
+ 1))
2360 return 0; /* FIXME: distinguish out of memory... */
2361 memcpy(pimg
->label_buf
+ len
, s
+ len
, n
- len
+ 1);
2363 return !ferror(pimg
->fh
);
2367 write_v8label(img
*pimg
, int opt
, int common_flag
, size_t common_val
,
2370 size_t len
, del
, add
;
2372 /* find length of common prefix */
2373 for (len
= 0; s
[len
] == pimg
->label_buf
[len
] && s
[len
] != '\0'; len
++) {
2376 SVX_ASSERT(len
<= pimg
->label_len
);
2377 del
= pimg
->label_len
- len
;
2378 add
= strlen(s
+ len
);
2380 if (add
== common_val
&& del
== common_val
) {
2381 PUTC(opt
| common_flag
, pimg
->fh
);
2383 PUTC(opt
, pimg
->fh
);
2384 if (del
<= 15 && add
<= 15 && (del
|| add
)) {
2385 PUTC((del
<< 4) | add
, pimg
->fh
);
2387 PUTC(0x00, pimg
->fh
);
2389 PUTC(del
, pimg
->fh
);
2391 PUTC(0xff, pimg
->fh
);
2392 put32(del
, pimg
->fh
);
2395 PUTC(add
, pimg
->fh
);
2397 PUTC(0xff, pimg
->fh
);
2398 put32(add
, pimg
->fh
);
2404 fwrite(s
+ len
, add
, 1, pimg
->fh
);
2406 pimg
->label_len
= len
+ add
;
2407 if (add
> del
&& !check_label_space(pimg
, pimg
->label_len
+ 1))
2408 return 0; /* FIXME: distinguish out of memory... */
2410 memcpy(pimg
->label_buf
+ len
, s
+ len
, add
+ 1);
2412 return !ferror(pimg
->fh
);
2416 img_write_item_date_new(img
*pimg
)
2419 /* Only write dates when they've changed. */
2420 #if IMG_API_VERSION == 0
2421 if (pimg
->date1
== pimg
->olddate1
&& pimg
->date2
== pimg
->olddate2
)
2424 same
= (pimg
->date1
== pimg
->date2
);
2425 unset
= (pimg
->date1
== 0);
2426 #else /* IMG_API_VERSION == 1 */
2427 if (pimg
->days1
== pimg
->olddays1
&& pimg
->days2
== pimg
->olddays2
)
2430 same
= (pimg
->days1
== pimg
->days2
);
2431 unset
= (pimg
->days1
== -1);
2436 PUTC(0x10, pimg
->fh
);
2438 PUTC(0x11, pimg
->fh
);
2439 #if IMG_API_VERSION == 0
2440 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2441 #else /* IMG_API_VERSION == 1 */
2442 put16(pimg
->days1
, pimg
->fh
);
2446 #if IMG_API_VERSION == 0
2447 int diff
= (pimg
->date2
- pimg
->date1
) / 86400;
2448 if (diff
> 0 && diff
<= 256) {
2449 PUTC(0x12, pimg
->fh
);
2450 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2451 PUTC(diff
- 1, pimg
->fh
);
2453 PUTC(0x13, pimg
->fh
);
2454 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2455 put16(pimg
->date2
/ 86400 + 25567, pimg
->fh
);
2457 #else /* IMG_API_VERSION == 1 */
2458 int diff
= pimg
->days2
- pimg
->days1
;
2459 if (diff
> 0 && diff
<= 256) {
2460 PUTC(0x12, pimg
->fh
);
2461 put16(pimg
->days1
, pimg
->fh
);
2462 PUTC(diff
- 1, pimg
->fh
);
2464 PUTC(0x13, pimg
->fh
);
2465 put16(pimg
->days1
, pimg
->fh
);
2466 put16(pimg
->days2
, pimg
->fh
);
2470 #if IMG_API_VERSION == 0
2471 pimg
->olddate1
= pimg
->date1
;
2472 pimg
->olddate2
= pimg
->date2
;
2473 #else /* IMG_API_VERSION == 1 */
2474 pimg
->olddays1
= pimg
->days1
;
2475 pimg
->olddays2
= pimg
->days2
;
2480 img_write_item_date(img
*pimg
)
2483 /* Only write dates when they've changed. */
2484 #if IMG_API_VERSION == 0
2485 if (pimg
->date1
== pimg
->olddate1
&& pimg
->date2
== pimg
->olddate2
)
2488 same
= (pimg
->date1
== pimg
->date2
);
2489 unset
= (pimg
->date1
== 0);
2490 #else /* IMG_API_VERSION == 1 */
2491 if (pimg
->days1
== pimg
->olddays1
&& pimg
->days2
== pimg
->olddays2
)
2494 same
= (pimg
->days1
== pimg
->days2
);
2495 unset
= (pimg
->days1
== -1);
2499 if (img_output_version
< 7) {
2500 PUTC(0x20, pimg
->fh
);
2501 #if IMG_API_VERSION == 0
2502 put32(pimg
->date1
, pimg
->fh
);
2503 #else /* IMG_API_VERSION == 1 */
2504 put32((pimg
->days1
- 25567) * 86400, pimg
->fh
);
2508 PUTC(0x24, pimg
->fh
);
2510 PUTC(0x20, pimg
->fh
);
2511 #if IMG_API_VERSION == 0
2512 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2513 #else /* IMG_API_VERSION == 1 */
2514 put16(pimg
->days1
, pimg
->fh
);
2519 if (img_output_version
< 7) {
2520 PUTC(0x21, pimg
->fh
);
2521 #if IMG_API_VERSION == 0
2522 put32(pimg
->date1
, pimg
->fh
);
2523 put32(pimg
->date2
, pimg
->fh
);
2524 #else /* IMG_API_VERSION == 1 */
2525 put32((pimg
->days1
- 25567) * 86400, pimg
->fh
);
2526 put32((pimg
->days2
- 25567) * 86400, pimg
->fh
);
2529 #if IMG_API_VERSION == 0
2530 int diff
= (pimg
->date2
- pimg
->date1
) / 86400;
2531 if (diff
> 0 && diff
<= 256) {
2532 PUTC(0x21, pimg
->fh
);
2533 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2534 PUTC(diff
- 1, pimg
->fh
);
2536 PUTC(0x23, pimg
->fh
);
2537 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2538 put16(pimg
->date2
/ 86400 + 25567, pimg
->fh
);
2540 #else /* IMG_API_VERSION == 1 */
2541 int diff
= pimg
->days2
- pimg
->days1
;
2542 if (diff
> 0 && diff
<= 256) {
2543 PUTC(0x21, pimg
->fh
);
2544 put16(pimg
->days1
, pimg
->fh
);
2545 PUTC(diff
- 1, pimg
->fh
);
2547 PUTC(0x23, pimg
->fh
);
2548 put16(pimg
->days1
, pimg
->fh
);
2549 put16(pimg
->days2
, pimg
->fh
);
2554 #if IMG_API_VERSION == 0
2555 pimg
->olddate1
= pimg
->date1
;
2556 pimg
->olddate2
= pimg
->date2
;
2557 #else /* IMG_API_VERSION == 1 */
2558 pimg
->olddays1
= pimg
->days1
;
2559 pimg
->olddays2
= pimg
->days2
;
2564 img_write_item_new(img
*pimg
, int code
, int flags
, const char *s
,
2565 double x
, double y
, double z
);
2567 img_write_item_v3to7(img
*pimg
, int code
, int flags
, const char *s
,
2568 double x
, double y
, double z
);
2570 img_write_item_ancient(img
*pimg
, int code
, int flags
, const char *s
,
2571 double x
, double y
, double z
);
2574 img_write_item(img
*pimg
, int code
, int flags
, const char *s
,
2575 double x
, double y
, double z
)
2578 if (pimg
->version
>= 8) {
2579 img_write_item_new(pimg
, code
, flags
, s
, x
, y
, z
);
2580 } else if (pimg
->version
>= 3) {
2581 img_write_item_v3to7(pimg
, code
, flags
, s
, x
, y
, z
);
2583 img_write_item_ancient(pimg
, code
, flags
, s
, x
, y
, z
);
2588 img_write_item_new(img
*pimg
, int code
, int flags
, const char *s
,
2589 double x
, double y
, double z
)
2593 write_v8label(pimg
, 0x80 | flags
, 0, -1, s
);
2596 INT32_T l
, r
, u
, d
, max_dim
;
2597 img_write_item_date_new(pimg
);
2598 l
= (INT32_T
)my_lround(pimg
->l
* 100.0);
2599 r
= (INT32_T
)my_lround(pimg
->r
* 100.0);
2600 u
= (INT32_T
)my_lround(pimg
->u
* 100.0);
2601 d
= (INT32_T
)my_lround(pimg
->d
* 100.0);
2606 max_dim
= max(max(l
, r
), max(u
, d
));
2607 flags
= (flags
& img_XFLAG_END
) ? 1 : 0;
2608 if (max_dim
>= 32768) flags
|= 2;
2609 write_v8label(pimg
, 0x30 | flags
, 0, -1, s
);
2611 /* Big passage! Need to use 4 bytes. */
2628 img_write_item_date_new(pimg
);
2629 if (pimg
->style
!= pimg
->oldstyle
) {
2630 switch (pimg
->style
) {
2631 case img_STYLE_NORMAL
:
2632 case img_STYLE_DIVING
:
2633 case img_STYLE_CARTESIAN
:
2634 case img_STYLE_CYLPOLAR
:
2635 case img_STYLE_NOSURVEY
:
2636 PUTC(pimg
->style
, pimg
->fh
);
2639 pimg
->oldstyle
= pimg
->style
;
2641 write_v8label(pimg
, 0x40 | flags
, 0x20, 0x00, s
? s
: "");
2643 default: /* ignore for now */
2646 write_coord(pimg
->fh
, x
, y
, z
);
2650 img_write_item_v3to7(img
*pimg
, int code
, int flags
, const char *s
,
2651 double x
, double y
, double z
)
2655 write_v3label(pimg
, 0x40 | flags
, s
);
2658 INT32_T l
, r
, u
, d
, max_dim
;
2659 /* Need at least version 5 for img_XSECT. */
2660 if (pimg
->version
< 5) return;
2661 img_write_item_date(pimg
);
2662 l
= (INT32_T
)my_lround(pimg
->l
* 100.0);
2663 r
= (INT32_T
)my_lround(pimg
->r
* 100.0);
2664 u
= (INT32_T
)my_lround(pimg
->u
* 100.0);
2665 d
= (INT32_T
)my_lround(pimg
->d
* 100.0);
2670 max_dim
= max(max(l
, r
), max(u
, d
));
2671 flags
= (flags
& img_XFLAG_END
) ? 1 : 0;
2672 if (max_dim
>= 32768) flags
|= 2;
2673 write_v3label(pimg
, 0x30 | flags
, s
);
2675 /* Big passage! Need to use 4 bytes. */
2692 if (pimg
->version
>= 4) {
2693 img_write_item_date(pimg
);
2695 write_v3label(pimg
, 0x80 | flags
, s
? s
: "");
2697 default: /* ignore for now */
2700 write_coord(pimg
->fh
, x
, y
, z
);
2704 img_write_item_ancient(img
*pimg
, int code
, int flags
, const char *s
,
2705 double x
, double y
, double z
)
2709 SVX_ASSERT(pimg
->version
> 0);
2712 if (pimg
->version
== 1) {
2713 /* put a move before each label */
2714 img_write_item_ancient(pimg
, img_MOVE
, 0, NULL
, x
, y
, z
);
2716 fputsnl(s
, pimg
->fh
);
2720 if (len
> 255 || strchr(s
, '\n')) {
2721 /* long label - not in early incarnations of v2 format, but few
2722 * 3d files will need these, so better not to force incompatibility
2723 * with a new version I think... */
2725 PUTC(flags
, pimg
->fh
);
2726 put32(len
, pimg
->fh
);
2729 PUTC(0x40 | (flags
& 0x3f), pimg
->fh
);
2730 fputsnl(s
, pimg
->fh
);
2738 if (pimg
->version
> 1) {
2739 opt
= 0x80 | (flags
& 0x3f);
2744 default: /* ignore for now */
2747 if (pimg
->version
== 1) {
2748 put32(opt
, pimg
->fh
);
2750 if (opt
) PUTC(opt
, pimg
->fh
);
2752 write_coord(pimg
->fh
, x
, y
, z
);
2755 /* Write error information for the current traverse
2756 * n_legs is the number of legs in the traverse
2757 * length is the traverse length (in m)
2758 * E is the ratio of the observed misclosure to the theoretical one
2759 * H is the ratio of the observed horizontal misclosure to the theoretical one
2760 * V is the ratio of the observed vertical misclosure to the theoretical one
2763 img_write_errors(img
*pimg
, int n_legs
, double length
,
2764 double E
, double H
, double V
)
2766 PUTC((pimg
->version
>= 8 ? 0x1f : 0x22), pimg
->fh
);
2767 put32(n_legs
, pimg
->fh
);
2768 put32((INT32_T
)my_lround(length
* 100.0), pimg
->fh
);
2769 put32((INT32_T
)my_lround(E
* 100.0), pimg
->fh
);
2770 put32((INT32_T
)my_lround(H
* 100.0), pimg
->fh
);
2771 put32((INT32_T
)my_lround(V
* 100.0), pimg
->fh
);
2775 img_close(img
*pimg
)
2781 osfree(pimg
->survey
);
2782 osfree(pimg
->title
);
2784 osfree(pimg
->datestamp
);
2786 /* write end of data marker */
2787 switch (pimg
->version
) {
2789 put32((INT32_T
)-1, pimg
->fh
);
2792 if (pimg
->version
<= 7 ?
2793 (pimg
->label_len
!= 0) :
2794 (pimg
->style
!= img_STYLE_NORMAL
)) {
2803 if (ferror(pimg
->fh
)) result
= 0;
2804 if (pimg
->close_func
&& pimg
->close_func(pimg
->fh
))
2806 if (!result
) img_errno
= pimg
->fRead
? IMG_READERROR
: IMG_WRITEERROR
;
2808 osfree(pimg
->label_buf
);
2809 osfree(pimg
->filename_opened
);