
/*
**
**  $VER: DTConvert.c 1.6 (5.2.98)
**  datatypes.library/Examples/DTConvert
**
**  Converts file into another format using datatypes
**
**  Main program and startup code
**
**  Written 1996-1998 by Roland 'Gizzy' Mainz
**
*/

/* project includes */
#include "DTConvert.h"
#include "gui.h"

/* version string */
#include "DTConvert_rev.h"

/*****************************************************************************/

/* list of datatypes, used in gadtools listview gadget */
struct DTList
{
    struct List dtl_List;
    UWORD       dtl_NumNodes;
    APTR        dtl_Pool;
};

/* single dtlist node */
struct DTNode
{
    struct Node dtn_Node;
    STRPTR      dtn_DTName;
    /* dynamic bufferspace follows here */
};


/*****************************************************************************/
/* shared libraries */

struct Library       *UtilityBase    = NULL;
struct GfxBase       *GfxBase        = NULL;
struct IntuitionBase *IntuitionBase  = NULL;
struct Library       *GadToolsBase   = NULL;
struct Library       *DataTypesBase  = NULL;
struct Library       *AslBase        = NULL;
struct Library       *IconBase       = NULL;
struct Library       *WorkbenchBase  = NULL;

/*****************************************************************************/

/* version_string */
STRPTR versionstring = VERSTAG;

/*****************************************************************************/

/* local prototypes */
static                 void                 DefaultSettings( void );
static                 void                 ClearRDA( void );
static                 void                 ScanRDA( void );
static                 void                 ScanToolTypes( TEXT ** );
static                 void                 FreeInitProjectResult( void );
static                 void                 ReadENVPrefs( void );

static                 BOOL                 CreateBasicResources( void );
static                 void                 DeleteBasicResources( void );

static                 BOOL                 OpenLibStuff( void );
static                 void                 CloseLibStuff( void );

static                 void                 RunTool( STRPTR );

static                 BOOL                 NewProject( STRPTR, STRPTR, BOOL, STRPTR, BOOL, STRPTR );

static                 struct DTList       *CreateDTDescrList( BPTR, ULONG );
static                 void                 FreeDTDescrList( struct DTList * );
static                 struct DTNode       *FindDTNodeByName( struct DTList *, STRPTR );

static                 void                 LockGUI( void );
static                 void                 UnlockGUI( void );

static                 STRPTR               StringSave( STRPTR );
static                 STRPTR               AllocStringBuff( ULONG );
static                 STRPTR               UpdateString( STRPTR *, STRPTR );
static                 void                 FreeString( STRPTR );

static                 struct DiskObject   *GetToolDiskObject( STRPTR );

static                 ULONG                SumString( STRPTR );


/*****************************************************************************/

long main_retval,
     main_retval2;

static struct RDArgs  *startuprda;

/* mempool for strings */
static APTR StringPool;

/*****************************************************************************/

/* template for ReadArgs */
#define STARTUP_TEMPLATE "FROM=NAME=SRCNAME,"          \
                         "DESTDATATYPE=DATATYPE=DTN,"  \
                         "IFF/S,"                      \
                         "TO=DESTNAME,"                \
                         "GUI/S,"                      \
                         "PUBSCREEN/K"

#define ENV_TEMPLATE STARTUP_TEMPLATE /* equal because there are no /A switches */

static
struct
{
    STRPTR  srcname;
    STRPTR  destdatatype;
    long   *iff;
    STRPTR  destname;
    long   *gui;
    STRPTR  pubscreen;
} result;

static
struct
{
    STRPTR  srcname;
    STRPTR  destdatatype;
    BOOL    iff;
    STRPTR  destname;
    BOOL    gui;
    STRPTR  pubscreen;
} project;

/*****************************************************************************/

