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

		|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
		|            ACDPlay - Utility              |
		|           -------------------             |
		|                                           |
		|               Version 1.51                |
		|___________________________________________|

   Autoren  : Marc Altmann  [MC3]
              Martin Kresse [mak]
   Copyright: E-Mailware

   Was: ACDPlay ist ein fantastisches Utility zum Abspielen von
		CDDA-Disks auf SCSI-CD-ROM-Laufwerken.

   Umgebung : Maxon C++ light 3.10.1 auf Amiga 12oo OS3.0 68o3o 2MB Chip 8MB Fast

   Status   : letzte öffentliche Version 1.48b
			  nächste geplante öffentliche Version 1.5

   Bugs     : - Manchmal werden die Parameter von ShowMsgA() in der Shell
				falsch angezeigt


   Autor   Version   Datum      	Veränderungen
  ------- --------- --------   	------------------------------------------
	MC3		0.1		22.06.96	Ersterstellung (Programmgerüst)

	mak		0.11	19.09.96	Sourcecode überarbeitet
                                - ShowMsg() dazu
                    20.09.96    - OpenLibs() / CloseLibs() dazu
                                - GetMem() / FreeAllMem() dazu
                                - InitCDStruct() / FreeCDStruct() dazu

	mak		0.12	22.09.96	- OpenApplication() dazu
								- SetDefaultApplication() vorläufig dazu
								- OpenApplicationWindow() dazu
					24.09.96	- CloseApplication() dazu
								- CloseAppWin() dazu
								- TextWidth() dazu
					26.09.96	- GetMem / FreeAllMem entfernt
								- CreateApplication() dazu
								- ReadConfig() dazu

	MC3		0.13	28.09.96	- IDCMP-Verwaltung implementiert
								- HandleEvents() dazu (nur rudimentär)
								- erste Commodity-Eigenschaften (Entfernen)
								- HandleCommodityEvents() dazu
					29.09.96	- Weitere Commodity-Eigenschaften (Verbergen, Sichtbar)
								- Hotkey-fähig
								- CX_PRIORITY-, CX_POPUP-, CX_POPKEY-Unterstützung

	MC3		0.14	30.09.96	- MakeMenus() dazu (Behelfsversion)
								- HandleEvents() um MENUPICK-Events ausgebaut
					02.10.96	- MakeMenus() in geplanter Form
								- FindWidest() dazu
					03.10.96	- About-Window fertiggestellt
								- Init/OpenCDStruct() und FreeCDStruct() verändert
					04.10.96	- Laufwerksinformationsfenster fertiggestellt

	mak		0.15	07.10.96	- Konstante STDWINDOWFLG eingeführt
								- ReadConfig() greift notfalls auf Defaultwerte aus
								- SaveConfig() und FreeConfig() dazu
					08.10.96	- GetVec() dazu
								- main() Struktur überarbeitet
								  (keine Enforcer-Hits mehr)
					10.10.96	- Read- und SaveConfig() fertiggestellt
								- CreateApp.() benutzt jetzt cfg-Struktur
								- CX-Bug in CreateApp.() entfernt
					11.10.96	- HandleEvents() unterstüzt jetzt "Multi-Selection"
								- Parameter *Window aus ShowMsg genommen
								- AllocVec´s() in GetVec´s() geändert

	mak		0.16.a	12.10.96	- WindowToFront() u. ActivateWindow()
								  sind jetzt in OpenAppWin() integriert

	mak		0.17.a	12.10.96	- Das EXECUTABLE-Flag wird jetzt bei
								  SaveConfig() gelöscht
								- Bug in SaveConfig() (wenn app=NULL) entfernt
								- Bug in HandleEvents() entfernt
								  (wenn Multi-Selection: 1.Verbergen 2.Über)

	MC3		0.17b	14.10.96	- CxFilter und CxSender zur Timer-Event-Synchronisation
								  erstellt (jede 1/10-Sekunde)
								- CD- und Laufwerks-Fenster jetzt zusätzlich mit Textgadgets,
								  zur besseren Aktualisierung (beim LW-Fenster unnötig)
								- Bug in der Hauptschleife behoben. Ein 'done = TRUE' in
								  HandleEvents() wurde mitunter in HandleCommodityEvents()
								  wieder rückgängig gemacht.
								- FlushMessages() dazu - beantwortet alle Messages eines Ports

	MC3		0.18	16.10.96	- Gadgets im Hauptfenster fertiggestellt. Durch ein Flag-Word
								  in der Application-Struktur können die einzelnen Reihen mit
								  Bedienungselementen an- oder ausgeschaltet werden.

	mak		0.19	17.10.96	- einige nichtüberprüfte GetVecs() überarbeitet
								- SCSI_DEVICE Check in OpenCDStruct() eingebaut
								- einige überflüssige Befehle entfernt
					18.10.96	- ToolTypes werden jetzt auch gelesen
								- OpenApplication holt den Screen nach vorne falls
								  die Application schon offen ist
								- Screennotify-Unterstützung für Open/CloseWorkBench()
								- Application.Flags kann frei benutzt werden

	MC3		0.20	19.10.96	- MakeInterior() und UpdateGadgets() erweitert (Fenstertitel)
								- Menü des Hauptfensters erweitert. Kontrolle der GUI-Elemente
								  nun über Menü möglich
								- Bug in CloseAppWin() behoben. Bei FreeVec() usw. wurden die
								  Zeiger nicht auf NULL gesetzt
								- Wenn keine GUI-Elemente angegeben werden, wird das Fenster als
								  Titelzeile *ohne* Zoom-Gadget göffnet
					20.10.96	- Neues Tooltype: PUBSCREEN
								- PubScreen-Fenster eingebaut (stürzt manchmal ab!)
								- Allerlei Listenfunktionen erstellt
					21.10.96	- Abstürz-Bug entfernt (siehe 20.10)
								- GetPubScreenList() und KillPubScreenList() an das
								  Application-Konzept angepaßt
					22.10.96	- Eine Reihe von Abspielfunktionen eingebaut, die sich hoffentlich
								  für AREXX eignen.

	???		0.21	???			???

	mak		0.22	24.10.96	- InitList() durch NewList() aus amiga.lib ersetzt
								- ClearMenuStrip steht jetzt vor CloseWindow() (keine Enforcerhits mehr)
								- Statusflags in Source eingefügt
								- das Pubscreen-Fenster wird jetzt richtig aufgefrischt (ohne GimmeZeroZero)
					26.10.96	- Programm lokalisiert
								- testweise Drive-Info Fenster verändert

	MC3		0.23	26.10.96	- Gadgets werden beim Öffnen des Fensters geshadet
								- Trackslider funktioniert (noch nicht ganz bugfrei)
								- ACDPlay_cat.c ab jetzt überflüssig
					27.10.96	- Bugfix: Wenn die CD zuende war, spielte der Trackslider verrückt
								- Bugfix: Trackslider-Level wird in MakeGadgets() gesetzt
			0.24	06.11.96	- Alpha-CD-Titel-Verwaltung implementiert
								- Aktualisierung der Zeitanzeige (pro Sekunde ein neues Gadget - krass!)
								- KillPubScreenList() entfernt, dafür FreeList() eingeführt
					07.11.96	- Fenstertitel arbeitet jetzt mit wirklichen CD-Namen (UpdateWinTitle())
								- Kein Flackern mehr beim Pausegadget-Refresh (außerdem: eigene Funktion)
								- NewSize-Events werden jetzt durch ManageNewSize() gehandhabt
								- Neues Fenster: Titelauswahl
			0.25	09.11.96	- VANILLAKEY-Support für Direktwahlbuttons
					10.11.96	- Neues Fenster: Titeleingabe (funktioniert!)
								- In den Info-Fenstern werden Intuitexte jetzt weiß gezeichnet
								- Index-Anzeige zeigt jetzt '1' beim Spielen an... Fake!  ;-)

			0.26	11.11.96	- Neues Tooltype: HOTKEYCAUSES
								- GetPubScreenName() holt sich einen bestimmten Screennamen
								- kleine Bugfixes (von >>>mak<<< erwünscht :-)
								- Im Titelauswahl- und Pubscreenfenster jetzt Doppelklick
								  notwendig, um ein Element auszuwählen

	mak		0.26b	11.11.96	- Bug in OpenCDStruct() entfernt (zehnstellige Unitnummer :-)

	MC3		0.27	13.11.96	- Zwei neue Tooltypes: USEWINDOWBAR und USESCREENBAR
								- Zeitanzeige mit 4 verschiedenen Modi implementiert
								- Alle Fenster benutzen denselben Menustrip
								- Code-Ersparnisse in MakeMenus() und HandleEvents()
								- Cursortasten können benutzt werden (ausprobieren!)

	mak		0.28	18.11.96	- AsmLib eingeführt, welche OpenLibs(), CloseLibs(), ShowMsg()
								  GetVec(), FlushMessages() und MyFormat() (jetzt FormatA()) durch Assemblerroutinen
								  (z.B. ShowMsgA()) ersetzt. Die Parameter bleiben wie gehabt.
								- ARexxPort (in AsmLib) integriert

	MC3		0.29	19.11.96	- Trackwiederholung eingebaut (Problem beim letzten Track)
								- Schreiben und Lesen von Titeldaten implementiert;
								  Pfad als Tooltype DISKPATH angeben oder "ACDPlay:Disks/"
								- AllocDosObject()-Einsatz auch in ReadConfig()

					21.11.96	- Bugfix: Wiederholung funktioniert jetzt (bis auf letzter Track)
								- Bugfix: Die Cycle-Gadgets zeigen jetzt den richtigen Modus an,
								  wenn sie erneut erzeugt werden.
								- Bugfix: Jede Sekunde wurden 2 * sizeof(Gadget) Bytes 'gefressen'
								- Wiederholung Track und Alles funktionieren vollständig (nach
								  Deinem System)

	mak		0.30	23.11.96	- ARexx endlich fertig und integriert (Errorrückgabe muß ich mir noch überlegen)
								  ACDPlay kennt jetzt: PLAY STOP EJECT NEXTSONG PREVSONG SHOW HIDE und QUIT
								- FindARexxPortA() am Anfang eingefügt

	MC3		0.31	29.11.96	- Programmierung implementiert (Arexx-günstig)
					30.11.96	- Bugfixes

	mak		0.32	30.11.96	- Volumewindow (z.Z. noch ohne Funktion) eingebaut
								- Menupunkt Fixieren funktioniert jetzt
								- Bugfix: Absturz im Editwindow wenn Programmwindow nicht offen war

	MC3		0.33	07.12.96	- Zufallsmodus eingebaut
								- Fixieren fixiert jetzt auch Fenstergrößen
								- Volume-Fenster und A-B-Repeat ausgeklammert
								- CD-Info-Fenster entsprechend Device-Info-Fenster geändert

			0.34	08.12.96	- Neues Tooltype: MAGICWB
								- MagicWB-Toggle-Menü funktioniert
								- FakeRequestA() bei drei Fenstern eingesetzt (sieht gut aus!)

			1.0		14.12.96	- SEARCHSECONDS-Tooltype
								- ein paar Bugs weg, Catalogs geändert

	mak		1.0b	15.12.96	- Bugfix in MakeInterior (EnforcerHits)
								- Volumeüberreste auskommentiert

	MC3		1.01a	01.01.97	- 'Update'-Button im Pubscreen-Fenster
								- Funktion AdjustNewSize() für zukünftige Sizing-Wins
								- In MakeVisuals() (ehem. MakeGadgets()) aufgeräumt
					02.01.97	- Die beiden Info-Fenster sind jetzt größenveränderbar
					04.01.97	- Das About-Fenster scrollt jetzt - mußte dazu leider die
								  Parameter bei TextWidth() und FindWidest() ändern.
								- Nicht-aktualisierte-Titel-Bug ist weg... :-)
			1.01ß	05.01.97	- numeric-Keypad-Bug ist weg!  :-)
								- Neue Menüs

			1.1		26.01.97	- Config-Teil total verändert
								- wahnsinnig viel anderes verändert :-)    (2 Tooltypes)
								- CLI-Parameter

			1.12	29.01.97	- AppIcon

	mak		1.3		02.02.97	- Play überspringt Datentracks am Anfang
								- viele CDPlayLib-Funktionen durch eigene ersetzt
					03.02.97	- Bug in ChangeButtonText() und ReadArguments() entfernt
					08.02.97	- restliche CD-Routinen ersetzt
								- einige Optimierungen im Source

	MC3		1.31	14.02.97	- ChangeButtonText() optimiert (ein ½ Hack!) + Asm
								- Richtige Searchbuttons... (und Trackslider)
								  (Ich weiß leider nicht, wie Du das im SCSI-Test-Programm
								  implementiert hattest. Wenn es besser war, bitte ersetzen)
								- den Betatestern gegeben

			1.32	16.02.97	- "PROGDIR:ARexx" wird im ARexx-Menü angezeigt
								- "Execute ARexx..." geht. mit Asl-Requester (nur mit asl.lib)

			1.33	16.02.97	- Neues Fenster A-B-Repeat: Vorsicht mit dem Konfig-File! Wenn man
								  ein neues Konfig-File hat und ein altes ACDP lädt, stürzt es ab(!)
								- Lokalisiert (catalogs version 2)

			1.34	17.02.97	- Programm- und
					19.02.97	- Titeleingabefenster sind jetzt größenveränderbar

	mak		1.35	01.03.97	- ExecuteARexx() funktioniert jetzt in Asm
								- Bugs in CloseApplication(), ReadConfig(), GetDir() und CreateAppIcon() beseitigt
								- ARexxfunktionen geben jetzt endlich definierte Rückgabewerte zurück
								- cdplay.library vollständig eleminiert
								- NULL-test in einige Funktionen eingebaut

	MC3		1.36	11.03.97	- A-B-Repeat funktioniert
								- alte ChangeButtonText()-Funktion
								- []-Bug entfernt (hoffentlich!)
								- setzen der Statusflags (im Source) vergessen... :-(

	mak		1.37	12.03.97	- Eject funktioniert jetzt richtig
								- Laufwerkssperre und Unit/LUN-Wechsel funktionieren jetzt
								- ACDPlay spult jetzt nicht mehr selbstständig
								- MathLib Check entfernt: nutzte nichts :-(
					13.03.97	- an einige Betatester gegeben

	MC3		1.4		14.03.97	- A-B-Repeat-Bug entfernt
								- Volumefenster im Menü geshadet
								- ans Amiga-Magazin geschickt

			1.41	23.03.97	- UpdateGadgets() in viele Funktionen aufgeteilt
								- einige Aktionen lösen jetzt direkt Aktualisierungen aus
							      (Trackslider, etc.)
							    - BF: HOTKEYCAUSES-Tooltype war immer auf HIDE

	mak		1.42	27.03.97	- BF: Konfig wird jetzt auch in ENV: gespeichert
					28.03.97	- Volumefenster funktioniert jetzt
								- Konfigversion 3 (da Fenster dazu: sonst Absturz)

	MC3		1.43	28.03.97	- A-B-Repeat-Sicherheitsleck behoben
								- neues Tooltype: SEARCHMODE
								  (Ergebnis steht in app->cdstr->searchmode)
								- Beim Titeleditieren werden Standardtitel gelöscht
								- Pfusch am Volume-Fenster :-)

	mak		1.44	29.03.97	- Slider im Volumefenster funktionieren jetzt unabhängig
								- cdx_SimpleStatus in cdx_Active zurückumbenannt
					02.04.97	- verschiedene Searchmodi
					03.04.97	- das Indexgadget ist jetzt kein Fake mehr

			1.45	05.04.97	- Sony-Searchmode dazu
								- globale Variablen CDROM_DEVICE und CDROM_UNIT werden benutzt,
								  falls kein ToolType oder Parameter vorhanden
								- bei DOS-Fehlern werden nun Fehlermeldungen statt Codes ausgegeben
								- UpdateUnitLUN() arbeitet jetzt vernünftig, glaube ich...

	MC3		1.46	05.04.97	- Öfteres Aktualisieren beim Spulen
								- ein paar Verbesserungen
	mak				08.04.97	- TOC wird bei zu langer Updatezeit aktualisiert
			1.47	20.04.97	- Volumerefreshing in MakeInterior() eingebaut
								- Tracktest in ProgramAppendElement() eingebaut (für ARexx)
			1.48	27.04.97	- MainWindow wird in ProcessStruktur eingetragen (für Requester)
								- WindowTitel flackern jetzt nicht mehr bei Volume-Fade-Makros
								- viele neue ARexx-Cmds dazu
	mak		1.49	11.07.97	- 2 Enforcerhits entfernt
								- ListEmpty() durch IsListEmpty()/exec.library ersetzt
					16.07.97	- MakeBusy() erstellt und in ARexxMenü eingebaut
								- kleine Verbesserungen
					28.07.97	- Menü Laufwerk/Laufwerksperre wird jetzt abhängig von cdx_Flags & SPECIALF_LOCKED gesetzt
					30.07.97	- cd.device-Laustärkeeinstellung (MONO) implementiert (was bei mir nicht funktioniert!)

	MC3		1.5		18.10.97	- Über Shift und Alt jetzt auch die Tracks 11-30 direkt anwählbar
								- ACDP kommt jetzt mit unvollständigen Titeldateien zurecht

			1.51	13.11.97	- Die A-B-Range wird jetzt durch absolute Adressen angegeben
								  -> Geschwindigkeitssteigerung bei TestABRepeat()
								- Fenster zur Audioabspeicherung programmiert
					22.11.97	- neue Verwaltung für ASL-Requester (ähnlich AppWindows)
								- GetFileName() ist jetzt allgemein verwendbar
					29.11.97	- ExamineDiskSpace() für Audiosaving entwickelt


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


/*	Erklärung der Statusflags:	< > keine Änderung		*** Die Statusflags sind toll ***
								<c> verändert
								<n> neu
								<x> entfernt */

/*----- Includes ---------------< >-*/
#include "Constants.h"

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <libraries/commodities.h>
#include <libraries/asl.h>
#include <graphics/rastport.h>
#include <devices/scsidisk.h>
#include <devices/cd.h>
#include <libraries/screennotify.h>
#include <pragma/screennotify_lib.h>
#include "AsmLib/CD-ROM.h"
#include "Locale.h"
#include "Structures/Application.h"
#include "Structures/DirNode.h"
#include "Structures/MyPubScreenNode.h"
#include "Structures/CDTitleNode.h"

#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/asl_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/commodities_protos.h>
#include <clib/icon_protos.h>
#include <clib/locale_protos.h>
#include <clib/wb_protos.h>
#include <wbstartup.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "ACDPlay_protos.h"

/*----- globale Variablen ------< >-*/
#define UNIT 0

char *ls[ANZ_MSG];
struct LocaleInfo li = {NULL, NULL};

struct Library *IntuitionBase 	= NULL;
struct Library *CxBase			= NULL;
struct Library *GadToolsBase  	= NULL;
struct Library *GfxBase			= NULL;
struct Library *IconBase		= NULL;
struct Library *LocaleBase		= NULL;
struct Library *RexxSysBase		= NULL;
struct Library *ScreenNotifyBase= NULL;
struct Library *WorkbenchBase	= NULL;
struct Library *AslBase			= NULL;

static char	PROG_VERSION[]				= "\0$VER: ACDPlay 1.5 [000] (24.12.97)";
char 	   *PROG_FULLNAME				= PROG_VERSION + 7;
char 		PROG_NAME[]					= "ACDPlay";



/*------------------ main() -----------------------------------< >-*/
int main(int argc, char *argv[])
{
	struct Application *app = NULL;
	ULONG rc = RETURN_FAIL;					/* Return-Wert auf 20 setzen */

	if (FindARexxPortA()) return(0);		/* ACDPlay läuft schon */

	if (!(OpenLibsA()))
	{
		CloseLibsA(); return(RETURN_FAIL);
	}

	if (app = CreateApplication(argv[0]))
	{
		ULONG		signals;
		ULONG		waitbitmask;
		BOOL		done = FALSE;

		if (app->cx_popup)
			OpenApplication(app);	/* nur öffnen, wenn CX_POPUP */
		else
			CreateAppIcon(app);

		/***********************************
		** Hier beginnt die Event-Abfrage **
		***********************************/
		while (!done)
		{
			waitbitmask	= 0;

			/* Control-C Signalbit dazu setzen */
			waitbitmask |= SIGBREAKF_CTRL_C;

			/* Signalbits des IDCMP dazu setzen */
			if (app->idcmp)
				waitbitmask |= (1L << app->idcmp->mp_SigBit);

			/* ARexxportsignalbit dazu odern :-) */
			waitbitmask |= (1L << app->arexx_mp->mp_SigBit);

			/* Das Appicon-Bit auch noch */
			if (app->appicon_mp)
				waitbitmask |= (1L << app->appicon_mp->mp_SigBit);

			/* Signalbit des Broker-Messageports dazu setzen */
			waitbitmask |= (1L << app->broker_mp->mp_SigBit);

			/* Signalbit des Screennotify-Ports dazu setzen */
			if (app->scrnotify_mp)
				waitbitmask |= (1L << app->scrnotify_mp->mp_SigBit);

			signals = Wait(waitbitmask);

			if (app->idcmp)
				if ((signals & (1L << app->idcmp->mp_SigBit)) && !done)
					done = HandleEvents(app);

			if ((signals & (1L << app->arexx_mp->mp_SigBit)) && !done)
				done = HandleARexxMsgA(app->arexx_mp, app);

			if (app->appicon_mp)
				if ((signals & (1L << app->appicon_mp->mp_SigBit)) && !done)
					HandleAppIconEvents(app);

			if ((signals & (1L << app->broker_mp->mp_SigBit)) && !done)
				done = HandleCommodityEvents(app);

			if (app->scrnotify_mp)
				if (signals & (1L << app->scrnotify_mp->mp_SigBit))
					HandleScreenNotify(app);

			if (signals & SIGBREAKF_CTRL_C)
				done = TRUE;
		}
		rc = RETURN_OK;
		DeleteApplication(app);
	}

	CloseLibsA();
	return rc;
}

void CopyRange(struct Application *app)
{
	char	*buf;
	UBYTE	command[12];
	int		i;

	struct SCSICmd *cmd;

	OpenAppWin(app, WINDOW_SAVING);

	if (buf = (char *)GetVecA(100000, MEMF_PUBLIC | MEMF_CLEAR))
	{
		printf("1");

		command[0] = 0xA8;	/* Command Description Block ausfüllen */
		command[1] = 0x00;
		command[2] = (app->cdstr->a_adr >> 24);
		command[3] = ((app->cdstr->a_adr >> 16) & 0xFF);
		command[4] = ((app->cdstr->a_adr >> 8) & 0xFF);
		command[5] = (app->cdstr->a_adr & 0xFF);
		command[6] = 0x0;
		command[7] = 0x0;
		command[8] = 0x0;
		command[9] = 0x01;
		command[10] = 0x0;
		command[11] = 0x0;

		cmd = app->cdstr->cdx->cdx_Cmd;

		printf("2");

		if (cmd)
		{
			cmd->scsi_Data			= (UWORD *)buf;
			cmd->scsi_Length		= 100000;
			cmd->scsi_Actual		= 0;
			cmd->scsi_Command  		= command;
			cmd->scsi_CmdLength		= 12;
			cmd->scsi_CmdActual		= 0;
			cmd->scsi_Flags			= SCSIF_READ | SCSIF_AUTOSENSE;
			cmd->scsi_Status		= 0;
			cmd->scsi_SenseData		= app->cdstr->cdx->cdx_SCSISense;
			cmd->scsi_SenseLength	= 252;
			cmd->scsi_SenseActual	= 0;

			printf("3");

			if (app->cdstr->cdx->cdx_IORequest)
			{
				app->cdstr->cdx->cdx_IORequest->io_Command = HD_SCSICMD;
				app->cdstr->cdx->cdx_IORequest->io_Length = sizeof(struct SCSICmd);
				app->cdstr->cdx->cdx_IORequest->io_Data = (APTR)app->cdstr->cdx->cdx_Cmd;
				printf("4");
				printf("  %d  ", DoIO((struct IORequest *)app->cdstr->cdx->cdx_IORequest));
				printf("5");

				/* Ausgabe */
				printf("\nSCSI-Command:");
				for (i = 0; i < 10; i++)
					printf(" %02x", (int)command[i]);

				printf("\nSense Key:");
				for (i = 2; i < 4; i++)
					printf(" %02x", (int)*(((char *)cmd->scsi_SenseData) + i));

				printf("\nAdditional Sense Code & Qualifier:");
				for (i = 12; i < 14; i++)
					printf(" %02x", (int)*(((char *)cmd->scsi_SenseData) + i));

				SaveFile("ram:test", buf, 100000);
			}
		}
		FreeVec(buf);
	}

	/*
	DoCDDBID(app->cdstr);
	printf("ID:");
	printf(app->cdstr->buf_cddb_id);
	*/
}

/*------------------ ReadConfig() -----------------------------< >-*/
void ReadConfig(struct Application *app, char *filename)
{
	WORD *buffer = NULL;
	WORD i;
	LONG err;

	/* Voreinstellungen */
	for (i = 0; i < ANZ_WINDOWS; i++)
	{
		app->appwin[i].minsize[LEFT]	= 0;
		app->appwin[i].minsize[TOP]		= 0;
		app->appwin[i].minsize[WIDTH]	= MINWIDTH_LISTVIEWWIN;
		/* Sonst wird aus nichtinitialisierten Feldern gelesen */
		if (app->screen)
			app->appwin[i].minsize[HEIGHT]	= MINHEIGHT_LISTVIEWWIN;
		else
			app->appwin[i].minsize[HEIGHT]	= 100;

		app->appwin[i].maxsize[LEFT]	= 200;
		app->appwin[i].maxsize[TOP]		= 100;
		app->appwin[i].maxsize[WIDTH]	= 150;
		app->appwin[i].maxsize[HEIGHT]	= 150;

		app->appwin[i].maxwidth			= ~0;
		app->appwin[i].maxheight		= ~0;
	}
	app->flag_main = FLAG_MAIN_DIRECTCHOICE0 | FLAG_MAIN_DIRECTCHOICE1 | FLAG_MAIN_TITLE | FLAG_MAIN_TIME | FLAG_MAIN_MODE | FLAG_MAIN_PANEL | FLAG_MAIN_TRACKSLIDER;

	if (buffer = (WORD *)OpenFile(filename))
	{
		WORD *offset = buffer;

		if ((*(LONG *)offset) == MAGIC)
		{
			UWORD win_anz, win_size, j;

			offset	   += 2;
			win_anz		= *(offset++);
			win_size	= *(offset++);

			for (i = 0; i < (ANZ_WINDOWS > win_anz ? ANZ_WINDOWS : win_anz); i++)
			{
				for (j = 0; j < 4; j++)
					app->appwin[i].minsize[j] = *(offset++);
				for (j = 0; j < 4; j++)
					app->appwin[i].maxsize[j] = *(offset++);

				app->appwin[i].winopen = *(offset++);

				((char *)offset) += win_size - SIZE_WINDOW;
			}

			((char *)offset) += (win_anz - ANZ_WINDOWS) * win_size;
			app->flag_main = *(LONG *)offset;
		}
		else ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_CFGFILE], filename);

		FreeVec(buffer);
	}
	else if ((err=IoErr()) != ERROR_OBJECT_NOT_FOUND)	/* nervt sonst... */
	{
		char errmsg[FAULT_MAX];
		Fault(err, NULL, errmsg, FAULT_MAX);
		ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_BAD_FILE], filename, errmsg);
	}

	/* Das auf jeden Fall */
	app->appwin[WINDOW_MAIN].winopen = TRUE;
	app->appwin[WINDOW_SAVING].winopen = FALSE;
}
/*------------------ SaveConfig() -----------------------------< >-*/
BOOL SaveConfig(Application *app, char *filename)
{
	BOOL success = FALSE;
	WORD i, j;
	WORD *buffer = NULL;

	/* Wenn die Application noch offen ist, werden die Windowkoordinaten */
	/* ausgelesen. Sonst hat CloseApplication() das schon erledigt. */
	if (app->screen)
	{
		for (i = ANZ_WINDOWS-1; i >= 0; i--)
		{
			if (app->appwin[i].winopen && app->appwin[i].window)
			{
				app->appwin[i].maxsize[LEFT]	= app->appwin[i].window->LeftEdge;
				app->appwin[i].maxsize[TOP]		= app->appwin[i].window->TopEdge;
			}
		}
	}

	/* Ein wenig Speicher allozieren und dort alles hineinschreiben */
	if (buffer = GetVecA(SIZE_CONFIG, MEMF_PUBLIC | MEMF_CLEAR))
	{
		WORD *offset = buffer;

		*(LONG *)offset = MAGIC;
		offset += 2;

		*(offset++) = ANZ_WINDOWS;
		*(offset++) = SIZE_WINDOW;

		for (i = 0; i < ANZ_WINDOWS; i++)
		{
			for (j = 0; j < 4; j++)
				*(offset++) = app->appwin[i].minsize[j];
			for (j = 0; j < 4; j++)
				*(offset++) = app->appwin[i].maxsize[j];

			*(offset++) = app->appwin[i].winopen;
		}

		*(LONG *)offset = app->flag_main;

		if (SaveFile(filename, (char *)buffer, SIZE_CONFIG))
			success = TRUE;

		/* Execute-Flag löschen */
		SetProtection(filename, FIBF_EXECUTE);

		FreeVec(buffer);
	}

	return (success);
}
/*------------------ ReadArguments() --------------------------< >-*/
void ReadArguments(struct Application *app)
{
	struct RDArgs *rda;
	struct DiskObject *dobj;
	char **toolarray, *s;
	char var[30];

	/* Erst werden die Voreinstellungen gesetzt */
	LONG unit = 0, lun = 0, updatedelay = 5, priority = 0;
	LONG vec[] = {	(LONG)	"1230scsi.device",
					(LONG)	&unit,
					(LONG)	&lun,
					(LONG)	"Workbench",
							FALSE,
							TRUE,
							FALSE,
							TRUE,
					(LONG)	NULL,
					(LONG)	&updatedelay,
					(LONG)	NULL,
					(LONG)	"PROGDIR:Disks/",
							TRUE,
					(LONG)	"alt shift F1",
					(LONG)	&priority};

	if (GetVar("CDROM_UNIT", var, 2, 0) != -1)
		unit = atoi(var);

	if (GetVar("CDROM_DEVICE", var, 30, 0) != -1)
		vec[0] = (LONG)var;

	/* Dann werden diese durch eventuelle Tooltypes überschrieben */
	if (dobj = GetDiskObject(app->progname))
	{
		toolarray = dobj->do_ToolTypes;

		if (s = (char *)FindToolType((UBYTE **)toolarray, "DEVICE"))
			vec[0] = (LONG)s;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "UNIT"))
			unit = atoi(s);
		if (s = (char *)FindToolType((UBYTE **)toolarray, "LUN"))
			lun = atoi(s);
		if (s = (char *)FindToolType((UBYTE **)toolarray, "PUBSCREEN"))
			vec[3] = (LONG)s;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "MAGICWB"))
			vec[4] = (MatchToolValue(s, "NO") || MatchToolValue(s, "no")) ? FALSE : TRUE;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "USEWINDOWBAR"))
			vec[5] = (MatchToolValue(s, "NO") || MatchToolValue(s, "no")) ? FALSE : TRUE;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "USESCREENBAR"))
			vec[6] = (MatchToolValue(s, "NO") || MatchToolValue(s, "no")) ? FALSE : TRUE;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "QUITONCLOSE"))
			vec[7] = (MatchToolValue(s, "NO") || MatchToolValue(s, "no")) ? FALSE : TRUE;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "HOTKEYCAUSES"))
			vec[8] = (LONG)s;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "UPDATEDELAY"))
			updatedelay = atoi(s);
		if (s = (char *)FindToolType((UBYTE **)toolarray, "SEARCHMODE"))
			vec[10] = (LONG)s;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "DISKPATH"))
			vec[11] = (LONG)s;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "CX_POPUP"))
			vec[12] = (MatchToolValue(s, "NO") || MatchToolValue(s, "no")) ? FALSE : TRUE;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "CX_POPKEY"))
			vec[13] = (LONG)s;
		if (s = (char *)FindToolType((UBYTE **)toolarray, "CX_PRIORITY"))
			priority = atoi(s);
	}

	/* Shell-Argumente können jedoch auch diese überschreiben */
	if (rda = AllocDosObject(DOS_RDARGS, NULL))
	{
		rda->RDA_ExtHelp =	"[4mACDPlay CLI parameters:[24m\n"
							"[1mDEVICE:        [22mdevice driver where the cd drive is connected, default: 1230scsi.device\n"
							"[1mUNIT:          [22munit that belongs to the drive, default: 0\n"
							"[1mLUN:           [22mstart logical unit number, default: 0\n"
							"[1mPUBSCREEN:     [22mwhere to start ACDPlay, default: Workbench\n"
							"[1mMAGICWB:       [22mwhether ACDPlay should use MagicWB graphics, default: no\n"
							"[1mUSEWINDOWBAR:  [22menables window bar as information display, default: yes\n"
							"[1mUSESCREENBAR:  [22menables screen bar as information display, default: no\n"
							"[1mQUITONCLOSE:   [22mwhether ACDPlay should quit when main window was closed, default: yes\n"
							"[1mHOTKEYCAUSES:  [22mSCREENTOFRONT, HIDE or FRONTMOST, default: SCREENTOFRONT\n"
							"[1mUPDATEDELAY:   [22mdelay between SCSI updates in 1/10 secs, default: 5\n"
							"[1mSEARCHMODE:    [22mSTANDARD, TOSHIBA or SONY (advanced search modes), default: STANDARD\n"
							"[1mDISKPATH:      [22mpath where ID and program files are stored, default: PROGDIR:Disks/\n"
							"[1mCX_POPUP:      [22mwhether the GUI is displayed when started, default: yes\n"
							"[1mCX_POPKEY:     [22mkey combination to popup GUI, default: lshift alt F1\n"
							"[1mCX_PRIORITY:   [22mpriority as a commodity, default: 0\n\n"
							"Enter parameters";

		if (ReadArgs("DEV=DEVICE/K,UNIT/N,LUN/N,PUBSCREEN/K,MWB=MAGICWB/T,USEWINDOWBAR/T,USESCREENBAR/T,QUITONCLOSE/T,HOTKEYCAUSES/K,UPDATEDELAY/N,SEARCHMODE/K,DISKPATH/K,CX_POPUP/T,CX_POPKEY/K,CX_PRIORITY/N", vec, rda))
		{
			FreeArgs(rda);
		}

		FreeDosObject(DOS_RDARGS, rda);
	}

	/* Werte verbindlich eintragen */
	strncpy(app->cdstr->device,		(char *)vec[0], 30);
	app->cdstr->unit			=	FitInteger(*(LONG *)vec[1], 0, 7);
	app->cdstr->lun				=	FitInteger(*(LONG *)vec[2], 0, 7);

	strncpy(app->pubscreenname,		(char *)vec[3], MAXPUBSCREENNAME);
	app->flag_tool				=	InsertBoolean(app->flag_tool, FLAG_TOOL_MWB, vec[4]);
	app->flag_tool				=	InsertBoolean(app->flag_tool, FLAG_TOOL_USEWINDOWBAR, vec[5]);
	app->flag_tool				=	InsertBoolean(app->flag_tool, FLAG_TOOL_USESCREENBAR, vec[6]);
	app->flag_tool				=	InsertBoolean(app->flag_tool, FLAG_TOOL_QUITONCLOSE, vec[7]);

	if (vec[8])
	{
		if (strcmp((char *)vec[8], "HIDE") == 0)
			app->hotkeymode   = HOTKEYMODE_HIDE;
		else if (strcmp((char *)vec[8], "FRONTMOST") == 0)
			app->hotkeymode   = HOTKEYMODE_FRONTMOST;
		else
			app->hotkeymode   = HOTKEYMODE_SCREENTOFRONT;
 	}

	app->cdstr->updatedelay		=	FitInteger(*(LONG *)vec[9], 1, 50);

	if (vec[10])
	{
		if (strcmp((char *)vec[10], "TOSHIBA") == 0)
			app->cdstr->searchmode   = SEARCHMODE_TOSHIBA;
		else if (strcmp((char *)vec[10], "SONY") == 0)
			app->cdstr->searchmode   = SEARCHMODE_SONY;
		else
			app->cdstr->searchmode   = SEARCHMODE_STANDARD;
 	}

	app->cdstr->searchseconds	=	10;

	strncpy(app->cdstr->diskpath,	(char *)vec[11], DISKPATHLENGTH - 1);   /* evt. muß noch ein '/' ran */
	if ((app->cdstr->diskpath[strlen(app->cdstr->diskpath) -1] != ':') && (app->cdstr->diskpath[strlen(app->cdstr->diskpath) - 1] != '/'))
		strcat(app->cdstr->diskpath, "/");

	app->cx_popup				=	vec[12];
	strncpy(app->cx_popkey,			(char *)vec[13], 40);
	app->cx_priority			=	FitInteger(*(LONG *)vec[14], -128, 127);

	if (dobj) FreeDiskObject(dobj);	/* Damit die Strings erhalten bleiben */
}

