/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/****************************************************************************
 * This source file was written by Acorn Computers Limited. It is part of   *
 * the "DrawFile" library for rendering RISCOS Draw files from applications *
 * in C. It may be used freely in the creation of programs for Archimedes.  *
 * It should be used with Acorn's C Compiler Release 2 or later.            *
 *                                                                          *
 * No support can be given to programmers using this code and, while we     *
 * believe that it is correct, no correspondence can be entered into        *
 * concerning behaviour or bugs.                                            *
 *                                                                          *
 * Upgrades of this code may or may not appear, and while every effort will *
 * be made to keep such upgrades upwards compatible, no guarantees can be   *
 * given.                                                                   *
 ***************************************************************************/

/* -> c.drawCheck
 *
 * DrawFile module, internal code
 * History:
 * Version 0.1: 15 Feb 89, DAHE: created
 *              11 Apr 89, IDJ : removed "&" refering to  ...->text (line220)
 *
 * This file consists of code taken from the !Draw version of c.drawCheck
 * (0.51), for use in the DrawFile module (release 0.2).
 *
 * Purpose: 1. check an Draw file for consistency
 *          2. shift all coordinates in an Draw file
 *
 * Checks a Draw file held in a buffer, and reports any problems, with the
 * offset from the start of the buffer. No error is flagged on an unknown
 * object type.
 *
 * The (global) ok flag can indicate no error, recoverable error or fatal
 * error.
 * A fatal error is generally some sort of mistake in an object size.
 * On a fatal error, we return as soon as possible. The returned buffer
 * location need not be sensible.
 *
 * Note that the code here must be changed if there are any changes in the
 * internal structure of an object.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include "os.h"
#include "sprite.h"
#include "bbc.h"
#include "font.h"
#include "drawmod.h"
#include "wimpt.h"
#include "trace.h"

#include "h.DrawIntern.drawfile1"
#include "drawferror.h"

/*-------------------------------------------------------------------------*/
/* Common stuff - both shift and check                                     */

/* Types for check/shift functions */
typedef void check_shift_fn(draw_objptr object);

typedef struct
{
    draw_tagtyp  tag;                /* Tag */
    int          sizelow, sizehigh;  /* Bounds on size */
    BOOL         box;                /* Flag - object has a bbox */
    check_shift_fn *fn;              /* Function to call, or NULL */
} check_shift_table;

/* Macro to help declare them */
#define Check_shift_fn(name)  void name(draw_objptr object)

/*-------------------------------------------------------------------------*/
/* Code for checking Draw files                                         */

/* Errors are represented as an integer. The type draw_error1 must be made
   consistent with these values; we do not include the header file here, to
   avoid the DrawFile module invading the Draw source
 */
static int errorCode;
static int errorLocation;
#define error(code, at, level) {errorCode = code; errorLocation = (int)at; \
                                check_ok |= level;}

/* Error levels */
#define OK_OK    0
#define OK_ERROR 1
#define OK_FATAL 2

/* Flag for indicating errors */
static int check_ok;

/* 'Infinite' size value */
#define check_BIG (0x7fffffff)

/* Status flags */
static BOOL check_fontSeen, check_textSeen;

/* Forward reference */
draw_objptr check_object(draw_objptr object);

/*
 Function    : check_bbox
 Purpose     : check a bounding box
 Parameters  : bounding box pointer
 Returns     : void
 Description : checks that the coordinate in the box are in the right order.
*/

static int  check_bbox(draw_bboxtyp *box)
{
    if (box->x0 > box->x1 || box->y0 > box->y1)
        /*error(draw_BBoxWrong, (char *)box, OK_ERROR)*/
        return 1;
    else
        return 0;
}

/*
 Function    : check_text
 Purpose     : check a string
 Parameters  : pointer to string
               maximum length
 Returns     : length of string
 Description : check the string for control characters, etc.
*/

