Copyright (c) Hyperion Entertainment and contributors.

Writing Datatype Classes

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Subclassing

For each of the DataType categories, there is a class that handles the data. Handlers for explicit data are subclasses under the appropriate class. For example, a class that handles ILBM pictures would be a subclass of the Picture class.

In order to fully understand the class concept used by DataTypes it helps to have a basic understanding of BOOPSI.

A subclass must provide an OM_NEW method that converts the source data into the data format required by the superclass. The subclass must optionally have a OM_DISPOSE method that discards any of the data constructed in the OM_NEW method. All other methods must be passed to the superclass.

Following is a listing of a example class dispatcher for a subclass of the picture class:

uint32 Dispatch(Class *cl, Object *o, Msg msg)
{
    struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
    uint32 retval;
 
    switch (msg->MethodID)
    {
        case OM_NEW:
            if (retval = IIntuition->IDoSuperMethodA(cl, o, msg))
            {
                /* Convert the source data to required data format */
                if (!GetObjectData (cb, cl, (Object *)retval,
                             ((struct opSet *) msg)->ops_AttrList))
                {
                    /* Force disposal of the object */
                    IIntuition->ICoerceMethod (cl, (Object *) retval, OM_DISPOSE);
                    retval = NULL;
                }
            }
            break;
 
        /* Let the superclass handle everything else */
        default:
            retval = (uint32) IIntuition->IDoSuperMethodA (cl, o, msg);
            break;
    }
 
    return (retval);
}

Obtaining a Handle to the Source Data

The first step that a class has to perform in order to convert the source data, is to get a handle on the data. This is obtained by passing the DTA_Handle tag to the superclass using the OM_GET method. For example:

Object *o;
BPTR fh;
 
IDataTypes->GetDTAttrs(o, DTA_Handle, &fh, TAG_END);

If the source Type is DTF_IFF, then DTA_Handle points to a struct IFFHandle that is already initialized and opened for reading, otherwise the handle points to a BPTR file handle.

Handling Errors

Whenever an error occurs and the class is unable to continue converting the data, then it must set an appropriate error using the DOS SetIoErr() function and the <dos/dos.h> error codes or the error codes defined in <datatypes/datatypes.h>.

For example, if the class is unable to allocate memory:

