Mehrere Resource-Dateien gleichzeitig

Resource-Dateien sind eine feine Sache. Mit dem RCS, sei es das von Digital Research oder jenes vom MEGAMAX-C-Compiler, werden solche Files, in denen Boxen, Knöpfe und ganze Formulare abgelegt werden, schnell und einfach erstellt. Sie machen die Verwendung solcher Objekte zum Kinderspiel, zumal sie ohne jegliche Programmierkenntnisse zusammenzubasteln sind. Die zu diesem Zweck entwickelten RCS-Programme bieten einen hohen Komfort, so daß kein Programmierer, der sich mit der GEM-Programmierung beschäftigt, solche Utilities mehr missen möchte. So ganz perfekt sind die Programme allerdings nicht, was man schon bei den ersten Versionen des DR-RCS sehen konnte. Beim Einladen eines Resource-Files, in dem man ein Icon konstruiert hatte, stürzte das Programm sang- und klanglos ab. Durch ein paar kleine Überarbeitungen hat man diese Probleme in den Griff bekommen. Trotzdem kann man mit diesen Programmen nicht uneingeschränkt arbeiten. Ein wichtiger Hinderungsgrund ist die maximale Größe einer RSC-Datei. Die Dateigröße darf den Wert von etwa 30 kB (Augenmaß) nicht überschreiten. Offenbar wurde beim Programmieren auf die besondere Speicherausstattung eines Atari ST keine Rücksicht genommen.

Wer hier und da mal ein kleines GEM-Programm schreibt, der wird nie in Schwierigkeiten kommen, daß plötzlich die RSC-Datei nicht mehr ausreicht. Aber auch da gibt es Tücken. Bei meiner Arbeit verwende ich grundsätzlich zwei RCS-Systeme, das von MEGAMAX und das von DR. Das MEGAMAX-Programm zeichnet sich durch einfache Bedienung und - das schätze ich besonders - durch den sehr schönen eingebauten Iconeditor aus. Durch häufige Änderungen an einer Datei kann es aber Vorkommen, daß - aus welchen Gründen auch immer - die RSC-Datei plötzlich defekt ist. Beim Laden einer fehlerhaften Datei erscheinen zwischen zwei und vier Bomben, so daß man gar nicht mehr an das File herankommt, um den Fehler zu eliminieren. Das DR-RCS ist nicht so pingelig. Zumindest laden läßt sich die Datei, stürzt aber beim Bearbeiten des fehlerhaften Objekts ab. Dies hat einen Vorteil: Man weiß, wo der Fehler steckt und kann (im günstigsten Falle) das Objekt oder zumindest dessen Wurzel löschen. Der Rest der Datei kann wieder abgespeichert werden und bleibt dem Programmierer erhalten. Machen Sie sich bei häufigen Änderungen immer eine, oder besser, mehrere Sicherheitskopien. Ob eine Datei auch wirklich richtig abgespeichert wurde, können Sie meistens nicht am Programm erkennen, das die Source verwendet, sondern erst beim erneuten Bearbeiten mit dem RCS. Und wenn dann der Fehler entdeckt wird, ist es zu spät.

Kommen wir zum Hauptproblem bei umfangreicheren Programmen. Sie brauchen lediglich in Ihren Programmen mit einigen Icons zu arbeiten, schon steigt der Platzbedarf einer solchen Datei steil an. Noch gravierender ist es, wenn man sich eine kleine Tabelle aus Objekten (Rähmchen, Boxen, Überschriften usw.) konstruieren will, um dort Eingaben zuzulassen. Das kostet, über den Daumen gerechnet, in ansprechender Form gut 5 kB Speicher. Für den Atari ST ist das keine Frage, wohl aber für das RCS.

Ganz gemein wird es, wenn Sie in Bereichen zwischen 20 und 30 kB pro RSC-Datei arbeiten. Dann kann es Vorkommen, daß die Datei einwandfrei abgespeichert wird, aber wegen Speicherplatzmangels nicht mehr wieder eingeladen und bearbeitet werden kann. Bevor es soweit kommt, sollte man seine Dateien in zwei etwa 10 bis 20 kB große Files aufsplitten.

Wie man die Dateien in den Speicher bekommt, ist bereits durch zahlreiche Artikel bekannt. Zum einen kann man eine Datei mit der Endung „.C“ vom DR-RCS ausgeben lassen, um sie anschließend durch „^include“ vom Compiler mit einbinden zu lassen. Beim Programmstart braucht dann lediglich eine kleine Routine diese Daten an die aktuelle Bildschirmauflösung anzupassen. Die zweite Möglichkeit ist wesentlich bequemer. Durch Aufruf der GEM-Funktion rsrc_load() wird die ".RSC“-Datei in den Speicher gelesen und automatisch angepaßt. Dies wird wohl der Normalfall sein. Hat man nun zwei Dateien, so benötigt man auch zwei rsrc_load()-Aufrufe.

So einfach, wie dies zunächst aussieht, ist es aber nicht. Denn der zweite Aufruf überschreibt zwar nicht die erste Datei, aber man hat nur Zugriff auf das zuletzt geladene Resource. Es wäre natürlich möglich - je nachdem, welche Objekte man aus den Dateien benötigt - die eine oder andere RSC-Datei zu laden und anschließend, wenn diese Informationen nicht mehr gebraucht werden, den belegten Speicherplatz mit rsrc_free() wieder freizugeben.

