Resource-Einbindung in C

Wollen Sie auf dem ATARI ST in Ihrem Programm ein selbsterstelltes Bild, einen neuen Font, eine RSC-Datei oder ein sonstiges Daten-File einbinden? Einige BASIC-Dialekte stellen für solche Zwecke (und für Maschinenspracheroutinen) den INLINE-Befehl zur Verfügung. Was aber z.B. in C und Assembler machen?

Einige Resource-Construction-Sets bieten die Möglichkeit, die Resource-Datei als C-Quelltext auszugeben. Dieses Feature bieten aber nicht alle Resource-Construction-Sets, außerdem erfordert dieser Weg einen mehr oder weniger großen Anpassungsaufwand. Andere Daten-Files können Sie eigentlich nur zur Laufzeit des Programmes dazuladen. oder mit einem Hilfsprogramm in ein C-Quelltext-File umwandeln, in dem ein global initialisiertes char-Array die Bytes des Daten-Files aufnimmt. Eine andere Möglichkeit zeigt das Programm RSC-20BJ.TTP (lies: Resource to Objekt.TTP) auf, die gerade beim Einbinden von Resource-files sehr elegant ist. Die vom Programm erwartete Kommandozeile lautet: infile outfile labelname [-r], infile ist der Dateiname des zu konvertierenden Daten-Files. outfile ist die konvertierte Objektdatei. labelname ist der Name des öffentlichen Labels, unter dessen Adresse Sie Ihre Daten ansprechen (siehe Beispielprogramm).

Um die Arbeitsweise des Konverters zu verstehen, müssen Sie etwas über den Aufbau von Objektdateien, wie Sie Compiler und Assembler erstellen, wissen. Die vorliegenden Betrachtungen sind teilweise etwas vereinfacht und beschränken sich auf den für Sie wesentlichen Aspekt. Das auf dem Atari ST von den gängigen Compilern/Assemblern benutzte Format ist jenes von Digital-Research (GEM stammt auch daher). In diesen Objekt-Files sind die Größen des Codes, der initialisierten und der uninitialisierten Datenbereiche vermerkt sowie die Code- und Datenbereiche abgelegt. Unsere Daten-Files landen komplett im Datensegment der erzeugten Objektdatei; die Größe des Datenbereiches entspricht der des Daten-Files. Codegröße und Größe der uninitialisierten Datenbereiche werden auf Null gesetzt. Weiterhin gibt es eine Symboltabelle, die hauptsächlich Auskunft gibt über die Namen von Variablen, welche Sie aus einem Programm-Modul exportieren bzw. importieren, und die Art der Variablen. EinBeispiel: Sie definieren in einem C-Modul eine Variable global (außerhalb jeder Funktion), z.B. int flag. Diese Variable soll in einem zweiten Programm-Modul benutzt werden und wird dort als extern int flag deklariert. Der Linker weiß nun aus der Symboltabelle des Objekt-Files von Modul 2. daß in einem anderen Modul eine Variable flag existieren muß und findet diese in der Symboltabelle des Objekt-Files von Modul 1. Der Label-Name, den Sie über die Kommandozeile angegeben haben, wird in der Symboltabelle vermerkt als "global definiert im Datensegment" mit dem Symbolwert 0. Dies bedeutet, daß die Adresse dieses Labels direkt auf die Daten (ohne Versatz) zeigt.

Bild 1: Der Aufbau einer Digital-Research-Objektdatei