/*------------------ SaveTitles() -----------------------------< >-*/
BOOL SaveTitles(Application *app)
{
	BOOL success 	= FALSE;

	if (!IsListEmpty(app->cdstr->cdtitles))
	{
		int i, bufferlen;
		char *buffer;

		bufferlen = strlen(app->cdstr->artistname) + strlen(app->cdstr->cdname) + 2;
		for (i = 0; i < app->cdstr->num_track; i++)
			bufferlen += strlen((FindNode(app->cdstr->cdtitles, i))->ln_Name) + 1;

		if (buffer = GetVecA(bufferlen, MEMF_PUBLIC | MEMF_CLEAR))
		{
			char *actpos = buffer;
			char *filename;

			/* Künstler speichern */
			strcpy(actpos, app->cdstr->artistname);
			actpos += strlen(app->cdstr->artistname);
			*(actpos) = 0x0A;
			actpos++;

			/* CD-Name speichern */
			strcpy(actpos, app->cdstr->cdname);
			actpos += strlen(app->cdstr->cdname);
			*(actpos) = 0x0A;
			actpos++;

			/* jeden Titel */
			for (i = 0; i < app->cdstr->num_track; i++)
			{
				strcpy(actpos, (FindNode(app->cdstr->cdtitles, i))->ln_Name);
				actpos += strlen((FindNode(app->cdstr->cdtitles, i))->ln_Name);
				*(actpos) = 0x0A;
				actpos++;
			}

			if (filename = GetVecA(strlen(app->cdstr->diskpath) + 22, MEMF_PUBLIC | MEMF_CLEAR))
			{
				strcpy(filename, app->cdstr->diskpath);
				strcat(filename, "ID");
				strcat(filename, app->cdstr->cdx->cdx_TOCData->CDID);

				if (SaveFile(filename, buffer, bufferlen))
					success = TRUE;

				FreeVec(filename);
			}

			FreeVec(buffer);
		}	}

	return (success);
}
/*------------------ ProgramStart() ---------------------------< >-*/
void ProgramStart(Application *app)
{
	if (app->cdstr->progtitles)
		if (!IsListEmpty(app->cdstr->progtitles))
		{
			switch(app->cdstr->playmode)
			{
				case PLAYMODE_PRG:
					app->cdstr->actprognode = (struct ProgramTitleNode *)app->cdstr->progtitles->lh_Head;
					break;
				case PLAYMODE_RNDPRG:
					app->cdstr->actprognode = GetRandomProgram(app) /*(struct ProgramTitleNode *)FindNode(app->cdstr->progtitles, RandomNum(0, ListLength(app->cdstr->progtitles)))*/;
					break;
			}
			CDPlayA(app->cdstr->cdx, app->cdstr->actprognode->ptn_track, 1);
		}
}
/*------------------ ProgramDelete() --------------------------< >-*/
BOOL ProgramDelete(Application *app)
{
	BOOL success = FALSE;

	/* Da diese Funktion auch von Arexx aus aufgerufen wird, muß sie alles mögliche */
	/* selber machen, z.B: */
	/* Laufwerk ggf. stoppen, Liste aus LV entfernen, Liste löschen, eine neue erstellen, */
	/* aus Prinzip Liste wieder ins LV bauen */

	if (((app->cdstr->playmode == PLAYMODE_PRG) || (app->cdstr->playmode == PLAYMODE_RNDPRG)) && ((app->cdstr->active == AUDIO_STAT_PLAYING) || (app->cdstr->active == AUDIO_STAT_PAUSED)))
	{
		CDStopA(app->cdstr->cdx);
		app->cdstr->useraction = TRUE;
	}

	if (app->appwin[WINDOW_PROGRAM].window)
		GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, ~0, TAG_END);

	FreeList(app->cdstr->progtitles);

	if (app->cdstr->progtitles = GetVecA(sizeof(struct List), MEMF_PUBLIC | MEMF_CLEAR))
	{
		NewList(app->cdstr->progtitles);
		GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, app->cdstr->progtitles, TAG_END);
		success = TRUE;
	}

	return (success);
}
/*------------------ ProgramAppendElement() -------------------< >-*/
BOOL ProgramAppendElement(Application *app, UWORD track)
{
	BOOL success = FALSE;
	struct ProgramTitleNode *newnode;

	if ((track > app->cdstr->cdx->cdx_TOCData->LastTrack) || (track < app->cdstr->cdx->cdx_TOCData->FirstAudioTrack))
		return (success);

	if (newnode = GetVecA(sizeof(struct ProgramTitleNode), MEMF_PUBLIC | MEMF_CLEAR))
	{
		if (app->appwin[WINDOW_PROGRAM].window)
			GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, ~0, TAG_END);

		newnode->ptn_track = track;
		newnode->ptn_Node.ln_Name = FindNode(app->cdstr->cdtitles, track - 1)->ln_Name;
		AddTail(app->cdstr->progtitles, (Node *) newnode);

		if (app->appwin[WINDOW_PROGRAM].window)
			GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, app->cdstr->progtitles, TAG_END);

		success = TRUE;
	}

	return (success);
}
/*------------------ ProgramDeleteElement() -------------------< >-*/
BOOL ProgramDeleteElement(Application *app, ULONG pos)
{
	BOOL success = FALSE;
	struct ProgramTitleNode *oldnode;

	if (pos < ListLengthA(app->cdstr->progtitles))
	{
		oldnode = (struct ProgramTitleNode *)FindNode(app->cdstr->progtitles, pos);

		if (app->appwin[WINDOW_PROGRAM].window)
			GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, ~0, TAG_END);

		/* evt. Spielvorgang abbrechen */
		if (((app->cdstr->playmode == PLAYMODE_PRG) || (app->cdstr->playmode == PLAYMODE_RNDPRG)) && ((app->cdstr->active == AUDIO_STAT_PLAYING) || (app->cdstr->active == AUDIO_STAT_PAUSED)) && (app->cdstr->actprognode == oldnode))
		{
			CDStopA(app->cdstr->cdx);
			app->cdstr->useraction = TRUE;
		}

		Remove((struct Node *)oldnode);
		FreeVec(oldnode);

		if (app->appwin[WINDOW_PROGRAM].window)
			GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, app->cdstr->progtitles, TAG_END);

		success = TRUE;
	}

	return (success);
}
/*------------------ ProgramCopyAll() -------------------------< >-*/
BOOL ProgramCopyAll(Application *app)
{
	BOOL success = FALSE;

	if (app->appwin[WINDOW_PROGRAM].window)
		GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, ~0, TAG_END);

	FreeList(app->cdstr->progtitles);

	if (app->cdstr->progtitles = GetVecA(sizeof(struct List), MEMF_PUBLIC | MEMF_CLEAR))
	{
		int i;
		struct ProgramTitleNode *tempnode;

		NewList(app->cdstr->progtitles);
		success = TRUE;

		for (i = 0; i < ListLengthA(app->cdstr->cdtitles); i++)
		{
			if (tempnode = GetVecA(sizeof(struct ProgramTitleNode), MEMF_PUBLIC | MEMF_CLEAR))
			{
				tempnode->ptn_track = i + 1;
				tempnode->ptn_Node.ln_Name = (FindNode(app->cdstr->cdtitles, i))->ln_Name;
				AddTail(app->cdstr->progtitles, ((struct Node *)tempnode));
			}
			else
				success = FALSE;
		}

		if ((app->cdstr->playmode == PLAYMODE_PRG) || (app->cdstr->playmode == PLAYMODE_RNDPRG))
		{
			switch (app->cdstr->active)
			{
				case AUDIO_STAT_PLAYING:
				case AUDIO_STAT_PAUSED:
					ProgramStart(app);
					app->cdstr->useraction = TRUE;
					break;
			}
			if (app->cdstr->active == AUDIO_STAT_PAUSED)
				CDPauseA(app->cdstr->cdx, TRUE);
		}
	}

	if (app->appwin[WINDOW_PROGRAM].window)
		GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, app->cdstr->progtitles, TAG_END);

	return (success);
}
/*------------------ ProgramSave() ----------------------------< >-*/
BOOL ProgramSave(Application *app)
{
	BOOL success = FALSE;
	char *filename;

	if (IsListEmpty(app->cdstr->progtitles))
	{
		if (filename = GetVecA(strlen(app->cdstr->diskpath) + 22, MEMF_PUBLIC | MEMF_CLEAR))
		{
			strcpy(filename, app->cdstr->diskpath);
			strcat(filename, "PR");
			strcat(filename, app->cdstr->cdx->cdx_TOCData->CDID);

			DeleteFile(filename);
			FreeVec(filename);
		}
		return (TRUE);
	}
	else
	{
		int bufferlen;
		char *buffer;

		bufferlen = ListLengthA(app->cdstr->progtitles);

		if (buffer = GetVecA(bufferlen, MEMF_PUBLIC | MEMF_CLEAR))
		{
			char *actpos = buffer;
			struct ProgramTitleNode *tempnode;

			for (tempnode = ((struct ProgramTitleNode *)app->cdstr->progtitles->lh_Head); tempnode->ptn_Node.ln_Succ; tempnode = (struct ProgramTitleNode *)tempnode->ptn_Node.ln_Succ)
			{
				*(actpos) = tempnode->ptn_track;
				actpos++;
			}

			if (filename = GetVecA(strlen(app->cdstr->diskpath) + 22, MEMF_PUBLIC | MEMF_CLEAR))
			{
				strcpy(filename, app->cdstr->diskpath);
				strcat(filename, "PR");
				strcat(filename, app->cdstr->cdx->cdx_TOCData->CDID);

				if (SaveFile(filename, buffer, bufferlen))
					success = TRUE;

				FreeVec(filename);
			}

			FreeVec(buffer);
		}
		return (success);
	}
}
/*------------------ ProgramLoad() ----------------------------< >-*/
BOOL ProgramLoad(Application *app)
{
	BOOL success = FALSE;
	char *wantedname;

	if (!app->cdstr->progtitles)
	{
		app->cdstr->progtitles = GetVecA(sizeof(struct List), MEMF_PUBLIC | MEMF_CLEAR);
		NewList(app->cdstr->progtitles);
	}

	if (wantedname = GetVecA(strlen(app->cdstr->diskpath) + 22, MEMF_PUBLIC | MEMF_CLEAR))
	{
		int filelength;
		char *filebuffer;

		strcpy(wantedname, app->cdstr->diskpath);
		strcat(wantedname, "PR");
		strcat(wantedname, app->cdstr->cdx->cdx_TOCData->CDID);

		if ((filelength = ExamineFileLength(wantedname)) && (filebuffer = OpenFile(wantedname)))
		{
			ProgramTitleNode *tempnode;

			if (app->appwin[WINDOW_PROGRAM].window)
				GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, ~0, TAG_END);

			FreeList(app->cdstr->progtitles);
			app->cdstr->progtitles = NULL;

			if (app->cdstr->progtitles = GetVecA(sizeof(struct List), MEMF_PUBLIC | MEMF_CLEAR))
			{
				register int i;
				NewList(app->cdstr->progtitles);

				success = TRUE;

				/* Für jedes Byte eine Node erstellen und ausfüllen */
				for (i = 0; i < filelength; i++)
				{
					if (tempnode = GetVecA(sizeof(struct ProgramTitleNode), MEMF_PUBLIC | MEMF_CLEAR))
					{
						tempnode->ptn_track = (UBYTE)filebuffer[i];
						tempnode->ptn_Node.ln_Name = FindNode(app->cdstr->cdtitles, (UBYTE)filebuffer[i] - 1)->ln_Name;
						AddTail(app->cdstr->progtitles, (Node *) tempnode);
					}
					else
						success = FALSE;
				}
			}

			if (app->appwin[WINDOW_PROGRAM].window)
				GT_SetGadgetAttrs(app->appwin[WINDOW_PROGRAM].gadget[GADGET_PROGRAM_LVPRG], app->appwin[WINDOW_PROGRAM].window, NULL, GTLV_Labels, app->cdstr->progtitles, TAG_END);

			if ((app->cdstr->playmode == PLAYMODE_PRG) || (app->cdstr->playmode == PLAYMODE_RNDPRG))
			{
				switch (app->cdstr->active)
				{
					case AUDIO_STAT_PLAYING:
					case AUDIO_STAT_PAUSED:
						ProgramStart(app);
						app->cdstr->useraction = TRUE;
						break;
				}
				if (app->cdstr->active == AUDIO_STAT_PAUSED)
					CDPauseA(app->cdstr->cdx, TRUE);
			}

			FreeVec(filebuffer);
		}
		FreeVec(wantedname);
	}

	return (success);
}
/*------------------ ProgramNext() ----------------------------< >-*/
BOOL ProgramNext(Application *app)
{
	BOOL success = FALSE;

	if (app->cdstr->actprognode)
	{
		switch (app->cdstr->playmode)
		{
			case PLAYMODE_PRG:
				if ((struct Node *)app->cdstr->actprognode == app->cdstr->progtitles->lh_TailPred)
				{
					if (app->cdstr->repeatmode == REPEATMODE_REPALL)
					{
						ProgramStart(app);
						success = TRUE;
					}
				}
				else
				{
					app->cdstr->actprognode = (struct ProgramTitleNode *)app->cdstr->actprognode->ptn_Node.ln_Succ;
					CDPlayVerify(app->cdstr, app->cdstr->actprognode->ptn_track);
					success = TRUE;
				}
				break;

			case PLAYMODE_RNDPRG:
				{
					struct ProgramTitleNode *tempnode = app->cdstr->actprognode;
					app->cdstr->actprognode = GetRandomProgram(app);

					if (app->cdstr->actprognode)
					{
						CDPlayVerify(app->cdstr, app->cdstr->actprognode->ptn_track);
						success = TRUE;
					}
					else
						app->cdstr->actprognode = tempnode;
				}
				break;
		}
	}

	return (success);
}
/*------------------ ProgramPrev() ----------------------------< >-*/
void ProgramPrev(Application *app)
{
	if (app->cdstr->actprognode)
	{
		switch (app->cdstr->playmode)
		{
			case PLAYMODE_PRG:
				if ((struct Node *)app->cdstr->actprognode != app->cdstr->progtitles->lh_Head)
				{
					app->cdstr->actprognode = (struct ProgramTitleNode *)app->cdstr->actprognode->ptn_Node.ln_Pred;
					CDPlayVerify(app->cdstr, app->cdstr->actprognode->ptn_track);
				}
				break;
			case PLAYMODE_RNDPRG:
				{
					struct ProgramTitleNode *tempnode = app->cdstr->actprognode;
					app->cdstr->actprognode = GetRandomProgram(app);

					if (app->cdstr->actprognode)
						CDPlayVerify(app->cdstr, app->cdstr->actprognode->ptn_track);
					else
						app->cdstr->actprognode = tempnode;
				}
				break;
		}
	}
}
/*------------------ GetRandomProgram() -----------------------< >-*/
struct ProgramTitleNode *GetRandomProgram(Application *app)
{
	struct ProgramTitleNode **buffer;
	struct ProgramTitleNode *returnnode = NULL;

