/*************************************************************************
  *                                                                      *
  *                            Preliminary                               *
  *                        Amiga AppShell (tm)                           *
  *                                                                      *
  *  Copyright (c) 1990,1991 Commodore-Amiga, Inc. All Rights Reserved.  *
  *                                                                      *
  *   This software and information is proprietary, preliminary, and     *
  *   subject to change without notice.                                  *
  *                                                                      *
  *                            DISCLAIMER                                *
  *                                                                      *
  *   THIS SOFTWARE IS PROVIDED "AS IS".                                 *
  *   NO REPRESENTATIONS OR WARRANTIES ARE MADE WITH RESPECT TO THE      *
  *   ACCURACY, RELIABILITY, PERFORMANCE, CURRENTNESS, OR OPERATION      *
  *   OF THIS SOFTWARE, AND ALL USE IS AT YOUR OWN RISK.                 *
  *   NEITHER COMMODORE NOR THE AUTHORS ASSUME ANY RESPONSIBILITY OR     *
  *   LIABILITY WHATSOEVER WITH RESPECT TO YOUR USE OF THIS SOFTWARE.    *
  *                                                                      *
  *                          Non-Disclosure                              *
  *                                                                      *
  *   This information is not to be disclosed to any other company,      *
  *   individual or party.  Discussion is to be restricted to CBM        *
  *   approved discussion areas, such as the closed conferences on bix;  *
  *   amiga.cert, amiga.com, amiga.beta/appshell.                        *
  *                                                                      *
  ************************************************************************
  */

/* ILBMer.c
 * Copyright (C) 1990, 1991 Commodore-Amiga, Inc.
 * Written by David N. Junod
 *
 * This is an example of an AppShell application.  It allows you to
 * load an ILBM from a file or clipboard, display it, copy it to the
 * clipboard, and save it as a 'C' source file in image or sprite format.
 *
 * It fully supports the ARexx, Command Shell, Intuition and Workbench
 * user interfaces.
 *
 */

#include <exec/types.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <utility/hooks.h>
#include <graphics/scale.h>
#include <graphics/displayinfo.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/classes.h>
#include <intuition/classusr.h>
#include <libraries/asl.h>
#include <libraries/appshell.h>
#include <libraries/gadtools.h>
#include <libraries/prefs.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <clib/macros.h>
#include <clib/asl_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/icon_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/appshell_protos.h>
#include <clib/appobjects_protos.h>
#include <clib/utility_protos.h>
#include <pragmas/exec.h>
#include <pragmas/dos.h>
#include <pragmas/appshell.h>
#include <pragmas/appobjects.h>
#include <pragmas/utility.h>
#include <pragmas/asl.h>
#include <string.h>
#include "ae:support/misc_protos.h"
#include "ae:clipboard/clipiff.h"

/* Output of bumprev */
#include "ilbmer.h"

/* Icon that we use for listings */
extern struct DiskObject Listing;

/* The AppShell maintains some basic information on the application.  This
 * information would appear in the About requester, and in AppExchange. */
#define	APPBASE "ILBMER"
#define	APPNAME "ILBMer"
#define	APPVERS	VERS
#define	APPCOPY "Copyright (C) 1990,1991 Commodore-Amiga, Inc."
#define	APPAUTH "Written by David N. Junod"

char *version = VERSTAG;

/* File requester ID's. */
#define	FR_ILBM	0
#define	FR_C	1
#define	FR_MAX	2

/* This structure contains all the data that our application will need.
 * Change to suit your application. */
struct AppData
{
    struct FileRequester *ad_FR[FR_MAX];	/* File requesters */
    struct ClipManager *ad_CM;	/* Clipboard manager handle */
    struct Image ad_Image;	/* Image... */
    struct PointerPref *ad_Ptr;	/* Pointer... */
    ILBM *ad_IR;		/* ILBM record */
    struct Screen *ad_Screen;	/* Display screen */
    UBYTE ad_Tmp[512];		/* Temporary work space */
    UBYTE ad_Name[512];		/* Output name */
    LONG ad_Mode;		/* Output mode */

    /* These are fields that are saved as preference items */

    UBYTE ad_Struct[32];	/* Structure name */
    ULONG ad_Unit;		/* Default clipboard unit */
    BOOL ad_SaveIcons;		/* Save icons? */
    WORD ad_LeftEdge;		/* File requester positioning */
    WORD ad_TopEdge;
    WORD ad_Width;
    WORD ad_Height;
};

/* Application specific data */
#define	IMAGE	0
#define	SPRITE	1
#define	ICON	2

/* Each public function gets a numeric ID assigned to it.  You must
 * assign an ID to each one of your functions.  Look at appshell.h for
 * standard function ID's for things like New, Open, Cut, Copy, Paste,
 * Quit, etc... */
enum
{
    DUMMYID = APSH_USER_ID,
    CInitID,
    CExitID,
    OpenMainID,
    OpenAboutID,
    SetNameID,
    SetModeID,
    DisplayID,
    ClipChangeID,
    SaveSettingsID,
    LAST_ID
};

/* These are the function prototypes for all the application implemented
 * functions used in this example.  You must prototype all the functions
 * that your application is going use in the function table. */
VOID CInitFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID CExitFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID OpenFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID SaveAsFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID AboutFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID QuitFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID CutFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID CopyFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID PasteFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID EraseFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID OpenMainFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID OpenAboutFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID DropIconFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID SetNameFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID SetModeFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID DisplayFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID ClipChangeFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID SaveSettingsFunc (struct Hook *, struct AppInfo *, struct AppFunction *);

/* These are function prototypes that aren't going in the function
 * table. */
ILBM *ReadILBM (BPTR drawer, STRPTR name, struct TagItem * attrs);
ILBM *GetILBM (struct IFFHandle * iff);
BOOL PutILBM (struct IFFHandle * iff, ILBM * ilbm);
VOID FreeILBM (ILBM * ilbm);
struct PointerPref *MakePointer (ILBM * ilbm);
VOID FreePointer (struct PointerPref * pp);

/* The AppShell will convert this array into function table entries and will
 * add them to the function table list.  Using the APSHF_PRIVATE flag, we are
 * able to have functions that can't be triggered by the user.  This
 * also defines the commands that are available through ARexx.  Note that
 * the command parser is case-INSENSITIVE, but for readability, your
 * entries in this table should follow standard capitalization rules. */
struct Funcs FTable[] =
{
    {"Clear", EraseFunc, ClearID, ",", 0L, NULL,},
    {"Open", OpenFunc, OpenID, "FILENAME,FORCE/S", 2L, NULL,},
    {"SaveAs", SaveAsFunc, SaveAsID, "FILENAME", 1L, NULL,},
    {"About", AboutFunc, AboutID, ",", 0L, NULL,},
    {"Quit", QuitFunc, QuitID, "FORCE/S", 1L, NULL,},
    {"Cut", CutFunc, CutID, ",", 0L, NULL,},
    {"Copy", CopyFunc, CopyID, ",", 0L, NULL,},
    {"Paste", PasteFunc, PasteID, ",", 0L, NULL,},
    {"Erase", EraseFunc, EraseID, ",", 0L, NULL,},
    {"OpenMain", OpenMainFunc, OpenMainID, NULL, NULL, APSHF_PRIVATE,},
    {"OpenAbout", OpenAboutFunc, OpenAboutID, NULL, NULL, APSHF_PRIVATE,},
    {"CInit", CInitFunc, CInitID, NULL, NULL, APSHF_PRIVATE,},
    {"CExit", CExitFunc, CExitID, NULL, NULL, APSHF_PRIVATE,},
    {"DropIcon", DropIconFunc, DropIconID, NULL, NULL, APSHF_PRIVATE,},
    {"SetName", SetNameFunc, SetNameID, "NAME", 1L, NULL,},
    {"SetMode", SetModeFunc, SetModeID, "IMAGE/S,SPRITE/S,ICON/S", 3L, NULL,},
    {"Display", DisplayFunc, DisplayID, "OFF/S", 1L, NULL,},
    {"ClipChange", ClipChangeFunc, ClipChangeID, NULL, NULL, APSHF_PRIVATE,},
    {"SaveSettings", SaveSettingsFunc, SaveSettingsID, ",", 0L, NULL,},

    {NULL, NO_FUNCTION,}	/* end of array */
};

/*
 * All text used in the application must be defined in a text array.  Then
 * referred to by numeric ID.
 */
