Extending BennuGD by adding a dynamic-memory allocation table to use the Sprites Generator

The problem

BennuGD has no way of knowing the total number of graphs stored inside a loaded FPG file. That means the only way to do that is by knowing, before using it in the code, this total amount of graph files and, therefore, hard-coding it using kinda sort of global variable.

It is really annoying, because you cannot determine its total number of graphic files at runtime. And, logically, this aid could be totally useful when it comes to generating animations, such explosions or, let’s say, sprites. For example, using the Sprites Generator, it is quite common to generate a FPG file containing, for example, a complete character-based movement: a jump, so as to speak. Let’s have a quick look at this simple and trivial code snippet:

145         if((demo==1 || (demo==0 && Key(_right))) && is_not_jumping==1)
146             x=(x+SPEED)%SCREEN_WIDTH;       // To the right ...
147           graph=(((graph+1)%fpg_graphs)==0)?1:((graph+1)%fpg_graphs);
148         End

The line numbered as 147 does use a global and hard-coded initialized variable, called fpg_graphs, storing the total number of graphic files inside this FPG file. I could do that because I did know this total number of graphics. But, what happened if I don’t know this exact number? Clearly, BennuGD language had to be extended.

Adding a dynamic-memory allocated table to BennuGD

The idea was easy as pie: I needed a dynamic-memory allocated table storing a trivial key-pair information linking the file id for any opened FPG file inside the BenuGD program code, and its number of graphics. So, I changed some code for mod_map BennuGD module, the one in charge of loading FPG and MAP files – among other things -.