Das Programm wäre natürlich wegen der dauernden Diskettenzugriffe viel zu langsam. Es gibt aber eine wesentlich bessere Lösung. Dazu ist etwas Wissen über die Interna von GEM nötig. Man muß, wie jeder GEM-Programmierer weiß, unter anderem ein Array namens „global“ für das Betriebssystem bereitstellen. In diesem Speicher legt GEM nach dem rsrc_load()-Befehl verschiedene Daten ab. Uns interessiert der Eintrag in global^] und global[6]. Hier findet man nach dem erfolgreichen Aufruf einen Zeiger auf die Baumstruktur. Da dieser Zeiger 4 Byte beansprucht und das Array Integerformat (2 Byte) besitzt, müssen zu diesem Zweck zwei Einträge herhalten. Das Low- und High-Word sitzen an der richtigen Stelle, so daß der geübte C-Programmierer nur noch eine Typumwandlung von Integer in Objektzeiger durchführen muß, um ihn zu verarbeiten. Diesen Zeiger müssen wir zwischenspeichern, da beim nächsten Funktionsaufruf von rsrc_load() dieser Wert überschrieben wird. Der belegte Speicherplatz jedoch und alle dazugehörigen Informationen bleiben unangetastet. Das zweite RSC-File wird irgendwo anders plaziert und der Zeiger darauf ist wieder im global-Array abgelegt. Für jede RSC-Datei brauchen wir also eine Variable für einen Objektzeiger.

Nach dem letzten Laden sind nur die aus dieser Datei stammenden Informationen erreichbar. Das liegt an dem "getarnten“ Zeiger in global. Jetzt kommt der Clou an der Sache! Wird dieser Zeiger jetzt auf den ersten Wert umgeändert, so ist wieder Datei Nummer eins ansprechbar, beziehungsweise deren Bäume und Objekte. Wie bei "älteren“ Rechnern, deren Prozessor mehr Speicherplatz zur Verfügung hat, als er eigentlich verarbeiten kann, wird durch Hin- und Herschalten zwischen verschiedenen Speicherbereichen ausgewählt. So dürfte das Problem übrigens auch bei 1st_Wordplus gelöst sein, da dort ebenfalls mit mehreren (5) RSC-Dateien gearbeitet wird, die getrennt auf der Diskette vorliegen.

Die Routine, die das Umschalten übernimmt, kann man Listing 1 entnehmen.

Vorausgesetzt wurde hier, daß in rsc_ptr[] bereits die Pointer nach dem Laden abgespeichert wurden. In diesem Beispiel gehe ich von 5 Dateien aus, die von 0 bis 4 durchnumeriert sind. Man kann dann durch den Aufruf von

	rsc_file(2);

auf Datei 2 umschalten. Wer sich schon etwas näher mit C und den untrennbar damit verbundenen Pointern befaßt hat, der wird auch die Zuweisung

	ptr = (OBJECT**) &global[5];

verstehen. "&global[5]“ liefert die Adresse dieses Elements (des gesuchten Pointers), das nun jedoch in einen Speicher eines Zeigers umgewandelt werden muß. Deshalb die zwei Sternchen, denn ein Stern würde lediglich aussagen, daß diese Adresse der Zeiger selbst auf eine Objekt-Struktur wäre. Die Zuweisung danach ist dann auch zu verstehen, wenngleich auch sie zusammen mit der obigen Zeile zu einer einzigen verschmolzen werden kann. Aber ich finde, es ist auch so schon kompliziert genug.

Das Auslesen beim Laden der RSC-Dateien entnehmen Sie bitte Listing 2.

Dazu bleibt nur zu sagen, daß in der Variablen „file_pfad“ der aktuelle Pfad zu den Resource-Dateien angegeben werden kann (z. B. mit der Gemdos-Routine Dgetpath oder im Source bestimmt). Die Dateinamen sind im rsc_dat[]-Array eingetragen. Nun viel Spaß beim Programmieren!

Listing 1:

OBJECT *rsc_ptr[5];/* Ptr auf Beginn einer RSC-Datei */

rsc_file(n) 
register int n;
{
	register OBJECT **ptr;

	ptr = (OBJECT **) &global[5];
	*ptr - rsc_ptr[n];
}

Listing 2:

#include "datei0.h"
#include "datei1.h"
#include "datei2.h" 
#include "datei3.h" 
#include "datei4.h"

char rsc dat[5][14] =
{
"datei0.rsc"
"datei1.rsc"
"datei2.rsc"
"datei3.rsc"
"datei4.rsc"
};

...
int abbruch, i;
for(abbruch = i = 0; i < 5;	i++)
{
	sprintf(filename, "%s%s", file_pfad, rsc_dat[1]);
	/* Immer aktuellen Patch benutzen */ 
	if(!rsrc 1oad(fi1ename))
	{
		abbruch = 1; 
		break;
	}
	ptr = (OBJECT **) &global[5];	/*	Ptr initialisieren */
	rsc_ptr[i] = *ptr;
}
if(!abbruch)
	hier geht’s weiter... 
else
	Fehlerbehandlung...

Dirk Schaun
Aus: ST-Computer 09 / 1987, Seite 38

Links

Copyright-Bestimmungen: siehe Über diese Seite