/****** DTConvert/DTConvert **************************************************
*
*    NAME
*       DTConvert -- DataTypes-based conversion tool
*
*    FORMAT
*       DTConvert [FROM|NAME|SRCNAME <file>]
*                 [DESTDATATYPE|DATATYPE|DTN <datatype>] [IFF]
*                 [TO|DESTNAME <file>] [GUI] [PUBSCREEN <public screen>]
*
*    TEMPLATE
*       FROM=NAME=SRCNAME,DESTDATATYPE=DATATYPE=DTN,IFF/S,TO=DESTNAME,
*       GUI/S,PUBSCREEN/K
*
*    PURPOSE
*       Convert data from one format into another format using datatypes
*       classes.
*
*    DESCRIPTION
*       DTConvert converts data from one format into another format,
*       e.g. picture -> picture,
*            animation -> anmation,
*            movie -> movie,
*            sound -> sound,
*            text -> text (n/a)
*
*      You simply have to set the souce file name (SRCNAME), the
*      datatype to convert to (DATATYPE) (or the IFF switch if you want to
*      convert into the base-IFF format and the destination file name (TO).
*
*    BUGS
*      - picture.datatype V43 interface not implemented.
*        This causes that any encoder only runs in V42 mode any may
*        only encode 8 bit data.
*
*      - path names are limitted to 1024 chars, larger filenames may cause
*        mailfunctions
*
*    NOTES
*      - picture.datatype V43 pixmap interface not supported.
*
*      - text conversion not supported yet.
*
*    TODO
*      - picture.datatype V43 compatibility
*
*      - text conversion
*
*      - GUI and WB suppport
*
*    HISTORY
*       V1.1:
*         - First release to dta@amigawolrd.com mailinglist.
*
*       V1.2:
*         - Added support for animation.datatype subclasses
*
*       V1.3:
*         - Minor fixes.
*
*       V1.4
*         - Added support for sound.datatype V40 subclasses, partial support
*           for suggested sound.datatype V41 interface.
*
*         - Added some usefull comments.
*
*         - WriteAnimClass/WriteSoundClass now sets
*           SetIoErr( ERROR_REQUIRED_ARGUMENT_MISSING ); if something goes
*           wrong (this should abort the encoder; only ERROR_OBJECT_NOT_FOUND
*           is accepted here).
*
*         - Fixed some holes in the error handling.
*
*         - GUI added.
*
*         - Added WB support
*
*         - Added ENV variable support; a local or global (ENV) variable
*           "DTConvert" takes the same arguments as the shell template.
*           Variable settings can be overridden by any argument.
*
*         - Project split in seperate sources (e.g. main, GUI, converters)
*
*       V1.5
*         - Added the feature that the datatypes selection requester shows
*           only entries which match the source group IDs
*           (if a source has been already selected).
*
*         - Added kluge which alows the conversion between GID_MOVIE
*           and GID_ANIMATION datatypes (both are based on
*           animation.datatype, here we have the same interface :-)
*
*         - Fixed the bug that ConvertAnimation function did not deal
*           with truecolor bitmaps (e.g. CyberGFX bitmaps for example,
*           truecolor bitmaps are indicated by ADTA_NumColors == 0.
*           Fixed.
*
*       V1.6
*         - Fixed the bug that ObtainDataTypeA was called for each
*           INTUITICK if the datatype selection requester was open.
*           Fixed.
*
*
*    AUTHOR's REQUEST
*        By  releasing  this program I do  not  place any obligations on you,
*        feel free to share this program with your  friends (and enemies).
*
*        If you want to blame me, report any bugs, or wants a new version
*        send your letter to:
*                        Roland Mainz
*                        Hohenstaufenstraße 8
*                        52388 Nörvenich
*                        GERMANY
*
*        Phone: (+49)(0)2426/901568
*        Fax:   (+49)(0)2426/901569
*
*        EMAIL is also available:
*        GISBURN@w-specht.rhein-ruhr.de
*
*        If you want to send me attachments larger than 1MB (up to 5MB,
*        more with my permission):
*        Up to April 1998 I'm reachable using this email address, too:
*        Reinhold.A.Mainz@KBV.DE
*
*        | Please put your name and address in your mails !
*        | German mailers should add their phone numbers.
*        | See BUGS section above when submitting bug reports.
*
*        Sorry, but I can only look once a week for mails.
*        If you don't hear something from me within three weeks, please
*        send your mail again (but watch about new releases) (problems with
*        this email port are caused by reconfigurations, hackers, network
*        problems etc.).
*
*        The  entire  "DTConvert"  package may  be  noncommercially
*        redistributed, provided  that  the package  is always  distributed
*        in it's complete  form (including it's documentation).  A small copy
*        fee for media costs is okay but any kind of commercial distribution
*        is strictly forbidden! Comments  and  suggestions  how  to  improve
*        this program  are generally appreciated!
*
*        Thanks to Matt Dillon for his DICE, David Junod for this datatypes
*        environment and Olaf 'Olsen' Barthel for his help, ideas and some
*        text clips from his documentations.
*
*    SEE ALSO
*
******************************************************************************
*
*/


#ifdef _DCC
/* disable CTRL_C break support (DICE CTRL_C abort function) */
void chkabort( void )
{
}


/* DICE workbench entry */
long wbmain( struct WBStartup *wbstartup )
{
    /* Call main like SAS-C */
    return( (int)main( 0L, (STRPTR *)wbstartup ) );
}
#endif /* _DCC */


int main( int ac, STRPTR *av )
{
    LONG               numArgs,
                       x;
    struct WBStartup  *wbstartup;
    struct WBArg      *wbarg;
    struct DiskObject *tooldobj,
                      *projectdobj;
    BPTR               oldToolLock    = NULL,
                       oldProjectLock = NULL;

    main_retval2 = 0L;
    main_retval  = RETURN_OK;

    if( CreateBasicResources() )
    {
      DefaultSettings();

      /* check if something goes wrong (ENV error etc.) */
      if( main_retval == RETURN_OK )
      {
/* Workbench */
        if( ac == 0L )
        {
          wbstartup = (struct WBStartup *)av;

          numArgs = wbstartup -> sm_NumArgs;
          wbarg   = wbstartup -> sm_ArgList;

          if( *(wbarg[ 0 ] . wa_Name) )
          {
            if( wbarg[ 0 ] . wa_Lock )
            {
              oldToolLock = CurrentDir( (wbarg[ 0 ] . wa_Lock) );
            }

            if( tooldobj = GetToolDiskObject( (wbarg[ 0 ] . wa_Name) ) )
            {
              /* two possible cases when started from workbench ... */
              if( numArgs < 2L )
              {
                /* ... first case, only our tool icon is given, create one project here */

                ScanToolTypes( (STRPTR *)(tooldobj -> do_ToolTypes) );

                /* check if something goes wrong (parsing error, etc.) */
                if( main_retval == RETURN_OK )
                {
                  RunTool( NULL );
                }

                FreeInitProjectResult();
              }
              else
              {
                /* ... second case, a couple of project icons are given, multiple projects will start from here */
                for( x = 1L ; x < numArgs ; x++ )
                {
                  if( wbarg[ x ] . wa_Lock )
                  {
                    oldProjectLock = CurrentDir( (wbarg[ x ] . wa_Lock) );
                  }

                  if( *(wbarg[ x ] . wa_Name) )
                  {
                    if( projectdobj = GetDiskObject( (wbarg[ x ] . wa_Name) ) )
                    {
                      ScanToolTypes( (STRPTR *)(tooldobj -> do_ToolTypes) );
                      ScanToolTypes( (STRPTR *)(projectdobj -> do_ToolTypes) );

                      /* check if something goes wrong (parsing error, etc.) */
                      if( main_retval == RETURN_OK )
                      {
                        RunTool( (wbarg[ x ] . wa_Name) );
                      }

                      FreeInitProjectResult();
                      DefaultSettings();

                      FreeDiskObject( projectdobj );
                    }
                  }

                  if( wbarg[ x ] . wa_Lock )
                  {
                    CurrentDir( oldProjectLock );
                  }
                }
              }

              FreeDiskObject( tooldobj );
            }

            if( wbarg[ 0 ] . wa_Lock )
            {
              CurrentDir( oldToolLock );
            }
          }
        }
        else
        {
/* CLI/Shell */
          if( startuprda = ReadArgs( STARTUP_TEMPLATE, (LONG *)(&result), NULL ) )
          {
            /* did we get a CTRL_C signal ? */
            if( !CheckSignal( SIGBREAKF_CTRL_C ) )
            {
              ScanRDA();

              /* check if something goes wrong (parsing error, etc.) */
              if( main_retval == RETURN_OK )
              {
                RunTool( NULL );
              }

              FreeInitProjectResult();
            }
            else
            {
              main_retval2 = ERROR_BREAK;
              main_retval  = RETURN_WARN;
            }

            FreeArgs( startuprda );
          }
          else
          {
            main_retval2 = IoErr();
            main_retval  = RETURN_FAIL;
          }
        }
      }

      DeleteBasicResources();
    }

    PrintFault( main_retval2, NAME );

    SetIoErr( main_retval2 );

    return( main_retval );
}


