/*
** $PROJECT: binary.datatype
**
** $VER: dispatch.c 39.10 (19.02.95)
**
** by
**
** Stefan Ruppert , Windthorststraße 5 , 65439 Flörsheim , GERMANY
**
** (C) Copyright 1994,1995
** All Rights Reserved !
**
** $HISTORY:
**
** 19.02.95 : 039.010 : fixed bug,which caused a crash ,if the display width was
**                      less than the required width for the offsets.
**                      prefs file parsing now via ReadArgs().
**                      new prefs directory : 'Env:DataTypes' .
** 28.01.95 : 039.009 : fixed down scrolling bug, which skipped one line
** 22.01.95 : 039.008 : now supports DTM_PRINT method and searchs for PROGDIR:binary.prefs
** 21.01.95 : 039.007 : bug fixed. Rendering address higher than the file length (small files)
** 28.12.94 : 039.006 : now it's a base datatype and no async layout is needed
** 24.11.94 : 039.005 : memory handling improved, layout only performed if needed
** 18.11.94 : 039.004 : added PREFS file
** 16.11.94 : 039.003 : BDTDH_NONE now works
** 15.11.94 : 039.002 : added BDTA_BytesPerLine,BDTA_DisplayHex and BDTA_ShowASCII attrs
** 13.11.94 : 039.001 : initial
*/

/* ------------------------------- include -------------------------------- */

#include "classbase.h"

/* ------------------------------- autodoc -------------------------------- */

/*FS*/ /*"AutoDoc"*/
/*GB*** binary.datatype/binary.datatype **************************************
*
*    NAME
*        binary.datatype -- data type for any binary file
*
*    FUNCTION
*        The binary data type, a base-class of all binary data, is used to
*        load any binary file and displays the contents of the file in hex
*        format.
*
*    PREFS
*        The data type tries to load the prefs file first from
*        "PROGDIR:Prefs/binary.prefs","PROGDIR:binary.prefs" and then
*        "ENV:DataTypes/binary.prefs" on each OM_NEW method to set up the
*        attributes !
*        Up from version 39.10 it uses the ReadArgs() function to parse the
*        prefs file. The template is :
*
*        NOASCII/S,NOWRAP/S,NONE/S,BYTE/S,WORD/S,LONG/S,BPL=BYTESPERLINE/N/K
*
*        NOASCII   - sets BDTA_ShowASCII to FALSE
*        NOWRAP    - sets BDTA_DisplayWrap to FALSE
*        NONE      - sets BDTA_DisplayHex to BDTDH_NONE
*        BYTE      - sets BDTA_DisplayHex to BDTDH_BYTE
*        WORD      - sets BDTA_DisplayHex to BDTDH_WORD
*        LONG      - sets BDTA_DisplayHex to BDTDH_LONG
*        BYTESPERLINE <bpl> or
*        BPL <bpl> - sets BDTA_BytesPerLine to <bpl> bytes
*
*        The options can be on several lines !
*
*    METHODS
*        OM_NEW -- Create a new text object from a binary file in hex mode.
*
*        OM_DISPOSE -- dispose a object
*
*        OM_GET -- get a attribute of the object
*
*        OM_SET -- set attributes of the object
*
*        OM_UPDATE -- update some attributes of the object
*
*        GM_LAYOUT -- Method to layout the hex text
*
*        GM_RENDER -- draw the object
*
*        DTM_WRITE -- DTWM_RAW mode is supported
*
*        DTM_PRINT -- prints the hex text
*
*    TAGS
*        BDTA_Buffer -- (UBYTE *) pointer to the buffer, which should be
*            displayed.
*            Applicability is (ISG).
*
*        BDTA_BufferLen -- (ULONG) length of the buffer supplied with
*            BDTA_Buffer tag. This must be given if the buffer tag is
*            specified.
*            Applicability is (ISG).
*
*        BDTA_BytesPerLine -- (UWORD) number of bytes per line.
*            If BDTA_DisplayHex is BDTDH_WORD it must be a multiply of 2,
*            if it is BDTDH_LONG it must be a multiply of 4 !
*            Default is 32.
*            Applicability is (ISGNU).
*
*        BDTA_DisplayHex -- (UWORD) type of the display. The following types
*            are supported : BDTDH_NONE  - displays no hex values
*                            BDTDH_BYTE  - displays each byte in hex ( 8 bit)
*                            BDTDH_WORD  - displays each word in hex (16 bit)
*                            BDTDH_LONG  - displays each long in hex (32 bit)
*            Default is BDTDH_LONG.
*            Applicability is (ISGNU).
*
*        BDTA_ShowASCII -- (BOOL) display at the end of the line the
*            appropriate ASCII string !
*            Default is TRUE.
*            Applicability is (ISGNU).
*
*        BDTA_DisplayWrap -- (BOOL) the BDTA_BytesPerLine are ignored and the
*            byte number is retrieved from the object width !
*            Default is TRUE.
*            Applicability is (ISGNU).
*
*    BUGS
*        At the moment proportional fonts can't be handled.
*
*    SEE ALSO
*        datatypesclass (where ?)
*
******************************************************************************
*
*/
/*FE*/