	if (buffer = GetVecA(ListLengthA(app->cdstr->progtitles) * sizeof(struct ProgramTitleNode *), MEMF_PUBLIC | MEMF_CLEAR))
	{
		struct Node *tempnode;
		int j = 0;

		for (tempnode = app->cdstr->progtitles->lh_Head; tempnode->ln_Succ; tempnode = tempnode->ln_Succ)
			if (!(((struct ProgramTitleNode *)tempnode)->ptn_randomplayed))
			{
				buffer[j] = (struct ProgramTitleNode *)tempnode;
				j++;
			}

		if (j > 0)
		{
			returnnode = buffer[RandomNum(0, j - 1)];
			/* Flag auf TRUE setzen */
			returnnode->ptn_randomplayed = TRUE;
		}
		else
		{
			if (app->cdstr->repeatmode == REPEATMODE_REPALL)
			{
				InitRandomTracks(app->cdstr->progtitles);
				returnnode = GetRandomProgram(app);
			}
		}

		FreeVec(buffer);
	}

	return (returnnode);
}

/*------------------ GetRandomTrack() -------------------------< >-*/
UBYTE GetRandomTrack(Application *app)
{
	UBYTE *buffer;
	int returnval = 0;

	if (buffer = GetVecA(app->cdstr->num_track, MEMF_PUBLIC | MEMF_CLEAR))
	{
		struct Node *tempnode;
		int j = 0, i = 1;

		/* Feld erstellen, aus dem dann zufällig ein Element ausgewählt wird */
		for (tempnode = app->cdstr->cdtitles->lh_Head; tempnode->ln_Succ; tempnode = tempnode->ln_Succ, i++)
			if (!(((struct CDTitleNode *)tempnode)->cdtn_randomplayed))
			{
				buffer[j] = i;
				j++;
			}

		if (j > 0)	/* Falls alles schon gespielt wurde, wird 0 zurückgegeben */
		{
			returnval = buffer[RandomNum(0, j - 1)];
			/* Flag setzen */
			((struct CDTitleNode *)FindNode(app->cdstr->cdtitles, returnval - 1))->cdtn_randomplayed = TRUE;
		}
		else
		{
			if (app->cdstr->repeatmode == REPEATMODE_REPALL)
			{
				InitRandomTracks(app->cdstr->cdtitles);
				returnval = GetRandomTrack(app);
			}
		}

		FreeVec(buffer);
	}

	return (returnval);
}
/*------------------ InitRandomTracks() -----------------------< >-*/
void InitRandomTracks(struct List *tracklist)
{
	/* Dies hier cleared die Random-Flags in jeder einzelnen Node-Struktur */
	struct Node *tempnode;

	for (tempnode = tracklist->lh_Head; tempnode->ln_Succ; tempnode = tempnode->ln_Succ)
		((struct ProgramTitleNode *)tempnode)->ptn_randomplayed = FALSE;
}
/*------------------ CDPlayVerify() ---------------------------< >-*/
void CDPlayVerify(struct CDStruct *cds, UBYTE track)
{
	if (track != cds->cur_track)
		CDPlayA(cds->cdx, track, 1);
}



