#include #include #include "../config.h" void cfg_seterr( CFG_PARSER* p, const char* fmt, ... ) { va_list args; va_start( args, fmt ); vsnprintf( p->err, sizeof(p->err), fmt, args ); p->iserr = 1; va_end( args ); } inline U8 is_whitespace( char c ) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } inline void trim_whitespace( char* buf ) { if (!buf || !*buf) return; U32 start = 0; while( buf[start] && is_whitespace( buf[start] ) ) start++; U32 end = strlen( buf ); while( end > start && is_whitespace( buf[end - 1] ) ) end--; U32 len = end - start; memmove( buf, buf + start, len ); buf[len] = '\0'; } inline void init_cfg_node( CFG_NODE* node, const char* name, CFG_NODE* parent, U8 type ) { node->name = name; node->parent = parent; if( parent ) ( (CFG_SECTION*)parent )->children.push( node ); node->type = type; } CFG_SECTION* cfg_section_new( const char* name, CFG_NODE* parent ) { CFG_SECTION* section = new CFG_SECTION; init_cfg_node( (CFG_NODE*)section, name, parent, CFGT_SECTION ); return section; } CFG_BYTES* cfg_bytes( const char* name, CFG_NODE* parent, U8* bytes, U32 size ) { CFG_BYTES* cfg_bytes = new CFG_BYTES; init_cfg_node( (CFG_NODE*)cfg_bytes, name, parent, CFGT_BYTES ); cfg_bytes->bytes = bytes; cfg_bytes->size = size; return cfg_bytes; } CFG_STR* cfg_str( const char* name, CFG_NODE* parent, const char* str, U32 len ) { CFG_STR* cfg_str = new CFG_STR; init_cfg_node( (CFG_NODE*)cfg_str, name, parent, CFGT_STR ); cfg_str->str = (char*)malloc( len + 1 ); strcpy( cfg_str->str, str ); cfg_str->str[len] = '\0'; cfg_str->len = len; return cfg_str; } CFG_INT* cfg_int( const char* name, CFG_NODE* parent, I32 value ) { CFG_INT* cfg_int = new CFG_INT; init_cfg_node( (CFG_NODE*)cfg_int, name, parent, CFGT_INT ); cfg_int->value = value; return cfg_int; } CFG_FLOAT* cfg_float( const char* name, CFG_NODE* parent, F32 value) { CFG_FLOAT* cfg_float = new CFG_FLOAT; init_cfg_node( (CFG_NODE*)cfg_float, name, parent, CFGT_FLOAT ); cfg_float->value = value; return cfg_float; } CFG_VEC2* cfg_vec2( const char* name, CFG_NODE* parent, VEC2 value ) { CFG_VEC2* cfg_vec2 = new CFG_VEC2; init_cfg_node( (CFG_NODE*)cfg_vec2, name, parent, CFGT_VEC2 ); cfg_vec2->value = value; return cfg_vec2; } CFG_VEC3* cfg_vec3( const char* name, CFG_NODE* parent, VEC3 value ) { CFG_VEC3* cfg_vec3 = new CFG_VEC3; init_cfg_node( (CFG_NODE*)cfg_vec3, name, parent, CFGT_VEC3 ); cfg_vec3->value = value; return cfg_vec3; } CFG_CLR* cfg_clr( const char* name, CFG_NODE* parent, CLR value ) { CFG_CLR* cfg_clr = new CFG_CLR; init_cfg_node( (CFG_NODE*)cfg_clr, name, parent, CFGT_CLR ); cfg_clr->value = value; return cfg_clr; } CFG_SECTION* cfg_section( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_SECTION && strcmp( n->name, name ) == 0 ) return (CFG_SECTION*)n; } return 0; } CFG_BYTES* cfg_bytes( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_BYTES && strcmp( n->name, name ) == 0 ) return (CFG_BYTES*)n; } return 0; } CFG_STR* cfg_str( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_STR && strcmp( n->name, name ) == 0 ) return (CFG_STR*)n; } return 0; } CFG_INT* cfg_int( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_INT && strcmp( n->name, name ) == 0 ) return (CFG_INT*)n; } return 0; } CFG_FLOAT* cfg_float( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_FLOAT && strcmp( n->name, name ) == 0 ) return (CFG_FLOAT*)n; } return 0; } CFG_VEC2* cfg_vec2( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_VEC2 && strcmp( n->name, name ) == 0 ) return (CFG_VEC2*)n; } return 0; } CFG_VEC3* cfg_vec3( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_VEC3 && strcmp( n->name, name ) == 0 ) return (CFG_VEC3*)n; } return 0; } CFG_CLR* cfg_clr( CFG_SECTION* parent, const char* name ) { for( I32 i = 0; i < parent->children.size; ++i ) { CFG_NODE* n = parent->children[i]; if( n->type == CFGT_CLR && strcmp( n->name, name ) == 0 ) return (CFG_CLR*)n; } return 0; } void parse_section( CFG_PARSER* parser, CFG_SECTION* current_section ) { char line[8192]; char* token; char* next_token; while( fgets( line, sizeof(line), parser->file ) ) { parser->linen++; trim_whitespace( line ); if( strlen( line ) == 0 ) continue; char line_copy[8192]; strcpy( line_copy, line ); token = strtok( line_copy, " \t\r\n" ); if( !token ) continue; if( strcmp( token, "{" ) == 0 ) { continue; } else if( strcmp( token, "}" ) == 0 ) { return; } else if( strcmp( token, cfg_types[CFGT_SECTION].def ) == 0 ) { next_token = strtok( NULL, " \t\r\n" ); if( !next_token ) { cfg_seterr( parser, "missing section name at line %d", parser->linen ); return; } char sectname[64]; strncpy( sectname, next_token, sizeof(sectname) - 1 ); sectname[sizeof(sectname) - 1] = '\0'; trim_whitespace( sectname ); CFG_SECTION* new_section = cfg_section_new( sectname, (CFG_NODE*)current_section ); char* brace = strtok( NULL, " \t\r\n" ); if( !brace || strcmp( brace, "{" ) != 0 ) { cfg_seterr( parser, "expected '{' after section name at line %d", parser->linen ); return; } parse_section( parser, new_section ); } else { char name[64]; strncpy( name, token, sizeof(name) - 1 ); name[sizeof(name) - 1] = '\0'; trim_whitespace( name ); token = strtok( NULL, "=[" ); if( !token ) { cfg_seterr( parser, "invalid variable declaration at line %d", parser->linen ); continue; } char varname[64]; strncpy( varname, token, sizeof(varname) - 1 ); varname[sizeof(varname) - 1] = '\0'; trim_whitespace( varname ); U8 found = 0; for( I32 i = 0; i < sizeof(cfg_types) / sizeof(CFG_TYPE); ++i ) { const CFG_TYPE* fn = &cfg_types[i]; if( strcmp( name, fn->def ) == 0 ) { strcpy( line_copy, line ); strtok( line_copy, " \t\r\n" ); strtok( NULL, "=[" ); fn->parser( parser, current_section, varname ); found = true; break; } } if( !found ) { cfg_seterr( parser, "unknown type '%s' at line %d", name, parser->linen ); return; } if( parser->iserr ) { dlog( "parse_section() : %s parse error:\n - %s\n", name, parser->err ); return; } } } } CFG_SECTION* cfg_load( const char* path ) { FILE* f = fopen( path, "r" ); if( !f ) return 0; CFG_PARSER p; memset( &p, 0, sizeof(p) ); p.iserr = 0; p.file = f; p.linen = 0; p.root = cfg_section_new( "root", 0 ); parse_section( &p, p.root ); fclose( f ); if( p.iserr ) { cfg_free( (CFG_NODE*)p.root ); return 0; } return p.root; } STAT cfg_save( CFG_SECTION* root, const char* path ) { FILE* f = fopen( path, "w" ); if( !f ) return STAT_ERR; CFG_SERIALIZER s; s.tabc = 0; s.f = f; STR buf = cfg_serialize_section( &s, (CFG_NODE*)root ); fwrite( buf, 1, buf.size, f ); fclose( f ); return STAT_OK; }