/* ------------------------------- defines -------------------------------- */

#define G(o)               ((struct Gadget *) (o))
#define GPR(msg)           ((struct gpRender *) (msg))
#define SET(msg)           ((struct opSet *) (msg))
#define WRMSG(msg)         ((struct dtWrite *) (msg))

#define prefstemplate      "NOASCII/S,NOWRAP/S,NONE/S,BYTE/S,WORD/S,LONG/S,BPL=BYTESPERLINE/N/K"

enum
{
   PREFSARG_NOASCII,
   PREFSARG_NOWRAP,
   PREFSARG_NONE,
   PREFSARG_BYTE,
   PREFSARG_WORD,
   PREFSARG_LONG,
   PREFSARG_BYTESPERLINE,
   PREFSARG_MAX
};

/* ---------------------------- instance data ----------------------------- */

struct BinaryData
{
   UBYTE *bd_Buffer;
   UBYTE *bd_BufferEnd;
   ULONG bd_BufferLen;

   UWORD bd_Flags;
   UWORD bd_DisplayHex;
   UWORD bd_BytesPerLine;

   UWORD bd_NumGroups;
   UWORD bd_LineBytes;

   UWORD bd_LineBufferLen;
   UBYTE *bd_LineBuffer;

   ULONG bd_OldX;
   ULONG bd_OldY;

   /* font support */
   struct TextFont *bd_Font;
   struct TextAttr bd_TextAttr;
   UBYTE bd_FontName[MAXFONTNAME];

   UBYTE bd_Name[256];
};

#define BDF_SHOWASCII         (1<<0)
#define BDF_ALLOCATED         (1<<1)
#define BDF_DISPLAYWRAP       (1<<2)

/* ---------------------------- constant data ----------------------------- */

const ULONG methods[] = {
   OM_NEW,
   OM_DISPOSE,
   OM_GET,
   OM_SET,
   OM_UPDATE,
   GM_LAYOUT,
   GM_RENDER,
   DTM_WRITE,
   DTM_PRINT,
   ~0};

/* ------------------------------ init class ------------------------------ */

/*FS*/ LibCall Class *initClass(REGA6 struct ClassBase *cb)
{
   Class *cl;

   D(bug("init binary.datatype !\n"));

   if((cl = MakeClass("binary.datatype","datatypesclass",NULL,sizeof(struct BinaryData),0)))
   {
      cl->cl_Dispatcher.h_Entry = (HOOKFUNC) dispatch;
      cl->cl_UserData = (ULONG) cb;

      AddClass(cl);
   }

   return(cl);
}
/*FE*/

/* -------------------------- support functions --------------------------- */

/*FS*/ ULONG notifyAttrChanges(Object * obj, void * ginfo, ULONG flags, ULONG tag1,...)
{
    return(DoMethod(obj, OM_NOTIFY, &tag1, ginfo, flags));
}
/*FE*/

/*FS*/ ULONG setSuperAttrs(Class *cl,Object * obj, void *ginfo,ULONG tag1,...)
{
    return(DoSuperMethod(cl,obj, OM_SET, &tag1, ginfo));
}
/*FE*/

