The AutoCAD 12 DWG file format

This page describes the DWG format used by AutoCAD version 12. The format is described in two complementary ways. One consist of a Binary File Format grammer, and the other consists of a C program which can parse a DWG file. AutoCAD versions up to version 12, all used the format very similar to the version described here. AutoCAD version 13, and later, started using a completely different version. I made some attempts to crack the AutoCAD R13 format.

A number of CAD vendors have decided to form the "http://www.opendwg.org/" OpenDWG Alliance, which aims at making the DWG format available to everybody who joins them.


DWG file format

An attempt to specify the DWG (R12) file format using the
BFF grammar for binary files.

Acknowledgements

I would like to thank Reini Urban for his contributions.

Definition of the elementary elements

typedef word word :=
     byte : b1, byte : b2
     return (word)f | ((word)s << 8).
typedef longword longp :=
     byte : b1, byte : b2, byte : b3, byte : b4
     return (longword)b1 | ((longword)b2 << 8)
          | ((longword)b3 << 16) | ((longword)b4 << 24).
typedef longword longword :=
     byte : b1, byte : b2, byte : b3, byte : b4
     return (longword)b1 | ((longword)b2 << 8)
          | ((longword)b3 << 16) | ((longword)b4 << 24).

Definition of the whole file

root dwg_file :=
 [begin : end](  
  char[12] : version,
  byte, word, word, word, byte,
  longp : p_entities,  longp : p_entend, 
  longp : p_blocksec,  byte[4],  longp : p_bsend,  byte[4],
  tablepos : block_table,
  tablepos : layer_table,
  tablepos : style_table,
  tablepos : ltype_table,
  tablepos : view_table,
  header,  [cur : 0x3EF]byte*,
  tablepos : ucs_table,  [cur : 0x500]byte*,
  tablepos : vport_table,  byte[8],
  tablepos : appid_table,  byte[6],
  tablepos : dimstyle_table,  [cur : 0x69F]byte*,
  tablepos : p13_table,  bytes[38],
  [p_entities : p_entend]entities : ents,   byte[19],
  [block_table.start : ]blocks : block_table,
  [layer_table.start : ]layers : layer_table,
  [style_table.start : ]styles : style_table,
  [ltype_table.start : ]ltypes : ltype_table,
  [view_table.start : ]table : view_table,
  [ucs_table.start : ]table : ucs_table,
  [vport_table.start : vport_table.end]table : vport_table,
  [appid_table.start : ]appids : appid_table,
  [dimstyle_table.start : ]table : dimstyle_table,
  [p13_table.start : ]table : p13_table,
  [p_blocksec : p_bsend]entities : blocks,  bytes[36],
  longp = p_entities,  longp = p_entend,
  longp = blocksec,  longp = bsend,
  bytes[12],
  bytes[6],
  longp = block_table.start,   bytes[6],
  longp = layer_table.start,   bytes[6],
  longp = style_table.start,   bytes[6],
  longp = ltype_table.start,   bytes[6]
  longp = view_table.start,   bytes[6],
  longp = ucs_table.start,   bytes[6],
  longp = vport_table.start,   bytes[6],
  longp = appid_table.start,   bytes[6],
  longp = dimstyle_table.start,   bytes[6],
  longp = p13_table.start,   bytes[6],
  longp  bytes*, 
 ).

A table position

tablepos :=
   word : size,
   long : nr,
   long : start,

The header

header :=
   word,
   point(TRUE) : inbase,
   point(TRUE) : extmin,
   point(TRUE) : extmax,
   point(FALSE) : limmin,
   point(FALSE) : limmax,
   double[4], 
   byte[2],
   double[2],
   byte[56],
   double[3],
   byte[18],
   double .

The block table

blocks :=
  ( [size](
      byte : flag,
      char[32] : name,
      word : used,
      byte, word, byte, word,
      check_2
    )
  )[nr] : blocks_info,
  check_32.

check_2 := byte[2].
check_32 := byte[32].

The layer table

layers :=
  ( [size](
      byte : flag,
      char[32] : name,
      word : used,
      word : color,
      word : style,     
      check_2
    )
  )[nr] : layer_info,
  check_32.

The style table

styles :=
  ( [size](
      byte : flag,
      char[32] : name,
      word, double[3], byte, double, char[128],
      check_2
    )
  )[nr] : style_info,
  check_32.

The line-type table

ltypes :=
  ( [size](
      byte : flag,
      char[32] : name,
      word,  char[48],  byte,
      byte,  double[13],
      check_2
    )
  )[nr] : ltype_info,
  check_32 .

The application identifier table

appids :=
  ( [size](
      byte : flag,
      char[32] : name,
      word,
      check_2
    )
  )[nr] : appid_info,
  check_32 .

The other tables

table :=
  ( [size](
      byte : flag,
      [size - 3]byte*,
      check_2
    )
  )[nr], 
  check_32 .

The entities