static int check_text(char *buffer, int length)
{
    int  i;
    int  c;

    for (i = 0 ; i < length ; i++)
    {
        if (buffer[i] == 0) return (i);

        c = (int)buffer[i];
        if (!((31 < c && c < 127) || (127 < c && c <= 255)))
            error(draw_BadCharacter, buffer, OK_ERROR)
    }

    return (length);
}

/*
 Function    : check_size
 Purpose     : check an object size
 Parameters  : object size
               lower bound
               upper bound (may be 'check_BIG' for any size)
               pointer to location for error report
 Returns     : void
*/

static void check_size(int size, int low, int high, char *where)
{
    if (size < low)
        error(draw_ObjectTooSmall, where, OK_FATAL)
    if (size > high)
        error(draw_ObjectTooLarge, where, OK_FATAL)
    if (size & 3 != 0)
        error(draw_ObjectNotMult4, where, OK_FATAL)
}

/*
 Function    : check_overrun
 Purpose     : check for data overrun
 Parameters  : remaining object size
               pointer to current location for error report
 Returns     : void
 Description :
*/

static void check_overrun(int size, char *where)
{
    if (size < 0)
        error(draw_ObjectOverrun, where, OK_FATAL)
}

/*--------------------------------------------------------------------------*/

/*
 Function    : check_<object>
 Purpose     : general object check routines
 Parameters  : pointer to start of object
 Returns     : void
*/

/* Font table: check we have only one, that it is after text, and that there
   is no rubbish at the end of it
 */

static Check_shift_fn(check_fontList)
{
    int fontNum;
    int ptr  = 8;              /* Skip over type and size */
    int size;

    size = object.fontlistp->size;

    if (check_fontSeen)
        error(draw_ManyFontTables, object.bytep, OK_ERROR)
    if (check_textSeen)
        error(draw_LateFontTable, object.bytep, OK_ERROR)

    check_fontSeen = TRUE;

    while (ptr < size)
    {
        if ((fontNum = (int)object.bytep[ptr++]) == 0) break;
        ptr += check_text(object.bytep+ptr, check_BIG);
    }

    check_overrun(size - ptr, object.bytep + ptr);
}

/* Text: check style, text */
static Check_shift_fn(check_textObject)
{
    int  ptr;

    check_textSeen = TRUE;

    if (object.textp->textstyle.reserved8 != 0
        || object.textp->textstyle.reserved16 != 0)
        error(draw_BadTextStyle, object.bytep, OK_ERROR)
    ptr = sizeof(draw_textstrhdr) + check_text(object.textp->text, check_BIG);

    check_overrun(object.textp->size - ptr, object.bytep + ptr);
}

/* Path: check elements of path. Must start with move, and have a line or
   curve in it somewhere */
static Check_shift_fn(check_pathObject)
{
    path_eleptr path;
    int  extra;
    int  lineSeen = FALSE;

    path.move = address_pathstart(object);

    if (path.move->tag.tag != Draw_PathMOVE)
        error(draw_MoveMissing, object.bytep, OK_ERROR)

    do
    {
        switch (path.move->tag.tag)
        {
            case Draw_PathTERM : break;
            case Draw_PathMOVE : path.bytep += sizeof(path_movestr)  ; break;
            case Draw_PathCLOSE: path.bytep += sizeof(path_closestr) ; break;
            case Draw_PathCURVE: path.bytep += sizeof(path_curvestr);
                                 lineSeen = TRUE; break;
            case Draw_PathLINE : path.bytep += sizeof(path_linestr);
                                 lineSeen = TRUE; break;
            default:
                error(draw_BadPathTag, path.bytep, OK_FATAL)
                return;
        }
    } while (path.move->tag.tag != Draw_PathTERM);

    if (!lineSeen)
        error(draw_NoPathElements, object.bytep, OK_ERROR)

    extra = object.pathp->size -
                  (path.bytep - object.bytep + sizeof(path_termstr));
    if (extra > 0)
        error(draw_PathExtraData, object.bytep, OK_FATAL)
    else
        check_overrun(extra, path.bytep);
}