/*FS*/ BOOL readPrefs(struct ClassBase *cb,struct BinaryData *bd,STRPTR file)
{
   struct RDArgs *rdargs;
   struct RDArgs *args;
   BPTR fh;

   ENTERING;

   if((fh = Open(file,MODE_OLDFILE)))
   {
      UBYTE buf[256];
      ULONG para[PREFSARG_MAX];
      LONG i;

      DB(("def file %s opened\n",file));

      while(FGets(fh,buf,sizeof(buf)))
      {
         DB(("line : %s",buf));

         if((rdargs = (struct RDArgs *) AllocDosObject(DOS_RDARGS,NULL)))
         {
            rdargs->RDA_Source.CS_Buffer   = buf;
            rdargs->RDA_Source.CS_Length   = strlen(buf);

            for(i=0 ; i < (sizeof(para)/sizeof(LONG)) ; i++)
               para[i]=0;

            DB(("rdargs at : %lx\n",rdargs));
            
            if(args = ReadArgs(prefstemplate,(LONG *) para,rdargs))
            {
               DB(("args at %lx\n",args));

               if(para[PREFSARG_NOASCII])
                  bd->bd_Flags &= ~BDF_SHOWASCII;

               if(para[PREFSARG_NOWRAP])
                  bd->bd_Flags &= ~BDF_DISPLAYWRAP;

               if(para[PREFSARG_NONE])
                  bd->bd_DisplayHex = BDTDH_NONE;

               if(para[PREFSARG_BYTE])
                  bd->bd_DisplayHex = BDTDH_BYTE;

               if(para[PREFSARG_WORD])
                  bd->bd_DisplayHex = BDTDH_WORD;

               if(para[PREFSARG_LONG])
                  bd->bd_DisplayHex = BDTDH_LONG;

               if(para[PREFSARG_BYTESPERLINE])
               {
                  bd->bd_BytesPerLine = (UWORD) *((LONG *) para[PREFSARG_BYTESPERLINE]);

                  if(bd->bd_BytesPerLine < 4)
                     bd->bd_BytesPerLine = 4;
                  if(bd->bd_BytesPerLine > (1<<16))
                     bd->bd_BytesPerLine = 32;
               }
            }
            FreeDosObject(DOS_RDARGS , rdargs);
         }
      }
      Close(fh);
   }

   LEAVING;

   return((BOOL) (fh == NULL));
}
/*FE*/

/*FS*/ void useDefaultFont(struct ClassBase *cb,struct BinaryData *bd)
{
   struct TextFont *deffont;

   D(bug("using default font !\n"));

   #undef GfxBase
   deffont = ((struct GfxBase *) cb->cb_GfxBase)->DefaultFont;
   #define GfxBase cb->cb_GfxBase

   bd->bd_TextAttr.ta_YSize = deffont->tf_YSize;
   bd->bd_TextAttr.ta_Style = FS_NORMAL;
   bd->bd_TextAttr.ta_Flags = deffont->tf_Flags;
   bd->bd_TextAttr.ta_Name  = bd->bd_FontName;
   strcpy(bd->bd_TextAttr.ta_Name,deffont->tf_Message.mn_Node.ln_Name);

   bd->bd_Font = OpenFont(&bd->bd_TextAttr);
}
/*FE*/

/* --------------------------- class dispatcher --------------------------- */

