2 * The following is used to read the existing content from CS:GO compiled assets
10 #ifndef CXR_VALVE_BIN_H
11 #define CXR_VALVE_BIN_H
16 #include "cxr_types.h"
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
;
30 * These are 'unofficial' representations of the original formats, more C
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
;
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
);
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
);
52 * File system implementation
62 u32 FileDataSectionSize
;
63 u32 ArchiveMD5SectionSize
;
64 u32 OtherMD5SectionSize
;
65 u32 SignatureSectionSize
;
68 struct VPKDirectoryEntry
80 static struct valve_file_system
89 cxr_abuffer searchpaths
;
91 FILE *current_archive
;
96 fs_global
= { .initialized
= 0 };
100 VPKDirectoryEntry
*vpk_entry
;
108 static VPKDirectoryEntry
*vpk_find( const char *asset
);
114 static vdf_node
*vdf_open_file( const char *fn
);
115 static void vdf_free_r( vdf_node
*p
);
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
);
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
);
124 static const char *kv_get( vdf_node
*node
, const char *key
,
125 const char *value_defalt
);
127 /* Iterate each keyvalue starting from *it until key is matched */
128 static char *kv_next( vdf_node
*node
, const char *key
, int *it
);
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
);
133 static void kv_int_array( vdf_node
*node
, const char *key
, u32 count
,
135 static void kv_float_array( vdf_node
*node
, const char *key
, u32 count
,
137 static void kv_double_array( vdf_node
*node
, const char *key
, u32 count
,
140 #define vdf_foreach( NODE, STR, AS ) \
141 int __vdf_it_##AS = 0; \
143 while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
145 #define kv_foreach( NODE, STR, AS ) \
146 int __kv_it_##AS = 0; \
148 while( (AS = kv_next( NODE, STR, &__kv_it_##AS )) )
150 static VPKDirectoryEntry
*vpk_find( const char *asset
)
152 valve_file_system
*fs
= &fs_global
;
154 if( !fs
->directory_tree
)
161 strcpy( wbuf
, asset
);
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
169 char *ext
= cxr_findext( wbuf
, '.' );
171 if( !ext
) return NULL
;
174 char *fn
= cxr_findext( wbuf
, '/' );
176 if( !fn
) return NULL
;
180 char *pCur
= fs
->directory_tree
;
186 int bExt
= !strcmp( ext
, pCur
);
188 while( *( pCur
++ ) ) {};
191 if( !*pCur
) { pCur
++; break; }
193 int bDir
= !strcmp( dir
, pCur
);
195 while( *( pCur
++ ) ) {};
198 if( !*pCur
) { pCur
++; break; }
200 const char *vpk_fn
= pCur
;
202 while( *( pCur
++ ) ) {};
203 VPKDirectoryEntry
*entry
= (VPKDirectoryEntry
*)pCur
;
205 if( !strcmp( vpk_fn
, fn
) && bExt
&& bDir
)
210 pCur
+= entry
->PreloadBytes
+ sizeof( VPKDirectoryEntry
);
213 if( bDir
&& bExt
) return NULL
;
216 if( bExt
) return NULL
;
245 static vdf_node
*vdf_next( vdf_node
*node
, const char *name
, int *it
)
250 for( int i
= it
? *it
: 0; i
< node
->abnodes
.count
; i
++ )
252 vdf_node
**ptr_child
= cxr_ab_ptr( &node
->abnodes
, i
),
255 if( !name
|| !strcmp( name
, child
->name
))
265 static char *kv_next( vdf_node
*node
, const char *key
, int *it
)
271 while( *it
< node
->abpairs
.count
)
273 vdf_kv
*kv
= cxr_ab_ptr( &node
->abpairs
, *it
);
275 if( !strcmp( kv
->key
, key
) )
289 static const char *kv_get( vdf_node
*node
, const char *key
,
290 const char *value_defalt
)
293 char *val
= kv_next( node
, key
, &it
);
301 static void vdf_str_to_int( const char *src
, void *dest
)
303 *((int *)dest
) = atoi( src
);
306 static void vdf_str_to_float( const char *src
, void *dest
)
308 *((float *)dest
) = atof( src
);
311 static void vdf_str_to_double( const char *src
, void *dest
)
313 *((double *)dest
) = atof( src
);
316 static void kv_parse_array( const char *source
, u32 esize
, u32 count
,
317 void(*interp_func
)(const char *src
, void *dest
), void *arr
)
322 char value_buf
[ 64 ];
328 char const *c
= source
;
332 if( *c
==' ' || *c
=='\t' || *c
=='[' || *c
==']' || *c
=='(' || *c
==')' )
336 value_buf
[ k
] = 0x00;
339 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
358 if( k
< sizeof( value_buf
) - 1 )
360 value_buf
[ k
++ ] = *c
;
368 /* Add remaining case if we hit null */
369 if( token
&& (i
< count
) )
371 value_buf
[ k
] = 0x00;
372 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
376 static void kv_int_array( vdf_node
*node
, const char *key
, u32 count
, int *arr
)
378 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(int), count
,
379 vdf_str_to_int
, arr
);
382 static void kv_float_array( vdf_node
*node
, const char *key
, u32 count
,
385 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(float), count
,
386 vdf_str_to_float
, arr
);
389 static void kv_double_array( vdf_node
*node
, const char *key
, u32 count
,
392 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(double), count
,
393 vdf_str_to_double
, arr
);
396 static int kv_get_int( vdf_node
*node
, const char *key
,
397 const int default_value
)
399 const char *v
= kv_get( node
, key
, NULL
);
400 return v
? atoi(v
): default_value
;
403 static float kv_get_float( vdf_node
*node
, const char *key
,
404 float default_value
)
406 const char *v
= kv_get( node
, key
, NULL
);
407 return v
? atof( v
): default_value
;
410 static vdf_node
*vdf_create_node( vdf_node
*parent
, const char *name
)
412 vdf_node
*node
= calloc( 1, sizeof( vdf_node
) );
415 cxr_ab_init( &node
->abnodes
, sizeof( vdf_node
* ), 0 );
416 cxr_ab_init( &node
->abpairs
, sizeof( vdf_kv
), 0 );
420 node
->name
= cxr_str_clone(name
, 0);
425 node
->parent
= parent
;
427 vdf_node
**child
= cxr_ab_empty( &parent
->abnodes
);
434 static void vdf_kv_append( vdf_node
*p
, const char *k
, const char *v
)
436 vdf_kv
*kv
= cxr_ab_empty( &p
->abpairs
);
438 u32 sv
= strlen(v
)+1;
439 u32 sk
= strlen(k
)+1;
441 kv
->key
= malloc( sv
+sk
);
442 kv
->value
= kv
->key
+sk
;
444 memcpy( kv
->key
, k
, sk
);
445 memcpy( kv
->value
, v
, sv
);
448 static void vdf_free_r( vdf_node
*p
)
450 for( int i
= 0; i
< p
->abpairs
.count
; i
++ )
452 vdf_kv
*kv
= cxr_ab_ptr( &p
->abpairs
, i
);
453 free( kv
->key
); /* key and value are allocated in the same buffer */
456 for( int i
= 0; i
< p
->abnodes
.count
; i
++ )
458 vdf_node
**ptr_node
= cxr_ab_ptr( &p
->abnodes
, i
);
459 vdf_free_r( *ptr_node
);
462 cxr_ab_free( &p
->abpairs
);
463 cxr_ab_free( &p
->abnodes
);
497 static void vdf_newln( vdf_ctx
*ctx
)
501 ctx
->st
.tokens
[0] = NULL
;
502 ctx
->st
.tokens
[1] = NULL
;
506 static void vdf_endl( vdf_ctx
*ctx
)
508 if( ctx
->st
.tokens
[0] )
511 if( ctx
->st
.tokens
[1] )
513 vdf_kv_append( ctx
->st
.pnode
, ctx
->st
.tokens
[0], ctx
->st
.tokens
[1] );
518 strcpy( ctx
->name
, ctx
->st
.tokens
[0] );
519 ctx
->st
.expect_decl
= 1;
526 static int vdf_line_control( vdf_ctx
*ctx
)
528 if( *ctx
->st
.ptr_read
== '\r' )
530 *ctx
->st
.ptr_read
= 0x00;
533 if( *ctx
->st
.ptr_read
== '\n' )
535 *ctx
->st
.ptr_read
= 0x00;
543 static void vdf_wait_endl( vdf_ctx
*ctx
)
545 while( (*ctx
->st
.ptr_read
) && (*ctx
->st
.ptr_read
!= '\n') )
547 if( vdf_line_control( ctx
) == 2 )
556 static void vdf_parse_string( vdf_ctx
*ctx
)
558 while( *ctx
->st
.ptr_read
)
560 if( *ctx
->st
.ptr_read
== '"' )
562 *ctx
->st
.ptr_read
= 0x00;
566 if( vdf_line_control( ctx
) )
568 /* Unexpected end of line */
569 cxr_log( "vdf: unexpected EOL\n" );
577 static int vdf_parse_structure( vdf_ctx
*ctx
)
579 if( *ctx
->st
.ptr_read
== '{' )
581 if( ctx
->st
.tokens
[0] || !ctx
->st
.expect_decl
)
583 /* Unexpected token '{' */
584 cxr_log( "vdf: Unexpected token '{'\n" );
588 ctx
->st
.expect_decl
= 0;
589 ctx
->st
.pnode
= vdf_create_node( ctx
->st
.pnode
, ctx
->name
);
591 vdf_wait_endl( ctx
);
595 if( *ctx
->st
.ptr_read
== '}' )
597 if( !ctx
->st
.pnode
->parent
)
599 /* Unexpected token '}' */
600 cxr_log( "vdf: Unexpected token '}'\n" );
605 ctx
->st
.pnode
= ctx
->st
.pnode
->parent
;
608 vdf_wait_endl( ctx
);
615 static void vdf_parse_begin_token( vdf_ctx
*ctx
, char *ptr
)
617 ctx
->st
.tokens
[ ctx
->st
.i
] = ptr
;
619 if( ctx
->st
.expect_decl
)
621 /* Unexpected token 'name' */
622 cxr_log( "vdf: unexpected token 'name'\n" );
627 static void vdf_parse_feedbuffer( vdf_ctx
*ctx
, char *buf
)
629 ctx
->st
.ptr_read
= buf
;
631 while( *ctx
->st
.ptr_read
)
633 if( !vdf_line_control( ctx
) )
635 if( (*ctx
->st
.ptr_read
== '/') && (ctx
->st
.ptr_read
[1] == '/') )
637 *ctx
->st
.ptr_read
= 0x00;
638 ctx
->st
.ptr_read
+= 2;
641 vdf_wait_endl( ctx
);
645 if( !vdf_parse_structure( ctx
) )
647 if( *ctx
->st
.ptr_read
== ' ' || *ctx
->st
.ptr_read
== '\t' )
649 *ctx
->st
.ptr_read
= 0x00;
651 if( ctx
->st
.tokens
[ ctx
->st
.i
] )
657 vdf_wait_endl( ctx
);
663 else if( !ctx
->st
.tokens
[ ctx
->st
.i
] )
665 if( *ctx
->st
.ptr_read
== '"' )
667 *ctx
->st
.ptr_read
= 0x00;
670 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
671 vdf_parse_string( ctx
);
675 if( !( *ctx
->st
.ptr_read
== '/' &&
676 *(ctx
->st
.ptr_read
+ 1) == *ctx
->st
.ptr_read
) )
678 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
690 static void vdf_debug_indent( int level
)
692 for(int i
=0; i
<level
; i
++)
696 static void vdf_debug_r( vdf_node
*node
, int level
)
698 vdf_debug_indent(level
);
699 cxr_log( "vdf_node(%p, name: '%s')\n", node
, node
->name
);
701 vdf_debug_indent(level
);
704 for( int i
=0; i
<node
->abpairs
.count
; i
++ )
706 vdf_kv
*kv
= cxr_ab_ptr( &node
->abpairs
, i
);
708 vdf_debug_indent(level
+1);
709 cxr_log( "vdf_kv(%p, k: '%s', v: '%s')\n",
710 kv
, kv
->key
, kv
->value
);
713 for( int i
=0; i
<node
->abnodes
.count
; i
++ )
715 vdf_node
**child
= cxr_ab_ptr( &node
->abnodes
, i
);
716 vdf_debug_r( *child
, level
+1 );
719 vdf_debug_indent(level
);
723 /* This will wreck the original buffer, but must stay alive! */
724 static vdf_node
*vdf_from_buffer( char *buffer
)
726 vdf_node
*root
= vdf_create_node( NULL
, NULL
);
729 ctx
.root
= ctx
.st
.pnode
= root
;
732 vdf_parse_feedbuffer( &ctx
, buffer
);
735 vdf_debug_r( root
, 0 );
741 static vdf_node
*vdf_open_file( const char *fn
)
743 char *text_src
= cxr_textasset_read( fn
);
748 vdf_node
*root
= vdf_from_buffer( text_src
);
758 CXR_API i32
cxr_fs_set_gameinfo( const char *path
)
760 valve_file_system
*fs
= &fs_global
;
762 if( fs
->initialized
)
765 vdf_node
*info
= vdf_open_file( path
);
769 fs
->gamedir
= cxr_str_clone( path
, 0 );
770 cxr_downlvl( fs
->gamedir
);
772 fs
->exedir
= cxr_str_clone( fs
->gamedir
, 0 );
773 cxr_downlvl( fs
->exedir
);
775 cxr_log( "Setting up file system:\n"
780 path
, fs
->gamedir
, fs
->exedir
);
782 /* get search paths */
783 vdf_node
*search_paths
=
788 vdf_next( info
, "GameInfo", NULL
),
796 cxr_ab_init( &fs
->searchpaths
, sizeof( char *), 0 );
798 kv_foreach( search_paths
, "Game", kv_game
)
800 cxr_log( "Game %s\n", kv_game
);
802 if( kv_game
[0] == '|' ) continue;
805 if( cxr_path_is_abs( kv_game
) )
807 buf
= cxr_str_clone( kv_game
, 1 );
812 buf
= cxr_str_clone( fs
->exedir
, strlen(kv_game
)+1 );
813 strcat( buf
, kv_game
);
817 char **sp
= cxr_ab_empty( &fs
->searchpaths
);
823 /* Find pack diretory */
825 fs
->current_archive
= NULL
;
826 fs
->current_idx
= 0x7fff;
828 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
830 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
832 strcpy( pack_path
, *sp
);
833 strcat( pack_path
, "pak01_dir.vpk" );
835 fs
->current_archive
= fopen( pack_path
, "rb" );
837 /* Read vpk directory */
838 if( fs
->current_archive
)
840 fread( &fs
->vpk
, sizeof(VPKHeader
), 1, fs
->current_archive
);
842 fs
->directory_tree
= malloc( fs
->vpk
.TreeSize
);
843 fread( fs
->directory_tree
, fs
->vpk
.TreeSize
, 1, fs
->current_archive
);
849 if( !fs
->current_archive
)
851 cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
852 "Stock models will not load!\n", fs
->searchpaths
.count
);
859 CXR_API
void cxr_fs_exit(void)
861 valve_file_system
*fs
= &fs_global
;
863 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
865 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
869 cxr_ab_free( &fs
->searchpaths
);
871 if( fs
->directory_tree
)
873 free( fs
->directory_tree
);
874 fs
->directory_tree
= NULL
;
877 if( fs
->current_archive
)
879 fclose( fs
->current_archive
);
880 fs
->current_archive
= NULL
;
886 memset( fs
, 0, sizeof( valve_file_system
) );
889 static char *cxr_vpk_read( VPKDirectoryEntry
*entry
, int stringbuffer
)
891 valve_file_system
*fs
= &fs_global
;
893 if( !fs
->initialized
)
898 /* Check if we need to change file handle */
899 if( entry
->ArchiveIndex
!= fs
->current_idx
)
901 if( fs
->current_archive
)
902 fclose( fs
->current_archive
);
904 fs
->current_archive
= NULL
;
905 fs
->current_idx
= entry
->ArchiveIndex
;
907 if( entry
->ArchiveIndex
== 0x7fff )
909 snprintf( pak
, 1023, "%scsgo/pak01_dir.vpk", fs
->exedir
);
910 cxr_log( "locate %s\n", pak
);
914 snprintf( pak
, 1023, "%scsgo/pak01_%03hu.vpk",
915 fs
->exedir
, entry
->ArchiveIndex
);
916 cxr_log( "locate %s\n", pak
);
919 fs
->current_archive
= fopen( pak
, "rb" );
921 if( !fs
->current_archive
)
922 cxr_log( "Warning: could not locate %s\n", pak
);
925 if( !fs
->current_archive
)
928 size_t offset
= entry
->EntryOffset
,
929 length
= entry
->EntryLength
;
932 * File is stored in the index, after the tree
934 if( entry
->ArchiveIndex
== 0x7fff )
936 offset
+= fs
->vpk
.TreeSize
+ sizeof(VPKHeader
);
940 * Entire file is stored in the preload bytes;
941 * Backtrack offset from directory to get absolute offset
945 offset
= (char *)entry
- (char *)fs
->directory_tree
;
946 offset
+= sizeof( VPKHeader
);
947 offset
+= sizeof( VPKDirectoryEntry
);
949 length
= entry
->PreloadBytes
;
952 length
+= entry
->PreloadBytes
;
954 fseek( fs
->current_archive
, offset
, SEEK_SET
);
956 size_t alloc_size
= stringbuffer
? length
+1: length
;
957 char *filebuf
= malloc( alloc_size
),
961 fullfile
[length
] = 0x00;
963 if( entry
->PreloadBytes
)
965 memcpy( filebuf
, ((char *)entry
) + sizeof(VPKDirectoryEntry
),
966 entry
->PreloadBytes
);
968 filebuf
+= entry
->PreloadBytes
;
969 length
-= entry
->PreloadBytes
;
971 /* TODO, this can sometimes come from the same index file.
972 * very small performance gain can be made */
975 if( fread( filebuf
, 1, length
, fs
->current_archive
) == length
)
987 CXR_API i32
cxr_fs_find( const char *path
, fs_locator
*locator
)
989 valve_file_system
*fs
= &fs_global
;
991 if( !fs
->initialized
)
994 VPKDirectoryEntry
*entry
;
996 if( fs
->directory_tree
)
998 if( (entry
= vpk_find( path
)) )
1000 locator
->vpk_entry
= entry
;
1001 locator
->path
[0] = 0x00;
1006 locator
->vpk_entry
= NULL
;
1008 /* Use physical search paths */
1009 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
1011 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
1013 snprintf( locator
->path
, 1024, "%s%s", *sp
, path
);
1015 if( cxr_file_exists( locator
->path
) )
1019 /* File not found */
1023 CXR_API
void cxr_fs_free( void *data
)
1028 CXR_API
void *cxr_fs_get( const char *path
, i32 stringbuffer
)
1030 valve_file_system
*fs
= &fs_global
;
1032 if( !fs
->initialized
)
1037 if( cxr_fs_find( path
, &locator
) )
1039 if( locator
.vpk_entry
)
1041 return cxr_vpk_read( locator
.vpk_entry
, stringbuffer
);
1048 return cxr_textasset_read( locator
.path
);
1050 return cxr_asset_read( locator
.path
);
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
1065 * All trademarks are property of their respective owners
1068 #define MAX_NUM_LODS 8
1069 #define MAX_NUM_BONES_PER_VERT 3
1071 #pragma pack(push, 1)
1074 float weight
[MAX_NUM_BONES_PER_VERT
];
1075 char bone
[MAX_NUM_BONES_PER_VERT
];
1082 boneWeight_t boneweights
;
1095 int numLodVertexes
[MAX_NUM_LODS
];
1097 int fixupTableStart
;
1098 int vertexDataStart
;
1099 int tangentDataStart
;
1105 static mstudiovertex_t
*GetVertexData( vertexFileHeader_t
*t
)
1107 return (mstudiovertex_t
*) ( (char *)t
+ t
->vertexDataStart
);
1114 #pragma pack(push, 1)
1118 unsigned char boneWeightIndex
[3];
1119 unsigned char numBones
;
1121 unsigned short origMeshVertID
;
1126 enum StripGroupFlags
1128 STRIPGROUP_IS_FLEXED
= 0x01,
1129 STRIPGROUP_IS_HWSKINNED
= 0x02,
1130 STRIPGROUP_IS_DELTA_FLEXED
= 0x04,
1131 STRIPGROUP_SUPPRESS_HW_MORPH
= 0x08
1134 enum StripHeaderFlags_t
{
1135 STRIP_IS_TRILIST
= 0x01,
1136 STRIP_IS_TRISTRIP
= 0x02 /* Unused by studiomdl 2015? */
1149 unsigned char flags
;
1151 int numBoneStateChanges
;
1152 int boneStateChangeOffset
;
1167 unsigned char flags
;
1169 VTXStripGroupHeader_t
;
1171 static VTXVertex_t
*pVertexVTX( VTXStripGroupHeader_t
*t
, int i
)
1173 return (VTXVertex_t
*)(((char *)t
) + t
->vertOffset
) + i
;
1176 static unsigned short *pIndexVTX( VTXStripGroupHeader_t
*t
, int i
)
1178 return (unsigned short *)(((char *)t
) + t
->indexOffset
) + i
;
1181 static VTXStripHeader_t
*pStripVTX( VTXStripGroupHeader_t
*t
, int i
)
1183 return (VTXStripHeader_t
*)(((char *)t
) + t
->stripOffset
) + i
;
1189 int stripGroupHeaderOffset
;
1191 unsigned char flags
;
1194 static VTXStripGroupHeader_t
*pStripGroupVTX( VTXMeshHeader_t
*t
, int i
)
1196 return (VTXStripGroupHeader_t
*)(((char *)t
) + t
->stripGroupHeaderOffset
) +i
;
1206 VTXModelLODHeader_t
;
1207 static VTXMeshHeader_t
*pMeshVTX( VTXModelLODHeader_t
*t
, int i
)
1209 return (VTXMeshHeader_t
*)(((char *)t
) + t
->meshOffset
) + i
;
1218 static VTXModelLODHeader_t
*pLODVTX( VTXModelHeader_t
*t
, int i
)
1220 return (VTXModelLODHeader_t
*)(((char *)t
) + t
->lodOffset
) + i
;
1228 VTXBodyPartHeader_t
;
1229 static VTXModelHeader_t
*pModelVTX( VTXBodyPartHeader_t
*t
, int i
)
1231 return (VTXModelHeader_t
*)(((char *)t
) + t
->modelOffset
) + i
;
1236 int version
; /* 7 */
1238 /* hardware params that affect how the model is to be optimized. */
1240 unsigned short maxBonesPerStrip
;
1241 unsigned short maxBonesPerTri
;
1242 int maxBonesPerVert
;
1246 int materialReplacementListOffset
;
1251 static VTXBodyPartHeader_t
*pBodyPartVTX( VTXFileHeader_t
*t
, int i
)
1253 return (VTXBodyPartHeader_t
*)(((char *)t
) + t
->bodyPartOffset
) + i
;
1258 =============================================
1266 L VerticesTable[StudioMDL.Vertex]
1267 L IndicesTable[UINT16]
1280 #pragma pack(push, 1)
1287 mstudio_modelvertexdata_t
;
1291 int unused_modelvertexdata
;
1292 int numLODVertexes
[MAX_NUM_LODS
];
1294 mstudio_modelvertexdata_t
*_the_death_ptr
;
1296 mstudio_meshvertexdata_t
;
1298 typedef struct mstudiomodel_t mstudiomodel_t
;
1311 mstudio_meshvertexdata_t vertexdata
;
1316 struct mstudiomodel_t
1320 float boundingradius
;
1330 int attachmentindex
;
1335 mstudio_modelvertexdata_t vertexdata
;
1339 static mstudiomesh_t
*studiomodel_pMesh( mstudiomodel_t
*t
, int i
)
1341 return (mstudiomesh_t
*)(((char *)t
) + t
->meshindex
) + i
;
1350 } mstudiobodyparts_t
;
1352 static mstudiomodel_t
*mstudiobodyparts_pModel( mstudiobodyparts_t
*t
, int i
)
1354 return (mstudiomodel_t
*)(((char *)t
) + t
->modelindex
) + i
;
1364 float eyeposition
[3];
1365 float illumposition
[3];
1368 float view_bbmin
[3];
1369 float view_bbmax
[3];
1373 int numbonecontrollers
;
1374 int bonecontrollerindex
;
1381 int activitylistversion
;
1388 int numskinfamilies
;
1392 int numlocalattachments
;
1393 int localattachmentindex
;
1396 int localnodenameindex
;
1399 int numflexcontrollers
;
1400 int flexcontrollerindex
;
1407 int numlocalposeparameters
;
1408 int localposeparamindex
;
1409 int surfacepropindex
;
1412 int numlocalikautoplaylocks
;
1413 int localikautoplaylockindex
;
1416 int numincludemodels
;
1417 int includemodelindex
;
1418 int szanimblocknameindex
;
1421 int bonetablebynameindex
;
1422 char constdirectionallightdot
;
1424 char numAllowedRootLODs
;
1427 int numflexcontrollerui
;
1428 int flexcontrolleruiindex
;
1429 float flVertAnimFixedPointScale
;
1431 int studiohdr2index
;
1436 static char *studiohdr_pCdtexture( studiohdr_t
*t
, int i
)
1438 return (((char *)t
) + *((int *)(((u8
*)t
) + t
->cdtextureindex
) + i
));
1441 static mstudiobodyparts_t
*studiohdr_pBodypart( studiohdr_t
*t
, int i
)
1443 return (mstudiobodyparts_t
*)(((char *)t
) + t
->bodypartindex
) + i
;
1452 int garbage
[12]; /* contains some studiomdl pointers */
1456 static char *mstudiotexture_pszName( mstudiotexture_t
*t
)
1458 return ((char *)t
) + t
->sznameindex
;
1461 static mstudiotexture_t
*studiohdr_pTexture( studiohdr_t
*t
, int i
)
1463 return (mstudiotexture_t
*)(((u8
*)t
) + t
->textureindex
) + i
;
1468 static void vtx_resource_counts( VTXFileHeader_t
*pVtxHdr
, studiohdr_t
*pMdl
,
1469 u32
*indices
, u32
*meshes
)
1474 for( int bodyID
= 0; bodyID
< pVtxHdr
->numBodyParts
; ++bodyID
)
1477 VTXBodyPartHeader_t
* pVtxBodyPart
= pBodyPartVTX( pVtxHdr
, bodyID
);
1478 mstudiobodyparts_t
*pBodyPart
= studiohdr_pBodypart( pMdl
, bodyID
);
1480 for( int modelID
= 0; modelID
< pBodyPart
->nummodels
; ++modelID
)
1483 VTXModelHeader_t
* pVtxModel
= pModelVTX( pVtxBodyPart
, modelID
);
1484 mstudiomodel_t
*pStudioModel
=
1485 mstudiobodyparts_pModel( pBodyPart
, modelID
);
1488 VTXModelLODHeader_t
*pVtxLOD
= pLODVTX( pVtxModel
, nLod
);
1490 for( int nMesh
= 0; nMesh
< pStudioModel
->nummeshes
; ++nMesh
)
1493 VTXMeshHeader_t
* pVtxMesh
= pMeshVTX( pVtxLOD
, nMesh
);
1494 mstudiomesh_t
* pMesh
= studiomodel_pMesh( pStudioModel
, nMesh
);
1498 for ( int nGroup
= 0; nGroup
< pVtxMesh
->numStripGroups
; ++nGroup
)
1501 VTXStripGroupHeader_t
* pStripGroup
=
1502 pStripGroupVTX( pVtxMesh
, nGroup
);
1504 for ( int nStrip
= 0; nStrip
< pStripGroup
->numStrips
; nStrip
++ )
1507 VTXStripHeader_t
*pStrip
= pStripVTX( pStripGroup
, nStrip
);
1509 if ( pStrip
->flags
& STRIP_IS_TRILIST
)
1511 *indices
+= pStrip
->numIndices
;
1521 * The following section is the wrappers for the underlying types
1524 struct valve_material
1532 float *vertex_data
; /* pos xyz, norm xyz, uv xy */
1542 struct valve_model_batch
1550 /* Internal valve data */
1551 studiohdr_t
*studiohdr
;
1552 VTXFileHeader_t
*vtxhdr
;
1553 vertexFileHeader_t
*vvdhdr
;
1556 CXR_API valve_model
*valve_load_model( const char *relpath
)
1559 strcpy( path
, relpath
);
1561 char *ext
= cxr_stripext( path
);
1566 /* Load data files */
1567 valve_model
*model
= malloc( sizeof( valve_model
) );
1568 model
->studiohdr
= NULL
;
1569 model
->vtxhdr
= NULL
;
1570 model
->vvdhdr
= NULL
;
1572 strcpy( ext
, ".dx90.vtx" );
1573 model
->vtxhdr
= cxr_fs_get( path
, 0 );
1575 strcpy( ext
, ".vvd" );
1576 model
->vvdhdr
= cxr_fs_get( path
, 0 );
1578 strcpy( ext
, ".mdl" );
1579 model
->studiohdr
= cxr_fs_get( path
, 0 );
1581 if( !model
->vvdhdr
|| !model
->studiohdr
|| !model
->vtxhdr
)
1583 cxr_log( "Error, failed to load: (%s)\n", relpath
);
1585 free( model
->studiohdr
);
1586 free( model
->vvdhdr
);
1587 free( model
->studiohdr
);
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
;
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
);
1604 /* Find materials */
1605 for( int i
=0; i
<model
->studiohdr
->numtextures
; i
++ )
1607 char material_path
[ 1024 ];
1610 mstudiotexture_t
*tex
= studiohdr_pTexture( model
->studiohdr
, i
);
1611 const char *name
= mstudiotexture_pszName( tex
);
1613 model
->materials
[i
] = NULL
;
1615 cxr_log( "search: %s\n", name
);
1617 for( int j
=0; j
<model
->studiohdr
->numcdtextures
; j
++ )
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
);
1624 if( cxr_fs_find( material_path
, &locator
))
1626 model
->materials
[i
] = cxr_str_clone( material_path
, 0 );
1636 /* Extract meshes */
1637 for( int bodyID
= 0; bodyID
< model
->studiohdr
->numbodyparts
; ++bodyID
)
1640 VTXBodyPartHeader_t
* pVtxBodyPart
= pBodyPartVTX( model
->vtxhdr
, bodyID
);
1641 mstudiobodyparts_t
*pBodyPart
=
1642 studiohdr_pBodypart( model
->studiohdr
, bodyID
);
1644 for( int modelID
= 0; modelID
< pBodyPart
->nummodels
; ++modelID
)
1647 VTXModelHeader_t
* pVtxModel
= pModelVTX( pVtxBodyPart
, modelID
);
1648 mstudiomodel_t
*pStudioModel
=
1649 mstudiobodyparts_pModel( pBodyPart
, modelID
);
1652 VTXModelLODHeader_t
*pVtxLOD
= pLODVTX( pVtxModel
, nLod
);
1654 for( int nMesh
= 0; nMesh
< pStudioModel
->nummeshes
; ++nMesh
)
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
);
1660 valve_model_batch
*curBatch
= &model
->parts
[ i_mesh
++ ];
1661 curBatch
->material
= pMesh
->material
;
1662 curBatch
->ibstart
= i_index
;
1663 curBatch
->ibcount
= 0;
1665 for( int nGroup
= 0; nGroup
< pVtxMesh
->numStripGroups
; ++nGroup
)
1668 VTXStripGroupHeader_t
* pStripGroup
=
1669 pStripGroupVTX( pVtxMesh
, nGroup
);
1671 for( int nStrip
= 0; nStrip
< pStripGroup
->numStrips
; nStrip
++ )
1674 VTXStripHeader_t
*pStrip
= pStripVTX( pStripGroup
, nStrip
);
1676 if( pStrip
->flags
& STRIP_IS_TRILIST
)
1679 for( int i
= 0; i
< pStrip
->numIndices
; i
++ )
1681 u16 i1
= *pIndexVTX( pStripGroup
,
1682 pStrip
->indexOffset
+ i
);
1684 model
->indices
[ i_index
++ ] =
1685 pVertexVTX( pStripGroup
, i1
)->origMeshVertID
+
1686 pMesh
->vertexoffset
;
1688 curBatch
->ibcount
++;
1693 /* This is unused? */
1701 mstudiovertex_t
*vertexData
= GetVertexData( model
->vvdhdr
);
1703 for( int i
= 0; i
< model
->vertex_count
; i
++ )
1705 mstudiovertex_t
*vert
= &vertexData
[i
];
1707 float *dest
= &model
->vertex_data
[ i
*8 ];
1709 dest
[0] = vert
->pos
[0];
1710 dest
[1] = vert
->pos
[1];
1711 dest
[2] = vert
->pos
[2];
1713 dest
[3] = vert
->norm
[0];
1714 dest
[4] = vert
->norm
[1];
1715 dest
[5] = vert
->norm
[2];
1717 dest
[6] = vert
->uv
[0];
1718 dest
[7] = vert
->uv
[1];
1724 CXR_API
void valve_free_model( valve_model
*model
)
1726 for( int i
=0; i
<model
->material_count
; i
++ )
1727 free( model
->materials
[i
] );
1729 free( model
->materials
);
1730 free( model
->parts
);
1731 free( model
->indices
);
1732 free( model
->vertex_data
);
1734 free( model
->studiohdr
);
1735 free( model
->vtxhdr
);
1736 free( model
->vvdhdr
);
1740 static char *valve_texture_path( const char *path
)
1746 malloc( strlen( path
) + strlen(".vtf") + strlen("materials/") +1 );
1747 strcpy( buf
, "materials/" );
1748 strcat( buf
, path
);
1749 strcat( buf
, ".vtf" );
1751 cxr_unixpath( buf
);
1752 cxr_lowercase( buf
);
1757 static void cxr_str_debug( const char *str
)
1759 cxr_log( "\n=== string debug ( %p ) === \n'", str
);
1760 const char *y
= str
;
1766 else if( *y
== '\n' )
1767 cxr_log( "<LF>\n" );
1772 cxr_log( "<EOF>'\n" );
1775 CXR_API valve_material
*valve_load_material( const char *path
)
1777 char *vmt
= cxr_fs_get( path
, 1 );
1782 cxr_str_debug( vmt
);
1784 valve_material
*material
= malloc( sizeof(valve_material
) );
1785 vdf_node
*vmt_root
= vdf_from_buffer( vmt
);
1787 if( vmt_root
->abnodes
.count
== 0 )
1789 cxr_log( "Error: vmt has no nodes\n" );
1792 vdf_free_r( vmt_root
);
1796 vdf_node
**body
= cxr_ab_ptr( &vmt_root
->abnodes
, 0 );
1798 /* Path semantics here are inconsistent
1799 * I believe they should all just be converted to lowercase, though */
1801 for( int i
=0; i
<(*body
)->abpairs
.count
; i
++ )
1803 vdf_kv
*kv
= cxr_ab_ptr( &(*body
)->abpairs
, i
);
1804 cxr_lowercase( kv
->key
);
1807 const char *basetexture
= kv_get( *body
, "$basetexture", NULL
),
1808 *bumpmap
= kv_get( *body
, "$bumpmap", NULL
);
1810 /* TODO: other shader parameters */
1811 material
->basetexture
= valve_texture_path( basetexture
);
1812 material
->bumpmap
= valve_texture_path( bumpmap
);
1814 vdf_free_r( vmt_root
);
1823 CXR_API
void valve_free_material( valve_material
*material
)
1825 free( material
->basetexture
);
1826 free( material
->bumpmap
);
1830 #endif /* CXR_VALVE_BIN_H */