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
)
158 strcpy( wbuf
, asset
);
161 * This currently fails if the filename doesn't have a file extension, or
162 * if it is located at the root path. I'm not sure if this is defined
163 * behaviour or not. TODO: allow it to work anyway
166 char *ext
= cxr_findext( wbuf
, '.' );
168 if( !ext
) return NULL
;
171 char *fn
= cxr_findext( wbuf
, '/' );
173 if( !fn
) return NULL
;
177 char *pCur
= fs
->directory_tree
;
183 int bExt
= !strcmp( ext
, pCur
);
185 while( *( pCur
++ ) ) {};
188 if( !*pCur
) { pCur
++; break; }
190 int bDir
= !strcmp( dir
, pCur
);
192 while( *( pCur
++ ) ) {};
195 if( !*pCur
) { pCur
++; break; }
197 const char *vpk_fn
= pCur
;
199 while( *( pCur
++ ) ) {};
200 VPKDirectoryEntry
*entry
= (VPKDirectoryEntry
*)pCur
;
202 if( !strcmp( vpk_fn
, fn
) && bExt
&& bDir
)
207 pCur
+= entry
->PreloadBytes
+ sizeof( VPKDirectoryEntry
);
210 if( bDir
&& bExt
) return NULL
;
213 if( bExt
) return NULL
;
242 static vdf_node
*vdf_next( vdf_node
*node
, const char *name
, int *it
)
247 for( int i
= it
? *it
: 0; i
< node
->abnodes
.count
; i
++ )
249 vdf_node
**ptr_child
= cxr_ab_ptr( &node
->abnodes
, i
),
252 if( !name
|| !strcmp( name
, child
->name
))
262 static char *kv_next( vdf_node
*node
, const char *key
, int *it
)
268 while( *it
< node
->abpairs
.count
)
270 vdf_kv
*kv
= cxr_ab_ptr( &node
->abpairs
, *it
);
272 if( !strcmp( kv
->key
, key
) )
286 static const char *kv_get( vdf_node
*node
, const char *key
,
287 const char *value_defalt
)
290 char *val
= kv_next( node
, key
, &it
);
298 static void vdf_str_to_int( const char *src
, void *dest
)
300 *((int *)dest
) = atoi( src
);
303 static void vdf_str_to_float( const char *src
, void *dest
)
305 *((float *)dest
) = atof( src
);
308 static void vdf_str_to_double( const char *src
, void *dest
)
310 *((double *)dest
) = atof( src
);
313 static void kv_parse_array( const char *source
, u32 esize
, u32 count
,
314 void(*interp_func
)(const char *src
, void *dest
), void *arr
)
319 char value_buf
[ 64 ];
325 char const *c
= source
;
329 if( *c
==' ' || *c
=='\t' || *c
=='[' || *c
==']' || *c
=='(' || *c
==')' )
333 value_buf
[ k
] = 0x00;
336 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
355 if( k
< sizeof( value_buf
) - 1 )
357 value_buf
[ k
++ ] = *c
;
365 /* Add remaining case if we hit null */
366 if( token
&& (i
< count
) )
368 value_buf
[ k
] = 0x00;
369 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
373 static void kv_int_array( vdf_node
*node
, const char *key
, u32 count
, int *arr
)
375 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(int), count
,
376 vdf_str_to_int
, arr
);
379 static void kv_float_array( vdf_node
*node
, const char *key
, u32 count
,
382 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(float), count
,
383 vdf_str_to_float
, arr
);
386 static void kv_double_array( vdf_node
*node
, const char *key
, u32 count
,
389 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(double), count
,
390 vdf_str_to_double
, arr
);
393 static int kv_get_int( vdf_node
*node
, const char *key
,
394 const int default_value
)
396 const char *v
= kv_get( node
, key
, NULL
);
397 return v
? atoi(v
): default_value
;
400 static float kv_get_float( vdf_node
*node
, const char *key
,
401 float default_value
)
403 const char *v
= kv_get( node
, key
, NULL
);
404 return v
? atof( v
): default_value
;
407 static vdf_node
*vdf_create_node( vdf_node
*parent
, const char *name
)
409 vdf_node
*node
= calloc( 1, sizeof( vdf_node
) );
412 cxr_ab_init( &node
->abnodes
, sizeof( vdf_node
* ), 0 );
413 cxr_ab_init( &node
->abpairs
, sizeof( vdf_kv
), 0 );
417 node
->name
= cxr_str_clone(name
, 0);
422 node
->parent
= parent
;
424 vdf_node
**child
= cxr_ab_empty( &parent
->abnodes
);
431 static void vdf_kv_append( vdf_node
*p
, const char *k
, const char *v
)
433 vdf_kv
*kv
= cxr_ab_empty( &p
->abpairs
);
435 u32 sv
= strlen(v
)+1;
436 u32 sk
= strlen(k
)+1;
438 kv
->key
= malloc( sv
+sk
);
439 kv
->value
= kv
->key
+sk
;
441 memcpy( kv
->key
, k
, sk
);
442 memcpy( kv
->value
, v
, sv
);
445 static void vdf_free_r( vdf_node
*p
)
447 for( int i
= 0; i
< p
->abpairs
.count
; i
++ )
449 vdf_kv
*kv
= cxr_ab_ptr( &p
->abpairs
, i
);
450 free( kv
->key
); /* key and value are allocated in the same buffer */
453 for( int i
= 0; i
< p
->abnodes
.count
; i
++ )
455 vdf_node
**ptr_node
= cxr_ab_ptr( &p
->abnodes
, i
);
456 vdf_free_r( *ptr_node
);
459 cxr_ab_free( &p
->abpairs
);
460 cxr_ab_free( &p
->abnodes
);
494 static void vdf_newln( vdf_ctx
*ctx
)
498 ctx
->st
.tokens
[0] = NULL
;
499 ctx
->st
.tokens
[1] = NULL
;
503 static void vdf_endl( vdf_ctx
*ctx
)
505 if( ctx
->st
.tokens
[0] )
508 if( ctx
->st
.tokens
[1] )
510 vdf_kv_append( ctx
->st
.pnode
, ctx
->st
.tokens
[0], ctx
->st
.tokens
[1] );
515 strcpy( ctx
->name
, ctx
->st
.tokens
[0] );
516 ctx
->st
.expect_decl
= 1;
523 static int vdf_line_control( vdf_ctx
*ctx
)
525 if( *ctx
->st
.ptr_read
== '\r' )
527 *ctx
->st
.ptr_read
= 0x00;
530 if( *ctx
->st
.ptr_read
== '\n' )
532 *ctx
->st
.ptr_read
= 0x00;
540 static void vdf_wait_endl( vdf_ctx
*ctx
)
542 while( (*ctx
->st
.ptr_read
) && (*ctx
->st
.ptr_read
!= '\n') )
544 if( vdf_line_control( ctx
) == 2 )
553 static void vdf_parse_string( vdf_ctx
*ctx
)
555 while( *ctx
->st
.ptr_read
)
557 if( *ctx
->st
.ptr_read
== '"' )
559 *ctx
->st
.ptr_read
= 0x00;
563 if( vdf_line_control( ctx
) )
565 /* Unexpected end of line */
566 cxr_log( "vdf: unexpected EOL\n" );
574 static int vdf_parse_structure( vdf_ctx
*ctx
)
576 if( *ctx
->st
.ptr_read
== '{' )
578 if( ctx
->st
.tokens
[0] || !ctx
->st
.expect_decl
)
580 /* Unexpected token '{' */
581 cxr_log( "vdf: Unexpected token '{'\n" );
585 ctx
->st
.expect_decl
= 0;
586 ctx
->st
.pnode
= vdf_create_node( ctx
->st
.pnode
, ctx
->name
);
588 vdf_wait_endl( ctx
);
592 if( *ctx
->st
.ptr_read
== '}' )
594 if( !ctx
->st
.pnode
->parent
)
596 /* Unexpected token '}' */
597 cxr_log( "vdf: Unexpected token '}'\n" );
602 ctx
->st
.pnode
= ctx
->st
.pnode
->parent
;
605 vdf_wait_endl( ctx
);
612 static void vdf_parse_begin_token( vdf_ctx
*ctx
, char *ptr
)
614 ctx
->st
.tokens
[ ctx
->st
.i
] = ptr
;
616 if( ctx
->st
.expect_decl
)
618 /* Unexpected token 'name' */
619 cxr_log( "vdf: unexpected token 'name'\n" );
624 static void vdf_parse_feedbuffer( vdf_ctx
*ctx
, char *buf
)
626 ctx
->st
.ptr_read
= buf
;
628 while( *ctx
->st
.ptr_read
)
630 if( !vdf_line_control( ctx
) )
632 if( (*ctx
->st
.ptr_read
== '/') && (ctx
->st
.ptr_read
[1] == '/') )
634 *ctx
->st
.ptr_read
= 0x00;
635 ctx
->st
.ptr_read
+= 2;
638 vdf_wait_endl( ctx
);
642 if( !vdf_parse_structure( ctx
) )
644 if( *ctx
->st
.ptr_read
== ' ' || *ctx
->st
.ptr_read
== '\t' )
646 *ctx
->st
.ptr_read
= 0x00;
648 if( ctx
->st
.tokens
[ ctx
->st
.i
] )
654 vdf_wait_endl( ctx
);
660 else if( !ctx
->st
.tokens
[ ctx
->st
.i
] )
662 if( *ctx
->st
.ptr_read
== '"' )
664 *ctx
->st
.ptr_read
= 0x00;
667 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
668 vdf_parse_string( ctx
);
672 if( !( *ctx
->st
.ptr_read
== '/' &&
673 *(ctx
->st
.ptr_read
+ 1) == *ctx
->st
.ptr_read
) )
675 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
687 static void vdf_debug_indent( int level
)
689 for(int i
=0; i
<level
; i
++)
693 static void vdf_debug_r( vdf_node
*node
, int level
)
695 vdf_debug_indent(level
);
696 cxr_log( "vdf_node(%p, name: '%s')\n", node
, node
->name
);
698 vdf_debug_indent(level
);
701 for( int i
=0; i
<node
->abpairs
.count
; i
++ )
703 vdf_kv
*kv
= cxr_ab_ptr( &node
->abpairs
, i
);
705 vdf_debug_indent(level
+1);
706 cxr_log( "vdf_kv(%p, k: '%s', v: '%s')\n",
707 kv
, kv
->key
, kv
->value
);
710 for( int i
=0; i
<node
->abnodes
.count
; i
++ )
712 vdf_node
**child
= cxr_ab_ptr( &node
->abnodes
, i
);
713 vdf_debug_r( *child
, level
+1 );
716 vdf_debug_indent(level
);
720 /* This will wreck the original buffer, but must stay alive! */
721 static vdf_node
*vdf_from_buffer( char *buffer
)
723 vdf_node
*root
= vdf_create_node( NULL
, NULL
);
726 ctx
.root
= ctx
.st
.pnode
= root
;
729 vdf_parse_feedbuffer( &ctx
, buffer
);
732 vdf_debug_r( root
, 0 );
738 static vdf_node
*vdf_open_file( const char *fn
)
740 char *text_src
= cxr_textasset_read( fn
);
745 vdf_node
*root
= vdf_from_buffer( text_src
);
755 CXR_API i32
cxr_fs_set_gameinfo( const char *path
)
757 valve_file_system
*fs
= &fs_global
;
759 if( fs
->initialized
)
762 vdf_node
*info
= vdf_open_file( path
);
766 fs
->gamedir
= cxr_str_clone( path
, 0 );
767 cxr_downlvl( fs
->gamedir
);
769 fs
->exedir
= cxr_str_clone( fs
->gamedir
, 0 );
770 cxr_downlvl( fs
->exedir
);
772 cxr_log( "Setting up file system:\n"
777 path
, fs
->gamedir
, fs
->exedir
);
779 /* get search paths */
780 vdf_node
*search_paths
=
785 vdf_next( info
, "GameInfo", NULL
),
793 cxr_ab_init( &fs
->searchpaths
, sizeof( char *), 0 );
795 kv_foreach( search_paths
, "Game", kv_game
)
797 cxr_log( "Game %s\n", kv_game
);
799 if( kv_game
[0] == '|' ) continue;
802 if( cxr_path_is_abs( kv_game
) )
804 buf
= cxr_str_clone( kv_game
, 1 );
809 buf
= cxr_str_clone( fs
->exedir
, strlen(kv_game
)+1 );
810 strcat( buf
, kv_game
);
814 char **sp
= cxr_ab_empty( &fs
->searchpaths
);
820 /* Find pack diretory */
822 fs
->current_archive
= NULL
;
823 fs
->current_idx
= 0x7fff;
825 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
827 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
829 strcpy( pack_path
, *sp
);
830 strcat( pack_path
, "pak01_dir.vpk" );
832 fs
->current_archive
= fopen( pack_path
, "rb" );
834 /* Read vpk directory */
835 if( fs
->current_archive
)
837 fread( &fs
->vpk
, sizeof(VPKHeader
), 1, fs
->current_archive
);
839 fs
->directory_tree
= malloc( fs
->vpk
.TreeSize
);
840 fread( fs
->directory_tree
, fs
->vpk
.TreeSize
, 1, fs
->current_archive
);
846 if( !fs
->current_archive
)
848 cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
849 "Stock models will not load!\n", fs
->searchpaths
.count
);
856 CXR_API
void cxr_fs_exit(void)
858 valve_file_system
*fs
= &fs_global
;
860 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
862 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
866 cxr_ab_free( &fs
->searchpaths
);
868 if( fs
->directory_tree
)
870 free( fs
->directory_tree
);
871 fs
->directory_tree
= NULL
;
874 if( fs
->current_archive
)
876 fclose( fs
->current_archive
);
877 fs
->current_archive
= NULL
;
883 memset( fs
, 0, sizeof( valve_file_system
) );
886 static char *cxr_vpk_read( VPKDirectoryEntry
*entry
, int stringbuffer
)
888 valve_file_system
*fs
= &fs_global
;
890 if( !fs
->initialized
)
895 /* Check if we need to change file handle */
896 if( entry
->ArchiveIndex
!= fs
->current_idx
)
898 if( fs
->current_archive
)
899 fclose( fs
->current_archive
);
901 fs
->current_archive
= NULL
;
902 fs
->current_idx
= entry
->ArchiveIndex
;
904 if( entry
->ArchiveIndex
== 0x7fff )
906 snprintf( pak
, 1023, "%scsgo/pak01_dir.vpk", fs
->exedir
);
910 snprintf( pak
, 1023, "%scsgo/pak01_%03hu.vpk",
911 fs
->exedir
, entry
->ArchiveIndex
);
914 fs
->current_archive
= fopen( pak
, "rb" );
916 if( !fs
->current_archive
)
917 cxr_log( "Warning: could not locate %s\n", pak
);
920 if( !fs
->current_archive
)
923 size_t offset
= entry
->EntryOffset
,
924 length
= entry
->EntryLength
;
927 * File is stored in the index, after the tree
929 if( entry
->ArchiveIndex
== 0x7fff )
930 offset
+= fs
->vpk
.TreeSize
+ sizeof(VPKHeader
);
933 * Entire file is stored in the preload bytes;
934 * Backtrack offset from directory to get absolute offset
938 offset
= (char *)entry
- (char *)fs
->directory_tree
;
939 offset
+= sizeof( VPKHeader
);
940 offset
+= sizeof( VPKDirectoryEntry
);
942 length
= entry
->PreloadBytes
;
945 length
+= entry
->PreloadBytes
;
947 fseek( fs
->current_archive
, offset
, SEEK_SET
);
949 size_t alloc_size
= stringbuffer
? length
+1: length
;
950 char *filebuf
= malloc( alloc_size
);
953 filebuf
[length
] = 0x00;
955 if( fread( filebuf
, 1, length
, fs
->current_archive
) == length
)
965 CXR_API i32
cxr_fs_find( const char *path
, fs_locator
*locator
)
967 valve_file_system
*fs
= &fs_global
;
969 if( !fs
->initialized
)
972 VPKDirectoryEntry
*entry
;
974 if( fs
->directory_tree
)
976 if( (entry
= vpk_find( path
)) )
978 locator
->vpk_entry
= entry
;
979 locator
->path
[0] = 0x00;
984 locator
->vpk_entry
= NULL
;
986 /* Use physical search paths */
987 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
989 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
991 snprintf( locator
->path
, 1023, "%s%s", *sp
, path
);
993 if( cxr_file_exists( locator
->path
) )
1001 CXR_API
void cxr_fs_free( void *data
)
1006 CXR_API
void *cxr_fs_get( const char *path
, i32 stringbuffer
)
1008 valve_file_system
*fs
= &fs_global
;
1010 if( !fs
->initialized
)
1015 if( cxr_fs_find( path
, &locator
) )
1017 if( locator
.vpk_entry
)
1019 return cxr_vpk_read( locator
.vpk_entry
, stringbuffer
);
1026 return cxr_textasset_read( locator
.path
);
1028 return cxr_asset_read( locator
.path
);
1038 * This software is not affiliated with Valve Corporation
1039 * We are not affiliated, associated, authorized, endorsed by, or in any way
1040 * officially connected with Valve Corporation, or any of its subsidiaries or
1043 * All trademarks are property of their respective owners
1046 #define MAX_NUM_LODS 8
1047 #define MAX_NUM_BONES_PER_VERT 3
1049 #pragma pack(push, 1)
1052 float weight
[MAX_NUM_BONES_PER_VERT
];
1053 char bone
[MAX_NUM_BONES_PER_VERT
];
1060 boneWeight_t boneweights
;
1073 int numLodVertexes
[MAX_NUM_LODS
];
1075 int fixupTableStart
;
1076 int vertexDataStart
;
1077 int tangentDataStart
;
1083 static mstudiovertex_t
*GetVertexData( vertexFileHeader_t
*t
)
1085 return (mstudiovertex_t
*) ( (char *)t
+ t
->vertexDataStart
);
1092 #pragma pack(push, 1)
1096 unsigned char boneWeightIndex
[3];
1097 unsigned char numBones
;
1099 unsigned short origMeshVertID
;
1104 enum StripGroupFlags
1106 STRIPGROUP_IS_FLEXED
= 0x01,
1107 STRIPGROUP_IS_HWSKINNED
= 0x02,
1108 STRIPGROUP_IS_DELTA_FLEXED
= 0x04,
1109 STRIPGROUP_SUPPRESS_HW_MORPH
= 0x08
1112 enum StripHeaderFlags_t
{
1113 STRIP_IS_TRILIST
= 0x01,
1114 STRIP_IS_TRISTRIP
= 0x02 /* Unused by studiomdl 2015? */
1127 unsigned char flags
;
1129 int numBoneStateChanges
;
1130 int boneStateChangeOffset
;
1145 unsigned char flags
;
1147 VTXStripGroupHeader_t
;
1149 static VTXVertex_t
*pVertexVTX( VTXStripGroupHeader_t
*t
, int i
)
1151 return (VTXVertex_t
*)(((char *)t
) + t
->vertOffset
) + i
;
1154 static unsigned short *pIndexVTX( VTXStripGroupHeader_t
*t
, int i
)
1156 return (unsigned short *)(((char *)t
) + t
->indexOffset
) + i
;
1159 static VTXStripHeader_t
*pStripVTX( VTXStripGroupHeader_t
*t
, int i
)
1161 return (VTXStripHeader_t
*)(((char *)t
) + t
->stripOffset
) + i
;
1167 int stripGroupHeaderOffset
;
1169 unsigned char flags
;
1172 static VTXStripGroupHeader_t
*pStripGroupVTX( VTXMeshHeader_t
*t
, int i
)
1174 return (VTXStripGroupHeader_t
*)(((char *)t
) + t
->stripGroupHeaderOffset
) +i
;
1184 VTXModelLODHeader_t
;
1185 static VTXMeshHeader_t
*pMeshVTX( VTXModelLODHeader_t
*t
, int i
)
1187 return (VTXMeshHeader_t
*)(((char *)t
) + t
->meshOffset
) + i
;
1196 static VTXModelLODHeader_t
*pLODVTX( VTXModelHeader_t
*t
, int i
)
1198 return (VTXModelLODHeader_t
*)(((char *)t
) + t
->lodOffset
) + i
;
1206 VTXBodyPartHeader_t
;
1207 static VTXModelHeader_t
*pModelVTX( VTXBodyPartHeader_t
*t
, int i
)
1209 return (VTXModelHeader_t
*)(((char *)t
) + t
->modelOffset
) + i
;
1214 int version
; /* 7 */
1216 /* hardware params that affect how the model is to be optimized. */
1218 unsigned short maxBonesPerStrip
;
1219 unsigned short maxBonesPerTri
;
1220 int maxBonesPerVert
;
1224 int materialReplacementListOffset
;
1229 static VTXBodyPartHeader_t
*pBodyPartVTX( VTXFileHeader_t
*t
, int i
)
1231 return (VTXBodyPartHeader_t
*)(((char *)t
) + t
->bodyPartOffset
) + i
;
1236 =============================================
1244 L VerticesTable[StudioMDL.Vertex]
1245 L IndicesTable[UINT16]
1258 #pragma pack(push, 1)
1265 mstudio_modelvertexdata_t
;
1269 int unused_modelvertexdata
;
1270 int numLODVertexes
[MAX_NUM_LODS
];
1272 mstudio_modelvertexdata_t
*_the_death_ptr
;
1274 mstudio_meshvertexdata_t
;
1276 typedef struct mstudiomodel_t mstudiomodel_t
;
1289 mstudio_meshvertexdata_t vertexdata
;
1294 struct mstudiomodel_t
1298 float boundingradius
;
1308 int attachmentindex
;
1313 mstudio_modelvertexdata_t vertexdata
;
1317 static mstudiomesh_t
*studiomodel_pMesh( mstudiomodel_t
*t
, int i
)
1319 return (mstudiomesh_t
*)(((char *)t
) + t
->meshindex
) + i
;
1328 } mstudiobodyparts_t
;
1330 static mstudiomodel_t
*mstudiobodyparts_pModel( mstudiobodyparts_t
*t
, int i
)
1332 return (mstudiomodel_t
*)(((char *)t
) + t
->modelindex
) + i
;
1342 float eyeposition
[3];
1343 float illumposition
[3];
1346 float view_bbmin
[3];
1347 float view_bbmax
[3];
1351 int numbonecontrollers
;
1352 int bonecontrollerindex
;
1359 int activitylistversion
;
1366 int numskinfamilies
;
1370 int numlocalattachments
;
1371 int localattachmentindex
;
1374 int localnodenameindex
;
1377 int numflexcontrollers
;
1378 int flexcontrollerindex
;
1385 int numlocalposeparameters
;
1386 int localposeparamindex
;
1387 int surfacepropindex
;
1390 int numlocalikautoplaylocks
;
1391 int localikautoplaylockindex
;
1394 int numincludemodels
;
1395 int includemodelindex
;
1396 int szanimblocknameindex
;
1399 int bonetablebynameindex
;
1400 char constdirectionallightdot
;
1402 char numAllowedRootLODs
;
1405 int numflexcontrollerui
;
1406 int flexcontrolleruiindex
;
1407 float flVertAnimFixedPointScale
;
1409 int studiohdr2index
;
1414 static char *studiohdr_pCdtexture( studiohdr_t
*t
, int i
)
1416 return (((char *)t
) + *((int *)(((u8
*)t
) + t
->cdtextureindex
) + i
));
1419 static mstudiobodyparts_t
*studiohdr_pBodypart( studiohdr_t
*t
, int i
)
1421 return (mstudiobodyparts_t
*)(((char *)t
) + t
->bodypartindex
) + i
;
1430 /* There is some extra unused stuff that was here...
1431 * Luckily since byte offsets are used, structure size doesn't matter */
1435 static char *mstudiotexture_pszName( mstudiotexture_t
*t
)
1437 return ((char *)t
) + t
->sznameindex
;
1440 static mstudiotexture_t
*studiohdr_pTexture( studiohdr_t
*t
, int i
)
1442 return (mstudiotexture_t
*)(((u8
*)t
) + t
->textureindex
) + i
;
1447 static void vtx_resource_counts( VTXFileHeader_t
*pVtxHdr
, studiohdr_t
*pMdl
,
1448 u32
*indices
, u32
*meshes
)
1453 for( int bodyID
= 0; bodyID
< pVtxHdr
->numBodyParts
; ++bodyID
)
1456 VTXBodyPartHeader_t
* pVtxBodyPart
= pBodyPartVTX( pVtxHdr
, bodyID
);
1457 mstudiobodyparts_t
*pBodyPart
= studiohdr_pBodypart( pMdl
, bodyID
);
1459 for( int modelID
= 0; modelID
< pBodyPart
->nummodels
; ++modelID
)
1462 VTXModelHeader_t
* pVtxModel
= pModelVTX( pVtxBodyPart
, modelID
);
1463 mstudiomodel_t
*pStudioModel
=
1464 mstudiobodyparts_pModel( pBodyPart
, modelID
);
1467 VTXModelLODHeader_t
*pVtxLOD
= pLODVTX( pVtxModel
, nLod
);
1469 for( int nMesh
= 0; nMesh
< pStudioModel
->nummeshes
; ++nMesh
)
1472 VTXMeshHeader_t
* pVtxMesh
= pMeshVTX( pVtxLOD
, nMesh
);
1473 mstudiomesh_t
* pMesh
= studiomodel_pMesh( pStudioModel
, nMesh
);
1477 for ( int nGroup
= 0; nGroup
< pVtxMesh
->numStripGroups
; ++nGroup
)
1480 VTXStripGroupHeader_t
* pStripGroup
=
1481 pStripGroupVTX( pVtxMesh
, nGroup
);
1483 for ( int nStrip
= 0; nStrip
< pStripGroup
->numStrips
; nStrip
++ )
1486 VTXStripHeader_t
*pStrip
= pStripVTX( pStripGroup
, nStrip
);
1488 if ( pStrip
->flags
& STRIP_IS_TRILIST
)
1490 *indices
+= pStrip
->numIndices
;
1500 * The following section is the wrappers for the underlying types
1503 struct valve_material
1511 float *vertex_data
; /* pos xyz, norm xyz, uv xy */
1521 struct valve_model_batch
1529 /* Internal valve data */
1530 studiohdr_t
*studiohdr
;
1531 VTXFileHeader_t
*vtxhdr
;
1532 vertexFileHeader_t
*vvdhdr
;
1535 CXR_API valve_model
*valve_load_model( const char *relpath
)
1538 strcpy( path
, relpath
);
1540 char *ext
= cxr_stripext( path
);
1545 /* Load data files */
1546 valve_model
*model
= malloc( sizeof( valve_model
) );
1547 model
->studiohdr
= NULL
;
1548 model
->vtxhdr
= NULL
;
1549 model
->vvdhdr
= NULL
;
1551 strcpy( ext
, ".dx90.vtx" );
1552 model
->vtxhdr
= cxr_fs_get( path
, 0 );
1554 strcpy( ext
, ".vvd" );
1555 model
->vvdhdr
= cxr_fs_get( path
, 0 );
1557 strcpy( ext
, ".mdl" );
1558 model
->studiohdr
= cxr_fs_get( path
, 0 );
1560 if( !model
->vvdhdr
|| !model
->studiohdr
|| !model
->vtxhdr
)
1562 cxr_log( "Error, failed to load: (%s)\n", relpath
);
1564 free( model
->studiohdr
);
1565 free( model
->vvdhdr
);
1566 free( model
->studiohdr
);
1572 /* allocate resources */
1573 vtx_resource_counts( model
->vtxhdr
, model
->studiohdr
,
1574 &model
->indices_count
, &model
->part_count
);
1575 model
->vertex_count
= model
->vvdhdr
->numLodVertexes
[0];
1576 model
->material_count
= model
->studiohdr
->numtextures
;
1578 model
->materials
= malloc( model
->material_count
* sizeof(char *) );
1579 model
->parts
= malloc( sizeof( valve_model_batch
) * model
->part_count
);
1580 model
->indices
= malloc( sizeof( u32
) * model
->indices_count
);
1581 model
->vertex_data
= malloc( sizeof( float ) * 8 * model
->vertex_count
);
1583 /* Find materials */
1584 for( int i
=0; i
<model
->studiohdr
->numtextures
; i
++ )
1586 char material_path
[ 1024 ];
1589 mstudiotexture_t
*tex
= studiohdr_pTexture( model
->studiohdr
, i
);
1590 const char *name
= mstudiotexture_pszName( tex
);
1592 model
->materials
[i
] = NULL
;
1594 for( int j
=0; j
<model
->studiohdr
->numcdtextures
; j
++ )
1596 char *cdpath
= studiohdr_pCdtexture( model
->studiohdr
, j
);
1597 snprintf( material_path
, 1023, "materials/%s%s.vmt", cdpath
, name
);
1598 cxr_unixpath( material_path
);
1600 if( cxr_fs_find( material_path
, &locator
))
1602 model
->materials
[i
] = cxr_str_clone( material_path
, 0 );
1611 /* Extract meshes */
1612 for( int bodyID
= 0; bodyID
< model
->studiohdr
->numbodyparts
; ++bodyID
)
1615 VTXBodyPartHeader_t
* pVtxBodyPart
= pBodyPartVTX( model
->vtxhdr
, bodyID
);
1616 mstudiobodyparts_t
*pBodyPart
=
1617 studiohdr_pBodypart( model
->studiohdr
, bodyID
);
1619 for( int modelID
= 0; modelID
< pBodyPart
->nummodels
; ++modelID
)
1622 VTXModelHeader_t
* pVtxModel
= pModelVTX( pVtxBodyPart
, modelID
);
1623 mstudiomodel_t
*pStudioModel
=
1624 mstudiobodyparts_pModel( pBodyPart
, modelID
);
1627 VTXModelLODHeader_t
*pVtxLOD
= pLODVTX( pVtxModel
, nLod
);
1629 for( int nMesh
= 0; nMesh
< pStudioModel
->nummeshes
; ++nMesh
)
1631 /* meshes, each of these creates a new draw CMD */
1632 VTXMeshHeader_t
* pVtxMesh
= pMeshVTX( pVtxLOD
, nMesh
);
1633 mstudiomesh_t
* pMesh
= studiomodel_pMesh( pStudioModel
, nMesh
);
1635 valve_model_batch
*curBatch
= &model
->parts
[ i_mesh
++ ];
1636 curBatch
->material
= pMesh
->material
;
1637 curBatch
->ibstart
= i_index
;
1638 curBatch
->ibcount
= 0;
1640 for( int nGroup
= 0; nGroup
< pVtxMesh
->numStripGroups
; ++nGroup
)
1643 VTXStripGroupHeader_t
* pStripGroup
=
1644 pStripGroupVTX( pVtxMesh
, nGroup
);
1646 for( int nStrip
= 0; nStrip
< pStripGroup
->numStrips
; nStrip
++ )
1649 VTXStripHeader_t
*pStrip
= pStripVTX( pStripGroup
, nStrip
);
1651 if( pStrip
->flags
& STRIP_IS_TRILIST
)
1654 for( int i
= 0; i
< pStrip
->numIndices
; i
++ )
1656 u16 i1
= *pIndexVTX( pStripGroup
,
1657 pStrip
->indexOffset
+ i
);
1659 model
->indices
[ i_index
++ ] =
1660 pVertexVTX( pStripGroup
, i1
)->origMeshVertID
+
1661 pMesh
->vertexoffset
;
1663 curBatch
->ibcount
++;
1668 /* This is unused? */
1676 mstudiovertex_t
*vertexData
= GetVertexData( model
->vvdhdr
);
1678 for( int i
= 0; i
< model
->vertex_count
; i
++ )
1680 mstudiovertex_t
*vert
= &vertexData
[i
];
1682 float *dest
= &model
->vertex_data
[ i
*8 ];
1684 dest
[0] = vert
->pos
[0];
1685 dest
[1] = vert
->pos
[1];
1686 dest
[2] = vert
->pos
[2];
1688 dest
[3] = vert
->norm
[0];
1689 dest
[4] = vert
->norm
[1];
1690 dest
[5] = vert
->norm
[2];
1692 dest
[6] = vert
->uv
[0];
1693 dest
[7] = vert
->uv
[1];
1699 CXR_API
void valve_free_model( valve_model
*model
)
1701 for( int i
=0; i
<model
->material_count
; i
++ )
1702 free( model
->materials
[i
] );
1704 free( model
->materials
);
1705 free( model
->parts
);
1706 free( model
->indices
);
1707 free( model
->vertex_data
);
1709 free( model
->studiohdr
);
1710 free( model
->vtxhdr
);
1711 free( model
->vvdhdr
);
1715 static char *valve_texture_path( const char *path
)
1721 malloc( strlen( path
) + strlen(".vtf") + strlen("materials/") +1 );
1722 strcpy( buf
, "materials/" );
1723 strcat( buf
, path
);
1724 strcat( buf
, ".vtf" );
1726 cxr_unixpath( buf
);
1727 cxr_lowercase( buf
);
1732 CXR_API valve_material
*valve_load_material( const char *path
)
1734 char *vmt
= cxr_fs_get( path
, 1 );
1738 valve_material
*material
= malloc( sizeof(valve_material
) );
1739 vdf_node
*vmt_root
= vdf_from_buffer( vmt
);
1741 if( vmt_root
->abnodes
.count
== 0 )
1743 cxr_log( "Error: vmt has no nodes\n" );
1745 vdf_free_r( vmt_root
);
1749 vdf_node
**body
= cxr_ab_ptr( &vmt_root
->abnodes
, 0 );
1751 /* Path semantics here are inconsistent
1752 * I believe they should all just be converted to lowercase, though */
1754 for( int i
=0; i
<(*body
)->abpairs
.count
; i
++ )
1756 vdf_kv
*kv
= cxr_ab_ptr( &(*body
)->abpairs
, i
);
1757 cxr_lowercase( kv
->key
);
1760 const char *basetexture
= kv_get( *body
, "$basetexture", NULL
),
1761 *bumpmap
= kv_get( *body
, "$bumpmap", NULL
);
1763 /* TODO: other shader parameters */
1764 material
->basetexture
= valve_texture_path( basetexture
);
1765 material
->bumpmap
= valve_texture_path( bumpmap
);
1767 vdf_free_r( vmt_root
);
1776 CXR_API
void valve_free_material( valve_material
*material
)
1778 free( material
->basetexture
);
1779 free( material
->bumpmap
);
1782 #endif /* CXR_VALVE_BIN_H */