static
void DefaultSettings( void )
{
    ClearRDA();

    memset( (void *)(&project), 0, sizeof( project ) );

    ReadENVPrefs();
}


static
void ReadENVPrefs( void )
{
    struct RDArgs envvarrda =
    {
      NULL,
      256L,
      0L,
      0L,
      NULL,
      0L,
      NULL,
      RDAF_NOPROMPT
    };

    TEXT varbuff[ 258 ];

    envvarrda . RDA_Source . CS_Buffer = varbuff;

    if( GetVar( NAME, varbuff, 256L, 0UL ) != (-1L) )
    {
      strcat( varbuff, "\n" );

      if( ReadArgs( ENV_TEMPLATE, (LONG *)(&result), (&envvarrda) ) )
      {
        ScanRDA();

        FreeArgs( (&envvarrda) );
      }
      else
      {
        main_retval2 = IoErr();
        main_retval  = RETURN_FAIL;
      }
    }
}


static
void ClearRDA( void )
{
    memset( (void *)(&result), 0, sizeof( result ) );
}


static
void ScanRDA( void )
{
    if( result . srcname )
    {
      UpdateString( (&(project . srcname)), (result . srcname) );
    }

    if( result . destdatatype )
    {
      UpdateString( (&(project . destdatatype)), (result . destdatatype) );
    }

    if( result . iff )
    {
      project . iff = TRUE;
    }

    if( result . destname )
    {
      UpdateString( (&(project . destname)), (result . destname) );
    }

    if( result . gui )
    {
      project . gui = TRUE;
    }

    if( result . pubscreen )
    {
      UpdateString( (&(project . pubscreen)), (result . pubscreen) );
    }

    ClearRDA();
}


static
void ScanToolTypes( TEXT **tt )
{
    STRPTR s;

    if( s = FindToolType( tt, "FROM" ) )
      result . srcname = s;

    if( s = FindToolType( tt, "NAME" ) )
      result . srcname = s;

    if( s = FindToolType( tt, "SRCNAME" ) )
      result . srcname = s;

    if( s = FindToolType( tt, "DESTDATATYPE" ) )
      result . destdatatype = s;

    if( s = FindToolType( tt, "DATATYPE" ) )
      result . destdatatype = s;

    if( s = FindToolType( tt, "DTN" ) )
      result . destdatatype = s;

    if( s = FindToolType( tt, "IFF" ) )
      result . iff = (long *)s;

    if( s = FindToolType( tt, "TO" ) )
      result . destname = s;

    if( s = FindToolType( tt, "DESTNAME" ) )
      result . destname = s;

    if( s = FindToolType( tt, "GUI" ) )
      result . gui = (long *)s;

    if( s = FindToolType( tt, "PUBSCREEN" ) )
      result . pubscreen = s;

    ScanRDA();
}


static
void FreeInitProjectResult( void )
{
    FreeString( (project . srcname) );
    FreeString( (project . destdatatype) );
    FreeString( (project . destname) );
    FreeString( (project . pubscreen) );
}


static
BOOL CreateBasicResources( void )
{
    if( OpenLibStuff() )
    {
      if( StringPool = CreatePool( (MEMF_PUBLIC | MEMF_CLEAR), 128UL, 128UL ) )
      {
        return( TRUE );

#ifdef COMMENTED_OUT
        DeletePool( StringPool );
#endif /* COMMENTED_OUT */
      }

      CloseLibStuff();
    }

    return( FALSE );
}


static
void DeleteBasicResources( void )
{
    DeletePool( StringPool );
    CloseLibStuff();
}


static
BOOL OpenLibStuff( void )
{
    AttemptOpenLibrary( (&UtilityBase),                          NAME, "utility.library",     39UL );
    AttemptOpenLibrary( (struct Library **)(&(GfxBase)),         NAME, "graphics.library",    39UL );
    AttemptOpenLibrary( (struct Library **)(&(IntuitionBase)),   NAME, "intuition.library",   39UL );
    AttemptOpenLibrary( (&GadToolsBase),                         NAME, "gadtools.library",    39UL );
    AttemptOpenLibrary( (&DataTypesBase),                        NAME, "datatypes.library",   45UL );
    AttemptOpenLibrary( (&IconBase),                             NAME, "icon.library",        39UL );
    AttemptOpenLibrary( (&WorkbenchBase),                        NAME, "workbench.library",   39UL );
    AttemptOpenLibrary( (&AslBase),                              NAME, "asl.library",         38UL );

    if( UtilityBase && GfxBase && IntuitionBase && GadToolsBase && DataTypesBase && IconBase && WorkbenchBase && AslBase  )
    {
      return( TRUE );
    }

    CloseLibStuff();

    return( FALSE );
}


static
void CloseLibStuff( void )
{
    CloseLibrary( AslBase );
    CloseLibrary( WorkbenchBase );
    CloseLibrary( IconBase );
    CloseLibrary( DataTypesBase );
    CloseLibrary( GadToolsBase );
    CloseLibrary( (&(IntuitionBase -> LibNode)) );
    CloseLibrary( (&(GfxBase -> LibNode)) );
    CloseLibrary( UtilityBase );
}


