#define NAME	 "XpkMaster"
#define REVISION "30"
#define ENDCODE

/* Programmheader

	Name:		XpkMaster
	Author:		SDI
	Distribution:	PD
	Description:	Prefs program for XpkMaster
	Compileropts:	-
	Linkeropts:	-l amiga
	ToDo:		speed up refresh, by flags based choose of to
			refresh gadgets

 1.0   10.11.96 : first Version, incomplete, started to form GUI
 1.1   11.11.96 : corrected, added the code a bit
 1.2   16.11.96 : some more stuff
 1.3   17.11.96 : completed GUI
 1.4   20.11.96 : added more functions
 1.5   21.11.96 : added icon creation, corrected some stuff
 1.6   23.11.96 : corrected some stuff
 1.7   24.11.96 : added loading and cleaned up source
 1.8   30.11.96 : fixed some errors, still working incorrect
 1.9   01.12.96 : and again some bugfixes
 1.10  07.12.96 : correct Default dir in asl request and wb call
 1.11  12.12.96 : fixed crash problem, when called by project icon with
 	ACTION=EDIT
 1.12  13.12.96 : made the window a bit smaller for larger texts and fonts
 1.13  14.12.96 : changed the Prefs include XPKM -> XPKT, XPKD -> XPKM,
 	other member prefixes
 1.14  20.12.96 : fixed Enforcer hit (I finally have a MMU :-) and little
	error
 1.15  25.12.96 : corrected List handling
 1.16  26.12.96 : once again changed xpkprefs.h -> now should be last time,
 	because I wrote the prefs support for master library
 1.17  29.12.96 : fixed bug with XPKM flags
 1.18  09.01.97 : changed default flags
 1.19  11.01.97 : splitted master library and prefs catalog
 1.20  01.03.97 : changed XpkMainPrefs structure a bit
 1.21  07.03.97 : recompiled, because of structures additions
 1.22  08.03.97 : fixed a crash causing typo error
 1.23  02.04.97 : changed XpkTypeData structure
 1.24  03.04.97 : fixed error
 1.25  04.04.97 : fixed error in texts
 1.26  10.04.97 : default entry now 1 and not 0
 1.27  18.05.97 : corrected internal string
 1.28  31.05.97 : SDI_locale changed
 1.29  17.06.97 : now prints entry name for illegal entry
 1.30  18.06.97 : library name file requester got wrong value
*/

#include <pragma/exec_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/icon_lib.h>
#include <pragma/gadtools_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/asl_lib.h>
#include <pragma/iffparse_lib.h>
#include <xpk/xpkprefs.h>
#include <clib/alib_protos.h>
#include <intuition/gadgetclass.h>
#include <workbench/startup.h>
#include <prefs/prefhdr.h>
#include <exec/memory.h>
#include "SDI_defines.h"
#define SDI_LOCALE_NO_STRUCTS
#include "SDI_locale.c"
#include "SDI_ASM_STD_protos.h"

/* ------------------ defines and structures --------------------------- */

#define PARAM	"FROM,EDIT/S,USE/S,SAVE/S,PUBSCREEN/K"

#define GADGET(a) 	((struct Gadget *) a->IAddress)
#define STRINF(a)	((struct StringInfo *) (a->SpecialInfo))
#define	DEFNODE(a)	((struct PackerNode *) ((a)->ld_List.lh_Head))

#define PicSize		509	// size of the icon

enum {
STRING_TypeName = 100,	LISTVIEW,		BUTTON_New,
BUTTON_Delete,		BUTTON_Up,		BUTTON_Down,
STRING_NamePatt,	STRING_FilePatt,	STRING_LibraryName,
GETIMAGE,		INTEGER_Mode,		INTEGER_ChunkSize,
CYCLE,			INTEGER_TimeOut,	CHECKBOX_XFD,
CHECKBOX_EXT,		CHECKBOX_Password,	BUTTON_Save,
BUTTON_Use,		BUTTON_Chancel
};

#define PREF_VERSION	0	// version of prefs I can work with

#define	DEFAULT_NODE	100
#define PACKER_NODE	101

#define DEF_TIMEOUT	120
#define DEF_XPKM_FLAGS	(XPKM_AutoPassword | XPKM_UseExternals)
#define DEF_CYCLE	2 // XTD_ReturnError

// window style defines
#define SEPARATE_SAME	2
#define SEPARATE_DIFF	5
#define SEPARATE_BORD	5

#define TypeSIZE	49
#define NamePattSIZE	49
#define FilePattSIZE	99

struct PackerNode {
  struct Node		pn_Node;
  ULONG			pn_StdID;
  UWORD			pad;	/* always StdID finished with 0 byte */
  UWORD			pn_Mode;
  ULONG			pn_ChunkSize;
  ULONG			pn_Cycle;
  UBYTE			pn_TypeName[TypeSIZE+1];
  UBYTE			pn_NamePattern[NamePattSIZE+1];
  UBYTE			pn_FilePattern[FilePattSIZE+1];
};

struct ListData {
  struct List	ld_List;
  struct PackerNode	*ld_curNode;
  ULONG			ld_entries;
  ULONG			ld_curNumber;
};

struct Args {
  STRPTR from;
  ULONG	 edit;
  ULONG	 use;
  ULONG  save;
  STRPTR pubscreen;
  ULONG	 createicons;
};

/* ----------------------------- Strings ------------------------------- */

#define TEXT_START_MENUMAIN	0
#define NUMBER_MENUMAIN		11

enum {
TEXT_WINDOW_TITLE = NUMBER_MENUMAIN,		TEXT_ENTRY_DEFAULT,
TEXT_ENTRY_NEW,		TEXT_SELECT_LIBRARY,	TEXT_LOAD_PREFERENCES,
TEXT_SAVE_PREFERENCES,	TEXT_TITLE_ERR_REQUEST,	TEXT_ERROR_FILEACCESS,
TEXT_LIB_REQUIRED,	TEXT_VERSION_TO_HIGH,	TEXT_ERROR_FILEPROCESS,
TEXT_ILLEGAL_ENTRY,	TEXT_NO_WINDOW,		GADGS_ERR_REQUEST,
GADG_Save,		GADG_Use,		GADG_Cancel,
GADG_Use_XFD,		GADG_Use_Externals,	GADG_AutoPassword,
GADG_Timeout,		GADG_New,		GADG_Delete,
GADG_Up,		GADG_Down,		GADG_Name_Pattern,
GADG_File_Pattern,	GADG_Library_Name,	GADG_Mode,
GADG_ChunkSize,		GADG_PackMode,		TEXT_START_SLIDER
};

#define TEXT_START_OTHER	(NUMBER_MENUMAIN)
#define TEXT_START_GADGET	(GADG_Save)

#define NUMBER_OTHER		(TEXT_START_GADGET-TEXT_START_OTHER)
#define NUMBER_GADGET		(TEXT_START_SLIDER-TEXT_START_GADGET)
#define NUMBER_SLIDER		3

struct SDI_SetLocale initloc[] = {
{ 400, NUMBER_MENUMAIN, TEXT_START_MENUMAIN}, // Main-Menutexts
{ 420, NUMBER_OTHER,    TEXT_START_OTHER},    // all other texts
{ 450, NUMBER_SLIDER,   TEXT_START_SLIDER},   // Slider texts
{ 460, NUMBER_GADGET,   TEXT_START_GADGET},   // Gadgettexts
{   0, 0, 0},				      // Ende
};

