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