STRPTR app_deftext[] =
{
 /* Main window labels */
    "<unassigned>",		/* 0 */
    "ILBMer",			/* 1 */
    "Graphic",			/* 2 */
    "Output:",			/* 3 */
    "_Label:",			/* 4 */
    APPNAME,			/* 5 */
    APPVERS,			/* 6 */
    APPCOPY,			/* 7 */
    APPAUTH,			/* 8 */
    "9",			/* 9 */
    "10",			/* 10 */
    "11",			/* 11 */

 /* Main window menus */
    "WINDOW MAIN",		/* 12 */
    " TITLE PROJECT  LABEL Project",
    "  ITEM OPEN     LABEL \"Open ILBM...\"     KEY O CMD Open",
    "  ITEM DISPLAY  LABEL Display              KEY D CMD Display",
    "  ITEM SAVEAS   LABEL \"Save...\"          KEY S CMD SaveAs",
    "  ITEM ABOUT    LABEL \"About...\"               CMD About",
    "  ITEM QUIT     LABEL Quit                 KEY Q CMD Quit",

    " TITLE EDIT     LABEL Edit",
    "  ITEM CUT      LABEL Cut			KEY X CMD Cut",
    "  ITEM COPY     LABEL Copy                 KEY C CMD Copy",
    "  ITEM PASTE    LABEL Paste                KEY V CMD Paste",
    "  ITEM ERASE    LABEL Erase                KEY E CMD Erase",

    " TITLE EXTRAS   LABEL Extras",
    "  ITEM CMDSHELL LABEL \"Command Shell...\" KEY . CMD CmdShell",

    " TITLE PREFS    LABEL Preferences",
    "  ITEM SNAPSHOT LABEL \"Save Settings\"	      CMD SaveSettings",

    "   END",

 /* About window labels */
    "About ILBMer",		/* 29 */
    "_Continue",		/* 30 */

    "Couldn't read brush",
#define	IERR_CANT_LOAD_BRUSH	31

    "Couldn't write brush",
#define	IERR_CANT_WRITE_BRUSH	32

    "No brush loaded",
#define	IERR_NO_BRUSH		33

    "Couldn't open clipboard.device",
#define	IERR_NO_CLIPBOARD	34

    "Requires a name",
#define	IERR_NO_NAME		35

    "Couldn't create %s",
#define	IERR_CANT_CREATE	36

    "Aborted by user",
#define	IERR_USER_ABORT		37

    "Monitor not available",
#define	IERR_NOMONITOR		38

    "New chipset not available",
#define	IERR_NOCHIPS		39

    "Not enough memory",
#define	IERR_NOMEM		40

    "Not enough chip memory",
#define	IERR_NOCHIPMEM		41

    "Public screen already open",
#define	IERR_PUBNOTUNIQUE	42

    "Display mode ID is unknown",
#define	IERR_UNKNOWNMODE	43

    "Unknown OpenScreen error",
#define	IERR_OPENSCREEN		44
    NULL
};

/* If you need special tags for a window, then attach them to the
 * window object, OBJ_Window. */
struct TagItem win_tags[] =
{
    {WA_SizeGadget, TRUE},
    {WA_MenuHelp, TRUE},
    {WA_MinWidth, 200},
    {WA_MinHeight, 90},
    {WA_InnerWidth, 200},
    {WA_InnerHeight, 90},
    {TAG_DONE,},
};

/* The following four tag arrays are GadTool tags for Window Environment
 * objects. */
STRPTR output_data[] =
{
    "C Image",
    "C Sprite",
    "Icon",
    NULL
};

STRPTR output2_data[] =
{
    "Image",
    NULL
};

struct TagItem output_tags[] =
{
    {GTCY_Labels, (LONG) output_data},
    {TAG_DONE,}
};

struct TagItem name_tags[] =
{
    {GTST_MaxChars, 30L},
    {TAG_DONE,},
};

/* These are the objects that are going to be within our main window.
 * Note that the window itself is an object, and receives the SAME name
 * as in the WindowEnvironment tag list, APSH_NameTag. */
struct Object objects[] =
{
    {&objects[1], 0, 0, OBJ_Window, NULL, NULL, NULL, "Main", 1L,
     {0, 0, 240, 75}, win_tags,},

    {&objects[2], 0, 0, OBJ_DblBevelOut, NULL, APSH_OBJF_DRAGGABLE, NULL, "Display", 2L,
     {8, 4, -8, -30},},

    {&objects[3], 0, 0, OBJ_String, SetNameID, NULL, NULL, "Name", 4L,
     {70, -26, 101, 16}, name_tags,},

    {NULL, 0, 0, OBJ_Cycle, SetModeID, NULL, NULL, "Mode", 3L,
     {70, -8, 101, 16}, output_tags,},
};

/* This is a Window Environment tag list.  It describes a window. */
struct TagItem mainenv[] =
{
    {APSH_NameTag, (ULONG) "Main"},
    {APSH_Objects, (ULONG) objects},
    {APSH_TTMenu, 12L},
    {APSH_WinAOpen, OpenMainID},
    {APSH_RefreshData, OpenMainID},
    {APSH_CloseWindow, QuitID},
    {TAG_DONE,}
};

struct TagItem icon_tags[] =
{
    {CGTA_MaxWidth, 80},
    {CGTA_MaxHeight, 40},
    {CGTA_LabelInfo, LABEL_CENTER},
    {CGTA_FrameInfo, FRAME_CENTER},
    {TAG_DONE,},
};

struct TagItem td_tags[] =
{
    {APSH_GTFlags, PLACETEXT_IN},
    {TAG_DONE,},
};

/* This is the object array for the About window. */
struct Object about[] =
{
    {&about[1], 0, 0, OBJ_Window, NULL, NULL, NULL, "About", 29L,
     {1, 1, 0, 0},},

    {&about[2], 0, 0, OBJ_Display, NULL, NULL, NULL, "Icon", 0L,
     {8, 4, 90, 44}, icon_tags,},

 /* Using the Label as the contents... */
    {&about[3], 0, 0, OBJ_Text, NULL, NULL, NULL, "Name", 5L,
     {116, 4, 350, 10}, td_tags,},

    {&about[4], 0, 0, OBJ_Text, NULL, NULL, NULL, "Copyright", 6L,
     {116, 14, 350, 10}, td_tags,},

    {&about[5], 0, 0, OBJ_Text, NULL, NULL, NULL, "Version", 7L,
     {116, 24, 350, 10}, td_tags,},

    {&about[6], 0, 0, OBJ_Text, NULL, NULL, NULL, "Author", 8L,
     {116, 34, 350, 10}, td_tags,},

  {NULL, 0, 0, OBJ_Button, NULL, APSH_OBJF_CLOSEWINDOW, NULL, "Continue", 30L,
   {-94, 46, 80, 12},},
};

/* This is the Window Environment tag array for the About window. */
struct TagItem aboutenv[] =
{
    {APSH_NameTag, (ULONG) "About"},	/* name to give to window */
    {APSH_Objects, (ULONG) about},	/* object list */
    {APSH_WinAOpen, OpenAboutID},
    {TAG_DONE,}
};

/* Define all the libraries that your application needs.  Note that
 * extern libraries are defined in the link files. */
extern ULONG SysBase, AppShellBase, DOSBase;
struct Library *AslBase;
struct Library *GadToolsBase;
struct Library *GfxBase;
struct Library *IconBase;
struct Library *UtilityBase;
struct Library *LayersBase;
struct Library *AppObjectsBase;
struct Library *IFFParseBase;
struct IntuitionBase *IntuitionBase;

/* This tag array is used to open and close the shared system libraries
 * needed by our application. */
struct TagItem Our_Libs[] =
{
    {APSH_LibVersion, 36L},
    {APSH_LibStatus, APSH_REQUIRED},
    {APSH_ASL, (ULONG) & AslBase},
    {APSH_GadTools, (ULONG) & GadToolsBase},
    {APSH_Gfx, (ULONG) & GfxBase},
    {APSH_Icon, (ULONG) & IconBase},
    {APSH_Intuition, (ULONG) & IntuitionBase},
    {APSH_Utility, (ULONG) & UtilityBase},
    {APSH_Layers, (ULONG) & LayersBase},
    {APSH_IFF, (ULONG) & IFFParseBase},
    {APSH_LibName, (ULONG) "appobjects.library"},
    {APSH_LibBase, (ULONG) & AppObjectsBase},
    {TAG_DONE,}
};