STRPTR text[] =
{
"Project",
"O\0Open...",
"M\0Load Add...",
"A\0Save As...",
"Q\0Quit",
"Edit",
"D\0Reset To Defaults",
"L\0Last Saved",
"R\0Restore",
"Settings",
"I\0Create Icons?",
"XpkMaster Preferences",	// the window title
"«default»",			// the default pack mode
"«new»",
"Select xpk or xex library",
"Load XpkMaster preferences",
"Save XpkMaster preferences",
"Program Error",
"Error accessing file\n%s\n%s",
"Requires %s V%ld",
"Prefs file version to high,\nloaded data may be incomplete.",
"Error processing IFF file",
"%s of entry %ld (%s)\nis illegal, cannot save.",
"Could not open window,\nthe used font is too large.",
"OK",
"Save",
"Use",
"Cancel",
"Use XFD",			// the checkboxes
"Use XEX",
"Auto password",
"Timeout",			// the timeout integer
"New",				// the Listview
"Delete",
"Up",
"Down",
"Name Pattern",
"File Pattern",
"Library Name",
"Mode",
"ChunkSize",
"PackMode",

"pack xpk or xex",
"do not pack",
"return error",
0,
};

STRPTR toolt[] =
{
"EDIT",
"USE",
"SAVE",
"PUBSCREEN",
"CREATEICONS",	// YES || NO
"ACTION"
};

/* ---------------------- protos and variables ------------------------- */

struct RDArgs		*rda		= 0;
struct Library		*GadToolsBase	= 0,
			*IconBase	= 0,
			*UtilityBase	= 0,
			*AslBase	= 0,
			*IFFParseBase	= 0;
struct LocaleBase	*LocaleBase	= 0;
struct IntuitionBase	*IntuitionBase	= 0;
struct GfxBase		*GfxBase	= 0;
struct Screen		*Scr		= 0;
APTR			VisualInfo	= 0;
struct Window		*window		= 0;
struct Gadget		*gadgList	= 0,
			*gadgdata[12];
/* List, NamePatt, FilePatt, LibraryName, GetImage, Mode, ChunkSize, Cycle,
TimeOut, XFD, Extern, Pwd */
struct Menu		*menus		= 0;
struct DiskObject	*diskobject	= 0;
ULONG			lock		= 0,
			DosVersion	= 37,
			TimeOut		= DEF_TIMEOUT,
			XPKM_Flags	= DEF_XPKM_FLAGS;
struct ListData		maindata	= {{(struct Node *) &maindata.ld_List.lh_Tail,
	 0, (struct Node *) &maindata.ld_List.lh_Head, NT_USER, 0}, 0, 0, 0};
UBYTE			namestring[256];
struct SDI_LocaleData	locdat = {"xpkmasterprefs.catalog", 1, 0,0,0,0};

#ifdef __MAXON__
extern "C" void		wbmain(struct WBStartup *);
void			main(void);
#else
void			wbmain(struct WBStartup *);
void			main(int argc, char **argv);
#endif
void			Work(struct Args *);
ULONG			SDIConvToUpper(STRPTR a);

void			CorrectAdr(APTR, APTR);
void			CopyStrings(STRPTR, STRPTR, ULONG);
void			CopyLibName(struct XpkTypeData *, struct PackerNode *);
void			ReadXPKM(struct XpkMainPrefs *, struct ListData *);
ULONG			ReadXPKT(struct XpkTypePrefs *, struct ListData *);
ULONG			LoadFile(STRPTR, ULONG);

ULONG			CopyData(STRPTR, ULONG *, APTR, STRPTR *);
ULONG			WriteXPKM(struct IFFHandle *);
ULONG			WriteXPKT(struct IFFHandle *, struct PackerNode *, ULONG);
ULONG			SaveFile(STRPTR, ULONG);

void			PrintError(STRPTR, ...);
void			FileReq(void);
ULONG			PrefsFileReq(STRPTR, ULONG, STRPTR);
void			FreeListView(void);
void			ResetListView(ULONG, struct ListData *);
void			SetMainData(void);
ULONG 			DoWindow(void);

void			NewPackerList(struct ListData *);
void			FreePackerList(struct ListData *);
struct Node		*FindNode(struct List *, ULONG);
struct PackerNode	*NewPackerNode(struct ListData *, STRPTR, struct PackerNode *, ULONG);

struct NewMenu NewMenus[] = {
  NM_TITLE, (STRPTR) &text[TEXT_START_MENUMAIN],   0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+1], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+2], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+3], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) NM_BARLABEL, 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+4], 0, 0, 0, 0,
  NM_TITLE, (STRPTR) &text[TEXT_START_MENUMAIN+5], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+6], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+7], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+8], 0, 0, 0, 0,
  NM_TITLE, (STRPTR) &text[TEXT_START_MENUMAIN+9], 0, 0, 0, 0,
  NM_ITEM,  (STRPTR) &text[TEXT_START_MENUMAIN+10], 0, MENUTOGGLE|CHECKIT, 0, 0,
  NM_END,   0, 0, 0, 0, 0};

ULONG PicData[] = {
  0xE3100001,0x00000000,0x0023000F,0x00360017,0x00050003,0x00010044,
  0x39A00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,
  0x049C004D,0x0CE00033,0x80F88000,0x00008000,0x00000000,0x00000000,
  0x00000000,0x10000000,0x00000036,0x00170002,0x00024288,0x03000000,
  0x00000000,0x00000004,0x00000000,0x00000001,0x00000000,0x07FF8000,
  0x40000000,0x18006000,0x10000000,0x20FC1000,0x08000000,0x41020800,
  0x0C000000,0x40820800,0x0C000000,0x40820800,0x0C000000,0x21040800,
  0x0C000000,0x1E181000,0x0C000000,0x00602000,0x0C000000,0x0080C000,
  0x0C000000,0x01030000,0x0C000000,0x021C0000,0x0C000000,0x01080000,
  0x0C000000,0x00F00000,0x0C000000,0x01080000,0x0C000000,0x01080000,
  0x0C004000,0x00F00000,0x0C001000,0x00000000,0x0C000400,0x00000000,
  0x0C0001FF,0xFFFFFFFF,0xFC000000,0x00000000,0x0000FFFF,0xFFFFFFF8,
  0x0000D555,0x55555556,0x0000D555,0x50005555,0x8000D555,0x47FF9555,
  0x6000D555,0x5F03E555,0x5000D555,0x3E55F555,0x5000D555,0x3F55F555,
  0x5000D555,0x3F55F555,0x5000D555,0x5E53F555,0x5000D555,0x4147E555,
  0x5000D555,0x551FD555,0x5000D555,0x557F1555,0x5000D555,0x54FC5555,
  0x5000D555,0x55E15555,0x5000D555,0x54F55555,0x5000D555,0x55055555,
  0x5000D555,0x54F55555,0x5000D555,0x54F55555,0x50003555,0x55055555,
  0x50000D55,0x55555555,0x50000355,0x55555555,0x50000000,0x00000000,
  0x00000000,0x00000000,0x00000000,0x00145359,0x533A5072,0x6566732F,
  0x58706B4D,0x61737465,0x72000000,0x00080000,0x000B4143,0x54494F4E,
  0x3D555345,0x00000000,
};

