1 /*
2 SOUNDFILE.CPP
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(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 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 */
22 
23 #include "SoundFile.h"
24 #include "Logging.h"
25 #include "csmisc.h"
26 #include "Decoder.h"
27 
28 #include <assert.h>
29 #include <boost/make_shared.hpp>
30 
31 #include "BStream.h"
32 #include <boost/iostreams/stream_buffer.hpp>
33 #include <utility>
34 
35 namespace io = boost::iostreams;
36 
SoundHeader()37 SoundHeader::SoundHeader() :
38 	SoundInfo(),
39 	data_offset(0)
40 {
41 }
42 
UnpackStandardSystem7Header(BIStreamBE & header)43 bool SoundHeader::UnpackStandardSystem7Header(BIStreamBE &header)
44 {
45 	try
46 	{
47 		bytes_per_frame = 1;
48 		signed_8bit = false;
49 		sixteen_bit = false;
50 		stereo = false;
51 		little_endian = false;
52 		header.ignore(4); // sample pointer
53 		header >> length;
54 		header >> rate;
55 		header >> loop_start;
56 		header >> loop_end;
57 
58 		return true;
59 	} catch (basic_bstream::failure e) {
60 		return false;
61 	}
62 }
63 
UnpackExtendedSystem7Header(BIStreamBE & header)64 bool SoundHeader::UnpackExtendedSystem7Header(BIStreamBE &header)
65 {
66 	try
67 	{
68 		signed_8bit = false;
69 		header.ignore(4); // sample pointer
70 		int32 num_channels;
71 		header >> num_channels;
72 		stereo = (num_channels == 2);
73 		header >> rate;
74 		header >> loop_start;
75 		header >> loop_end;
76 		uint8 header_type;
77 		header >> header_type;
78 		header.ignore(1); // baseFrequency
79 		int32 num_frames;
80 		header >> num_frames;
81 
82 		if (header_type == 0xfe)
83 		{
84 			header.ignore(10); // AIFF rate
85 			header.ignore(4); // marker chunk
86 			uint32 format;
87 			header >> format;
88 			header.ignore(4 * 3); // future use, ptr, ptr
89 			int16 comp_id;
90 			header >> comp_id;
91 			if (format != FOUR_CHARS_TO_INT('t','w','o','s') || comp_id != -1) {
92 				return false;
93 			}
94 			signed_8bit = true;
95 			header.ignore(4);
96 		} else {
97 			header.ignore(22);
98 		}
99 
100 		int16 sample_size;
101 		header >> sample_size;
102 
103 		sixteen_bit = (sample_size == 16);
104 		bytes_per_frame = (sixteen_bit ? 2 : 1) * (stereo ? 2 : 1);
105 
106 		length = num_frames * bytes_per_frame;
107 		little_endian = false;
108 
109 		if ((loop_end - loop_start >= 4) && ((loop_start % bytes_per_frame) || (loop_end % bytes_per_frame)))
110 		{
111 			logWarning("loop_start=%i and loop_end=%i but bytes_per_frame=%i; interpreting as frame offsets", loop_start, loop_end, bytes_per_frame);
112 			loop_start *= bytes_per_frame;
113 			loop_end *= bytes_per_frame;
114 		}
115 
116 		return true;
117 	} catch (basic_bstream::failure e) {
118 		return false;
119 	}
120 }
121 
Load(BIStreamBE & s)122 bool SoundHeader::Load(BIStreamBE& s)
123 {
124 	Clear();
125 
126 	uint8 encoding;
127 	s.rdbuf()->pubseekoff(20, std::ios_base::cur);
128 	encoding = s.rdbuf()->sgetc();
129 	s.rdbuf()->pubseekoff(-20, std::ios_base::cur);
130 
131 	switch (encoding)
132 	{
133 	case stdSH:
134 		if (UnpackStandardSystem7Header(s))
135 		{
136 			data_offset = 22;
137 			return true;
138 		}
139 		break;
140 	case extSH:
141 	case cmpSH:
142 		if (UnpackExtendedSystem7Header(s))
143 		{
144 			data_offset = 64;
145 			return true;
146 		}
147 		break;
148 	}
149 
150 	return false;
151 }
152 
LoadData(BIStreamBE & s)153 boost::shared_ptr<SoundData> SoundHeader::LoadData(BIStreamBE& s)
154 {
155 	if (!data_offset)
156 	{
157 		return boost::shared_ptr<SoundData>();
158 	}
159 
160 	s.ignore(data_offset);
161 	boost::shared_ptr<SoundData> p = boost::make_shared<SoundData>(length);
162 	try
163 	{
164 		s.read(reinterpret_cast<char*>(&(*p)[0]), length);
165 	}
166 	catch (basic_bstream::failure e)
167 	{
168 		p.reset();
169 	}
170 
171 	return p;
172 }
173 
Load(OpenedFile & SoundFile)174 bool SoundHeader::Load(OpenedFile &SoundFile)
175 {
176 	io::stream_buffer<opened_file_device> sb(SoundFile);
177 	BIStreamBE s(&sb);
178 
179 	return Load(s);
180 }
181 
LoadData(OpenedFile & SoundFile)182 boost::shared_ptr<SoundData> SoundHeader::LoadData(OpenedFile& SoundFile)
183 {
184 	io::stream_buffer<opened_file_device> sb(SoundFile);
185 	BIStreamBE s(&sb);
186 
187 	return LoadData(s);
188 }
189 
Load(LoadedResource & rsrc)190 bool SoundHeader::Load(LoadedResource& rsrc)
191 {
192 	io::stream_buffer<io::array_source> sb(reinterpret_cast<char*>(rsrc.GetPointer()), rsrc.GetLength());
193 	BIStreamBE s(&sb);
194 
195 	// Get resource format
196 	uint16 format;
197 	s >> format;
198 	if (format != 1 && format != 2)
199 	{
200 		logWarning("Unknown sound resource format %d", format);
201 		return false;
202 	}
203 
204 	// Skip sound data types or reference count
205 	if (format == 1)
206 	{
207 		uint16 num_data_formats;
208 		s >> num_data_formats;
209 		s.ignore(num_data_formats * 6);
210 	}
211 	else if (format == 2)
212 	{
213 		s.ignore(2);
214 	}
215 
216 	// Scan sound commands for bufferCmd
217 	uint16 num_cmds;
218 	s >> num_cmds;
219 	for (int i = 0; i < num_cmds; ++i)
220 	{
221 		uint16 cmd, param1;
222 		uint32 param2;
223 
224 		s >> cmd
225 		  >> param1
226 		  >> param2;
227 
228 		if (cmd == bufferCmd)
229 		{
230 			s.rdbuf()->pubseekpos(param2);
231 			if (Load(s))
232 			{
233 				data_offset += param2;
234 				return true;
235 			}
236 		}
237 	}
238 
239 	return false;
240 }
241 
LoadData(LoadedResource & rsrc)242 boost::shared_ptr<SoundData> SoundHeader::LoadData(LoadedResource& rsrc)
243 {
244 	io::stream_buffer<io::array_source> sb(reinterpret_cast<char*>(rsrc.GetPointer()), rsrc.GetLength());
245 	BIStreamBE s(&sb);
246 
247 	return LoadData(s);
248 }
249 
SoundDefinition()250 SoundDefinition::SoundDefinition() :
251 	sound_code(0),
252 	behavior_index(1),
253 	flags(0),
254 	chance(0),
255 	low_pitch(0),
256 	high_pitch(0),
257 	permutations(1),
258 	permutations_played(0),
259 	group_offset(0), single_length(0), total_length(0),
260 	last_played(0)
261 {
262 }
263 
Unpack(OpenedFile & SoundFile)264 bool SoundDefinition::Unpack(OpenedFile &SoundFile)
265 {
266 	if (!SoundFile.IsOpen()) return false;
267 
268 	vector<uint8> headerBuffer(HeaderSize());
269 	if (!SoundFile.Read(headerBuffer.size(), &headerBuffer[0]))
270 		return false;
271 
272 	AIStreamBE header(&headerBuffer[0], headerBuffer.size());
273 
274 	header >> sound_code;
275 
276 	header >> behavior_index;
277 	header >> flags;
278 
279 	header >> chance;
280 
281 	header >> low_pitch;
282 	header >> high_pitch;
283 
284 	header >> permutations;
285 	header >> permutations_played;
286 	header >> group_offset;
287 	header >> single_length;
288 	header >> total_length;
289 
290 	sound_offsets.resize(MAXIMUM_PERMUTATIONS_PER_SOUND);
291 	for (int i = 0; i < sound_offsets.size(); i++)
292 	{
293 		header >> sound_offsets[i];
294 	}
295 
296 	header >> last_played;
297 
298 	header.ignore(4 * 2);
299 
300 	return true;
301 }
302 
Load(OpenedFile & SoundFile,bool LoadPermutations)303 bool SoundDefinition::Load(OpenedFile &SoundFile, bool LoadPermutations)
304 {
305 	if (!SoundFile.IsOpen()) return false;
306 
307 	if (LoadPermutations)
308 		sounds.resize(permutations);
309 	else
310 		sounds.resize(std::min(permutations, static_cast<int16>(1)));
311 
312 	for (int i = 0; i < sounds.size(); i++)
313 	{
314 		if (!SoundFile.SetPosition(group_offset + sound_offsets[i])
315 		    || !sounds[i].Load(SoundFile))
316 		{
317 			sounds.clear();
318 			return false;
319 		}
320 	}
321 
322     return true;
323 }
324 
325 
LoadData(OpenedFile & SoundFile,short permutation)326 boost::shared_ptr<SoundData> SoundDefinition::LoadData(OpenedFile& SoundFile, short permutation)
327 {
328 	boost::shared_ptr<SoundData> p;
329 	if (!SoundFile.IsOpen())
330 	{
331 		return p;
332 	}
333 
334 	if (!SoundFile.SetPosition(group_offset + sound_offsets[permutation]))
335 	{
336 		return p;
337 	}
338 
339 	return sounds[permutation].LoadData(SoundFile);
340 }
341 
Open(FileSpecifier & SoundFileSpec)342 bool M2SoundFile::Open(FileSpecifier& SoundFileSpec)
343 {
344 	Close();
345 
346 	std::unique_ptr<OpenedFile> sound_file(new OpenedFile);
347 
348 	if (!SoundFileSpec.Open(*sound_file, false)) return false;
349 
350 	std::vector<uint8> headerBuffer;
351 	headerBuffer.resize(HeaderSize());
352 
353 	if (!sound_file->Read(headerBuffer.size(), &headerBuffer[0]))
354 		return false;
355 
356 	AIStreamBE header(&headerBuffer[0], headerBuffer.size());
357 	header >> version;
358 	header >> tag;
359 	header >> source_count;
360 	header >> sound_count;
361 	header.ignore(v1Unused * 2);
362 
363 	if ((version != 0 && version != 1) ||
364 	    tag != FOUR_CHARS_TO_INT('s','n','d','2') ||
365 	    sound_count < 0 ||
366 	    source_count < 0)
367 	{
368 		return false;
369 	}
370 
371 	if (sound_count == 0)
372 	{
373 		sound_count = source_count;
374 		source_count = 1;
375 	}
376 
377 	// load the definitions
378 	sound_definitions.resize(source_count);
379 	for (int source = 0; source < source_count; source++)
380 	{
381 		sound_definitions[source].resize(sound_count);
382 		for (int i = 0; i < sound_count; i++)
383 		{
384 			if (!sound_definitions[source][i].Unpack(*sound_file))
385 			{
386 				Close();
387 				return false;
388 			}
389 		}
390 	}
391 
392 	// load all the headers
393 	for (int source = 0; source < source_count; ++source)
394 	{
395 		for (int i = 0; i < sound_count; ++i)
396 		{
397 			sound_definitions[source][i].Load(*sound_file, true);
398 		}
399 	}
400 
401 	// keep the sound file opened
402 	opened_sound_file = std::move(sound_file);
403 
404 	return true;
405 }
406 
Close()407 void M2SoundFile::Close()
408 {
409 	sound_definitions.clear();
410 }
411 
GetSoundDefinition(int source,int sound_index)412 SoundDefinition* M2SoundFile::GetSoundDefinition(int source, int sound_index)
413 {
414 	if (source < sound_definitions.size() && sound_index < sound_definitions[source].size())
415 		return &sound_definitions[source][sound_index];
416 	else
417 		return 0;
418 }
419 
GetSoundData(SoundDefinition * definition,int permutation)420 boost::shared_ptr<SoundData> M2SoundFile::GetSoundData(SoundDefinition* definition, int permutation)
421 {
422 	return definition->LoadData(*opened_sound_file, permutation);
423 }
424 
Open(FileSpecifier & SoundFile)425 bool M1SoundFile::Open(FileSpecifier& SoundFile)
426 {
427 	Close();
428 	return SoundFile.Open(resource_file);
429 }
430 
Close()431 void M1SoundFile::Close()
432 {
433 	headers.clear();
434 	definitions.clear();
435 	cached_sound_code = -1;
436 	cached_rsrc.Unload();
437 	resource_file.Close();
438 }
439 
GetSoundDefinition(int,int sound_index)440 SoundDefinition* M1SoundFile::GetSoundDefinition(int, int sound_index)
441 {
442 	if (resource_file.Check('s', 'n', 'd', ' ', sound_index))
443 	{
444 		std::map<int16, SoundDefinition>::iterator it = definitions.find(sound_index);
445 		if (it == definitions.end())
446 		{
447 			SoundDefinition definition;
448 			definition.behavior_index = 2; // sound_is_loud
449 			definition.sound_code = sound_index;
450 			// look for permutations
451 			definition.permutations = 1;
452 			while (resource_file.Check('s', 'n', 'd', ' ', sound_index + definition.permutations) && definition.permutations < MAXIMUM_PERMUTATIONS_PER_SOUND)
453 			{
454 				++definition.permutations;
455 			}
456 
457 			it = definitions.insert(std::pair<int16, SoundDefinition>(sound_index, definition)).first;
458 		}
459 		return &(it->second);
460 	}
461 	else
462 	{
463 		return 0;
464 	}
465 }
466 
467 const int M1SoundFile::MAXIMUM_PERMUTATIONS_PER_SOUND = 5;
468 
GetSoundHeader(SoundDefinition * definition,int permutation)469 SoundHeader M1SoundFile::GetSoundHeader(SoundDefinition* definition, int permutation)
470 {
471 	int sound_index = definition->sound_code + std::min(permutation, MAXIMUM_PERMUTATIONS_PER_SOUND);
472 
473 	SoundHeader header;
474 	std::map<int16, SoundHeader>::iterator it = headers.find(sound_index);
475 	if (it == headers.end())
476 	{
477 		if (cached_sound_code != sound_index)
478 		{
479 			resource_file.Get('s', 'n', 'd', ' ', sound_index, cached_rsrc);
480 			cached_sound_code = sound_index;
481 		}
482 
483 		SoundHeader header;
484 		header.Load(cached_rsrc);
485 		it = headers.insert(std::pair<int16, SoundHeader>(sound_index, header)).first;
486 	}
487 
488 	return it->second;
489 }
490 
GetSoundData(SoundDefinition * definition,int permutation)491 boost::shared_ptr<SoundData> M1SoundFile::GetSoundData(SoundDefinition* definition, int permutation)
492 {
493 	int sound_index = definition->sound_code + std::min(permutation, MAXIMUM_PERMUTATIONS_PER_SOUND);
494 
495 	if (cached_sound_code != sound_index)
496 	{
497 		resource_file.Get('s', 'n', 'd', ' ', sound_index, cached_rsrc);
498 		cached_sound_code = sound_index;
499 	}
500 
501 	return GetSoundHeader(definition, permutation).LoadData(cached_rsrc);
502 }
503