#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'; // Added \r } inline void trim_whitespace( char* buf ) { if (!buf || !*buf) return; // Find first non-whitespace character U32 start = 0; while( buf[start] && is_whitespace( buf[start] ) ) start++; // Find last non-whitespace character U32 end = strlen( buf ); while( end > start && is_whitespace( buf[end - 1] ) ) end--; // Move string to beginning and null-terminate 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 ) { memset( node->name, 0, sizeof(node->name) ); strcpy( node->name, name ); node->name[sizeof(node->name) - 1] = '\0'; 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++; // Remove all whitespace including \r\n trim_whitespace( line ); // Skip empty lines if( strlen(line) == 0 ) continue; // Use a copy for tokenization to preserve original char line_copy[8192]; strcpy( line_copy, line ); token = strtok( line_copy, " \t\r\n" ); // Added \r 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 ); // Look for opening brace 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 { // Parse variable declaration char name[64]; strncpy( name, token, sizeof(name) - 1 ); name[sizeof(name) - 1] = '\0'; trim_whitespace( name ); // Find the variable name (between type and =) 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 ); // Find matching parser bool found = false; 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 ) { // Reset strtok to work on original line for parser strcpy( line_copy, line ); strtok( line_copy, " \t\r\n" ); // Skip type strtok( NULL, "=[" ); // Skip variable name 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" ); // Changed from "rb" to "r" for text mode if( !f ) return 0; CFG_PARSER p; memset( &p, 0, sizeof(p) ); // Initialize all fields 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; char* buf = (char*)malloc( 999999 ); buf[0] = 0; CFG_SERIALIZER s; s.tabc = 0; s.f = f; cfg_serialize_section( &s, (CFG_NODE*)root, buf ); U32 len = strlen( buf ); fwrite( buf, 1, len, f ); free( buf ); fclose( f ); return STAT_OK; }