/* -------------------------- central functions ------------------------ */

void wbmain(struct WBStartup *wb)
{
  struct Args args = {0, 0, 0, 0, 0, 0};

  if(wb->sm_NumArgs > 2 || !wb->sm_NumArgs)
    End(RETURN_FAIL);

  lock = CurrentDir(wb->sm_ArgList[wb->sm_NumArgs-1].wa_Lock);

  if((IconBase = OpenLibrary("icon.library", 37)) &&
  (diskobject = GetDiskObject(wb->sm_ArgList[wb->sm_NumArgs-1].wa_Name)))
  {
    STRPTR a, *c = (STRPTR *) diskobject->do_ToolTypes;

    if(wb->sm_NumArgs == 2)
      args.from = wb->sm_ArgList[1].wa_Name;

    args.edit = (ULONG) FindToolType(c, toolt[0]);
    args.use = (ULONG) FindToolType(c, toolt[1]);
    args.save = (ULONG) FindToolType(c, toolt[2]);
    args.pubscreen = FindToolType(c, toolt[3]);
    if((a = FindToolType(c, toolt[4])))
    {
      if(MatchToolValue(a, "YES"))
        args.createicons = 1;
    }
    if((a = FindToolType(c, toolt[5])))
    {
      if(MatchToolValue(toolt[0], a))
        args.edit = 1;
      else if(MatchToolValue(toolt[1], a))
        args.use = 1;
      else if(MatchToolValue(toolt[2], a))
        args.save = 1;
    }
  }

  Work(&args);
  End(RETURN_OK);
}

#ifdef __MAXON__
void main(void)
#else
void main(int argc, char **argv)
#endif
{
  struct Args args = {0, 0, 0, 0, 0, 0};

#ifdef __SASC
  TestOS;
  if(!argc)
    wbmain((struct WBStartup *) argv);
#endif

  if(!(rda = ReadArgs(PARAM, (LONG *) &args, 0)))
    End(RETURN_FAIL);

  Work(&args);
  End(RETURN_OK);
}

