Resourcen in GFA-BASIC 3

Das neue GFA-BASIC 3.0 bietet sehr viele schöne Erweiterungen und zusätzliche Funktionen und setzt damit einen Standard bei den BASIC- Dialekten auf dem Atari ST (und nicht nur dort). Eine der neuen Möglichkeiten ist der Inline- Befehl mit dem man beliebige Daten in den BASIC-Programmcode integrieren kann.

Eine mögliche Anwendung möchte ich hier beschreiben: die Einbindung einer RSC-Datei in die GFA (BASIC)-Datei. Normalerweise benutzen die meisten umfangreicheren GEM-Programme RSC-Dateien, um die Dialogbox- und Menüstrukturen zu speichern. Diese Dateien werden beim Programmstart mit einer AES-Funktion in den Speicher geladen. Da sie so als getrennte Datei vorliegen, hat das den Vorteil, daß sie leicht mit einem RCS zu edieren sind. Der Nachteil ist aber, daß pro Programm mindestens zwei Dateien vorhanden sind und sich so auch die Ladezeit erhöht.

Andere Programme bauen die Resourcen direkt im Speicher auf. Damit entfallen die oben genannten Nachteile, es entsteht dafür aber ein viel größerer: Das Erstellen der Resourcen wird zu einer Fummelarbeit, die einem sonst vom RCS abgenommen wird.

Eine Lösung dieses Problems wäre, daß man die RSC-Datei mit einem RCS erstellen und dann in das Programm einbinden würde. Genau das wird mit diesen GFA 3.0-Routinen erreicht. Dabei wurde auf eine möglichst große Kompatibilität zu den Originalfunktionen geachtet. Zunächst muß man sich den Unterschied zwischen einer Resource-Datei und den Resourcen im Speicher klarmachen: In der RSC-Datei sind keine absoluten Adressen enthalten, sondern nur relative Adressen zum Dateianfang. Außerdem sind die Objekt-Positionen/-Ausmaße in einem anderen (zeichenorientierten und damit auflösungsunabhängigen) Format vorhanden. Zusätzlich befindet sich am Dateianfang noch ein Header, aus dem man wichtige Informationen entnehmen muß. Wenn man also die RSC-Datei in einen Inline-Bereich lädt, muß man beim Programmstart statt von Diskette zu laden, eine entsprechende Konvertierung der Daten vornehmen.

Im folgenden werde ich zuerst auf die Anwendung der abgedruckten Routinen eingehen und mich dann ein wenig näher mit den Grundlagen beschäftigen. Die Routinen ersetzen die beiden AES-Funktion Rsrc_Load und Rsrc_Gaddr zum Laden von Resource-Dateien bzw. Bestimmen von (Baum-)Adressen. Dabei wird das Parameterformat vollständig beibehalten, so daß Sie in vorhandenen Programmen nur ein @ vor den Funktionsnamen setzen müssen. Hierbei hat der Dateiname bei der Rsrc_Load-Funktion normalerweise keine Bedeutung. Bei Anwendung der Inline-Resource brauchen Sie keinen Rsrc_Free zu benutzen, es führt allerdings auch nicht zu einem Fehler. Außerdem sind Reserves nicht notwendig, da der Speicher nicht vom Betriebssystem angefordert wird.

Sie werden sich jetzt natürlich fragen, wie die Resource-Datei in den Programmtext kommt.

Dazu erkläre ich noch einmal, vor allem für Nichtbesitzer von GFA-BASIC 3.0, wie der Inline-Befehl funktioniert. Wenn Sie in einer Programmzeile INLINE ad%, 10000 schreiben, werden an dieser Stelle 10000 Bytes im Programmtext reserviert und der Variablen ad% die Anfangsadresse dieses Bereichs zugewiesen. In diesen Bereich können Sie nun Daten hineinladen (s.u.), und wenn das Programm mit Save gespeichert wird, werden diese Daten mit abgespeichert. Beim Abspeichern mit Save,A/List gehen diese Daten natürlich verloren! Zum Einladen einer Datei (z.B. RSC-Datei) ist folgendes zu machen:

Die Längenangabe beim Inline-Befehl muß genau der Dateilänge entsprechen (z.B. mit Files bestimmen). Dann wird der Cursor neu auf die Inline-Zeile gesetzt und < Help > gedrückt. Im dann erscheinenden Menü können Sie mit < L > die Datei laden.

Bei der RSC-Datei geschieht das in der ersten Inline-Zeile der Funktion Rsrc_Load. Nach dem Laden muß außerdem auf jeden Fall die zweite Inline- Zeile mit < Help >..< C > gelöscht werden, da es sonst zu Fehlem kommen kann. Diese Zeile enthält Kontrollinformationen über den derzeitigen Zustand der Resourcen. Außerdem muß darauf geachtet werden, daß Inline-Bereiche nicht größer als ca. 32600 Bytes sein dürfen, die Resource-Datei also auch nicht länger sein kann. Dies wird aber wohl nicht sehr häufig Vorkommen.

Wenn Sie einmal während der Arbeit am Programm die RSC-Datei von Disktte laden wollen, brauchen Sie nicht alle Funktionen wieder umzubenennen sondern setzen nur am Programmanfang die Variable Rsrc_load! auf TRUE, dann werden die original AES-Funktionen ausgeführt.

Beim Benutzen der Routinen ist außerdem noch unbedingt ein weiterer Punkt zu beachten (siehe auch unten): Da die Resourcen direkt im Programm stehen und dort auch aufgerufen werden, stehen nach dem Programmlauf noch alle Änderungen, also vor allem die der Edit-Felder und der Status-Angaben, dort. Diese werden bei einem erneuten Start aus dem Editor nicht neu initialisiert (wie denn auch?), so daß z.B. die Inhalte der Edit-Felder noch vorhanden sind. Dies führt dazu, daß bei einem zweiten Programmlauf die Originaldaten nicht mehr vorhanden sind.Beim Abspeichem des Programms nach einem Programmlauf werden diese also auch mit gespeichert. (Das sollte man aber sowieso vermeiden). Näheres dazu sage ich weiter unten. Hiermit müßte die Anwendung der Routinen eigentlich außreichend beschrieben sein, und Sie können diese in Ihre GFA-GEM-Programme einbinden. Im folgenden erkläre ich jetzt das Funktionsprinzip der Routinen. Dabei setzte ich allerdings gewisse Grundkenntnisse bei der internen Darstellung von Resourcen voraus.

# Der Aufbau des Resource-Headers:

18 Worte mit folgendem Inhalt:

Adr. relativ zum Dateianfang Bedeutung
0 (rsh_versn) Versions-Nummer (reserviert, normalerweise 0 oder 1)
2 (rsh_object) rel. Zeiger auf den Beginn
4 (rsh_tedinfo) rel. Zeiger auf den
6 (rsh_iconblk) rel. Zeiger auf den
8 (rsh_bitblk) rel. Zeiger auf den
10 (rsh_frstr) * rel. Zeiger auf eine Adreßtabelle aller FreeStrings (z.B. für Alert-Boxen)
12 (rsh_string) rel. Zeiger auf den Beginn der anderen Strings
14 (rsh_imdata) rel. Zeiger auf den Beginn aller Image-Daten (für Icons und BitBlks)
16 (rsh_frimg) rel. Zeiger auf eine Adreßtabelle aller Freeimages
18 (rsh_trindex) * rel. Zeiger auf eine Adreßtabelle mit den Anfangsadressen der Objekt-Bäume
20 (rsh_nobs) Anzahl der Objekt-Strukturen
22 (rsh_ntree) * Anzahl der Objekt-Bäume
24 (rsh_nted) Anzahl der Tedinfo-Strukturen
26 (rsh_nib) Anzahl der IconBlk-Strukturen
28 (rsh_nbb) Anzahl der BitBlk-Strukturen
30 (rsh_nstring) * Anzahl der FreeStrings (!)
32 (rsh_nimages) Anzahl der Freeimages (!)
34 (rsh_rssize) Größe der RSC-Datei in Bytes (Wenn dieser Wert nicht mit der realen Datei große übereinstimmt, handelt es sich normalerweise nicht um eine RSC-Datei)