/* These tags describe the Intuition user interface. */
struct TagItem Handle_IDCMP[] =
{
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

struct TagItem appwinenv[] =
{
    {APSH_NameTag, (ULONG) "Main"},	/* Window to make into AppWindow */
    {APSH_AppDDrop, DropIconID},/* function after drop */
    {APSH_CmdFlags, APSH_WBF_NOLIST},	/* Don't add the icons to any list */
    {TAG_DONE,}
};

/* These tags describe the Workbench user interface. */
struct TagItem Handle_WB[] =
{
    {APSH_AppWindowEnv, (ULONG) appwinenv},
    {APSH_Rating, APSH_OPTIONAL},
    {TAG_DONE,}
};

/* These tags describe the Simple IPC user interface. */
struct TagItem Handle_SIPC[] =
{
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

/* These tags are used for the Asynchronous process message handler. */
struct TagItem Handle_Tool[] =
{
    {APSH_Status, APSHP_INACTIVE},
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

/* These tags describe the ARexx user interface.  The default macro file
 * extension is build. */
struct TagItem Handle_AREXX[] =
{
    {APSH_Extens, (ULONG) "ilbmer"},
    {APSH_Rating, APSH_OPTIONAL},
    {TAG_DONE,}
};

/* These tags describe the Command Shell user interface. */
struct TagItem Handle_DOS[] =
{
    {APSH_Status, APSHP_INACTIVE},
    {APSH_Rating, APSH_OPTIONAL},
    {TAG_DONE,}
};

/* This is the Application Description tag list.  It is used to describe our
 * application's multiple user interfaces. */
struct TagItem Our_App[] =
{
    {APSH_BaseName, (ULONG) APPBASE},
    {APSH_AppName, (ULONG) APPNAME},
    {APSH_AppVersion, (ULONG) APPVERS},
    {APSH_AppCopyright, (ULONG) APPCOPY},
    {APSH_AppAuthor, (ULONG) APPAUTH},
    {APSH_UserDataSize, sizeof (struct AppData)},
    {APSH_FuncTable, (ULONG) FTable},
    {APSH_DefText, (ULONG) app_deftext},
    {APSH_OpenLibraries, (ULONG) Our_Libs},
    {APSH_AddSIPC_UI, (ULONG) Handle_SIPC},
    {APSH_AddTool_UI, (ULONG) Handle_Tool},
    {APSH_AddARexx_UI, (ULONG) Handle_AREXX},
    {APSH_AddIntui_UI, (ULONG) Handle_IDCMP},
    {APSH_AddCmdShell_UI, (ULONG) Handle_DOS},
    {APSH_AddWB_UI, (ULONG) Handle_WB},
    {APSH_AppInit, CInitID},
    {APSH_AppExit, CExitID},
    {APSH_SIG_E, ClipChangeID},
    {TAG_DONE,}
};

/* handle messages between function handlers */
BOOL HandlerFunc (struct AppInfo * ai, ULONG tags,...)
{
    return (HandlerFuncA (ai, (struct TagItem *) & tags));
}

/* get handler data */
APTR HandlerData (struct AppInfo * ai, ULONG tags,...)
{
    return (HandlerDataA (ai, (struct TagItem *) & tags));
}

LONG
fprint (BPTR fh, UBYTE * format, LONG data,...)
{
    LONG VFPrintf (BPTR fh, UBYTE * format, LONG * argarray);

    return (VFPrintf (fh, format, (ULONG *) & data));
}

BOOL CopyImage (struct Image * src, struct Image * dst)
{
    BOOL retval = FALSE;

    if (src && dst)
    {
	int msize;

	/* Copy the Image structure */
	CopyMem (src, dst, sizeof (struct Image));

	/* Don't go pointing off into nowhere land */
	dst->NextImage = NULL;

	/* Compute the size of the image data needed */
	msize = RASSIZE (src->Width, src->Height) * src->Depth;

	/* Allocate the image data */
	if (dst->ImageData = (USHORT *)
	    AllocVec (msize, MEMF_CLEAR | MEMF_CHIP))
	{
	    /* Copy image data */
	    CopyMem (src->ImageData, dst->ImageData, msize);

	    /* Indicate success */
	    retval = TRUE;
	}
    }

    return (retval);
}

VOID FreeImage (struct Image * im)
{

    if (im && im->ImageData)
    {
	FreeVec (im->ImageData);
	im->ImageData = NULL;
    }
}

/* Get the file requester set up properly.  Returns 0 if no file node
 * present, returns > 0 if there is a file node.  */
LONG FixFileAndPath (struct FileRequester *rf, STRPTR name)
{
    LONG count = 0L;

    if (rf)
    {
	if (name)
	{
	    stcgfp (rf->rf_Dir, name);
	    count = (LONG) stcgfn (rf->rf_File, name);
	}
	else
	{
	    WORD dlen = (strlen (rf->rf_Dir) - 1);

	    if ((dlen > 0) && (rf->rf_Dir[dlen] == '/'))
	    {
		rf->rf_Dir[dlen] = 0;
	    }
	}
    }

    return (count);
}

VOID
ImageToC (BPTR fh, STRPTR name, struct Image * i)
{
    int w, h, d, c, t, cp;
    register int j, k;
    USHORT *data;

    /* Image data portion */
    fprint (fh, "UWORD chip %s_idata[] =\n{\n", (LONG) name);
    w = i->Width;
    h = i->Height;
    d = i->Depth;
    c = ((w + 15) / 16) * h;
    cp = 0;
    data = i->ImageData;
    for (k = 0; k < d; k++)
    {
	if (k > 0)
	    fprint (fh, "\n", NULL);
	fprint (fh, "/* Plane %ld */\n    ", (LONG) k);
	t = 0;
	for (j = 0; j < c; j++)
	{
	    if (t > 7)
	    {
		fprint (fh, "\n    ", NULL);
		t = 0;
	    }
	    t++;
	    fprint (fh, "0x%04lx,", (LONG) data[cp++]);
	}
    }
    fprint (fh, "\n};\n\n", NULL);

    /* Image structure portion */
    fprint (fh, "struct Image %s_data =\n{\n", (LONG) name);
    fprint (fh, "    %ld, %ld,			/* Upper left corner */\n",
	    (LONG) i->LeftEdge, (LONG) i->TopEdge);
    fprint (fh, "    %ld, %ld, %ld,			/* Width, Height, Depth */\n",
	    (LONG) w, (LONG) h, (LONG) d);
    fprint (fh, "    %s_idata,		/* Image data */\n", (LONG) name);
    fprint (fh, "    0x%04lx, 0x%04lx,		/* PlanePick, PlaneOnOff */\n",
	    (LONG) i->PlanePick, (LONG) i->PlaneOnOff);
    fprint (fh, "    NULL			/* Next image */\n", NULL);
    fprint (fh, "};\n\n", NULL);
}

VOID SpriteToC (BPTR fh, STRPTR name, struct PointerPref * pp)
{
    WORD i, j, num;
    WORD gw, gh, gx, gy;

    /* Get the sizes */
    gw = pp->pp_Width;
    gh = pp->pp_Height;
    gx = pp->pp_XOffset;
    gy = pp->pp_YOffset;
    num = (gh * 2) + 4;

    /* Pointer header */
    fprint (fh, "UWORD chip %s_sdata[] =\n{\n", (LONG) name);

    /* Header */
    fprint (fh, "    0x0000, 0x0000,\n\n", NULL);

    /* Body */
    for (i = 2; i < ((gh * 2) + 2); i++)
    {
	j = i + 1;

	fprint (fh, "    0x%04lx, 0x%04lx,\n",
		(LONG) pp->pp_PData[i], (LONG) pp->pp_PData[j]);
	i++;
    }

    /* Tail */
    fprint (fh, "\n    0x0000, 0x0000\n};\n\n", NULL);

    /* Set?Pointer function */
    fprint (fh, "VOID Set%sPointer (struct Window *win)\n{\n", (LONG) name);
    fprint (fh, "    SetPointer (win, %s_sdata, %ld, 16, %ld, %ld);\n",
	    (LONG) name, (LONG) gh, (LONG) gx, (LONG) gy);

    fprint (fh, "}\n", NULL);
}

UWORD
Pick (UWORD depth)
{
    UWORD ret;

    switch (depth)
    {
	case 1:
	    ret = 1;
	    break;

	case 2:
	    ret = 3;
	    break;

	case 3:
	    ret = 7;
	    break;

	case 4:
	    ret = 15;
	    break;

	case 5:
	    ret = 31;
	    break;

	case 6:
	    ret = 31;
	    break;

	default:
	    ret = 0;
	    break;
    }

    return (ret);
}

BOOL
MakeImage (struct Image * im, ILBM * ir)
{
    BOOL retval = FALSE;

    if (im && ir)
    {
	struct BitMap *bm = &(ir->ir_BMap);
	UWORD *wp, *data;
	ULONG msize;
	int p, j, nb, nwords, total = 0;
	int w, h, d;

	w = ir->ir_Width;
	h = ir->ir_Height;
	d = ir->ir_Depth;

	total = 0;
	nwords = (bm->BytesPerRow / 2) * bm->Rows;
	msize = (ULONG) ((bm->BytesPerRow * bm->Rows) * (bm->Depth) / 2) * (sizeof (USHORT));

	if (data = (UWORD *) AllocVec (msize, MEMF_CLEAR | MEMF_CHIP))
	{
	    for (p = 0; p < bm->Depth; ++p)
	    {
		wp = (UWORD *) bm->Planes[p];

		for (j = 0; j < bm->Rows; j++, wp += (bm->BytesPerRow >> 1))
		{
		    for (nb = 0; nb < (bm->BytesPerRow) >> 1; nb++)
		    {
			data[total++] = *(wp + nb);
		    }
		}
	    }

	    /* Fill out the image structure */
	    im->LeftEdge = 0;
	    im->TopEdge = 0;
	    im->Width = w;
	    im->Height = h;
	    im->Depth = bm->Depth;
	    im->PlanePick = Pick (bm->Depth);
	    im->PlaneOnOff = 1;
	    im->ImageData = data;

	    /* Success! */
	    retval = TRUE;
	}
    }
    return (retval);
}

VOID FreeGraphic (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;

    if (ad)
    {
	if (ad->ad_Screen)
	{
	    CloseScreen (ad->ad_Screen);
	    ad->ad_Screen = NULL;
	}

	/* Free the image structure */
	FreeImage (&(ad->ad_Image));

	/* Free the pointer */
	if (ad->ad_Ptr)
	{
	    FreePointer (ad->ad_Ptr);
	    ad->ad_Ptr = NULL;
	}
	/* Free the ILBM record */
	if (ad->ad_IR)
	{
	    FreeILBM (ad->ad_IR);
	    ad->ad_IR = NULL;
	}
    }
}

/* This function gets called after all the user interfaces have been
 * initialized, and before the AppShell enters the main event processing
 * loop.  It is specified by the APSH_AppInit tag in the Application
 * Description tag array.  Note that the AppData structure has been
 * allocated for us, due to the APSH_UserDataSize tag. */

VOID
CInitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    extern struct TagItem mainenv[];
    struct DiskObject *dob;
    WORD i;