/* Sprite: check size (based on minimum size for a sprite definition),
   and sprite header block */
static Check_shift_fn(check_spriteObject)
{
    int  spriteSize;

    spriteSize = object.spritep->sprite.next;
    if (object.spritep->size - sizeof(draw_objhdr) < spriteSize)
     error(draw_BadSpriteSize, object.bytep, OK_FATAL)
}

static Check_shift_fn(check_groupObject)
{
    char *end;

    end = object.bytep + object.groupp->size;
    object.bytep += sizeof(draw_groustr);

    while ((check_ok & OK_FATAL) == 0 && object.bytep < end)
        object = check_object(object);
}

#ifdef draw_OBJTAGG
Check_shift_fn(check_tagObject)
{
    char *end;

    end = check_object(object.bytep + 28);
    check_overrun((int)(end - object.bytep) - object.objhdrp->size, end);
}
#endif

/* Text column: check size and tag. TRUE if it was a text column */
static BOOL check_textColumn(draw_textcolhdr *column)
{
    if (column->tag == 0) return (FALSE);
    else if (column->tag != draw_OBJTEXTCOL)
    {
        error(draw_BadTextColumnEnd, column, OK_FATAL)
        return (FALSE);
    }

    check_size(column->size, sizeof(draw_textcolhdr), sizeof(draw_textcolhdr),
               (char *)column);
    return((check_ok & OK_FATAL) == 0);
}

/* Text area: check size and verify using text column code */
static Check_shift_fn(check_textArea)
{
    int  columns, actualColumns;
    draw_objptr column, area;
    int code;
    char *location;

    for (column.textcolp = &(object.textareastrp->column), actualColumns = 0 ;
         check_ok != OK_FATAL && check_textColumn(column.textcolp) ;
         column.bytep += sizeof(draw_textcolhdr), actualColumns += 1) ;

    if (!draw_verifyTextArea(sizeof(draw_textareastrend) + column.bytep,
         &code, &location, &columns) || columns < 1)
     {
       error(code, location, OK_ERROR)
     }
    else if (columns != actualColumns)
    {
        error(draw_ColumnsMismatch, object.bytep, OK_ERROR)
    }
    else /* Ensure reserved words are zero */
    {
        area.bytep = column.bytep;
        if (area.textareaendp->blank1 != 0 || area.textareaendp->blank2 != 0)
            error(draw_NonZeroReserved, object.bytep, OK_ERROR)
    }
}

/*-------------------------------------------------------------------------*/

/*
 Function    : check_fileHeader
 Purpose     : check Draw file header
 Parameters  : object pointer
 Returns     : pointer to object after header
 Description : checks the file header for:
   - not being an Draw file
   - bad version
   - bad bbox
*/

static draw_objptr check_fileHeader(draw_objptr object)
{
    int drawName = 0x77617244;      /* the text 'Draw' as an integer */

    if (object.wordp[0] != drawName)
    {
        error(draw_NotDrawFile, 0, OK_FATAL)
    }
    else
    {
        if (object.filehdrp->majorstamp > majorformatversionstamp)
        {
            error(draw_VersionTooHigh, 0, OK_FATAL)
        }
    }

    object.bytep += sizeof(draw_fileheader);
    return (object);
}

/*
 Data Group  : functions descriptions table
 Description :
*/

