/*
	Namn		:	rxcommands.c
	Skapad		:	25/09/95
	Senast ändrad	:	10/02/98
	Programmerare	:	Urban Lindeskog
	Organisation	:	ProNotion SoftWare Development Group

	Funktion	:	Arexx-funktionerna
	Kommentarer	:
	Att göra	:

*/


// #define DEBUG 1


 //	Includes och Defines
//
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/asl.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/intuition.h>
#include <proto/rexxsyslib.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <dos/dosextens.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <libraries/asl.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <rexx/rxslib.h>
#include <rexx/errors.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "rxcommands.h"
#include "globdef.h"
#include "scsicd.h"
#include "rev.h"
#include "texts.h"
#include "protos/support.h"
#include "protos/rxcommands.h"
#include "protos/CDFuncs.h"
#include "protos/main.h"


#define	DEFAULTLEARNFILE "REXX:RexxCD_Learn.rexx"


  //	Externa variabler
 //
//
extern	UBYTE			*VendorID;
extern	UBYTE			*ProductID;
extern	UBYTE			*ProductRev;
extern	UBYTE			*VendorSpec;
extern	UBYTE			CD_Identifier[], CD_EANBarCode[];
extern	UBYTE			*CD_Titles[];
extern	UBYTE			*CD_Artist[];
extern	UBYTE			*TrackElapsedTime;
extern	UBYTE			*DiscElapsedTime;
extern	UBYTE			**LocaleArray;
extern	ULONG			Current_Index;
extern	UBYTE			*Current_IndexStr; // *Namnet på den aktuella indexet
extern	ULONG			TotalTime, TitleTime, CDStatus, CurrentTrack, CDUnitNumber, DriveType;
extern	ULONG			BlocksPerSec, StereoVolume, Repeat;
extern	UBYTE			*StrVarAry[];
extern	struct	CD_TOC		*TOCBuffer;
extern	struct	Window		*Wins[];
extern	struct	BulkWin		BWins[];
extern	struct	List		*CD_Lyrics[];
extern	APTR			SettingAslReq;
extern	APTR			FontAslReq;
extern	USHORT			*BitMapAry[];
extern	struct	Gadget		*TrackCycleGad;
extern	ULONG			FontSizeAry[];
extern	ULONG			lvol, rvol;
extern	struct	Gadget		*LeftVolGad, *RightVolGad;
extern	BOOL			RunLyricMacros;
extern	ULONG			GlobalRefresh;
extern	ULONG			Compatibility;


  //	Globala variabler
 //
//
struct	List			*LearnBufList=NULL;
struct	RDArgs			*TmpRDArgs=NULL;
struct	BulkWin			BulkWindow;
ULONG				RxArgs[MAX_RX_ARGS];
UWORD				CmdHash[256];
UBYTE				*CommandBuffer=NULL, *StringBuffer=NULL, *LineBuffer=NULL;
BOOL				DebugEnabled=FALSE, Learn=FALSE;
BPTR				DebugCON=NULL;
UBYTE				*AAStrPtr=NULL;
ULONG				AAInt=0;
UBYTE				TempNumBuf[20];
ULONG				PendingRxReplies=0;
struct	MsgPort			*RxReplyPort=NULL;
void				*RxMemPool=NULL;
UBYTE				TmpTrkLen[8]={0, 0, 0, 0, 0, 0, 0, 0};
UBYTE				*LearnFile=NULL;
UBYTE				*FontNameAry[]=
					{
					"TEXTFONT",
					"DIGITFONT",
					"LYRICFONT",
					NULL
					};
UBYTE				*WinNameAry[]=
					{
					"MAIN",
					"LYRICS",
					"LYRICS_EDITOR",
					"ABOUT",
					"DEBUG",
					"SETTINGS_FREQ",
					"MACROS_FREQ",
					NULL
					};

  //	Prototyper för dina implementerade kommandon.
 //	RxStub är funktionen som anropas om en funktion inte är definierad.
//
RxProto (RxStub);
RxProto (RxDisable);
RxProto (RxEnable);
RxProto (RxNOP);
RxProto (RxHelp);
RxProto (RxQuit);
RxProto (RxDeBug);
RxProto (RxGetAttr);
RxProto (RxSetAttr);
RxProto (RxPlay);
RxProto (RxEject);
RxProto (RxInsert);
RxProto (RxLock);
RxProto (RxUnLock);
RxProto (RxSaveSettings);
RxProto (RxLoadSettings);
RxProto (RxStart);
RxProto (RxStop);
RxProto (RxTest);
RxProto (RxPause);
RxProto (RxResume);
RxProto (RxSkip);
RxProto (RxVolume);
RxProto (RxActivate);
RxProto (RxDeActivate);
RxProto (RxLearn);
RxProto (RxButton);


//	Standarliserade ARexx-kommandon som ett program kan tänkas stödja,
//	om du implementerar ett kommando, se till att det har samma
//	funktion som Amiga User Interface Style Guide (sid. 123-140)
//	definerat det som. Om inte, ta ett annat namn.
//	Sortera dem i bokstavsordning för att snabba upp tolkningen
//	(Om ett kommando ligger i fel ordning kommer tolken inte ens hitta det)
//
//	Format:
//	Namn, Template, Funktionspekare
//
//
//
struct	RxCommand	RxCommands[]=
	{
//	{"ALIAS", "NAME/A,COMMAND/F", &RxStub, 0},		// Implementeras i tolken
	{"ACTIVATE", "PUBSCREEN", &RxActivate, 0},
//	{"ACTIVATEWINDOW", ",", &RxStub, 0},
//	{"BACKGROUNDPEN", "/N", &RxStub, 0},
//	{"BAUD", "/N", &RxStub, 0},
	{"BUTTON", "NAME/A,LABEL/K,WINDOW/K,PROMPT/S,CMD/F", &RxButton, 0},
//	{"CAPTURE", "NAME/K", &RxStub, 0},
//	{"CHANGEWINDOW", "LEFTEDGE/N,TOPEDGE/N,WIDTH/N,HEIGHT/N", &RxStub, 0},
//	{"CLEAR", "FORCE/S", &RxStub, 0},
//	{"CLOSE", "FORCE/S", &RxStub, 0},
//	{"CMDSHELL", "OPEN/S,CLOSE/S", &RxStub, 0},
//	{"COLUMN", "/N/A", &RxStub, 0},
//	{"COPY", ",", &RxStub, 0},
//	{"CURSOR", "UP/S,DOWN/S,LEFT/S,RIGHT/S", &RxStub, 0},
//	{"CUT", ",", &RxStub, 0},
	{"DEACTIVATE", ",", &RxDeActivate, 0},
   //	{"DEBUG", "OFF/S", &RxDeBug, 0},
//	{"DIAL", "NUM/F", &RxStub, 0},
	{"DISABLE", "NAMES/M", &RxDisable, 0},
//	{"DUPLEX", "FULL/S,HALF/S", &RxStub, 0},
	{"EJECT", ",", &RxEject, 0},
	{"ENABLE", "NAMES/M", &RxEnable, 0},
//	{"ERASE", "FORCE/S", &RxStub, 0},
//	{"FAULT", "/N", &RxStub, 0},
//	{"FIND", "TEXT/F", &RxStub, 0},
//	{"FINDCHANGE", "ALL/S,PROMPT/S,FIND/K,CHANGE/K", &RxStub, 0},
//	{"FINDNEXT", ",", &RxStub, 0},
//	{"FONT", "NAME/S,SIZE/N,ITALIC/S,BOLD/S,PLAIN/S,UNDERLINED/S", &RxStub, 0},
	{"GETATTR", "OBJECT/A,NAME,FIELD,STEM/K,VAR/K", &RxGetAttr, 0},
//	{"GOTOBOOKMARK", "/S", &RxStub, 0},
//	{"GOTOCOLUMN", "/N/A", &RxStub, 0},
//	{"GOTOLINE", "/N/A", &RxStub, 0},
	{"HELP", "COMMAND,PROMPT/S", &RxHelp, 0},
	{"INSERT", ",", &RxInsert, 0},
//	{"KEYBOARD", "KEY/A,WINDOW/K,GLOBAL/S,HOTKEY/S,PROMPT/S,CMD/F", &RxStub, 0},
    //	{"LEARN", "FILE/K,STOP/S", &RxLearn, 0},
//	{"LINE", "/N/A", &RxStub, 0},
	{"LOADSETTINGS", "NAME/K,PROMPT/S", &RxLoadSettings, 0},
	{"LOCK", ",", &RxLock, 0},
//	{"LOCKGUI", ",", &RxStub, 0},
//	{"LOWERCASE", "CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S", &RxStub, 0},
//	{"MENU", "NAME/A,LABEL/K,MENU/K,WINDOW/K,PROMPT/K,CMD/F", &RxStub, 0},
//	{"MOVEWINDOW", "LEFTEDGE/N,TOPEDGE/N", &RxStub, 0},
//	{"NEW", "PORTNAME/K", &RxStub, 0},
//	{"NEXT", "WORD/S,SENTENCE/S,PARAGRAPH/S,PAGE/S", &RxStub, 0},
	{"NOP", ",", &RxNOP, 0},
//	{"OPEN", "FILENAME/K,FORCE/S", &RxStub, 0},
//	{"PARITY", "EVEN/S,ODD/S,NONE/S", &RxStub, 0},
//	{"PASTE", ",", &RxStub, 0},
	{"PAUSE", ",", &RxPause, 0},
	{"PLAY", "TRACK/N,INDEX/K/N,OFFSET/K,PREVIOUS/S,TMOFFSET/K,BLOCKS/K/N,PREVIOUSINDEX/S,NEXTINDEX/S", &RxPlay, 0},
//	{"POSITION", "SOF/S,EOF/S,EOL/S,SOW/S,EOW/S,SOV/S,EOV/S", &RxStub, 0},
//	{"PREVIOUS", "WORD/S,SENTENCE/S,PARAGRAPH/S,PAGE/S", &RxStub, 0},
//	{"PRINT", "PROMPT/S", &RxStub, 0},
//	{"PROTOCOL", "/K", &RxStub, 0},
	{"QUIT", "FORCE/S", &RxQuit, 0},
//	{"REDIAL", ",", &RxStub, 0},
//	{"REDO", ",", &RxStub, 0},
	{"RESUME", ",", &RxResume, 0},
//	{"REQUESTFILE", "TITLE/K,PATH/K,FILE/K,PATTERN/K", &RxStub, 0},
//	{"REQUESTNOTIFY", "PROMPT/S", &RxStub, 0},
//	{"REQUESTNUMBER", "PROMPT/K,DEFAULT/K", &RxStub, 0},
//	{"REQUESTRESPONSE", "TITLE/K,PROMPT/K", &RxStub, 0},
//	{"REQUESTSTRING", "TEXT/A,DEFAULT", &RxStub, 0},
//	{"RX", "CONSOLE/S,ASYNC/S,COMMAND/F", &RxStub, 0},
//	{"SAVE", ",", &RxStub, 0},
//	{"SAVEAS", "NAME/K", &RxStub, 0},
	{"SAVESETTINGS", "NAME/K,PROMPT/S", &RxSaveSettings, 0},
//	{"SEND", "TEXT/F", &RxStub 0},
//	{"SENDFILE", "NAME/K", &RxStub, 0},
	{"SETATTR", "OBJECT/A,NAME,FIELD,STEM/K,VAR/K,VALUE/K,PROMPT/S", &RxSetAttr, 0},
//	{"SETBOOKMARK", "/S", &RxStub, 0},
//	{"SIZEWINDOW", "WIDTH/N,HEIGHT/N", &RxStub, 0},
	{"SKIP", "TRACKS/K/N,SECONDS/K/N,BLOCKS/K/N,BACK/S", &RxSkip, 0},
	{"START", ",", &RxStart, 0},
	{"STOP", ",", &RxStop, 0},
//	{"STOPBITS", "1/S,0/S", &RxStub, 0},
//	{"SWAPCASE", "CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S", &RxStub, 0},
    // 	{"TEST", "NUM/N", &RxTest, 0},
//	{"TEXT", "NOLINE/S,TEXT/F", &RxStub, 0},
//	{"TEXTPEN", "/N", &RxStub, 0},
//	{"TIMEOUT", "/N", &RxStub, 0},
//	{"UNDO", ",", &RxStub, 0},
	{"UNLOCK", ",", &RxUnLock, 0},
//	{"UNLOCKGUI", ",", &RxStub, 0},
//	{"UNZOOMWINDOW", ",", &RxStub, 0},
//	{"UPPERCASE", "CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S", &RxStub, 0},
	{"VOLUME", "LEFT/N,RIGHT/N", &RxVolume, 0},
//	{"WAIT", ",", &RxStub, 0},
//	{"WINDOW", "NAMES/M,OPEN/S,CLOSE/S,SNAPSHOP/S,ACTIVATE/S,MIN/S,MAX/S,FRONT/S,BACK/S", &RxStub, 0},
//	{"WINDOWTOBACK", ",", &RxStub, 0},
//	{"WINDOWTOFRONT", ",", &RxStub, 0},
//	{"ZOOMWINDOW", ",", &RxStub, 0},

	// Terminerar listan
	{NULL, NULL, NULL, 0}
	};


 //	Prototyper för Update_ -funktioner
