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.
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
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