static check_shift_table check_functions[] =
{
 {draw_OBJFONTLIST, sizeof(draw_fontliststrhdr), check_BIG, FALSE,
   check_fontList},
 {draw_OBJTEXT,     sizeof(draw_textstrhdr),     check_BIG, TRUE,
   check_textObject},
 {draw_OBJPATH,     sizeof(draw_pathstrhdr),     check_BIG, TRUE,
   check_pathObject},
#ifdef draw_OBJRECT
 {draw_OBJRECT,     sizeof(draw_objhdr),         sizeof(draw_objhdr), TRUE,
   NULL},
#endif
#ifdef draw_OBJELLI
 {draw_OBJELLI,     sizeof(draw_objhdr),         sizeof(draw_objhdr), TRUE,
   NULL},
#endif
 {draw_OBJSPRITE,   sizeof(draw_spristrhdr),     check_BIG, TRUE,
   check_spriteObject},
 {draw_OBJGROUP,    sizeof(draw_groustr),        check_BIG, TRUE,
   check_groupObject},
#ifdef draw_OBJTAGG
 {draw_OBJTAGG,     sizeof(draw_objhdr),         check_BIG, TRUE,
   check_tagObject},
#endif
 {draw_OBJTEXTAREA, sizeof(draw_textareastrhdr), check_BIG, TRUE,
   check_textArea},
 {-1,               0,  0,         FALSE, NULL}
};

#define MAXZOOMFACTOR 8
#define check_draw_text_oneColumn(obj)  \
(*(int *)(obj.bytep + sizeof(draw_textareahdr) + sizeof(draw_textcolhdr)) == 0)

static void check_bound_minmax(int x, int y, draw_bboxtyp *boundp)
{
  if (x < boundp->x0) boundp->x0 = x;
  if (x > boundp->x1) boundp->x1 = x;
  if (y < boundp->y0) boundp->y0 = y;
  if (y > boundp->y1) boundp->y1 = y;
}

static os_error *check_draw_displ_font_stringpixbbox(font fonth, char *ptr,
                                        draw_bboxtyp *boundp)
{ os_error *err = 0;
  draw_bboxtyp bound;
  int x,y, x2,y2;

  bound.x0 = bound.y0 = INT_MAX;
  bound.x1 = bound.y1 = INT_MIN;
  x = y = 0;

  while (*ptr)
  { font_info info;

    err = font_charbbox(fonth, *ptr, font_OSCOORDS, &info);
    err = font_converttoos(x,y, &x2, &y2);

    if (x2+info.minx < bound.x0) bound.x0 = x2+info.minx;
    if (y2+info.miny < bound.y0) bound.y0 = y2+info.miny;
    if (x2+info.maxx > bound.x1) bound.x1 = x2+info.maxx;
    if (y2+info.maxy > bound.y1) bound.y1 = y2+info.maxy;

    { font_string fs;
      fs.s = ptr;
      fs.x = fs.y = INT_MAX;   /*?????*/
      fs.split = -1;
      fs.term = 1;

      err = font_strwidth(&fs);

      x += fs.x; y += fs.y;
      ptr++;
    }
  }

  *boundp = bound;
  return(err);
}

static void check_bound_text(draw_objptr object, draw_bboxtyp *bbox)
{
  os_error *err = 0;
  font      fonth;
  char     *text;

  text = &(object.textp->text[0]);

  if(object.textp->textstyle.fontref)
  {
    if (err = font_find("System",
                        MAXZOOMFACTOR*object.textp->fsizex/40,
                        MAXZOOMFACTOR*object.textp->fsizey/40,
                        0,0, &fonth), !err)
    {
       err = check_draw_displ_font_stringpixbbox(fonth, text, bbox);
      font_lose(fonth);

      if (!err)
      { /* Calculate actual bbox in Draw units */
        bbox->x0 = (bbox->x0 << 8) / MAXZOOMFACTOR + object.textp->coord.x;
        bbox->x1 = (bbox->x1 << 8) / MAXZOOMFACTOR + object.textp->coord.x;
        bbox->y0 = (bbox->y0 << 8) / MAXZOOMFACTOR + object.textp->coord.y;
        bbox->y1 = (bbox->y1 << 8) / MAXZOOMFACTOR + object.textp->coord.y;

        return;
      }
    }
  }

/* Either text is in system font, OR an unfound fancy font (so rendered in  */
/* system font) or some font manager call went bang, so..                   */
/* Return BBox for system font. Assumes character base line is row 7 (of 8) */
/* Calculate actual bbox in Draw units */

  bbox->x0 = object.textp->coord.x;
  bbox->x1 = object.textp->coord.x + object.textp->fsizex*
                                                   strlen(object.textp->text);

  /* Assume char base line is row 7 (of 8) */
  bbox->y1 = object.textp->coord.y + 7*object.textp->fsizey/8;
  bbox->y0 = bbox->y1 - object.textp->fsizey;

  return;
}

