+u32 vmf_get_mdl( vmf_map *map, const char *mdl )
+{
+ u32 hash = djb2( (const unsigned char *)mdl );
+
+ for( u32 i = 0; i < csr_sb_count( map->models ); i ++ )
+ {
+ if( hash == map->models[i].hash && !strcmp( map->models[i].str, mdl ) )
+ {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+void vmf_populate_models( vdf_node *vmf, vmf_map *map )
+{
+ vdf_foreach( vmf, "entity", ent )
+ {
+ // Use any class name with prop_
+ if( !strncmp( kv_get( ent, "classname", "" ), "prop_", 5 ))
+ {
+ // Check if it exists
+ const char *model_path = kv_get( ent, "model", "" );
+ u32 mdl_id = vmf_get_mdl( map, model_path );
+
+ if( !mdl_id )
+ {
+ map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
+
+ struct vmf_model *entry = &map->models[ csr_sb_count( map->models ) ];
+ entry->str = csr_malloc( strlen( model_path ) +1 );
+ strcpy( entry->str, model_path );
+ entry->hash = djb2( (const unsigned char *)model_path );
+
+ mdl_id = csr_sb_count( map->models );
+ csr_sb_use( map->models );
+ }
+
+ // Assign prop-ID for later use
+ ent->user = mdl_id;
+ }
+ }
+}
+
+// Load all models
+void vmf_load_models( vmf_map *map )
+{
+ printf( "Loading all models\n" );
+
+ // Error model. TODO: Maybe don't have this be junk data.
+ map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
+ csr_sb_use( map->models );
+ mdl_error( &map->models[0].mdl );
+
+ // Create listings for each model
+ vmf_populate_models( map->root, map );
+
+ for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
+ {
+ vmf_populate_models( map->cache[i].root, map );
+ }
+
+ printf( "Indexed (%u) models\n", csr_sb_count( map->models ) );
+
+ u32 num_success = 0;
+
+ // Load model data
+ // TODO: Make nice loading bar
+ for( int i = 1; i < csr_sb_count( map->models ); i ++ )
+ {
+ printf( "Load model (%d)\n", i );
+
+ struct vmf_model *mdl = &map->models[i];
+
+ if( mdl_from_find_files( mdl->str, &mdl->mdl ) )
+ {
+ num_success ++;
+ }
+ }
+
+ printf( "Done (%u of %u loaded)\n", num_success, csr_sb_count( map->models ) );
+}
+
+void vmf_init_subvmf( vmf_map *map, const char *subvmf );
+
+void vmf_load_all_instances( vmf_map *map, vdf_node *vmf )
+{
+ vdf_foreach( vmf, "entity", ent )
+ {
+ if( !strcmp( kv_get( ent, "classname", "" ), "func_instance" ))
+ {
+ // Entity is in use if file is specified, if not just ignore the entity.
+ const char *path = kv_get( ent, "file", "" );
+ if( strcmp( path, "" ) )
+ {
+ vmf_init_subvmf( map, path );
+ }
+ }
+ }
+}
+
+void vmf_init_subvmf( vmf_map *map, const char *subvmf )
+{
+ printf( "Loading subvmf: %s\n", subvmf );
+
+ u32 hash = djb2( (const unsigned char *)subvmf );
+
+ // Check if present
+ for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
+ {
+ if( hash == map->cache[i].hash )
+ {
+ if( !strcmp( map->cache[i].name, subvmf ) )
+ {
+ return;
+ }
+ }
+ }
+
+ map->cache = csr_sb_reserve( map->cache, 1, sizeof( struct vmf_instance ));
+ struct vmf_instance *inst = &map->cache[ csr_sb_count( map->cache ) ];
+
+ if( (inst->root = vdf_open_file( subvmf )) )
+ {
+ csr_sb_use( map->cache );
+
+ inst->hash = hash;
+ inst->name = csr_malloc( strlen( subvmf )+1 );
+ strcpy( inst->name, subvmf );
+
+ // Recursive load other instances
+ vmf_load_all_instances( map, inst->root );
+ }
+ else
+ {
+ // TODO: Don't die here?
+ fprintf( stderr, "Failed to load instance file\n" );
+ exit(0);
+ }
+}
+
+vmf_map *vmf_init( const char *path, int load_models )
+{
+ vmf_map *map = csr_calloc( sizeof( vmf_map ) );
+ map->root = vdf_open_file( path );
+
+ // Prepare instances
+ vmf_load_all_instances( map, map->root );
+
+ // Other resources
+ if( load_models )
+ {
+ vmf_load_models( map );
+ }
+
+ return map;
+}
+
+void vmf_free( vmf_map *map )
+{
+ for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
+ {
+ vdf_free_r( map->cache[i].root );
+ free( map->cache[i].name );
+ }
+
+ for( int i = 1; i < csr_sb_count( map->models ); i ++ )
+ {
+ free( map->models[i].str );
+ mdl_free( &map->models[i].mdl );
+ }
+
+ vdf_free_r( map->root );
+ csr_sb_free( map->models );
+ csr_sb_free( map->cache );
+ free( map );
+}
+