void Work(struct Args *args)
{
  struct IntuiMessage *IntuiMessagePtr;
  ULONG run = 1;
  LONG Long;
  STRPTR txt;

  if(args->edit)
    args->use = args->save = 0;
  if(args->save)
    args->use = 0;

  if(
  !(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37)) ||
  !(GadToolsBase = OpenLibrary("gadtools.library", 37)) ||
  !(UtilityBase = OpenLibrary("utility.library", 37)) ||
  !(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",37)))
    End(RETURN_FAIL);

  SDI_InitLocale(&locdat);
  SDI_LocaleStrings(&locdat, initloc, text);
  SDI_LocaleNewMenu(NewMenus, text + TEXT_START_MENUMAIN,
    text + TEXT_START_OTHER);

  if(!(IFFParseBase = OpenLibrary("iffparse.library", 37)))
  {
    PrintError(text[TEXT_LIB_REQUIRED], "iffparse.library", 37);
    End(RETURN_FAIL);
  }

  NewPackerList(&maindata);

  if(args->use || args->save)
  {
    if(!args->from)
      args->from = "";
    if(LoadFile(args->from, 0) ||
    (args->save && SaveFile((STRPTR)1 , 0)) ||
    SaveFile(0, 0))
      End(RETURN_FAIL);
    End(RETURN_OK);
  }

  if(args->createicons)
    NewMenus[11].nm_Flags |= CHECKED;

  if(args->from && LoadFile(args->from, 0))
    args->from = 0;

  if(!args->from && LoadFile(0, 0))
    LoadFile((STRPTR) 1, 0);

  if(!(Scr = LockPubScreen(args->pubscreen)) ||
  !(VisualInfo = GetVisualInfoA(Scr, 0)) ||
  DoWindow())
    End(RETURN_FAIL);

  do
  {
    if(Wait((1L<<window->UserPort->mp_SigBit)|SIGBREAKF_CTRL_C)
    == SIGBREAKF_CTRL_C)
      run = 0;
    else
      while(run && (IntuiMessagePtr = GT_GetIMsg(window->UserPort)))
      {
	switch(IntuiMessagePtr->Class)
 	{
	case IDCMP_MENUPICK:
	  Long = (ULONG) IntuiMessagePtr->Code;
	  while(run && (ULONG) Long != MENUNULL)
	  {
	    switch(MENUNUM((ULONG) Long))
	    {
	    case 0:					// Projekt
	      switch(ITEMNUM((ULONG) Long))
	      {
	      case 0:					// Open...
		if(PrefsFileReq(args->from, 0, text[TEXT_LOAD_PREFERENCES]))
		{
		  args->from = namestring;
		  LoadFile(args->from, 0);
 	          SetMainData();
		  ResetListView(0, &maindata);
		}
		break;
	      case 1:					// Load Add...
		if(PrefsFileReq(args->from, 0, text[TEXT_LOAD_PREFERENCES]))
		{
		  args->from = namestring;
		  LoadFile(args->from, 1);
	          ResetListView(0, &maindata);
		}
		break;
	      case 2:					// Save As...
		if(PrefsFileReq(args->from, 1, text[TEXT_SAVE_PREFERENCES]))
		{
		  args->from = namestring;
		  SaveFile(args->from, args->createicons);
		}
		break;
	      case 4: run = 0; break;			// Quit
	      } break;
	    case 1:					// Edit
	      switch(ITEMNUM((ULONG) Long))
	      {
	      case 0:					// Reset To Defaults
		NewPackerList(&maindata);
		ResetListView(0, &maindata);
		break;
	      case 1:					// Last Saved
		LoadFile((STRPTR) 1, 0);
		SetMainData();
	        ResetListView(0, &maindata);
		break;
	      case 2:					// Restore
		LoadFile(0, 0);
	        SetMainData();
		ResetListView(0, &maindata);
		break;
	      } break;
	    case 2:					// Settings
	      args->createicons =
	      ItemAddress(menus, (ULONG) Long)->Flags & CHECKED;
	      break;
	    }
	    Long = (ULONG) ItemAddress(menus, (ULONG) Long)->NextSelect;
	  } break;
	case IDCMP_GADGETUP:
	  switch(GADGET(IntuiMessagePtr)->GadgetID)
	  {
	  case STRING_TypeName:
	    txt = STRINF(GADGET(IntuiMessagePtr))->Buffer;
	    FreeListView();
	    CopyMem(txt, maindata.ld_curNode->pn_TypeName, TypeSIZE);
	    ResetListView(~0, &maindata);
	    break;
	  case LISTVIEW: ResetListView(IntuiMessagePtr->Code, &maindata); break;
	  case BUTTON_New:
	    FreeListView();
	    if(!NewPackerNode(&maindata, text[TEXT_ENTRY_NEW],
	    DEFNODE(&maindata), maindata.ld_curNumber))
	      End(RETURN_FAIL);
	    ResetListView(~0, &maindata); break;
	  case BUTTON_Delete:
	    if(maindata.ld_curNumber)
	    {
	      FreeListView();
	      Remove((struct Node *) maindata.ld_curNode);
	      FreeMem(maindata.ld_curNode, sizeof(struct PackerNode));
	      if(--maindata.ld_entries == maindata.ld_curNumber)
	        --maindata.ld_curNumber;
	      ResetListView(~0, &maindata);
	    } break;
	  case BUTTON_Up:
	    if(maindata.ld_curNumber > 1)
	    {
	      struct Node *p;
	      FreeListView();
	      p = maindata.ld_curNode->pn_Node.ln_Pred->ln_Pred;
	      Remove((struct Node *) maindata.ld_curNode);
	      Insert(&maindata.ld_List, (struct Node *) maindata.ld_curNode, p);
	      ResetListView(--maindata.ld_curNumber, &maindata);
	    } break;
	  case BUTTON_Down:
	    if(maindata.ld_curNumber &&
	    maindata.ld_curNumber != maindata.ld_entries - 1)
	    {
	      struct Node *p;
	      FreeListView();
	      p = maindata.ld_curNode->pn_Node.ln_Succ;
	      Remove((struct Node *) maindata.ld_curNode);
	      Insert(&maindata.ld_List, (struct Node *) maindata.ld_curNode, p);
	      ResetListView(++maindata.ld_curNumber, &maindata);
	    } break;
	  case STRING_NamePatt:
	    txt = STRINF(GADGET(IntuiMessagePtr))->Buffer;
// test if correct pattern
	    CopyMem(txt, &maindata.ld_curNode->pn_NamePattern, NamePattSIZE);
	    break;
	  case STRING_FilePatt:
	    txt = STRINF(GADGET(IntuiMessagePtr))->Buffer;
// test if correct pattern
	    CopyMem(txt, &maindata.ld_curNode->pn_FilePattern, FilePattSIZE);
	    break;
	  case GETIMAGE: FileReq(); GT_SetGadgetAttrs(gadgdata[3], window,
	    0, GTST_String, &maindata.ld_curNode->pn_StdID, GA_Disabled,
	    maindata.ld_curNode->pn_Cycle, TAG_DONE);
	    break;
	  case STRING_LibraryName:
	    txt = STRINF(GADGET(IntuiMessagePtr))->Buffer;
	    if((Long = SDIConvToUpper(txt)))
	    {
	      maindata.ld_curNode->pn_StdID = Long;
	      GT_SetGadgetAttrs(gadgdata[3], window,
	      0, GTST_String, &maindata.ld_curNode->pn_StdID, GA_Disabled,
	      maindata.ld_curNode->pn_Cycle, TAG_DONE);
	    }
	    else
	    {
	      DisplayBeep(Scr);
	      ActivateGadget(GADGET(IntuiMessagePtr), window,0);
	    }
	    break;
	  case INTEGER_Mode:
	    Long = STRINF(GADGET(IntuiMessagePtr))->LongInt;
	    if((Long > 100) || (Long < 0))
	    {
	      DisplayBeep(Scr);
	      ActivateGadget(GADGET(IntuiMessagePtr), window,0);
	    }
	    else maindata.ld_curNode->pn_Mode = Long;
	    break;
	  case INTEGER_ChunkSize:
	    Long = STRINF(GADGET(IntuiMessagePtr))->LongInt;
	    if(Long < 0)
	    {
	      DisplayBeep(Scr);
	      ActivateGadget(GADGET(IntuiMessagePtr), window,0);
	    }
	    else maindata.ld_curNode->pn_ChunkSize = Long;
	    break;
	  case CYCLE: maindata.ld_curNode->pn_Cycle = IntuiMessagePtr->Code;
	    ResetListView(~0, &maindata); break;
	  case INTEGER_TimeOut:
	    Long = STRINF(GADGET(IntuiMessagePtr))->LongInt;
	    if(Long < 0 || Long > 0xFFFF)
	    {
	      DisplayBeep(Scr);
	      ActivateGadget(GADGET(IntuiMessagePtr), window,0);
	    }
	    else TimeOut = Long;
	    break;
	  case CHECKBOX_XFD:
	    XPKM_Flags ^= XPKM_UseXFD; break;
	  case CHECKBOX_EXT:
	    XPKM_Flags ^= XPKM_UseExternals; break;
	  case CHECKBOX_Password:
	    XPKM_Flags ^= XPKM_AutoPassword; break;
	  case BUTTON_Save:
	    if(!SaveFile((STRPTR) 1, 0))
	      if(!SaveFile(0, 0))
		run = 0;
 	    break;
	  case BUTTON_Use: if(!SaveFile(0, 0)) run = 0; break;
	  case BUTTON_Chancel: run = 0; break;
	  } break;
	}
        GT_ReplyIMsg(IntuiMessagePtr);
      }
  }
  while(run);
}

ULONG SDIConvToUpper(STRPTR b)
{
  ULONG i;
  ULONG out = 0;

  for(i = 0; i < 4; ++i)
  {
    if(!SDI_isalnum(b[i]))
      return 0;
    out = (out<<8) + SDI_toupper(b[i]);
  }

  return out;
}

/* ----------------------- loading functions --------------------------- */

void CorrectAdr(APTR pos, APTR mempos)
{ // changes relative addresses into normal ones
  if(*(ULONG *)pos)
    *(ULONG *)pos += (ULONG) mempos;
}

void CopyStrings(STRPTR from, STRPTR to, ULONG Size)
{
  ULONG i;

  if(from)
  {
    if((i = SDI_strlen(from)) > Size)
      i = Size;
    CopyMem(from, to, i);
  }
}

void CopyLibName(struct XpkTypeData *p, struct PackerNode *n)
{
  if(p->xtd_Flags & XTD_NoPack)
    n->pn_Cycle = 1;
  else if(p->xtd_Flags & XTD_ReturnError || !p->xtd_StdID)
    n->pn_Cycle = 2;
  else
  {
    n->pn_Cycle = 0;
    n->pn_StdID = p->xtd_StdID;
  }
}

void ReadXPKM(struct XpkMainPrefs *pref, struct ListData *dat)
{
  struct PackerNode *n = DEFNODE(dat);
  struct XpkTypeData *p;

  CorrectAdr(&pref->xmp_DefaultType, pref);
  p = pref->xmp_DefaultType;
  CopyLibName(p, n);
  n->pn_Mode = p->xtd_Mode;
  n->pn_ChunkSize = p->xtd_ChunkSize;
  TimeOut = pref->xmp_Timeout;
  XPKM_Flags = pref->xmp_Flags;
}

ULONG ReadXPKT(struct XpkTypePrefs *pref, struct ListData *dat)
{
  struct PackerNode *n;

  CorrectAdr(&pref->xtp_NamePattern, pref);
  CorrectAdr(&pref->xtp_FilePattern, pref);
  CorrectAdr(&pref->xtp_TypeName, pref);
  CorrectAdr(&pref->xtp_PackerData, pref);
  if(!pref->xtp_TypeName)
    pref->xtp_TypeName = text[TEXT_ENTRY_NEW];

  if(!(n = NewPackerNode(dat, pref->xtp_TypeName, 0, dat->ld_curNumber)))
    return 2;

  CopyStrings(pref->xtp_NamePattern, n->pn_NamePattern, NamePattSIZE);
  CopyStrings(pref->xtp_FilePattern, n->pn_FilePattern, FilePattSIZE);
  CopyLibName(pref->xtp_PackerData, n);
  n->pn_Mode = pref->xtp_PackerData->xtd_Mode;
  n->pn_ChunkSize = pref->xtp_PackerData->xtd_ChunkSize;
  return 0;
}

ULONG LoadFile(STRPTR file, ULONG addmode)
{
// addmode only processes PRHD and XPKT chunks (not XPKM) -- adds the
// chunks to the list -- no replacement

  struct ContextNode  *cn;
  struct IFFHandle *iff;
  ULONG data = (ULONG) file > 1 ? 1 : 0;
  ULONG error = 0;

  if(file == (STRPTR) 1)
    file = "ENVARC:xpkmaster.prefs";
  else if(!file)
    file = "ENV:xpkmaster.prefs";

  if((iff = AllocIFF()))
  {
    if((iff->iff_Stream = Open(file, MODE_OLDFILE)))
    {
      InitIFFasDOS(iff);
      if(!(error = OpenIFF(iff, IFFF_READ)))
      {
	LONG a[] = { ID_PREF, ID_PRHD, ID_PREF, ID_XPKT, ID_PREF, ID_XPKM};
	if(!(error = StopChunks(iff, a, 3)))
	{
	  STRPTR buf;
	  ULONG Flags = (addmode ? 2 : 0);

	  while(!error)
          {
            if((error = ParseIFF(iff,IFFPARSE_SCAN)))
              break;

            cn = CurrentChunk(iff);

	    if(!addmode && cn->cn_ID != ID_PRHD)
	    {	// clear list only when XPKT or XPKM and not addmode
	      NewPackerList(&maindata); addmode = 1;
	    }

	    if((buf = (STRPTR) AllocMem(cn->cn_Size, MEMF_ANY|MEMF_CLEAR)))
	    {
	      ReadChunkBytes(iff,buf,cn->cn_Size);
	      switch(cn->cn_ID)
	      {
	      case ID_PRHD:
		if(!(Flags & 1))
		{
		  if(((struct PrefHeader *) buf)->ph_Version > PREF_VERSION)
		    PrintError(text[TEXT_VERSION_TO_HIGH]);
		  Flags |= 1;
		} break;
	      case ID_XPKM:
		if(!(Flags & 2))
		{
		  ReadXPKM((struct XpkMainPrefs *) buf, &maindata);
		  Flags |= 2;
		} break;
	      case ID_XPKT:
		error = ReadXPKT((struct XpkTypePrefs *) buf, &maindata);
		break;
	      }
	      FreeMem(buf, cn->cn_Size);
	    }
	    else error = 2;
	  }
	  if(error == IFFERR_EOF)
	    error = 0;
	}
        CloseIFF(iff);
      }
      Close(iff->iff_Stream);
    }
    else if(data)
    {
      UBYTE err[100];
      Fault(IoErr(), 0, err, 100);
      PrintError(text[TEXT_ERROR_FILEACCESS], file, &err);
      error = 1;
    }
    else
      error = 1;
    FreeIFF(iff);
  }

  if(error && error != 1)
    PrintError(text[TEXT_ERROR_FILEPROCESS]);

  return error;
}

/* ----------------------- saving functions ---------------------------- */

ULONG CopyData(STRPTR from, ULONG *to, APTR data, STRPTR *entry)
{
  ULONG i = SDI_strlen(from);

  if(!(*from))
    return 0;
  CopyMem(from, (STRPTR) data + *to, i);
  *entry = (STRPTR) *to;
  *to += ++i;
}

#define XPKM_Size	(sizeof(struct XpkMainPrefs) + \
			sizeof(struct XpkTypeData))

