01 Oct 2010
This commit is contained in:
parent
ed98f35b45
commit
ada0238004
|
@ -856,5 +856,32 @@ BulletImpactParticles
|
|||
*/
|
||||
void CParticleSystem :: BulletImpactParticles( const Vector &pos )
|
||||
{
|
||||
// FIXME: implement
|
||||
CBaseParticle *p;
|
||||
|
||||
// uncomment this to enable texture based particle
|
||||
// HSPRITE sprtex = SPR_Load("sprites/debris/debris_concrete01.spr");
|
||||
|
||||
g_pTempEnts->SparkShower( pos ); // Sparks
|
||||
|
||||
for( int i = 0; i < 25; i++ )
|
||||
{
|
||||
p = AllocParticle();
|
||||
if( !p ) return;
|
||||
|
||||
p->SetLifetime( 2.0 );
|
||||
p->SetColor( 0, 0, 0 ); // 0,0,0 = black
|
||||
p->SetAlpha( 255 );
|
||||
|
||||
//p->SetTexture ( sprtex ) ; // Uncomment to enable texture based particle
|
||||
|
||||
if( i & 1 )
|
||||
{
|
||||
p->SetType( pt_grav );
|
||||
for( int j = 0; j < 3; j++ )
|
||||
{
|
||||
p->m_Pos[j] = pos[j] + RANDOM_FLOAT( -2, 3 ); // distance between particles
|
||||
p->m_Velocity[j] = RANDOM_FLOAT( -70, 70 ); // speed, velocity of particles
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,6 +68,11 @@ typedef struct ui_enginefuncs_s
|
|||
// sound handlers
|
||||
void (*pfnPlayLocalSound)( const char *szSound );
|
||||
|
||||
// cinematic handlers
|
||||
void (*pfnDrawLogo)( const char *filename, float x, float y, float width, float height );
|
||||
int (*pfnGetLogoWidth)( void );
|
||||
int (*pfnGetLogoHeight)( void );
|
||||
|
||||
// text message system
|
||||
void (*pfnDrawCharacter)( int x, int y, int width, int height, int ch, int ulRGBA, HIMAGE hFont );
|
||||
int (*pfnDrawConsoleString)( int x, int y, const char *string );
|
||||
|
|
|
@ -715,7 +715,7 @@ void CL_Reconnect_f( void )
|
|||
|
||||
if( cls.state == ca_connected )
|
||||
{
|
||||
cls.demonum = -1; // not in the demo loop now
|
||||
cls.demonum = cls.movienum = -1; // not in the demo loop now
|
||||
cls.state = ca_connected;
|
||||
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
|
||||
BF_WriteString( &cls.netchan.message, "new" );
|
||||
|
@ -731,7 +731,7 @@ void CL_Reconnect_f( void )
|
|||
}
|
||||
else cls.connect_time = MAX_HEARTBEAT; // fire immediately
|
||||
|
||||
cls.demonum = -1; // not in the demo loop now
|
||||
cls.demonum = cls.movienum = -1; // not in the demo loop now
|
||||
cls.state = ca_connecting;
|
||||
Msg( "reconnecting...\n" );
|
||||
}
|
||||
|
@ -1281,6 +1281,7 @@ void CL_InitLocal( void )
|
|||
Cmd_AddCommand ("startdemos", CL_StartDemos_f, "start playing back the selected demos sequentially" );
|
||||
Cmd_AddCommand ("demos", CL_Demos_f, "restart looping demos defined by the last startdemos command" );
|
||||
Cmd_AddCommand ("movie", CL_PlayVideo_f, "playing a movie" );
|
||||
Cmd_AddCommand ("startmovies", SCR_StartMovies_f, "start playing back the selected movies sequentially" );
|
||||
Cmd_AddCommand ("stop", CL_Stop_f, "stop playing or recording a demo" );
|
||||
Cmd_AddCommand ("info", NULL, "collect info about local servers with specified protocol" );
|
||||
Cmd_AddCommand ("escape", CL_Escape_f, "escape from game to menu" );
|
||||
|
|
|
@ -29,8 +29,18 @@ void UI_MouseMove( int x, int y )
|
|||
|
||||
void UI_SetActiveMenu( bool fActive )
|
||||
{
|
||||
movie_state_t *cin_state;
|
||||
|
||||
if( !gameui.hInstance ) return;
|
||||
gameui.dllFuncs.pfnSetActiveMenu( fActive );
|
||||
gameui.drawLogo = fActive;
|
||||
|
||||
if( !fActive )
|
||||
{
|
||||
// close logo when menu is shutdown
|
||||
cin_state = AVI_GetState( CIN_LOGO );
|
||||
AVI_CloseVideo( cin_state );
|
||||
}
|
||||
}
|
||||
|
||||
void UI_AddServerToList( netadr_t adr, const char *info )
|
||||
|
@ -81,6 +91,80 @@ bool UI_IsVisible( void )
|
|||
return gameui.dllFuncs.pfnIsVisible();
|
||||
}
|
||||
|
||||
static void UI_DrawLogo( const char *filename, float x, float y, float width, float height )
|
||||
{
|
||||
static float video_duration;
|
||||
static float cin_time;
|
||||
static int last_frame = -1;
|
||||
static byte *cin_data; // dynamically allocated array
|
||||
movie_state_t *cin_state;
|
||||
int cin_frame;
|
||||
bool redraw = false;
|
||||
|
||||
if( !gameui.drawLogo ) return;
|
||||
|
||||
cin_state = AVI_GetState( CIN_LOGO );
|
||||
|
||||
if( !AVI_IsActive( cin_state ))
|
||||
{
|
||||
string path;
|
||||
const char *fullpath;
|
||||
|
||||
// run cinematic if not
|
||||
com.snprintf( path, sizeof( path ), "media/%s", filename );
|
||||
FS_DefaultExtension( path, ".avi" );
|
||||
fullpath = FS_GetDiskPath( path );
|
||||
|
||||
if( FS_FileExists( path ) && !fullpath )
|
||||
{
|
||||
MsgDev( D_ERROR, "couldn't load %s from packfile. Please extract it\n", path );
|
||||
gameui.drawLogo = false;
|
||||
return;
|
||||
}
|
||||
|
||||
AVI_OpenVideo( cin_state, fullpath, false, false );
|
||||
if( !( AVI_GetVideoInfo( cin_state, &gameui.logo_xres, &gameui.logo_yres, &video_duration )))
|
||||
{
|
||||
AVI_CloseVideo( cin_state );
|
||||
gameui.drawLogo = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cin_data = Mem_Realloc( cls.mempool, cin_data, gameui.logo_xres * gameui.logo_yres * 3 );
|
||||
cin_time = 0.0f;
|
||||
last_frame = -1;
|
||||
}
|
||||
|
||||
// advances cinematic time
|
||||
cin_time += cls.frametime;
|
||||
|
||||
// restarts the cinematic
|
||||
if( cin_time > video_duration )
|
||||
cin_time = 0.0f;
|
||||
|
||||
// read the next frame
|
||||
cin_frame = AVI_GetVideoFrameNumber( cin_state, cin_time );
|
||||
|
||||
if( cin_frame != last_frame )
|
||||
{
|
||||
AVI_GetVideoFrame( cin_state, cin_data, cin_frame );
|
||||
last_frame = cin_frame;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
re->DrawStretchRaw( x, y, width, height, gameui.logo_xres, gameui.logo_yres, cin_data, redraw );
|
||||
}
|
||||
|
||||
static int UI_GetLogoWidth( void )
|
||||
{
|
||||
return gameui.logo_xres;
|
||||
}
|
||||
|
||||
static int UI_GetLogoHeight( void )
|
||||
{
|
||||
return gameui.logo_yres;
|
||||
}
|
||||
|
||||
void Host_Credits( void )
|
||||
{
|
||||
if( !gameui.hInstance ) return;
|
||||
|
@ -801,6 +885,9 @@ static ui_enginefuncs_t gEngfuncs =
|
|||
Con_Printf,
|
||||
Con_DPrintf,
|
||||
pfnPlaySound,
|
||||
UI_DrawLogo,
|
||||
UI_GetLogoWidth,
|
||||
UI_GetLogoHeight,
|
||||
pfnDrawCharacter,
|
||||
pfnDrawConsoleString,
|
||||
pfnDrawSetTextColor,
|
||||
|
|
|
@ -408,6 +408,7 @@ void SCR_Shutdown( void )
|
|||
Cmd_RemoveCommand( "viewpos" );
|
||||
|
||||
UI_SetActiveMenu( false );
|
||||
SCR_FreeCinematic();
|
||||
UI_UnloadProgs();
|
||||
scr_init = false;
|
||||
}
|
|
@ -5,71 +5,106 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
#include <vfw.h> // video for windows
|
||||
|
||||
/*
|
||||
=================================================================
|
||||
|
||||
ROQ PLAYING
|
||||
AVI PLAYING
|
||||
|
||||
=================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_StopCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_StopCinematic( void )
|
||||
cvar_t *vid_gamma;
|
||||
static long xres, yres;
|
||||
static float video_duration;
|
||||
static float cin_time;
|
||||
static int cin_frame;
|
||||
static char *cin_data; // dynamically allocated array
|
||||
static wavdata_t cin_audio;
|
||||
static movie_state_t *cin_state;
|
||||
|
||||
void SCR_RebuildGammaTable( void )
|
||||
{
|
||||
cinematics_t *cin = cl.cin;
|
||||
float g;
|
||||
int i;
|
||||
|
||||
if( !cin || !cin->file )
|
||||
return;
|
||||
g = bound( 0.5f, vid_gamma->value, 2.3f );
|
||||
|
||||
cl.cin = NULL;
|
||||
cin->time = 0.0f; // done
|
||||
cin->pic = NULL;
|
||||
cin->pic_pending = NULL;
|
||||
|
||||
if( cin->file ) FS_Close( cin->file );
|
||||
cin->file = NULL;
|
||||
|
||||
Mem_Free( cin->name );
|
||||
cin->name = NULL;
|
||||
|
||||
if( cin->vid_buffer )
|
||||
// screenshots gamma
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
Mem_Free( cin->vid_buffer );
|
||||
cin->vid_buffer = NULL;
|
||||
if( g == 1 ) clgame.ds.gammaTable[i] = i;
|
||||
else clgame.ds.gammaTable[i] = bound( 0, pow( i * ( 1.0f / 255.0f ), g ) * 255.0f, 255 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_NextMovie
|
||||
|
||||
Called when a demo or cinematic finishes
|
||||
If the "nextmovie" cvar is set, that command will be issued
|
||||
==================
|
||||
*/
|
||||
bool SCR_NextMovie( void )
|
||||
{
|
||||
string str;
|
||||
|
||||
S_StopAllSounds();
|
||||
SCR_StopCinematic();
|
||||
|
||||
if( cls.movienum == -1 )
|
||||
return false; // don't play movies
|
||||
|
||||
if( !cls.movies[cls.movienum][0] || cls.movienum == MAX_MOVIES )
|
||||
{
|
||||
cls.movienum = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
cls.state = ca_disconnected;
|
||||
UI_SetActiveMenu( true );
|
||||
}
|
||||
com.snprintf( str, MAX_STRING, "movie %s\n", cls.movies[cls.movienum] );
|
||||
|
||||
//==========================================================================
|
||||
Cbuf_InsertText( str );
|
||||
cls.movienum++;
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_InitCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_InitCinematic( void )
|
||||
{
|
||||
CIN_Init ();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_InitCinematic
|
||||
SCR_StartMovies_f
|
||||
==================
|
||||
*/
|
||||
uint SCR_GetCinematicTime( void )
|
||||
void SCR_StartMovies_f( void )
|
||||
{
|
||||
cinematics_t *cin = cl.cin;
|
||||
return (cin ? cin->time : 0.0f);
|
||||
}
|
||||
int i, c;
|
||||
|
||||
if( host.developer >= 2 )
|
||||
{
|
||||
// don't run movies where we in developer-mode
|
||||
cls.movienum = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
c = Cmd_Argc() - 1;
|
||||
if( c > MAX_MOVIES )
|
||||
{
|
||||
MsgDev( D_WARN, "Host_StartMovies: max %i movies in StartupVids\n", MAX_DEMOS );
|
||||
c = MAX_MOVIES;
|
||||
}
|
||||
|
||||
for( i = 1; i < c + 1; i++ )
|
||||
com.strncpy( cls.movies[i-1], Cmd_Argv( i ), sizeof( cls.movies[0] ));
|
||||
|
||||
if( !SV_Active() && cls.movienum != -1 && cls.state != ca_cinematic )
|
||||
{
|
||||
cls.movienum = 0;
|
||||
SCR_NextMovie ();
|
||||
}
|
||||
else cls.movienum = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_RunCinematic
|
||||
|
@ -77,32 +112,27 @@ SCR_RunCinematic
|
|||
*/
|
||||
void SCR_RunCinematic( void )
|
||||
{
|
||||
uint frame;
|
||||
cinematics_t *cin = cl.cin;
|
||||
if( !AVI_IsActive( cin_state ))
|
||||
return;
|
||||
|
||||
if( !cin || cin->time == 0.0f )
|
||||
if( vid_gamma->modified )
|
||||
{
|
||||
SCR_StopCinematic ();
|
||||
SCR_RebuildGammaTable();
|
||||
vid_gamma->modified = false;
|
||||
}
|
||||
|
||||
// advances cinematic time
|
||||
cin_time += cls.frametime;
|
||||
|
||||
// stop the video after it finishes
|
||||
if( cin_time > video_duration + 0.1f )
|
||||
{
|
||||
SCR_NextMovie( );
|
||||
return;
|
||||
}
|
||||
|
||||
frame = (Sys_DoubleTime() - cin->time) * (float)(RoQ_FRAMERATE);
|
||||
if( frame <= cin->frame ) return;
|
||||
|
||||
if( frame > cin->frame + 1 )
|
||||
{
|
||||
MsgDev( D_WARN, "dropped frame: %i > %i\n", frame, cin->frame + 1 );
|
||||
cin->time = Sys_DoubleTime() - cin->frame / RoQ_FRAMERATE;
|
||||
}
|
||||
|
||||
cin->pic = cin->pic_pending;
|
||||
cin->pic_pending = CIN_ReadNextFrame( cin, false );
|
||||
|
||||
if( !cin->pic_pending )
|
||||
{
|
||||
SCR_StopCinematic ();
|
||||
return;
|
||||
}
|
||||
// read the next frame
|
||||
cin_frame = AVI_GetVideoFrameNumber( cin_state, cin_time );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -115,19 +145,24 @@ should be skipped
|
|||
*/
|
||||
bool SCR_DrawCinematic( void )
|
||||
{
|
||||
cinematics_t *cin = cl.cin;
|
||||
|
||||
if( !re || !cin || cin->time <= 0.0f )
|
||||
static int last_frame = -1;
|
||||
bool redraw = false;
|
||||
|
||||
if( !re || cin_time <= 0.0f )
|
||||
return false;
|
||||
|
||||
if( !cin->pic )
|
||||
return true;
|
||||
if( cin_frame != last_frame )
|
||||
{
|
||||
AVI_GetVideoFrame( cin_state, cin_data, cin_frame );
|
||||
last_frame = cin_frame;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
re->DrawStretchRaw( 0, 0, scr_width->integer, scr_height->integer, cin->width, cin->height, cin->pic, true );
|
||||
re->DrawStretchRaw( 0, 0, scr_width->integer, scr_height->integer, xres, yres, cin_data, redraw );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_PlayCinematic
|
||||
|
@ -135,57 +170,101 @@ SCR_PlayCinematic
|
|||
*/
|
||||
bool SCR_PlayCinematic( const char *arg )
|
||||
{
|
||||
size_t name_size;
|
||||
static cinematics_t clientCin;
|
||||
cinematics_t *cin = cl.cin = &clientCin;
|
||||
droqchunk_t *chunk = &cin->chunk;
|
||||
string path;
|
||||
const char *fullpath;
|
||||
|
||||
if( cls.state == ca_cinematic )
|
||||
com.snprintf( path, sizeof( path ), "media/%s.avi", arg );
|
||||
fullpath = FS_GetDiskPath( path );
|
||||
|
||||
if( FS_FileExists( path ) && !fullpath )
|
||||
{
|
||||
// first stop the old movie
|
||||
SCR_StopCinematic ();
|
||||
}
|
||||
|
||||
name_size = com.strlen( "media/" ) + com.strlen( arg ) + com.strlen( ".roq" ) + 1;
|
||||
cin->name = Mem_Alloc( cls.mempool, name_size );
|
||||
com.snprintf( cin->name, name_size, "media/%s", arg );
|
||||
FS_DefaultExtension( cin->name, ".roq" );
|
||||
|
||||
// nasty hack
|
||||
cin->s_rate = 22050;
|
||||
cin->s_width = 2;
|
||||
cin->width = cin->height = 0;
|
||||
|
||||
cin->frame = 0;
|
||||
cin->file = FS_Open( cin->name, "rb" );
|
||||
|
||||
if( !cin->file )
|
||||
{
|
||||
MsgDev( D_INFO, "SCR_PlayCinematic: unable to find %s\n", cin->name );
|
||||
SCR_StopCinematic ();
|
||||
MsgDev( D_ERROR, "couldn't load %s from packfile. Please extract it\n", path );
|
||||
return false;
|
||||
}
|
||||
|
||||
// read header
|
||||
CIN_ReadChunk( cin );
|
||||
|
||||
if( chunk->id != RoQ_HEADER1 || chunk->size != RoQ_HEADER2 || chunk->argument != RoQ_HEADER3 )
|
||||
AVI_OpenVideo( cin_state, fullpath, true, true );
|
||||
if( !AVI_IsActive( cin_state ))
|
||||
{
|
||||
MsgDev( D_ERROR, "%s invalid header chunk %x\n", cin->name, chunk->id );
|
||||
SCR_StopCinematic();
|
||||
AVI_CloseVideo( cin_state );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !( AVI_GetVideoInfo( cin_state, &xres, &yres, &video_duration ))) // couldn't open this at all.
|
||||
{
|
||||
AVI_CloseVideo( cin_state );
|
||||
return false;
|
||||
}
|
||||
|
||||
cin_data = Mem_Realloc( cls.mempool, cin_data, xres * yres * 3 );
|
||||
|
||||
if( AVI_GetAudioInfo( cin_state, &cin_audio ))
|
||||
{
|
||||
// begin streaming
|
||||
S_StopAllSounds();
|
||||
S_StartStreaming();
|
||||
}
|
||||
|
||||
UI_SetActiveMenu( false );
|
||||
S_StopAllSounds();
|
||||
S_StartStreaming();
|
||||
SCR_RebuildGammaTable();
|
||||
|
||||
cls.state = ca_cinematic;
|
||||
|
||||
cin->headerlen = FS_Tell( cin->file );
|
||||
cin->frame = 0;
|
||||
cin->pic = cin->pic_pending = CIN_ReadNextFrame( cin, false );
|
||||
cin->time = Sys_DoubleTime();
|
||||
|
||||
cin_time = 0.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
long SCR_GetAudioChunk( char *rawdata, long length )
|
||||
{
|
||||
int r;
|
||||
|
||||
r = AVI_GetAudioChunk( cin_state, rawdata, cin_audio.loopStart, length );
|
||||
cin_audio.loopStart += r; // advance play position
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
wavdata_t *SCR_GetMovieInfo( void )
|
||||
{
|
||||
if( AVI_IsActive( cin_state ))
|
||||
return &cin_audio;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_StopCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_StopCinematic( void )
|
||||
{
|
||||
AVI_CloseVideo( cin_state );
|
||||
S_StopStreaming();
|
||||
cin_time = 0.0f;
|
||||
|
||||
cls.state = ca_disconnected;
|
||||
UI_SetActiveMenu( true );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_InitCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_InitCinematic( void )
|
||||
{
|
||||
AVIFileInit();
|
||||
|
||||
// used for movie gamma correction
|
||||
vid_gamma = Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE, "gamma amount" );
|
||||
cin_state = AVI_GetState( CIN_MAIN );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_FreeCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_FreeCinematic( void )
|
||||
{
|
||||
AVIFileExit();
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2009 ©
|
||||
// cl_video.c - roq video player
|
||||
//=======================================================================
|
||||
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
|
||||
/*
|
||||
=================================================================
|
||||
|
||||
ROQ PLAYING
|
||||
|
||||
=================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_StopCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_StopCinematic2( void )
|
||||
{
|
||||
cinematics_t *cin = cl.cin;
|
||||
|
||||
if( !cin || !cin->file )
|
||||
return;
|
||||
|
||||
cl.cin = NULL;
|
||||
cin->time = 0.0f; // done
|
||||
cin->pic = NULL;
|
||||
cin->pic_pending = NULL;
|
||||
|
||||
if( cin->file ) FS_Close( cin->file );
|
||||
cin->file = NULL;
|
||||
|
||||
Mem_Free( cin->name );
|
||||
cin->name = NULL;
|
||||
|
||||
if( cin->vid_buffer )
|
||||
{
|
||||
Mem_Free( cin->vid_buffer );
|
||||
cin->vid_buffer = NULL;
|
||||
}
|
||||
|
||||
cls.state = ca_disconnected;
|
||||
UI_SetActiveMenu( true );
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_InitCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_InitCinematic2( void )
|
||||
{
|
||||
CIN_Init ();
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_FreeCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_FreeCinematic2( void )
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_InitCinematic
|
||||
==================
|
||||
*/
|
||||
uint SCR_GetCinematicTime( void )
|
||||
{
|
||||
cinematics_t *cin = cl.cin;
|
||||
return (cin ? cin->time : 0.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_RunCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_RunCinematic2( void )
|
||||
{
|
||||
uint frame;
|
||||
cinematics_t *cin = cl.cin;
|
||||
|
||||
if( !cin || cin->time == 0.0f )
|
||||
{
|
||||
SCR_StopCinematic ();
|
||||
return;
|
||||
}
|
||||
|
||||
frame = (Sys_DoubleTime() - cin->time) * (float)(RoQ_FRAMERATE);
|
||||
if( frame <= cin->frame ) return;
|
||||
|
||||
if( frame > cin->frame + 1 )
|
||||
{
|
||||
MsgDev( D_WARN, "dropped frame: %i > %i\n", frame, cin->frame + 1 );
|
||||
cin->time = Sys_DoubleTime() - cin->frame / RoQ_FRAMERATE;
|
||||
}
|
||||
|
||||
cin->pic = cin->pic_pending;
|
||||
cin->pic_pending = CIN_ReadNextFrame( cin, false );
|
||||
|
||||
if( !cin->pic_pending )
|
||||
{
|
||||
SCR_StopCinematic ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_DrawCinematic
|
||||
|
||||
Returns true if a cinematic is active, meaning the view rendering
|
||||
should be skipped
|
||||
==================
|
||||
*/
|
||||
bool SCR_DrawCinematic2( void )
|
||||
{
|
||||
cinematics_t *cin = cl.cin;
|
||||
|
||||
if( !re || !cin || cin->time <= 0.0f )
|
||||
return false;
|
||||
|
||||
if( !cin->pic )
|
||||
return true;
|
||||
|
||||
re->DrawStretchRaw( 0, 0, scr_width->integer, scr_height->integer, cin->width, cin->height, cin->pic, true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_PlayCinematic
|
||||
==================
|
||||
*/
|
||||
bool SCR_PlayCinematic2( const char *arg )
|
||||
{
|
||||
size_t name_size;
|
||||
static cinematics_t clientCin;
|
||||
cinematics_t *cin = cl.cin = &clientCin;
|
||||
droqchunk_t *chunk = &cin->chunk;
|
||||
|
||||
if( cls.state == ca_cinematic )
|
||||
{
|
||||
// first stop the old movie
|
||||
SCR_StopCinematic ();
|
||||
}
|
||||
|
||||
name_size = com.strlen( "media/" ) + com.strlen( arg ) + com.strlen( ".roq" ) + 1;
|
||||
cin->name = Mem_Alloc( cls.mempool, name_size );
|
||||
com.snprintf( cin->name, name_size, "media/%s", arg );
|
||||
FS_DefaultExtension( cin->name, ".roq" );
|
||||
|
||||
// nasty hack
|
||||
cin->s_rate = 22050;
|
||||
cin->s_width = 2;
|
||||
cin->width = cin->height = 0;
|
||||
|
||||
cin->frame = 0;
|
||||
cin->file = FS_Open( cin->name, "rb" );
|
||||
|
||||
if( !cin->file )
|
||||
{
|
||||
MsgDev( D_INFO, "SCR_PlayCinematic: unable to find %s\n", cin->name );
|
||||
SCR_StopCinematic ();
|
||||
return false;
|
||||
}
|
||||
|
||||
// read header
|
||||
CIN_ReadChunk( cin );
|
||||
|
||||
if( chunk->id != RoQ_HEADER1 || chunk->size != RoQ_HEADER2 || chunk->argument != RoQ_HEADER3 )
|
||||
{
|
||||
MsgDev( D_ERROR, "%s invalid header chunk %x\n", cin->name, chunk->id );
|
||||
SCR_StopCinematic();
|
||||
return false;
|
||||
}
|
||||
|
||||
UI_SetActiveMenu( false );
|
||||
S_StopAllSounds();
|
||||
S_StartStreaming();
|
||||
|
||||
cls.state = ca_cinematic;
|
||||
|
||||
cin->headerlen = FS_Tell( cin->file );
|
||||
cin->frame = 0;
|
||||
cin->pic = cin->pic_pending = CIN_ReadNextFrame( cin, false );
|
||||
cin->time = Sys_DoubleTime();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
#include "world.h"
|
||||
|
||||
#define MAX_DEMOS 32
|
||||
#define MAX_MOVIES 8
|
||||
|
||||
#define NUM_FOR_EDICT(e) ((int)((cl_entity_t *)(e) - clgame.entities))
|
||||
#define EDICT_NUM( num ) CL_EDICT_NUM( num, __FILE__, __LINE__ )
|
||||
|
@ -196,6 +197,7 @@ typedef struct
|
|||
HSPRITE hCrosshair;
|
||||
wrect_t rcCrosshair;
|
||||
rgba_t rgbaCrosshair;
|
||||
byte gammaTable[256];
|
||||
} draw_stuff_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -260,6 +262,9 @@ typedef struct
|
|||
GAMEINFO *modsInfo[MAX_MODS]; // simplified gameInfo for GameUI
|
||||
|
||||
ui_globalvars_t *globals;
|
||||
bool drawLogo; // set to TRUE if logo.avi missed or corrupted
|
||||
long logo_xres;
|
||||
long logo_yres;
|
||||
} gameui_static_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -307,6 +312,10 @@ typedef struct
|
|||
int demonum; // -1 = don't play demos
|
||||
string demos[MAX_DEMOS]; // when not playing
|
||||
|
||||
// movie playlist
|
||||
int movienum;
|
||||
string movies[MAX_MOVIES];
|
||||
|
||||
// demo recording info must be here, so it isn't clearing on level change
|
||||
bool demorecording;
|
||||
bool demoplayback;
|
||||
|
@ -537,10 +546,12 @@ bool UI_IsVisible( void );
|
|||
// cl_video.c
|
||||
//
|
||||
void SCR_InitCinematic( void );
|
||||
void SCR_FreeCinematic( void );
|
||||
bool SCR_PlayCinematic( const char *name );
|
||||
bool SCR_DrawCinematic( void );
|
||||
void SCR_RunCinematic( void );
|
||||
void SCR_StopCinematic( void );
|
||||
void SCR_StartMovies_f( void );
|
||||
void CL_PlayVideo_f( void );
|
||||
|
||||
//
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#define MAX_ENTNUMBER 99999 // for server and client parsing
|
||||
#define MAX_HEARTBEAT -99999 // connection time
|
||||
|
||||
#define CIN_MAIN 0
|
||||
#define CIN_LOGO 1
|
||||
|
||||
#ifdef _DEBUG
|
||||
void DBG_AssertFunction( bool fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage );
|
||||
#define Assert( f ) DBG_AssertFunction( f, #f, __FILE__, __LINE__, NULL )
|
||||
|
@ -235,6 +238,20 @@ void CIN_Init( void );
|
|||
void CIN_ReadChunk( cinematics_t *cin );
|
||||
byte *CIN_ReadNextFrame( cinematics_t *cin, bool silent );
|
||||
|
||||
//
|
||||
// movie.c
|
||||
//
|
||||
typedef struct movie_state_s movie_state_t;
|
||||
long AVI_GetVideoFrameNumber( movie_state_t *Avi, float time );
|
||||
void AVI_GetVideoFrame( movie_state_t *Avi, char *framedata, long frame );
|
||||
bool AVI_GetVideoInfo( movie_state_t *Avi, long *xres, long *yres, float *duration );
|
||||
bool AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info );
|
||||
fs_offset_t AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length );
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, bool load_audio, bool ignore_hwgamma );
|
||||
void AVI_CloseVideo( movie_state_t *Avi );
|
||||
bool AVI_IsActive( movie_state_t *Avi );
|
||||
movie_state_t *AVI_GetState( int num );
|
||||
|
||||
// shared calls
|
||||
bool CL_IsInGame( void );
|
||||
bool CL_IsInMenu( void );
|
||||
|
@ -268,6 +285,8 @@ void CL_ForceVid( void );
|
|||
void CL_ForceSnd( void );
|
||||
void SCR_Init( void );
|
||||
void SCR_UpdateScreen( void );
|
||||
long SCR_GetAudioChunk( char *rawdata, long length );
|
||||
wavdata_t *SCR_GetMovieInfo( void );
|
||||
void SCR_Shutdown( void );
|
||||
void Con_Print( const char *txt );
|
||||
void Con_NPrintf( int idx, char *fmt, ... );
|
||||
|
|
|
@ -24,6 +24,7 @@ _inline int CL_CreateDecalList( decallist_t *pList, bool changelevel )
|
|||
//
|
||||
#define S_Shutdown if( se ) se->Shutdown
|
||||
#define S_StartStreaming if( se ) se->StartStreaming
|
||||
#define S_StopStreaming if( se ) se->StopStreaming
|
||||
#define S_StartSound if( se ) se->StartSound
|
||||
#define S_AmbientSound if( se ) se->StaticSound
|
||||
#define S_StartLocalSound if( se ) se->StartLocalSound
|
||||
|
|
|
@ -258,31 +258,31 @@ bool Cmd_GetMovieList( const char *s, char *completedname, int length )
|
|||
string matchbuf;
|
||||
int i, nummovies;
|
||||
|
||||
t = FS_Search( va( "media/%s*.roq", s ), true );
|
||||
t = FS_Search( va( "media/%s*.avi", s ), true );
|
||||
if( !t ) return false;
|
||||
|
||||
FS_FileBase(t->filenames[0], matchbuf );
|
||||
if(completedname && length) com.strncpy( completedname, matchbuf, length );
|
||||
if(t->numfilenames == 1) return true;
|
||||
FS_FileBase( t->filenames[0], matchbuf );
|
||||
if( completedname && length ) com.strncpy( completedname, matchbuf, length );
|
||||
if( t->numfilenames == 1 ) return true;
|
||||
|
||||
for(i = 0, nummovies = 0; i < t->numfilenames; i++)
|
||||
{
|
||||
const char *ext = FS_FileExtension( t->filenames[i] );
|
||||
|
||||
if( com.stricmp( ext, "roq" )) continue;
|
||||
FS_FileBase(t->filenames[i], matchbuf );
|
||||
Msg("%16s\n", matchbuf );
|
||||
if( com.stricmp( ext, "avi" )) continue;
|
||||
FS_FileBase( t->filenames[i], matchbuf );
|
||||
Msg( "%16s\n", matchbuf );
|
||||
nummovies++;
|
||||
}
|
||||
Msg("\n^3 %i movies found.\n", nummovies );
|
||||
Mem_Free(t);
|
||||
Msg( "\n^3 %i movies found.\n", nummovies );
|
||||
Mem_Free( t );
|
||||
|
||||
// cut shortestMatch to the amount common with s
|
||||
if(completedname && length)
|
||||
if( completedname && length )
|
||||
{
|
||||
for( i = 0; matchbuf[i]; i++ )
|
||||
{
|
||||
if(com.tolower(completedname[i]) != com.tolower(matchbuf[i]))
|
||||
if( com.tolower( completedname[i] ) != com.tolower( matchbuf[i] ))
|
||||
completedname[i] = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -505,6 +505,12 @@ void Key_Event( int key, bool down, int time )
|
|||
if( down )
|
||||
{
|
||||
keys[key].repeats++;
|
||||
|
||||
if( key != K_BACKSPACE && key != K_PAUSE && keys[key].repeats > 1 )
|
||||
{
|
||||
// ignore most autorepeats
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,564 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2010 ©
|
||||
// movie.c - playing AVI files (based on original AVIKit code)
|
||||
//=======================================================================
|
||||
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
#include "byteorder.h"
|
||||
#include <vfw.h> // video for windows
|
||||
|
||||
typedef struct movie_state_s
|
||||
{
|
||||
bool active;
|
||||
bool ignore_hwgamma; // ignore hw gamma-correction
|
||||
|
||||
PAVIFILE pfile; // avi file pointer
|
||||
PAVISTREAM video_stream; // video stream pointer
|
||||
PGETFRAME video_getframe; // pointer to getframe object for video stream
|
||||
long video_frames; // total frames
|
||||
long video_xres; // video stream resolution
|
||||
long video_yres;
|
||||
float video_fps; // video stream fps
|
||||
|
||||
PAVISTREAM audio_stream; // audio stream pointer
|
||||
WAVEFORMAT *audio_header; // audio stream header
|
||||
long audio_header_size; // WAVEFORMAT is returned for PCM data; WAVEFORMATEX for others
|
||||
long audio_codec; // WAVE_FORMAT_PCM is oldstyle: anything else needs conversion
|
||||
long audio_length; // in converted samples
|
||||
long audio_bytes_per_sample; // guess.
|
||||
|
||||
// compressed audio specific data
|
||||
dword cpa_blockalign; // block size to read
|
||||
HACMSTREAM cpa_conversion_stream;
|
||||
ACMSTREAMHEADER cpa_conversion_header;
|
||||
byte *cpa_srcbuffer; // maintained buffer for raw data
|
||||
byte *cpa_dstbuffer;
|
||||
|
||||
dword cpa_blocknum; // current block
|
||||
dword cpa_blockpos; // read position in current block
|
||||
dword cpa_blockoffset; // corresponding offset in bytes in the output stream
|
||||
|
||||
// for additional unpack Ms-RLE codecs etc
|
||||
HDC hDC; // compatible DC
|
||||
HDRAWDIB hDD; // DrawDib handler
|
||||
HBITMAP hBitmap; // for DIB conversions
|
||||
byte *pframe_data; // converted framedata
|
||||
} movie_state_t;
|
||||
|
||||
static movie_state_t avi[2];
|
||||
|
||||
// Converts a compressed audio stream into uncompressed PCM.
|
||||
bool AVI_ACMConvertAudio( movie_state_t *Avi )
|
||||
{
|
||||
WAVEFORMATEX dest_header, *sh, *dh;
|
||||
AVISTREAMINFO stream_info;
|
||||
dword dest_length;
|
||||
short bits;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
// WMA codecs, both versions - they simply don't work.
|
||||
if( Avi->audio_header->wFormatTag == 0x160 || Avi->audio_header->wFormatTag == 0x161 )
|
||||
{
|
||||
MsgDev( D_ERROR, "ACM does not support this audio codec.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get audio stream info to work with
|
||||
AVIStreamInfo( Avi->audio_stream, &stream_info, sizeof( stream_info ));
|
||||
|
||||
if( Avi->audio_header_size < sizeof( WAVEFORMATEX ))
|
||||
{
|
||||
MsgDev( D_ERROR, "ACM failed to open conversion stream.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
sh = (WAVEFORMATEX *)Avi->audio_header;
|
||||
bits = 16; // predict state
|
||||
|
||||
// how much of this is actually required?
|
||||
dest_header.wFormatTag = WAVE_FORMAT_PCM; // yay
|
||||
dest_header.wBitsPerSample = bits; // 16bit
|
||||
dest_header.nChannels = sh->nChannels;
|
||||
dest_header.nSamplesPerSec = sh->nSamplesPerSec; // take straight from the source stream
|
||||
dest_header.nAvgBytesPerSec = (bits >> 3) * sh->nChannels * sh->nSamplesPerSec;
|
||||
dest_header.nBlockAlign = (bits >> 3) * sh->nChannels;
|
||||
dest_header.cbSize = 0; // no more data.
|
||||
|
||||
dh = &dest_header;
|
||||
|
||||
// open the stream
|
||||
if( acmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
// try with 8 bit destination instead
|
||||
bits = 8;
|
||||
|
||||
dest_header.wBitsPerSample = bits; // 8bit
|
||||
dest_header.nAvgBytesPerSec = ( bits >> 3 ) * sh->nChannels * sh->nSamplesPerSec;
|
||||
dest_header.nBlockAlign = ( bits >> 3 ) * sh->nChannels; // 1 sample at a time
|
||||
|
||||
if( acmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
MsgDev( D_ERROR, "ACM failed to open conversion stream.\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Avi->cpa_blockalign = sh->nBlockAlign;
|
||||
dest_length = 0;
|
||||
|
||||
// mp3 specific fix
|
||||
if( sh->wFormatTag == 0x55 )
|
||||
{
|
||||
LPMPEGLAYER3WAVEFORMAT k;
|
||||
|
||||
k = (LPMPEGLAYER3WAVEFORMAT)sh;
|
||||
Avi->cpa_blockalign = k->nBlockSize;
|
||||
}
|
||||
|
||||
// get the size of the output buffer for streaming the compressed audio
|
||||
if( acmStreamSize( Avi->cpa_conversion_stream, Avi->cpa_blockalign, &dest_length, ACM_STREAMSIZEF_SOURCE ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
MsgDev( D_ERROR, "Couldn't get ACM conversion stream size.\n" );
|
||||
acmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
Avi->cpa_srcbuffer = (byte *)Mem_Alloc( cls.mempool, Avi->cpa_blockalign );
|
||||
Avi->cpa_dstbuffer = (byte *)Mem_Alloc( cls.mempool, dest_length ); // maintained buffer for raw data
|
||||
|
||||
// prep the headers!
|
||||
Avi->cpa_conversion_header.cbStruct = sizeof( ACMSTREAMHEADER );
|
||||
Avi->cpa_conversion_header.fdwStatus = 0;
|
||||
Avi->cpa_conversion_header.dwUser = 0; // no user data
|
||||
Avi->cpa_conversion_header.pbSrc = Avi->cpa_srcbuffer; // source buffer
|
||||
Avi->cpa_conversion_header.cbSrcLength = Avi->cpa_blockalign; // source buffer size
|
||||
Avi->cpa_conversion_header.cbSrcLengthUsed = 0;
|
||||
Avi->cpa_conversion_header.dwSrcUser = 0; // no user data
|
||||
Avi->cpa_conversion_header.pbDst = Avi->cpa_dstbuffer; // dest buffer
|
||||
Avi->cpa_conversion_header.cbDstLength = dest_length; // dest buffer size
|
||||
Avi->cpa_conversion_header.cbDstLengthUsed = 0;
|
||||
Avi->cpa_conversion_header.dwDstUser = 0; // no user data
|
||||
|
||||
if( acmStreamPrepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
MsgDev( D_ERROR, "couldn't prep headers.\n" );
|
||||
acmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
Avi->cpa_blocknum = 0; // start at 0.
|
||||
Avi->cpa_blockpos = 0;
|
||||
Avi->cpa_blockoffset = 0;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START );
|
||||
|
||||
// convert first chunk twice. it often fails the first time. BLACK MAGIC.
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
Avi->audio_bytes_per_sample = (bits >> 3 ) * Avi->audio_header->nChannels;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVI_GetVideoInfo( movie_state_t *Avi, long *xres, long *yres, float *duration )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active )
|
||||
return false;
|
||||
|
||||
if( xres != NULL )
|
||||
*xres = Avi->video_xres;
|
||||
|
||||
if( yres != NULL )
|
||||
*yres = Avi->video_yres;
|
||||
|
||||
if( duration != NULL )
|
||||
*duration = (float)Avi->video_frames / Avi->video_fps;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns a unique frame identifier
|
||||
long AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active )
|
||||
return 0;
|
||||
|
||||
return (time * Avi->video_fps);
|
||||
}
|
||||
|
||||
// gets the raw frame data
|
||||
void AVI_GetVideoFrame( movie_state_t *Avi, char *framedata, long frame )
|
||||
{
|
||||
LPBITMAPINFOHEADER frame_info;
|
||||
byte *frame_raw;
|
||||
byte *in, *out;
|
||||
int i;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active ) return;
|
||||
|
||||
if( frame >= Avi->video_frames )
|
||||
frame = Avi->video_frames - 1;
|
||||
|
||||
frame_info = (LPBITMAPINFOHEADER)AVIStreamGetFrame( Avi->video_getframe, frame );
|
||||
frame_raw = (char *)frame_info + frame_info->biSize + frame_info->biClrUsed * sizeof( RGBQUAD );
|
||||
DrawDibDraw( Avi->hDD, Avi->hDC, 0, 0, Avi->video_xres, Avi->video_yres, frame_info, frame_raw, 0, 0, Avi->video_xres, Avi->video_yres, 0 );
|
||||
|
||||
in = Avi->pframe_data;
|
||||
out = framedata;
|
||||
|
||||
if( Avi->ignore_hwgamma )
|
||||
{
|
||||
// flip BGR to RGB and renormalize gamma
|
||||
for( i = 0; i < Avi->video_xres * Avi->video_yres; i++, in += 3 )
|
||||
{
|
||||
*out++ = clgame.ds.gammaTable[in[2]];
|
||||
*out++ = clgame.ds.gammaTable[in[1]];
|
||||
*out++ = clgame.ds.gammaTable[in[0]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only flip BGR to RGB
|
||||
for( i = 0; i < Avi->video_xres * Avi->video_yres; i++, in += 3 )
|
||||
{
|
||||
*out++ = in[2];
|
||||
*out++ = in[1];
|
||||
*out++ = in[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active || Avi->audio_stream == NULL || snd_info == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_info->rate = Avi->audio_header->nSamplesPerSec;
|
||||
snd_info->channels = Avi->audio_header->nChannels;
|
||||
|
||||
if( Avi->audio_codec == WAVE_FORMAT_PCM ) // uncompressed audio!
|
||||
snd_info->width = ( Avi->audio_bytes_per_sample > Avi->audio_header->nChannels ) ? 2 : 1;
|
||||
else snd_info->width = 2; // assume compressed audio is always 16 bit
|
||||
|
||||
snd_info->size = snd_info->rate * snd_info->width * snd_info->channels;
|
||||
snd_info->loopStart = 0; // HACKHACK: use loopStart as streampos
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// sync the current audio read to a specific offset
|
||||
bool AVI_SeekPosition( movie_state_t *Avi, dword offset )
|
||||
{
|
||||
int breaker;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( offset < Avi->cpa_blockoffset ) // well, shit. we can't seek backwards... restart
|
||||
{
|
||||
if( Avi->cpa_blockoffset - offset < 500000 )
|
||||
return false; // don't bother if it's gonna catch up soon (cheap hack! works!)
|
||||
|
||||
Avi->cpa_blocknum = 0; // start at 0, eh.
|
||||
Avi->cpa_blockpos = 0;
|
||||
Avi->cpa_blockoffset = 0;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START );
|
||||
|
||||
// convert first chunk twice. it often fails the first time. BLACK MAGIC.
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
}
|
||||
|
||||
// now then: seek forwards to the required block
|
||||
breaker = 30; // maximum zero blocks: anti-freeze protection
|
||||
|
||||
while( Avi->cpa_blockoffset + Avi->cpa_conversion_header.cbDstLengthUsed < offset )
|
||||
{
|
||||
Avi->cpa_blocknum++;
|
||||
Avi->cpa_blockoffset += Avi->cpa_conversion_header.cbDstLengthUsed;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
if( Avi->cpa_conversion_header.cbDstLengthUsed == 0 )
|
||||
breaker--;
|
||||
else breaker = 30;
|
||||
|
||||
if( breaker <= 0 )
|
||||
return false;
|
||||
|
||||
Avi->cpa_blockpos = 0;
|
||||
}
|
||||
|
||||
// seek to the right position inside the block
|
||||
Avi->cpa_blockpos = offset - Avi->cpa_blockoffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get a chunk of audio from the stream (in bytes)
|
||||
fs_offset_t AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length )
|
||||
{
|
||||
int i;
|
||||
long result = 0;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
// zero data past the end of the file
|
||||
if( offset + length > Avi->audio_length )
|
||||
{
|
||||
if( offset <= Avi->audio_length )
|
||||
{
|
||||
long remaining_length = Avi->audio_length - offset;
|
||||
|
||||
AVI_GetAudioChunk( Avi, audiodata, offset, remaining_length );
|
||||
|
||||
for( i = remaining_length; i < length; i++ )
|
||||
audiodata[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < length; i++ )
|
||||
audiodata[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// uncompressed audio!
|
||||
if( Avi->audio_codec == WAVE_FORMAT_PCM )
|
||||
{
|
||||
// very simple - read straight out
|
||||
AVIStreamRead( Avi->audio_stream, offset / Avi->audio_bytes_per_sample, length / Avi->audio_bytes_per_sample, audiodata, length, &result, NULL );
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// compressed audio!
|
||||
result = 0;
|
||||
|
||||
// seek to correct chunk and all that stuff
|
||||
if( !AVI_SeekPosition( Avi, offset ))
|
||||
return 0; // don't continue if we're waiting for the play pointer to catch up
|
||||
|
||||
while( length > 0 )
|
||||
{
|
||||
long blockread = Avi->cpa_conversion_header.cbDstLengthUsed - Avi->cpa_blockpos;
|
||||
|
||||
if( blockread <= 0 ) // read next
|
||||
{
|
||||
Avi->cpa_blocknum++;
|
||||
Avi->cpa_blockoffset += Avi->cpa_conversion_header.cbDstLengthUsed;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
Avi->cpa_blockpos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( blockread > length )
|
||||
blockread = length;
|
||||
|
||||
// copy the data
|
||||
Mem_Copy( audiodata + result, (void *)( Avi->cpa_dstbuffer + Avi->cpa_blockpos ), blockread );
|
||||
|
||||
Avi->cpa_blockpos += blockread;
|
||||
result += blockread;
|
||||
length -= blockread;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void AVI_CloseVideo( movie_state_t *Avi )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( Avi->active )
|
||||
{
|
||||
AVIStreamGetFrameClose( Avi->video_getframe );
|
||||
|
||||
if( Avi->audio_stream != NULL )
|
||||
{
|
||||
AVIStreamRelease( Avi->audio_stream );
|
||||
Mem_Free( Avi->audio_header );
|
||||
|
||||
if( Avi->audio_codec != WAVE_FORMAT_PCM )
|
||||
{
|
||||
acmStreamUnprepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 );
|
||||
acmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
Mem_Free( Avi->cpa_srcbuffer );
|
||||
Mem_Free( Avi->cpa_dstbuffer );
|
||||
}
|
||||
}
|
||||
|
||||
AVIStreamRelease( Avi->video_stream );
|
||||
|
||||
DeleteObject( Avi->hBitmap );
|
||||
DrawDibClose( Avi->hDD );
|
||||
DeleteDC( Avi->hDC );
|
||||
}
|
||||
|
||||
Mem_Set( Avi, 0, sizeof( movie_state_t ));
|
||||
}
|
||||
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, bool load_audio, bool ignore_hwgamma )
|
||||
{
|
||||
AVISTREAMINFO stream_info;
|
||||
BITMAPINFOHEADER bmih;
|
||||
long opened_streams = 0;
|
||||
LONG hr;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
// default state: non-working.
|
||||
Avi->active = false;
|
||||
|
||||
// load the AVI
|
||||
hr = AVIFileOpen( &Avi->pfile, filename, OF_SHARE_DENY_WRITE, 0L );
|
||||
|
||||
if( hr != 0 ) // error opening AVI:
|
||||
{
|
||||
switch( hr )
|
||||
{
|
||||
case AVIERR_BADFORMAT:
|
||||
MsgDev( D_ERROR, "corrupt file or unknown format.\n" );
|
||||
break;
|
||||
case AVIERR_MEMORY:
|
||||
MsgDev( D_ERROR, "insufficient memory to open file.\n" );
|
||||
break;
|
||||
case AVIERR_FILEREAD:
|
||||
MsgDev( D_ERROR, "disk error reading file.\n" );
|
||||
break;
|
||||
case AVIERR_FILEOPEN:
|
||||
MsgDev( D_ERROR, "disk error opening file.\n" );
|
||||
break;
|
||||
case REGDB_E_CLASSNOTREG:
|
||||
default:
|
||||
MsgDev( D_ERROR, "no handler found (or file not found).\n" );
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Avi->video_stream = Avi->audio_stream = NULL;
|
||||
|
||||
// open the streams until a stream is not available.
|
||||
while( 1 )
|
||||
{
|
||||
PAVISTREAM stream = NULL;
|
||||
|
||||
if( AVIFileGetStream( Avi->pfile, &stream, 0L, opened_streams++ ) != AVIERR_OK )
|
||||
break;
|
||||
|
||||
if( stream == NULL )
|
||||
break;
|
||||
|
||||
AVIStreamInfo( stream, &stream_info, sizeof( stream_info ));
|
||||
|
||||
if( stream_info.fccType == streamtypeVIDEO && Avi->video_stream == NULL )
|
||||
{
|
||||
Avi->video_stream = stream;
|
||||
Avi->video_frames = stream_info.dwLength;
|
||||
Avi->video_xres = stream_info.rcFrame.right - stream_info.rcFrame.left;
|
||||
Avi->video_yres = stream_info.rcFrame.bottom - stream_info.rcFrame.top;
|
||||
Avi->video_fps = (float)stream_info.dwRate / (float)stream_info.dwScale;
|
||||
}
|
||||
else if( stream_info.fccType == streamtypeAUDIO && Avi->audio_stream == NULL && load_audio )
|
||||
{
|
||||
long size;
|
||||
|
||||
Avi->audio_stream = stream;
|
||||
|
||||
// read the audio header
|
||||
AVIStreamReadFormat( Avi->audio_stream, AVIStreamStart( Avi->audio_stream ), 0, &size );
|
||||
|
||||
Avi->audio_header = (WAVEFORMAT *)Mem_Alloc( cls.mempool, size );
|
||||
AVIStreamReadFormat( Avi->audio_stream, AVIStreamStart( Avi->audio_stream ), Avi->audio_header, &size );
|
||||
Avi->audio_header_size = size;
|
||||
Avi->audio_codec = Avi->audio_header->wFormatTag;
|
||||
|
||||
// length of converted audio in samples
|
||||
Avi->audio_length = (long)((float)stream_info.dwLength / Avi->audio_header->nAvgBytesPerSec );
|
||||
Avi->audio_length *= Avi->audio_header->nSamplesPerSec;
|
||||
|
||||
if( Avi->audio_codec != WAVE_FORMAT_PCM )
|
||||
{
|
||||
if( !AVI_ACMConvertAudio( Avi ))
|
||||
{
|
||||
Mem_Free( Avi->audio_header );
|
||||
Avi->audio_stream = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else Avi->audio_bytes_per_sample = Avi->audio_header->nBlockAlign;
|
||||
Avi->audio_length *= Avi->audio_bytes_per_sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
AVIStreamRelease( stream );
|
||||
}
|
||||
}
|
||||
|
||||
// display error message-stream not found.
|
||||
if( Avi->video_stream == NULL )
|
||||
{
|
||||
if( Avi->pfile ) // If file is open, close it
|
||||
AVIFileRelease( Avi->pfile );
|
||||
MsgDev( D_ERROR, "couldn't find a valid video stream.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
AVIFileRelease( Avi->pfile ); // release the file
|
||||
Avi->video_getframe = AVIStreamGetFrameOpen( Avi->video_stream, NULL ); // open the frame getter
|
||||
|
||||
if( Avi->video_getframe == NULL )
|
||||
{
|
||||
MsgDev( D_ERROR, "error attempting to read video frames.\n" );
|
||||
return; // couldn't open frame getter.
|
||||
}
|
||||
|
||||
Avi->ignore_hwgamma = ignore_hwgamma;
|
||||
|
||||
bmih.biSize = sizeof( BITMAPINFOHEADER );
|
||||
bmih.biPlanes = 1;
|
||||
bmih.biBitCount = 24;
|
||||
bmih.biCompression = BI_RGB;
|
||||
|
||||
// FIXME: check for supports GL_ARB_texture_non_power_of_two
|
||||
bmih.biWidth = Avi->video_xres;
|
||||
bmih.biHeight = -Avi->video_yres; // invert height to flip image upside down
|
||||
|
||||
Avi->hDC = CreateCompatibleDC( 0 );
|
||||
Avi->hDD = DrawDibOpen();
|
||||
Avi->hBitmap = CreateDIBSection( Avi->hDC, (BITMAPINFO*)(&bmih), DIB_RGB_COLORS, (void**)(&Avi->pframe_data), NULL, 0 );
|
||||
SelectObject( Avi->hDC, Avi->hBitmap );
|
||||
|
||||
Avi->active = true; // done
|
||||
}
|
||||
|
||||
bool AVI_IsActive( movie_state_t *Avi )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
return Avi->active;
|
||||
}
|
||||
|
||||
movie_state_t *AVI_GetState( int num )
|
||||
{
|
||||
return &avi[num];
|
||||
}
|
|
@ -0,0 +1,848 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2010 ©
|
||||
// movie.c - playing AVI files (based on original AVIKit code)
|
||||
//=======================================================================
|
||||
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
#include "byteorder.h"
|
||||
#include <vfw.h> // video for windows
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool active;
|
||||
|
||||
PAVIFILE pfile; // avi file pointer
|
||||
PAVISTREAM video_stream; // video stream pointer
|
||||
PGETFRAME video_getframe; // pointer to getframe object for video stream
|
||||
long video_frames; // total frames
|
||||
long video_xres; // video stream resolution
|
||||
long video_yres;
|
||||
float video_fps; // video stream fps
|
||||
|
||||
PAVISTREAM audio_stream; // audio stream pointer
|
||||
WAVEFORMAT *audio_header; // audio stream header
|
||||
long audio_header_size; // WAVEFORMAT is returned for PCM data; WAVEFORMATEX for others
|
||||
long audio_codec; // WAVE_FORMAT_PCM is oldstyle: anything else needs conversion
|
||||
long audio_length; // in converted samples
|
||||
long audio_bytes_per_sample; // guess.
|
||||
|
||||
// compressed audio specific data
|
||||
dword cpa_blockalign; // block size to read
|
||||
HACMSTREAM cpa_conversion_stream;
|
||||
ACMSTREAMHEADER cpa_conversion_header;
|
||||
byte *cpa_srcbuffer; // maintained buffer for raw data
|
||||
byte *cpa_dstbuffer;
|
||||
|
||||
dword cpa_blocknum; // current block
|
||||
dword cpa_blockpos; // read position in current block
|
||||
dword cpa_blockoffset; // corresponding offset in bytes in the output stream
|
||||
} movie_state_t;
|
||||
|
||||
cvar_t *vid_gamma;
|
||||
static byte vid_gammaTable[256];// movie gamma-correction
|
||||
|
||||
static movie_state_t avi;
|
||||
|
||||
// Converts a compressed audio stream into uncompressed PCM.
|
||||
bool AVI_ACMConvertAudio( movie_state_t *Avi )
|
||||
{
|
||||
WAVEFORMATEX dest_header, *sh, *dh;
|
||||
AVISTREAMINFO stream_info;
|
||||
dword dest_length;
|
||||
short bits;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
// WMA codecs, both versions - they simply don't work.
|
||||
if( Avi->audio_header->wFormatTag == 0x160 || Avi->audio_header->wFormatTag == 0x161 )
|
||||
{
|
||||
MsgDev( D_ERROR, "ACM does not support this audio codec.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get audio stream info to work with
|
||||
AVIStreamInfo( Avi->audio_stream, &stream_info, sizeof( stream_info ));
|
||||
|
||||
if( Avi->audio_header_size < sizeof( WAVEFORMATEX ))
|
||||
{
|
||||
MsgDev( D_ERROR, "ACM failed to open conversion stream.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
sh = (WAVEFORMATEX *)Avi->audio_header;
|
||||
bits = 16; // predict state
|
||||
|
||||
// how much of this is actually required?
|
||||
dest_header.wFormatTag = WAVE_FORMAT_PCM; // yay
|
||||
dest_header.wBitsPerSample = bits; // 16bit
|
||||
dest_header.nChannels = sh->nChannels;
|
||||
dest_header.nSamplesPerSec = sh->nSamplesPerSec; // take straight from the source stream
|
||||
dest_header.nAvgBytesPerSec = (bits >> 3) * sh->nChannels * sh->nSamplesPerSec;
|
||||
dest_header.nBlockAlign = (bits >> 3) * sh->nChannels;
|
||||
dest_header.cbSize = 0; // no more data.
|
||||
|
||||
dh = &dest_header;
|
||||
|
||||
// open the stream
|
||||
if( acmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
// try with 8 bit destination instead
|
||||
bits = 8;
|
||||
|
||||
dest_header.wBitsPerSample = bits; // 8bit
|
||||
dest_header.nAvgBytesPerSec = ( bits >> 3 ) * sh->nChannels * sh->nSamplesPerSec;
|
||||
dest_header.nBlockAlign = ( bits >> 3 ) * sh->nChannels; // 1 sample at a time
|
||||
|
||||
if( acmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
MsgDev( D_ERROR, "ACM failed to open conversion stream.\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Avi->cpa_blockalign = sh->nBlockAlign;
|
||||
dest_length = 0;
|
||||
|
||||
// mp3 specific fix
|
||||
if( sh->wFormatTag == 0x55 )
|
||||
{
|
||||
LPMPEGLAYER3WAVEFORMAT k;
|
||||
|
||||
k = (LPMPEGLAYER3WAVEFORMAT)sh;
|
||||
Avi->cpa_blockalign = k->nBlockSize;
|
||||
}
|
||||
|
||||
// get the size of the output buffer for streaming the compressed audio
|
||||
if( acmStreamSize( Avi->cpa_conversion_stream, Avi->cpa_blockalign, &dest_length, ACM_STREAMSIZEF_SOURCE ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
MsgDev( D_ERROR, "Couldn't get ACM conversion stream size.\n" );
|
||||
acmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
Avi->cpa_srcbuffer = (byte *)Mem_Alloc( cls.mempool, Avi->cpa_blockalign );
|
||||
Avi->cpa_dstbuffer = (byte *)Mem_Alloc( cls.mempool, dest_length ); // maintained buffer for raw data
|
||||
|
||||
// prep the headers!
|
||||
Avi->cpa_conversion_header.cbStruct = sizeof( ACMSTREAMHEADER );
|
||||
Avi->cpa_conversion_header.fdwStatus = 0;
|
||||
Avi->cpa_conversion_header.dwUser = 0; // no user data
|
||||
Avi->cpa_conversion_header.pbSrc = Avi->cpa_srcbuffer; // source buffer
|
||||
Avi->cpa_conversion_header.cbSrcLength = Avi->cpa_blockalign; // source buffer size
|
||||
Avi->cpa_conversion_header.cbSrcLengthUsed = 0;
|
||||
Avi->cpa_conversion_header.dwSrcUser = 0; // no user data
|
||||
Avi->cpa_conversion_header.pbDst = Avi->cpa_dstbuffer; // dest buffer
|
||||
Avi->cpa_conversion_header.cbDstLength = dest_length; // dest buffer size
|
||||
Avi->cpa_conversion_header.cbDstLengthUsed = 0;
|
||||
Avi->cpa_conversion_header.dwDstUser = 0; // no user data
|
||||
|
||||
if( acmStreamPrepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
MsgDev( D_ERROR, "couldn't prep headers.\n" );
|
||||
acmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
Avi->cpa_blocknum = 0; // start at 0.
|
||||
Avi->cpa_blockpos = 0;
|
||||
Avi->cpa_blockoffset = 0;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START );
|
||||
|
||||
// convert first chunk twice. it often fails the first time. BLACK MAGIC.
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
Avi->audio_bytes_per_sample = (bits >> 3 ) * Avi->audio_header->nChannels;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVI_GetVideoInfo( movie_state_t *Avi, long *xres, long *yres, float *duration )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active )
|
||||
return false;
|
||||
|
||||
if( xres != NULL )
|
||||
*xres = Avi->video_xres;
|
||||
|
||||
if( yres != NULL )
|
||||
*yres = Avi->video_yres;
|
||||
|
||||
if( duration != NULL )
|
||||
*duration = (float)Avi->video_frames / Avi->video_fps;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns a unique frame identifier
|
||||
long AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active )
|
||||
return 0;
|
||||
|
||||
return (time * Avi->video_fps);
|
||||
}
|
||||
|
||||
// gets the raw frame data
|
||||
void AVI_GetVideoFrame( movie_state_t *Avi, byte *outbuffer, long frame )
|
||||
{
|
||||
LPBITMAPINFOHEADER image, frame_info;
|
||||
byte palette[256][4], *pixbuf, *buf_p;
|
||||
int i, columns, column, rows, row, bps;
|
||||
int cbPalBytes = 0, padSize = 0;
|
||||
char *frame_raw;
|
||||
char *in, *out, *bufend;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active ) return;
|
||||
|
||||
if( frame >= Avi->video_frames )
|
||||
frame = Avi->video_frames - 1;
|
||||
|
||||
frame_info = image = (LPBITMAPINFOHEADER)AVIStreamGetFrame( Avi->video_getframe, frame );
|
||||
|
||||
if( image->biSize != 0x28 )
|
||||
{
|
||||
MsgDev( D_ERROR, "AVI_GetVideoFrame: invalid header size %i\n", image->biSize );
|
||||
SCR_StopCinematic();
|
||||
return;
|
||||
}
|
||||
|
||||
// bogus compression? Only non-compressed supported.
|
||||
if( image->biCompression != BI_RGB )
|
||||
{
|
||||
MsgDev( D_ERROR, "AVI_GetVideoFrame: only uncompressed AVI frames supported\n" );
|
||||
SCR_StopCinematic();
|
||||
return;
|
||||
}
|
||||
|
||||
columns = image->biWidth;
|
||||
rows = abs( image->biHeight );
|
||||
|
||||
ASSERT( Avi->video_xres == image->biWidth );
|
||||
ASSERT( Avi->video_yres == image->biHeight );
|
||||
|
||||
if( image->biBitCount <= 8 )
|
||||
{
|
||||
// figure out how many entires are actually in the table
|
||||
if( image->biClrUsed == 0 )
|
||||
{
|
||||
image->biClrUsed = 256;
|
||||
cbPalBytes = (1 << image->biBitCount) * sizeof( RGBQUAD );
|
||||
}
|
||||
else cbPalBytes = image->biClrUsed * sizeof( RGBQUAD );
|
||||
}
|
||||
|
||||
// copy palette
|
||||
Mem_Copy( palette, frame_info + frame_info->biSize, cbPalBytes );
|
||||
buf_p = (byte *)frame_info + frame_info->biSize + cbPalBytes;
|
||||
bps = columns * (image->biBitCount >> 3);
|
||||
|
||||
switch( image->biBitCount )
|
||||
{
|
||||
case 1:
|
||||
padSize = (( 32 - ( image->biWidth % 32 )) / 8 ) % 4;
|
||||
break;
|
||||
case 4:
|
||||
padSize = (( 8 - ( image->biWidth % 8 )) / 2 ) % 4;
|
||||
break;
|
||||
case 16:
|
||||
padSize = ( 4 - ( image->biWidth * 2 % 4 )) % 4;
|
||||
break;
|
||||
case 8:
|
||||
case 24:
|
||||
padSize = ( 4 - ( bps % 4 )) % 4;
|
||||
break;
|
||||
}
|
||||
|
||||
for( row = rows - 1; row >= 0; row-- )
|
||||
{
|
||||
pixbuf = outbuffer + (row * columns * 3);
|
||||
|
||||
for( column = 0; column < columns; column++ )
|
||||
{
|
||||
byte red, green, blue, alpha;
|
||||
word shortPixel;
|
||||
int c, k, palIndex;
|
||||
|
||||
switch( image->biBitCount )
|
||||
{
|
||||
case 1:
|
||||
alpha = *buf_p++;
|
||||
column--; // ingnore main iterations
|
||||
for( c = 0, k = 128; c < 8; c++, k >>= 1 )
|
||||
{
|
||||
red = (!!(alpha & k) == 1 ? 0xFF : 0x00);
|
||||
*pixbuf++ = vid_gammaTable[red];
|
||||
*pixbuf++ = vid_gammaTable[red];
|
||||
*pixbuf++ = vid_gammaTable[red];
|
||||
if( ++column == columns )
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
alpha = *buf_p++;
|
||||
palIndex = alpha >> 4;
|
||||
*pixbuf++ = vid_gammaTable[palette[palIndex][2]];
|
||||
*pixbuf++ = vid_gammaTable[palette[palIndex][1]];
|
||||
*pixbuf++ = vid_gammaTable[palette[palIndex][0]];
|
||||
if( ++column == columns ) break;
|
||||
palIndex = alpha & 0x0F;
|
||||
*pixbuf++ = vid_gammaTable[palette[palIndex][2]];
|
||||
*pixbuf++ = vid_gammaTable[palette[palIndex][1]];
|
||||
*pixbuf++ = vid_gammaTable[palette[palIndex][0]];
|
||||
break;
|
||||
case 8:
|
||||
palIndex = *buf_p++;
|
||||
red = palette[palIndex][2];
|
||||
green = palette[palIndex][1];
|
||||
blue = palette[palIndex][0];
|
||||
alpha = palette[palIndex][3];
|
||||
*pixbuf++ = vid_gammaTable[red];
|
||||
*pixbuf++ = vid_gammaTable[green];
|
||||
*pixbuf++ = vid_gammaTable[blue];
|
||||
break;
|
||||
case 16:
|
||||
shortPixel = *(word *)buf_p, buf_p += 2;
|
||||
*pixbuf++ = vid_gammaTable[(shortPixel & ( 31 << 10 )) >> 7];
|
||||
*pixbuf++ = vid_gammaTable[(shortPixel & ( 31 << 5 )) >> 2];
|
||||
*pixbuf++ = vid_gammaTable[(shortPixel & ( 31 )) << 3];
|
||||
break;
|
||||
case 24:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
*pixbuf++ = vid_gammaTable[red];
|
||||
*pixbuf++ = vid_gammaTable[green];
|
||||
*pixbuf++ = vid_gammaTable[blue];
|
||||
break;
|
||||
case 32:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
alpha = *buf_p++;
|
||||
*pixbuf++ = vid_gammaTable[red];
|
||||
*pixbuf++ = vid_gammaTable[green];
|
||||
*pixbuf++ = vid_gammaTable[blue];
|
||||
break;
|
||||
default:
|
||||
MsgDev( D_ERROR, "AVI_GetVideoFrame: illegal pixel size\n" );
|
||||
SCR_StopCinematic();
|
||||
return;
|
||||
}
|
||||
}
|
||||
buf_p += padSize; // actual only for 4-bit bmps
|
||||
}
|
||||
return;
|
||||
|
||||
|
||||
|
||||
frame_raw = (char *)frame_info + frame_info->biSize + frame_info->biClrUsed * sizeof( RGBQUAD );
|
||||
out = outbuffer;
|
||||
|
||||
// convert BGR to RGB and flip upside down
|
||||
for( i = Avi->video_yres - 1; i >= 0; i-- )
|
||||
{
|
||||
in = frame_raw + i * Avi->video_xres * 3;
|
||||
bufend = in + Avi->video_xres * 3;
|
||||
for ( ; in < bufend; in += 3 )
|
||||
{
|
||||
*out++ = (char)vid_gammaTable[(byte)in[2]];
|
||||
*out++ = (char)vid_gammaTable[(byte)in[1]];
|
||||
*out++ = (char)vid_gammaTable[(byte)in[0]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( !Avi->active || Avi->audio_stream == NULL || snd_info == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_info->rate = Avi->audio_header->nSamplesPerSec;
|
||||
snd_info->channels = Avi->audio_header->nChannels;
|
||||
|
||||
if( Avi->audio_codec == WAVE_FORMAT_PCM ) // uncompressed audio!
|
||||
snd_info->width = ( Avi->audio_bytes_per_sample > Avi->audio_header->nChannels ) ? 2 : 1;
|
||||
else snd_info->width = 2; // assume compressed audio is always 16 bit
|
||||
|
||||
snd_info->size = snd_info->rate * snd_info->width * snd_info->channels;
|
||||
snd_info->loopStart = 0; // HACKHACK: use loopStart as streampos
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// sync the current audio read to a specific offset
|
||||
bool AVI_SeekPosition( movie_state_t *Avi, dword offset )
|
||||
{
|
||||
int breaker;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( offset < Avi->cpa_blockoffset ) // well, shit. we can't seek backwards... restart
|
||||
{
|
||||
if( Avi->cpa_blockoffset - offset < 500000 )
|
||||
return false; // don't bother if it's gonna catch up soon (cheap hack! works!)
|
||||
|
||||
Avi->cpa_blocknum = 0; // start at 0, eh.
|
||||
Avi->cpa_blockpos = 0;
|
||||
Avi->cpa_blockoffset = 0;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START );
|
||||
|
||||
// convert first chunk twice. it often fails the first time. BLACK MAGIC.
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
}
|
||||
|
||||
// now then: seek forwards to the required block
|
||||
breaker = 30; // maximum zero blocks: anti-freeze protection
|
||||
|
||||
while( Avi->cpa_blockoffset + Avi->cpa_conversion_header.cbDstLengthUsed < offset )
|
||||
{
|
||||
Avi->cpa_blocknum++;
|
||||
Avi->cpa_blockoffset += Avi->cpa_conversion_header.cbDstLengthUsed;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
if( Avi->cpa_conversion_header.cbDstLengthUsed == 0 )
|
||||
breaker--;
|
||||
else breaker = 30;
|
||||
|
||||
if( breaker <= 0 )
|
||||
return false;
|
||||
|
||||
Avi->cpa_blockpos = 0;
|
||||
}
|
||||
|
||||
// seek to the right position inside the block
|
||||
Avi->cpa_blockpos = offset - Avi->cpa_blockoffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get a chunk of audio from the stream (in bytes)
|
||||
fs_offset_t AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length )
|
||||
{
|
||||
int i;
|
||||
long result = 0;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
// zero data past the end of the file
|
||||
if( offset + length > Avi->audio_length )
|
||||
{
|
||||
if( offset <= Avi->audio_length )
|
||||
{
|
||||
long remaining_length = Avi->audio_length - offset;
|
||||
|
||||
AVI_GetAudioChunk( Avi, audiodata, offset, remaining_length );
|
||||
|
||||
for( i = remaining_length; i < length; i++ )
|
||||
audiodata[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < length; i++ )
|
||||
audiodata[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// uncompressed audio!
|
||||
if( Avi->audio_codec == WAVE_FORMAT_PCM )
|
||||
{
|
||||
// very simple - read straight out
|
||||
AVIStreamRead( Avi->audio_stream, offset / Avi->audio_bytes_per_sample, length / Avi->audio_bytes_per_sample, audiodata, length, &result, NULL );
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// compressed audio!
|
||||
result = 0;
|
||||
|
||||
// seek to correct chunk and all that stuff
|
||||
if( !AVI_SeekPosition( Avi, offset ))
|
||||
return 0; // don't continue if we're waiting for the play pointer to catch up
|
||||
|
||||
while( length > 0 )
|
||||
{
|
||||
long blockread = Avi->cpa_conversion_header.cbDstLengthUsed - Avi->cpa_blockpos;
|
||||
|
||||
if( blockread <= 0 ) // read next
|
||||
{
|
||||
Avi->cpa_blocknum++;
|
||||
Avi->cpa_blockoffset += Avi->cpa_conversion_header.cbDstLengthUsed;
|
||||
|
||||
AVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
acmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
Avi->cpa_blockpos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( blockread > length )
|
||||
blockread = length;
|
||||
|
||||
// copy the data
|
||||
Mem_Copy( audiodata + result, (void *)( Avi->cpa_dstbuffer + Avi->cpa_blockpos ), blockread );
|
||||
|
||||
Avi->cpa_blockpos += blockread;
|
||||
result += blockread;
|
||||
length -= blockread;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void AVI_CloseVideo( movie_state_t *Avi )
|
||||
{
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
if( Avi->active )
|
||||
{
|
||||
AVIStreamGetFrameClose( Avi->video_getframe );
|
||||
|
||||
if( Avi->audio_stream != NULL )
|
||||
{
|
||||
AVIStreamRelease( Avi->audio_stream );
|
||||
Mem_Free( Avi->audio_header );
|
||||
|
||||
if( Avi->audio_codec != WAVE_FORMAT_PCM )
|
||||
{
|
||||
acmStreamUnprepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 );
|
||||
acmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
Mem_Free( Avi->cpa_srcbuffer );
|
||||
Mem_Free( Avi->cpa_dstbuffer );
|
||||
}
|
||||
}
|
||||
|
||||
AVIStreamRelease( Avi->video_stream );
|
||||
}
|
||||
|
||||
Mem_Set( Avi, 0, sizeof( movie_state_t ));
|
||||
}
|
||||
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, bool load_audio )
|
||||
{
|
||||
AVISTREAMINFO stream_info;
|
||||
long opened_streams = 0;
|
||||
LONG hr;
|
||||
|
||||
ASSERT( Avi != NULL );
|
||||
|
||||
// default state: non-working.
|
||||
Avi->active = false;
|
||||
|
||||
// load the AVI
|
||||
hr = AVIFileOpen( &Avi->pfile, filename, OF_SHARE_DENY_WRITE, 0L );
|
||||
|
||||
if( hr != 0 ) // error opening AVI:
|
||||
{
|
||||
switch( hr )
|
||||
{
|
||||
case AVIERR_BADFORMAT:
|
||||
MsgDev( D_ERROR, "corrupt file or unknown format.\n" );
|
||||
break;
|
||||
case AVIERR_MEMORY:
|
||||
MsgDev( D_ERROR, "insufficient memory to open file.\n" );
|
||||
break;
|
||||
case AVIERR_FILEREAD:
|
||||
MsgDev( D_ERROR, "disk error reading file.\n" );
|
||||
break;
|
||||
case AVIERR_FILEOPEN:
|
||||
MsgDev( D_ERROR, "disk error opening file.\n" );
|
||||
break;
|
||||
case REGDB_E_CLASSNOTREG:
|
||||
default:
|
||||
MsgDev( D_ERROR, "no handler found (or file not found).\n" );
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Avi->video_stream = Avi->audio_stream = NULL;
|
||||
|
||||
// open the streams until a stream is not available.
|
||||
while( 1 )
|
||||
{
|
||||
PAVISTREAM stream = NULL;
|
||||
|
||||
if( AVIFileGetStream( Avi->pfile, &stream, 0L, opened_streams++ ) != AVIERR_OK )
|
||||
break;
|
||||
|
||||
if( stream == NULL )
|
||||
break;
|
||||
|
||||
AVIStreamInfo( stream, &stream_info, sizeof( stream_info ));
|
||||
|
||||
if( stream_info.fccType == streamtypeVIDEO && Avi->video_stream == NULL )
|
||||
{
|
||||
Avi->video_stream = stream;
|
||||
Avi->video_frames = stream_info.dwLength;
|
||||
Avi->video_xres = stream_info.rcFrame.right - stream_info.rcFrame.left;
|
||||
Avi->video_yres = stream_info.rcFrame.bottom - stream_info.rcFrame.top;
|
||||
Avi->video_fps = (float)stream_info.dwRate / (float)stream_info.dwScale;
|
||||
}
|
||||
else if( stream_info.fccType == streamtypeAUDIO && Avi->audio_stream == NULL && load_audio )
|
||||
{
|
||||
long size;
|
||||
|
||||
Avi->audio_stream = stream;
|
||||
|
||||
// read the audio header
|
||||
AVIStreamReadFormat( Avi->audio_stream, AVIStreamStart( Avi->audio_stream ), 0, &size );
|
||||
|
||||
Avi->audio_header = (WAVEFORMAT *)Mem_Alloc( cls.mempool, size );
|
||||
AVIStreamReadFormat( Avi->audio_stream, AVIStreamStart( Avi->audio_stream ), Avi->audio_header, &size );
|
||||
Avi->audio_header_size = size;
|
||||
Avi->audio_codec = Avi->audio_header->wFormatTag;
|
||||
|
||||
// length of converted audio in samples
|
||||
Avi->audio_length = (long)((float)stream_info.dwLength / Avi->audio_header->nAvgBytesPerSec );
|
||||
Avi->audio_length *= Avi->audio_header->nSamplesPerSec;
|
||||
|
||||
if( Avi->audio_codec != WAVE_FORMAT_PCM )
|
||||
{
|
||||
if( !AVI_ACMConvertAudio( Avi ))
|
||||
{
|
||||
Mem_Free( Avi->audio_header );
|
||||
Avi->audio_stream = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else Avi->audio_bytes_per_sample = Avi->audio_header->nBlockAlign;
|
||||
Avi->audio_length *= Avi->audio_bytes_per_sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
AVIStreamRelease( stream );
|
||||
}
|
||||
}
|
||||
|
||||
// display error message-stream not found.
|
||||
if( Avi->video_stream == NULL )
|
||||
{
|
||||
if( Avi->pfile ) // If file is open, close it
|
||||
AVIFileRelease( Avi->pfile );
|
||||
MsgDev( D_ERROR, "couldn't find a valid video stream.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
AVIFileRelease( Avi->pfile ); // release the file
|
||||
|
||||
Avi->video_getframe = AVIStreamGetFrameOpen( Avi->video_stream, NULL ); // open the frame getter
|
||||
|
||||
if( Avi->video_getframe == NULL )
|
||||
{
|
||||
MsgDev( D_ERROR, "error attempting to read video frames.\n" );
|
||||
return; // couldn't open frame getter.
|
||||
}
|
||||
|
||||
Avi->active = true; // done
|
||||
}
|
||||
|
||||
/*
|
||||
=================================================================
|
||||
|
||||
AVI PLAYING
|
||||
|
||||
=================================================================
|
||||
*/
|
||||
|
||||
static long xres, yres;
|
||||
static float video_duration;
|
||||
static float cin_time;
|
||||
static int cin_frame;
|
||||
static byte *cin_data; // dynamically allocated array
|
||||
static wavdata_t cin_audio;
|
||||
|
||||
void SCR_RebuildGammaTable( void )
|
||||
{
|
||||
float g;
|
||||
int i;
|
||||
|
||||
g = bound( 0.5f, vid_gamma->value, 2.3f );
|
||||
|
||||
// screenshots gamma
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
if( g == 1 ) vid_gammaTable[i] = i;
|
||||
else vid_gammaTable[i] = bound( 0, pow( i * ( 1.0f / 255.0f ), g ) * 255.0f, 255 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_RunCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_RunCinematic( void )
|
||||
{
|
||||
if( !avi.active ) return;
|
||||
|
||||
if( vid_gamma->modified )
|
||||
{
|
||||
SCR_RebuildGammaTable();
|
||||
vid_gamma->modified = false;
|
||||
}
|
||||
|
||||
// advances cinematic time
|
||||
cin_time += cls.frametime;
|
||||
|
||||
// stop the video after it finishes
|
||||
if( cin_time > video_duration + 0.1f )
|
||||
{
|
||||
SCR_StopCinematic();
|
||||
return;
|
||||
}
|
||||
|
||||
// read the next frame
|
||||
cin_frame = AVI_GetVideoFrameNumber( &avi, cin_time );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_DrawCinematic
|
||||
|
||||
Returns true if a cinematic is active, meaning the view rendering
|
||||
should be skipped
|
||||
==================
|
||||
*/
|
||||
bool SCR_DrawCinematic( void )
|
||||
{
|
||||
static int last_frame = -1;
|
||||
bool redraw = false;
|
||||
|
||||
if( !re || cin_time <= 0.0f )
|
||||
return false;
|
||||
|
||||
if( cin_frame != last_frame )
|
||||
{
|
||||
AVI_GetVideoFrame( &avi, cin_data, cin_frame );
|
||||
last_frame = cin_frame;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
re->DrawStretchRaw( 0, 0, scr_width->integer, scr_height->integer, xres, yres, cin_data, redraw );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_PlayCinematic
|
||||
==================
|
||||
*/
|
||||
bool SCR_PlayCinematic( const char *arg )
|
||||
{
|
||||
string path;
|
||||
const char *fullpath;
|
||||
|
||||
com.snprintf( path, sizeof( path ), "media/%s.avi", arg );
|
||||
fullpath = FS_GetDiskPath( path );
|
||||
|
||||
if( FS_FileExists( path ) && !fullpath )
|
||||
{
|
||||
MsgDev( D_ERROR, "couldn't load %s from packfile. Please extract it\n", path );
|
||||
return false;
|
||||
}
|
||||
|
||||
AVI_OpenVideo( &avi, fullpath, true );
|
||||
if( !avi.active )
|
||||
{
|
||||
AVI_CloseVideo( &avi );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !( AVI_GetVideoInfo( &avi, &xres, &yres, &video_duration ))) // couldn't open this at all.
|
||||
{
|
||||
AVI_CloseVideo( &avi );
|
||||
return false;
|
||||
}
|
||||
|
||||
cin_data = Mem_Realloc( cls.mempool, cin_data, xres * yres * 3 );
|
||||
|
||||
if( AVI_GetAudioInfo( &avi, &cin_audio ))
|
||||
{
|
||||
// begin streaming
|
||||
S_StopAllSounds();
|
||||
S_StartStreaming();
|
||||
}
|
||||
|
||||
UI_SetActiveMenu( false );
|
||||
SCR_RebuildGammaTable();
|
||||
|
||||
cls.state = ca_cinematic;
|
||||
cin_time = 0.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
long SCR_GetAudioChunk( char *rawdata, long length )
|
||||
{
|
||||
int r;
|
||||
|
||||
r = AVI_GetAudioChunk( &avi, rawdata, cin_audio.loopStart, length );
|
||||
cin_audio.loopStart += r; // advance play position
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
wavdata_t *SCR_GetMovieInfo( void )
|
||||
{
|
||||
if( avi.active )
|
||||
return &cin_audio;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_StopCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_StopCinematic( void )
|
||||
{
|
||||
AVI_CloseVideo( &avi );
|
||||
|
||||
cls.state = ca_disconnected;
|
||||
UI_SetActiveMenu( true );
|
||||
S_StopStreaming();
|
||||
cin_time = 0.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_InitCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_InitCinematic( void )
|
||||
{
|
||||
AVIFileInit();
|
||||
|
||||
// used for movie gamma correction
|
||||
vid_gamma = Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE, "gamma amount" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_FreeCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_FreeCinematic( void )
|
||||
{
|
||||
AVIFileExit();
|
||||
}
|
|
@ -54,7 +54,7 @@ BSC32=bscmake.exe
|
|||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /opt:nowin98
|
||||
# ADD LINK32 user32.lib msvcrt.lib /nologo /subsystem:windows /dll /pdb:none /machine:I386 /nodefaultlib:"libc.lib" /opt:nowin98
|
||||
# ADD LINK32 user32.lib msvcrt.lib vfw32.lib msacm32.lib gdi32.lib /nologo /subsystem:windows /dll /pdb:none /machine:I386 /nodefaultlib:"libc.lib" /opt:nowin98
|
||||
# SUBTRACT LINK32 /debug /nodefaultlib
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\engine\!release
|
||||
|
@ -91,7 +91,7 @@ BSC32=bscmake.exe
|
|||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
|
||||
# ADD LINK32 user32.lib msvcrtd.lib /nologo /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /pdbtype:sept
|
||||
# ADD LINK32 user32.lib msvcrtd.lib vfw32.lib msacm32.lib gdi32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /pdbtype:sept
|
||||
# SUBTRACT LINK32 /incremental:no /map /nodefaultlib
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\engine\!debug
|
||||
|
@ -242,6 +242,10 @@ SOURCE=.\common\keys.c
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\common\movie.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\common\net_buffer.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|
|
@ -166,6 +166,8 @@ bool Host_InitSound( void )
|
|||
si.AmbientLevels = CM_AmbientLevels;
|
||||
si.GetClientEdict = CL_GetEntityByIndex;
|
||||
si.GetServerTime = CL_GetServerTime;
|
||||
si.GetAudioChunk = SCR_GetAudioChunk;
|
||||
si.GetMovieInfo = SCR_GetMovieInfo;
|
||||
si.IsInMenu = CL_IsInMenu;
|
||||
si.IsActive = CL_IsInGame;
|
||||
|
||||
|
|
|
@ -1739,6 +1739,9 @@ void SV_SaveGame( const char *pName )
|
|||
if( FS_FileExists( va( "save/%s.%s", savename, SI->savshot_ext )))
|
||||
FS_Delete( va( "save/%s.%s", savename, SI->savshot_ext ));
|
||||
|
||||
// HACKHACK: unload previous shader from memory
|
||||
if( re ) re->FreeShader( va( "save/%s.%s", savename, SI->savshot_ext ));
|
||||
|
||||
SV_BuildSaveComment( comment, sizeof( comment ));
|
||||
SV_SaveGameSlot( savename, comment );
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#define GetScreenInfo (*g_engfuncs.pfnGetScreenInfo)
|
||||
#define GetGameInfo (*g_engfuncs.pfnGetGameInfo)
|
||||
|
||||
#define DRAW_LOGO (*g_engfuncs.pfnDrawLogo)
|
||||
#define GetLogoWidth (*g_engfuncs.pfnGetLogoWidth)
|
||||
#define GetLogoHeight (*g_engfuncs.pfnGetLogoHeight)
|
||||
|
||||
#define PIC_Load (*g_engfuncs.pfnPIC_Load)
|
||||
#define PIC_Free (*g_engfuncs.pfnPIC_Free)
|
||||
#define PLAY_SOUND (*g_engfuncs.pfnPlayLocalSound)
|
||||
|
|
|
@ -92,6 +92,30 @@ static void UI_MsgBox_Ownerdraw( void *self )
|
|||
UI_FillRect( item->x, item->y, item->width, item->height, uiPromptBgColor );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
UI_Background_Ownerdraw
|
||||
=================
|
||||
*/
|
||||
static void UI_Background_Ownerdraw( void *self )
|
||||
{
|
||||
menuCommon_s *item = (menuCommon_s *)self;
|
||||
|
||||
UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic );
|
||||
|
||||
float logoWidth, logoHeight, logoPosY;
|
||||
float scaleX, scaleY;
|
||||
|
||||
scaleX = ScreenWidth / 640.0f;
|
||||
scaleY = ScreenHeight / 480.0f;
|
||||
|
||||
logoWidth = GetLogoWidth() * scaleX;
|
||||
logoHeight = GetLogoHeight() * scaleY;
|
||||
logoPosY = 70 * scaleY; // 70 it's empirically determined value (magic number)
|
||||
|
||||
DRAW_LOGO( "logo.avi", 0, logoPosY, logoWidth, logoHeight );
|
||||
}
|
||||
|
||||
static void UI_QuitDialog( void )
|
||||
{
|
||||
// toggle main menu between active\inactive
|
||||
|
@ -307,6 +331,7 @@ static void UI_Main_Init( void )
|
|||
uiMain.background.generic.width = 1024;
|
||||
uiMain.background.generic.height = 768;
|
||||
uiMain.background.pic = ART_BACKGROUND;
|
||||
uiMain.background.generic.ownerdraw = UI_Background_Ownerdraw;
|
||||
|
||||
uiMain.console.generic.id = ID_CONSOLE;
|
||||
uiMain.console.generic.type = QMTYPE_ACTION;
|
||||
|
|
|
@ -2893,6 +2893,25 @@ bool FS_FileExists( const char *filename, bool gamedironly )
|
|||
return false;
|
||||
}
|
||||
|
||||
const char *FS_GetDiskPath( const char *name, bool gamedironly )
|
||||
{
|
||||
int index;
|
||||
searchpath_t *search;
|
||||
|
||||
search = FS_FindFile( name, &index, gamedironly );
|
||||
|
||||
if( search )
|
||||
{
|
||||
if( index != -1 )
|
||||
{
|
||||
// file in pack or wad
|
||||
return NULL;
|
||||
}
|
||||
return va( "%s%s", search->filename, name );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FS_FindLibrary
|
||||
|
|
|
@ -310,6 +310,7 @@ const char *FS_FileExtension( const char *in );
|
|||
void FS_DefaultExtension( char *path, const char *extension );
|
||||
bool FS_GetParmFromCmdLine( char *parm, char *out, size_t size );
|
||||
void FS_ExtractFilePath( const char* const path, char* dest );
|
||||
const char *FS_GetDiskPath( const char *name, bool gamedironly );
|
||||
void FS_UpdateEnvironmentVariables( void );
|
||||
const char *FS_FileWithoutPath( const char *in );
|
||||
extern char sys_rootdir[];
|
||||
|
|
|
@ -103,6 +103,7 @@ void Sys_GetStdAPI( void )
|
|||
com.Com_FileTime = FS_FileTime; // same as Com_FileExists but return filetime
|
||||
com.Com_FileExtension = FS_FileExtension; // return extension of file
|
||||
com.Com_RemovePath = FS_FileWithoutPath; // return file without path
|
||||
com.Com_DiskPath = FS_GetDiskPath; // build fullpath for disk files
|
||||
com.Com_StripExtension = FS_StripExtension; // remove extension if present
|
||||
com.Com_StripFilePath = FS_ExtractFilePath; // get file path without filename.ext
|
||||
com.Com_DefaultExtension = FS_DefaultExtension; // append extension if not present
|
||||
|
|
|
@ -529,6 +529,7 @@ typedef struct stdilib_api_s
|
|||
long (*Com_FileTime)( const char *filename, bool gamedir ); // same as Com_FileExists but return filetime
|
||||
const char *(*Com_FileExtension)( const char *in ); // return extension of file
|
||||
const char *(*Com_RemovePath)( const char *in ); // return filename without path
|
||||
const char *(*Com_DiskPath)( const char *in, bool gamedir );// return disk path for unpacked files
|
||||
void (*Com_StripExtension)(char *path); // remove extension if present
|
||||
void (*Com_StripFilePath)(const char* const src, char* dst);// get file path without filename.ext
|
||||
void (*Com_DefaultExtension)(char *path, const char *ext ); // append extension if not present
|
||||
|
@ -808,6 +809,7 @@ filesystem manager
|
|||
#define FS_FileExists( file ) com.Com_FileExists( file, false )
|
||||
#define FS_FileSize( file ) com.Com_FileSize( file, false )
|
||||
#define FS_FileTime( file ) com.Com_FileTime( file, false )
|
||||
#define FS_GetDiskPath( file ) com.Com_DiskPath( file, false )
|
||||
#define FS_FileExistsEx com.Com_FileExists
|
||||
#define FS_FileSizeEx com.Com_FileSize
|
||||
#define FS_FileTimeEx com.Com_FileTime
|
||||
|
|
|
@ -172,7 +172,7 @@ typedef struct render_exp_s
|
|||
bool (*ScrShot)( const char *filename, int shot_type ); // write screenshot with same name
|
||||
bool (*EnvShot)( const char *filename, uint size, const float *vieworg, bool skyshot );
|
||||
void (*LightForPoint)( const vec3_t point, vec3_t ambientLight );
|
||||
void (*DrawStretchRaw)( int x, int y, int w, int h, int cols, int rows, byte *data, bool redraw );
|
||||
void (*DrawStretchRaw)( float x, float y, float w, float h, int cols, int rows, byte *data, bool redraw );
|
||||
void (*DrawStretchPic)( float x, float y, float w, float h, float s1, float t1, float s2, float t2, shader_t shader );
|
||||
int (*WorldToScreen)( const float *world, float *screen );
|
||||
void (*ScreenToWorld)( const float *screen, float *world );
|
||||
|
|
|
@ -66,6 +66,8 @@ typedef struct vsound_imp_s
|
|||
bool (*GetEntitySpatialization)( int entnum, vec3_t origin, vec3_t velocity );
|
||||
void (*AmbientLevels)( const vec3_t p, byte *pvolumes );
|
||||
struct cl_entity_s *(*GetClientEdict)( int index );
|
||||
long (*GetAudioChunk)( char *rawdata, long length ); // movie soundtrack update
|
||||
wavdata_t *(*GetMovieInfo)( void ); // params for soundtrack
|
||||
float (*GetServerTime)( void );
|
||||
bool (*IsInMenu)( void ); // returns true when client is in-menu
|
||||
bool (*IsActive)( void ); // returns true when client is completely in-game
|
||||
|
|
|
@ -687,18 +687,7 @@ void S_UpdateChannels( void )
|
|||
|
||||
// updates DMA time
|
||||
soundtime = SNDDMA_GetSoundtime();
|
||||
#if 0
|
||||
// check to make sure that we haven't overshot
|
||||
if( paintedtime < soundtime ) paintedtime = soundtime;
|
||||
|
||||
// mix ahead of current position
|
||||
endtime = soundtime + s_mixahead->value * SOUND_DMA_SPEED;
|
||||
|
||||
// mix to an even submission block size
|
||||
endtime = (endtime + dma.submission_chunk - 1) & ~(dma.submission_chunk - 1);
|
||||
samps = dma.samples >> 1;
|
||||
if( endtime - soundtime > samps ) endtime = soundtime + samps;
|
||||
#else
|
||||
// soundtime - total samples that have been played out to hardware at dmaspeed
|
||||
// paintedtime - total samples that have been mixed at speed
|
||||
// endtime - target for samples in mixahead buffer at speed
|
||||
|
@ -714,7 +703,6 @@ void S_UpdateChannels( void )
|
|||
// boundaries of 4 samples. This is important when upsampling from 11khz -> 44khz.
|
||||
endtime -= ( endtime - paintedtime ) & 0x3;
|
||||
}
|
||||
#endif
|
||||
|
||||
MIX_PaintChannels( endtime );
|
||||
|
||||
|
@ -788,6 +776,7 @@ void S_RenderFrame( ref_params_t *fd )
|
|||
}
|
||||
|
||||
S_StreamBackgroundTrack ();
|
||||
S_StreamSoundTrack ();
|
||||
|
||||
// mix some sound
|
||||
S_UpdateChannels ();
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#include "byteorder.h"
|
||||
|
||||
portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
|
||||
int s_rawend;
|
||||
static bg_track_t s_bgTrack;
|
||||
int s_rawend;
|
||||
|
||||
/*
|
||||
=================
|
||||
|
@ -78,6 +78,7 @@ void S_StreamBackgroundTrack( void )
|
|||
int r, fileBytes;
|
||||
|
||||
if( !s_bgTrack.stream ) return;
|
||||
if( s_listener.streaming ) return; // we are playing movie or somewhat
|
||||
|
||||
// don't bother playing anything if musicvolume is 0
|
||||
if( !s_musicvolume->value || s_listener.paused ) return;
|
||||
|
@ -145,6 +146,8 @@ S_StartStreaming
|
|||
*/
|
||||
void S_StartStreaming( void )
|
||||
{
|
||||
// begin streaming movie soundtrack
|
||||
s_listener.streaming = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -154,7 +157,62 @@ S_StopStreaming
|
|||
*/
|
||||
void S_StopStreaming( void )
|
||||
{
|
||||
// clear s_rawend here ?
|
||||
s_listener.streaming = false;
|
||||
s_rawend = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_StreamSoundTrack
|
||||
=================
|
||||
*/
|
||||
void S_StreamSoundTrack( void )
|
||||
{
|
||||
int bufferSamples;
|
||||
int fileSamples;
|
||||
byte raw[MAX_RAW_SAMPLES];
|
||||
int r, fileBytes;
|
||||
|
||||
if( !s_listener.streaming || s_listener.paused ) return;
|
||||
|
||||
// see how many samples should be copied into the raw buffer
|
||||
if( s_rawend < soundtime )
|
||||
s_rawend = soundtime;
|
||||
|
||||
while( s_rawend < soundtime + MAX_RAW_SAMPLES )
|
||||
{
|
||||
wavdata_t *info = si.GetMovieInfo();
|
||||
|
||||
bufferSamples = MAX_RAW_SAMPLES - (s_rawend - soundtime);
|
||||
|
||||
// decide how much data needs to be read from the file
|
||||
fileSamples = bufferSamples * info->rate / SOUND_DMA_SPEED;
|
||||
|
||||
// our max buffer size
|
||||
fileBytes = fileSamples * ( info->width * info->channels );
|
||||
|
||||
if( fileBytes > sizeof( raw ))
|
||||
{
|
||||
fileBytes = sizeof( raw );
|
||||
fileSamples = fileBytes / ( info->width * info->channels );
|
||||
}
|
||||
|
||||
// read audio stream
|
||||
r = si.GetAudioChunk( raw, fileBytes );
|
||||
|
||||
if( r < fileBytes )
|
||||
{
|
||||
fileBytes = r;
|
||||
fileSamples = r / ( info->width * info->channels );
|
||||
}
|
||||
|
||||
if( r > 0 )
|
||||
{
|
||||
// add to raw buffer
|
||||
S_StreamRawSamples( fileSamples, info->rate, info->width, info->channels, raw );
|
||||
}
|
||||
else break; // no more samples for this frame
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -181,7 +239,8 @@ void S_StreamRawSamples( int samples, int rate, int width, int channels, const b
|
|||
(a = in[src+(s)],\
|
||||
b = (src < incount - channels) ? (in[src+channels+(s)]) : 0))
|
||||
|
||||
#define LERP_SAMPLE (((((b - a) * (samplefrac & 255)) >> 8) + a))
|
||||
// NOTE: disable lerping for cinematic sountracks
|
||||
#define LERP_SAMPLE s_listener.streaming ? a : (((((b - a) * (samplefrac & 255)) >> 8) + a))
|
||||
|
||||
#define RESAMPLE_RAW \
|
||||
if( channels == 2 ) { \
|
||||
|
|
|
@ -153,6 +153,7 @@ typedef struct
|
|||
bool active;
|
||||
bool inmenu; // listener in-menu ?
|
||||
bool paused;
|
||||
bool streaming; // playing AVI-file
|
||||
} listener_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -238,10 +239,6 @@ void DSP_Process( int idsp, portable_samplepair_t *pbfront, int sampleCount );
|
|||
float DSP_GetGain( int idsp );
|
||||
void DSP_ClearState( void );
|
||||
|
||||
void SX_Init( void );
|
||||
void SX_Free( void );
|
||||
void SX_RoomFX( portable_samplepair_t *pbuf, int sampleCount );
|
||||
|
||||
bool S_Init( void *hInst );
|
||||
void S_Shutdown( void );
|
||||
void S_Activate( bool active, void *hInst );
|
||||
|
@ -273,6 +270,7 @@ void SND_CloseMouth( channel_t *ch );
|
|||
//
|
||||
void S_StartStreaming( void );
|
||||
void S_StopStreaming( void );
|
||||
void S_StreamSoundTrack( void );
|
||||
void S_StreamRawSamples( int samples, int rate, int width, int channels, const byte *data );
|
||||
void S_StartBackgroundTrack( const char *intro, const char *loop );
|
||||
void S_StreamBackgroundTrack( void );
|
||||
|
|
|
@ -99,7 +99,7 @@ void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, f
|
|||
R_DrawStretchRaw
|
||||
=============
|
||||
*/
|
||||
void R_DrawStretchRaw( int x, int y, int w, int h, int cols, int rows, const byte *data, bool dirty )
|
||||
void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, bool dirty )
|
||||
{
|
||||
if( !GL_Support( R_ARB_TEXTURE_NPOT_EXT ))
|
||||
{
|
||||
|
@ -118,17 +118,23 @@ void R_DrawStretchRaw( int x, int y, int w, int h, int cols, int rows, const byt
|
|||
if( rows > glConfig.max_2d_texture_size )
|
||||
Host_Error( "R_DrawStretchRaw: size exceeds hardware limits (%i > %i)\n", rows, glConfig.max_2d_texture_size );
|
||||
|
||||
if( pic_mbuffer.infokey != -1 )
|
||||
{
|
||||
R_RenderMeshBuffer( &pic_mbuffer );
|
||||
pic_mbuffer.infokey = -1;
|
||||
}
|
||||
|
||||
GL_Bind( 0, tr.cinTexture );
|
||||
|
||||
if( cols == tr.cinTexture->width && rows == tr.cinTexture->height )
|
||||
{
|
||||
if( dirty ) pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
|
||||
if( dirty ) pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, data );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.cinTexture->width = cols;
|
||||
tr.cinTexture->height = rows;
|
||||
if( dirty ) pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
|
||||
if( dirty ) pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, cols, rows, 0, GL_RGB, GL_UNSIGNED_BYTE, data );
|
||||
}
|
||||
|
||||
R_CheckForErrors();
|
||||
|
|
|
@ -529,7 +529,7 @@ void R_ShutdownOcclusionQueries( void );
|
|||
void R_Set2DMode( bool enable );
|
||||
void R_DrawSetColor( const void *data );
|
||||
void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, shader_t shadernum );
|
||||
void R_DrawStretchRaw( int x, int y, int w, int h, int cols, int rows, const byte *data, bool redraw );
|
||||
void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, bool redraw );
|
||||
void R_DrawSetParms( shader_t handle, kRenderMode_t rendermode, int frame );
|
||||
void R_DrawGetParms( int *w, int *h, int *f, int frame, shader_t shader );
|
||||
void Tri_RenderMode( const kRenderMode_t mode );
|
||||
|
|
Reference in New Issue