static void NET_QueuePacket( int length, const void *data, netadr_t to,
                             int offset )
{
	packetQueue_t *newp, *next = packetQueue;

	if ( offset > 999 )
	{
		offset = 999;
	}

	newp = (packetQueue_t*) S_Malloc( sizeof( packetQueue_t ) );
	newp->data = (byte*) S_Malloc( length );
	Com_Memcpy( newp->data, data, length );
	newp->length = length;
	newp->to = to;
	newp->release = Sys_Milliseconds() + ( int )( ( float ) offset / com_timescale->value );
	newp->next = nullptr;

	if ( !packetQueue )
	{
		packetQueue = newp;
		return;
	}

	while ( next )
	{
		if ( !next->next )
		{
			next->next = newp;
			return;
		}

		next = next->next;
	}
}
Exemple #2
0
/*
============
Cmd_AddCommand
============
*/
qboolean Cmd_AddCommandGeneric( const char *cmd_name, const char* helptext, xcommand_t function, qboolean warn, int power ) {

	cmd_function_t  *cmd;

	// fail if the command already exists
	for ( cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
		if ( !strcmp( cmd_name, cmd->name )) {
			// allow completion-only commands to be silently doubled
			if ( function != NULL && warn) {
				Com_PrintWarning( "Cmd_AddCommand: %s already defined\n", cmd_name );
			}
			return qfalse;
		}
	}
	// use a small malloc to avoid zone fragmentation
	if(helptext != NULL)
	{
		cmd = S_Malloc( sizeof( cmd_function_t ) + strlen(cmd_name) + 1 + strlen(helptext) + 1);
		strcpy((char*)(cmd +1) + strlen(cmd_name) +1, helptext);
		cmd->helptext = (char*)(cmd +1) + strlen(cmd_name) +1;
	}else{
		cmd = S_Malloc( sizeof( cmd_function_t ) + strlen(cmd_name) + 1);
	}
	strcpy((char*)(cmd +1), cmd_name);
	cmd->name = (char*)(cmd +1);
	cmd->function = function;
	cmd->minPower = power;
	cmd->next = cmd_functions;
	cmd_functions = cmd;
	return qtrue;
}
Exemple #3
0
sfxcache_t *AL_UploadSfx(sfx_t *s)
{
    sfxcache_t *sc;
    ALsizei size = s_info.samples * s_info.width;
    ALenum format = s_info.width == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8;
    ALuint name;

    if (!size) {
        s->error = Q_ERR_TOO_FEW;
        return NULL;
    }

    qalGetError();
    qalGenBuffers(1, &name);
    qalBufferData(name, format, s_info.data, size, s_info.rate);
    if (qalGetError() != AL_NO_ERROR) {
        s->error = Q_ERR_LIBRARY_ERROR;
        return NULL;
    }

    // allocate placeholder sfxcache
    sc = s->cache = S_Malloc(sizeof(*sc));
    sc->length = s_info.samples * 1000 / s_info.rate; // in msec
    sc->loopstart = s_info.loopstart;
    sc->width = s_info.width;
    sc->size = size;
    sc->bufnum = name;

    return sc;
}
Exemple #4
0
/*
* S_InitSources
*/
qboolean S_InitSources( int maxEntities, qboolean verbose )
{
	int i;

	memset( srclist, 0, sizeof( srclist ) );
	src_count = 0;

	// Allocate as many sources as possible
	for( i = 0; i < MAX_SRC; i++ )
	{
		qalGenSources( 1, &srclist[i].source );
		if( qalGetError() != AL_NO_ERROR )
			break;
		src_count++;
	}
	if( !src_count )
		return qfalse;

	if( verbose )
		Com_Printf( "allocated %d sources\n", src_count );

	if( maxEntities < 1 )
		return qfalse;

	entlist = ( sentity_t * )S_Malloc( sizeof( sentity_t ) * maxEntities );

	src_inited = qtrue;
	return qtrue;
}
Exemple #5
0
/*
============
Cmd_AddCommand
============
*/
void	Cmd_AddCommand( const char *cmd_name, xcommand_t function )
{
    cmd_function_t	*cmd;

    // fail if the command already exists
    for ( cmd = cmd_functions ; cmd ; cmd=cmd->next )
    {
        if ( !strcmp( cmd_name, cmd->name ) )
        {
            // allow completion-only commands to be silently doubled
            if ( function != NULL )
            {
                Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
            }
            return;
        }
    }

    // use a small malloc to avoid zone fragmentation
    cmd = (cmd_function_t *)S_Malloc (sizeof(cmd_function_t));
    cmd->name = CopyString( cmd_name );
    cmd->function = function;
    cmd->next = cmd_functions;
    cmd_functions = cmd;
}
Exemple #6
0
/*
============
Cmd_AddCommand
============
*/
void Cmd_AddCommand(const char *cmd_name, xcommand_t function, const char *cmd_desc)
{
	cmd_function_t *cmd;

	// fail if the command is a variable name
	if( Cvar_FindVar( cmd_name ))
	{
		Com_Printf( "Cmd_AddCommand: %s already defined as a var\n", cmd_name );
		return;
	}
	
	// fail if the command already exists
	if( Cmd_Exists( cmd_name ))
	{
		Com_Printf( "Cmd_AddCommand: %s already defined\n", cmd_name );
		// ALARM! HACKED FOR NOW
		Cmd_RemoveCommand(cmd_name);
		return;
	}

	// use a small malloc to avoid zone fragmentation
	cmd = (cmd_function_t*)S_Malloc(sizeof(cmd_function_t));
	cmd->name = CopyString(cmd_name);
	cmd->desc = CopyString(cmd_desc);
	cmd->function = function;
	cmd->next = cmd_functions;
	cmd->complete = NULL;
	cmd_functions = cmd;
}
snd_stream_t *decoder_wav_open( const char *filename, qboolean *delay )
{
	snd_stream_t *stream;
	snd_wav_stream_t *wav_stream;

	stream = decoder_stream_init( &wav_decoder );
	if( !stream )
		return NULL;

	stream->isUrl = trap_FS_IsUrl( filename );
	if( stream->isUrl )
		return NULL;

	if( delay )
		*delay = qfalse;

	stream->ptr = S_Malloc( sizeof( snd_wav_stream_t ) );
	wav_stream = (snd_wav_stream_t *)stream->ptr;

	trap_FS_FOpenFile( filename, &wav_stream->filenum, FS_READ|FS_NOSIZE );
	if( !wav_stream->filenum )
	{
		decoder_wav_stream_shutdown( stream );
		return NULL;
	}

	if( !decoder_wav_cont_open( stream ) )
	{
		return NULL;
	}

	return stream;
}
Exemple #8
0
/**
* Util functions used by decoders (snd_decoder.h)
*/
snd_stream_t *decoder_stream_init( snd_decoder_t *decoder )
{
	snd_stream_t *stream;

	// Allocate a stream
	stream = S_Malloc( sizeof( snd_stream_t ) );
	stream->decoder = decoder;
	return stream;
}
Exemple #9
0
/*
* SF_RawSamples
*/
void SF_RawSamples( unsigned int samples, unsigned int rate, unsigned short width, 
	unsigned short channels, const uint8_t *data, bool music )
{
	size_t data_size = samples * width * channels;
	uint8_t *data_copy = S_Malloc( data_size );

	memcpy( data_copy, data, data_size );

	S_IssueRawSamplesCmd( s_cmdPipe, samples, rate, width, channels, data_copy, music );
}
Exemple #10
0
/*
* S_FindRawSound
*/
static rawsound_t *S_FindRawSound( int entnum, bool addNew )
{
	int i, free;
	int best, best_time;
	rawsound_t *rawsound;

	// check for replacement sound, or find the best one to replace
	best = free = -1;
	best_time = 0x7fffffff;
	for( i = 0; i < MAX_RAW_SOUNDS; i++ ) {
		rawsound = raw_sounds[i];

		if( free < 0 && !rawsound ) {
			free = i;
		}
		else if( rawsound ) {
			int time;

			if( rawsound->entnum == entnum ) {
				// exact match
				return rawsound;
			}
			
			time = rawsound->rawend - paintedtime;
			if( time < best_time ) {
				best = i;
				best_time = time;
			}
		}
	}

	if( !addNew ) {
		return NULL;
	}

	if( free >= 0 ) {
		best = free;
	}
	if( best < 0 ) {
		// no free slots
		return NULL;
	}

	if( !raw_sounds[best] ) {
		raw_sounds[best] = S_Malloc( sizeof( *rawsound ) 
			+ sizeof( portable_samplepair_t ) * MAX_RAW_SAMPLES );
	}

	rawsound = raw_sounds[best];
	rawsound->entnum = entnum;
	rawsound->rawend = 0;
	rawsound->left_volume = rawsound->right_volume = 0; // will be spatialized later
	return rawsound;
}
Exemple #11
0
/*
* SF_PositionedRawSamples
*/
void SF_PositionedRawSamples( int entnum, float fvol, float attenuation, 
	unsigned int samples, unsigned int rate, 
	unsigned short width, unsigned short channels, const uint8_t *data )
{
	size_t data_size = samples * width * channels;
	uint8_t *data_copy = S_Malloc( data_size );

	memcpy( data_copy, data, data_size );

	S_IssuePositionedRawSamplesCmd( s_cmdPipe, entnum, fvol, attenuation, 
		samples, rate, width, channels, data_copy );
}
Exemple #12
0
/*
* S_AllocTrack
*/
static bgTrack_t *S_AllocTrack( const char *filename ) {
	bgTrack_t *track;

	track = S_Malloc( sizeof( *track ) + strlen( filename ) + 1 );
	track->ignore = false;
	track->filename = (char *)( (uint8_t *)track + sizeof( *track ) );
	strcpy( track->filename, filename );
	track->isUrl = trap_FS_IsUrl( track->filename );
	track->muteOnPause = track->isUrl;
	track->anext = s_bgTrackHead;
	s_bgTrackHead = track;

	return track;
}
Exemple #13
0
/*
* S_AllocTrack
*/
static bgTrack_t *S_AllocTrack( const char *filename )
{
    bgTrack_t *track;

    track = S_Malloc( sizeof( *track ) + strlen( filename ) + 1 );
    track->stream = NULL;
    track->ignore = qfalse;
    track->filename = (char *)((qbyte *)track + sizeof( *track ));
    strcpy( track->filename, filename );
    track->isUrl = trap_FS_IsUrl( filename );
    track->anext = s_bgTrackHead;
    s_bgTrackHead = track;

    return track;
}
Exemple #14
0
static void decoder_register( snd_decoder_t *decoder )
{
	decoder->next = decoders;
	decoders = decoder;

	if( extensionlist_size - strlen( extensionlist ) - 1 < strlen( decoder->ext ) + 1 )
	{
		char *oldlist = extensionlist;
		extensionlist_size = max( extensionlist_size * 2, (int)( strlen( extensionlist ) + strlen( decoder->ext ) + 1 + 1 ) );
		extensionlist = S_Malloc( extensionlist_size );
		Q_strncpyz( extensionlist, oldlist, extensionlist_size );
		S_Free( oldlist );
	}
	Q_strncatz( extensionlist, " ", extensionlist_size );
	Q_strncatz( extensionlist, decoder->ext, extensionlist_size );
}
Exemple #15
0
/*
========================
CopyString

 NOTE:	never write over the memory CopyString returns because
		memory from a memstatic_t might be returned
========================
*/
char *CopyString( const char *in ) {
	char	*out;

	if (!in[0]) {
		return ((char *)&gEmptyString) + sizeof(zoneHeader_t);
	}
	else if (!in[1]) {
		if (in[0] >= '0' && in[0] <= '9') {
			return ((char *)&gNumberString[in[0]-'0']) + sizeof(zoneHeader_t);
		}
	}

	out = (char *) S_Malloc (strlen(in)+1);
	strcpy (out, in);
	return out;
}
Exemple #16
0
qboolean S_InitDecoders( qboolean verbose )
{
	extensionlist_size = 32;
	extensionlist = S_Malloc( extensionlist_size );
	extensionlist[0] = 0;

	// First codec has the priority.
	decoders = NULL;

	decoder_register( &wav_decoder );
	if( SNDOGG_Init( verbose ) )
	{
		decoder_register( &ogg_decoder );
	}

	return qtrue;
}
Exemple #17
0
void * stereo_mono( void *data, snd_info_t *info )
{
	int i, interleave, gain;
	void *outdata;

	outdata = S_Malloc( info->samples * info->width );
	interleave = info->channels * info->width;
	gain = s_stereo2mono->integer;
	clamp( gain, -1, 1 );

	if( info->width == 2 )
	{
		short *pin, *pout;

		pin = (short*)data;
		pout = (short*)outdata;

		for( i = 0; i < info->size; i += interleave, pin += info->channels, pout++ )
		{
			*pout = ((1-gain) * pin[0] + (1+gain) * pin[1]) / 2;
		}
	}
	else if( info->width == 1 )
	{
		char *pin, *pout;

		pin = (char*)data;
		pout = (char*)outdata;

		for( i = 0; i < info->size; i += interleave, pin += info->channels, pout++ )
		{
			*pout = ((1-gain) * pin[0] + (1+gain) * pin[1]) / 2;
		}
	}
	else
	{
		S_Free( outdata );
		return NULL;
	}

	info->channels = 1;
	info->size = info->samples * info->width;

	return outdata;
}
Exemple #18
0
snd_stream_t *decoder_ogg_open( const char *filename, qboolean *delay )
{
	snd_stream_t *stream;
	snd_ogg_stream_t *ogg_stream;

	// Open
	stream = decoder_stream_init( &ogg_decoder );
	if( !stream )
	{
		Com_Printf( "Error initializing .ogg stream: %s\n", filename );
		return NULL;
	}

	stream->isUrl = trap_FS_IsUrl( filename );
	stream->ptr = S_Malloc( sizeof( snd_ogg_stream_t ) );
	ogg_stream = (snd_ogg_stream_t *)stream->ptr;

	trap_FS_FOpenFile( filename, &ogg_stream->filenum, FS_READ|FS_NOSIZE );
	if( !ogg_stream->filenum )
	{
		decoder_ogg_stream_shutdown( stream );
		return NULL;
	}

	if( delay )
		*delay = qfalse;

	if( stream->isUrl && delay )
	{
		*delay = qtrue;
		return stream;
	}

	if( !decoder_ogg_cont_open( stream ) )
	{
		decoder_ogg_close( stream );
		return NULL;
	}

	return stream;
}
Exemple #19
0
/*
============
Cmd_AddCommandExtended
============
*/
void Cmd_AddSystemCommand(const char *cmd_name, xcommand_t function, const char *description, completionFunc_t complete)
{
	cmd_function_t *cmd;

	if (!cmd_name || !cmd_name[0])
	{
		Com_Printf(S_COLOR_RED "Cmd_AddSystemCommand can't add NULL or empty command name\n");
		return;
	}

	// fail if the command already exists
	if (Cmd_FindCommand(cmd_name))
	{
		// allow completion-only commands to be silently doubled
		if (function != NULL)
		{
			Com_Printf("Cmd_AddCommandExtended: %s already defined\n", cmd_name);
		}
		return;
	}

	// use a small malloc to avoid zone fragmentation
	cmd           = S_Malloc(sizeof(cmd_function_t));
	cmd->name     = CopyString(cmd_name);
	cmd->function = function;
	cmd->complete = complete;
	cmd->next     = cmd_functions;
	cmd_functions = cmd;

	if (description && description[0])
	{
		cmd->description = CopyString(description);
	}
	else
	{
		cmd->description = NULL;
	}
}
Exemple #20
0
void *decoder_wav_load( const char *filename, snd_info_t *info )
{
	int filenum;
	int read;
	void *buffer;

	if( trap_FS_IsUrl( filename ) )
		return NULL;

	trap_FS_FOpenFile( filename, &filenum, FS_READ|FS_NOSIZE );
	if( !filenum )
		return NULL;

	if( !read_wav_header( filenum, info ) )
	{
		trap_FS_FCloseFile( filenum );
		Com_Printf( "Can't understand .wav file: %s\n", filename );
		return NULL;
	}

	buffer = S_Malloc( info->size );
	read = trap_FS_Read( buffer, info->size, filenum );
	if( read != info->size )
	{
		S_Free( buffer );
		trap_FS_FCloseFile( filenum );
		Com_Printf( "Error reading .wav file: %s\n", filename );
		return NULL;
	}

	byteSwapRawSamples( info->samples, info->width, info->channels, (qbyte *)buffer );

	trap_FS_FCloseFile( filenum );

	return buffer;
}
Exemple #21
0
/*
* S_ReadPlaylistFile
*/
static bgTrack_t *S_ReadPlaylistFile( const char *filename, bool shuffle, bool loop )
{
	int filenum, length;
	char *tmpname = 0;
	size_t tmpname_size = 0;
	char *data, *line, *entry;
	playlistItem_t items[MAX_PLAYLIST_ITEMS];
	int i, numItems = 0;

	length = trap_FS_FOpenFile( filename, &filenum, FS_READ );
	if( length < 0 )
		return NULL;

	// load the playlist into memory
	data = S_Malloc( length + 1 );
	trap_FS_Read( data, length, filenum );
	trap_FS_FCloseFile( filenum );

	srand( time( NULL ) );

	while( *data )
	{
		size_t s;

		entry = data;

		// read the whole line
		for( line = data; *line != '\0' && *line != '\n'; line++ );

		// continue reading from the next character, if possible
		data = (*line == '\0' ? line : line + 1);

		*line = '\0';

		// trim whitespaces, tabs, etc
		entry = Q_trim( entry );

		// special M3U entry or comment
		if( !*entry || *entry == '#' )
			continue;

		if( trap_FS_IsUrl( entry ) )
		{
			items[numItems].track = S_AllocTrack( entry );
		}
		else
		{
			// append the entry name to playlist path
			s = strlen( filename ) + 1 + strlen( entry ) + 1;
			if( s > tmpname_size )
			{
				if( tmpname )
					S_Free( tmpname );
				tmpname_size = s;
				tmpname = S_Malloc( tmpname_size );
			}

			Q_strncpyz( tmpname, filename, tmpname_size );
			COM_StripFilename( tmpname );
			Q_strncatz( tmpname, "/", tmpname_size );
			Q_strncatz( tmpname, entry, tmpname_size );
			COM_SanitizeFilePath( tmpname );

			items[numItems].track = S_AllocTrack( tmpname );
		}

		if( ++numItems == MAX_PLAYLIST_ITEMS )
			break;
	}

	if( tmpname )
	{
		S_Free( tmpname );
		tmpname = NULL;
	}

	if( !numItems )
		return NULL;

	// set the playing order
	for( i = 0; i < numItems; i++ )
		items[i].order = (shuffle ? (rand() % numItems) : i);

	// sort the playlist
	R_SortPlaylistItems( numItems, items );

	// link the playlist
	for( i = 1; i < numItems; i++ )
	{
		items[i-1].track->next = items[i].track;
		items[i].track->prev = items[i-1].track;
		items[i].track->loop = loop;
	}
	items[numItems-1].track->next = items[0].track;
	items[0].track->prev = items[numItems-1].track;
	items[0].track->loop = loop;

	return items[0].track;
}
/*
========================
CopyString

 NOTE:	never write over the memory CopyString returns because
		memory from a memstatic_t might be returned
========================
*/
char *CopyString( const char *in )
{
	struct ZoneSingleChar
	{
		ZoneHeader header;
#ifdef _DEBUG
		ZoneDebugHeader start;
#endif
		char data[2];
#ifdef _DEBUG
		ZoneDebugFooter end;
#endif
	};

#ifdef _DEBUG
	static ZoneSingleChar empty = {(TAG_STATIC << 25) | 2, ZONE_MAGIC, "\0", ZONE_MAGIC};
	static ZoneSingleChar numbers[10] =
	{
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "0", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "1", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "2", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "3", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "4", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "5", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "6", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "7", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "8", ZONE_MAGIC},
		{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "9", ZONE_MAGIC},
	};