#define	xpkm	((struct XpkMainPrefs *) data)
#define xpkd	((struct XpkTypeData *) (data + sizeof(struct XpkMainPrefs)))

ULONG WriteXPKM(struct IFFHandle *iff)
{
  STRPTR data;
  struct PackerNode *r = DEFNODE(&maindata);
  ULONG ptr = sizeof(struct XpkMainPrefs), error;

  if(!r->pn_Cycle && !r->pn_StdID)
  {
    PrintError(text[TEXT_ILLEGAL_ENTRY], text[GADG_Library_Name], 1, r->pn_TypeName);
    return 1;
  }

  if((data = (STRPTR) AllocMem(XPKM_Size, MEMF_ANY|MEMF_CLEAR)))
  {
//    xpkm->xmp_Version		= 0;	/* structure version */
    xpkm->xmp_Flags		= XPKM_Flags;
    xpkm->xmp_Timeout		= TimeOut;
    xpkm->xmp_DefaultType	= (struct XpkTypeData *) ptr;
    ptr += sizeof(struct XpkTypeData);

    switch(r->pn_Cycle)
    {
    case 0:
      xpkd->xtd_Mode = r->pn_Mode;
      xpkd->xtd_ChunkSize = r->pn_ChunkSize;
      xpkd->xtd_StdID = r->pn_StdID;
      break;
    case 1: xpkd->xtd_Flags |= XTD_NoPack; break;
    case 2: xpkd->xtd_Flags |= XTD_ReturnError; break;
    }

    if(!(error = PushChunk(iff, 0, ID_XPKM, ptr)))
    {
      if(WriteChunkBytes(iff, data, ptr) != ptr)
	error = 3;
      else
	error = PopChunk(iff);
    }
    FreeMem(data, XPKM_Size);
  }
  else
    error = 2;

  return error;
}

#define xpkt		((struct XpkTypePrefs *) data)
#define xtd		((struct XpkTypeData *) (data + sizeof(struct XpkTypePrefs)))

#define XPKT_Size	(TypeSIZE + 1 + NamePattSIZE + 1 + \
			FilePattSIZE + 1 + sizeof(struct XpkTypePrefs)) \
			+ sizeof(struct XpkTypeData)

ULONG WriteXPKT(struct IFFHandle *iff, struct PackerNode *r, ULONG num)
{
  STRPTR data;
  ULONG error, ptr = sizeof(struct XpkTypePrefs) + sizeof(struct XpkTypeData);

  if(!r->pn_Cycle && !r->pn_StdID)
  {
    PrintError(text[TEXT_ILLEGAL_ENTRY], text[GADG_Library_Name], num+1, r->pn_TypeName);
    return 1;
  }
  else if(!r->pn_NamePattern[0] && !r->pn_FilePattern[0])
  {
    PrintError(text[TEXT_ILLEGAL_ENTRY], text[GADG_File_Pattern], num+1, r->pn_TypeName);
    return 1;
  }

  if((data = (STRPTR) AllocMem(XPKT_Size, MEMF_ANY|MEMF_CLEAR)))
  {
    if(CopyData(r->pn_NamePattern, &ptr, data, &xpkt->xtp_NamePattern))
      xpkt->xtp_Flags |= XPKT_NamePattern;
    if(CopyData(r->pn_FilePattern, &ptr, data, &xpkt->xtp_FilePattern))
      xpkt->xtp_Flags |= XPKT_FilePattern;
    CopyData(r->pn_TypeName, &ptr, data, &xpkt->xtp_TypeName);
    xpkt->xtp_PackerData = (struct XpkTypeData *) sizeof(struct XpkTypePrefs);

    switch(r->pn_Cycle)
    {
    case 0:
      xtd->xtd_Mode = r->pn_Mode;
      xtd->xtd_ChunkSize = r->pn_ChunkSize;
      xtd->xtd_StdID = r->pn_StdID;
      break;
    case 1: xtd->xtd_Flags |= XTD_NoPack; break;
    case 2: xtd->xtd_Flags |= XTD_ReturnError; break;
    }

    if(!(error = PushChunk(iff, 0, ID_XPKT, ptr)))
    {
      if(WriteChunkBytes(iff, data, ptr) != ptr)
	error = 3;
      else
	error = PopChunk(iff);
    }
    FreeMem(data, XPKT_Size);
  }
  else
    error = 2;

  return error;
}

