1 // This software is not affiliated with Valve Corporation
2 // We are not affiliated, associated, authorized, endorsed by, or in any way officially
3 // connected with Valve Corporation, or any of its subsidiaries or its affiliates.
5 // All trademarks are property of their respective owners
7 // VDF aka Keyvalue text format parser / writer
9 #define vdf_foreach( NODE, STR, AS ) \
10 int __vdf_it_##AS = 0; \
12 while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
14 #define kv_foreach( NODE, STR, AS ) \
15 int __kv_it_##AS = 0; \
17 while( (AS = kv_iter( NODE, STR, &__kv_it_##AS )) )
21 // ==================================================================================================================
23 typedef struct vdf_kv vdf_kv
;
24 typedef struct vdf_node vdf_node
;
25 typedef struct vdf_ctx vdf_ctx
;
28 // ==================================================================================================================
31 vdf_node
*vdf_open_file( const char *fn
);
32 void vdf_free_r( vdf_node
*p
);
37 // Starting from *it, get next child with matching name from node.
38 vdf_node
*vdf_next( vdf_node
*node
, const char *name
, int *it
);
40 // Create new empty node attached to parent. name can be NULL
41 vdf_node
*vdf_create_node( vdf_node
*parent
, const char *name
);
46 void vdf_out( vdf_node
*h
, int lvl
, int declare
, FILE *file
);
47 void vdf_save( vdf_node
*node
, const char *fn
);
48 void vdf_print( vdf_node
*node
);
53 // Get value string pointer from node's dictionary
54 const char *kv_get( vdf_node
*node
, const char *key
, const char *value_defalt
);
56 // Iterate each keyvalue starting from *it until key is matched
57 char *kv_iter( vdf_node
*node
, const char *key
, int *it
);
59 // Get keyvalue from node as int / float
60 int kv_get_int( vdf_node
*node
, const char *key
, const int default_value
);
61 float kv_get_float( vdf_node
*node
, const char *key
, float default_value
);
63 // Parse values into sepecifc type
64 void kv_int_array( vdf_node
*node
, const char *key
, u32 count
, int *arr
);
65 void kv_float_array( vdf_node
*node
, const char *key
, u32 count
, float *arr
);
66 void kv_double_array( vdf_node
*node
, const char *key
, u32 count
, double *arr
);
69 // ==================================================================================================================
70 #ifdef VALVE_IMPLEMENTATION
72 // Add keyvalue pair to node
73 void vdf_kv_append( vdf_node
*p
, const char *k
, const char *v
);
75 // (low level api for arrays)
76 void kv_parse_array( const char *source
, u32 esize
, u32 count
, void(*interp_func
)(const char *src
, void *dest
), void *arr
);
77 void vdf_str_to_float( const char *src
, void *dest
);
78 void vdf_str_to_int( const char *src
, void *dest
);
82 void vdf_newln( vdf_ctx
*ctx
);
83 void vdf_endl( vdf_ctx
*ctx
);
84 int vdf_line_control( vdf_ctx
*ctx
);
85 void vdf_wait_endl( vdf_ctx
*ctx
);
86 void vdf_parse_string( vdf_ctx
*ctx
);
87 int vdf_parse_structure( vdf_ctx
*ctx
);
88 void vdf_parse_begin_token( vdf_ctx
*ctx
, char *ptr
);
89 void vdf_parse_feedbuffer( vdf_ctx
*ctx
, char *buf
);
92 void vdf_out_indent( const int n
, FILE *file
);
97 // ==================================================================================================================
118 #ifdef VALVE_IMPLEMENTATION
120 vdf_node
*vdf_next( vdf_node
*node
, const char *name
, int *it
)
125 for( int i
= it
? *it
: 0; i
< csr_sb_count( node
->nodes
); i
++ )
127 if( !name
|| !strcmp( name
, node
->nodes
[i
]->name
))
130 return node
->nodes
[i
];
137 const char *kv_get( vdf_node
*node
, const char *key
, const char *value_defalt
)
141 for( int i
= 0; i
< csr_sb_count( node
->pairs
); i
++ )
143 if( !strcmp( node
->pairs
[ i
].key
, key
) )
145 return node
->pairs
[ i
].value
;
153 char *kv_iter( vdf_node
*node
, const char *key
, int *it
)
159 while( *it
< csr_sb_count( node
->pairs
) )
161 if( !strcmp( node
->pairs
[ *it
].key
, key
) )
163 val
= node
->pairs
[ *it
].value
;
175 void vdf_str_to_int( const char *src
, void *dest
)
177 *((int *)dest
) = atoi( src
);
180 void vdf_str_to_float( const char *src
, void *dest
)
182 *((float *)dest
) = atof( src
);
185 void vdf_str_to_double( const char *src
, void *dest
)
187 *((double *)dest
) = atof( src
);
190 void kv_parse_array( const char *source
, u32 esize
, u32 count
, void(*interp_func
)(const char *src
, void *dest
), void *arr
)
195 char value_buf
[ 64 ];
201 char const *c
= source
;
205 if( *c
== ' ' || *c
== '\t' || *c
== '[' || *c
== ']' || *c
== '(' || *c
== ')' )
209 value_buf
[ k
] = 0x00;
212 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
231 if( k
< sizeof( value_buf
) - 1 )
233 value_buf
[ k
++ ] = *c
;
241 // Add remaining case if we hit null
242 if( token
&& (i
< count
) )
244 value_buf
[ k
] = 0x00;
245 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
249 void kv_int_array( vdf_node
*node
, const char *key
, u32 count
, int *arr
)
251 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(int), count
, vdf_str_to_int
, arr
);
254 void kv_float_array( vdf_node
*node
, const char *key
, u32 count
, float *arr
)
256 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(float), count
, vdf_str_to_float
, arr
);
259 void kv_double_array( vdf_node
*node
, const char *key
, u32 count
, double *arr
)
261 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(double), count
, vdf_str_to_double
, arr
);
264 int kv_get_int( vdf_node
*node
, const char *key
, const int default_value
)
266 const char *v
= kv_get( node
, key
, NULL
);
267 return v
? atoi(v
): default_value
;
270 float kv_get_float( vdf_node
*node
, const char *key
, float default_value
)
272 const char *v
= kv_get( node
, key
, NULL
);
273 return v
? atof( v
): default_value
;
276 vdf_node
*vdf_create_node( vdf_node
*parent
, const char *name
)
278 vdf_node
*node
= calloc( 1, sizeof( vdf_node
) );
282 node
->name
= csr_malloc( strlen( name
)+1 );
283 strcpy( node
->name
, name
);
288 node
->parent
= parent
;
290 parent
->nodes
= csr_sb_reserve( parent
->nodes
, 1, sizeof(vdf_node
*) );
292 vdf_node
**child
= (vdf_node
**)csr_sb_use( parent
->nodes
);
299 void vdf_kv_append( vdf_node
*p
, const char *k
, const char *v
)
301 p
->pairs
= csr_sb_reserve( p
->pairs
, 1, sizeof(vdf_kv
) );
302 vdf_kv
*kv
= (vdf_kv
*)csr_sb_use( p
->pairs
);
304 u32 sv
= strlen(v
)+1;
305 u32 sk
= strlen(k
)+1;
307 kv
->key
= csr_malloc( sv
+sk
);
308 kv
->value
= kv
->key
+sk
;
310 memcpy( kv
->key
, k
, sk
);
311 memcpy( kv
->value
, v
, sv
);
314 void vdf_free_r( vdf_node
*p
)
316 for( int i
= 0; i
< csr_sb_count( p
->pairs
); i
++ )
318 free( p
->pairs
[ i
].key
);
321 for( int i
= 0; i
< csr_sb_count( p
->nodes
); i
++ )
323 vdf_free_r( p
->nodes
[ i
] );
326 csr_sb_free( p
->pairs
);
327 csr_sb_free( p
->nodes
);
333 // ==================================================================================================================
360 void vdf_newln( vdf_ctx
*ctx
)
364 ctx
->st
.tokens
[0] = NULL
;
365 ctx
->st
.tokens
[1] = NULL
;
369 void vdf_endl( vdf_ctx
*ctx
)
372 if( ctx
->st
.tokens
[0] )
375 if( ctx
->st
.tokens
[1] )
377 vdf_kv_append( ctx
->st
.pnode
, ctx
->st
.tokens
[0], ctx
->st
.tokens
[1] );
382 strcpy( ctx
->name
, ctx
->st
.tokens
[0] );
383 ctx
->st
.expect_decl
= 1;
390 int vdf_line_control( vdf_ctx
*ctx
)
392 if( *ctx
->st
.ptr_read
== '\r' )
394 *ctx
->st
.ptr_read
= 0x00;
397 if( *ctx
->st
.ptr_read
== '\n' )
399 *ctx
->st
.ptr_read
= 0x00;
407 void vdf_wait_endl( vdf_ctx
*ctx
)
409 while( (*ctx
->st
.ptr_read
) && (*ctx
->st
.ptr_read
!= '\n') )
411 if( vdf_line_control( ctx
) == 2 )
420 void vdf_parse_string( vdf_ctx
*ctx
)
422 while( *ctx
->st
.ptr_read
)
424 if( *ctx
->st
.ptr_read
== '"' )
426 *ctx
->st
.ptr_read
= 0x00;
430 if( vdf_line_control( ctx
) )
432 log_error( "Unexpected end of line character (Line: %u)\n", ctx
->lines
);
440 int vdf_parse_structure( vdf_ctx
*ctx
)
442 if( *ctx
->st
.ptr_read
== '{' )
444 if( ctx
->st
.tokens
[0] || !ctx
->st
.expect_decl
)
446 log_error( "Unexpected token '{' (Line: %u)\n", ctx
->lines
);
450 ctx
->st
.expect_decl
= 0;
451 ctx
->st
.pnode
= vdf_create_node( ctx
->st
.pnode
, ctx
->name
);
453 vdf_wait_endl( ctx
);
457 // Closing block, jump read head back to parent
458 if( *ctx
->st
.ptr_read
== '}' )
460 if( !ctx
->st
.pnode
->parent
)
462 log_error( "Unexpected token '}' (Line: %u)\n", ctx
->lines
);
467 ctx
->st
.pnode
= ctx
->st
.pnode
->parent
;
470 vdf_wait_endl( ctx
);
477 void vdf_parse_begin_token( vdf_ctx
*ctx
, char *ptr
)
479 ctx
->st
.tokens
[ ctx
->st
.i
] = ptr
;
481 if( ctx
->st
.expect_decl
)
483 log_error( "Unexpected token '%s' (Line: %u)\n", ctx
->name
, ctx
->lines
);
488 void vdf_parse_feedbuffer( vdf_ctx
*ctx
, char *buf
)
490 ctx
->st
.ptr_read
= buf
;
492 while( *ctx
->st
.ptr_read
)
494 if( !vdf_line_control( ctx
) )
496 if( (*ctx
->st
.ptr_read
== '/') && (ctx
->st
.ptr_read
[1] == '/') )
498 *ctx
->st
.ptr_read
= 0x00;
499 ctx
->st
.ptr_read
+= 2;
502 vdf_wait_endl( ctx
);
506 if( !vdf_parse_structure( ctx
) )
508 if( *ctx
->st
.ptr_read
== ' ' || *ctx
->st
.ptr_read
== '\t' )
510 *ctx
->st
.ptr_read
= 0x00;
512 if( ctx
->st
.tokens
[ ctx
->st
.i
] )
518 vdf_wait_endl( ctx
);
523 else if( !ctx
->st
.tokens
[ ctx
->st
.i
] )
525 if( *ctx
->st
.ptr_read
== '"' )
527 *ctx
->st
.ptr_read
= 0x00;
530 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
531 vdf_parse_string( ctx
);
535 if( !( *ctx
->st
.ptr_read
== '/' && *(ctx
->st
.ptr_read
+ 1) == *ctx
->st
.ptr_read
) )
537 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
549 int vdf_load_into( const char *fn
, vdf_node
*node
)
551 char *text_src
= csr_textasset_read( fn
);
559 ctx
.root
= ctx
.st
.pnode
= node
;
562 vdf_parse_feedbuffer( &ctx
, text_src
);
568 vdf_node
*vdf_open_file( const char *fn
)
570 vdf_node
*root
= vdf_create_node( NULL
, NULL
);
571 if( vdf_load_into( fn
, root
) )
583 // ==================================================================================================================
585 void vdf_out_indent( const int n
, FILE *file
)
587 for( int x
= 0; x
< n
; x
++ )
588 fprintf( file
, "\t" );
591 void vdf_out( vdf_node
*h
, int lvl
, int declare
, FILE *file
)
595 vdf_out_indent( lvl
, file
); fprintf( file
, "\"%s\"\n", h
->name
);
596 vdf_out_indent( lvl
, file
); fprintf( file
, "{\n" );
599 for( int i
= 0; i
< csr_sb_count( h
->pairs
); i
++ )
601 vdf_out_indent( lvl
+1, file
); fprintf( file
, "\"%s\" \"%s\"\n", h
->pairs
[i
].key
, h
->pairs
[i
].value
);
604 for( int i
= 0; i
< csr_sb_count( h
->nodes
); i
++ )
606 vdf_out( h
->nodes
[i
], lvl
+ 1, 1, file
);
611 vdf_out_indent( lvl
, file
); fprintf( file
, "}\n" );
615 void vdf_save( vdf_node
*node
, const char *fn
)
617 FILE* file
= fopen( fn
, "w" );
619 vdf_out( node
, -1, 0, file
);
624 void vdf_print( vdf_node
*node
)
626 vdf_out( node
, -1, 0, stdout
);