diff options
| author | navewindre <boneyaard@gmail.com> | 2025-09-03 20:10:09 +0200 |
|---|---|---|
| committer | navewindre <boneyaard@gmail.com> | 2025-09-03 20:10:09 +0200 |
| commit | f8b92ce3aa08b1445c9f956d8166830946562d12 (patch) | |
| tree | 94e63a5aec9f8f52b577f56799e0c9201fd976a5 /src/util/config | |
a
Diffstat (limited to 'src/util/config')
| -rw-r--r-- | src/util/config/config.cpp | 263 | ||||
| -rw-r--r-- | src/util/config/parsers.cpp | 156 | ||||
| -rw-r--r-- | src/util/config/serializers.cpp | 109 |
3 files changed, 528 insertions, 0 deletions
diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp new file mode 100644 index 0000000..1601afe --- /dev/null +++ b/src/util/config/config.cpp @@ -0,0 +1,263 @@ +#include <cstdarg> +#include <cstdio> + +#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'; +} + +inline void trim_whitespace( char* buf ) { + U32 i; + for( i = 0; !!buf[i]; ++i ) + if( !is_whitespace( buf[i] ) ) break; + for( U32 i2 = i; !!buf[i2]; ++i2 ) { + if( is_whitespace( buf[i2] ) ) { + buf[i2 - i] = 0; + return; + } + buf[i2 - i] = buf[i2]; + } +} + +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 ) ) { + token = strtok( line, " \t\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\n" ); + char sectname[64]; + strcpy( sectname, next_token ); + trim_whitespace( sectname ); + + CFG_SECTION* new_section = cfg_section_new( sectname, (CFG_NODE*)current_section ); + strtok( NULL, " \t\n" ); + parse_section( parser, new_section ); + } else { + char name[64]; + strcpy( name, token ); + trim_whitespace( name ); + + token = strtok( NULL, "=[" ); + if( !token ) + continue; + + char varname[64]; + strcpy( varname, token ); + trim_whitespace( varname ); + + for( I32 i = 0; i < sizeof(cfg_types) / sizeof(CFG_TYPE); ++i ) { + const CFG_TYPE* fn = &cfg_types[i]; + if( strncmp( name, fn->def, strlen( fn->def ) ) == 0 ) { + fn->parser( parser, current_section, varname ); + break; + } + } + + if( parser->iserr ) { + dlog( "parse_section() : %s parse error:\n - %s\n", name, parser->err ); + return; + } + } + + parser->linen++; + } +} + +CFG_SECTION* cfg_load( const char* path ) { + FILE* f = fopen( path, "rb" ); + if( !f ) + return 0; + + CFG_PARSER p; + p.iserr = 0; + p.file = f; + p.linen = 0; + p.root = cfg_section_new( "root", 0 ); + parse_section( &p, p.root ); + + fclose( f ); + 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; +} diff --git a/src/util/config/parsers.cpp b/src/util/config/parsers.cpp new file mode 100644 index 0000000..1dbfc07 --- /dev/null +++ b/src/util/config/parsers.cpp @@ -0,0 +1,156 @@ +#include <cstdarg> +#include <cstdio> + +#include "../config.h" + +U8* parse_hex_string( CFG_PARSER* p, const char* hex_str, U8* bytes, U32* size ) { + *size = strlen( hex_str ) / 2; + for( U32 i = 0; i < *size; ++i ) { + U32 buf; + I32 c = sscanf( hex_str + 2 * i, "%02x", &buf ); + if( c != 1 ) { + cfg_seterr( p, "Invalid hex string: %s", hex_str ); + free( bytes ); + return NULL; + } + + bytes[i] = ( buf & 0xff ); + } + + return bytes; +} + +STAT cfg_parser_bytes( CFG_PARSER* p, CFG_SECTION* s, char* name ) { + char* sizep = strtok( NULL, "]" ); + if( !sizep ) { + cfg_seterr( p, "cfg_parser_bytes() : %s: invalid size [L:%d]", name, p->linen ); + return STAT_ERR; + } + + U32 size = atoi( sizep ); + strtok( NULL, "=" ); + strtok( NULL, "\"" ); + char* hex_value = strtok( NULL, "\"" ); + if( !hex_value ) { + cfg_seterr( p, "cfg_parser_bytes() : %s: invalid value (size: %d) [L:%d]", name, size, p->linen ); + return STAT_ERR; + } + + U32 totals; + U8* bytes = (U8*)malloc( size ); + parse_hex_string( p, hex_value, bytes, &totals ); + cfg_bytes( name, (CFG_NODE*)s, bytes, size ); + + return STAT_OK; +} + +STAT cfg_parser_str( CFG_PARSER* p, CFG_SECTION* s, char* name ) { + char* sizep = strtok( NULL, "]" ); + if( !sizep ) { + cfg_seterr( p, "cfg_parser_str() : %s: invalid size [L:%d]", name, p->linen ); + return STAT_ERR; + } + + U32 size = atoi( sizep ); + strtok( NULL, "=" ); + strtok( NULL, "\"" ); + char* v = strtok( NULL, "\"" ); + if( !v ) { + cfg_seterr( p, "cfg_parser_str() : %s: incorrect value [L:%d]", name, p->linen ); + return STAT_ERR; + } + + cfg_str( name, (CFG_NODE*)s, v, size ); + + return STAT_OK; +} + +STAT cfg_parser_int( CFG_PARSER* p, CFG_SECTION* s, char* name ) { + char* vp = strtok( NULL, ";" ); + if( !vp ) { + cfg_seterr( p, "cfg_parser_int() : %s: incorrect value [L:%d]", name, p->linen ); + return STAT_ERR; + } + + I32 value = atoi( vp ); + cfg_int( name, (CFG_NODE*)s, value ); + + return STAT_OK; +} + +STAT cfg_parser_float( CFG_PARSER* p, CFG_SECTION* s, char* name ) { + char* vp = strtok( NULL, ";" ); + if( !vp ) { + cfg_seterr( p, "cfg_parser_float() : %s: incorrect value [L:%d]", name, p->linen ); + return STAT_ERR; + } + + F32 value = atof( vp ); + cfg_float( name, (CFG_NODE*)s, value ); + + return STAT_OK; +} + +STAT cfg_parser_vec2( CFG_PARSER *p, CFG_SECTION* s, char* name ) { + char* vp = strtok( NULL, "{" ); + const char* errstr = "cfg_parser_vec2() : %s: incorrect value [L:%d]"; + + if( !vp ) { cfg_seterr( p, errstr, name, p->linen ); return STAT_ERR; } + vp = strtok( NULL, "," ); + if( !vp ) { cfg_seterr( p, errstr, name, p->linen ); return STAT_ERR; } + F32 v1 = atof( vp ); + vp = strtok( NULL, "}" ); + if( !vp ) { cfg_seterr( p, errstr, name, p->linen ); return STAT_ERR; } + F32 v2 = atof( vp ); + + VEC2 v = { v1, v2 }; + cfg_vec2( name, (CFG_NODE*)s, v ); + + return STAT_OK; +} + +STAT cfg_parser_vec3( CFG_PARSER *parser, CFG_SECTION *section, char *name ) { + char *vp = strtok( NULL, "{" ); + const char* errstr = "cfg_parser_vec3() : %s: incorrect value [L:%d]"; + + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + vp = strtok( NULL, "," ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 x = atof( vp ); + vp = strtok( NULL, "," ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 y = atof( vp ); + vp = strtok( NULL, "}" ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 z = atof( vp ); + VEC3 v = { x, y, z }; + cfg_vec3( name, (CFG_NODE*)section, v ); + + return STAT_OK; +} + +STAT cfg_parser_clr( CFG_PARSER* parser, CFG_SECTION* section, char* name ) { + const char* vp = strtok( NULL, "{" ); + const char* errstr = "cfg_parser_clr() : %s: incorrect value [L:%d]"; + if( !vp ) { + cfg_seterr( parser, errstr, name, parser->linen ); + return STAT_ERR; + } + + vp = strtok( NULL, "," ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 r = atof( vp ); + vp = strtok( NULL, "," ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 g = atof( vp ); + vp = strtok( NULL, "," ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 b = atof( vp ); + vp = strtok( NULL, "}" ); + if( !vp ) { cfg_seterr( parser, errstr, name, parser->linen ); return STAT_ERR; } + F32 a = atof( vp ); + + CLR c = { r, g, b, a }; + cfg_clr( name, (CFG_NODE*)section, c ); + return STAT_OK; +} diff --git a/src/util/config/serializers.cpp b/src/util/config/serializers.cpp new file mode 100644 index 0000000..01f416c --- /dev/null +++ b/src/util/config/serializers.cpp @@ -0,0 +1,109 @@ +#include <cstdarg> +#include <cstdio> + +#include "../config.h" + +void serialize_node( CFG_SERIALIZER* s, CFG_NODE* n, char* buf ) { + U32 len = strlen( buf ); + for( U32 i = 0; i < s->tabc * 2; ++i ) buf[i + len] = ' '; + buf[len + s->tabc * 2] = 0; + sprintf( buf, "%s%s %s", buf, cfg_types[n->type].def, n->name ); +} + +void cfg_serialize_section( CFG_SERIALIZER* s, CFG_NODE *n, char *buf ) { + CFG_SECTION* sec = (CFG_SECTION*)n; + if( sec->parent ) { + serialize_node( s, n, buf ); + strcat( buf, " {\n" ); + s->tabc++; + } + + char line[8192]; + for( U32 i = 0; i < sec->children.size; ++i ) { + line[0] = 0; + CFG_NODE* c = sec->children[i]; + if( c->type == CFGT_SECTION ) { + cfg_serialize_section( s, c, buf ); + continue; + } + + cfg_types[c->type].serializer( s, c, line ); + strcat( buf, line ); + strcat( buf, "\n" ); + } + + if( sec->parent ) { + s->tabc--; + char tabbuf[512] = { 0 }; + for( U32 i = 0 ; i < s->tabc * 2; ++i ) + tabbuf[i] = ' '; + tabbuf[s->tabc * 2] = 0; + strcat( buf, tabbuf ); + strcat( buf, "}" ); + if( sec->parent ) + strcat( buf, "\n" ); + } +} + +void cfg_serialize_bytes( CFG_SERIALIZER* s, CFG_NODE* n, char* buf ) { + CFG_BYTES* b = (CFG_BYTES*)n; + U32 size = b->size; + U8* bytes = b->bytes; + + serialize_node( s, n, buf ); + sprintf( buf, "%s[%d] = \"", buf, size ); + U32 len = strlen( buf ); + for( U32 i = 0; i < size; ++i ) { + sprintf( buf + len + i * 2, "%02X", bytes[i] ); + } + + sprintf( buf, "%s%s", buf, "\";" ); +} + +void cfg_serialize_str( CFG_SERIALIZER* s, CFG_NODE* n, char *buf ) { + CFG_STR* sn = (CFG_STR*)n; + char* str = sn->str; + + serialize_node( s, n, buf ); + sprintf( buf, "%s[%d] = \"%s\";", buf, sn->len, str ); +} + +void cfg_serialize_int( CFG_SERIALIZER* s, CFG_NODE* n, char *buf ) { + CFG_INT* i = (CFG_INT*)n; + I32 ival = i->value; + + serialize_node( s, n, buf ); + sprintf( buf, "%s = %d;", buf, ival ); +} + +void cfg_serialize_float( CFG_SERIALIZER* s, CFG_NODE* n, char *buf ) { + CFG_FLOAT* f = (CFG_FLOAT*)n; + F32 fval = f->value; + + serialize_node( s, n, buf ); + sprintf( buf, "%s = %g;", buf, fval ); +} + +void cfg_serialize_vec2( CFG_SERIALIZER* s, CFG_NODE* n, char *buf ) { + CFG_VEC2* v = (CFG_VEC2*)n; + VEC2 val = v->value; + + serialize_node( s, n, buf ); + sprintf( buf, "%s = { %g, %g };", buf, val.x, val.y ); +} + +void cfg_serialize_vec3( CFG_SERIALIZER* s, CFG_NODE* n, char *buf ) { + CFG_VEC3* v = (CFG_VEC3*)n; + VEC3 val = v->value; + + serialize_node( s, n, buf ); + sprintf( buf, "%s = { %g, %g, %g };", buf, val.x, val.y, val.z ); +} + +void cfg_serialize_clr( CFG_SERIALIZER* s, CFG_NODE* n, char *buf ) { + CFG_CLR* v = (CFG_CLR*)n; + CLR val = v->value; + + serialize_node( s, n, buf ); + sprintf( buf, "%s = { %g, %g, %g, %g };", buf, val.r, val.g, val.b, val.a ); +} |