static
void RunTool( STRPTR srcname )
{
    STRPTR destname     = project . destname;
    STRPTR datatypename = project . destdatatype;
    BOOL   srcchanged   = FALSE;      /* srcname changed ? */
    ULONG  srcsum       = SumString( srcname );

    if( project . srcname )
    {
      srcname = project . srcname;
    }

    /* Check if datatype name is valid */
    if( datatypename )
    {
      struct DataType *dtn;

      if( dtn = ObtainDataTypeA( DTST_RAM, (APTR)datatypename, NULL ) )
      {
        ReleaseDataType( dtn );
      }
      else
      {
        datatypename = NULL;
      }
    }

    /* GUI requested / required ? */
    if( (project . gui) || (srcname == NULL) || (destname == NULL) || (datatypename == NULL) )
    {
      struct MsgPort *appport;
      struct DTList  *dtl;
      struct DTNode  *selecteddt;

      if( appport = CreateMsgPort() )
      {
        BPTR srclock = NULL;

        if( Strlen( srcname ) )
        {
          srclock = Lock( srcname, SHARED_LOCK );
        }

        if( dtl = CreateDTDescrList( srclock, 0UL ) )
        {
          PubScreenName = project . pubscreen; /* set pubscreen name for GadToolsBox's windows */

          selecteddt = FindDTNodeByName( dtl, datatypename );

          if( !SetupScreen() )
          {
            struct FileRequester *SrcFileReq  = NULL,
                                 *DestFileReq = NULL;

            if( !OpenDTConvert_MAINWindow() )
            {
              BOOL                 done             = FALSE;
              struct IntuiMessage *imsg;
              ULONG                sigmask;
              BOOL                 get_source       = FALSE,
                                   get_datatype     = FALSE,
                                   get_destination  = FALSE;
              BOOL                 convert          = FALSE;
              BOOL                 convertable      = Strlen( srcname ) && selecteddt && Strlen( destname );
              struct AppWindow    *appwindow        = NULL;
              struct DiskObject   *appdobj          = NULL;
              struct AppIcon      *appicon          = NULL;

              /* Running on WB screen ? */
              if( ((DTConvert_MAINWnd -> WScreen -> Flags) & SCREENTYPE) == WBENCHSCREEN )
              {
                appwindow = AddAppWindowA( 0UL, 0UL, DTConvert_MAINWnd, appport, NULL );
              }
              else
              {
                if( appdobj = GetDefDiskObject( WBPROJECT ) )
                {
                  /* Not running on workbench screen ? - Create AppIcon for wb app support ! */
                  appicon = AddAppIconA( 0UL, 0UL, NAME, appport, NULL, appdobj, NULL );
                }
              }

              GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_SOURCE ],      DTConvert_MAINWnd, NULL, GTST_String, srcname,                                                  TAG_DONE );
              GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_DATATYPE ],    DTConvert_MAINWnd, NULL, GTTX_Text,   ((selecteddt)?(selecteddt -> dtn_Node . ln_Name):(NULL)), TAG_DONE );
              GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_DESTINATION ], DTConvert_MAINWnd, NULL, GTST_String, destname,                                                 TAG_DONE );
              GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_CONVERT ],     DTConvert_MAINWnd, NULL, GA_Disabled, (!convertable),                                           TAG_DONE );

              /* Main event loop */
              while( !done )
              {
                ULONG sigr;

                sigmask = (1UL << (DTConvert_MAINWnd -> UserPort -> mp_SigBit)) | (1UL << (appport -> mp_SigBit));

                if( DTConvert_DATATYPEWnd )
                {
                  sigmask |= (1UL << (DTConvert_DATATYPEWnd -> UserPort -> mp_SigBit));
                }

                sigr = Wait( sigmask );

/* Maiain window */
                if( sigr & (1UL << (DTConvert_MAINWnd -> UserPort -> mp_SigBit)) )
                {
                  while( imsg = GT_GetIMsg( (DTConvert_MAINWnd -> UserPort) ) )
                  {
                    switch( imsg -> Class )
                    {
                      case IDCMP_CLOSEWINDOW:
                      {
                          done = TRUE;
                      }
                          break;

                      case IDCMP_REFRESHWINDOW:
                      {
                          GT_BeginRefresh( (imsg -> IDCMPWindow) );
                          GT_EndRefresh( (imsg -> IDCMPWindow), TRUE );
                      }
                          break;

                      case IDCMP_GADGETUP:
                      {
                          switch( G( (imsg -> IAddress) ) -> GadgetID )
                          {
                            case GD_SOURCE: break;   /* NOP */

                            case GD_DATATYPE: break; /* NOP */

                            case GD_DESTINATION: break; /* NOP */

                            case GD_GET_SOURCE:
                            {
                                get_source = TRUE;
                            }
                                break;

                            case GD_GET_DATATYPE:
                            {
                                get_datatype = TRUE;
                            }
                                break;

                            case GD_GET_DESTINATION:
                            {
                                get_destination = TRUE;
                            }
                                break;

                            case GD_CONVERT:
                            {
                                convert = TRUE;
                            }
                                break;

                            case GD_CANCEL:
                            {
                                done = TRUE;
                            }
                                break;
                          }
                      }
                          break;

                      case IDCMP_VANILLAKEY:
                      {
                          switch( imsg -> Code )
                          {
                            case 's': /* Source */
                                ActivateGadget( DTConvert_MAINGadgets[ GD_SOURCE ], DTConvert_MAINWnd, NULL );
                                break;

                            case 'S': /* Get Source */
                                get_source = TRUE;
                                break;

                            case 't': /* datatype */
                            case 'T': /* datatype */
                                get_datatype = TRUE;
                                break;

                            case 'd': /* Destination */
                                ActivateGadget( DTConvert_MAINGadgets[ GD_DESTINATION ], DTConvert_MAINWnd, NULL );
                                break;

                            case 'D': /* Get Destination */
                                get_destination = TRUE;
                                break;

                            case 'c': /* Convert */
                            case 'C': /* Convert */
                            {
                                convert = TRUE;
                            }
                                break;

                            case 'a': /* Cancel */
                            case 'A': /* Cancel */
                            {
                                done = TRUE;
                            }
                                break;
                          }
                      }
                          break;
                    }

                    GT_ReplyIMsg( imsg );
                  }
                }

/* DatataTypes requester */
                if( DTConvert_DATATYPEWnd )
                {
                  if( sigr & (1UL << (DTConvert_DATATYPEWnd -> UserPort -> mp_SigBit)) )
                  {
                    BOOL  dtdone = FALSE;
                    BOOL  dtok   = FALSE;
                    LONG  oldselected,
                          selected;

                    GT_GetGadgetAttrs( DTConvert_DATATYPEGadgets[ GD_DATATYPEDT ], DTConvert_DATATYPEWnd, NULL,
                                       GTLV_Selected, (&oldselected),
                                       TAG_DONE );

                    selected = oldselected;

                    while( imsg = GT_GetIMsg( (DTConvert_DATATYPEWnd -> UserPort) ) )
                    {
                      switch( imsg -> Class )
                      {
                        case IDCMP_CLOSEWINDOW:
                        {
                            dtdone = TRUE;
                        }
                            break;

                        case IDCMP_REFRESHWINDOW:
                        {
                            GT_BeginRefresh( (imsg -> IDCMPWindow) );
                            GT_EndRefresh( (imsg -> IDCMPWindow), TRUE );
                        }
                            break;

                        case IDCMP_GADGETUP:
                        {
                            switch( G( (imsg -> IAddress) ) -> GadgetID )
                            {
                              case GD_DATATYPEDT: break; /* NOP */

                              case GD_OKDT:
                              {
                                  dtdone = dtok = TRUE;
                              }
                                  break;

                              case GD_CANCELDT:
                              {
                                  dtdone = TRUE;
                              }
                                  break;
                            }
                        }
                            break;

                        case IDCMP_VANILLAKEY:
                        {
                            switch( imsg -> Code )
                            {
                              case 'o': /* Ok */
                              case 'O': /* Ok */
                                  dtdone = dtok = TRUE;
                                  break;

                              case 'c': /* Cancel */
                              case 'C': /* Cancel */
                                  dtdone = TRUE;
                                  break;

                              case 'd': /* DT Selection down */
                              {
                                  selected++;
                              }
                                  break;

                              case 'D': /* DT Selection up */
                              {
                                  selected--;
                              }
                                  break;
                            }
                        }
                            break;

                        case IDCMP_RAWKEY:
                        {
                            switch( imsg -> Code )
                            {
                              case 76: /* up */
                              {
                                  if( (imsg -> Qualifier) & (IEQUALIFIER_LALT | IEQUALIFIER_RALT) )
                                  {
                                    selected = 0L; /* top */
                                  }
                                  else
                                  {
                                    if( (imsg -> Qualifier) & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT) )
                                    {
                                      selected -= 5L; /* page up */
                                    }
                                    else
                                    {
                                      selected--; /* previous entry */
                                    }
                                  }
                              }
                                  break;

                              case 77: /* down */
                              {
                                  if( (imsg -> Qualifier) & (IEQUALIFIER_LALT | IEQUALIFIER_RALT) )
                                  {
                                    selected = dtl -> dtl_NumNodes; /* bottom */
                                  }
                                  else
                                  {
                                    if( (imsg -> Qualifier) & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT) )
                                    {
                                      selected += 5L; /* page down */
                                    }
                                    else
                                    {
                                      selected++; /* next entry */
                                    }
                                  }
                              }
                                  break;
                            }
                        }
                            break;
                      }

                      /* Check bounds */
                      if( selected < 0L )
                      {
                        selected = 0L;
                      }
                      else
                      {
                        if( selected > (LONG)(dtl -> dtl_NumNodes) )
                        {
                          selected = (LONG)(dtl -> dtl_NumNodes);
                        }
                      }

                      GT_ReplyIMsg( imsg );
                    }

                    if( selected != oldselected )
                    {
                      GT_SetGadgetAttrs( DTConvert_DATATYPEGadgets[ GD_DATATYPEDT ], DTConvert_DATATYPEWnd, NULL,
                                         GTLV_Selected,    selected,
                                         GTLV_MakeVisible, selected,
                                         TAG_DONE );
                    }

                    if( dtok )
                    {
                      if( selecteddt = (struct DTNode *)GetNumNode( (&(dtl -> dtl_List)), selected ) )
                      {
                        GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_DATATYPE ], DTConvert_MAINWnd, NULL,
                                           GTTX_Text, (selecteddt -> dtn_Node . ln_Name),
                                           TAG_DONE );
                      }
                    }

                    if( dtdone )
                    {
                      CloseDTConvert_DATATYPEWindow();
                    }
                  }
                }