ULONG SaveFile(STRPTR file, ULONG icons)
{
  struct IFFHandle *iff;
  ULONG error = 0;

  if(file == (STRPTR) 1)
    file = "ENVARC:xpkmaster.prefs";
  else if(!file)
    file = "ENV:xpkmaster.prefs";

  if((iff = AllocIFF()))
  {
    if((iff->iff_Stream = Open(file, MODE_NEWFILE)))
    {
      InitIFFasDOS(iff);
      if(!(error = OpenIFF(iff, IFFF_WRITE)))
      {
        if(!(error = PushChunk(iff, ID_PREF, ID_FORM, IFFSIZE_UNKNOWN)))
        {
          if(!(error = PushChunk(iff, 0, ID_PRHD, sizeof(struct PrefHeader))))
          {
	    struct PrefHeader a = {PREF_VERSION, 0, 0};
	    if(WriteChunkBytes(iff, &a, sizeof(struct PrefHeader))
	    == sizeof(struct PrefHeader))
	    {
	      if(!(error = PopChunk(iff)))
	      {
	        if(!(error = WriteXPKM(iff)))
	        {
		  struct Node *n = FindNode(&maindata.ld_List, 1);
		  ULONG num = 1, a;
		  for(a = maindata.ld_entries-1; a; --a)
		  {
		    if((error = WriteXPKT(iff, (struct PackerNode *) n, num)))
		      break;
		    n = n->ln_Succ; ++num;
		  }
	        }
	      }
	    }
	    else
	      error = 2;
	  }
	  if(!error)
	    error = PopChunk(iff);
	}
        CloseIFF(iff);
      }
      Close(iff->iff_Stream);
      if(error)
      	DeleteFile(file);
    }
    else
    {
      UBYTE err[100];
      Fault(IoErr(), 0, err, 100);
      PrintError(text[TEXT_ERROR_FILEACCESS], file, &err);
      error = 1;
    }
    FreeIFF(iff);
  }

  if(!error && icons)
  {
    ULONG i = SDI_strlen(file);
    STRPTR a;

    if((a = (STRPTR) AllocMem(i+1+5, MEMF_ANY)))
    {
      ULONG fh;

      CopyMem(file, a, i);
      CopyMem(".info", a+i, 6);
      if((fh = Open(a, MODE_NEWFILE)))
      {
	Write(fh, PicData, PicSize);
	Close(fh);
      }
      FreeMem(a, i+1+5);
    }
  }

  if(error && error != 1)
    PrintError(text[TEXT_ERROR_FILEPROCESS]);

  return error;
}

/* ---------------------- graphics functions --------------------------- */

void PrintError(STRPTR txt, ...)
{
  struct EasyStruct easystruct;

  SDI_memset(&easystruct, 0, sizeof(struct EasyStruct));

  easystruct.es_StructSize = sizeof(struct EasyStruct);
  easystruct.es_Title = text[TEXT_TITLE_ERR_REQUEST];
  easystruct.es_TextFormat = txt;
  easystruct.es_GadgetFormat = text[GADGS_ERR_REQUEST];

  EasyRequestArgs(window, &easystruct, 0, (APTR)
  ((ULONG) &txt + sizeof(STRPTR)));
}

void FileReq(void)
{
  ULONG Long;
  UBYTE libname[] = "x..____.library";
  struct FileRequester *r;

  CopyMem(&maindata.ld_curNode->pn_StdID, libname+3, 4);
  if(libname[3] == 'X')
  {
    libname[1] = 'e'; libname[2] = 'x';
  }
  else
  {
    libname[1] = 'p'; libname[2] = 'k';
  }

  if((AslBase = OpenLibrary("asl.library", 37)))
  {
    if((r = (struct FileRequester *) AllocAslRequest(ASL_FileRequest, 0)))
    {
      if((AslRequestTags(r, ASLFR_TitleText, text[TEXT_SELECT_LIBRARY],
	ASLFR_InitialFile, libname,
	ASLFR_InitialDrawer, "LIBS:compressors", ASLFR_DoSaveMode, FALSE,
	ASLFR_InitialPattern, "(xpk|xex)????.library", TAG_DONE)))
        if((!SDI_strncmp(r->fr_File, "xpk", 3) ||
        !SDI_strncmp(r->fr_File, "xex", 3) &&
        !SDI_strcmp(r->fr_File+7, ".library")) &&
        (Long = SDIConvToUpper(r->fr_File+3)))
	  maindata.ld_curNode->pn_StdID = Long;
	else
	  DisplayBeep(Scr);
      FreeAslRequest(r);
    }
    CloseLibrary(AslBase);
    AslBase = 0;
  }
  else
    PrintError(text[TEXT_LIB_REQUIRED], "asl.library", 37);
}

ULONG PrefsFileReq(STRPTR dir, ULONG mode, STRPTR title)
{
  ULONG error = 0;
  struct FileRequester *r;

  if((AslBase = OpenLibrary("asl.library", 37)))
  {
    if((r = (struct FileRequester *) AllocAslRequest(ASL_FileRequest, 0)))
    {
      UBYTE d[200], *f = 0;

      if(dir)
      {
        CopyMem(dir, d, 200);
        if((f = FilePart(dir)))
  	  d[f-dir] = 0;
      }
      
      if((AslRequestTags(r, ASLFR_TitleText, title,
	ASLFR_InitialDrawer, dir ? d : "SYS:Prefs/Presets",
	ASLFR_DoSaveMode, mode,	(f ? ASLFR_InitialFile : TAG_DONE), f,
	TAG_DONE)))
// only give ASLFR_InitialFile, when f is not zero, because else
// asl.library makes Enforcer hit!
      {
        CopyMem(r->fr_Drawer, namestring, SDI_strlen(r->fr_Drawer)+1);
        AddPart(namestring, r->fr_File, 256);
        error = 1;
      }
      FreeAslRequest(r);
    }
    CloseLibrary(AslBase);
    AslBase = 0;
  }
  else
    PrintError(text[TEXT_LIB_REQUIRED], "asl.library", 37);

  return error;
}

void FreeListView(void)
{
  GT_SetGadgetAttrs(gadgdata[0], window, 0, GTLV_Labels, ~0, TAG_DONE);
}