/*------------------ OpenAppWin() -----------------------------< >-*/
BOOL OpenAppWin(Application *app, WORD win)
{
	BOOL success = FALSE;

	/* PopUp falls Window schon offen ist */
	if (app->appwin[win].window != NULL)
	{
		WindowToFront(app->appwin[win].window);
		ActivateWindow(app->appwin[win].window);
		return (TRUE);
	}

	if (MakeVisuals(app, win))
	{
		ULONG idcmpflags = IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK;
		struct TagItem wintags[] = {	WA_Left,		app->appwin[win].maxsize[LEFT],
										WA_Top,			app->appwin[win].maxsize[TOP],
	 									WA_Width,		app->appwin[win].maxsize[WIDTH],
										WA_Height,		app->appwin[win].maxsize[HEIGHT],
										WA_Flags,		WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE | WFLG_NEWLOOKMENUS,
										WA_Gadgets,		(ULONG)app->appwin[win].gadgetlist,
										WA_PubScreen,	(ULONG)app->screen,
										WA_Title,		NULL,
										TAG_IGNORE,		(ULONG)&app->appwin[win].minsize,
										WA_MinWidth,	app->appwin[win].minwidth,
										WA_MinHeight,	app->appwin[win].minheight,
										WA_MaxWidth,	app->appwin[win].maxwidth,
										WA_MaxHeight,	app->appwin[win].maxheight,
										(IntuitionBase->lib_Version >= 39)?WA_BusyPointer:TAG_IGNORE,	(app->Flags & APPF_BUSY)?TRUE:FALSE,
										TAG_END		};

		switch(win)
		{
			case WINDOW_MAIN:
				idcmpflags |= BUTTONIDCMP | SLIDERIDCMP | IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_CHANGEWINDOW | IDCMP_INTUITICKS | IDCMP_MOUSEBUTTONS;
				/* gegebenenfalls ohne Zoomgadget öffnen */
				wintags[8].ti_Tag = app->flag_main ? WA_Zoom : TAG_IGNORE;
				break;

			case WINDOW_ABOUT:
				idcmpflags |= BUTTONIDCMP | IDCMP_INTUITICKS;
				wintags[7].ti_Data = (ULONG)ls[MSG_ABOUT_TITLE];
				break;

			case WINDOW_CDINFO:
				idcmpflags |= BUTTONIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data	= (ULONG)ls[MSG_CDINFO_TITLE];
				wintags[8].ti_Tag	= WA_Zoom;
				break;

			case WINDOW_DRIVEINFO:
				idcmpflags |= BUTTONIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data	= (ULONG)ls[MSG_DRIVEINFO_TITLE];
				wintags[8].ti_Tag	= WA_Zoom;
				break;

			case WINDOW_PUBSCREEN:
				idcmpflags |= LISTVIEWIDCMP | BUTTONIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data	= (ULONG)ls[MSG_PUBSCREEN_TITLE];
				wintags[8].ti_Tag	= WA_Zoom;
				break;

			case WINDOW_TITLES:
				idcmpflags |= LISTVIEWIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[8].ti_Tag	= WA_Zoom;
				break;

			case WINDOW_EDIT:
				idcmpflags |= BUTTONIDCMP | LISTVIEWIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data	= (ULONG)ls[MSG_EDIT_TITLE];
				wintags[8].ti_Data	= WA_Zoom;
				break;

			case WINDOW_PROGRAM:
				idcmpflags |= BUTTONIDCMP | LISTVIEWIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data	= (ULONG)ls[MSG_PROGRAM_TITLE];
				wintags[8].ti_Tag	= WA_Zoom;
				break;

			case WINDOW_VOLUME:
				idcmpflags |= SLIDERIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data = (ULONG)ls[MSG_VOLUME_TITLE];
				wintags[8].ti_Tag	= WA_Zoom;
				break;

			case WINDOW_AB:
				idcmpflags |= BUTTONIDCMP | STRINGIDCMP | IDCMP_NEWSIZE | IDCMP_CHANGEWINDOW;
				wintags[4].ti_Data |= WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM;
				wintags[7].ti_Data	= (ULONG)ls[MSG_AB_TITLE];
				wintags[8].ti_Tag	= WA_Zoom;
				break;
		}

		if (app->appwin[win].window = OpenWindowTagList(NULL, wintags))
		{
			success = TRUE;

			switch(win)
			{
				case WINDOW_MAIN:
					((struct Process *)FindTask(NULL))->pr_WindowPtr = app->appwin[win].window;
					break;
				case WINDOW_ABOUT:
				case WINDOW_CDINFO:
				case WINDOW_DRIVEINFO:
					/* cool :-) */
					FakeRequestA(app->appwin[win].window, app->visualinfo, FAKEREQUESTSPACE, FAKEREQUESTSPACE, app->appwin[win].maxsize[HEIGHT] - 3 - app->font->ta_YSize - 2 * FAKEREQUESTSPACE - INTERHEIGHT - BUTTONHEIGHT - (win == WINDOW_ABOUT ? 2 : 10));
					RefreshGadgets(app->appwin[win].gadgetlist, app->appwin[win].window, NULL);
					break;
				case WINDOW_PUBSCREEN:
					GetPubScreenList(app);
					break;
			}

			GT_RefreshWindow(app->appwin[win].window, NULL);

			/* Den shared-IDCMP mit noch einem Fenster teilen */
			app->appwin[win].window->UserPort = app->idcmp;
			ModifyIDCMP(app->appwin[win].window, idcmpflags);

			if (app->appwin[win].img && (win != WINDOW_MAIN))
				DrawImage(app->appwin[win].window->RPort, app->appwin[win].img, 0, 0);
			if (app->appwin[win].itext)
				PrintIText(app->appwin[win].window->RPort, app->appwin[win].itext, 0, 0);
			if (app->appwin[win].bbox)
				RefreshBevelBox(app->appwin[win].window->RPort, app->appwin[win].bbox, 0, 0);

			/*  Menü mit Fenster verbinden  */
			if (app->menustrip)
			{
				if (!SetMenuStrip(app->appwin[win].window, app->menustrip))
				{
					CloseWindow(app->appwin[win].window);
					return (FALSE);
				}
			}

			app->appwin[win].winopen = TRUE;

			/* Gadget-Inhalte zeichnen */
			MakeInterior(app, win);

			/* Fenster mit Requester blockieren, falls die Application busy ist */
			if (app->Flags & APPF_BUSY)
			{
				if (app->appwin[win].request = GetVecA(sizeof(struct Requester), MEMF_PUBLIC))
				{
					InitRequester(app->appwin[win].request);
					if (!Request(app->appwin[win].request, app->appwin[win].window))
					{
						FreeVec(app->appwin[win].request);
						app->appwin[win].request = NULL;
					}
				}
			}
		}
		else
			ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_WINDOW], app->pubscreenname);
	}

	return (success);
}
/*------------------ CloseAppWin() ----------------------------< >-*/
void CloseAppWin(Application *app, WORD win, BOOL realclose)
{
	if (!app->appwin[win].window)
		return;

	app->appwin[win].maxsize[LEFT]	= app->appwin[win].window->LeftEdge;
	app->appwin[win].maxsize[TOP]	= app->appwin[win].window->TopEdge;

	/* Menüs entfernen */
	if (app->menustrip)
		ClearMenuStrip(app->appwin[win].window);

	/* Requester entfernen */
	if (app->appwin[win].request)
	{
		EndRequest(app->appwin[win].request, app->appwin[win].window);
		FreeVec(app->appwin[win].request);
		app->appwin[win].request = NULL;
	}

	/* So, hier ist die CloseWindowSafely()-Funktion aus den RKMs integriert */
	Forbid();
	StripIntuiMessages(app->appwin[win].window);
	app->appwin[win].window->UserPort = NULL;
	ModifyIDCMP(app->appwin[win].window, 0L);
	Permit();
	/* originalen WindowPointer aus der Process-Struktur wieder eintragen */
	if (win == WINDOW_MAIN)
		((struct Process *)FindTask(NULL))->pr_WindowPtr = app->orgprwin;
	CloseWindow(app->appwin[win].window);
	app->appwin[win].window = NULL;

	if (realclose)
		app->appwin[win].winopen = FALSE;

	/* Gadgetliste(n) entfernen */
	if (app->appwin[win].gadgetlist)
	{
		FreeGadgets(app->appwin[win].gadgetlist);
		app->appwin[win].gadgetlist = NULL;
	}
	FreeVec(app->appwin[win].gadget);
	app->appwin[win].gadget = NULL;

	/* Intuitexte entfernen */
	FreeVec(app->appwin[win].itext);
	app->appwin[win].itext = NULL;

	/* Images entfernen */
	FreeVec(app->appwin[win].img);
	app->appwin[win].img = NULL;

	/* BevelBoxen entfernen */
	FreeVec(app->appwin[win].bbox);
	app->appwin[win].bbox = NULL;

	/* Window-spezifisches */
	switch (win)
	{
		case WINDOW_ABOUT:
			DisposeScrollObject(app->scroller);
			break;
		case WINDOW_PUBSCREEN:
			FreeList(app->pubscreenlist);
			app->pubscreenlist = NULL;
			break;
		case WINDOW_EDIT:
			if (realclose)
				app->edit_pos = -1;
			break;
	}
}
/*------------------ StripIntuiMessages() ---------------------< >-*/
void StripIntuiMessages(struct Window *win)
{
	/* Aus den RKMs, aber mit nur einem Parameter */

	struct IntuiMessage *msg;
	struct Node *succ;

	msg = (struct IntuiMessage *)win->UserPort->mp_MsgList.lh_Head;

	while (succ = msg->ExecMessage.mn_Node.ln_Succ)
	{
		if (msg->IDCMPWindow == win)
		{
			Remove((struct Node *)msg);
			ReplyMsg((struct Message *)msg);
		}
		msg = (struct IntuiMessage *)succ;
	}
}