/* appppwindow / appicon port */
                if( sigr & (1UL << (appport -> mp_SigBit)) )
                {
                  struct AppMessage *amsg;

                  while( amsg = (struct AppMessage *)GetMsg( appport ) )
                  {
                    switch( amsg -> am_Type )
                    {
                      case AMTYPE_APPWINDOW:
                      case AMTYPE_APPICON:
                      {
                          if( amsg -> am_NumArgs )
                          {
                            LockGUI();

                            if( (amsg -> am_NumArgs) == 1UL )
                            {
                              STRPTR name;

                              if( name = GetLockName( (amsg -> am_ArgList[ 0 ] . wa_Lock), (amsg -> am_ArgList[ 0 ] . wa_Name) ) )
                              {
                                GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_SOURCE ], DTConvert_MAINWnd, NULL, GTST_String, name, TAG_DONE );

                                FreeVec( name );
                              }
                            }
                            else
                            {
                              ULONG i;

                              for( i = 0UL ; i < (amsg -> am_NumArgs) ; i++ )
                              {
                                STRPTR name;

                                if( name = GetLockName( (amsg -> am_ArgList[ i ] . wa_Lock), (amsg -> am_ArgList[ i ] . wa_Name) ) )
                                {
                                  NewProject( name, ((selecteddt)?(selecteddt -> dtn_DTName):(NULL)), (project . iff), destname, TRUE, (project . pubscreen) );

                                  FreeVec( name );
                                }
                              }
                            }

                            UnlockGUI();
                          }
                      }
                          break;
                    }

                    ReplyMsg( (&(amsg -> am_Message)) );
                  }
                }