//
UpdateProto(Update_DevName);
UpdateProto(Update_DevUnit);
UpdateProto(Update_Artist);
UpdateProto(Update_Title);
UpdateProto(Update_TrackCount);
UpdateProto(Update_TrackArtist);
UpdateProto(Update_TrackTitle);
UpdateProto(Update_TrackLength);
UpdateProto(Update_TrackLyrics);
UpdateProto(Update_Drive);
UpdateProto(Update_Disc);
UpdateProto(Update_DiscLength);
UpdateProto(Update_ID);
UpdateProto(Update_BarCode);
UpdateProto(Update_Bps);
UpdateProto(Update_Stereo);
UpdateProto(Update_IDFile);
UpdateProto(Update_BulkWindow);
UpdateProto(Update_BulkFont);
UpdateProto(Update_Repeat);
UpdateProto(Update_CreateIcons);
UpdateProto(Update_BulkVolume);
UpdateProto(Update_AllowMacros);


struct ApplAttrs ProjectAttrs[]=
	{
	// APPLICATION.#?
	{"APPLICATION",	AAF_OBJECT, (APTR)NULL, NULL},
	{"NAME",	AAF_FIELD, (APTR)&progname, NULL},
	{"VERSION",	AAF_FIELD, (APTR)&versstr, NULL},
	{"SCREEN",	AAF_FIELD | AAF_WRITE | AAF_SETTING | AAF_STRVAR, (APTR)SV_PUBSCREEN, NULL},
//	{"PRIORITY",	AAF_FIELD | AAF_WRITE | AAF_LONG | AAF_SETTING, (APTR)&AAInt, NULL},

	// SETTINGS.#?
	{"SETTINGS",	AAF_OBJECT, (APTR)NULL, NULL},
	{"REPEAT",	AAF_FIELD | AAF_SETTING | AAF_LONG | AAF_WRITE | AAF_UPDATE, (APTR)&AAInt, &Update_Repeat},
	{"CREATEICONS",	AAF_FIELD | AAF_SETTING | AAF_LONG | AAF_WRITE | AAF_UPDATE, (APTR)&AAInt, &Update_CreateIcons},
	{"ALLOWMACROS",	AAF_FIELD | AAF_SETTING | AAF_LONG | AAF_WRITE | AAF_UPDATE, (APTR)&AAInt, &Update_AllowMacros},
	{"TOCPATH",	AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_TOCPATH, NULL},

	// BUTTON.#?
	{"BUTTON",	AAF_OBJECT, (APTR)NULL, NULL},
	{"1",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_0, NULL},
	{"2",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_1, NULL},
	{"3",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_2, NULL},
	{"4",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_3, NULL},
	{"5",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_4, NULL},
	{"6",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_5, NULL},
	{"7",		AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_BUTTON_6, NULL},

	// ACTION.#?
	{"ACTION",	AAF_OBJECT, (APTR)NULL, NULL},
	{"NEWDISC",	AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR, (APTR)SV_ACTION_NEWDISC, NULL},

	// DEVICE.#?
	{"DEVICE",	AAF_OBJECT, (APTR)NULL, NULL},
	{"NAME",	AAF_FIELD | AAF_SETTING | AAF_WRITE | AAF_STRVAR | AAF_UPDATE, (APTR)SV_DEVICE, &Update_DevName},
	{"UNIT",	AAF_FIELD | AAF_SETTING | AAF_LONG | AAF_WRITE | AAF_UPDATE, (APTR)&CDUnitNumber, &Update_DevUnit},

	// DRIVE.#?
	{"DRIVE",	AAF_OBJECT | AAF_UPDATE, (APTR)NULL, &Update_Drive},
	{"NAME",	AAF_FIELD, (APTR)&ProductID, NULL},
	{"VENDOR",	AAF_FIELD, (APTR)&VendorID, NULL},
	{"REVISION",	AAF_FIELD, (APTR)&ProductRev, NULL},
	{"PRIVATE",	AAF_FIELD, (APTR)&VendorSpec, NULL},
	{"STATUS",	AAF_FIELD | AAF_LONG, (APTR)&AAInt, NULL},
	{"TYPE",	AAF_FIELD | AAF_LONG | AAF_WRITE, (APTR)&DriveType, NULL},
	{"BPS",		AAF_FIELD | AAF_LONG | AAF_WRITE | AAF_UPDATE, (APTR)&BlocksPerSec, &Update_Bps},
	{"STEREO",	AAF_FIELD | AAF_SETTING | AAF_LONG | AAF_WRITE | AAF_UPDATE, (APTR)&StereoVolume, &Update_Stereo},
	{"COMPATFIXES",	AAF_FIELD | AAF_STRVAR, (APTR)SV_TEMP2, NULL},

	// VOLUME.#?
	{"VOLUME",	AAF_OBJECT | AAF_UPDATE | AAF_BULK, (APTR)NULL, &Update_BulkVolume},
	{"LEFT",	AAF_FIELD | AAF_LONG | AAF_WRITE, (APTR)&lvol, NULL},
	{"RIGHT",	AAF_FIELD | AAF_LONG | AAF_WRITE, (APTR)&rvol, NULL},

	// DISC.#?
	{"DISC",	AAF_OBJECT | AAF_UPDATE, (APTR)NULL, &Update_Disc},
	{"ARTIST",	AAF_FIELD | AAF_UPDATE, (APTR)&AAStrPtr, &Update_Artist},
	{"TITLE",	AAF_FIELD | AAF_UPDATE, (APTR)&AAStrPtr, &Update_Title},
	{"ID",		AAF_FIELD | AAF_UPDATE, (APTR)&AAStrPtr, &Update_ID},
	{"BARCODE",	AAF_FIELD | AAF_UPDATE, (APTR)&AAStrPtr, &Update_BarCode},
	{"IDFILE",	AAF_FIELD | AAF_UPDATE, (APTR)&AAStrPtr, &Update_IDFile},
	{"LENGTH",	AAF_FIELD | AAF_UPDATE, (APTR)&AAStrPtr, &Update_DiscLength},
	{"ELAPSED",	AAF_FIELD, (APTR)&DiscElapsedTime, NULL},

	// TRACK.#?
	{"TRACK",	AAF_OBJECT, (APTR)NULL, NULL},
	{"COUNT",	AAF_FIELD | AAF_LONG | AAF_UPDATE, (APTR)&AAInt, &Update_TrackCount},
	{"CURRENT",	AAF_FIELD | AAF_LONG, (APTR)&CurrentTrack, NULL},
	{"CURRENTINDEX",AAF_FIELD | AAF_LONG, (APTR)&Current_Index, NULL},
	{"ELAPSED",	AAF_FIELD, (APTR)&TrackElapsedTime, NULL},
	{"ARTIST",	AAF_FIELD | AAF_UPDATE | AAF_NAME, (APTR)&AAStrPtr, &Update_TrackArtist},
	{"TITLE",	AAF_FIELD | AAF_UPDATE | AAF_NAME, (APTR)&AAStrPtr, &Update_TrackTitle},
	{"LENGTH",	AAF_FIELD | AAF_UPDATE | AAF_NAME, (APTR)&AAStrPtr, &Update_TrackLength},
	{"LYRICS",	AAF_FIELD | AAF_LONG | AAF_NAME | AAF_UPDATE, (APTR)&AAInt, &Update_TrackLyrics},

/*	// LYRIC.#?
	{"LYRIC",	AAF_OBJECT, (APTR)NULL, NULL},
	{"CURRENT",	AAF_FIELD | AAF_LONG, (APTR), NULL},
	{"COUNT",	AAF_FIELD | AAF_NAME | AAF_LONG, (APTR), NULL},
	{"",		AAF_FIELD | AAF_NAME | AAF_LIST, (APTR), NULL},
*/

	// FONT.#?
	{"FONT",	AAF_OBJECT | AAF_NAME | AAF_STRARY | AAF_UPDATE | AAF_BULK | AAF_SETTING, (APTR)&FontNameAry, &Update_BulkFont},
	{"NAME",	AAF_FIELD | AAF_NAME | AAF_WRITE | AAF_SETTING | AAF_STRVAR, (APTR)SV_TEMP2, NULL},
	{"SIZE",	AAF_FIELD | AAF_NAME | AAF_LONG | AAF_WRITE | AAF_SETTING, (APTR)&AAInt, NULL},

//	{"WINDOWS",	AAF_OBJECT | AAF_LIST | AAF_STRARY | AAF_UPDATE, (APTR)&WinNameAry, NULL},
//	{"COUNT",	AAF_FIELD | AAF_LONG, (APTR)NULL, NULL},

	// WINDOW.#?
	{"WINDOW",	AAF_OBJECT | AAF_NAME | AAF_STRARY | AAF_UPDATE | AAF_BULK | AAF_SETTING, (APTR)&WinNameAry, &Update_BulkWindow},
	{"LEFT",	AAF_FIELD | AAF_NAME | AAF_LONG | AAF_WRITE | AAF_SETTING, (APTR)&BulkWindow.Left, NULL},
	{"TOP",		AAF_FIELD | AAF_NAME | AAF_LONG | AAF_WRITE | AAF_SETTING, (APTR)&BulkWindow.Top, NULL},
	{"WIDTH",	AAF_FIELD | AAF_NAME | AAF_LONG | AAF_WRITE | AAF_SETTING, (APTR)&BulkWindow.Width, NULL},
	{"HEIGHT",	AAF_FIELD | AAF_NAME | AAF_LONG | AAF_WRITE | AAF_SETTING, (APTR)&BulkWindow.Height, NULL},
	{"TITLE",	AAF_FIELD | AAF_NAME | AAF_SETTING, (APTR)&BulkWindow.Title, NULL},
	{"OPEN",	AAF_FIELD | AAF_NAME | AAF_LONG | AAF_WRITE | AAF_SETTING, (APTR)&BulkWindow.Open, NULL},

	// Terminerar listan
	{NULL, 0, NULL, NULL}
	};



 //	ARexx-Funktionerna följer nedan:
