1 //----------------------------------------------------------------------------
2 //  EDGE Data Definition File Code (Sounds)
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2008  The EDGE Team.
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 //----------------------------------------------------------------------------
18 //
19 // -KM- 1998/09/27 Finished :-)
20 //
21 
22 #include "local.h"
23 
24 #include "sfx.h"
25 
26 static sfxdef_c *dynamic_sfx;
27 
28 sfxdef_container_c sfxdefs;
29 
30 #define DDF_CMD_BASE  dummy_sfx
31 static sfxdef_c dummy_sfx;
32 
33 static const commandlist_t sfx_commands[] =
34 {
35 	DDF_FIELD("LUMP_NAME", lump_name, DDF_MainGetLumpName),
36 	DDF_FIELD("FILE_NAME", file_name, DDF_MainGetString),
37 	DDF_FIELD("SINGULAR",  singularity, DDF_MainGetNumeric),
38 	DDF_FIELD("PRIORITY",  priority, DDF_MainGetNumeric),
39 	DDF_FIELD("VOLUME",    volume,   DDF_MainGetPercent),
40 	DDF_FIELD("LOOP",      looping,  DDF_MainGetBoolean),
41 	DDF_FIELD("PRECIOUS",  precious, DDF_MainGetBoolean),
42 	DDF_FIELD("MAX_DISTANCE", max_distance, DDF_MainGetFloat),
43 
44 	DDF_CMD_END
45 };
46 
47 
48 //
49 //  DDF PARSE ROUTINES
50 //
51 
SoundStartEntry(const char * name,bool extend)52 static void SoundStartEntry(const char *name, bool extend)
53 {
54 	if (!name || !name[0])
55 	{
56 		DDF_WarnError("New sound entry is missing a name!");
57 		name = "SOUND_WITH_NO_NAME";
58 	}
59 
60 	dynamic_sfx = sfxdefs.Lookup(name);
61 
62 	if (extend)
63 	{
64 		if (! dynamic_sfx)
65 			DDF_Error("Unknown sound to extend: %s\n", name);
66 		return;
67 	}
68 
69 	// replaces an existing entry?
70 	if (dynamic_sfx)
71 	{
72 		// maintain the internal ID
73 		int id = dynamic_sfx->normal.sounds[0];
74 
75 		dynamic_sfx->Default();
76 
77 		dynamic_sfx->normal.num = 1;
78 		dynamic_sfx->normal.sounds[0] = id;
79 		return;
80 	}
81 
82 	// not found, create a new one
83 	dynamic_sfx = new sfxdef_c;
84 
85 	dynamic_sfx->name = name;
86 
87 	sfxdefs.Insert(dynamic_sfx);
88 
89 	// give it a self-referencing ID number
90 	dynamic_sfx->normal.sounds[0] = sfxdefs.GetSize()-1;
91 	dynamic_sfx->normal.num = 1;
92 }
93 
94 
SoundParseField(const char * field,const char * contents,int index,bool is_last)95 static void SoundParseField(const char *field, const char *contents,
96     int index, bool is_last)
97 {
98 #if (DEBUG_DDF)
99 	I_Debugf("SOUND_PARSE: %s = %s;\n", field, contents);
100 #endif
101 
102 	// -AJA- ignore these for backwards compatibility
103 	if (DDF_CompareName(field, "BITS") == 0 ||
104 	    DDF_CompareName(field, "STEREO") == 0)
105 		return;
106 
107 	if (DDF_MainParseField(sfx_commands, field, contents, (byte *)dynamic_sfx))
108 		return;  // OK
109 
110 	DDF_WarnError("Unknown sounds.ddf command: %s\n", field);
111 }
112 
113 
SoundFinishEntry(void)114 static void SoundFinishEntry(void)
115 {
116 	if (!dynamic_sfx->lump_name[0] &&
117 		!(dynamic_sfx->file_name && dynamic_sfx->file_name[0]))
118 	{
119 		DDF_Error("Missing LUMP_NAME or FILE_NAME for sound.\n");
120 	}
121 }
122 
123 
SoundClearAll(void)124 static void SoundClearAll(void)
125 {
126 	I_Warning("Ignoring #CLEARALL in sounds.ddf\n");
127 }
128 
129 
DDF_ReadSFX(void * data,int size)130 bool DDF_ReadSFX(void *data, int size)
131 {
132 	readinfo_t sfx_r;
133 
134 	sfx_r.memfile = (char*)data;
135 	sfx_r.memsize = size;
136 	sfx_r.tag = "SOUNDS";
137 	sfx_r.entries_per_dot = 8;
138 
139 	if (sfx_r.memfile)
140 	{
141 		sfx_r.message = NULL;
142 		sfx_r.filename = NULL;
143 		sfx_r.lumpname = "DDFSFX";
144 	}
145 	else
146 	{
147 		sfx_r.message = "DDF_InitSounds";
148 		sfx_r.filename = "sounds.ddf";
149 		sfx_r.lumpname = NULL;
150 	}
151 
152 	sfx_r.start_entry  = SoundStartEntry;
153 	sfx_r.parse_field  = SoundParseField;
154 	sfx_r.finish_entry = SoundFinishEntry;
155 	sfx_r.clear_all    = SoundClearAll;
156 
157 	return DDF_MainReadFile(&sfx_r);
158 }
159 
DDF_SFXInit(void)160 void DDF_SFXInit(void)
161 {
162 	sfxdefs.Clear();
163 }
164 
DDF_SFXCleanUp(void)165 void DDF_SFXCleanUp(void)
166 {
167 	sfxdefs.Trim();
168 }
169 
170 //
171 // DDF_MainLookupSound
172 //
173 // Lookup the sound specified.
174 //
175 // -ACB- 1998/07/08 Checked the S_sfx table for sfx names.
176 // -ACB- 1998/07/18 Removed to the need set *currentcmdlist[commandref].data to -1
177 // -KM- 1998/09/27 Fixed this func because of sounds.ddf
178 // -KM- 1998/10/29 sfx_t finished
179 //
DDF_MainLookupSound(const char * info,void * storage)180 void DDF_MainLookupSound(const char *info, void *storage)
181 {
182 	sfx_t **dest = (sfx_t **)storage;
183 
184 	SYS_ASSERT(info && storage);
185 
186 	*dest = sfxdefs.GetEffect(info);
187 }
188 
189 // --> Sound Effect Definition Class
190 
191 //
192 // sfxdef_c Constructor
193 //
sfxdef_c()194 sfxdef_c::sfxdef_c() : name()
195 {
196 	Default();
197 }
198 
199 //
200 // sfxdef_c Destructor
201 //
~sfxdef_c()202 sfxdef_c::~sfxdef_c()
203 {
204 }
205 
206 //
207 // sfxdef_c::CopyDetail()
208 //
CopyDetail(sfxdef_c & src)209 void sfxdef_c::CopyDetail(sfxdef_c &src)
210 {
211 	lump_name = src.lump_name;
212 	file_name = src.file_name;
213 
214 	// clear the internal sfx_t (ID would be wrong)
215 	normal.sounds[0] = 0;
216 	normal.num = 0;
217 
218 	singularity = src.singularity;      // singularity
219 	priority = src.priority;    		// priority (lower is more important)
220 	volume = src.volume; 				// volume
221 	looping = src.looping;  			// looping
222 	precious = src.precious;  			// precious
223 	max_distance = src.max_distance; 	// max_distance
224 }
225 
226 //
227 // sfxdef_c::Default()
228 //
Default()229 void sfxdef_c::Default()
230 {
231 	lump_name.clear();
232 	file_name.clear();
233 
234 	normal.sounds[0] = 0;
235 	normal.num = 0;
236 
237 	singularity = 0;      			// singularity
238 	priority = 999;    				// priority (lower is more important)
239 	volume = PERCENT_MAKE(100); 	// volume
240 	looping = false;  				// looping
241 	precious = false;  				// precious
242 	max_distance = S_CLIPPING_DIST; // max_distance
243 }
244 
245 
246 // --> Sound Effect Definition Containter Class
247 
248 //
249 // sfxdef_container_c::CleanupObject()
250 //
CleanupObject(void * obj)251 void sfxdef_container_c::CleanupObject(void *obj)
252 {
253 	sfxdef_c *s = *(sfxdef_c**)obj;
254 
255 	if (s)
256 		delete s;
257 
258 	return;
259 }
260 
strncasecmpwild(const char * s1,const char * s2,int n)261 static int strncasecmpwild(const char *s1, const char *s2, int n)
262 {
263 	int i = 0;
264 
265 	for (i = 0; s1[i] && s2[i] && i < n; i++)
266 	{
267 		if ((toupper(s1[i]) != toupper(s2[i])) && (s1[i] != '?') && (s2[i] != '?'))
268 			break;
269 	}
270 	// -KM- 1999/01/29 If strings are equal return equal.
271 	if (i == n)
272 		return 0;
273 
274 	if (s1[i] == '?' || s2[i] == '?')
275 		return 0;
276 
277 	return s1[i] - s2[i];
278 }
279 
280 
281 //
282 // sfxdef_container_c::GetEffect()
283 //
284 // FIXME!! Remove error param hack
285 // FIXME!! Cache results for those we create
286 //
GetEffect(const char * name,bool error)287 sfx_t* sfxdef_container_c::GetEffect(const char *name, bool error)
288 {
289 	epi::array_iterator_c it, last;
290 	int count;
291 
292 	sfxdef_c *si;
293 	sfx_t *r;
294 
295 	// NULL Sound
296 	if (!name || !name[0] || DDF_CompareName(name, "NULL")==0)
297 		return NULL;
298 
299 	// count them
300 	for (count=0, it=GetTailIterator();
301 		it.IsValid() && it.GetPos() >= 0;
302 		it--)
303 	{
304 		si = ITERATOR_TO_TYPE(it, sfxdef_c*);
305 
306 		if (strncasecmpwild(name, si->name.c_str(), 8) == 0)
307 		{
308 			count++;
309 			last = it;
310 		}
311 	}
312 
313 	if (count == 0)
314 	{
315 		if (error)
316 			DDF_WarnError("Unknown SFX: '%.8s'\n", name);
317 
318 		return NULL;
319 	}
320 
321 	// -AJA- optimisation to save some memory
322 	if (count == 1)
323 	{
324 		si = ITERATOR_TO_TYPE(last, sfxdef_c*);
325 		r = &si->normal;
326 
327 		SYS_ASSERT(r->num == 1);
328 
329 		return r;
330 	}
331 
332 	//
333 	// allocate elements.  Uses (count-1) since sfx_t already includes
334 	// the first integer.
335 	//
336 	r = (sfx_t*) new byte[sizeof(sfx_t) + ((count-1) * sizeof(int))];
337 
338 	// now store them
339 	for (r->num=0, it=GetTailIterator();
340 		it.IsValid() && it.GetPos() >= 0;
341 		it--)
342 	{
343 		si = ITERATOR_TO_TYPE(it, sfxdef_c*);
344 
345 		if (strncasecmpwild(name, si->name.c_str(), 8) == 0)
346 			r->sounds[r->num++] = it.GetPos();
347 	}
348 
349 	SYS_ASSERT(r->num == count);
350 
351 	return r;
352 }
353 
354 //
355 // sfxdef_container_c::Lookup()
356 //
Lookup(const char * name)357 sfxdef_c* sfxdef_container_c::Lookup(const char *name)
358 {
359 	epi::array_iterator_c it;
360 
361 	for (it=GetIterator(0); it.IsValid(); it++)
362 	{
363 		sfxdef_c *s = ITERATOR_TO_TYPE(it, sfxdef_c*);
364 
365 		if (DDF_CompareName(s->name.c_str(), name) == 0)
366 			return s;
367 	}
368 
369 	return NULL;
370 }
371 
372 //--- editor settings ---
373 // vi:ts=4:sw=4:noexpandtab
374