/* prorocess collected events */
                if( get_source || get_datatype || get_destination )
                {
                  LockGUI();

                  if( get_source )
                  {
                    if( SrcFileReq == NULL )
                    {
                      SrcFileReq = (struct FileRequester *)AllocAslRequestTags( ASL_FileRequest,
                                                                                ASLFR_Screen,          (DTConvert_MAINWnd -> WScreen),
                                                                                ASLFR_TitleText,       "Open File...",
                                                                                ASLFR_InitialLeftEdge, ((DTConvert_MAINWnd -> LeftEdge) + (DTConvert_MAINWnd -> BorderLeft) + 1UL),
                                                                                ASLFR_InitialTopEdge,  ((DTConvert_MAINWnd -> TopEdge)  + (DTConvert_MAINWnd -> BorderTop) + 1UL),
                                                                                ASLFR_DoPatterns,      TRUE,
                                                                                TAG_DONE );
                    }

                    if( SrcFileReq )
                    {
                      if( AslRequest( SrcFileReq, NULL ) )
                      {
                        ULONG  buffsize;
                        STRPTR filebuffer;

                        buffsize = Strlen( (SrcFileReq -> fr_Drawer) ) + Strlen( (SrcFileReq -> fr_File) ) + 10UL;

                        if( filebuffer = (STRPTR)AllocVec( buffsize, MEMF_PUBLIC ) )
                        {
                          strcpy( filebuffer, (SrcFileReq -> fr_Drawer) );
                          AddPart( filebuffer, (SrcFileReq -> fr_File), buffsize );

                          GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_SOURCE ], DTConvert_MAINWnd, NULL, GTST_String, filebuffer, TAG_DONE );

                          FreeVec( filebuffer );
                        }
                      }
                    }

                    get_source = FALSE;
                  }

                  if( get_datatype )
                  {
                    if( DTConvert_DATATYPEWnd == NULL )
                    {
                      if( !OpenDTConvert_DATATYPEWindow() )
                      {
                        GT_SetGadgetAttrs( DTConvert_DATATYPEGadgets[ GD_DATATYPEDT ], DTConvert_DATATYPEWnd, NULL, GTLV_Labels, dtl, TAG_DONE );
                      }
                    }

                    get_datatype = FALSE;
                  }

                  if( get_destination )
                  {
                    if( DestFileReq == NULL )
                    {
                      DestFileReq = (struct FileRequester *)AllocAslRequestTags( ASL_FileRequest,
                                                                                 ASLFR_Screen,          (DTConvert_MAINWnd -> WScreen),
                                                                                 ASLFR_TitleText,       "Save File...",
                                                                                 ASLFR_InitialLeftEdge, ((DTConvert_MAINWnd -> LeftEdge) + (DTConvert_MAINWnd -> BorderLeft) + 1UL),
                                                                                 ASLFR_InitialTopEdge,  ((DTConvert_MAINWnd -> TopEdge)  + (DTConvert_MAINWnd -> BorderTop) + 1UL),
                                                                                 ASLFR_DoPatterns,      TRUE,
                                                                                 ASLFR_DoSaveMode,      TRUE,
                                                                                 TAG_DONE );
                    }

                    if( DestFileReq )
                    {
                      if( AslRequest( DestFileReq, NULL ) )
                      {
                        ULONG  buffsize;
                        STRPTR filebuffer;

                        buffsize = Strlen( (DestFileReq -> fr_Drawer) ) + Strlen( (DestFileReq -> fr_File) ) + 10UL;

                        if( filebuffer = (STRPTR)AllocVec( buffsize, MEMF_PUBLIC ) )
                        {
                          strcpy( filebuffer, (DestFileReq -> fr_Drawer) );
                          AddPart( filebuffer, (DestFileReq -> fr_File), buffsize );

                          GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_DESTINATION ], DTConvert_MAINWnd, NULL, GTST_String, filebuffer, TAG_DONE );

                          FreeVec( filebuffer );
                        }
                      }
                    }

                    get_destination = FALSE;
                  }

                  UnlockGUI();
                }

/* update */
                GT_GetGadgetAttrs( DTConvert_MAINGadgets[ GD_SOURCE ], DTConvert_MAINWnd, NULL,
                                   GTST_String, (&srcname),
                                   TAG_DONE );
                                   

                /* My quick hack to check if the src filename has been changed... */
                srcchanged = (SumString( srcname ) != srcsum);

                /* following block is experimental...  */
                if( srcchanged )
                {
                  struct DTList *old_dtl = dtl;

                  /* Update checksum... */
                  srcsum = SumString( srcname );

                  if( srclock )
                  {
                    UnLock( srclock );
                    srclock = NULL;
                  }

                  if( Strlen( srcname ) )
                  {
                    srclock = Lock( srcname, SHARED_LOCK );
                  }

                  dtl = CreateDTDescrList( srclock, 0UL );

                  if( selecteddt )
                  {
                    selecteddt = FindDTNodeByName( dtl, (selecteddt -> dtn_DTName) );

                    GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_DATATYPE ], DTConvert_MAINWnd, NULL,
                                       GTTX_Text, ((selecteddt)?(selecteddt -> dtn_Node . ln_Name):("")),
                                       TAG_DONE );
                  }

                  /* update DataTypes selection gadget */
                  if( DTConvert_DATATYPEWnd )
                  {
                    GT_SetGadgetAttrs( DTConvert_DATATYPEGadgets[ GD_DATATYPEDT ], DTConvert_DATATYPEWnd, NULL, GTLV_Labels, dtl, TAG_DONE );
                  }

                  FreeDTDescrList( old_dtl );
                }

                GT_GetGadgetAttrs( DTConvert_MAINGadgets[ GD_DESTINATION ], DTConvert_MAINWnd, NULL,
                                   GTST_String, (&destname),
                                   TAG_DONE );

                convertable = Strlen( srcname ) && selecteddt && Strlen( destname );

                GT_SetGadgetAttrs( DTConvert_MAINGadgets[ GD_CONVERT ], DTConvert_MAINWnd, NULL,
                                   GA_Disabled, (!convertable),
                                   TAG_DONE );