/*FS*/ ClassCall ULONG dispatch(REGA0 Class *cl,REGA2 Object *obj,REGA1 Msg msg)
{
   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   struct BinaryData *bd = INST_DATA(cl,obj);
   ULONG retval = 0;

   switch(msg->MethodID)
   {
   case OM_NEW:
      {
         Object *newobj;
         if((newobj = (Object *) DoSuperMethodA(cl,obj,msg)))
         {
            bd = INST_DATA(cl,newobj);

            /* set default setting */
            bd->bd_BytesPerLine = 32;
            bd->bd_DisplayHex   = BDTDH_LONG;
            bd->bd_Flags        = BDF_SHOWASCII | BDF_DISPLAYWRAP;

            /* read default user settings */
            if(readPrefs(cb,bd,"PROGDIR:Prefs/binary.prefs"))
               if(readPrefs(cb,bd,"PROGDIR:binary.prefs"))
                  readPrefs(cb,bd,"ENV:DataTypes/binary.prefs");

            setattrs(cl,newobj,(struct opSet *) msg);

            /* use default textfont */
            if(!bd->bd_Font)
               useDefaultFont(cb,bd);

            /* if no buffer is provided , get the handle from the superclass
               and load the file in the buffer */
            if(!bd->bd_Buffer)
            {
               BPTR fh;
               ULONG type;
               LONG num;

               if(((num = GetDTAttrs((Object *) newobj,DTA_Handle     ,&fh,
                                                       DTA_SourceType ,&type,
                                                       TAG_DONE)) == 2) && fh)
               {
                  struct FileInfoBlock *fib;

                  D(bug("type : %ld\n",type));

                  switch(type)
                  {
                  case DTST_FILE:
                     if((fib = AllocDosObject(DOS_FIB,NULL)))
                     {
                        if(ExamineFH(fh,fib) && fib->fib_Size > 0)
                        {
                           if((bd->bd_Buffer = AllocMem(fib->fib_Size,MEMF_ANY)))
                           {
                              bd->bd_Flags |= BDF_ALLOCATED;
                              bd->bd_BufferLen = fib->fib_Size;
                              bd->bd_BufferEnd = &bd->bd_Buffer[fib->fib_Size];
                              Seek(fh,0,OFFSET_BEGINNING);
                              Read(fh,bd->bd_Buffer,bd->bd_BufferLen);
                           }
                        }
                     }
                     FreeDosObject(DOS_FIB,fib);
                     if(!NameFromFH(fh,bd->bd_Name,sizeof(bd->bd_Name)))
                        strcpy(bd->bd_Name,"unknown");
                     break;
                  case DTST_RAM:
                     strcpy(bd->bd_Name,"RAM");
                     break;
                  default:
                     D(bug("not supported SourceType : %ld\n",type));
                  }

               }
            }

            if(bd->bd_Buffer)
            {
               retval = (ULONG) newobj;
               setSuperAttrs(cl,newobj,NULL,GA_RelSpecial,TRUE,TAG_DONE);
               ((struct ExtGadget *) newobj)->MoreFlags = GMORE_SCROLLRASTER;
            } else  /* something was going wrong, thus dispose myself */
            {
               D(bug("binary.datatype error : %ld\n",IoErr()));
               CoerceMethod(cl,(Object *) retval,OM_DISPOSE);
            }
         }
      }
      break;
   case OM_DISPOSE:
      if(bd->bd_Buffer && (bd->bd_Flags & BDF_ALLOCATED))
         FreeMem(bd->bd_Buffer,bd->bd_BufferLen);

      if(bd->bd_LineBuffer && bd->bd_LineBufferLen)
         FreeMem(bd->bd_LineBuffer,bd->bd_LineBufferLen);

      if(bd->bd_Font)
         CloseFont(bd->bd_Font);

      retval = DoSuperMethodA(cl,obj,msg);
      break;
   case OM_UPDATE:
   case OM_SET:
      if((retval = setattrs(cl,obj,(struct opSet *) msg)))
         DoMethod(obj,GM_LAYOUT,((struct opSet *) msg)->ops_GInfo,TRUE);

      /* Pass the attributes to the super class and force a refresh
       * if we need it */
      if((retval += DoSuperMethodA (cl, obj, msg)) && (OCLASS (obj) == cl))
      {
         struct RastPort *rp;

         /* Get a pointer to the rastport */
         if((rp = ObtainGIRPort (((struct opSet *) msg)->ops_GInfo)))
         {
            struct gpRender gpr;

            /* Force a redraw */
            gpr.MethodID   = GM_RENDER;
            gpr.gpr_GInfo  = ((struct opSet *) msg)->ops_GInfo;
            gpr.gpr_RPort  = rp;
            gpr.gpr_Redraw = GREDRAW_UPDATE;
            DoMethodA (obj, (Msg) &gpr);

            /* Release the temporary rastport */
            ReleaseGIRPort (rp);
         }

         retval = 0;
      }
      break;
   case OM_GET:
      if(!(retval = getattr(cl,obj,(struct opGet *) msg)))
         retval = DoSuperMethodA(cl,obj,msg);
      break;
   case GM_LAYOUT:
      /* Tell everyone that we are busy doing things */
      notifyAttrChanges (obj, ((struct gpLayout *) msg)->gpl_GInfo, NULL,
                         GA_ID,       G(obj)->GadgetID,
                         DTA_Busy,    TRUE,
                         TAG_DONE);

      /* Let the super-class partake */
      retval = DoSuperMethodA (cl, obj, msg);

      retval += layout(cb, cl, obj, (struct gpLayout *) msg);

      break;

   case GM_RENDER:
      {
         struct RastPort *rp = GPR(msg)->gpr_RPort;
         struct DrawInfo *dri = GPR(msg)->gpr_GInfo->gi_DrInfo;
         struct Rectangle rect;
         struct IBox *domain;
         ULONG x,y;
         ULONG ux,uy;
         ULONG py,px;
         LONG topy,topx,num;
         LONG w,h;
         LONG dx,dy,height;
         LONG chrs;

         STRPTR ptr;

         UBYTE fgpen = (dri) ? dri->dri_Pens[TEXTPEN]       : 1;
         UBYTE bgpen = (dri) ? dri->dri_Pens[BACKGROUNDPEN] : 0;

         D(bug("draw mode : %lx\n",GPR(msg)->gpr_Redraw));

         if(GetDTAttrs(obj,DTA_TopVert     ,&y,
                           DTA_TopHoriz    ,&x,
                           DTA_VisibleVert ,&h,
                           DTA_VisibleHoriz,&w,
                           DTA_VertUnit    ,&uy,
                           DTA_HorizUnit   ,&ux,
                           DTA_Domain      ,&domain,
                           TAG_DONE) == 7)
         {

            topy = y;
            topx = x;

            height = (h-1) * uy;

            rect.MinX = domain->Left;
            rect.MinY = domain->Top;
            rect.MaxX = domain->Left + w * ux - 1;
            rect.MaxY = domain->Top  + h * uy - 1;

            SetFont(GPR(msg)->gpr_RPort,bd->bd_Font);
            SetABPenDrMd(rp,fgpen,bgpen,JAM2);

            y *= uy;
            x *= ux;

            dx = (x - bd->bd_OldX);
            dy = (y - bd->bd_OldY);

            bd->bd_OldX = x;
            bd->bd_OldY = y;

            if(dx > domain->Width/2 || dx <-domain->Width/2)
               dx = 0;

            if(dy > height/2 || dy < -height/2)
               dy = 0;

            py = 0;
            px = 0;

            if(dx || dy)
               ScrollRasterBF(rp,dx,dy,
                              rect.MinX,rect.MinY,rect.MaxX,rect.MaxY);

            D(bug("dx : %ld , dy : %ld\n",dx,dy));

            if(dx == 0)
            {
               if(dy < 0)
                  height = -dy;
               else if(dy > 0)
               {
                  py = height - dy;
                  topy += ((domain->Height / uy) - dy/uy) - 1;
               }
            } else if(dx > 0)
            {
               px = w * ux - dx;
               topx += (w - (dx/ux));
               w = dx/ux;
            } else
            {
               w = (-dx/ux);
            }

            num = topy * bd->bd_BytesPerLine;
            ptr = &bd->bd_Buffer[num];

            D(bug("py : %ld , height : %ld , topy : %ld\n",py,height,topy));

            for(; py <= height && ptr < bd->bd_BufferEnd ; py += rp->TxHeight)
            {
               if((chrs = formatline(bd,&ptr,bd->bd_LineBuffer,num) - topx) > w)
                  chrs = w;

               if(chrs > 0)
               {
                  Move(rp,domain->Left + px,domain->Top + py + rp->TxBaseline);
                  Text(rp,&bd->bd_LineBuffer[topx],chrs);
               }

               num += bd->bd_BytesPerLine;
            }
         }
      }
      retval = DoSuperMethodA(cl,obj,msg);
      break;
   case DTM_WRITE:
      switch(WRMSG(msg)->dtw_Mode)
      {
      case DTWM_RAW:
         D(bug("dtWrite Rawmode : filehandle %lx !\n",WRMSG(msg)->dtw_FileHandle));
         D(bug("attrs at : %lx\n",WRMSG(msg)->dtw_AttrList));
         D({
               struct TagItem *tstate = WRMSG(msg)->dtw_AttrList;
               struct TagItem *tag;

               while((tag = NextTagItem(&tstate)))
                  bug("{%08lx,%ld}\n",tag->ti_Tag,tag->ti_Data);
           });

         Write(WRMSG(msg)->dtw_FileHandle,bd->bd_Buffer,bd->bd_BufferLen);
         retval = 1;
         break;
      default:
         retval = DoSuperMethodA(cl,obj,msg);
      }
      break;
   case DTM_PRINT:
      {
         union printerIO *pio = ((struct dtPrint *) msg)->dtp_PIO;
         STRPTR ptr;
         STRPTR buf;
         LONG len;
         ULONG num = 0;

         ptr = &bd->bd_Buffer[0];

         if((buf = AllocVec(bd->bd_LineBufferLen,MEMF_ANY)))
         {
            pio->ios.io_Command = CMD_WRITE;
            pio->ios.io_Data    = buf;

            while(ptr < bd->bd_BufferEnd && pio->ios.io_Error  == 0)
            {
               len = formatline(bd,&ptr,buf,num);

               if(len > 0)
               {
                  buf[len] = '\n';
                  pio->ios.io_Length = len + 1;
                  DoIO((struct IORequest *) &pio->ios);
               }
               num += bd->bd_BytesPerLine;
            }
            FreeVec(buf);
         }
      }
      break;
   default:
      retval = DoSuperMethodA(cl,obj,msg);
   }

   return(retval);
}
/*FE*/