static draw_dashstr *draw_obj_dashstart(draw_objptr object)
{
  return (object.pathp->pathstyle.joincapwind & packmask_dashed) ? &object.pathp->data : 0;
}

static drawmod_pathelemptr draw_obj_pathstart(draw_objptr hdrptr)
{
  drawmod_pathelemptr pathptr;

  if (hdrptr.pathp->pathstyle.joincapwind & packmask_dashed)
    pathptr.wordp =
             &(hdrptr.pathp->data.dashelements[hdrptr.pathp->data.dashcount]);
  else
    pathptr.bytep = (char *)&hdrptr.pathp->data;

  return pathptr;
}


static void check_draw_displ_unpackpathstyle(draw_objptr hdrptr,
                                drawmod_capjoinspec *jspecp)
{
  draw_pathstyle style = hdrptr.pathp->pathstyle;

  /*jspecp->join           = style.joincapwind&(unsigned char)0x0003;
  jspecp->leadcap        = style.joincapwind&(unsigned char)0x000c;
  jspecp->trailcap       = style.joincapwind&(unsigned char)0x0030;*/
  jspecp->join       = (style.joincapwind & packmask_join);
  jspecp->leadcap     = (style.joincapwind & packmask_endcap) >> packshft_endcap;
  jspecp->trailcap   = (style.joincapwind & packmask_startcap) >> packshft_startcap;
  jspecp->reserved8      = 0;
  jspecp->mitrelimit     = 0xA0000; /* Mitre limit=10.0 (postscript default) */
  jspecp->lead_tricap_w  =
  jspecp->trail_tricap_w = style.tricapwid << 4;
  jspecp->lead_tricap_h  =
  jspecp->trail_tricap_h = style.tricaphei << 4;
}


static drawmod_transmat MaxZoomMatrix = { MAXZOOMFACTOR*65536, 0,
                                         0, MAXZOOMFACTOR*65536,
                                         0, 0};

static void check_bound_path(draw_objptr object)
{
 #define FillStyle (fill_PFlatten | fill_PThicken | fill_PFlatten | \
                   fill_FBint | fill_FNonbint | fill_FBext)
  drawmod_line        linestyle;
  drawmod_options     options;
  drawmod_pathelemptr pathptr = draw_obj_pathstart(object);

  /* Set up line style */
  linestyle.flatness     = 200/MAXZOOMFACTOR;
  linestyle.thickness    = object.pathp->pathwidth;
  linestyle.dash_pattern = (drawmod_dashhdr *)draw_obj_dashstart(object);
  check_draw_displ_unpackpathstyle(object, &linestyle.spec);

  /* Set up options to get box */
  options.tag      = tag_box;
  options.data.box = (drawmod_box *)&object.pathp->bbox;

  wimpt_noerr(drawmod_processpath(pathptr, FillStyle, &MaxZoomMatrix,
                                   &linestyle, &options, NULL));

#define RoundUp(i) (int)((double)(i)/MAXZOOMFACTOR+0.5)
  /* Bounding box for path at maximum magnification, so scale down */
  object.pathp->bbox.x0 = RoundUp(object.pathp->bbox.x0);
  object.pathp->bbox.y0 = RoundUp(object.pathp->bbox.y0);
  object.pathp->bbox.x1 = RoundUp(object.pathp->bbox.x1);
  object.pathp->bbox.y1 = RoundUp(object.pathp->bbox.y1);

  /* Zero length paths produce a funny BBox, so merge MoveTo(x,y) into BBox */
  check_bound_minmax(pathptr.move2->x, pathptr.move2->y,
                        &(object.pathp->bbox));
}