/* run converter ? */
                if( convert )
                {
                  if( convertable )
                  {
                    LockGUI();

                    Message( "converting '%s' '%s' '%s'\n", srcname, (selecteddt -> dtn_DTName), destname );

                    Convert( srcname, (selecteddt -> dtn_DTName), FALSE, destname );

                    UnlockGUI();
                  }

                  convert = FALSE;
                }
              }

              if( appwindow )
              {
                (void)RemoveAppWindow( appwindow );
              }

              if( appdobj )
              {
                FreeDiskObject( appdobj );
              }

              if( appicon )
              {
                (void)RemoveAppIcon( appicon );
              }

              CloseDTConvert_DATATYPEWindow();
              CloseDTConvert_MAINWindow();
            }

            FreeAslRequest( SrcFileReq );
            FreeAslRequest( DestFileReq );

            CloseDownScreen();
          }

          FreeDTDescrList( dtl );
        }

        if( srclock )
        {
          UnLock( srclock );
        }

        ClearMsgPort( appport );
        DeleteMsgPort( appport );
      }
    }
    else
    {
      Convert( srcname, datatypename, (project . iff), destname );
    }

    if( IoErr() )
    {
      main_retval2 = IoErr();
      main_retval  = RETURN_ERROR;
    }
}


static
BOOL NewProject( STRPTR from, STRPTR datatype, BOOL iff, STRPTR destname, BOOL gui, STRPTR pubscreen )
{
    struct Process              *ThisProc = (struct Process *)FindTask( NULL );
    BPTR                         oldir    = CurrentDir( (ThisProc -> pr_HomeDir) );
    BOOL                         success  = FALSE;
    STRPTR                       buffer;
    ULONG                        buffersize;
    struct CommandLineInterface *cli = Cli();

    buffersize = Strlen( from ) + Strlen( datatype ) + Strlen( destname ) + Strlen( pubscreen ) + 512UL;

    if( buffer = (STRPTR)AllocVec( buffersize, MEMF_PUBLIC ) )
    {
      STRPTR next = buffer;

      if( cli )
      {
        STRPTR s = (STRPTR)BADDR( (cli -> cli_CommandName) );

        stccpy( next, (&s[ 1 ]), (int)(s[ 0 ] + 1U) );
      }
      else
      {
        strcpy( next, (ThisProc -> pr_Task . tc_Node . ln_Name) );
      }

      next += strlen( next );

      if( Strlen( from ) )
      {
        mysprintf( next, " FROM=\"%s\"", from );
        next += strlen( next );
      }

      if( Strlen( datatype ) )
      {
        mysprintf( next, " DATATYPE=\"%s\"", datatype );
        next += strlen( next );
      }

      if( iff )
      {
        mysprintf( next, " IFF" );
        next += strlen( next );
      }

      if( Strlen( destname ) )
      {
        mysprintf( next, " TO=\"%s\"", destname );
        next += strlen( next );
      }

      if( gui )
      {
        mysprintf( next, " GUI" );
        next += strlen( next );
      }

      if( Strlen( pubscreen ) )
      {
        mysprintf( next, " PUBSCREEN=\"%s\"", pubscreen );
      }

      if( SystemTags( buffer,
                      SYS_Input,  NULL,
                      SYS_Output, NULL,
                      SYS_Asynch, TRUE,
                      TAG_DONE ) != (-1L) )
      {
        success = TRUE;
      }

      FreeVec( buffer );
    }

    (void)CurrentDir( oldir );

    return( success );
}


/* GUI locking stuff */
static ULONG            GUILock                = 0UL;
static struct Requester MAINBlockReq           = { 0 };
static BOOL             MAINBlockReqActive     = FALSE;
static struct Requester DATATYPEBlockReq       = { 0 };
static BOOL             DATATYPEBlockReqActive = FALSE;


static
void LockGUI( void )
{
    if( GUILock++ == 0UL )
    {
      InitRequester( (&MAINBlockReq) );
      InitRequester( (&DATATYPEBlockReq) );

      if( DTConvert_MAINWnd )
      {
        SetWindowPointer( DTConvert_MAINWnd, WA_BusyPointer, TRUE, WA_PointerDelay, TRUE, TAG_DONE );

        if( MAINBlockReqActive == FALSE )
        {
          MAINBlockReqActive = Request( (&MAINBlockReq), DTConvert_MAINWnd );
        }
      }

      if( DTConvert_DATATYPEWnd )
      {
        SetWindowPointer( DTConvert_DATATYPEWnd, WA_BusyPointer, TRUE,  WA_PointerDelay, TRUE, TAG_DONE );

        if( DATATYPEBlockReqActive == FALSE )
        {
          DATATYPEBlockReqActive = Request( (&DATATYPEBlockReq), DTConvert_DATATYPEWnd );
        }
      }
    }
}


static
void UnlockGUI( void )
{
    if( --GUILock == 0UL )
    {
      if( DTConvert_MAINWnd )
      {
        SetWindowPointer( DTConvert_MAINWnd, WA_BusyPointer, FALSE, TAG_DONE );

        if( MAINBlockReqActive )
        {
          EndRequest( (&MAINBlockReq), DTConvert_MAINWnd );
          MAINBlockReqActive = FALSE;
        }
      }

      if( DTConvert_DATATYPEWnd )
      {
        SetWindowPointer( DTConvert_DATATYPEWnd, WA_BusyPointer, FALSE, TAG_DONE );

        if( DATATYPEBlockReqActive )
        {
          EndRequest( (&DATATYPEBlockReq), DTConvert_DATATYPEWnd );
          DATATYPEBlockReqActive = FALSE;
        }
      }
    }
}


void Message( STRPTR fmt, ... )
{
    VPrintf( fmt, (APTR)((&fmt) + 1) );
}


static
struct DTList *CreateDTDescrList( BPTR lock, ULONG groupid )
{
    APTR  pool;
#define NUM_GID_TABLE (32) /* we have much less GID_#? types defined yet (datatypes.library V45), but... */
    ULONG groupid_table[ NUM_GID_TABLE ] = { 0 };

