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