    /* Allocate the file requesters */
    for (i = 0; i < FR_MAX; i++)
    {
	ad->ad_FR[i] = AllocAslRequest (ASL_FileRequest, NULL);
    }

    /* Unset the file requester positioning */
    ad->ad_Width = (-1);

    /* Do we want to save icons? */
    ad->ad_SaveIcons = TRUE;

    /* Do we have a tool icon? */
    if (dob = ai->ai_ProgDO)
    {
	LONG hold;
	UBYTE *tmp;

	/* Get the clipboard unit to use */
	if (tmp = FindToolType (dob->do_ToolTypes, "UNIT"))
	{
	    if (StrToLong (tmp, &hold) > 0L)
	    {
		ad->ad_Unit = hold;
	    }
	}

	/* Get the file requester topedge */
	if (tmp = FindToolType (dob->do_ToolTypes, "FRTOPEDGE"))
	{
	    if (StrToLong (tmp, &hold) > 0L)
	    {
		ad->ad_TopEdge = (WORD) hold;
	    }
	}

	/* Get the file requester leftedge */
	if (tmp = FindToolType (dob->do_ToolTypes, "FRLEFTEDGE"))
	{
	    if (StrToLong (tmp, &hold) > 0L)
	    {
		ad->ad_LeftEdge = (WORD) hold;
	    }
	}

	/* Get the file requester width */
	if (tmp = FindToolType (dob->do_ToolTypes, "FRWIDTH"))
	{
	    if (StrToLong (tmp, &hold) > 0L)
	    {
		ad->ad_Width = (WORD) hold;
	    }
	}

	/* Get the file requester height */
	if (tmp = FindToolType (dob->do_ToolTypes, "FRHEIGHT"))
	{
	    if (StrToLong (tmp, &hold) > 0L)
	    {
		ad->ad_Height = (WORD) hold;
	    }
	}

	/* Do we have a default brush drawer? */
	if (tmp = FindToolType (dob->do_ToolTypes, "ILBMDRAWER"))
	{
	    strcpy (ad->ad_FR[FR_ILBM]->rf_Dir, tmp);
	}

	/* Do we have a default source drawer? */
	if (tmp = FindToolType (dob->do_ToolTypes, "SRCDRAWER"))
	{
	    strcpy (ad->ad_FR[FR_C]->rf_Dir, tmp);
	}

	/* Do we have a default structure label? */
	if (tmp = FindToolType (dob->do_ToolTypes, "LABEL"))
	{
	    strncpy (ad->ad_Struct, tmp, 31);
	}

	/* Save icons? */
	if (tmp = FindToolType (dob->do_ToolTypes, "NOICONS"))
	{
	    ad->ad_SaveIcons = FALSE;
	}
    }

    /* Allocate clipboard manager */
    if (ad->ad_CM = AllocClip ())
    {
	/* Start the change hook */
	StartClipChangeHook (ad->ad_CM, ad->ad_Unit);
    }

    /* open the Main window */
    HandlerFunc (ai,
		 APSH_Handler, "IDCMP",
		 APSH_Command, APSH_MH_OPEN,
		 APSH_WindowEnv, (ULONG) mainenv,
		 TAG_DONE);

    /* Initialize the clipboard stuff */
    ClipChangeFunc (h, ai, af);

    /* Set the function attributes */
    PerfFunc (ai, NULL, "disable display saveas copy setname setmode savesettings", NULL);
}

/* This function gets called after the main event processing loop exits,
 * and before deallocating all the user interfaces.  It is specified by the
 * APSH_AppExit tag in the Application Description tag array.  Note that the
 * AppData structure which has been allocated for us, is deallocated by
 * AppShell after freeing the user interfaces. */

VOID
CExitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    WORD i;

    if (ad)
    {
	/* Free any existing graphics */
	FreeGraphic (h, ai, af);

	/* Free the clipboard manager */
	if (ad->ad_CM)
	{
	    FreeClip (ad->ad_CM);
	}

	/* Free the file requesters */
	for (i = 0; i < FR_MAX; i++)
	{
	    /* Do we have a file requester? */
	    if (ad->ad_FR[i])
	    {
		/* Free the file requester */
		FreeFileRequest (ad->ad_FR[i]);
		ad->ad_FR[i] = NULL;
	    }
	}
    }
}

/* This function is called every time the Main window is opened.  It
 * was specified by using the APSH_WinAOpen tag in the Main Window
 * Environemt tag array.  I'm also 'cheating' and calling it everytime
 * I want to refresh the window and graphics. */

VOID
OpenMainFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct Image *im = &(ad->ad_Image);
    struct Window *win;
    struct Gadget *gad;

    /* Do we have an image? */
    if (im->ImageData)
    {
	/* Set the function attributes */
	PerfFunc (ai, NULL, "enable force display saveas cut copy erase setname setmode", NULL);
    }
    /* No image */
    else
    {
	/* Set the function attributes */
	PerfFunc (ai, NULL, "disable display saveas cut copy erase setname setmode", NULL);
    }

    /* Update the imagry */
    if (APSHGetGadgetInfo (ai, "Main", "Display",
			   (ULONG *) & win, (ULONG *) & gad))
    {
	/* Do we have an image? */
	if (im->ImageData)
	{
	    /* Let the gadget know that there is an image */
	    SetGadgetAttrs (gad, win, NULL, GA_LABELIMAGE, im, TAG_DONE);
	}
	/* No image */
	else
	{
	    /* Let the gadget know that there isn't an image */
	    SetGadgetAttrs (gad, win, NULL, GA_LABELIMAGE, NULL, TAG_DONE);
	}
    }

    /* Update the structure name */
    if (APSHGetGadgetInfo (ai, "Main", "Name",
			   (ULONG *) & win, (ULONG *) & gad))
    {
	/* update the string gadget if there is one */
	GT_SetGadgetAttrs (gad, win, NULL,
			   GTST_String, ad->ad_Struct,
			   TAG_DONE);
    }

    /* Update the mode gadget */
    if (APSHGetGadgetInfo (ai, "Main", "Mode", (ULONG *) & win, (ULONG *) & gad))
    {
	/* Update the string gadget */
	GT_SetGadgetAttrs (gad, win, NULL, GTCY_Active, ad->ad_Mode, TAG_DONE);
    }
}

/*

RequestFile
WINDOW/K,SAVE/S,PROMPT/K,PATTERN/K,FILENAME/F

RequestFile opens the file requester and prompts the user to select a
file.

    WINDOW
    Specify the name of the window to open relative to.  Defaults to
    "Main".

    SAVE
    Indicate the Save file requester is to be used.

    PROMPT
    Allows you to specify the text which appears in the title bar of the
    file requester.

    PATTERN
    Specifies the pattern string to include in the file requester's
    Pattern gadget.

    FILENAME
    Specifies the directory path to include in the file requester's
    Drawer and File gadgets.

The resulting filename is returned in the ARexx RESULT variable. If the
user cancels the file requester, an error is generated.

This command can only be used through the ARexx interface.

    {"RequestFile", RequestFileFunc, RequestFileID,
     "WINDOW/K,SAVE/S,PROMPT/K,PATTERN/K,FILENAME/F", 5L, NULL,},

 */

VOID
RequestFileFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
}