(Experimental)
entities :=
  ( byte : kind,
    byte : flag,
    word : length,
    [length - 4](
      word : layer,
      word : opts,
      if (flag & 1) then byte : color else color = 0 fi,
      if (flag & 0x40) then byte : extra else extra = 0 fi,
      if (extra & 2) then xdata fi,
      if (flag & 2) then word : type fi,
      if (flag & 4 && kind > 2 && kind != 22) then double : z fi,
      if (flag & 8) then double : th fi,
      if (flag & 0x20) then handle fi,
      if (extra & 4) then word : paper fi,
      switch (kind)
      case 1:  /* LINE */
        point(!(flag & 4)) : l10,
        point(!(flag & 4)) : l11,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi,
      case 2:  /* POINT */
        point(!(flag & 4)) : l10,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi,
      case 3:  /* CIRCLE */
        point(FALSE) : l10,
        double : l40,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi,
      case 4:  /* SHAPE */
        point(FALSE) : l10,
        word : l2,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi,
      case 7: /* TEXT */
        point(FALSE) : l10,
        double : l40,
        string : l1,
        if (opts & 1) then double : l50 fi,
        if (opts & 2) then double : l41 fi,
        if (opts & 4) then double : l51 fi,             /*?*/
        if (opts & 8) then byte : l7 fi,
        if (opts & 0x10) then byte : l71 fi,
        if (opts & 0x20) then byte : l72 fi,
        if (opts & 0x40) then point(FALSE) : l11 fi,
        if (opts & 0x100) then byte : l73 fi,
      case 8:  /* ARC */
        point(FALSE) : l10,
        double : l40,
        double : l50,
        double : l51,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi,
      case 9:   /* TRACE */
        point(FALSE) : l10,
        point(FALSE) : l11,
        point(FALSE) : l12,
        point(FALSE) : l13,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi,
      case 11:   /* SOLID */
        point(FALSE) : l11,
        point(FALSE) : l12,
        point(FALSE) : l13,
        point(FALSE) : l14,
        if (opts & 1) then point(TRUE) : l210 fi,
        if (opts & 2) then double : l38 fi
      case 12:   /* BLOCK */
        point(FALSE) : l10,                /*?*/
        string : l1,                       /* if (opts & 1) then ? */
        if (opts & 2) then string : l3 fi  
      case 13:  /* ENDBLK */
      case 14:   /* INSERT */
        word : l1,
        point(FALSE) : l10,
        if (opts & 1) then double : l41 fi,
        if (opts & 2) then double : l42 fi,
        if (opts & 4) then double : l43 fi,
        if (opts & 8) then double : l50 fi,
        if (opts & 0x10) then word : l70 fi,               
        if (opts & 0x20) then word : l71 fi,               
        if (opts & 0x40) then double : l44 fi,              /*?*/
        if (opts & 0x80) then double : l45 fi              /*?*/
      case 15:    /* ATTDEF */
        point(FALSE) : l10,
        double : l40,
        string : l1,
        string : l3,
        string : l2,
        byte : l70,
        if (opts & 1) then byte : l73 fi,           /*?*/
        if (opts & 2) then double : l50 fi,         /*?*/
        if (opts & 4) then double : l41 fi,
        if (opts & 8) then double : l42 fi,
        if (opts & 0x10) then byte : l7 fi,
        if (opts & 0x20) then byte : l71 fi,
        if (opts & 0x40) then byte : l72 fi,
        if (opts & 0x80) then point(FALSE) : l11 fi,   /*?*/
        if (opts & 0x100) then point(TRUE) : l210 fi,
        if (opts & 0x200) then double : l38 fi        /*?*/
      case 16:   /* ATTRIB */
        point(FALSE) : l10,
        double : l40,
        string : l1,
        string : l2,
        byte : l70,
        if (opts & 1) then byte : l73 fi,           /*?*/
        if (opts & 2) then double : l50 fi,         /*?*/
        if (opts & 4) then double : l41 fi,
        if (opts & 8) then double : l42 fi,
        if (opts & 0x10) then byte : l7 fi,
        if (opts & 0x20) then byte : l71 fi,
        if (opts & 0x40) then byte : l72 fi,
        if (opts & 0x80) then point(FALSE) : l11 fi,   /*?*/
        if (opts & 0x100) then point(TRUE) : l210 fi,
        if (opts & 0x200) then double : l38 fi        /*?*/
      case 17:   /* S/BEND */
        long
      case 19:   /* PLINE */
        if (opts & 1) then byte : l70 fi,
        if (opts & 2) then double : l40 fi,                   /*?*/
        if (opts & 4) then byte : l71 fi,         /*?*/
        if (opts & 8) then byte : l72 fi,         /*?*/
        if (opts & 0x10) then byte : l73 fi,         /*?*/
        if (opts & 0x20) then byte : l74 fi,         /*?*/
        if (opts & 0x40) then byte : l75 fi         /*?*/
      case 20:   /* VERTEX */
        point(FALSE) : l10,
        if (opts & 1) then double : l40 fi,           /*?*/
        if (opts & 2) then double : l41 fi,           /*?*/
        if (opts & 4) then byte : l70 fi,             /*?*/
        if (opts & 8) then double : l50 fi           /*?*/
      case 22:   /* 3DFACE */
        point(!(flag & 4)) : l10,
        point(!(flag & 4)) : l11,
        point(!(flag & 4)) : l12,
        point(!(flag & 4)) : l13
      case 23:   /* DIM */
        word : l1,
        point(TRUE) : l10,
        point(FALSE) : l11,   /*?*/
        if (opts & 2) then byte : l70 fi,
        if (opts & 1) then point(TRUE) : l12 fi,    /*?*/
        if (opts & 4) then string : l1 fi,
        if (opts & 8) then point(TRUE) : l13 fi,
        if (opts & 0x10) then point(TRUE) : l14 fi,
        if (opts & 0x20) then point(TRUE) : l15 fi,
        if (opts & 0x40) then point(TRUE) : l16 fi,
        if (opts & 0x80) then double : l40 fi,
        if (opts & 0x100) then double : l50 fi,
        if (opts & 0x200) then double : l51 fi,
        if (opts & 0x400) then double : l52 fi,
        if (opts & 0x800) then double : l53 fi
      case 24:   /* VPORT */
        point(TRUE) : l10,
        double : l40,
        double : l41,
        word : l68
      endswitch
      check_2
    )
  )* : entities.
Still need to define xdata and handle.


BFF: A grammar for Binary File Formats

With a growing number of binary formats that are being used, there is a need for specifying these formats in a well-defined way. Context free grammars have been used to specify the syntax of programming langauges. To use a grammar for binary file formats seems to be a logical choice.

In this page such a grammar, named BFF, is described. It has several construct that are not traditionally found in context free grammars for programming language. Due to the nature of binary file formats, it is important to be able to reference information that has been read before. For example, a string of characters might be preceded by a number that indicates the lengt of the string.

The terminal symbols of the grammar consist of a number of bytes, representing one of the basic data types, such as: char, short int, long int, float and double. Differences in byte ordering for integers, and the different formats for floating point numbers should be taken into account.

Due to the nature of binary formats, it is not too restrictive to use only recursive descent grammars, e.i., grammars that can be parsed top-down, and belong to the LL(1) class.

The BFF is tested for the DWG file format. As we start with this file format, BFF will naturally first focus on the requirements based on this format, and because of this it will be slightly biased.

To specify the grammar of BFF we will use a form of extended BNF.

Tool support for BFF

The first tool I am thinking about is a program that can read the grammar and apply this to a given binary file, resulting in an annotated output.

This tool should also support the reverse engineering of binary file formats.

In a later state, a tool could be made that generates a parser and the needed data structures for reading a binary file into memory according to the grammar. As it is not always required (nor possible) to read the whole file into memory, it should be possible to generate procedures to read the file interactively.

The form of the BFF grammar

A grammar that describes a Binary File Format consists of the specification of the elementary units of data, and the rules by which these should be grouped together.

The elementary units

We assume that a Binary File can be viewed as a stream of bytes (as this is the most commonly used unit of data). Usually a number of bytes are grouped together to form data values that cannot be represented by a single byte. To specify a word value consisting of two bytes, for example, we propose the following defintion style:
type  word := 
    byte : first, 
    byte : second
    return ((word)first | ((word)second << 8)).
A word representation where the lower order byte comes before the higher order is usually used by small Endian processors. The expression used on will be based on C. We assume that the following types have been defined on top of the default types of C:
typedef unsigned char       byte;
typedef unsigned short int  word;
typedef unsigned long int   longword;
This leeds us to the definition of the basic types that will be supported:
C_data_types :=
    "char"  | "byte" | 
    "short" | "word" |
    "long"  | "longword" |
    "float" | "double" .
(We assume for the moment that float and double represent floating numbers of 4, respectively 10 bytes.) The grammar of the rule used for defining types is:
type_def_rule :=
     "typedef" C_data_type basic_type_name ":="
     ("byte" ":" byte_name) LIST
     "return" expr "." .
Here expr stands for C-like expression using the byte_names as they are used in the rule. The basic_type_names should not be confused with the C_type_names. It is possible that the same name is both used as a basic_type_name and a C_type_name.

The rules

