#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include static surface_t *screen = NULL; typedef struct hsearch_data hashtable_t; typedef struct _ENTRY { unsigned int used; ENTRY entry; } _ENTRY; hashtable_t * hashtable_new( void ) { hashtable_t *hash = calloc( 1, sizeof(*hash) ); hcreate_r( 20, hash ); return hash; } void hashtable_free( hashtable_t *hash ) { _ENTRY *p, *end; end = hash->table + hash->size; for ( p=hash->table; p < end; p++ ) { if ( p->entry.key ) free( p->entry.key ); if ( p->entry.data ) free( p->entry.data ); } hdestroy_r( hash ); free( hash ); } int consume_line( FILE *fd ) { int c; while ( ( ( c = fgetc( fd ) ) != EOF ) && ( c != '\n' ) ); if ( c == EOF ) { fprintf( stderr, "Error: missing '\\n'\n" ); return 0; } else return 0; } char * strstrip( char *s ) { int len = strlen( s ); char *d = calloc( len + 1, sizeof( char ) ); char *a, *b; for ( a=s, b=d; *a != 0; a++ ) { if ( isalnum( *a ) || ( *a == '.' ) || ( *a == ',' ) || ( *a == '(' ) || ( *a == ')' ) ) { *b = *a; b++; } } *b = 0; return d; } int parse_values( FILE *fd, hashtable_t *hash ) { int c, lastline=0; char buf[ 4096 ]; /* find '{' */ while ( ( ( c = fgetc( fd ) ) != EOF ) && ( c != '{' ) ); while ( ( ! feof( fd ) ) && ( fgets( buf, 4095, fd ) != NULL ) && ( ! lastline ) ) { if ( index( buf, '}' ) != NULL ) lastline=1; char *equalsign = index( buf, '=' ); if ( equalsign == NULL ) continue; else if ( equalsign == buf ) return -1; *equalsign = 0; equalsign ++; char *key = strstrip( buf ); char *value = strstrip( equalsign ); ENTRY e = { key, value }; ENTRY *ep; hsearch_r( e, ENTER, &ep, hash ); if ( ep == NULL ) { free( key ); free( value ); return -1; } } if ( lastline ) return 0; else return -1; } int get_int_value( hashtable_t *hash, char *key, int *ret ) { ENTRY e, *ep; e.key = key; hsearch_r( e, FIND, &ep, hash ); if ( ep == NULL ) { fprintf( stderr, "Error: missing property \"%s\"\n", key ); return -1; } *ret = atoi( (char *)ep->data ); return 0; } int get_hex_value( hashtable_t *hash, char *key, int *ret ) { ENTRY e, *ep; e.key = key; hsearch_r( e, FIND, &ep, hash ); if ( ep == NULL ) { fprintf( stderr, "Error: missing property \"%s\"\n", key ); return -1; } *ret = strtol( (char *)ep->data, NULL, 16 ); return 0; } int get_float_value( hashtable_t *hash, char *key, float *ret ) { ENTRY e, *ep; e.key = key; hsearch_r( e, FIND, &ep, hash ); if ( ep == NULL ) { fprintf( stderr, "Error: missing property \"%s\"\n", key ); return -1; } *ret = atof( (char *)ep->data ); return 0; } int get_point_value( hashtable_t *hash, char *key, coord_t *ret ) { ENTRY e, *ep; e.key = key; hsearch_r( e, FIND, &ep, hash ); if ( ep == NULL ) { fprintf( stderr, "Error: missing property \"%s\"\n", key ); return -1; } char *value = (char *)ep->data; unsigned x, y; if ( sscanf( value, "(%u,%u)", &x, &y ) != 2 ) { fprintf( stderr, "Error: point \"%s\"in invalid format!\n", key ); return -1; } SET_COORD( (*ret), x, y ); return 0; } int get_opt_drawopts( hashtable_t *hash, char *key, int *ret ) { ENTRY e, *ep; *ret = DRAW_NORMAL; e.key = key; hsearch_r( e, FIND, &ep, hash ); if ( ep == NULL ) { fprintf( stderr, "Warning: missing property \"%s\"\n", key ); return 0; } char *value = (char *)ep->data; char *a=value, *b=value, *end = value + strlen( value ); char buf[ 512 ] = ""; while ( ( a < end ) && ( *a != 0 ) ) { b = index( b, ',' ); if ( b != NULL ) *b = '\0'; if ( sscanf( a, "%511s", buf ) != 1 ) break; buf[ 511 ] = 0; if ( strcasecmp( buf, "normal" ) == 0 ) *ret |= DRAW_NORMAL; else if ( strcasecmp( buf, "fill" ) == 0 ) *ret |= DRAW_FILL; else if ( strcasecmp( buf, "antialias" ) == 0 ) *ret |= DRAW_ANTIALIAS; else fprintf( stderr, "Warning: invalid draw option: \"%s\".\n", buf ); if ( b == NULL ) break; a = b + 1; } return 0; } int set_object_values( object_t *object, hashtable_t *hash ) { int ret, i; float f; ret = get_int_value( hash, "depth", &i ); if ( ret != 0 ) return ret; else object->depth = i; ret = get_hex_value( hash, "color", &i ); if ( ret != 0 ) return ret; else COLOR_FROM_ARGB( object->color, i ); ret = get_float_value( hash, "transp", &f ); if ( ret != 0 ) return ret; else object->transp = f; ret = get_opt_drawopts( hash, "draw", &i ); if ( ret != 0 ) return ret; else object->draw_opts = i; return 0; } int set_line_values( line_t *line, hashtable_t *hash ) { return get_point_value( hash, "begin", &(line->r.p0) ) || get_point_value( hash, "end", &(line->r.p1) ); } int create_line( FILE *fd, scene_t *scene ) { line_t *line = line_new(); hashtable_t *hash = hashtable_new(); int ret=0; ret = parse_values( fd, hash ); if ( ret == 0 ) ret = set_object_values( OBJECT( line ), hash ); if ( ret == 0 ) ret = set_line_values( line, hash ); if ( ret == 0 ) scene_add_object( scene, OBJECT( line ) ); hashtable_free( hash ); return 0; } int set_circle_values( circle_t *circle, hashtable_t *hash ) { int r, i; r = get_int_value( hash, "radius", &i ); if ( r != 0 ) return r; else circle->radius = i; r = get_point_value( hash, "center", &(circle->center) ); if ( r != 0 ) return r; return 0; } int create_circle( FILE *fd, scene_t *scene ) { circle_t *circle = circle_new(); hashtable_t *hash = hashtable_new(); int ret=0; ret = parse_values( fd, hash ); if ( ret == 0 ) ret = set_object_values( OBJECT( circle ), hash ); if ( ret == 0 ) ret = set_circle_values( circle, hash ); if ( ret == 0 ) scene_add_object( scene, OBJECT( circle ) ); hashtable_free( hash ); return 0; } int set_ellipse_values( ellipse_t *ellipse, hashtable_t *hash ) { int r, i; r = get_int_value( hash, "radiusx", &i ); if ( r != 0 ) return r; else ellipse->radiusx = i; r = get_int_value( hash, "radiusy", &i ); if ( r != 0 ) return r; else ellipse->radiusy = i; r = get_point_value( hash, "center", &(ellipse->center) ); if ( r != 0 ) return r; return 0; } int create_ellipse( FILE *fd, scene_t *scene ) { ellipse_t *ellipse = ellipse_new(); hashtable_t *hash = hashtable_new(); int ret=0; ret = parse_values( fd, hash ); if ( ret == 0 ) ret = set_object_values( OBJECT( ellipse ), hash ); if ( ret == 0 ) ret = set_ellipse_values( ellipse, hash ); if ( ret == 0 ) scene_add_object( scene, OBJECT( ellipse ) ); hashtable_free( hash ); return 0; } int set_polygon_values( polygon_t *polygon, hashtable_t *hash ) { ENTRY e, *ep; e.key = "vertexes"; hsearch_r( e, FIND, &ep, hash ); if ( ep == NULL ) { fprintf( stderr, "Error: missing property \"vertexes\"\n" ); return -1; } char *value = (char *)ep->data; unsigned x, y; while ( sscanf( value, "(%u,%u)", &x, &y ) == 2 ) { polygon_add_vertex( polygon, x, y ); value = index( value, ')' ); if ( value == NULL ) break; else value ++; } return 0; } int create_polygon( FILE *fd, scene_t *scene ) { polygon_t *polygon = polygon_new(); hashtable_t *hash = hashtable_new(); int ret=0; ret = parse_values( fd, hash ); if ( ret == 0 ) ret = set_object_values( OBJECT( polygon ), hash ); if ( ret == 0 ) ret = set_polygon_values( polygon, hash ); if ( ret == 0 ) scene_add_object( scene, OBJECT( polygon ) ); hashtable_free( hash ); return 0; } int screen_new( uint16_t x, uint16_t y ) { screen = surface_new( x, y ); return visual_init( x, y ); } int screen_free( void ) { if ( screen != NULL ) { visual_quit(); surface_free( screen ); screen = NULL; } return 0; } int scene_size( FILE *fd, scene_t *scene ) { char buf[ 512 ] = ""; if ( screen != NULL ) { fprintf( stderr, "Warning: scene size already set! Ignored.\n" ); return 0; } if ( fgets( buf, 511, fd ) == NULL ) return -1; char *equalsign = index( buf, '=' ); if ( equalsign == NULL ) { fprintf( stderr, "Error: wrong \"scene_size\" specification.\n" ); return -1; } equalsign++; char *value = strstrip( equalsign ); unsigned x, y; int r = sscanf( value, "%u,%u", &x, &y ); free( value ); if ( r != 2 ) { fprintf( stderr, "Error: wrong \"scene_size\" format!\n" ); return -1; } return screen_new( x, y ); } int scene_show( FILE *fd, scene_t *scene ) { if ( consume_line( fd ) != 0 ) return -1; if ( screen == NULL ) { fprintf( stderr, "Error: You should set \"scene_size\" before " "\"scene_show\"!\n" ); return -1; } scene_render( scene, screen ); coord_t pos = { 0, 0 }; visual_display( screen, pos ); visual_update(); return 0; } int scene_clear( FILE *fd, scene_t *scene ) { if ( consume_line( fd ) != 0 ) return -1; if ( screen == NULL ) { fprintf( stderr, "Error: You should set \"scene_size\" before " "\"scene_show\"!\n" ); return -1; } scene_free_objects( scene ); surface_clear( screen ); return visual_clear(); } int waitkey( FILE *fd, scene_t *scene ) { if ( consume_line( fd ) != 0 ) return -1; visual_waitkey(); return 0; } static struct parse_map { char *string; int (*function)( FILE *fd, scene_t *scene ); } parse_map[] = { { .string = "scene_size", .function = scene_size, }, { .string = "scene_show", .function = scene_show, }, { .string = "scene_clear", .function = scene_clear, }, { .string = "waitkey", .function = waitkey, }, { .string = "line", .function = create_line, }, { .string = "circle", .function = create_circle, }, { .string = "ellipse", .function = create_ellipse, }, { .string = "polygon", .function = create_polygon, }, { NULL, NULL }, /* keep tail! */ }; int __parse_file( FILE *fd, scene_t *scene ) { char buf[ 1024 ] = ""; while ( ! feof( fd ) ) { if ( fscanf( fd, "%1023s", buf ) == EOF ) return 0; struct parse_map *p; for ( p=parse_map; p->string != NULL; p++ ) { if ( strcmp( buf, p->string ) == 0 ) { int ret = p->function( fd, scene ); if ( ret != 0 ) return ret; } } } return 0; } int parse_file( FILE *fd ) { if ( fd == NULL ) return -1; scene_t *scene = scene_new(); int ret = __parse_file( fd, scene ); scene_free_objects( scene ); scene_free( scene ); screen_free(); return ret; } void usage( const char *progname ) { fprintf( stderr, "Usage:\n" "\t%s scene-files\n\n", progname ); } int main( int argc, char *argv[] ) { FILE *fd; if ( argc > 1 ) { int i; for ( i=1; i < argc; i++ ) { char *fname = argv[ i ]; if ( ( strcmp( fname, "--help" ) == 0 ) || ( strcmp( fname, "-help" ) == 0 ) || ( strcmp( fname, "-h" ) == 0 ) ) { usage( argv[ 0 ] ); return 0; } else { fd = fopen( fname, "r" ); if ( fd == NULL ) perror( "Could not open file" ); else { int r = parse_file( fd ); if ( r != 0 ) { fprintf( stderr, "Error parsing file: \"%s\"\n", fname ); return r; } fclose( fd ); } } } } else return parse_file( stdin ); return 0; }