VOID
OpenFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct FileRequester *rf;
    struct Funcs *f;
    STRPTR name = NULL;
    BOOL cancel = FALSE;

    /* set the wait pointer */
    APSHSetWaitPointer (ai, NULL);

    /* See if we have a parsed command */
    if (f = af->af_FE)
    {
	/* See if they passed a file name */
	name = (STRPTR) f->fe_Options[0];
    }
    else
    {
	/* See if they passed a name with a tag */
	name = (STRPTR) GetTagData (APSH_NameTag, NULL, attrs);
    }

    /* Do we need to prompt for a name? */
    if (!(name))
    {
	/* Do we have a file requester? */
	if (rf = ad->ad_FR[FR_ILBM])
	{
	    struct TagItem tg[10];
	    struct Window *win = NULL;

	    /* Get the window pointer */
	    APSHGetWindowInfo (ai, "Main", (ULONG *) & win);

	    /* Initialize the file requester position */
	    if (ad->ad_Width == (-1))
	    {
		ad->ad_LeftEdge = (win) ? (win->BorderLeft + 1) : 0;
		ad->ad_TopEdge = (win) ? win->BorderTop : 0;
		ad->ad_Width = 320;
		ad->ad_Height = MAX (((win) ? (win->Height - (win->BorderTop + win->BorderBottom)) : 150), 140);
	    }

	    /* Set the file requester position */
	    tg[4].ti_Tag = ASL_LeftEdge;
	    tg[4].ti_Data = (ULONG) (win) ? (win->LeftEdge + ad->ad_LeftEdge) : ad->ad_LeftEdge;
	    tg[5].ti_Tag = ASL_TopEdge;
	    tg[5].ti_Data = (ULONG) (win) ? (win->TopEdge + ad->ad_TopEdge) : ad->ad_TopEdge;
	    tg[6].ti_Tag = ASL_Width;
	    tg[6].ti_Data = (ULONG) ad->ad_Width;
	    tg[7].ti_Tag = ASL_Height;
	    tg[7].ti_Data = (ULONG) ad->ad_Height;

	    tg[0].ti_Tag = ASL_Hail;
	    tg[0].ti_Data = (ULONG) "Select File to Open";
	    tg[1].ti_Tag = ASL_FuncFlags;
	    tg[1].ti_Data = NULL;
	    tg[2].ti_Tag = ASL_OKText;
	    tg[2].ti_Data = (ULONG) "Open";
	    tg[3].ti_Tag = ASL_Window;
	    tg[3].ti_Data = (ULONG) win;
	    tg[8].ti_Tag = ASL_Pattern;
	    tg[8].ti_Data = (ULONG) "~(#?.info)";
	    tg[9].ti_Tag = TAG_DONE;

	    /* Request the name */
	    if (AslRequest (rf, tg))
	    {
		/* Fix the entries */
		FixFileAndPath (rf, NULL);
		strcpy (ad->ad_Tmp, rf->rf_Dir);
		AddPart (ad->ad_Tmp, rf->rf_File, 510);

		/* See if a name was entered */
		if (FixFileAndPath (rf, ad->ad_Tmp))
		{
		    name = ad->ad_Tmp;
		}
	    }
	    else
	    {
		/* User aborted requester */
		ai->ai_Pri_Ret = RETURN_WARN;
		ai->ai_Sec_Ret = IERR_USER_ABORT;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, IERR_USER_ABORT, NULL);

		/* Canceled */
		cancel = TRUE;
	    }

	    /* Remember where they moved it */
	    ad->ad_LeftEdge = rf->rf_LeftEdge - ((win) ? win->LeftEdge : 0);
	    ad->ad_TopEdge = rf->rf_TopEdge - ((win) ? win->TopEdge : 0);
	    ad->ad_Width = rf->rf_Width;
	    ad->ad_Height = rf->rf_Height;
	}
    }

    /* Do we have a file name */
    if (!cancel && name)
    {
	/* Free any existing graphics */
	FreeGraphic (h, ai, af);

	/* Update the file name */
	strcpy (ad->ad_Name, name);

	/* Update the file requester gadgets */
	FixFileAndPath (rf, ad->ad_Name);

	/* Read the ILBM record into memory */
	if (ad->ad_IR = ReadILBM (NULL, ad->ad_Name, NULL))
	{
	    /* Make an Image out of the bitmap */
	    MakeImage (&(ad->ad_Image), ad->ad_IR);

	    /* Make a pointer out of the bitmap */
	    ad->ad_Ptr = MakePointer (ad->ad_IR);

	}
	else
	{
	    /* Notify user unable to load brush */
	    ai->ai_Pri_Ret = RETURN_ERROR;
	    ai->ai_Sec_Ret = IERR_CANT_LOAD_BRUSH;
	    ai->ai_TextRtn =
	      PrepText (ai, APSH_USER_ID, IERR_CANT_LOAD_BRUSH, NULL);
	}

	/* Refresh the display */
	OpenMainFunc (h, ai, af);
    }
    else if (!cancel)
    {
	/* No name given */
	ai->ai_Pri_Ret = RETURN_ERROR;
	ai->ai_Sec_Ret = IERR_NO_NAME;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, IERR_NO_NAME, NULL);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

VOID
SaveAsFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct FileRequester *rf;
    BOOL cancel = FALSE;

    /* set the wait pointer */
    APSHSetWaitPointer (ai, NULL);

    /* Do we have a window? */
    if (ad->ad_Image.ImageData)
    {
	struct Funcs *f;
	STRPTR name;

	/* See if we have a parsed command */
	if (f = af->af_FE)
	{
	    /* See if they passed a file name */
	    name = (STRPTR) f->fe_Options[0];
	}
	else
	{
	    /* See if they passed a name with a tag */
	    name = (STRPTR) GetTagData (APSH_NameTag, NULL, attrs);
	}

	/* Do we need to prompt for a name? */
	if (!(name))
	{
	    /* Do we have a file requester? */
	    if (rf = ad->ad_FR[FR_C])
	    {
		struct TagItem tg[10];
		struct Window *win;

		/* Get the window pointer */
		APSHGetWindowInfo (ai, "Main", (ULONG *) & win);

		/* Initialize the file requester position */
		if (ad->ad_Width == (-1))
		{
		    ad->ad_LeftEdge = (win) ? (win->BorderLeft + 1) : 0;
		    ad->ad_TopEdge = (win) ? win->BorderTop : 0;
		    ad->ad_Width = 320;
		    ad->ad_Height = MAX (((win) ? (win->Height - (win->BorderTop + win->BorderBottom)) : 150), 140);
		}

		/* Set the file requester position */
		tg[4].ti_Tag = ASL_LeftEdge;
		tg[4].ti_Data = (ULONG) (win) ? (win->LeftEdge + ad->ad_LeftEdge) : ad->ad_LeftEdge;
		tg[5].ti_Tag = ASL_TopEdge;
		tg[5].ti_Data = (ULONG) (win) ? (win->TopEdge + ad->ad_TopEdge) : ad->ad_TopEdge;
		tg[6].ti_Tag = ASL_Width;
		tg[6].ti_Data = (ULONG) ad->ad_Width;
		tg[7].ti_Tag = ASL_Height;
		tg[7].ti_Data = (ULONG) ad->ad_Height;

		tg[0].ti_Tag = ASL_Hail;
		tg[0].ti_Data = (ULONG) "Select File to Save";
		tg[1].ti_Tag = ASL_FuncFlags;
		tg[1].ti_Data = FILF_SAVE;
		tg[2].ti_Tag = ASL_OKText;
		tg[2].ti_Data = (ULONG) "Save";
		tg[3].ti_Tag = ASL_Window;
		tg[3].ti_Data = (ULONG) win;
		tg[8].ti_Tag = ASL_Pattern;
		tg[8].ti_Data = (ULONG) "~(#?.info)";
		tg[9].ti_Tag = TAG_DONE;

		/* Request the name */
		if (AslRequest (rf, tg))
		{
		    /* Fix the entries */
		    FixFileAndPath (rf, NULL);
		    strcpy (ad->ad_Tmp, rf->rf_Dir);
		    AddPart (ad->ad_Tmp, rf->rf_File, 510);

		    /* Did they enter a file name? */
		    if (FixFileAndPath (rf, ad->ad_Tmp))
		    {
			name = ad->ad_Tmp;
		    }
		}
		else
		{
		    /* User aborted requester */
		    ai->ai_Pri_Ret = RETURN_WARN;
		    ai->ai_Sec_Ret = IERR_USER_ABORT;
		    ai->ai_TextRtn =
		      PrepText (ai, APSH_USER_ID, IERR_USER_ABORT, NULL);

		    /* Indicate canceled */
		    cancel = TRUE;
		}

		/* Remember where they moved it */
		ad->ad_LeftEdge = rf->rf_LeftEdge - ((win) ? win->LeftEdge : 0);
		ad->ad_TopEdge = rf->rf_TopEdge - ((win) ? win->TopEdge : 0);
		ad->ad_Width = rf->rf_Width;
		ad->ad_Height = rf->rf_Height;
	    }
	}

	/* Do we have a name to save to? */
	if (!cancel && name)
	{
	    BPTR fh = NULL;

	    /* Update the file requester */
	    strcpy (ad->ad_Name, name);
	    FixFileAndPath (rf, ad->ad_Name);

	    /* See if we should be saving an icon */
	    if (ad->ad_Mode == ICON)
	    {
		struct Image *nim = &(ad->ad_Image);
		struct Image *oim1 = NULL;
		struct Image *oim2 = NULL;
		struct DiskObject *dob;
		struct Gadget *gad;
		USHORT flags;

		/* Get a template icon */
		if (dob = GetDefDiskObject (WBPROJECT))
		{
		    /* Cache the pointer to the gadget */
		    gad = &(dob->do_Gadget);

		    /* Remember the original imagry */
		    oim1 = (struct Image *) gad->GadgetRender;
		    oim2 = (struct Image *) gad->SelectRender;
		    flags = gad->Flags;

		    /* Put our stuff there */
		    gad->Width = nim->Width;
		    gad->Height = nim->Height;
		    gad->GadgetRender = (APTR) nim;
		    gad->SelectRender = NULL;
		    gad->Flags &= ~GFLG_GADGHIGHBITS;
		    gad->Flags |= (GFLG_GADGIMAGE | GFLG_GADGHCOMP);

		    /* Save the icon */
		    PutDiskObject (ad->ad_Name, dob);

		    /* Restore the original image */
		    gad->GadgetRender = (APTR) oim1;
		    gad->SelectRender = (APTR) oim2;
		    gad->Flags = flags;

		    /* Free the temporary icon */
		    FreeDiskObject (dob);
		}
	    }
	    /* Open the output file */
	    else if (fh = Open (ad->ad_Name, MODE_NEWFILE))
	    {
		if (ad->ad_Mode == IMAGE)
		{
		    /* Save the image as C source */
		    ImageToC (fh, ad->ad_Struct, &(ad->ad_Image));
		}
		else if (ad->ad_Mode == SPRITE)
		{
		    /* Save the pointer as C source */
		    SpriteToC (fh, ad->ad_Struct, ad->ad_Ptr);
		}

		/* Close the file */
		Close (fh);

		/* Clear the execute file attribute */
		SetProtection (ad->ad_Name, FIBF_EXECUTE);

		/* Do we save icons? */
		if (ad->ad_SaveIcons)
		{
		    /*
		     * This is a bit silly, I just did it because I could.
		     * Remember MORE uses this Env variable also.
		     */
		    if (GetVar ("EDITOR", ad->ad_Tmp, 510, NULL))
		    {
			Listing.do_DefaultTool = ad->ad_Tmp;
		    }

		    /* Save the icon */
		    PutDiskObject (ad->ad_Name, &Listing);
		}
	    }
	    else
	    {
		/* Unable to create the new file */
		ai->ai_Pri_Ret = RETURN_ERROR;
		ai->ai_Sec_Ret = IERR_CANT_CREATE;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, IERR_CANT_CREATE, (int) ad->ad_Name);
	    }
	}
	else if (!cancel)
	{
	    /* No name given */
	    ai->ai_Pri_Ret = RETURN_ERROR;
	    ai->ai_Sec_Ret = IERR_NO_NAME;
	    ai->ai_TextRtn =
	      PrepText (ai, APSH_USER_ID, IERR_NO_NAME, NULL);
	}
    }
    else
    {
	/* No image loaded */
	ai->ai_Pri_Ret = RETURN_ERROR;
	ai->ai_Sec_Ret = IERR_NO_BRUSH;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, IERR_NO_BRUSH, NULL);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