/*FS*/ ULONG getattr(Class *cl,Object *obj,struct opGet *msg)
{
   struct BinaryData *bd = INST_DATA(cl,obj);
   ULONG *storage = msg->opg_Storage;

   switch(msg->opg_AttrID)
   {
   case DTA_TextFont:
      *storage = (ULONG) bd->bd_Font;
      break;
   case DTA_TextAttr:
      *storage = (ULONG) &bd->bd_TextAttr;
      break;
   case DTA_Methods:
      *storage = (ULONG) methods;
      break;
   case BDTA_Buffer:
      *storage = (ULONG) bd->bd_Buffer;
      break;
   case BDTA_BufferLen:
      *storage = (ULONG) bd->bd_BufferLen;
      break;
   case BDTA_BytesPerLine:
      *storage = (ULONG) bd->bd_BytesPerLine;
      break;
   case BDTA_DisplayHex:
      *storage = (ULONG) bd->bd_DisplayHex;
      break;
   case BDTA_ShowASCII:
      *storage = (ULONG) ((bd->bd_Flags & BDF_SHOWASCII) == BDF_SHOWASCII);
      break;
   case BDTA_DisplayWrap:
      *storage = (ULONG) ((bd->bd_Flags & BDF_DISPLAYWRAP) == BDF_DISPLAYWRAP);
      break;
   default:
      return(0);
   }
   return(1);
}
/*FE*/

