From 904bbc1a8fd83d6798c35975726440e4b8221d31 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 4 Jul 2017 13:48:52 -0500 Subject: [PATCH] Allow more than 32 surfaces in skin files Models don't have a surface limit; skins shouldn't either. Some player models require more than 32 surfaces since vanilla Quake 3 did not enforce the limit. Skins are now limited to 256 surfaces because having no limit would require parsing the skin file twice. The skin surfaces are dynamically allocated so it doesn't increase memory usage when less surfaces are used. --- code/renderergl1/tr_animation.c | 4 ++-- code/renderergl1/tr_image.c | 23 ++++++++++++++--------- code/renderergl1/tr_local.h | 8 +++++++- code/renderergl1/tr_mesh.c | 4 ++-- code/renderergl1/tr_model_iqm.c | 4 ++-- code/renderergl2/tr_animation.c | 4 ++-- code/renderergl2/tr_image.c | 23 ++++++++++++++--------- code/renderergl2/tr_local.h | 8 +++++++- code/renderergl2/tr_mesh.c | 4 ++-- code/renderergl2/tr_model_iqm.c | 4 ++-- 10 files changed, 54 insertions(+), 32 deletions(-) diff --git a/code/renderergl1/tr_animation.c b/code/renderergl1/tr_animation.c index b01cf813..2979ab9f 100644 --- a/code/renderergl1/tr_animation.c +++ b/code/renderergl1/tr_animation.c @@ -263,9 +263,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index b131f366..a926dd82 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -1508,6 +1508,7 @@ RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { + skinSurface_t parseSurfaces[MAX_SKIN_SURFACES]; qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; @@ -1557,8 +1558,8 @@ qhandle_t RE_RegisterSkin( const char *name ) { // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); return hSkin; } @@ -1591,12 +1592,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MD3_MAX_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES ); + if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); break; } - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + surf = &parseSurfaces[skin->numSurfaces]; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); skin->numSurfaces++; @@ -1610,6 +1611,10 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; // use default skin } + // copy surfaces to skin + skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low ); + memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) ); + return hSkin; } @@ -1628,8 +1633,8 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = tr.defaultShader; } /* @@ -1658,10 +1663,10 @@ void R_SkinList_f( void ) { for ( i = 0 ; i < tr.numSkins ; i++ ) { skin = tr.skins[i]; - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces ); for ( j = 0 ; j < skin->numSurfaces ; j++ ) { ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + skin->surfaces[j].name, skin->surfaces[j].shader->name ); } } ri.Printf (PRINT_ALL, "------------------\n"); diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index aedf1a4f..37e0e070 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -411,6 +411,12 @@ typedef struct { //================================================================================= +// max surfaces per-skin +// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to +// enforce the maximum limit when reading skin files. It was possile to use more than 32 +// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block. +#define MAX_SKIN_SURFACES 256 + // skins allow models to be retextured without modifying the model file typedef struct { char name[MAX_QPATH]; @@ -420,7 +426,7 @@ typedef struct { typedef struct skin_s { char name[MAX_QPATH]; // game path, including extension int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; + skinSurface_t *surfaces; // dynamically allocated array of surfaces } skin_t; diff --git a/code/renderergl1/tr_mesh.c b/code/renderergl1/tr_mesh.c index d0be29bc..4cdd9fc2 100644 --- a/code/renderergl1/tr_mesh.c +++ b/code/renderergl1/tr_mesh.c @@ -361,8 +361,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.defaultShader; for ( j = 0 ; j < skin->numSurfaces ; j++ ) { // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; + if ( !strcmp( skin->surfaces[j].name, surface->name ) ) { + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index 6c47ed07..2cb5cd4f 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -903,9 +903,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_animation.c b/code/renderergl2/tr_animation.c index 9acd6fc7..38fffc64 100644 --- a/code/renderergl2/tr_animation.c +++ b/code/renderergl2/tr_animation.c @@ -267,9 +267,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 0fa79612..7340377d 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -3060,6 +3060,7 @@ RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { + skinSurface_t parseSurfaces[MAX_SKIN_SURFACES]; qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; @@ -3109,8 +3110,8 @@ qhandle_t RE_RegisterSkin( const char *name ) { // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); return hSkin; } @@ -3143,12 +3144,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MD3_MAX_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES ); + if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); break; } - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + surf = &parseSurfaces[skin->numSurfaces]; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); skin->numSurfaces++; @@ -3162,6 +3163,10 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; // use default skin } + // copy surfaces to skin + skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low ); + memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) ); + return hSkin; } @@ -3180,8 +3185,8 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = tr.defaultShader; } /* @@ -3210,10 +3215,10 @@ void R_SkinList_f( void ) { for ( i = 0 ; i < tr.numSkins ; i++ ) { skin = tr.skins[i]; - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces ); for ( j = 0 ; j < skin->numSurfaces ; j++ ) { ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + skin->surfaces[j].name, skin->surfaces[j].shader->name ); } } ri.Printf (PRINT_ALL, "------------------\n"); diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 1fc91f70..bdd5a859 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -766,6 +766,12 @@ typedef struct { //================================================================================= +// max surfaces per-skin +// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to +// enforce the maximum limit when reading skin files. It was possile to use more than 32 +// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block. +#define MAX_SKIN_SURFACES 256 + // skins allow models to be retextured without modifying the model file typedef struct { char name[MAX_QPATH]; @@ -775,7 +781,7 @@ typedef struct { typedef struct skin_s { char name[MAX_QPATH]; // game path, including extension int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; + skinSurface_t *surfaces; // dynamically allocated array of surfaces } skin_t; diff --git a/code/renderergl2/tr_mesh.c b/code/renderergl2/tr_mesh.c index 5f7c4ee9..53bccb46 100644 --- a/code/renderergl2/tr_mesh.c +++ b/code/renderergl2/tr_mesh.c @@ -365,8 +365,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.defaultShader; for ( j = 0 ; j < skin->numSurfaces ; j++ ) { // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; + if ( !strcmp( skin->surfaces[j].name, surface->name ) ) { + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 67d3491c..caa4308b 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -907,9 +907,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } }