1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
5 Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
6 
7 This file is part of Quake III Arena source code.
8 
9 Quake III Arena source code is free software; you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
13 
14 Quake III Arena source code is distributed in the hope that it will be
15 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Quake III Arena source code; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 ===========================================================================
23 */
24 
25 // OGG support is enabled by this define
26 #ifdef USE_CODEC_VORBIS
27 
28 // includes for the Q3 sound system
29 #include "client.h"
30 #include "snd_codec.h"
31 
32 // includes for the OGG codec
33 #include <errno.h>
34 #include <vorbis/vorbisfile.h>
35 
36 // The OGG codec can return the samples in a number of different formats,
37 // we use the standard signed short format.
38 #define OGG_SAMPLEWIDTH 2
39 
40 // Q3 OGG codec
41 snd_codec_t ogg_codec =
42 {
43 	".ogg",
44 	S_OGG_CodecLoad,
45 	S_OGG_CodecOpenStream,
46 	S_OGG_CodecReadStream,
47 	S_OGG_CodecCloseStream,
48 	NULL
49 };
50 
51 // callbacks for vobisfile
52 
53 // fread() replacement
S_OGG_Callback_read(void * ptr,size_t size,size_t nmemb,void * datasource)54 size_t S_OGG_Callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
55 {
56 	snd_stream_t *stream;
57 	int byteSize = 0;
58 	int bytesRead = 0;
59 	size_t nMembRead = 0;
60 
61 	// check if input is valid
62 	if(!ptr)
63 	{
64 		errno = EFAULT;
65 		return 0;
66 	}
67 
68 	if(!(size && nmemb))
69 	{
70 		// It's not an error, caller just wants zero bytes!
71 		errno = 0;
72 		return 0;
73 	}
74 
75 	if(!datasource)
76 	{
77 		errno = EBADF;
78 		return 0;
79 	}
80 
81 	// we use a snd_stream_t in the generic pointer to pass around
82 	stream = (snd_stream_t *) datasource;
83 
84 	// FS_Read does not support multi-byte elements
85 	byteSize = nmemb * size;
86 
87 	// read it with the Q3 function FS_Read()
88 	bytesRead = FS_Read(ptr, byteSize, stream->file);
89 
90 	// update the file position
91 	stream->pos += bytesRead;
92 
93 	// this function returns the number of elements read not the number of bytes
94 	nMembRead = bytesRead / size;
95 
96 	// even if the last member is only read partially
97 	// it is counted as a whole in the return value
98 	if(bytesRead % size)
99 	{
100 		nMembRead++;
101 	}
102 
103 	return nMembRead;
104 }
105 
106 // fseek() replacement
S_OGG_Callback_seek(void * datasource,ogg_int64_t offset,int whence)107 int S_OGG_Callback_seek(void *datasource, ogg_int64_t offset, int whence)
108 {
109 	snd_stream_t *stream;
110 	int retVal = 0;
111 
112 	// check if input is valid
113 	if(!datasource)
114 	{
115 		errno = EBADF;
116 		return -1;
117 	}
118 
119 	// snd_stream_t in the generic pointer
120 	stream = (snd_stream_t *) datasource;
121 
122 	// we must map the whence to its Q3 counterpart
123 	switch(whence)
124 	{
125 		case SEEK_SET :
126 		{
127 			// set the file position in the actual file with the Q3 function
128 			retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
129 
130 			// something has gone wrong, so we return here
131 			if(retVal < 0)
132 			{
133 			 return retVal;
134 			}
135 
136 			// keep track of file position
137 			stream->pos = (int) offset;
138 			break;
139 		}
140 
141 		case SEEK_CUR :
142 		{
143 			// set the file position in the actual file with the Q3 function
144 			retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
145 
146 			// something has gone wrong, so we return here
147 			if(retVal < 0)
148 			{
149 			 return retVal;
150 			}
151 
152 			// keep track of file position
153 			stream->pos += (int) offset;
154 			break;
155 		}
156 
157 		case SEEK_END :
158 		{
159 			// Quake 3 seems to have trouble with FS_SEEK_END
160 			// so we use the file length and FS_SEEK_SET
161 
162 			// set the file position in the actual file with the Q3 function
163 			retVal = FS_Seek(stream->file, (long) stream->length + (long) offset, FS_SEEK_SET);
164 
165 			// something has gone wrong, so we return here
166 			if(retVal < 0)
167 			{
168 			 return retVal;
169 			}
170 
171 			// keep track of file position
172 			stream->pos = stream->length + (int) offset;
173 			break;
174 		}
175 
176 		default :
177 		{
178 			// unknown whence, so we return an error
179 			errno = EINVAL;
180 			return -1;
181 		}
182 	}
183 
184 	// stream->pos shouldn't be smaller than zero or bigger than the filesize
185 	stream->pos = (stream->pos < 0) ? 0 : stream->pos;
186 	stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
187 
188 	return 0;
189 }
190 
191 // fclose() replacement
S_OGG_Callback_close(void * datasource)192 int S_OGG_Callback_close(void *datasource)
193 {
194 	// we do nothing here and close all things manually in S_OGG_CodecCloseStream()
195 	return 0;
196 }
197 
198 // ftell() replacement
S_OGG_Callback_tell(void * datasource)199 long S_OGG_Callback_tell(void *datasource)
200 {
201 	snd_stream_t   *stream;
202 
203 	// check if input is valid
204 	if(!datasource)
205 	{
206 		errno = EBADF;
207 		return -1;
208 	}
209 
210 	// snd_stream_t in the generic pointer
211 	stream = (snd_stream_t *) datasource;
212 
213 	return (long) FS_FTell(stream->file);
214 }
215 
216 // the callback structure
217 const ov_callbacks S_OGG_Callbacks =
218 {
219  &S_OGG_Callback_read,
220  &S_OGG_Callback_seek,
221  &S_OGG_Callback_close,
222  &S_OGG_Callback_tell
223 };
224 
225 /*
226 =================
227 S_OGG_CodecOpenStream
228 =================
229 */
S_OGG_CodecOpenStream(const char * filename)230 snd_stream_t *S_OGG_CodecOpenStream(const char *filename)
231 {
232 	snd_stream_t *stream;
233 
234 	// OGG codec control structure
235 	OggVorbis_File *vf;
236 
237 	// some variables used to get informations about the OGG
238 	vorbis_info *OGGInfo;
239 	ogg_int64_t numSamples;
240 
241 	// check if input is valid
242 	if(!filename)
243 	{
244 		return NULL;
245 	}
246 
247 	// Open the stream
248 	stream = S_CodecUtilOpen(filename, &ogg_codec);
249 	if(!stream)
250 	{
251 		return NULL;
252 	}
253 
254 	// alloctate the OggVorbis_File
255 	vf = Z_Malloc(sizeof(OggVorbis_File));
256 	if(!vf)
257 	{
258 		S_CodecUtilClose(&stream);
259 
260 		return NULL;
261 	}
262 
263 	// open the codec with our callbacks and stream as the generic pointer
264 	if(ov_open_callbacks(stream, vf, NULL, 0, S_OGG_Callbacks) != 0)
265 	{
266 		Z_Free(vf);
267 
268 		S_CodecUtilClose(&stream);
269 
270 		return NULL;
271 	}
272 
273 	// the stream must be seekable
274 	if(!ov_seekable(vf))
275 	{
276 		ov_clear(vf);
277 
278 		Z_Free(vf);
279 
280 		S_CodecUtilClose(&stream);
281 
282 		return NULL;
283 	}
284 
285 	// we only support OGGs with one substream
286 	if(ov_streams(vf) != 1)
287 	{
288 		ov_clear(vf);
289 
290 		Z_Free(vf);
291 
292 		S_CodecUtilClose(&stream);
293 
294 		return NULL;
295 	}
296 
297 	// get the info about channels and rate
298 	OGGInfo = ov_info(vf, 0);
299 	if(!OGGInfo)
300 	{
301 		ov_clear(vf);
302 
303 		Z_Free(vf);
304 
305 		S_CodecUtilClose(&stream);
306 
307 		return NULL;
308 	}
309 
310 	// get the number of sample-frames in the OGG
311 	numSamples = ov_pcm_total(vf, 0);
312 
313 	// fill in the info-structure in the stream
314 	stream->info.rate = OGGInfo->rate;
315 	stream->info.width = OGG_SAMPLEWIDTH;
316 	stream->info.channels = OGGInfo->channels;
317 	stream->info.samples = numSamples;
318 	stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
319 	stream->info.dataofs = 0;
320 
321 	// We use stream->pos for the file pointer in the compressed ogg file
322 	stream->pos = 0;
323 
324 	// We use the generic pointer in stream for the OGG codec control structure
325 	stream->ptr = vf;
326 
327 	return stream;
328 }
329 
330 /*
331 =================
332 S_OGG_CodecCloseStream
333 =================
334 */
S_OGG_CodecCloseStream(snd_stream_t * stream)335 void S_OGG_CodecCloseStream(snd_stream_t *stream)
336 {
337 	// check if input is valid
338 	if(!stream)
339 	{
340 		return;
341 	}
342 
343 	// let the OGG codec cleanup its stuff
344 	ov_clear((OggVorbis_File *) stream->ptr);
345 
346 	// free the OGG codec control struct
347 	Z_Free(stream->ptr);
348 
349 	// close the stream
350 	S_CodecUtilClose(&stream);
351 }
352 
353 /*
354 =================
355 S_OGG_CodecReadStream
356 =================
357 */
S_OGG_CodecReadStream(snd_stream_t * stream,int bytes,void * buffer)358 int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
359 {
360 	// buffer handling
361 	int bytesRead, bytesLeft, c;
362 	char *bufPtr;
363 
364 	// Bitstream for the decoder
365 	int BS = 0;
366 
367 	// big endian machines want their samples in big endian order
368 	int IsBigEndian = 0;
369 
370 #	ifdef Q3_BIG_ENDIAN
371 	IsBigEndian = 1;
372 #	endif // Q3_BIG_ENDIAN
373 
374 	// check if input is valid
375 	if(!(stream && buffer))
376 	{
377 		return 0;
378 	}
379 
380 	if(bytes <= 0)
381 	{
382 		return 0;
383 	}
384 
385 	bytesRead = 0;
386 	bytesLeft = bytes;
387 	bufPtr = buffer;
388 
389 	// cycle until we have the requested or all available bytes read
390 	while(-1)
391 	{
392 		// read some bytes from the OGG codec
393 		c = ov_read((OggVorbis_File *) stream->ptr, bufPtr, bytesLeft, IsBigEndian, OGG_SAMPLEWIDTH, 1, &BS);
394 
395 		// no more bytes are left
396 		if(c <= 0)
397 		{
398 			break;
399 		}
400 
401 		bytesRead += c;
402 		bytesLeft -= c;
403 		bufPtr += c;
404 
405 		// we have enough bytes
406 		if(bytesLeft <= 0)
407 		{
408 			break;
409 		}
410 	}
411 
412 	return bytesRead;
413 }
414 
415 /*
416 =====================================================================
417 S_OGG_CodecLoad
418 
419 We handle S_OGG_CodecLoad as a special case of the streaming functions
420 where we read the whole stream at once.
421 ======================================================================
422 */
S_OGG_CodecLoad(const char * filename,snd_info_t * info)423 void *S_OGG_CodecLoad(const char *filename, snd_info_t *info)
424 {
425 	snd_stream_t *stream;
426 	byte *buffer;
427 	int bytesRead;
428 
429 	// check if input is valid
430 	if(!(filename && info))
431 	{
432 		return NULL;
433 	}
434 
435 	// open the file as a stream
436 	stream = S_OGG_CodecOpenStream(filename);
437 	if(!stream)
438 	{
439 		return NULL;
440 	}
441 
442 	// copy over the info
443 	info->rate = stream->info.rate;
444 	info->width = stream->info.width;
445 	info->channels = stream->info.channels;
446 	info->samples = stream->info.samples;
447 	info->size = stream->info.size;
448 	info->dataofs = stream->info.dataofs;
449 
450 	// allocate a buffer
451 	// this buffer must be free-ed by the caller of this function
452     	buffer = Z_Malloc(info->size);
453 	if(!buffer)
454 	{
455 		S_OGG_CodecCloseStream(stream);
456 
457 		return NULL;
458 	}
459 
460 	// fill the buffer
461 	bytesRead = S_OGG_CodecReadStream(stream, info->size, buffer);
462 
463 	// we don't even have read a single byte
464 	if(bytesRead <= 0)
465 	{
466 		Z_Free(buffer);
467 		S_OGG_CodecCloseStream(stream);
468 
469 		return NULL;
470 	}
471 
472 	S_OGG_CodecCloseStream(stream);
473 
474 	return buffer;
475 }
476 
477 #endif // USE_CODEC_VORBIS
478