VOID
AboutFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct Window *win;
    struct Gadget *gad;

    /* open the Hotkey editor */
    HandlerFunc (ai,
		 APSH_Handler, "IDCMP",
		 APSH_Command, APSH_MH_OPEN,
		 APSH_WindowEnv, (ULONG) aboutenv,
		 TAG_DONE);

    /* See if we have a tool icon */
    if (ai->ai_ProgDO)
    {
	struct Image *im;

	im = (struct Image *) (ai->ai_ProgDO->do_Gadget.GadgetRender);

	/* Update the imagry */
	if (APSHGetGadgetInfo (ai, "About", "Icon",
			       (ULONG *) & win, (ULONG *) & gad))
	{
	    /* Let the gadget know that there is an image */
	    SetGadgetAttrs (gad, win, NULL,
			    GA_LABELIMAGE, im, TAG_DONE);
	}
    }
    else
    {
	/* Update the imagry to show the program name */
	if (APSHGetGadgetInfo (ai, "About", "Icon",
			       (ULONG *) & win, (ULONG *) & gad))
	{
	    /* Let the gadget know that there is an image */
	    SetGadgetAttrs (gad, win, NULL,
			    GA_TEXT, ai->ai_ProgName, TAG_DONE);
	}
    }
}

VOID
OpenAboutFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
}

VOID
QuitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{

    /* We're all done */
    ai->ai_Done = TRUE;
}

VOID
CutFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{

    /* Set the wait pointer */
    APSHSetWaitPointer (ai, NULL);

    /* Copy the contents to the clipboard */
    CopyFunc (h, ai, af);

    /* Able to copy? */
    if (ai->ai_Pri_Ret == RETURN_OK)
    {
	/* Erase the image */
	EraseFunc (h, ai, af);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

VOID
CopyFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct IFFHandle *iff;
    ULONG type, id;

    /* Just to open the clipboard unit */
    QueryClip (ad->ad_CM, ad->ad_Unit, (ULONG *) & type, (ULONG *) & id);

    /* Get a handle on the IFF */
    if (iff = ad->ad_CM->cm_Unit[ad->ad_Unit])
    {
	if (ad->ad_IR)
	{
	    if (!(PutILBM (ad->ad_CM->cm_Unit[ad->ad_Unit], ad->ad_IR)))
	    {
		/* Notify user unable to write brush */
		ai->ai_Pri_Ret = RETURN_ERROR;
		ai->ai_Sec_Ret = IERR_CANT_LOAD_BRUSH;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, IERR_CANT_WRITE_BRUSH, NULL);
	    }
	}
	else
	{
	    /* Notify user no brush available */
	    ai->ai_Pri_Ret = RETURN_ERROR;
	    ai->ai_Sec_Ret = IERR_NO_BRUSH;
	    ai->ai_TextRtn =
	      PrepText (ai, APSH_USER_ID, IERR_NO_BRUSH, NULL);
	}
    }
    else
    {
	/* Notify user unable to open clipboard */
	ai->ai_Pri_Ret = RETURN_FAIL;
	ai->ai_Sec_Ret = IERR_NO_CLIPBOARD;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, IERR_NO_CLIPBOARD, NULL);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

VOID
PasteFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct IFFHandle *iff;
    ULONG type, id;

    /* Set the busy pointer */
    APSHSetWaitPointer (ai, NULL);

    /* See if there is an ILBM in the clipboard */
    QueryClip (ad->ad_CM, ad->ad_Unit, (ULONG *) & type, (ULONG *) & id);

    /* Get a handle on the IFF */
    if (iff = ad->ad_CM->cm_Unit[ad->ad_Unit])
    {
	/* Do we have an ILBM in there? */
	if (type == ID_ILBM)
	{
	    struct Window *win = NULL;

	    /* Get a pointer to the window */
	    APSHGetWindowInfo (ai, "Main", (ULONG *) & win);

	    /* Free any existing graphics */
	    FreeGraphic (h, ai, af);

	    /* Read the ILBM record into memory */
	    if (ad->ad_IR = GetILBM (ad->ad_CM->cm_Unit[ad->ad_Unit]))
	    {
		/* Make an Image out of the bitmap */
		MakeImage (&(ad->ad_Image), ad->ad_IR);

		/* Make a pointer out of the bitmap */
		ad->ad_Ptr = MakePointer (ad->ad_IR);
	    }
	    else
	    {
		/* Couldn't read brush from clipboard */
		ai->ai_Pri_Ret = RETURN_ERROR;
		ai->ai_Sec_Ret = IERR_NO_BRUSH;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, IERR_CANT_LOAD_BRUSH, NULL);
	    }

	    /* Redraw the graphic */
	    OpenMainFunc (h, ai, af);
	}
	else
	{
	    /* Tell user it isn't an ILBM in there... */
	    ai->ai_Pri_Ret = RETURN_ERROR;
	    ai->ai_Sec_Ret = IERR_NO_BRUSH;
	    ai->ai_TextRtn =
	      PrepText (ai, APSH_USER_ID, IERR_NO_BRUSH, NULL);
	}
    }
    else
    {
	/* Notify user unable to open clipboard */
	ai->ai_Pri_Ret = RETURN_FAIL;
	ai->ai_Sec_Ret = IERR_NO_CLIPBOARD;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, IERR_NO_CLIPBOARD, NULL);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

VOID
EraseFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{

    /* Set the busy pointer */
    APSHSetWaitPointer (ai, NULL);

    /* Free any existing graphics */
    FreeGraphic (h, ai, af);

    /* Redraw the graphic */
    OpenMainFunc (h, ai, af);

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

VOID ClipChangeFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    ULONG type, id;

    /* See what is in the clipboard */
    QueryClip (ad->ad_CM, ad->ad_Unit, (ULONG *) & type, (ULONG *) & id);

    /* What type are the contents? */
    if (type == ID_ILBM)
    {
	/* Enable the Paste function no matter what */
	PerfFunc (ai, NULL, "enable paste force", NULL);
    }
    else
    {
	/* Disable the Paste function */
	PerfFunc (ai, NULL, "disable paste", NULL);
    }
}