/*------------------ DoTimeBuffer() ---------------------------< >-*/
VOID DoTimeBuffer(Application *app)
{
	/* Damit ist das Programm tatsächlich kürzer... */
	register struct CDxRequest *cdx = app->cdstr->cdx;

	switch (app->cdstr->timemode)
	{
		case TIMEMODE_TRACKCUR:
			FormatA(app->timeshowbuf, "%02ld:%02ld / %02ld:%02ld", (LONG)base2min(cdx->cdx_CurrentRelAddr), (LONG)base2sec(cdx->cdx_CurrentRelAddr), (LONG)base2min(cdx->cdx_TrackLength), (LONG)base2sec(cdx->cdx_TrackLength));
			break;
		case TIMEMODE_TRACKREM:
			FormatA(app->timeshowbuf, "%02ld:%02ld / %02ld:%02ld", (LONG)base2min(cdx->cdx_RelRemainBlocks), (LONG)base2sec(cdx->cdx_RelRemainBlocks), (LONG)base2min(cdx->cdx_TrackLength), (LONG)base2sec(cdx->cdx_TrackLength));
			break;
		case TIMEMODE_ALLCUR:
			FormatA(app->timeshowbuf, "%02ld:%02ld / %02ld:%02ld", (LONG)base2min(cdx->cdx_CurrentAbsAddr), (LONG)base2sec(cdx->cdx_CurrentAbsAddr), (LONG)base2min(cdx->cdx_TOCData->Entry[0].Address), (LONG)base2sec(cdx->cdx_TOCData->Entry[0].Address));
			break;
		case TIMEMODE_ALLREM:
			FormatA(app->timeshowbuf, "%02ld:%02ld / %02ld:%02ld", (LONG)base2min(cdx->cdx_AbsRemainBlocks), (LONG)base2sec(cdx->cdx_AbsRemainBlocks), (LONG)base2min(cdx->cdx_TOCData->Entry[0].Address), (LONG)base2sec(cdx->cdx_TOCData->Entry[0].Address));
			break;
	}
}
/*------------------ DoABBuffer() -----------------------------< >-*/
void DoABBuffer(Application *app)
{
	if (app->cdstr->a_track > 0)
		FormatA(app->cdstr->buf_ab_time_a, "%02ld:%02ld.%02ld", (LONG)base2min((app->cdstr->a_adr - app->cdstr->cdx->cdx_TOCData->Entry[app->cdstr->a_track].Address)), (LONG)base2sec((app->cdstr->a_adr - app->cdstr->cdx->cdx_TOCData->Entry[app->cdstr->a_track].Address)), (LONG)base2frm((app->cdstr->a_adr - app->cdstr->cdx->cdx_TOCData->Entry[app->cdstr->a_track].Address)));
	else
		strcpy(app->cdstr->buf_ab_time_a, "n/a");

	if (app->cdstr->b_track > 0)
		FormatA(app->cdstr->buf_ab_time_b, "%02ld:%02ld.%02ld", (LONG)base2min((app->cdstr->b_adr - app->cdstr->cdx->cdx_TOCData->Entry[app->cdstr->b_track].Address)), (LONG)base2sec((app->cdstr->b_adr - app->cdstr->cdx->cdx_TOCData->Entry[app->cdstr->b_track].Address)), (LONG)base2frm((app->cdstr->b_adr - app->cdstr->cdx->cdx_TOCData->Entry[app->cdstr->b_track].Address)));
	else
		strcpy(app->cdstr->buf_ab_time_b, "n/a");

	if (app->appwin[WINDOW_AB].window)
	{
		GT_SetGadgetAttrs(app->appwin[WINDOW_AB].gadget[GADGET_AB_A_TIME], app->appwin[WINDOW_AB].window, NULL, GTST_String, app->cdstr->buf_ab_time_a, TAG_END);
		GT_SetGadgetAttrs(app->appwin[WINDOW_AB].gadget[GADGET_AB_A_TRACK], app->appwin[WINDOW_AB].window, NULL, GTIN_Number, app->cdstr->a_track, TAG_END);
		GT_SetGadgetAttrs(app->appwin[WINDOW_AB].gadget[GADGET_AB_B_TIME], app->appwin[WINDOW_AB].window, NULL, GTST_String, app->cdstr->buf_ab_time_b, TAG_END);
		GT_SetGadgetAttrs(app->appwin[WINDOW_AB].gadget[GADGET_AB_B_TRACK], app->appwin[WINDOW_AB].window, NULL, GTIN_Number, app->cdstr->b_track, TAG_END);
	}
}
/*------------------ DoSizeBuffer() ---------------------------< >-*/
void DoSizeBuffer(Application *app)
{
	float	size;
	BOOL	pointflag;
	char	str[16];
	int		pos_s,
			pos_d,
			i,
			j;

	size = CalcAudioSize(app->cdstr->a_adr, app->cdstr->b_adr, 44100);

	inttostr((LONG)size, str, 10);

	pointflag = FALSE;
	for (pos_s = 0, pos_d = 0; pos_s < strlen(str) % 3; pos_s++, pos_d++)
	{
		pointflag = TRUE;
		app->cdstr->buf_copy_size[pos_d] = str[pos_s];
	}

	for (i = 0; i < strlen(str) / 3; i++)
	{
		/* Punkt setzen */
		if (i > 0 || pointflag)
		{
			app->cdstr->buf_copy_size[pos_d] = '.';
			pos_d++;
		}
		/* 3 Stellen kopieren */
		for (j = 0; j < 3; j++, pos_s++, pos_d++)
			app->cdstr->buf_copy_size[pos_d] = str[pos_s];
	}

	/* Nullstelle */
	app->cdstr->buf_copy_size[pos_d] = 0;

	if (strlen(app->cdstr->buf_copy_size) < 15)
	strcat(app->cdstr->buf_copy_size, " Byte");

	if (app->appwin[WINDOW_SAVEAUDIO].window)
		GT_SetGadgetAttrs(app->appwin[WINDOW_SAVEAUDIO].gadget[GADGET_SAVEAUDIO_SIZE], app->appwin[WINDOW_SAVEAUDIO].window, NULL, GTTX_Text, app->cdstr->buf_copy_size, TAG_END);
}
/*------------------ CalcAudioSize() --------------------------<n>-*/
float CalcAudioSize(LONG start, LONG end, LONG freq)
{
	/* Berechnet die Länge in Bytes bei gegebener Start- und Endadresse
	   und Frequenz in Hz */

	float	size;

	size = end - start;
	/* Abs */
	if (size < 0)
		size *= -1;

	/* Umrechnen in Bytes */
	return (size / 75 * 4 * freq);
}
/*------------------ DoCDDBID() -------------------------------<n>-*/
void DoCDDBID(struct CDStruct *cdstr)
{
	int		i,
			n 		= 0,
			t 		= 0;

	char	buf[12],
			*z;

	struct CDTOCEntry *entry = cdstr->cdx->cdx_TOCData->Entry;

	for (i = 0; i < cdstr->num_track; i++)
	{
		FormatA(buf, "%lu", (int)(entry[i].Address / 75));
		for (z = buf; *z != 0; z++)	/* Quersumme */
			n += (*z - '0');
	}

	t = ((int)(entry[cdstr->num_track].Address / 75)) - ((int)(entry[0].Address / 75));

	FormatA(cdstr->buf_cddb_id, "%08x", (((n % 0xff) << 24) | (t << 8) | cdstr->num_track));
}





