1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 2005-2020  Warzone 2100 Project
4 
5 	Warzone 2100 is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	Warzone 2100 is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with Warzone 2100; if not, write to the Free Software
17 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #include "lib/framework/frame.h"
21 #include <physfs.h>
22 #include <limits>
23 #include "lib/framework/physfs_ext.h"
24 
25 #include <vorbis/vorbisfile.h>
26 #include <vorbis/codec.h>
27 
28 #ifdef __BIG_ENDIAN__
29 #define OGG_ENDIAN 1
30 #else
31 #define OGG_ENDIAN 0
32 #endif
33 
34 #include "oggvorbis.h"
35 
36 struct OggVorbisDecoderState
37 {
38 	// Internal identifier towards PhysicsFS
39 	PHYSFS_file *fileHandle;
40 
41 	// Wether to allow seeking or not
42 	bool         allowSeeking;
43 
44 	// Internal identifier towards libVorbisFile
45 	OggVorbis_File oggVorbis_stream;
46 
47 	// Internal meta data
48 	vorbis_info *VorbisInfo;
49 
50 	// Total time
51 	double total_stream_time = 0.0;
52 };
53 
wz_oggVorbis_getErrorStr(int error)54 static const char *wz_oggVorbis_getErrorStr(int error)
55 {
56 	switch (error)
57 	{
58 	case OV_FALSE:
59 		return "OV_FALSE";
60 	case OV_HOLE:
61 		return "OV_HOLE";
62 	case OV_EREAD:
63 		return "OV_EREAD";
64 	case OV_EFAULT:
65 		return "OV_EFAULT";
66 	case OV_EIMPL:
67 		return "OV_EIMPL";
68 	case OV_EINVAL:
69 		return "OV_EINVAL";
70 	case OV_ENOTVORBIS:
71 		return "OV_ENOTVORBIS";
72 	case OV_EBADHEADER:
73 		return "OV_EBADHEADER";
74 	case OV_EVERSION:
75 		return "OV_EVERSION";
76 	case OV_EBADLINK:
77 		return "OV_EBADLINK";
78 	case OV_ENOSEEK:
79 		return "OV_ENOSEEK";
80 	default:
81 		return "Unknown Ogg error.";
82 	}
83 }
84 
wz_oggVorbis_read(void * ptr,size_t size,size_t nmemb,void * datasource)85 static size_t wz_oggVorbis_read(void *ptr, size_t size, size_t nmemb, void *datasource)
86 {
87 	PHYSFS_file *fileHandle;
88 
89 	ASSERT(datasource != nullptr, "NULL decoder passed!");
90 
91 	fileHandle = ((struct OggVorbisDecoderState *)datasource)->fileHandle;
92 	ASSERT(fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
93 
94 	size_t readLen = size * nmemb;
95 	ASSERT(readLen <= static_cast<size_t>(std::numeric_limits<PHYSFS_uint32>::max()), "readLen (%zu) exceeds PHYSFS_uint32::max", readLen);
96 	return WZ_PHYSFS_readBytes(fileHandle, ptr, static_cast<PHYSFS_uint32>(readLen));
97 }
98 
wz_oggVorbis_seek(void * datasource,ogg_int64_t offset,int whence)99 static int wz_oggVorbis_seek(void *datasource, ogg_int64_t offset, int whence)
100 {
101 	PHYSFS_file *fileHandle;
102 	bool allowSeeking;
103 	int newPos;
104 
105 	ASSERT(datasource != nullptr, "NULL decoder passed!");
106 
107 	fileHandle = ((struct OggVorbisDecoderState *)datasource)->fileHandle;
108 	ASSERT(fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
109 
110 	allowSeeking = ((struct OggVorbisDecoderState *)datasource)->allowSeeking;
111 
112 	if (!allowSeeking)
113 	{
114 		return -1;
115 	}
116 
117 	switch (whence)
118 	{
119 	// Seek to absolute position
120 	case SEEK_SET:
121 		newPos = offset;
122 		break;
123 
124 	// Seek `offset` ahead
125 	case SEEK_CUR:
126 		{
127 			int curPos = PHYSFS_tell(fileHandle);
128 			if (curPos == -1)
129 			{
130 				return -1;
131 			}
132 
133 			newPos = curPos + offset;
134 			break;
135 		}
136 
137 	// Seek backwards from the end of the file
138 	case SEEK_END:
139 		{
140 			int fileSize = PHYSFS_fileLength(fileHandle);
141 			if (fileSize == -1)
142 			{
143 				return -1;
144 			}
145 
146 			newPos = fileSize - 1 - offset;
147 			break;
148 		}
149 
150 	// unrecognized seek instruction
151 	default:
152 		// indicate failure
153 		return -1;
154 	}
155 
156 	// PHYSFS_seek return value of non-zero means success
157 	if (PHYSFS_seek(fileHandle, newPos) != 0)
158 	{
159 		return newPos;    // success
160 	}
161 	else
162 	{
163 		return -1;    // failure
164 	}
165 }
166 
wz_oggVorbis_close(WZ_DECL_UNUSED void * datasource)167 static int wz_oggVorbis_close(WZ_DECL_UNUSED void *datasource)
168 {
169 	return 0;
170 }
171 
wz_oggVorbis_tell(void * datasource)172 static long wz_oggVorbis_tell(void *datasource)
173 {
174 	PHYSFS_file *fileHandle;
175 
176 	ASSERT(datasource != nullptr, "NULL decoder passed!");
177 
178 	fileHandle = ((struct OggVorbisDecoderState *)datasource)->fileHandle;
179 	ASSERT(fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
180 
181 	return PHYSFS_tell(fileHandle);
182 }
183 
184 static const ov_callbacks wz_oggVorbis_callbacks =
185 {
186 	wz_oggVorbis_read,
187 	wz_oggVorbis_seek,
188 	wz_oggVorbis_close,
189 	wz_oggVorbis_tell
190 };
191 
sound_CreateOggVorbisDecoder(PHYSFS_file * PHYSFS_fileHandle,bool allowSeeking)192 struct OggVorbisDecoderState *sound_CreateOggVorbisDecoder(PHYSFS_file *PHYSFS_fileHandle, bool allowSeeking)
193 {
194 	int error;
195 
196 	struct OggVorbisDecoderState *decoder = (struct OggVorbisDecoderState *)malloc(sizeof(struct OggVorbisDecoderState));
197 	if (decoder == nullptr)
198 	{
199 		debug(LOG_FATAL, "Out of memory");
200 		abort();
201 		return nullptr;
202 	}
203 
204 	ASSERT(PHYSFS_fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
205 
206 	decoder->fileHandle = PHYSFS_fileHandle;
207 	decoder->allowSeeking = allowSeeking;
208 
209 	error = ov_open_callbacks(decoder, &decoder->oggVorbis_stream, nullptr, 0, wz_oggVorbis_callbacks);
210 	if (error < 0)
211 	{
212 		debug(LOG_ERROR, "ov_open_callbacks failed with errorcode %s", wz_oggVorbis_getErrorStr(error));
213 		free(decoder);
214 		return nullptr;
215 	}
216 
217 	// Aquire some info about the sound data
218 	decoder->VorbisInfo = ov_info(&decoder->oggVorbis_stream, -1);
219 	if (allowSeeking)
220 	{
221 		decoder->total_stream_time = ov_time_total(&decoder->oggVorbis_stream, -1);
222 	}
223 
224 	return decoder;
225 }
226 
sound_GetOggVorbisTotalTime(struct OggVorbisDecoderState * decoder)227 double sound_GetOggVorbisTotalTime(struct OggVorbisDecoderState *decoder)
228 {
229 	ASSERT(decoder != nullptr, "NULL decoder passed!");
230 	return decoder->total_stream_time;
231 }
232 
sound_DestroyOggVorbisDecoder(struct OggVorbisDecoderState * decoder)233 void sound_DestroyOggVorbisDecoder(struct OggVorbisDecoderState *decoder)
234 {
235 	ASSERT(decoder != nullptr, "NULL decoder passed!");
236 
237 	// Close the OggVorbis decoding stream
238 	ov_clear(&decoder->oggVorbis_stream);
239 
240 	free(decoder);
241 }
242 
getSampleCount(struct OggVorbisDecoderState * decoder)243 static inline unsigned int getSampleCount(struct OggVorbisDecoderState *decoder)
244 {
245 	int numSamples;
246 
247 	ASSERT(decoder != nullptr, "NULL decoder passed!");
248 
249 	numSamples = ov_pcm_total(&decoder->oggVorbis_stream, -1);
250 
251 	if (numSamples == OV_EINVAL)
252 	{
253 		return 0;
254 	}
255 
256 	return numSamples;
257 }
258 
getCurrentSample(struct OggVorbisDecoderState * decoder)259 static inline unsigned int getCurrentSample(struct OggVorbisDecoderState *decoder)
260 {
261 	int samplePos;
262 
263 	ASSERT(decoder != nullptr, "NULL decoder passed!");
264 
265 	samplePos = ov_pcm_tell(&decoder->oggVorbis_stream);
266 
267 	if (samplePos == OV_EINVAL)
268 	{
269 		return 0;
270 	}
271 
272 	return samplePos;
273 }
274 
sound_DecodeOggVorbis(struct OggVorbisDecoderState * decoder,size_t bufferSize)275 soundDataBuffer *sound_DecodeOggVorbis(struct OggVorbisDecoderState *decoder, size_t bufferSize)
276 {
277 	size_t		size = 0;
278 	int		result;
279 
280 	soundDataBuffer *buffer;
281 
282 	ASSERT(decoder != nullptr, "NULL decoder passed!");
283 
284 	if (decoder->allowSeeking)
285 	{
286 		unsigned int sampleCount = getSampleCount(decoder);
287 
288 		unsigned int sizeEstimate = sampleCount * decoder->VorbisInfo->channels * 2;
289 
290 		if (((bufferSize == 0) || (bufferSize > sizeEstimate)) && (sizeEstimate != 0))
291 		{
292 			bufferSize = static_cast<size_t>(sampleCount - getCurrentSample(decoder)) * static_cast<size_t>(decoder->VorbisInfo->channels) * 2;
293 		}
294 	}
295 
296 	// If we can't seek nor receive any suggested size for our buffer, just quit
297 	if (bufferSize == 0)
298 	{
299 		debug(LOG_ERROR, "can't find a proper buffer size");
300 		return nullptr;
301 	}
302 
303 	buffer = (soundDataBuffer *)malloc(bufferSize + sizeof(soundDataBuffer));
304 	if (buffer == nullptr)
305 	{
306 		debug(LOG_ERROR, "couldn't allocate memory (%lu bytes requested)", (unsigned long) bufferSize + sizeof(soundDataBuffer));
307 		return nullptr;
308 	}
309 
310 	buffer->data = (char *)(buffer + 1);
311 	buffer->bufferSize = bufferSize;
312 	buffer->bitsPerSample = 16;
313 
314 	buffer->channelCount = decoder->VorbisInfo->channels;
315 	buffer->frequency = decoder->VorbisInfo->rate;
316 
317 	// Decode PCM data into the buffer until there is nothing to decode left
318 	do
319 	{
320 		// Decode
321 		int section;
322 		size_t readLen = bufferSize - size;
323 		ASSERT(readLen <= static_cast<size_t>(std::numeric_limits<int>::max()), "readLen (%zu) exceeds int::max", readLen);
324 		result = ov_read(&decoder->oggVorbis_stream, &buffer->data[size], static_cast<int>(readLen), OGG_ENDIAN, 2, 1, &section);
325 
326 		if (result < 0)
327 		{
328 			debug(LOG_ERROR, "error decoding from OggVorbis file; errorcode from ov_read: %s", wz_oggVorbis_getErrorStr(result));
329 			free(buffer);
330 			return nullptr;
331 		}
332 		else
333 		{
334 			size += result;
335 		}
336 
337 	}
338 	while ((result != 0 && size < bufferSize));
339 
340 	buffer->size = size;
341 
342 	return buffer;
343 }
344