1 //------------------------------------------------------------------------
2 //  SOUND Definitions
3 //------------------------------------------------------------------------
4 //
5 //  DEH_EDGE  Copyright (C) 2004-2005  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 (in COPYING.txt) for more details.
16 //
17 //------------------------------------------------------------------------
18 //
19 //  DEH_EDGE is based on:
20 //
21 //  +  DeHackEd source code, by Greg Lewis.
22 //  -  DOOM source code (C) 1993-1996 id Software, Inc.
23 //  -  Linux DOOM Hack Editor, by Sam Lantinga.
24 //  -  PrBoom's DEH/BEX code, by Ty Halderman, TeamTNT.
25 //
26 //------------------------------------------------------------------------
27 
28 #include "i_defs.h"
29 #include "sounds.h"
30 
31 #include "buffer.h"
32 #include "dh_plugin.h"
33 #include "patch.h"
34 #include "storage.h"
35 #include "system.h"
36 #include "util.h"
37 #include "wad.h"
38 
39 #include <assert.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 
44 namespace Deh_Edge
45 {
46 
47 // workaround for EDGE 1.27 bug with sound replacements
48 #define EDGE127_BUG  (target_version <= 127)
49 
50 
51 //
52 // Information about all the music
53 //
54 
55 musicinfo_t S_music[NUMMUSIC] =
56 {
57     { NULL, -1, NULL },  // dummy entry
58 
59     { "e1m1", 33, NULL },
60     { "e1m2", 34, NULL },
61     { "e1m3", 35, NULL },
62     { "e1m4", 36, NULL },
63     { "e1m5", 37, NULL },
64     { "e1m6", 38, NULL },
65     { "e1m7", 39, NULL },
66     { "e1m8", 40, NULL },
67     { "e1m9", 41, NULL },
68     { "e2m1", 42, NULL },
69     { "e2m2", 43, NULL },
70     { "e2m3", 44, NULL },
71     { "e2m4", 45, NULL },
72     { "e2m5", 46, NULL },
73     { "e2m6", 47, NULL },
74     { "e2m7", 48, NULL },
75     { "e2m8", 49, NULL },
76     { "e2m9", 50, NULL },
77     { "e3m1", 51, NULL },
78     { "e3m2", 52, NULL },
79     { "e3m3", 53, NULL },
80     { "e3m4", 54, NULL },
81     { "e3m5", 55, NULL },
82     { "e3m6", 56, NULL },
83     { "e3m7", 57, NULL },
84     { "e3m8", 58, NULL },
85     { "e3m9", 59, NULL },
86 
87     { "inter",  63, NULL },
88     { "intro",  62, NULL },
89     { "bunny",  67, NULL },
90     { "victor", 61, NULL },
91     { "introa", 68, NULL },
92     { "runnin",  1, NULL },
93     { "stalks",  2, NULL },
94     { "countd",  3, NULL },
95     { "betwee",  4, NULL },
96     { "doom",    5, NULL },
97     { "the_da",  6, NULL },
98     { "shawn",   7, NULL },
99     { "ddtblu",  8, NULL },
100     { "in_cit",  9, NULL },
101     { "dead",   10, NULL },
102     { "stlks2", 11, NULL },
103     { "theda2", 12, NULL },
104     { "doom2",  13, NULL },
105     { "ddtbl2", 14, NULL },
106     { "runni2", 15, NULL },
107     { "dead2",  16, NULL },
108     { "stlks3", 17, NULL },
109     { "romero", 18, NULL },
110     { "shawn2", 19, NULL },
111     { "messag", 20, NULL },
112     { "count2", 21, NULL },
113     { "ddtbl3", 22, NULL },
114     { "ampie",  23, NULL },
115     { "theda3", 24, NULL },
116     { "adrian", 25, NULL },
117     { "messg2", 26, NULL },
118     { "romer2", 27, NULL },
119     { "tense",  28, NULL },
120     { "shawn3", 29, NULL },
121     { "openin", 30, NULL },
122     { "evil",   31, NULL },
123     { "ultima", 32, NULL },
124     { "read_m", 60, NULL },
125     { "dm2ttl", 65, NULL },
126     { "dm2int", 64, NULL }
127 };
128 
129 
130 //------------------------------------------------------------------------
131 //
132 // Information about all the sfx
133 //
134 
135 sfxinfo_t S_sfx[NUMSFX_BEX] =
136 {
137 	// S_sfx[0] needs to be a dummy for odd reasons.
138 	{ "none", 0,  0, 0, -1, -1, NULL },
139 
140 	{ "pistol", 0,  64, 0, -1, -1, NULL },
141 	{ "shotgn", 0,  64, 0, -1, -1, NULL },
142 	{ "sgcock", 0,  64, 0, -1, -1, NULL },
143 	{ "dshtgn", 0,  64, 0, -1, -1, NULL },
144 	{ "dbopn",  0,  64, 0, -1, -1, NULL },
145 	{ "dbcls",  0,  64, 0, -1, -1, NULL },
146 	{ "dbload", 0,  64, 0, -1, -1, NULL },
147 	{ "plasma", 0,  64, 0, -1, -1, NULL },
148 	{ "bfg",    0,  64, 0, -1, -1, NULL },
149 	{ "sawup",  2,  64, 0, -1, -1, NULL },
150 	{ "sawidl", 2, 118, 0, -1, -1, NULL },
151 	{ "sawful", 2,  64, 0, -1, -1, NULL },
152 	{ "sawhit", 2,  64, 0, -1, -1, NULL },
153 	{ "rlaunc", 0,  64, 0, -1, -1, NULL },
154 	{ "rxplod", 0,  70, 0, -1, -1, NULL },
155 	{ "firsht", 0,  70, 0, -1, -1, NULL },
156 	{ "firxpl", 0,  70, 0, -1, -1, NULL },
157 	{ "pstart", 18, 100, 0, -1, -1, NULL },
158 	{ "pstop",  18, 100, 0, -1, -1, NULL },
159 	{ "doropn", 0,  100, 0, -1, -1, NULL },
160 	{ "dorcls", 0,  100, 0, -1, -1, NULL },
161 	{ "stnmov", 18, 119, 0, -1, -1, NULL },
162 	{ "swtchn", 0,  78, 0, -1, -1, NULL },
163 	{ "swtchx", 0,  78, 0, -1, -1, NULL },
164 	{ "plpain", 0,  96, 0, -1, -1, NULL },
165 	{ "dmpain", 0,  96, 0, -1, -1, NULL },
166 	{ "popain", 0,  96, 0, -1, -1, NULL },
167 	{ "vipain", 0,  96, 0, -1, -1, NULL },
168 	{ "mnpain", 0,  96, 0, -1, -1, NULL },
169 	{ "pepain", 0,  96, 0, -1, -1, NULL },
170 	{ "slop",   0,  78, 0, -1, -1, NULL },
171 	{ "itemup", 20, 78, 0, -1, -1, NULL },
172 	{ "wpnup",  21, 78, 0, -1, -1, NULL },
173 	{ "oof",    0,  96, 0, -1, -1, NULL },
174 	{ "telept", 0,  32, 0, -1, -1, NULL },
175 	{ "posit1", 3,  98, 0, -1, -1, NULL },
176 	{ "posit2", 3,  98, 0, -1, -1, NULL },
177 	{ "posit3", 3,  98, 0, -1, -1, NULL },
178 	{ "bgsit1", 4,  98, 0, -1, -1, NULL },
179 	{ "bgsit2", 4,  98, 0, -1, -1, NULL },
180 	{ "sgtsit", 5,  98, 0, -1, -1, NULL },
181 	{ "cacsit", 6,  98, 0, -1, -1, NULL },
182 	{ "brssit", 7,  94, 0, -1, -1, NULL },
183 	{ "cybsit", 8,  92, 0, -1, -1, NULL },
184 	{ "spisit", 9,  90, 0, -1, -1, NULL },
185 	{ "bspsit", 10, 90, 0, -1, -1, NULL },
186 	{ "kntsit", 11, 90, 0, -1, -1, NULL },
187 	{ "vilsit", 12, 90, 0, -1, -1, NULL },
188 	{ "mansit", 13, 90, 0, -1, -1, NULL },
189 	{ "pesit",  14, 90, 0, -1, -1, NULL },
190 	{ "sklatk", 0,  70, 0, -1, -1, NULL },
191 	{ "sgtatk", 0,  70, 0, -1, -1, NULL },
192 	{ "skepch", 0,  70, 0, -1, -1, NULL },
193 	{ "vilatk", 0,  70, 0, -1, -1, NULL },
194 	{ "claw",   0,  70, 0, -1, -1, NULL },
195 	{ "skeswg", 0,  70, 0, -1, -1, NULL },
196 	{ "pldeth", 0,  32, 0, -1, -1, NULL },
197 	{ "pdiehi", 0,  32, 0, -1, -1, NULL },
198 	{ "podth1", 0,  70, 0, -1, -1, NULL },
199 	{ "podth2", 0,  70, 0, -1, -1, NULL },
200 	{ "podth3", 0,  70, 0, -1, -1, NULL },
201 	{ "bgdth1", 0,  70, 0, -1, -1, NULL },
202 	{ "bgdth2", 0,  70, 0, -1, -1, NULL },
203 	{ "sgtdth", 0,  70, 0, -1, -1, NULL },
204 	{ "cacdth", 0,  70, 0, -1, -1, NULL },
205 	{ "skldth", 0,  70, 0, -1, -1, NULL },
206 	{ "brsdth", 0,  32, 0, -1, -1, NULL },
207 	{ "cybdth", 0,  32, 0, -1, -1, NULL },
208 	{ "spidth", 0,  32, 0, -1, -1, NULL },
209 	{ "bspdth", 0,  32, 0, -1, -1, NULL },
210 	{ "vildth", 0,  32, 0, -1, -1, NULL },
211 	{ "kntdth", 0,  32, 0, -1, -1, NULL },
212 	{ "pedth",  0,  32, 0, -1, -1, NULL },
213 	{ "skedth", 0,  32, 0, -1, -1, NULL },
214 	{ "posact", 3,  120, 0, -1, -1, NULL },
215 	{ "bgact",  4,  120, 0, -1, -1, NULL },
216 	{ "dmact",  15, 120, 0, -1, -1, NULL },
217 	{ "bspact", 10, 100, 0, -1, -1, NULL },
218 	{ "bspwlk", 16, 100, 0, -1, -1, NULL },
219 	{ "vilact", 12, 100, 0, -1, -1, NULL },
220 	{ "noway",  0,  78, 0, -1, -1, NULL },
221 	{ "barexp", 0,  60, 0, -1, -1, NULL },
222 	{ "punch",  0,  64, 0, -1, -1, NULL },
223 	{ "hoof",   0,  70, 0, -1, -1, NULL },
224 	{ "metal",  0,  70, 0, -1, -1, NULL },
225 	{ "chgun",  0,  64, sfx_pistol, 150, 0, NULL },
226 	{ "tink",   0,  60, 0, -1, -1, NULL },
227 	{ "bdopn",  0, 100, 0, -1, -1, NULL },
228 	{ "bdcls",  0, 100, 0, -1, -1, NULL },
229 	{ "itmbk",  0, 100, 0, -1, -1, NULL },
230 	{ "flame",  0,  32, 0, -1, -1, NULL },
231 	{ "flamst", 0,  32, 0, -1, -1, NULL },
232 	{ "getpow", 0,  60, 0, -1, -1, NULL },
233 	{ "bospit", 0,  70, 0, -1, -1, NULL },
234 	{ "boscub", 0,  70, 0, -1, -1, NULL },
235 	{ "bossit", 0,  70, 0, -1, -1, NULL },
236 	{ "bospn",  0,  70, 0, -1, -1, NULL },
237 	{ "bosdth", 0,  70, 0, -1, -1, NULL },
238 	{ "manatk", 0,  70, 0, -1, -1, NULL },
239 	{ "mandth", 0,  70, 0, -1, -1, NULL },
240 	{ "sssit",  0,  70, 0, -1, -1, NULL },
241 	{ "ssdth",  0,  70, 0, -1, -1, NULL },
242 	{ "keenpn", 0,  70, 0, -1, -1, NULL },
243 	{ "keendt", 0,  70, 0, -1, -1, NULL },
244 	{ "skeact", 0,  70, 0, -1, -1, NULL },
245 	{ "skesit", 0,  70, 0, -1, -1, NULL },
246 	{ "skeatk", 0,  70, 0, -1, -1, NULL },
247 	{ "radio",  0,  60, 0, -1, -1, NULL },
248 
249 	// BOOM and MBF sounds...
250 	{ "dgsit",  0,  98, 0, -1, -1, NULL },
251 	{ "dgatk",  0,  70, 0, -1, -1, NULL },
252 	{ "dgact",  0, 120, 0, -1, -1, NULL },
253 	{ "dgdth",  0,  70, 0, -1, -1, NULL },
254 	{ "dgpain", 0,  96, 0, -1, -1, NULL }
255 };
256 
257 
258 //------------------------------------------------------------------------
259 
Startup(void)260 void Sounds::Startup(void)
261 {
262 	for (int s = 0; s < NUMSFX_BEX; s++)
263 	{
264 		free(S_sfx[s].new_name);
265 		S_sfx[s].new_name = NULL;
266 	}
267 
268 	for (int m = 0; m < NUMMUSIC; m++)
269 	{
270 		free(S_music[m].new_name);
271 		S_music[m].new_name = NULL;
272 	}
273 }
274 
275 namespace Sounds
276 {
277 	bool some_sound_modified = false;
278 	bool got_one;
279 
280 
MarkSound(int s_num)281 	void MarkSound(int s_num)
282 	{
283 		// can happen since the binary patches contain the dummy sound
284 		if (s_num == sfx_None)
285 			return;
286 
287 		assert(1 <= s_num && s_num < NUMSFX_BEX);
288 
289 		some_sound_modified = true;
290 	}
291 
AlterSound(int new_val)292 	void AlterSound(int new_val)
293 	{
294 		int s_num = Patch::active_obj;
295 		const char *deh_field = Patch::line_buf;
296 
297 		assert(0 <= s_num && s_num < NUMSFX_BEX);
298 
299 		if (StrCaseCmpPartial(deh_field, "Zero") == 0 ||
300 		    StrCaseCmpPartial(deh_field, "Neg. One") == 0)
301 			return;
302 
303 		if (StrCaseCmp(deh_field, "Offset") == 0)
304 		{
305 			PrintWarn("Line %d: raw sound Offset not supported.\n", Patch::line_num);
306 			return;
307 		}
308 
309 		if (StrCaseCmp(deh_field, "Value") == 0)  // priority
310 		{
311 			if (new_val < 0)
312 			{
313 				PrintWarn("Line %d: bad sound priority value: %d.\n",
314 					Patch::line_num, new_val);
315 				new_val = 0;
316 			}
317 
318 			Storage::RememberMod(&S_sfx[s_num].priority, new_val);
319 
320 			MarkSound(s_num);
321 			return;
322 		}
323 
324 		if (StrCaseCmp(deh_field, "Zero/One") == 0)  // singularity, ignored
325 			return;
326 
327 		PrintWarn("UNKNOWN SOUND FIELD: %s\n", deh_field);
328 	}
329 
GetEdgeSfxName(int sound_id)330 	const char *GetEdgeSfxName(int sound_id)
331 	{
332 		assert(sound_id != sfx_None);
333 
334 		switch (sound_id)
335 		{
336 			// EDGE uses different names for the DOG sounds
337 			case sfx_dgsit:  return "DOG_SIGHT";
338 			case sfx_dgatk:  return "DOG_BITE";
339 			case sfx_dgact:  return "DOG_LOOK";
340 			case sfx_dgdth:  return "DOG_DIE";
341 			case sfx_dgpain: return "DOG_PAIN";
342 
343 			default: break;
344 		}
345 
346 		return S_sfx[sound_id].orig_name;
347 	}
348 
GetSound(int sound_id)349 	const char *GetSound(int sound_id)
350 	{
351 		assert(sound_id != sfx_None);
352 		assert(strlen(S_sfx[sound_id].orig_name) < 16);
353 
354 		// handle random sounds
355 		switch (sound_id)
356 		{
357 			case sfx_podth1: case sfx_podth2: case sfx_podth3:
358 				return "\"PODTH?\"";
359 
360 			case sfx_posit1: case sfx_posit2: case sfx_posit3:
361 				return "\"POSIT?\"";
362 
363 			case sfx_bgdth1: case sfx_bgdth2:
364 				return "\"BGDTH?\"";
365 
366 			case sfx_bgsit1: case sfx_bgsit2:
367 				return "\"BGSIT?\"";
368 
369 			default: break;
370 		}
371 
372 		static char name_buf[40];
373 
374 		sprintf(name_buf, "\"%s\"", StrUpper(GetEdgeSfxName(sound_id)));
375 
376 		return name_buf;
377 	}
378 
BeginSoundLump(void)379 	void BeginSoundLump(void)
380 	{
381 		WAD::NewLump("DDFSFX");
382 
383 		WAD::Printf(GEN_BY_COMMENT);
384 		WAD::Printf("<SOUNDS>\n\n");
385 
386 		if (EDGE127_BUG)
387 			WAD::Printf("#CLEARALL\n\n");
388 	}
389 
FinishSoundLump(void)390 	void FinishSoundLump(void)
391 	{
392 		if (EDGE127_BUG)
393 		{
394 			WAD::Printf(
395 				"[JPRISE] LUMP_NAME=\"DSJPRISE\"; SINGULAR=29;\n"
396 				"[JPMOVE] LUMP_NAME=\"DSJPMOVE\"; SINGULAR=29;\n"
397 				"[JPIDLE] LUMP_NAME=\"DSJPIDLE\"; SINGULAR=29;\n"
398 				"[JPDOWN] LUMP_NAME=\"DSJPDOWN\"; SINGULAR=29;\n"
399 				"[JPFLOW] LUMP_NAME=\"DSJPFLOW\"; SINGULAR=29;\n"
400 				"[CRUSH] LUMP_NAME=\"DSCRUSH\"; PRIORITY=100;\n"
401 			);
402 		}
403 
404 		WAD::Printf("\n");
405 		WAD::FinishLump();
406 	}
407 
BeginMusicLump(void)408 	void BeginMusicLump(void)
409 	{
410 		WAD::NewLump("DDFPLAY");
411 
412 		WAD::Printf(GEN_BY_COMMENT);
413 		WAD::Printf("<PLAYLISTS>\n\n");
414 	}
415 
FinishMusicLump(void)416 	void FinishMusicLump(void)
417 	{
418 		WAD::Printf("\n");
419 		WAD::FinishLump();
420 	}
421 
WriteSound(int s_num)422 	void WriteSound(int s_num)
423 	{
424 		sfxinfo_t *sound = S_sfx + s_num;
425 
426 		if (! got_one)
427 		{
428 			got_one = true;
429 			BeginSoundLump();
430 		}
431 
432 		WAD::Printf("[%s]\n", StrUpper(GetEdgeSfxName(s_num)));
433 
434 		const char *lump = sound->new_name ? sound->new_name : sound->orig_name;
435 
436 		if (sound->link)
437 		{
438 			sfxinfo_t *link = S_sfx + sound->link;
439 
440 			lump = link->new_name ? link->new_name : GetEdgeSfxName(sound->link);
441 		}
442 
443 		WAD::Printf("LUMP_NAME = \"DS%s\";\n", StrUpper(lump));
444 		WAD::Printf("PRIORITY = %d;\n", sound->priority);
445 
446 		if (sound->singularity != 0)
447 			WAD::Printf("SINGULAR = %d;\n", sound->singularity);
448 
449 		if (s_num == sfx_stnmov)
450 			WAD::Printf("LOOP = TRUE;\n");
451 
452 		WAD::Printf("\n");
453 	}
454 
WriteMusic(int m_num)455 	void WriteMusic(int m_num)
456 	{
457 		musicinfo_t *mus = S_music + m_num;
458 
459 		if (! got_one)
460 		{
461 			got_one = true;
462 			BeginMusicLump();
463 		}
464 
465 		WAD::Printf("[%02d] ", mus->ddf_num);
466 
467 		const char *lump = mus->new_name ? mus->new_name : mus->orig_name;
468 
469 		WAD::Printf("MUSICINFO = MUS:LUMP:\"D_%s\";\n", StrUpper(lump));
470 	}
471 }
472 
ConvertSFX(void)473 void Sounds::ConvertSFX(void)
474 {
475 	if (! all_mode && ! some_sound_modified)
476 		return;
477 
478 	got_one = false;
479 
480 	for (int i = 1; i < NUMSFX_BEX; i++)
481 	{
482 	    if (! EDGE127_BUG && ! all_mode && S_sfx[i].new_name == NULL)
483 			continue;
484 
485 		WriteSound(i);
486 	}
487 
488 	if (got_one)
489 		FinishSoundLump();
490 }
491 
ConvertMUS(void)492 void Sounds::ConvertMUS(void)
493 {
494 	got_one = false;
495 
496 	for (int i = 1; i < NUMMUSIC; i++)
497 	{
498 	    if (! all_mode && ! S_music[i].new_name)
499 			continue;
500 
501 		WriteMusic(i);
502 	}
503 
504 	if (got_one)
505 		FinishMusicLump();
506 }
507 
508 
509 //------------------------------------------------------------------------
510 
ReplaceSound(const char * before,const char * after)511 bool Sounds::ReplaceSound(const char *before, const char *after)
512 {
513 	for (int i = 1; i < NUMSFX_BEX; i++)
514 	{
515 		if (StrCaseCmp(S_sfx[i].orig_name, before) != 0)
516 			continue;
517 
518 		if (S_sfx[i].new_name)
519 			free(S_sfx[i].new_name);
520 
521 		S_sfx[i].new_name = StringDup(after);
522 
523 		MarkSound(i);
524 
525 		return true;
526 	}
527 
528 	return false;
529 }
530 
ReplaceMusic(const char * before,const char * after)531 bool Sounds::ReplaceMusic(const char *before, const char *after)
532 {
533 	for (int j = 1; j < NUMMUSIC; j++)
534 	{
535 		if (StrCaseCmp(S_music[j].orig_name, before) != 0)
536 			continue;
537 
538 		if (S_music[j].new_name)
539 			free(S_music[j].new_name);
540 
541 		S_music[j].new_name = StringDup(after);
542 
543 		return true;
544 	}
545 
546 	return false;
547 }
548 
AlterBexSound(const char * new_val)549 void Sounds::AlterBexSound(const char *new_val)
550 {
551 	const char *old_val = Patch::line_buf;
552 
553 	if (strlen(old_val) < 1 || strlen(old_val) > 6)
554 	{
555 		PrintWarn("Bad length for sound name '%s'.\n", old_val);
556 		return;
557 	}
558 
559 	if (strlen(new_val) < 1 || strlen(new_val) > 6)
560 	{
561 		PrintWarn("Bad length for sound name '%s'.\n", new_val);
562 		return;
563 	}
564 
565 	if (! ReplaceSound(old_val, new_val))
566 		PrintWarn("Line %d: unknown sound name '%s'.\n",
567 			Patch::line_num, old_val);
568 }
569 
AlterBexMusic(const char * new_val)570 void Sounds::AlterBexMusic(const char *new_val)
571 {
572 	const char *old_val = Patch::line_buf;
573 
574 	if (strlen(old_val) < 1 || strlen(old_val) > 6)
575 	{
576 		PrintWarn("Bad length for music name '%s'.\n", old_val);
577 		return;
578 	}
579 
580 	if (strlen(new_val) < 1 || strlen(new_val) > 6)
581 	{
582 		PrintWarn("Bad length for music name '%s'.\n", new_val);
583 		return;
584 	}
585 
586 	if (! ReplaceMusic(old_val, new_val))
587 		PrintWarn("Line %d: unknown music name '%s'.\n",
588 			Patch::line_num, old_val);
589 }
590 
591 }  // Deh_Edge
592