/* * R_StartCinematic */ unsigned int R_StartCinematic( const char *arg ) { char uploadName[128]; size_t name_size; r_cinhandle_t *handle, *hnode, *next; struct cinematics_s *cin; qboolean yuv; // find cinematics with the same name hnode = &r_cinematics_headnode; for( handle = hnode->prev; handle != hnode; handle = next ) { next = handle->prev; assert( handle->cin ); // reuse if( !Q_stricmp( handle->name, arg ) ) return handle->id; } // open the file, read header, etc cin = ri.CIN_Open( arg, ri.Sys_Milliseconds(), qtrue, &yuv, NULL ); // take a free cinematic handle if possible if( !r_free_cinematics || !cin ) return 0; handle = r_free_cinematics; r_free_cinematics = handle->next; // copy name name_size = strlen( arg ) + 1; handle->name = R_Malloc( name_size ); memcpy( handle->name, arg, name_size ); // copy upload name Q_snprintfz( uploadName, sizeof( uploadName ), "***r_cinematic%i***", handle->id-1 ); name_size = strlen( uploadName ) + 1; handle->uploadName = R_Malloc( name_size ); memcpy( handle->uploadName, uploadName, name_size ); handle->cin = cin; handle->new_frame = qfalse; handle->yuv = yuv; handle->image = NULL; handle->yuv_images[0] = handle->yuv_images[1] = handle->yuv_images[2] = NULL; handle->registrationSequence = rsh.registrationSequence; handle->pic = NULL; handle->cyuv = NULL; // put handle at the start of the list handle->prev = &r_cinematics_headnode; handle->next = r_cinematics_headnode.next; handle->next->prev = handle; handle->prev->next = handle; return handle->id; }
// if return == true, no further action needed by the caller... // void *RE_RegisterModels_Malloc(int iSize, void *pvDiskBufferIfJustLoaded, const char *psModelFileName, qboolean *pqbAlreadyFound, memtag_t eTag) { char sModelName[MAX_QPATH]; Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName)); Q_strlwr (sModelName); CachedEndianedModelBinary_t &ModelBin = (*CachedModels)[sModelName]; if (ModelBin.pModelDiskImage == NULL) { // ... then this entry has only just been created, ie we need to load it fully... // // new, instead of doing a R_Malloc and assigning that we just morph the disk buffer alloc // then don't thrown it away on return - cuts down on mem overhead // // ... groan, but not if doing a limb hierarchy creation (some VV stuff?), in which case it's NULL // if ( pvDiskBufferIfJustLoaded ) { R_MorphMallocTag( pvDiskBufferIfJustLoaded, eTag ); } else { pvDiskBufferIfJustLoaded = R_Malloc(iSize,eTag, qfalse ); } ModelBin.pModelDiskImage= pvDiskBufferIfJustLoaded; ModelBin.iAllocSize = iSize; *pqbAlreadyFound = qfalse; } else { // if we already had this model entry, then re-register all the shaders it wanted... // const int iEntries = ModelBin.ShaderRegisterData.size(); for (int i=0; i<iEntries; i++) { int iShaderNameOffset = ModelBin.ShaderRegisterData[i].first; int iShaderPokeOffset = ModelBin.ShaderRegisterData[i].second; const char *const psShaderName = &((char*)ModelBin.pModelDiskImage)[iShaderNameOffset]; int *const piShaderPokePtr= (int *) &((char*)ModelBin.pModelDiskImage)[iShaderPokeOffset]; shader_t *sh = R_FindShader( psShaderName, lightmapsNone, stylesDefault, qtrue ); if ( sh->defaultShader ) { *piShaderPokePtr = 0; } else { *piShaderPokePtr = sh->index; } } *pqbAlreadyFound = qtrue; // tell caller not to re-Endian or re-Shader this binary } ModelBin.iLastLevelUsedOn = RE_RegisterMedia_GetLevel(); return ModelBin.pModelDiskImage; }
/* * R_VBOVertBuffer */ static void *R_VBOVertBuffer( unsigned numVerts, size_t vertSize ) { size_t size = numVerts * vertSize; if( size > r_vbo_tempvsoupsize ) { if( r_vbo_tempvsoup ) R_Free( r_vbo_tempvsoup ); r_vbo_tempvsoupsize = size; r_vbo_tempvsoup = ( float * )R_Malloc( size ); } return r_vbo_tempvsoup; }
/* * R_VBOElemBuffer */ static elem_t *R_VBOElemBuffer( unsigned numElems ) { if( numElems > r_vbo_numtempelems ) { if( r_vbo_numtempelems ) R_Free( r_vbo_tempelems ); r_vbo_numtempelems = numElems; r_vbo_tempelems = ( elem_t * )R_Malloc( sizeof( *r_vbo_tempelems ) * numElems ); } return r_vbo_tempelems; }
void RE_SaveJPG(const char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding) { byte *out; size_t bufSize; bufSize = image_width * image_height * 3; out = (byte *) R_Malloc( bufSize, TAG_TEMP_WORKSPACE, qfalse ); bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding, false); ri.FS_WriteFile(filename, out, bufSize); R_Free(out); }
/* ================ R_MipMap2 Uses temp mem, but then copies back to input, quartering the size of the texture Proper linear filter ================ */ static void R_MipMap2( unsigned *in, int inWidth, int inHeight ) { int i, j, k; byte *outpix; int inWidthMask, inHeightMask; int total; int outWidth, outHeight; unsigned *temp; outWidth = inWidth >> 1; outHeight = inHeight >> 1; temp = (unsigned int *) R_Malloc( outWidth * outHeight * 4, TAG_TEMP_WORKSPACE, qfalse ); inWidthMask = inWidth - 1; inHeightMask = inHeight - 1; for ( i = 0 ; i < outHeight ; i++ ) { for ( j = 0 ; j < outWidth ; j++ ) { outpix = (byte *) ( temp + i * outWidth + j ); for ( k = 0 ; k < 4 ; k++ ) { total = 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k]; outpix[k] = total / 36; } } } memcpy( in, temp, outWidth * outHeight * 4 ); R_Free( temp ); }
/* * R_InitCinematics */ void R_InitCinematics( void ) { int i; r_cinematics = R_Malloc( sizeof( r_cinhandle_t ) * MAX_CINEMATICS ); memset( r_cinematics, 0, sizeof( r_cinhandle_t ) * MAX_CINEMATICS ); // link cinemtics r_free_cinematics = r_cinematics; r_cinematics_headnode.id = 0; r_cinematics_headnode.prev = &r_cinematics_headnode; r_cinematics_headnode.next = &r_cinematics_headnode; for( i = 0; i < MAX_CINEMATICS - 1; i++ ) { if( i < MAX_CINEMATICS - 1 ) { r_cinematics[i].next = &r_cinematics[i + 1]; } r_cinematics[i].id = i + 1; } }
/* * R_SkinFileForName */ static skinfile_t *R_SkinFileForName( const char *name ) { char filename[MAX_QPATH]; int i; char *buffer; skinfile_t *skinfile; Q_strncpyz( filename, name, sizeof( filename ) ); COM_DefaultExtension( filename, ".skin", sizeof( filename ) ); for( i = 0, skinfile = r_skinfiles; i < r_numskinfiles; i++, skinfile++ ) { if( !skinfile->name ) { break; // free slot } if( !Q_stricmp( skinfile->name, filename ) ) { return skinfile; } } if( i == MAX_SKINFILES ) { Com_Printf( S_COLOR_YELLOW "R_SkinFile_Load: Skin files limit exceeded\n" ); return NULL; } if( R_LoadFile( filename, (void **)&buffer ) == -1 ) { ri.Com_DPrintf( S_COLOR_YELLOW "R_SkinFile_Load: Failed to load %s\n", name ); return NULL; } r_numskinfiles++; skinfile = &r_skinfiles[i]; skinfile->name = R_CopyString( filename ); skinfile->numpairs = SkinFile_ParseBuffer( buffer, NULL ); if( skinfile->numpairs ) { skinfile->pairs = R_Malloc( skinfile->numpairs * sizeof( mesh_shader_pair_t ) ); SkinFile_ParseBuffer( buffer, skinfile->pairs ); } else { ri.Com_DPrintf( S_COLOR_YELLOW "R_SkinFile_Load: no mesh/shader pairs in %s\n", name ); } R_FreeFile( (void *)buffer ); return skinfile; }
/* * R_ReserveDrawSurfaces */ static void R_ReserveDrawSurfaces( drawList_t *list, int minMeshes ) { int oldSize, newSize; sortedDrawSurf_t *newDs; sortedDrawSurf_t *ds = list->drawSurfs; int maxMeshes = list->maxDrawSurfs; oldSize = maxMeshes; newSize = max( minMeshes, oldSize * 2 ); newDs = R_Malloc( newSize * sizeof( sortedDrawSurf_t ) ); if( ds ) { memcpy( newDs, ds, oldSize * sizeof( sortedDrawSurf_t ) ); R_Free( ds ); } list->drawSurfs = newDs; list->maxDrawSurfs = newSize; }
/* * R_AddDebugBounds */ void R_AddDebugBounds( const vec3_t mins, const vec3_t maxs, const byte_vec4_t color ) { unsigned i; i = r_num_debug_bounds; r_num_debug_bounds++; if( r_num_debug_bounds > r_debug_bounds_current_size ) { r_debug_bounds_current_size = ALIGN( r_num_debug_bounds, 256 ); if( r_debug_bounds ) r_debug_bounds = R_Realloc( r_debug_bounds, r_debug_bounds_current_size * sizeof( r_debug_bound_t ) ); else r_debug_bounds = R_Malloc( r_debug_bounds_current_size * sizeof( r_debug_bound_t ) ); } VectorCopy( mins, r_debug_bounds[i].mins ); VectorCopy( maxs, r_debug_bounds[i].maxs ); Vector4Copy( color, r_debug_bounds[i].color ); }
// I added this function for development purposes (and it's VM-safe) so we don't have problems // with our use of cached models but uncached animation.cfg files (so frame sequences are out of sync // if someone rebuild the model while you're ingame and you change levels)... // // Usage: call with psDest == NULL for a size enquire (for malloc), // then with NZ ptr for it to copy to your supplied buffer... // int RE_GetAnimationCFG(const char *psCFGFilename, char *psDest, int iDestSize) { char *psText = NULL; AnimationCFGs_t::iterator it = AnimationCFGs.find(psCFGFilename); if (it != AnimationCFGs.end()) { psText = (*it).second; } else { // not found, so load it... // fileHandle_t f; int iLen = ri.FS_FOpenFileRead( psCFGFilename, &f, qfalse ); if (iLen <= 0) { return 0; } psText = (char *) R_Malloc( iLen+1, TAG_ANIMATION_CFG, qfalse ); ri.FS_Read( psText, iLen, f ); psText[iLen] = '\0'; ri.FS_FCloseFile( f ); AnimationCFGs[psCFGFilename] = psText; } if (psText) // sanity, but should always be NZ { if (psDest) { Q_strncpyz(psDest,psText,iDestSize); } return strlen(psText); } return 0; }
// returns qtrue if loaded, and sets the supplied qbool to true if it was from cache (instead of disk) // (which we need to know to avoid LittleLong()ing everything again (well, the Mac needs to know anyway)... // qboolean RE_RegisterModels_GetDiskFile( const char *psModelFileName, void **ppvBuffer, qboolean *pqbAlreadyCached) { char sModelName[MAX_QPATH]; Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName)); Q_strlwr (sModelName); CachedEndianedModelBinary_t &ModelBin = (*CachedModels)[sModelName]; if (ModelBin.pModelDiskImage == NULL) { // didn't have it cached, so try the disk... // // special case intercept first... // if (!strcmp(sDEFAULT_GLA_NAME ".gla" , psModelFileName)) { // return fake params as though it was found on disk... // void *pvFakeGLAFile = R_Malloc( sizeof(FakeGLAFile), TAG_FILESYS, qfalse ); memcpy(pvFakeGLAFile, &FakeGLAFile[0], sizeof(FakeGLAFile)); *ppvBuffer = pvFakeGLAFile; *pqbAlreadyCached = qfalse; // faking it like this should mean that it works fine on the Mac as well return qtrue; } ri.FS_ReadFile( sModelName, ppvBuffer ); *pqbAlreadyCached = qfalse; return (qboolean)(*ppvBuffer != 0); } else { *ppvBuffer = ModelBin.pModelDiskImage; *pqbAlreadyCached = qtrue; return qtrue; } }
/* ================ IMG_Load ================ */ void IMG_Load(image_t *image, byte *pic) { int i, c, b; int width, height; width = image->upload_width; height = image->upload_height; if (image->flags & IF_TURBULENT) { image->width = TURB_SIZE; image->height = TURB_SIZE; } b = image->width * image->height; c = width * height; if (image->type == IT_WALL) { image->pixels[0] = R_Malloc(MIPSIZE(b) * TEX_BYTES); image->pixels[1] = image->pixels[0] + b * TEX_BYTES; image->pixels[2] = image->pixels[1] + b * TEX_BYTES / 4; image->pixels[3] = image->pixels[2] + b * TEX_BYTES / 16; if (!(r_config.flags & QVF_GAMMARAMP)) R_LightScaleTexture(pic, width, height); if (width == image->width && height == image->height) { memcpy(image->pixels[0], pic, width * height * TEX_BYTES); } else { IMG_ResampleTexture(pic, width, height, image->pixels[0], image->width, image->height); image->upload_width = image->width; image->upload_height = image->height; } IMG_MipMap(image->pixels[1], image->pixels[0], image->width >> 0, image->height >> 0); IMG_MipMap(image->pixels[2], image->pixels[1], image->width >> 1, image->height >> 1); IMG_MipMap(image->pixels[3], image->pixels[2], image->width >> 2, image->height >> 2); Z_Free(pic); } else {
/* =============== ParseTriSurf =============== */ static void ParseTriSurf( dsurface_t *ds, mapVert_t *verts, msurface_t *surf, int *indexes, world_t &worldData, int index ) { srfTriangles_t *tri; int i, j, k; int numVerts, numIndexes; // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; if (index && !surf->fogIndex && tr.world && tr.world->globalFog != -1) { surf->fogIndex = worldData.globalFog; } // get shader surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapsVertex, ds->lightmapStyles, ds->vertexStyles, worldData ); if ( r_singleShader->integer && !surf->shader->sky ) { surf->shader = tr.defaultShader; } numVerts = LittleLong( ds->numVerts ); numIndexes = LittleLong( ds->numIndexes ); if ( numVerts >= SHADER_MAX_VERTEXES ) { Com_Error(ERR_DROP, "ParseTriSurf: verts > MAX (%d > %d) on misc_model %s", numVerts, SHADER_MAX_VERTEXES, surf->shader->name ); } if ( numIndexes >= SHADER_MAX_INDEXES ) { Com_Error(ERR_DROP, "ParseTriSurf: indices > MAX (%d > %d) on misc_model %s", numIndexes, SHADER_MAX_INDEXES, surf->shader->name ); } tri = (srfTriangles_t *) R_Malloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) + numIndexes * sizeof( tri->indexes[0] ), TAG_HUNKMISCMODELS, qfalse ); tri->dlightBits = 0; //JIC tri->surfaceType = SF_TRIANGLES; tri->numVerts = numVerts; tri->numIndexes = numIndexes; tri->verts = (drawVert_t *)(tri + 1); tri->indexes = (int *)(tri->verts + tri->numVerts ); surf->data = (surfaceType_t *)tri; // copy vertexes verts += LittleLong( ds->firstVert ); ClearBounds( tri->bounds[0], tri->bounds[1] ); for ( i = 0 ; i < numVerts ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] ); } AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); for ( j = 0 ; j < 2 ; j++ ) { tri->verts[i].st[j] = LittleFloat( verts[i].st[j] ); for(k=0;k<MAXLIGHTMAPS;k++) { tri->verts[i].lightmap[k][j] = LittleFloat( verts[i].lightmap[k][j] ); } } for(k=0;k<MAXLIGHTMAPS;k++) { R_ColorShiftLightingBytes( verts[i].color[k], tri->verts[i].color[k] ); } } // copy indexes indexes += LittleLong( ds->firstIndex ); for ( i = 0 ; i < numIndexes ; i++ ) { tri->indexes[i] = LittleLong( indexes[i] ); if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { Com_Error( ERR_DROP, "Bad index in triangle surface" ); } } }
void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height ) { /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo = { NULL }; /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ /* This struct represents a JPEG error handler. It is declared separately * because applications often want to supply a specialized error handler * (see the second half of this file for an example). But here we just * take the easy way out and use the standard error handler, which will * print a message on stderr and call exit() if compression fails. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ struct jpeg_error_mgr jerr; /* More stuff */ JSAMPARRAY buffer; /* Output row buffer */ unsigned int row_stride; /* physical row width in output buffer */ unsigned int pixelcount, memcount; unsigned int sindex, dindex; byte *out; union { byte *b; void *v; } fbuffer; byte *buf; /* In this example we want to open the input file before doing anything else, * so that the setjmp() error recovery below can assume the file is open. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to read binary files. */ int len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); if (!fbuffer.b || len < 0) { return; } /* Step 1: allocate and initialize JPEG decompression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error(&jerr); cinfo.err->error_exit = R_JPGErrorExit; cinfo.err->output_message = R_JPGOutputMessage; /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress(&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_mem_src(&cinfo, fbuffer.b, len); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header(&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* Make sure it always converts images to RGB color space. This will * automatically convert 8-bit greyscale images to RGB as well. */ cinfo.out_color_space = JCS_RGB; /* Step 5: Start decompressor */ (void) jpeg_start_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* JSAMPLEs per row in output buffer */ pixelcount = cinfo.output_width * cinfo.output_height; if(!cinfo.output_width || !cinfo.output_height || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3 ) { // Free the memory to make sure we don't leak memory ri.FS_FreeFile (fbuffer.v); jpeg_destroy_decompress(&cinfo); ri.Printf( PRINT_ALL, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename, cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components); return; } memcount = pixelcount * 4; row_stride = cinfo.output_width * cinfo.output_components; out = (byte *)R_Malloc(memcount, TAG_TEMP_WORKSPACE, qfalse); *width = cinfo.output_width; *height = cinfo.output_height; /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ buf = ((out+(row_stride*cinfo.output_scanline))); buffer = &buf; (void) jpeg_read_scanlines(&cinfo, buffer, 1); } buf = out; // Expand from RGB to RGBA sindex = pixelcount * cinfo.output_components; dindex = memcount; do { buf[--dindex] = 255; buf[--dindex] = buf[--sindex]; buf[--dindex] = buf[--sindex]; buf[--dindex] = buf[--sindex]; } while(sindex); *pic = out; /* Step 7: Finish decompression */ (void) jpeg_finish_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(&cinfo); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ ri.FS_FreeFile (fbuffer.v); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). */ /* And we're done! */ }
/* * R_StartCinematic */ unsigned int R_StartCinematic( const char *arg ) { char uploadName[128]; size_t name_size; char *name; r_cinhandle_t *handle, *hnode, *next; struct cinematics_s *cin; bool yuv; name_size = strlen( "video/" ) + strlen( arg ) + 1; name = alloca( name_size ); if( strstr( arg, "/" ) == NULL && strstr( arg, "\\" ) == NULL ) { Q_snprintfz( name, name_size, "video/%s", arg ); } else { Q_snprintfz( name, name_size, "%s", arg ); } // find cinematics with the same name hnode = &r_cinematics_headnode; for( handle = hnode->prev; handle != hnode; handle = next ) { next = handle->prev; assert( handle->cin ); // reuse if( !Q_stricmp( handle->name, name ) ) { return handle->id; } } // open the file, read header, etc cin = ri.CIN_Open( name, ri.Sys_Milliseconds(), &yuv, NULL ); // take a free cinematic handle if possible if( !r_free_cinematics || !cin ) { return 0; } handle = r_free_cinematics; r_free_cinematics = handle->next; // copy name handle->name = R_CopyString( name ); // copy upload name Q_snprintfz( uploadName, sizeof( uploadName ), "***r_cinematic%i***", handle->id - 1 ); name_size = strlen( uploadName ) + 1; handle->uploadName = R_Malloc( name_size ); memcpy( handle->uploadName, uploadName, name_size ); handle->cin = cin; handle->new_frame = false; handle->yuv = yuv; handle->image = NULL; handle->yuv_images[0] = handle->yuv_images[1] = handle->yuv_images[2] = NULL; handle->registrationSequence = rsh.registrationSequence; handle->pic = NULL; handle->cyuv = NULL; handle->lock = ri.Mutex_Create(); // put handle at the start of the list handle->prev = &r_cinematics_headnode; handle->next = r_cinematics_headnode.next; handle->next->prev = handle; handle->prev->next = handle; return handle->id; }