model view prototype
authorhgn <hgodden00@gmail.com>
Sun, 1 May 2022 00:03:32 +0000 (01:03 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 1 May 2022 00:03:32 +0000 (01:03 +0100)
__init__.py
build.sh
cxr/cxr.h
cxr/cxr_io.h [new file with mode: 0644]
cxr/cxr_log.h [new file with mode: 0644]
cxr/cxr_math.h
cxr/cxr_mem.h
cxr/cxr_types.h [new file with mode: 0644]
cxr/cxr_valve_bin.h [new file with mode: 0644]
cxr/test.c

index 3ce19bdce4b7eb645e855d44fbcde0b1f9250d88..9eba53c76808714d79414d3a69485091795a47ea 100644 (file)
@@ -48,12 +48,14 @@ cxr_ui_draw_handler = None
 # Batches
 cxr_view_lines = None
 cxr_view_mesh = None
+cxr_mdl_mesh = None
 cxr_jobs_batch = None
 cxr_jobs_inf = []
 cxr_error_inf = None
 
 # Shaders
 cxr_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
+
 cxr_ui_shader = gpu.types.GPUShader("""
 uniform mat4 ModelViewProjectionMatrix;
 uniform float scale;
@@ -78,6 +80,88 @@ void main()
 }
 """)
 
+cxr_mdl_shader = gpu.types.GPUShader("""
+uniform mat4 modelMatrix;
+uniform mat4 viewProjectionMatrix;
+
+in vec3 aPos;
+in vec3 aNormal;
+
+out vec3 lPos;
+out vec3 lNormal;
+
+void main()
+{
+   vec4 pWorldPos = modelMatrix * vec4(aPos, 1.0); 
+   vec3 worldPos = pWorldPos.xyz;
+
+   gl_Position = viewProjectionMatrix * pWorldPos;
+   lNormal = aNormal; //mat3(transpose(inverse(modelMatrix))) * aNormal;
+   lPos = worldPos;
+}
+""","""
+
+uniform vec4 colour;
+uniform vec3 testLightDir;
+
+in vec3 lNormal;
+in vec3 lPos;
+
+out vec4 FragColor;
+
+float SoftenCosineTerm( float flDot )
+{
+       return ( flDot + ( flDot * flDot ) ) * 0.5;
+}
+
+vec3 DiffuseTerm( vec3 worldNormal, vec3 lightDir )
+{
+   float fResult = 0.0;
+   float NDotL = dot( worldNormal, lightDir );
+
+   fResult = clamp( NDotL, 0.0, 1.0 );
+   fResult = SoftenCosineTerm( fResult );
+
+   vec3 fOut = vec3( fResult, fResult, fResult );
+   return fOut;
+}
+
+vec3 PixelShaderDoLightingLinear( vec3 worldPos, vec3 worldNormal )
+{
+   vec3 linearColor = vec3(0.0,0.0,0.0);
+   linearColor += DiffuseTerm( worldNormal, testLightDir );
+
+   return linearColor;
+}
+
+vec3 LinearToGamma( vec3 f3linear )
+{
+       return pow( f3linear, vec3(1.0 / 2.2) );
+}
+
+void main()
+{
+   vec3 tangentSpaceNormal = vec3( 0.0, 0.0, 1.0 );
+   vec4 normalTexel = vec4(1.0,1.0,1.0,1.0);
+   vec4 baseColor = colour;
+
+       //normalTexel = tex2D( BumpmapSampler, i.detailOrBumpTexCoord );
+       //tangentSpaceNormal = 2.0 * normalTexel - 1.0;
+
+       vec3 diffuseLighting = vec3( 1.0, 1.0, 1.0 );
+
+   vec3 staticLightingColor = vec3( 0.0, 0.0, 0.0 );
+   diffuseLighting = PixelShaderDoLightingLinear( lPos, lNormal );
+
+   // multiply by .5 since we want a 50% (in gamma space) reflective surface)
+   diffuseLighting *= pow( 0.5, 2.2 );
+
+   vec3 result = diffuseLighting * baseColor.xyz;
+
+   FragColor = vec4( LinearToGamma(result), 1.0 );
+}
+""")
+
 # Render functions
 #
 def cxr_ui(_,context):
@@ -137,7 +221,8 @@ def cxr_ui(_,context):
    CXR_COMPILER_CHAIN.WAIT_REDRAW = False
 
 def cxr_draw():
-   global cxr_view_shader, cxr_view_mesh, cxr_view_lines
+   global cxr_view_shader, cxr_view_mesh, cxr_view_lines, cxr_mdl_shader,\
+          cxr_mdl_mesh
 
    cxr_view_shader.bind()
 
@@ -150,11 +235,33 @@ def cxr_draw():
    if cxr_view_lines != None:
       cxr_view_lines.draw( cxr_view_shader )
 
-   gpu.state.depth_test_set('LESS_EQUAL')
-   gpu.state.blend_set('ADDITIVE')
    if cxr_view_mesh != None:
+      gpu.state.depth_test_set('LESS_EQUAL')
+      gpu.state.blend_set('ADDITIVE')
+
       cxr_view_mesh.draw( cxr_view_shader )
 
+   if cxr_mdl_mesh != None:
+      gpu.state.depth_mask_set(True)
+      gpu.state.depth_test_set('LESS_EQUAL')
+      gpu.state.face_culling_set('FRONT')
+      gpu.state.blend_set('NONE')
+      cxr_mdl_shader.bind()
+      cxr_mdl_shader.uniform_float('colour',(0.5,0.5,0.5,1.0))
+      cxr_mdl_shader.uniform_float("viewProjectionMatrix", \
+            bpy.context.region_data.perspective_matrix)
+
+      testmdl = bpy.context.scene.objects['target']
+      light = bpy.context.scene.objects['point']
+      relative = light.location - testmdl.location 
+      relative.normalize()
+
+      cxr_mdl_shader.uniform_float("modelMatrix", testmdl.matrix_world)
+      cxr_mdl_shader.uniform_float("testLightDir", relative)
+
+
+      cxr_mdl_mesh.draw( cxr_mdl_shader )
+
 def cxr_jobs_update_graph(jobs):
    global cxr_jobs_batch, cxr_ui_shader, cxr_jobs_inf
 
@@ -287,6 +394,8 @@ class cxr_static_mesh(Structure):
 
 class cxr_tri_mesh(Structure):
    _fields_ = [("vertices",POINTER(c_double *3)),
+               ("normals",POINTER(c_double *3)),
+               ("uvs",POINTER(c_double *2)),
                ("colours",POINTER(c_double *4)),
                ("indices",POINTER(c_int32)),
                ("indices_count",c_int32),
@@ -481,15 +590,22 @@ class vdf_structure():
 # Other
 libcxr_lightpatch_bsp = extern( "cxr_lightpatch_bsp", [c_char_p], None )
 
+# Binary file formats and FS
+libcxr_fs_set_gameinfo = extern( "cxr_fs_set_gameinfo", [c_char_p], c_int32 )
+libcxr_fs_exit = extern( "cxr_fs_exit", [], None )
+libcxr_fs_get = extern( "cxr_fs_get", [c_char_p], c_char_p )
+libcxr_load_mdl = extern( "cxr_load_mdl", [c_char_p], POINTER(cxr_tri_mesh) )
+
 libcxr_funcs = [ libcxr_decompose, libcxr_free_world, libcxr_begin_vmf, \
                  libcxr_vmf_begin_entities, libcxr_push_world_vmf, \
                  libcxr_end_vmf, libcxr_vdf_open, libcxr_vdf_close, \
                  libcxr_vdf_put, libcxr_vdf_node, libcxr_vdf_edon, 
                  libcxr_vdf_kv, libcxr_lightpatch_bsp, libcxr_write_test_data,\
-                 libcxr_world_preview, libcxr_free_tri_mesh ]
+                 libcxr_world_preview, libcxr_free_tri_mesh, \
+                 libcxr_fs_set_gameinfo, libcxr_fs_exit, libcxr_fs_get, \
+                 libcxr_load_mdl ]
 
 # Callbacks
-
 def libcxr_log_callback(logStr):
    print( F"{logStr.decode('utf-8')}",end='' )
 
@@ -829,7 +945,7 @@ def cxr_decompose_globalerr( mesh_src ):
    err = c_int32(0)
    world = libcxr_decompose.call( mesh_src, pointer(err) )
 
-   if world == None:
+   if not world:
       cxr_view_mesh = None
       cxr_batch_lines()
       
@@ -1588,6 +1704,58 @@ class CXR_DEV_OPERATOR(bpy.types.Operator):
       libcxr_write_test_data.call( pointer(mesh_src) )
       return {'FINISHED'}
 
+class CXR_INIT_FS_OPERATOR(bpy.types.Operator):
+   bl_idname="convexer.fs_init"
+   bl_label="Initialize filesystem"
+
+   def execute(_,context):
+      gameinfo = F'{bpy.context.scene.cxr_data.subdir}/gameinfo.txt'
+      
+      if libcxr_fs_set_gameinfo.call( gameinfo.encode('utf-8') ) == 1:
+         print( "File system ready" )
+      else:
+         print( "File system failed to initialize" )
+
+      return {'FINISHED'}
+
+class CXR_LOAD_MODEL_OPERATOR(bpy.types.Operator):
+   bl_idname="convexer.model_load"
+   bl_label="Load model"
+
+   def execute(_,context):
+      global cxr_mdl_mesh, cxr_mdl_shader
+
+      mdlpath = bpy.context.scene.cxr_data.dev_mdl.encode('utf-8')
+      pmesh = libcxr_load_mdl.call( mdlpath )
+
+      if not pmesh:
+         print( "Failed to load model" )
+         return {'FINISHED'}
+      
+      mesh = pmesh[0]
+
+      #TODO: remove code dupe
+      vertices = mesh.vertices[:mesh.vertex_count]
+      vertices = [(_[0],_[1],_[2]) for _ in vertices]
+
+      normals = mesh.normals[:mesh.vertex_count]
+      normals = [(_[0],_[1],_[2]) for _ in normals]
+
+      indices = mesh.indices[:mesh.indices_count]
+      indices = [ (indices[i*3+0],indices[i*3+1],indices[i*3+2]) \
+                  for i in range(int(mesh.indices_count/3)) ]
+      
+      cxr_mdl_mesh = batch_for_shader(
+         cxr_mdl_shader, 'TRIS',
+         { "aPos": vertices, "aNormal": normals },
+         indices = indices,
+      )
+
+      libcxr_free_tri_mesh.call( pmesh )
+
+      scene_redraw()
+      return {'FINISHED'}
+
 # UI: Preview how the brushes will looks in 3D view
 #
 class CXR_PREVIEW_OPERATOR(bpy.types.Operator):
@@ -2153,6 +2321,9 @@ class CXR_VIEW3D( bpy.types.Panel ):
       row.scale_y = 2
       row.operator("convexer.reset")
 
+      layout.prop( bpy.context.scene.cxr_data, "dev_mdl" )
+      layout.operator( "convexer.model_load" )
+
 # Main scene properties interface, where all the settings go
 #
 class CXR_INTERFACE(bpy.types.Panel):
@@ -2166,6 +2337,7 @@ class CXR_INTERFACE(bpy.types.Panel):
       if CXR_GNU_LINUX==1:
          _.layout.operator("convexer.reload")
          _.layout.operator("convexer.dev_test")
+         _.layout.operator("convexer.fs_init")
 
       _.layout.operator("convexer.hash_reset")
       settings = context.scene.cxr_data
@@ -2560,13 +2732,16 @@ class CXR_SCENE_SETTINGS(bpy.types.PropertyGroup):
    comp_compile: bpy.props.BoolProperty(name="Compile",default=True)
    comp_pack: bpy.props.BoolProperty(name="Pack",default=False)
 
+   dev_mdl: bpy.props.StringProperty(name="Model",default="")
+
 classes = [ CXR_RELOAD, CXR_DEV_OPERATOR, CXR_INTERFACE, \
             CXR_MATERIAL_PANEL, CXR_IMAGE_SETTINGS,\
             CXR_MODEL_SETTINGS, CXR_ENTITY_SETTINGS, CXR_CUBEMAP_SETTINGS,\
             CXR_LIGHT_SETTINGS, CXR_SCENE_SETTINGS, CXR_DETECT_COMPILERS,\
             CXR_ENTITY_PANEL, CXR_LIGHT_PANEL, CXR_PREVIEW_OPERATOR,\
             CXR_VIEW3D, CXR_COMPILER_CHAIN, CXR_RESET_HASHES,\
-            CXR_COMPILE_MATERIAL, CXR_COLLECTION_PANEL, CXR_RESET ]
+            CXR_COMPILE_MATERIAL, CXR_COLLECTION_PANEL, CXR_RESET, \
+            CXR_INIT_FS_OPERATOR, CXR_LOAD_MODEL_OPERATOR ]
 
 vmt_param_dynamic_class = None
 