    /* If a lock is given, we build here a list of all DT group ids the source file may be of... */
    if( lock )
    {
      struct DataType *dtn,
                      *prevdtn = NULL;

      /* Print whole list or determine the DataType of the file */
      while( dtn = ObtainDataType( DTST_FILE, (APTR)lock, DTA_DataType,                  prevdtn,
                                                          XTAG( groupid, DTA_GroupID ),  groupid,
                                                          TAG_DONE ) )
      {
        ULONG i,
              groupid = dtn -> dtn_Header -> dth_GroupID;

        /* Release previous DataType */
        ReleaseDataType( prevdtn );

        /* the follwoing statement assmes that GID_SYSTEM datatypes are not convertable
         * (and it filters some invalid datatypes which have a 0 group id)
         */
        if( (groupid != GID_SYSTEM) && (groupid != 0UL) )
        {
          for( i = 0UL ; i < NUM_GID_TABLE ; i++ )
          {
            if( !groupid_table[ i ] )
            {
              groupid_table[ i ] = groupid;
              break;
            }
          }
        }

        prevdtn = dtn;
      }

      /* Release last datatype */
      ReleaseDataType( prevdtn );
    }

    if( pool = CreatePool( (MEMF_PUBLIC | MEMF_CLEAR), 1024UL, 1024UL ) )
    {
      struct DTList *dtl;

      if( dtl = (struct DTList *)AllocVecPooled( pool, sizeof( struct DTList ) ) )
      {
        struct DataType *dtn,
                        *prevdtn = NULL;
        BOOL             error   = FALSE;

        NewList( (&(dtl -> dtl_List)) );
        dtl -> dtl_Pool = pool;

        /* Print whole list or determine the DataType of the file */
        while( dtn = ObtainDataType( DTST_RAM, NULL, DTA_DataType,                  prevdtn,
                                                     XTAG( groupid, DTA_GroupID ),  groupid,
                                                     TAG_DONE ) )
        {
          struct DataTypeHeader *dth;          /* datatypes header        */
          BOOL                   match = TRUE; /* include this datatype ? */

          /* Release previous DataType */
          ReleaseDataType( prevdtn );

          dth = dtn -> dtn_Header; /* Shortcut... */

          /* If we have a lock given, we previously build a group id table... */
          if( lock )
          {
            ULONG i;

            match = FALSE;

            /* ... now check if this datatype node matches any source groupid */
            for( i = 0UL ; ((i < NUM_GID_TABLE) && groupid_table[ i ]) ; i++ )
            {
              /* Match ? */
              if( groupid_table[ i ] == (dth -> dth_GroupID) )
              {
                match = TRUE;
                break;
              }

              /* Special kluge that we can convert GID_ANIMATION to GID_MOVIE and backwards
               * (both are based on animation.datatype, here we have the same interface :-)
               */
              if( ((groupid_table[ i ]   == GID_MOVIE) || (groupid_table[ i ]   == GID_ANIMATION)) &&
                  (((dth -> dth_GroupID) == GID_MOVIE) || ((dth -> dth_GroupID) == GID_ANIMATION)) )
              {
                match = TRUE;
                break;
              }
            }
          }

          /* Include this datatype ? */
          if( match )
          {
            STRPTR         groupid_name; /* short-cut to GID_#? name string    */
            ULONG          size;         /* size of struct DTNode and contents */
            struct DTNode *namenode;

            groupid_name = GetDTString( (dth -> dth_GroupID) );

            size = sizeof( struct DTNode ) + (strlen( (dth -> dth_Name) ) * 2UL) + Strlen( groupid_name ) + 16UL;

            /* Alloc and build node... */
            if( namenode = (struct DTNode *)AllocVecPooled( pool, size ) )
            {
              STRPTR s = (STRPTR)(namenode + 1);

              /* store listview names */
              mysprintf( s, "%s %s", (dth -> dth_Name), groupid_name );
              namenode -> dtn_Node . ln_Name = s;

              s = s + strlen( s ) + 2; /* next free space */

              /* store datatype name */
              strcpy( s, (dth -> dth_Name) );
              namenode -> dtn_DTName = s;

              /* add node */
              AddTail( (&(dtl -> dtl_List)), (&(namenode -> dtn_Node)) );
              dtl -> dtl_NumNodes++;
            }
            else
            {
              error = TRUE;
            }
          }

          prevdtn = dtn;
        }

        /* Release last datatype */
        ReleaseDataType( prevdtn );

        /* Success ? */
        if( !error )
        {
          return( dtl );
        }
      }

      DeletePool( pool );
    }

    return( NULL );
}


static
void FreeDTDescrList( struct DTList *list )
{
   if( list )
   {
     DeletePool( (list -> dtl_Pool) );
   }
}


static
struct DTNode *FindDTNodeByName( struct DTList *list, STRPTR name )
{
    struct DTNode *node;
    struct DTNode *result = NULL;

    if( list && name )
    {
      for( node = (struct DTNode *)(list -> dtl_List . lh_Head) ; node -> dtn_Node . ln_Succ ; node = (struct DTNode *)(node -> dtn_Node . ln_Succ) )
      {
        if( !Stricmp( (node -> dtn_DTName), name ) )
        {
          result = node;
          break;
        }
      }
    }

    return( result );
}


static
STRPTR StringSave( STRPTR s )
{
    STRPTR saved;

    if( s )
    {
      if( saved = AllocStringBuff( (ULONG)strlen( s ) ) )
      {
        strcpy( saved, s );

        return( saved );
      }
    }

    return( NULL );
}


static
STRPTR AllocStringBuff( ULONG size )
{
    return( (STRPTR)AllocVecPooled( StringPool, (size + 2UL) ) );
}


static
STRPTR UpdateString( STRPTR *storage, STRPTR newstring )
{
    STRPTR s;

    s = NULL;

    if( storage )
    {
      FreeString( (*storage) );

      s = (*storage) = StringSave( newstring );
    }

    return( s );
}


static
void FreeString( STRPTR s )
{
    if( s )
    {
      FreeVecPooled( StringPool, s );
    }
}


static
struct DiskObject *GetToolDiskObject( STRPTR name )
{
    struct DiskObject *dobj;
    struct Process    *ThisProc = (struct Process *)FindTask( NULL );
    BPTR               olddir   = CurrentDir( (ThisProc -> pr_HomeDir) );

    dobj = GetDiskObjectNew( name );

    (void)CurrentDir( olddir );

    return( dobj );
}


static
ULONG SumString( STRPTR s )
{
    ULONG sum = 0UL;

    if( s )
    {
      ULONG ch;

      while( ch = *s++ )
      {
        sum = (sum * 7UL) + ch;
      }
    }

    return( sum );
}

