#include "parser.h" #ifndef PARSER_H #define PARSER_H #include "data_ini.h" #ifndef DATA_INI_H #define DATA_INI_H #include // Original game level size is fixed to 1664x160. We want to display all the game with a x2 zoom #define LEVEL_WIDTH 1664*2 #define LEVEL_HEIGHT 160*2 #define MAX_NAMELEN 64 #define MAX_PARTICLE_COLORS 16 #define MAX_MUSICS_COUNT 32 #define MAX_DIFFICULTY_COUNT 8 #define MAX_OBJECTS_COUNT 256 #define MAX_TERRAINS_COUNT 1024 #define MAX_STEELS_COUNT 256 //////////////////////// LEVEL INI FILES //////////////////////// // Item should be an object, terrain or steel struct levelItem { int id, xpos, ypos; // Common to all types (but no id for steel) int paintMode, ud; // Specific to objects int modifier; // Specific to terrains int width, height; // Specific to steels }; struct levelIni { int releaseRate, numLemmings, numToRescue, timeLimit; int numClimbers, numFloaters, numBlockers, numBombers; int numBuilders, numBashers, numMiners, numDiggers; int xPos; // Initial camera position on level char style[MAX_NAMELEN], name[MAX_NAMELEN]; int superLemming; int objectCount, terrainCount, steelCount; struct levelItem objects[MAX_OBJECTS_COUNT]; struct levelItem terrains[MAX_OBJECTS_COUNT]; struct levelItem steels[MAX_OBJECTS_COUNT]; }; //////////////////////// LEVELPACK INI FILES //////////////////////// struct levelPackIni { char name[MAX_NAMELEN]; int maxFallDistance; char codeSeed[MAX_NAMELEN]; int musicCount, levelDifficultyCount; char music[MAX_MUSICS_COUNT][MAX_NAMELEN]; char levelDifficulty[MAX_DIFFICULTY_COUNT][MAX_NAMELEN]; }; //////////////////////// STYLE INI FILES //////////////////////// struct styleIni { char name[MAX_NAMELEN]; uint32_t bgColor, debrisColor; int tiles, particleColorCount; uint32_t particleColor[MAX_PARTICLE_COLORS]; int objectCount; int frames[MAX_OBJECTS_COUNT]; int anim[MAX_OBJECTS_COUNT]; int type[MAX_OBJECTS_COUNT]; int sound[MAX_OBJECTS_COUNT]; }; /*////////////////////// MISC/LEMMING.INI FILE //////////////////////// struct lemmingAnim { int haveMask, haveImask; int lemmFrames, lemmDirs, lemmAnimType; int maskFrames, maskDirs, maskStartFrame; int imaskFrames, imaskDirs; int xPos, yPos, posFlags; }; struct miscIni { int lemmingAnimCount; struct lemmingAnim *lemmingAnims; }; */ //////////////////////// GLOBAL INI FILES //////////////////////// typedef struct { struct styleIni style; struct levelIni level; struct levelPackIni levelPack; //struct miscIni misc; } gameIni_t; #endif /*DATA_INI_H*/ //#define OUT_OF_BOUNDS "out of bounds" //#define CANNOT_BE_NEGATIVE "cannot be negative" //#define BAD_VALUE "bad value" //#define MAX_LEMMTYPES_COUNT 16 //#define MAX_OBJECT_FRAMES 64 //#define MAX_NUMLEMMINGS 100 //#define STEEL_MAX_WIDTH 256 //#define STEEL_MAX_HEIGHT 256 #define ERR_UNKNOWN_KEY 1 #define ERR_NOT_YET_IMPLEMENTED 2 #define ERR_KEY_OUT_OF_RANGE 3 #define ERR_VAL_OUT_OF_RANGE 4 #define ERR_STRING_TOO_LONG 5 enum ini_type { ini_style, ini_levelpack, ini_level }; int loadIni(enum ini_type type, const char *filepath, gameIni_t *ini); #endif /*PARSER_H*/ #include "minIni.h" #define _MATCH_SIMPLETYPE(keysymbol,parsefunc) \ do { \ if (SDL_strcasecmp(key,#keysymbol)==0) { \ data->keysymbol = parsefunc(value); \ return 1; \ } \ } while(0) #define MATCH_INT(keysymbol) _MATCH_SIMPLETYPE(keysymbol,SDL_atoi) #define MATCH_HEXCOLOR(keysymbol) _MATCH_SIMPLETYPE(keysymbol,hextext2rgb) #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; 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) { res=state.error_code; //FIXME : logging printf("Parse error %i for this key/value pair : %s\n", state.error_code, state.error_line_content); } else { res=0; } return res; } uint32_t 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_t)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); MATCH_INT_ARRAY(frames,MAX_OBJECTS_COUNT,data->objectCount,data->frames,0,32); 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); int *err= &(((parserState_t*) userData)->error_code); int i; MATCH_STRING(name,MAX_NAMELEN); MATCH_INT(maxFallDistance); 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 *err=ERR_NOT_YET_IMPLEMENTED; return 0; } } // 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); MATCH_INT(releaseRate); MATCH_INT(numLemmings); MATCH_INT(numToRescue); MATCH_INT(timeLimit); MATCH_INT(numClimbers); MATCH_INT(numFloaters); MATCH_INT(numBombers); MATCH_INT(numBlockers); MATCH_INT(numBuilders); MATCH_INT(numBashers); MATCH_INT(numMiners); MATCH_INT(numDiggers); MATCH_INT(xPos); MATCH_STRING(style,MAX_NAMELEN); MATCH_STRING(name,MAX_NAMELEN); // TODO : object/terrain/steel à la main (car struct, et complications) // No match is an error (unkown key) *err=ERR_UNKNOWN_KEY; return 0; }