//


/*
  //				  //
 //	RxTest()		 //
//				//
ULONG RxTest(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
UBYTE	Tmp[40];
struct	LyricNode	*NextNode, *ThisNode;
struct	RDArgs		*RxRDArgs=NULL;
ULONG			Result=RXCR_ALLOCERR;
LONG			temp=0, temp2=0;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;
	RxArgs[0]=(ULONG)NULL;		// NUM/N

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	if((RxIntArg(0, -1)==0))
		{
		if(!(CD_Lyrics[CurrentTrack]))
			return(NULL);

		ThisNode = (struct LyricNode *)CD_Lyrics[CurrentTrack]->lh_Head;

		while(NextNode = (struct LyricNode *)ThisNode->Node.ln_Succ)
			{
			sprintf(Tmp, "%02d:%02d.%1d-%02d:%02d.%1d", ThisNode->StartBlock/BlocksPerSec/60, ThisNode->StartBlock/BlocksPerSec%60, (ThisNode->StartBlock%60)/(BlocksPerSec/10),  ThisNode->EndBlock/BlocksPerSec/60, ThisNode->EndBlock/BlocksPerSec%60, (ThisNode->EndBlock%60)/(BlocksPerSec/10));
			ThisNode=NextNode;
			}
		}
	else if((RxIntArg(0, -1)==1))
		{
		CD_CheckCaddie();
		}
	else if((RxIntArg(0, -1)==2))
		{
		CD_GetVolume(&temp, &temp2, NULL, NULL);
		}

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}
*/


  //				  //
 //	RxStub()		 //
//				//
ULONG RxStub(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
	RxMessage->rm_Result1=RC_WARN;
	RxMessage->rm_Result2=0;

	return(0);
}


  //				  //
 //	RxNOP()			 //
//				//
ULONG RxNOP(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;

	return(0);
}


  //				  //
 //	RxDisable()		 //
//				//
ULONG RxDisable(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, Count=0, Count2=0;
struct	RDArgs		*RxRDArgs=NULL;
UBYTE			FirstChar=0, **Temp=NULL;
BOOL			Found=FALSE;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;
	RxArgs[0]=(ULONG)NULL;		// NAMES/M

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}


	if(Temp=(UBYTE **)RxArgs[0])
		while(Temp[Count2])
			{
			FirstChar = *Temp[Count2];
			FirstChar &= 223;
			Found=FALSE;

			for(Count = CmdHash[FirstChar];
				RxCommands[Count].Name &&
				(*RxCommands[Count].Name == FirstChar) &&
				!(Found=commcmp(RxCommands[Count].Name, Temp[Count2]));
				Count++);

			if(Found)
				{
				RxCommands[Count].Flags |= RXCF_DISABLED;
				}
			else
				{
				}

			Count2++;
			}

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxEnable()		 //
//				//
ULONG RxEnable(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, Count=0, Count2=0;
struct	RDArgs		*RxRDArgs=NULL;
UBYTE			FirstChar=0, **Temp=NULL;
BOOL			Found=FALSE;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;
	RxArgs[0]=(ULONG)NULL;		// NAMES/M

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}


		if(Temp=(UBYTE **)RxArgs[0])
		while(Temp[Count2])
			{
			FirstChar = *Temp[Count2];
			FirstChar &= 223;
			Found=FALSE;

			for(Count = CmdHash[FirstChar];
				RxCommands[Count].Name &&
				(*RxCommands[Count].Name == FirstChar) &&
				!(Found=commcmp(RxCommands[Count].Name, Temp[Count2]));
				Count++);

			if(Found)
				{
				RxCommands[Count].Flags &= ~(RXCF_DISABLED);
				}
			Count2++;
			}


	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxQuit()		 //
//				//
ULONG RxQuit(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_QUIT;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)FALSE;		// FORCE

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	if(RxArgs[0])
		Result=RXCR_QUITFORCE;

CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxHelp()		 //
//				//
ULONG RxHelp(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, Counter, Stat;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;		// COMMAND
	RxArgs[1]=(ULONG)FALSE;		// PROMPT

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	if(!(RxArgs[1]))
		{
		if(RxArgs[0])
			{
			if(!(stricmp((UBYTE *)RxArgs[0], "GETATTR")))
				{
				for(Stat=0, Counter=0; ProjectAttrs[Counter].AttrName; Counter++)
					{
					if(Stat && (!(ProjectAttrs[Counter].Flags & AAF_NAME)))
						Stat=0;
					if(Stat && (ProjectAttrs[Counter].Flags & AAF_OBJECT))
						Stat=0;

					if((!Stat) && (ProjectAttrs[Counter].Flags & AAF_NAME))
						{
						FPrintf(RxMessage->rm_Stdout, ApplStr(LT_NAMEARGREQUIRED));
						Stat=1;
						}
					FPrintf(RxMessage->rm_Stdout, "%s%s\n", (ProjectAttrs[Counter].Flags & AAF_OBJECT)?"\n":"\t.", ProjectAttrs[Counter].AttrName);
					}
				}
			else
				{
				}
			}
		else
			{
			FPrintf(RxMessage->rm_Stdout, ApplStr(LT_COMMANDSAVAIL), fullname);

			for(Counter=0; RxCommands[Counter].Name; Counter++)
				{
				FPrintf(RxMessage->rm_Stdout, "%s %s%s\n", RxCommands[Counter].Name, RxCommands[Counter].Template, (RxCommands[Counter].Flags & RXCF_DISABLED?ApplStr(LT_DISABLED):""));
				}
			}
		}
	else
		{
		}

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}

/*
  //				  //
 //	RxDeBug()		 //
//				//
ULONG RxDeBug(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)FALSE;		// OFF/S

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	DebugEnabled = (!(RxArgs[0]));

	SetCheckMark(2, CHK_SHOW_DEBUG, DebugEnabled);

CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}
*/

  //				  //
 //	RxGetAttr()		 //
//				//
ULONG RxGetAttr(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, StringLen, Counter=0;
struct	RDArgs		*RxRDArgs=NULL;
UBYTE			*TempSP;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;		// OBJECT/A
	RxArgs[1]=(ULONG)NULL;		// NAME
	RxArgs[2]=(ULONG)NULL;		// FIELD
	RxArgs[3]=(ULONG)NULL;		// STEM/K
	RxArgs[4]=(ULONG)NULL;		// VAR/K

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	 // Kör på om det anropande macrot har satt 'OPTIONS RESULTS'
	//
	if(RxMessage->rm_Action & RXFF_RESULT)
		{
		 // Leta upp rätt OBJECT
		//
		while(ProjectAttrs[Counter].AttrName)
			{
			if(ProjectAttrs[Counter].Flags & AAF_OBJECT)
				{
				if(!(stricmp(ProjectAttrs[Counter].AttrName, (UBYTE *)RxArgs[0])))
					break;
				}
			Counter++;
			}

		 // Finns det FIELD's efter OBJEKT'et så kan vi köra på...
		//
		if(ProjectAttrs[Counter].AttrName)
			{
			 // Kör OBJECT's Update-funktion
			//
			if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
				ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_GETATTR, (UBYTE *)RxArgs[1], NULL);

			Counter++;
			StringLen=0;

			 // Stega igenom de FIELD's som ligger under OBJECT'et
			//
			while(ProjectAttrs[Counter].AttrName && (ProjectAttrs[Counter].Flags & AAF_FIELD))
				{
				 // Värdena skall läggas i en STEM
				//
				if(RxArgs[3])		// STEM/K
					{
					if((StringLen=BuildString(0, (UBYTE *)RxArgs[3]))==-1) goto CleanUp;
					if(StringBuffer[StringLen-1]!='.') if((StringLen=BuildString(StringLen, "."))==-1) goto CleanUp;
					if((StringLen=BuildString(StringLen, ProjectAttrs[Counter].AttrName))==-1) goto CleanUp;

					 // Kör FIELD's Update-funktion
					//
					if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
						ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_GETATTR, (UBYTE *)RxArgs[1], NULL);

					 // Är FIELD ett heltal eller en sträng?
					//
					if(ProjectAttrs[Counter].Flags & AAF_LONG)
						{
						stcl_d(TempNumBuf, *((ULONG *)ProjectAttrs[Counter].Value));
						if(SetRexxVar((struct Message *)RxMessage, StringBuffer, TempNumBuf, strlen(TempNumBuf))) goto CleanUp;
						}
					else
						{
						if(ProjectAttrs[Counter].Flags & AAF_STRVAR)
							TempSP=StrVarAry[(ULONG)(ProjectAttrs[Counter].Value)];
						else
							TempSP=*((UBYTE **)ProjectAttrs[Counter].Value);

						if(!(TempSP)) TempSP="null pointer";
						if(SetRexxVar((struct Message *)RxMessage, StringBuffer, TempSP, strlen(TempSP))) goto CleanUp;
						}
					}
				 // Värdena skall läggas i en VAR eller i RESULT
				//
				else
					{
					 // Det är bara ett FIELD som skall hämtas
					//
					if(RxArgs[2])
						{
						if( (!(stricmp((UBYTE *)RxArgs[2], ProjectAttrs[Counter].AttrName))) )
							{
							 // Kör FIELD's Update-funktion
							//
							if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
								ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_GETATTR, (UBYTE *)RxArgs[1], NULL);

							 // Är FIELD ett heltal eller en sträng?
							//
							if(ProjectAttrs[Counter].Flags & AAF_LONG)
								{
								stcl_d(TempNumBuf, *((ULONG *)ProjectAttrs[Counter].Value));

								 // Skall värdet in i VAR eller RESULT ?
								//
								if(RxArgs[4])
									{
									if(SetRexxVar((struct Message *)RxMessage, (UBYTE *)RxArgs[4], TempNumBuf, strlen(TempNumBuf))) goto CleanUp;
									}
								else
									{
									RxMessage->rm_Result2=(LONG)CreateArgstring(TempNumBuf, strlen(TempNumBuf)+1);
									//if(RxMessage->rm_Result2=(LONG)CreateArgstring(TempNumBuf, strlen(TempNumBuf)+1))
									//	SetArgstringFlags((UBYTE *)RxMessage->rm_Result2, (NSF_STRING | NSF_NUMBER | NSF_EXT));
									}
								goto JumpOut;
								}
							else
								{
								if(ProjectAttrs[Counter].Flags & AAF_STRVAR)
									TempSP=StrVarAry[(ULONG)(ProjectAttrs[Counter].Value)];
								else
									TempSP=*((UBYTE **)ProjectAttrs[Counter].Value);

								if(!(TempSP)) TempSP="null pointer";

								 // Skall värdet in i VAR eller RESULT ?
								//
								if(RxArgs[4])
									{
									if(SetRexxVar((struct Message *)RxMessage, (UBYTE *)RxArgs[4], TempSP, strlen(TempSP))) goto CleanUp;
									}
								else
									{
									RxMessage->rm_Result2=(LONG)CreateArgstring(TempSP, strlen(TempSP)+1);
									}
								goto JumpOut;
								}
							}
						}
					 // Lägg alla FIELD's på rad i VAR eller RESULT
					//
					else
						{
						if( ((!(ProjectAttrs[Counter].Flags & AAF_NAME)) || ((ProjectAttrs[Counter].Flags & AAF_NAME) && RxArgs[1])) )
							{
							if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
								ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_GETATTR, (UBYTE *)RxArgs[1], NULL);

							if(ProjectAttrs[Counter].Flags & AAF_LONG)
								{
								stcl_d(TempNumBuf, *((ULONG *)ProjectAttrs[Counter].Value));
								if((StringLen=BuildString(StringLen, (UBYTE *)TempNumBuf))==-1) goto CleanUp;
								if((StringLen=BuildString(StringLen, " "))==-1) goto CleanUp;
								}
							else
								{
								if(ProjectAttrs[Counter].Flags & AAF_STRVAR)
									TempSP=StrVarAry[(ULONG)(ProjectAttrs[Counter].Value)];
								else
									TempSP=*((UBYTE **)ProjectAttrs[Counter].Value);

								if(!(TempSP)) TempSP="null pointer";

								if((StringLen=BuildString(StringLen, "\""))==-1) goto CleanUp;
								if((StringLen=BuildString(StringLen, (UBYTE *)TempSP))==-1) goto CleanUp;
								if((StringLen=BuildString(StringLen, "\" "))==-1) goto CleanUp;
								}
							}

						 // Om nästa element i ary inte är ett FIELD, så är vi färdiga
						//
						if(!(ProjectAttrs[Counter+1].AttrName && (ProjectAttrs[Counter+1].Flags & AAF_FIELD)))
							{
							if(RxArgs[4])
								{
								if(SetRexxVar((struct Message *)RxMessage, (UBYTE *)RxArgs[4], StringBuffer, strlen(StringBuffer))) goto CleanUp;
								}
							else
								{
								RxMessage->rm_Result2=(LONG)CreateArgstring(StringBuffer, strlen(StringBuffer)+1);
								}

							goto JumpOut;
							}
						}
					}

				Counter++;
				}
			}
		else
			{
			goto CleanUp;
			}
		}