static void check_bound_objtextcol(draw_objptr object)
{ object=object;}

static void check_bound_objtextarea(draw_objptr object)
{
    draw_textcolhdr *column;
    draw_bboxtyp    *bbox;

    /* Point to first column, and get its box */
    column = &(object.textareastrp->column);
    bbox = &(object.textareastrp->bbox);
    bbox->x0 = column->bbox.x0;
    bbox->y0 = column->bbox.y0;
    bbox->x1 = column->bbox.x1;
    bbox->y1 = column->bbox.y1;

    /* Form union of boxes */
    while ((++column)->tag == draw_OBJTEXTCOL)
    {
        if (column->bbox.x0 < bbox->x0) bbox->x0 = column->bbox.x0;
        if (column->bbox.y0 < bbox->y0) bbox->y0 = column->bbox.y0;
        if (column->bbox.x1 > bbox->x1) bbox->x1 = column->bbox.x1;
        if (column->bbox.y1 > bbox->y1) bbox->y1 = column->bbox.y1;
    }

        /* Apply a small shift to the box, if there is more than one column */
    if (!check_draw_text_oneColumn(object))
    {
        bbox->x0 -= (36<<8);
        bbox->y0 -= (36<<8);
        bbox->x1 += (36<<8);
        bbox->y1 += (36<<8);
    }
}

static void check_bound_sprite(draw_objptr object)
{
    int XEigFactor = bbc_modevar(object.spritep->sprite.mode, bbc_XEigFactor);
    int YEigFactor = bbc_modevar(object.spritep->sprite.mode, bbc_YEigFactor);
    int pixsizex = 0x100 << XEigFactor;
    int pixsizey = 0x100 << YEigFactor;

    sprite_id id;
    sprite_info info ;

    id.tag    = sprite_id_addr;
    id.s.addr = &object.spritep->sprite;

    (void)sprite_readsize((sprite_area*)0xFF, /* this op needs no spArea */
                          &id,                /* pass address of sprite  */
                          &info               /* result block            */
                         );

    object.spritep->bbox.x0 = 0;
    object.spritep->bbox.y0 = 0;
    object.spritep->bbox.x1 = pixsizex * info.width;
    object.spritep->bbox.y1 = pixsizey * info.height;

tracef4("sprite BBox is (%d,%d,%d,%d) \n", object.spritep->bbox.x0,
                                           object.spritep->bbox.y0,
                                           object.spritep->bbox.x1,
                                           object.spritep->bbox.y1
                        );

}


/*
 Function    : check_fix_bbox
 Purpose     : fix up a bounding box
 Parameters  : pointer to object data
 Returns     : void
*/

static void check_fix_bbox(draw_objptr object)
{
  switch(object.objhdrp->tag)
  {
    case draw_OBJTEXT:
      check_bound_text(object, &(object.textp->bbox));
      break;

    case draw_OBJPATH:
      check_bound_path(object);
      break;

    case draw_OBJSPRITE:
      check_bound_sprite(object);
      break;

    case draw_OBJGROUP:
    {  int i;
       int start = sizeof(draw_groustr);
       int limit = object.objhdrp->size;
       draw_objptr objptr;
       draw_bboxtyp bound;

       bound.x0 = bound.y0 = INT_MAX;
       bound.x1 = bound.y1 = INT_MIN;

       for (i = start; i < limit; i += objptr.objhdrp->size)
       {
         objptr.bytep = object.bytep+i;
         check_fix_bbox(objptr);
         check_bound_minmax(objptr.objhdrp->bbox.x0,
                            objptr.objhdrp->bbox.y0, &bound);
         check_bound_minmax(objptr.objhdrp->bbox.x1,
                            objptr.objhdrp->bbox.y1, &bound);
        }

        object.groupp->bbox.x1 = bound.x1;
        object.groupp->bbox.y1 = bound.y1;
        object.groupp->bbox.x0 = bound.x0;
        object.groupp->bbox.y0 = bound.y0;

        break;
    }

    case draw_OBJTEXTCOL:
      check_bound_objtextcol(object);
      break;

    case draw_OBJTEXTAREA:
      check_bound_objtextarea(object);
      break;

  }
}
/*
 Function    : check_object
 Purpose     : check an Draw object
 Parameters  : pointer to object data
 Returns     : pointer to next object
*/

