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 i32
cxr_fs_find( const char *path
, fs_locator
*locator
);
45 CXR_API valve_model
*valve_load_model( const char *relpath
);
46 CXR_API
void valve_free_model( valve_model
*model
);
47 CXR_API valve_material
*valve_load_material( const char *path
);
48 CXR_API
void valve_free_material( valve_material
*material
);
51 * File system implementation
61 u32 FileDataSectionSize
;
62 u32 ArchiveMD5SectionSize
;
63 u32 OtherMD5SectionSize
;
64 u32 SignatureSectionSize
;
67 struct VPKDirectoryEntry
79 static struct valve_file_system
88 cxr_abuffer searchpaths
;
90 FILE *current_archive
;
95 fs_global
= { .initialized
= 0 };
99 VPKDirectoryEntry
*vpk_entry
;
107 static VPKDirectoryEntry
*vpk_find( const char *asset
);
113 static vdf_node
*vdf_open_file( const char *fn
);
114 static void vdf_free_r( vdf_node
*p
);
116 /* Starting from *it, get next child with matching name from node. */
117 static vdf_node
*vdf_next( vdf_node
*node
, const char *name
, int *it
);
119 /* Create new empty node attached to parent. name can be NULL */
120 static vdf_node
*vdf_create_node( vdf_node
*parent
, const char *name
);
123 static const char *kv_get( vdf_node
*node
, const char *key
,
124 const char *value_defalt
);
126 /* Iterate each keyvalue starting from *it until key is matched */
127 static char *kv_next( vdf_node
*node
, const char *key
, int *it
);
129 static int kv_get_int( vdf_node
*node
, const char *key
, const int fallback
);
130 static float kv_get_float( vdf_node
*node
, const char *key
, float fallback
);
132 static void kv_int_array( vdf_node
*node
, const char *key
, u32 count
,
134 static void kv_float_array( vdf_node
*node
, const char *key
, u32 count
,
136 static void kv_double_array( vdf_node
*node
, const char *key
, u32 count
,
139 #define vdf_foreach( NODE, STR, AS ) \
140 int __vdf_it_##AS = 0; \
142 while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
144 #define kv_foreach( NODE, STR, AS ) \
145 int __kv_it_##AS = 0; \
147 while( (AS = kv_next( NODE, STR, &__kv_it_##AS )) )
149 static VPKDirectoryEntry
*vpk_find( const char *asset
)
151 valve_file_system
*fs
= &fs_global
;
153 if( !fs
->directory_tree
)
157 strcpy( wbuf
, asset
);
160 * This currently fails if the filename doesn't have a file extension, or
161 * if it is located at the root path. I'm not sure if this is defined
162 * behaviour or not. TODO: allow it to work anyway
165 char *ext
= cxr_findext( wbuf
, '.' );
167 if( !ext
) return NULL
;
170 char *fn
= cxr_findext( wbuf
, '/' );
172 if( !fn
) return NULL
;
176 char *pCur
= fs
->directory_tree
;
182 int bExt
= !strcmp( ext
, pCur
);
184 while( *( pCur
++ ) ) {};
187 if( !*pCur
) { pCur
++; break; }
189 int bDir
= !strcmp( dir
, pCur
);
191 while( *( pCur
++ ) ) {};
194 if( !*pCur
) { pCur
++; break; }
196 const char *vpk_fn
= pCur
;
198 while( *( pCur
++ ) ) {};
199 VPKDirectoryEntry
*entry
= (VPKDirectoryEntry
*)pCur
;
201 if( !strcmp( vpk_fn
, fn
) && bExt
&& bDir
)
206 pCur
+= entry
->PreloadBytes
+ sizeof( VPKDirectoryEntry
);
209 if( bDir
&& bExt
) return NULL
;
212 if( bExt
) return NULL
;
241 static vdf_node
*vdf_next( vdf_node
*node
, const char *name
, int *it
)
246 for( int i
= it
? *it
: 0; i
< node
->abnodes
.count
; i
++ )
248 vdf_node
**ptr_child
= cxr_ab_ptr( &node
->abnodes
, i
),
251 if( !name
|| !strcmp( name
, child
->name
))
261 static char *kv_next( vdf_node
*node
, const char *key
, int *it
)
267 while( *it
< node
->abpairs
.count
)
269 vdf_kv
*kv
= cxr_ab_ptr( &node
->abpairs
, *it
);
271 if( !strcmp( kv
->key
, key
) )
285 static const char *kv_get( vdf_node
*node
, const char *key
,
286 const char *value_defalt
)
289 char *val
= kv_next( node
, key
, &it
);
297 static void vdf_str_to_int( const char *src
, void *dest
)
299 *((int *)dest
) = atoi( src
);
302 static void vdf_str_to_float( const char *src
, void *dest
)
304 *((float *)dest
) = atof( src
);
307 static void vdf_str_to_double( const char *src
, void *dest
)
309 *((double *)dest
) = atof( src
);
312 static void kv_parse_array( const char *source
, u32 esize
, u32 count
,
313 void(*interp_func
)(const char *src
, void *dest
), void *arr
)
318 char value_buf
[ 64 ];
324 char const *c
= source
;
328 if( *c
==' ' || *c
=='\t' || *c
=='[' || *c
==']' || *c
=='(' || *c
==')' )
332 value_buf
[ k
] = 0x00;
335 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
354 if( k
< sizeof( value_buf
) - 1 )
356 value_buf
[ k
++ ] = *c
;
364 /* Add remaining case if we hit null */
365 if( token
&& (i
< count
) )
367 value_buf
[ k
] = 0x00;
368 interp_func( value_buf
, ((u8
*)arr
) + i
*esize
);
372 static void kv_int_array( vdf_node
*node
, const char *key
, u32 count
, int *arr
)
374 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(int), count
,
375 vdf_str_to_int
, arr
);
378 static void kv_float_array( vdf_node
*node
, const char *key
, u32 count
,
381 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(float), count
,
382 vdf_str_to_float
, arr
);
385 static void kv_double_array( vdf_node
*node
, const char *key
, u32 count
,
388 kv_parse_array( kv_get( node
, key
, NULL
), sizeof(double), count
,
389 vdf_str_to_double
, arr
);
392 static int kv_get_int( vdf_node
*node
, const char *key
,
393 const int default_value
)
395 const char *v
= kv_get( node
, key
, NULL
);
396 return v
? atoi(v
): default_value
;
399 static float kv_get_float( vdf_node
*node
, const char *key
,
400 float default_value
)
402 const char *v
= kv_get( node
, key
, NULL
);
403 return v
? atof( v
): default_value
;
406 static vdf_node
*vdf_create_node( vdf_node
*parent
, const char *name
)
408 vdf_node
*node
= calloc( 1, sizeof( vdf_node
) );
411 cxr_ab_init( &node
->abnodes
, sizeof( vdf_node
* ), 0 );
412 cxr_ab_init( &node
->abpairs
, sizeof( vdf_kv
), 0 );
416 node
->name
= cxr_str_clone(name
, 0);
421 node
->parent
= parent
;
423 vdf_node
**child
= cxr_ab_empty( &parent
->abnodes
);
430 static void vdf_kv_append( vdf_node
*p
, const char *k
, const char *v
)
432 vdf_kv
*kv
= cxr_ab_empty( &p
->abpairs
);
434 u32 sv
= strlen(v
)+1;
435 u32 sk
= strlen(k
)+1;
437 kv
->key
= malloc( sv
+sk
);
438 kv
->value
= kv
->key
+sk
;
440 memcpy( kv
->key
, k
, sk
);
441 memcpy( kv
->value
, v
, sv
);
444 static void vdf_free_r( vdf_node
*p
)
446 for( int i
= 0; i
< p
->abpairs
.count
; i
++ )
448 vdf_kv
*kv
= cxr_ab_ptr( &p
->abpairs
, i
);
449 free( kv
->key
); /* key and value are allocated in the same buffer */
452 for( int i
= 0; i
< p
->abnodes
.count
; i
++ )
454 vdf_node
**ptr_node
= cxr_ab_ptr( &p
->abnodes
, i
);
455 vdf_free_r( *ptr_node
);
458 cxr_ab_free( &p
->abpairs
);
459 cxr_ab_free( &p
->abnodes
);
493 static void vdf_newln( vdf_ctx
*ctx
)
497 ctx
->st
.tokens
[0] = NULL
;
498 ctx
->st
.tokens
[1] = NULL
;
502 static void vdf_endl( vdf_ctx
*ctx
)
504 if( ctx
->st
.tokens
[0] )
507 if( ctx
->st
.tokens
[1] )
509 vdf_kv_append( ctx
->st
.pnode
, ctx
->st
.tokens
[0], ctx
->st
.tokens
[1] );
514 strcpy( ctx
->name
, ctx
->st
.tokens
[0] );
515 ctx
->st
.expect_decl
= 1;
522 static int vdf_line_control( vdf_ctx
*ctx
)
524 if( *ctx
->st
.ptr_read
== '\r' )
526 *ctx
->st
.ptr_read
= 0x00;
529 if( *ctx
->st
.ptr_read
== '\n' )
531 *ctx
->st
.ptr_read
= 0x00;
539 static void vdf_wait_endl( vdf_ctx
*ctx
)
541 while( (*ctx
->st
.ptr_read
) && (*ctx
->st
.ptr_read
!= '\n') )
543 if( vdf_line_control( ctx
) == 2 )
552 static void vdf_parse_string( vdf_ctx
*ctx
)
554 while( *ctx
->st
.ptr_read
)
556 if( *ctx
->st
.ptr_read
== '"' )
558 *ctx
->st
.ptr_read
= 0x00;
562 if( vdf_line_control( ctx
) )
564 /* Unexpected end of line */
565 cxr_log( "vdf: unexpected EOL\n" );
573 static int vdf_parse_structure( vdf_ctx
*ctx
)
575 if( *ctx
->st
.ptr_read
== '{' )
577 if( ctx
->st
.tokens
[0] || !ctx
->st
.expect_decl
)
579 /* Unexpected token '{' */
580 cxr_log( "vdf: Unexpected token '{'\n" );
584 ctx
->st
.expect_decl
= 0;
585 ctx
->st
.pnode
= vdf_create_node( ctx
->st
.pnode
, ctx
->name
);
587 vdf_wait_endl( ctx
);
591 if( *ctx
->st
.ptr_read
== '}' )
593 if( !ctx
->st
.pnode
->parent
)
595 /* Unexpected token '}' */
596 cxr_log( "vdf: Unexpected token '}'\n" );
601 ctx
->st
.pnode
= ctx
->st
.pnode
->parent
;
604 vdf_wait_endl( ctx
);
611 static void vdf_parse_begin_token( vdf_ctx
*ctx
, char *ptr
)
613 ctx
->st
.tokens
[ ctx
->st
.i
] = ptr
;
615 if( ctx
->st
.expect_decl
)
617 /* Unexpected token 'name' */
618 cxr_log( "vdf: unexpected token 'name'\n" );
623 static void vdf_parse_feedbuffer( vdf_ctx
*ctx
, char *buf
)
625 ctx
->st
.ptr_read
= buf
;
627 while( *ctx
->st
.ptr_read
)
629 if( !vdf_line_control( ctx
) )
631 if( (*ctx
->st
.ptr_read
== '/') && (ctx
->st
.ptr_read
[1] == '/') )
633 *ctx
->st
.ptr_read
= 0x00;
634 ctx
->st
.ptr_read
+= 2;
637 vdf_wait_endl( ctx
);
641 if( !vdf_parse_structure( ctx
) )
643 if( *ctx
->st
.ptr_read
== ' ' || *ctx
->st
.ptr_read
== '\t' )
645 *ctx
->st
.ptr_read
= 0x00;
647 if( ctx
->st
.tokens
[ ctx
->st
.i
] )
653 vdf_wait_endl( ctx
);
659 else if( !ctx
->st
.tokens
[ ctx
->st
.i
] )
661 if( *ctx
->st
.ptr_read
== '"' )
663 *ctx
->st
.ptr_read
= 0x00;
666 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
667 vdf_parse_string( ctx
);
671 if( !( *ctx
->st
.ptr_read
== '/' &&
672 *(ctx
->st
.ptr_read
+ 1) == *ctx
->st
.ptr_read
) )
674 vdf_parse_begin_token( ctx
, ctx
->st
.ptr_read
);
686 static void vdf_debug_indent( int level
)
688 for(int i
=0; i
<level
; i
++)
692 static void vdf_debug_r( vdf_node
*node
, int level
)
694 vdf_debug_indent(level
);
695 cxr_log( "vdf_node(%p, name: '%s')\n", node
, node
->name
);
697 vdf_debug_indent(level
);
700 for( int i
=0; i
<node
->abpairs
.count
; i
++ )
702 vdf_kv
*kv
= cxr_ab_ptr( &node
->abpairs
, i
);
704 vdf_debug_indent(level
+1);
705 cxr_log( "vdf_kv(%p, k: '%s', v: '%s')\n",
706 kv
, kv
->key
, kv
->value
);
709 for( int i
=0; i
<node
->abnodes
.count
; i
++ )
711 vdf_node
**child
= cxr_ab_ptr( &node
->abnodes
, i
);
712 vdf_debug_r( *child
, level
+1 );
715 vdf_debug_indent(level
);
719 /* This will wreck the original buffer, but must stay alive! */
720 static vdf_node
*vdf_from_buffer( char *buffer
)
722 vdf_node
*root
= vdf_create_node( NULL
, NULL
);
725 ctx
.root
= ctx
.st
.pnode
= root
;
728 vdf_parse_feedbuffer( &ctx
, buffer
);
731 vdf_debug_r( root
, 0 );
737 static vdf_node
*vdf_open_file( const char *fn
)
739 char *text_src
= cxr_textasset_read( fn
);
744 vdf_node
*root
= vdf_from_buffer( text_src
);
754 CXR_API i32
cxr_fs_set_gameinfo( const char *path
)
756 valve_file_system
*fs
= &fs_global
;
758 if( fs
->initialized
)
761 vdf_node
*info
= vdf_open_file( path
);
765 fs
->gamedir
= cxr_str_clone( path
, 0 );
766 cxr_downlvl( fs
->gamedir
);
768 fs
->exedir
= cxr_str_clone( fs
->gamedir
, 0 );
769 cxr_downlvl( fs
->exedir
);
771 cxr_log( "Setting up file system:\n"
776 path
, fs
->gamedir
, fs
->exedir
);
778 /* get search paths */
779 vdf_node
*search_paths
=
784 vdf_next( info
, "GameInfo", NULL
),
792 cxr_ab_init( &fs
->searchpaths
, sizeof( char *), 0 );
794 kv_foreach( search_paths
, "Game", kv_game
)
796 cxr_log( "Game %s\n", kv_game
);
798 if( kv_game
[0] == '|' ) continue;
801 if( cxr_path_is_abs( kv_game
) )
803 buf
= cxr_str_clone( kv_game
, 1 );
808 buf
= cxr_str_clone( fs
->exedir
, strlen(kv_game
)+1 );
809 strcat( buf
, kv_game
);
813 char **sp
= cxr_ab_empty( &fs
->searchpaths
);
819 /* Find pack diretory */
821 fs
->current_archive
= NULL
;
822 fs
->current_idx
= 0x7fff;
824 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
826 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
828 strcpy( pack_path
, *sp
);
829 strcat( pack_path
, "pak01_dir.vpk" );
831 fs
->current_archive
= fopen( pack_path
, "rb" );
833 /* Read vpk directory */
834 if( fs
->current_archive
)
836 fread( &fs
->vpk
, sizeof(VPKHeader
), 1, fs
->current_archive
);
838 fs
->directory_tree
= malloc( fs
->vpk
.TreeSize
);
839 fread( fs
->directory_tree
, fs
->vpk
.TreeSize
, 1, fs
->current_archive
);
845 if( !fs
->current_archive
)
847 cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
848 "Stock models will not load!\n", fs
->searchpaths
.count
);
855 CXR_API
void cxr_fs_exit(void)
857 valve_file_system
*fs
= &fs_global
;
859 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
861 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
865 cxr_ab_free( &fs
->searchpaths
);
867 if( fs
->directory_tree
)
869 free( fs
->directory_tree
);
870 fs
->directory_tree
= NULL
;
873 if( fs
->current_archive
)
875 fclose( fs
->current_archive
);
876 fs
->current_archive
= NULL
;
882 memset( fs
, 0, sizeof( valve_file_system
) );
885 static char *cxr_vpk_read( VPKDirectoryEntry
*entry
, int stringbuffer
)
887 valve_file_system
*fs
= &fs_global
;
889 if( !fs
->initialized
)
894 /* Check if we need to change file handle */
895 if( entry
->ArchiveIndex
!= fs
->current_idx
)
897 if( fs
->current_archive
)
898 fclose( fs
->current_archive
);
900 fs
->current_archive
= NULL
;
901 fs
->current_idx
= entry
->ArchiveIndex
;
903 if( entry
->ArchiveIndex
== 0x7fff )
905 snprintf( pak
, 1023, "%scsgo/pak01_dir.vpk", fs
->exedir
);
909 snprintf( pak
, 1023, "%scsgo/pak01_%03hu.vpk",
910 fs
->exedir
, entry
->ArchiveIndex
);
913 fs
->current_archive
= fopen( pak
, "rb" );
915 if( !fs
->current_archive
)
916 cxr_log( "Warning: could not locate %s\n", pak
);
919 if( !fs
->current_archive
)
922 size_t offset
= entry
->EntryOffset
,
923 length
= entry
->EntryLength
;
926 * File is stored in the index, after the tree
928 if( entry
->ArchiveIndex
== 0x7fff )
929 offset
+= fs
->vpk
.TreeSize
+ sizeof(VPKHeader
);
932 * Entire file is stored in the preload bytes;
933 * Backtrack offset from directory to get absolute offset
937 offset
= (char *)entry
- (char *)fs
->directory_tree
;
938 offset
+= sizeof( VPKHeader
);
939 offset
+= sizeof( VPKDirectoryEntry
);
941 length
= entry
->PreloadBytes
;
944 length
+= entry
->PreloadBytes
;
946 fseek( fs
->current_archive
, offset
, SEEK_SET
);
948 size_t alloc_size
= stringbuffer
? length
+1: length
;
949 char *filebuf
= malloc( alloc_size
);
952 filebuf
[length
] = 0x00;
954 if( fread( filebuf
, 1, length
, fs
->current_archive
) == length
)
964 CXR_API i32
cxr_fs_find( const char *path
, fs_locator
*locator
)
966 valve_file_system
*fs
= &fs_global
;
968 if( !fs
->initialized
)
971 VPKDirectoryEntry
*entry
;
973 if( fs
->directory_tree
)
975 if( (entry
= vpk_find( path
)) )
977 locator
->vpk_entry
= entry
;
978 locator
->path
[0] = 0x00;
983 locator
->vpk_entry
= NULL
;
985 /* Use physical search paths */
986 for( int i
= 0; i
< fs
->searchpaths
.count
; i
++ )
988 char **sp
= cxr_ab_ptr( &fs
->searchpaths
, i
);
990 snprintf( locator
->path
, 1023, "%s%s", *sp
, path
);
992 if( cxr_file_exists( locator
->path
) )
1000 CXR_API
void *cxr_fs_get( const char *path
, i32 stringbuffer
)
1002 valve_file_system
*fs
= &fs_global
;
1004 if( !fs
->initialized
)
1009 if( cxr_fs_find( path
, &locator
) )
1011 if( locator
.vpk_entry
)
1013 return cxr_vpk_read( locator
.vpk_entry
, stringbuffer
);
1020 return cxr_textasset_read( locator
.path
);
1022 return cxr_asset_read( locator
.path
);
1032 * This software is not affiliated with Valve Corporation
1033 * We are not affiliated, associated, authorized, endorsed by, or in any way
1034 * officially connected with Valve Corporation, or any of its subsidiaries or
1037 * All trademarks are property of their respective owners
1040 #define MAX_NUM_LODS 8
1041 #define MAX_NUM_BONES_PER_VERT 3
1043 #pragma pack(push, 1)
1046 float weight
[MAX_NUM_BONES_PER_VERT
];
1047 char bone
[MAX_NUM_BONES_PER_VERT
];
1054 boneWeight_t boneweights
;
1067 int numLodVertexes
[MAX_NUM_LODS
];
1069 int fixupTableStart
;
1070 int vertexDataStart
;
1071 int tangentDataStart
;
1077 static mstudiovertex_t
*GetVertexData( vertexFileHeader_t
*t
)
1079 return (mstudiovertex_t
*) ( (char *)t
+ t
->vertexDataStart
);
1086 #pragma pack(push, 1)
1090 unsigned char boneWeightIndex
[3];
1091 unsigned char numBones
;
1093 unsigned short origMeshVertID
;
1098 enum StripGroupFlags
1100 STRIPGROUP_IS_FLEXED
= 0x01,
1101 STRIPGROUP_IS_HWSKINNED
= 0x02,
1102 STRIPGROUP_IS_DELTA_FLEXED
= 0x04,
1103 STRIPGROUP_SUPPRESS_HW_MORPH
= 0x08
1106 enum StripHeaderFlags_t
{
1107 STRIP_IS_TRILIST
= 0x01,
1108 STRIP_IS_TRISTRIP
= 0x02 /* Unused by studiomdl 2015? */
1121 unsigned char flags
;
1123 int numBoneStateChanges
;
1124 int boneStateChangeOffset
;
1139 unsigned char flags
;
1141 VTXStripGroupHeader_t
;
1143 static VTXVertex_t
*pVertexVTX( VTXStripGroupHeader_t
*t
, int i
)
1145 return (VTXVertex_t
*)(((char *)t
) + t
->vertOffset
) + i
;
1148 static unsigned short *pIndexVTX( VTXStripGroupHeader_t
*t
, int i
)
1150 return (unsigned short *)(((char *)t
) + t
->indexOffset
) + i
;
1153 static VTXStripHeader_t
*pStripVTX( VTXStripGroupHeader_t
*t
, int i
)
1155 return (VTXStripHeader_t
*)(((char *)t
) + t
->stripOffset
) + i
;
1161 int stripGroupHeaderOffset
;
1163 unsigned char flags
;
1166 static VTXStripGroupHeader_t
*pStripGroupVTX( VTXMeshHeader_t
*t
, int i
)
1168 return (VTXStripGroupHeader_t
*)(((char *)t
) + t
->stripGroupHeaderOffset
) +i
;
1178 VTXModelLODHeader_t
;
1179 static VTXMeshHeader_t
*pMeshVTX( VTXModelLODHeader_t
*t
, int i
)
1181 return (VTXMeshHeader_t
*)(((char *)t
) + t
->meshOffset
) + i
;
1190 static VTXModelLODHeader_t
*pLODVTX( VTXModelHeader_t
*t
, int i
)
1192 return (VTXModelLODHeader_t
*)(((char *)t
) + t
->lodOffset
) + i
;
1200 VTXBodyPartHeader_t
;
1201 static VTXModelHeader_t
*pModelVTX( VTXBodyPartHeader_t
*t
, int i
)
1203 return (VTXModelHeader_t
*)(((char *)t
) + t
->modelOffset
) + i
;
1208 int version
; /* 7 */
1210 /* hardware params that affect how the model is to be optimized. */
1212 unsigned short maxBonesPerStrip
;
1213 unsigned short maxBonesPerTri
;
1214 int maxBonesPerVert
;
1218 int materialReplacementListOffset
;
1223 static VTXBodyPartHeader_t
*pBodyPartVTX( VTXFileHeader_t
*t
, int i
)
1225 return (VTXBodyPartHeader_t
*)(((char *)t
) + t
->bodyPartOffset
) + i
;
1230 =============================================
1238 L VerticesTable[StudioMDL.Vertex]
1239 L IndicesTable[UINT16]
1252 #pragma pack(push, 1)
1259 mstudio_modelvertexdata_t
;
1263 int unused_modelvertexdata
;
1264 int numLODVertexes
[MAX_NUM_LODS
];
1266 mstudio_modelvertexdata_t
*_the_death_ptr
;
1268 mstudio_meshvertexdata_t
;
1270 typedef struct mstudiomodel_t mstudiomodel_t
;
1283 mstudio_meshvertexdata_t vertexdata
;
1288 struct mstudiomodel_t
1292 float boundingradius
;
1302 int attachmentindex
;
1307 mstudio_modelvertexdata_t vertexdata
;
1311 static mstudiomesh_t
*studiomodel_pMesh( mstudiomodel_t
*t
, int i
)
1313 return (mstudiomesh_t
*)(((char *)t
) + t
->meshindex
) + i
;
1322 } mstudiobodyparts_t
;
1324 static mstudiomodel_t
*mstudiobodyparts_pModel( mstudiobodyparts_t
*t
, int i
)
1326 return (mstudiomodel_t
*)(((char *)t
) + t
->modelindex
) + i
;
1336 float eyeposition
[3];
1337 float illumposition
[3];
1340 float view_bbmin
[3];
1341 float view_bbmax
[3];
1345 int numbonecontrollers
;
1346 int bonecontrollerindex
;
1353 int activitylistversion
;
1360 int numskinfamilies
;
1364 int numlocalattachments
;
1365 int localattachmentindex
;
1368 int localnodenameindex
;
1371 int numflexcontrollers
;
1372 int flexcontrollerindex
;
1379 int numlocalposeparameters
;
1380 int localposeparamindex
;
1381 int surfacepropindex
;
1384 int numlocalikautoplaylocks
;
1385 int localikautoplaylockindex
;
1388 int numincludemodels
;
1389 int includemodelindex
;
1390 int szanimblocknameindex
;
1393 int bonetablebynameindex
;
1394 char constdirectionallightdot
;
1396 char numAllowedRootLODs
;
1399 int numflexcontrollerui
;
1400 int flexcontrolleruiindex
;
1401 float flVertAnimFixedPointScale
;
1403 int studiohdr2index
;
1408 static char *studiohdr_pCdtexture( studiohdr_t
*t
, int i
)
1410 return (((char *)t
) + *((int *)(((u8
*)t
) + t
->cdtextureindex
) + i
));
1413 static mstudiobodyparts_t
*studiohdr_pBodypart( studiohdr_t
*t
, int i
)
1415 return (mstudiobodyparts_t
*)(((char *)t
) + t
->bodypartindex
) + i
;
1424 /* There is some extra unused stuff that was here...
1425 * Luckily since byte offsets are used, structure size doesn't matter */
1429 static char *mstudiotexture_pszName( mstudiotexture_t
*t
)
1431 return ((char *)t
) + t
->sznameindex
;
1434 static mstudiotexture_t
*studiohdr_pTexture( studiohdr_t
*t
, int i
)
1436 return (mstudiotexture_t
*)(((u8
*)t
) + t
->textureindex
) + i
;
1441 static void vtx_resource_counts( VTXFileHeader_t
*pVtxHdr
, studiohdr_t
*pMdl
,
1442 u32
*indices
, u32
*meshes
)
1447 for( int bodyID
= 0; bodyID
< pVtxHdr
->numBodyParts
; ++bodyID
)
1450 VTXBodyPartHeader_t
* pVtxBodyPart
= pBodyPartVTX( pVtxHdr
, bodyID
);
1451 mstudiobodyparts_t
*pBodyPart
= studiohdr_pBodypart( pMdl
, bodyID
);
1453 for( int modelID
= 0; modelID
< pBodyPart
->nummodels
; ++modelID
)
1456 VTXModelHeader_t
* pVtxModel
= pModelVTX( pVtxBodyPart
, modelID
);
1457 mstudiomodel_t
*pStudioModel
=
1458 mstudiobodyparts_pModel( pBodyPart
, modelID
);
1461 VTXModelLODHeader_t
*pVtxLOD
= pLODVTX( pVtxModel
, nLod
);
1463 for( int nMesh
= 0; nMesh
< pStudioModel
->nummeshes
; ++nMesh
)
1466 VTXMeshHeader_t
* pVtxMesh
= pMeshVTX( pVtxLOD
, nMesh
);
1467 mstudiomesh_t
* pMesh
= studiomodel_pMesh( pStudioModel
, nMesh
);
1471 for ( int nGroup
= 0; nGroup
< pVtxMesh
->numStripGroups
; ++nGroup
)
1474 VTXStripGroupHeader_t
* pStripGroup
=
1475 pStripGroupVTX( pVtxMesh
, nGroup
);
1477 for ( int nStrip
= 0; nStrip
< pStripGroup
->numStrips
; nStrip
++ )
1480 VTXStripHeader_t
*pStrip
= pStripVTX( pStripGroup
, nStrip
);
1482 if ( pStrip
->flags
& STRIP_IS_TRILIST
)
1484 *indices
+= pStrip
->numIndices
;
1494 * The following section is the wrappers for the underlying types
1497 struct valve_material
1505 float *vertex_data
; /* pos xyz, norm xyz, uv xy */
1515 struct valve_model_batch
1523 /* Internal valve data */
1524 studiohdr_t
*studiohdr
;
1525 VTXFileHeader_t
*vtxhdr
;
1526 vertexFileHeader_t
*vvdhdr
;
1529 CXR_API valve_model
*valve_load_model( const char *relpath
)
1532 strcpy( path
, relpath
);
1534 char *ext
= cxr_stripext( path
);
1539 /* Load data files */
1540 valve_model
*model
= malloc( sizeof( valve_model
) );
1541 model
->studiohdr
= NULL
;
1542 model
->vtxhdr
= NULL
;
1543 model
->vvdhdr
= NULL
;
1545 strcpy( ext
, ".dx90.vtx" );
1546 model
->vtxhdr
= cxr_fs_get( path
, 0 );
1548 strcpy( ext
, ".vvd" );
1549 model
->vvdhdr
= cxr_fs_get( path
, 0 );
1551 strcpy( ext
, ".mdl" );
1552 model
->studiohdr
= cxr_fs_get( path
, 0 );
1554 if( !model
->vvdhdr
|| !model
->studiohdr
|| !model
->vtxhdr
)
1556 cxr_log( "Error, failed to load: (%s)\n", relpath
);
1558 free( model
->studiohdr
);
1559 free( model
->vvdhdr
);
1560 free( model
->studiohdr
);
1566 /* allocate resources */
1567 vtx_resource_counts( model
->vtxhdr
, model
->studiohdr
,
1568 &model
->indices_count
, &model
->part_count
);
1569 model
->vertex_count
= model
->vvdhdr
->numLodVertexes
[0];
1570 model
->material_count
= model
->studiohdr
->numtextures
;
1572 model
->materials
= malloc( model
->material_count
* sizeof(char *) );
1573 model
->parts
= malloc( sizeof( valve_model_batch
) * model
->part_count
);
1574 model
->indices
= malloc( sizeof( u32
) * model
->indices_count
);
1575 model
->vertex_data
= malloc( sizeof( float ) * 8 * model
->vertex_count
);
1577 /* Find materials */
1578 for( int i
=0; i
<model
->studiohdr
->numtextures
; i
++ )
1580 char material_path
[ 1024 ];
1583 mstudiotexture_t
*tex
= studiohdr_pTexture( model
->studiohdr
, i
);
1584 const char *name
= mstudiotexture_pszName( tex
);
1586 model
->materials
[i
] = NULL
;
1588 for( int j
=0; j
<model
->studiohdr
->numcdtextures
; j
++ )
1590 char *cdpath
= studiohdr_pCdtexture( model
->studiohdr
, j
);
1591 snprintf( material_path
, 1023, "materials/%s%s.vmt", cdpath
, name
);
1592 cxr_unixpath( material_path
);
1594 if( cxr_fs_find( material_path
, &locator
))
1596 model
->materials
[i
] = cxr_str_clone( material_path
, 0 );
1605 /* Extract meshes */
1606 for( int bodyID
= 0; bodyID
< model
->studiohdr
->numbodyparts
; ++bodyID
)
1609 VTXBodyPartHeader_t
* pVtxBodyPart
= pBodyPartVTX( model
->vtxhdr
, bodyID
);
1610 mstudiobodyparts_t
*pBodyPart
=
1611 studiohdr_pBodypart( model
->studiohdr
, bodyID
);
1613 for( int modelID
= 0; modelID
< pBodyPart
->nummodels
; ++modelID
)
1616 VTXModelHeader_t
* pVtxModel
= pModelVTX( pVtxBodyPart
, modelID
);
1617 mstudiomodel_t
*pStudioModel
=
1618 mstudiobodyparts_pModel( pBodyPart
, modelID
);
1621 VTXModelLODHeader_t
*pVtxLOD
= pLODVTX( pVtxModel
, nLod
);
1623 for( int nMesh
= 0; nMesh
< pStudioModel
->nummeshes
; ++nMesh
)
1625 /* meshes, each of these creates a new draw CMD */
1626 VTXMeshHeader_t
* pVtxMesh
= pMeshVTX( pVtxLOD
, nMesh
);
1627 mstudiomesh_t
* pMesh
= studiomodel_pMesh( pStudioModel
, nMesh
);
1629 valve_model_batch
*curBatch
= &model
->parts
[ i_mesh
++ ];
1630 curBatch
->material
= pMesh
->material
;
1631 curBatch
->ibstart
= i_index
;
1632 curBatch
->ibcount
= 0;
1634 for( int nGroup
= 0; nGroup
< pVtxMesh
->numStripGroups
; ++nGroup
)
1637 VTXStripGroupHeader_t
* pStripGroup
=
1638 pStripGroupVTX( pVtxMesh
, nGroup
);
1640 for( int nStrip
= 0; nStrip
< pStripGroup
->numStrips
; nStrip
++ )
1643 VTXStripHeader_t
*pStrip
= pStripVTX( pStripGroup
, nStrip
);
1645 if( pStrip
->flags
& STRIP_IS_TRILIST
)
1648 for( int i
= 0; i
< pStrip
->numIndices
; i
++ )
1650 u16 i1
= *pIndexVTX( pStripGroup
,
1651 pStrip
->indexOffset
+ i
);
1653 model
->indices
[ i_index
++ ] =
1654 pVertexVTX( pStripGroup
, i1
)->origMeshVertID
+
1655 pMesh
->vertexoffset
;
1657 curBatch
->ibcount
++;
1662 /* This is unused? */
1670 mstudiovertex_t
*vertexData
= GetVertexData( model
->vvdhdr
);
1672 for( int i
= 0; i
< model
->vertex_count
; i
++ )
1674 mstudiovertex_t
*vert
= &vertexData
[i
];
1676 float *dest
= &model
->vertex_data
[ i
*8 ];
1678 dest
[0] = vert
->pos
[0];
1679 dest
[1] = vert
->pos
[1];
1680 dest
[2] = vert
->pos
[2];
1682 dest
[3] = vert
->norm
[0];
1683 dest
[4] = vert
->norm
[1];
1684 dest
[5] = vert
->norm
[2];
1686 dest
[6] = vert
->uv
[0];
1687 dest
[7] = vert
->uv
[1];
1693 CXR_API
void valve_free_model( valve_model
*model
)
1695 for( int i
=0; i
<model
->material_count
; i
++ )
1696 free( model
->materials
[i
] );
1698 free( model
->materials
);
1699 free( model
->parts
);
1700 free( model
->indices
);
1701 free( model
->vertex_data
);
1703 free( model
->studiohdr
);
1704 free( model
->vtxhdr
);
1705 free( model
->vvdhdr
);
1709 static char *valve_texture_path( const char *path
)
1714 char *buf
= cxr_str_clone( path
, 4 );
1716 strcat( buf
, ".vtf" );
1717 cxr_unixpath( buf
);
1718 cxr_lowercase( buf
);
1723 CXR_API valve_material
*valve_load_material( const char *path
)
1725 char *vmt
= cxr_fs_get( path
, 1 );
1729 valve_material
*material
= malloc( sizeof(valve_material
) );
1730 vdf_node
*vmt_root
= vdf_from_buffer( vmt
);
1732 if( vmt_root
->abnodes
.count
== 0 )
1734 cxr_log( "Error: vmt has no nodes\n" );
1736 vdf_free_r( vmt_root
);
1740 vdf_node
**body
= cxr_ab_ptr( &vmt_root
->abnodes
, 0 );
1742 /* Path semantics here are inconsistent
1743 * I believe they should all just be converted to lowercase, though */
1745 for( int i
=0; i
<(*body
)->abpairs
.count
; i
++ )
1747 vdf_kv
*kv
= cxr_ab_ptr( &(*body
)->abpairs
, i
);
1748 cxr_lowercase( kv
->key
);
1751 const char *basetexture
= kv_get( *body
, "$basetexture", NULL
),
1752 *bumpmap
= kv_get( *body
, "$bumpmap", NULL
);
1754 /* TODO: other shader parameters */
1755 material
->basetexture
= valve_texture_path( basetexture
);
1756 material
->bumpmap
= valve_texture_path( bumpmap
);
1758 vdf_free_r( vmt_root
);
1767 CXR_API
void valve_free_material( valve_material
*material
)
1769 free( material
->basetexture
);
1770 free( material
->bumpmap
);
1773 #endif /* CXR_VALVE_BIN_H */