#include "parser.h" #include "minIni.h" #define MATCH_HEXCOLOR(keysymbol) \ do { \ if (SDL_strcasecmp(key,#keysymbol)==0) { \ data->keysymbol = hextext2rgb(value); \ return 1; \ } \ } while(0) #define MATCH_INT(keysymbol,maxval) \ do { \ if (SDL_strcasecmp(key,#keysymbol)==0) { \ int v = SDL_atoi(value); \ if ( v > maxval ) { *err=ERR_VAL_OUT_OF_RANGE; return 0; } \ data->keysymbol = v; \ return 1; \ } \ } while(0) #define _SAFE_STRING_COPY(dst,src,maxlen) \ do { \ int srclen = SDL_strlcpy(dst,src,maxlen-1); \ if (srclen > maxlen-1 ) { \ *err=ERR_STRING_TOO_LONG; return 0; \ } \ } while(0) #define MATCH_STRING(keysymbol,valmaxlen) \ do { \ if (SDL_strcasecmp(key,#keysymbol)==0) { \ _SAFE_STRING_COPY(data->keysymbol,value,valmaxlen); \ return 1; \ } \ } while(0) #define MATCH_STRING_ARRAY(keysymbol,keyidxmax,countvar,arrayvar,valmaxlen) \ do { \ int keysymlen=sizeof(#keysymbol)-1; \ if (SDL_strncasecmp(key,#keysymbol,keysymlen)==0 && key[keysymlen]=='_') { \ int k = atoi(key+keysymlen+1); \ if (k<0 || k>keyidxmax) { *err=ERR_KEY_OUT_OF_RANGE; return 0; } \ if (countvar < k+1) countvar = k+1; \ _SAFE_STRING_COPY(arrayvar[k],value,valmaxlen); \ return 1; \ } \ } while(0) #define MATCH_INT_ARRAY(keysymbol,keyidxmax,countvar,arrayvar,minval,maxval) \ do { \ int keysymlen=sizeof(#keysymbol)-1; \ if (SDL_strncasecmp(key,#keysymbol,keysymlen)==0 && key[keysymlen]=='_') { \ int k = atoi(key+keysymlen+1); \ if ( k<0 || k>keyidxmax) { *err=ERR_KEY_OUT_OF_RANGE; return 0; } \ if (countvar < k+1) countvar = k+1; \ int v = atoi(value); \ if ( vmaxval) { *err=ERR_VAL_OUT_OF_RANGE; return 0; } \ arrayvar[k] = v; \ return 1; \ } \ } while(0) int callback_ini_style (const mTCHAR *section, const mTCHAR *key, const mTCHAR *value, const void *userData); int callback_ini_levelpack(const mTCHAR *section, const mTCHAR *key, const mTCHAR *value, const void *userData); int callback_ini_level (const mTCHAR *section, const mTCHAR *key, const mTCHAR *value, const void *userData); // struct for void *UserData pointer (app context passed to each callback func) #define MAX_LINELEN 256 typedef struct { gameIni_t *ini; int error_code; char error_line_content[MAX_LINELEN]; } parserState_t; const char error_strings[][32] = { "ERR_NO_ERROR", "ERR_UNKNOWN_KEY", "ERR_NOT_YET_IMPLEMENTED", "ERR_KEY_OUT_OF_RANGE", "ERR_VAL_OUT_OF_RANGE", "ERR_STRING_TOO_LONG", "ERR_BAD_FIELDS" }; int loadIni(enum ini_type type, const char *filepath, gameIni_t *ini) { int res; parserState_t state; const void *UserData=(void *)&state; (void) SDL_memset(&state,0,sizeof(parserState_t)); state.ini=ini; switch (type) { case ini_style: (void) SDL_memset(&ini->style,0,sizeof(struct styleIni)); res=ini_browse(callback_ini_style, UserData, filepath); break; case ini_levelpack: (void) SDL_memset(&ini->levelPack,0,sizeof(struct levelPackIni)); res=ini_browse(callback_ini_levelpack, UserData, filepath); break; case ini_level: (void) SDL_memset(&ini->level,0,sizeof(struct levelIni)); res=ini_browse(callback_ini_level, UserData, filepath); break; default: return -1; //Should be unreachable } if (res!=1 && state.error_code == 0) { //FIXME : logging res=-1; printf("Unknown error on '%s' : can't read file ?\n", filepath); } else if (state.error_code != 0 ) { res=state.error_code; printf("Parse error %s in '%s'. key/value : %s\n", error_strings[state.error_code], filepath, state.error_line_content); } else { res=0; } return res; } Uint32 hextext2rgb(const char str[]) { int res; unsigned int val; res=SDL_sscanf(str,"0x%x",&val); // Default value to opaque white. Should be visible on screen if problem with that return (res!=1)?0xffffffff:(Uint32)val; } int callback_ini_style(const mTCHAR *section, const mTCHAR *key, const mTCHAR *value, const void *userData) { struct styleIni *data = &( ((parserState_t*) userData)->ini->style ); int *err= &(((parserState_t*) userData)->error_code); SDL_snprintf(((parserState_t*) userData)->error_line_content, MAX_LINELEN, "%s => %s", key, value); // Special case for particleColor. No macros here if (SDL_strcasecmp(key,"particleColor")==0) { char *wordBoundary; int i=0; while ( SDL_strlen(value)>0 && (wordBoundary=SDL_strchr(value,','))!=NULL && iparticleColor[i++]=hextext2rgb(value); value = wordBoundary+1; } // Parse the last one data->particleColor[i++]=hextext2rgb(value); data->particleColorCount=i; return 1; } // Common cases, extensive use of macros MATCH_HEXCOLOR(bgColor); MATCH_HEXCOLOR(debrisColor); MATCH_INT(tiles,MAX_TILES_COUNT); MATCH_INT_ARRAY(frames,MAX_OBJECTS_COUNT,data->objectCount,data->frames,0,64); MATCH_INT_ARRAY(anim ,MAX_OBJECTS_COUNT,data->objectCount,data->anim ,0,3); MATCH_INT_ARRAY(type ,MAX_OBJECTS_COUNT,data->objectCount,data->type ,0,32); MATCH_INT_ARRAY(sound ,MAX_OBJECTS_COUNT,data->objectCount,data->sound ,-1,32); // No match is an error (unkown key) *err=ERR_UNKNOWN_KEY; return 0; } int callback_ini_levelpack(const mTCHAR *section, const mTCHAR *key, const mTCHAR *value, const void *userData) { struct levelPackIni *data = &(((parserState_t*) userData)->ini->levelPack); SDL_snprintf(((parserState_t*) userData)->error_line_content, MAX_LINELEN, "%s => %s", key, value); int *err= &(((parserState_t*) userData)->error_code); int i; MATCH_STRING(name,MAX_NAMELEN); MATCH_INT(maxFallDistance,LEVEL_HEIGHT); MATCH_STRING(codeSeed,MAX_NAMELEN); MATCH_STRING_ARRAY(music,MAX_MUSICS_COUNT,data->musicCount,data->music,MAX_NAMELEN); MATCH_STRING_ARRAY(level,MAX_DIFFICULTY_COUNT,data->levelDifficultyCount,data->levelDifficulty,MAX_NAMELEN); // Parse all entries like _ (ex: tame_0, havoc_5) for (i=0;i < data->levelDifficultyCount; i++ ) { char *diffname = data->levelDifficulty[i]; int len=SDL_strlen(diffname); if ( (len > 0) && (SDL_strncasecmp(key,diffname,len) == 0) && (key[len] == '_') ) { //FIXME : checher position de .ini : copier la chaine avant + 4 //FIXME : chercher , et sscandf de la suite //FIXME : passe sous silence : *err=ERR_NOT_YET_IMPLEMENTED; return 0; return 1; } } // No match is an error (unkown key) *err=ERR_UNKNOWN_KEY; return 0; } int callback_ini_level(const mTCHAR *section, const mTCHAR *key, const mTCHAR *value, const void *userData) { struct levelIni *data = &(((parserState_t*) userData)->ini->level); int *err= &(((parserState_t*) userData)->error_code); SDL_snprintf(((parserState_t*) userData)->error_line_content, MAX_LINELEN, "%s => %s", key, value); MATCH_INT(releaseRate,100); MATCH_INT(numLemmings,100); MATCH_INT(numToRescue,100); MATCH_INT(timeLimit,100); MATCH_INT(numClimbers,100); MATCH_INT(numFloaters,100); MATCH_INT(numBombers,100); MATCH_INT(numBlockers,100); MATCH_INT(numBuilders,100); MATCH_INT(numBashers,100); MATCH_INT(numMiners,100); MATCH_INT(numDiggers,100); MATCH_INT(xPos,LEVEL_WIDTH); MATCH_STRING(style,MAX_NAMELEN); MATCH_STRING(name,MAX_NAMELEN); if (SDL_strncasecmp(key,"object",6)==0 && key[6]=='_') { return 1; } if (SDL_strncasecmp(key,"steel",5)==0 && key[5]=='_') { return 1; } if (SDL_strncasecmp(key,"terrain",7)==0 && key[7]=='_') { char *wordBoundary; int v; int k = atoi(key+8); if ( k<0 || k>MAX_TERRAINS_COUNT) { *err=ERR_KEY_OUT_OF_RANGE; return 0; } if (data->terrainCount < k+1) data->terrainCount = k+1; if ( (wordBoundary=SDL_strchr(value,','))==NULL ) { *err=ERR_BAD_FIELDS; return 0; } *wordBoundary='\0'; v = atoi(value); if ( v<0 || v>MAX_OBJECTS_COUNT) { *err=ERR_VAL_OUT_OF_RANGE; return 0; } data->terrains[k].id = v; value = wordBoundary+1; if ( (wordBoundary=SDL_strchr(value,','))==NULL ) { *err=ERR_BAD_FIELDS; return 0; } *wordBoundary='\0'; v = atoi(value); if ( v < -LEVEL_WIDTH || v > LEVEL_WIDTH ) { *err=ERR_VAL_OUT_OF_RANGE; return 0; } data->terrains[k].xpos = v; value = wordBoundary+1; if ( (wordBoundary=SDL_strchr(value,','))==NULL ) { *err=ERR_BAD_FIELDS; return 0; } *wordBoundary='\0'; v = atoi(value); if ( v < -LEVEL_HEIGHT || v > LEVEL_HEIGHT ) { *err=ERR_VAL_OUT_OF_RANGE; return 0; } data->terrains[k].ypos = v; value = wordBoundary+1; v = atoi(value); if ( v<0 || v>16) { *err=ERR_VAL_OUT_OF_RANGE; return 0; } data->terrains[k].modifier = v; return 1; } //MATCH_INT_ARRAY_QUAD(terrain,MAX_OBJECTS_COUNT,data->terrainCount,data->terrains,id,xpos,ypos,modifier); //MATCH_INT_ARRAY_QUAD(terrain,MAX_STEELS_COUNT,data->terrainCount,data->terrains,id,xpos,ypos,modifier); // TODO : object/terrain/steel à la main (car struct, et complications) // No match is an error (unkown key) *err=ERR_UNKNOWN_KEY; return 0; }