The grammar that specifies in which order elementary units are taken from a binary file, makes use of non-terminal symbols and rules for each non-terminal symbol. There will be one non-terminal symbol that will parse the whole binary file, which will be called the root non-terminal symbol. For each non-terminal symbol there has to be a rule describing the elements it consists of, where each element is either an elementary elements or a non-terminal symbols. The rule of the root non-terminal symbol comes as the first rule, and is preceded with the word `root'. The whole BFF grammar follows the following grammar:
BFF_grammar := 
     type_def_rule SEQ
     "root" rule
     rule SEQ.
Each rule has a non-terminal symbol on the left-hand side, and a list of elements on the right-hand side. Each element is either a elementary element, a non-terminal symbol, or a grouping of elements. Because BFF assumes a top-down parsing method, it is possible to give each non-terminal symbol a number of parameters. This leads to the following grammar for the rules:
rule :=  
      non_term_name  ( "(" param LIST ")" )OPT
      ":=" elem LIST ".".
Each element consist of the following parts: The following grammar describes an element:
elem := range OPT
        data_type 
        ( "[" expr "]"
        | "*" )
        ( ":" elem_name )OPT
        ( "=" C_expr )OPT.

range := "[" file_pos (":" file_pos)OPT "]".

file_pos := "begin" | "end" | "cur" | expr.

data_type := "(" elem LIST ")"
           | basic_type_name 
           | non_term_name  ( "(" expr LIST ")" )OPT.


A C program for parsing DWG 12 files

/* Scan DWG program -- for scanning a DWG file
   Copyright (C) 1995, 1996 Frans Faase, Reini Urban

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Authors:
   Frans Faase,  
   Reini Urban,  rurban at sbox.tu-graz.ac.at

Contributions by:
   Stan Hunkins,  shunkins@msn.com

GNU General Public License:
   http://home.wxs.nl/~faase009/GNU.txt
 */

/*-----------------22.10.95 22:59-------------------
changed by Reini Urban:
 new features:
  Julian dates formatted
  DIMSTYLE Table section (former P12)
  VIEW Table section     (former P8)
  UCS Table section      (former P9)
  VPORT Table section
  found some header vars from the active VPORT at scan_header()
  changed ADS to APPID in table section
  group 3 in BLOCK entity with opt 2

 for debugging:
  defined POINTER_VALUE
  changed all pointer output from long to hex "%ld" -> "%05lX"
   (for my hex editor)
  added output of pointer values to some functions in []:
    echo_16_bytes(), get_pointer()
  moved rest of header to scan_header()

 comment:
  somewhere the last handle must be stored,
    (only in the DWG, not in the DXF)
  missing  flags opts:
    01 01 PLINE
    01 01 VERTEX
    01    S/BEND
    40    INSERT
--------------------------------------------------*/


#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include <math.h> 

#define TRUE     1
#define FALSE    0

#define DEBUG_P(X)  printf X

#define MIN(x,y) ((x) < (y) ? (x) : (y))

typedef unsigned char byte;
typedef unsigned int word;
typedef unsigned long int longword;
typedef int bool;

#define POINTER_VALUE

longword cur_pos,
         last_pos;

typedef struct
{   longword start,
             nr;
    word     size;
} ttable,
  *ptable;


/* prototypes: */

int main(int argc, char *argv[]);

void scan_table_position(char *name, ptable pt);
void check_table_pos(char *name, ptable pt, int t_nr);
void scan_header(void);
void scan_blocks(ptable pt);
void scan_layers(ptable pt);
void scan_styles(ptable pt);
void scan_ltypes(ptable pt);
void scan_ads_table(ptable pt);   /* new name */
void scan_ucs_table(char *name, ptable pt);
void scan_view_table(char *name, ptable pt);
void scan_vport_table(char *name, ptable pt);
void scan_dim_table(ptable pt);
void scan_table(char *name, ptable pt);
void scan_entities(longword start, longword endi, longword sce_offset);
void scan_xdata(void);
void scan_handle(void);

void scan_point(bool z_coord);
void scan_fixed_ident(int len);

void echo_16_bytes(word len);
void echo_bytes(long int len);
longword get_pointer();
void check_pointer(longword p);
void get_check_2(void);
void get_check_32(void);
void skip_to_check_2(longword p);
void goto_pointer(longword p);

/* fio prototypes: */

void _assign(FILE *file);
longword _tell(void);
void _seek(long int pos);
void _advance(word steps);
bool _eof(void);
byte _read_byte(void);
short _read_word(void);
longword _read_long(void);
double   _read_date(void);
float    _read_float();
double   _read_double(void);
char *_read_string(void);
char *_read_fixed_string(int len);

/*-----------------25.10.95 21:47-------------------
 conversion from julian date to string
--------------------------------------------------*/
char *date_string (char *s, double date);


longword pointer[20];
#define P_ENTITIES  0
#define P_ENTEND    1
#define P_BLOCKSEC  2
#define P_BSEND     3

ttable table[11];
/*-----------------22.10.95 00:56-------------------
 missing tables VIEW, UCS, VPORT added               */
#define T_BLOCK     1
#define T_LAYER     2
#define T_STYLE     3
#define T_LTYPE     4
#define T_VIEW      5   /* changed */
#define T_UCS       6   /* changed */
#define T_VPORT     7
#define T_APPID     8    /* APPID section */
#define T_DIMSTYLE  9
#define T_13       10


static bool version_13 = FALSE;

int main(int argc, char *argv[])
{
  char version[13];
  { char *file1 = NULL;
    word i;
    bool error = FALSE;
    FILE *f;

    for (i = 1; i < argc && !error ; i++)
        { if   (argv[i][0] == '/')
               { printf("No options");
                 error = TRUE;
               }
          else if (file1 == NULL)
               file1 = argv[i];
          else
               { printf("dwg: too many arguments\n\n");
                 error = TRUE;
               }
        }
    if (!error && file1 == NULL)
       { printf("dwg: too few arguments.\n\n");
         error = TRUE;
       }

    if (error)
       { printf("Usages: dwg file1\n");
         return 2;
       }

    if (!(f = fopen(file1, "rb")))
       { printf("dwg: cannot open %s - No such file\n", file1);
         return 2;
       }

    _assign(f);
  }


  strcpy(version, _read_fixed_string(12));
  DEBUG_P(("Version: %s", version));
  version_13 = !strcmp(version, "AC1010");
  { int b1 = _read_byte(),
        w1 = _read_word(),
        w2 = _read_word(),
        w3 = _read_word(),
        b2 = _read_byte();
    DEBUG_P((" %d %d %d %d %d\n", b1, w1, w2, w3, b2));
  }

  pointer[P_ENTITIES] = _read_long();
  pointer[P_ENTEND]   = _read_long();
  DEBUG_P(("ENTITIES                 (%05lX-%05lX)\n",
         pointer[P_ENTITIES], pointer[P_ENTEND]));

  DEBUG_P(("BLOCK SECTION "));
  pointer[P_BLOCKSEC] = get_pointer();
  echo_bytes(4);
  pointer[P_BSEND] = get_pointer();
  echo_bytes(4);
  DEBUG_P(("\n"));
  DEBUG_P(("BSEC                     (%05lX-%05lX)\n",
  pointer[P_BLOCKSEC], pointer[P_BSEND]));

  scan_table_position("BLOCK", &table[T_BLOCK]);

  scan_table_position("LAYER", &table[T_LAYER]);

  scan_table_position("STYLE", &table[T_STYLE]);

  scan_table_position("LTYPE", &table[T_LTYPE]);

  scan_table_position("VIEW", &table[T_VIEW]);

  DEBUG_P(("HEADER\n"));
  scan_header();


  scan_table_position("UCS", &table[T_UCS]);

  echo_bytes(0x500 - cur_pos);

  scan_table_position("VPORT", &table[T_VPORT]);

  echo_bytes(8);

  scan_table_position("APPID", &table[T_APPID]);

  echo_bytes(6);

  scan_table_position("DIMSTYLE", &table[T_DIMSTYLE]);    /* prev. P12 */

  echo_bytes(0x69F - cur_pos);

  scan_table_position("P13", &table[T_13]);

  echo_bytes(38);

  scan_entities(pointer[P_ENTITIES], pointer[P_ENTEND], 0L);

  echo_bytes(19);

  scan_blocks(&table[T_BLOCK]);

  scan_layers(&table[T_LAYER]);

  scan_styles(&table[T_STYLE]);

  scan_ltypes(&table[T_LTYPE]);

  scan_view_table("VIEW TABLE", &table[T_VIEW]);   /* new */

  scan_ucs_table("UCS TABLE", &table[T_UCS]);      /* new */

  scan_vport_table("VPORT TABLE", &table[T_VPORT]);/* new */

  scan_ads_table(&table[T_APPID]);                  /* new name */

  scan_dim_table(&table[T_DIMSTYLE]);              /* new */

  scan_table("P13 TABLE", &table[T_13]);

  DEBUG_P(("BLOCK SECTION\n"));

  scan_entities(pointer[P_BLOCKSEC], pointer[P_BSEND], 
                pointer[P_BLOCKSEC] - 0x40000000);

  DEBUG_P(("P3\n"));    /* new Header vars */
  echo_bytes(36);

  check_pointer(pointer[P_ENTITIES]);
  check_pointer(pointer[P_ENTEND]);
  check_pointer(pointer[P_BLOCKSEC]);
  check_pointer(pointer[P_BSEND]);
  echo_bytes(12);
  check_table_pos("BLOCK", &table[T_BLOCK], 1);
  check_table_pos("LAYER", &table[T_LAYER], 2);
  check_table_pos("STYLE", &table[T_STYLE], 3);
  check_table_pos("LTYPE", &table[T_LTYPE], 5);
  check_table_pos("VIEW", &table[T_VIEW], 6);
  check_table_pos("UCS", &table[T_UCS], 7);
  check_table_pos("VPORT", &table[T_VPORT], 8);
  check_table_pos("APPID", &table[T_APPID], 9);
  check_table_pos("DIMSTYLE", &table[T_DIMSTYLE], 10);
  check_table_pos("P13", &table[T_13], 11);
  DEBUG_P(("P?: ")); get_pointer();
  echo_bytes(last_pos - cur_pos);

  return 0;
}

void scan_table_position(char *name, ptable pt)
{
  pt->size  = _read_word();
  pt->nr    = _read_long();
  pt->start = _read_long();
  DEBUG_P(("%-8.8s %05lX size: %3d (%05lX-%05lX)\n",
         name,
         pt->nr, pt->size,
         pt->start,
         pt->start + pt->size * pt->nr));
}

void check_table_pos(char *name, ptable pt, int t_nr)
{  word t, size, nr;
   longword start;
   t     = _read_word();
   size  = _read_word();
   nr    = _read_word();
   start = _read_long();
   printf("%-8.8s %05x size: %3d (%05lx)  table:%d",
          name, nr, size, start, t);
   if (t != t_nr)
       printf(" t %d!=%d", t, t_nr);
   if (nr != (pt->nr & 0xFFFF))
       printf(" nr %d!=%ld", nr, pt->nr);
   if (size != pt->size)
       printf(" size %d!=%d", size, pt->size);
   if (start != pt->start)
       printf(" start %ld!=%ld", start, pt->start);
   printf("\n");
}   


void scan_header()
{   char tmpstr[40];
    double date;

    DEBUG_P(("\n?w  %d", _read_word()));
    DEBUG_P(("\n$INSBASE ")); scan_point(TRUE);
    DEBUG_P(("\n$EXTMIN  ")); scan_point(TRUE);
    DEBUG_P(("\n$EXTMAX  ")); scan_point(TRUE);
    DEBUG_P(("\n$LIMMIN  ")); scan_point(FALSE);
    DEBUG_P(("\n$LIMMAX  ")); scan_point(FALSE);
    /*-----------------22.10.95 01:01-------------------
    active vport: groups 12 ?? 40
    --------------------------------------------------*/
    DEBUG_P(("\n$VIEWCTR  (%f,%f) ", _read_double()
                                     , _read_double()));
    DEBUG_P(("\nf? %f "        , _read_double()));   /*?*/
    DEBUG_P(("\n$VIEWSIZE %f " , _read_double()));
    DEBUG_P(("\nw? %d ", _read_word()));
    /*-----------------22.10.95 01:01-------------------
    active vport: groups 14 or 15
    --------------------------------------------------*/
    DEBUG_P(("\n$SNAPUNIT  (%f,%f) ", _read_double()
                                    , _read_double()));
    echo_bytes(56);
    DEBUG_P(("\nf? %f ", _read_double()));
    DEBUG_P(("\nf? %f ", _read_double()));
    DEBUG_P(("\nf? %f ", _read_double()));
    echo_bytes(18);
    DEBUG_P(("\nf? %f ", _read_double()));

    /* rest of header */
    DEBUG_P(("\nw? %d ", _read_word()));
    DEBUG_P(("\nw? %d ", _read_word()));
      echo_bytes(44);
    /* DEBUG_P(("\nf? %f ", _read_double()));
      echo_bytes(12); */
    /* menuname: length of pathstr 128 ?? */
      echo_bytes((22*16)+2);
    DEBUG_P(("\n$DIMBLK: %s ", _read_fixed_string(32)));

    DEBUG_P(("\nb? %d ", _read_byte()));
    DEBUG_P(("\nb? %d ", _read_byte()));
    DEBUG_P(("\nw? %d ", _read_word()));
    DEBUG_P(("\nw? %d ", _read_word()));
    DEBUG_P(("\nb? %d ", _read_byte()));
    DEBUG_P(("\nb? %d ", _read_byte()));
    DEBUG_P(("\nb? %d ", _read_byte()));

    date = _read_date();
    DEBUG_P(("\n$TDCREATE: %.8f %s ", date, date_string(tmpstr,date)));
    /* at 0x31E */
    date = _read_date();
    DEBUG_P(("\n$TDUPDATE: %.8f %s ", date, date_string(tmpstr,date)));
    /* at 0x327 */

      echo_bytes(0x379 - cur_pos);
    DEBUG_P(("\nf? %f ", _read_double()));
      echo_bytes(0x3c5 - cur_pos);
    DEBUG_P(("\nf? %f ", _read_double()));
    DEBUG_P(("\nf? %f ", _read_double()));

      echo_bytes(0x3EF - cur_pos);
}

void scan_blocks(ptable pt)
{   longword i;

    DEBUG_P(("\nBLOCK TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        byte flag, b1, b2;
        word used, w1, w2, w3, w4;

        goto_pointer(begin);

        flag = _read_byte();
        scan_fixed_ident(32);
        used = _read_word();
        b1 = _read_byte();
        w1 = _read_word();
        b2 = _read_byte();
        w3 = _read_word();
        w4 = _read_word();

        DEBUG_P(("%02x %5d   %3d %5d %3d %5d %5d %5d",
               flag, used, b1, w1, b2, w2, w3, w4));


        skip_to_check_2(end);
    }

    get_check_32();
}

void scan_layers(ptable pt)
{   longword i;

    DEBUG_P(("\nLAYER TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        int flag,
            used,
            color,
            style;

        goto_pointer(begin);

        flag  = _read_byte();
        scan_fixed_ident(32);
        used  = _read_word();
        color = _read_word();
        style = _read_word();
        DEBUG_P(("%6d %s %s %s %s color: %3d ltype: %3d ",
               used,
               color < 0 ? "off" : "on ",
               flag & 1 ? "fr" : "  ",
               flag & 4 ? "lc" : "  ",
               flag & 64 ? "    " : "used",
               color < 0 ? -color : color,
               style));

        skip_to_check_2(end);
    }
  get_check_32();
}

void scan_styles(ptable pt)
{   longword i;

    DEBUG_P(("\nSTYLE TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        int flag;

        goto_pointer(begin);

        printf("%02X ", flag = _read_byte());

        scan_fixed_ident(32);
        printf("?w: %5d ", _read_word());
        printf("%f ",  _read_double());
        printf("%f ",  _read_double());
        printf("%f ",  _read_double());
        printf("%02X ",_read_byte());
        printf("%f ",  _read_double());
        printf("`%s'", _read_fixed_string(128));

        skip_to_check_2(end);
    }
  get_check_32();
}