/*FS*/ ULONG setattrs(Class *cl,Object *obj,struct opSet *msg)
{
   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   struct BinaryData *bd = INST_DATA(cl,obj);

   struct TagItem *tstate = msg->ops_AttrList;
   struct TagItem *tag;

   ULONG retval = 0;
   ULONG tidata;

   while((tag = NextTagItem(&tstate)))
   {
      tidata = tag->ti_Data;

      retval++;
      switch(tag->ti_Tag)
      {
      case DTA_TextAttr:
         {
            struct TextFont *font;

            if((font = OpenFont((struct TextAttr *) tidata)))
            {
               /* only use fixed width fonts */
               if(font->tf_Flags & FPF_PROPORTIONAL)
               {
                  CloseFont(font);
                  useDefaultFont(cb,bd);
               } else
               {
                  bd->bd_TextAttr = *((struct TextAttr *) tidata);
                  bd->bd_TextAttr.ta_Name = bd->bd_FontName;
                  strcpy(bd->bd_FontName,((struct TextAttr *) tidata)->ta_Name);

                  if(bd->bd_Font)
                     CloseFont(bd->bd_Font);

                  bd->bd_Font = font;
               }
            } else
               retval--;
         }
         break;
      case BDTA_Buffer:
         {
            ULONG len = GetTagData(BDTA_BufferLen,0,msg->ops_AttrList);

            if(tidata && len)
            {
               if((bd->bd_Buffer) && (bd->bd_Flags & BDF_ALLOCATED))
                  FreeMem(bd->bd_Buffer,bd->bd_BufferLen);

               bd->bd_Flags &= ~BDF_ALLOCATED;

               bd->bd_Buffer    = (UBYTE *) tidata;
               bd->bd_BufferLen = len;
               bd->bd_BufferEnd = &bd->bd_Buffer[len];

               retval++;
            } else
               retval--;
         }
         break;
      case BDTA_BytesPerLine:
         if(tag->ti_Data != bd->bd_BytesPerLine)
            bd->bd_BytesPerLine = (UWORD) tag->ti_Data;
         else
            retval--;
         break;
      case BDTA_DisplayHex:
         if(tag->ti_Data != bd->bd_DisplayHex)
            bd->bd_DisplayHex   = (UWORD) tag->ti_Data;
         else
            retval--;
         break;
      case BDTA_ShowASCII:
         if(tag->ti_Data)
            bd->bd_Flags |= BDF_SHOWASCII;
         else
            bd->bd_Flags &= ~BDF_SHOWASCII;
         break;
      case BDTA_DisplayWrap:
         if(tag->ti_Data)
            bd->bd_Flags |= BDF_DISPLAYWRAP;
         else
            bd->bd_Flags &= ~BDF_DISPLAYWRAP;
         break;
      default:
         retval--;
      }
   }

   return(retval);
}
/*FE*/