void ResetListView(ULONG number, struct ListData *dat)
{
  STRPTR lib = "";

  if(number == ~0)
    number = dat->ld_curNumber;
  dat->ld_curNode = (struct PackerNode *) FindNode(&dat->ld_List, number);

  // NamePattern
  GT_SetGadgetAttrs(gadgdata[1], window, 0, GTST_String,
  dat->ld_curNode->pn_NamePattern, GA_Disabled, !number, TAG_DONE);

  // FilePattern
  GT_SetGadgetAttrs(gadgdata[2], window, 0, GTST_String,
  dat->ld_curNode->pn_FilePattern, GA_Disabled, !number, TAG_DONE);

  // Cycle
  GT_SetGadgetAttrs(gadgdata[7], window, 0, GTCY_Active,
  dat->ld_curNode->pn_Cycle, TAG_DONE);

  // Listview
  GT_SetGadgetAttrs(gadgdata[0], window, 0, GTLV_Labels, &dat->ld_List,
  GTLV_Selected, number, GTLV_Top, ((LONG) number - 5 < 0 ?
  0 : number - 5), TAG_DONE);

  if(dat->ld_curNode->pn_StdID)
    lib = (STRPTR) &dat->ld_curNode->pn_StdID;

  // Name
  GT_SetGadgetAttrs(gadgdata[3], window, 0, GTST_String,
  lib, GA_Disabled, dat->ld_curNode->pn_Cycle, TAG_DONE);

  // GetImage
  GT_SetGadgetAttrs(gadgdata[4], window, 0, GA_Disabled,
  dat->ld_curNode->pn_Cycle, TAG_DONE);

  // Mode
  GT_SetGadgetAttrs(gadgdata[5], window, 0, GTIN_Number,
  dat->ld_curNode->pn_Mode, GA_Disabled, dat->ld_curNode->pn_Cycle,
  TAG_DONE);

  // ChunkSize
  GT_SetGadgetAttrs(gadgdata[6], window, 0, GTIN_Number,
  dat->ld_curNode->pn_ChunkSize, GA_Disabled, dat->ld_curNode->pn_Cycle,
  TAG_DONE);

  dat->ld_curNumber = number;
}

void SetMainData(void)
{
  ULONG i;

  for(i = 0; i < 3; i++)
  {
    GT_SetGadgetAttrs(gadgdata[9+i], window, 0, GTCB_Checked,
    XPKM_Flags & (1<<i), TAG_DONE);
  }
  GT_SetGadgetAttrs(gadgdata[8], window, 0, GTIN_Number, TimeOut, TAG_DONE);
}

ULONG DoWindow(void)
{
  UWORD	winwidth, winheight;
  STRPTR *txt = text + GADG_New;
  ULONG i, j, k;
  struct Gadget		*g;
  struct NewGadget	ng;

  SDI_memset(&ng, 0, sizeof(struct NewGadget));

  ng.ng_LeftEdge = Scr->WBorLeft + SEPARATE_BORD;
  ng.ng_TopEdge = Scr->WBorTop + Scr->Font->ta_YSize + 1 + SEPARATE_BORD;
  ng.ng_Height = Scr->Font->ta_YSize+5;
  ng.ng_TextAttr = Scr->Font;
  ng.ng_GadgetID = 100;
  ng.ng_VisualInfo = VisualInfo;

  if(!(g = CreateContext(&gadgList)))
    return 1;

  for(i = k = 0; i < 4; ++i)
  {
    ng.ng_Width = 7 + TextLength(&Scr->RastPort, txt[i], SDI_strlen(txt[i]));
    if(ng.ng_Width > k)
      k = ng.ng_Width;
  }
  ng.ng_Width = k << 2;

  if(!(g = CreateGadget(STRING_KIND, g, &ng, GTST_MaxChars, TypeSIZE, TAG_DONE)))
    return 2;
  ++ng.ng_GadgetID;

  ng.ng_Height *= 10;

  if(!(gadgdata[0] = g = CreateGadget(LISTVIEW_KIND,g,&ng,
  GTLV_ShowSelected, g, TAG_DONE)))
    return 3;

  ++ng.ng_GadgetID;
  ng.ng_LeftEdge = Scr->WBorLeft + SEPARATE_BORD;
  ng.ng_TopEdge += g->Height + Scr->Font->ta_YSize+5;
  ng.ng_Height = Scr->Font->ta_YSize+5;
  ng.ng_Flags = PLACETEXT_IN;
  ng.ng_Width = k;

  for(i = 0; i < 4; ++i)
  {
    ng.ng_GadgetText = *(txt++);
    if(!(g = CreateGadgetA(BUTTON_KIND, g, &ng, 0)))
      return 4;
    ++ng.ng_GadgetID;
    ng.ng_LeftEdge += ng.ng_Width;
  }

  winheight = ng.ng_TopEdge + ng.ng_Height + SEPARATE_DIFF;

  ng.ng_TopEdge = Scr->WBorTop + Scr->Font->ta_YSize + 1 + SEPARATE_BORD;
  txt = text + GADG_Name_Pattern;

  for(i = k = 0; i < 6; ++i)
  {
    j = 7 + TextLength(&Scr->RastPort, txt[i], SDI_strlen(txt[i]));
    if(j > k)
      k = j;
  }

  j = ng.ng_LeftEdge + SEPARATE_DIFF; /* store startpos */
  ng.ng_LeftEdge = j + k;

  ng.ng_Width = 0;
  for(i = 0; i < 3; ++i)
  {
    STRPTR *tx = text + TEXT_START_SLIDER;
    k = 50 + TextLength(&Scr->RastPort, tx[i], SDI_strlen(tx[i]));
    if(k > ng.ng_Width)
      ng.ng_Width = k;
  } /* width of gadgets is the one of the cycle gadget */

  ng.ng_Flags = PLACETEXT_LEFT;
  winwidth = ng.ng_LeftEdge + ng.ng_Width; /* set resulting window width */

  for(i = 0; i < 2; ++i)
  {
    ng.ng_GadgetText = *(txt++);
    if(!(gadgdata[i+1] = g = CreateGadget(STRING_KIND,g,&ng, GTST_MaxChars,
    (i ? FilePattSIZE : NamePattSIZE), TAG_DONE)))
      return 5;
    ++ng.ng_GadgetID;
    ng.ng_TopEdge += ng.ng_Height + SEPARATE_SAME;
  }

  ng.ng_Width -= ng.ng_Height;
  ng.ng_GadgetText = *(txt++);
  if(!(gadgdata[3] = g = CreateGadget(STRING_KIND,g,&ng, GTST_MaxChars, 4,
  STRINGA_Justification, GACT_STRINGCENTER, TAG_DONE)))
    return 6;
  ++ng.ng_GadgetID;
  ng.ng_GadgetText = 0;
  k = ng.ng_Width;
  ng.ng_Width = ng.ng_Height;
  ng.ng_LeftEdge += k;
  if(!(gadgdata[4] = g = CreateGadgetA(BUTTON_KIND,g,&ng, 0)))
    return 7;
  ng.ng_LeftEdge -= k;
  ng.ng_Width += k;
  ++ng.ng_GadgetID;
  ng.ng_TopEdge += ng.ng_Height + SEPARATE_SAME;

  for(i = 0; i < 2; ++i)
  {
    ng.ng_GadgetText = *(txt++);
    if(!(gadgdata[i+5] = g = CreateGadget(INTEGER_KIND,g,&ng, 
    STRINGA_Justification, GACT_STRINGCENTER, TAG_DONE)))
      return 8;
    ++ng.ng_GadgetID;
    ng.ng_TopEdge += ng.ng_Height + SEPARATE_SAME;
  }

  ng.ng_GadgetText = *txt;

  if(!(gadgdata[7] = g = CreateGadget(CYCLE_KIND,g,&ng, GTCY_Labels,
  text + TEXT_START_SLIDER, TAG_DONE)))
      return 9;

  ++ng.ng_GadgetID;
  ng.ng_TopEdge += ng.ng_Height + SEPARATE_DIFF;
  txt = text + GADG_Use_XFD;
  ng.ng_LeftEdge = j;

  for(i = k = 0; i < 3; ++i)
  {
    ng.ng_Width = 50 + TextLength(&Scr->RastPort, txt[i], SDI_strlen(txt[i]));
    if(ng.ng_Width > k)
      k = ng.ng_Width;
  }

  ng.ng_GadgetText = txt[3];
  ng.ng_LeftEdge += k + ng.ng_Height;
  if((ng.ng_Width = 7 + TextLength(&Scr->RastPort, txt[3],
  SDI_strlen(txt[3]))) < Scr->RastPort.TxWidth << 3)
    ng.ng_Width = Scr->RastPort.TxWidth << 3;
  ng.ng_Flags = PLACETEXT_BELOW;
  if(!(gadgdata[8] = g = CreateGadget(INTEGER_KIND,g,&ng, GTIN_MaxChars, 5, STRINGA_Justification,
  GACT_STRINGCENTER, TAG_DONE)))
      return 10;
  ++ng.ng_GadgetID;
  ng.ng_LeftEdge -= k + ng.ng_Height;
  ng.ng_Width = ng.ng_Height;
  ng.ng_Flags = PLACETEXT_RIGHT;

  for(i = 0; i < 3; ++i)
  {
    ng.ng_GadgetText = *(txt++);
    if(!(gadgdata[9+i] = g = CreateGadgetA(CHECKBOX_KIND,g,&ng,0)))
      return 11;
    ++ng.ng_GadgetID;
    ng.ng_TopEdge += ng.ng_Height;
  }

  if(winheight < ng.ng_TopEdge + 5)
    winheight = ng.ng_TopEdge + SEPARATE_DIFF;

  ng.ng_TopEdge = winheight;
  ng.ng_LeftEdge = Scr->WBorLeft + SEPARATE_BORD;
  ng.ng_Flags = PLACETEXT_IN;
  txt = text + GADG_Save;

  ng.ng_Width = 0;
  for(i = 0; i < 3; ++i)
  {
    k = 7 + TextLength(&Scr->RastPort, txt[i], SDI_strlen(txt[i]));
    if(k >ng.ng_Width)
      ng.ng_Width = k;
  }
  k = (winwidth - SEPARATE_BORD - 3*ng.ng_Width)/6;
  ng.ng_LeftEdge += k;

  for(i = 0; i < 3; ++i)
  {
    ng.ng_GadgetText = *(txt++);
    if(!(g = CreateGadgetA(BUTTON_KIND, g, &ng, 0)))
      return 12;
    ++ng.ng_GadgetID;
    ng.ng_LeftEdge += ng.ng_Width + (k<<1);
  }  

  winwidth += Scr->WBorRight + SEPARATE_BORD;
  winheight += Scr->WBorBottom + ng.ng_Height + SEPARATE_BORD;

  if(winwidth > Scr->Width || winheight > Scr->Height)
  {
    PrintError(text[TEXT_NO_WINDOW],0);
    return 20;
  }

  if(!(menus = CreateMenusA(NewMenus, 0)))
    return 21;

  LayoutMenus(menus, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_DONE);

  if(!(window = OpenWindowTags(0,
	WA_Top,		Scr->BarHeight + 1,
	WA_Width,	winwidth,
	WA_Height,	winheight,
	WA_IDCMP,	IDCMP_GADGETUP|IDCMP_MENUPICK|IDCMP_GADGETDOWN|
			IDCMP_MOUSEMOVE|IDCMP_INTUITICKS|IDCMP_MOUSEBUTTONS,
	WA_Flags,	WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_SMART_REFRESH,
	WA_Gadgets,	gadgList,
	WA_Title,	text[TEXT_WINDOW_TITLE],
	WA_AutoAdjust,	TRUE,
        WA_Activate,	TRUE,
	WA_NewLookMenus,TRUE,
	WA_PubScreen,	Scr,
	TAG_DONE)))
    return 22;

  ResetListView(0, &maindata);
  SetMainData();
  SetMenuStrip(window, menus);
  GT_RefreshWindow(window,0);
  ScreenToFront(Scr);

  return 0;
}