Firstly, I wrote the classic and trivial C typedef structure to store a single table entry in the mod_map/mod_map.h file this way:

 79 // TCG Struct to store a dybamic key-pair: FPG_ID - Number of graphs
 80 typedef struct {
 81     int _fpg_id;            /* The FPG ID returned after calling fpg_load */
 82     /* 10 bits can store up to 0-1023, in theory there are up to 999 graphs inside a FPG file. */
 83     unsigned int _fpg_count:10;

According to the DIV manual, a FPG file can store up to 999 graphics. So, I decided to use a 10 bit integer, to save spatial complexity.

Then, I defined a dynamic not-initialized table inside mod_map/file_fpg.c file:

 31 FPG_GRAPH *_graph_table = NULL;
 32 int _table_count = 0;

Having this completed, I created four different functions in order to accomplish the trivial tasks of adding, removing or even searching inside this dynamic-allocated table in the mod_map/mod_map.h header file:ç

111 /* These functions add or remove an slot inside *FPG_GRAPHS structure */
112 extern int gr_tcg_insert_fpg_slot (FPG_GRAPH );
113 extern void gr_tcg_remove_fpg_slot (int );
114 extern int gr_tcg_find_fpg_slot  (int );
115 extern int gr_tcg_get_fpg_count (int );
116 extern void gr_tcg_set_fpg_count (int );

Thus, I implemented them inside the  mod_map/file_fpg.c file. The first one, called  gr_tcg_insert_fpg_slot() was in charge of allocating a new slot and insert a new entry:

390 int  gr_tcg_insert_fpg_slot (FPG_GRAPH fpg_gr_t){
391     /* Try to reserve memory for, at least, the number of data plus a new one: */
392     _graph_table = (FPG_GRAPH *)realloc(_graph_table , ++_table_count*sizeof(FPG_GRAPH));
393     /* Probably there's no memory ... */
394     if(!_graph_table){
395         --_table_count;
396         perror("gr_tcg_insert_fpg_slot");
397         return errno;
398     }
399     /* Insert a new entry and show the number of items loaded into memory : */
400     _graph_table[_table_count-1] = fpg_gr_t;
401     return 1;   /* Okay */
402 }

This function would be called each time the BennuGD program code called to load_fpg() routine. In order to do so, I added some trivial code lines in the gr_read_lib() function, implemented in the mod_map/file_fpg.c archive. Basically, I stored in a FPG_GRAPH data structure the FPG file id and its number of graphics, thanks to the loop. Finally, I called the gr_tcg_insert_fpg_slot this way:

190     n_gr._fpg_id = libid;   // Add the Id reference ...
191     gr_tcg_insert_fpg_slot (n_gr);  // Add this FPG reference to the dynamic table ... - or not! ;-) -

It seemed pretty obvious that the right place to remove some table entry had to be unload_fpg call. First, I implemented my gr_tcg_remove_fpg_slot() function:

423 void gr_tcg_remove_fpg_slot (int fpg_id){
424     int slot = gr_tcg_find_fpg_slot(fpg_id);
425     if(slot>-1){
426         for(slot;
427             slot<_table_count;
428             _graph_table[slot]._fpg_id    = _graph_table[slot+1]._fpg_id,
429             _graph_table[slot]._fpg_count = _graph_table[slot+1]._fpg_count,
430             ++slot
431         );
432         /* Decrease its size */
433         _graph_table = (FPG_GRAPH *)realloc(_graph_table , --_table_count*sizeof(FPG_GRAPH));
434     }
435 }

And then I asured it was called each time unload_fpg() was executed:

 714 static int modmap_unload_fpg( INSTANCE * my, int * params )
 715 {
 716     // TCG added code, eliminate its reference to table:
 717     gr_tcg_remove_fpg_slot(params[0]);
 718     grlib_destroy( params[0] ) ;
 719     return 1 ;
 720 }

My routine to search for a given FPG file id could not be much simpler:

408 int gr_tcg_find_fpg_slot (int fpg_id){
409     int idx, found=-1;
410     for(idx=0;
411         idx<_table_count && found==-1;
412         (_graph_table[idx]._fpg_id==fpg_id)?found=idx:++idx
413     );return found;
414 }

Finally, there’s a certain routine called fpg_add() which allows a developer to insert a new graphic file inside an opened FPG file id at runtime. So, I coded a trivial function to allow that behaviour, that is, to update the _fpg_count field on the appropriate slot for the dynamic-memory allocated table _graph_table[]:

419 // Simply set the new counter for this FPG file id: 
420 void gr_tcg_set_fpg_count(int fpg_id){
421     _graph_table[gr_tcg_find_fpg_slot(fpg_id)]._fpg_count++;
422 }
732 static int modmap_fpg_add( INSTANCE * my, int * params )
742     gr_tcg_set_fpg_count(params[0]);

Curiously, however, there is NO fpg_del() documented on BennuGD website, but this function does exist inside the mod_map/mod_map.c module file. Even so, this BennuGD routine calls, internally, the well-known modmap_unload_fpg(), which is in charge of unloading the previously loaded FPG file. Clearly, there is a lack of porting DIV language, ’cause in DIV this function does delete a certain graphic inside a given FPG file id at runtime! Okay, I’ll implement this function soon, so stay tuned.

Adding a new function to the BennuGD core to read this data

Reading the sources and this howto, I fathomed how to do so. I chose the name for my own function to be called from the BennuGD program code: it would be graphics_in_fpg(). So, I added it to the list of exported ones:

1089     /* FPG Toni Castillo Girona */
1090     { "GRAPHICS_IN_FPG"          , "I"           , TYPE_INT      , modmap_graphics_fpg        },

Then, I coded the C hook function modmap_graphics_fpg() thanks to the previous routines I had implemented:

753 static int modmap_graphics_fpg (INSTANCE * my, int * params){
 754     int slot = gr_tcg_find_fpg_slot(params[0]);
 755     return ((slot>-1)?gr_tcg_get_fpg_count(slot):slot);
 756 }

The final way to obtain the total amount of graphics for my Bennu sample code snippet

Now, there is no need of having such a global pre-hard-coded variable fpg_graphs. It is just something one can achieve at runtime this way:

141         if((demo==1 || (demo==0 && Key(_right))) && is_not_jumping==1)
142             x=(x+SPEED)%SCREEN_WIDTH;       // To the right ...
143             graph=(((graph+1)%graphics_in_fpg(file))==0)?1:((graph+1)%graphics_in_fpg(file))
144         End