1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/endian.h"
24 #include "common/stream.h"
25 
26 #include "engines/grim/resource.h"
27 
28 #include "engines/grim/imuse/imuse_sndmgr.h"
29 #include "engines/grim/imuse/imuse_mcmp_mgr.h"
30 
31 namespace Grim {
32 
ImuseSndMgr(bool demo)33 ImuseSndMgr::ImuseSndMgr(bool demo) {
34 	_demo = demo;
35 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
36 		memset(&_sounds[l], 0, sizeof(SoundDesc));
37 	}
38 }
39 
~ImuseSndMgr()40 ImuseSndMgr::~ImuseSndMgr() {
41 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
42 		closeSound(&_sounds[l]);
43 	}
44 }
45 
countElements(SoundDesc * sound)46 void ImuseSndMgr::countElements(SoundDesc *sound) {
47 	uint32 tag;
48 	int32 size = 0;
49 	uint32 pos = sound->inStream->pos();
50 
51 	do {
52 		tag = sound->inStream->readUint32BE();
53 		switch(tag) {
54 		case MKTAG('T','E','X','T'):
55 		case MKTAG('S','T','O','P'):
56 		case MKTAG('F','R','M','T'):
57 			size = sound->inStream->readUint32BE();
58 			sound->inStream->seek(size, SEEK_CUR);
59 			break;
60 		case MKTAG('R','E','G','N'):
61 			sound->numRegions++;
62 			size = sound->inStream->readUint32BE();
63 			sound->inStream->seek(size, SEEK_CUR);
64 			break;
65 		case MKTAG('J','U','M','P'):
66 			sound->numJumps++;
67 			size = sound->inStream->readUint32BE();
68 			sound->inStream->seek(size, SEEK_CUR);
69 			break;
70 		case MKTAG('D','A','T','A'):
71 			break;
72 		default:
73 			error("ImuseSndMgr::countElements() Unknown MAP tag '%s'", Common::tag2string(tag).c_str());
74 		}
75 	} while (tag != MKTAG('D','A','T','A'));
76 
77 	sound->inStream->seek(pos, SEEK_SET);
78 }
79 
parseSoundHeader(SoundDesc * sound,int & headerSize)80 void ImuseSndMgr::parseSoundHeader(SoundDesc *sound, int &headerSize) {
81 	Common::SeekableReadStream *data = sound->inStream;
82 
83 	uint32 tag = data->readUint32BE();
84 	if (tag == MKTAG('R','I','F','F')) {
85 		sound->region = new Region[1];
86 		sound->jump = new Jump[1];
87 		sound->numJumps = 0;
88 		sound->numRegions = 1;
89 		sound->region[0].offset = 0;
90 		data->seek(18, SEEK_CUR);
91 		sound->channels = data->readByte();
92 		data->readByte();
93 		sound->freq = data->readUint32LE();
94 		data->seek(6, SEEK_CUR);
95 		sound->bits = data->readByte();
96 		data->seek(5, SEEK_CUR);
97 		sound->region[0].length = data->readUint32LE();
98 		headerSize = 44;
99 	} else if (tag == MKTAG('i','M','U','S')) {
100 		int32 size = 0;
101 		int32 headerStart = data->pos();
102 		data->seek(12, SEEK_CUR);
103 
104 		int curIndexRegion = 0;
105 		int curIndexJump = 0;
106 
107 		sound->numRegions = 0;
108 		sound->numJumps = 0;
109 		countElements(sound);
110 		sound->region = new Region [sound->numRegions];
111 		sound->jump = new Jump [sound->numJumps];
112 
113 		do {
114 			tag = data->readUint32BE();
115 			switch(tag) {
116 			case MKTAG('F','R','M','T'):
117 				data->seek(12, SEEK_CUR);
118 				sound->bits = data->readUint32BE();
119 				sound->freq = data->readUint32BE();
120 				sound->channels = data->readUint32BE();
121 				break;
122 			case MKTAG('T','E','X','T'):
123 			case MKTAG('S','T','O','P'):
124 				size = data->readUint32BE();
125 				data->seek(size, SEEK_CUR);
126 				break;
127 			case MKTAG('R','E','G','N'):
128 				data->seek(4, SEEK_CUR);
129 				sound->region[curIndexRegion].offset = data->readUint32BE();
130 				sound->region[curIndexRegion].length = data->readUint32BE();
131 				curIndexRegion++;
132 				break;
133 			case MKTAG('J','U','M','P'):
134 				data->seek(4, SEEK_CUR);
135 				sound->jump[curIndexJump].offset = data->readUint32BE();
136 				sound->jump[curIndexJump].dest = data->readUint32BE();
137 				sound->jump[curIndexJump].hookId = data->readUint32BE();
138 				sound->jump[curIndexJump].fadeDelay = data->readUint32BE();
139 				curIndexJump++;
140 				break;
141 			case MKTAG('D','A','T','A'):
142 				data->seek(4, SEEK_CUR);
143 				break;
144 			default:
145 				error("ImuseSndMgr::prepareSound(%s) Unknown MAP tag '%s'", sound->name, Common::tag2string(tag).c_str());
146 			}
147 		} while (tag != MKTAG('D','A','T','A'));
148 		headerSize = data->pos() - headerStart;
149 		int i;
150 		for (i = 0; i < sound->numRegions; i++) {
151 			sound->region[i].offset -= headerSize;
152 		}
153 		for (i = 0; i < sound->numJumps; i++) {
154 			sound->jump[i].offset -= headerSize;
155 			sound->jump[i].dest -= headerSize;
156 		}
157 	} else {
158 		error("ImuseSndMgr::prepareSound() Unknown sound format");
159 	}
160 }
161 
allocSlot()162 ImuseSndMgr::SoundDesc *ImuseSndMgr::allocSlot() {
163 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
164 		if (!_sounds[l].inUse) {
165 			_sounds[l].inUse = true;
166 			return &_sounds[l];
167 		}
168 	}
169 
170 	return nullptr;
171 }
172 
openSound(const char * soundName,int volGroupId)173 ImuseSndMgr::SoundDesc *ImuseSndMgr::openSound(const char *soundName, int volGroupId) {
174 	Common::String s = soundName;
175 	s.toLowercase();
176 	soundName = s.c_str();
177 	const char *extension = soundName + strlen(soundName) - 3;
178 	int headerSize = 0;
179 
180 	SoundDesc *sound = allocSlot();
181 	if (!sound) {
182 		error("ImuseSndMgr::openSound() Can't alloc free sound slot");
183 	}
184 
185 	strcpy(sound->name, soundName);
186 	sound->volGroupId = volGroupId;
187 	sound->inStream = nullptr;
188 
189 	sound->inStream = g_resourceloader->openNewStreamFile(soundName);
190 	if (!sound->inStream) {
191 		closeSound(sound);
192 		return nullptr;
193 	}
194 
195 	if (!_demo && scumm_stricmp(extension, "imu") == 0) {
196 		parseSoundHeader(sound, headerSize);
197 		sound->mcmpData = false;
198 		sound->headerSize = headerSize;
199 	} else if (scumm_stricmp(extension, "wav") == 0 || scumm_stricmp(extension, "imc") == 0 ||
200 			(_demo && scumm_stricmp(extension, "imu") == 0)) {
201 		sound->mcmpMgr = new McmpMgr();
202 		if (!sound->mcmpMgr->openSound(soundName, sound->inStream, headerSize)) {
203 			closeSound(sound);
204 			return nullptr;
205 		}
206 		parseSoundHeader(sound, headerSize);
207 		sound->mcmpData = true;
208 	} else {
209 		error("ImuseSndMgr::openSound() Unrecognized extension for sound file %s", soundName);
210 	}
211 
212 	return sound;
213 }
214 
closeSound(SoundDesc * sound)215 void ImuseSndMgr::closeSound(SoundDesc *sound) {
216 	assert(checkForProperHandle(sound));
217 
218 	if (sound->mcmpMgr) {
219 		delete sound->mcmpMgr;
220 		sound->mcmpMgr = nullptr;
221 	}
222 
223 	if (sound->region) {
224 		delete[] sound->region;
225 		sound->region = nullptr;
226 	}
227 
228 	if (sound->jump) {
229 		delete[] sound->jump;
230 		sound->jump = nullptr;
231 	}
232 
233 	if (sound->inStream) {
234 		delete sound->inStream;
235 		sound->inStream = nullptr;
236 	}
237 
238 	memset(sound, 0, sizeof(SoundDesc));
239 }
240 
cloneSound(SoundDesc * sound)241 ImuseSndMgr::SoundDesc *ImuseSndMgr::cloneSound(SoundDesc *sound) {
242 	assert(checkForProperHandle(sound));
243 
244 	return openSound(sound->name, sound->volGroupId);
245 }
246 
checkForProperHandle(SoundDesc * sound)247 bool ImuseSndMgr::checkForProperHandle(SoundDesc *sound) {
248 	if (!sound)
249 		return false;
250 
251 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
252 		if (sound == &_sounds[l])
253 			return true;
254 	}
255 
256 	return false;
257 }
258 
getFreq(SoundDesc * sound)259 int ImuseSndMgr::getFreq(SoundDesc *sound) {
260 	assert(checkForProperHandle(sound));
261 	return sound->freq;
262 }
263 
getBits(SoundDesc * sound)264 int ImuseSndMgr::getBits(SoundDesc *sound) {
265 	assert(checkForProperHandle(sound));
266 	return sound->bits;
267 }
268 
getChannels(SoundDesc * sound)269 int ImuseSndMgr::getChannels(SoundDesc *sound) {
270 	assert(checkForProperHandle(sound));
271 	return sound->channels;
272 }
273 
isEndOfRegion(SoundDesc * sound,int region)274 bool ImuseSndMgr::isEndOfRegion(SoundDesc *sound, int region) {
275 	assert(checkForProperHandle(sound));
276 	assert(region >= 0 && region < sound->numRegions);
277 	return sound->endFlag;
278 }
279 
getNumRegions(SoundDesc * sound)280 int ImuseSndMgr::getNumRegions(SoundDesc *sound) {
281 	assert(checkForProperHandle(sound));
282 	return sound->numRegions;
283 }
284 
getNumJumps(SoundDesc * sound)285 int ImuseSndMgr::getNumJumps(SoundDesc *sound) {
286 	assert(checkForProperHandle(sound));
287 	return sound->numJumps;
288 }
289 
getRegionOffset(SoundDesc * sound,int region)290 int ImuseSndMgr::getRegionOffset(SoundDesc *sound, int region) {
291 	assert(checkForProperHandle(sound));
292 	assert(region >= 0 && region < sound->numRegions);
293 	return sound->region[region].offset;
294 }
295 
getRegionLength(SoundDesc * sound,int region)296 int ImuseSndMgr::getRegionLength(SoundDesc *sound, int region) {
297 	assert(checkForProperHandle(sound));
298 	assert(region >= 0 && region < sound->numRegions);
299 	return sound->region[region].length;
300 }
301 
getJumpIdByRegionAndHookId(SoundDesc * sound,int region,int hookId)302 int ImuseSndMgr::getJumpIdByRegionAndHookId(SoundDesc *sound, int region, int hookId) {
303 	assert(checkForProperHandle(sound));
304 	assert(region >= 0 && region < sound->numRegions);
305 	int32 offset = sound->region[region].offset;
306 	for (int l = 0; l < sound->numJumps; l++) {
307 		if (offset == sound->jump[l].offset) {
308 			if (sound->jump[l].hookId == hookId)
309 				return l;
310 		}
311 	}
312 
313 	return -1;
314 }
315 
getRegionIdByJumpId(SoundDesc * sound,int jumpId)316 int ImuseSndMgr::getRegionIdByJumpId(SoundDesc *sound, int jumpId) {
317 	assert(checkForProperHandle(sound));
318 	assert(jumpId >= 0 && jumpId < sound->numJumps);
319 	int32 dest = sound->jump[jumpId].dest;
320 	for (int l = 0; l < sound->numRegions; l++) {
321 		if (dest == sound->region[l].offset) {
322 			return l;
323 		}
324 	}
325 
326 	return -1;
327 }
328 
getJumpHookId(SoundDesc * sound,int number)329 int ImuseSndMgr::getJumpHookId(SoundDesc *sound, int number) {
330 	assert(checkForProperHandle(sound));
331 	assert(number >= 0 && number < sound->numJumps);
332 	return sound->jump[number].hookId;
333 }
334 
getJumpFade(SoundDesc * sound,int number)335 int ImuseSndMgr::getJumpFade(SoundDesc *sound, int number) {
336 	assert(checkForProperHandle(sound));
337 	assert(number >= 0 && number < sound->numJumps);
338 	return sound->jump[number].fadeDelay;
339 }
340 
getDataFromRegion(SoundDesc * sound,int region,byte ** buf,int32 offset,int32 size)341 int32 ImuseSndMgr::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size) {
342 	assert(checkForProperHandle(sound));
343 	assert(buf && offset >= 0 && size >= 0);
344 	assert(region >= 0 && region < sound->numRegions);
345 
346 	int32 region_offset = sound->region[region].offset;
347 	int32 region_length = sound->region[region].length;
348 
349 	if (offset + size > region_length) {
350 		size = region_length - offset;
351 		sound->endFlag = true;
352 	} else {
353 		sound->endFlag = false;
354 	}
355 
356 	if (sound->mcmpData) {
357 		size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf);
358 	} else {
359 		*buf = static_cast<byte *>(malloc(size));
360 		sound->inStream->seek(region_offset + offset + sound->headerSize, SEEK_SET);
361 		sound->inStream->read(*buf, size);
362 	}
363 
364 	return size;
365 }
366 
367 } // end of namespace Grim
368