model view prototype
[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 valve_file_system valve_file_system;
28
29 /*
30 * File system
31 */
32
33 static struct valve_file_system
34 {
35 char *gamedir,
36 *exedir;
37
38 /* Runtime */
39 VPKHeader *vpk;
40
41 cxr_abuffer searchpaths;
42
43 FILE *current_archive;
44 u16 current_idx;
45
46 int initialized;
47 }
48 fs_global = { .initialized = 0 };
49
50 CXR_API int cxr_fs_set_gameinfo( const char *path ); /* Setup system */
51 CXR_API void cxr_fs_exit(void); /* Clean up */
52 CXR_API char *cxr_fs_get( const char *path ); /* Get a file */
53
54 /*
55 * VPK reading
56 */
57
58 static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset );
59 static void vpk_free( VPKHeader *self );
60
61 /*
62 * VDF reading
63 */
64
65 static vdf_node *vdf_open_file( const char *fn );
66 static void vdf_free_r( vdf_node *p );
67
68 /* Starting from *it, get next child with matching name from node. */
69 static vdf_node *vdf_next( vdf_node *node, const char *name, int *it );
70
71 /* Create new empty node attached to parent. name can be NULL */
72 static vdf_node *vdf_create_node( vdf_node *parent, const char *name );
73
74 /* KV access */
75 static const char *kv_get( vdf_node *node, const char *key,
76 const char *value_defalt );
77
78 /* Iterate each keyvalue starting from *it until key is matched */
79 static char *kv_next( vdf_node *node, const char *key, int *it );
80
81 static int kv_get_int( vdf_node *node, const char *key, const int fallback );
82 static float kv_get_float( vdf_node *node, const char *key, float fallback );
83
84 static void kv_int_array( vdf_node *node, const char *key, u32 count,
85 int *arr );
86 static void kv_float_array( vdf_node *node, const char *key, u32 count,
87 float *arr );
88 static void kv_double_array( vdf_node *node, const char *key, u32 count,
89 double *arr );
90
91 #define vdf_foreach( NODE, STR, AS ) \
92 int __vdf_it_##AS = 0; \
93 vdf_node * AS;\
94 while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
95
96 #define kv_foreach( NODE, STR, AS ) \
97 int __kv_it_##AS = 0; \
98 const char * AS;\
99 while( (AS = kv_next( NODE, STR, &__kv_it_##AS )) )
100 #pragma pack(push, 1)
101
102 struct VPKHeader
103 {
104 u32 Signature;
105 u32 Version;
106 u32 TreeSize;
107 u32 FileDataSectionSize;
108 u32 ArchiveMD5SectionSize;
109 u32 OtherMD5SectionSize;
110 u32 SignatureSectionSize;
111 };
112
113 struct VPKDirectoryEntry
114 {
115 u32 CRC;
116 u16 PreloadBytes;
117 u16 ArchiveIndex;
118 u32 EntryOffset;
119 u32 EntryLength;
120 u16 Terminator;
121 };
122 #pragma pack(pop)
123
124 static void vpk_free( VPKHeader *self )
125 {
126 free( self );
127 }
128
129 static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset )
130 {
131 if( !self )
132 return NULL;
133
134 char wbuf[ 512 ];
135 strcpy( wbuf, asset );
136
137 /*
138 * This currently fails if the filename doesn't have a file extension, or
139 * if it is located at the root path. I'm not sure if this is defined
140 * behaviour or not. TODO: allow it to work anyway
141 */
142
143 char *ext = cxr_findext( wbuf, '.' );
144
145 if( !ext ) return NULL;
146 *(ext-1) = 0x00;
147
148 char *fn = cxr_findext( wbuf, '/' );
149
150 if( !fn ) return NULL;
151 *(fn-1) = 0x00;
152
153 char *dir = wbuf;
154 char *pCur = ((char *)self) + sizeof( VPKHeader );
155
156 while( 1 )
157 {
158 if( !*pCur ) break;
159
160 int bExt = !strcmp( ext, pCur );
161
162 while( *( pCur ++ ) ) {};
163 while( 1 )
164 {
165 if( !*pCur ) { pCur ++; break; }
166
167 int bDir = !strcmp( dir, pCur );
168
169 while( *( pCur ++ ) ) {};
170 while( 1 )
171 {
172 if( !*pCur ) { pCur ++; break; }
173
174 const char *vpk_fn = pCur;
175
176 while( *( pCur ++ ) ) {};
177 VPKDirectoryEntry *entry = (VPKDirectoryEntry *)pCur;
178
179 if( !strcmp( vpk_fn, fn ) && bExt && bDir )
180 {
181 return entry;
182 }
183
184 pCur += entry->PreloadBytes + sizeof( VPKDirectoryEntry );
185 }
186
187 if( bDir && bExt ) return NULL;
188 }
189
190 if( bExt ) return NULL;
191 }
192
193 return NULL;
194 }
195
196 /*
197 * VDF
198 */
199
200 struct vdf_kv
201 {
202 char *key;
203 char *value;
204 };
205
206 struct vdf_node
207 {
208 char *name;
209
210 vdf_node *parent;
211
212 cxr_abuffer abnodes,
213 abpairs;
214
215 u32 user;
216 u32 user1;
217 };
218
219 static vdf_node *vdf_next( vdf_node *node, const char *name, int *it )
220 {
221 if( !node )
222 return NULL;
223
224 for( int i = it? *it: 0; i < node->abnodes.count; i ++ )
225 {
226 vdf_node **ptr_child = cxr_ab_ptr( &node->abnodes, i ),
227 *child = *ptr_child;
228
229 if( !name || !strcmp( name, child->name ))
230 {
231 if( it ) *it = i+1;
232 return child;
233 }
234 }
235
236 return NULL;
237 }
238
239 static char *kv_next( vdf_node *node, const char *key, int *it )
240 {
241 char *val;
242
243 if( node )
244 {
245 while( *it < node->abpairs.count )
246 {
247 vdf_kv *kv = cxr_ab_ptr( &node->abpairs, *it );
248
249 if( !strcmp( kv->key, key ) )
250 {
251 val = kv->value;
252 *it = *it + 1;
253 return val;
254 }
255
256 *it = *it + 1;
257 }
258 }
259
260 return NULL;
261 }
262
263 static const char *kv_get( vdf_node *node, const char *key,
264 const char *value_defalt )
265 {
266 int it = 0;
267 char *val = kv_next( node, key, &it );
268
269 if( val )
270 return val;
271
272 return value_defalt;
273 }
274
275 static void vdf_str_to_int( const char *src, void *dest )
276 {
277 *((int *)dest) = atoi( src );
278 }
279
280 static void vdf_str_to_float( const char *src, void *dest )
281 {
282 *((float *)dest) = atof( src );
283 }
284
285 static void vdf_str_to_double( const char *src, void *dest )
286 {
287 *((double *)dest) = atof( src );
288 }
289
290 static void kv_parse_array( const char *source, u32 esize, u32 count,
291 void(*interp_func)(const char *src, void *dest), void *arr )
292 {
293 if( !source )
294 return;
295
296 char value_buf[ 64 ];
297 int token = 0;
298
299 u32 i = 0;
300 u32 k = 0;
301
302 char const *c = source;
303
304 while( *c )
305 {
306 if( *c==' ' || *c=='\t' || *c=='[' || *c==']' || *c=='(' || *c==')' )
307 {
308 if( token )
309 {
310 value_buf[ k ] = 0x00;
311 token = 0;
312
313 interp_func( value_buf, ((u8 *)arr) + i*esize );
314 i ++;
315
316 if( i >= count )
317 {
318 break;
319 }
320 }
321 }
322 else
323 {
324 if( !token )
325 {
326 token = 1;
327 k = 0;
328 }
329
330 if( token )
331 {
332 if( k < sizeof( value_buf ) - 1 )
333 {
334 value_buf[ k ++ ] = *c;
335 }
336 }
337 }
338
339 c ++;
340 }
341
342 /* Add remaining case if we hit null */
343 if( token && (i < count) )
344 {
345 value_buf[ k ] = 0x00;
346 interp_func( value_buf, ((u8 *)arr) + i*esize );
347 }
348 }
349
350 static void kv_int_array( vdf_node *node, const char *key, u32 count, int *arr )
351 {
352 kv_parse_array( kv_get( node, key, NULL ), sizeof(int), count,
353 vdf_str_to_int, arr );
354 }
355
356 static void kv_float_array( vdf_node *node, const char *key, u32 count,
357 float *arr )
358 {
359 kv_parse_array( kv_get( node, key, NULL ), sizeof(float), count,
360 vdf_str_to_float, arr );
361 }
362
363 static void kv_double_array( vdf_node *node, const char *key, u32 count,
364 double *arr )
365 {
366 kv_parse_array( kv_get( node, key, NULL ), sizeof(double), count,
367 vdf_str_to_double, arr );
368 }
369
370 static int kv_get_int( vdf_node *node, const char *key,
371 const int default_value )
372 {
373 const char *v = kv_get( node, key, NULL );
374 return v? atoi(v): default_value;
375 }
376
377 static float kv_get_float( vdf_node *node, const char *key,
378 float default_value )
379 {
380 const char *v = kv_get( node, key, NULL );
381 return v? atof( v ): default_value;
382 }
383
384 static vdf_node *vdf_create_node( vdf_node *parent, const char *name )
385 {
386 vdf_node *node = calloc( 1, sizeof( vdf_node ) );
387
388 /* init buffers */
389 cxr_ab_init( &node->abnodes, sizeof( vdf_node* ), 0 );
390 cxr_ab_init( &node->abpairs, sizeof( vdf_kv ), 0 );
391
392 if( name )
393 {
394 node->name = cxr_str_clone(name, 0);
395 }
396
397 if( parent )
398 {
399 node->parent = parent;
400
401 vdf_node **child = cxr_ab_empty( &parent->abnodes );
402 *child = node;
403 }
404
405 return node;
406 }
407
408 static void vdf_kv_append( vdf_node *p, const char *k, const char *v )
409 {
410 vdf_kv *kv = cxr_ab_empty( &p->abpairs );
411
412 u32 sv = strlen(v)+1;
413 u32 sk = strlen(k)+1;
414
415 kv->key = malloc( sv+sk );
416 kv->value = kv->key+sk;
417
418 memcpy( kv->key, k, sk );
419 memcpy( kv->value, v, sv );
420 }
421
422 static void vdf_free_r( vdf_node *p )
423 {
424 for( int i = 0; i < p->abpairs.count; i ++ )
425 {
426 vdf_kv *kv = cxr_ab_ptr( &p->abpairs, i );
427 free( kv->key ); /* key and value are allocated in the same buffer */
428 }
429
430 for( int i = 0; i < p->abnodes.count; i ++ )
431 {
432 vdf_node **ptr_node = cxr_ab_ptr( &p->abnodes, i );
433 vdf_free_r( *ptr_node );
434 }
435
436 cxr_ab_free( &p->abpairs );
437 cxr_ab_free( &p->abnodes );
438
439 free( p->name );
440 free( p );
441 }
442
443 /*
444 * Parsing
445 */
446
447 struct vdf_ctx
448 {
449 char name[1024];
450
451 vdf_node *root;
452
453 u32 lines;
454 u32 errors;
455
456 struct
457 {
458 int wait;
459 int expect_decl;
460
461 char *tokens[2];
462 int i;
463
464 char *ptr_read;
465
466 vdf_node *pnode;
467 }
468 st;
469 };
470
471 static void vdf_newln( vdf_ctx *ctx )
472 {
473 ctx->lines ++;
474
475 ctx->st.tokens[0] = NULL;
476 ctx->st.tokens[1] = NULL;
477 ctx->st.i = 0;
478 }
479
480 static void vdf_endl( vdf_ctx *ctx )
481 {
482 if( ctx->st.tokens[0] )
483 {
484 /* Keypair */
485 if( ctx->st.tokens[1] )
486 {
487 vdf_kv_append( ctx->st.pnode, ctx->st.tokens[0], ctx->st.tokens[1] );
488 }
489 else
490 {
491 /* decl */
492 strcpy( ctx->name, ctx->st.tokens[0] );
493 ctx->st.expect_decl = 1;
494 }
495 }
496
497 vdf_newln( ctx );
498 }
499
500 static int vdf_line_control( vdf_ctx *ctx )
501 {
502 if( *ctx->st.ptr_read == '\r' )
503 {
504 *ctx->st.ptr_read = 0x00;
505 return 1;
506 }
507 if( *ctx->st.ptr_read == '\n' )
508 {
509 *ctx->st.ptr_read = 0x00;
510 vdf_endl( ctx );
511 return 2;
512 }
513
514 return 0;
515 }
516
517 static void vdf_wait_endl( vdf_ctx *ctx )
518 {
519 while( (*ctx->st.ptr_read) && (*ctx->st.ptr_read != '\n') )
520 {
521 if( vdf_line_control( ctx ) == 2 )
522 {
523 return;
524 }
525
526 ctx->st.ptr_read ++;
527 }
528 }
529
530 static void vdf_parse_string( vdf_ctx *ctx )
531 {
532 while( *ctx->st.ptr_read )
533 {
534 if( *ctx->st.ptr_read == '"' )
535 {
536 *ctx->st.ptr_read = 0x00;
537 return;
538 }
539
540 if( vdf_line_control( ctx ) )
541 {
542 /* Unexpected end of line */
543 return;
544 }
545
546 ctx->st.ptr_read ++;
547 }
548 }
549
550 static int vdf_parse_structure( vdf_ctx *ctx )
551 {
552 if( *ctx->st.ptr_read == '{' )
553 {
554 if( ctx->st.tokens[0] || !ctx->st.expect_decl )
555 {
556 /* Unexpected token '{' */
557 ctx->errors ++;
558 }
559
560 ctx->st.expect_decl = 0;
561 ctx->st.pnode = vdf_create_node( ctx->st.pnode, ctx->name );
562
563 vdf_wait_endl( ctx );
564 return 1;
565 }
566
567 if( *ctx->st.ptr_read == '}' )
568 {
569 if( !ctx->st.pnode->parent )
570 {
571 /* Unexpected token '}' */
572 ctx->errors ++;
573 }
574 else
575 {
576 ctx->st.pnode = ctx->st.pnode->parent;
577 }
578
579 vdf_wait_endl( ctx );
580 return 1;
581 }
582
583 return 0;
584 }
585
586 static void vdf_parse_begin_token( vdf_ctx *ctx, char *ptr )
587 {
588 ctx->st.tokens[ ctx->st.i ] = ptr;
589
590 if( ctx->st.expect_decl )
591 {
592 /* Unexpected token 'name' */
593 ctx->errors ++;
594 }
595 }
596
597 static void vdf_parse_feedbuffer( vdf_ctx *ctx, char *buf )
598 {
599 ctx->st.ptr_read = buf;
600
601 while( *ctx->st.ptr_read )
602 {
603 if( !vdf_line_control( ctx ) )
604 {
605 if( (*ctx->st.ptr_read == '/') && (ctx->st.ptr_read[1] == '/') )
606 {
607 *ctx->st.ptr_read = 0x00;
608 ctx->st.ptr_read += 2;
609
610 vdf_endl( ctx );
611 vdf_wait_endl( ctx );
612 }
613 else
614 {
615 if( !vdf_parse_structure( ctx ) )
616 {
617 if( *ctx->st.ptr_read == ' ' || *ctx->st.ptr_read == '\t' )
618 {
619 *ctx->st.ptr_read = 0x00;
620
621 if( ctx->st.tokens[ ctx->st.i ] )
622 {
623 ctx->st.i ++;
624
625 if( ctx->st.i == 2 )
626 {
627 vdf_wait_endl( ctx );
628 }
629 }
630 }
631
632 /* New entry */
633 else if( !ctx->st.tokens[ ctx->st.i ] )
634 {
635 if( *ctx->st.ptr_read == '"' )
636 {
637 *ctx->st.ptr_read = 0x00;
638 ctx->st.ptr_read ++;
639
640 vdf_parse_begin_token( ctx, ctx->st.ptr_read );
641 vdf_parse_string( ctx );
642 }
643 else
644 {
645 if( !( *ctx->st.ptr_read == '/' &&
646 *(ctx->st.ptr_read + 1) == *ctx->st.ptr_read ) )
647 {
648 vdf_parse_begin_token( ctx, ctx->st.ptr_read );
649 }
650 }
651 }
652 }
653 }
654 }
655
656 ctx->st.ptr_read ++;
657 }
658 }
659
660 static int vdf_load_into( const char *fn, vdf_node *node )
661 {
662 char *text_src = cxr_textasset_read( fn );
663
664 if( !text_src )
665 {
666 return 0;
667 }
668
669 vdf_ctx ctx = {0};
670 ctx.root = ctx.st.pnode = node;
671
672 vdf_newln( &ctx );
673 vdf_parse_feedbuffer( &ctx, text_src );
674 free( text_src );
675
676 return 1;
677 }
678
679 static vdf_node *vdf_open_file( const char *fn )
680 {
681 vdf_node *root = vdf_create_node( NULL, NULL );
682 if( vdf_load_into( fn, root ) )
683 {
684 return root;
685 }
686 else
687 {
688 vdf_free_r( root );
689 return NULL;
690 }
691 }
692
693 /*
694 * File system
695 */
696
697 CXR_API i32 cxr_fs_set_gameinfo( const char *path )
698 {
699 valve_file_system *fs = &fs_global;
700
701 if( fs->initialized )
702 cxr_fs_exit();
703
704 vdf_node *info = vdf_open_file( path );
705 if( !info )
706 return 0;
707
708 fs->gamedir = cxr_str_clone( path, 0 );
709 cxr_downlvl( fs->gamedir );
710
711 fs->exedir = cxr_str_clone( fs->gamedir, 0 );
712 cxr_downlvl( fs->exedir );
713
714 cxr_log( "Setting up file system:\n"
715 "gameinfo: %s\n"
716 "gamedir: %s\n"
717 "exedir: %s\n",
718
719 path, fs->gamedir, fs->exedir );
720
721 /* get search paths */
722 vdf_node *search_paths =
723 vdf_next
724 (
725 vdf_next
726 (
727 vdf_next( info, "GameInfo", NULL ),
728 "FileSystem",
729 NULL
730 ),
731 "SearchPaths",
732 NULL
733 );
734
735 cxr_ab_init( &fs->searchpaths, sizeof( char *), 0 );
736
737 kv_foreach( search_paths, "Game", kv_game )
738 {
739 cxr_log( "Game %s\n", kv_game );
740
741 if( kv_game[0] == '|' ) continue;
742
743 char *buf;
744 if( cxr_path_is_abs( kv_game ) )
745 {
746 buf = cxr_str_clone( kv_game, 1 );
747 strcat( buf, "/" );
748 }
749 else
750 {
751 buf = cxr_str_clone( fs->exedir, strlen(kv_game)+1 );
752 strcat( buf, kv_game );
753 strcat( buf, "/" );
754 }
755
756 char **sp = cxr_ab_empty( &fs->searchpaths );
757 *sp = buf;
758 }
759
760 vdf_free_r( info );
761
762 /* Find pack diretory */
763 char pack_path[512];
764 for( int i = 0; i < fs->searchpaths.count; i ++ )
765 {
766 char **sp = cxr_ab_ptr( &fs->searchpaths, i );
767
768 strcpy( pack_path, *sp );
769 strcat( pack_path, "pak01_dir.vpk" );
770
771 if( (fs->vpk = (VPKHeader *)cxr_asset_read( pack_path )) )
772 break;
773 }
774
775 if( !fs->vpk )
776 {
777 cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
778 "Stock models will not load!\n", fs->searchpaths.count );
779 }
780
781 fs->initialized = 1;
782 return 1;
783 }
784
785 CXR_API void cxr_fs_exit(void)
786 {
787 valve_file_system *fs = &fs_global;
788
789 for( int i = 0; i < fs->searchpaths.count; i ++ )
790 {
791 char **sp = cxr_ab_ptr( &fs->searchpaths, i );
792 free( *sp );
793 }
794
795 cxr_ab_free( &fs->searchpaths );
796
797 if( fs->vpk )
798 {
799 vpk_free( fs->vpk );
800 fs->vpk = NULL;
801 }
802
803 if( fs->current_archive )
804 {
805 fclose( fs->current_archive );
806 fs->current_archive = NULL;
807 }
808
809 free( fs->gamedir );
810 free( fs->exedir );
811
812 memset( fs, 0, sizeof( valve_file_system ) );
813 }
814
815 CXR_API char *cxr_fs_get( const char *path )
816 {
817 valve_file_system *fs = &fs_global;
818
819 if( !fs->initialized )
820 return NULL;
821
822 VPKDirectoryEntry *entry;
823 char pak[ 533 ];
824
825 if( fs->vpk )
826 {
827 if( (entry = vpk_find( fs->vpk, path )) )
828 {
829 if( entry->ArchiveIndex != fs->current_idx )
830 {
831 if( fs->current_archive )
832 {
833 fclose( fs->current_archive );
834 fs->current_archive = NULL;
835 }
836
837 fs->current_idx = entry->ArchiveIndex;
838 }
839
840 if( !fs->current_archive )
841 {
842 sprintf( pak, "%scsgo/pak01_%03hu.vpk", fs->exedir,
843 fs->current_idx );
844 fs->current_archive = fopen( pak, "rb" );
845
846 if( !fs->current_archive )
847 {
848 cxr_log( "Could not locate %s\n", pak );
849 return NULL;
850 }
851 }
852
853 char *filebuf = malloc( entry->EntryLength );
854
855 fseek( fs->current_archive, entry->EntryOffset, SEEK_SET );
856 if( fread( filebuf, 1, entry->EntryLength, fs->current_archive )
857 == entry->EntryLength )
858 {
859 return filebuf;
860 }
861 else
862 {
863 free( filebuf );
864 return NULL;
865 }
866 }
867 }
868
869 /* Use physical search paths */
870 char path_buf[ 512 ];
871
872 for( int i = 0; i < fs->searchpaths.count; i ++ )
873 {
874 char **sp = cxr_ab_ptr( &fs->searchpaths, i );
875
876 strcpy( path_buf, *sp );
877 strcat( path_buf, path );
878
879 char *filebuf;
880 if( (filebuf = cxr_asset_read( path_buf )) )
881 return filebuf;
882 }
883
884 /* File not found */
885 return NULL;
886 }
887
888 /*
889 * VMDL interface
890 *
891 * This software is not affiliated with Valve Corporation
892 * We are not affiliated, associated, authorized, endorsed by, or in any way
893 * officially connected with Valve Corporation, or any of its subsidiaries or
894 * its affiliates.
895 *
896 * All trademarks are property of their respective owners
897 */
898
899 #define MAX_NUM_LODS 8
900 #define MAX_NUM_BONES_PER_VERT 3
901
902 #pragma pack(push, 1)
903 typedef struct
904 {
905 float weight[MAX_NUM_BONES_PER_VERT];
906 char bone[MAX_NUM_BONES_PER_VERT];
907 char numbones;
908 }
909 boneWeight_t;
910
911 typedef struct
912 {
913 boneWeight_t boneweights;
914 float pos[3];
915 float norm[3];
916 float uv[2];
917 }
918 mstudiovertex_t;
919
920 typedef struct
921 {
922 int id;
923 int version;
924 int checksum;
925 int numLods;
926 int numLodVertexes[MAX_NUM_LODS];
927 int numFixups;
928 int fixupTableStart;
929 int vertexDataStart;
930 int tangentDataStart;
931 }
932 vertexFileHeader_t;
933
934 #pragma pack(pop)
935
936 static mstudiovertex_t *GetVertexData( vertexFileHeader_t *t )
937 {
938 return (mstudiovertex_t *) ( (char *)t + t->vertexDataStart );
939 }
940
941 /*
942 * VTX file format
943 */
944
945 #pragma pack(push, 1)
946
947 typedef struct
948 {
949 unsigned char boneWeightIndex[3];
950 unsigned char numBones;
951
952 unsigned short origMeshVertID;
953 char boneID[3];
954 }
955 VTXVertex_t;
956
957 enum StripGroupFlags
958 {
959 STRIPGROUP_IS_FLEXED = 0x01,
960 STRIPGROUP_IS_HWSKINNED = 0x02,
961 STRIPGROUP_IS_DELTA_FLEXED = 0x04,
962 STRIPGROUP_SUPPRESS_HW_MORPH = 0x08
963 };
964
965 enum StripHeaderFlags_t {
966 STRIP_IS_TRILIST = 0x01,
967 STRIP_IS_TRISTRIP = 0x02 /* Unused by studiomdl 2015? */
968 };
969
970 typedef struct
971 {
972 int numIndices;
973 int indexOffset;
974
975 int numVerts;
976 int vertOffset;
977
978 short numBones;
979
980 unsigned char flags;
981
982 int numBoneStateChanges;
983 int boneStateChangeOffset;
984 }
985 VTXStripHeader_t;
986
987 typedef struct
988 {
989 int numVerts;
990 int vertOffset;
991
992 int numIndices;
993 int indexOffset;
994
995 int numStrips;
996 int stripOffset;
997
998 unsigned char flags;
999 }
1000 VTXStripGroupHeader_t;
1001
1002 static VTXVertex_t *pVertexVTX( VTXStripGroupHeader_t *t, int i )
1003 {
1004 return (VTXVertex_t *)(((char *)t) + t->vertOffset) + i;
1005 }
1006
1007 static unsigned short *pIndexVTX( VTXStripGroupHeader_t *t, int i )
1008 {
1009 return (unsigned short *)(((char *)t) + t->indexOffset) + i;
1010 }
1011
1012 static VTXStripHeader_t *pStripVTX( VTXStripGroupHeader_t *t, int i )
1013 {
1014 return (VTXStripHeader_t *)(((char *)t) + t->stripOffset) + i;
1015 }
1016
1017 typedef struct
1018 {
1019 int numStripGroups;
1020 int stripGroupHeaderOffset;
1021
1022 unsigned char flags;
1023 }
1024 VTXMeshHeader_t;
1025 static VTXStripGroupHeader_t *pStripGroupVTX( VTXMeshHeader_t *t, int i )
1026 {
1027 return (VTXStripGroupHeader_t *)(((char *)t) + t->stripGroupHeaderOffset) +i;
1028 }
1029
1030 typedef struct
1031 {
1032 int numMeshes;
1033 int meshOffset;
1034
1035 float switchPoint;
1036 }
1037 VTXModelLODHeader_t;
1038 static VTXMeshHeader_t *pMeshVTX( VTXModelLODHeader_t *t, int i )
1039 {
1040 return (VTXMeshHeader_t *)(((char *)t) + t->meshOffset) + i;
1041 }
1042
1043 typedef struct
1044 {
1045 int numLODs;
1046 int lodOffset;
1047 }
1048 VTXModelHeader_t;
1049 static VTXModelLODHeader_t *pLODVTX( VTXModelHeader_t *t, int i )
1050 {
1051 return (VTXModelLODHeader_t *)(((char *)t) + t->lodOffset) + i;
1052 }
1053
1054 typedef struct
1055 {
1056 int numModels;
1057 int modelOffset;
1058 }
1059 VTXBodyPartHeader_t;
1060 static VTXModelHeader_t *pModelVTX( VTXBodyPartHeader_t *t, int i )
1061 {
1062 return (VTXModelHeader_t *)(((char *)t) + t->modelOffset) + i;
1063 }
1064
1065 typedef struct
1066 {
1067 int version; /* 7 */
1068
1069 /* hardware params that affect how the model is to be optimized. */
1070 int vertCacheSize;
1071 unsigned short maxBonesPerStrip;
1072 unsigned short maxBonesPerTri;
1073 int maxBonesPerVert;
1074
1075 int checkSum;
1076 int numLODs;
1077 int materialReplacementListOffset;
1078 int numBodyParts;
1079 int bodyPartOffset;
1080 }
1081 VTXFileHeader_t;
1082 static VTXBodyPartHeader_t *pBodyPartVTX( VTXFileHeader_t *t, int i )
1083 {
1084 return (VTXBodyPartHeader_t *)(((char *)t) + t->bodyPartOffset) + i;
1085 }
1086
1087 /*
1088 .VTX file structure
1089 =============================================
1090
1091 FileHeader
1092 L BodyParts::
1093 L Models::
1094 L LODS::
1095 L Meshes::
1096 L StripGroups::
1097 L VerticesTable[StudioMDL.Vertex]
1098 L IndicesTable[UINT16]
1099 |
1100 L Strips::
1101 L Vertices[UINT16]
1102 L Indices[UINT16]
1103 */
1104
1105 #pragma pack(pop)
1106
1107 /*
1108 * mdl format
1109 */
1110
1111 #pragma pack(push, 1)
1112
1113 typedef struct
1114 {
1115 void *pVertexData;
1116 void *pTangentData;
1117 }
1118 mstudio_modelvertexdata_t;
1119
1120 typedef struct
1121 {
1122 int unused_modelvertexdata;
1123 int numLODVertexes[MAX_NUM_LODS];
1124
1125 mstudio_modelvertexdata_t *_the_death_ptr;
1126 }
1127 mstudio_meshvertexdata_t;
1128
1129 typedef struct mstudiomodel_t mstudiomodel_t;
1130 typedef struct
1131 {
1132 int material;
1133 int modelindex;
1134 int numvertices;
1135 int vertexoffset;
1136 int numflexes;
1137 int flexindex;
1138 int materialtype;
1139 int materialparam;
1140 int meshid;
1141 float center[3];
1142 mstudio_meshvertexdata_t vertexdata;
1143 int unused[6];
1144 }
1145 mstudiomesh_t;
1146
1147 struct mstudiomodel_t
1148 {
1149 char name[64];
1150 int type;
1151 float boundingradius;
1152
1153 int nummeshes;
1154 int meshindex;
1155
1156 int numvertices;
1157 int vertexindex;
1158 int tangentsindex;
1159
1160 int numattachments;
1161 int attachmentindex;
1162
1163 int numeyeballs;
1164 int eyeballindex;
1165
1166 mstudio_modelvertexdata_t vertexdata;
1167
1168 int unused[8];
1169 };
1170 static mstudiomesh_t *studiomodel_pMesh( mstudiomodel_t *t, int i )
1171 {
1172 return (mstudiomesh_t *)(((char *)t) + t->meshindex) + i;
1173 }
1174
1175 typedef struct
1176 {
1177 int sznameindex;
1178 int nummodels;
1179 int base;
1180 int modelindex;
1181 } mstudiobodyparts_t;
1182
1183 static mstudiomodel_t *mstudiobodyparts_pModel( mstudiobodyparts_t *t, int i )
1184 {
1185 return (mstudiomodel_t *)(((char *)t) + t->modelindex) + i;
1186 }
1187
1188 typedef struct
1189 {
1190 int id;
1191 int version;
1192 int checksum;
1193 char name[64];
1194 int length;
1195 float eyeposition[3];
1196 float illumposition[3];
1197 float hull_min[3];
1198 float hull_max[3];
1199 float view_bbmin[3];
1200 float view_bbmax[3];
1201 int flags;
1202 int numbones;
1203 int boneindex;
1204 int numbonecontrollers;
1205 int bonecontrollerindex;
1206 int numhitboxsets;
1207 int hitboxsetindex;
1208 int numlocalanim;
1209 int localanimindex;
1210 int numlocalseq;
1211 int localseqindex;
1212 int activitylistversion;
1213 int eventsindexed;
1214 int numtextures;
1215 int textureindex;
1216 int numcdtextures;
1217 int cdtextureindex;
1218 int numskinref;
1219 int numskinfamilies;
1220 int skinindex;
1221 int numbodyparts;
1222 int bodypartindex;
1223 int numlocalattachments;
1224 int localattachmentindex;
1225 int numlocalnodes;
1226 int localnodeindex;
1227 int localnodenameindex;
1228 int numflexdesc;
1229 int flexdescindex;
1230 int numflexcontrollers;
1231 int flexcontrollerindex;
1232 int numflexrules;
1233 int flexruleindex;
1234 int numikchains;
1235 int ikchainindex;
1236 int nummouths;
1237 int mouthindex;
1238 int numlocalposeparameters;
1239 int localposeparamindex;
1240 int surfacepropindex;
1241 int keyvalueindex;
1242 int keyvaluesize;
1243 int numlocalikautoplaylocks;
1244 int localikautoplaylockindex;
1245 float mass;
1246 int contents;
1247 int numincludemodels;
1248 int includemodelindex;
1249 int szanimblocknameindex;
1250 int numanimblocks;
1251 int animblockindex;
1252 int bonetablebynameindex;
1253 char constdirectionallightdot;
1254 char rootLOD;
1255 char numAllowedRootLODs;
1256 char unused[1];
1257 int unused4;
1258 int numflexcontrollerui;
1259 int flexcontrolleruiindex;
1260 float flVertAnimFixedPointScale;
1261 int unused3[1];
1262 int studiohdr2index;
1263 int unused2[1];
1264 }
1265 studiohdr_t;
1266
1267 static mstudiobodyparts_t *studiohdr_pBodypart( studiohdr_t *t, int i )
1268 {
1269 return (mstudiobodyparts_t *)(((char *)t) + t->bodypartindex) + i;
1270 }
1271
1272 #pragma pack(pop)
1273
1274 static u32 vtx_count_indices( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl )
1275 {
1276 int indice_count = 0;
1277
1278 for( int bodyID = 0; bodyID < pVtxHdr->numBodyParts; ++bodyID )
1279 {
1280 /* Body parts */
1281 VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( pVtxHdr, bodyID );
1282 mstudiobodyparts_t *pBodyPart = studiohdr_pBodypart( pMdl, bodyID );
1283
1284 for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
1285 {
1286 /* models */
1287 VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
1288 mstudiomodel_t *pStudioModel =
1289 mstudiobodyparts_pModel( pBodyPart, modelID );
1290
1291 int nLod = 0;
1292 VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
1293
1294 for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
1295 {
1296 /* meshes */
1297 VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
1298 mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
1299
1300 for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
1301 {
1302 /* groups */
1303 VTXStripGroupHeader_t* pStripGroup =
1304 pStripGroupVTX( pVtxMesh, nGroup );
1305
1306 for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
1307 {
1308 /* strips */
1309 VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
1310
1311 if ( pStrip->flags & STRIP_IS_TRILIST )
1312 {
1313 indice_count += pStrip->numIndices;
1314 }
1315 }
1316 }
1317 }
1318 }
1319 }
1320
1321 return indice_count;
1322 }
1323
1324 #endif /* CXR_VALVE_BIN_H */