/*------------------ MakeBusy() -------------------------------< >-*/
void MakeBusy(struct Application *app, BOOL really)
{
	WORD win;

	if (really)
	{
		app->Flags |= APPF_BUSY;

		for (win = ANZ_WINDOWS-1; win >= 0; win--)
			if (app->appwin[win].window)
			{
				if (IntuitionBase->lib_Version >= 39)
					SetWindowPointer(app->appwin[win].window, WA_BusyPointer, TRUE, WA_PointerDelay, TRUE, TAG_END);

				if (app->appwin[win].request == NULL)
					if (app->appwin[win].request = GetVecA(sizeof(struct Requester), MEMF_PUBLIC))
					{
						InitRequester(app->appwin[win].request);
						if (!Request(app->appwin[win].request, app->appwin[win].window))
						{
							FreeVec(app->appwin[win].request);
							app->appwin[win].request = NULL;
						}
					}
			}
	}
	else
	{
		app->Flags &= ~APPF_BUSY;

		for (win = ANZ_WINDOWS-1; win >= 0; win--)
			if (app->appwin[win].window)
			{
				if (IntuitionBase->lib_Version >= 39)
					SetWindowPointer(app->appwin[win].window, WA_BusyPointer, FALSE, TAG_END);

				if (app->appwin[win].request)
				{
					EndRequest(app->appwin[win].request, app->appwin[win].window);
					FreeVec(app->appwin[win].request);
					app->appwin[win].request = NULL;
				}
			}
	}
}