JumpOut:

	RxMessage->rm_Result1=RC_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxSaveSettings()	 //
//				//
ULONG RxSaveSettings(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
ULONG			TempL;
UBYTE			*Temp;
struct	RDArgs		*RxRDArgs=NULL;
struct	Window		*TargetWin=NULL;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;		// NAME/K
	RxArgs[1]=(ULONG)NULL;		// PROMPT/S

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	TargetWin=Wins[WIN_MAIN]?Wins[WIN_MAIN]:Wins[WIN_LYRIC];

	if(RxArgs[1])
		{
		if(!(AslRequestTags(SettingAslReq,
				    ASLFR_DoSaveMode, TRUE,
				    ASLFR_PrivateIDCMP, TRUE,
				    ASLFR_Window, TargetWin,
				    ASLFR_PubScreenName, TargetWin?NULL:(StrVar(SV_PUBSCREEN, "Workbench")),
				    ASLFR_TitleText, LocaleArray[LT_SAVESETTINGS],
				    ASLFR_InitialLeftEdge, TargetWin?TargetWin->LeftEdge+4:0,
				    ASLFR_InitialTopEdge, TargetWin?TargetWin->TopEdge+4:0,
				    ASLFR_InitialWidth, TargetWin?TargetWin->Width:200,
				    ASLFR_InitialHeight, TargetWin?TargetWin->Height*2:300,
				    TAG_END)))
					    goto CleanUp;

		if(Temp=SetStrVar(SV_SETFILE, ((struct FileRequester *)SettingAslReq)->fr_Drawer))
			{
			if(TempL=strlen(Temp))
				{
				switch(Temp[TempL-1])
					{
					case ':':
					case '/':
					break;

					default:
					if(TempL)
						CatStrVar(SV_SETFILE, "/");
					}
				}
			}
		TempL=0;
		if(Temp=(((struct FileRequester *)SettingAslReq)->fr_File) )
			{
			TempL=strlen(Temp);
			}

		CatStrVar(SV_SETFILE, ((Temp && TempL)?Temp:"RexxCD.settings"));

		Temp=StrVar(SV_SETFILE,"RexxCD.settings");

		SaveSettings(Temp);
		}
	else
		SaveSettings(RxStrArg(0, "RexxCD.settings"));

CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxLoadSettings()	 //
//				//
ULONG RxLoadSettings(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
ULONG			TempL;
UBYTE			*Temp;
struct	RDArgs		*RxRDArgs=NULL;
struct	Window		*TargetWin=NULL;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;		// NAME/K
	RxArgs[1]=(ULONG)NULL;		// PROMPT/S

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	TargetWin=Wins[WIN_MAIN]?Wins[WIN_MAIN]:Wins[WIN_LYRIC];

	if(RxArgs[1])
		{
		if(!(AslRequestTags(SettingAslReq,
				    ASLFR_DoSaveMode, FALSE,
				    ASLFR_PrivateIDCMP, TRUE,
				    ASLFR_Window, TargetWin,
				    ASLFR_PubScreenName, TargetWin?NULL:(StrVar(SV_PUBSCREEN, "Workbench")),
				    ASLFR_TitleText, LocaleArray[LT_LOADSETTINGS],
				    ASLFR_InitialLeftEdge, TargetWin?TargetWin->LeftEdge+4:0,
				    ASLFR_InitialTopEdge, TargetWin?TargetWin->TopEdge+4:0,
				    ASLFR_InitialWidth, TargetWin?TargetWin->Width:200,
				    ASLFR_InitialHeight, TargetWin?TargetWin->Height*2:300,
				    TAG_END)))
					    goto CleanUp;

		if(Temp=SetStrVar(SV_SETFILE, ((struct FileRequester *)SettingAslReq)->fr_Drawer))
			{
			if(TempL=strlen(Temp))
				{
				switch(Temp[TempL-1])
					{
					case ':':
					case '/':
					break;

					default:
					if(TempL)
						CatStrVar(SV_SETFILE, "/");
					}
				}
			}
		TempL=0;
		if(Temp=(((struct FileRequester *)SettingAslReq)->fr_File) )
			{
			TempL=strlen(Temp);
			}

		CatStrVar(SV_SETFILE, ((Temp && TempL)?Temp:"RexxCD.settings"));

		Temp=StrVar(SV_SETFILE, "RexxCD.settings");

		LoadSettings(Temp);
		}
	else
		LoadSettings(RxStrArg(0, "RexxCD.settings"));

CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxSetAttr()		 //
//				//
ULONG RxSetAttr(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, StringLen, TempL, Counter=0, LastObject=0;
struct	RDArgs		*RxRDArgs=NULL;
UBYTE			*BufPointer=NULL;
ULONG			Bulk=0;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;		// OBJECT/A
	RxArgs[1]=(ULONG)NULL;		// NAME
	RxArgs[2]=(ULONG)NULL;		// FIELD
	RxArgs[3]=(ULONG)NULL;		// STEM/K
	RxArgs[4]=(ULONG)NULL;		// VAR/K
	RxArgs[5]=(ULONG)NULL;		// VALUE/K
	RxArgs[6]=(ULONG)NULL;		// PROMPT/S

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	 // Kolla så att åtminstone en av STEM, VAR eller VALUE är satt.
	//
	if(!(RxArgs[3] || RxArgs[4] || RxArgs[5]))
		{
		goto CleanUp;
		}

	 // Stega fram till rätt OBJECT
	//
	while(ProjectAttrs[Counter].AttrName)
		{
		if(ProjectAttrs[Counter].Flags & AAF_OBJECT)
			{
			if(!(stricmp(ProjectAttrs[Counter].AttrName, (UBYTE *)RxArgs[0])))
				break;
			}
		Counter++;
		}


	 // Hoppa ur om OBJECT'et inte fanns
	//
	if(!(ProjectAttrs[Counter].AttrName))
		goto CleanUp;


	 // Kör OBJECT'ets Update-funktion
	//
	LastObject=Counter;
	if((ProjectAttrs[LastObject].Flags & AAF_UPDATE) && ProjectAttrs[LastObject].UpdateFunc)
		{
		 // Kolla om OBJECT'et har BULK-initiering och kör med START
		//
		Bulk=(ProjectAttrs[LastObject].Flags & AAF_BULK)?UDF_BULK:0;
		ProjectAttrs[LastObject].UpdateFunc(&ProjectAttrs[LastObject], UDF_SETATTR | (Bulk?(UDF_START | UDF_BULK):0) | (RxArgs[6]?UDF_PROMPT:0), (UBYTE *)RxArgs[1], NULL);
		}


	 // Gå igenom alla FIELD's i OBJECT'et
	//
	Counter++;
	while(ProjectAttrs[Counter].AttrName && (ProjectAttrs[Counter].Flags & AAF_FIELD))
		{
		 // Får vi skriva i detta FIELD ?
		//
		if(ProjectAttrs[Counter].Flags & AAF_WRITE)
			{
			 // Lägg namn på ev. VAR's el. STEM's i buffern.
			//
			if(RxArgs[3])
				{
				if((StringLen=BuildString(0, (UBYTE *)RxArgs[3]))==-1) goto CleanUp;
				if(StringBuffer[StringLen-1]!='.') if((StringLen=BuildString(StringLen, "."))==-1) goto CleanUp;
				if((StringLen=BuildString(StringLen, ProjectAttrs[Counter].AttrName))==-1) goto CleanUp;
				}
			else if(RxArgs[4])
				{
				if((StringLen=BuildString(0, (UBYTE *)RxArgs[4]))==-1) goto CleanUp;
				}

			 // Antingen en STEM eller VAR/VALUE+FIELD
			//
			if( RxArgs[3] || ( (RxArgs[2]) &&  (!(stricmp(ProjectAttrs[Counter].AttrName, (UBYTE *)RxArgs[2]))) ) )
				{
				 // Är VALUE och FIELD satta?
				//
				if(RxArgs[5] && RxArgs[2])
					{
					BufPointer=(UBYTE *)RxArgs[5];
					}
				 // Nej, läs värden från VAR eller STEM
				//
				else
					{
					if(GetRexxVar((struct Message *)RxMessage, StringBuffer, &BufPointer))
						goto CleanUp;
					}

				 // FIELD är ett heltal
				//
				if(ProjectAttrs[Counter].Flags & AAF_LONG)
					{
					TempL=atol(BufPointer);
					 // FIELD har en Update-funktion
					//
					if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
						{
						ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | Bulk | (RxArgs[6]?UDF_PROMPT:0), (UBYTE *)RxArgs[1], (APTR)TempL);
						}
					 // Ingen Update-funktion, skriv direkt i variabeln
					//
					else if(ProjectAttrs[Counter].Value)
						{
						(*((ULONG *)ProjectAttrs[Counter].Value))=TempL;
						}
					}
				 // FIELD är en sträng
				//
				else
					{
					if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
						ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | Bulk | (RxArgs[6]?UDF_PROMPT:0), (UBYTE *)RxArgs[1], (APTR)BufPointer);
					else if(ProjectAttrs[Counter].Flags & AAF_STRVAR)
						SetStrVar((ULONG)ProjectAttrs[Counter].Value, BufPointer);
					}
				}
			}
		Counter++;
		}

	 // Om OBJECT'et hade BULK-initiering skall vi köra Update-funktionen en gång till
	//
	if(Bulk)
		{
		ProjectAttrs[LastObject].UpdateFunc(&ProjectAttrs[LastObject], UDF_SETATTR | UDF_STOP | UDF_BULK | (RxArgs[6]?UDF_PROMPT:0), (UBYTE *)RxArgs[1], NULL);
		}

	RxMessage->rm_Result1=RC_OK;

CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
//FEL:	if(BufPointer)	DeleteArgstring(BufPointer);
//	Man får inte göra DeleteArgstring(), eftersom när man svarar
//	på arexx-meddelandet, så deallokeras den där. Dessutom: VALUE!!
	return(Result);
}


  //				  //
 //	RxPlay()		 //
