/* ============== CG_SoundPlaySoundScript returns qtrue is a script is found ============== */ qboolean CG_SoundPlaySoundScript( const char *name, vec3_t org, int entnum ) { long hash; char *s; soundScript_t *sound; if ( !name || !name[0] ) { return qfalse; } hash = generateHashValue( name ); s = (char *)name; sound = hashTable[hash]; while ( sound ) { if ( !Q_strcasecmp( s, sound->name ) ) { // found a match, pick the oldest sound CG_SoundPickOldestRandomSound( sound, org, entnum ); return qtrue; } sound = sound->nextHash; } //CG_Printf( S_COLOR_RED "CG_SoundPlaySoundScript: cannot find sound script '%s'\n", name ); return qfalse; }
/* ============ Cvar_FindVar ============ */ cvar_t *Cvar_FindVar(const char *var_name) { cvar_t *var; long hash; hash = generateHashValue(var_name); for (var=hashTable[hash] ; var ; var=var->hashNext) { if (!Q_stricmp(var_name, var->name)) { return var; } } return NULL; }
void RE_InsertModelIntoHash(const char *name, model_t *mod) { int hash; modelHash_t *mh; hash = generateHashValue(name, FILE_HASH_SIZE); // insert this file into the hash table so we can look it up faster later mh = (modelHash_t*)R_Hunk_Alloc( sizeof( modelHash_t ), qtrue ); mh->next = mhHashTable[hash]; // I have the breakpoint triggered here where mhHashTable[986] would be assigned mh->handle = mod->index; strcpy(mh->name, name); mhHashTable[hash] = mh; }
void RE_InsertModelIntoHash(const char *name, model_t *mod) { int hash; modelHash_t *mh; hash = generateHashValue(name, FILE_HASH_SIZE); // insert this file into the hash table so we can look it up faster later mh = (modelHash_t*)Hunk_Alloc( sizeof( modelHash_t ), h_low ); mh->next = mhHashTable[hash]; mh->handle = mod->index; strcpy(mh->name, name); mhHashTable[hash] = mh; }
/* ============== CG_SoundScriptPrecache returns the index+1 of the script in the global list, for fast calling ============== */ int CG_SoundScriptPrecache( const char *name ) { soundScriptSound_t *scriptSound; long hash; char *s; soundScript_t *sound; byte buf[1024]; if ( !name || !name[0] ) { return 0; } hash = generateHashValue( name ); s = (char *)name; sound = hashTable[hash]; while ( sound ) { if ( !Q_strcasecmp( s, sound->name ) ) { // found a match, precache these sounds scriptSound = sound->soundList; if ( !sound->streaming ) { while ( scriptSound ) { scriptSound->sfxHandle = trap_S_RegisterSound( scriptSound->filename ); scriptSound = scriptSound->next; } } else /*if (cg_buildScript.integer)*/ { // RF, 11/6/01 enabled this permanently so that streaming sounds get touched within file system on startup while ( scriptSound ) { // just open the file so it gets copied to the build dir fileHandle_t f; trap_FS_FOpenFile( scriptSound->filename, &f, FS_READ ); // read a few bytes so the operating system does a better job of caching it for us trap_FS_Read( buf, sizeof( buf ), f ); trap_FS_FCloseFile( f ); scriptSound = scriptSound->next; } } return sound->index + 1; } sound = sound->nextHash; } return 0; }
/* ============== CG_SoundScriptPrecache returns the index+1 of the script in the global list, for fast calling ============== */ int CG_SoundScriptPrecache( const char *name ) { soundScriptSound_t *scriptSound; long hash; char *s; soundScript_t *sound; if ( !name || !name[0] ) { return 0; } hash = generateHashValue( name ); s = (char *)name; sound = hashTable[hash]; while ( sound ) { if ( !Q_strcasecmp( s, sound->name ) ) { // found a match, precache these sounds scriptSound = sound->soundList; if ( !sound->streaming ) { while ( scriptSound ) { scriptSound->sfxHandle = trap_S_RegisterSound( scriptSound->filename ); scriptSound = scriptSound->next; } } else if ( cg_buildScript.integer ) { while ( scriptSound ) { // just open the file so it gets copied to the build dir fileHandle_t f; trap_FS_FOpenFile( scriptSound->filename, &f, FS_READ ); trap_FS_FCloseFile( f ); scriptSound = scriptSound->next; } } return sound->index + 1; } sound = sound->nextHash; } return 0; }
/* ============ Cvar_SetIFlag Sets the cvar by the name that begins with a backslash to "1". This creates a cvar that can be set by the engine but not by the sure, and can be read by interpreted modules. ============ */ void Cvar_SetIFlag( const char *var_name ) { cvar_t *var; long hash; int index; if ( !var_name ) { Com_Error( ERR_FATAL, "Cvar_SetIFlag: NULL parameter" ); } if ( *var_name != '\\' ) { Com_Error( ERR_FATAL, "Cvar_SetIFlag: var_name must begin with a '\\'" ); } /* if ( Cvar_FindVar( var_name ) ) { Com_Error( ERR_FATAL, "Cvar_SetIFlag: %s already exists.", var_name ); } */ if ( !Cvar_ValidateString( var_name + 1 ) ) { Com_Printf( "invalid cvar name string: %s\n", var_name ); var_name = "BADNAME"; } // find a free cvar for(index = 0; index < MAX_CVARS; index++) { if(!cvar_indexes[index].name) break; } if(index >= MAX_CVARS) { if(!com_errorEntered) Com_Error(ERR_FATAL, "Error: Too many cvars, cannot create a new one!"); return; } var = &cvar_indexes[index]; if(index >= cvar_numIndexes) cvar_numIndexes = index + 1; var->name = CopyString (var_name); var->string = CopyString ("1"); var->modified = qtrue; var->modificationCount = 1; var->value = atof (var->string); var->integer = atoi(var->string); var->resetString = CopyString( "1" ); var->validate = qfalse; // link the variable in var->next = cvar_vars; cvar_vars = var; var->flags = CVAR_INIT; // note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo) cvar_modifiedFlags |= var->flags; hash = generateHashValue(var_name); var->hashNext = hashTable[hash]; hashTable[hash] = var; }
/* ============ Cvar_Get If the variable already exists, the value will not be set unless CVAR_ROM The flags will be or'ed in if the variable exists. ============ */ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_t *var; long hash; if ( !var_name || !var_value ) { Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); } if ( !Cvar_ValidateString( var_name ) ) { Com_Printf( "invalid cvar name string: %s\n", var_name ); var_name = "BADNAME"; } #if 0 // FIXME: values with backslash happen if ( !Cvar_ValidateString( var_value ) ) { Com_Printf( "invalid cvar value string: %s\n", var_value ); var_value = "BADVALUE"; } #endif var = Cvar_FindVar( var_name ); if ( var ) { // if the C code is now specifying a variable that the user already // set a value for, take the new value as the reset value if ( ( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED ) && var_value[ 0 ] ) { var->flags &= ~CVAR_USER_CREATED; Z_Free( var->resetString ); var->resetString = CopyString( var_value ); if ( flags & CVAR_ROM ) { // this variable was set by the user, // so force it to value given by the engine. if ( var->latchedString ) { Z_Free( var->latchedString ); } var->latchedString = CopyString( var_value ); } // ZOID--needs to be set so that cvars the game sets as // SERVERINFO get sent to clients cvar_modifiedFlags |= flags; } var->flags |= flags; // only allow one non-empty reset string without a warning if ( !var->resetString[ 0 ] ) { // we don't have a reset string yet Z_Free( var->resetString ); var->resetString = CopyString( var_value ); } else if ( var_value[ 0 ] && strcmp( var->resetString, var_value ) ) { Com_DPrintf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", var_name, var->resetString, var_value ); } // if we have a latched string, take that value now if ( var->latchedString ) { char *s; s = var->latchedString; var->latchedString = NULL; // otherwise cvar_set2 would free it Cvar_Set2( var_name, s, qtrue ); Z_Free( s ); } // TTimo // if CVAR_USERINFO was toggled on for an existing cvar, check whether the value needs to be cleaned from foreign characters // (for instance, seta name "name-with-foreign-chars" in the config file, and toggle to CVAR_USERINFO happens later in CL_Init) if ( flags & CVAR_USERINFO ) { const char *cleaned = Com_ClearForeignCharacters( var->string ); // NOTE: it is probably harmless to call Cvar_Set2 in all cases, but I don't want to risk it if ( strcmp( var->string, cleaned ) ) { Cvar_Set2( var->name, var->string, qfalse ); // call Cvar_Set2 with the value to be cleaned up for verbosity } } // use a CVAR_SET for rom sets, get won't override #if 0 // CVAR_ROM always overrides if ( flags & CVAR_ROM ) { Cvar_Set2( var_name, var_value, qtrue ); } #endif return var; } // // allocate a new cvar // if ( cvar_numIndexes >= MAX_CVARS ) { Com_Error( ERR_FATAL, "MAX_CVARS (%d) hit: too many cvars!", MAX_CVARS ); } var = &cvar_indexes[ cvar_numIndexes ]; cvar_numIndexes++; var->name = CopyString( var_name ); var->string = CopyString( var_value ); var->modified = qtrue; var->modificationCount = 0; var->value = atof( var->string ); var->integer = atoi( var->string ); var->resetString = CopyString( var_value ); var->transient = qtrue; // link the variable in var->next = cvar_vars; cvar_vars = var; var->flags = flags; // note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo) cvar_modifiedFlags |= var->flags; hash = generateHashValue( var_name ); var->hashNext = hashTable[ hash ]; hashTable[ hash ] = var; return var; }
/* ============ Cvar_Get If the variable already exists, the value will not be set unless CVAR_ROM The flags will be or'ed in if the variable exists. ============ */ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_t *var; long hash; if ( !var_name || ! var_value ) { Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); } if ( !Cvar_ValidateString( var_name ) ) { Com_Printf("invalid cvar name string: %s\n", var_name ); var_name = "BADNAME"; } #if 0 // FIXME: values with backslash happen if ( !Cvar_ValidateString( var_value ) ) { Com_Printf("invalid cvar value string: %s\n", var_value ); var_value = "BADVALUE"; } #endif var = Cvar_FindVar (var_name); if ( var ) { // if the C code is now specifying a variable that the user already // set a value for, take the new value as the reset value if ( ( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED ) && var_value[0] ) { var->flags &= ~CVAR_USER_CREATED; Z_Free( var->resetString ); var->resetString = CopyString( var_value ); // ZOID--needs to be set so that cvars the game sets as // SERVERINFO get sent to clients cvar_modifiedFlags |= flags; } var->flags |= flags; // only allow one non-empty reset string without a warning if ( !var->resetString[0] ) { // we don't have a reset string yet Z_Free( var->resetString ); var->resetString = CopyString( var_value ); } else if ( var_value[0] && strcmp( var->resetString, var_value ) ) { Com_DPrintf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", var_name, var->resetString, var_value ); } // if we have a latched string, take that value now if ( var->latchedString ) { char *s; s = var->latchedString; var->latchedString = NULL; // otherwise cvar_set2 would free it Cvar_Set2( var_name, s, qtrue ); Z_Free( s ); } // use a CVAR_SET for rom sets, get won't override #if 0 // CVAR_ROM always overrides if ( flags & CVAR_ROM ) { Cvar_Set2( var_name, var_value, qtrue ); } #endif return var; } // // allocate a new cvar // if ( cvar_numIndexes >= MAX_CVARS ) { Com_Error( ERR_FATAL, "MAX_CVARS" ); } var = &cvar_indexes[cvar_numIndexes]; cvar_numIndexes++; var->name = CopyString (var_name); var->string = CopyString (var_value); var->modified = qtrue; var->modificationCount = 1; var->value = atof (var->string); var->integer = atoi(var->string); var->resetString = CopyString( var_value ); // link the variable in var->next = cvar_vars; cvar_vars = var; var->flags = flags; hash = generateHashValue(var_name); var->hashNext = hashTable[hash]; hashTable[hash] = var; return var; }
/* ==================== RE_RegisterModel Loads in a model for the given name Zero will be returned if the model fails to load. An entry will be retained for failed models as an optimization to prevent disk rescanning if they are asked for again. ==================== */ static qhandle_t RE_RegisterModel_Actual( const char *name ) { model_t *mod; unsigned *buf; int lod; int ident; qboolean loaded; // qhandle_t hModel; int numLoaded; /* Ghoul2 Insert Start */ int hash; modelHash_t *mh; /* Ghoul2 Insert End */ if ( !name || !name[0] ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { ri.Printf( PRINT_DEVELOPER, "Model name exceeds MAX_QPATH\n" ); return 0; } /* Ghoul2 Insert Start */ // if (!tr.registered) { // ri.Printf( PRINT_WARNING, "RE_RegisterModel (%s) called before ready!\n",name ); // return 0; // } // // search the currently loaded models // hash = generateHashValue(name, FILE_HASH_SIZE); // // see if the model is already loaded //_ for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, name) == 0) { if (tr.models[mh->handle]->type == MOD_BAD) { return 0; } return mh->handle; } } /* Ghoul2 Insert End */ if (name[0] == '#') { char temp[MAX_QPATH]; tr.numBSPModels++; #ifndef DEDICATED RE_LoadWorldMap_Actual(va("maps/%s.bsp", name + 1), tr.bspModels[tr.numBSPModels - 1], tr.numBSPModels); //this calls R_LoadSubmodels which will put them into the Hash #endif Com_sprintf(temp, MAX_QPATH, "*%d-0", tr.numBSPModels); hash = generateHashValue(temp, FILE_HASH_SIZE); for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, temp) == 0) { return mh->handle; } } return 0; } // allocate a new model_t if ( ( mod = R_AllocModel() ) == NULL ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); // make sure the render thread is stopped R_IssuePendingRenderCommands(); // int iLODStart = 0; if (strstr (name, ".md3")) { iLODStart = MD3_MAX_LODS-1; //this loads the md3s in reverse so they can be biased } mod->numLods = 0; // // load the files // numLoaded = 0; for ( lod = iLODStart; lod >= 0 ; lod-- ) { char filename[1024]; strcpy( filename, name ); if ( lod != 0 ) { char namebuf[80]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } qboolean bAlreadyCached = qfalse; if (!RE_RegisterModels_GetDiskFile(filename, (void **)&buf, &bAlreadyCached)) { if (numLoaded) //we loaded one already, but a higher LOD is missing! { Com_Error (ERR_DROP, "R_LoadMD3: %s has LOD %d but is missing LOD %d ('%s')!", mod->name, lod+1, lod, filename); } continue; } //loadmodel = mod; // this seems to be fairly pointless // important that from now on we pass 'filename' instead of 'name' to all model load functions, // because 'filename' accounts for any LOD mangling etc so guarantees unique lookups for yet more // internal caching... // ident = *(unsigned *)buf; if (!bAlreadyCached) { ident = LittleLong(ident); } switch (ident) { // if you add any new types of model load in this switch-case, tell me, // or copy what I've done with the cache scheme (-ste). // case MDXA_IDENT: loaded = R_LoadMDXA( mod, buf, filename, bAlreadyCached ); break; case MDXM_IDENT: loaded = R_LoadMDXM( mod, buf, filename, bAlreadyCached ); break; case MD3_IDENT: loaded = R_LoadMD3( mod, lod, buf, filename, bAlreadyCached ); break; default: ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", filename); goto fail; } if (!bAlreadyCached){ // important to check!! ri.FS_FreeFile (buf); } if ( !loaded ) { if ( lod == 0 ) { ri.Printf (PRINT_WARNING,"RE_RegisterModel: cannot load %s\n", filename); goto fail; } else { break; } } else { mod->numLods++; numLoaded++; // if we have a valid model and are biased // so that we won't see any higher detail ones, // stop loading them if ( lod <= r_lodbias->integer ) { break; } } } if ( numLoaded ) { // duplicate into higher lod spots that weren't // loaded, in case the user changes r_lodbias on the fly for ( lod-- ; lod >= 0 ; lod-- ) { mod->numLods++; mod->md3[lod] = mod->md3[lod+1]; } /* Ghoul2 Insert Start */ RE_InsertModelIntoHash(name, mod); return mod->index; /* Ghoul2 Insert End */ } fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; RE_InsertModelIntoHash(name, mod); return 0; }
/** * @brief Finds and loads all .shader files, combining them into * a single large text block that can be scanned for shader names */ int ScanAndLoadShaderFilesR1() { char **shaderFiles; char *buffers[MAX_SHADER_FILES]; char *p; int numShaderFiles, i; char *oldp, *token, *textEnd; char **hashMem; int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash; unsigned int size; char filename[MAX_QPATH]; long sum = 0, summand; Com_Memset(buffers, 0, MAX_SHADER_FILES); Com_Memset(shaderTextHashTableSizes, 0, MAX_SHADER_FILES); // scan for shader files shaderFiles = ri.FS_ListFiles("scripts", ".shader", &numShaderFiles); if (!shaderFiles || !numShaderFiles) { Ren_Print("----- ScanAndLoadShaderFilesR1 (no files)-----\n"); return 0; } Ren_Print("----- ScanAndLoadShaderFilesR1 (%i files)-----\n", numShaderFiles); if (numShaderFiles >= MAX_SHADER_FILES) { Ren_Drop("MAX_SHADER_FILES limit is reached!"); } // load and parse shader files for (i = 0; i < numShaderFiles; i++) { Com_sprintf(filename, sizeof(filename), "scripts/%s", shaderFiles[i]); COM_BeginParseSession(filename); Ren_Developer("...loading '%s'\n", filename); summand = ri.FS_ReadFile(filename, (void **)&buffers[i]); if (!buffers[i]) { Ren_Drop("Couldn't load %s", filename); // in this case shader file is cought/listed but the file can't be read - drop! } p = buffers[i]; while (1) { token = COM_ParseExt(&p, qtrue); if (!*token) { break; } // Step over the "table"/"guide" and the name if (!Q_stricmp(token, "table") || !Q_stricmp(token, "guide")) { token = COM_ParseExt2(&p, qtrue); if (!*token) { break; } } oldp = p; token = COM_ParseExt2(&p, qtrue); if (token[0] != '{' && token[1] != '\0') { Ren_Warning("WARNING: Bad shader file %s has incorrect syntax near token '%s' line %i\n", filename, token, COM_GetCurrentParseLine()); ri.FS_FreeFile(buffers[i]); buffers[i] = NULL; break; } SkipBracedSection(&oldp); p = oldp; } if (buffers[i]) { sum += summand; } } // build single large buffer s_shaderTextR1 = (char *)ri.Hunk_Alloc(sum + numShaderFiles * 2, h_low); s_shaderTextR1[0] = '\0'; textEnd = s_shaderTextR1; // free in reverse order, so the temp files are all dumped for (i = numShaderFiles - 1; i >= 0 ; i--) { if (!buffers[i]) { continue; } strcat(textEnd, buffers[i]); strcat(textEnd, "\n"); textEnd += strlen(textEnd); ri.FS_FreeFile(buffers[i]); } COM_Compress(s_shaderTextR1); // free up memory ri.FS_FreeFileList(shaderFiles); Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); size = 0; p = s_shaderTextR1; // look for shader names while (1) { token = COM_ParseExt(&p, qtrue); if (token[0] == 0) { break; } // skip shader tables if (!Q_stricmp(token, "table")) { // skip table name (void) COM_ParseExt2(&p, qtrue); SkipBracedSection(&p); } // support shader templates else if (!Q_stricmp(token, "guide")) { // parse shader name token = COM_ParseExt2(&p, qtrue); //Ren_Print("...guided '%s'\n", token); hash = generateHashValue(token, MAX_SHADERTEXT_HASH); shaderTextHashTableSizes[hash]++; size++; // skip guide name token = COM_ParseExt2(&p, qtrue); // skip parameters token = COM_ParseExt2(&p, qtrue); if (Q_stricmp(token, "(")) { Ren_Warning("expected ( found '%s'\n", token); break; } while (1) { token = COM_ParseExt2(&p, qtrue); if (!token[0]) { break; } if (!Q_stricmp(token, ")")) { break; } } if (Q_stricmp(token, ")")) { Ren_Warning("expected ( found '%s'\n", token); break; } } else { hash = generateHashValue(token, MAX_SHADERTEXT_HASH); shaderTextHashTableSizes[hash]++; size++; SkipBracedSection(&p); } } //Ren_Print("Shader hash table size %i\n", size); size += MAX_SHADERTEXT_HASH; hashMem = (char **)ri.Hunk_Alloc(size * sizeof(char *), h_low); for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { shaderTextHashTableR1[i] = hashMem; hashMem += shaderTextHashTableSizes[i] + 1; } Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); p = s_shaderTextR1; // look for shader names while (1) { oldp = p; token = COM_ParseExt(&p, qtrue); if (token[0] == 0) { break; } // parse shader tables if (!Q_stricmp(token, "table")) { int depth; float values[FUNCTABLE_SIZE]; int numValues; shaderTable_t *tb; qboolean alreadyCreated; Com_Memset(&values, 0, sizeof(values)); Com_Memset(&table, 0, sizeof(table)); token = COM_ParseExt2(&p, qtrue); Q_strncpyz(table.name, token, sizeof(table.name)); // check if already created alreadyCreated = qfalse; hash = generateHashValue(table.name, MAX_SHADERTABLE_HASH); for (tb = shaderTableHashTable[hash]; tb; tb = tb->next) { if (Q_stricmp(tb->name, table.name) == 0) { // match found alreadyCreated = qtrue; break; } } depth = 0; numValues = 0; do { token = COM_ParseExt2(&p, qtrue); if (!Q_stricmp(token, "snap")) { table.snap = qtrue; } else if (!Q_stricmp(token, "clamp")) { table.clamp = qtrue; } else if (token[0] == '{') { depth++; } else if (token[0] == '}') { depth--; } else if (token[0] == ',') { continue; } else { if (numValues == FUNCTABLE_SIZE) { Ren_Warning("WARNING: FUNCTABLE_SIZE hit\n"); break; } values[numValues++] = atof(token); } } while (depth && p); if (!alreadyCreated) { Ren_Developer("...generating '%s'\n", table.name); GeneratePermanentShaderTable(values, numValues); } } // support shader templates else if (!Q_stricmp(token, "guide")) { // parse shader name oldp = p; token = COM_ParseExt2(&p, qtrue); //Ren_Print("...guided '%s'\n", token); hash = generateHashValue(token, MAX_SHADERTEXT_HASH); shaderTextHashTableR1[hash][shaderTextHashTableSizes[hash]++] = oldp; // skip guide name token = COM_ParseExt2(&p, qtrue); // skip parameters token = COM_ParseExt2(&p, qtrue); if (Q_stricmp(token, "(")) { Ren_Warning("expected ( found '%s'\n", token); break; } while (1) { token = COM_ParseExt2(&p, qtrue); if (!token[0]) { break; } if (!Q_stricmp(token, ")")) { break; } } if (Q_stricmp(token, ")")) { Ren_Warning("expected ( found '%s'\n", token); break; } } else { hash = generateHashValue(token, MAX_SHADERTEXT_HASH); shaderTextHashTableR1[hash][shaderTextHashTableSizes[hash]++] = oldp; SkipBracedSection(&p); } } return numShaderFiles; }
/* ============ Cvar_Get If the variable already exists, the value will not be set unless CVAR_ROM The flags will be or'ed in if the variable exists. ============ */ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_t *var; long hash; int index; if ( !var_name || ! var_value ) { Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); } if ( !Cvar_ValidateString( var_name ) ) { Com_Printf("invalid cvar name string: %s\n", var_name ); var_name = "BADNAME"; } #if 0 // FIXME: values with backslash happen if ( !Cvar_ValidateString( var_value ) ) { Com_Printf("invalid cvar value string: %s\n", var_value ); var_value = "BADVALUE"; } #endif var = Cvar_FindVar (var_name); if(var) { var_value = Cvar_Validate(var, var_value, qfalse); // Make sure the game code cannot mark engine-added variables as gamecode vars if(var->flags & CVAR_VM_CREATED) { if(!(flags & CVAR_VM_CREATED)) var->flags &= ~CVAR_VM_CREATED; } else if (!(var->flags & CVAR_USER_CREATED)) { if(flags & CVAR_VM_CREATED) flags &= ~CVAR_VM_CREATED; } // if the C code is now specifying a variable that the user already // set a value for, take the new value as the reset value if(var->flags & CVAR_USER_CREATED) { var->flags &= ~CVAR_USER_CREATED; Z_Free( var->resetString ); var->resetString = CopyString( var_value ); if(flags & CVAR_ROM) { // this variable was set by the user, // so force it to value given by the engine. if(var->latchedString) Z_Free(var->latchedString); var->latchedString = CopyString(var_value); } } // Make sure servers cannot mark engine-added variables as SERVER_CREATED if(var->flags & CVAR_SERVER_CREATED) { if(!(flags & CVAR_SERVER_CREATED)) var->flags &= ~CVAR_SERVER_CREATED; } else { if(flags & CVAR_SERVER_CREATED) flags &= ~CVAR_SERVER_CREATED; } var->flags |= flags; // only allow one non-empty reset string without a warning if ( !var->resetString[0] ) { // we don't have a reset string yet Z_Free( var->resetString ); var->resetString = CopyString( var_value ); } else if ( var_value[0] && strcmp( var->resetString, var_value ) ) { Com_DPrintf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", var_name, var->resetString, var_value ); } // if we have a latched string, take that value now if ( var->latchedString ) { char *s; s = var->latchedString; var->latchedString = NULL; // otherwise cvar_set2 would free it Cvar_Set2( var_name, s, qtrue ); Z_Free( s ); } // ZOID--needs to be set so that cvars the game sets as // SERVERINFO get sent to clients cvar_modifiedFlags |= flags; return var; } // // allocate a new cvar // // find a free cvar for(index = 0; index < MAX_CVARS; index++) { if(!cvar_indexes[index].name) break; } if(index >= MAX_CVARS) { if(!com_errorEntered) Com_Error(ERR_FATAL, "Error: Too many cvars, cannot create a new one!"); return NULL; } var = &cvar_indexes[index]; if(index >= cvar_numIndexes) cvar_numIndexes = index + 1; var->name = CopyString (var_name); var->string = CopyString (var_value); var->modified = qtrue; var->modificationCount = 1; var->value = atof (var->string); var->integer = atoi(var->string); var->resetString = CopyString( var_value ); var->validate = qfalse; // link the variable in var->next = cvar_vars; if(cvar_vars) cvar_vars->prev = var; var->prev = NULL; cvar_vars = var; var->flags = flags; // note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo) cvar_modifiedFlags |= var->flags; hash = generateHashValue(var_name); var->hashIndex = hash; var->hashNext = hashTable[hash]; if(hashTable[hash]) hashTable[hash]->hashPrev = var; var->hashPrev = NULL; hashTable[hash] = var; return var; }
/* ==================== RE_RegisterModel Loads in a model for the given name Zero will be returned if the model fails to load. An entry will be retained for failed models as an optimization to prevent disk rescanning if they are asked for again. ==================== */ static qhandle_t RE_RegisterModel_Actual( const char *name ) { model_t *mod; unsigned *buf; int lod; int ident; qboolean loaded; // qhandle_t hModel; int numLoaded; /* Ghoul2 Insert Start */ int hash; modelHash_t *mh; /* Ghoul2 Insert End */ if ( !name || !name[0] ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { ri.Printf( PRINT_DEVELOPER, "Model name exceeds MAX_QPATH\n" ); return 0; } /* Ghoul2 Insert Start */ // if (!tr.registered) { // ri.Printf( PRINT_WARNING, "RE_RegisterModel (%s) called before ready!\n",name ); // return 0; // } // // search the currently loaded models // hash = generateHashValue(name, FILE_HASH_SIZE); // // see if the model is already loaded // for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, name) == 0) { if (tr.models[mh->handle]->type == MOD_BAD) { return 0; } return mh->handle; } } // for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { // mod = tr.models[hModel]; // if ( !strcmp( mod->name, name ) ) { // if( mod->type == MOD_BAD ) { // return 0; // } // return hModel; // } // } /* Ghoul2 Insert End */ // allocate a new model_t if ( ( mod = R_AllocModel() ) == NULL ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); #ifdef _NPATCH // Make sure we don't n-patch unless explicitly specified /* mod->npatchable = qfalse; for ( int i = 0; npatchableModels[i]; i++ ) { if ( stricmp(name, npatchableModels[i]) == 0 ) { mod->npatchable = qtrue; break; } } */ // new version, npatching is no controlled by the existance of a "*.npatch" file in the same place as *.glm extern cvar_t *r_ATI_NPATCH_available; if (r_ATI_NPATCH_available && r_ATI_NPATCH_available->integer && strstr(name,".glm")) { extern char *Filename_WithoutExt(const char *psFilename); fileHandle_t f; FS_FOpenFileRead( va("%s.npatch",Filename_WithoutExt(name)),&f, qtrue ); // qtrue = dup handle, so I can close it ok later mod->npatchable = !!f; if (f) { FS_FCloseFile(f); } } #endif // _NPATCH // make sure the render thread is stopped R_SyncRenderThread(); int iLODStart = 0; if (strstr (name, ".md3")) { iLODStart = MD3_MAX_LODS-1; //this loads the md3s in reverse so they can be biased } mod->numLods = 0; // // load the files // numLoaded = 0; for ( lod = iLODStart; lod >= 0 ; lod-- ) { char filename[1024]; strcpy( filename, name ); if ( lod != 0 ) { char namebuf[80]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } qboolean bAlreadyCached = qfalse; if (!RE_RegisterModels_GetDiskFile(filename, (void **)&buf, &bAlreadyCached)) { continue; } //loadmodel = mod; // this seems to be fairly pointless // important that from now on we pass 'filename' instead of 'name' to all model load functions, // because 'filename' accounts for any LOD mangling etc so guarantees unique lookups for yet more // internal caching... // ident = *(unsigned *)buf; if (!bAlreadyCached) { ident = LittleLong(ident); } switch (ident) { // if you add any new types of model load in this switch-case, tell me, // or copy what I've done with the cache scheme (-ste). // case MD4_IDENT: loaded = R_LoadMD4( mod, buf, filename, bAlreadyCached ); break; case MDXA_IDENT: loaded = R_LoadMDXA( mod, buf, filename, bAlreadyCached ); break; case MDXM_IDENT: loaded = R_LoadMDXM( mod, buf, filename, bAlreadyCached ); break; case MD3_IDENT: loaded = R_LoadMD3( mod, lod, buf, filename, bAlreadyCached ); break; default: ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", filename); goto fail; } if (!bAlreadyCached){ // important to check!! ri.FS_FreeFile (buf); } if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { break; } } else { mod->numLods++; numLoaded++; // if we have a valid model and are biased // so that we won't see any higher detail ones, // stop loading them if ( lod <= r_lodbias->integer ) { break; } } } if ( numLoaded ) { // duplicate into higher lod spots that weren't // loaded, in case the user changes r_lodbias on the fly for ( lod-- ; lod >= 0 ; lod-- ) { mod->numLods++; mod->md3[lod] = mod->md3[lod+1]; } /* Ghoul2 Insert Start */ RE_InsertModelIntoHash(name, mod); return mod->index; /* Ghoul2 Insert End */ } fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; RE_InsertModelIntoHash(name, mod); return 0; }
/* =============== CG_SoundParseSounds =============== */ static void CG_SoundParseSounds( char *filename, char *buffer ) { char *token, **text; int s; long hash; soundScript_t sound; // the current sound being read soundScriptSound_t *scriptSound; qboolean inSound, wantSoundName; s = 0; inSound = qfalse; wantSoundName = qtrue; text = &buffer; while ( 1 ) { token = COM_ParseExt( text, qtrue ); if ( !token[0] ) { if ( inSound ) { CG_Error( "no concluding '}' in sound %s, file %s\n", sound.name, filename ); } return; } if ( !Q_strcasecmp( token, "{" ) ) { if ( inSound ) { CG_Error( "no concluding '}' in sound %s, file %s\n", sound.name, filename ); } if ( wantSoundName ) { CG_Error( "'{' found but not expected, after %s, file %s\n", sound.name, filename ); } inSound = qtrue; continue; } if ( !Q_strcasecmp( token, "}" ) ) { if ( !inSound ) { CG_Error( "'}' unexpected after sound %s, file %s\n", sound.name, filename ); } // end of a sound, copy it to the global list and stick it in the hashTable hash = generateHashValue( sound.name ); sound.nextHash = hashTable[hash]; soundScripts[numSoundScripts] = sound; hashTable[hash] = &soundScripts[numSoundScripts++]; if ( numSoundScripts == MAX_SOUND_SCRIPTS ) { CG_Error( "MAX_SOUND_SCRIPTS exceeded.\nReduce number of sound scripts.\n" ); } inSound = qfalse; wantSoundName = qtrue; continue; } if ( !inSound ) { // this is the identifier for a new sound if ( !wantSoundName ) { CG_Error( "'%s' unexpected after sound %s, file %s\n", token, sound.name, filename ); } memset( &sound, 0, sizeof( sound ) ); Q_strncpyz( sound.name, token, sizeof( sound.name ) ); wantSoundName = qfalse; sound.index = numSoundScripts; // setup the new sound defaults sound.channel = CHAN_AUTO; sound.attenuation = 1; // default to fade away with distance (for streaming sounds) // continue; } // we are inside a sound script if ( !Q_strcasecmp( token, "channel" ) ) { // ignore this now, just look for the channel identifiers explicitly continue; } if ( !Q_strcasecmp( token, "local" ) ) { sound.channel = CHAN_LOCAL; continue; } else if ( !Q_strcasecmp( token, "announcer" ) ) { sound.channel = CHAN_ANNOUNCER; continue; } else if ( !Q_strcasecmp( token, "body" ) ) { sound.channel = CHAN_BODY; continue; } else if ( !Q_strcasecmp( token, "voice" ) ) { sound.channel = CHAN_VOICE; continue; } else if ( !Q_strcasecmp( token, "weapon" ) ) { sound.channel = CHAN_WEAPON; continue; } else if ( !Q_strcasecmp( token, "item" ) ) { sound.channel = CHAN_ITEM; continue; } else if ( !Q_strcasecmp( token, "auto" ) ) { sound.channel = CHAN_AUTO; continue; } if ( !Q_strcasecmp( token, "global" ) ) { sound.attenuation = 0; continue; } if ( !Q_strcasecmp( token, "streaming" ) ) { sound.streaming = qtrue; continue; } if ( !Q_strcasecmp( token, "looping" ) ) { sound.looping = qtrue; continue; } if ( !Q_strcasecmp( token, "sound" ) ) { // grab a free scriptSound scriptSound = &soundScriptSounds[numSoundScriptSounds++]; if ( numSoundScripts == MAX_SOUND_SCRIPT_SOUNDS ) { CG_Error( "MAX_SOUND_SCRIPT_SOUNDS exceeded.\nReduce number of sound scripts.\n" ); } token = COM_ParseExt( text, qtrue ); Q_strncpyz( scriptSound->filename, token, sizeof( scriptSound->filename ) ); scriptSound->lastPlayed = 0; scriptSound->sfxHandle = 0; scriptSound->next = sound.soundList; sound.soundList = scriptSound; continue; } } }
/** * @brief Scans the combined text description of all the shader files for * the given shader name. * @param[in] shaderName * @return NULL if not found, otherwise it will return a valid shader */ char *FindShaderInShaderTextR1(const char *shaderName) { char *token, *p; int i, hash; hash = generateHashValue(shaderName, MAX_SHADERTEXT_HASH); for (i = 0; shaderTextHashTableR1[hash][i]; i++) { p = shaderTextHashTableR1[hash][i]; token = COM_ParseExt2(&p, qtrue); if (!Q_stricmp(token, shaderName)) { //Ren_Print("found shader '%s' by hashing\n", shaderName); return p; } } p = s_shaderTextR1; if (!p) { return NULL; } // look for label while (1) { token = COM_ParseExt2(&p, qtrue); if (token[0] == 0) { break; } if (!Q_stricmp(token, shaderName)) { //Ren_Print("found shader '%s' by linear search\n", shaderName); return p; } // skip shader tables else if (!Q_stricmp(token, "table")) { // skip table name (void) COM_ParseExt2(&p, qtrue); SkipBracedSection(&p); } // support shader templates else if (!Q_stricmp(token, "guide")) { // parse shader name token = COM_ParseExt2(&p, qtrue); if (!Q_stricmp(token, shaderName)) { Ren_Print("found shader '%s' by linear search\n", shaderName); return p; } // skip guide name token = COM_ParseExt2(&p, qtrue); // skip parameters token = COM_ParseExt2(&p, qtrue); if (Q_stricmp(token, "(")) { break; } while (1) { token = COM_ParseExt2(&p, qtrue); if (!token[0]) { break; } if (!Q_stricmp(token, ")")) { break; } } if (Q_stricmp(token, ")")) { break; } } else { // skip the shader body SkipBracedSection(&p); } } return NULL; }
/* ==================== RE_RegisterModel Loads in a model for the given name Zero will be returned if the model fails to load. An entry will be retained for failed models as an optimization to prevent disk rescanning if they are asked for again. ==================== */ static qhandle_t RE_RegisterModel_Actual( const char *name ) { model_t *mod; unsigned *buf; int lod; int ident; qboolean loaded; // qhandle_t hModel; int numLoaded; /* Ghoul2 Insert Start */ int hash; modelHash_t *mh; /* Ghoul2 Insert End */ if ( !name || !name[0] ) { Com_Printf ("RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { ri->Printf( PRINT_DEVELOPER, S_COLOR_RED "Model name exceeds MAX_QPATH\n" ); return 0; } /* Ghoul2 Insert Start */ // if (!tr.registered) { // Com_Printf (S_COLOR_YELLOW "RE_RegisterModel (%s) called before ready!\n",name ); // return 0; // } // // search the currently loaded models // hash = generateHashValue(name, FILE_HASH_SIZE); // // see if the model is already loaded // for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, name) == 0) { return mh->handle; } } // for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { // mod = tr.models[hModel]; // if ( !strcmp( mod->name, name ) ) { // if( mod->type == MOD_BAD ) { // return 0; // } // return hModel; // } // } if (name[0] == '#') { char temp[MAX_QPATH]; tr.numBSPModels++; Com_sprintf(temp, MAX_QPATH, "*%d-0", tr.numBSPModels); hash = generateHashValue(temp, FILE_HASH_SIZE); for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, temp) == 0) { return mh->handle; } } return 0; } if (name[0] == '*') { // don't create a bad model for a bsp model if (Q_stricmp(name, "*default.gla")) { return 0; } } /* Ghoul2 Insert End */ // allocate a new model_t if ( ( mod = R_AllocModel() ) == NULL ) { Com_Printf (S_COLOR_YELLOW "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); int iLODStart = 0; if (strstr (name, ".md3")) { iLODStart = MD3_MAX_LODS-1; // this loads the md3s in reverse so they can be biased } mod->numLods = 0; // // load the files // numLoaded = 0; for ( lod = iLODStart; lod >= 0 ; lod-- ) { char filename[1024]; strcpy( filename, name ); if ( lod != 0 ) { char namebuf[80]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } qboolean bAlreadyCached = qfalse; if (!RE_RegisterModels_GetDiskFile(filename, (void **)&buf, &bAlreadyCached)) { continue; } //loadmodel = mod; // this seems to be fairly pointless // important that from now on we pass 'filename' instead of 'name' to all model load functions, // because 'filename' accounts for any LOD mangling etc so guarantees unique lookups for yet more // internal caching... // ident = *(unsigned *)buf; if (!bAlreadyCached) { LL(ident); } switch (ident) { // if you add any new types of model load in this switch-case, tell me, // or copy what I've done with the cache scheme (-ste). // case MDXA_IDENT: loaded = R_LoadMDXA( mod, buf, filename, bAlreadyCached ); break; case MDXM_IDENT: loaded = R_LoadMDXM( mod, buf, filename, bAlreadyCached ); break; case MD3_IDENT: loaded = R_LoadMD3( mod, lod, buf, filename, bAlreadyCached ); break; default: Com_Printf (S_COLOR_YELLOW"RE_RegisterModel: unknown fileid for %s\n", filename); goto fail; } if (!bAlreadyCached){ // important to check!! ri->FS_FreeFile (buf); } if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { break; } } else { mod->numLods++; numLoaded++; // if we have a valid model and are biased // so that we won't see any higher detail ones, // stop loading them if ( lod <= r_lodbias->integer ) { break; } } } if ( numLoaded ) { // duplicate into higher lod spots that weren't // loaded, in case the user changes r_lodbias on the fly for ( lod-- ; lod >= 0 ; lod-- ) { mod->numLods++; mod->md3[lod] = mod->md3[lod+1]; } /* Ghoul2 Insert Start */ #ifdef _DEBUG if (r_noPrecacheGLA && r_noPrecacheGLA->integer && ident == MDXA_IDENT) { //I expect this will cause leaks, but I don't care because it's a debugging utility. return mod->index; } #endif RE_InsertModelIntoHash(name, mod); return mod->index; /* Ghoul2 Insert End */ } #ifdef _DEBUG else { Com_Printf (S_COLOR_YELLOW"RE_RegisterModel: couldn't load %s\n", name); } #endif fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; RE_InsertModelIntoHash(name, mod); return 0; }
/* ==================== RE_RegisterServerModel Same as RE_RegisterModel, except used by the server to handle ghoul2 instance models. ==================== */ qhandle_t RE_RegisterServerModel( const char *name ) { model_t *mod; unsigned *buf; int lod; int ident; qboolean loaded; // qhandle_t hModel; int numLoaded; /* Ghoul2 Insert Start */ int hash; modelHash_t *mh; /* Ghoul2 Insert End */ if (!r_noServerGhoul2) { //keep it from choking when it gets to these checks in the g2 code. Registering all r_ cvars for the server would be a Bad Thing though. r_noServerGhoul2 = ri->Cvar_Get( "r_noserverghoul2", "0", 0); } if ( !name || !name[0] ) { return 0; } if ( strlen( name ) >= MAX_QPATH ) { return 0; } hash = generateHashValue(name, FILE_HASH_SIZE); // // see if the model is already loaded // for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, name) == 0) { return mh->handle; } } if ( ( mod = R_AllocModel() ) == NULL ) { return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); // make sure the render thread is stopped R_IssuePendingRenderCommands(); int iLODStart = 0; if (strstr (name, ".md3")) { iLODStart = MD3_MAX_LODS-1; // this loads the md3s in reverse so they can be biased } mod->numLods = 0; // // load the files // numLoaded = 0; for ( lod = iLODStart; lod >= 0 ; lod-- ) { char filename[1024]; strcpy( filename, name ); if ( lod != 0 ) { char namebuf[80]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } qboolean bAlreadyCached = qfalse; if (!RE_RegisterModels_GetDiskFile(filename, (void **)&buf, &bAlreadyCached)) { continue; } //loadmodel = mod; // this seems to be fairly pointless // important that from now on we pass 'filename' instead of 'name' to all model load functions, // because 'filename' accounts for any LOD mangling etc so guarantees unique lookups for yet more // internal caching... // ident = *(unsigned *)buf; if (!bAlreadyCached) { LL(ident); } switch (ident) { //if you're trying to register anything else as a model type on the server, you are out of luck case MDXA_IDENT: loaded = ServerLoadMDXA( mod, buf, filename, bAlreadyCached ); break; case MDXM_IDENT: loaded = ServerLoadMDXM( mod, buf, filename, bAlreadyCached ); break; default: goto fail; } if (!bAlreadyCached){ // important to check!! ri->FS_FreeFile (buf); } if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { break; } } else { mod->numLods++; numLoaded++; } } if ( numLoaded ) { // duplicate into higher lod spots that weren't // loaded, in case the user changes r_lodbias on the fly for ( lod-- ; lod >= 0 ; lod-- ) { mod->numLods++; mod->md3[lod] = mod->md3[lod+1]; } /* Ghoul2 Insert Start */ RE_InsertModelIntoHash(name, mod); return mod->index; /* Ghoul2 Insert End */ } fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; RE_InsertModelIntoHash(name, mod); return 0; }