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
862 if (*p
== '\0' || strcmp(p
, " +no_defs") == 0) {
865 for (i
= 0; i
< 4; ++i
) {
866 cs
[i
] = toupper(cs
[i
]);
871 } else if (memcmp(cs
, "+proj=", 6) == 0) {
872 /* Convert S_MERC and UTM proj strings which cavern used
873 * to generate to their corresponding EPSG:<number> codes.
876 if (memcmp(p
, "utm +ellps=WGS84 +datum=WGS84 +units=m +zone=", 45) == 0) {
879 while (isdigit((unsigned char)*p
)) {
880 n
= n
* 10 + (*p
- '0');
883 if (memcmp(p
, " +south", 7) == 0) {
889 /* Allow +no_defs to be omitted as it seems to not
890 * actually do anything with recent PROJ - cavern always
891 * included it, but other software generating 3d files
894 if (*p
== '\0' || strcmp(p
, " +no_defs") == 0) {
895 sprintf(cs
, "EPSG:%d", n
);
897 } else if (memcmp(p
, "merc +lat_ts=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +units=m +nadgrids=@null", 89) == 0) {
899 /* Allow +no_defs to be omitted as it seems to not
900 * actually do anything with recent PROJ - cavern always
901 * included it, but other software generating 3d files
904 if (*p
== '\0' || strcmp(p
, " +no_defs") == 0) {
905 strcpy(cs
, "EPSG:3857");
909 pimg
->cs
= my_strdup(cs
);
918 pimg
->datestamp
= getline_alloc(pimg
->fh
);
919 if (!pimg
->title
|| !pimg
->datestamp
) {
920 img_errno
= IMG_OUTOFMEMORY
;
924 osfree(pimg
->datestamp
);
925 osfree(pimg
->filename_opened
);
926 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
931 if (pimg
->version
>= 8) {
932 int flags
= GETC(pimg
->fh
);
933 if (flags
& img_FFLAG_EXTENDED
) pimg
->is_extended_elevation
= 1;
935 len
= strlen(pimg
->title
);
936 if (len
> 11 && strcmp(pimg
->title
+ len
- 11, " (extended)") == 0) {
937 pimg
->title
[len
- 11] = '\0';
938 pimg
->is_extended_elevation
= 1;
942 if (pimg
->datestamp
[0] == '@') {
946 v
= strtoul(pimg
->datestamp
+ 1, &p
, 10);
947 if (errno
== 0 && *p
== '\0')
948 pimg
->datestamp_numeric
= v
;
949 /* FIXME: We're assuming here that the C time_t epoch is 1970, which is
950 * true for Unix-like systems, macOS and Windows, but isn't guaranteed
954 /* %a,%Y.%m.%d %H:%M:%S %Z */
957 char * p
= pimg
->datestamp
;
958 while (isalpha((unsigned char)*p
)) ++p
;
960 while (isspace((unsigned char)*p
)) ++p
;
961 v
= strtoul(p
, &p
, 10);
962 if (v
== ULONG_MAX
|| *p
++ != '.')
964 tm
.tm_year
= v
- 1900;
965 v
= strtoul(p
, &p
, 10);
966 if (v
< 1 || v
> 12 || *p
++ != '.')
969 v
= strtoul(p
, &p
, 10);
970 if (v
< 1 || v
> 31 || *p
++ != ' ')
973 v
= strtoul(p
, &p
, 10);
974 if (v
>= 24 || *p
++ != ':')
977 v
= strtoul(p
, &p
, 10);
978 if (v
>= 60 || *p
++ != ':')
981 v
= strtoul(p
, &p
, 10);
986 while (isspace((unsigned char)*p
)) ++p
;
987 /* p now points to the timezone string.
989 * However, it's likely to be a string like "BST", and such strings can
990 * be ambiguous (BST could be UTC+1 or UTC+6), so it is impossible to
991 * reliably convert in all cases. Just pass what we have to tzset() - if
992 * it doesn't handle it, UTC will be used.
994 pimg
->datestamp_numeric
= mktime_with_tz(&tm
, p
);
998 pimg
->start
= ftell(pimg
->fh
);
1004 img_rewind(img
*pimg
)
1007 img_errno
= IMG_WRITEERROR
;
1010 if (fseek(pimg
->fh
, pimg
->start
, SEEK_SET
) != 0) {
1011 img_errno
= IMG_READERROR
;
1015 /* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
1016 * [version 0] not in the middle of a 'LINE' command
1017 * [version >= 3] not in the middle of turning a LINE into a MOVE */
1020 img_errno
= IMG_NONE
;
1022 /* for version >= 3 we use label_buf to store the prefix for reuse */
1023 /* for VERSION_COMPASS_PLT, 0 value indicates we haven't entered a survey
1025 /* for VERSION_CMAP_SHOT, we store the last station here to detect whether
1026 * we MOVE or LINE */
1027 pimg
->label_len
= 0;
1028 pimg
->style
= img_STYLE_UNKNOWN
;
1033 img_open_write_cs(const char *fnm
, const char *title
, const char *cs
, int flags
)
1035 if (fDirectory(fnm
)) {
1036 img_errno
= IMG_DIRECTORY
;
1040 return img_write_stream(fopen(fnm
, "wb"), fclose
, title
, cs
, flags
);
1044 img_write_stream(FILE *stream
, int (*close_func
)(FILE*),
1045 const char *title
, const char *cs
, int flags
)
1050 if (stream
== NULL
) {
1051 img_errno
= IMG_FILENOTFOUND
;
1057 img_errno
= IMG_OUTOFMEMORY
;
1058 if (close_func
) close_func(stream
);
1063 pimg
->close_func
= close_func
;
1064 pimg
->buf_len
= 257;
1065 pimg
->label_buf
= (char *)xosmalloc(pimg
->buf_len
);
1066 if (!pimg
->label_buf
) {
1067 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
1069 img_errno
= IMG_OUTOFMEMORY
;
1073 pimg
->filename_opened
= NULL
;
1075 /* Output image file header */
1076 fputs("Survex 3D Image File\n", pimg
->fh
); /* file identifier string */
1077 if (img_output_version
< 2) {
1079 fputs("Bv0.01\n", pimg
->fh
); /* binary file format version number */
1081 pimg
->version
= (img_output_version
> IMG_VERSION_MAX
) ? IMG_VERSION_MAX
: img_output_version
;
1082 fprintf(pimg
->fh
, "v%d\n", pimg
->version
); /* file format version no. */
1085 fputs(title
, pimg
->fh
);
1086 if (pimg
->version
< 8 && (flags
& img_FFLAG_EXTENDED
)) {
1087 /* Older format versions append " (extended)" to the title to mark
1088 * extended elevations. */
1089 size_t len
= strlen(title
);
1090 if (len
< 11 || strcmp(title
+ len
- 11, " (extended)") != 0)
1091 fputs(" (extended)", pimg
->fh
);
1093 if (pimg
->version
== 8 && cs
&& *cs
) {
1094 /* We sneak in an extra field after a zero byte here, containing the
1095 * specified coordinate system (if any). Older readers will just not
1096 * see it (which is fine), and this trick avoids us having to bump the
1097 * 3d format version.
1099 PUTC('\0', pimg
->fh
);
1100 fputs(cs
, pimg
->fh
);
1102 PUTC('\n', pimg
->fh
);
1105 if (tm
== (time_t)-1) {
1106 fputsnl(TIMENA
, pimg
->fh
);
1107 } else if (pimg
->version
<= 7) {
1109 /* output current date and time in format specified */
1110 strftime(date
, 256, TIMEFMT
, localtime(&tm
));
1111 fputsnl(date
, pimg
->fh
);
1113 fprintf(pimg
->fh
, "@%ld\n", (long)tm
);
1116 if (pimg
->version
>= 8) {
1117 /* Clear bit one in case anyone has been passing true for fBinary. */
1119 PUTC(flags
, pimg
->fh
);
1123 if (img_output_version
>= 5) {
1124 static const unsigned char codelengths
[32] = {
1125 4, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1128 fwrite(codelengths
, 32, 1, pimg
->fh
);
1131 pimg
->fRead
= 0; /* writing to this file */
1132 img_errno
= IMG_NONE
;
1134 /* for version >= 3 we use label_buf to store the prefix for reuse */
1135 pimg
->label_buf
[0] = '\0';
1136 pimg
->label_len
= 0;
1138 #if IMG_API_VERSION == 0
1139 pimg
->date1
= pimg
->date2
= 0;
1140 pimg
->olddate1
= pimg
->olddate2
= 0;
1141 #else /* IMG_API_VERSION == 1 */
1142 pimg
->days1
= pimg
->days2
= -1;
1143 pimg
->olddays1
= pimg
->olddays2
= -1;
1145 pimg
->style
= pimg
->oldstyle
= img_STYLE_UNKNOWN
;
1147 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1151 pimg
->E
= pimg
->H
= pimg
->V
= 0.0;
1153 /* Don't check for write errors now - let img_close() report them... */
1158 read_xyz_station_coords(img_point
*pt
, const char *line
)
1161 memcpy(num
, line
+ 6, 9);
1163 pt
->x
= atof(num
) / METRES_PER_FOOT
;
1164 memcpy(num
, line
+ 15, 9);
1165 pt
->y
= atof(num
) / METRES_PER_FOOT
;
1166 memcpy(num
, line
+ 24, 8);
1168 pt
->z
= atof(num
) / METRES_PER_FOOT
;
1172 read_xyz_shot_coords(img_point
*pt
, const char *line
)
1175 memcpy(num
, line
+ 40, 10);
1177 pt
->x
= atof(num
) / METRES_PER_FOOT
;
1178 memcpy(num
, line
+ 50, 10);
1179 pt
->y
= atof(num
) / METRES_PER_FOOT
;
1180 memcpy(num
, line
+ 60, 9);
1182 pt
->z
= atof(num
) / METRES_PER_FOOT
;
1186 subtract_xyz_shot_deltas(img_point
*pt
, const char *line
)
1189 memcpy(num
, line
+ 15, 9);
1191 pt
->x
-= atof(num
) / METRES_PER_FOOT
;
1192 memcpy(num
, line
+ 24, 8);
1194 pt
->y
-= atof(num
) / METRES_PER_FOOT
;
1195 memcpy(num
, line
+ 32, 8);
1196 pt
->z
-= atof(num
) / METRES_PER_FOOT
;
1200 read_coord(FILE *fh
, img_point
*pt
)
1204 pt
->x
= get32(fh
) / 100.0;
1205 pt
->y
= get32(fh
) / 100.0;
1206 pt
->z
= get32(fh
) / 100.0;
1207 if (ferror(fh
) || feof(fh
)) {
1208 img_errno
= feof(fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1215 skip_coord(FILE *fh
)
1217 return (fseek(fh
, 12, SEEK_CUR
) == 0);
1221 read_v3label(img
*pimg
)
1224 long len
= GETC(pimg
->fh
);
1226 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1230 len
+= get16(pimg
->fh
);
1231 if (feof(pimg
->fh
)) {
1232 img_errno
= IMG_BADFORMAT
;
1235 if (ferror(pimg
->fh
)) {
1236 img_errno
= IMG_READERROR
;
1239 } else if (len
== 0xff) {
1240 len
= get32(pimg
->fh
);
1241 if (ferror(pimg
->fh
)) {
1242 img_errno
= IMG_READERROR
;
1245 if (feof(pimg
->fh
) || len
< 0xfe + 0xffff) {
1246 img_errno
= IMG_BADFORMAT
;
1251 if (!check_label_space(pimg
, pimg
->label_len
+ len
+ 1)) {
1252 img_errno
= IMG_OUTOFMEMORY
;
1255 q
= pimg
->label_buf
+ pimg
->label_len
;
1256 pimg
->label_len
+= len
;
1257 if (len
&& fread(q
, len
, 1, pimg
->fh
) != 1) {
1258 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1266 read_v8label(img
*pimg
, int common_flag
, size_t common_val
)
1271 if (common_val
== 0) return 0;
1272 add
= del
= common_val
;
1274 int ch
= GETC(pimg
->fh
);
1276 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1283 ch
= GETC(pimg
->fh
);
1285 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1291 del
= get32(pimg
->fh
);
1292 if (ferror(pimg
->fh
)) {
1293 img_errno
= IMG_READERROR
;
1297 ch
= GETC(pimg
->fh
);
1299 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1305 add
= get32(pimg
->fh
);
1306 if (ferror(pimg
->fh
)) {
1307 img_errno
= IMG_READERROR
;
1313 if (add
> del
&& !check_label_space(pimg
, pimg
->label_len
+ add
- del
+ 1)) {
1314 img_errno
= IMG_OUTOFMEMORY
;
1318 if (del
> pimg
->label_len
) {
1319 img_errno
= IMG_BADFORMAT
;
1322 pimg
->label_len
-= del
;
1323 q
= pimg
->label_buf
+ pimg
->label_len
;
1324 pimg
->label_len
+= add
;
1325 if (add
&& fread(q
, add
, 1, pimg
->fh
) != 1) {
1326 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1333 static int img_read_item_new(img
*pimg
, img_point
*p
);
1334 static int img_read_item_v3to7(img
*pimg
, img_point
*p
);
1335 static int img_read_item_ancient(img
*pimg
, img_point
*p
);
1336 static int img_read_item_ascii_wrapper(img
*pimg
, img_point
*p
);
1337 static int img_read_item_ascii(img
*pimg
, img_point
*p
);
1340 img_read_item(img
*pimg
, img_point
*p
)
1344 if (pimg
->version
>= 8) {
1345 return img_read_item_new(pimg
, p
);
1346 } else if (pimg
->version
>= 3) {
1347 return img_read_item_v3to7(pimg
, p
);
1348 } else if (pimg
->version
>= 1) {
1349 return img_read_item_ancient(pimg
, p
);
1351 return img_read_item_ascii_wrapper(pimg
, p
);
1356 img_read_item_new(img
*pimg
, img_point
*p
)
1360 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1361 if (pimg
->pending
>= 0x40) {
1362 if (pimg
->pending
== 256) {
1364 return img_XSECT_END
;
1367 pimg
->flags
= (int)(pimg
->pending
) & 0x3f;
1371 again3
: /* label to goto if we get a prefix, date, or lrud */
1372 pimg
->label
= pimg
->label_buf
;
1373 opt
= GETC(pimg
->fh
);
1375 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1378 if (opt
>> 6 == 0) {
1380 if (opt
== 0 && pimg
->style
== 0)
1381 return img_STOP
; /* end of data marker */
1388 case 0x10: { /* No date info */
1389 #if IMG_API_VERSION == 0
1390 pimg
->date1
= pimg
->date2
= 0;
1391 #else /* IMG_API_VERSION == 1 */
1392 pimg
->days1
= pimg
->days2
= -1;
1396 case 0x11: { /* Single date */
1397 int days1
= (int)getu16(pimg
->fh
);
1398 #if IMG_API_VERSION == 0
1399 pimg
->date2
= pimg
->date1
= (days1
- 25567) * 86400;
1400 #else /* IMG_API_VERSION == 1 */
1401 pimg
->days2
= pimg
->days1
= days1
;
1405 case 0x12: { /* Date range (short) */
1406 int days1
= (int)getu16(pimg
->fh
);
1407 int days2
= days1
+ GETC(pimg
->fh
) + 1;
1408 #if IMG_API_VERSION == 0
1409 pimg
->date1
= (days1
- 25567) * 86400;
1410 pimg
->date2
= (days2
- 25567) * 86400;
1411 #else /* IMG_API_VERSION == 1 */
1412 pimg
->days1
= days1
;
1413 pimg
->days2
= days2
;
1417 case 0x13: { /* Date range (long) */
1418 int days1
= (int)getu16(pimg
->fh
);
1419 int days2
= (int)getu16(pimg
->fh
);
1420 #if IMG_API_VERSION == 0
1421 pimg
->date1
= (days1
- 25567) * 86400;
1422 pimg
->date2
= (days2
- 25567) * 86400;
1423 #else /* IMG_API_VERSION == 1 */
1424 pimg
->days1
= days1
;
1425 pimg
->days2
= days2
;
1429 case 0x1f: /* Error info */
1430 pimg
->n_legs
= get32(pimg
->fh
);
1431 pimg
->length
= get32(pimg
->fh
) / 100.0;
1432 pimg
->E
= get32(pimg
->fh
) / 100.0;
1433 pimg
->H
= get32(pimg
->fh
) / 100.0;
1434 pimg
->V
= get32(pimg
->fh
) / 100.0;
1435 return img_ERROR_INFO
;
1436 case 0x30: case 0x31: /* LRUD */
1437 case 0x32: case 0x33: /* Big LRUD! */
1438 if (read_v8label(pimg
, 0, 0) == img_BAD
) return img_BAD
;
1439 pimg
->flags
= (int)opt
& 0x01;
1441 pimg
->l
= get16(pimg
->fh
) / 100.0;
1442 pimg
->r
= get16(pimg
->fh
) / 100.0;
1443 pimg
->u
= get16(pimg
->fh
) / 100.0;
1444 pimg
->d
= get16(pimg
->fh
) / 100.0;
1446 pimg
->l
= get32(pimg
->fh
) / 100.0;
1447 pimg
->r
= get32(pimg
->fh
) / 100.0;
1448 pimg
->u
= get32(pimg
->fh
) / 100.0;
1449 pimg
->d
= get32(pimg
->fh
) / 100.0;
1451 if (!stn_included(pimg
)) {
1452 return img_XSECT_END
;
1454 /* If this is the last cross-section in this passage, set
1455 * pending so we return img_XSECT_END next time. */
1456 if (pimg
->flags
& 0x01) {
1457 pimg
->pending
= 256;
1458 pimg
->flags
&= ~0x01;
1461 default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1462 img_errno
= IMG_BADFORMAT
;
1468 /* 1-14 and 16-31 reserved */
1469 img_errno
= IMG_BADFORMAT
;
1473 } else if (opt
>= 0x80) {
1474 if (read_v8label(pimg
, 0, 0) == img_BAD
) return img_BAD
;
1478 if (!stn_included(pimg
)) {
1479 if (!skip_coord(pimg
->fh
)) return img_BAD
;
1484 pimg
->flags
= (int)opt
& 0x7f;
1485 } else if ((opt
>> 6) == 1) {
1486 if (read_v8label(pimg
, opt
& 0x20, 0) == img_BAD
) return img_BAD
;
1490 if (!survey_included(pimg
)) {
1491 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1496 if (pimg
->pending
) {
1498 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1499 pimg
->pending
= opt
;
1502 pimg
->flags
= (int)opt
& 0x1f;
1504 img_errno
= IMG_BADFORMAT
;
1507 if (!read_coord(pimg
->fh
, p
)) return img_BAD
;
1513 img_read_item_v3to7(img
*pimg
, img_point
*p
)
1517 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1518 if (pimg
->pending
== 256) {
1520 return img_XSECT_END
;
1522 if (pimg
->pending
>= 0x80) {
1524 pimg
->flags
= (int)(pimg
->pending
) & 0x3f;
1528 again3
: /* label to goto if we get a prefix, date, or lrud */
1529 pimg
->label
= pimg
->label_buf
;
1530 opt
= GETC(pimg
->fh
);
1532 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1538 if (!pimg
->label_len
) return img_STOP
; /* end of data marker */
1539 pimg
->label_len
= 0;
1543 /* 1-14 mean trim that many levels from current prefix */
1545 if (pimg
->label_len
<= 17) {
1546 /* zero prefix using "0" */
1547 img_errno
= IMG_BADFORMAT
;
1550 /* extra - 1 because label_len points to one past the end */
1551 c
= pimg
->label_len
- 17 - 1;
1552 while (pimg
->label_buf
[c
] != '.' || --opt
> 0) {
1554 /* zero prefix using "0" */
1555 img_errno
= IMG_BADFORMAT
;
1560 pimg
->label_len
= c
;
1569 case 0x20: /* Single date */
1570 if (pimg
->version
< 7) {
1571 int date1
= get32(pimg
->fh
);
1572 #if IMG_API_VERSION == 0
1573 pimg
->date2
= pimg
->date1
= date1
;
1574 #else /* IMG_API_VERSION == 1 */
1576 pimg
->days2
= pimg
->days1
= (date1
/ 86400) + 25567;
1578 pimg
->days2
= pimg
->days1
= -1;
1582 int days1
= (int)getu16(pimg
->fh
);
1583 #if IMG_API_VERSION == 0
1584 pimg
->date2
= pimg
->date1
= (days1
- 25567) * 86400;
1585 #else /* IMG_API_VERSION == 1 */
1586 pimg
->days2
= pimg
->days1
= days1
;
1590 case 0x21: /* Date range (short for v7+) */
1591 if (pimg
->version
< 7) {
1592 INT32_T date1
= get32(pimg
->fh
);
1593 INT32_T date2
= get32(pimg
->fh
);
1594 #if IMG_API_VERSION == 0
1595 pimg
->date1
= date1
;
1596 pimg
->date2
= date2
;
1597 #else /* IMG_API_VERSION == 1 */
1598 pimg
->days1
= (date1
/ 86400) + 25567;
1599 pimg
->days2
= (date2
/ 86400) + 25567;
1602 int days1
= (int)getu16(pimg
->fh
);
1603 int days2
= days1
+ GETC(pimg
->fh
) + 1;
1604 #if IMG_API_VERSION == 0
1605 pimg
->date1
= (days1
- 25567) * 86400;
1606 pimg
->date2
= (days2
- 25567) * 86400;
1607 #else /* IMG_API_VERSION == 1 */
1608 pimg
->days1
= days1
;
1609 pimg
->days2
= days2
;
1613 case 0x22: /* Error info */
1614 pimg
->n_legs
= get32(pimg
->fh
);
1615 pimg
->length
= get32(pimg
->fh
) / 100.0;
1616 pimg
->E
= get32(pimg
->fh
) / 100.0;
1617 pimg
->H
= get32(pimg
->fh
) / 100.0;
1618 pimg
->V
= get32(pimg
->fh
) / 100.0;
1619 if (feof(pimg
->fh
)) {
1620 img_errno
= IMG_BADFORMAT
;
1623 if (ferror(pimg
->fh
)) {
1624 img_errno
= IMG_READERROR
;
1627 return img_ERROR_INFO
;
1628 case 0x23: { /* v7+: Date range (long) */
1629 if (pimg
->version
< 7) {
1630 img_errno
= IMG_BADFORMAT
;
1633 int days1
= (int)getu16(pimg
->fh
);
1634 int days2
= (int)getu16(pimg
->fh
);
1635 if (feof(pimg
->fh
)) {
1636 img_errno
= IMG_BADFORMAT
;
1639 if (ferror(pimg
->fh
)) {
1640 img_errno
= IMG_READERROR
;
1643 #if IMG_API_VERSION == 0
1644 pimg
->date1
= (days1
- 25567) * 86400;
1645 pimg
->date2
= (days2
- 25567) * 86400;
1646 #else /* IMG_API_VERSION == 1 */
1647 pimg
->days1
= days1
;
1648 pimg
->days2
= days2
;
1652 case 0x24: { /* v7+: No date info */
1653 #if IMG_API_VERSION == 0
1654 pimg
->date1
= pimg
->date2
= 0;
1655 #else /* IMG_API_VERSION == 1 */
1656 pimg
->days1
= pimg
->days2
= -1;
1660 case 0x30: case 0x31: /* LRUD */
1661 case 0x32: case 0x33: /* Big LRUD! */
1662 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1663 pimg
->flags
= (int)opt
& 0x01;
1665 pimg
->l
= get16(pimg
->fh
) / 100.0;
1666 pimg
->r
= get16(pimg
->fh
) / 100.0;
1667 pimg
->u
= get16(pimg
->fh
) / 100.0;
1668 pimg
->d
= get16(pimg
->fh
) / 100.0;
1670 pimg
->l
= get32(pimg
->fh
) / 100.0;
1671 pimg
->r
= get32(pimg
->fh
) / 100.0;
1672 pimg
->u
= get32(pimg
->fh
) / 100.0;
1673 pimg
->d
= get32(pimg
->fh
) / 100.0;
1675 if (feof(pimg
->fh
)) {
1676 img_errno
= IMG_BADFORMAT
;
1679 if (ferror(pimg
->fh
)) {
1680 img_errno
= IMG_READERROR
;
1683 if (!stn_included(pimg
)) {
1684 return img_XSECT_END
;
1686 /* If this is the last cross-section in this passage, set
1687 * pending so we return img_XSECT_END next time. */
1688 if (pimg
->flags
& 0x01) {
1689 pimg
->pending
= 256;
1690 pimg
->flags
&= ~0x01;
1693 default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1694 img_errno
= IMG_BADFORMAT
;
1697 if (feof(pimg
->fh
)) {
1698 img_errno
= IMG_BADFORMAT
;
1701 if (ferror(pimg
->fh
)) {
1702 img_errno
= IMG_READERROR
;
1707 /* 16-31 mean remove (n - 15) characters from the prefix */
1708 /* zero prefix using 0 */
1709 if (pimg
->label_len
<= (size_t)(opt
- 15)) {
1710 img_errno
= IMG_BADFORMAT
;
1713 pimg
->label_len
-= (opt
- 15);
1716 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1720 if (!stn_included(pimg
)) {
1721 if (!skip_coord(pimg
->fh
)) return img_BAD
;
1726 pimg
->flags
= (int)opt
& 0x3f;
1729 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1733 if (!survey_included(pimg
)) {
1734 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1739 if (pimg
->pending
) {
1741 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1742 pimg
->pending
= opt
;
1745 pimg
->flags
= (int)opt
& 0x3f;
1748 img_errno
= IMG_BADFORMAT
;
1751 if (!read_coord(pimg
->fh
, p
)) return img_BAD
;
1757 img_read_item_ancient(img
*pimg
, img_point
*p
)
1760 static long opt_lookahead
= 0;
1761 static img_point pt
= { 0.0, 0.0, 0.0 };
1764 again
: /* label to goto if we get a cross */
1765 pimg
->label
= pimg
->label_buf
;
1766 pimg
->label
[0] = '\0';
1768 if (pimg
->version
== 1) {
1769 if (opt_lookahead
) {
1770 opt
= opt_lookahead
;
1773 opt
= get32(pimg
->fh
);
1776 opt
= GETC(pimg
->fh
);
1779 if (feof(pimg
->fh
)) {
1780 img_errno
= IMG_BADFORMAT
;
1783 if (ferror(pimg
->fh
)) {
1784 img_errno
= IMG_READERROR
;
1790 return img_STOP
; /* end of data marker */
1792 /* skip coordinates */
1793 if (!skip_coord(pimg
->fh
)) {
1794 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1801 if (!fgets(pimg
->label_buf
, pimg
->buf_len
, pimg
->fh
)) {
1802 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1805 if (pimg
->label
[0] == '\\') pimg
->label
++;
1806 len
= strlen(pimg
->label
);
1807 if (len
== 0 || pimg
->label
[len
- 1] != '\n') {
1808 img_errno
= IMG_BADFORMAT
;
1811 /* Ignore empty labels in some .3d files (caused by a bug) */
1812 if (len
== 1) goto again
;
1813 pimg
->label
[len
- 1] = '\0';
1814 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* no flags given... */
1815 if (opt
== 2) goto done
;
1823 pimg
->flags
= GETC(pimg
->fh
);
1825 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* no flags given... */
1827 len
= get32(pimg
->fh
);
1829 if (feof(pimg
->fh
)) {
1830 img_errno
= IMG_BADFORMAT
;
1833 if (ferror(pimg
->fh
)) {
1834 img_errno
= IMG_READERROR
;
1838 /* Ignore empty labels in some .3d files (caused by a bug) */
1839 if (len
== 0) goto again
;
1840 if (!check_label_space(pimg
, len
+ 1)) {
1841 img_errno
= IMG_OUTOFMEMORY
;
1844 if (fread(pimg
->label_buf
, len
, 1, pimg
->fh
) != 1) {
1845 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1848 pimg
->label_buf
[len
] = '\0';
1858 switch ((int)opt
& 0xc0) {
1860 pimg
->flags
= (int)opt
& 0x3f;
1865 pimg
->flags
= (int)opt
& 0x3f;
1867 if (!fgets(pimg
->label_buf
, pimg
->buf_len
, pimg
->fh
)) {
1868 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1871 q
= pimg
->label_buf
+ strlen(pimg
->label_buf
) - 1;
1872 /* Ignore empty-labels in some .3d files (caused by a bug) */
1873 if (q
== pimg
->label_buf
) goto again
;
1875 img_errno
= IMG_BADFORMAT
;
1882 img_errno
= IMG_BADFORMAT
;
1888 if (!read_coord(pimg
->fh
, &pt
)) return img_BAD
;
1890 if (result
== img_LABEL
&& !stn_included(pimg
)) {
1897 if (result
== img_MOVE
&& pimg
->version
== 1) {
1898 /* peek at next code and see if it's an old-style label */
1899 opt_lookahead
= get32(pimg
->fh
);
1901 if (feof(pimg
->fh
)) {
1902 img_errno
= IMG_BADFORMAT
;
1905 if (ferror(pimg
->fh
)) {
1906 img_errno
= IMG_READERROR
;
1910 if (opt_lookahead
== 2) return img_read_item_ancient(pimg
, p
);
1917 img_read_item_ascii_wrapper(img
*pimg
, img_point
*p
)
1919 /* We need to set the default locale for fscanf() to work on
1920 * numbers with "." as decimal point. */
1922 char * current_locale
= my_strdup(setlocale(LC_NUMERIC
, NULL
));
1923 setlocale(LC_NUMERIC
, "C");
1924 result
= img_read_item_ascii(pimg
, p
);
1925 setlocale(LC_NUMERIC
, current_locale
);
1926 free(current_locale
);
1930 /* Handle all ASCII formats. */
1932 img_read_item_ascii(img
*pimg
, img_point
*p
)
1935 pimg
->label
= pimg
->label_buf
;
1936 if (pimg
->version
== 0) {
1938 pimg
->label
[0] = '\0';
1939 if (feof(pimg
->fh
)) return img_STOP
;
1940 if (pimg
->pending
) {
1945 /* Stop if nothing found */
1946 if (fscanf(pimg
->fh
, "%6s", cmd
) < 1) return img_STOP
;
1947 if (strcmp(cmd
, "move") == 0)
1949 else if (strcmp(cmd
, "draw") == 0)
1951 else if (strcmp(cmd
, "line") == 0) {
1952 /* set flag to indicate to process second triplet as LINE */
1955 } else if (strcmp(cmd
, "cross") == 0) {
1956 if (fscanf(pimg
->fh
, "%lf%lf%lf", &p
->x
, &p
->y
, &p
->z
) < 3) {
1957 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1961 } else if (strcmp(cmd
, "name") == 0) {
1963 int ch
= GETC(pimg
->fh
);
1964 if (ch
== ' ') ch
= GETC(pimg
->fh
);
1966 if (ch
== '\n' || ch
== EOF
) {
1967 img_errno
= ferror(pimg
->fh
) ? IMG_READERROR
: IMG_BADFORMAT
;
1970 if (off
== pimg
->buf_len
) {
1971 if (!check_label_space(pimg
, pimg
->buf_len
* 2)) {
1972 img_errno
= IMG_OUTOFMEMORY
;
1976 pimg
->label_buf
[off
++] = ch
;
1977 ch
= GETC(pimg
->fh
);
1979 pimg
->label_buf
[off
] = '\0';
1981 pimg
->label
= pimg
->label_buf
;
1982 if (pimg
->label
[0] == '\\') pimg
->label
++;
1984 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
1988 img_errno
= IMG_BADFORMAT
;
1989 return img_BAD
; /* unknown keyword */
1993 if (fscanf(pimg
->fh
, "%lf%lf%lf", &p
->x
, &p
->y
, &p
->z
) < 3) {
1994 img_errno
= ferror(pimg
->fh
) ? IMG_READERROR
: IMG_BADFORMAT
;
1998 if (result
== img_LABEL
&& !stn_included(pimg
)) {
2003 } else if (pimg
->version
== VERSION_SURVEX_POS
) {
2004 /* Survex .pos file */
2007 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
2009 while (fscanf(pimg
->fh
, "(%lf,%lf,%lf )", &p
->x
, &p
->y
, &p
->z
) != 3) {
2010 if (ferror(pimg
->fh
)) {
2011 img_errno
= IMG_READERROR
;
2014 if (feof(pimg
->fh
)) return img_STOP
;
2015 if (pimg
->pending
) {
2016 img_errno
= IMG_BADFORMAT
;
2020 /* ignore rest of line */
2022 ch
= GETC(pimg
->fh
);
2023 } while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
);
2026 pimg
->label_buf
[0] = '\0';
2028 ch
= GETC(pimg
->fh
);
2029 } while (ch
== ' ' || ch
== '\t');
2030 if (ch
== '\n' || ch
== EOF
) {
2031 /* If there's no label, set img_SFLAG_ANON. */
2032 pimg
->flags
|= img_SFLAG_ANON
;
2035 pimg
->label_buf
[0] = ch
;
2037 while (!feof(pimg
->fh
)) {
2038 if (!fgets(pimg
->label_buf
+ off
, pimg
->buf_len
- off
, pimg
->fh
)) {
2039 img_errno
= IMG_READERROR
;
2043 off
+= strlen(pimg
->label_buf
+ off
);
2044 if (off
&& pimg
->label_buf
[off
- 1] == '\n') {
2045 pimg
->label_buf
[off
- 1] = '\0';
2048 if (!check_label_space(pimg
, pimg
->buf_len
* 2)) {
2049 img_errno
= IMG_OUTOFMEMORY
;
2054 pimg
->label
= pimg
->label_buf
;
2056 if (pimg
->label
[0] == '\\') pimg
->label
++;
2058 if (!stn_included(pimg
)) goto againpos
;
2061 } else if (pimg
->version
== VERSION_COMPASS_PLT
) {
2062 /* Compass .plt file */
2063 if (pimg
->pending
> 0) {
2064 /* -1 signals we've entered the first survey we want to
2065 * read, and need to fudge lots if the first action is 'D'...
2067 /* pending MOVE or LINE */
2068 int r
= pimg
->pending
- 4;
2071 pimg
->label
[pimg
->label_len
] = '\0';
2079 int ch
= GETC(pimg
->fh
);
2082 case '\x1a': case EOF
: /* Don't insist on ^Z at end of file */
2084 case 'X': case 'F': case 'S':
2085 /* bounding boX (marks end of survey), Feature survey, or
2086 * new Section - skip to next survey */
2087 if (pimg
->survey
) return img_STOP
;
2091 ch
= GETC(pimg
->fh
);
2092 } while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
);
2093 while (ch
== '\n' || ch
== '\r') ch
= GETC(pimg
->fh
);
2094 if (ch
== 'N') break;
2095 if (ch
== '\x1a' || ch
== EOF
) return img_STOP
;
2099 line
= getline_alloc(pimg
->fh
);
2101 img_errno
= IMG_OUTOFMEMORY
;
2104 while (line
[len
] > 32) ++len
;
2105 if (pimg
->label_len
== 0) pimg
->pending
= -1;
2106 if (!check_label_space(pimg
, len
+ 1)) {
2108 img_errno
= IMG_OUTOFMEMORY
;
2111 pimg
->label_len
= len
;
2112 pimg
->label
= pimg
->label_buf
;
2113 memcpy(pimg
->label
, line
, len
);
2114 pimg
->label
[len
] = '\0';
2117 case 'M': case 'D': {
2120 if (pimg
->survey
&& pimg
->label_len
== 0) {
2121 /* We're only holding onto this line in case the first line
2122 * of the 'N' is a 'D', so skip it for now...
2126 if (ch
== 'D' && pimg
->pending
== -1) {
2128 fpos
= ftell(pimg
->fh
) - 1;
2129 fseek(pimg
->fh
, pimg
->start
, SEEK_SET
);
2130 ch
= GETC(pimg
->fh
);
2133 /* If a file actually has a 'D' before any 'M', then
2134 * pretend the 'D' is an 'M' - one of the examples
2135 * in the docs was like this! */
2139 line
= getline_alloc(pimg
->fh
);
2141 img_errno
= IMG_OUTOFMEMORY
;
2144 /* Compass stores coordinates as North, East, Up = (y,x,z)! */
2145 if (sscanf(line
, "%lf%lf%lf", &p
->y
, &p
->x
, &p
->z
) != 3) {
2147 if (ferror(pimg
->fh
)) {
2148 img_errno
= IMG_READERROR
;
2150 img_errno
= IMG_BADFORMAT
;
2154 p
->x
*= METRES_PER_FOOT
;
2155 p
->y
*= METRES_PER_FOOT
;
2156 p
->z
*= METRES_PER_FOOT
;
2157 q
= strchr(line
, 'S');
2160 img_errno
= IMG_BADFORMAT
;
2165 while (q
[len
] > ' ') ++len
;
2167 len
+= 2; /* ' ' and '\0' */
2168 if (!check_label_space(pimg
, pimg
->label_len
+ len
)) {
2169 img_errno
= IMG_OUTOFMEMORY
;
2172 pimg
->label
= pimg
->label_buf
;
2173 if (pimg
->label_len
) {
2174 pimg
->label
[pimg
->label_len
] = ' ';
2175 memcpy(pimg
->label
+ pimg
->label_len
+ 1, q
, len
- 1);
2177 memcpy(pimg
->label
, q
, len
- 1);
2180 /* Now read LRUD. Technically, this is optional but virtually
2181 * all PLT files have it (with dummy negative values if no LRUD
2182 * was measured) and some versions of Compass can't read PLT
2185 while (*q
&& *q
<= ' ') q
++;
2187 if (sscanf(q
+ 1, "%lf%lf%lf%lf",
2188 &pimg
->l
, &pimg
->r
, &pimg
->u
, &pimg
->d
) != 4) {
2190 if (ferror(pimg
->fh
)) {
2191 img_errno
= IMG_READERROR
;
2193 img_errno
= IMG_BADFORMAT
;
2197 pimg
->l
*= METRES_PER_FOOT
;
2198 pimg
->r
*= METRES_PER_FOOT
;
2199 pimg
->u
*= METRES_PER_FOOT
;
2200 pimg
->d
*= METRES_PER_FOOT
;
2202 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1;
2205 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
2207 fseek(pimg
->fh
, fpos
, SEEK_SET
);
2209 pimg
->pending
= (ch
== 'M' ? img_MOVE
: img_LINE
) + 4;
2214 img_errno
= IMG_BADFORMAT
;
2219 /* CMAP .xyz file */
2224 if (pimg
->pending
) {
2225 /* pending MOVE or LINE or LABEL or STOP */
2226 int r
= pimg
->pending
- 4;
2227 /* Set label to empty - don't use "" as we adjust label relative
2228 * to label_buf when label_buf is reallocated. */
2229 pimg
->label
= pimg
->label_buf
+ strlen(pimg
->label_buf
);
2231 if (r
== img_LABEL
) {
2233 read_xyz_shot_coords(p
, pimg
->label_buf
+ 16);
2234 subtract_xyz_shot_deltas(p
, pimg
->label_buf
+ 16);
2235 pimg
->pending
= img_STOP
+ 4;
2241 if (r
== img_STOP
) {
2243 read_xyz_shot_coords(p
, pimg
->label_buf
+ 16);
2250 pimg
->label
= pimg
->label_buf
;
2253 if (feof(pimg
->fh
)) return img_STOP
;
2254 line
= getline_alloc(pimg
->fh
);
2256 img_errno
= IMG_OUTOFMEMORY
;
2259 } while (line
[0] == ' ' || line
[0] == '\0');
2260 if (line
[0] == '\x1a') return img_STOP
;
2263 if (pimg
->version
== VERSION_CMAP_STATION
) {
2264 /* station variant */
2267 img_errno
= IMG_BADFORMAT
;
2270 memcpy(pimg
->label
, line
, 6);
2271 q
= (char *)memchr(pimg
->label
, ' ', 6);
2272 if (!q
) q
= pimg
->label
+ 6;
2275 read_xyz_station_coords(p
, line
);
2277 /* FIXME: look at prev for lines (line + 32, 5) */
2278 /* FIXME: duplicate stations... */
2281 /* Shot variant (VERSION_CMAP_SHOT) */
2282 char old
[8], new_
[8];
2285 img_errno
= IMG_BADFORMAT
;
2289 memcpy(old
, line
, 7);
2290 q
= (char *)memchr(old
, ' ', 7);
2291 if (!q
) q
= old
+ 7;
2294 memcpy(new_
, line
+ 7, 7);
2295 q
= (char *)memchr(new_
, ' ', 7);
2296 if (!q
) q
= new_
+ 7;
2299 pimg
->flags
= img_SFLAG_UNDERGROUND
;
2301 if (strcmp(old
, new_
) == 0) {
2302 pimg
->pending
= img_MOVE
+ 4;
2303 read_xyz_shot_coords(p
, line
);
2304 strcpy(pimg
->label
, new_
);
2309 if (strcmp(old
, pimg
->label
) == 0) {
2310 pimg
->pending
= img_LINE
+ 4;
2311 read_xyz_shot_coords(p
, line
);
2312 strcpy(pimg
->label
, new_
);
2317 pimg
->pending
= img_LABEL
+ 4;
2318 read_xyz_shot_coords(p
, line
);
2319 strcpy(pimg
->label
, new_
);
2320 memcpy(pimg
->label
+ 16, line
, 70);
2329 write_coord(FILE *fh
, double x
, double y
, double z
)
2333 static INT32_T X_
, Y_
, Z_
;
2334 INT32_T X
= my_lround(x
* 100.0);
2335 INT32_T Y
= my_lround(y
* 100.0);
2336 INT32_T Z
= my_lround(z
* 100.0);
2344 X_
= X
; Y_
= Y
; Z_
= Z
;
2348 write_v3label(img
*pimg
, int opt
, const char *s
)
2352 /* find length of common prefix */
2354 for (len
= 0; s
[len
] == pimg
->label_buf
[len
] && s
[len
] != '\0'; len
++) {
2355 if (s
[len
] == '.') dot
= len
+ 1;
2358 SVX_ASSERT(len
<= pimg
->label_len
);
2359 n
= pimg
->label_len
- len
;
2361 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2362 } else if (n
<= 16) {
2363 if (n
) PUTC(n
+ 15, pimg
->fh
);
2364 } else if (dot
== 0) {
2365 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2368 const char *p
= pimg
->label_buf
+ dot
;
2370 for (len
= pimg
->label_len
- dot
- 17; len
; len
--) {
2371 if (*p
++ == '.') n
++;
2377 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2382 n
= strlen(s
+ len
);
2383 PUTC(opt
, pimg
->fh
);
2386 } else if (n
< 0xffff + 0xfe) {
2387 PUTC(0xfe, pimg
->fh
);
2388 put16((short)(n
- 0xfe), pimg
->fh
);
2390 PUTC(0xff, pimg
->fh
);
2393 fwrite(s
+ len
, n
, 1, pimg
->fh
);
2396 pimg
->label_len
= n
;
2397 if (!check_label_space(pimg
, n
+ 1))
2398 return 0; /* FIXME: distinguish out of memory... */
2399 memcpy(pimg
->label_buf
+ len
, s
+ len
, n
- len
+ 1);
2401 return !ferror(pimg
->fh
);
2405 write_v8label(img
*pimg
, int opt
, int common_flag
, size_t common_val
,
2408 size_t len
, del
, add
;
2410 /* find length of common prefix */
2411 for (len
= 0; s
[len
] == pimg
->label_buf
[len
] && s
[len
] != '\0'; len
++) {
2414 SVX_ASSERT(len
<= pimg
->label_len
);
2415 del
= pimg
->label_len
- len
;
2416 add
= strlen(s
+ len
);
2418 if (add
== common_val
&& del
== common_val
) {
2419 PUTC(opt
| common_flag
, pimg
->fh
);
2421 PUTC(opt
, pimg
->fh
);
2422 if (del
<= 15 && add
<= 15 && (del
|| add
)) {
2423 PUTC((del
<< 4) | add
, pimg
->fh
);
2425 PUTC(0x00, pimg
->fh
);
2427 PUTC(del
, pimg
->fh
);
2429 PUTC(0xff, pimg
->fh
);
2430 put32(del
, pimg
->fh
);
2433 PUTC(add
, pimg
->fh
);
2435 PUTC(0xff, pimg
->fh
);
2436 put32(add
, pimg
->fh
);
2442 fwrite(s
+ len
, add
, 1, pimg
->fh
);
2444 pimg
->label_len
= len
+ add
;
2445 if (add
> del
&& !check_label_space(pimg
, pimg
->label_len
+ 1))
2446 return 0; /* FIXME: distinguish out of memory... */
2448 memcpy(pimg
->label_buf
+ len
, s
+ len
, add
+ 1);
2450 return !ferror(pimg
->fh
);
2454 img_write_item_date_new(img
*pimg
)
2457 /* Only write dates when they've changed. */
2458 #if IMG_API_VERSION == 0
2459 if (pimg
->date1
== pimg
->olddate1
&& pimg
->date2
== pimg
->olddate2
)
2462 same
= (pimg
->date1
== pimg
->date2
);
2463 unset
= (pimg
->date1
== 0);
2464 #else /* IMG_API_VERSION == 1 */
2465 if (pimg
->days1
== pimg
->olddays1
&& pimg
->days2
== pimg
->olddays2
)
2468 same
= (pimg
->days1
== pimg
->days2
);
2469 unset
= (pimg
->days1
== -1);
2474 PUTC(0x10, pimg
->fh
);
2476 PUTC(0x11, pimg
->fh
);
2477 #if IMG_API_VERSION == 0
2478 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2479 #else /* IMG_API_VERSION == 1 */
2480 put16(pimg
->days1
, pimg
->fh
);
2484 #if IMG_API_VERSION == 0
2485 int diff
= (pimg
->date2
- pimg
->date1
) / 86400;
2486 if (diff
> 0 && diff
<= 256) {
2487 PUTC(0x12, pimg
->fh
);
2488 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2489 PUTC(diff
- 1, pimg
->fh
);
2491 PUTC(0x13, pimg
->fh
);
2492 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2493 put16(pimg
->date2
/ 86400 + 25567, pimg
->fh
);
2495 #else /* IMG_API_VERSION == 1 */
2496 int diff
= pimg
->days2
- pimg
->days1
;
2497 if (diff
> 0 && diff
<= 256) {
2498 PUTC(0x12, pimg
->fh
);
2499 put16(pimg
->days1
, pimg
->fh
);
2500 PUTC(diff
- 1, pimg
->fh
);
2502 PUTC(0x13, pimg
->fh
);
2503 put16(pimg
->days1
, pimg
->fh
);
2504 put16(pimg
->days2
, pimg
->fh
);
2508 #if IMG_API_VERSION == 0
2509 pimg
->olddate1
= pimg
->date1
;
2510 pimg
->olddate2
= pimg
->date2
;
2511 #else /* IMG_API_VERSION == 1 */
2512 pimg
->olddays1
= pimg
->days1
;
2513 pimg
->olddays2
= pimg
->days2
;
2518 img_write_item_date(img
*pimg
)
2521 /* Only write dates when they've changed. */
2522 #if IMG_API_VERSION == 0
2523 if (pimg
->date1
== pimg
->olddate1
&& pimg
->date2
== pimg
->olddate2
)
2526 same
= (pimg
->date1
== pimg
->date2
);
2527 unset
= (pimg
->date1
== 0);
2528 #else /* IMG_API_VERSION == 1 */
2529 if (pimg
->days1
== pimg
->olddays1
&& pimg
->days2
== pimg
->olddays2
)
2532 same
= (pimg
->days1
== pimg
->days2
);
2533 unset
= (pimg
->days1
== -1);
2537 if (img_output_version
< 7) {
2538 PUTC(0x20, pimg
->fh
);
2539 #if IMG_API_VERSION == 0
2540 put32(pimg
->date1
, pimg
->fh
);
2541 #else /* IMG_API_VERSION == 1 */
2542 put32((pimg
->days1
- 25567) * 86400, pimg
->fh
);
2546 PUTC(0x24, pimg
->fh
);
2548 PUTC(0x20, pimg
->fh
);
2549 #if IMG_API_VERSION == 0
2550 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2551 #else /* IMG_API_VERSION == 1 */
2552 put16(pimg
->days1
, pimg
->fh
);
2557 if (img_output_version
< 7) {
2558 PUTC(0x21, pimg
->fh
);
2559 #if IMG_API_VERSION == 0
2560 put32(pimg
->date1
, pimg
->fh
);
2561 put32(pimg
->date2
, pimg
->fh
);
2562 #else /* IMG_API_VERSION == 1 */
2563 put32((pimg
->days1
- 25567) * 86400, pimg
->fh
);
2564 put32((pimg
->days2
- 25567) * 86400, pimg
->fh
);
2567 #if IMG_API_VERSION == 0
2568 int diff
= (pimg
->date2
- pimg
->date1
) / 86400;
2569 if (diff
> 0 && diff
<= 256) {
2570 PUTC(0x21, pimg
->fh
);
2571 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2572 PUTC(diff
- 1, pimg
->fh
);
2574 PUTC(0x23, pimg
->fh
);
2575 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2576 put16(pimg
->date2
/ 86400 + 25567, pimg
->fh
);
2578 #else /* IMG_API_VERSION == 1 */
2579 int diff
= pimg
->days2
- pimg
->days1
;
2580 if (diff
> 0 && diff
<= 256) {
2581 PUTC(0x21, pimg
->fh
);
2582 put16(pimg
->days1
, pimg
->fh
);
2583 PUTC(diff
- 1, pimg
->fh
);
2585 PUTC(0x23, pimg
->fh
);
2586 put16(pimg
->days1
, pimg
->fh
);
2587 put16(pimg
->days2
, pimg
->fh
);
2592 #if IMG_API_VERSION == 0
2593 pimg
->olddate1
= pimg
->date1
;
2594 pimg
->olddate2
= pimg
->date2
;
2595 #else /* IMG_API_VERSION == 1 */
2596 pimg
->olddays1
= pimg
->days1
;
2597 pimg
->olddays2
= pimg
->days2
;
2602 img_write_item_new(img
*pimg
, int code
, int flags
, const char *s
,
2603 double x
, double y
, double z
);
2605 img_write_item_v3to7(img
*pimg
, int code
, int flags
, const char *s
,
2606 double x
, double y
, double z
);
2608 img_write_item_ancient(img
*pimg
, int code
, int flags
, const char *s
,
2609 double x
, double y
, double z
);
2612 img_write_item(img
*pimg
, int code
, int flags
, const char *s
,
2613 double x
, double y
, double z
)
2616 if (pimg
->version
>= 8) {
2617 img_write_item_new(pimg
, code
, flags
, s
, x
, y
, z
);
2618 } else if (pimg
->version
>= 3) {
2619 img_write_item_v3to7(pimg
, code
, flags
, s
, x
, y
, z
);
2621 img_write_item_ancient(pimg
, code
, flags
, s
, x
, y
, z
);
2626 img_write_item_new(img
*pimg
, int code
, int flags
, const char *s
,
2627 double x
, double y
, double z
)
2631 write_v8label(pimg
, 0x80 | flags
, 0, -1, s
);
2634 INT32_T l
, r
, u
, d
, max_dim
;
2635 img_write_item_date_new(pimg
);
2636 l
= (INT32_T
)my_lround(pimg
->l
* 100.0);
2637 r
= (INT32_T
)my_lround(pimg
->r
* 100.0);
2638 u
= (INT32_T
)my_lround(pimg
->u
* 100.0);
2639 d
= (INT32_T
)my_lround(pimg
->d
* 100.0);
2644 max_dim
= max(max(l
, r
), max(u
, d
));
2645 flags
= (flags
& img_XFLAG_END
) ? 1 : 0;
2646 if (max_dim
>= 32768) flags
|= 2;
2647 write_v8label(pimg
, 0x30 | flags
, 0, -1, s
);
2649 /* Big passage! Need to use 4 bytes. */
2666 img_write_item_date_new(pimg
);
2667 if (pimg
->style
!= pimg
->oldstyle
) {
2668 switch (pimg
->style
) {
2669 case img_STYLE_NORMAL
:
2670 case img_STYLE_DIVING
:
2671 case img_STYLE_CARTESIAN
:
2672 case img_STYLE_CYLPOLAR
:
2673 case img_STYLE_NOSURVEY
:
2674 PUTC(pimg
->style
, pimg
->fh
);
2677 pimg
->oldstyle
= pimg
->style
;
2679 write_v8label(pimg
, 0x40 | flags
, 0x20, 0x00, s
? s
: "");
2681 default: /* ignore for now */
2684 write_coord(pimg
->fh
, x
, y
, z
);
2688 img_write_item_v3to7(img
*pimg
, int code
, int flags
, const char *s
,
2689 double x
, double y
, double z
)
2693 write_v3label(pimg
, 0x40 | flags
, s
);
2696 INT32_T l
, r
, u
, d
, max_dim
;
2697 /* Need at least version 5 for img_XSECT. */
2698 if (pimg
->version
< 5) return;
2699 img_write_item_date(pimg
);
2700 l
= (INT32_T
)my_lround(pimg
->l
* 100.0);
2701 r
= (INT32_T
)my_lround(pimg
->r
* 100.0);
2702 u
= (INT32_T
)my_lround(pimg
->u
* 100.0);
2703 d
= (INT32_T
)my_lround(pimg
->d
* 100.0);
2708 max_dim
= max(max(l
, r
), max(u
, d
));
2709 flags
= (flags
& img_XFLAG_END
) ? 1 : 0;
2710 if (max_dim
>= 32768) flags
|= 2;
2711 write_v3label(pimg
, 0x30 | flags
, s
);
2713 /* Big passage! Need to use 4 bytes. */
2730 if (pimg
->version
>= 4) {
2731 img_write_item_date(pimg
);
2733 write_v3label(pimg
, 0x80 | flags
, s
? s
: "");
2735 default: /* ignore for now */
2738 write_coord(pimg
->fh
, x
, y
, z
);
2742 img_write_item_ancient(img
*pimg
, int code
, int flags
, const char *s
,
2743 double x
, double y
, double z
)
2747 SVX_ASSERT(pimg
->version
> 0);
2750 if (pimg
->version
== 1) {
2751 /* put a move before each label */
2752 img_write_item_ancient(pimg
, img_MOVE
, 0, NULL
, x
, y
, z
);
2754 fputsnl(s
, pimg
->fh
);
2758 if (len
> 255 || strchr(s
, '\n')) {
2759 /* long label - not in early incarnations of v2 format, but few
2760 * 3d files will need these, so better not to force incompatibility
2761 * with a new version I think... */
2763 PUTC(flags
, pimg
->fh
);
2764 put32(len
, pimg
->fh
);
2767 PUTC(0x40 | (flags
& 0x3f), pimg
->fh
);
2768 fputsnl(s
, pimg
->fh
);
2776 if (pimg
->version
> 1) {
2777 opt
= 0x80 | (flags
& 0x3f);
2782 default: /* ignore for now */
2785 if (pimg
->version
== 1) {
2786 put32(opt
, pimg
->fh
);
2788 if (opt
) PUTC(opt
, pimg
->fh
);
2790 write_coord(pimg
->fh
, x
, y
, z
);
2793 /* Write error information for the current traverse
2794 * n_legs is the number of legs in the traverse
2795 * length is the traverse length (in m)
2796 * E is the ratio of the observed misclosure to the theoretical one
2797 * H is the ratio of the observed horizontal misclosure to the theoretical one
2798 * V is the ratio of the observed vertical misclosure to the theoretical one
2801 img_write_errors(img
*pimg
, int n_legs
, double length
,
2802 double E
, double H
, double V
)
2804 PUTC((pimg
->version
>= 8 ? 0x1f : 0x22), pimg
->fh
);
2805 put32(n_legs
, pimg
->fh
);
2806 put32((INT32_T
)my_lround(length
* 100.0), pimg
->fh
);
2807 put32((INT32_T
)my_lround(E
* 100.0), pimg
->fh
);
2808 put32((INT32_T
)my_lround(H
* 100.0), pimg
->fh
);
2809 put32((INT32_T
)my_lround(V
* 100.0), pimg
->fh
);
2813 img_close(img
*pimg
)
2819 osfree(pimg
->survey
);
2820 osfree(pimg
->title
);
2822 osfree(pimg
->datestamp
);
2824 /* write end of data marker */
2825 switch (pimg
->version
) {
2827 put32((INT32_T
)-1, pimg
->fh
);
2830 if (pimg
->version
<= 7 ?
2831 (pimg
->label_len
!= 0) :
2832 (pimg
->style
!= img_STYLE_NORMAL
)) {
2841 if (ferror(pimg
->fh
)) result
= 0;
2842 if (pimg
->close_func
&& pimg
->close_func(pimg
->fh
))
2844 if (!result
) img_errno
= pimg
->fRead
? IMG_READERROR
: IMG_WRITEERROR
;
2846 osfree(pimg
->label_buf
);
2847 osfree(pimg
->filename_opened
);