/*------------------ IsDefaultTitle() -------------------------< >-*/
BOOL IsDefaultTitle(char *str, UBYTE track)
{
	char defaultstr[MAXTITLENAME + 1];

	/* Defaultstring erstellen */
	strncpy(defaultstr, ls[MSG_CDTITLE_TRACK], MAXTITLENAME - 5);
	strcat(defaultstr, " #");
	inttostr(track, defaultstr + strlen(defaultstr), 10);

	/* Jetzt vergleichen */
	if (strcmp(str, defaultstr) == 0)
		return (TRUE);
	else
		return (FALSE);
}

/*------------------ RefreshBevelBox() ------------------------< >-*/
void RefreshBevelBox(struct RastPort *rp, struct BevelBox *bbox, WORD xoffset, WORD yoffset)
{
	struct BevelBox *tempbox;

	for (tempbox = bbox; tempbox; tempbox = tempbox->bb_NextBevelBox)
		DrawBevelBox(rp, xoffset + tempbox->bb_LeftEdge, yoffset + tempbox->bb_TopEdge, tempbox->bb_Width, tempbox->bb_Height,
											GT_VisualInfo,	tempbox->bb_VisualInfo,
											GTBB_FrameType,	tempbox->bb_FrameType,
											GTBB_Recessed,	tempbox->bb_Recessed,
											TAG_DONE);
}