draw_objptr check_object(draw_objptr object)
{
    draw_tagtyp type;
    check_shift_table *c;

    type = object.objhdrp->tag;

    for (c = check_functions ; c->tag != -1 ; c++)
    {
        if (c->tag == type)
        {
            /* Check size */
            check_size(object.objhdrp->size, c->sizelow, c->sizehigh,
                       object.bytep);
            if (check_ok & OK_FATAL)
                object.bytep = NULL;
            else
            {
                /* Check bbox */
                if (c->box)
                  if(check_bbox(&(object.objhdrp->bbox)))
                    check_fix_bbox(object);

                /* Additional checks */
                if (c->fn) (c->fn)(object);
            }
            break;
        }
    }

    object.bytep += object.objhdrp->size;
    return (object);
}

/*
 Function    : check_Draw_object
 Purpose     : check an individual object (for DrawFile module)
 Parameters  : object pointer
               OUT: error code
               OUT: error location
 Returns     : TRUE if object is OK
*/

BOOL check_Draw_object(draw_objptr object, int *code, int *location)
{
    check_fontSeen = check_textSeen = FALSE;
    check_ok       = OK_OK;

    check_object(object);

    if (check_ok == OK_OK)
      return (TRUE);
    else
    {
      *code     = errorCode;
      *location = errorLocation;
      return (FALSE);
    }
}

/*
 Function    : check_Draw_file
 Purpose     : check Draw file (for DrawFile module)
 Parameters  : pointer to buffer
               length of buffer
               OUT: code, location
 Returns     : TRUE if ok
*/

BOOL check_Draw_file(char *buffer, int length, int *code, int *location)
{
    draw_objptr object;
    char *end = buffer + length;

    check_fontSeen = check_textSeen = FALSE;
    check_ok       = OK_OK;
    object.bytep   = buffer;
    object         = check_fileHeader(object);

    while ((check_ok & OK_FATAL) == 0 && object.bytep < end)
        object = check_object(object);

    if (check_ok == OK_OK)
    {
      return (TRUE);
    }
    else
    {
        *code     = errorCode;
        *location = errorLocation - (int)buffer;
        return (FALSE);
    }
}



/*-------------------------------------------------------------------------*/
/* Code for shifting files                                                 */

/* Globals to hold the current base */
static int shift_x, shift_y;

/* Forward reference */
draw_objptr shift_object(draw_objptr object);

/*
 Function    : shift_coord
 Purpose     : shift a coordinate
 Parameters  : pointer to x (y assumed to be 1 word after x)
 Returns     : void
*/

static void shift_coord(int *at)
{
    at[0] += shift_x;
    at[1] += shift_y;
}

/*
 Function    : shift_bbox
 Purpose     : shift a bounding box
 Parameters  : box pointer
 Returns     : void
 Description : shifts each coordinate
*/

static void  shift_bbox(draw_bboxtyp *box)
{
    box->x0 += shift_x;
    box->y0 += shift_y;
    box->x1 += shift_x;
    box->y1 += shift_y;
}

/* Text - shift base location */
static Check_shift_fn(shift_textObject)
{
    shift_coord(&(object.textp->coord.x));
}

