the asumptions were of course, incorrect
[convexer.git] / cxr / cxr_valve_bin.h
1 /*
2 * The following is used to read the existing content from CS:GO compiled assets
3 *
4 * Supported formats:
5 * vdf
6 * vpk
7 * mdl
8 */
9
10 #ifndef CXR_VALVE_BIN_H
11 #define CXR_VALVE_BIN_H
12
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "cxr_types.h"
17 #include "cxr_math.h"
18 #include "cxr_io.h"
19 #include "cxr_mem.h"
20 #include "cxr_log.h"
21
22 typedef struct VPKHeader VPKHeader;
23 typedef struct VPKDirectoryEntry VPKDirectoryEntry;
24 typedef struct vdf_kv vdf_kv;
25 typedef struct vdf_node vdf_node;
26 typedef struct vdf_ctx vdf_ctx;
27 typedef struct fs_locator fs_locator;
28
29 /*
30 * These are 'unofficial' representations of the original formats, more C
31 * friendly
32 */
33 typedef struct valve_file_system valve_file_system;
34 typedef struct valve_model valve_model;
35 typedef struct valve_model_batch valve_model_batch;
36 typedef struct valve_material valve_material;
37
38 /* Api */
39
40 CXR_API i32 cxr_fs_set_gameinfo( const char *path ); /* Setup system */
41 CXR_API void cxr_fs_exit(void); /* Clean up */
42 CXR_API void *cxr_fs_get( const char *path, i32 stringbuffer ); /* Get a file */
43 CXR_API void cxr_fs_free( void *data );
44 CXR_API i32 cxr_fs_find( const char *path, fs_locator *locator );
45
46 CXR_API valve_model *valve_load_model( const char *relpath );
47 CXR_API void valve_free_model( valve_model *model );
48 CXR_API valve_material *valve_load_material( const char *path );
49 CXR_API void valve_free_material( valve_material *material );
50
51 /*
52 * File system implementation
53 */
54
55 #pragma pack(push, 1)
56
57 struct VPKHeader
58 {
59 u32 Signature;
60 u32 Version;
61 u32 TreeSize;
62 u32 FileDataSectionSize;
63 u32 ArchiveMD5SectionSize;
64 u32 OtherMD5SectionSize;
65 u32 SignatureSectionSize;
66 };
67
68 struct VPKDirectoryEntry
69 {
70 u32 CRC;
71 u16 PreloadBytes;
72 u16 ArchiveIndex;
73 u32 EntryOffset;
74 u32 EntryLength;
75 u16 Terminator;
76 };
77
78 #pragma pack(pop)
79
80 static struct valve_file_system
81 {
82 char *gamedir,
83 *exedir;
84
85 /* Runtime */
86 VPKHeader vpk;
87 char *directory_tree;
88
89 cxr_abuffer searchpaths;
90
91 FILE *current_archive;
92 u16 current_idx;
93
94 int initialized;
95 }
96 fs_global = { .initialized = 0 };
97
98 struct fs_locator
99 {
100 VPKDirectoryEntry *vpk_entry;
101 char path[ 1024 ];
102 };
103
104 /*
105 * VPK reading
106 */
107
108 static VPKDirectoryEntry *vpk_find( const char *asset );
109
110 /*
111 * VDF reading
112 */
113
114 static vdf_node *vdf_open_file( const char *fn );
115 static void vdf_free_r( vdf_node *p );
116
117 /* Starting from *it, get next child with matching name from node. */
118 static vdf_node *vdf_next( vdf_node *node, const char *name, int *it );
119
120 /* Create new empty node attached to parent. name can be NULL */
121 static vdf_node *vdf_create_node( vdf_node *parent, const char *name );
122
123 /* KV access */
124 static const char *kv_get( vdf_node *node, const char *key,
125 const char *value_defalt );
126
127 /* Iterate each keyvalue starting from *it until key is matched */
128 static char *kv_next( vdf_node *node, const char *key, int *it );
129
130 static int kv_get_int( vdf_node *node, const char *key, const int fallback );
131 static float kv_get_float( vdf_node *node, const char *key, float fallback );
132
133 static void kv_int_array( vdf_node *node, const char *key, u32 count,
134 int *arr );
135 static void kv_float_array( vdf_node *node, const char *key, u32 count,
136 float *arr );
137 static void kv_double_array( vdf_node *node, const char *key, u32 count,
138 double *arr );
139
140 #define vdf_foreach( NODE, STR, AS ) \
141 int __vdf_it_##AS = 0; \
142 vdf_node * AS;\
143 while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
144
145 #define kv_foreach( NODE, STR, AS ) \
146 int __kv_it_##AS = 0; \
147 const char * AS;\
148 while( (AS = kv_next( NODE, STR, &__kv_it_##AS )) )
149
150 static VPKDirectoryEntry *vpk_find( const char *asset )
151 {
152 valve_file_system *fs = &fs_global;
153
154 if( !fs->directory_tree )
155 return NULL;
156
157 if( !asset )
158 return NULL;
159
160 char wbuf[ 512 ];
161 strcpy( wbuf, asset );
162
163 /*
164 * This currently fails if the filename doesn't have a file extension, or
165 * if it is located at the root path. I'm not sure if this is defined
166 * behaviour or not. TODO: allow it to work anyway
167 */
168
169 char *ext = cxr_findext( wbuf, '.' );
170
171 if( !ext ) return NULL;
172 *(ext-1) = 0x00;
173
174 char *fn = cxr_findext( wbuf, '/' );
175
176 if( !fn ) return NULL;
177 *(fn-1) = 0x00;
178
179 char *dir = wbuf;
180 char *pCur = fs->directory_tree;
181
182 while( 1 )
183 {
184 if( !*pCur ) break;
185
186 int bExt = !strcmp( ext, pCur );
187
188 while( *( pCur ++ ) ) {};
189 while( 1 )
190 {
191 if( !*pCur ) { pCur ++; break; }
192
193 int bDir = !strcmp( dir, pCur );
194
195 while( *( pCur ++ ) ) {};
196 while( 1 )
197 {
198 if( !*pCur ) { pCur ++; break; }
199
200 const char *vpk_fn = pCur;
201
202 while( *( pCur ++ ) ) {};
203 VPKDirectoryEntry *entry = (VPKDirectoryEntry *)pCur;
204
205 if( !strcmp( vpk_fn, fn ) && bExt && bDir )
206 {
207 return entry;
208 }
209
210 pCur += entry->PreloadBytes + sizeof( VPKDirectoryEntry );
211 }
212
213 if( bDir && bExt ) return NULL;
214 }
215
216 if( bExt ) return NULL;
217 }
218
219 return NULL;
220 }
221
222 /*
223 * VDF
224 */
225
226 struct vdf_kv
227 {
228 char *key;
229 char *value;
230 };
231
232 struct vdf_node
233 {
234 char *name;
235
236 vdf_node *parent;
237
238 cxr_abuffer abnodes,
239 abpairs;
240
241 u32 user;
242 u32 user1;
243 };
244
245 static vdf_node *vdf_next( vdf_node *node, const char *name, int *it )
246 {
247 if( !node )
248 return NULL;
249
250 for( int i = it? *it: 0; i < node->abnodes.count; i ++ )
251 {
252 vdf_node **ptr_child = cxr_ab_ptr( &node->abnodes, i ),
253 *child = *ptr_child;
254
255 if( !name || !strcmp( name, child->name ))
256 {
257 if( it ) *it = i+1;
258 return child;
259 }
260 }
261
262 return NULL;
263 }
264
265 static char *kv_next( vdf_node *node, const char *key, int *it )
266 {
267 char *val;
268
269 if( node )
270 {
271 while( *it < node->abpairs.count )
272 {
273 vdf_kv *kv = cxr_ab_ptr( &node->abpairs, *it );
274
275 if( !strcmp( kv->key, key ) )
276 {
277 val = kv->value;
278 *it = *it + 1;
279 return val;
280 }
281
282 *it = *it + 1;
283 }
284 }
285
286 return NULL;
287 }
288
289 static const char *kv_get( vdf_node *node, const char *key,
290 const char *value_defalt )
291 {
292 int it = 0;
293 char *val = kv_next( node, key, &it );
294
295 if( val )
296 return val;
297
298 return value_defalt;
299 }
300
301 static void vdf_str_to_int( const char *src, void *dest )
302 {
303 *((int *)dest) = atoi( src );
304 }
305
306 static void vdf_str_to_float( const char *src, void *dest )
307 {
308 *((float *)dest) = atof( src );
309 }
310
311 static void vdf_str_to_double( const char *src, void *dest )
312 {
313 *((double *)dest) = atof( src );
314 }
315
316 static void kv_parse_array( const char *source, u32 esize, u32 count,
317 void(*interp_func)(const char *src, void *dest), void *arr )
318 {
319 if( !source )
320 return;
321
322 char value_buf[ 64 ];
323 int token = 0;
324
325 u32 i = 0;
326 u32 k = 0;
327
328 char const *c = source;
329
330 while( *c )
331 {
332 if( *c==' ' || *c=='\t' || *c=='[' || *c==']' || *c=='(' || *c==')' )
333 {
334 if( token )
335 {
336 value_buf[ k ] = 0x00;
337 token = 0;
338
339 interp_func( value_buf, ((u8 *)arr) + i*esize );
340 i ++;
341
342 if( i >= count )
343 {
344 break;
345 }
346 }
347 }
348 else
349 {
350 if( !token )
351 {
352 token = 1;
353 k = 0;
354 }
355
356 if( token )
357 {
358 if( k < sizeof( value_buf ) - 1 )
359 {
360 value_buf[ k ++ ] = *c;
361 }
362 }
363 }
364
365 c ++;
366 }
367
368 /* Add remaining case if we hit null */
369 if( token && (i < count) )
370 {
371 value_buf[ k ] = 0x00;
372 interp_func( value_buf, ((u8 *)arr) + i*esize );
373 }
374 }
375
376 static void kv_int_array( vdf_node *node, const char *key, u32 count, int *arr )
377 {
378 kv_parse_array( kv_get( node, key, NULL ), sizeof(int), count,
379 vdf_str_to_int, arr );
380 }
381
382 static void kv_float_array( vdf_node *node, const char *key, u32 count,
383 float *arr )
384 {
385 kv_parse_array( kv_get( node, key, NULL ), sizeof(float), count,
386 vdf_str_to_float, arr );
387 }
388
389 static void kv_double_array( vdf_node *node, const char *key, u32 count,
390 double *arr )
391 {
392 kv_parse_array( kv_get( node, key, NULL ), sizeof(double), count,
393 vdf_str_to_double, arr );
394 }
395
396 static int kv_get_int( vdf_node *node, const char *key,
397 const int default_value )
398 {
399 const char *v = kv_get( node, key, NULL );
400 return v? atoi(v): default_value;
401 }
402
403 static float kv_get_float( vdf_node *node, const char *key,
404 float default_value )
405 {
406 const char *v = kv_get( node, key, NULL );
407 return v? atof( v ): default_value;
408 }
409
410 static vdf_node *vdf_create_node( vdf_node *parent, const char *name )
411 {
412 vdf_node *node = calloc( 1, sizeof( vdf_node ) );
413
414 /* init buffers */
415 cxr_ab_init( &node->abnodes, sizeof( vdf_node* ), 0 );
416 cxr_ab_init( &node->abpairs, sizeof( vdf_kv ), 0 );
417
418 if( name )
419 {
420 node->name = cxr_str_clone(name, 0);
421 }
422
423 if( parent )
424 {
425 node->parent = parent;
426
427 vdf_node **child = cxr_ab_empty( &parent->abnodes );
428 *child = node;
429 }
430
431 return node;
432 }
433
434 static void vdf_kv_append( vdf_node *p, const char *k, const char *v )
435 {
436 vdf_kv *kv = cxr_ab_empty( &p->abpairs );
437
438 u32 sv = strlen(v)+1;
439 u32 sk = strlen(k)+1;
440
441 kv->key = malloc( sv+sk );
442 kv->value = kv->key+sk;
443
444 memcpy( kv->key, k, sk );
445 memcpy( kv->value, v, sv );
446 }
447
448 static void vdf_free_r( vdf_node *p )
449 {
450 for( int i = 0; i < p->abpairs.count; i ++ )
451 {
452 vdf_kv *kv = cxr_ab_ptr( &p->abpairs, i );
453 free( kv->key ); /* key and value are allocated in the same buffer */
454 }
455
456 for( int i = 0; i < p->abnodes.count; i ++ )
457 {
458 vdf_node **ptr_node = cxr_ab_ptr( &p->abnodes, i );
459 vdf_free_r( *ptr_node );
460 }
461
462 cxr_ab_free( &p->abpairs );
463 cxr_ab_free( &p->abnodes );
464
465 free( p->name );
466 free( p );
467 }
468
469 /*
470 * Parsing
471 */
472
473 struct vdf_ctx
474 {
475 char name[1024];
476
477 vdf_node *root;
478
479 u32 lines;
480 u32 errors;
481
482 struct
483 {
484 int wait;
485 int expect_decl;
486
487 char *tokens[2];
488 int i;
489
490 char *ptr_read;
491
492 vdf_node *pnode;
493 }
494 st;
495 };
496
497 static void vdf_newln( vdf_ctx *ctx )
498 {
499 ctx->lines ++;
500
501 ctx->st.tokens[0] = NULL;
502 ctx->st.tokens[1] = NULL;
503 ctx->st.i = 0;
504 }
505
506 static void vdf_endl( vdf_ctx *ctx )
507 {
508 if( ctx->st.tokens[0] )
509 {
510 /* Keypair */
511 if( ctx->st.tokens[1] )
512 {
513 vdf_kv_append( ctx->st.pnode, ctx->st.tokens[0], ctx->st.tokens[1] );
514 }
515 else
516 {
517 /* decl */
518 strcpy( ctx->name, ctx->st.tokens[0] );
519 ctx->st.expect_decl = 1;
520 }
521 }
522
523 vdf_newln( ctx );
524 }
525
526 static int vdf_line_control( vdf_ctx *ctx )
527 {
528 if( *ctx->st.ptr_read == '\r' )
529 {
530 *ctx->st.ptr_read = 0x00;
531 return 1;
532 }
533 if( *ctx->st.ptr_read == '\n' )
534 {
535 *ctx->st.ptr_read = 0x00;
536 vdf_endl( ctx );
537 return 2;
538 }
539
540 return 0;
541 }
542
543 static void vdf_wait_endl( vdf_ctx *ctx )
544 {
545 while( (*ctx->st.ptr_read) && (*ctx->st.ptr_read != '\n') )
546 {
547 if( vdf_line_control( ctx ) == 2 )
548 {
549 return;
550 }
551
552 ctx->st.ptr_read ++;
553 }
554 }
555
556 static void vdf_parse_string( vdf_ctx *ctx )
557 {
558 while( *ctx->st.ptr_read )
559 {
560 if( *ctx->st.ptr_read == '"' )
561 {
562 *ctx->st.ptr_read = 0x00;
563 return;
564 }
565
566 if( vdf_line_control( ctx ) )
567 {
568 /* Unexpected end of line */
569 cxr_log( "vdf: unexpected EOL\n" );
570 return;
571 }
572
573 ctx->st.ptr_read ++;
574 }
575 }
576
577 static int vdf_parse_structure( vdf_ctx *ctx )
578 {
579 if( *ctx->st.ptr_read == '{' )
580 {
581 if( ctx->st.tokens[0] || !ctx->st.expect_decl )
582 {
583 /* Unexpected token '{' */
584 cxr_log( "vdf: Unexpected token '{'\n" );
585 ctx->errors ++;
586 }
587
588 ctx->st.expect_decl = 0;
589 ctx->st.pnode = vdf_create_node( ctx->st.pnode, ctx->name );
590
591 vdf_wait_endl( ctx );
592 return 1;
593 }
594
595 if( *ctx->st.ptr_read == '}' )
596 {
597 if( !ctx->st.pnode->parent )
598 {
599 /* Unexpected token '}' */
600 cxr_log( "vdf: Unexpected token '}'\n" );
601 ctx->errors ++;
602 }
603 else
604 {
605 ctx->st.pnode = ctx->st.pnode->parent;
606 }
607
608 vdf_wait_endl( ctx );
609 return 1;
610 }
611
612 return 0;
613 }
614
615 static void vdf_parse_begin_token( vdf_ctx *ctx, char *ptr )
616 {
617 ctx->st.tokens[ ctx->st.i ] = ptr;
618
619 if( ctx->st.expect_decl )
620 {
621 /* Unexpected token 'name' */
622 cxr_log( "vdf: unexpected token 'name'\n" );
623 ctx->errors ++;
624 }
625 }
626
627 static void vdf_parse_feedbuffer( vdf_ctx *ctx, char *buf )
628 {
629 ctx->st.ptr_read = buf;
630
631 while( *ctx->st.ptr_read )
632 {
633 if( !vdf_line_control( ctx ) )
634 {
635 if( (*ctx->st.ptr_read == '/') && (ctx->st.ptr_read[1] == '/') )
636 {
637 *ctx->st.ptr_read = 0x00;
638 ctx->st.ptr_read += 2;
639
640 vdf_endl( ctx );
641 vdf_wait_endl( ctx );
642 }
643 else
644 {
645 if( !vdf_parse_structure( ctx ) )
646 {
647 if( *ctx->st.ptr_read == ' ' || *ctx->st.ptr_read == '\t' )
648 {
649 *ctx->st.ptr_read = 0x00;
650
651 if( ctx->st.tokens[ ctx->st.i ] )
652 {
653 ctx->st.i ++;
654
655 if( ctx->st.i == 2 )
656 {
657 vdf_wait_endl( ctx );
658 }
659 }
660 }
661
662 /* New entry */
663 else if( !ctx->st.tokens[ ctx->st.i ] )
664 {
665 if( *ctx->st.ptr_read == '"' )
666 {
667 *ctx->st.ptr_read = 0x00;
668 ctx->st.ptr_read ++;
669
670 vdf_parse_begin_token( ctx, ctx->st.ptr_read );
671 vdf_parse_string( ctx );
672 }
673 else
674 {
675 if( !( *ctx->st.ptr_read == '/' &&
676 *(ctx->st.ptr_read + 1) == *ctx->st.ptr_read ) )
677 {
678 vdf_parse_begin_token( ctx, ctx->st.ptr_read );
679 }
680 }
681 }
682 }
683 }
684 }
685
686 ctx->st.ptr_read ++;
687 }
688 }
689
690 static void vdf_debug_indent( int level )
691 {
692 for(int i=0; i<level; i++)
693 cxr_log(" ");
694 }
695
696 static void vdf_debug_r( vdf_node *node, int level )
697 {
698 vdf_debug_indent(level);
699 cxr_log( "vdf_node(%p, name: '%s')\n", node, node->name );
700
701 vdf_debug_indent(level);
702 cxr_log( "{\n" );
703
704 for( int i=0; i<node->abpairs.count; i++ )
705 {
706 vdf_kv *kv = cxr_ab_ptr( &node->abpairs, i );
707
708 vdf_debug_indent(level+1);
709 cxr_log( "vdf_kv(%p, k: '%s', v: '%s')\n",
710 kv, kv->key, kv->value );
711 }
712
713 for( int i=0; i<node->abnodes.count; i++ )
714 {
715 vdf_node **child = cxr_ab_ptr( &node->abnodes, i );
716 vdf_debug_r( *child, level+1 );
717 }
718
719 vdf_debug_indent(level);
720 cxr_log( "}\n" );
721 }
722
723 /* This will wreck the original buffer, but must stay alive! */
724 static vdf_node *vdf_from_buffer( char *buffer )
725 {
726 vdf_node *root = vdf_create_node( NULL, NULL );
727
728 vdf_ctx ctx = {0};
729 ctx.root = ctx.st.pnode = root;
730
731 vdf_newln( &ctx );
732 vdf_parse_feedbuffer( &ctx, buffer );
733
734 #if 0
735 vdf_debug_r( root, 0 );
736 #endif
737
738 return root;
739 }
740
741 static vdf_node *vdf_open_file( const char *fn )
742 {
743 char *text_src = cxr_textasset_read( fn );
744
745 if( !text_src )
746 return NULL;
747
748 vdf_node *root = vdf_from_buffer( text_src );
749 free( text_src );
750
751 return root;
752 }
753
754 /*
755 * File system
756 */
757
758 CXR_API i32 cxr_fs_set_gameinfo( const char *path )
759 {
760 valve_file_system *fs = &fs_global;
761
762 if( fs->initialized )
763 cxr_fs_exit();
764
765 vdf_node *info = vdf_open_file( path );
766 if( !info )
767 return 0;
768
769 fs->gamedir = cxr_str_clone( path, 0 );
770 cxr_downlvl( fs->gamedir );
771
772 fs->exedir = cxr_str_clone( fs->gamedir, 0 );
773 cxr_downlvl( fs->exedir );
774
775 cxr_log( "Setting up file system:\n"
776 "gameinfo: %s\n"
777 "gamedir: %s\n"
778 "exedir: %s\n",
779
780 path, fs->gamedir, fs->exedir );
781
782 /* get search paths */
783 vdf_node *search_paths =
784 vdf_next
785 (
786 vdf_next
787 (
788 vdf_next( info, "GameInfo", NULL ),
789 "FileSystem",
790 NULL
791 ),
792 "SearchPaths",
793 NULL
794 );
795
796 cxr_ab_init( &fs->searchpaths, sizeof( char *), 0 );
797
798 kv_foreach( search_paths, "Game", kv_game )
799 {
800 cxr_log( "Game %s\n", kv_game );
801
802 if( kv_game[0] == '|' ) continue;
803
804 char *buf;
805 if( cxr_path_is_abs( kv_game ) )
806 {
807 buf = cxr_str_clone( kv_game, 1 );
808 strcat( buf, "/" );
809 }
810 else
811 {
812 buf = cxr_str_clone( fs->exedir, strlen(kv_game)+1 );
813 strcat( buf, kv_game );
814 strcat( buf, "/" );
815 }
816
817 char **sp = cxr_ab_empty( &fs->searchpaths );
818 *sp = buf;
819 }
820
821 vdf_free_r( info );
822
823 /* Find pack diretory */
824 char pack_path[512];
825 fs->current_archive = NULL;
826 fs->current_idx = 0x7fff;
827
828 for( int i = 0; i < fs->searchpaths.count; i ++ )
829 {
830 char **sp = cxr_ab_ptr( &fs->searchpaths, i );
831
832 strcpy( pack_path, *sp );
833 strcat( pack_path, "pak01_dir.vpk" );
834
835 fs->current_archive = fopen( pack_path, "rb" );
836
837 /* Read vpk directory */
838 if( fs->current_archive )
839 {
840 fread( &fs->vpk, sizeof(VPKHeader), 1, fs->current_archive );
841
842 fs->directory_tree = malloc( fs->vpk.TreeSize );
843 fread( fs->directory_tree, fs->vpk.TreeSize, 1, fs->current_archive );
844
845 break;
846 }
847 }
848
849 if( !fs->current_archive )
850 {
851 cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
852 "Stock models will not load!\n", fs->searchpaths.count );
853 }
854
855 fs->initialized = 1;
856 return 1;
857 }
858
859 CXR_API void cxr_fs_exit(void)
860 {
861 valve_file_system *fs = &fs_global;
862
863 for( int i = 0; i < fs->searchpaths.count; i ++ )
864 {
865 char **sp = cxr_ab_ptr( &fs->searchpaths, i );
866 free( *sp );
867 }
868
869 cxr_ab_free( &fs->searchpaths );
870
871 if( fs->directory_tree )
872 {
873 free( fs->directory_tree );
874 fs->directory_tree = NULL;
875 }
876
877 if( fs->current_archive )
878 {
879 fclose( fs->current_archive );
880 fs->current_archive = NULL;
881 }
882
883 free( fs->gamedir );
884 free( fs->exedir );
885
886 memset( fs, 0, sizeof( valve_file_system ) );
887 }
888
889 static char *cxr_vpk_read( VPKDirectoryEntry *entry, int stringbuffer )
890 {
891 valve_file_system *fs = &fs_global;
892
893 if( !fs->initialized )
894 return NULL;
895
896 char pak[1024];
897
898 /* Check if we need to change file handle */
899 if( entry->ArchiveIndex != fs->current_idx )
900 {
901 if( fs->current_archive )
902 fclose( fs->current_archive );
903
904 fs->current_archive = NULL;
905 fs->current_idx = entry->ArchiveIndex;
906
907 if( entry->ArchiveIndex == 0x7fff )
908 {
909 snprintf( pak, 1023, "%scsgo/pak01_dir.vpk", fs->exedir );
910 cxr_log( "locate %s\n", pak );
911 }
912 else
913 {
914 snprintf( pak, 1023, "%scsgo/pak01_%03hu.vpk",
915 fs->exedir, entry->ArchiveIndex );
916 cxr_log( "locate %s\n", pak );
917 }
918
919 fs->current_archive = fopen( pak, "rb" );
920
921 if( !fs->current_archive )
922 cxr_log( "Warning: could not locate %s\n", pak );
923 }
924
925 if( !fs->current_archive )
926 return NULL;
927
928 size_t offset = entry->EntryOffset,
929 length = entry->EntryLength;
930
931 /*
932 * File is stored in the index, after the tree
933 */
934 if( entry->ArchiveIndex == 0x7fff )
935 {
936 offset += fs->vpk.TreeSize + sizeof(VPKHeader);
937 }
938
939 /*
940 * Entire file is stored in the preload bytes;
941 * Backtrack offset from directory to get absolute offset
942 */
943 if( length == 0 )
944 {
945 offset = (char *)entry - (char *)fs->directory_tree;
946 offset += sizeof( VPKHeader );
947 offset += sizeof( VPKDirectoryEntry );
948
949 length = entry->PreloadBytes;
950 }
951 else
952 length += entry->PreloadBytes;
953
954 fseek( fs->current_archive, offset, SEEK_SET );
955
956 size_t alloc_size = stringbuffer? length+1: length;
957 char *filebuf = malloc( alloc_size ),
958 *fullfile = filebuf;
959
960 if( stringbuffer )
961 fullfile[length] = 0x00;
962
963 if( entry->PreloadBytes )
964 {
965 memcpy( filebuf, ((char *)entry) + sizeof(VPKDirectoryEntry),
966 entry->PreloadBytes );
967
968 filebuf += entry->PreloadBytes;
969 length -= entry->PreloadBytes;
970
971 /* TODO, this can sometimes come from the same index file.
972 * very small performance gain can be made */
973 }
974
975 if( fread( filebuf, 1, length, fs->current_archive ) == length )
976 {
977 return fullfile;
978 }
979 else
980 {
981 /* Invalid read */
982 free( filebuf );
983 return NULL;
984 }
985 }
986
987 CXR_API i32 cxr_fs_find( const char *path, fs_locator *locator )
988 {
989 valve_file_system *fs = &fs_global;
990
991 if( !fs->initialized )
992 return 0;
993
994 VPKDirectoryEntry *entry;
995
996 if( fs->directory_tree )
997 {
998 if( (entry = vpk_find( path )) )
999 {
1000 locator->vpk_entry = entry;
1001 locator->path[0] = 0x00;
1002 return 1;
1003 }
1004 }
1005
1006 locator->vpk_entry = NULL;
1007
1008 /* Use physical search paths */
1009 for( int i = 0; i < fs->searchpaths.count; i ++ )
1010 {
1011 char **sp = cxr_ab_ptr( &fs->searchpaths, i );
1012
1013 snprintf( locator->path, 1024, "%s%s", *sp, path );
1014
1015 if( cxr_file_exists( locator->path ) )
1016 return 1;
1017 }
1018
1019 /* File not found */
1020 return 0;
1021 }
1022
1023 CXR_API void cxr_fs_free( void *data )
1024 {
1025 free( data );
1026 }
1027
1028 CXR_API void *cxr_fs_get( const char *path, i32 stringbuffer )
1029 {
1030 valve_file_system *fs = &fs_global;
1031
1032 if( !fs->initialized )
1033 return NULL;
1034
1035 fs_locator locator;
1036
1037 if( cxr_fs_find( path, &locator ) )
1038 {
1039 if( locator.vpk_entry )
1040 {
1041 return cxr_vpk_read( locator.vpk_entry, stringbuffer );
1042 }
1043 else
1044 {
1045 char *filebuf;
1046
1047 if( stringbuffer )
1048 return cxr_textasset_read( locator.path );
1049 else
1050 return cxr_asset_read( locator.path );
1051 }
1052 }
1053
1054 return NULL;
1055 }
1056
1057 /*
1058 * VMDL interface
1059 *
1060 * This software is not affiliated with Valve Corporation
1061 * We are not affiliated, associated, authorized, endorsed by, or in any way
1062 * officially connected with Valve Corporation, or any of its subsidiaries or
1063 * its affiliates.
1064 *
1065 * All trademarks are property of their respective owners
1066 */
1067
1068 #define MAX_NUM_LODS 8
1069 #define MAX_NUM_BONES_PER_VERT 3
1070
1071 #pragma pack(push, 1)
1072 typedef struct
1073 {
1074 float weight[MAX_NUM_BONES_PER_VERT];
1075 char bone[MAX_NUM_BONES_PER_VERT];
1076 char numbones;
1077 }
1078 boneWeight_t;
1079
1080 typedef struct
1081 {
1082 boneWeight_t boneweights;
1083 float pos[3];
1084 float norm[3];
1085 float uv[2];
1086 }
1087 mstudiovertex_t;
1088
1089 typedef struct
1090 {
1091 int id;
1092 int version;
1093 int checksum;
1094 int numLods;
1095 int numLodVertexes[MAX_NUM_LODS];
1096 int numFixups;
1097 int fixupTableStart;
1098 int vertexDataStart;
1099 int tangentDataStart;
1100 }
1101 vertexFileHeader_t;
1102
1103 #pragma pack(pop)
1104
1105 static mstudiovertex_t *GetVertexData( vertexFileHeader_t *t )
1106 {
1107 return (mstudiovertex_t *) ( (char *)t + t->vertexDataStart );
1108 }
1109
1110 /*
1111 * VTX file format
1112 */
1113
1114 #pragma pack(push, 1)
1115
1116 typedef struct
1117 {
1118 unsigned char boneWeightIndex[3];
1119 unsigned char numBones;
1120
1121 unsigned short origMeshVertID;
1122 char boneID[3];
1123 }
1124 VTXVertex_t;
1125
1126 enum StripGroupFlags
1127 {
1128 STRIPGROUP_IS_FLEXED = 0x01,
1129 STRIPGROUP_IS_HWSKINNED = 0x02,
1130 STRIPGROUP_IS_DELTA_FLEXED = 0x04,
1131 STRIPGROUP_SUPPRESS_HW_MORPH = 0x08
1132 };
1133
1134 enum StripHeaderFlags_t {
1135 STRIP_IS_TRILIST = 0x01,
1136 STRIP_IS_TRISTRIP = 0x02 /* Unused by studiomdl 2015? */
1137 };
1138
1139 typedef struct
1140 {
1141 int numIndices;
1142 int indexOffset;
1143
1144 int numVerts;
1145 int vertOffset;
1146
1147 short numBones;
1148
1149 unsigned char flags;
1150
1151 int numBoneStateChanges;
1152 int boneStateChangeOffset;
1153 }
1154 VTXStripHeader_t;
1155
1156 typedef struct
1157 {
1158 int numVerts;
1159 int vertOffset;
1160
1161 int numIndices;
1162 int indexOffset;
1163
1164 int numStrips;
1165 int stripOffset;
1166
1167 unsigned char flags;
1168 }
1169 VTXStripGroupHeader_t;
1170
1171 static VTXVertex_t *pVertexVTX( VTXStripGroupHeader_t *t, int i )
1172 {
1173 return (VTXVertex_t *)(((char *)t) + t->vertOffset) + i;
1174 }
1175
1176 static unsigned short *pIndexVTX( VTXStripGroupHeader_t *t, int i )
1177 {
1178 return (unsigned short *)(((char *)t) + t->indexOffset) + i;
1179 }
1180
1181 static VTXStripHeader_t *pStripVTX( VTXStripGroupHeader_t *t, int i )
1182 {
1183 return (VTXStripHeader_t *)(((char *)t) + t->stripOffset) + i;
1184 }
1185
1186 typedef struct
1187 {
1188 int numStripGroups;
1189 int stripGroupHeaderOffset;
1190
1191 unsigned char flags;
1192 }
1193 VTXMeshHeader_t;
1194 static VTXStripGroupHeader_t *pStripGroupVTX( VTXMeshHeader_t *t, int i )
1195 {
1196 return (VTXStripGroupHeader_t *)(((char *)t) + t->stripGroupHeaderOffset) +i;
1197 }
1198
1199 typedef struct
1200 {
1201 int numMeshes;
1202 int meshOffset;
1203
1204 float switchPoint;
1205 }
1206 VTXModelLODHeader_t;
1207 static VTXMeshHeader_t *pMeshVTX( VTXModelLODHeader_t *t, int i )
1208 {
1209 return (VTXMeshHeader_t *)(((char *)t) + t->meshOffset) + i;
1210 }
1211
1212 typedef struct
1213 {
1214 int numLODs;
1215 int lodOffset;
1216 }
1217 VTXModelHeader_t;
1218 static VTXModelLODHeader_t *pLODVTX( VTXModelHeader_t *t, int i )
1219 {
1220 return (VTXModelLODHeader_t *)(((char *)t) + t->lodOffset) + i;
1221 }
1222
1223 typedef struct
1224 {
1225 int numModels;
1226 int modelOffset;
1227 }
1228 VTXBodyPartHeader_t;
1229 static VTXModelHeader_t *pModelVTX( VTXBodyPartHeader_t *t, int i )
1230 {
1231 return (VTXModelHeader_t *)(((char *)t) + t->modelOffset) + i;
1232 }
1233
1234 typedef struct
1235 {
1236 int version; /* 7 */
1237
1238 /* hardware params that affect how the model is to be optimized. */
1239 int vertCacheSize;
1240 unsigned short maxBonesPerStrip;
1241 unsigned short maxBonesPerTri;
1242 int maxBonesPerVert;
1243
1244 int checkSum;
1245 int numLODs;
1246 int materialReplacementListOffset;
1247 int numBodyParts;
1248 int bodyPartOffset;
1249 }
1250 VTXFileHeader_t;
1251 static VTXBodyPartHeader_t *pBodyPartVTX( VTXFileHeader_t *t, int i )
1252 {
1253 return (VTXBodyPartHeader_t *)(((char *)t) + t->bodyPartOffset) + i;
1254 }
1255
1256 /*
1257 .VTX file structure
1258 =============================================
1259
1260 FileHeader
1261 L BodyParts::
1262 L Models::
1263 L LODS::
1264 L Meshes::
1265 L StripGroups::
1266 L VerticesTable[StudioMDL.Vertex]
1267 L IndicesTable[UINT16]
1268 |
1269 L Strips::
1270 L Vertices[UINT16]
1271 L Indices[UINT16]
1272 */
1273
1274 #pragma pack(pop)
1275
1276 /*
1277 * mdl format
1278 */
1279
1280 #pragma pack(push, 1)
1281
1282 typedef struct
1283 {
1284 void *pVertexData;
1285 void *pTangentData;
1286 }
1287 mstudio_modelvertexdata_t;
1288
1289 typedef struct
1290 {
1291 int unused_modelvertexdata;
1292 int numLODVertexes[MAX_NUM_LODS];
1293
1294 mstudio_modelvertexdata_t *_the_death_ptr;
1295 }
1296 mstudio_meshvertexdata_t;
1297
1298 typedef struct mstudiomodel_t mstudiomodel_t;
1299 typedef struct
1300 {
1301 int material;
1302 int modelindex;
1303 int numvertices;
1304 int vertexoffset;
1305 int numflexes;
1306 int flexindex;
1307 int materialtype;
1308 int materialparam;
1309 int meshid;
1310 float center[3];
1311 mstudio_meshvertexdata_t vertexdata;
1312 int unused[6];
1313 }
1314 mstudiomesh_t;
1315
1316 struct mstudiomodel_t
1317 {
1318 char name[64];
1319 int type;
1320 float boundingradius;
1321
1322 int nummeshes;
1323 int meshindex;
1324
1325 int numvertices;
1326 int vertexindex;
1327 int tangentsindex;
1328
1329 int numattachments;
1330 int attachmentindex;
1331
1332 int numeyeballs;
1333 int eyeballindex;
1334
1335 mstudio_modelvertexdata_t vertexdata;
1336
1337 int unused[8];
1338 };
1339 static mstudiomesh_t *studiomodel_pMesh( mstudiomodel_t *t, int i )
1340 {
1341 return (mstudiomesh_t *)(((char *)t) + t->meshindex) + i;
1342 }
1343
1344 typedef struct
1345 {
1346 int sznameindex;
1347 int nummodels;
1348 int base;
1349 int modelindex;
1350 } mstudiobodyparts_t;
1351
1352 static mstudiomodel_t *mstudiobodyparts_pModel( mstudiobodyparts_t *t, int i )
1353 {
1354 return (mstudiomodel_t *)(((char *)t) + t->modelindex) + i;
1355 }
1356
1357 typedef struct
1358 {
1359 int id;
1360 int version;
1361 int checksum;
1362 char name[64];
1363 int length;
1364 float eyeposition[3];
1365 float illumposition[3];
1366 float hull_min[3];
1367 float hull_max[3];
1368 float view_bbmin[3];
1369 float view_bbmax[3];
1370 int flags;
1371 int numbones;
1372 int boneindex;
1373 int numbonecontrollers;
1374 int bonecontrollerindex;
1375 int numhitboxsets;
1376 int hitboxsetindex;
1377 int numlocalanim;
1378 int localanimindex;
1379 int numlocalseq;
1380 int localseqindex;
1381 int activitylistversion;
1382 int eventsindexed;
1383 int numtextures;
1384 int textureindex;
1385 int numcdtextures;
1386 int cdtextureindex;
1387 int numskinref;
1388 int numskinfamilies;
1389 int skinindex;
1390 int numbodyparts;
1391 int bodypartindex;
1392 int numlocalattachments;
1393 int localattachmentindex;
1394 int numlocalnodes;
1395 int localnodeindex;
1396 int localnodenameindex;
1397 int numflexdesc;
1398 int flexdescindex;
1399 int numflexcontrollers;
1400 int flexcontrollerindex;
1401 int numflexrules;
1402 int flexruleindex;
1403 int numikchains;
1404 int ikchainindex;
1405 int nummouths;
1406 int mouthindex;
1407 int numlocalposeparameters;
1408 int localposeparamindex;
1409 int surfacepropindex;
1410 int keyvalueindex;
1411 int keyvaluesize;
1412 int numlocalikautoplaylocks;
1413 int localikautoplaylockindex;
1414 float mass;
1415 int contents;
1416 int numincludemodels;
1417 int includemodelindex;
1418 int szanimblocknameindex;
1419 int numanimblocks;
1420 int animblockindex;
1421 int bonetablebynameindex;
1422 char constdirectionallightdot;
1423 char rootLOD;
1424 char numAllowedRootLODs;
1425 char unused[1];
1426 int unused4;
1427 int numflexcontrollerui;
1428 int flexcontrolleruiindex;
1429 float flVertAnimFixedPointScale;
1430 int unused3[1];
1431 int studiohdr2index;
1432 int unused2[1];
1433 }
1434 studiohdr_t;
1435
1436 static char *studiohdr_pCdtexture( studiohdr_t *t, int i )
1437 {
1438 return (((char *)t) + *((int *)(((u8 *)t) + t->cdtextureindex) + i));
1439 }
1440
1441 static mstudiobodyparts_t *studiohdr_pBodypart( studiohdr_t *t, int i )
1442 {
1443 return (mstudiobodyparts_t *)(((char *)t) + t->bodypartindex) + i;
1444 }
1445
1446 typedef struct
1447 {
1448 int sznameindex;
1449 int flags;
1450 int used;
1451 int unused1;
1452 int garbage[12]; /* contains some studiomdl pointers */
1453 }
1454 mstudiotexture_t;
1455
1456 static char *mstudiotexture_pszName( mstudiotexture_t *t )
1457 {
1458 return ((char *)t) + t->sznameindex;
1459 }
1460
1461 static mstudiotexture_t *studiohdr_pTexture( studiohdr_t *t, int i )
1462 {
1463 return (mstudiotexture_t *)(((u8 *)t) + t->textureindex) + i;
1464 }
1465
1466 #pragma pack(pop)
1467
1468 static void vtx_resource_counts( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl,
1469 u32 *indices, u32 *meshes )
1470 {
1471 *indices = 0;
1472 *meshes = 0;
1473
1474 for( int bodyID = 0; bodyID < pVtxHdr->numBodyParts; ++bodyID )
1475 {
1476 /* Body parts */
1477 VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( pVtxHdr, bodyID );
1478 mstudiobodyparts_t *pBodyPart = studiohdr_pBodypart( pMdl, bodyID );
1479
1480 for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
1481 {
1482 /* models */
1483 VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
1484 mstudiomodel_t *pStudioModel =
1485 mstudiobodyparts_pModel( pBodyPart, modelID );
1486
1487 int nLod = 0;
1488 VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
1489
1490 for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
1491 {
1492 /* meshes */
1493 VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
1494 mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
1495
1496 (*meshes)++;
1497
1498 for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
1499 {
1500 /* groups */
1501 VTXStripGroupHeader_t* pStripGroup =
1502 pStripGroupVTX( pVtxMesh, nGroup );
1503
1504 for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
1505 {
1506 /* strips */
1507 VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
1508
1509 if ( pStrip->flags & STRIP_IS_TRILIST )
1510 {
1511 *indices += pStrip->numIndices;
1512 }
1513 }
1514 }
1515 }
1516 }
1517 }
1518 }
1519
1520 /*
1521 * The following section is the wrappers for the underlying types
1522 */
1523
1524 struct valve_material
1525 {
1526 char *basetexture,
1527 *bumpmap;
1528 };
1529
1530 struct valve_model
1531 {
1532 float *vertex_data; /* pos xyz, norm xyz, uv xy */
1533
1534 u32 *indices,
1535 indices_count,
1536 vertex_count,
1537 part_count,
1538 material_count;
1539
1540 char **materials;
1541
1542 struct valve_model_batch
1543 {
1544 u32 material,
1545 ibstart,
1546 ibcount;
1547 }
1548 *parts;
1549
1550 /* Internal valve data */
1551 studiohdr_t *studiohdr;
1552 VTXFileHeader_t *vtxhdr;
1553 vertexFileHeader_t *vvdhdr;
1554 };
1555
1556 CXR_API valve_model *valve_load_model( const char *relpath )
1557 {
1558 char path[1024];
1559 strcpy( path, relpath );
1560
1561 char *ext = cxr_stripext( path );
1562
1563 if( !ext )
1564 return NULL;
1565
1566 /* Load data files */
1567 valve_model *model = malloc( sizeof( valve_model ) );
1568 model->studiohdr = NULL;
1569 model->vtxhdr = NULL;
1570 model->vvdhdr = NULL;
1571
1572 strcpy( ext, ".dx90.vtx" );
1573 model->vtxhdr = cxr_fs_get( path, 0 );
1574
1575 strcpy( ext, ".vvd" );
1576 model->vvdhdr = cxr_fs_get( path, 0 );
1577
1578 strcpy( ext, ".mdl" );
1579 model->studiohdr = cxr_fs_get( path, 0 );
1580
1581 if( !model->vvdhdr || !model->studiohdr || !model->vtxhdr )
1582 {
1583 cxr_log( "Error, failed to load: (%s)\n", relpath );
1584
1585 free( model->studiohdr );
1586 free( model->vvdhdr );
1587 free( model->studiohdr );
1588 free( model );
1589
1590 return NULL;
1591 }
1592
1593 /* allocate resources */
1594 vtx_resource_counts( model->vtxhdr, model->studiohdr,
1595 &model->indices_count, &model->part_count );
1596 model->vertex_count = model->vvdhdr->numLodVertexes[0];
1597 model->material_count = model->studiohdr->numtextures;
1598
1599 model->materials = malloc( model->material_count * sizeof(char *) );
1600 model->parts = malloc( sizeof( valve_model_batch ) * model->part_count );
1601 model->indices = malloc( sizeof( u32 ) * model->indices_count );
1602 model->vertex_data = malloc( sizeof( float ) * 8 * model->vertex_count );
1603
1604 /* Find materials */
1605 for( int i=0; i<model->studiohdr->numtextures; i++ )
1606 {
1607 char material_path[ 1024 ];
1608 fs_locator locator;
1609
1610 mstudiotexture_t *tex = studiohdr_pTexture( model->studiohdr, i );
1611 const char *name = mstudiotexture_pszName( tex );
1612
1613 model->materials[i] = NULL;
1614
1615 cxr_log( "search: %s\n", name );
1616
1617 for( int j=0; j<model->studiohdr->numcdtextures; j++ )
1618 {
1619 char *cdpath = studiohdr_pCdtexture( model->studiohdr, j );
1620 snprintf( material_path, 1023, "materials/%s%s.vmt", cdpath, name );
1621 cxr_unixpath( material_path );
1622 cxr_lowercase( material_path );
1623
1624 if( cxr_fs_find( material_path, &locator ))
1625 {
1626 model->materials[i] = cxr_str_clone( material_path, 0 );
1627 break;
1628 }
1629 }
1630
1631 }
1632
1633 u32 i_index = 0,
1634 i_mesh = 0;
1635
1636 /* Extract meshes */
1637 for( int bodyID = 0; bodyID < model->studiohdr->numbodyparts; ++bodyID )
1638 {
1639 /* Body parts */
1640 VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( model->vtxhdr, bodyID );
1641 mstudiobodyparts_t *pBodyPart =
1642 studiohdr_pBodypart( model->studiohdr, bodyID );
1643
1644 for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
1645 {
1646 /* models */
1647 VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
1648 mstudiomodel_t *pStudioModel =
1649 mstudiobodyparts_pModel( pBodyPart, modelID );
1650
1651 int nLod = 0;
1652 VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
1653
1654 for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
1655 {
1656 /* meshes, each of these creates a new draw CMD */
1657 VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
1658 mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
1659
1660 valve_model_batch *curBatch = &model->parts[ i_mesh ++ ];
1661 curBatch->material = pMesh->material;
1662 curBatch->ibstart = i_index;
1663 curBatch->ibcount = 0;
1664
1665 for( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
1666 {
1667 /* groups */
1668 VTXStripGroupHeader_t* pStripGroup =
1669 pStripGroupVTX( pVtxMesh, nGroup );
1670
1671 for( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
1672 {
1673 /* strips */
1674 VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
1675
1676 if( pStrip->flags & STRIP_IS_TRILIST )
1677 {
1678 /* indices */
1679 for( int i = 0; i < pStrip->numIndices; i ++ )
1680 {
1681 u16 i1 = *pIndexVTX( pStripGroup,
1682 pStrip->indexOffset + i );
1683
1684 model->indices[ i_index ++ ] =
1685 pVertexVTX( pStripGroup, i1 )->origMeshVertID +
1686 pMesh->vertexoffset;
1687
1688 curBatch->ibcount ++;
1689 }
1690 }
1691 else
1692 {
1693 /* This is unused? */
1694 }
1695 }
1696 }
1697 }
1698 }
1699 }
1700
1701 mstudiovertex_t *vertexData = GetVertexData( model->vvdhdr );
1702
1703 for( int i = 0; i < model->vertex_count; i ++ )
1704 {
1705 mstudiovertex_t *vert = &vertexData[i];
1706
1707 float *dest = &model->vertex_data[ i*8 ];
1708
1709 dest[0] = vert->pos[0];
1710 dest[1] = vert->pos[1];
1711 dest[2] = vert->pos[2];
1712
1713 dest[3] = vert->norm[0];
1714 dest[4] = vert->norm[1];
1715 dest[5] = vert->norm[2];
1716
1717 dest[6] = vert->uv[0];
1718 dest[7] = vert->uv[1];
1719 }
1720
1721 return model;
1722 }
1723
1724 CXR_API void valve_free_model( valve_model *model )
1725 {
1726 for( int i=0; i<model->material_count; i++ )
1727 free( model->materials[i] );
1728
1729 free( model->materials );
1730 free( model->parts );
1731 free( model->indices );
1732 free( model->vertex_data );
1733
1734 free( model->studiohdr );
1735 free( model->vtxhdr );
1736 free( model->vvdhdr );
1737 free( model );
1738 }
1739
1740 static char *valve_texture_path( const char *path )
1741 {
1742 if( !path )
1743 return NULL;
1744
1745 char *buf =
1746 malloc( strlen( path ) + strlen(".vtf") + strlen("materials/") +1 );
1747 strcpy( buf, "materials/" );
1748 strcat( buf, path );
1749 strcat( buf, ".vtf" );
1750
1751 cxr_unixpath( buf );
1752 cxr_lowercase( buf );
1753
1754 return buf;
1755 }
1756
1757 static void cxr_str_debug( const char *str )
1758 {
1759 cxr_log( "\n=== string debug ( %p ) === \n'", str );
1760 const char *y = str;
1761
1762 while(*y)
1763 {
1764 if( *y == '\r' )
1765 cxr_log( "<CR>" );
1766 else if( *y == '\n' )
1767 cxr_log( "<LF>\n" );
1768 else
1769 cxr_log( "%c", *y);
1770 y ++;
1771 }
1772 cxr_log( "<EOF>'\n" );
1773 }
1774
1775 CXR_API valve_material *valve_load_material( const char *path )
1776 {
1777 char *vmt = cxr_fs_get( path, 1 );
1778
1779 if( vmt )
1780 {
1781 #if 0
1782 cxr_str_debug( vmt );
1783 #endif
1784 valve_material *material = malloc( sizeof(valve_material) );
1785 vdf_node *vmt_root = vdf_from_buffer( vmt );
1786
1787 if( vmt_root->abnodes.count == 0 )
1788 {
1789 cxr_log( "Error: vmt has no nodes\n" );
1790 free( vmt );
1791 free( material );
1792 vdf_free_r( vmt_root );
1793 return NULL;
1794 }
1795
1796 vdf_node **body = cxr_ab_ptr( &vmt_root->abnodes, 0 );
1797
1798 /* Path semantics here are inconsistent
1799 * I believe they should all just be converted to lowercase, though */
1800
1801 for( int i=0; i<(*body)->abpairs.count; i++ )
1802 {
1803 vdf_kv *kv = cxr_ab_ptr( &(*body)->abpairs, i );
1804 cxr_lowercase( kv->key );
1805 }
1806
1807 const char *basetexture = kv_get( *body, "$basetexture", NULL ),
1808 *bumpmap = kv_get( *body, "$bumpmap", NULL );
1809
1810 /* TODO: other shader parameters */
1811 material->basetexture = valve_texture_path( basetexture );
1812 material->bumpmap = valve_texture_path( bumpmap );
1813
1814 vdf_free_r( vmt_root );
1815 free(vmt);
1816
1817 return material;
1818 }
1819
1820 return NULL;
1821 }
1822
1823 CXR_API void valve_free_material( valve_material *material )
1824 {
1825 free( material->basetexture );
1826 free( material->bumpmap );
1827 free( material );
1828 }
1829
1830 #endif /* CXR_VALVE_BIN_H */