(Alle Einträge der Adreßtabellen sind ebenfalls relativ)

Der Aufbau des Resource-Headers

Die Grundlagen oder wie funktionierte?

Wie oben schon gesagt, besteht eine RSC-Datei aus einem Header und den eigentlichen Resource-Daten in einem adressen-und auflösungsunabhängigen Format. Der Resource-Header hat hierbei den in Bild 1 dargestellten Aufbau. In ihm stehen vor allem Informationen zu den einzelnen Strukturen in einer Resourcedatei. Für uns wichtig sind vor allem die mit einem Sternchen gekennzeichneten Einträge. So bestimmt Rsrc_load zunächst einmal den Anfang der Treetabelle, die Anzahl der Objektbäume und das gleiche für die FreeStrings. Da in der RSC- Datei die Koordinaten in den Objekt-Strukturen in einem zeichenorientierten Format vorliegen, werden dann zunächst die aktuellen Zeichenmaße bestimmt und ebenfalls die Maße für die im Speicher vorliegenden Resourcen. Danach wird überprüft ob die Resourcedaten noch im Originalformat vorliegen bzw. die Anfangsadresse des Inline-Bereichs (oder die Auflösung) sich geändert hat.

In diesem Fall wird die Konvertierungsroutine Rsrc_convert aufgerufen, die dann die Hauptarbeit erledigt. Anschließend werden nur noch die Kontrolldaten abgespeichert und ein entsprechender Wert zurückgegeben. Dieser Wert unterscheidet sich je nach Art der Konvertierung (1,2 oder 3,0 bei Fehler).

Rsrc_Convert geht mit Hilfe der Treetabelle alle Bäume durch und bearbeitet dabei jedes Objekt (d.h. alle Objekte bis zu dem. bei dem das LASTOB-Flag gesetzt ist). Bei einem Objekt werden zunächst die Koordinaten konvertiert. Das bedeutet evtl, auch eine Rückumwandlung bei Änderung der Auflösung. In einer RSC-Datei liegen die Objektkoordinaten in folgendem Format vor: im Low-Byte die Anzahl der Zeichen und im High-Byte die “überbleibenden” Pixel.

Nach der Koordinatenumwandlung wird der Objekttyp bestimmt. Anhand dieses Typs werden nun einige Unterscheidungen getroffen. Wenn es sich um ein Boxobjekt handelt, ist schon alles fertig. Ansonsten wird die Ob_Spec-Adresse bestimmt und angepaßt. Diese Adresse zeigt je nach Objekttyp auf weitere Datenstrukturen.

(Ich kann hier die Strukturen leider nicht im einzelnen darstellen, deshalb verweise ich auf die unten angegebene Literatur zu GEM). In Abhängigkeit vom Objekttyp werden also noch einige weitere Adressen angepaßt. Damit ist die gesamte Konvertierung schon beendet.

Die zweite zu den Routinen gehörige Funktion ist Rsrc_Gaddr. Sie bestimmt (Baum-)Adressen in einer Resource. Der erste Parameter gibt dabei die Art der zu ermittelnden Adresse an. Dabei werden nur 0 (Objekt-Baum) und 15 (FreeString/ Alert) behandelt, alle anderen erzeugen einen Fehler (0 als Rückgabe wert). Dies ist sinnvoll, da andere Werte eigentlich sowieso keine normale Anwendung haben (außer vielleicht 16 für Freeimages) und bei der Originalfunktion auch nicht funktionieren. Der zweite Parameter ist der Index (des zu bestimmenden Baums). In der letzten Variablen wird dann die Adresse zurückgegeben. Die Routine liest dazu einfach die Adresse aus der entsprechenden Tabelle aus.

Die beiden Routinen benutzen 6 globale Longinteger-Variablen, die mit rsrc_inln. beginnen und nicht verändert werden dürfen. Überrsrcjoad! kann man bestimmen, ob die Originalfunktionen aufgerufen werden sollen (s.o.).