void scan_ltypes(ptable pt)
{   longword i;

    DEBUG_P(("\nLTYPE TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        printf("%02X ", _read_byte());
        scan_fixed_ident(32);
        printf("%5d ", _read_word());
        printf("`%s' ", _read_fixed_string(48));
        printf("%02X ", _read_byte());
        {   word j,
                 k = _read_byte();

            for (j = 0; j < 13; j++)
                printf("%s%f ", j < k ? "" : "*", _read_double());
        }
        skip_to_check_2(end);
    }
  get_check_32();
}

void scan_ads_table(ptable pt)
{   longword i;

    DEBUG_P(("\nAPPID TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        printf("%02X ", _read_byte());
        scan_fixed_ident(32);
        printf("?w: %5d ", _read_word());
        skip_to_check_2(end);
    }
    get_check_32();
}

#define OPTS(X) if (opts & (1 << (X-1)))
#define SCAN_POINT(G,C) { printf(" %d:", G); scan_point(C); }
#define SCAN_DOUBLE(G)  printf(" %d:%f", G, _read_double());
#define SCAN_STRING(G)  printf(" %d:`%s'", G, _read_string());
/*  new */
#define SCAN_FIXED_STRING(G,L)  printf(" %d:`%s'", G, _read_fixed_string(L));
#define SCAN_BYTE(G)    printf(" %d:%02X", G, _read_byte());
#define SCAN_WORD(G)    printf(" %d:%d",   G, _read_word());
#define SCAN_LONG(G)    printf(" %d:%ld",  G, _read_long());

/*-----------------23.10.95 11:14-------------------
  new:
--------------------------------------------------*/
void scan_dim_table(ptable pt)
{   longword i;

    DEBUG_P(("\nDIMSTYLE TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        int flag;

        goto_pointer(begin);

        printf("70: %02X ", flag = _read_byte());

        scan_fixed_ident(32);
        printf("?w: %5d ", _read_word());

        printf ("\n");
        SCAN_DOUBLE(40);   /* DIMSCALE:  */
        SCAN_DOUBLE(41);   /* DIMASZ:    */
        SCAN_DOUBLE(42);   /* DIMEXO:    */
        SCAN_DOUBLE(43);   /* DIMDLI:    */
        SCAN_DOUBLE(44);   /* DIMEXE:    */
        SCAN_DOUBLE(45);   /* DIMRND:    */
        SCAN_DOUBLE(46);   /* DIMDLE:    */
        SCAN_DOUBLE(47);   /* DIMTP:     */
        SCAN_DOUBLE(48);   /* DIMTM:     */
        printf ("\n");
        SCAN_DOUBLE(140);  /* DIMTXT :   */
        SCAN_DOUBLE(141);  /* DIMCEN :   */
        SCAN_DOUBLE(142);  /* DIMTSZ :   */
        SCAN_DOUBLE(143);  /* DIMALTF:   */
        SCAN_DOUBLE(144);  /* DIMLFAC:   */
        SCAN_DOUBLE(145);  /* DIMTVP :   */
        printf ("\n");
        SCAN_BYTE (71);
        SCAN_BYTE (72);
        SCAN_BYTE (73);
        SCAN_BYTE (74);
        SCAN_BYTE (75);
        SCAN_BYTE (76);
        SCAN_BYTE (77);
        SCAN_BYTE (78);
        printf ("\n");
        SCAN_BYTE (170);
        SCAN_BYTE (171);
        SCAN_BYTE (172);
        SCAN_BYTE (173);
        SCAN_BYTE (174);  /*?*/
        SCAN_BYTE (175);  /*?*/

        SCAN_FIXED_STRING (3,16);  /* length ? */
        SCAN_FIXED_STRING (4,16);  /* length ? */
        SCAN_FIXED_STRING (5,32);
        SCAN_FIXED_STRING (6,32);
        SCAN_FIXED_STRING (7,32);

        echo_bytes (3);   /*?*/
        SCAN_WORD (176);  /* Colors as int ? */
        SCAN_WORD (177);
        SCAN_WORD (178);
        SCAN_DOUBLE(146);  /* DIMTFAC  */
        SCAN_DOUBLE(147);  /* DIMDIST  */

        skip_to_check_2(end);
    }
  get_check_32();
}

/*-----------------23.10.95 11:14-------------------
  new:
--------------------------------------------------*/
void scan_view_table(char *name, ptable pt)
{   longword i;

    DEBUG_P(("\n%s\n", name));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        printf("70: %02X ", _read_byte());

        scan_fixed_ident(32);
        printf("used: %5d\n", _read_word());  /* used */
        SCAN_DOUBLE(40);
        SCAN_POINT(10,FALSE);
        SCAN_DOUBLE(41);
        SCAN_POINT(11,TRUE);
        SCAN_POINT(12,TRUE);

        SCAN_WORD(71);  /*?*/

        SCAN_DOUBLE(42);
        SCAN_DOUBLE(43);
        SCAN_DOUBLE(44);
        SCAN_DOUBLE(50);

        echo_bytes(2);    /*?*/

        skip_to_check_2(end);
    }
    get_check_32();
}
/*-----------------23.10.95 11:14-------------------
  new:
--------------------------------------------------*/
void scan_ucs_table(char *name, ptable pt)
{   longword i;

    DEBUG_P(("\n%s\n", name));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        printf("70: %02X ", _read_byte());

        scan_fixed_ident(32);
        printf("used: %5d\n", _read_word());  /* used */
        SCAN_POINT(10,TRUE);
        SCAN_POINT(11,TRUE);
        SCAN_POINT(12,TRUE);

        skip_to_check_2(end);
    }
    get_check_32();
}

/*-----------------23.10.95 11:14-------------------
  new:
--------------------------------------------------*/
void scan_vport_table(char *name, ptable pt)
{   longword i;

    DEBUG_P(("\n%s\n", name));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        printf("70: %02X ", _read_byte());

        scan_fixed_ident(32);
        printf("used: %5d\n", _read_word());  /* used */
        SCAN_POINT(10,FALSE);
        SCAN_POINT(11,FALSE);
        printf ("\n");
        SCAN_POINT(17,TRUE);
        SCAN_POINT(16,TRUE);
        /*43-51 */
        printf ("\n");
        SCAN_DOUBLE(50);
        SCAN_DOUBLE(40);
        SCAN_POINT(12,FALSE);

        printf ("\n");
        SCAN_DOUBLE(41);
        SCAN_DOUBLE(42);
        SCAN_DOUBLE(43);
        SCAN_DOUBLE(44);
        printf ("\n");
        SCAN_WORD(71);  /*?*/
        SCAN_WORD(72);
        SCAN_WORD(73);
        SCAN_WORD(74);
        SCAN_WORD(75);
        SCAN_WORD(76);
        SCAN_WORD(77);
        SCAN_WORD(78);
        printf ("\n");
        SCAN_DOUBLE(51);

        printf ("\n");
        SCAN_POINT(13,FALSE);
        SCAN_POINT(14,FALSE);
        SCAN_POINT(15,FALSE);

        skip_to_check_2(end);
    }
    get_check_32();
}