/* --------------------------- list functions -------------------------- */

void NewPackerList(struct ListData *ld)
{
  if(ld->ld_entries)
    FreePackerList(ld);
  TimeOut = DEF_TIMEOUT; XPKM_Flags = DEF_XPKM_FLAGS;
  if(!NewPackerNode(&maindata, text[TEXT_ENTRY_DEFAULT], 0, 0))
    End(RETURN_FAIL);
  maindata.ld_curNode->pn_Cycle = DEF_CYCLE;
}

void FreePackerList(struct ListData *li)
{
  struct Node *n;
  while((n = RemHead(&li->ld_List)))
    FreeMem(n, sizeof(struct PackerNode));
  li->ld_curNumber = li->ld_entries = 0;
  li->ld_curNode = 0;
}

struct Node *FindNode(struct List *li, ULONG num)
{
  struct Node *n = li->lh_Head;

  ++num;
  if(n == (struct Node *) li)
    return 0;
  while(--num)
  {
    if(n == li->lh_TailPred)
      return 0;
    n = n->ln_Succ;
  }
  return n;
}

struct PackerNode *NewPackerNode(struct ListData *li, STRPTR name,
struct PackerNode *pref, ULONG mode)
{
  struct PackerNode *n;
  ULONG i;

  if(!(n = (struct PackerNode *) AllocMem(sizeof(struct PackerNode), MEMF_ANY|MEMF_CLEAR)))
    return 0;

  if(pref)
    CopyMem((STRPTR) pref + sizeof(struct Node), (STRPTR) n +
    sizeof(struct Node), sizeof(struct PackerNode) - sizeof(struct Node));

  n->pn_Node.ln_Name = n->pn_TypeName;
  n->pn_Node.ln_Type = mode ? PACKER_NODE : DEFAULT_NODE;

  if((i = SDI_strlen(name) + 1) > TypeSIZE)
    i = TypeSIZE;
  CopyMem(name, &n->pn_TypeName, i);

  if(li)
  {
    struct Node *p;
    if((p = FindNode(&li->ld_List, mode)))
    {
      Insert(&li->ld_List, (struct Node *) n, p);
      li->ld_curNumber = mode + 1;
    }
    else
    {
      AddTail(&li->ld_List, (struct Node *) n);
      li->ld_curNumber = li->ld_entries;
    }
    li->ld_curNode = n;
    ++li->ld_entries;
  }

  return (struct PackerNode *) n;
}

/* ---------------------- the cleanup stuff ---------------------------- */

void end(void)
{
  if(diskobject)	FreeDiskObject(diskobject);
  if(window)	 	CloseWindow(window);
  if(menus)		FreeMenus(menus);
  if(gadgList)		FreeGadgets(gadgList);
  if(VisualInfo)	FreeVisualInfo(VisualInfo);
  if(Scr)		UnlockPubScreen(0, Scr);
  if(GfxBase)		CloseLibrary((struct Library *) GfxBase);
  if(UtilityBase)	CloseLibrary(UtilityBase);
  if(GadToolsBase)	CloseLibrary(GadToolsBase);
  if(IntuitionBase)	CloseLibrary((struct Library *) IntuitionBase);
  if(IconBase)		CloseLibrary(IconBase);
  if(rda)		FreeArgs(rda);
  FreePackerList(&maindata);
  SDI_FreeLocale(&locdat);
  if(lock)		CurrentDir(lock);
}