/* Path: shift each element of path */
static Check_shift_fn(shift_pathObject)
{
    path_eleptr path;

    path.move = address_pathstart(object);
    do
    {
        switch (path.move->tag.tag)
        {
            case Draw_PathTERM:
                path.bytep += sizeof(path_termstr); break;
            case Draw_PathMOVE:
                shift_coord(&(path.move->x));
                path.bytep += sizeof(path_movestr); break;
            case Draw_PathLINE:
                shift_coord(&(path.line->x));
                path.bytep += sizeof(path_linestr); break;
            case Draw_PathCLOSE:
                path.bytep += sizeof(path_closestr); break;
            case Draw_PathCURVE:
                shift_coord(&(path.curve->x1));
                shift_coord(&(path.curve->x2));
                shift_coord(&(path.curve->x3));
                path.bytep += sizeof(path_curvestr); break;
        }
    } while (path.move->tag.tag != Draw_PathTERM);
}

/* Group - recurse on contents */
static Check_shift_fn(shift_groupObject)
{
    char *end;

    end = object.bytep + object.groupp->size;
    object.bytep += sizeof(draw_groustr);

    while (object.bytep < end)
        object = shift_object(object);
}

#ifdef draw_OBJTAGG
/* Tagged - recurse */
Check_shift_fn(shift_tagObject)
{
    shift_object(object.bytep + 28);
}
#endif

/* Text area: shift each column */
static Check_shift_fn(shift_textArea)
{
    /* Point to first column */
    for (object.bytep += sizeof(draw_textareahdr) ;
         object.objhdrp->tag == draw_OBJTEXTCOL ;
         object.bytep += sizeof(draw_textcolhdr))
    {
        shift_bbox(&(object.textcolp->bbox));
    }
}

/* Functions table - size fields are not used */
static check_shift_table shift_functions[] =
{
 {draw_OBJFONTLIST, 0, 0, FALSE, NULL},
 {draw_OBJTEXT,     0, 0, TRUE,  shift_textObject},
 {draw_OBJPATH,     0, 0, TRUE,  shift_pathObject},
#ifdef draw_OBJRECT
 {draw_OBJRECT,     0, 0, TRUE,  NULL},
#endif
#ifdef draw_OBJELLI
 {draw_OBJELLI,     0, 0, TRUE,  NULL},
#endif
 {draw_OBJSPRITE,   0, 0, TRUE,  NULL},
 {draw_OBJGROUP,    0, 0, TRUE,  shift_groupObject},
#ifdef draw_OBJTAGG
 {draw_OBJTAGG,     0, 0, TRUE,  shift_tagObject},
#endif
 {draw_OBJTEXTAREA, 0, 0, TRUE,  shift_textArea},
 {-1,               0, 0, FALSE, NULL}
};

/*
 Function    : shift_object
 Purpose     : shift an arc draw object
 Parameters  : object pointer
 Returns     : pointer to next object
*/

draw_objptr shift_object(draw_objptr object)
{
    draw_tagtyp type;
    check_shift_table *s;

    type = object.objhdrp->tag;

    for (s = shift_functions ; s->tag != -1 ; s++)
    {
        if (s->tag == type)
        {
            /* Shift bbox */
            if (s->box) shift_bbox(&(object.objhdrp->bbox));

            /* Additional shifting */
            if (s->fn) (s->fn)(object);
            break;
        }
    }

    object.bytep += object.objhdrp->size;
    return (object);
}

/*
 Function    : shift_Draw_file
 Purpose     : transform all coordinates to a new origin
 Parameters  : pointer to buffer
               buffer length
               x base, y base
 Returns     : void
 Description : this shifts all coordinates in the Draw file held in the
               buffer to the given base. It uses code similar to the checking
               code to follow the structure of the file.
               Assumes the buffer has already been checked.
*/

void shift_Draw_file(char *buffer, int length, int xMove, int yMove)
{
    draw_objptr object;
    char *end = buffer + length;

    shift_x = xMove;
    shift_y = yMove;

    object.bytep = buffer;
    shift_bbox(&object.filehdrp->bbox);

    object.bytep = buffer + sizeof(draw_fileheader);

    while (object.bytep < end)
        object = shift_object(object);
}