//				//
ULONG RxPlay(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
struct	RDArgs		*RxRDArgs=NULL;
UBYTE			Track, *TempStr;
ULONG			FromAddr, ToAddr, Offset;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;	// TRACK/N
	RxArgs[1]=(ULONG)NULL;	// INDEX/N
	RxArgs[2]=(ULONG)NULL;	// OFFSET/K
	RxArgs[3]=(ULONG)FALSE;	// PREVIOUS/S
	RxArgs[4]=(ULONG)FALSE;	// TMOFFSET/K
	RxArgs[5]=(ULONG)FALSE;	// BLOCKS/K/N
	RxArgs[6]=(ULONG)FALSE;	// PREVIOUSINDEX/S
	RxArgs[7]=(ULONG)FALSE;	// NEXTINDEX/S


	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	if(RxArgs[0]) // TRACK
		{
		Track=(((RxIntArg(0, 1))<(TOCBuffer->LastTrack+1))?(RxIntArg(0, 1)):0);
		}
	else
		{
		if(CDStatus==CDSTAT_STOPPED)
			CurrentTrack=0;

		if(RxArgs[3]) // PREVIOUS
			{
			Track=CurrentTrack>1?CurrentTrack-1:(TOCBuffer->LastTrack);
			}
		else
			{
			Track=(CurrentTrack<TOCBuffer->LastTrack)?CurrentTrack+1:1;
			}
		}

	GlobalRefresh=1;

	FromAddr=TOCBuffer->Data[Track-1].AbsoluteAddress;
	ToAddr=TOCBuffer->Data[TOCBuffer->LastTrack].AbsoluteAddress;

	// INDEX
	if(RxArgs[1])
		{
		if(Offset=GetIndex(Track, RxIntArg(1, 1)))
			{
			FromAddr+=Offset-1;
			}
		}
	else if(!(RxArgs[0]))
		{
		if(RxArgs[6]) // PREVIOUSINDEX
			{
			if(Offset=GetIndex(CurrentTrack, (Current_Index)?(Current_Index-1):0))
				{
				FromAddr=TOCBuffer->Data[CurrentTrack-1].AbsoluteAddress;
				FromAddr+=Offset-1;
				}
			else
				{
				Track=CurrentTrack>1?CurrentTrack-1:(TOCBuffer->LastTrack);
				FromAddr=TOCBuffer->Data[Track-1].AbsoluteAddress;
				Offset=GetIndex(Track, -1);
				FromAddr+=Offset?Offset-1:0;
				}
			}
		else if(RxArgs[7]) // NEXTINDEX
			{
			if(Offset=GetIndex(CurrentTrack, Current_Index+1))
				{
				FromAddr=TOCBuffer->Data[CurrentTrack-1].AbsoluteAddress;
				FromAddr+=Offset-1;
				}
			}
		}

	if(RxArgs[2])
		{
		TempStr=(UBYTE *)RxArgs[2];
		Offset=AtoBlock(&TempStr);
		if((Offset+FromAddr)<ToAddr)
			FromAddr+=Offset;
		}
	else if(RxArgs[4])
		{
		FromAddr=TM_AToBlock((UBYTE *)RxArgs[4]);
		}

	if(RxArgs[5])
		{
		ToAddr=FromAddr+RxIntArg(5, 0);
		}


	if(Compatibility & COMPAT_NEC)
		{
		if(!(CD_Play10(FromAddr, ToAddr)))
			goto CleanUp;
		}
	else
		{
		if(!(CD_PlayFromTo(FromAddr, ToAddr)))
			goto CleanUp;
		}


	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxSkip()		 //
//				//
ULONG RxSkip(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, Blocks=0;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;	// TRACKS/N
	RxArgs[1]=(ULONG)NULL;	// SECONDS/N
	RxArgs[2]=(ULONG)NULL;	// BLOCKS/N
	RxArgs[3]=(ULONG)NULL;	// BACK/S

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	if(RxArgs[1])
		Blocks=(RxIntArg(1, 0))*BlocksPerSec;

	if(RxArgs[2])
		Blocks=(RxIntArg(2, 0));

	if(!(Blocks))
		goto CleanUp;

	if(!(CD_Skip(Blocks, RxArgs[3]?0:1)))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxVolume()		 //
//				//
ULONG RxVolume(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;	// LEFT/N
	RxArgs[1]=(ULONG)NULL;	// RIGHT/N

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	lvol=RxIntArg(0, 0);
	rvol=RxIntArg(1, RxIntArg(0, 0));

	if(!(CD_SetVolume(lvol, rvol, 0, 0)))
		goto CleanUp;

	if(Wins[WIN_MAIN])
		{
		if(LeftVolGad)
			GT_SetGadgetAttrs(LeftVolGad, Wins[WIN_MAIN], NULL, GTSL_Level, lvol, TAG_END);
		if(RightVolGad)
			GT_SetGadgetAttrs(RightVolGad, Wins[WIN_MAIN], NULL, GTSL_Level, rvol, TAG_END);
		}
	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxEject()		 //
//				//
ULONG RxEject(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Eject()))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxInsert()		 //
//				//
ULONG RxInsert(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Insert()))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxStart()		 //
//				//
ULONG RxStart(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Start()))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxStop()		 //
//				//
ULONG RxStop(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Stop()))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxPause()		 //
//				//
ULONG RxPause(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Pause(FALSE)))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxResume()		 //
//				//
ULONG RxResume(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Pause(TRUE)))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxLock()		 //
//				//
ULONG RxLock(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Lock(TRUE)))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxUnLock()		 //
//				//
ULONG RxUnLock(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG	Result=RXCR_ALLOCERR;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;

	if(!(CD_Lock(FALSE)))
		goto CleanUp;

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	return(Result);
}


  //				  //
 //	RxActivate()		 //