index 6cf104cdab8285be63d89bdbb367b719fc65daf5..c5a1a51fb66fc70af83e3d888876cf72ab7f07b5 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -65,7 +65,7 @@ fi
 mkdir -p nbvtf/obj
 compile $C -O1 -ggdb -fPIC -shared \
    -Wall -Wno-unused-variable -Wno-unused-function -std=c99 -pedantic \
-   -DCXR_SO -DCXR_DEBUG -DCXR_VALVE_MAP_FILE \
+   -DCXR_SO -DCXR_DEBUG -DCXR_VALVE_MAP_FILE -DCXR_VALVE_BIN \
    -xc cxr/cxr.h \
    -o libcxr$lib_ext \
    -lm
@@ -96,8 +96,9 @@ compile $C -O3 \
       -lm
 
 # This is for testing with asan on linux
-# compile gcc -ggdb -O1 -Wall \
-#              -Wno-unused-variable -Wno-unused-function $asan -Werror=vla \
-#              cxr/test.c \
-#              -o test$exe_ext \
-#              -lm 
+compile $C -ggdb -O1 -Wall \
+               -Wno-unused-variable -Wno-unused-function $asan -Werror=vla \
+      -DCXR_SO -DCXR_DEBUG -DCXR_VALVE_MAP_FILE \
+               cxr/test.c \
+               -o test$exe_ext \
+               -lm 
index e1c1679f405ff2fda3080bcc67ea304acc20b0b7..2a4267f0a152ffa3e0b34fd3eb4c5f4fd8a36db0 100644 (file)
--- a/cxr/cxr.h
+++ b/cxr/cxr.h
@@ -48,7 +48,6 @@ LICENSE: GPLv3.0, please see COPYING and LICENSE for more information
    IMPLEMENTATION
 */
 
-#define CXR_API
 #define CXR_EPSILON 0.001
 #define CXR_PLANE_SIMILARITY_MAX 0.998
 #define CXR_BIG_NUMBER 1e300
@@ -60,31 +59,17 @@ LICENSE: GPLv3.0, please see COPYING and LICENSE for more information
 
 #include <stdio.h>
 #include <math.h>
-#include <stdint.h>
-#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 
-typedef uint8_t        u8;
-typedef uint16_t       u16;
-typedef uint32_t       u32;
-typedef uint64_t       u64;
-typedef int8_t         i8;
-typedef int16_t        i16;
-typedef int32_t        i32;
-typedef int64_t        i64;
-
-typedef unsigned int uint;
-
-typedef double         v2f[2];
-typedef double         v3f[3];
-typedef double         v4f[4];
-typedef v3f                    m3x3f[3];
-typedef v3f                    m4x3f[4];
-typedef v3f                    boxf[2];
-
+#include "cxr_types.h"
 #include "cxr_math.h"
 #include "cxr_mem.h"
+#include "cxr_log.h"
+
+#ifdef CXR_VALVE_BIN
+ #include "cxr_valve_bin.h"
+#endif
 
 typedef struct cxr_world cxr_world;
 typedef struct cxr_solid cxr_solid;
