01 Oct 2010

This commit is contained in:
g-cont 2010-10-01 00:00:00 +04:00 committed by Alibek Omarov
parent ed98f35b45
commit ada0238004
30 changed files with 2113 additions and 149 deletions

View File

@ -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
}
}
}
}

View File

@ -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 );

View File

@ -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" );

View File

@ -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,

View File

@ -408,6 +408,7 @@ void SCR_Shutdown( void )
Cmd_RemoveCommand( "viewpos" );
UI_SetActiveMenu( false );
SCR_FreeCinematic();
UI_UnloadProgs();
scr_init = false;
}

View File

@ -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();
}

200
engine/client/cl_video.old Normal file
View File

@ -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;
}

View File

@ -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 );
//

View File

@ -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, ... );

View File

@ -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

View File

@ -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;
}
}

View File

@ -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
{

564
engine/common/movie.c Normal file
View File

@ -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];
}

848
engine/common/movie.old Normal file
View File

@ -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();
}

View File

@ -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

View 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;

View File

@ -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 );

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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[];

View File

@ -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

View File

@ -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

View File

@ -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 );

View File

@ -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

View File

@ -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 ();

View File

@ -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 ) { \

View File

@ -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 );

View File

@ -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();

View File

@ -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 );