//				//
ULONG RxActivate(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
struct	RDArgs		*RxRDArgs=NULL;
BOOL			OpenLyrics;

	RxMessage->rm_Result1=20;
	RxMessage->rm_Result2=0;
	RxArgs[0]=(ULONG)NULL;		// PUBSCREEN

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	if(Wins[WIN_MAIN])
		OffWin();

	if(OpenLyrics=(BOOL)Wins[WIN_LYRIC])
		CloseLyricWin();

	CloseAbout();

	SetStrVar(SV_PUBSCREEN, RxStrArg(0, "Workbench"));

	if(!Wins[WIN_MAIN])
		{
		if(PopWin((UBYTE *)RxStrArg(0, NULL)))
			{
			PopDownIcon();
			if(OpenLyrics)
				PopLyricWin();

			CD_Ping(1);
			}
		}

	RxMessage->rm_Result1=0;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxDeActivate()		 //
//				//
ULONG RxDeActivate(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
	RxMessage->rm_Result1=0;
	RxMessage->rm_Result2=0;

	if(Wins[WIN_MAIN])
		{
		PopUpIcon();
		OffWin();
		}

	CloseLyricWin();
	CloseAbout();

	return(0);
}


  //				  //
 //	RxLearn()		 //
//				//
ULONG RxLearn(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=20;
	RxMessage->rm_Result2=0;

	RxArgs[0]=(ULONG)NULL;		// FILE/K
	RxArgs[1]=(ULONG)FALSE;		// STOP/S

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

/*	if(Learn)	// Macroinspelning håller på sedan tidigare
		{
		if(RxArgs[0])	// FILE
			{
			SaveLearn(RxStrArg(0, DEFAULTLEARNFILE));
			}

		if(RxArgs[1])	// STOP
			{
			Learn=FALSE;
			}

		Result=RXCR_OK;
		}
	else		// Macroinspelning är inte igång
		{
		if(RxArgs[0])	// FILE
			{
			SaveLearn(RxStrArg(0, DEFAULTLEARNFILE));
			}

		if(RxArgs[1])	// STOP
			goto CleanUp;

		Learn=TRUE;
		Result=RXCR_LEARN;
		}
*/
	RxMessage->rm_Result1=0;
	RxMessage->rm_Result2=0;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}


  //				  //
 //	RxButton()		 //
//				//
ULONG RxButton(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR, Num=0;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=20;
	RxMessage->rm_Result2=0;
	RxArgs[0]=(ULONG)NULL;		// NAME/A
	RxArgs[1]=(ULONG)NULL;		// LABEL/K
	RxArgs[2]=(ULONG)NULL;		// WINDOW/K
	RxArgs[3]=(ULONG)NULL;		// PROMPT/K
	RxArgs[4]=(ULONG)NULL;		// CMD/F

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	Num=atol(RxStrArg(0, ""));

	if((!Num) || (Num>7))
		 goto CleanUp;

	if( (!(SetStrVar(Num-1, (UBYTE *)RxArgs[4])) && RxArgs[4]) )
		goto CleanUp;


	RxMessage->rm_Result1=0;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}

/*
//
//	Använd denna kod som mall för nya funktioner
//	Fyll i där det är punkter.
  //				  //
 //	Rx....()		 //
//				//
ULONG Rx...(struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
ULONG			Result=RXCR_ALLOCERR;
struct	RDArgs		*RxRDArgs=NULL;

	RxMessage->rm_Result1=RC_FATAL;
	RxMessage->rm_Result2=0;
	RxArgs[0]=(ULONG)......;
	...

	if(!(RxRDArgs=DoRxRDArgs(TmpRDArgs, CmdEntry, RxMessage)))
		{
		PrintFault(IoErr(), CmdEntry->Name);
		goto CleanUp;
		}

	// Gör något...

	RxMessage->rm_Result1=RC_OK;
	RxMessage->rm_Result2=0;
	Result=RXCR_OK;
CleanUp:
	if(RxRDArgs)	FreeArgs(RxRDArgs);
	return(Result);
}
//	Var noga med att fylla i alla delar, och att initalisera rätt antal
//	parametrar.
//
*/

//
//
//
//               SUPPORT-FUNKTIONER
//
//
//
//

  //				  //
 //	InitCmdHash()		 //
//				//
void	InitCmdHash(void)
{
UWORD	Counter = 256;
UBYTE	LastChar = '@';

	while(Counter)
		{
		Counter--;
		CmdHash[Counter]=0;
		}

	while(RxCommands[Counter].Name)
		{
		if(LastChar < *(RxCommands[Counter].Name))
			{
			LastChar = *(RxCommands[Counter].Name);
			CmdHash[LastChar] = Counter;
			}
		Counter++;
		}
}

  //				  //
 //	ProcessRx()		 //
//				//
ULONG ProcessRx(struct RexxMsg *RxMessage)
{
ULONG	RxAction, Count, RxCmdResult=RXCR_OK, Length;
BOOL	Found;
UBYTE	FirstChar, *Buffer;

	if(IsRexxMsg(RxMessage))
		RxAction=RxMessage->rm_Action;
	else
		RxAction=0;

	if((RxAction & RXCODEMASK)==RXCOMM)
		{
		FirstChar = *(RxMessage->rm_Args[0]);
		FirstChar &= 223;

		for(Found=FALSE, Count = CmdHash[FirstChar] ;
			   RxCommands[Count].Name
			&& (*RxCommands[Count].Name == FirstChar)
			&& !(Found=commcmp(RxCommands[Count].Name, RxMessage->rm_Args[0]))
			;  Count++);

		Buffer=RxMessage->rm_Args[0];
		Length=strlen(Buffer);

		CopyMem(Buffer, CommandBuffer, Length);
		CommandBuffer[Length]='\n';
		CommandBuffer[Length+1]=0;

		if(RxCommands[Count].Flags & RXCF_DISABLED)
			{
			if(Learn)
				LearnLine(CommandBuffer);
			}
		else if(!Found)
			{
			if(Learn)
				LearnLine(CommandBuffer);

			RxMessage->rm_Result1=RC_ERROR;
			}
		else
			{
			RxCmdResult=RxCommands[Count].Function(&RxCommands[Count], RxMessage);

			if(Learn && (RxCmdResult!=RXCR_LEARN))
				LearnLine(CommandBuffer);

			switch(RxCmdResult)
				{
				case RXCR_OK:
				break;

				case RXCR_ALLOCERR:
				break;

				case RXCR_SHOWGUI:
				break;

				case RXCR_DOSBREAK_C:
				case RXCR_DOSBREAK_D:
				case RXCR_QUITFORCE:
				case RXCR_QUIT:
				goto JumpOut;
				break;
				}
			}
		}

JumpOut:
	ReplyMsg((struct Message *)RxMessage);

	return(RxCmdResult);
}

  //				  //
 //	DoRxRDArgs()		 //
//				//
struct RDArgs	*DoRxRDArgs(struct RDArgs *TempRD, struct RxCommand *CmdEntry, struct RexxMsg *RxMessage)
{
struct RDArgs	*Result=NULL;
ULONG		Tmp=strlen(CmdEntry->Name);

	TempRD->RDA_Source.CS_Buffer=CommandBuffer+Tmp;
	TempRD->RDA_Source.CS_Length=strlen(CommandBuffer)+1-Tmp;
	TempRD->RDA_Source.CS_CurChr=0;
	TempRD->RDA_DAList=NULL;
	TempRD->RDA_Buffer=NULL;
	TempRD->RDA_BufSiz=0;
	TempRD->RDA_ExtHelp=NULL;
	TempRD->RDA_Flags=RDAF_NOPROMPT;

	Result=ReadArgs(CmdEntry->Template, (LONG *)RxArgs, TempRD);

	return(Result);
}


  //			  //
 //	InitRx()	 //
//			//
BOOL	InitRx(void)
{
	InitCmdHash();

	if(!(TmpRDArgs=AllocDosObject(DOS_RDARGS, NULL)))
		goto CleanUp;

	if(!(RxMemPool=CreatePool(MEMF_PUBLIC, 8192, 4096)))
		goto CleanUp;

	if(!(CommandBuffer=AllocPooled(RxMemPool, CMD_BUFFER_SIZE)))
		goto CleanUp;

	if(!(StringBuffer=AllocPooled(RxMemPool, STR_BUFFER_SIZE)))
		goto CleanUp;

	if(!(LineBuffer=AllocPooled(RxMemPool, LIN_BUFFER_SIZE)))
		goto CleanUp;

	if(!(LearnBufList=AllocPooled(RxMemPool, sizeof(struct List))))
		goto CleanUp;

	NewList(LearnBufList);

	if(!(RxReplyPort=CreatePort(NULL, 0)))
		goto CleanUp;


#ifdef DEBUG
	if(!(DebugCON=Open("KON:340/42/340/230/RexxCD Debug Output/WAIT/CLOSE/AUTO", MODE_READWRITE)))
		goto CleanUp;
#endif
	return(TRUE);
CleanUp:
	CloseRx();
	return(FALSE);
}


  //			  //
 //	CloseRx()	 //
//			//
void	CloseRx(void)
{
struct	RexxMsg		*RxMessage;

	if(TmpRDArgs)		FreeDosObject(DOS_RDARGS, TmpRDArgs);
	if(RxMemPool)		DeletePool(RxMemPool);
	if(RxReplyPort)
		{
		while(PendingRxReplies)
			{
			WaitPort(RxReplyPort);
			while(RxMessage=(struct RexxMsg *)GetMsg(RxReplyPort))
				{
				ClearRexxMsg(RxMessage, 1);
				DeleteRexxMsg(RxMessage);
				PendingRxReplies--;
				}
			}

		DeletePort(RxReplyPort);
		}

	if(DebugCON)		Close(DebugCON);

	TmpRDArgs=NULL;
	CommandBuffer=NULL;
	DebugCON=NULL;
	StringBuffer=NULL;
	LineBuffer=NULL;
	LearnBufList=NULL;
	RxReplyPort=NULL;
}


  //			  //
 //	BuildString()	 //
//			//
ULONG BuildString(ULONG Position, UBYTE *String)
{
ULONG	Temp, InLen=strlen(String);

	if((Position+InLen+1) > STR_BUFFER_SIZE)
		return(-1);

	Temp = (ULONG)stccpy(StringBuffer+Position, String, InLen+1);

	return(Position+Temp-1);
}


  //			  //
 //	LearnLine()	 //
//			//
BOOL	LearnLine(UBYTE *String)
{
struct	Node	*TempNode;
ULONG		Length=strlen(String);

	if(!(TempNode=AllocPooled(RxMemPool, sizeof(struct Node)+Length+1)))
		return(FALSE);

	TempNode->ln_Name=(UBYTE *)(TempNode+sizeof(struct Node));

	strcpy(TempNode->ln_Name, String);

	AddTail(LearnBufList, TempNode);

	return(TRUE);
}


  //			  //
 //	ClearLearn()	 //
//			//
void	ClearLearn(void)
{
struct	Node	*TempNode;
ULONG		Length;

	while(TempNode=RemTail(LearnBufList))
		{
		Length=strlen(TempNode->ln_Name);
		FreePooled(RxMemPool, TempNode, sizeof(struct Node)+Length+1);
		}
}


  //			  //
 //	SaveLearn()	 //
//			//
BOOL	SaveLearn(UBYTE *FileName)
{
BPTR		FileH=NULL;
ULONG		Length;
struct	Node	*TempNode;

	if(!(FileH=Open(FileName, MODE_READWRITE)))
		goto CleanUp;

	Seek(FileH, 0, OFFSET_END);

	SetProtection(FileName, FIBF_SCRIPT | ~(FIBF_EXECUTE|FIBF_WRITE|FIBF_READ|FIBF_DELETE));

	FWritef(FileH, ApplStr(LT_LEARNHEADER), FileName);

	while(TempNode=RemHead(LearnBufList))
		{
		FWritef(FileH, TempNode->ln_Name);

		Length=strlen(TempNode->ln_Name);
		FreePooled(RxMemPool, TempNode, sizeof(struct Node)+Length+1);
		}

CleanUp:
	if(FileH)	Close(FileH);

	return((BOOL)FileH);
}


//	Type -	0 = filename of arexx script
//		1 = macro string
//
BOOL RunRexxCommand(UBYTE *String, ULONG Type)
{
struct	RexxMsg		*RexxMsg=NULL;
struct	MsgPort		*REXXMastPort=NULL;
BOOL			Filled=FALSE;

	if(!(RexxMsg=CreateRexxMsg(RxReplyPort, NULL, StrVar(SV_PORTNAME, "REXXCD"))))
		goto CleanUp;

	RexxMsg->rm_Action=RXCOMM | RXFF_NOIO | (Type?RXFF_STRING:0);
	RexxMsg->rm_Args[0]=String;

	if(!(Filled=FillRexxMsg(RexxMsg, 1, 0)))
		goto CleanUp;

	FORBID
		if(REXXMastPort=FindPort("REXX"))
			{
			PutMsg(REXXMastPort, (struct Message *)RexxMsg);
			PendingRxReplies++;
			}
	PERMIT

	return(TRUE);
CleanUp:
	if(Filled)	ClearRexxMsg(RexxMsg, 1);
	if(RexxMsg)	DeleteRexxMsg(RexxMsg);

	return(FALSE);
}


void SetArgstringFlags(UBYTE *string, UBYTE flags)
{
UBYTE	*FlagPtr;

	FlagPtr=string-2;

//	Printf("rexxstr flags:%ld\n", FlagPtr[0]);

	FlagPtr[0]=flags;
}


BOOL SaveSettings(UBYTE *FileName)
{
BOOL	NameLoop=FALSE;
BPTR	FileH=NULL;
ULONG	Counter=0, LastObject=0, LastName=0;
UBYTE	*Value=NULL, *Name=NULL;

	if(!(FileH=Open(FileName, MODE_NEWFILE)))
		return(FALSE);

	while(1)
		{
		if(!(ProjectAttrs[Counter].AttrName))
			{
			if(NameLoop)
				{
				Counter=LastObject;

				if(!(((UBYTE **)(ProjectAttrs[Counter].Value))[LastName+1]))
					goto JumpOut;

				}
			else
				goto JumpOut;
			}

		if(ProjectAttrs[Counter].Flags & AAF_OBJECT)
			{
			if(NameLoop)
				{
				LastName++;
				FPrintf(FileH, "\n");

				if(((UBYTE **)(ProjectAttrs[LastObject].Value))[LastName])
					{
					Counter=LastObject;
					}
				else
					{
					NameLoop=FALSE;
					LastObject=Counter;
					}
				}
			else
				LastObject=Counter;
			}

		if(ProjectAttrs[Counter].Flags & AAF_SETTING)
			{
			if(ProjectAttrs[Counter].Flags & AAF_NAME)
				{
				if(!(NameLoop))
					{
					NameLoop=TRUE;
					LastName=0;
					Name=NULL;
					FPrintf(FileH, "\n");
					}
				if(ProjectAttrs[Counter].Flags & AAF_STRARY)
					{
					Name=((UBYTE **)(ProjectAttrs[Counter].Value))[LastName];
					}
				}

			if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
				ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_GETATTR, Name, NULL);

			if(ProjectAttrs[Counter].Flags & AAF_FIELD)
				{
				if(ProjectAttrs[Counter].Flags & AAF_LONG)
					{
					Value=NULL;

					if(ProjectAttrs[Counter].Value)
						{
						stcl_d(TempNumBuf, *((ULONG *)ProjectAttrs[Counter].Value));
						Value=TempNumBuf;
						}
					}
				else
					{
					if(ProjectAttrs[Counter].Flags & AAF_STRVAR)
						Value=StrVarAry[(ULONG)(ProjectAttrs[Counter].Value)];
					else
						Value=*((UBYTE **)ProjectAttrs[Counter].Value);
					}

				FPrintf(FileH, "%s%s%s.%s=%s\n", ProjectAttrs[LastObject].AttrName, Name?".":"", Name?Name:"", ProjectAttrs[Counter].AttrName,  Value?Value:"");
				}
			}
		Counter++;
		}

JumpOut:
	if(FileH)	Close(FileH);
	return(TRUE);
}