Den für uns wichtigsten Teil des Objekt-Files (zumindest bei RSC-Dateien) stellt die Fixup-Tabelle dar. Programme können zur Laufzeit vom Betriebssystem an ganz unterschiedliche Startadressen geladen werden. eben dort, wo gerade genug Platz frei ist. Aus Codegröße-und Geschwindigkeitsgründen benutzen Compiler (und Assembler-Programmierer) aber viele Maschinenbefehle, die auf eine feste Adresse (z.B. einer Variablen) zugreifen, und zwar so, als begänne der Code die Daten ab Speicheradresse 1 Das bedeutet z.B.: Fall- ein Label relativ zum Codesegmentstart $4711 Bytes entfernt liegt, ist im compilierten Maschinencode (Objektdatei) die Zugriffsadresse $4711. Dem Linker kann aber über die Fix-up-Tabelle gesagt werden, daß er für solche Zugriffe Einträge im fertigen Programm in der sogenannten Relokationstabelle macht. Der Lader (das ist die Komponente des Betriebssystems, welches ein Programm von Diskette/Platte lädt und alle nötigen Anpassungen vornimmt) wertet solche Einträge aus und addiert bei allen solchen absoluten Adressen die tatsächliche Startadresse des Codes (er 'relozierf die Adressen). Die Fixup-Tabelle ist genauso groß wie die Größen von Code- und Datensegment zusammengenommen. Jede zu relozierende Adresse (Länge = 4 Bytes = 1 Long) wird durch 4 Bytes in der Fixup-Tabelle vermerkt, wobei der Abstand des Longs vom Fixup-Tabellenanfang genau dem Abstand des zu relozierenden Longs vom Start des Code- bzw. Datensegments entspricht. Für die Konversion sind zwei beschreibende Werte der Fixup-Tabelle wichtig: 0x00000000 und 0x00050001. Der erste Wert führt dazu, das der Linker in der Relokationstabelle des fertig gelinkten Programms einen Wert einträgt, der dem Lader bitte dieses Wort (long) nicht verändern!" mitteilt. Der zweite Wert sagt dem Linker, daß es sich um ein zu relozierendes Long im Datensegment handelt, was er entsprechend in der Relokationstabelle berücksichtigt. Bei reinen Daten-Files [KEIN Switch -r (!)] wird die gesamte Fixup-Tabelle mit Nullen aufgefüllt, es sollen ja auch keine Daten vom Lader verändert werden. Bei Resourcefiles existieren eine Menge interner Zeiger, z.B. auf TEDINFOs, ICONBLKs, BITBLKs sowie innerhalb dieser Strukturen Zeiger auf Daten etc. (siehe [2],[6]). Alle diese Zeigerwerte beziehen sich aber auf den Anfang der Resource-Datei und werden von rsrc_load(..) beim Einladen der Resourcedaten auf die absoluten Speicheradressen durch Addition der RSC-Speicherbasisadresse angepaßt. Na, klingelt’s? Das ist ja genau dasselbe wie beim Relozieren! Wenn wir in der Fixup-Tabelle diese Zeiger als relozierbar markieren, wird der Lader uns alle RSC-intemen Zeiger setzen.

Die einzige zur Laufzeit notwendige Anpassung besteht in der Umrechnung von Zeichen in Pixel-Koordinaten. In Resourcefiles sind nämlich die Positionen und Abmessungen der Objekte nicht in Pixeln, sondern in Zeichenbreiten bzw. Zeichenhöhen angegeben, wobei noch ein vorzeichenbehafteter Byteoffset (-128..127) einen Pixel-Versatz angeben kann. Diese Umrechnung kann sehr bequem mittels der AES-Funktion rsrc_obfix(..) gemacht werden. Die Funktion rsrc gadchf..) muß durch eine eigendefinierte Funktion ersetzt werden, da die Originalfunktion auf Daten im global-Array zurückgreift, die durch rsrc_load gesetzt werden.