#else
	static ZoneSingleChar empty = {(TAG_STATIC << 25) | 2, "\0"};
	static ZoneSingleChar numbers[10] =
	{
		{(TAG_STATIC << 25) | 2, "0"},
		{(TAG_STATIC << 25) | 2, "1"},
		{(TAG_STATIC << 25) | 2, "2"},
		{(TAG_STATIC << 25) | 2, "3"},
		{(TAG_STATIC << 25) | 2, "4"},
		{(TAG_STATIC << 25) | 2, "5"},
		{(TAG_STATIC << 25) | 2, "6"},
		{(TAG_STATIC << 25) | 2, "7"},
		{(TAG_STATIC << 25) | 2, "8"},
		{(TAG_STATIC << 25) | 2, "9"},
	};
#endif

	char	*out;

	if (!in[0])
	{
		return empty.data;
	}
	else if (!in[1])
	{
		if (in[0] >= '0' && in[0] <= '9')
		{
			return numbers[in[0]-'0'].data;
		}
	}

	out = (char *) S_Malloc (strlen(in)+1);
	strcpy (out, in);

//	Z_Label(out,in);

	return out;
}
Exemple #23
0
/*
============
Cmd_Alias_f
============
*/
void Cmd_Alias_f(void)
{
	cmd_alias_t	*alias;
	const char	*name;

	// Get args
	if (Cmd_Argc() < 2)
	{
		Com_Printf("alias <name> : show an alias\n");
		Com_Printf("alias <name> <exec> : create an alias\n");
		return;
	}
	name = Cmd_Argv(1);

	// Find existing alias
	for (alias = cmd_aliases; alias; alias = alias->next)
	{
		if (!Q_stricmp(name, alias->name))
			break;
	}

	// Modify/create an alias
	if (Cmd_Argc() > 2)
	{
		cmd_function_t	*cmd;

		// Crude protection from infinite loops
		if (!Q_stricmp(Cmd_Argv(2), name))
		{
			Com_Printf("Can't make an alias to itself\n");
			return;
		}

		// Don't allow overriding builtin commands
 		cmd = Cmd_FindCommand( name );
		if (cmd && cmd->function != Cmd_RunAlias_f)
		{
			Com_Printf("Can't override a builtin function with an alias\n");
			return;
		}

		// Create/update an alias
		if (!alias)
		{
			alias = (cmd_alias_t*)S_Malloc(sizeof(cmd_alias_t));
			alias->name = CopyString(name);
			alias->exec = CopyString(Cmd_ArgsFrom(2));
			alias->next = cmd_aliases;
			cmd_aliases = alias;
			Cmd_AddCommand(name, Cmd_RunAlias_f, NULL);
		}
		else
		{
			// Reallocate the exec string
			Z_Free(alias->exec);
			alias->exec = CopyString(Cmd_ArgsFrom(2));
			Cmd_AddCommand(name, Cmd_RunAlias_f, NULL);
		}
	}
	
	// Show the alias
	if (!alias)
		Com_Printf("Alias %s does not exist\n", name);
	else if (Cmd_Argc() == 2)
		Com_Printf("%s ==> %s\n", alias->name, alias->exec);
	
	// update autogen.cfg
	cvar_modifiedFlags |= CVAR_ARCHIVE;
}
Exemple #24
0
void *decoder_ogg_load( const char *filename, snd_info_t *info )
{
	OggVorbis_File vorbisfile;
	int filenum, bitstream, bytes_read, bytes_read_total;
	char *buffer;
	ov_callbacks callbacks = { ovcb_read, ovcb_seek, ovcb_close, ovcb_tell };

	trap_FS_FOpenFile( filename, &filenum, FS_READ|FS_NOSIZE );
	if( !filenum )
		return NULL;

	if( trap_FS_IsUrl( filename ) )
	{
		callbacks.seek_func = NULL;
		callbacks.tell_func = NULL;
	}

	qov_open_callbacks( (void *) (qintptr) filenum, &vorbisfile, NULL, 0, callbacks );

	if( callbacks.seek_func && !qov_seekable( &vorbisfile ) )
	{
		Com_Printf( "Error unsupported .ogg file (not seekable): %s\n", filename );
		qov_clear( &vorbisfile ); // Does FS_FCloseFile
		return NULL;
	}

	if( qov_streams( &vorbisfile ) != 1 )
	{
		Com_Printf( "Error unsupported .ogg file (multiple logical bitstreams): %s\n", filename );
		qov_clear( &vorbisfile ); // Does FS_FCloseFile
		return NULL;
	}

	if( !read_ogg_header( vorbisfile, info ) )
	{
		Com_Printf( "Error reading .ogg file header: %s\n", filename );
		qov_clear( &vorbisfile ); // Does FS_FCloseFile
		return NULL;
	}

	buffer = S_Malloc( info->size );

	bytes_read_total = 0;
	do
	{
#ifdef ENDIAN_BIG
		bytes_read = qov_read( &vorbisfile, buffer+bytes_read_total, info->size-bytes_read_total, 1, 2, 1, &bitstream );
#elif defined (ENDIAN_LITTLE)
		bytes_read = qov_read( &vorbisfile, buffer+bytes_read_total, info->size-bytes_read_total, 0, 2, 1, &bitstream );
#else
#error "runtime endianess detection support missing"
#endif
		bytes_read_total += bytes_read;
	}
	while( bytes_read > 0 && bytes_read_total < info->size );

	qov_clear( &vorbisfile ); // Does FS_FCloseFile
	if( !bytes_read_total )
	{
		Com_Printf( "Error reading .ogg file: %s\n", filename );
		S_Free( buffer );
		return NULL;
	}

	return buffer;
}