/*FS*/ GetA4 ULONG layout(struct ClassBase *cb, Class * cl, Object * obj, struct gpLayout * gpl)
{
   struct DTSpecialInfo *si = (struct DTSpecialInfo *) G(obj)->SpecialInfo;
   struct BinaryData *bd = INST_DATA(cl,obj);

   struct TextFont *font = bd->bd_Font;

   /* Attributes obtained from super-class */
   struct IBox *domain;

   ULONG retval = 0;

   ENTERING;

   /* Get all the attributes that we are going to need for a successful layout */
   if((GetDTAttrs(obj,DTA_Domain,      (ULONG) &domain,
                      TAG_DONE) == 1))
   {
      /* Lock the global object data so that nobody else can manipulate it */
      ObtainSemaphore (&(si->si_Lock));

      retval = si->si_TotVert;

      /* We only need to perform layout if we are doing word wrap, or this
       * is the initial layout call */
      if(gpl->gpl_Initial || (bd->bd_Flags & BDF_DISPLAYWRAP))
      {
         const UBYTE displayhexbytes[]   = {0,3,5,9};
         const UBYTE displayasciibytes[] = {1,1,2,4};

         WORD displayaddr = (bd->bd_BufferLen >= (1<<16)) ? 10 : 6;
         WORD numgroups;
         WORD bytesperline;
         WORD showascii = 0;
         LONG lines;

         D(bug("calc new layout values !\n"));

         if((bd->bd_Flags & BDF_SHOWASCII) || bd->bd_DisplayHex == BDTDH_NONE)
            showascii = 1;

         numgroups    =
         bytesperline = bd->bd_BytesPerLine;

         if(bd->bd_DisplayHex > 0)
            numgroups >>= (bd->bd_DisplayHex - 1);

         /* Note this wrap function doesn't work right with proportional fonts ! */
         if(bd->bd_Flags & BDF_DISPLAYWRAP)
         {
            ULONG chars = domain->Width / bd->bd_Font->tf_XSize;

            D(bug("object width : %ld\n",domain->Width));
            D(bug("font width   : %ld\n",font->tf_XSize));
            D(bug("chars        : %ld\n",chars));

            if(chars > displayaddr + showascii)
            {
               bytesperline =
               numgroups    = (chars - displayaddr - showascii) / (displayhexbytes[bd->bd_DisplayHex] +
                                                                   ((showascii) ? displayasciibytes[bd->bd_DisplayHex] : 0));

               if(numgroups < 1)
                  numgroups = 1;
            } else
               numgroups = 1;

            if(bd->bd_DisplayHex > 0)
               bytesperline = (numgroups) << (bd->bd_DisplayHex - 1);
         }

         if(bytesperline < 4)
         {
            bytesperline  = 4;
            numgroups     = 4 / displayasciibytes[bd->bd_DisplayHex];
         }

         lines = (bd->bd_BufferLen / bytesperline) + ((bd->bd_BufferLen % bytesperline) ? 1 : 0) - 1;

         bd->bd_LineBytes = displayaddr + numgroups * displayhexbytes[bd->bd_DisplayHex] +
                            ((showascii) ? bytesperline : 0) + 1;
         bd->bd_NumGroups    = numgroups;
         bd->bd_BytesPerLine = bytesperline;

         if(bd->bd_LineBuffer && bd->bd_LineBytes > bd->bd_LineBufferLen)
         {
            FreeMem(bd->bd_LineBuffer,bd->bd_LineBufferLen);
            bd->bd_LineBuffer = NULL;
         }

         if(!bd->bd_LineBuffer)
         {
            bd->bd_LineBufferLen = bd->bd_LineBytes;
            bd->bd_LineBuffer    = AllocMem(bd->bd_LineBufferLen,MEMF_ANY);
         }

         /* Compute the lines and columns type information */
         si->si_VertUnit  = font->tf_YSize;
         si->si_VisVert   = domain->Height / si->si_VertUnit;
         si->si_TotVert   = lines;

         si->si_HorizUnit = font->tf_XSize;
         si->si_VisHoriz  = domain->Width / font->tf_XSize;
         si->si_TotHoriz  = bd->bd_LineBytes;

         /* Release the global data lock */
         ReleaseSemaphore(&si->si_Lock);

         retval = lines;

         D(bug("after releasesem !\n"));

         if(gpl->gpl_Initial)
         {
            struct RastPort *rp;

            /* Get a pointer to the rastport */
            if((rp = ObtainGIRPort (gpl->gpl_GInfo)))
            {
               struct gpRender gpr;

               /* Force a redraw */
               gpr.MethodID   = GM_RENDER;
               gpr.gpr_GInfo  = gpl->gpl_GInfo;
               gpr.gpr_RPort  = rp;
               gpr.gpr_Redraw = GREDRAW_REDRAW;
               DoMethodA (obj, (Msg) &gpr);

               /* Release the temporary rastport */
               ReleaseGIRPort (rp);
            }
         }
      } else
         ReleaseSemaphore(&si->si_Lock);

      /* Not aborted, so tell the world of our newest attributes */
      notifyAttrChanges (obj, gpl->gpl_GInfo, NULL,
                         GA_ID,                   G(obj)->GadgetID,

                         DTA_VisibleVert,         domain->Height / font->tf_YSize,
                         DTA_TotalVert,           retval,
                         DTA_NominalVert,         font->tf_YSize * 25,
                         DTA_VertUnit,            font->tf_YSize,

                         DTA_VisibleHoriz,        domain->Width / font->tf_XSize,
                         DTA_TotalHoriz,          bd->bd_LineBytes,
                         DTA_NominalHoriz,        font->tf_XSize * 80,
                         DTA_HorizUnit,           font->tf_XSize,

                         DTA_Title,               bd->bd_Name,
                         DTA_Busy,                FALSE,
                         DTA_Sync,                TRUE,
                         TAG_DONE);

   }

   LEAVING;

   return(retval);
}
/*FE*/