void scan_table(char *name, ptable pt)
{   longword i;

    DEBUG_P(("\n%s\n", name));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;
        int flag;

        goto_pointer(begin);
        printf("70: %02X ", flag = _read_byte());

        scan_fixed_ident(32);
        printf("used: %5d\n", _read_word());  /* used */

        skip_to_check_2(end);
    }
    get_check_32();
}



#define NR_ENT 25


struct {
  char *name;
  int  kn_flag,
       kn_opts;
} ent_type[NR_ENT] = {
   { NULL, 0, 0 },
   { "LINE   ", 0x6F /* 1 2 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "POINT  ", 0x6F /* 1 2 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "CIRCLE ", 0x6F /* 1 2 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "SHAPE  ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { NULL, 0, 0 },
   { NULL, 0, 0 },
   { "TEXT   ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x17B/* 1 2 + 8 10 20 40 ++ 50 41 51 7 71 72 72p ? 73
                                                        1 . . . .. .. .. .. */},
   { "ARC    ", 0x6D /* 1 . 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "TRACE  ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { NULL, 0, 0 },
   { "SOLID  ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   /*-----------------23.10.95 12:59-------------------
    opts changed
   --------------------------------------------------*/
   { "BLOCK  ", 0    /* . . . . .. .. .. .. */, 0x06 /* . 2 4 . .. .. .. .. */},
   { "ENDBLK ", 0, 0x004 },
   { "INSERT ", 0xA2 /* . 2 . . .. 20 .. 80 */, 0x0F /* 1 2 4 8 .. .. .. .. */},
   { "ATTDEF ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x030/* + + + + 10 20 ++ ++ 73 50 41 51 7 71 72 72'
                                                        1 . . . .. .. .. .. */},
   { "ATTRIB ", 0x22 /* . . . . .. .. .. .. */, 0x116/* + 2 4 + 10 ++ ++ ++
                                                        1 . . . .. .. .. .. */},
                     /* 1 */
   { "S/BEND ", 0x22 /* . 2 . . .. 20 .. .. */, 0x00 /* . . . . .. .. .. .. */},
   { NULL, 0, 0 },
   { "PLINE  ", 0xA2 /* . 2 . . .. 20 .. 80 */, 0x01 /* 1 . . . .. .. .. .. */},
   { "VERTEX ", 0x22 /* . 2 . . .. 20 .. .. */, 0x04 /* + + . . .. .. .. .. */},
   { NULL, 0, 0 },
   { "3DFACE ", 0x26 /* . 2 . . .. 20 .. .. */, 0x00 /* . . . . .. .. .. .. */},
   { "DIM    ", 0x22 /* . . . . .. .. .. .. */, 0x11A/* + 2 + 8 10 ++ ++ ++
                                                        1 . . . .. .. .. .. */},
   { "VPORT  ", 0x63 /* 1 2 . . .. 20 40 .. */, 0x00 /* . . . . .. .. .. .. */},
};


void scan_entities(longword start, longword end, longword sce_offset)
{ long s_comp_ent = 0L;

  DEBUG_P(("ENTITIES\n"));
  goto_pointer(start);

  while (cur_pos < end - 32L)
     { word kind,
            flag,
            length,
            layer,
            opts,
            color = 0,
            extra = 0;
       longword b_pos = cur_pos,
                pos = cur_pos;

       /*printf("cur_pos %ld end %ld\n", cur_pos, end);*/

       DEBUG_P(("[%05lX]", cur_pos));

       kind = _read_byte();

       flag = _read_byte();

       length = _read_word();
       pos += (long)(length - 2);
       if (pos > end - 2)
          pos = end - 2;

       /* printf("cur_pos %ld, till %ld, end %ld", cur_pos, pos, end); */

       if (kind < NR_ENT && ent_type[kind].name != NULL)
            { printf("%s %02X ", ent_type[kind].name, (int)flag);
              if (flag & ~ent_type[kind].kn_flag)
                 printf("!%02X", flag & ~ent_type[kind].kn_flag);
            }
       else
            printf("ENT_%d! %02X (%d) ", (int)kind, (int)flag, length);

       layer = _read_word();

       opts = _read_word();

       printf("l:%d %04X ", layer, opts);
       if (   ent_type[kind].name != NULL
           && opts & ~ent_type[kind].kn_opts)
          printf("!%02X", opts & ~ent_type[kind].kn_opts);

       if (flag & 1)
          color = _read_byte();

       printf("c:%d ", color);

       if (flag & 0x40)
          extra = _read_byte();

       if (extra & ~6)           /* . 2 4 . .. .. .. .. */
          printf("!(extra)");

       if (extra & 2)
          scan_xdata();

       if (flag & 2)
          printf(" TYPE:%d ", _read_word());

       if (flag & 4 && kind > 2 && kind != 22)
          { printf(" z:");
            printf("%1.2f", _read_double());
          }

       if (flag & 8)
          { printf(" TH:");
            printf("%1.2f", _read_double());
          }

       if (flag & 0x20)
          scan_handle();

       if (extra & 4)
          printf(" PAPER:%d", _read_word());

       switch (kind)

       { case 1:   /* LINE */

                    SCAN_POINT(10, !(flag & 4));
                    SCAN_POINT(11, !(flag & 4));
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 2:  /* POINT */

                    SCAN_POINT(10, !(flag & 4));
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 3:  /* CIRCLE */

                    SCAN_POINT(10, FALSE);
                    SCAN_DOUBLE(40);
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 4:  /* SHAPE */

                    SCAN_POINT(10, FALSE);
                    SCAN_WORD(2);
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 7: /* TEXT */

                    SCAN_POINT(10, FALSE);
                    SCAN_DOUBLE(40);
                    SCAN_STRING(1);
            OPTS(1) SCAN_DOUBLE(50);
            OPTS(2) SCAN_DOUBLE(41);
            OPTS(3) SCAN_DOUBLE(51);             /*?*/
            OPTS(4) SCAN_BYTE(7);
            OPTS(5) SCAN_BYTE(71);
            OPTS(6) SCAN_BYTE(72);
            OPTS(7) SCAN_POINT(11, FALSE);
            OPTS(9) SCAN_BYTE(73);

            break;

         case 8:  /* ARC */

                    SCAN_POINT(10, FALSE);
                    SCAN_DOUBLE(40);
                    SCAN_DOUBLE(50);
                    SCAN_DOUBLE(51);
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 9:   /* TRACE */

                    SCAN_POINT(10, FALSE);
                    SCAN_POINT(11, FALSE);
                    SCAN_POINT(12, FALSE);
                    SCAN_POINT(13, FALSE);
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 11:   /* SOLID */

                    SCAN_POINT(11, FALSE);
                    SCAN_POINT(12, FALSE);
                    SCAN_POINT(13, FALSE);
                    SCAN_POINT(14, FALSE);
            OPTS(1) SCAN_POINT(210, TRUE);
            OPTS(2) SCAN_DOUBLE(38);

            break;


         case 12:   /* BLOCK */

                    SCAN_POINT(10,FALSE);   /*?*/
                    SCAN_STRING(1);
            OPTS(2) SCAN_STRING(3);            /* nur bei opts 2 */

            break;

         case 13:  /* ENDBLK */

            break;

         case 14:   /* INSERT */

            s_comp_ent = b_pos - sce_offset; 

                    SCAN_WORD(1);
                    SCAN_POINT(10, FALSE);
            OPTS(1) SCAN_DOUBLE(41);
            OPTS(2) SCAN_DOUBLE(42);
            OPTS(3) SCAN_DOUBLE(43);
            OPTS(4) SCAN_DOUBLE(50);
            OPTS(5) SCAN_WORD(70);              
            OPTS(6) SCAN_WORD(71);              
            OPTS(7) SCAN_DOUBLE(44);                          /*?*/
            OPTS(8) SCAN_DOUBLE(45);                          /*?*/

            break;

         case 15:    /* ATTDEF */

                    SCAN_POINT(10, FALSE);
                    SCAN_DOUBLE(40);
                    SCAN_STRING(1);
                    SCAN_STRING(3);
                    SCAN_STRING(2);
                    SCAN_BYTE(70);
            OPTS(1) SCAN_BYTE(73);           /*?*/
            OPTS(2) SCAN_DOUBLE(50);         /*?*/
            OPTS(3) SCAN_DOUBLE(41);
            OPTS(4) SCAN_DOUBLE(42);
            OPTS(5) SCAN_BYTE(7);
            OPTS(6) SCAN_BYTE(71);
            OPTS(7) SCAN_BYTE(72);
            OPTS(8) SCAN_POINT(11, FALSE);   /*?*/
            OPTS(9) SCAN_POINT(210, TRUE);
            OPTS(10) SCAN_DOUBLE(38);        /*?*/

            break;

         case 16:   /* ATTRIB */

                    SCAN_POINT(10, FALSE);
                    SCAN_DOUBLE(40);
                    SCAN_STRING(1);
                    SCAN_STRING(2);
                    SCAN_BYTE(70);
            OPTS(1) SCAN_BYTE(73);           /*?*/
            OPTS(2) SCAN_DOUBLE(50);         /*?*/
            OPTS(3) SCAN_DOUBLE(41);
            OPTS(4) SCAN_DOUBLE(42);
            OPTS(5) SCAN_BYTE(7);
            OPTS(6) SCAN_BYTE(71);
            OPTS(7) SCAN_BYTE(72);
            OPTS(8) SCAN_POINT(11, FALSE);   /*?*/
            OPTS(9) SCAN_POINT(210, TRUE);
            OPTS(10) SCAN_DOUBLE(38);        /*?*/

            break;

         case 17:   /* S/BEND */

            {   longword p = _read_long();
                if (p != s_comp_ent)
                    printf("!!%04lX != %04lX", s_comp_ent, p);
            }
            break;

         case 19:   /* PLINE */
           
            s_comp_ent = b_pos - sce_offset; 

            OPTS(1) SCAN_BYTE(70);
            OPTS(2) SCAN_DOUBLE(40);                   /*?*/
            OPTS(3) SCAN_BYTE(71);                     /*?*/
            OPTS(4) SCAN_BYTE(72);                     /*?*/
            OPTS(5) SCAN_BYTE(73);                     /*?*/
            OPTS(6) SCAN_BYTE(74);                     /*?*/
            OPTS(7) SCAN_BYTE(75);                     /*?*/

            break;

         case 20:   /* VERTEX */

                    SCAN_POINT(10, FALSE);
            OPTS(1) SCAN_DOUBLE(40);           /*?*/
            OPTS(2) SCAN_DOUBLE(41);           /*?*/
            OPTS(3) SCAN_DOUBLE(50);           /*?*/
            OPTS(4) SCAN_BYTE(70);             /*?*/

            break;

         case 22:   /* 3DFACE */

                    SCAN_POINT(10, !(flag & 4));
                    SCAN_POINT(11, !(flag & 4));
                    SCAN_POINT(12, !(flag & 4));
                    SCAN_POINT(13, !(flag & 4));

            break;

         case 23:   /* DIM */

                    SCAN_WORD(1);
                    SCAN_POINT(10, TRUE);
                    SCAN_POINT(11, FALSE);   /*?*/
            OPTS(2) SCAN_BYTE(70);
            OPTS(1) SCAN_POINT(12, TRUE);    /*?*/
            OPTS(3) SCAN_STRING(1);
            OPTS(4) SCAN_POINT(13, TRUE);
            OPTS(5) SCAN_POINT(14, TRUE);
            OPTS(6) SCAN_POINT(15, TRUE);
            OPTS(7) SCAN_POINT(16, TRUE);
            OPTS(8) SCAN_DOUBLE(40);
            OPTS(9) SCAN_DOUBLE(50);
            OPTS(10) SCAN_DOUBLE(51);
            OPTS(11) SCAN_DOUBLE(52);
            OPTS(12) SCAN_DOUBLE(53);

            break;

         case 24:   /* VPORT */

                    SCAN_POINT(10, TRUE);
                    SCAN_DOUBLE(40);
                    SCAN_DOUBLE(41);
                    SCAN_WORD(68);

            break;
       }

       skip_to_check_2(pos);
     }

  get_check_32();
}

void scan_xdata()
{ int xd_length = _read_word(),
      i;
  bool go = TRUE;
  printf("XDATA %d: (", xd_length);

  while (xd_length > 0 && go)
    { int c = _read_byte();
      xd_length--;
      printf(" %d:", c);
      switch (c)
      { case  0:
           if   (xd_length < 0)
                go = FALSE;
           else
                { int l = _read_byte();
                  xd_length--;

                  if (version_13)
                     { _read_byte();
                       xd_length--;
                     }
                  if   (l <= xd_length)
                       { printf("`%s'", _read_fixed_string(l));
                         xd_length -= l;
                       }
                  else
                       go = FALSE;
                }
           break;
        case  1:
        case  3:
        case  70:
           if   (xd_length < 2)
                go = FALSE;
           else
                { printf("%d", _read_word());
                  xd_length -= 2;
                }
           break;
        case 2:
           if   (xd_length < 1)
                go = FALSE;
           else
                { printf("%c", _read_byte() == 0 ? '{' : '}');
                  xd_length--;
                }
           break;
        case 5:
        case 40:
        case 41:
        case 42:
           if   (xd_length < 8)
                go = FALSE;
           else
                { printf("%1.2f", _read_double());
                  xd_length -= 8;
                }
           break;
        case 10:
        case 11:
        case 12:
           if   (xd_length < 24)
                go = FALSE;
           else
                { scan_point(TRUE);
                  xd_length -= 24;
                }
           break;
        case 71:
           if   (xd_length < 4)
                go = FALSE;
           else
                { printf("%ld", _read_long());
                  xd_length -= 4;
                }
           break;
        default:
           go = FALSE;
      }
    }
  if (!go)
     printf("!");

  for (i = 0; i < xd_length; i++)
      { int w = _read_byte();
        if (w < 0) w += 256;
        printf("%02X ", w);
      }
  printf(") ");
}

void scan_handle()
{ int n = _read_byte(),
      i;
  printf(" 5:");
  for (i = 0; i < n; i++)
     { int w = _read_byte();
       if (w < 0) w += 256;
       printf("%02X", w);
     }
}


void echo_16_bytes(word len)
{   int i;
    char buffer[16];

    printf("[%05lX]  ", cur_pos);
    for (i = 0; i < len; i++)
        buffer[i] = _read_byte();


    for (i = 0; i < 16; i++)
    {   if (i > 0 && i % 4 == 0)
        printf("| ");
        if (i < len)
        {   int w = buffer[i];
            if (w < 0)
                w += 256;
            printf("%02X ", w);
        }
        else
            printf("   ");
    }
    printf("  ");

    for (i = 0; i < len; i++)
    {   char c = buffer[i];
        if ((c >= 0) && (c < 32))
            printf(".");
        else
            printf("%c", c);
    }
    printf("\n");
}

void echo_bytes(long int len)
{ printf("\ndata: %ld\n", len);
  while(len > 0)
    { int len16 = (int)MIN(len, 16);
      echo_16_bytes(len16);
      len -= (longword)len16;
    }
}


void scan_point(bool z_coord)
{   double x = _read_double(),
           y = _read_double(),
           z = z_coord ? _read_double() : 0.0;
    if (z_coord)
        printf("(%f,%f,%f)", x, y, z);
    else
        printf("(%f,%f)", x, y);
}


void scan_fixed_ident(int len)
{   char *s = _read_fixed_string(len);
    if (version_13)
        s++;
    printf("`%-*.*s'", len, len, s);
}

longword get_pointer()
{ longword result = _read_long();
#ifdef POINTER_VALUE
  printf("(pointer: %05lX)\n", result);
#else
  printf("(pointer)\n");
#endif

  return result;
}


void check_pointer(longword p)
{
    printf("(pointer: %05lX) %s\n", p,_read_long() == p ? "Correct" : "Wrong");
}

void get_check_2()
{
    printf("(check_2)\n");
    _advance(2);
}

void get_check_32()
{
    printf("(check_32)\n");
    _advance(32);
}

void skip_to_check_2(longword pos)
{
    if (cur_pos < pos)
    {  printf("!\n");
       echo_bytes(pos - cur_pos);
    }
    if (cur_pos > pos)
    {   printf("!%05lX>", cur_pos - pos);
        _seek(pos);
    }
    get_check_2();
}

void goto_pointer(longword pos)
{
    if (cur_pos != pos)
    {   printf("!(pointer: %05lX)\n", pos - cur_pos);  /* /n inserted */
        _seek(pos);
    }
}


/************** File IO *************************/

static FILE *f = NULL;

#define MAX_STRING_LENGTH 1000
static char str_buffer[MAX_STRING_LENGTH];

#define GETBYTE (byte)fgetc(f)

void _assign(file)
FILE *file;
{ int fh = fileno(file);
  f = file;

  cur_pos = 0;
  last_pos = lseek(fh, 0L, SEEK_END);
  lseek(fh, 0L, SEEK_SET);

}

void _advance(word len)
{
    fseek(f, len, SEEK_CUR);

    cur_pos += len;
}

longword _tell()
{
    return cur_pos;
}

void _seek(long int pos)
{
    cur_pos = pos;
    fseek(f, pos, SEEK_SET);
}


bool _eof()
{
    return cur_pos >= last_pos;
}

byte _read_byte()
{   cur_pos++;
    return GETBYTE;
}

short _read_word()
{   short result;

    *((byte *)&result) = GETBYTE;
    *((byte *)&result + 1) = GETBYTE;

    cur_pos += 2;

    return result;
}

longword _read_long()
{   longword result;

    {   short i;
        byte *p = (byte *)&result;

        for (i = 0; i < sizeof(longword); i++)
            *p++ = GETBYTE;
    }
    cur_pos += sizeof(longword);

    return result;
}

float _read_float()
{   float result;

    {   short i;
        byte *p = (byte *)&result;

        for (i = 0; i < sizeof(double); i++)
            *p++ = GETBYTE;
    }
    cur_pos += sizeof(float);

    return result;
}

/*-----------------23.10.95 23:38-------------------
 corrected version
--------------------------------------------------*/
double _read_date()
{
    longword l1,l2;
    double d1;

    /* <julian date> . <daytime ratio> */
    /* seconds = 86400 * <daytime ratio> */

    l1 = _read_long();
    l2 = _read_long();

    d1 = (double)l1;
    d1 += (double)l2 * 1e-8; /* here was an error in the prev. version!*/

    return d1;

}


double _read_double()
{   double result;

    {   short i;
        byte *p = (byte *)&result;

        for (i = 0; i < sizeof(double); i++)
            *p++ = GETBYTE;
    }
    cur_pos += sizeof(double);

    return result;
}

char *_read_string()
{
    return _read_fixed_string(_read_word());
}

char *_read_fixed_string(len)
int len;
{   int i;
    char *str = str_buffer;

    for (i = 0; i < len; i++)
         *str++ = GETBYTE;
    *str = '\0';
    cur_pos += len;

    return str_buffer;
}

/*-----------------25.10.95 21:47-------------------

  conversion of julian date into calendar date
  returns formatted date output
  from "Numerical Recipes in Pascal", p.12
  room for s must be allocated!
--------------------------------------------------*/
char *date_string (char *s, double date)
{    double t,ss;
    int y,m,d,hh,mm;
    long ja,jalpha,jb,jc,jd,je;

#define TRUNC(n) (long) floor(n)

    t = 86400*(date-floor(date));     /* in seconds */
    date = floor (date);
    if ( date>2299161.0 )
    {    jalpha = TRUNC(((date-1867216)-0.25)/36524.25);
        ja = (long) (date+1+jalpha-TRUNC(0.25*jalpha));
    } else ja = (long)date;
    jb = ja+1524;
       jc = TRUNC(6680.0+((jb-2439870)-122.1)/365.25);
    jd = 365*jc+TRUNC(0.25*jc);
      je = TRUNC((jb-jd)/30.6001);
    d = (int) (jb-jd-TRUNC(30.6001*je));

    m = (int) (je-1);
    if ( m>12 ) mm -= 12;
    y = (int) (jc-4715);
    if ( m>2 ) y--;
    if ( y<=0 ) y--;

    hh = (int) floor(t/3600.0);
    t -= hh*3600.0;
    mm = (int) floor(t/60.0);
    ss = t- (mm*60.0);

    sprintf (s, "%02d.%02d.%4d  %02d:%02d:%05.2f", d,m,y,hh,mm,ss);
    return s;
}