BOOL LoadSettings(UBYTE *FileName)
{
BOOL	Result=FALSE;
BPTR	FileH=NULL;
ULONG	LineNum=0, Counter=0, Length, Bulk=0, LastObject=0, TempL;
UBYTE	*AttrObjStr, *AttrNameStr, *AttrFieldStr, *AttrValueStr;
UBYTE	*LastNameStr="", *LastObjStr="";

	if(!(FileH=Open(FileName, MODE_OLDFILE)))
		return(FALSE);


	while(FGets(FileH, LineBuffer, LIN_BUFFER_SIZE-1))
		{
		LineNum++;
		AttrObjStr=LineBuffer;
		AttrNameStr=NULL;
		AttrFieldStr=NULL;
		AttrValueStr=NULL;

		 // Ta bort NewLine
		//
		Length=strlen(LineBuffer);
		if(Length && (LineBuffer[Length-1]=='\n'))
			{
			Length--;
			LineBuffer[Length]=0;
			}

		if(Length)
			{
			 // Separera upp OBJECT, NAME, FIELD och VALUE
			//
			if(AttrValueStr=strchr(LineBuffer, '='))
				{
				*AttrValueStr=0;
				AttrValueStr++;
				}
			if(AttrNameStr=strchr(AttrObjStr, '.'))
				{
				*AttrNameStr=0;
				AttrNameStr++;
				}
			if(AttrFieldStr=strchr(AttrNameStr, '.'))
				{
				*AttrFieldStr=0;
				AttrFieldStr++;
				}

			if(!(AttrFieldStr))
				{
				AttrFieldStr=AttrNameStr;
				AttrNameStr=NULL;
				}

			if(AttrNameStr>AttrValueStr)
				AttrNameStr=NULL;


			 // Stega fram till rätt OBJECT
			//
			Counter=0;
			while(ProjectAttrs[Counter].AttrName)
				{
				if(ProjectAttrs[Counter].Flags & AAF_OBJECT)
					{
					if(!(stricmp(ProjectAttrs[Counter].AttrName, AttrObjStr)))
						break;
					}
				Counter++;
				}


			if(ProjectAttrs[Counter].AttrName)
				{
				 // Detta OBJECT och/eller NAME skiljer sig från raden innan
				//
				if(strcmp(AttrObjStr, LastObjStr) || strcmp(AttrNameStr?AttrNameStr:"", LastNameStr))
					{
					 // Var vi i Bulk-mode innan?
					//
					if(Bulk)
						{
						 // Kör förra OBJECT's Update-funktion med STOP
						//
						ProjectAttrs[LastObject].UpdateFunc(&ProjectAttrs[LastObject], UDF_SETATTR | UDF_STOP | UDF_BULK, LastNameStr, NULL);

						 // Använder det nya OBJECT'et BULK-initiering?
						//
						if((ProjectAttrs[Counter].Flags & AAF_BULK) && ProjectAttrs[Counter].UpdateFunc)
							{
							ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | UDF_START | UDF_BULK, AttrNameStr, NULL);
							Bulk=UDF_BULK;
							}
						 // OBJECT'et har en Update-funktion, med det är inte BULK
						//
						else if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
							{
							ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | UDF_START | UDF_BULK, AttrNameStr, NULL);
							Bulk=0;
							}
						else
							{
							Bulk=0;
							}
						}
					 // Det var inte Bulk-mode tidigare
					//
					else
						{
						 // Använder det nya OBJECT'et BULK-initiering?
						//
						if((ProjectAttrs[Counter].Flags & AAF_BULK) && ProjectAttrs[Counter].UpdateFunc)
							{
							ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | UDF_START | UDF_BULK, AttrNameStr, NULL);
							Bulk=UDF_BULK;
							}
						 // OBJECT'et har en Update-funktion, med det är inte BULK
						//
						else if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
							{
							ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | UDF_START | UDF_BULK, AttrNameStr, NULL);
							Bulk=0;
							}
						else
							{
							Bulk=0;
							}
						}
					LastObject=Counter;
					LastObjStr=ProjectAttrs[Counter].AttrName;
					SetStrVar(SV_TEMP, AttrNameStr?AttrNameStr:"");
					LastNameStr=StrVarAry[SV_TEMP];
					}


				 // Stega fram till rätt FIELD
				//
				Counter++;
				while(ProjectAttrs[Counter].AttrName && (ProjectAttrs[Counter].Flags & AAF_FIELD))
					{
					if(!(stricmp(ProjectAttrs[Counter].AttrName, AttrFieldStr)))
						break;
					Counter++;
					}

				 // Om FIELD fanns och är en skrivbar setting, skall vi göra det
				//
				if(ProjectAttrs[Counter].AttrName && (ProjectAttrs[Counter].Flags & AAF_FIELD))
					{
					 // FIELD är ett heltal
					//
					if(ProjectAttrs[Counter].Flags & AAF_LONG)
						{
						TempL=atol(AttrValueStr?AttrValueStr:"");
						 // FIELD har en Update-funktion
						//
						if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
							{
							ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | Bulk, AttrNameStr, (APTR)TempL);
							}
						 // Ingen Update-funktion, skriv direkt i variabeln
						//
						else if(ProjectAttrs[Counter].Value)
							{
							(*((ULONG *)ProjectAttrs[Counter].Value))=TempL;
							}
						}
					 // FIELD är en sträng
					//
					else
						{
						if((ProjectAttrs[Counter].Flags & AAF_UPDATE) && ProjectAttrs[Counter].UpdateFunc)
							ProjectAttrs[Counter].UpdateFunc(&ProjectAttrs[Counter], UDF_SETATTR | Bulk, AttrNameStr, (APTR)AttrValueStr?AttrValueStr:"");
						else if(ProjectAttrs[Counter].Flags & AAF_STRVAR)
							SetStrVar((ULONG)ProjectAttrs[Counter].Value, AttrValueStr?AttrValueStr:"");
						}
					}
				}
			}
		}

	 // Senaste rundan var i BULK-mode, kör OBJECT's Update-funktionen
	//
	if(Bulk)
		{
		ProjectAttrs[LastObject].UpdateFunc(&ProjectAttrs[LastObject], UDF_SETATTR | UDF_STOP | UDF_BULK, LastNameStr, NULL);
		}

	if(IoErr())
		{
		goto CleanUp;
		}

JumpOut:
	Result=TRUE;
CleanUp:
	if(FileH)	Close(FileH);
	return(Result);
}



//
//	Update-funktioner
//
//
//
UpdateProto(Update_DevName)
{
	if(Flags & UDF_GETATTR)
		return(0);

	if(Flags & UDF_SETATTR)
		{
		CloseDown_CDFuncs();

		SetStrVar(SV_DEVICE, NewValue);

		DriveType=-1;
		if(!(CD_Inquiry()))
			return(1);

		CD_Ping(2);
		}
return(0);
}

UpdateProto(Update_DevUnit)
{
	if(Flags & UDF_GETATTR)
		return(0);

	if(Flags & UDF_SETATTR)
		{
		CloseDown_CDFuncs();

		CDUnitNumber=(ULONG)NewValue;

		DriveType=-1;
		if(!(CD_Inquiry()))
			return(1);

		CD_Ping(2);
		}
return(0);
}

UpdateProto(Update_Artist)
{
	if(Flags & UDF_GETATTR)
		AAStrPtr=CD_Titles[0]?CD_Titles[0]:ApplStr(LT_UNKNOWNARTIST);

return(0);
}

UpdateProto(Update_Title)
{
	if(Flags & UDF_GETATTR)
		AAStrPtr=CD_Titles[1]?CD_Titles[1]:ApplStr(LT_UNKNOWNCDTITLE);

return(0);
}

UpdateProto(Update_TrackArtist)
{
ULONG	TempL;

	if(!NameArg)
		return(1);

	TempL=atol(NameArg);
	if(TempL<TOCBuffer->FirstTrack || TempL>(TOCBuffer->LastTrack))
		return(1);

	if(Flags & UDF_GETATTR)
		{
		AAStrPtr=CD_Artist[TempL+1]?CD_Artist[TempL+1]:CD_Titles[0];
		AAStrPtr=AAStrPtr?AAStrPtr:ApplStr(LT_UNKNOWNARTIST);
		}

return(0);
}

UpdateProto(Update_TrackTitle)
{
ULONG	TempL;

	if(!NameArg)
		return(1);

	TempL=atol(NameArg);
	if(TempL<TOCBuffer->FirstTrack || TempL>(TOCBuffer->LastTrack))
		return(1);

	if(Flags & UDF_GETATTR)
		AAStrPtr=CD_Titles[TempL+1]?CD_Titles[TempL+1]:ApplStr(LT_UNKNOWNTITLE);
return(0);
}

UpdateProto(Update_TrackLyrics)
{
ULONG	TempL;

	if(!NameArg)
		return(1);

	TempL=atol(NameArg);
	if(TempL<TOCBuffer->FirstTrack || TempL>(TOCBuffer->LastTrack))
		return(1);

	if(Flags & UDF_GETATTR)
		AAInt=CD_Lyrics[TempL]?1:0;
return(0);
}

UpdateProto(Update_TrackLength)
{
ULONG	TempL;

	if(!NameArg)
		return(1);

	TempL=atol(NameArg);
	if(TempL<TOCBuffer->FirstTrack || TempL>(TOCBuffer->LastTrack))
		return(1);

	if(Flags & UDF_GETATTR)
		{
		sprintf(TmpTrkLen, "%02d:%02d",
			(TOCBuffer->Data[TempL].AbsoluteAddress-TOCBuffer->Data[TempL-1].AbsoluteAddress)/BlocksPerSec/60,
			(TOCBuffer->Data[TempL].AbsoluteAddress-TOCBuffer->Data[TempL-1].AbsoluteAddress)/BlocksPerSec%60);

		AAStrPtr=TmpTrkLen;
		}

return(0);
}

UpdateProto(Update_DiscLength)
{
ULONG	TempL;

	TempL=(TOCBuffer->Data[TOCBuffer->LastTrack].AbsoluteAddress)/BlocksPerSec;

	if(Flags & UDF_GETATTR)
		{
		sprintf(TmpTrkLen, "%02d:%02d",
			TempL/60,
			TempL%60);

		AAStrPtr=TmpTrkLen;
		}

return(0);
}

UpdateProto(Update_TrackCount)
{
	if(Flags & UDF_GETATTR)
		AAInt=TOCBuffer->LastTrack;

return(0);
}

UpdateProto(Update_Drive)
{
	if(Flags & UDF_GETATTR)
		{
		if(!(CD_Inquiry()))
			return(1);

		switch(CDStatus)
			{
			case 0:		AAInt=0;	break;
			case 17:	AAInt=1;	break;
			case 18:	AAInt=2;	break;
			case 21:	AAInt=3;	break;
			default:	AAInt=255;
			}

		SetStrVar(SV_TEMP2, "");

		if(Compatibility & COMPAT_ACER)
			CatStrVar(SV_TEMP2, "ATAPI_ACER ");

		if(Compatibility & COMPAT_OLDTOSHIBA)
			CatStrVar(SV_TEMP2, "OLD_TOSHIBA ");

		if(Compatibility & COMPAT_BTC)
			CatStrVar(SV_TEMP2, "BTC ");

		if(Compatibility & COMPAT_NEC)
			CatStrVar(SV_TEMP2, "NEC ");

		if(!(Compatibility))
			SetStrVar(SV_TEMP2, "-No fixes-");
		}
return(0);
}

UpdateProto(Update_Disc)
{
	AAStrPtr="limbo pointer";

	if(Flags & UDF_GETATTR)
		{
//		if(!(CD_ReadTOC(0)))
//			return(1);
		}
return(0);
}

UpdateProto(Update_ID)
{
	AAStrPtr = &CD_Identifier[0];

return(0);
}

UpdateProto(Update_BarCode)
{
	CD_GetEANBarCode();
	AAStrPtr = &CD_EANBarCode[0];

return(0);
}

UpdateProto(Update_IDFile)
{
	AAStrPtr = StrVar(SV_IDFILE, "");

return(0);
}

UpdateProto(Update_Bps)
{
	if(Flags & UDF_GETATTR)
		return(0);

	if(Flags & UDF_SETATTR)
		{
		BlocksPerSec=(ULONG)NewValue?(ULONG)NewValue:75;
		CD_Ping(2);
		}
return(0);
}