Bei der Konversion von Resourcefiles (Flag -r) wird sicherheitshalber noch ein kleiner Test vorgenommen, ob es sich wirklich um ein Resourcefile handelt, damit beim versehentlichen Setzen dieses Flags normale Daten-Files durch die Relokationsinformationen nicht verunstaltet werden. Aber nicht nur deswegen: Sämtliche relativen Zeiger beziehen sich auf den Resource-Dateistart (wie schon erwähnt) und können deshalb auch nicht negativ sein. Die für die Konversion allokierten Speicherbereiche liegen meist direkt hinter dem Konversionsprogramm im Speicher (an höheren Adressen). Negative Zeiger-Offsets (das sind bei normalen Daten-Files fälschlicherweise als Zeiger-Offsets aufgefaßte Daten) könnten (beim ’Poken' der Fix-up-Werte in den dafür allozierten Buffer) das Konversionsprogramm teilweise überschreiben und zu Programmabstürzen führen. Details zum Aufbau der Resource-Dateien würden den Rahmen dieses Artikels bei weitem sprengen. Ich verweise speziell auf [2],[3] und 'die Bibel' [6]. Die Programme wurden in Turbo C 1.0 entwickelt, wobei zunächst eine komplette Implementation in ANSI-Funktionen vorlag. Die I/O-Library von Turbo-C ist jedoch mit einigen herrlichen (nervtötenden) Fehlern versehen, so daß fast zur Gänze auf Gemdos ausgewichen wurde. Als positiver Nebeneffekt ergab sich eine Verkleinerung des fertigen Programmcodes auf ein Drittel der Ursprungsgröße (jetzt ca. 2.5 kByte).

Im Programm setze ich zur Behandlung von Ausnahmesituationen (sprich Fehlern) 'Go-to's ein. Da Sie diesen Satz noch lesen, darf ich davon ausgehen, das Sie nicht zu den absoluten Anti-'Goto'-Puristen gehören, welche sicherlich gerade damit beschäftigt sind, diesen Artikel fein säuberlich aus dieser Zeitung herauszutrennen. Daß 'Goto's zum Verlassen tief verschachtelter Kontrollstrukturen gut geignet sind und eine Menge überflüssiger if-Abfragen ersparen, hat sich ja zum Glück herumgesprochen. Aber auch bei Fehlern ersparen Sie es sich, jedesmal den Ausnahmebehandlungscode durchzulesen, was das Verständnis der eigentlichen Programmabsicht doch erheblich stört. Außerdem sind alle Fehlerbehandlungsmaßnahmen übersichtlich am Ende einer Routine gesammelt.

Zum Beispielprogramm können Sie eine beliebige, mit einem Resource-Construction-Set erstellte und durch den Konverter geschleuste Resource-Datei dazulinken. Der Label-Name sollte recource sein. Das Programm zeigt Ihnen alle Objektbäume zentriert auf dem Bildschirm. Menüleisten werden normalerweise nicht zentriert; hier schon, da das Programm nicht wissen kann, ob Ihre Resource eine Menüleiste enthält. Bei Menüs bleibt etwas Pixel-Schrott am oberen Bildschirmrand.

Bild 2: Der Aufbau eines Symboltabelleneintrags

[1] Jürgen Schultz-Kappler, Dialog über Tasten, c't 3/88

[2] Stefan Höhn, Objektstrukturen im AES. ST-Computer Sonderheft 2 (sehr informativ und verständlich!)

[3] Stefan Höhn, Einführung in das Resource-Construction-Set von Digital Research, ST-Computer Sonderheft No. 2

[4] Jürgen Leonhard. Resource-Datei? Nein danke!, ST-Computer 12/87

[5] Lutz Preßler, Resourcen in GFA-Basic 3.0. ST-Computer 10/88

[6] Jankowski/Reschke/Rabich, ATARI ST Profibuch. Sybex Verlag

/************************************************
* RSC2OBJ.TTP - geschrieben in Turbo-C 1.0
*               Mai 1990 Christoph Conrad
*
* KOMMANDOZEILE: infile outfile labelname [-r]
*
* FUNKTION: Konvertiert die Datei infile in eine Objectdatei namens
*           outfile im Digital Research Objectformat. Das komplette
*           infile wird dabei als DATA-Segment deklariert und kann
*           über die Adresse der Public-Definition (des externen Labels)
*           labelname referenziert werden.
*
*           Ist der optionale Parameter -r angegeben, muss es sich beim
*           infile um eine Resourcedatei handeln. Dann werden zusätzlich
*           alle ob_spec-Zeiger auf TEDINFO/ICONBLK/BITBLK-Strukturen
*           als zu relozierend vermerkt, die Zeiger innerhalb dieser
*           Strukturen, die Indize der Objectbaumtabelle, der
*           Freestringtabelle, der Freeimagestabelle sowie die bi_pdata-
*           Zeiger der Freeimages.
*           Dies bewirkt, das beim Laden des Programmes durch den
*           GEMDOS-Loader diese Referenzen automatisch auf die absoluten
*           Speicheradressen angepasst werden. Als einzige Arbeit bleibt
*           die Umrechnung von Zeichen- in Pixelkoordinaten mittels
*           rsrc_obfix(...).
*           Der labelname wird immer auf 8 Zeichen begrenzt und in
*           Grossbuchstaben konvertiert.
*
* Benötigte Bibliotheken: TOS- und Standardlib. 
*/

#include <tos.h>
#include <aes.h>

typedef unsigned long size_t;

extern void exit(int status); 
extern char *strupr(char *s); 
extern int  toupper(int c); 
extern int  atexit(void (*func)());

typedef enum{false, true} bool;

/* Vor der Rückkehr des Programmes zum Desktop
 * wird auf einen Tastendruck gewartet, damit
 * alle Meldungen gelesen werden können.
 */
void holdScreen(void) {Cconws("\r\n<Press any key>"); Crawcin();}

/* Ausgabe von count Nullbytes auf die Datei FILE* file */ 
bool fputNull(int count, int file)
{   int byte;

    for(byte = 0; byte < count; ++byte) 
        if(Fwrite(file, 1L, "") != 1L) 
            return false; 
    return true;
}

int main
    (int    argc,           /* Anzahl übergebener Parameter + 1 */ 
     char   *argv[])        /* Einzelne Parameter (ab argv[1]) als String */
{
    int  in, out;           /* Ein/Ausgabedatei - Filedeskriptoren */ 
    bool rscReloc = false;  /* RSC-Dateikonvertierung ? */ 
    long length;            /* Länge der Eingabedatei */ 
    char *inbuf;            /* Zeiger auf Einlesebuffer für Eingabedatei */ 
    int  *outbuf;           /* Zeiger auf Buffer für Fixup-table */ 
  /* Allgemeiner Schleifenindex, long wegen Löschen des outbuf (s.u.) */ 
    long i;

  /* Bei Programmabschluss soll der Ausgabebildschirm bis zu 
   * einem Tastendruck erhalten bleiben.
   */
    atexit(holdScreen);

  /* Anzahl der Übergabeparameter checken */ 
    if(!(4 <=argc && arge <= 5)) goto Usage;

  /* Öffnen der Eingabedatei */
    if((in = Fopen(argv[1], 0)) < 0) goto InfileOpen;

  /* Übergabeparameter "-r": Relocation einer RSC-Datei an */ 
    if(argv[4] && argv[4][0] == && toupper(argv[4][1]) == 'R') 
        rscReloc = true;

  /* Dateilänge der Eingabedatei ermitteln */ 
    if((length = Fseek(0L, in, 2)) <= 0 || Fseek(0L, in, 0) != 0)
        goto InfileLen;

  /* Buffer für Eingabedatei allokieren... */ 
    if(!(inbuf = (char*) Malloc(length))) goto Malloc;

  /* ...und Datei einlesen */
    if(Fread(in, length, inbuf) != length) goto InfileRead;

  /* Test, ob (bei Angabe des Flags -r, Resourcedatei konvertieren)
   * die im RSC-Dateiheader abgelegte Dateilänge mit
   * der tatsächlichen Länge übereinstimmt.
   */
    if(rscReloc && ((int*)inbuf)[17] != length) goto NoRSCFile;

  /* Öffnen der Ausgabedatei */
    if((out = Fcreate(argv[2], 0)) < 0) goto OutfileCreate;

  /* Objectdatei-Magic setzen */
    if(Fwrite(out, 2, "\x60\x1A") != 2) goto Write;

  /* CODE-Länge == 0 */
    if(!fputNull(4, out)) goto Write;

  /* DATAlänge = Grösse der Eingabedatei */ 
    if(Fwrite(out, sizeof(long), filength) != sizeof(long)) 
        goto Write;

  /* 4 Nullbytes für BSSlänge
   * 3 Nullbytes + 14 (Länge der Symboltabelle als Long, ein Eintrag
   * in der Symboltabelle = 14 Bytes)
   */
    if(!fputNull(7, out)) goto Write;
    if(Fwrite(out, 1L, "\016") != 1L) goto Write;

  /* Zehn reservierte Bytes (Null) */ 
    if(!fputNull(10, out)) goto Write;

  /* DATA == Eingabedatei schreiben */
    if(Fwrite(out, length, inbuf) != length) goto Write;

  /* Der Desktop wandelt ihm übergebene Kommandozeilen bei
   * TTP-Programmen immer in Grossbuchstaben!! strupr hier,
   * damit auch bei Kommandozeileninterpretern, die dies evt.
   * nicht machen, das gleiche Programmverhalten auftritt.
   */
    strupr(argv[3]);

  /* Eintrag in der Symboltabelle
   * Labelname (8 Bytes) + (0xA400 == Defined global in DATA-Segment)
   */
    for(i = 0; argv[3][i] && i < 8; ++i)
        if(Fwrite(out, 1L, &argv[3][i]) != 1L) goto Write; 
    for(; i < 8; ++i)
        if(Fwrite(out, 1L, "") != 1L) goto Write;

  /* 0xA400 == defined global symbol in DATA-Segment */
    if(Fwrite(out, 1L, "\xA4") != 1L) goto Write; 
  /* Ein Nullbyte von 0xA400, vier Nullbytes Symbolwert */ 
    if(!fputNull(5, out)) goto Write;

    if(!(outbuf = (int*) Malloc(length)))
    {   Fdelete(argv[2]); /* Ausgabedatei löschen */
        goto Malloc;
    }
    for(i = 0; i < length; ++i) ((char* )outbuf)[i] = 0;
    if(rscReloc)
    /* Resourcedatei, Fixuptabelle aufbereiten */ 
    { /* Zeiger auf Resourcedateiheader */
         RSHDR *rsc = (RSHDR*) inbuf;
      /* Zeiger auf erstes Object */
         OBJECT *obj = (OBJECT*) (rsc->rsh_object + (long) rsc); 
      /* Gesamtzahl der Objecte */ 
         int nrObj = rsc->rsh_nobs;
      /* Gesamtzahl der Objektbäume */ 
         int nrObjTrees = rsc->rsh_ntree;
      /* Gesamtzahl der Freestrings */
         int nrFreeStr = rsc->rsh_nstring;
      /* Gesamtzahl der Freeimages */
         int nrFreeImg = rsc->rsh_nimages;
      /* Abstand eines zu relozierenden Wertes vom Resourcedateistart
       * in sizeof(int) (sizeof(int), da int * outbuf)
       */
        int dist;

      /* Das Fixup-Info sieht HIER immer gleich aus: Die Tabelle
       * enthält für jedes Long des DATA-Segments (== RSC-Datei)
       * einen Wert, wobei ein 0L (wegen dem calloc) vorgegeben ist
       * und "keine Relokation" bedeutet.
       * Für alle zu relozierenden Longs wird eine 0x00050001 eingetragen,
       * wobei das Highword für 'relocatable', das Lowword für 'in DATA-
       * Segment' steht.
       */

      /* Objektbaumtabelleneinträge */ 
        dist = rsc->rsh_trindex / 2; 
        for(i = 0; i < nrObjTrees; ++i)
        {   outbuf[dist++] = 5; 
            outbuf[dist++] = 1;
        }

      /* Freestringtabelleneinträge */ 
        dist = rsc->rsh_frstr / 2; 
        for(i = 0; i < nrFreeStr; ++i)
        {   outbuf[dist++] = 5; 
            outbuf[dist++] = 1;
        }

      /* Freeimagestabelleneinträge + zugehörige BITBLK's */ 
        dist = rsc->rsh_frimg / 2; 
        for(i =0; i < nrFreeImg; ++i)
        {   int bi_pdataDist = ( (long*) (rsc->rsh_frimg + (long) rsc) )[i] / 2;

            /* bi_pdata-Zeiger in BITBLK's */ 
              outbuf[bi_pdataDist++] = 5; 
              outbuf[bi_pdataDist] = 1;

            /* Tabelleneinträge */ 
              outbuf[dist++] = 5; 
              outbuf[dist++] = 1;
        }

      /* Alle Objekte durchgehen */ 
        for(i =0; i < nrObj; ++i)
        { /* Bei ob__type extended-Flags (Bits 8.. 15) ausblenden */ 
            int ob_type = obj[i].ob_type &0xFF; 
            int j; /* Lokaler Schleifenindex */

          /* Anpassung der ob_spec-Zeiger */ 
            switch(ob_type)
            {
            case 21: case 22: case 23: case 26: case 28:
            case 29: case 30: case 31: case 32: 
                dist = ((long) &obj[i].ob_spec - (long) rsc) / 2; 
                outbuf[dist++] = 5; 
                outbuf[dist] = 1;
            }

          /* Anpassung der Zeiger in TEDINFO/ICONBLK/BITBLK-Strukturen */ 
            switch(ob_type)
            {
            case 21: case 22: case 29: case 30: case 31:
              /* TEDINFO/ICONBLK */
                dist = (long) obj[i].ob_spec / 2; 
                for(j = 0; j < 3; j++)
                {   outbuf[dist++] = 5;
                    outbuf[dist++] = 1;
                }
                break; 
            case 23:
              /* BITBLK */
                dist = (long) obj[i].ob_spec / 2; 
                outbuf[dist++] = 5; 
                outbuf[dist]   = 1;
            }
        }
    }
  /* Fixup-Tabelle schreiben */
     if(Fwrite(out, length, outbuf) != length) goto Write;

     Fclose(in);
     Fclose(out);

     Cconws("Alles paletti!"); 
     return false;

     Usage:
     Cconws("Falsche Anzahl Parameter!\r\n"); 
     Cconws("RSC2OBJ infile outfile labelname [-r]");
     exit(1);

     InfileOpen:
     Cconws(argv[1]); Cconws(" konnte nicht geöffnet werden!");
     exit (2);

     InfileLen:
     Cconws(argv[1]); Cconws(": Fehler beim Ermitteln der Dateilänge!");
     exit(3);

     Malloc:
     Cconws("Zuwenig Speicher!"); 
     exit(4);

     InfileRead:
     Cconws(argv[1]); Cconws(": Fehler beim Einlesen!");
     exit(5);

     NoRSCFile:
     Cconws(argv[1]); Cconws(": Keine RSC-Datei oder fehlerhaft!");
     exit(6);

     OutfileCreate:
     Cconws(argv[2]); Cconws(" konnte nicht geöffnet werden!");
     exit(7);

     Write:
     Cconws("Fehler beim Schreiben!");
     Fdelete(argv[2]); 
     exit(8);
}
/************************************************
 * RSCTEST.PRG - geschrieben in Turbo-C 1.0
 * Mai 1990 Christoph Conrad
 *
 * FUNKTION: Test einer RSC-Objectdatei Einbindung
 * durch Anzeigen aller Objektbäume
 * Der labelname auf der Kommandozeile von RSC20BJ.TTP
 * muss 'resource' lauten (ohne Anführungsstriche)
 *
 * Benötigte Bibliotheken: Standard/Gemlib/TOS-Lib.
 */

#include <vdi.h>
#include <aes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tos.h>

typedef enum {false, true} bool;

/* Die üblichen GEM-Variablen...*/ 
int contrl[12], intin[128], intout[128], ptsin[128], ptsout[128]; 
int work_in[12], work_out[57]; 
int handle, phys_handle;
int gl_hchar, gl_wchar, gl_hbox, gl_wbox; 
int gl_apid;

/* Beim AES/VDI anmelden */ 
bool open_vwork(void)
{
    register int i;

    if ( (gl_apid = appl_init()) != -1)
    {   for(i = 1; i < 10; work_in[i++] = 0); 
        work_in[10] = 2;
        phys_handle = graf_handle(&gl_wchar, &gl_hchar, &gl_wbox, &gl_hbox); 
        work_in[0] = handle = phys_handle; 
        v_opnvwk(work_in, &handle, work_out); 
        return(true);
    }
    else
        return(false);
}

/* Beim VDI/AES abmelden */ 
void close_vwork(void)
{   v_clsvwk(handle); 
    appl_exit();
}

extern RESOURCE;
/* rsc zeigt auf den Anfang der Resourcedatei, also den Resourceheader */
RSHDR *rsc = (RSHDR*) &RESOURCE;

/* Ersatz der Funktion rsrc_gaddr des AES.
 * Es werden Objektbäume/Freestrings/Freeimages berücksichtigt.
 * Die Original-AES-Funktion liefert bei anderen Typen zum Teil
 * seltsame Ergebnisse.
 * Die Positionsoffsets im Resourceheader verweisen auf (relozierte)
 * Adresstabellen mit Zeigern auf die eigentlichen Objekte.
 * rsrc_gaddr liefert bei
 * - Objectbäumen (re_gtype == 0): Die Anfangsadresse des Baums
 * - Free-Strings/Images: Zeiger auf Zeiger auf das eigentliche Object
 */

int rsrc_gaddr(int re_gtype, int re_gindex, OBJECT **re_gaddr)
{   if(re_gtype == 0 && 0 <= re_gindex && re_gindex < rsc->rsh_ntree)
    {   *re_gaddr = *((OBJECT**)(rsc->rsh_trindex + (long) rsc + 4 * re_gindex));
        return true;

    }

    if(re_gtype == 15 && 0 <= re_gindex && re_gindex < rsc->rsh_nstring)
    {   *re_gaddr = (OBJECT*)   /* eigentlich OBJECT** */ 
                    (rsc->rsh_frstr + (long) rsc + 4 * re_gindex);
        return true;
    }

    if(re_gtype == 16 && 0 <= re_gindex && re_gindex < rsc->rsh_nimages)
    {   *re_gaddr = (OBJECT*)   /* eigentlich OBJECT** */ 
                    (rsc->rsh_frimg + (long) rsc + 4 * re_gindex);
        return true;
    }

    return false;
}

/* Für alle Objecte wird eine Transformation von 
 * Zeichen in Pixelkoordinaten vorgenommen.
 */
void rsrc_AllFix(OBJECT *firstObj, int nrObj)
{   int obj;

    for(obj = 0; obj < nrObj; ++obj) 
        rsrc_obfix(firstObj, obj);
}

bool main(void)
{
    if(open_vwork() == true)
    {   OBJECT *objTree;
        int nrObj = rsc->rsh_nobs; 
        int x,y,w,h;
        int nrObjTrees = rsc->rsh_ntree; 
        int actObjTree;

        graf_mouse(0,0);            /* Maus als Pfeil */

        rsrc_gaddr(0,0,&objTree);   /* Adresse des ersten Objectbaums */ 
        rsrc_AllFix(objTree, nrObj);/* Zeichen -> Pixelkoordinaten */

        for(actObjTree = 0; actObjTree < nrObjTrees; actObjTree++)
        {
            /* Objektbaumadresse bestimmen */
               rsrc_gaddr(0, actObjTree, &objTree); 
            /* Zentrieren des Objektbaums
             * Menüleisten sind nicht zum Zentrieren gedacht, bei
             * ihnen bleibt Müll am oberen Bildschirmrand nach
             * dem Restaurieren mit form_dial(3,..).
             */
               form_center(objTree, &x, &y, &w, &h);

            /* Bildschirmbereich reservieren */
              form_dial(0, x, y, w, h, x, y, w, h); 
            /* Objektbaum zeichnen */
              objc_draw(objTree, 0, 8, x, y, w, h);
              Crawcin();
            /* Bildschirmbereich freigeben */
              form_dial(3, x, y, w, h, x, y, w, h);
        }

        close_vwork(); 
        return false;
    }
    else
    {   printf("Fehler bei der Programminitialisierung!"); 
        return true;
    }
}

Christoph Conrad
Links

Copyright-Bestimmungen: siehe Über diese Seite