[DEV] Nuevo sistema de librerias dinamicas

Started by SplinterGU, September 05, 2007, 10:08:42 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

SplinterGU

Bien, estrenando el foro, quiero mostrarle algo de los avances de Bennu, y dar a conocer la primer version del sistema de librerias dinamicas o modulos BennuGD (Bennu Game Development).

Caracteristicas del nuevo sistema:
- Agregar funciones del lenguaje (DLLs, como hasta ahora)
- Agregar Variables Globales
- Agregar Variables Locales
- Agregar Constantes
- Poder acceder a cualquier Variable Global o Local del sistema
- Ya no es necesario recompilar el .dcb ante una nueva version de libreria dinamica que incorpore funciones o variables nuevas.
(Salvo caso de que se cambie de tipo de variable o se le cambie la cantidad de argumentos o el tipo de retorno de una funcion)/
- Funciones de inicializacion y terminacion de la libreria.
- Hooks varios

Nada mejor que un ejemplo, para explicar como viene la cosa.

mod_cd.c (plugin)

[code language="c"]
/*
*  Copyright © 2006-2009 SplinterGU (Fenix/Bennugd)
*  Copyright © 2002-2006 Fenix Team (Fenix)
*  Copyright © 1999-2002 José Luis Cebrián Pagüe (Fenix)
*
*  This file is part of Bennu - Game Development
*
*  Bennu is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  Bennu is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*
*/

/* --------------------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef TARGET_MAC
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif

#include "bgddl.h"
#include "bgdrtm.h"
#include "xstrings.h"
#include "dlvaracc.h"

/* --------------------------------------------------------------------------- */

static SDL_CD * sdl_cd = NULL;
static int      sdl_cdnum = -1;

/* ----------------------------------------------------------------- */

#define CD_TRACK        0
#define CD_FRAME        1
#define CD_TRACKS       2
#define CD_MINUTE       3
#define CD_SECOND       4
#define CD_SUBFRAME     5
#define CD_MINUTES      6
#define CD_SECONDS      7
#define CD_FRAMES       8
#define CD_TRACKINFO    9     /* 400 INTs */

/* ----------------------------------------------------------------- */
/* Definicion de constantes (usada en tiempo de compilacion)         */
DLCONSTANT  __bgdexport( mod_cd, constants_def )[] =
{
    { "CD_TRAYEMPTY", TYPE_INT, 0  },
    { "CD_STOPPED"  , TYPE_INT, 1  },
    { "CD_PLAYING"  , TYPE_INT, 2  },
    { "CD_PAUSED"   , TYPE_INT, 3  },
    { "CD_ERROR"    , TYPE_INT, -1 },
    { NULL          , 0       , 0  }
} ;

/* ----------------------------------------------------------------- */
/* Definicion de variables globales (usada en tiempo de compilacion) */
char * __bgdexport( mod_cd, globals_def ) =
    "STRUCT cdinfo\n"
    " current_track;\n"
    " current_frame;\n"
    " tracks;\n"
    " minute;\n"
    " second;\n"
    " subframe;\n"
    " minutes;\n"
    " seconds;\n"
    " subframes;\n"
    " STRUCT track[99]\n"
    "  audio;\n"
    "  minutes;\n"
    "  seconds;\n"
    "  subframes;\n"
    " END;\n"
    "END;\n" ;

/* ----------------------------------------------------------------- */
/* Son las variables que se desea acceder.                           */
/* El interprete completa esta estructura, si la variable existe.    */
/* (usada en tiempo de ejecucion)                                    */
DLVARFIXUP  __bgdexport( mod_cd, globals_fixup )[] =
{
    /* Nombre de variable global, puntero al dato, tamaño del elemento, cantidad de elementos */
    { "cdinfo.current_track", NULL, -1, -1 },
    { "cdinfo.current_frame", NULL, -1, -1 },
    { "cdinfo.tracks", NULL, -1, -1 },
    { "cdinfo.minute", NULL, -1, -1 },
    { "cdinfo.second", NULL, -1, -1 },
    { "cdinfo.subframe", NULL, -1, -1 },
    { "cdinfo.minutes", NULL, -1, -1 },
    { "cdinfo.seconds", NULL, -1, -1 },
    { "cdinfo.subframes", NULL, -1, -1 },
    { "cdinfo.track", NULL, -1, -1 },
    { NULL, NULL, -1, -1 }
};