UpdateProto(Update_Stereo)
{
	if(Flags & UDF_GETATTR)
		return(0);

	if(Flags & UDF_SETATTR)
		{
		if(Wins[WIN_MAIN] && (StereoVolume != (ULONG)NewValue))
			{
			StereoVolume=(ULONG)NewValue;
			PopWin(NULL);
			}

		StereoVolume=(ULONG)NewValue;
		}
return(0);
}

UpdateProto(Update_BulkWindow)
{
struct	Window	*TempWin=NULL;
ULONG		Counter;

	if(Flags & UDF_GETATTR)
		{
		if(!NameArg)
			return(1);

		for(Counter=0; WinNameAry[Counter]; Counter++)
			{
			if(!(stricmp(NameArg, WinNameAry[Counter])))
				{
				switch(Counter)
					{
					case 0:
					TempWin=Wins[WIN_MAIN];
					break;
					case 1:
					TempWin=Wins[WIN_LYRIC];
					break;
					case 2:
					TempWin=NULL;
					break;
					case 3:
					TempWin=NULL;
					break;
					case 4:
					TempWin=NULL;
					break;
					case 5:
					TempWin=NULL;
					break;
					}
				break;
				}
			}

		if(TempWin)
			{
			BulkWindow.Left=TempWin->LeftEdge;
			BulkWindow.Top=TempWin->TopEdge;
			BulkWindow.Width=TempWin->Width;
			BulkWindow.Height=TempWin->Height;
			BulkWindow.Open=1;
			BulkWindow.Title=TempWin->Title;
			}
		else
			{
			BulkWindow.Left=BWins[Counter].Left;
			BulkWindow.Top=BWins[Counter].Top;
			BulkWindow.Width=BWins[Counter].Width;
			BulkWindow.Height=BWins[Counter].Height;
			BulkWindow.Open=0;
			BulkWindow.Title="";
			}
		}

	if(Flags & UDF_SETATTR)
		{
		if(!NameArg)
			return(1);

		for(Counter=0; WinNameAry[Counter]; Counter++)
			{
			if(!(stricmp(NameArg, WinNameAry[Counter])))
				{
				break;
				}
			}

		if(!(WinNameAry[Counter]))
			return(1);

		 // Alla fälten skall uppdateras på en gång
		//
		if(Flags & UDF_BULK)
			{
			 // Nu har vi gått igenom alla fälten, -uppdatera.
			//
			if(Flags & UDF_STOP)
				{
				if(Counter==WIN_MAIN)
					{
					BWins[WIN_MAIN].Left=BulkWindow.Left;
					BWins[WIN_MAIN].Top=BulkWindow.Top;
					BWins[WIN_MAIN].Width=BulkWindow.Width;
					BWins[WIN_MAIN].Height=BulkWindow.Height;

					if(BulkWindow.Open==1)
						{
						if(Wins[WIN_MAIN])
							{
							ChangeWindowBox(Wins[WIN_MAIN],
								BWins[WIN_MAIN].Left,
								BWins[WIN_MAIN].Top,
								BWins[WIN_MAIN].Width,
								Wins[WIN_MAIN]->Height);

							PopWin(NULL);
							}
						else
							{
							if(PopWin(NULL))
								{
								PopDownIcon();
								CD_Ping(1);
								}
							}
						}
					else
						{
						if(Wins[WIN_MAIN])
							{
							if(PopUpIcon())
								OffWin();
							}
						else
							{
							PopUpIcon();
							}
						}
					}

				if(Counter==WIN_LYRIC)
					{
					BWins[WIN_LYRIC].Left=BulkWindow.Left;
					BWins[WIN_LYRIC].Top=BulkWindow.Top;
					BWins[WIN_LYRIC].Width=BulkWindow.Width;
					BWins[WIN_LYRIC].Height=BulkWindow.Height;

					if(BulkWindow.Open)
						{
						if(Wins[WIN_LYRIC])
							{
							ChangeWindowBox(Wins[WIN_LYRIC],
									BWins[WIN_LYRIC].Left,
									BWins[WIN_LYRIC].Top,
									BWins[WIN_LYRIC].Width,
									Wins[WIN_LYRIC]->Height
									);
							PopLyricWin();
							}
						else
							{
							PopLyricWin();
							}
						}
					else
						{
						if(Wins[WIN_LYRIC])
							{
							CloseLyricWin();
							}
 						}
					}
				}
			 // Initialisera strukturen till värden som betyder "unset"
			//
			else if(Flags & UDF_START)
				{
				if(Wins[Counter])
					{
					BulkWindow.Left=Wins[Counter]->LeftEdge;
					BulkWindow.Top=Wins[Counter]->TopEdge;
					BulkWindow.Width=Wins[Counter]->Width;
					BulkWindow.Height=Wins[Counter]->Height;
					BulkWindow.Open=1;
					BulkWindow.Title=NULL;
					}
				else
					{
					BulkWindow.Left=BWins[Counter].Left;
					BulkWindow.Top=BWins[Counter].Top;
					BulkWindow.Width=BWins[Counter].Width;
					BulkWindow.Height=BWins[Counter].Height;
					BulkWindow.Open=0;
					BulkWindow.Title=NULL;
					}
				}
			}

		return(0);
		}
return(0);
}

UpdateProto(Update_BulkFont)
{
ULONG		Counter, Temp;

	if(!NameArg)
		return(1);

	for(Counter=0; FontNameAry[Counter]; Counter++)
		{
		if(!(strcmp(NameArg, FontNameAry[Counter])))
			break;
		}

	if(!(FontNameAry[Counter]))
		return(1);

	if(Flags & UDF_GETATTR)
		{
		AAInt=0;
		AAStrPtr=NULL;
		switch(Counter)
			{
			case 2:
			SetStrVar(SV_TEMP2, StrVarAry[SV_LYRICFONT]);
			AAInt=FontSizeAry[2];
			break;

			case 1:
			SetStrVar(SV_TEMP2, StrVarAry[SV_DIGITFONT]);
			AAInt=FontSizeAry[1];
			break;

			case 0:
			SetStrVar(SV_TEMP2, StrVarAry[SV_TEXTFONT]);
			AAInt=FontSizeAry[0];
			break;
			}
		if(!(StrVarAry[SV_TEMP] && AAInt))
			{
			SetStrVar(SV_TEMP2, "topaz.font");
			AAInt=8;
			}
		}

	if(Flags & UDF_SETATTR)
		{
		 // Alla fälten skall uppdateras på en gång
		//
		if(Flags & UDF_BULK)
			{
			 // Nu har vi gått igenom alla fälten, -uppdatera.
			//
			if(Flags & UDF_STOP)
				{
				switch(Counter)
					{
					case 2:
					Temp=SV_LYRICFONT;
					if(AAInt)
						FontSizeAry[2]=AAInt;
					break;

					case 1:
					Temp=SV_DIGITFONT;
					if(AAInt)
						FontSizeAry[1]=AAInt;
					break;

					case 0:
					Temp=SV_TEXTFONT;
					if(AAInt)
						FontSizeAry[0]=AAInt;
					break;
					}

				if(StrVarAry[SV_TEMP2])
					{
					SetStrVar(Temp, StrVarAry[SV_TEMP2]);

					if(!(strcmpend(StrVar(Temp, ""), ".font")))
						CatStrVar(Temp, ".font");

					Temp=1;
					}
				else
					Temp=0;

				if(Temp || AAInt)
					{
					if((Counter<=1) && (Wins[WIN_MAIN]))
						{
						OffWin();
						PopWin(NULL);
						}

					if((Counter==2) && (Wins[WIN_LYRIC]))
						{
						CloseLyricWin();
						PopLyricWin();
						}
					}
				}
			 // Initialisera till värden som betyder "unset"
			//
			else if(Flags & UDF_START)
				{
				SetStrVar(SV_TEMP2, NULL);
				AAInt=0;
				}
			}

		return(0);
		}
return(0);
}

UpdateProto(Update_Repeat)
{
UWORD		Pos;

	if(Flags & UDF_GETATTR)
		{
		AAInt=(Repeat & 3);
		}
	if(Flags & UDF_SETATTR)
		{
		if(((ULONG)NewValue)>3)
			return(1);

		Repeat=(ULONG)NewValue;

		if(!(Wins[WIN_MAIN]))
			return(0);

		Pos=RemoveGadget(Wins[WIN_MAIN], TrackCycleGad);
		if((Repeat & 3)==2)
			{
			((struct Image *)TrackCycleGad->GadgetRender)->ImageData=BitMapAry[16];
			((struct Image *)TrackCycleGad->SelectRender)->ImageData=BitMapAry[17];
			}
		else if((Repeat & 3)==3)
			{
			((struct Image *)TrackCycleGad->GadgetRender)->ImageData=BitMapAry[18];
			((struct Image *)TrackCycleGad->SelectRender)->ImageData=BitMapAry[19];
			}
		else if((Repeat & 3)==1)
			{
			((struct Image *)TrackCycleGad->GadgetRender)->ImageData=BitMapAry[14];
			((struct Image *)TrackCycleGad->SelectRender)->ImageData=BitMapAry[15];
			}
		AddGadget(Wins[WIN_MAIN], TrackCycleGad, Pos);
		RefreshGadgets(TrackCycleGad, Wins[WIN_MAIN], NULL);
		}
return(0);
}


UpdateProto(Update_CreateIcons)
{
	if(Flags & UDF_GETATTR)
		{
		AAInt=42;
		}
	if(Flags & UDF_SETATTR)
		{
		}
return(0);
}

UpdateProto(Update_AllowMacros)
{
	if(Flags & UDF_GETATTR)
		{
		AAInt=RunLyricMacros;
		}
	if(Flags & UDF_SETATTR)
		{
		RunLyricMacros=(BOOL)(NewValue?1:0);
		SetCheckMark(2, CHK_RUN_LYRICSMACROS, RunLyricMacros);
		}
return(0);
}


UpdateProto(Update_BulkVolume)
{
	if(Flags & UDF_GETATTR)
		{
		CD_GetVolume(&lvol, &rvol, NULL, NULL);
		}

	if(Flags & UDF_SETATTR)
		{
		if(Flags & UDF_BULK)
			{
			 // Nu har vi gått igenom alla fälten, -uppdatera.
			//
			if(Flags & UDF_STOP)
				{
				CD_SetVolume(lvol, rvol, 0, 0);

				if(Wins[WIN_MAIN])
					{
					if(LeftVolGad)
						GT_SetGadgetAttrs(LeftVolGad, Wins[WIN_MAIN], NULL, GTSL_Level, lvol, TAG_END);
					if(RightVolGad)
						GT_SetGadgetAttrs(RightVolGad, Wins[WIN_MAIN], NULL, GTSL_Level, rvol, TAG_END);
					}
				}
			 // Initialisera till värden som betyder "unset"
			//
			else if(Flags & UDF_START)
				{
				}
			}

		}
return(0);
}


 //	Format som Thorsten Marquardt ville ha.
//
ULONG TM_AToBlock(UBYTE *String)
{
ULONG	Result, Temp;

//	ttmmssbb t-track m-minute s-second b-block
//	01234567

	Result=((String[0]-'0')*10);
	Result+=(String[1]-'0');

	Result=TOCBuffer->Data[Result-1].AbsoluteAddress;

	Temp=((String[2]-'0')*600);
	Temp+=((String[3]-'0')*60);
	Temp+=((String[4]-'0')*10);
	Temp+=(String[5]-'0');

	Result+=(Temp*75);
	Result+=((String[6]-'0')*10);
	Result+=(String[7]-'0');

return(Result);
}