Nun zu dem Problem der geänderten Resourcen. Generell kann man dazu nur sagen, daß man die Werte beim Programmstart initialisieren (z.B. Edit-Felder) oder nach dem Aufruf zurücksetzen sollte (Edit-Status). Zum Initialisieren aller Edit-Felder eines Baumes mit Null-Strings läßt sich z.B. die Procedure Form_clreds aus dem Beispielprogramm (Listing 2) benutzen. Ihr wird einfach die Baumadresse übergeben. Außerdem sollte man darauf achten, daß man Pro-grammemit Inline-Resource nur dann abspeichert, wenn sie noch nicht gestartet wurden oder die Resource neu geladen wurde. Dies hat nicht nur die oben angegebenen Gründe, sondern gilt auch, weil man sonst die Resource nicht mehr so leicht als eigene Datei abspeichem kann. Dies ist aber z.B. nötig, wenn man sie mit einem RCS edieren will, und das Original nicht mehr vorhanden ist. Um dies zu erreichen kann man den Inline-Bereich mit < Help >..< S > als RSC-Datei abspeichem.

Nun zum Beispielprogramm. Dies zeigt einfach nur alle Bäume nacheinander an. Danach werden noch alle vorhandenen Alertboxen angezeigt. Dazu wird die ebenfalls im Listing vorhandene Funktion @Form_Alert benutzt. Dieser wird ein Index und der Default-Button übergeben. Wenn das erste Byte des so mit Hilfe von Rsrc_Gaddr(15,..) erhaltenden Free-Strings ein “[“ ist (Alert-Box), wird diese mit Hilfe der AES-Funktion Form_Alert dargestellt. Diese Funktion läßt sich auch anwenden wenn man normale RSC-Da-teien benutzen will. Dazu ersetzt man einfach @rsrc_gaddr durch RSRC_GADDR.

Literatur:

[1] GFA-BASIC 3.0 Anwenderdokumentation, GFA Systemtechnik [2] Atari ST - GEM, Data Becker

' * Inline-Resourcen: Neue Rsrc_Load und      *
' * Rsrc_Gaddr-Funktionen                     *
' *                                           *
' * 29.5.1988 by Lutz Preßler , Ahornweg 11,  *
' * 2904 Hatten                               *
'
FUNCTION rsrc_load(na$)
	LOCAL cw%,ch%,acw%,ach%,dum%
	' **********************************
	' *** Datenbereich für RSC-Datei ***
	' (Länge muß mit Dateilänge übereinstimmen. 
	' Dann mit < Help >..< Load > laden.
	' u.d.Kontrollbereich mit < Help >..< Clear > löschen.)
	'
	INLINE rsrc_inln.adress%,1182 
	' **********************************
	' Hilfsbereich für Kontrolle 
	INLINE rsrc_inln.control%,6 
	' **********************************
	'
	IF rsrc_load!
		' Wenn gewünscht, normal RSC-Datei laden 
		RETURN RSRC_LOAD(na$)
	ELSE
		IF CARD{rsrc_inln.adress%+34}=0 
			' Gar nichts da. -> Fehler 
			RETURN 0 
		ELSE
			' Adresse der Tabelle mit Baumadressen und 
			' Anzahl der Bäume bestimmen 
			rsrc_inln.treetab%=CARD{rsrc_inln.adress%+18}+rsrc_inln.adress% 
			rsrc_inln.treeanz&=CARD{rsrc_inln.adress%+22}
			' Daten für FreeStrings (Alerts) bestimmen 
			rsrc_inln.frstrtab%=CARD{rsrc_inln.adress%+10} +rsrc_inln.adress% 
			rsrc_inln.frstranz&=CARD{rsrc_inln.adress%+30}
			'
			' Zeichenmaße bestimmen 
			~GRAF_HANDLE(cw%,ch%,dum%, dum%) 
			acw%=BYTE{rsrc_inln.control%+4} 
			ach%=BYTE{rsrc_inln.control%+5}
			'
			IF {rsrc_inln.control%}=0 OR {rsrc_inln.control%} <>rsrc_inln.adress% OR acw%<>cw% OR ach%<>ch%
				' Wenn Daten noch nicht konvertiert oder an neuer Adresse
				rsrc_convert(rsrc_inln.adress%-{rsrc_inln.control%} ,cw%,ch%,acw%,ach%) 
				dum%={rsrc_inln.control%}
				' Konvertieren und Kontrollstruktur entsprechend setzen
				{rsrc_inln.control%}=rsrc_inln.adress%
				BYTE{rsrc_inln.control%+4}=cw%
				BYTE{rsrc_inln.control%+5}=ch%
				RETURN 1-(dum%<>0)
			ELSE
				RETURN 3 
			ENDIF
		ENDIF 
	ENDIF 
ENDFUNC

PROCEDURE rsrc_convert (delta%, cw%, ch%, acw%, ach%)
	' RSC-Daten v.Datei- i.Speicherformat konvert.	*
	'
	LOCAL t&, o&, tree%, type&, ad%
	' Alle Bäume konvertieren 
	FOR t&=0 TO rsrc_inln.treeanz&-1 
		' Baumadresse bestimmen 
		tree%={rsrc_inln.treetab%+4*t&}+rsrc_inln.adress% 
		o&=—1
		' Alle Objektidizes durchgehen bis LASTOB 
		REPEAT 
			INC o&
			' Koordinaten anpassen 
			IF cw%<>acw% OR ch%<>ach%
				IF acw%<>0
					OB_X(tree%,o&)=SHL((OB_X(tree%,o&) MOD acw%), 8)+OB_X(tree%,o&) DIV acw% 
					OB_Y(tree%,o&)=SHL((OB_Y(tree%, o&) MOD ach%), 8)+OB_Y(tree%,o&) DIV ach% 
					OB_W (tree%,o&)=SHL((OB_W(tree%,o&) MOD acw%), 8)+OB_W(tree%,o&) DIV acw% 
					OB_H(tree%,o&)=SHL((OB_H(tree%,o&) MOD ach%), 8)+OB_H(tree%,o&) DIV ach%
				ENDIF
				OB_X(tree%,o&)=SHR(OB_X(tree%,o&),8)+ BYTE(OB_X(tree%,o&))*cw% 
				OB_Y(tree%,o&)=SHR(OB_Y(tree%,o&),8)+ BYTE(OB_Y(tree%,o&))*ch%
				OB_W(tree%,o&)=SHR(OB_W(tree%,o&),8) + BYTE(OB_W(tree%,o&))*cw% 
				OB_H(tree%,o&)=SHR(OB_H(tree%,o&),8)+ BYTE(OB_H(tree%,o&))*ch%
			ENDIF
			'	Objekttyp bestimmen
			type&=OB_TYPE(tree%,o&)
			' Ist Ob_Spec eine Adresse?
			IF type&>20 AND type&<33 AND NOT (type&=25 OR type&=27)
				' Ja, dann anpassen
				ad%=OB_SPEC(tree%,o&)+delta%
				OB_SPEC(tree%,o&)=ad%
				' Datenstruktur des entspr. Typs konvertieren
				SELECT type&
				CASE 21,22,29,30,31	!	TEDINFO	und ICONBLK
					{ad%} = {ad%}+delta%
					{ad%+4}={ad%+4}+delta%
					{ad%+8}={ad%+8}+delta%
				CASE 23	!	BITBLK
					{ad%}={ad%}+delta%
				CASE 24	!	USERBLK
					{ad%} = {ad%}+delta%
					{ad%+4}={ad%+4}+delta%
				ENDSELECT
			ENDIF
		UNTIL BTST (OB_FLAGS (tree%, o&) ,5)
	NEXT t&
RETURN
'
FUNCTION rsrc_gaddr (art%,nr%, VAR adr%)
	'	Neue Funktion zur Adressbestimmung
	IF rsrc_load!
		' Wenn gewünscht, Originalfunktion
		RETURN RSRC_GADDR (art%, nr%, adr%)
	ELSE
		adr%=0
		SELECT art%
		' Nur ObjectTrees oder Freeimages, sonst Fehler
		CASE 0
			IF nr%>-1 AND nr%<rsrc_inln.treeanz&
				adr%={rsrc_inln.treetab%+nr%*4}+rsrc_inln.adress%
			ENDIF
		CASE 15
			IF nr%>-1 AND nr%<rsrc_inln.frstranz&
				adr%={rsrc_inln.frstrtab%+nr%*4}+rsrc_inln.adress%
			ENDIF
		ENDSELECT
		RETURN -(adr%<>0)
	ENDIF
ENDFUNC

Listing 1: Neue RSVC-Load-und RSVC-Gaddi-Funktionen

' ***************************************************** 
' *	       Beispielprogramm für Inline-Resourcen      *
' ***************************************************** 
' *              29.5.1988 by Lutz Preßler            *
' *              Sandkrug, Ahornweg 11                *
' *              2904 Hatten                          *
' *****************************************************
'
' Flag, ob @rsrc_load und @rsrc_gaddr die normalen 
' AES-Funktionen ausführen
' sollen, oder nicht. Wenn True wird Resource von 
' Diskette geladen.
'	15:	'
' rsrc_load!=TRUE
'
IF @rsrc_load("DEMO. RSC") =0 
	' Fehler aufgetreten
	EDIT 
ENDIF
'
' Alle Bäume anzeigen 
FOR i&=0 TO 100 CLS
	EXIT IF (@rsrc_gaddr (0, i&, tr%)=0) OR tr%=0 
	form_clreds(tr%)
	~FORM_CENTER(tr%,dum,dum,dum,dum)
	~OBJC_DRAW(tr%,0,7,0,0,640,400) 
	ex%=BCLR(FORM_DO(tr%,0),15)
	OB_STATE(tr%,ex%)=BCLR(OB_STATE(tr%, ex%),0)
NEXT i&
'
' Alle Alerts anzeigen 
CLS
FOR i&=0 TO 100 
	EXIT IF @form_alert(i&,1)=0 
NEXT i&
'
~RSRC_FREE()
EDIT
'
'
' * Inline-Resourcen: Neue Rsrc_Load- und Rsrc_Gaddr-Funktionen *
'
FUNCTION rsrc_load(na$) ! siehe Routinen-Listing 
	' ...
ENDFUNC
'

> PROCEDURE rsrc_convert (delta%, cw%, ch%, acw%, ach%)
				! s. Routinen-Listing

FUNCTION rsrc_gaddr (art%,nr%,VAR adr%)
	! s. Routinen-Listing
	' ...
ENDFUNC
'
'
'
FUNCTION form_alert (nr%,bu%)
	' Alert aus Resource darstellen 
	LOCAL ad%,rt%
	IF @rsrc_gaddr(15,nr%,ad%)>0 
		IF BYTE{ad%}=91 
			GINTIN(0)=bu%
			ADDRIN(0)=ad%
			GEMSYS 52 
			rt%=GINTOUT(0)
		ENDIF 
	ENDIF 
	RETURN rt%
ENDFUNC
'
PROCEDURE form_clreds(tr%)
	' Alle editierbaren Textfelder in einem Baum löschen 
	LOCAL t&, i& 
	i&=-1 
	REPEAT 
		INC i& 
		t&=OB_TYPE(tr%,i&)
		IF (t&=21 OR t&=22 OR t&=29 OR t&=30) AND BTST(OB_FLAGS(tr%,i&),3)
			BYTE{{OB_SPEC(tr%,i&)}}=0
		ENDIF
	UNTIL BTST(OB_FLAGS(tr%,i&) ,5)
RETURN

Listing 2: Ein Beispielprogramm für Inline-Resourcen


Lutz Pressler
Aus: ST-Computer 10 / 1988, Seite 76

Links

Copyright-Bestimmungen: siehe Über diese Seite