/* * CG_CreateBonesTreeNode * Find out the original tree */ static bonenode_t *CG_CreateBonesTreeNode( cgs_skeleton_t *skel, int bone ) { int i, count; int children[SKM_MAX_BONES]; bonenode_t *bonenode; bonenode = CG_Malloc( sizeof( bonenode_t ) ); bonenode->bonenum = bone; if( bone != -1 ) skel->bones[bone].node = bonenode; // store a pointer in the linear array for fast first access. // find childs of this bone count = 0; for( i = 0; i < skel->numBones; i++ ) { if( skel->bones[i].parent == bone ) { children[count] = i; count++; } } bonenode->numbonechildren = count; if( bonenode->numbonechildren ) { bonenode->bonechildren = CG_Malloc( sizeof( bonenode_t * ) * bonenode->numbonechildren ); for( i = 0; i < bonenode->numbonechildren; i++ ) { bonenode->bonechildren[i] = CG_CreateBonesTreeNode( skel, children[i] ); } } return bonenode; }
/* * CG_DemocamInit */ void CG_DemocamInit( void ) { int name_size; bool hassoundstream = false; democam_editing_mode = false; demo_initial_timestamp = 0; if( !cgs.demoPlaying ) return; if( !*cgs.demoName ) CG_Error( "CG_LoadRecamScriptFile: no demo name string\n" ); // see if there is any script for this demo, and load it name_size = sizeof( char ) * ( strlen( cgs.demoName ) + strlen( ".cam" ) + 1 ); demoscriptname = ( char * )CG_Malloc( name_size ); Q_snprintfz( demoscriptname, name_size, "%s", cgs.demoName ); COM_ReplaceExtension( demoscriptname, ".cam", name_size ); CG_Printf( "cam: %s\n", demoscriptname ); // add console commands trap_Cmd_AddCommand( "demoEditMode", CG_DemoEditMode_Cmd_f ); trap_Cmd_AddCommand( "demoFreeFly", CG_DemoFreeFly_Cmd_f ); trap_Cmd_AddCommand( "camswitch", CG_CamSwitch_Cmd_f ); if( CG_LoadRecamScriptFile( demoscriptname ) ) { CG_Printf( "Loaded demo cam script\n" ); } // check for a sound stream file cgs.demoAudioStream = ( char * )CG_Malloc( name_size ); Q_snprintfz( cgs.demoAudioStream, name_size, "%s", cgs.demoName ); COM_ReplaceExtension( cgs.demoAudioStream, ".wav", name_size ); if( trap_FS_FOpenFile( cgs.demoAudioStream, NULL, FS_READ ) != -1 ) { hassoundstream = true; } else { COM_ReplaceExtension( cgs.demoAudioStream, ".ogg", name_size ); if( trap_FS_FOpenFile( cgs.demoAudioStream, NULL, FS_READ ) != -1 ) { hassoundstream = true; } } if( !hassoundstream ) { CG_Free( cgs.demoAudioStream ); cgs.demoAudioStream = NULL; } }
/* * CG_Democam_RegisterCam */ static cg_democam_t *CG_Democam_RegisterCam( int type ) { cg_democam_t *cam; CG_DemoCam_UpdateDemoTime(); cam = cg_cams_headnode; while( cam != NULL ) { if( cam->timeStamp == demo_time ) { // a cam exists with the very same timestamp CG_Printf( "warning: There was a cam with the same timestamp, it's being replaced\n" ); break; } cam = cam->next; } if( cam == NULL ) { cam = ( cg_democam_t * )CG_Malloc( sizeof( cg_democam_t ) ); cam->next = cg_cams_headnode; cg_cams_headnode = cam; } cam->timeStamp = demo_time; cam->type = type; VectorCopy( cam_origin, cam->origin ); VectorCopy( cam_angles, cam->angles ); if( type == DEMOCAM_ORBITAL ) { // in orbital cams, the angles are the angular velocity VectorSet( cam->angles, 0, 96, 0 ); } if( type == DEMOCAM_FIRSTPERSON || type == DEMOCAM_THIRDPERSON ) { cam->fov = 0; } else { cam->fov = 90; } return cam; }
/* * CG_Democam_ImportCams_f */ void CG_Democam_ImportCams_f( void ) { int name_size; char *customName; if( trap_Cmd_Argc() < 2 ) { CG_Printf( "Usage: importcams <filename> (relative to demos directory)\n" ); return; } // see if there is any script for this demo, and load it name_size = sizeof( char ) * ( strlen( "demos/" ) + strlen( trap_Cmd_Argv( 1 ) ) + strlen( ".cam" ) + 1 ); customName = ( char * )CG_Malloc( name_size ); Q_snprintfz( customName, name_size, "demos/%s", trap_Cmd_Argv( 1 ) ); COM_ReplaceExtension( customName, ".cam", name_size ); if( CG_LoadRecamScriptFile( customName ) ) { CG_Printf( "cam script imported\n" ); } else { CG_Printf( "CG_Democam_ImportCams_f: no valid file found\n" ); } }
/* * CG_Democam_RegisterSubtitle */ static cg_subtitle_t *CG_Democam_RegisterSubtitle( void ) { cg_subtitle_t *sub; CG_DemoCam_UpdateDemoTime(); sub = cg_subs_headnode; while( sub != NULL ) { if( sub->timeStamp == demo_time ) { // a subtitle exists with the very same timestamp CG_Printf( "warning: There was a subtitle with the same timestamp, it's being replaced\n" ); break; } sub = sub->next; } if( sub == NULL ) { sub = ( cg_subtitle_t * )CG_Malloc( sizeof( cg_subtitle_t ) ); sub->next = cg_subs_headnode; cg_subs_headnode = sub; } sub->timeStamp = demo_time; sub->maxDuration = DEFAULT_SUBTITLE_SECONDS * 1000; sub->highprint = false; return sub; }
/* * CG_AsyncGetRequest */ int CG_AsyncGetRequest( const char *resource, void (*done_cb)(int status, const char *resp), void *privatep ) { char url[1024]; cg_asyncrequest_t *req; trap_GetBaseServerURL( url, sizeof( url ) ); Q_strncatz( url, resource, sizeof( url ) ); req = ( cg_asyncrequest_t * )CG_Malloc( sizeof( *req ) ); req->buf_size = 1; req->buf = ( char * )CG_Malloc( 1 ); *req->buf = '\0'; req->privatep = privatep; req->done_cb = done_cb; return trap_AsyncStream_PerformRequest( url, "GET", "", 10, CG_AsyncGetRequest_ReadCb, CG_AsyncGetRequest_DoneCb, (void *)req ); }
/* * CG_SkeletonForModel */ cgs_skeleton_t *CG_SkeletonForModel( struct model_s *model ) { int i, j; cgs_skeleton_t *skel; qbyte *buffer; cgs_bone_t *bone; bonepose_t *bonePose; int numBones, numFrames; if( !model ) return NULL; numBones = trap_R_SkeletalGetNumBones( model, &numFrames ); if( !numBones || !numFrames ) return NULL; // no bones or frames for( skel = skel_headnode; skel; skel = skel->next ) { if( skel->model == model ) return skel; } // allocate one huge array to hold our data buffer = CG_Malloc( sizeof( cgs_skeleton_t ) + numBones * sizeof( cgs_bone_t ) + numFrames * ( sizeof( bonepose_t * ) + numBones * sizeof( bonepose_t ) ) ); skel = ( cgs_skeleton_t * )buffer; buffer += sizeof( cgs_skeleton_t ); skel->bones = ( cgs_bone_t * )buffer; buffer += numBones * sizeof( cgs_bone_t ); skel->numBones = numBones; skel->bonePoses = ( bonepose_t ** )buffer; buffer += numFrames * sizeof( bonepose_t * ); skel->numFrames = numFrames; // register bones for( i = 0, bone = skel->bones; i < numBones; i++, bone++ ) bone->parent = trap_R_SkeletalGetBoneInfo( model, i, bone->name, sizeof( bone->name ), &bone->flags ); // register poses for all frames for all bones for( i = 0; i < numFrames; i++ ) { skel->bonePoses[i] = ( bonepose_t * )buffer; buffer += numBones * sizeof( bonepose_t ); for( j = 0, bonePose = skel->bonePoses[i]; j < numBones; j++, bonePose++ ) trap_R_SkeletalGetBonePose( model, j, i, bonePose ); } skel->next = skel_headnode; skel_headnode = skel; skel->model = model; // create a bones tree that can be run from parent to children skel->bonetree = CG_CreateBonesTreeNode( skel, -1 ); #ifdef SKEL_PRINTBONETREE CG_PrintBoneTree( skel, skel->bonetree, 1 ); #endif return skel; }
/* * CG_ExpandTemporaryBoneposesCache */ static void CG_ExpandTemporaryBoneposesCache( int num ) { bonepose_t *temp; temp = TBC; TBC = CG_Malloc( sizeof( bonepose_t ) * ( TBC_Size + max( num, TBC_Block_Size ) ) ); memcpy( TBC, temp, sizeof( bonepose_t ) * TBC_Size ); TBC_Size += max( num, TBC_Block_Size ); CG_Free( temp ); }
/* * CG_PModel_SpawnTeleportEffect */ void CG_PModel_SpawnTeleportEffect( centity_t *cent ) { int j; cgs_skeleton_t *skel; lentity_t *le; vec3_t teleportOrigin; vec3_t rgb; skel = CG_SkeletonForModel( cent->ent.model ); if( !skel || !cent->ent.boneposes ) return; for( j = LOCALEFFECT_EV_PLAYER_TELEPORT_IN; j <= LOCALEFFECT_EV_PLAYER_TELEPORT_OUT; j++ ) { if( cent->localEffects[j] ) { cent->localEffects[j] = 0; VectorSet( rgb, 0.5, 0.5, 0.5 ); if( j == LOCALEFFECT_EV_PLAYER_TELEPORT_OUT ) { VectorCopy( cent->teleportedFrom, teleportOrigin ); } else { VectorCopy( cent->teleportedTo, teleportOrigin ); if( ISVIEWERENTITY( cent->current.number ) ) { VectorSet( rgb, 0.1, 0.1, 0.1 ); } } // spawn a dummy model le = CG_AllocModel( LE_RGB_FADE, teleportOrigin, vec3_origin, 10, rgb[0], rgb[1], rgb[2], 1, 0, 0, 0, 0, cent->ent.model, CG_MediaShader( cgs.media.shaderTeleportShellGfx ) ); if( cent->skel ) { // use static bone pose, no animation le->skel = cent->skel; le->static_boneposes = ( bonepose_t * )CG_Malloc( sizeof( bonepose_t ) * le->skel->numBones ); memcpy( le->static_boneposes, cent->ent.boneposes, sizeof( bonepose_t ) * le->skel->numBones ); le->ent.boneposes = le->static_boneposes; le->ent.oldboneposes = le->ent.boneposes; } le->ent.frame = cent->ent.frame; le->ent.oldframe = cent->ent.oldframe; le->ent.backlerp = 1.0f; Matrix3_Copy( cent->ent.axis, le->ent.axis ); } } }
/* * CG_SaveCam_Cmd_f */ void CG_SaveCam_Cmd_f( void ) { if( !cgs.demoPlaying ) { return; } if( trap_Cmd_Argc() > 1 ) { char *customName; int custom_name_size; custom_name_size = sizeof( char ) * ( strlen( "demos/" ) + strlen( trap_Cmd_Argv( 1 ) ) + strlen( ".cam" ) + 1 ); customName = ( char * )CG_Malloc( custom_name_size ); Q_snprintfz( customName, custom_name_size, "demos/%s", trap_Cmd_Argv( 1 ) ); COM_ReplaceExtension( customName, ".cam", custom_name_size ); CG_SaveRecamScriptFile( customName ); CG_Free( customName ); return; } CG_SaveRecamScriptFile( demoscriptname ); }
/* * CG_RegisterMediaModel */ static cgs_media_handle_t *CG_RegisterMediaModel( const char *name, bool precache ) { cgs_media_handle_t *mediamodel; for( mediamodel = model_headnode; mediamodel; mediamodel = mediamodel->next ) { if( !Q_stricmp( mediamodel->name, name ) ) return mediamodel; } mediamodel = ( cgs_media_handle_t * )CG_Malloc( sizeof( cgs_media_handle_t ) ); mediamodel->name = CG_CopyString( name ); mediamodel->next = model_headnode; model_headnode = mediamodel; if( precache ) mediamodel->data = ( void * )CG_RegisterModel( mediamodel->name ); return mediamodel; }
/* * CG_RegisterMediaShader */ static cgs_media_handle_t *CG_RegisterMediaShader( const char *name, bool precache ) { cgs_media_handle_t *mediashader; for( mediashader = shader_headnode; mediashader; mediashader = mediashader->next ) { if( !Q_stricmp( mediashader->name, name ) ) return mediashader; } mediashader = ( cgs_media_handle_t * )CG_Malloc( sizeof( cgs_media_handle_t ) ); mediashader->name = CG_CopyString( name ); mediashader->next = shader_headnode; shader_headnode = mediashader; if( precache ) mediashader->data = ( void * )trap_R_RegisterPic( mediashader->name ); return mediashader; }
/* * CG_RegisterMediaSfx */ static cgs_media_handle_t *CG_RegisterMediaSfx( const char *name, bool precache ) { cgs_media_handle_t *mediasfx; for( mediasfx = sfx_headnode; mediasfx; mediasfx = mediasfx->next ) { if( !Q_stricmp( mediasfx->name, name ) ) return mediasfx; } mediasfx = ( cgs_media_handle_t * )CG_Malloc( sizeof( cgs_media_handle_t ) ); mediasfx->name = CG_CopyString( name ); mediasfx->next = sfx_headnode; sfx_headnode = mediasfx; if( precache ) mediasfx->data = ( void * )trap_S_RegisterSound( mediasfx->name ); return mediasfx; }
/* * CG_AsyncGetRequest_ReadCb */ static size_t CG_AsyncGetRequest_ReadCb( const void *buf, size_t numb, float percentage, int status, const char *contentType, void *privatep ) { char *newbuf; cg_asyncrequest_t *req = ( cg_asyncrequest_t * )privatep; if( status < 0 || status >= 300 ) { return 0; } newbuf = ( char * )CG_Malloc( req->buf_size + numb + 1 ); memcpy( newbuf, req->buf, req->buf_size - 1 ); memcpy( newbuf + req->buf_size - 1, buf, numb ); newbuf[numb] = '\0'; // EOF CG_Free( req->buf ); req->buf = newbuf; req->buf_size = req->buf_size + numb + 1; return numb; }
/* * CG_CS_UpdateTeamInfo */ static void CG_CS_UpdateTeamInfo( void ) { char *ti; ti = trap_Cmd_Argv( 1 ); if( !ti[0] ) { cg.teaminfo_size = 0; CG_Free( cg.teaminfo ); cg.teaminfo = NULL; return; } if( strlen( ti ) + 1 > cg.teaminfo_size ) { if( cg.teaminfo ) CG_Free( cg.teaminfo ); cg.teaminfo_size = strlen( ti ) + 1; cg.teaminfo = CG_Malloc( cg.teaminfo_size ); } Q_strncpyz( cg.teaminfo, ti, cg.teaminfo_size ); }
/* * CG_vWeap_ParseAnimationScript * * script: * 0 = first frame * 1 = lastframe/number of frames * 2 = looping frames * 3 = frame time * * keywords: * "islastframe":Will read the second value of each animation as lastframe (usually means numframes) * "rotationscale": value witch will scale the barrel rotation speed */ static bool CG_vWeap_ParseAnimationScript( weaponinfo_t *weaponinfo, char *filename ) { qbyte *buf; char *ptr, *token; int rounder, counter, i; bool debug = true; int anim_data[4][VWEAP_MAXANIMS]; int length, filenum; rounder = 0; counter = 1; // reserve 0 for 'no animation' // set some defaults weaponinfo->barrelSpeed = 0; weaponinfo->flashFade = true; if( !cg_debugWeaponModels->integer ) debug = false; // load the file length = trap_FS_FOpenFile( filename, &filenum, FS_READ ); if( length == -1 ) return false; if( !length ) { trap_FS_FCloseFile( filenum ); return false; } buf = ( qbyte * )CG_Malloc( length + 1 ); trap_FS_Read( buf, length, filenum ); trap_FS_FCloseFile( filenum ); if( !buf ) { CG_Free( buf ); return false; } if( debug ) CG_Printf( "%sLoading weapon animation script:%s%s\n", S_COLOR_BLUE, filename, S_COLOR_WHITE ); memset( anim_data, 0, sizeof( anim_data ) ); //proceed ptr = ( char * )buf; while( ptr ) { token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) break; //see if it is keyword or number if( *token < '0' || *token > '9' ) { if( !Q_stricmp( token, "barrel" ) ) { if( debug ) CG_Printf( "%sScript: barrel:%s", S_COLOR_BLUE, S_COLOR_WHITE ); // time i = atoi( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->barrelTime = (unsigned int)( i > 0 ? i : 0 ); // speed weaponinfo->barrelSpeed = atof( COM_ParseExt( &ptr, qfalse ) ); if( debug ) CG_Printf( "%s time:%i, speed:%.2f\n", S_COLOR_BLUE, (int)weaponinfo->barrelTime, weaponinfo->barrelSpeed, S_COLOR_WHITE ); } else if( !Q_stricmp( token, "flash" ) ) { if( debug ) CG_Printf( "%sScript: flash:%s", S_COLOR_BLUE, S_COLOR_WHITE ); // time i = atoi( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->flashTime = (unsigned int)( i > 0 ? i : 0 ); // radius i = atoi( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->flashRadius = (float)( i > 0 ? i : 0 ); // fade token = COM_ParseExt( &ptr, qfalse ); if( !Q_stricmp( token, "no" ) ) weaponinfo->flashFade = false; if( debug ) CG_Printf( "%s time:%i, radius:%i, fade:%s%s\n", S_COLOR_BLUE, (int)weaponinfo->flashTime, (int)weaponinfo->flashRadius, weaponinfo->flashFade ? "YES" : "NO", S_COLOR_WHITE ); } else if( !Q_stricmp( token, "flashColor" ) ) { if( debug ) CG_Printf( "%sScript: flashColor:%s", S_COLOR_BLUE, S_COLOR_WHITE ); weaponinfo->flashColor[0] = atof( token = COM_ParseExt( &ptr, qfalse ) ); weaponinfo->flashColor[1] = atof( token = COM_ParseExt( &ptr, qfalse ) ); weaponinfo->flashColor[2] = atof( token = COM_ParseExt( &ptr, qfalse ) ); if( debug ) CG_Printf( "%s%f %f %f%s\n", S_COLOR_BLUE, weaponinfo->flashColor[0], weaponinfo->flashColor[1], weaponinfo->flashColor[2], S_COLOR_WHITE ); } else if( !Q_stricmp( token, "handOffset" ) ) { if( debug ) CG_Printf( "%sScript: handPosition:%s", S_COLOR_BLUE, S_COLOR_WHITE ); weaponinfo->handpositionOrigin[FORWARD] = atof( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->handpositionOrigin[RIGHT] = atof( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->handpositionOrigin[UP] = atof( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->handpositionAngles[PITCH] = atof( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->handpositionAngles[YAW] = atof( COM_ParseExt( &ptr, qfalse ) ); weaponinfo->handpositionAngles[ROLL] = atof( COM_ParseExt( &ptr, qfalse ) ); if( debug ) CG_Printf( "%s%f %f %f %f %f %f%s\n", S_COLOR_BLUE, weaponinfo->handpositionOrigin[0], weaponinfo->handpositionOrigin[1], weaponinfo->handpositionOrigin[2], weaponinfo->handpositionAngles[0], weaponinfo->handpositionAngles[1], weaponinfo->handpositionAngles[2], S_COLOR_WHITE ); } else if( !Q_stricmp( token, "firesound" ) ) { if( debug ) CG_Printf( "%sScript: firesound:%s", S_COLOR_BLUE, S_COLOR_WHITE ); if( weaponinfo->num_fire_sounds >= WEAPONINFO_MAX_FIRE_SOUNDS ) { if( debug ) CG_Printf( S_COLOR_BLUE "too many firesounds defined. Max is %i" S_COLOR_WHITE "\n", WEAPONINFO_MAX_FIRE_SOUNDS ); break; } token = COM_ParseExt( &ptr, qfalse ); if( Q_stricmp( token, "NULL" ) ) { weaponinfo->sound_fire[weaponinfo->num_fire_sounds] = trap_S_RegisterSound( token ); if( weaponinfo->sound_fire[weaponinfo->num_fire_sounds] != NULL ) weaponinfo->num_fire_sounds++; } if( debug ) CG_Printf( "%s%s%s\n", S_COLOR_BLUE, token, S_COLOR_WHITE ); } else if( !Q_stricmp( token, "strongfiresound" ) ) { if( debug ) CG_Printf( "%sScript: strongfiresound:%s", S_COLOR_BLUE, S_COLOR_WHITE ); if( weaponinfo->num_strongfire_sounds >= WEAPONINFO_MAX_FIRE_SOUNDS ) { if( debug ) CG_Printf( S_COLOR_BLUE "too many strongfiresound defined. Max is %i" S_COLOR_WHITE "\n", WEAPONINFO_MAX_FIRE_SOUNDS ); break; } token = COM_ParseExt( &ptr, qfalse ); if( Q_stricmp( token, "NULL" ) ) { weaponinfo->sound_strongfire[weaponinfo->num_strongfire_sounds] = trap_S_RegisterSound( token ); if( weaponinfo->sound_strongfire[weaponinfo->num_strongfire_sounds] != NULL ) weaponinfo->num_strongfire_sounds++; } if( debug ) CG_Printf( "%s%s%s\n", S_COLOR_BLUE, token, S_COLOR_WHITE ); } else if( token[0] && debug ) CG_Printf( "%signored: %s%s\n", S_COLOR_YELLOW, token, S_COLOR_WHITE ); } else { //frame & animation values i = (int)atoi( token ); if( debug ) { if( rounder == 0 ) CG_Printf( "%sScript: %s", S_COLOR_BLUE, S_COLOR_WHITE ); CG_Printf( "%s%i - %s", S_COLOR_BLUE, i, S_COLOR_WHITE ); } anim_data[rounder][counter] = i; rounder++; if( rounder > 3 ) { rounder = 0; if( debug ) CG_Printf( "%s anim: %i%s\n", S_COLOR_BLUE, counter, S_COLOR_WHITE ); counter++; if( counter == VWEAP_MAXANIMS ) break; } } } CG_Free( buf ); if( counter < VWEAP_MAXANIMS ) { CG_Printf( "%sERROR: incomplete WEAPON script: %s - Using default%s\n", S_COLOR_YELLOW, filename, S_COLOR_WHITE ); return false; } //reorganize to make my life easier for( i = 0; i < VWEAP_MAXANIMS; i++ ) { weaponinfo->firstframe[i] = anim_data[0][i]; weaponinfo->lastframe[i] = anim_data[1][i]; weaponinfo->loopingframes[i] = anim_data[2][i]; if( anim_data[3][i] < 10 ) //never allow less than 10 fps anim_data[3][i] = 10; weaponinfo->frametime[i] = 1000/anim_data[3][i]; } return true; }
/* * CG_InitTemporaryBoneposesCache * allocate space for temporary boneposes */ void CG_InitTemporaryBoneposesCache( void ) { TBC_Size = TBC_Block_Size; TBC = CG_Malloc( sizeof( bonepose_t ) * TBC_Size ); TBC_Count = 0; }
/* * CG_GS_Malloc * * Used only for gameshared linking */ static void *CG_GS_Malloc( size_t size ) { return CG_Malloc( size ); }
/* * CG_LoadRecamScriptFile */ bool CG_LoadRecamScriptFile( char *filename ) { int filelen, filehandle; uint8_t *buf = NULL; char *ptr, *token; int linecount; cg_democam_t *cam = NULL; if( !filename ) { CG_Printf( "CG_LoadRecamScriptFile: no filename\n" ); return false; } filelen = trap_FS_FOpenFile( filename, &filehandle, FS_READ ); if( !filehandle || filelen < 1 ) { trap_FS_FCloseFile( filehandle ); } else { buf = ( uint8_t * )CG_Malloc( filelen + 1 ); filelen = trap_FS_Read( buf, filelen, filehandle ); trap_FS_FCloseFile( filehandle ); } if( !buf ) { return false; } // parse the script linecount = 0; ptr = ( char * )buf; while( ptr ) { token = COM_ParseExt( &ptr, true ); if( !token[0] ) { break; } if( !Q_stricmp( token, "subtitle" ) || !Q_stricmp( token, "print" ) ) { cg_subtitle_t *sub; sub = CG_Democam_RegisterSubtitle(); sub->highprint = ( Q_stricmp( token, "print" ) == 0 ); token = COM_ParseExt( &ptr, true ); if( !token[0] ) { break; } sub->timeStamp = (unsigned int)atoi( token ); token = COM_ParseExt( &ptr, true ); if( !token[0] ) { break; } sub->maxDuration = (unsigned int)atoi( token ); sub->text = CG_CopyString( COM_ParseExt( &ptr, true ) ); linecount = 0; } else { switch( linecount ) { case 0: cam = CG_Democam_RegisterCam( atoi( token ) ); break; case 1: cam->timeStamp = (unsigned int)atoi( token ); break; case 2: cam->origin[0] = atof( token ); break; case 3: cam->origin[1] = atof( token ); break; case 4: cam->origin[2] = atof( token ); break; case 5: cam->angles[0] = atof( token ); break; case 6: cam->angles[1] = atof( token ); break; case 7: cam->angles[2] = atof( token ); break; case 8: cam->trackEnt = atoi( token ); break; case 9: cam->fov = atoi( token ); break; default: CG_Error( "CG_LoadRecamScriptFile: bad switch\n" ); } linecount++; if( linecount == 10 ) { linecount = 0; } } } CG_Free( buf ); if( linecount != 0 ) { CG_Printf( "CG_LoadRecamScriptFile: Invalid script. Ignored\n" ); CG_Democam_FreeCams(); CG_Democam_FreeSubtitles(); return false; } CG_Democam_ExecutePathAnalysis(); return true; }