(Fixed) Storing and reading sprite data from a linked list
Winfield

Note: a character I pasted in my previous attempt killed the post. I hope someone can delete that one. Sorry. :/

I'm writing a small sprite loading and management system for my game which reads sprite data out of Allegro config files which look like this:

#SelectExpand
1[still_up_1] 2x = 64 3y = 0 4w = 64 5h = 96 6 7[still_down_1] 8x = 192 9y = 0 10w = 64 11h = 96 12 13[still_left_1] 14x = 384 15y = 0 16w = 64 17h = 96 18 19[still_right_1] 20x = 256 21y = 480 22w = 64 23h = 96 24 25 26[walk_up_1] 27x = 0 28y = 0 29w = 64 30h = 96 31 32[walk_up_2] 33x = 64 34y = 0 35w = 64 36h = 96 37 38[walk_up_3] 39x = 128 40y = 0 41w = 64 42h = 96

I store that data in a series of structs as follows:

#SelectExpand
1const char *sprite_directions[] = {"up", "down", "left", "right", NULL}; 2const char *sprite_states[] = { "still", "walk", NULL }; 3 4typedef struct 5{ 6 char state[32]; // Textual identifier for animation (walk, run, etc) 7 char dir[32]; // Direction (north, south, etc) 8 int cycle; // Frame number 9 int x; // Starting X of frame in bitmap 10 int y; // duh 11 int h; // Width on bitmap 12 int w; // yeah 13} ANNE_SPRITE_FRAME; 14 15typedef struct 16{ 17 GList *frame; 18} ANNE_SPRITE_ANIMATIONS; 19 20typedef struct 21{ 22 ALLEGRO_BITMAP *bitmap; 23 char name[255]; 24 ANNE_SPRITE_ANIMATIONS anim; 25 ANNE_SPRITE_FRAME current; 26} ANNE_SPRITE;

As you can see, each frame has a direction, state, and animation index, and each state (walk, stand, run, etc) can have an indeterminate number of frames. I have several functions for working with these - to load a sprite:

#SelectExpand
1// Loads a sprite from the filesystem and into memory, and initializes a starting 2// frame. 3ANNE_SPRITE anne_sprite_load(char sprite_name[]){ 4 ANNE_SPRITE *sprite = malloc(sizeof(*sprite)); 5 ANNE_SPRITE_ANIMATIONS *anim = malloc(sizeof(*anim)); 6 7 anim->frame = NULL; 8 9 int k = 0; 10 11 char def_file_name[255]; 12 char image_file_name[255]; 13 char current_anim_state[64]; 14 char current_anim_frame[128]; 15 16 char state[32]; 17 char dir[16]; 18 19 int cycle = 0; 20 int x = 0; 21 int y = 0; 22 int w = 0; 23 int h = 0; 24 srand(time(NULL)); 25 26 sprintf(def_file_name, "data/sprites/%s.spr", sprite_name); 27 sprintf(image_file_name, "data/sprites/%s.png", sprite_name); 28 sprintf(sprite->name, "%s", sprite_name); 29 sprintf(sprite->current.state, "%s", sprite_states[0]); 30 sprintf(sprite->current.dir, "%s", sprite_directions[0]); 31 32 // for convenience we use the config reader to parse the sprite definition file 33 ALLEGRO_CONFIG* sprite_definition = al_load_config_file(def_file_name); 34 35 36 37 for (int i = 0; 1; i++) { 38 39 if (sprite_states[i] == NULL) { break; } 40 sprintf(current_anim_state, "%s", sprite_states[i]); 41 42 #ifdef DEBUG 43 printf("i | %s\n", sprite_states[i]); 44 #endif 45 46 for (int j = 0; 1; j++) { 47 // The center loop simply iterates through the directions with the 48 // innermost loop. 49 if (sprite_directions[j] == NULL) { break; } 50 for (k = 1; 1; k++) { 51 // In the innermost loop, we're stepping through a single state/direction 52 // pair and extracting the bitmap coordinates and dimensions and feeding them 53 // into a struct which we store in the animation. 54 // 55 // Let's get the current animation frame title from our lookups... 56 sprintf(current_anim_frame, "%s_%s_%i", sprite_states[i], sprite_directions[j], k); 57 58 // If it's missing any values, we can't render it properly. In this case, we break 59 // the current loop because chances are we've already read in all of this animation's 60 // frames. 61 if ( NULL == al_get_config_value(sprite_definition, current_anim_frame, "x") || 62 NULL == al_get_config_value(sprite_definition, current_anim_frame, "y") || 63 NULL == al_get_config_value(sprite_definition, current_anim_frame, "w") || 64 NULL == al_get_config_value(sprite_definition, current_anim_frame, "h") 65 ) { break; } 66 67 sprintf(state, "%s", sprite_states[i]); 68 sprintf(dir, "%s", sprite_directions[j]); 69 x = atoi(al_get_config_value(sprite_definition, current_anim_frame, "x")); 70 y = atoi(al_get_config_value(sprite_definition, current_anim_frame, "y")); 71 w = atoi(al_get_config_value(sprite_definition, current_anim_frame, "w")); 72 h = atoi(al_get_config_value(sprite_definition, current_anim_frame, "h")); 73 74 ANNE_SPRITE_FRAME thisFrame; 75 sprintf(thisFrame.state, "%s", sprite_states[i]); 76 sprintf(thisFrame.dir, "%s", dir); 77 thisFrame.x = x; 78 printf("> x: %i | ", x); 79 thisFrame.y = y; 80 thisFrame.w = w; 81 thisFrame.h = h; 82 thisFrame.cycle = k; 83 84 85 anim->frame = g_list_append(anim->frame, &thisFrame); // Prepend would be more performant but performance is irrelevant for sprite loading :) 86 87 #ifdef DEBUG 88 printf("j | state %s:%s, dir %s:%s | x %i:%i, y %i:%i, h %i:%i, w %i:%i, k %i:%i. | frame: %s\n", 89 ((ANNE_SPRITE_FRAME *) anim->frame->data)->state,state, 90 ((ANNE_SPRITE_FRAME *) anim->frame->data)->dir,dir, 91 ((ANNE_SPRITE_FRAME *) anim->frame->data)->x,x, 92 ((ANNE_SPRITE_FRAME *) anim->frame->data)->y,y, 93 ((ANNE_SPRITE_FRAME *) anim->frame->data)->h,h, 94 ((ANNE_SPRITE_FRAME *) anim->frame->data)->w,w, 95 ((ANNE_SPRITE_FRAME *) anim->frame->data)->cycle,k, 96 current_anim_frame); 97 #endif 98 } 99 } 100 }

This appears to work correctly, and the debug printout at the end would appear to indicate that all the data is being stored correctly. However, when I try to read that data out later:

printf("frame | state: %s  dir: %s  cycle: %i  x: %i  y: %i  w: %i  h: %i\n",
      ((ANNE_SPRITE_FRAME *) search->data)->state,
      ((ANNE_SPRITE_FRAME *) search->data)->dir,
      ((ANNE_SPRITE_FRAME *) search->data)->cycle,
      ((ANNE_SPRITE_FRAME *) search->data)->x,
      ((ANNE_SPRITE_FRAME *) search->data)->y,
      ((ANNE_SPRITE_FRAME *) search->data)->w,
      ((ANNE_SPRITE_FRAME *) search->data)->h
);

I get garbage data like this:

#SelectExpand
1frame | state: (Redacted) dir: right cycle: -3446<form action id="post_form" name="post_form" method="post" onsubmit="return validate_post()​;​"><form xmlns="http://www.w3.org/1999/xhtml" action="" id="post_form" name="post_form" method="post" onsubmit="return validate_post();"> 2<input type="hidden" name="forum_id" value="" /> 3 4<table id="table-add-post-inner" width="800"> 5<tbody> 6 <tr id="tr-topic"> 7 <td class="label">Topic:</td> 8 <td> 9<input name="topic" type="hidden" value="Storing and reading sprite data from a linked list" />Storing and reading sprite data from a linked list </td> 10 </tr> 11 <tr id="tr-icon"> 12 <td class="label">Icon:</td> 13 <td> 14 <div> 15 <input type="radio" name="icon" class="checkbox" tabindex="1" value="default.gif" checked="checked" /><img src="/forums/icons/default.gif" width="16" height="16" alt="default" /> 16 <input type="radio" name="icon" class="checkbox" tabindex="1" value="c.gif" /><img src="/forums/icons/c.gif" width="16" height="16" alt="c" /> 17 <input type="radio" name="icon" class="checkbox" tabindex="1" value="cpp.gif" /><img src="/forums/icons/cpp.gif" width="16" height="16" alt="c++" /> 18 <input type="radio" name="icon" class="checkbox" tabindex="1" value="display.gif" /><img src="/forums/icons/display.gif" width="16" height="16" alt="display" /> 19 <input type="radio" name="icon" class="checkbox" tabindex="1" value="joystick.gif" /><img src="/forums/icons/joystick.gif" width="16" height="16" alt="joystick" /> 20 <input type="radio" name="icon" class="checkbox" tabindex="1" value="mad.gif" /><img src="/forums/icons/mad.gif" width="16" height="16" alt="mad" /> 21 <input type="radio" name="icon" class="checkbox" tabindex="1" value="network.gif" /><img src="/forums/icons/network.gif" width="16" height="16" alt="network" /> 22 <input type="radio" name="icon" class="checkbox" tabindex="1" value="note.gif" /><img src="/forums/icons/note.gif" width="16" height="16" alt="midi" /> 23 <input type="radio" name="icon" class="checkbox" tabindex="1" value="question.gif" /><img src="/forums/icons/question.gif" width="16" height="16" alt="question" /> 24 <input type="radio" name="icon" class="checkbox" tabindex="1" value="smile.gif" /><img src="/forums/icons/smile.gif" width="16" height="16" alt="smiley" /> 25 <input type="radio" name="icon" class="checkbox" tabindex="1" value="speaker.gif" /><img src="/forums/icons/speaker.gif" width="16" height="16" alt="sound" /> 26 </div> 27 </td> 28 </tr> 29 <tr id="tr-message"> 30 <td width="100" valign="top" class="label">Message:</td> 31 <td width="700"> 32 33<div id="post-988957-upload-button" class="upload-button" draggable="true" style="position: relative; overflow: hidden;">Drop Attachments Here<input multiple="multiple" style="opacity: 0; position: absolute; top: 0; left: 0px; width: 500px; height: 32px; background: transparent; border: none;" type="file" /></div> 34 35<div id="post-988957-files" class="upload-files"> 36 37 38</div> 39 40<input id="btnPost" name="submit_button" tabindex="5" type="submit" class="button" value="Update Post" /> 41 42<div class="mockup-box2"><div class="toolbar"><span class="button"><img src="/theme/default/icon/preview.png" /><span> Preview</span></span><span class="button"><img src="/theme/default/icon/help.png" /><span> Formatting Help</span></span></div><div class="container"><div class="compose" style="visibility: visible;"><textarea id="mub-body" class="mockup-box2" name="body" tabindex="2" rows="20" cols="50" style="height: 348px;"></textarea></div><div class="preview" style="visibility: hidden; display: block;"></div><input type="hidden" name="recovery_token" value="post-988957" /><input type="hidden" name="attachments" value="" /></div></div></td></tr></tbody></table></form>07570 x: 0 y: -532077906 w: -1434671328 h: 32604 43frame | state: (Redacted) dir: right cycle: -344607570 x: 0 y: -532077906 w: -1434671328 h: 32604 44frame | state: (Redacted) dir: right cycle: -344607570 x: 0 y: -532077906 w: -1434671328 h: 32604

My sanity check printf confirms that there are 17 frames in the frameset, as I expected, but I've been trying to confirm whether the junk output is due to a mistake saving the data or a mistake reading it since last night. If anyone could take a look, I'd really appreciate it. :)

I would like to note that I'm aware of hashmaps, and in particular I'm considering rewriting in order to take advantage of Sparsehash. There are a couple reasons why I'm putting that off.

Most importantly, I'm concerned about adding more code I don't understand, to replace a problem I've failed to diagnose. The flexibility of a linked list is also attractive and given the relatively small sets involved the performance penalty of iterating through them should be minimal.

Edit again: It appears I'm an idiot who allocated the variables on the stack.

Thread #613149. Printed from Allegro.cc