/* This gets called when a Workbench or AppShell icon is dropped on it */
VOID
DropIconFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct DiskObject *dob = NULL;
    struct Image *im = NULL;
    struct ObjectNode *on;
    struct WBArg *wbarg;

    if (wbarg = (struct WBArg *) GetTagData (APSH_WBArg, NULL, attrs))
    {
	BPTR lock;

	/* Go to the directory represented by the icon */
	if (lock = CurrentDir (wbarg->wa_Lock))
	{
	    /* Get the name of the directory */
	    NameFromLock (wbarg->wa_Lock, ad->ad_Tmp, 510);
	    AddPart (ad->ad_Tmp, wbarg->wa_Name, 510);

	    /* Free any existing graphics */
	    FreeGraphic (h, ai, af);

	    /* Read the ILBM record into memory */
	    if (ad->ad_IR = ReadILBM (NULL, ad->ad_Tmp, NULL))
	    {
		/* Make an Image out of the bitmap */
		MakeImage (&(ad->ad_Image), ad->ad_IR);

		/* Make a pointer out of the bitmap */
		ad->ad_Ptr = MakePointer (ad->ad_IR);

		/* We have a name */
		strcpy (ad->ad_Name, ad->ad_Tmp);
	    }
	    else
	    {
		/* Notify user unable to load brush */
		ai->ai_Pri_Ret = RETURN_ERROR;
		ai->ai_Sec_Ret = IERR_CANT_LOAD_BRUSH;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, IERR_CANT_LOAD_BRUSH, NULL);
	    }

	    /* Refresh the display */
	    OpenMainFunc (h, ai, af);

	    /* Go back home */
	    CurrentDir (lock);
	}

	/* Set the real file name */
	FixFileAndPath (ad->ad_FR[FR_ILBM], ad->ad_Name);
    }
    else if (on = (struct ObjectNode *)
	     GetTagData (APSH_MsgIAddress, NULL, attrs))
    {
	struct Gadget *gad = on->on_Gadget;

	if (gad->GadgetText)
	{
	    WORD label = (gad->Flags & LABELMASK);

	    if (label == LABELIMAGE)
	    {
		im = (struct Image *) gad->GadgetText;
	    }
	}
	else if (gad->Flags & GADGIMAGE)
	{
	    im = (struct Image *) gad->GadgetRender;
	}
    }

    /* Do we have an image, and is somebody other than ourself? */
    if (im && (im != &(ad->ad_Image)))
    {
	struct Window *win;
	struct Gadget *gad;

	/* Free any existing graphics */
	FreeGraphic (h, ai, af);

	/* Copy the image over */
	CopyImage (im, &(ad->ad_Image));

	/* Refresh the pointer */
	im = &(ad->ad_Image);

	/* Update the imagry */
	if (APSHGetGadgetInfo (ai, "Main", "Display",
			       (ULONG *) & win, (ULONG *) & gad))
	{
	    /* Let the gadget know that there is an image */
	    SetGadgetAttrs (gad, win, NULL, GA_LABELIMAGE, im, TAG_DONE);
	}
    }

    if (dob)
    {
	FreeDiskObject (dob);
    }
}

VOID
SetNameFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct Window *win;
    struct Gadget *gad;
    struct Funcs *f;
    STRPTR name = NULL;

    if (f = af->af_FE)
    {
	/* See if they passed a file name */
	name = (STRPTR) f->fe_Options[0];
    }
    else
    {
	/* See if they passed a name with a tag */
	name = (STRPTR) GetTagData (APSH_NameTag, NULL, attrs);
    }

    /* Update the gadget */
    if (APSHGetGadgetInfo (ai, "Main", "Name",
			   (ULONG *) & win, (ULONG *) & gad))
    {
	if (name)
	{
	    /* Update the string gadget */
	    GT_SetGadgetAttrs (gad, win, NULL,
			       GTST_String, name,
			       TAG_DONE);
	}
	else
	{
	    name = (STRPTR) (((struct StringInfo *) gad->SpecialInfo)->Buffer);
	}
    }

    /* Do we have a value? */
    if (name)
    {
	/* Update the field */
	strcpy (ad->ad_Struct, name);
    }
}

VOID
SetModeFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct TagItem *tag;
    struct Gadget *gad;
    struct Window *win;
    LONG mode = (-1);
    struct Funcs *f;

    if (f = af->af_FE)
    {
	if (f->fe_Options[0])
	{
	    ad->ad_Mode = mode = 0;
	}
	else if (f->fe_Options[1])
	{
	    ad->ad_Mode = mode = 1;
	}
	else if (f->fe_Options[2])
	{
	    ad->ad_Mode = mode = 2;
	}
    }
    /* Being set from the gadget */
    else if (tag = FindTagItem (APSH_MsgCode, attrs))
    {
	ad->ad_Mode = tag->ti_Data;
    }

    /* Update the gadget */
    if ((mode >= 0) &&
    (APSHGetGadgetInfo (ai, "Main", "Mode", (ULONG *) & win, (ULONG *) & gad)))
    {
	/* Update the string gadget */
	GT_SetGadgetAttrs (gad, win, NULL, GTCY_Active, mode, TAG_DONE);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

/*
 * For now, simply masks out new modes. This may be invalid in future.
 * Should search the display database for a suitable mode...
 *
 * Written by: Carolyn Scheppner
 */

#define MODE_ID_MASK (LACE|HIRES|HAM|EXTRA_HALFBRITE)

ULONG
modefallback (ULONG mode, SHORT wide, SHORT high, SHORT deep)
{
    ULONG newmode;

    newmode = mode & MODE_ID_MASK;
    return (newmode);
}

/*
 * clipit - passed width and height of a display, and the
 *          text, std, and max overscan rectangles for the mode,
 *          clipit fills in spos (screen pos) and dclip rectangles
 *          to use in centering the display like the user's std oscan
 *          prefs, with dclip confined to legal maxoscan limits.
 *          Screens which center such that their top is below text
 *          oscan top, will be moved up.
 *          If a non-null uclip is passed, that clip is used instead.
 *
 * Written by: Carolyn Scheppner
 */
VOID
clipit (SHORT wide, SHORT high,
	struct Rectangle * spos, struct Rectangle * dclip,
	struct Rectangle * txto, struct Rectangle * stdo,
	struct Rectangle * maxo, struct Rectangle * uclip)
{
    SHORT minx, maxx, miny, maxy;
    SHORT stdw, stdh, maxw, maxh;

    /* get the std and max widths and heights */
    stdw = stdo->MaxX - stdo->MinX + 1;
    stdh = stdo->MaxY - stdo->MinY + 1;
    maxw = maxo->MaxX - maxo->MinX + 1;
    maxh = maxo->MaxY - maxo->MinY + 1;

    if (uclip)
    {
	*dclip = *uclip;
	spos->MinX = uclip->MinX;
	spos->MinY = uclip->MinY;
    }
    else
    {

	/*
	 * CENTER the screen based on stdoscan prefs but confine dclip within
	 * max oscan limits
	 *
	 * FIX MinX first
	 */
	spos->MinX = minx = stdo->MinX - ((wide - stdw) >> 1);
	maxx = wide + minx - 1;
	if (maxx > maxo->MaxX)
	    maxx = maxo->MaxX;	/* too right */
	if (minx < maxo->MinX)
	    minx = maxo->MinX;	/* too left  */

	/* FIX MinY */
	spos->MinY = miny = stdo->MinY - ((high - stdh) >> 1);
	/* if lower than top of txto, move up */
	spos->MinY = miny = MIN (spos->MinY, txto->MinY);
	maxy = high + miny - 1;
	if (maxy > maxo->MaxY)
	    maxy = maxo->MaxY;	/* too down  */
	if (miny < maxo->MinY)
	    miny = maxo->MinY;	/* too up    */

	/* SET up dclip */
	dclip->MinX = minx;
	dclip->MinY = miny;
	dclip->MaxX = maxx;
	dclip->MaxY = maxy;

    }
}

struct Screen *
OpenILBMScreen (struct AppInfo * ai, struct NewScreen * ns, ILBM * ir)
{
    struct Screen *scr = NULL;

    /* See if we have an bitmap to display */
    if (ir)
    {
	struct TagItem tag[15];
	LONG mode = ir->ir_ModeID;
	WORD w = ir->ir_Width;
	WORD h = ir->ir_Height;
	WORD d = ir->ir_Depth;
	struct Rectangle spos, dclip, txto, stdo, maxo;
	LONG error, trynew;

	/* See if the screen mode is available */
	if (error = ModeNotAvailable (mode))
	{
	    /* If not available, try fall back mode */
	    mode = modefallback (mode, w, h, d);

	    /* See if the new mode is available */
	    error = ModeNotAvailable (mode);
	}

	/* Were we able to come up with a ModeID? */
	if (error)
	{
	    trynew = FALSE;
	}
	else
	{
	    trynew = ((QueryOverscan (mode, &txto, OSCAN_TEXT)) &&
		      (QueryOverscan (mode, &stdo, OSCAN_STANDARD)) &&
		      (QueryOverscan (mode, &maxo, OSCAN_MAX)));
	}

	/* Fill out the tags */
	tag[0].ti_Tag = SA_DisplayID;
	tag[0].ti_Data = (LONG) mode;
	tag[1].ti_Tag = TAG_IGNORE;
	tag[2].ti_Tag = TAG_IGNORE;
	tag[3].ti_Tag = SA_Width;
	tag[3].ti_Data = (LONG) w;
	tag[4].ti_Tag = SA_Height;
	tag[4].ti_Data = (LONG) h;
	tag[5].ti_Tag = SA_Depth;
	tag[5].ti_Data = (LONG) d;
	tag[6].ti_Tag = TAG_IGNORE;
	tag[7].ti_Tag = SA_Quiet;
	tag[7].ti_Data = (LONG) TRUE;
	tag[8].ti_Tag = SA_AutoScroll;
	tag[8].ti_Data = (LONG) TRUE;
	tag[9].ti_Tag = SA_FullPalette;
	tag[9].ti_Data = (LONG) TRUE;
	tag[10].ti_Tag = SA_BitMap;
	tag[10].ti_Data = (LONG) & (ir->ir_BMap);
	tag[11].ti_Tag = SA_ErrorCode;
	tag[11].ti_Data = (LONG) & error,
	  tag[12].ti_Tag = TAG_DONE;

	/* Are we going to try opening based on the clips? */
	if (trynew)
	{
	    clipit (w, h, &spos, &dclip, &txto, &stdo, &maxo, NULL);

	    tag[1].ti_Tag = SA_Top;
	    tag[1].ti_Data = spos.MinY;
	    tag[2].ti_Tag = SA_Left;
	    tag[2].ti_Data = spos.MinX;
	    tag[6].ti_Tag = SA_DClip;
	    tag[6].ti_Data = (LONG) & dclip;
	}

	/* Open up that screen */
	if (scr = OpenScreenTagList (NULL, tag))
	{
	    /* Set the screen colors */
	    LoadRGB4 (&(scr->ViewPort), ir->ir_CRegs, ir->ir_NumColors);
	}
	else
	{
	    /* Unable to open the screen */
	    ai->ai_Pri_Ret = RETURN_ERROR;
	    ai->ai_Sec_Ret = (error + (IERR_NOMONITOR - 1));
	    ai->ai_TextRtn =
	      PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, NULL);
	}
    }
    else
    {
	/* No image loaded */
	ai->ai_Pri_Ret = RETURN_ERROR;
	ai->ai_Sec_Ret = IERR_NO_BRUSH;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, IERR_NO_BRUSH, NULL);
    }

    return (scr);
}