/* ----------------------------------------------------------------- */
/**
   int CD_DRIVES()
   Returns the number of CD drives in the system
**/

static int modcd_drives( INSTANCE * my, int * params )
{
    return SDL_CDNumDrives();
}

/* --------------------------------------------------------------------------- */
/**
   int CD_STATUS (int CD)
   Returns the status of a CD (using SDL constants)
**/

static int modcd_status( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    return SDL_CDStatus( sdl_cd );
}

/* --------------------------------------------------------------------------- */
/**
   string CD_NAME (int CD)
   Returns a human-readable string with the name of a CD drive
**/

static int modcd_name( INSTANCE * my, int * params )
{
    int result;

    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    result = string_new( SDL_CDName( params[0] ) );
    string_use( result );
    return result;
}

/* --------------------------------------------------------------------------- */
/**
   CD_GETINFO(int CD)
   Fills the global structure CD with information about the current CD
   Returns 1 if there is a valid CD in the drive or 0 otherwise
**/

static int modcd_getinfo( INSTANCE * my, int * params )
{
    int i, total = 0;
    char * trackinfo;

    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    GLODWORD( mod_cd, CD_TRACKS ) = sdl_cd->numtracks;
    GLODWORD( mod_cd, CD_TRACK )  = sdl_cd->cur_track;
    FRAMES_TO_MSF( sdl_cd->cur_frame, &GLODWORD( mod_cd, CD_MINUTE ), &GLODWORD( mod_cd, CD_SECOND ), &GLODWORD( mod_cd, CD_SUBFRAME ) );

    trackinfo = ( char * ) & GLODWORD( mod_cd, CD_TRACKINFO );

    for ( i = 0; i < sdl_cd->numtracks ; i++, trackinfo += 16 )
    {
        total += sdl_cd->track.length;
        *( Uint32 * ) trackinfo = ( sdl_cd->track.type == SDL_AUDIO_TRACK );
        FRAMES_TO_MSF( sdl_cd->track.length, trackinfo + 4, trackinfo + 8, trackinfo + 12 );
    }
    FRAMES_TO_MSF( total, &GLODWORD( mod_cd, CD_MINUTES ), &GLODWORD( mod_cd, CD_SECONDS ), &GLODWORD( mod_cd, CD_FRAMES ) );
    return 1;
}

/* --------------------------------------------------------------------------- */
/**
   CD_PLAY (int CD, int TRACK)
   Starts playing a track of the given CD
**/

static int modcd_play( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    if ( CD_INDRIVE( SDL_CDStatus( sdl_cd ) ) )
        return !SDL_CDPlayTracks( sdl_cd, params[1], 0, 1, 0 );

    return 0;
}

/* --------------------------------------------------------------------------- */
/**
   CD_PLAY (int CD, int TRACK, int NUMTRACKS)
   Plays a series of tracks of the CD
**/

static int modcd_playtracks( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    if ( CD_INDRIVE( SDL_CDStatus( sdl_cd ) ) )
        return !SDL_CDPlayTracks( sdl_cd, params[1], 0, params[2], 0 );

    return 0;
}

/* --------------------------------------------------------------------------- */
/**
   CD_EJECT (int CD)
   Ejects a CD
**/

static int modcd_eject( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    return !SDL_CDEject( sdl_cd );
}

/* --------------------------------------------------------------------------- */
/**
   CD_PAUSE (int CD)
   Pauses the CD playing
**/

static int modcd_pause( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    return !SDL_CDPause( sdl_cd );
}

/* --------------------------------------------------------------------------- */
/**
   CD_RESUME (int CD)
   Resumes a CD in pause
**/

static int modcd_resume( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    return !SDL_CDResume( sdl_cd );
}

/* --------------------------------------------------------------------------- */
/**
   CD_STOP (int CD)
   Stops the CD
**/

static int modcd_stop( INSTANCE * my, int * params )
{
    if ( params[0] < 0 || params[0] >= SDL_CDNumDrives() ) return 0;

    if ( sdl_cd == NULL || sdl_cdnum != params[0] )
    {
        if ( sdl_cd ) SDL_CDClose( sdl_cd );
        sdl_cd = SDL_CDOpen( params[0] );
        if ( sdl_cd == NULL ) return 0;
        sdl_cdnum = params[0];
    }

    return !SDL_CDStop( sdl_cd );
}

/* --------------------------------------------------------------------------- */

DLSYSFUNCS  __bgdexport( mod_cd, functions_exports )[] =
{
    /* Funciones de manejo de CD */
    { "CD_DRIVES"   , ""      , TYPE_INT    , modcd_drives     },
    { "CD_STATUS"   , "I"     , TYPE_INT    , modcd_status     },
    { "CD_NAME"     , "I"     , TYPE_STRING , modcd_name       },
    { "CD_GETINFO"  , "I"     , TYPE_INT    , modcd_getinfo    },
    { "CD_PLAY"     , "II"    , TYPE_INT    , modcd_play       },
    { "CD_PLAY"     , "III"   , TYPE_INT    , modcd_playtracks },
    { "CD_STOP"     , "I"     , TYPE_INT    , modcd_stop       },
    { "CD_PAUSE"    , "I"     , TYPE_INT    , modcd_pause      },
    { "CD_RESUME"   , "I"     , TYPE_INT    , modcd_resume     },
    { "CD_EJECT"    , "I"     , TYPE_INT    , modcd_eject      },
    { 0             , 0       , 0           , 0                }
};

/* --------------------------------------------------------------------------- */
/* Funciones de inicializacion del modulo/plugin                               */

void  __bgdexport( mod_cd, module_initialize )()
{
    if ( !SDL_WasInit( SDL_INIT_CDROM ) ) SDL_InitSubSystem( SDL_INIT_CDROM );
}

/* --------------------------------------------------------------------------- */

void  __bgdexport( mod_cd, module_finalize )()
{
    if ( SDL_WasInit( SDL_INIT_CDROM ) ) SDL_QuitSubSystem( SDL_INIT_CDROM );
}

/* --------------------------------------------------------------------------- */
[/code]

Actualizado el ejemplo, y eliminado todo otro posts antiguo que puede ocasionar confusion.

Sugiero ver todos los ejemplos que estan en el SVN (modulos)
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

syous

muyyyyyyyyyyy interesante si esto promete tanto Sod online seguaramente sera portado  y primer juego en bennu ;)
Un Saludo
EL dia que la humanidad aprenda a mirar y sentir con los ojos del alma, recuperara su humanidad
http://sodonline.net/
http://darknessage.ayudaprogramacion.net/
http://www.ayudaprogramacion.net/

Proyecto: MMORPG
Completado: 2%
Estado: En Desarrollo...

l1nk3rn3l

es posible acceder  a una variable global declarada en la dll
solo conociendo su nombre?

sino es posible como se accede a una variable global
me regala un ejemplo :

  /* Macros para acceder a datos globales */
    #define GLODWORD(b)   (*(Sint32 *)(globals_fixup.data_offset))
    #define GLOWORD(b)    (*(Uint16 *)(globals_fixup.data_offset))
    #define GLOBYTE(b)    (*(Uint8  *)(globals_fixup.data_offset))
 
gracias splinter

SplinterGU

desde un modulo a variables de otro modulo?
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

#4
Si te fijas en el primer ejemplo, la estructura:

[code language="c"]
DLVARFIXUP  __bgdexport( mod_cd, globals_fixup )[] =
{
   /* Nombre de variable global, puntero al dato, tamaño del elemento, cantidad de elementos */
   { "cdinfo.current_track", NULL, -1, -1 },
   { "cdinfo.current_frame", NULL, -1, -1 },
   { "cdinfo.tracks", NULL, -1, -1 },
   { "cdinfo.minute", NULL, -1, -1 },
   { "cdinfo.second", NULL, -1, -1 },
   { "cdinfo.subframe", NULL, -1, -1 },
   { "cdinfo.minutes", NULL, -1, -1 },
   { "cdinfo.seconds", NULL, -1, -1 },
   { "cdinfo.subframes", NULL, -1, -1 },
   { "cdinfo.track", NULL, -1, -1 },
   { NULL, NULL, -1, -1 }
};
[/code]

Todas las variables que estan en esa estructura no pertenecen al modulo, por consiguiente estan en otro modulo o en el core de Bennu o incluso escritas en codigo Bennu... en resumen, en esa lista pones las variables globales que quieres acceder desde tu modulo... aca hay que tener cuidado con el orden de importacion de los modulos, ya que solo seran importadas aquellas variables que se encuentren definidas en el momento de incluir el modulo... si pertenecen a un modulo que todavia no fue incluido, entonces seran importadas...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

l1nk3rn3l

DLVARFIXUP  globals_fixup[] =   {
   /* Nombre de variable global, puntero a la direccion del dato, tamaño del elemento, cantidad de elementos */
                               { "timer[1]", &timer, -1, -1 },
                               { "fps", NULL, -1, -1 },
                               { "fileinfo.path", NULL, -1, -1 },
                               { "cdinfo.track", NULL, -1, -1 },
                               { "cdinfo.track[1].audio", NULL, -1, -1 },
                               { "os_id", NULL, -1, -1 },
                               { "glob1", NULL, -1, -1 },
                               { "glob2", NULL, -1, -1 },
                               { "a", NULL, -1, -1 },
                               { "b", NULL, -1, -1 },
                               { "c", NULL, -1, -1 },
                               { "d", NULL, -1, -1 },
                               { "e", NULL, -1, -1 },
                               { "e[0]", NULL, -1, -1 },
                               { "e[1]", NULL, -1, -1 },
                               { "e[2][1]", NULL, -1, -1 },
                               { NULL, NULL, -1, -1 }
                               };

podria ser algo asi como el timer que se ve arriba que se pasa como &timer
si es asi podrias hacer un ejemplo de esta declaracion con array, float , enteros y cadena

gracias .

SplinterGU

No... el segundo miembro, el que vos pusiste "&timer" lo llena el core de bennu, con la direccion real donde se encuentra la variable...
Es un puntero... vos necesitas castear ese puntero a el dato que es realmente la variable, se supone que si uno esta pidiendo la direccion de una variable por un nombre especifico, entonces tambien conoce el tipo de dato contenido y por ende, sabe como castearlo...

El ejemplo de arrays, lo tenes arriba, en el mod_cd.c puesto de ejemplo...

Correccion a mi post anterior:

Donde dice, "si pertenecen a un modulo que todavia no fue incluido, entonces seran importadas..." debe decir "si pertenecen a un modulo que todavia no fue incluido, entonces no seran importadas..."
Quiero agregar, que las variables globales y locales escritas en codigo Bennu, pueden ser importadas perfectamente a los modulos, ya que el dcb esta generado, y por ende ya tiene toda la informacion necesaria para importar las variables, aun si estan declaradas luego de las importaciones...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Elelegido

#7
Ya he portado mi editor a bennu con éxito :D. Ahora estoy intentando crear una DLL para poder usar TinyXML, un parser bastante majo.

Estoy intentando compilar el primer ejemplo, pero parece que la DLL no me sale como debe ser. Al usar 'moddesc.exe' no me muestra ningún contenido aparte de la cabecera y el nombre del módulo.

Igual mi error viene del archivo bennudl.sym. Si lo pongo tal y como está en el primer ejemplo, el compilador dice que lo ignora, pero si pongo "EXPORTS" en la primera linea se lo traga, el problema es que no se si así es como debería ser.