if (buf = IExec->AllocVecTags(size, AVT_ClearWithValue, 0, TAG_END))
    {
	... continue with conversion ...
    }
    else
    {
	IDOS->SetIoErr (ERROR_NO_FREE_STORE);

Data Format

The following sections outline the required data format for each of the main object types.

Picture Class

The picture class is the superclass for any static graphic classes. The structures and tags used by this class are defined in <datatypes/pictureclass.h>.

A picture subclass must fill out a BitMapHeader structure as well as provide a Mode ID, a BitMap and a ColorMap during the OM_NEW method of the class. There is no need to provide any other methods, as the remainder is handled by the picture class itself.

The picture subclass needs to fill in any fields of the BitMapHeader structure that the class has data for. This will ensure that the picture data can then be saved to a file or copied to the clipboard.

struct BitMapHeader *bmh;
 
/* Obtain a pointer to the BitMapHeader structure from the picture class */
if (IDataTypes->GetDTAttrs (dto, PDTA_BitMapHeader, &bmh, TAG_END) && bmh)
{
  /* Fill in some fields */
  bmh->bmh_Width  = 640;
  bmh->bmh_Height = 200;
  bmh->bmh_Depth  = 2;
}

Before manipulating any color information, the picture subclass must first tell the picture class how many colors it has, so that the information required for color remapping can be established.

IDataTypes->SetDTAttrs (dto, PDTA_NumColors, ncolors, TAG_END);

After the number of colors have been established, the palette information can be filled in for the picture. The following fragment illustrates filling in the palette information.

struct ColorRegister *cmap;
int16 n, ncolors;
int32 *cregs;
 
/* Get a pointer to the color registers that need to be
 * filled in */
IDataTypes->GetDTAttrs (dto,
  PDTA_ColorRegisters, &cmap,
  PDTA_CRegs, &cregs,
  TAG_END);
 
/* Set the color information */
for (n = 0; n < ncolors; n++)
{
  if (IDOS->Read (fh, &rgb, QSIZE) == QSIZE)
  {
    /* Set the master color table */
    cmap->red   = rgb.rgbRed;
    cmap->green = rgb.rgbGreen;
    cmap->blue  = rgb.rgbBlue;
    cmap++;
 
    /* Set the color table used for remapping */
    cregs[n * 3 + 0] = rgb.rgbRed   << 24;
    cregs[n * 3 + 1] = rgb.rgbGreen << 24;
    cregs[n * 3 + 2] = rgb.rgbBlue  << 24;
  }
  else
  {
    /* Indicate that we encountered an error. DOS Read
     * will have already filled in the IoErr() value */
    return FALSE;
  }
}

The picture class must get the actual picture information in standard Amiga bitmap format. If the bitmap is allocated using the graphics AllocBitMap() function, then the picture class can dispose of the bitmap at OM_DISPOSE time.

struct BitMap *bm;
 
if (bm = IGraphics->AllocBitMap (bmh->bmh_Width, bmh->bmh_Height, bmh->bmh_Depth, BMF_CLEAR, NULL))
{
  /* Tell the picture class about the picture data */
  IDataTypes->SetDTAttrsA (dto, PDTA_BitMap, bm, TAG_END);
}
else
{
  /* Indicate the error and that we encountered an error */
  IDOS->SetIoErr (ERROR_NO_FREE_STORE);
  return FALSE;
}

A picture subclass needs to provide the following fields to the superclass, during the OM_NEW method:

DTA_NominalHoriz (int32) Set to the width of the picture.
DTA_NominalVert (int32) Set to the height of the picture.
PDTA_ModeID (uint32) A valid mode ID for the BitMap.
DTA_ObjName (STRPTR) The name, or title, of the picture
DTA_ObjAuthor (STRPTR) The author of the picture.
DTA_ObjAnnotation (STRPTR) Notes on the picture.
DTA_ObjCopyright (STRPTR) Copyright notice for the picture.
DTA_ObjVersion (STRPTR) Version of the picture.

If a picture subclass uses something other than AllocBitMap() to allocate the bitmap, then it must free the bitmap itself. This is done by implementing an OM_DISPOSE method for the subclass.

uint32 Dispatch (Class *cl, Object *o, Msg msg)
{
  struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
  struct localData *lod;
  uint32 retval;
 
  switch (msg->MethodID)
  {
      case OM_NEW:
        /* ... */
        break;
 
      case OM_DISPOSE:
        /* Get a pointer to our object data */
        lod = INST_DATA (cl, o);
 
        /* Tell the picture class that it doesn't have a
         * bitmap to free any more */
        IDataTypes->SetDTAttrs (o, PDTA_BitMap, NULL, TAG_END);
 
        /* Free the bitmap ourself */
        myfreebitmap (lod->lod_BitMap);
 
      /* Let the superclass handle everything else */
      default:
        retval = (uint32) IIntuition->DoSuperMethodA (cl, o, msg);
        break;
  }
 
  return retval;
}

Sound Class

The sound class is the superclass for any sampled audio classes. The structures and tags used by this class are defined in <datatypes/soundclass.h>.

A sound subclass needs to provide the following fields to the superclass, during the OM_NEW method:

SDTA_Sample (uint8 *) 8-bit sound data. The sound class will FreeVec() the sample data.
SDTA_SampleLength (uint32) Number of 8-bit bytes in the sound data.
SDTA_Volume (uint16) Number ranging from 0 being the quietest to 64 being the loudest.
SDTA_Period (uint16) Amount of time to play the sound.
SDTA_Cycles (uint16) Number of times to play the sound. 0 being an infinite loop.
DTA_ObjName (STRPTR) The name, or title, of the sound
DTA_ObjAuthor (STRPTR) The author of the sound.
DTA_ObjAnnotation (STRPTR) Notes on the sound.
DTA_ObjCopyright (STRPTR) Copyright notice for the sound.
DTA_ObjVersion (STRPTR) Version of the sound.

No methods other OM_NEW are need, as the remainder is handled by the sound class itself.

The sound subclass also needs to fill in any fields of the VoiceHeader structure that the class has data for. This will ensure that the sound data can then be saved to a file or copied to the clipboard.

struct VoiceHeader *vh;
 
/* Obtain a pointer to the VoiceHeader structure from the sound class */
if (IDataTypes->GetDTAttrs (dto, SDTA_VoiceHeader, &vh, TAG_END) && vh)
{
  /* Fill in some fields */
  vh->vh_Octaves     = 1;
  vh->vh_Compression = 0;
  vh->vh_Volume      = 63;
}

If a sound subclass uses something other than AllocVec() to allocate the sound data, then it must free the sample itself. This is done by implementing an OM_DISPOSE method for the subclass.

uint32 Dispatch (Class *cl, Object *o, Msg msg)
{
  struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
  struct localData *lod;
  uint32 retval;
 
  switch (msg->MethodID)
  {
    case OM_NEW:
      /* ... */
      break;
 
    case OM_DISPOSE:
      /* Get a pointer to our object data */
      lod = INST_DATA (cl, o);
 
      /* Tell the sound class that it doesn't have a
       * sample to free any more */
      IDataTypes->SetDTAttrs (o, SDTA_Sample, NULL, TAG_END);
 
      /* Free the sample ourself */
      IExec->FreeVec(lod->lod_Sample);
 
      /* Let the superclass handle everything else */
    default:
      retval = (uint32) IIntuition->IDoSuperMethodA (cl, o, msg);
      break;
  }
 
  return retval;
}

Text Class

The text class is the superclass for any formatted or non-formatted text classes. The structures and tags used by this class are defined in <datatypes/textclass.h>.

The text class provides the subclass with a buffer that contains the text data. The subclass must then provide the text class with a list of Line segments during the layout method. This Line list should only be created at gpl_Initial time, unless the TDTA_WordWrap attribute is TRUE.

struct Line
{
  struct MinNode ln_Link;
  STRPTR         ln_Text;
  ULONG		 ln_TextLen;
  UWORD		 ln_XOffset;
  UWORD		 ln_YOffset;
  UWORD		 ln_Width;
  UWORD		 ln_Height;
  UWORD		 ln_Flags;
  BYTE		 ln_FgPen;
  BYTE		 ln_BgPen;
  ULONG		 ln_Style;
  APTR		 ln_Data;
};

The Line structure fields are as follows:

ln_Link
MinNode used to link to the line list.
ln_Text
Pointer to the text for this line segment.
ln_TextLen
Number of bytes of text in this line segment.
ln_XOffset
Left pixel offset from the left edge of the object for this line segment.
ln_YOffset
Top pixel offset from the top edge of the object for this line segment.
ln_Width
Width of the line segment in pixels.
ln_Height
Height of the line segment in pixels.
ln_Flags
Control flags for this line segment.
LNF_LF -- Used to indicate that this segment is the end of a line.
ln_FgPen
Pen to use for the foreground (the text color) for this line segment.
ln_BgPen
Pen to use for the background color for this line segment.
ln_Style
Text attribute soft style to use for this line segment.

As each Line segment is allocated it must be added to the Line list.

struct List *linelist;
struct Line *line;
 
/* Get a pointer to the line list */
if (IDataTypes->GetDTAttrs (o, TDTA_LineList, (uint32) &linelist, TAG_END) && linelist)
{
  /* Create a Line segment */
  if (line = AllocVecTags(sizeof (struct Line), AVT_ClearWithValue, 0, TAG_END))
  {
    /* Add it to the list */
    IExec->AddTail (linelist, (struct Node *)&line->ln_Link);
  }
}

Currently, this is the hardest class to subclass, due to the number of methods that must be implemented. Following is the shell for a dispatcher and the layout method for a text subclass.

struct localData
{
  VOID  *lod_Pool;
  uint32 lod_Flags;
};
 
uint32 Dispatch (Class * cl, Object * o, Msg msg)
{
    struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
    struct localData *lod;
    struct List *linelist;
    uint32G retval = 0;
 
    switch (msg->MethodID)
    {
        case OM_NEW:
            if (retval = IIntuition->IDoSuperMethodA (cl, o, msg))
            {
                uint32 len, estlines, poolsize;
                BOOL success = FALSE;
                STRPTR buffer;
 
                /* Get a pointer to the object data */
                lod = INST_DATA (cl, (Object *) retval);
 
                /* Get the attributes that we need to determine memory pool size */
                IDataTypes->GetDTAttrs ((Object *) retval,
                             TDTA_Buffer,    &buffer,
                             TDTA_BufferLen, &len,
                             TAG_END);
 
                 /* Make sure we have a text buffer */
                 if (buffer && len)
                 {
                     /* Estimate the pool size that we will need */
                     estlines = (len / 80) + 1;
                     estlines = (estlines > 200) ? 200 : estlines;
                     poolsize = sizeof (struct Line) * estlines;
 
                     /* Create a memory pool for the line list */
                     lod->lod_Pool = IExec->AllocSysObjectTags(ASOT_MEMPOOL,
                         ASOPOOL_MFlags, MEMF_SHARED | MEMF_CLEAR,
                         ASOPOOL_Puddle, poolsize,
                         ASOPOOL_Threshold, poolsize,
                         TAG_END);
                     if (lod->lod_Pool != NULL)
                         success = TRUE;
                     else
                         IDOS->SetIoErr (ERROR_NO_FREE_STORE);
                 }
                 else
                 {
                     /* Indicate that something was missing that we needed */
                     IDOS->SetIoErr (ERROR_REQUIRED_ARG_MISSING);
                 }
 
                 if (!success)
                 {
                     IIntuition->ICoerceMethod (cl, (Object *) retval, OM_DISPOSE);
                     retval = NULL;
                 }
             }
             break;
 
         case OM_UPDATE:
         case OM_SET:
             /* Pass the attributes to the text class and force a refresh if we need it */
             if ((retval = IIntuition->IDoSuperMethodA (cl, o, msg)) && (OCLASS (o) == cl))
             {
                 struct RastPort *rp;
 
                 /* Get a pointer to the rastport */
                 if (rp = IGraphics->ObtainGIRPort (((struct opSet *) msg)->ops_GInfo))
                 {
                     IIntuition->DoRender(o, ((struct opSet *) msg)->ops_GInfo, GREDRAW_UPDATE);
 
                     /* Release the temporary rastport */
                     IGraphics->ReleaseGIRPort (rp);
                 }
                 retval = 0;
             }
             break;
 
         case GM_LAYOUT:
             /* Tell everyone that we are busy doing things */
             notifyAttrChanges (o, ((struct gpLayout *) msg)->gpl_GInfo, NULL,
                                GA_ID, G(o)->GadgetID,
                                DTA_Busy,  TRUE,
                                TAG_END);
 
             /* Let the superclass partake */
             retval = (uint32) IIntuition->IDoSuperMethodA (cl, o, msg);
 
             /* We need to do this one asynchronously */
             retval += IDataTypes->DoAsyncLayout (o, (struct gpLayout *) msg);
             break;
 
         case DTM_PROCLAYOUT:
             /* Tell everyone that we are busy doing things */
             notifyAttrChanges (o, ((struct gpLayout *) msg)->gpl_GInfo, NULL,
                                GA_ID, G(o)->GadgetID,
                                DTA_Busy,  TRUE,
                                TAG_END);
 
             /* Let the superclass partake and then fall through to our layout method */
             retval = (uint32) IDataTypes->DoSuperMethodA (cl, o, msg);
 
         case DTM_ASYNCLAYOUT:
             /* Layout the text */
             retval = layoutMethod (cb, cl, o, (struct gpLayout *) msg);
             break;
 
         case OM_DISPOSE:
             /* Get a pointer to our object data */
             lod = INST_DATA (cl, o);
 
             /* Don't let the super class free the line list */
             if (IIntuition->GetDTAttrs (o, TDTA_LineList, &linelist, TAG_END) && linelist)
                 IExec->NewList (linelist);
 
             /* Delete the line pool */
             IExec->FreeSysObject(ASOT_MEMPOOL, lod->lod_Pool);
 
         /* Let the superclass handle everything else */
         default:
             retval = (uint32) IIntuition->DoSuperMethodA (cl, o, msg);
             break;
     }
 
     return (retval);
 }
 
 uint32 layoutMethod (struct ClassBase *cb, Class * cl, Object * o, struct gpLayout * gpl)
 {
     struct DTSpecialInfo *si = (struct DTSpecialInfo *) G (o)->SpecialInfo;
     struct localData *lod = INST_DATA (cl, o);
     uint32 visible = 0, total = 0;
     struct RastPort trp;
     uint32 hunit = 1;
     uint32 bsig = 0;
 
     /* Switches */
     BOOL linefeed = FALSE;
     BOOL newseg = FALSE;
     BOOL abort = FALSE;
 
     /* Attributes obtained from superclass */
     struct TextAttr *tattr;
     struct TextFont *font;
     struct List *linelist;
     struct IBox *domain;
     uint32 wrap = FALSE;
     uint32 bufferlen;
     STRPTR buffer;
     STRPTR title;
 
     /* Line information */
     uint32 num, offset, swidth;
     uint32 anchor, newanchor;
     uint32 style = FS_NORMAL;
     struct Line *line;
     uint32 yoffset = 0;
     uint8 fgpen = 1;
     uint8 bgpen = 0;
     uint32 tabspace;
     uint32 numtabs;
     uint32 i, j;
 
     uint32 nomwidth, nomheight;
 
     /* Get all the attributes that we are going to need for a successful layout */
     if (IDataTypes->GetDTAttrs (o,
                          DTA_TextAttr,  &tattr,
                          DTA_TextFont,  &font,
                          DTA_Domain,    &domain,
                          DTA_ObjName,   &title,
                          TDTA_Buffer,   &buffer,
                          TDTA_BufferLen,&bufferlen,
                          TDTA_LineList, &linelist,
                          TDTA_WordWrap, &wrap,
                          TAG_END) == 8)
     {
         /* Lock the global object data so that nobody else can manipulate it */
         IExec->ObtainSemaphore (&(si->si_Lock));
 
         /* Make sure we have a buffer */
         if (buffer)
         {
             /* Initialize the temporary RastPort */
             IGraphics->InitRastPort (&trp);
             IGraphics->SetFont (&trp, font);
 
             /* Calculate the nominal size */
             nomheight = (ULONG) (24 * font->tf_YSize);
             nomwidth  = (ULONG) (80 * font->tf_XSize);
 
             /* Calculate the tab space */
             tabspace = font->tf_XSize * 8;
 
             /* We only need to perform layout if we are doing word wrap, or this
              * is the initial layout call */
             if (wrap || gpl->gpl_Initial)
             {
                 /* Delete the old line list */
                 while (line = (struct Line *) IExec->RemHead (linelist))
                     IExec->FreePooled (lod->lod_Pool, line, sizeof (struct Line));
 
                 /* Step through the text buffer */
                 for (i = offset = num = numtabs = 0;
                      (i <= bufferlen) && (bsig == 0) && !abort;
                      i++)
                 {
                     /* Check for end of line */
                     if (buffer[i]==13 && buffer[i+1]==10)
                     {
                         newseg = linefeed = TRUE;
                         newanchor = i + 2;
                         i++;
                     }
                     /* Check for end of page */
                     else if (buffer[i] == 12)
                     {
                         newseg = linefeed = TRUE;
                         newanchor = i + 1;
                     }
                     /* Check for tab */
                     else if (buffer[i] == 9)
                     {
                         /* See if we need to terminate a line segment */
                         if ((numtabs == 0) && num)
                             newseg = TRUE;
                         numtabs++;
                     }
                     else
                     {
                         /* See if we have any TABs that we need to finish out */
                         if (numtabs)
                         {
                             offset += (((offset / tabspace) + 1) * tabspace) - offset;
                             num = numtabs = 0;
                             anchor = i;
                         }
 
                         /* Compute the width of the line. */
                         swidth = IGraphics->TextLength (&trp, &buffer[anchor], num+1);
                         if (offset + swidth > domain->Width)
                         {
                             /* Search for a whitespace character */
                             for (j = i; (j >= anchor) && !newseg; j--)
                             {
                                 if (buffer[j] == ' ')
                                 {
                                     num -= (i - j);
                                     newseg = TRUE;
                                     i = j + 1;
                                 }
                             }
 
                             newseg = linefeed = TRUE;
                             newanchor = i;
                             i--;
                         }
                         else
                         {
                             num++;
                         }
                     }
 
                     /* Time for a new text segment yet? */
                     if (newseg)
                     {
                         /* Allocate a new line segment from our memory pool */
                         if (line = IExec->AllocPooled (lod->lod_Pool, sizeof (struct Line)))
                         {
                             swidth = IGraphics->TextLength (&trp, &buffer[anchor], num);
                             line->ln_Text    = &buffer[anchor];
                             line->ln_TextLen = num;
                             line->ln_XOffset = offset;
                             line->ln_YOffset = yoffset + font->tf_Baseline;
                             line->ln_Width   = swidth;
                             line->ln_Height  = font->tf_YSize;
                             line->ln_Flags   = (linefeed) ? LNF_LF : NULL;
                             line->ln_FgPen   = fgpen;
                             line->ln_BgPen   = bgpen;
                             line->ln_Style   = style;
                             line->ln_Data    = NULL;
 
                             /* Add the line to the list */
                             IExec->AddTail (linelist, (struct Node *) line);
 
                             /* Increment the line count */
                             if (linefeed)
                             {
                                 yoffset += font->tf_YSize;
                                 offset = 0;
                                 total++;
                             }
                             else
                             {
                                 /* Increment the offset */
                                 offset += swidth;
                             }
                         }
                         else
                         {
                             abort = TRUE;
                         }
 
                         /* Clear the variables */
                         newseg = linefeed = FALSE;
                         anchor = newanchor;
                         num = 0;
 
                         /* Check to see if layout has been aborted */
                         bsig = IDOS->CheckSignal (SIGBREAKF_CTRL_C);
                     }
                 }
             }
             else
             {
                 /* No layout to perform */
                 total = si->si_TotVert;
             }
         }
 
         /* Compute the lines and columns type information */
         si->si_VertUnit  = font->tf_YSize;
         si->si_VisVert   = visible = domain->Height / si->si_VertUnit;
         si->si_TotVert   = total;
 
         si->si_HorizUnit = hunit = 1;
         si->si_VisHoriz  = (int32) domain->Width / hunit;
         si->si_TotHoriz  = domain->Width;
 
         /* Release the global data lock */
         IExec->ReleaseSemaphore (&si->si_Lock);
 
         /* Were we aborted? */
         if (bsig == 0)
         {
             /* Not aborted, so tell the world of our newest attributes */
             notifyAttrChanges (o, gpl->gpl_GInfo, NULL,
                                GA_ID,                   G(o)->GadgetID,
 
                                DTA_VisibleVert,         visible,
                                DTA_TotalVert,           total,
                                DTA_NominalVert,         nomheight,
                                DTA_VertUnit,            font->tf_YSize,
 
                                DTA_VisibleHoriz,        (uint32) (domain->Width / hunit),
                                DTA_TotalHoriz,          domain->Width,
                                DTA_NominalHoriz,        nomwidth,
                                DTA_HorizUnit,           hunit,
 
                                DTA_Title,               title,
                                DTA_Busy,                FALSE,
                                DTA_Sync,                TRUE,
                                TAG_END);
         }
     }
 
     return total;
}

Writing a Superclass

(relevant text to be provided)

Defining a DataType Descriptor

DataTypes uses a simple descriptor to determine what type of data a file contains and what class, if any, is used to handle that data.

The DTDesc utility is used to define a DataType descriptor. The following steps describe how to use this utility to define a descriptor.

1. Load several sample files of the type being defined. This can be done by dropping their icons into the DTDesc window or by using the "Extras/Load Samples..." menu item.

2. The view area in the bottom right side of the DTDesc window will show the first 64 characters of the files. This area is used to define the Mask. The characters that don't match will be blotted out and only the similar characters will be shown. If more characters are shown as similar than really are, then they can easily be blotted out by rubbing over them.

3. DataTypes can be broken out into several different categories. Use the Group menu to select the category that the DataType descriptor belongs in.

4. The remaining fields must be filled out. Following is a table describing the fields and the information they require.

Name Description
File Type User description of the DataType.
Base Name The base name of the DataType. The class library name is derived from this.
Name Pattern The name of the file can be used to indicate the DataType. AmigaDOS wildcards can be used to specify the file name.
Function A function can be used to further define a data type. This function must be a stand-alone executable that uses the DataType Descriptor Function Interface.
Case Sensitive? The Mask can be either case sensitive or not.
Priority Descriptors are sorted by Type, Function, Name Pattern and Mask. The priority field allows a DataType descriptor to be assigned a different priority than a similar DataType descriptor.
Type This is a read-only field and is used to indicate what basic type a DataType is. Types include IFF, Binary and ASCII.

5. Once a DataType descriptor has been defined, it must be saved. Select the "Project/Save As..." menu item for the file requester used to save a DataType descriptor. The default name for a DataType descriptor is the text entered into File Type field.

6. In order for a new DataType descriptor to be loaded, the datatypes.library must be flushed from the system. This can either be done with the Expunge command included in the SDK. Another way that the new DataType descriptor can be loaded is by using the AddDataTypes command with the REFRESH option.

DataType Descriptor Function Interface

Sometimes the fields within the DTDesc utility are not enough to define a DataType descriptor. In this case it is necessary to use a Function to further narrow down a DataType.

A Function is a stand-alone executable that expects the following arguments.

BOOL retval = Function(struct DTHookContext *dthc);

The function must return TRUE if the data matches, FALSE if it doesn't.

The DTHookContext structure contains the fields that are necessary for narrowing down the DataType. Following is a listing of the DTHookContext structure.

struct DTHookContext
{
    struct Library        *dthc_SysBase;
    struct Library        *dthc_DOSBase;
    struct Library        *dthc_IFFParseBase;
    struct Library        *dthc_UtilityBase;
 
    /* File context */
    BPTR                   dthc_Lock;          /* Lock on the file */
    struct FileInfoBlock  *dthc_FIB;           /* Pointer to a FileInfoBlock */
    BPTR                   dthc_FileHandle;    /* Pointer to the file handle (may be NULL) */
    struct IFFHandle      *dthc_IFF;           /* Pointer to an IFFHandle (may be NULL) */
    STRPTR                 dthc_Buffer;        /* Buffer */
    ULONG                  dthc_BufferLength;  /* Length of the buffer */
 
    ULONG                  dthc_Length;       /* size of this structure */
    struct ExecIFace *     dthc_IExec;
    struct DOSIFace  *     dthc_IDOS;
    struct IFFParseIFace * dthc_IIFFParse;
    struct UtilityIFace *  dthc_IUtility;
};

The DTHookContext structure fields are as follows:

dthc_SysBase
dthc_DOSBase
dthc_IFFParseBase
dthc_UtilityBase
These are library bases that your function can utilize. It will need to open any other libraries that it needs.
dthc_Lock
If the source data is a DOS file, then this is a lock on the file.
dthc_FIB
If the source data is a DOS file, then this is a filled in FileInfoBlock for the file.
dthc_FileHandle
If the source data is a DOS file, then this is the file handle for the file otherwise the handle will be NULL. The file is guaranteed to be at the beginning.
dthc_IFF
If the source data is IFF, then this is the IFFHandle for accessing the data, otherwise the handle will be NULL. The position is guaranteed to be at the beginning of the data. The DOS file fields must not be accessed if the data is IFF.
dthc_Buffer
This buffer contains the first dthc_BufferLength bytes of data.
dthc_BufferLength
Indicates the number of bytes in dthc_Buffer, up to 64.
dthc_Length
Size in bytes of struct DTHookContext
dthc_IExec
dthc_IDOS
dthc_IIFFParse
dthc_IUtility
These are interface pointers that your function can utilize.

The following example shows how to write a simple DataTypes Descriptor Function.

/* This example must not be linked with any startup code so that
 * DTHook is the entry point for the executable.
 *
 */
#include <exec/types.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <datatypes/datatypes.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
 
BOOL DTHook (struct DTHookContext * dthc)
{
    BOOL retval = FALSE;
    uint32 i;
    uint8 ch;
 
    /* Make sure we have a buffer */
    if (dthc->dthc_Buffer)
    {
        for (i = 0; (i < dthc->dthc_BufferLength) && !retval; i++)
        {
            ch = dthc->dthc_Buffer[i];
 
            /* Look at the data... */
        }
    }
    return retval;
}

The next example shows how to write a DataTypes Descriptor that looks into an IFF file for needed chunk information.

/* This example must not be linked with any startup code
 * so that DTHook is the entry point for the executable.
 */
#include <exec/types.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <datatypes/datatypes.h>
#include <datatypes/pictureclass.h>
#include <libraries/iffparse.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/iffparse.h>
#include <proto/utility.h>
 
#define BMH_SIZE (sizeof (struct BitMapHeader))
 
BOOL DTHook(struct DTHookContext * dthc)
{
  struct BitMapHeader bmh;
  struct ContextNode *cn;
  struct IFFHandle *iff;
  BOOL retval = FALSE;
 
  /* Make sure that this is an IFF data type */
  if (iff = dthc->dthc_IFF)
 
    /* Stop on the BitMapHeader (type, id) */
    if (dthc->dthc_IIFFParse->StopChunk (iff, ID_ILBM, ID_BMHD) == 0)
 
      /* Scan through the IFF handle */
      if (dthc->dthc_IIFFParse->ParseIFF (iff, IFFPARSE_SCAN) == 0L)
 
        /* Make sure we have a current chunk */
        if (cn = dthc->dthc_IIFFParse->CurrentChunk (iff))
 
          /* Make sure the current chunk is ILBM BMHD */
          if ((cn->cn_Type == ID_ILBM) && (cn->cn_ID == ID_BMHD))
 
            /* Read the chunk data */
            if (dthc->dthc_IIFFParse->ReadChunkBytes (iff, &bmh, BMH_SIZE) == BMH_SIZE)
 
              /* See if the depth is set to 24 */
              if (bmh.bmh_Depth == 24)
                retval = TRUE;
 
  return (retval);
}