VOID DisplayFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    ILBM *ir = ad->ad_IR;
    struct Window *win;
    struct Funcs *f;
    BOOL close;

    /* See if we have a parsed command */
    if (f = af->af_FE)
    {
	/* See if they passed a file name */
	close = (BOOL) f->fe_Options[0];
    }

    /* Get a pointer to the window */
    APSHGetWindowInfo (ai, "Main", (ULONG *) & win);

    /* Set the pointer back to normal */
    APSHClearPointer (ai, NULL);

    /* Close the display? */
    if (close)
    {
	/* Is the screen open? */
	if (ad->ad_Screen)
	{
	    /* Close the Screen.  Bug? in Intuition clears the bitmap. */
	    CloseScreen (ad->ad_Screen);
	    ad->ad_Screen = NULL;
	}
    }
    /* Open the display */
    else
    {
	switch (ad->ad_Mode)
	{
	    case ICON:
	    case IMAGE:
		if (ir && (ad->ad_Screen == NULL))
		{
		    ad->ad_Screen = OpenILBMScreen (ai, NULL, ad->ad_IR);
		}

		/* Is the screen open? */
		if (ad->ad_Screen)
		{
		    /* Bring it to front */
		    ScreenToFront (ad->ad_Screen);
		}
		break;

	    case SPRITE:
		if (win)
		{
		    struct PointerPref *pp = ad->ad_Ptr;

		    /* Set the pointer */
		    SetPointer (win, pp->pp_PData, pp->pp_Height, pp->pp_Width,
				pp->pp_XOffset, pp->pp_YOffset);
		}
		else
		{
		}
		break;
	}
    }
}

VOID
SaveSettingsFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
}

UWORD chip listingI1Data[] =
{
/* Plane 0 */
    0x0000, 0x0000, 0x0000, 0x0400, 0x0000, 0x0000, 0x0000, 0x0C00,
    0x0000, 0x0000, 0x0000, 0x0C00, 0x0FFF, 0xFFFF, 0xFFE0, 0x0C00,
    0x0800, 0x0000, 0x0030, 0x0C00, 0x0980, 0x0000, 0x0028, 0x0C00,
    0x0800, 0x0000, 0x0024, 0x0C00, 0x098A, 0xA000, 0x0022, 0x0C00,
    0x0800, 0x0000, 0x003F, 0x0C00, 0x098A, 0xA800, 0x000C, 0x8C00,
    0x0800, 0x0000, 0x0000, 0x8C00, 0x0980, 0x2AAA, 0xA80C, 0x8C00,
    0x0800, 0x0000, 0x0000, 0x8C00, 0x0980, 0x2AAA, 0xA00C, 0x8C00,
    0x0800, 0x0000, 0x0000, 0x8C00, 0x098A, 0xA800, 0x000C, 0x8C00,
    0x0800, 0x0000, 0x0000, 0x8C00, 0x098A, 0x0000, 0x000C, 0x8C00,
    0x0FFF, 0xFFFF, 0xFFFF, 0x8C00, 0x0000, 0x0000, 0x0000, 0x0C00,
    0x0000, 0x0000, 0x0000, 0x0C00, 0x7FFF, 0xFFFF, 0xFFFF, 0xFC00,
/* Plane 1 */
    0xFFFF, 0xFFFF, 0xFFFF, 0xF800, 0xD555, 0x5555, 0x5555, 0x5000,
    0xD555, 0x5555, 0x5555, 0x5000, 0xD000, 0x0000, 0x0015, 0x5000,
    0xD7FF, 0xFFFF, 0xFFC5, 0x5000, 0xD67F, 0xFFFF, 0xFFD5, 0x5000,
    0xD7FF, 0xFFFF, 0xFFD9, 0x5000, 0xD675, 0x5FFF, 0xFFDD, 0x5000,
    0xD7FF, 0xFFFF, 0xFFC0, 0x5000, 0xD675, 0x57FF, 0xFFF3, 0x5000,
    0xD7FF, 0xFFFF, 0xFFFF, 0x5000, 0xD67F, 0xD555, 0x57F3, 0x5000,
    0xD7FF, 0xFFFF, 0xFFFF, 0x5000, 0xD67F, 0xD555, 0x5FF3, 0x5000,
    0xD7FF, 0xFFFF, 0xFFFF, 0x5000, 0xD675, 0x57FF, 0xFFF3, 0x5000,
    0xD7FF, 0xFFFF, 0xFFFF, 0x5000, 0xD675, 0xFFFF, 0xFFF3, 0x5000,
    0xD000, 0x0000, 0x0000, 0x5000, 0xD555, 0x5555, 0x5555, 0x5000,
    0xD555, 0x5555, 0x5555, 0x5000, 0x8000, 0x0000, 0x0000, 0x0000,
};

struct Image listingI1 =
{
    0, 0,			/* Upper left corner */
    54, 22, 2,			/* Width, Height, Depth */
    listingI1Data,		/* Image data */
    0x0003, 0x0000,		/* PlanePick, PlaneOnOff */
    NULL			/* Next image */
};

struct DiskObject Listing =
{
    WB_DISKMAGIC,		/* Magic Number */
    WB_DISKVERSION,		/* Version */
    {				/* Embedded Gadget Structure */
	NULL,			/* Next Gadget Pointer */
	0, 0, 54, 23,		/* Left,Top,Width,Height */
	GADGIMAGE | GADGHCOMP,	/* Flags */
	RELVERIFY,		/* Activation Flags */
	BOOLGADGET,		/* Gadget Type */
	(APTR) & listingI1,	/* Render Image */
	NULL,			/* Select Image */
	NULL,			/* Gadget Text */
	NULL,			/* Mutual Exclude */
	NULL,			/* Special Info */
	0,			/* Gadget ID */
	NULL,			/* User Data */
    },
    WBPROJECT,			/* Icon Type */
    (char *) "Ed",		/* Default Tool */
    NULL,			/* Tool Type Array */
    NO_ICON_POSITION,		/* Current X */
    NO_ICON_POSITION,		/* Current Y */
    NULL,			/* Drawer Structure */
    NULL,			/* Tool Window */
    0				/* Stack Size */
};