/*------------------ ManageNewSize() --------------------------< >-*/
void ManageNewSize(Application *app, WORD win)
{
	register struct AppWin *appwin = &app->appwin[win];

	appwin->maxsize[WIDTH]	= appwin->window->Width;
	appwin->maxsize[HEIGHT]	= appwin->window->Height;

	RemoveGList(appwin->window, appwin->gadgetlist, -1);

	/* Gadgetliste(n) entfernen */
	if (appwin->gadgetlist)
	{
		FreeGadgets(appwin->gadgetlist);
		appwin->gadgetlist = NULL;
	}
	FreeVec(appwin->gadget);
	appwin->gadget = NULL;

	/* Intuitexte entfernen */
	FreeVec(appwin->itext);
	appwin->itext = NULL;

	EraseRect(appwin->window->RPort,appwin->window->BorderLeft,
									appwin->window->BorderTop,
									appwin->window->Width - appwin->window->BorderRight - 1,
									appwin->window->Height - appwin->window->BorderBottom - 1);
	RefreshWindowFrame(appwin->window);
	MakeVisuals(app, win);
	AddGList(appwin->window, appwin->gadgetlist, ~0, -1, NULL);

	/* Nicht so toll, aber was soll es */
	switch (win)
	{
		case WINDOW_CDINFO:
		case WINDOW_DRIVEINFO:
			FakeRequestA(appwin->window, app->visualinfo, FAKEREQUESTSPACE, FAKEREQUESTSPACE, appwin->maxsize[HEIGHT] - 3 - app->font->ta_YSize - 2 * FAKEREQUESTSPACE - INTERHEIGHT - BUTTONHEIGHT - 10);
			break;
	}

	/* ggf. Intuitexte neu */
	if (appwin->itext)
		PrintIText(appwin->window->RPort, appwin->itext, 0, 0);


	MakeInterior(app, win);
	RefreshGList(appwin->gadgetlist, appwin->window, NULL, -1);
}
/*------------------ AdjustNewSize() --------------------------< >-*/
void AdjustNewSize(struct Application *app, WORD win, WORD newx, WORD newy)
{
	register struct AppWin *appwin = &app->appwin[win];

	/* Manchmal müssen die vorgegebenen Werte bei größenveränderbaren Gadgets */
	/* überschrieben werden, da die Mindestgröße vom verwendeten Font und     */
	/* natürlich vom verwendeten Text (Locale) abhängen kann */
	/* Diese Funktion überprüft also, ob eine derartige Änderung notwendig ist */

	if (newx > appwin->maxsize[WIDTH])
		appwin->maxsize[WIDTH] = newx;
	if (newx > appwin->minsize[WIDTH])
		appwin->minsize[WIDTH] = newx;
	if (newx > appwin->minwidth)
		appwin->minwidth = newx;

	if (appwin->maxsize[WIDTH] > appwin->maxwidth)
		appwin->maxsize[WIDTH] = appwin->maxwidth;

	if (newy > appwin->maxsize[HEIGHT])
		appwin->maxsize[HEIGHT] = newy;
	if (newy > appwin->minsize[HEIGHT])
		appwin->minsize[HEIGHT] = newy;
	if (newy > appwin->minheight)
		appwin->minheight = newy;

	if (appwin->maxsize[HEIGHT] > appwin->maxheight)
		appwin->maxsize[HEIGHT] = appwin->maxheight;
}

/*------------------ TextWidth() ------------------------------< >-*/
WORD TextWidth(char *text, struct TextAttr *font)
{
	struct IntuiText itext = {	1, 0,		/* FrontPen, BackPen */
								JAM1,		/* DrawMode */
								0, 0,		/* LeftEdge, TopEdge */
								font,		/* ITextFont */
								text,		/* IText */
								NULL };		/* NextText */

	return ((WORD)IntuiTextLength(&itext));
}
/*------------------ FindWidest() -----------------------------< >-*/
WORD FindWidest(char **textarr, struct TextAttr *font)
{
	int highest = 0, counter, temp;

	for (counter = 0; textarr[counter] != NULL; counter++)
	{
		temp = TextWidth(textarr[counter], font);
		if (temp > highest)
			highest = temp;
	}

	return highest;
}


/*------------------ NumToStr() -------------------------------< >-*/
char *NumToStr(LONG number)
{
	static char str[14];
	return (inttostr(number, str, 10));
}
/*------------------ RandomNum() ------------------------------< >-*/
LONG RandomNum(LONG startnum, LONG endnum)
{
	LONG result;
	struct DateStamp date;

	DateStamp(&date);
	srand((unsigned int)date.ds_Tick);

	/* Es wird eine Zahl zwischen startnum und endnum ermittelt */
	/* (einschließlich startnum bzw. endnum!) */

	result = LONG((((double)(endnum - startnum + 1) * ((double)rand() / double(RAND_MAX))) + startnum));
	if (result > endnum) result = endnum;
	return (result);
}
/*------------------ FitInteger() -----------------------------< >-*/
LONG FitInteger(LONG num, LONG lowborder, LONG highborder)
{
	if (num < lowborder)
		return (lowborder);
	else if (num > highborder)
		return (highborder);
	else
		return (num);
}
/*------------------ InsertBoolean() --------------------------< >-*/
LONG InsertBoolean(LONG dest, LONG flag, BOOL state)
{
	if (state)
		return (dest | flag);
	else
		return (dest & ~flag);
}


/*------------------ FreeList() -------------------------------< >-*/
void FreeList(List *list)
{
	if (list)
	{
		Node *temp_node, *next_temp_node;

		for (temp_node = list->lh_Head; temp_node->ln_Succ;)
		{
			next_temp_node = temp_node->ln_Succ;
			FreeVec(temp_node);
			temp_node = next_temp_node;
		}

		FreeVec(list);
	}
}
/*------------------ FindNode() -------------------------------< >-*/
Node *FindNode(List * list, ULONG number)
{
	Node * tempnode = list->lh_Head;
	ULONG i;

	for (i = 0; i < number; i++)
	{
		if (tempnode->ln_Succ)
			tempnode = tempnode->ln_Succ;
		else
		{
			tempnode = NULL;
			break;
		}
	}
	return (tempnode);
}



/*------------------ CreateScrollObject() ---------------------< >-*/
BOOL CreateScrollObject(struct Scroll *so)
{
	int widest, count;
	struct IntuiText *itexts;
	BOOL success = TRUE;

	for (count = 0; so->Text[count]; count++)
	{
	}

	so->p_height	= count * so->Font->ta_YSize + 2 * so->Height;
	so->p_pos		= 0;

	widest = FindWidest(so->Text, so->Font);

	if (itexts = GetVecA(sizeof(struct IntuiText) * count, MEMF_CLEAR))
	{
		/* Rastport etc. */
		if (so->RPort = GetVecA(sizeof(struct RastPort), MEMF_CLEAR))
		{
			InitRastPort(so->RPort);

			if (so->RPort->BitMap = GetVecA(sizeof(struct BitMap), MEMF_CLEAR))
			{
				if (so->RPort->BitMap->Planes[0] = GetVecA((so->Width / 8) * so->p_height, MEMF_CLEAR | MEMF_CHIP))
				{
					int i;

					InitBitMap(so->RPort->BitMap, 1, so->Width, so->p_height);

					for (i = 0; i < count; i++)
					{
						itexts[i].FrontPen	= 1;
						itexts[i].DrawMode	= JAM1;
						itexts[i].LeftEdge	= (so->Width - TextWidth(so->Text[i], so->Font)) / 2;
						itexts[i].TopEdge	= so->Height + i * so->Font->ta_YSize;
						itexts[i].ITextFont = so->Font;
						itexts[i].IText		= so->Text[i];
						itexts[i].NextText	= NULL;

						PrintIText(so->RPort, &itexts[i], 0, 0);
						if (so->Flags[i])
							PrintIText(so->RPort, &itexts[i], 1, 0);
					}

					/* Image vorbereiten */
					so->Img.LeftEdge	= so->LeftEdge;
					so->Img.TopEdge		= so->TopEdge;
					so->Img.Width		= so->Width;
					so->Img.Height		= so->Height;
					so->Img.Depth		= 1;
					so->Img.PlanePick	= 1;
					so->Img.PlaneOnOff	= 0;
					so->Img.NextImage	= NULL;

					success = TRUE;
				}
			}
		}
		FreeVec(itexts);
	}

	return (success);
}
/*------------------ UpdateScrollObject() ---------------------< >-*/
void UpdateScrollObject(struct Scroll *so, struct Window *win)
{
	so->Img.ImageData = (UWORD *)(so->RPort->BitMap->Planes[0] + so->p_pos * (so->Width / 8));

	so->p_pos++;
	if (so->p_pos > so->p_height - so->Height)
		so->p_pos = 0;

	DrawImage(win->RPort, &(so->Img), 0, 0);
}
/*------------------ DisposeScrollObject() --------------------< >-*/
void DisposeScrollObject(struct Scroll *so)
{
	if (so->RPort->BitMap->Planes[0])
	{
		FreeVec(so->RPort->BitMap->Planes[0]);
		so->RPort->BitMap->Planes[0] = NULL;
	}
	if (so->RPort->BitMap)
	{
		FreeVec(so->RPort->BitMap);
		so->RPort->BitMap = NULL;
	}
	if (so->RPort)
	{
		FreeVec(so->RPort);
		so->RPort = NULL;
	}
}



/*------------------ GetPubScreenName() -----------------------< >-*/
BOOL GetPubScreenName(struct Screen *wantedscreen, char *buffer)
{
	/* Diese Funktion schaut in der PubScreenList nach, ob die Adresse  */
	/* vorhanden ist, und kopiert den Screennamen in buffer (Buffer muß */
	/* also mindestens MAXPUBSCREENNAME + 1 lang sein */

	struct PubScreenNode *actnode = NULL;
	struct List *pubscreenlist = NULL;
	BOOL success = FALSE;

	if (pubscreenlist = LockPubScreenList())
	{
		for (actnode = (struct PubScreenNode *)pubscreenlist->lh_Head; actnode->psn_Node.ln_Succ; actnode = (struct PubScreenNode *)actnode->psn_Node.ln_Succ)
		{
			if (wantedscreen == actnode->psn_Screen)
			{
				strncpy(buffer, actnode->psn_Node.ln_Name, MAXPUBSCREENNAME);
				success = TRUE;
				break;
			}
		}
		UnlockPubScreenList();
	}

	return (success);
}
/*------------------ GetPubScreenList() -----------------------< >-*/
BOOL GetPubScreenList(Application *app)
{
	struct List *systemlist;
	struct MyPubScreenNode *cur_node;
	struct Node *temp_node;

	if (app->appwin[WINDOW_PUBSCREEN].window)
		GT_SetGadgetAttrs(app->appwin[WINDOW_PUBSCREEN].gadget[GADGET_PUBSCREEN_LISTVIEW], app->appwin[WINDOW_PUBSCREEN].window, NULL, GTLV_Labels, ~0, TAG_END);

	FreeList(app->pubscreenlist);

	if (!(app->pubscreenlist = GetVecA(sizeof(struct List), MEMF_PUBLIC | MEMF_CLEAR)))
		return (FALSE);

	NewList(app->pubscreenlist);

	if (!(systemlist = LockPubScreenList()))
		return (FALSE);

	for (temp_node = systemlist->lh_Head; temp_node->ln_Succ; temp_node = temp_node->ln_Succ)
	{
		if (!(cur_node = GetVecA(sizeof(struct MyPubScreenNode), MEMF_PUBLIC | MEMF_CLEAR)))
			return (NULL);
		strncpy(cur_node->mpsn_pubscreenname, temp_node->ln_Name, MAXPUBSCREENNAME);
		cur_node->mpsn_Node.ln_Name = cur_node->mpsn_pubscreenname;
		AddTail(app->pubscreenlist, (Node *) cur_node);
	}

	UnlockPubScreenList();

	if (app->appwin[WINDOW_PUBSCREEN].window)
		GT_SetGadgetAttrs(app->appwin[WINDOW_PUBSCREEN].gadget[GADGET_PUBSCREEN_LISTVIEW], app->appwin[WINDOW_PUBSCREEN].window, NULL, GTLV_Labels, app->pubscreenlist, TAG_END);

	return (TRUE);
}
