1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM 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 
24 #include "common/scummsys.h"
25 
26 #include "audio/audiostream.h"
27 #include "audio/decoders/flac.h"
28 #include "audio/decoders/voc.h"
29 #include "audio/decoders/vorbis.h"
30 #include "audio/decoders/mp3.h"
31 
32 #include "scumm/resource.h"
33 #include "scumm/scumm.h"
34 #include "scumm/imuse_digi/dimuse_bndmgr.h"
35 #include "scumm/imuse_digi/dimuse_codecs.h"
36 #include "scumm/imuse_digi/dimuse_sndmgr.h"
37 
38 namespace Scumm {
39 
ImuseDigiSndMgr(ScummEngine * scumm)40 ImuseDigiSndMgr::ImuseDigiSndMgr(ScummEngine *scumm) {
41 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
42 		memset(&_sounds[l], 0, sizeof(SoundDesc));
43 	}
44 	_vm = scumm;
45 	_disk = 0;
46 	_cacheBundleDir = new BundleDirCache();
47 	assert(_cacheBundleDir);
48 	BundleCodecs::initializeImcTables();
49 }
50 
~ImuseDigiSndMgr()51 ImuseDigiSndMgr::~ImuseDigiSndMgr() {
52 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
53 		closeSound(&_sounds[l]);
54 	}
55 
56 	delete _cacheBundleDir;
57 	BundleCodecs::releaseImcTables();
58 }
59 
countElements(byte * ptr,int & numRegions,int & numJumps,int & numSyncs,int & numMarkers)60 void ImuseDigiSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs, int &numMarkers) {
61 	uint32 tag;
62 	int32 size = 0;
63 
64 	do {
65 		tag = READ_BE_UINT32(ptr); ptr += 4;
66 		switch (tag) {
67 		case MKTAG('S','T','O','P'):
68 		case MKTAG('F','R','M','T'):
69 		case MKTAG('D','A','T','A'):
70 			size = READ_BE_UINT32(ptr); ptr += size + 4;
71 			break;
72 		case MKTAG('T','E','X','T'):
73 			if (!scumm_stricmp((const char *)(ptr + 8), "exit"))
74 				numMarkers++;
75 			size = READ_BE_UINT32(ptr); ptr += size + 4;
76 			break;
77 		case MKTAG('R','E','G','N'):
78 			numRegions++;
79 			size = READ_BE_UINT32(ptr); ptr += size + 4;
80 			break;
81 		case MKTAG('J','U','M','P'):
82 			numJumps++;
83 			size = READ_BE_UINT32(ptr); ptr += size + 4;
84 			break;
85 		case MKTAG('S','Y','N','C'):
86 			numSyncs++;
87 			size = READ_BE_UINT32(ptr); ptr += size + 4;
88 			break;
89 		default:
90 			error("ImuseDigiSndMgr::countElements() Unknown sfx header '%s'", tag2str(tag));
91 		}
92 	} while (tag != MKTAG('D','A','T','A'));
93 }
94 
prepareSoundFromRMAP(Common::SeekableReadStream * file,SoundDesc * sound,int32 offset,int32 size)95 void ImuseDigiSndMgr::prepareSoundFromRMAP(Common::SeekableReadStream *file, SoundDesc *sound, int32 offset, int32 size) {
96 	int l;
97 
98 	file->seek(offset, SEEK_SET);
99 	uint32 tag = file->readUint32BE();
100 	assert(tag == MKTAG('R','M','A','P'));
101 	int32 version = file->readUint32BE();
102 	if (version != 3) {
103 		if (version == 2) {
104 			warning("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version of compressed *.bun file, expected 3, but it's 2");
105 			warning("Suggested to recompress with latest tool from daily builds");
106 		} else
107 			error("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version number, expected 3, but it's: %d", version);
108 	}
109 	sound->bits = file->readUint32BE();
110 	sound->freq = file->readUint32BE();
111 	sound->channels = file->readUint32BE();
112 	sound->numRegions = file->readUint32BE();
113 	sound->numJumps = file->readUint32BE();
114 	sound->numSyncs = file->readUint32BE();
115 	if (version >= 3)
116 		sound->numMarkers = file->readUint32BE();
117 	else
118 		sound->numMarkers = 0;
119 
120 	sound->region = new Region[sound->numRegions];
121 	assert(sound->region);
122 	sound->jump = new Jump[sound->numJumps];
123 	assert(sound->jump);
124 	sound->sync = new Sync[sound->numSyncs];
125 	assert(sound->sync);
126 	sound->marker = new Marker[sound->numMarkers];
127 	assert(sound->marker);
128 
129 	for (l = 0; l < sound->numRegions; l++) {
130 		sound->region[l].offset = file->readUint32BE();
131 		sound->region[l].length = file->readUint32BE();
132 	}
133 	for (l = 0; l < sound->numJumps; l++) {
134 		sound->jump[l].offset = file->readUint32BE();
135 		sound->jump[l].dest = file->readUint32BE();
136 		sound->jump[l].hookId = file->readUint32BE();
137 		sound->jump[l].fadeDelay = file->readUint32BE();
138 	}
139 	for (l = 0; l < sound->numSyncs; l++) {
140 		sound->sync[l].size = file->readUint32BE();
141 		sound->sync[l].ptr = new byte[sound->sync[l].size];
142 		file->read(sound->sync[l].ptr, sound->sync[l].size);
143 	}
144 	if (version >= 3) {
145 		for (l = 0; l < sound->numMarkers; l++) {
146 			sound->marker[l].pos = file->readUint32BE();
147 			sound->marker[l].length = file->readUint32BE();
148 			sound->marker[l].ptr = new char[sound->marker[l].length];
149 			file->read(sound->marker[l].ptr, sound->marker[l].length);
150 		}
151 	}
152 }
153 
prepareSound(byte * ptr,SoundDesc * sound)154 void ImuseDigiSndMgr::prepareSound(byte *ptr, SoundDesc *sound) {
155 	if (READ_BE_UINT32(ptr) == MKTAG('C','r','e','a')) {
156 		bool quit = false;
157 		int len;
158 
159 		int32 offset = READ_LE_UINT16(ptr + 20);
160 		int16 code = READ_LE_UINT16(ptr + 24);
161 
162 		sound->numRegions = 0;
163 		sound->region = new Region[70];
164 		assert(sound->region);
165 
166 		sound->numJumps = 0;
167 		sound->jump = new Jump[1];
168 		assert(sound->jump);
169 
170 		sound->numSyncs = 0;
171 
172 		sound->resPtr = ptr;
173 		sound->bits = 8;
174 		sound->channels = 1;
175 
176 		while (!quit) {
177 			len = READ_LE_UINT32(ptr + offset);
178 			code = len & 0xFF;
179 			if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
180 				// try again with 2 bytes forward (workaround for some FT sounds (ex.362, 363)
181 				offset += 2;
182 				len = READ_LE_UINT32(ptr + offset);
183 				code = len & 0xFF;
184 				if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
185 					error("Invalid code in VOC file : %d", code);
186 				}
187 			}
188 			offset += 4;
189 			len >>= 8;
190 			switch (code) {
191 			case 0:
192 				quit = true;
193 				break;
194 			case 1:
195 				{
196 					int time_constant = ptr[offset];
197 					offset += 2;
198 					len -= 2;
199 					sound->freq = Audio::getSampleRateFromVOCRate(time_constant);
200 					sound->region[sound->numRegions].offset = offset;
201 					sound->region[sound->numRegions].length = len;
202 					sound->numRegions++;
203 				}
204 				break;
205 			case 6:	// begin of loop
206 				sound->jump[0].dest = offset + 8;
207 				sound->jump[0].hookId = 0;
208 				sound->jump[0].fadeDelay = 0;
209 				break;
210 			case 7:	// end of loop
211 				sound->jump[0].offset = offset - 4;
212 				sound->numJumps++;
213 				sound->region[sound->numRegions].offset = offset - 4;
214 				sound->region[sound->numRegions].length = 0;
215 				sound->numRegions++;
216 				break;
217 			default:
218 				error("Invalid code in VOC file : %d", code);
219 				quit = true;
220 				break;
221 			}
222 			offset += len;
223 		}
224 	} else if (READ_BE_UINT32(ptr) == MKTAG('i','M','U','S')) {
225 		uint32 tag;
226 		int32 size = 0;
227 		byte *s_ptr = ptr;
228 		ptr += 16;
229 
230 		int curIndexRegion = 0;
231 		int curIndexJump = 0;
232 		int curIndexSync = 0;
233 		int curIndexMarker = 0;
234 
235 		sound->numRegions = 0;
236 		sound->numJumps = 0;
237 		sound->numSyncs = 0;
238 		sound->numMarkers = 0;
239 		countElements(ptr, sound->numRegions, sound->numJumps, sound->numSyncs, sound->numMarkers);
240 		sound->region = new Region[sound->numRegions];
241 		assert(sound->region);
242 		sound->jump = new Jump[sound->numJumps];
243 		assert(sound->jump);
244 		sound->sync = new Sync[sound->numSyncs];
245 		assert(sound->sync);
246 		sound->marker = new Marker[sound->numMarkers];
247 		assert(sound->marker);
248 
249 		do {
250 			tag = READ_BE_UINT32(ptr); ptr += 4;
251 			switch (tag) {
252 			case MKTAG('F','R','M','T'):
253 				ptr += 12;
254 				sound->bits = READ_BE_UINT32(ptr); ptr += 4;
255 				sound->freq = READ_BE_UINT32(ptr); ptr += 4;
256 				sound->channels = READ_BE_UINT32(ptr); ptr += 4;
257 				break;
258 			case MKTAG('T','E','X','T'):
259 				if (!scumm_stricmp((const char *)(ptr + 8), "exit")) {
260 					sound->marker[curIndexMarker].pos = READ_BE_UINT32(ptr + 4);
261 					sound->marker[curIndexMarker].length = strlen((const char *)(ptr + 8)) + 1;
262 					sound->marker[curIndexMarker].ptr = new char[sound->marker[curIndexMarker].length];
263 					assert(sound->marker[curIndexMarker].ptr);
264 					strcpy(sound->marker[curIndexMarker].ptr, (const char *)(ptr + 8));
265 					curIndexMarker++;
266 				}
267 				size = READ_BE_UINT32(ptr); ptr += size + 4;
268 				break;
269 			case MKTAG('S','T','O','P'):
270 				size = READ_BE_UINT32(ptr); ptr += size + 4;
271 				break;
272 			case MKTAG('R','E','G','N'):
273 				ptr += 4;
274 				sound->region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4;
275 				sound->region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4;
276 				curIndexRegion++;
277 				break;
278 			case MKTAG('J','U','M','P'):
279 				ptr += 4;
280 				sound->jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4;
281 				sound->jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4;
282 				sound->jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4;
283 				sound->jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4;
284 				curIndexJump++;
285 				break;
286 			case MKTAG('S','Y','N','C'):
287 				size = READ_BE_UINT32(ptr); ptr += 4;
288 				sound->sync[curIndexSync].size = size;
289 				sound->sync[curIndexSync].ptr = new byte[size];
290 				assert(sound->sync[curIndexSync].ptr);
291 				memcpy(sound->sync[curIndexSync].ptr, ptr, size);
292 				curIndexSync++;
293 				ptr += size;
294 				break;
295 			case MKTAG('D','A','T','A'):
296 				ptr += 4;
297 				break;
298 			default:
299 				error("ImuseDigiSndMgr::prepareSound(%d/%s) Unknown sfx header '%s'", sound->soundId, sound->name, tag2str(tag));
300 			}
301 		} while (tag != MKTAG('D','A','T','A'));
302 		sound->offsetData = ptr - s_ptr;
303 	} else {
304 		error("ImuseDigiSndMgr::prepareSound(): Unknown sound format");
305 	}
306 }
307 
allocSlot()308 ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::allocSlot() {
309 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
310 		if (!_sounds[l].inUse) {
311 			_sounds[l].inUse = true;
312 			return &_sounds[l];
313 		}
314 	}
315 
316 	return NULL;
317 }
318 
openMusicBundle(SoundDesc * sound,int & disk)319 bool ImuseDigiSndMgr::openMusicBundle(SoundDesc *sound, int &disk) {
320 	bool result = false;
321 
322 	sound->bundle = new BundleMgr(_cacheBundleDir);
323 	assert(sound->bundle);
324 	if (_vm->_game.id == GID_CMI) {
325 		if (_vm->_game.features & GF_DEMO) {
326 			result = sound->bundle->open("music.bun", sound->compressed);
327 		} else {
328 			char musicfile[20];
329 			if (disk == -1)
330 				disk = _vm->VAR(_vm->VAR_CURRENTDISK);
331 			sprintf(musicfile, "musdisk%d.bun", disk);
332 //			if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
333 //				_vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
334 //				_vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
335 //				_vm->_imuseDigital->stopAllSounds();
336 //				sound->bundle->closeFile();
337 //			}
338 
339 			result = sound->bundle->open(musicfile, sound->compressed, true);
340 
341 			// FIXME: Shouldn't we only set _disk if result == true?
342 			_disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
343 		}
344 	} else if (_vm->_game.id == GID_DIG)
345 		result = sound->bundle->open("digmusic.bun", sound->compressed, true);
346 	else
347 		error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load");
348 
349 	_vm->VAR(_vm->VAR_MUSIC_BUNDLE_LOADED) = result ? 1 : 0;
350 
351 	return result;
352 }
353 
openVoiceBundle(SoundDesc * sound,int & disk)354 bool ImuseDigiSndMgr::openVoiceBundle(SoundDesc *sound, int &disk) {
355 	bool result = false;
356 
357 	sound->bundle = new BundleMgr(_cacheBundleDir);
358 	assert(sound->bundle);
359 	if (_vm->_game.id == GID_CMI) {
360 		if (_vm->_game.features & GF_DEMO) {
361 			result = sound->bundle->open("voice.bun", sound->compressed);
362 		} else {
363 			char voxfile[20];
364 			if (disk == -1)
365 				disk = _vm->VAR(_vm->VAR_CURRENTDISK);
366 			sprintf(voxfile, "voxdisk%d.bun", disk);
367 //			if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
368 //				_vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
369 //				_vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
370 //				_vm->_imuseDigital->stopAllSounds();
371 //				sound->bundle->closeFile();
372 //			}
373 
374 			result = sound->bundle->open(voxfile, sound->compressed);
375 
376 			// FIXME: Shouldn't we only set _disk if result == true?
377 			_disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
378 		}
379 	} else if (_vm->_game.id == GID_DIG)
380 		result = sound->bundle->open("digvoice.bun", sound->compressed);
381 	else
382 		error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load");
383 
384 	_vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = result ? 1 : 0;
385 
386 	return result;
387 }
388 
openSound(int32 soundId,const char * soundName,int soundType,int volGroupId,int disk)389 ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk) {
390 	assert(soundId >= 0);
391 	assert(soundType);
392 
393 	SoundDesc *sound = allocSlot();
394 	if (!sound) {
395 		error("ImuseDigiSndMgr::openSound() can't alloc free sound slot");
396 	}
397 
398 	const bool header_outside = ((_vm->_game.id == GID_CMI) && !(_vm->_game.features & GF_DEMO));
399 	bool result = false;
400 	byte *ptr = NULL;
401 
402 	switch (soundType) {
403 	case IMUSE_RESOURCE:
404 		assert(soundName[0] == 0);	// Paranoia check
405 
406 		_vm->ensureResourceLoaded(rtSound, soundId);
407 		_vm->_res->lock(rtSound, soundId);
408 		ptr = _vm->getResourceAddress(rtSound, soundId);
409 		if (ptr == NULL) {
410 			closeSound(sound);
411 			return NULL;
412 		}
413 		sound->resPtr = ptr;
414 		break;
415 	case IMUSE_BUNDLE:
416 		if (volGroupId == IMUSE_VOLGRP_VOICE)
417 			result = openVoiceBundle(sound, disk);
418 		else if (volGroupId == IMUSE_VOLGRP_MUSIC)
419 			result = openMusicBundle(sound, disk);
420 		else
421 			error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId);
422 		if (!result) {
423 			closeSound(sound);
424 			return NULL;
425 		}
426 		if (sound->compressed) {
427 			char fileName[24];
428 			int32 offset = 0, size = 0;
429 			sprintf(fileName, "%s.map", soundName);
430 			Common::SeekableReadStream *rmapFile = sound->bundle->getFile(fileName, offset, size);
431 			if (!rmapFile) {
432 				closeSound(sound);
433 				return NULL;
434 			}
435 			prepareSoundFromRMAP(rmapFile, sound, offset, size);
436 			strcpy(sound->name, soundName);
437 			sound->soundId = soundId;
438 			sound->type = soundType;
439 			sound->volGroupId = volGroupId;
440 			sound->disk = disk;
441 			return sound;
442 		} else if (soundName[0] == 0) {
443 			if (sound->bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside) == 0 || ptr == NULL) {
444 				closeSound(sound);
445 				free(ptr);
446 				return NULL;
447 			}
448 		} else {
449 			if (sound->bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside) == 0 || ptr == NULL) {
450 				closeSound(sound);
451 				free(ptr);
452 				return NULL;
453 			}
454 		}
455 		sound->resPtr = 0;
456 		break;
457 	default:
458 		error("ImuseDigiSndMgr::openSound() Unknown soundType %d (trying to load sound %d)", soundType, soundId);
459 	}
460 
461 	strcpy(sound->name, soundName);
462 	sound->soundId = soundId;
463 	sound->type = soundType;
464 	sound->volGroupId = volGroupId;
465 	sound->disk = _disk;
466 	prepareSound(ptr, sound);
467 	if ((soundType == IMUSE_BUNDLE) && !sound->compressed) {
468 		free(ptr);
469 	}
470 	return sound;
471 }
472 
closeSound(SoundDesc * soundDesc)473 void ImuseDigiSndMgr::closeSound(SoundDesc *soundDesc) {
474 	assert(checkForProperHandle(soundDesc));
475 
476 	if (soundDesc->resPtr) {
477 		bool found = false;
478 		for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
479 			if ((_sounds[l].soundId == soundDesc->soundId) && (&_sounds[l] != soundDesc))
480 				found = true;
481 		}
482 		if (!found)
483 			_vm->_res->unlock(rtSound, soundDesc->soundId);
484 	}
485 
486 	delete soundDesc->compressedStream;
487 	delete soundDesc->bundle;
488 
489 	for (int r = 0; r < soundDesc->numSyncs; r++)
490 		delete[] soundDesc->sync[r].ptr;
491 	for (int r = 0; r < soundDesc->numMarkers; r++)
492 		delete[] soundDesc->marker[r].ptr;
493 	delete[] soundDesc->region;
494 	delete[] soundDesc->jump;
495 	delete[] soundDesc->sync;
496 	delete[] soundDesc->marker;
497 	memset(soundDesc, 0, sizeof(SoundDesc));
498 }
499 
cloneSound(SoundDesc * soundDesc)500 ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::cloneSound(SoundDesc *soundDesc) {
501 	ImuseDigiSndMgr::SoundDesc *desc;
502 	assert(checkForProperHandle(soundDesc));
503 
504 	desc = openSound(soundDesc->soundId, soundDesc->name, soundDesc->type, soundDesc->volGroupId, soundDesc->disk);
505 	if (!desc)
506 		desc = openSound(soundDesc->soundId, soundDesc->name, soundDesc->type, soundDesc->volGroupId, 1);
507 	if (!desc)
508 		desc = openSound(soundDesc->soundId, soundDesc->name, soundDesc->type, soundDesc->volGroupId, 2);
509 	return desc;
510 }
511 
checkForProperHandle(SoundDesc * soundDesc)512 bool ImuseDigiSndMgr::checkForProperHandle(SoundDesc *soundDesc) {
513 	if (!soundDesc)
514 		return false;
515 	for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
516 		if (soundDesc == &_sounds[l])
517 			return true;
518 	}
519 	return false;
520 }
521 
isSndDataExtComp(SoundDesc * soundDesc)522 bool ImuseDigiSndMgr::isSndDataExtComp(SoundDesc *soundDesc) {
523 	assert(checkForProperHandle(soundDesc));
524 	return soundDesc->compressed;
525 }
526 
getFreq(SoundDesc * soundDesc)527 int ImuseDigiSndMgr::getFreq(SoundDesc *soundDesc) {
528 	assert(checkForProperHandle(soundDesc));
529 	return soundDesc->freq;
530 }
531 
getBits(SoundDesc * soundDesc)532 int ImuseDigiSndMgr::getBits(SoundDesc *soundDesc) {
533 	assert(checkForProperHandle(soundDesc));
534 	return soundDesc->bits;
535 }
536 
getChannels(SoundDesc * soundDesc)537 int ImuseDigiSndMgr::getChannels(SoundDesc *soundDesc) {
538 	assert(checkForProperHandle(soundDesc));
539 	return soundDesc->channels;
540 }
541 
isEndOfRegion(SoundDesc * soundDesc,int region)542 bool ImuseDigiSndMgr::isEndOfRegion(SoundDesc *soundDesc, int region) {
543 	assert(checkForProperHandle(soundDesc));
544 	assert(region >= 0 && region < soundDesc->numRegions);
545 	return soundDesc->endFlag;
546 }
547 
getNumRegions(SoundDesc * soundDesc)548 int ImuseDigiSndMgr::getNumRegions(SoundDesc *soundDesc) {
549 	assert(checkForProperHandle(soundDesc));
550 	return soundDesc->numRegions;
551 }
552 
getNumJumps(SoundDesc * soundDesc)553 int ImuseDigiSndMgr::getNumJumps(SoundDesc *soundDesc) {
554 	assert(checkForProperHandle(soundDesc));
555 	return soundDesc->numJumps;
556 }
557 
getRegionOffset(SoundDesc * soundDesc,int region)558 int ImuseDigiSndMgr::getRegionOffset(SoundDesc *soundDesc, int region) {
559 	debug(5, "getRegionOffset() region:%d", region);
560 	assert(checkForProperHandle(soundDesc));
561 	assert(region >= 0 && region < soundDesc->numRegions);
562 	return soundDesc->region[region].offset;
563 }
564 
getJumpIdByRegionAndHookId(SoundDesc * soundDesc,int region,int hookId)565 int ImuseDigiSndMgr::getJumpIdByRegionAndHookId(SoundDesc *soundDesc, int region, int hookId) {
566 	debug(5, "getJumpIdByRegionAndHookId() region:%d, hookId:%d", region, hookId);
567 	assert(checkForProperHandle(soundDesc));
568 	assert(region >= 0 && region < soundDesc->numRegions);
569 	int32 offset = soundDesc->region[region].offset;
570 	for (int l = 0; l < soundDesc->numJumps; l++) {
571 		if (offset == soundDesc->jump[l].offset) {
572 			if (soundDesc->jump[l].hookId == hookId)
573 				return l;
574 		}
575 	}
576 
577 	return -1;
578 }
579 
checkForTriggerByRegionAndMarker(SoundDesc * soundDesc,int region,const char * marker)580 bool ImuseDigiSndMgr::checkForTriggerByRegionAndMarker(SoundDesc *soundDesc, int region, const char *marker) {
581 	debug(5, "checkForTriggerByRegionAndMarker() region:%d, marker:%s", region, marker);
582 	assert(checkForProperHandle(soundDesc));
583 	assert(region >= 0 && region < soundDesc->numRegions);
584 	assert(marker);
585 	int32 offset = soundDesc->region[region].offset;
586 	for (int l = 0; l < soundDesc->numMarkers; l++) {
587 		if (offset == soundDesc->marker[l].pos) {
588 			if (!scumm_stricmp(soundDesc->marker[l].ptr, marker))
589 				return true;
590 		}
591 	}
592 
593 	return false;
594 }
595 
getSyncSizeAndPtrById(SoundDesc * soundDesc,int number,int32 & sync_size,byte ** sync_ptr)596 void ImuseDigiSndMgr::getSyncSizeAndPtrById(SoundDesc *soundDesc, int number, int32 &sync_size, byte **sync_ptr) {
597 	assert(checkForProperHandle(soundDesc));
598 	assert(number >= 0);
599 	if (number < soundDesc->numSyncs) {
600 		sync_size = soundDesc->sync[number].size;
601 		*sync_ptr = soundDesc->sync[number].ptr;
602 	} else {
603 		sync_size = 0;
604 		*sync_ptr = NULL;
605 	}
606 }
607 
getRegionIdByJumpId(SoundDesc * soundDesc,int jumpId)608 int ImuseDigiSndMgr::getRegionIdByJumpId(SoundDesc *soundDesc, int jumpId) {
609 	debug(5, "getRegionIdByJumpId() jumpId:%d", jumpId);
610 	assert(checkForProperHandle(soundDesc));
611 	assert(jumpId >= 0 && jumpId < soundDesc->numJumps);
612 	int32 dest = soundDesc->jump[jumpId].dest;
613 	for (int l = 0; l < soundDesc->numRegions; l++) {
614 		if (dest == soundDesc->region[l].offset) {
615 			return l;
616 		}
617 	}
618 
619 	return -1;
620 }
621 
getJumpHookId(SoundDesc * soundDesc,int number)622 int ImuseDigiSndMgr::getJumpHookId(SoundDesc *soundDesc, int number) {
623 	debug(5, "getJumpHookId() number:%d", number);
624 	assert(checkForProperHandle(soundDesc));
625 	assert(number >= 0 && number < soundDesc->numJumps);
626 	return soundDesc->jump[number].hookId;
627 }
628 
getJumpFade(SoundDesc * soundDesc,int number)629 int ImuseDigiSndMgr::getJumpFade(SoundDesc *soundDesc, int number) {
630 	debug(5, "getJumpFade() number:%d", number);
631 	assert(checkForProperHandle(soundDesc));
632 	assert(number >= 0 && number < soundDesc->numJumps);
633 	return soundDesc->jump[number].fadeDelay;
634 }
635 
getDataFromRegion(SoundDesc * soundDesc,int region,byte ** buf,int32 offset,int32 size)636 int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte **buf, int32 offset, int32 size) {
637 	debug(6, "getDataFromRegion() region:%d, offset:%d, size:%d, numRegions:%d", region, offset, size, soundDesc->numRegions);
638 	assert(checkForProperHandle(soundDesc));
639 	assert(buf && offset >= 0 && size >= 0);
640 	assert(region >= 0 && region < soundDesc->numRegions);
641 
642 	int32 region_offset = soundDesc->region[region].offset;
643 	int32 region_length = soundDesc->region[region].length;
644 	int32 offset_data = soundDesc->offsetData;
645 	int32 start = region_offset - offset_data;
646 
647 	if (offset + size + offset_data > region_length) {
648 		size = region_length - offset;
649 		soundDesc->endFlag = true;
650 	} else {
651 		soundDesc->endFlag = false;
652 	}
653 
654 	int header_size = soundDesc->offsetData;
655 	bool header_outside = ((_vm->_game.id == GID_CMI) && !(_vm->_game.features & GF_DEMO));
656 	if ((soundDesc->bundle) && (!soundDesc->compressed)) {
657 		size = soundDesc->bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside);
658 	} else if (soundDesc->resPtr) {
659 		*buf = (byte *)malloc(size);
660 		assert(*buf);
661 		memcpy(*buf, soundDesc->resPtr + start + offset + header_size, size);
662 	} else if ((soundDesc->bundle) && (soundDesc->compressed)) {
663 		*buf = (byte *)malloc(size);
664 		assert(*buf);
665 		char fileName[26];
666 		int offsetMs = (((offset * 8 * 10) / soundDesc->bits) / (soundDesc->channels * soundDesc->freq)) * 100;
667 		sprintf(fileName, "%s_reg%03d", soundDesc->name, region);
668 		if (scumm_stricmp(fileName, soundDesc->lastFileName) != 0) {
669 			int32 offs = 0, len = 0;
670 			Common::SeekableReadStream *cmpFile;
671 #if defined(USE_FLAC) || defined(USE_VORBIS) || defined(USE_MAD)
672 			uint8 soundMode = 0;
673 #endif
674 
675 			sprintf(fileName, "%s_reg%03d.fla", soundDesc->name, region);
676 			cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
677 			if (len) {
678 #ifndef USE_FLAC
679 				error("FLAC library compiled support needed");
680 #else
681 				soundMode = 3;
682 #endif
683 			}
684 			if (!len) {
685 				sprintf(fileName, "%s_reg%03d.ogg", soundDesc->name, region);
686 				cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
687 				if (len) {
688 #ifndef USE_VORBIS
689 					error("Vorbis library compiled support needed");
690 #else
691 					soundMode = 2;
692 #endif
693 				}
694 			}
695 			if (!len) {
696 				sprintf(fileName, "%s_reg%03d.mp3", soundDesc->name, region);
697 				cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
698 				if (len) {
699 #ifndef USE_MAD
700 					error("Mad library compiled support needed");
701 #else
702 					soundMode = 1;
703 #endif
704 				}
705 			}
706 			assert(len);
707 
708 			if (!soundDesc->compressedStream) {
709 				Common::SeekableReadStream *tmp = cmpFile->readStream(len);
710 				assert(tmp);
711 #ifdef USE_FLAC
712 				if (soundMode == 3)
713 					soundDesc->compressedStream = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
714 #endif
715 #ifdef USE_VORBIS
716 				if (soundMode == 2)
717 					soundDesc->compressedStream = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
718 #endif
719 #ifdef USE_MAD
720 				if (soundMode == 1)
721 					soundDesc->compressedStream = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
722 #endif
723 				assert(soundDesc->compressedStream);
724 				soundDesc->compressedStream->seek(offsetMs);
725 			}
726 			strcpy(soundDesc->lastFileName, fileName);
727 		}
728 		size = soundDesc->compressedStream->readBuffer((int16 *)*buf, size / 2) * 2;
729 		if (soundDesc->compressedStream->endOfData() || soundDesc->endFlag) {
730 			delete soundDesc->compressedStream;
731 			soundDesc->compressedStream = NULL;
732 			soundDesc->lastFileName[0] = 0;
733 			soundDesc->endFlag = true;
734 		}
735 	}
736 
737 	return size;
738 }
739 
740 } // End of namespace Scumm
741