@@ -232,8 +217,11 @@ struct cxr_mesh
 /* Simple mesh type mainly for debugging */
 struct cxr_tri_mesh
 {
-   v3f *vertices;
+   v3f *vertices,
+       *normals;
+   v2f *uvs;
    v4f *colours;
+
    i32 *indices,
         indices_count,
         vertex_count;
@@ -314,9 +302,6 @@ enum cxr_soliderr
 #endif
 #endif
 
-static void (*cxr_log_func)(const char *str);
-static void (*cxr_line_func)( v3f p0, v3f p1, v4f colour );
-
 static int cxr_range(int x, int bound)
 {
    if( x < 0 )
@@ -391,21 +376,6 @@ static void colour_random_brush(int n, v4f colour)
 
 #ifdef CXR_DEBUG
 
-static void cxr_log( const char *fmt, ... )
-{
-   char buf[512];
-
-   va_list args;
-   va_start( args, fmt );
-   vsnprintf( buf, sizeof(buf)-1, fmt, args );
-   va_end(args);
-
-   if( cxr_log_func )
-      cxr_log_func( buf );
-
-   fputs(buf,stdout);
-}
-
 static void cxr_debug_line( v3f p0, v3f p1, v4f colour )
 {
    if( cxr_line_func )
@@ -2218,6 +2188,8 @@ CXR_API cxr_tri_mesh *cxr_world_preview( cxr_world *world )
    out->colours = malloc( sizeof(v4f)*out->vertex_count );
    out->vertices = malloc( sizeof(v3f)*out->vertex_count );
    out->indices = malloc( sizeof(i32)*out->indices_count );
+   out->uvs = NULL;
+   out->normals = NULL;
 
    v3f *overts = out->vertices;
    v4f *colours = out->colours;
@@ -2299,6 +2271,8 @@ CXR_API void cxr_free_tri_mesh( cxr_tri_mesh *mesh )
    free( mesh->colours );
    free( mesh->indices );
    free( mesh->vertices );
+   free( mesh->normals );
+   free( mesh->uvs );
    free( mesh );
 }
 
@@ -3535,5 +3509,144 @@ CXR_API int cxr_lightpatch_bsp( const char *path )
 
    return 1;
 }
+
+#ifdef CXR_VALVE_BIN
+
+CXR_API cxr_tri_mesh *cxr_load_mdl( const char *mdlname )
+{
+       char path[1024];
+       strcpy( path, mdlname );
+       cxr_stripext( path );
+       strcat( path, ".dx90.vtx" );
+       VTXFileHeader_t *pVtxHdr = (VTXFileHeader_t *)cxr_fs_get( path );
+       
+       if( !pVtxHdr )
+               return NULL;
+       
+       /* .VVD */
+       strcpy( path, mdlname );
+       cxr_stripext( path );
+       strcat( path, ".vvd" );
+       vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)cxr_fs_get( path );
+       
+       if( !pVvdHdr )
+       {
+               free( pVtxHdr );
+               return 0;
+       }
+       
+       /* .MDL */
+       strcpy( path, mdlname );
+       cxr_stripext( path );
+       strcat( path, ".mdl" );
+       studiohdr_t *pMdl = (studiohdr_t *)cxr_fs_get( path );
+       
+       if( !pMdl )
+       {
+               free( pVtxHdr );
+               free( pVvdHdr );
+               return 0;
+       }
+       
+   cxr_tri_mesh *mesh = malloc( sizeof(cxr_tri_mesh) );
+   mesh->colours = NULL;
+   
+       mesh->indices_count = vtx_count_indices( pVtxHdr, pMdl );
+       mesh->indices = malloc( mesh->indices_count * sizeof( u32 ) );
+
+       mesh->vertex_count = pVvdHdr->numLodVertexes[0];
+       mesh->vertices = malloc( mesh->vertex_count * sizeof(v3f) );
+   mesh->uvs = malloc( mesh->vertex_count * sizeof(v2f) );
+   mesh->normals = malloc( mesh->vertex_count * sizeof(v3f) );
+       
+#if 0
+   mesh->bounds[0][0] = pMdl->hull_min[0];
+   mesh->bounds[0][1] = pMdl->hull_min[1];
+   mesh->bounds[0][2] = pMdl->hull_min[2];
+   mesh->bounds[0][0] = pMdl->hull_max[0];
+   mesh->bounds[0][1] = pMdl->hull_max[1];
+   mesh->bounds[0][2] = pMdl->hull_max[2];
+#endif
+       
+       mesh->indices_count = 0;
+       
+       for( int bodyID = 0; bodyID < pVtxHdr->numBodyParts; ++bodyID )
+       {
+               /* Body parts */
+               VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( pVtxHdr, bodyID );
+               mstudiobodyparts_t *pBodyPart = studiohdr_pBodypart( pMdl, bodyID );
+               
+               for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+               {       
+         /* models */
+                       VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
+                       mstudiomodel_t *pStudioModel = 
+            mstudiobodyparts_pModel( pBodyPart, modelID );
+
+                       int nLod = 0;
+                       VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
+
+                       for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+                       {
+            /* meshes */
+                               VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
+                               mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
+
+                               for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+                               {
+               /* groups */
+                                       VTXStripGroupHeader_t* pStripGroup = 
+                  pStripGroupVTX( pVtxMesh, nGroup );
+
+                                       for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+                                       {
+                  /* strips */
+                                               VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
+
+                                               if ( pStrip->flags & STRIP_IS_TRILIST )
+                                               {
+                     /* indices */
+                                                       for ( int i = 0; i < pStrip->numIndices; i ++ )
+                                                       {
+                                                               u16 i1 = *pIndexVTX( pStripGroup, 
+                              pStrip->indexOffset + i );                                                               
+
+                                                               mesh->indices[ mesh->indices_count ++ ] = 
+                           pVertexVTX( pStripGroup, i1 )->origMeshVertID + 
+                           pMesh->vertexoffset;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       mstudiovertex_t *vertexData = GetVertexData( pVvdHdr );
+       
+       for( int i = 0; i < mesh->vertex_count; i ++ )
+       {
+               mstudiovertex_t *vert = vertexData + i;
+
+      mesh->vertices[i][0] = vert->pos[0];
+      mesh->vertices[i][1] = vert->pos[1];
+      mesh->vertices[i][2] = vert->pos[2];
+
+      mesh->normals[i][0] = vert->norm[0];
+      mesh->normals[i][1] = vert->norm[1];
+      mesh->normals[i][2] = vert->norm[2];
+
+      mesh->uvs[i][0] = vert->uv[0];
+      mesh->uvs[i][1] = vert->uv[1];
+       }
+       
+       free( pVtxHdr );
+       free( pVvdHdr );
+       free( pMdl );
+       
+       return mesh;
+}
+#endif /* CXR_VALVE_BIN */
+
 #endif /* CXR_VALVE_MAP_FILE */
 #endif /* CXR_IMPLEMENTATION */
diff --git a/cxr/cxr_io.h b/cxr/cxr_io.h
new file mode 100644 (file)
index 0000000..ed28bb8
--- /dev/null
@@ -0,0 +1,215 @@
+#ifndef CXR_IO_H
+#define CXR_IO_H
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "cxr_types.h"
+
+/* Read binary or text assets in full from file */
+static void *cxr_asset_read_s( const char *path, i64 *size );
+static void *cxr_asset_read( const char *path );
+static char *cxr_textasset_read_s( const char *path, i64 *size );
+static char *cxr_textasset_read( const char *name );
+
+static i64 cxr_file_size( FILE *fileptr );
+
+/* Path utilities */
+/* Returns pointer to the extension in path */
+static char *cxr_findext( char *path, char const delim );
+static char *cxr_findsep( char *path );
+
+static void cxr_stripext( char *path );
+static int cxr_path_is_abs( char const *path );
+static char *cxr_filename( char *path );
+
+/* Remove one level (nop if can't) eg: /home/harry/test.file -> /home/harry/ */
+static void cxr_downlvl( char *path );
+
+#ifdef _WIN32
+ #define CXR_FOLDER_CHAR '\\'
+#else
+ #define CXR_FOLDER_CHAR '/'
+#endif
+
+static i64 cxr_file_size( FILE *fileptr )
+{
+       fseek( fileptr, 0, SEEK_END );
+       i64 fsize = ftell( fileptr );
+       fseek( fileptr, 0, SEEK_SET );
+       
+       return fsize;
+}
+
+static void *fs_disk_open_read( const char *path, int reserve_end, i64 *size )
+{
+       FILE *f = fopen( path, "rb" );
+       if( f )
+       {
+               i64 fsize = cxr_file_size( f );
+               void *buf = malloc( fsize + reserve_end );
+               
+               if( buf )
+               {
+                       if( fread( buf, 1, fsize, f ) != fsize )
+                       {
+                               free( buf );
+                               buf = NULL;
+                       }
+               }
+               
+               *size = fsize;
+               
+               fclose( f );
+               return buf;
+       }
+       else
+       {
+               return NULL;
+       }
+}
+
+static char *fs_disk_load_text( const char *path, i64 *size )
+{
+       char *buf;
+       i64 fsize;
+       
+       if( (buf = fs_disk_open_read( path, 1, &fsize )) )
+       {
+               buf[ fsize ] = 0x00;
+               *size = fsize +1;
+               
+               return buf;
+       }
+
+       return NULL;
+}
+
+static void *cxr_asset_read_s( const char *path, i64 *size )
+{
+       return fs_disk_open_read( path, 0, size );
+}
+
+static void *cxr_asset_read( const char *path )
+{
+       i64 size;
+       return fs_disk_open_read( path, 0, &size );
+}
+
+static char *cxr_textasset_read_s( const char *path, i64 *size )
+{
+       return fs_disk_load_text( path, size );
+}
+
+static char *cxr_textasset_read( const char *name )
+{
+       i64 size;
+       return fs_disk_load_text( name, &size );
+}
+
+static char *cxr_findext( char *path, char const delim )
+{
+       char *c, *ptr;
+
+       c = path;
+       ptr = NULL;
+       
+       while( *c )
+       {
+               if( *c == delim )
+               {
+                       ptr = c + 1;
+               }
+       
+               c ++;
+       }
+
+       return ptr;
+}
+
+static char *cxr_findsep( char *path )
+{
+       char *c, *ptr;
+
+       c = path;
+       ptr = NULL;
+       
+       while( *c )
+       {
+               if( *c == '/' || *c == '\\' )
+               {
+                       ptr = c + 1;
+               }
+       
+               c ++;
+       }
+
+       return ptr;
+}
+
+static void cxr_stripext( char *path )
+{
+       char *point, *start;
+       
+       // Skip folders
+       if( !(start = cxr_findsep( path )) )
+       {
+               start = path;
+       }
+       
+       if( (point = cxr_findext( start, '.' )) )
+       {
+               if( point > path )
+               {
+                       *(point-1) = 0x00;
+               }
+       }
+}
+
+static void cxr_downlvl( char *path )
+{
+       char *start_name, *c;
+
+       c = path;
+       while( *c )
+               c ++;
+       int len = c - path;
+       
+       if( len )
+               path[ len -1 ] = 0x00;
+       
+       if( (start_name = cxr_findsep( path ) ))
+               *start_name = 0x00;
+       else
+               path[0] = 0x00;
+}
+
+static char *cxr_filename( char *path )
+{
+       char *base_name;
+       if( (base_name = cxr_findsep( path ) ))
+               return base_name;
+               
+       return path;
+}
+
+static int cxr_path_is_abs( char const *path )
+{
+#ifdef _WIN32
+       if( strlen( path ) < 2 ) return 0;
+       return path[1] == ':';
+#else
+       if( strlen( path ) < 1 ) return 0;
+       return path[0] == '/';
+#endif
+}
+
+static char *cxr_str_clone( char const *str, int extra )
+{
+   char *newstr = malloc(strlen(str)+1+extra);
+   strcpy( newstr, str );
+
+   return newstr;
+}
+
+#endif /* CXR_IO_H */
diff --git a/cxr/cxr_log.h b/cxr/cxr_log.h
new file mode 100644 (file)
index 0000000..48ebadf
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef CXR_LOG_H
+#define CXR_LOG_H
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "cxr_types.h"
+
+static void (*cxr_log_func)(const char *str);
+static void (*cxr_line_func)( v3f p0, v3f p1, v4f colour );
+
+static void cxr_log( const char *fmt, ... )
+{
+   char buf[512];
+
+   va_list args;
+   va_start( args, fmt );
+   vsnprintf( buf, sizeof(buf)-1, fmt, args );
+   va_end(args);
+
+   if( cxr_log_func )
+      cxr_log_func( buf );
+
+   fputs(buf,stdout);
+}
+
+
+#endif /* CXR_LOG_H */
index 68f32ffe035ce6c79837ab4b764bf810b299874f..dcaaab54e401703e9a9df017769f8e27e0e31943 100644 (file)
@@ -7,6 +7,11 @@
  *   Other useful geometric functions
  */
 
+#ifndef CXR_MATH_H
+#define CXR_MATH_H
+
+#include "cxr_types.h"
+
 #define CXR_INLINE static inline
 #define CXR_PIf 3.14159265358979323846264338327950288f
 
@@ -617,3 +622,5 @@ CXR_INLINE double segment_segment_dist( v3f a0, v3f a1, v3f b0, v3f b1,
 
    return v3_dist( a, b );
 }
+
+#endif /* CXR_MATH_H */
index 437831489ce2c741a810b00401730467abf9b38d..38e7a3f1bd7a1d4cff8d88f14da6e9dbc226614c 100644 (file)
@@ -1,3 +1,12 @@
+#ifndef CXR_MEM_H
+#define CXR_MEM_H
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "cxr_types.h"
+#include "cxr_math.h"
+
 typedef struct cxr_abuffer cxr_abuffer;
 
 struct cxr_abuffer
@@ -28,7 +37,8 @@ static void *__cxr_ab_ptr( struct cxr_abuffer *buffer, u32 index
 #ifdef CXR_DEBUG_ALLOCATORS
    if( index >= buffer->capacity || index < 0 )
    {
-      printf( "index out of capactity (%d /: [0->%d (cap)]) (%s)\n", index, buffer->capacity, debug_str );
+      printf( "index out of capactity (%d /: [0->%d (cap)]) (%s)\n", index, 
+            buffer->capacity, debug_str );
       exit(1);
    }
 #endif
@@ -84,10 +94,13 @@ static void cxr_ab_push( struct cxr_abuffer *buffer, void *em )
 static void cxr_ab_init( struct cxr_abuffer *buffer, u32 esize, u32 cap )
 {
    buffer->esize = esize;
-   buffer->capacity = cxr_max(1,cap);
+   buffer->capacity = cap;
    buffer->count = 0;
 
-   buffer->arr = malloc( buffer->esize*buffer->capacity );
+   if( cap )
+      buffer->arr = malloc( buffer->esize*buffer->capacity );
+   else
+      buffer->arr = NULL;
 }
 
 static void cxr_ab_clear( struct cxr_abuffer *buffer )
@@ -99,3 +112,5 @@ static void cxr_ab_free( struct cxr_abuffer *buffer )
 {
    free( buffer->arr );
 }
+
+#endif /* CXR_TYPES_H */
diff --git a/cxr/cxr_types.h b/cxr/cxr_types.h
new file mode 100644 (file)
index 0000000..98229dd
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef CXR_TYPES_H
+#define CXR_TYPES_H
+
+#include <stdint.h>
+
+#define CXR_API
+
+typedef uint8_t        u8;
+typedef uint16_t       u16;
+typedef uint32_t       u32;
+typedef uint64_t       u64;
+typedef int8_t         i8;
+typedef int16_t        i16;
+typedef int32_t        i32;
+typedef int64_t        i64;
+
+typedef unsigned int uint;
+
+typedef double         v2f[2];
+typedef double         v3f[3];
+typedef double         v4f[4];
+typedef v3f                    m3x3f[3];
+typedef v3f                    m4x3f[4];
+typedef v3f                    boxf[2];
+
+#endif
diff --git a/cxr/cxr_valve_bin.h b/cxr/cxr_valve_bin.h
new file mode 100644 (file)
index 0000000..da6c84d
--- /dev/null
@@ -0,0 +1,1324 @@
+/*
+ * The following is used to read the existing content from CS:GO compiled assets
+ *
+ * Supported formats:
+ *   vdf
+ *   vpk
+ *   mdl
+ */
+
+#ifndef CXR_VALVE_BIN_H
+#define CXR_VALVE_BIN_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "cxr_types.h"
+#include "cxr_math.h"
+#include "cxr_io.h"
+#include "cxr_mem.h"
+#include "cxr_log.h"
+
+typedef struct VPKHeader VPKHeader;
+typedef struct VPKDirectoryEntry VPKDirectoryEntry;
+typedef struct vdf_kv vdf_kv;
+typedef struct vdf_node vdf_node;
+typedef struct vdf_ctx vdf_ctx;
+typedef struct valve_file_system valve_file_system;
+
+/*
+ * File system
+ */
+
+static struct valve_file_system
+{
+   char *gamedir,
+        *exedir;
+
+   /* Runtime */
+   VPKHeader *vpk;
+
+   cxr_abuffer searchpaths;
+
+   FILE *current_archive;
+   u16   current_idx;
+
+   int  initialized;
+}
+fs_global = { .initialized = 0 };
+
+CXR_API int cxr_fs_set_gameinfo( const char *path ); /* Setup system */
+CXR_API void cxr_fs_exit(void);                       /* Clean up */
+CXR_API char *cxr_fs_get( const char *path );         /* Get a file */
+
+/* 
+ * VPK reading
+ */
+
+static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset );
+static void vpk_free( VPKHeader *self );
+
+/*
+ * VDF reading
+ */
+
+static vdf_node *vdf_open_file( const char *fn );
+static void vdf_free_r( vdf_node *p );
+
+/* Starting from *it, get next child with matching name from node. */
+static vdf_node *vdf_next( vdf_node *node, const char *name, int *it );
+
+/* Create new empty node attached to parent. name can be NULL */
+static vdf_node *vdf_create_node( vdf_node *parent, const char *name );
+
+/* KV access */
+static const char *kv_get( vdf_node *node, const char *key, 
+      const char *value_defalt );
+
+/* Iterate each keyvalue starting from *it until key is matched */
+static char *kv_next( vdf_node *node, const char *key, int *it );
+
+static int kv_get_int( vdf_node *node, const char *key, const int fallback );
+static float kv_get_float( vdf_node *node, const char *key, float fallback );
+
+static void kv_int_array( vdf_node *node, const char *key, u32 count, 
+      int *arr );
+static void kv_float_array( vdf_node *node, const char *key, u32 count, 
+      float *arr );
+static void kv_double_array( vdf_node *node, const char *key, u32 count, 
+      double *arr );
+
+#define vdf_foreach( NODE, STR, AS ) \
+int __vdf_it_##AS = 0; \
+vdf_node * AS;\
+while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
+
+#define kv_foreach( NODE, STR, AS ) \
+int __kv_it_##AS = 0; \
+const char * AS;\
+while( (AS = kv_next( NODE, STR, &__kv_it_##AS )) )
+#pragma pack(push, 1)
+
+struct VPKHeader
+{
+       u32 Signature;
+       u32 Version;
+       u32 TreeSize;
+       u32 FileDataSectionSize;
+       u32 ArchiveMD5SectionSize;
+       u32 OtherMD5SectionSize;
+       u32 SignatureSectionSize;
+};
+
+struct VPKDirectoryEntry
+{
+       u32 CRC;
+       u16 PreloadBytes; 
+       u16 ArchiveIndex;
+       u32 EntryOffset;
+       u32 EntryLength;
+       u16 Terminator;
+};
+#pragma pack(pop)
+
+static void vpk_free( VPKHeader *self )
+{
+       free( self );
+}
+
+static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset )
+{
+       if( !self ) 
+               return NULL;
+       
+       char wbuf[ 512 ];
+       strcpy( wbuf, asset );
+          
+   /* 
+    * This currently fails if the filename doesn't have a file extension, or
+    * if it is located at the root path. I'm not sure if this is defined
+    * behaviour or not. TODO: allow it to work anyway
+    */
+
+       char *ext = cxr_findext( wbuf, '.' );
+   
+   if( !ext ) return NULL;
+       *(ext-1) = 0x00;
+
+       char *fn = cxr_findext( wbuf, '/' );
+
+   if( !fn ) return NULL;
+       *(fn-1) = 0x00;
+
+       char *dir = wbuf;
+       char *pCur = ((char *)self) + sizeof( VPKHeader );
+       
+       while( 1 )
+       {
+               if( !*pCur ) break;
+
+               int bExt = !strcmp( ext, pCur );                
+               
+               while( *( pCur ++ ) ) {};
+               while( 1 )
+               {
+                       if( !*pCur ) { pCur ++; break; }
+
+                       int bDir = !strcmp( dir, pCur );
+
+                       while( *( pCur ++ ) ) {};
+                       while( 1 )
+                       {
+                               if( !*pCur ) { pCur ++; break; }
+                       
+                               const char *vpk_fn = pCur;
+                       
+                               while( *( pCur ++ ) ) {};
+                               VPKDirectoryEntry *entry = (VPKDirectoryEntry *)pCur;
+                               
+                               if( !strcmp( vpk_fn, fn ) && bExt && bDir ) 
+                               {
+                                       return entry;
+                               }
+                               
+                               pCur += entry->PreloadBytes + sizeof( VPKDirectoryEntry );
+                       }
+                       
+                       if( bDir && bExt ) return NULL;
+               }
+               
+               if( bExt ) return NULL;
+       }
+       
+       return NULL;
+}
+
+/*
+ * VDF 
+ */
+
+struct vdf_kv
+{
+       char *key;
+       char *value;
+};
+
+struct vdf_node
+{
+       char *name;
+       
+       vdf_node *parent;
+       
+   cxr_abuffer abnodes,
+               abpairs;
+       
+       u32             user;
+       u32             user1;
+};
+
+static vdf_node *vdf_next( vdf_node *node, const char *name, int *it )
+{      
+       if( !node ) 
+               return NULL;
+
+       for( int i = it? *it: 0; i < node->abnodes.count; i ++ )
+       {
+      vdf_node **ptr_child = cxr_ab_ptr( &node->abnodes, i ),
+                *child = *ptr_child;
+
+               if( !name || !strcmp( name, child->name ))
+               {
+                       if( it ) *it = i+1;
+                       return child;
+               }
+       }
+       
+       return NULL;
+}
+
+static char *kv_next( vdf_node *node, const char *key, int *it )
+{
+       char *val;
+
+       if( node )
+       {
+               while( *it < node->abpairs.count )
+               {
+         vdf_kv *kv = cxr_ab_ptr( &node->abpairs, *it );
+
+                       if( !strcmp( kv->key, key ) )
+                       {
+                               val = kv->value;
+                               *it = *it + 1;
+                               return val;
+                       }
+                       
+                       *it = *it + 1;
+               }
+       }
+       
+       return NULL;
+}
+
+static const char *kv_get( vdf_node *node, const char *key, 
+      const char *value_defalt )
+{
+   int it = 0;
+   char *val = kv_next( node, key, &it );
+
+   if( val )
+      return val;
+
+   return value_defalt;
+}
+
+static void vdf_str_to_int( const char *src, void *dest )
+{
+       *((int *)dest) = atoi( src );
+}
+
+static void vdf_str_to_float( const char *src, void *dest )
+{
+       *((float *)dest) = atof( src );
+}
+
+static void vdf_str_to_double( const char *src, void *dest )
+{
+       *((double *)dest) = atof( src );
+}
+
+static void kv_parse_array( const char *source, u32 esize, u32 count, 
+      void(*interp_func)(const char *src, void *dest), void *arr )
+{
+       if( !source )
+               return;
+
+       char value_buf[ 64 ];
+       int token = 0;
+       
+       u32 i = 0;
+       u32 k = 0;
+       
+       char const *c = source;
+       
+       while( *c )
+       {
+               if( *c==' ' || *c=='\t' || *c=='[' || *c==']' || *c=='(' || *c==')' )
+               {
+                       if( token )
+                       {
+                               value_buf[ k ] = 0x00;
+                               token = 0;
+                               
+                               interp_func( value_buf, ((u8 *)arr) + i*esize );
+                               i ++;
+                               
+                               if( i >= count )
+                               {
+                                       break;
+                               }
+                       }
+               }
+               else
+               {
+                       if( !token )
+                       {
+                               token = 1;
+                               k = 0;
+                       }
+                       
+                       if( token )
+                       {
+                               if( k < sizeof( value_buf ) - 1 )
+                               {
+                                       value_buf[ k ++ ] = *c;
+                               }
+                       }
+               }
+       
+               c ++;
+       }
+       
+       /* Add remaining case if we hit null */
+       if( token && (i < count) )
+       {
+               value_buf[ k ] = 0x00;
+               interp_func( value_buf, ((u8 *)arr) + i*esize );
+       }
+}
+
+static void kv_int_array( vdf_node *node, const char *key, u32 count, int *arr )
+{
+       kv_parse_array( kv_get( node, key, NULL ), sizeof(int), count, 
+         vdf_str_to_int, arr );
+}
+
+static void kv_float_array( vdf_node *node, const char *key, u32 count, 
+      float *arr )
+{
+       kv_parse_array( kv_get( node, key, NULL ), sizeof(float), count, 
+         vdf_str_to_float, arr );
+}
+
+static void kv_double_array( vdf_node *node, const char *key, u32 count, 
+      double *arr )
+{
+       kv_parse_array( kv_get( node, key, NULL ), sizeof(double), count, 
+         vdf_str_to_double, arr );
+}
+
+static int kv_get_int( vdf_node *node, const char *key, 
+      const int default_value )
+{
+       const char *v = kv_get( node, key, NULL );
+       return v? atoi(v): default_value;
+}
+
+static float kv_get_float( vdf_node *node, const char *key, 
+      float default_value )
+{
+       const char *v = kv_get( node, key, NULL );
+       return v? atof( v ): default_value;
+}
+
+static vdf_node *vdf_create_node( vdf_node *parent, const char *name )
+{
+       vdf_node *node = calloc( 1, sizeof( vdf_node ) );
+       
+   /* init buffers */
+   cxr_ab_init( &node->abnodes, sizeof( vdf_node* ), 0 );
+   cxr_ab_init( &node->abpairs, sizeof( vdf_kv ), 0 );
+
+       if( name )
+       {
+      node->name = cxr_str_clone(name, 0);
+       }
+       
+       if( parent )
+       {
+               node->parent = parent;
+               
+      vdf_node **child = cxr_ab_empty( &parent->abnodes );
+               *child = node;
+       }
+       
+       return node;
+}
+
+static void vdf_kv_append( vdf_node *p, const char *k, const char *v )
+{
+   vdf_kv *kv = cxr_ab_empty( &p->abpairs );
+
+       u32 sv = strlen(v)+1;
+       u32 sk = strlen(k)+1;
+       
+       kv->key = malloc( sv+sk );
+       kv->value = kv->key+sk;
+       
+       memcpy( kv->key, k, sk );
+       memcpy( kv->value, v, sv );
+}
+
+static void vdf_free_r( vdf_node *p )
+{
+       for( int i = 0; i < p->abpairs.count; i ++ )
+       {
+      vdf_kv *kv = cxr_ab_ptr( &p->abpairs, i );
+               free( kv->key );  /* key and value are allocated in the same buffer */
+       }
+
+       for( int i = 0; i < p->abnodes.count; i ++ )
+       {
+      vdf_node **ptr_node = cxr_ab_ptr( &p->abnodes, i );
+               vdf_free_r( *ptr_node );
+       }
+       
+   cxr_ab_free( &p->abpairs );
+   cxr_ab_free( &p->abnodes );
+
+       free( p->name );
+       free( p );
+}
+
+/* 
+ * Parsing 
+ */
+
+struct vdf_ctx
+{
+       char name[1024];
+       
+       vdf_node *root;
+       
+       u32 lines;
+       u32 errors;
+       
+       struct 
+       {
+               int wait;
+               int expect_decl;
+
+               char *tokens[2];
+               int i;
+               
+               char *ptr_read;
+               
+               vdf_node *pnode;
+       } 
+       st;
+};
+
+static void vdf_newln( vdf_ctx *ctx )
+{
+       ctx->lines ++;
+
+       ctx->st.tokens[0] = NULL;
+       ctx->st.tokens[1] = NULL;
+       ctx->st.i = 0;
+}
+
+static void vdf_endl( vdf_ctx *ctx )
+{
+       if( ctx->st.tokens[0] )
+       {
+               /* Keypair */
+               if( ctx->st.tokens[1] )
+               {
+                       vdf_kv_append( ctx->st.pnode, ctx->st.tokens[0], ctx->st.tokens[1] );
+               }
+               else
+               {
+         /* decl */
+                       strcpy( ctx->name, ctx->st.tokens[0] );
+                       ctx->st.expect_decl = 1;
+               }
+       }
+       
+       vdf_newln( ctx );
+}
+
+static int vdf_line_control( vdf_ctx *ctx )
+{
+       if( *ctx->st.ptr_read == '\r' )
+       {
+               *ctx->st.ptr_read = 0x00;
+               return 1;
+       }
+       if( *ctx->st.ptr_read == '\n' )
+       {
+               *ctx->st.ptr_read = 0x00;
+               vdf_endl( ctx );
+               return 2;
+       }
+       
+       return 0;
+}
+
+static void vdf_wait_endl( vdf_ctx *ctx )
+{
+       while( (*ctx->st.ptr_read) && (*ctx->st.ptr_read != '\n') )
+       {
+               if( vdf_line_control( ctx ) == 2 )
+               {
+                       return;
+               }
+       
+               ctx->st.ptr_read ++;
+       }
+}
+
+static void vdf_parse_string( vdf_ctx *ctx )
+{
+       while( *ctx->st.ptr_read )
+       {
+               if( *ctx->st.ptr_read == '"' )
+               {
+                       *ctx->st.ptr_read = 0x00;
+                       return;
+               }
+               
+               if( vdf_line_control( ctx ) )
+               {
+         /* Unexpected end of line */
+                       return;
+               }
+       
+               ctx->st.ptr_read ++;
+       }
+}
+
+static int vdf_parse_structure( vdf_ctx *ctx )
+{
+       if( *ctx->st.ptr_read == '{' )
+       {
+               if( ctx->st.tokens[0] || !ctx->st.expect_decl )
+               {
+         /* Unexpected token '{' */
+                       ctx->errors ++;
+               }
+               
+               ctx->st.expect_decl = 0;
+               ctx->st.pnode = vdf_create_node( ctx->st.pnode, ctx->name );
+               
+               vdf_wait_endl( ctx );
+               return 1;
+       }
+       
+       if( *ctx->st.ptr_read == '}' )
+       {
+               if( !ctx->st.pnode->parent )
+               {
+         /* Unexpected token '}' */
+                       ctx->errors ++;
+               }
+               else
+               {
+                       ctx->st.pnode = ctx->st.pnode->parent;
+               }
+               
+               vdf_wait_endl( ctx );
+               return 1;
+       }
+       
+       return 0;
+}
+
+static void vdf_parse_begin_token( vdf_ctx *ctx, char *ptr )
+{
+       ctx->st.tokens[ ctx->st.i ] = ptr;
+
+       if( ctx->st.expect_decl )
+       {
+      /* Unexpected token 'name' */
+               ctx->errors ++;
+       }
+}
+
+static void vdf_parse_feedbuffer( vdf_ctx *ctx, char *buf )
+{
+       ctx->st.ptr_read = buf;
+       
+       while( *ctx->st.ptr_read )
+       {
+               if( !vdf_line_control( ctx ) )
+               {
+                       if( (*ctx->st.ptr_read == '/') && (ctx->st.ptr_read[1] == '/') )
+                       {
+                               *ctx->st.ptr_read = 0x00;
+                               ctx->st.ptr_read += 2;
+                               
+                               vdf_endl( ctx );
+                               vdf_wait_endl( ctx );
+                       }
+                       else
+                       {
+                               if( !vdf_parse_structure( ctx ) )
+                               {
+                                       if( *ctx->st.ptr_read == ' ' || *ctx->st.ptr_read == '\t' )
+                                       {
+                                               *ctx->st.ptr_read = 0x00;
+
+                                               if( ctx->st.tokens[ ctx->st.i ] )
+                                               {
+                                                       ctx->st.i ++;
+                                                       
+                                                       if( ctx->st.i == 2 ) 
+                                                       {
+                                                               vdf_wait_endl( ctx );
+                                                       }
+                                               }
+                                       }
+
+               /* New entry */
+                                       else if( !ctx->st.tokens[ ctx->st.i ] )
+                                       {
+                                               if( *ctx->st.ptr_read == '"' )
+                                               {
+                                                       *ctx->st.ptr_read = 0x00;
+                                                       ctx->st.ptr_read ++;                                                    
+                                                       
+                                                       vdf_parse_begin_token( ctx, ctx->st.ptr_read );
+                                                       vdf_parse_string( ctx );
+                                               }
+                                               else
+                                               {
+                                                       if( !( *ctx->st.ptr_read == '/' && 
+                              *(ctx->st.ptr_read + 1) == *ctx->st.ptr_read ) )
+                                                       {       
+                                                               vdf_parse_begin_token( ctx, ctx->st.ptr_read );
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               
+               ctx->st.ptr_read ++;
+       }
+}
+
+static int vdf_load_into( const char *fn, vdf_node *node )
+{
+       char *text_src = cxr_textasset_read( fn );
+       
+       if( !text_src )
+       {
+               return 0;
+       }
+       
+       vdf_ctx ctx = {0};
+       ctx.root = ctx.st.pnode = node;
+       
+       vdf_newln( &ctx );
+       vdf_parse_feedbuffer( &ctx, text_src );
+       free( text_src );
+       
+       return 1;
+}
+
+static vdf_node *vdf_open_file( const char *fn )
+{      
+       vdf_node *root = vdf_create_node( NULL, NULL );
+       if( vdf_load_into( fn, root ) )
+       {
+               return root;
+       }
+       else
+       {
+               vdf_free_r( root );
+               return NULL;
+       }
+}
+
+/*
+ * File system
+ */
+
+CXR_API i32 cxr_fs_set_gameinfo( const char *path )
+{
+   valve_file_system *fs = &fs_global;
+
+   if( fs->initialized )
+      cxr_fs_exit();
+
+       vdf_node *info = vdf_open_file( path );
+       if( !info ) 
+               return 0;
+       
+   fs->gamedir = cxr_str_clone( path, 0 );
+       cxr_downlvl( fs->gamedir );
+       
+   fs->exedir = cxr_str_clone( fs->gamedir, 0 );
+       cxr_downlvl( fs->exedir );
+
+   cxr_log( "Setting up file system:\n"
+            "gameinfo: %s\n"
+            "gamedir: %s\n"
+            "exedir: %s\n",
+
+            path, fs->gamedir, fs->exedir );
+       
+   /* get search paths */
+       vdf_node *search_paths = 
+      vdf_next
+      (
+         vdf_next
+         (
+            vdf_next( info, "GameInfo", NULL ), 
+            "FileSystem", 
+            NULL 
+         ), 
+         "SearchPaths", 
+         NULL 
+      );
+       
+   cxr_ab_init( &fs->searchpaths, sizeof( char *), 0 );
+
+       kv_foreach( search_paths, "Game", kv_game )
+       {
+      cxr_log( "Game %s\n", kv_game );
+
+               if( kv_game[0] == '|' ) continue;
+       
+               char *buf;
+               if( cxr_path_is_abs( kv_game ) )
+               {
+         buf = cxr_str_clone( kv_game, 1 );
+                       strcat( buf, "/" );
+               }
+               else
+               {
+         buf = cxr_str_clone( fs->exedir, strlen(kv_game)+1 );
+                       strcat( buf, kv_game );
+                       strcat( buf, "/" );
+               }
+               
+      char **sp = cxr_ab_empty( &fs->searchpaths );
+      *sp = buf;
+       }
+       
+       vdf_free_r( info );
+       
+   /* Find pack diretory */
+       char pack_path[512];
+       for( int i = 0; i < fs->searchpaths.count; i ++ )
+       {
+      char **sp = cxr_ab_ptr( &fs->searchpaths, i );
+
+               strcpy( pack_path, *sp );
+               strcat( pack_path, "pak01_dir.vpk" );
+       
+               if( (fs->vpk = (VPKHeader *)cxr_asset_read( pack_path )) )
+                       break;
+       }
+       
+       if( !fs->vpk )
+       {
+               cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
+               "Stock models will not load!\n", fs->searchpaths.count );
+       }
+   
+   fs->initialized = 1;
+   return 1;
+}
+
+CXR_API void cxr_fs_exit(void)
+{
+       valve_file_system *fs = &fs_global;
+
+       for( int i = 0; i < fs->searchpaths.count; i ++ )
+       {
+      char **sp = cxr_ab_ptr( &fs->searchpaths, i );
+               free( *sp );
+       }
+   
+   cxr_ab_free( &fs->searchpaths );
+
+       if( fs->vpk )
+       {
+               vpk_free( fs->vpk );
+               fs->vpk = NULL;
+       }
+       
+       if( fs->current_archive )
+       {
+               fclose( fs->current_archive );
+               fs->current_archive = NULL;
+       }
+
+   free( fs->gamedir );
+   free( fs->exedir );
+   
+   memset( fs, 0, sizeof( valve_file_system ) );
+}
+
+CXR_API char *cxr_fs_get( const char *path )
+{
+       valve_file_system *fs = &fs_global;
+
+   if( !fs->initialized )
+      return NULL;
+       
+       VPKDirectoryEntry *entry;
+       char pak[ 533 ];
+
+       if( fs->vpk )
+       {
+               if( (entry = vpk_find( fs->vpk, path )) )
+               {
+                       if( entry->ArchiveIndex != fs->current_idx )
+                       {
+                               if( fs->current_archive )
+                               {
+                                       fclose( fs->current_archive );
+                                       fs->current_archive = NULL;
+                               }
+                               
+                               fs->current_idx = entry->ArchiveIndex;
+                       }
+                       
+                       if( !fs->current_archive )
+                       {
+                               sprintf( pak, "%scsgo/pak01_%03hu.vpk", fs->exedir, 
+                  fs->current_idx );
+                               fs->current_archive = fopen( pak, "rb" );
+                               
+                               if( !fs->current_archive )
+                               {
+                                       cxr_log( "Could not locate %s\n", pak );
+                                       return NULL;
+                               }
+                       }
+                       
+                       char *filebuf = malloc( entry->EntryLength );
+                       
+                       fseek( fs->current_archive, entry->EntryOffset, SEEK_SET );
+                       if( fread( filebuf, 1, entry->EntryLength, fs->current_archive ) 
+               == entry->EntryLength )
+                       {
+                               return filebuf;
+                       }
+                       else
+                       {
+                               free( filebuf );
+                               return NULL;
+                       }
+               }
+       }
+       
+   /* Use physical search paths */
+       char path_buf[ 512 ];
+       
+       for( int i = 0; i < fs->searchpaths.count; i ++ )
+       {
+      char **sp = cxr_ab_ptr( &fs->searchpaths, i );
+
+               strcpy( path_buf, *sp );
+               strcat( path_buf, path );
+               
+               char *filebuf;
+               if( (filebuf = cxr_asset_read( path_buf )) )
+                       return filebuf;
+       }
+       
+   /* File not found */
+       return NULL;
+}
+
+/*
+ * VMDL interface
+ *
+ * This software is not affiliated with Valve Corporation
+ *   We are not affiliated, associated, authorized, endorsed by, or in any way 
+ *   officially connected with Valve Corporation, or any of its subsidiaries or 
+ *   its affiliates. 
+ *
+ *   All trademarks are property of their respective owners
+ */
+
+#define MAX_NUM_LODS 8
+#define MAX_NUM_BONES_PER_VERT 3
+
+#pragma pack(push, 1)
+typedef struct
+{
+       float   weight[MAX_NUM_BONES_PER_VERT];
+       char    bone[MAX_NUM_BONES_PER_VERT];
+       char    numbones;
+} 
+boneWeight_t;
+
+typedef struct
+{
+       boneWeight_t boneweights;
+       float           pos[3];
+       float           norm[3];        
+       float           uv[2];
+} 
+mstudiovertex_t;
+
+typedef struct
+{
+       int id;
+       int version;
+       int checksum;
+       int numLods;
+       int numLodVertexes[MAX_NUM_LODS];
+       int numFixups;
+       int fixupTableStart;
+       int vertexDataStart;
+       int tangentDataStart;
+} 
+vertexFileHeader_t;
+
+#pragma pack(pop)
+
+static mstudiovertex_t *GetVertexData( vertexFileHeader_t *t ) 
+{      
+       return (mstudiovertex_t *) ( (char *)t + t->vertexDataStart );
+}
+
+/*
+ * VTX file format
+ */
+
+#pragma pack(push, 1)
+
+typedef struct
+{
+       unsigned char boneWeightIndex[3];
+       unsigned char numBones;
+
+       unsigned short origMeshVertID;
+       char boneID[3];
+} 
+VTXVertex_t;
+
+enum StripGroupFlags
+{
+       STRIPGROUP_IS_FLEXED = 0x01,
+       STRIPGROUP_IS_HWSKINNED = 0x02,
+       STRIPGROUP_IS_DELTA_FLEXED = 0x04,
+       STRIPGROUP_SUPPRESS_HW_MORPH = 0x08
+};
+
+enum StripHeaderFlags_t {
+       STRIP_IS_TRILIST        = 0x01,
+       STRIP_IS_TRISTRIP       = 0x02      /* Unused by studiomdl 2015? */
+};
+
+typedef struct 
+{
+       int numIndices;
+       int indexOffset;
+
+       int numVerts;
+       int vertOffset;
+
+       short numBones;
+
+       unsigned char flags;
+
+       int numBoneStateChanges;
+       int boneStateChangeOffset;
+} 
+VTXStripHeader_t;
+
+typedef struct
+{
+       int numVerts;
+       int vertOffset;
+
+       int numIndices;
+       int indexOffset;
+
+       int numStrips;
+       int stripOffset;
+
+       unsigned char flags;
+} 
+VTXStripGroupHeader_t;
+
+static VTXVertex_t *pVertexVTX( VTXStripGroupHeader_t *t, int i ) 
+{ 
+       return (VTXVertex_t *)(((char *)t) + t->vertOffset) + i;
+}
+
+static unsigned short *pIndexVTX( VTXStripGroupHeader_t *t, int i ) 
+{ 
+       return (unsigned short *)(((char *)t) + t->indexOffset) + i;
+}
+
+static VTXStripHeader_t *pStripVTX( VTXStripGroupHeader_t *t, int i ) 
+{ 
+       return (VTXStripHeader_t *)(((char *)t) + t->stripOffset) + i; 
+}
+
+typedef struct
+{
+       int numStripGroups;
+       int stripGroupHeaderOffset;
+
+       unsigned char flags;
+} 
+VTXMeshHeader_t;
+static VTXStripGroupHeader_t *pStripGroupVTX( VTXMeshHeader_t *t, int i ) 
+{ 
+       return (VTXStripGroupHeader_t *)(((char *)t) + t->stripGroupHeaderOffset) +i; 
+}
+
+typedef struct
+{
+       int numMeshes;
+       int meshOffset;
+
+       float switchPoint;
+} 
+VTXModelLODHeader_t;
+static VTXMeshHeader_t *pMeshVTX( VTXModelLODHeader_t *t, int i ) 
+{ 
+       return (VTXMeshHeader_t *)(((char *)t) + t->meshOffset) + i; 
+}
+
+typedef struct
+{
+       int numLODs;
+       int lodOffset;
+} 
+VTXModelHeader_t;
+static VTXModelLODHeader_t *pLODVTX( VTXModelHeader_t *t, int i ) 
+{ 
+       return (VTXModelLODHeader_t *)(((char *)t) + t->lodOffset) + i; 
+}
+
+typedef struct
+{
+       int numModels;
+       int modelOffset;
+} 
+VTXBodyPartHeader_t;
+static VTXModelHeader_t *pModelVTX( VTXBodyPartHeader_t *t, int i ) 
+{ 
+       return (VTXModelHeader_t *)(((char *)t) + t->modelOffset) + i;
+}
+
+typedef struct
+{
+       int version; /* 7 */
+
+       /* hardware params that affect how the model is to be optimized. */
+       int vertCacheSize;
+       unsigned short maxBonesPerStrip;
+       unsigned short maxBonesPerTri;
+       int maxBonesPerVert;
+
+       int checkSum;
+       int numLODs;
+       int materialReplacementListOffset;
+       int numBodyParts;
+       int bodyPartOffset;
+} 
+VTXFileHeader_t;
+static VTXBodyPartHeader_t *pBodyPartVTX( VTXFileHeader_t *t, int i ) 
+{
+       return (VTXBodyPartHeader_t *)(((char *)t) + t->bodyPartOffset) + i;
+}
+
+/*
+                .VTX file structure
+   =============================================
+
+   FileHeader
+     L BodyParts::
+        L      Models::
+           L   LODS::
+              L        Meshes::
+                 L     StripGroups::
+                    L  VerticesTable[StudioMDL.Vertex]
+                    L  IndicesTable[UINT16]
+                    |
+                    L  Strips::
+                       L       Vertices[UINT16]
+                       L       Indices[UINT16]
+*/
+
+#pragma pack(pop)
+
+/* 
+ * mdl format
+ */
+
+#pragma pack(push, 1)
+
+typedef struct
+{
+       void                    *pVertexData;
+       void                    *pTangentData;
+} 
+mstudio_modelvertexdata_t;
+
+typedef struct
+{
+       int                                     unused_modelvertexdata;
+       int                                     numLODVertexes[MAX_NUM_LODS];
+       
+       mstudio_modelvertexdata_t *_the_death_ptr;
+} 
+mstudio_meshvertexdata_t;
+
+typedef struct mstudiomodel_t mstudiomodel_t;
+typedef struct
+{
+       int                                     material;
+       int                                     modelindex; 
+       int                                     numvertices;
+       int                                     vertexoffset;
+       int                                     numflexes;      
+       int                                     flexindex;
+       int                                     materialtype;
+       int                                     materialparam;
+       int                                     meshid;
+       float                                   center[3];
+       mstudio_meshvertexdata_t vertexdata;
+       int                                     unused[6]; 
+} 
+mstudiomesh_t;
+
+struct mstudiomodel_t
+{
+       char                                    name[64];
+       int                                     type;
+       float                                   boundingradius;
+
+       int                                     nummeshes;      
+       int                                     meshindex;
+
+       int                                     numvertices;
+       int                                     vertexindex;
+       int                                     tangentsindex;
+
+       int                                     numattachments;
+       int                                     attachmentindex;
+
+       int                                     numeyeballs;
+       int                                     eyeballindex;
+
+       mstudio_modelvertexdata_t vertexdata;
+
+       int                                     unused[8];
+};
+static mstudiomesh_t *studiomodel_pMesh( mstudiomodel_t *t, int i ) 
+{ 
+       return (mstudiomesh_t *)(((char *)t) + t->meshindex) + i; 
+}
+
+typedef struct
+{
+       int                                     sznameindex;
+       int                                     nummodels;
+       int                                     base;
+       int                                     modelindex;
+} mstudiobodyparts_t;
+
+static mstudiomodel_t *mstudiobodyparts_pModel( mstudiobodyparts_t *t, int i ) 
+{ 
+       return (mstudiomodel_t *)(((char *)t) + t->modelindex) + i;
+}
+
+typedef struct
+{
+       int     id;
+       int     version;
+       int     checksum;
+       char    name[64];
+       int     length;
+       float   eyeposition[3];
+       float   illumposition[3];
+       float   hull_min[3];
+       float   hull_max[3];
+       float   view_bbmin[3];
+       float   view_bbmax[3];
+       int     flags;
+       int     numbones;
+       int     boneindex;
+       int     numbonecontrollers;
+       int     bonecontrollerindex;
+       int     numhitboxsets;
+       int     hitboxsetindex;
+       int     numlocalanim;           
+       int     localanimindex;
+       int     numlocalseq;
+       int     localseqindex;
+       int     activitylistversion;
+       int     eventsindexed;
+       int     numtextures;
+       int     textureindex;
+       int     numcdtextures;
+       int     cdtextureindex;
+       int     numskinref;
+       int     numskinfamilies;
+       int     skinindex;
+       int     numbodyparts;
+       int     bodypartindex;
+       int     numlocalattachments;
+       int     localattachmentindex;
+       int     numlocalnodes;
+       int     localnodeindex;
+       int     localnodenameindex;
+       int     numflexdesc;
+       int     flexdescindex;
+       int     numflexcontrollers;
+       int     flexcontrollerindex;
+       int     numflexrules;
+       int     flexruleindex;
+       int     numikchains;
+       int     ikchainindex;
+       int     nummouths;
+       int     mouthindex;
+       int     numlocalposeparameters;
+       int     localposeparamindex;
+       int     surfacepropindex;
+       int     keyvalueindex;
+       int     keyvaluesize;
+       int     numlocalikautoplaylocks;
+       int     localikautoplaylockindex;
+       float   mass;
+       int     contents;
+       int     numincludemodels;
+       int     includemodelindex;
+       int     szanimblocknameindex;
+       int     numanimblocks;
+       int     animblockindex;
+       int     bonetablebynameindex;
+       char    constdirectionallightdot;
+       char    rootLOD;
+       char    numAllowedRootLODs;
+       char    unused[1];
+       int     unused4;
+       int     numflexcontrollerui;
+       int     flexcontrolleruiindex;
+       float   flVertAnimFixedPointScale;
+       int     unused3[1];
+       int     studiohdr2index;
+       int     unused2[1];
+} 
+studiohdr_t;
+
+static mstudiobodyparts_t *studiohdr_pBodypart( studiohdr_t *t, int i ) 
+{ 
+       return (mstudiobodyparts_t *)(((char *)t) + t->bodypartindex) + i; 
+}
+
+#pragma pack(pop)
+
+static u32 vtx_count_indices( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl )
+{
+   int indice_count = 0;
+
+       for( int bodyID = 0; bodyID < pVtxHdr->numBodyParts; ++bodyID )
+       {
+               /* Body parts */
+               VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( pVtxHdr, bodyID );
+               mstudiobodyparts_t *pBodyPart = studiohdr_pBodypart( pMdl, bodyID );
+               
+               for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+               {       
+         /* models */
+                       VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
+                       mstudiomodel_t *pStudioModel = 
+            mstudiobodyparts_pModel( pBodyPart, modelID );
+
+                       int nLod = 0;
+                       VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
+
+                       for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+                       {
+            /* meshes */
+                               VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
+                               mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
+
+                               for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+                               {
+               /* groups */
+                                       VTXStripGroupHeader_t* pStripGroup = 
+                  pStripGroupVTX( pVtxMesh, nGroup );
+
+                                       for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+                                       {
+                  /* strips */
+                                               VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
+
+                                               if ( pStrip->flags & STRIP_IS_TRILIST )
+                                               {
+                     indice_count += pStrip->numIndices;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+   return indice_count;
+}
+
+#endif /* CXR_VALVE_BIN_H */
index b8d91d89a67ba679a974d781341f240eb4a8fd26..3903b9636e0a291c283fb72ec7bc690c2e92d3c9 100644 (file)
@@ -1,4 +1,31 @@
-#define CXR_IMPLEMENTATION
+#define CXR_VALVE_BIN
+#include "cxr.h"
+
+int main(int arc, const char *argv[])
+{
+   if( cxr_fs_set_gameinfo( 
+      "/home/harry/.steam/steam/steamapps/common/"
+      "Counter-Strike Global Offensive/csgo/gameinfo.txt" ) )
+   {
+      cxr_tri_mesh *mesh_test = cxr_load_mdl( "models/pigeon.mdl" );
+
+      if( mesh_test )
+      {
+         cxr_log( "Mesh loaded\n" );
+         cxr_free_tri_mesh( mesh_test );
+      }
+      else
+         cxr_log( "Mesh failed to load\n" );
+   }
+
+   cxr_fs_exit();
+   return 0;
+}
+
+
+
+
+#if 0
 #define CXR_VALVE_MAP_FILE
 #define CXR_DEBUG 1
 #include "cxr.h"
@@ -36,3 +63,4 @@ int main(int arc, const char *argv[])
    cxr_vdf_close(vdo);
    return 0;
 }
+#endif