/*FS*/ LONG formatline(struct BinaryData *bd,STRPTR *start,STRPTR buf,ULONG num)
{
   STRPTR bptr = *start;
   STRPTR ptr  = bptr;
   STRPTR ptr1 = buf;
   UWORD numadd = (bd->bd_BufferLen > ((1<<16) - 1)) ? 10 : 6;
   UWORD grp;

   if(numadd == 10)
      sprintf(ptr1,"%08lx: ",num);
   else
      sprintf(ptr1,"%04lx: ",num);
   ptr1 += numadd;

   switch(bd->bd_DisplayHex)
   {
   case BDTDH_BYTE:
      for(grp = 0 ; grp < bd->bd_NumGroups && ptr < bd->bd_BufferEnd ; grp++)
      {
         sprintf(ptr1,"%02lx ",*ptr);
         ptr++;
         ptr1 += 3;
      }

      while(grp < bd->bd_NumGroups)
      {
         strcpy(ptr1,"   ");
         ptr1 += 3;
         ptr++;
         grp++;
      }
      break;
   case BDTDH_WORD:
      for(grp = 0 ; grp < bd->bd_NumGroups && ptr < bd->bd_BufferEnd ; grp++)
      {
         sprintf(ptr1,"%04lx ",*((UWORD *) ptr));
         ptr += sizeof(UWORD);
         ptr1 += 5;
      }

      while(grp < bd->bd_NumGroups)
      {
         strcpy(ptr1,"     ");
         ptr1 += 5;
         ptr += sizeof(UWORD);
         grp++;
      }
      break;
   case BDTDH_LONG:
      for(grp = 0 ; grp < bd->bd_NumGroups && ptr < bd->bd_BufferEnd ; grp++)
      {
         sprintf(ptr1,"%08lx ",*((ULONG *) ptr));
         ptr += sizeof(ULONG);
         ptr1 += 9;
      }

      while(grp < bd->bd_NumGroups)
      {
         strcpy(ptr1,"         ");
         ptr1 += 9;
         ptr += sizeof(ULONG);
         grp++;
      }
      break;
   default:
      ptr += bd->bd_BytesPerLine;
   }

   /* write ASCII string */
   if((bd->bd_Flags & BDF_SHOWASCII) ||
      (bd->bd_DisplayHex == BDTDH_NONE))
   {
      while(bptr < ptr && bptr < bd->bd_BufferEnd)
      {
         if(*bptr < 32)
            *ptr1 = '.';
         else
            *ptr1 = *bptr;
         ptr1++;
         bptr++;
      }

      while(bptr < ptr)
      {
         *ptr1++ = ' ';
         bptr++;
      }
   }

   *start = ptr;

   return((LONG) (ptr1 - buf));
}
/*FE*/