Otra cosa que veo rara, es que en el 1er ejemplo, el .c no incluye (#include) el .h, y que si yo opto por hacerlo, obtengo errores por redefinición de tipos.

Uso VC++ 2008, y el .sym indico que lo enlace como "Module Definition File", que es como lo he visto hacer en otro ejemplo. El resto lo configuro como una DLL normal, desactivando cualquier optimización por si acaso. La verdad es que no tengo experiencia con esto, en fenix no llegué a hacer ninguna DLL (y lo cierto es que lo intenté varias veces xD), y no entiendo la tarea del .sym en todo este proceso, si se pudiera explicar lo agradecería.

Posteo aquí porque creo que puede servir para que este ejemplo quede lo menos ambigüo posible.

Vaya mensaje me ha quedado, parece un telegrama... a ver si me le doy una tregua a mi cabeza que me está empezando a fallar xD.

SplinterGU

este post esta un poco desactualizado, pero sirve para introducirte en la base...
te sugiero veas alguno de los fuentes que estan en el repositorio en sourceforge...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Drumpi

Pregunta tonta: ¿cual es la extension correcta para las dlls, para Fenix y Bennu, en linux? .so ó .o
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

SplinterGU

.so

pero en los import no deben ir la extension ni el path...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Elelegido

Me gustaría saber cual es la función del bennu.sym o .diff, si no es mucha molestia.

Por cierto, creo que si se actualizase este ejemplo sería de gran ayuda. Es lógico que la gente que quiera documentarse venga primero a este hilo.

SplinterGU

sym son los symbolos exportables, pero creo que ya no hace falta usarlo...

de que .diff hablas?

si, tengo que actualizarlo, luego lo hare...

gracias...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

osk

Hola.

He visto que el código que pones al principio de este post es ligeramente diferente del que viene con el BennuPack "ejemplo Show Vars" (lo adjunto a continuación: en realidad tiene extensión c pero el foro no me deja subirla tal cual). ¿Cuál es el que está más desactualizado?

Por otro lado, la plantilla que hay en el BennuPack para hacerte tu propio módulo (también lo adjunto) es también ligeramente diferente del módulo de ejemplo que está puesto más arriba. ¿Cuál es el bueno?

Finalmente, para ir probando, he intentado volver a compilar el código de la librería Water que hay en el BennuPack. Después de ponerles lar rutas de las .h necesarias, me salen errores del tipo "undefined reference". ¿Qué me falta por hacer? (Aquí dejo la salida...):

C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c||In function `water_draw':|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|92|warning: implicit declaration of function `cos'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|107|warning: assignment makes integer from pointer without a cast|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|63|warning: unused variable `width'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c||In function `water_start':|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|147|warning: implicit declaration of function `gr_destroy_object'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|156|warning: implicit declaration of function `gr_new_object'|
obj\Debug\main.o||In function `water_draw':|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|67|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|69|undefined reference to `_SDL_GetTicks'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|71|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|71|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|87|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|93|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|93|undefined reference to `_scrbitmap'|
obj\Debug\main.o:C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|93|more undefined references to `_scrbitmap' follow|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|105|undefined reference to `_gr_make_trans_table'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|106|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|107|undefined reference to `_trans_table'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|109|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|111|undefined reference to `_scrbitmap'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|112|undefined reference to `_colorghost'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|112|undefined reference to `_colorghost'|
obj\Debug\main.o||In function `water_info':|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|128|undefined reference to `_scr_height'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|129|undefined reference to `_scr_width'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|130|undefined reference to `_scr_height'|
obj\Debug\main.o||In function `water_start':|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|147|undefined reference to `_gr_destroy_object'|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|156|undefined reference to `_gr_new_object'|
obj\Debug\main.o||In function `water_stop':|
C:\Documents and Settings\pepe\Escritorio\puta2\puta2\main.c|167|undefined reference to `_gr_destroy_object'|
||=== Build finished: 21 errors, 5 warnings ===|


Muchas gracias!!!

SplinterGU

Download Lastest BennuGD Release: http://www.bennugd.org/node/2