1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: dehacked.c 1564 2020-12-19 06:21:07Z wesleyjohnson $
5 //
6 // Copyright (C) 1998-2000 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: dehacked.c,v $
20 // Revision 1.17 2004/04/20 00:34:26 andyp
21 // Linux compilation fixes and string cleanups
22 //
23 // Revision 1.16 2003/11/21 17:52:05 darkwolf95
24 // added "Monsters Infight" for Dehacked patches
25 //
26 // Revision 1.15 2002/01/12 12:41:05 hurdler
27 // Revision 1.14 2002/01/12 02:21:36 stroggonmeth
28 //
29 // Revision 1.13 2001/07/16 22:35:40 bpereira
30 // - fixed crash of e3m8 in heretic
31 // - fixed crosshair not drawed bug
32 //
33 // Revision 1.12 2001/06/30 15:06:01 bpereira
34 // fixed wronf next level name in intermission
35 //
36 // Revision 1.11 2001/04/30 17:19:24 stroggonmeth
37 // HW fix and misc. changes
38 //
39 // Revision 1.10 2001/02/10 12:27:13 bpereira
40 //
41 // Revision 1.9 2001/01/25 22:15:41 bpereira
42 // added heretic support
43 //
44 // Revision 1.8 2000/11/04 16:23:42 bpereira
45 //
46 // Revision 1.7 2000/11/03 13:15:13 hurdler
47 // Some debug comments, please verify this and change what is needed!
48 //
49 // Revision 1.6 2000/11/02 17:50:06 stroggonmeth
50 // Big 3Dfloors & FraggleScript commit!!
51 //
52 // Revision 1.5 2000/08/31 14:30:55 bpereira
53 // Revision 1.4 2000/04/16 18:38:07 bpereira
54 //
55 // Revision 1.3 2000/04/05 15:47:46 stroggonmeth
56 // Added hack for Dehacked lumps. Transparent sprites are now affected by colormaps.
57 //
58 // Revision 1.2 2000/02/27 00:42:10 hurdler
59 // Revision 1.1.1.1 2000/02/22 20:32:32 hurdler
60 // Initial import into CVS (v1.29 pr3)
61 //
62 //
63 // DESCRIPTION:
64 // Load dehacked file and change table and text from the exe
65 //
66 //-----------------------------------------------------------------------------
67
68
69 #include "doomincl.h"
70
71 #include "command.h"
72 #include "console.h"
73
74 #include "g_game.h"
75 #include "p_local.h"
76 // monster_infight
77
78 #include "sounds.h"
79 #include "info.h"
80 #include "action.h"
81
82 #include "m_cheat.h"
83 #include "d_think.h"
84 #include "dstrings.h"
85 #include "m_argv.h"
86 #include "p_inter.h"
87
88 //SoM: 4/4/2000: DEHACKED LUMPS
89 #include "z_zone.h"
90 #include "w_wad.h"
91
92 #include "p_fab.h"
93 // translucent change
94
95 boolean deh_loaded = false;
96 byte flags_valid_deh = false; // flags altered flags (from DEH), boolean
97 byte pars_valid_bex = false; // have valid PAR values (from BEX), boolean
98
99 uint16_t helper_MT = 0xFFFF; // Substitute helper thing (like DOG).
100
101 static boolean bex_include_notext = 0; // bex include with skip deh text
102
103 // Save compare values, to handle multiple DEH files and lumps
104 static actionf_t deh_actions[NUMSTATES];
105 static char *deh_sprnames[NUMSPRITES];
106 static char *deh_sfxnames[NUMSFX];
107 static char *deh_musicname[NUMMUSIC];
108 static char *deh_text[NUMTEXT];
109
110
111
112 #define MAXLINELEN 200
113
114 // The code was first written for a file
115 // and converted to use memory with these functions.
116 typedef struct {
117 char *data;
118 char *curpos;
119 int size;
120 } myfile_t;
121
122 #define myfeof( a ) (a->data+a->size<=a->curpos)
123
124 // Get string upto \n, or eof.
myfgets(char * buf,int bufsize,myfile_t * f)125 char* myfgets(char *buf, int bufsize, myfile_t *f)
126 {
127 int i=0;
128
129 if( myfeof(f) )
130 return NULL;
131
132 bufsize--; // we need a extra byte for null terminated string
133 while(i<bufsize && !myfeof(f) )
134 {
135 char c = *f->curpos++;
136 if( c!='\r' )
137 buf[i++]=c;
138 if( c=='\n' )
139 break;
140 }
141 buf[i] = '\0';
142 #ifdef DEBUG_DEH
143 memset( &buf[i], 0, bufsize-i-1 );
144 #endif
145 //debug_Printf( "fgets [0]=%d [1]=%d '%s'\n",buf[0],buf[1],buf);
146
147 if( (devparm > 2) || (verbose>1) )
148 {
149 // List DEH lines, but not blank lines.
150 if( i > 1 )
151 GenPrintf(EMSG_errlog, "DEH: %s", buf); // buf has \n
152 }
153 return buf;
154 }
155
156 // Get line, skipping comments.
myfgets_nocom(char * buf,int bufsize,myfile_t * f)157 char* myfgets_nocom(char *buf, int bufsize, myfile_t *f)
158 {
159 char* ret;
160
161 do {
162 ret = myfgets( buf, bufsize, f );
163 } while( ret && ret[0] == '#' ); // skip comment
164 return ret;
165 }
166
167
168 // Read multiple lines into buf
169 // Only used for text.
myfread(char * buf,size_t reqsize,myfile_t * f)170 size_t myfread( char *buf, size_t reqsize, myfile_t *f )
171 {
172 size_t byteread = f->size - (f->curpos-f->data); // bytes left
173 if( reqsize < byteread )
174 byteread = reqsize;
175 #ifdef DEBUG_DEH
176 memset( &buf[0], 0, reqsize-1 );
177 #endif
178 if( byteread>0 )
179 {
180 uint32_t i;
181 // Read lines except for any '\r'.
182 // But should only be taking the '\r' off end of line (/cr/lf)
183 for(i=0; i<byteread; )
184 {
185 char c=*f->curpos++;
186 if( c!='\r' )
187 buf[i++]=c;
188 }
189 }
190 buf[byteread] = '\0';
191 return byteread;
192 }
193
194 static int deh_num_error = 0;
195
deh_error(const char * fmt,...)196 static void deh_error(const char * fmt, ...)
197 {
198 va_list ap;
199
200 // Show the DEH errors for devparm, devgame, or verbose switches.
201 if (devparm || verbose)
202 {
203 va_start(ap, fmt);
204 GenPrintf_va( EMSG_error, fmt, ap );
205 va_end(ap);
206 }
207
208 deh_num_error++;
209 }
210
211
212 // Reject src string if greater than maxlen, or has non-alphanumeric
213 // For filename protection must reject
214 // ( . .. ../ / \ & * [] {} space leading-dash <32 >127 133 254 255 )
filename_reject(char * src,int maxlen)215 boolean filename_reject( char * src, int maxlen )
216 {
217 int j;
218 for( j=0; ; j++ )
219 {
220 if( j >= maxlen ) goto reject;
221
222 register char ch = src[j];
223 // legal values, all else is illegal
224 if(! (( ch >= 'A' && ch <= 'Z' )
225 || ( ch >= 'a' && ch <= 'z' )
226 || ( ch >= '0' && ch <= '9' )
227 || ( ch == '_' ) ))
228 goto reject;
229 }
230 return false; // no reject
231
232 reject:
233 return true; // rejected
234 }
235
236
237 // [WDJ] Do not write into const strings, segfaults will occur on Linux.
238 // Without fixing all sources of strings to be consistent, the best that
239 // can be done is to abandon the original string (may be some lost memory)
240 // Dehacked is only run once at startup and the lost memory is not enough
241 // to dedicate code to recover.
242
243 typedef enum { DRS_nocheck, DRS_name, DRS_string, DRS_format } DRS_type_e;
244
245 typedef struct {
246 uint16_t text_id;
247 byte num_s;
248 } PACKED_ATTR format_ref_t;
249
250 format_ref_t format_ref_table[] =
251 {
252 {QSPROMPT_NUM, 1},
253 {QLPROMPT_NUM, 1},
254 {DOSY_NUM, 1},
255 {DEATHMSG_SUICIDE, 1},
256 {DEATHMSG_TELEFRAG, 2},
257 {DEATHMSG_FIST, 2},
258 {DEATHMSG_GUN, 2},
259 {DEATHMSG_SHOTGUN, 2},
260 {DEATHMSG_MACHGUN, 2},
261 {DEATHMSG_ROCKET, 2},
262 {DEATHMSG_GIBROCKET, 2},
263 {DEATHMSG_PLASMA, 2},
264 {DEATHMSG_BFGBALL, 2},
265 {DEATHMSG_CHAINSAW, 2},
266 {DEATHMSG_SUPSHOTGUN, 2},
267 {DEATHMSG_PLAYUNKNOW, 2},
268 {DEATHMSG_HELLSLIME, 1},
269 {DEATHMSG_NUKE, 1},
270 {DEATHMSG_SUPHELLSLIME, 1},
271 {DEATHMSG_SPECUNKNOW, 1},
272 {DEATHMSG_BARRELFRAG, 2},
273 {DEATHMSG_BARREL, 1},
274 {DEATHMSG_POSSESSED, 1},
275 {DEATHMSG_SHOTGUY, 1},
276 {DEATHMSG_VILE, 1},
277 {DEATHMSG_FATSO, 1},
278 {DEATHMSG_CHAINGUY, 1},
279 {DEATHMSG_TROOP, 1},
280 {DEATHMSG_SERGEANT, 1},
281 {DEATHMSG_SHADOWS, 1},
282 {DEATHMSG_HEAD, 1},
283 {DEATHMSG_BRUISER, 1},
284 {DEATHMSG_UNDEAD, 1},
285 {DEATHMSG_KNIGHT, 1},
286 {DEATHMSG_SKULL, 1},
287 {DEATHMSG_SPIDER, 1},
288 {DEATHMSG_BABY, 1},
289 {DEATHMSG_CYBORG, 1},
290 {DEATHMSG_PAIN, 1},
291 {DEATHMSG_WOLFSS, 1},
292 {DEATHMSG_DEAD, 1},
293 {0xFFFF, 0}
294 };
295
296 // [WDJ] 8/26/2011 DEH/BEX replace string
297 // newstring is a temp buffer ptr, it gets modified for backslash literals
298 // oldstring is ptr to text ptr ( &text[i] )
deh_replace_string(char ** oldstring,char * newstring,DRS_type_e drstype)299 void deh_replace_string( char ** oldstring, char * newstring, DRS_type_e drstype )
300 {
301 #ifdef DEH_RECOVER_STRINGS
302 // Record bounds for replacement strings.
303 // If an oldstring is found within these bounds, it can be free().
304 // This is depends on const and malloc heap being two separate areas.
305 static char * deh_string_ptr_min = NULL;
306 static char * deh_string_ptr_max = NULL;
307 #endif
308 // Most text strings are format strings, and % are significant.
309 // New string must not have have any %s %d etc. not present in old string.
310 // New freedoom.bex has "1%", that is not present in original string
311 // Strings have %s, %s %s (old strings also had %c and %d).
312 // Music and sound strings may have '-' and '\0'.
313 // [WDJ] Newer compiler could not tolerate these as unsigned char.
314 char * newp = &newstring[0];
315 if( drstype == DRS_string )
316 {
317 // Test new string against table
318 // Fixes when Chex replacement strings have fewer %s in them,
319 // so Chex newmaps.wad can replace that string again.
320 int text_id = oldstring - (&text[0]);
321 byte num_s = 0;
322 int i;
323 // look up in table
324 for(i=0; ; i++)
325 {
326 if( format_ref_table[i].text_id > NUMTEXT ) break;
327 if( format_ref_table[i].text_id == text_id )
328 {
329 num_s = format_ref_table[i].num_s;
330 drstype = DRS_format;
331 break;
332 }
333 }
334
335 for(i=0; ;)
336 {
337 // new string must have same or fewer %
338 newp = strchr( newp, '%' );
339 if( newp == NULL ) break;
340 if( drstype == DRS_format )
341 {
342 // must block %n, write to memory
343 // Only %s are left in the text strings
344 switch( newp[1] )
345 {
346 case '%': // literal %
347 break;
348 case 's':
349 i++;
350 if( i > num_s ) goto bad_replacement;
351 break;
352 default:
353 goto bad_replacement;
354 }
355 }
356 else
357 {
358 // only %% allowed
359 if( newp[1] != '%' )
360 newp[0] = ' '; // rubout the %
361 }
362 newp +=2;
363 }
364 }
365
366 // rewrite backslash literals into newstring, because it only gets shorter
367 char * chp = &newstring[0];
368 for( newp = &newstring[0]; *newp ; newp++ )
369 {
370 // Backslash in DEH and BEX strings are not interpreted by printf
371 // Must convert \n to LF.
372 register char ch = *newp;
373 if( ch == 0x5C ) // backslash
374 {
375 char * endvp = NULL;
376 unsigned long v;
377 ch = *(++newp);
378 switch( ch )
379 {
380 case 'N': // some file are all caps
381 case 'n': // newline
382 ch = '\n'; // LF char
383 goto incl_char;
384 case '\0': // safety
385 goto term_string;
386 case '0': // NUL, should be unnecessary
387 goto term_string;
388 case 'x': // hex
389 // These do not get interpreted unless we do it here.
390 // Need this for foreign language ??
391 v = strtoul( &newp[1], &endvp, 16); // get hex
392 goto check_backslash_value;
393 default:
394 if( ch >= '1' && ch <= '9' ) // octal
395 {
396 // These do not get interpreted unless we do it here.
397 // Need this for foreign language ??
398 v = strtoul( newp, &endvp, 8); // get octal
399 goto check_backslash_value;
400 }
401 }
402 continue; // ignore unrecognized backslash
403
404 check_backslash_value:
405 if( v > 255 ) goto bad_char; // long check
406 ch = v;
407 newp = endvp - 1; // continue after number
408 // check value against tests
409 }
410 // reject special character attacks
411 #if defined( FRENCH_INLINE ) || defined( BEX_LANGUAGE )
412 // place checks for allowed foreign lang chars here
413 // reported dangerous escape chars
414 if( (unsigned char)ch == 133 ) goto bad_char;
415 if( (unsigned char)ch >= 254 ) goto bad_char;
416 // if( ch == 27 ) continue; // ESCAPE
417 #else
418 if( (unsigned char)ch > 127 ) goto bad_char;
419 #endif
420 if( ch < 32 )
421 {
422 if( ch == '\t' ) ch = ' '; // change to space
423 if( ch == '\r' ) continue; // remove
424 if( ch == '\n' ) goto incl_char;
425 if( ch == '\0' ) goto term_string; // end of string
426 goto bad_char;
427 }
428 incl_char:
429 // After a backslash, chp < newp
430 *chp++ = ch; // rewrite
431 }
432 term_string:
433 *chp = '\0'; // term rewrite
434
435 if( drstype == DRS_name )
436 {
437 if( strlen(newstring) > 10 ) goto bad_replacement;
438 }
439
440 char * nb = strdup( newstring ); // by malloc
441 if( nb == NULL )
442 I_Error( "Dehacked/BEX string memory allocate failure" );
443 #ifdef DEH_RECOVER_STRINGS
444 // check if was in replacement string bounds
445 if( *oldstring && deh_string_ptr_min
446 && *oldstring >= deh_string_ptr_min
447 && *oldstring <= deh_string_ptr_max )
448 free( *oldstring );
449 // track replacement string bounds
450 if( deh_string_ptr_min == NULL || nb < deh_string_ptr_min )
451 deh_string_ptr_min = nb;
452 if( nb > deh_string_ptr_max )
453 deh_string_ptr_max = nb;
454 #else
455 // Abandon old strings, might be const
456 // Linux GCC programs will segfault if try to free a const string (correct behavior).
457 // The lost memory is small and this occurs only once in the program.
458 #endif
459 *oldstring = nb; // replace the string in the tables
460 return;
461
462 bad_char:
463 if( chp )
464 *chp = '\0'; // hide the bad character
465 bad_replacement:
466 CONS_Printf( "Replacement string illegal : %s\n", newstring );
467 return;
468 }
469
470
471 /* ======================================================================== */
472 // Load a dehacked file format 6 I (BP) don't know other format
473 /* ======================================================================== */
474 /* a sample to see
475 Thing 1 (Player) { // MT_PLAYER
476 int doomednum; ID # = 3232 -1, // doomednum
477 int spawnstate; Initial frame = 32 S_PLAY, // spawnstate
478 int spawnhealth; Hit points = 3232 100, // spawnhealth
479 int seestate; First moving frame = 32 S_PLAY_RUN1, // seestate
480 int seesound; Alert sound = 32 sfx_None, // seesound
481 int reactiontime; Reaction time = 3232 0, // reactiontime
482 int attacksound; Attack sound = 32 sfx_None, // attacksound
483 int painstate; Injury frame = 32 S_PLAY_PAIN, // painstate
484 int painchance; Pain chance = 3232 255, // painchance
485 int painsound; Pain sound = 32 sfx_plpain, // painsound
486 int meleestate; Close attack frame = 32 S_NULL, // meleestate
487 int missilestate; Far attack frame = 32 S_PLAY_ATK1, // missilestate
488 int deathstate; Death frame = 32 S_PLAY_DIE1, // deathstate
489 int xdeathstate; Exploding frame = 32 S_PLAY_XDIE1, // xdeathstate
490 int deathsound; Death sound = 32 sfx_pldeth, // deathsound
491 int speed; Speed = 3232 0, // speed
492 int radius; Width = 211812352 16*FRACUNIT, // radius
493 int height; Height = 211812352 56*FRACUNIT, // height
494 int mass; Mass = 3232 100, // mass
495 int damage; Missile damage = 3232 0, // damage
496 int activesound; Action sound = 32 sfx_None, // activesound
497 int flags; Bits = 3232 MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH,
498 int raisestate; Respawn frame = 32 S_NULL // raisestate
499 }, */
500 // [WDJ] BEX flags 9/10/2011
501 typedef enum { BFexit, BF1, BF2, BF2x, BFT } bex_flags_ctrl_e;
502
503 typedef struct {
504 char * name;
505 bex_flags_ctrl_e ctrl;
506 uint32_t flagval;
507 } flag_name_t;
508
509 // [WDJ] From boomdeh.txt, and DoomLegacy2.0
510 flag_name_t BEX_flag_name_table[] =
511 {
512 {"SPECIAL", BF1, MF_SPECIAL }, // Call TouchSpecialThing when touched.
513 {"SOLID", BF1, MF_SOLID }, // Blocks
514 {"SHOOTABLE", BF1, MF_SHOOTABLE }, // Can be hit
515 {"NOSECTOR", BF1, MF_NOSECTOR }, // Don't link to sector (invisible but touchable)
516 {"NOBLOCKMAP", BF1, MF_NOBLOCKMAP }, // Don't link to blockmap (inert but visible)
517 {"AMBUSH", BF1, MF_AMBUSH }, // Not to be activated by sound, deaf monster.
518 {"JUSTHIT", BF1, MF_JUSTHIT }, // Will try to attack right back.
519 {"JUSTATTACKED", BF1, MF_JUSTATTACKED }, // Will take at least one step before attacking.
520 {"SPAWNCEILING", BF1, MF_SPAWNCEILING }, // Spawned hanging from the ceiling
521 {"NOGRAVITY", BF1, MF_NOGRAVITY }, // Does not feel gravity
522 {"DROPOFF", BF1, MF_DROPOFF }, // Can jump/drop from high places
523 {"PICKUP", BF1, MF_PICKUP }, // Can/will pick up items. (players)
524 // two clip bits, set them both
525 {"NOCLIP", BF1, MF_NOCLIP }, // Does not clip against lines.
526 {"NOCLIP", BF2, MF2_NOCLIPTHING }, // Does not clip against Actors.
527 // two slide bits, set them both
528 {"SLIDE", BF1, MF_SLIDE }, // Player: keep info about sliding along walls.
529 {"SLIDE", BF2, MF2_SLIDE }, // Slides against walls
530
531 {"FLOAT", BF1, MF_FLOAT }, // Active floater, can move freely in air (cacodemons etc.)
532 {"TELEPORT", BF1, MF_TELEPORT }, // Don't cross lines or look at heights on teleport.
533 {"MISSILE", BF1, MF_MISSILE }, // Missile. Don't hit same species, explode on block.
534 {"DROPPED", BF1, MF_DROPPED }, // Dropped by a monster
535 {"SHADOW", BF1, MF_SHADOW }, // Partial invisibility (spectre). Makes targeting harder.
536 {"NOBLOOD", BF1, MF_NOBLOOD }, // Does not bleed when shot (furniture)
537 {"CORPSE", BF1, MF_CORPSE }, // Acts like a corpse, falls down stairs etc.
538 {"INFLOAT", BF1, MF_INFLOAT }, // Don't auto float to target's height.
539 {"COUNTKILL", BF1, MF_COUNTKILL }, // On kill, count towards intermission kill total.
540 {"COUNTITEM", BF1, MF_COUNTITEM }, // On pickup, count towards intermission item total.
541 {"SKULLFLY", BF1, MF_SKULLFLY }, // Flying skulls, neither a cacodemon nor a missile.
542 {"NOTDMATCH", BF1, MF_NOTDMATCH }, // Not spawned in DM (keycards etc.)
543 // 4 bits of player color translation (gray/red/brown)
544 // PrBoom, MBF, EternityEngine have only 2 bits of color translation.
545 {"TRANSLATION1", BFT, (1<<MFT_TRANSSHIFT) }, // Boom
546 {"TRANSLATION2", BFT, (2<<MFT_TRANSSHIFT) }, // Boom
547 {"TRANSLATION3", BFT, (4<<MFT_TRANSSHIFT) },
548 {"TRANSLATION4", BFT, (8<<MFT_TRANSSHIFT) },
549 {"TRANSLATION", BFT, (1<<MFT_TRANSSHIFT) }, // Boom/prboom compatibility
550 {"UNUSED1 ", BFT, (2<<MFT_TRANSSHIFT) }, // Boom/prboom compatibility
551 // Boom/BEX
552 {"TRANSLUCENT", BF1, MF_TRANSLUCENT }, // Boom translucent
553 // MBF/Prboom Extensions
554 {"TOUCHY", BF1, MF_TOUCHY }, // (MBF) Reacts upon contact
555 {"BOUNCES", BF1, MF_BOUNCES }, // (MBF) Bounces off walls, etc.
556 {"FRIEND", BF1, MF_FRIEND }, // (MBF) Friend to player (dog, etc.)
557
558 {"MF2CLEAR", BF2x, 0 }, // clear MF2 bits, no bits set
559 // DoomLegacy 1.4x Extensions
560 {"FLOORHUGGER", BF2x, MF2_FLOORHUGGER }, // [WDJ] moved to MF2
561 // Heretic
562 {"LOWGRAVITY", BF2x, MF2_LOGRAV }, // Experiences only 1/8 gravity
563 {"WINDTHRUST", BF2x, MF2_WINDTHRUST }, // Is affected by wind
564 // {"FLOORBOUNCE", BF2, MF2_FLOORBOUNCE }, // Bounces off the floor
565 // see MBF/Prboom "BOUNCES"
566 {"HERETICBOUNCE", BF2x, MF2_FLOORBOUNCE }, // Bounces off the floor
567 {"THRUGHOST", BF2x, MF2_THRUGHOST }, // Will pass through ghosts (missile)
568 {"FLOORCLIP", BF2x, MF2_FOOTCLIP }, // Feet may be be clipped
569 {"SPAWNFLOAT", BF2x, MF2_SPAWNFLOAT }, // Spawned at random height
570 {"NOTELEPORT", BF2x, MF2_NOTELEPORT }, // Does not teleport
571 {"RIPPER", BF2x, MF2_RIP }, // Rips through solid targets (missile)
572 {"PUSHABLE", BF2x, MF2_PUSHABLE }, // Can be pushed by other moving actors
573 // {"SLIDE", BF2x, MF2_SLIDE }, // Slides against walls
574 // see other "SLIDE", and MF_SLIDE
575 {"PASSMOBJ", BF2x, MF2_PASSMOBJ }, // Enable z block checking.
576 // If on, this flag will allow the mobj to pass over/under other mobjs.
577 {"CANNOTPUSH", BF2x, MF2_CANNOTPUSH }, // Cannot push other pushable actors
578 {"BOSS", BF2x, MF2_BOSS }, // Is a major boss, not as easy to kill
579 {"FIREDAMAGE", BF2x, MF2_FIREDAMAGE }, // does fire damage
580 {"NODAMAGETHRUST", BF2x, MF2_NODMGTHRUST }, // Does not thrust target when damaging
581 {"TELESTOMP", BF2x, MF2_TELESTOMP }, // Can telefrag another Actor
582 {"FLOATBOB", BF2x, MF2_FLOATBOB }, // use float bobbing z movement
583 {"DONTDRAW", BF2x, MF2_DONTDRAW }, // Invisible (does not generate a vissprite)
584 // DoomLegacy 1.4x Internal flags, non-standard
585 // Exist but have little use being set by a WAD
586 {"ONMOBJ", BF2x, MF2_ONMOBJ }, // mobj is resting on top of another
587 // {"FEETARECLIPPED", BF2x, MF2_FEETARECLIPPED }, // a mobj's feet are now being cut
588 // {"FLY", BF2x, MF2_FLY }, // Fly mode
589
590 // DoomLegacy 2.0 Extensions
591 // Heretic/Hexen/ZDoom additions
592 // {"HEXENBOUNCE", BF2x, MF2_FULLBOUNCE }, // Bounces off walls and floor
593 // {"SLIDESONWALLS", BF2x, MF2_SLIDE }, // Slides against walls
594 // {"FLOORHUGGER", BF2x, MF2_FLOORHUGGER },
595 // {"CEILINGHUGGER", BF2x, MF2_CEILINGHUGGER },
596 // {"DONTBLAST", BF2x, MF2_NONBLASTABLE },
597 // {"QUICKTORETALIATE", BF2x, MF2_QUICKTORETALIATE },
598 // {"NOTARGET", BF2x, MF2_NOTARGET }, // Will not be targeted by other monsters of same team (like Arch-Vile)
599 // {"FLOATBOB", BF2x, MF2_FLOATBOB }, // Bobs up and down in the air (item)
600 // {"CANPASS", BF2x, 0, }, // TODO inverted! Can move over/under other Actors
601 // {"NONSHOOTABLE", BF2x, MF2_NONSHOOTABLE }, // Transparent to MF_MISSILEs
602 // {"INVULNERABLE", BF2x, MF2_INVULNERABLE }, // Does not take damage
603 // {"DORMANT", BF2x, MF2_DORMANT }, // Cannot be damaged, is not noticed by seekers
604 // {"CANTLEAVEFLOORPIC", BF2x, MF2_CANTLEAVEFLOORPIC }, // Stays within a certain floor texture
605 // {"SEEKERMISSILE", BF2x, MF2_SEEKERMISSILE }, // Is a seeker (for reflection)
606 // {"REFLECTIVE", BF2x, MF2_REFLECTIVE }, // Reflects missiles
607 // {"ACTIVATEIMPACT", BF2x, MF2_IMPACT }, // Can activate SPAC_IMPACT
608 // {"CANPUSHWALLS", BF2x, MF2_PUSHWALL }, // Can activate SPAC_PUSH
609 // {"DONTSPLASH", BFC_x, MF_NOSPLASH }, // Does not cause splashes in liquid.
610 // {"ISMONSTER", BFC_x, MF_MONSTER },
611 // {"ACTIVATEMCROSS", BFC_x, MF2_MCROSS }, // Can activate SPAC_MCROSS
612 // {"ACTIVATEPCROSS", BFC_x, MF2_PCROSS }, // Can activate SPAC_PCROSS
613 {NULL, BFexit, 0} // terminator
614 };
615
616 //#define CHECK_FLAGS2_DEFAULT
617 #ifdef CHECK_FLAGS2_DEFAULT
618 // Old PWAD do not know of MF2 bits.
619 // Default for PWAD that do not set MF2 flags
620 const uint32_t flags2_default_value = 0; // other ports like prboom
621 // | MF2_PASSMOBJ // heretic monsters
622 // | MF2_FOOTCLIP // heretic only
623 // | MF2_WINDTHRUST; // requires heretic wind sectors
624 #endif
625
626
627
628 // Standardized state ranges
629 enum {
630 // Doom
631 STS_TECH2LAMP4 = 966,
632 // TNT
633 STS_TNT1 = 967,
634 // MBF
635 STS_GRENADE = 968,
636 STS_DOGS_PR_STND = 972,
637 STS_DOGS_RAISE6 = 998,
638 STS_MUSHROOM = 1075,
639 // Doom Beta
640 STS_OLDBFG1 = 999,
641 STS_BSKUL_DIE8 = 1074,
642 // HERETIC
643 STH_FREETARGMOBJ = 1,
644 STH_PODGENERATOR = 114,
645 STH_SPLASH1 = 115, // is S_HSPLASH1
646 STH_TFOG1 = 223, // is S_HTFOG1
647 STH_TFOG13 = 235,
648 STH_LIGHTDONE = 236, // is S_HLIGHTDONE, but unused
649 STH_STAFFREADY = 237,
650 STH_CRBOWFX4_2 = 562,
651 STH_BLOOD1 = 563, // mapped to S_BLOOD1 (S_HBLOOD dis)
652 STH_BLOOD3 = 565,
653 STH_BLOODSPLATTER1 = 566,
654 STH_BLOODSPLATTERX = 569,
655 STH_PLAY = 570, // mapped to S_PLAY (Heretic S_PLAY dis)
656 STH_PLAY_XDIE9 = 596,
657 STH_PLAY_FDTH1 = 597, // unmapped, TODO
658 STH_PLAY_FDTH20 = 616,
659 STH_BLOODYSKULL1 = 617,
660 STH_REDAXE3 = 986,
661 STH_SND_WATERFALL = 1204,
662 };
663
664
665 // Translate deh frame number to internal state index.
666 // return S_NULL when invalid state (deh frame)
deh_frame_to_state(int deh_frame)667 statenum_t deh_frame_to_state( int deh_frame )
668 {
669 // Some old wads had negative frame numbers, which should be S_NULL.
670 if( deh_frame <= 0 ) goto null_frame;
671
672 // remapping
673 if( EN_heretic )
674 {
675 if( deh_frame <= STH_CRBOWFX4_2 )
676 return deh_frame + (S_FREETARGMOBJ - STH_FREETARGMOBJ);
677 // STH_BLOOD Mapped to the doom blood
678 if( deh_frame >= STH_BLOOD1
679 && deh_frame <= STH_BLOOD3 )
680 return deh_frame + (S_BLOOD1 - STH_BLOOD1);
681 if( deh_frame >= STH_BLOODSPLATTER1
682 && deh_frame <= STH_BLOODSPLATTERX )
683 return deh_frame + (S_BLOODSPLATTER1 - STH_BLOODSPLATTER1);
684 // STH_PLAY Mapped to the doom player, except for STH_PLAY_FDTH.
685 if( deh_frame >= STH_PLAY
686 && deh_frame <= STH_PLAY_XDIE9 )
687 return deh_frame + (S_PLAY - STH_PLAY);
688 #if 0
689 // TODO
690 if( deh_frame >= STH_PLAY_FDTH1
691 && deh_frame <= STH_PLAY_FDTH20 )
692 return deh_frame + (S_PLAY_FDTH1 - STH_PLAY_FDTH1);
693 #endif
694 if( deh_frame >= STH_BLOODYSKULL1
695 && deh_frame <= STH_SND_WATERFALL )
696 return deh_frame + (S_BLOODYSKULL1 - STH_BLOODYSKULL1);
697 }
698 else
699 {
700 if( deh_frame <= STS_TECH2LAMP4 )
701 return deh_frame;
702 if( deh_frame == STS_TNT1 )
703 return S_TNT1;
704 if( deh_frame >= STS_GRENADE
705 && deh_frame <= STS_DOGS_RAISE6 )
706 return deh_frame + (S_GRENADE - STS_GRENADE);
707 if( deh_frame == STS_MUSHROOM )
708 return S_MUSHROOM;
709 }
710
711 // Necessary for DEH that add new states, like chex newmaps.wad.
712 if( deh_frame >= 0 && deh_frame < NUMSTATES )
713 return deh_frame; // emulate old behavior, without translation
714
715 null_frame:
716 return S_NULL;
717 }
718
719
720 // Implement assigns of a state num, from deh.
721 static
set_state(statenum_t * snp,int deh_frame_id)722 void set_state( statenum_t * snp, int deh_frame_id )
723 {
724 statenum_t si = deh_frame_to_state(deh_frame_id);
725 *snp = si; // valid or S_NULL
726 if( deh_frame_id > 0 && si == S_NULL )
727 GenPrintf(EMSG_errlog, "DEH/BEX set state has bad frame id: %i\n", deh_frame_id );
728 }
729
730
searchvalue(char * s)731 static int searchvalue(char *s)
732 {
733 while(s[0]!='=' && s[0]!='\0') s++;
734 if (s[0]=='=')
735 return atoi(&s[1]);
736 else
737 {
738 deh_error("No value found\n");
739 return 0;
740 }
741 }
742
743 // Have read "Thing <deh_thing_id>"
readthing(myfile_t * f,int deh_thing_id)744 static void readthing(myfile_t *f, int deh_thing_id )
745 {
746 // DEH thing 1.. , but mobjinfo array is 0..
747 mobjinfo_t * mip = & mobjinfo[ deh_thing_id - 1 ];
748 char s[MAXLINELEN];
749 char *word;
750 int value;
751 uint32_t flags1, flags2, tflags;
752
753 do{
754 if(myfgets(s,sizeof(s),f)!=NULL) // get line
755 {
756 if(s[0]=='\n') break;
757 value=searchvalue(s);
758 word=strtok(s," ");
759
760 // Wads that use Bits: phobiata, hth2, DRCRYPT
761 if(!strcasecmp(word,"Bits"))
762 {
763 boolean flags2x_hit = 0; // doomlegacy extensions hit
764 flag_name_t * fnp; // BEX flag names ptr
765
766 flags1 = flags2 = tflags = 0;
767 for(;;)
768 {
769 word = strtok(NULL, " +|\t=\n");
770 if( word == NULL ) goto set_flags;
771 if( word[0] == '\n' ) goto set_flags;
772 // detect bits by integer value
773 // MBF DRCRYPT.WAD sets color and MF_FRIEND using Bits, with neg value.
774 // Prevent using signed char as index.
775 if( isdigit( (unsigned char)word[0] ) || word[0] == '-' )
776 {
777 // old style integer value, flags only (not flags2)
778 flags1 = atoi(word); // numeric entry
779 if( flags1 & MF_TRANSLUCENT )
780 {
781 // Boom bit defined in boomdeh.txt
782 // Was MF_FLOORHUGGER bit, and now need to determine which the PWAD means.
783 // MBF DRCRYPT.WAD has object with bits =
784 // MF_TRANSLUCENT, MF_COUNTKILL, MF_SHADOW, MF_SOLID, MF_SHOOTABLE.
785 GenPrintf(EMSG_errlog, "Sets flag MF_FLOORHUGGER or MF_TRANSLUCENT by numeric, guessing ");
786 if( flags1 & (MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_COUNTITEM|MF_SHADOW))
787 {
788 // assume TRANSLUCENT, check for known exceptions
789 GenPrintf(EMSG_errlog, "MF_TRANSLUCENT\n");
790 }
791 else
792 {
793 // Don't know of any wads setting FLOORHUGGER using
794 // bits.
795 // assume FLOORHUGGER, check for known exceptions
796 flags1 &= ~MF_TRANSLUCENT;
797 flags2 |= MF2_FLOORHUGGER;
798 GenPrintf(EMSG_errlog, "MF_FLOORHUGGER\n");
799 }
800 }
801 if( flags1 & MFO_TRANSLATION4 )
802 {
803 // Color translate, moved to tflags.
804 GenPrintf(EMSG_errlog, "Sets color flag MF_TRANSLATE using Bits\n" );
805 tflags |= (flags1 & MFO_TRANSLATION4) >> (MFO_TRANSSHIFT - MFT_TRANSSHIFT);
806 flags1 &= ~MFO_TRANSLATION4;
807 }
808 // we are still using same flags bit order
809 goto next_line;
810 }
811 // handle BEX flag names
812 for( fnp = &BEX_flag_name_table[0]; ; fnp++ )
813 {
814 if(fnp->name == NULL) goto name_unknown;
815 if(!strcasecmp( word, fnp->name )) // find name
816 {
817 switch( fnp->ctrl )
818 {
819 case BF1:
820 flags1 |= fnp->flagval;
821 break;
822 case BF2x: // DoomLegacy extension BEX name
823 flags2x_hit = 1;
824 case BF2: // standard name that happens to be MF2
825 flags2 |= fnp->flagval;
826 break;
827 case BFT: // standard name that happens to be MFT
828 tflags |= fnp->flagval;
829 break;
830 default:
831 goto name_unknown;
832 }
833 // if next entry is same keyword then process it too
834 if( (fnp[1].name != fnp[0].name) )
835 goto next_word; // done with this word
836 // next entry is same word, process it too
837 }
838 }
839 name_unknown:
840 deh_error("Bits name unknown: %s\n", word);
841 // continue with next keyword
842 next_word:
843 continue;
844 }
845
846 set_flags:
847 // clear std flags in flags2
848 mip->flags2 &= ~(MF2_SLIDE|MF2_FLOORBOUNCE);
849 if( flags2x_hit )
850 {
851 // clear extension flags2 only if some extension names appeared
852 mip->flags2 = 0;
853 }
854 #ifdef CHECK_FLAGS2_DEFAULT
855 else
856 {
857 // Unless explicitly used BF2x bit, then put in default bits
858 // Avoid by using MF2CLEAR
859 mip->flags2 = flags2_default_value;
860 }
861 #endif
862 next_line:
863 mip->flags = flags1;
864 mip->flags2 |= flags2;
865 mip->tflags = tflags; // info extension has color now
866 flags_valid_deh = true;
867 continue; // next line
868 }
869
870 // set the value in apropriet field
871 else if(!strcasecmp(word,"ID")) mip->doomednum =value;
872 else if(!strcasecmp(word,"Initial")) set_state( &mip->spawnstate, value );
873 else if(!strcasecmp(word,"Hit")) mip->spawnhealth =value;
874 else if(!strcasecmp(word,"First")) set_state( &mip->seestate, value );
875 else if(!strcasecmp(word,"Alert")) mip->seesound =value;
876 else if(!strcasecmp(word,"Reaction")) mip->reactiontime=value;
877 else if(!strcasecmp(word,"Attack")) mip->attacksound =value;
878 else if(!strcasecmp(word,"Injury")) set_state( &mip->painstate, value );
879 else if(!strcasecmp(word,"Pain"))
880 {
881 word=strtok(NULL," ");
882 if(!strcasecmp(word,"chance")) mip->painchance =value;
883 else if(!strcasecmp(word,"sound")) mip->painsound =value;
884 }
885 else if(!strcasecmp(word,"Close")) set_state( &mip->meleestate, value );
886 else if(!strcasecmp(word,"Far")) set_state( &mip->missilestate, value );
887 else if(!strcasecmp(word,"Death"))
888 {
889 word=strtok(NULL," ");
890 if(!strcasecmp(word,"frame")) set_state( &mip->deathstate, value );
891 else if(!strcasecmp(word,"sound")) mip->deathsound =value;
892 }
893 else if(!strcasecmp(word,"Exploding")) set_state( &mip->xdeathstate, value );
894 else if(!strcasecmp(word,"Speed")) mip->speed =value;
895 else if(!strcasecmp(word,"Width")) mip->radius =value;
896 else if(!strcasecmp(word,"Height")) mip->height =value;
897 else if(!strcasecmp(word,"Mass")) mip->mass =value;
898 else if(!strcasecmp(word,"Missile")) mip->damage =value;
899 else if(!strcasecmp(word,"Action")) mip->activesound =value;
900 else if(!strcasecmp(word,"Bits2")) mip->flags2 =value;
901 else if(!strcasecmp(word,"Respawn")) set_state( &mip->raisestate, value );
902 else deh_error("Thing %d : unknown word '%s'\n", deh_thing_id,word);
903 }
904 } while(s[0]!='\n' && !myfeof(f)); //finish when the line is empty
905 }
906
907 // Have read "Frame <deh_frame_id>"
908 /*
909 Sprite number = 10
910 Sprite subnumber = 32968
911 Duration = 200
912 Next frame = 200
913 // used as param 1 and param2 by MBF functions
914 Unknown 1 = 5
915 Unknown 2 = 17
916 */
readframe(myfile_t * f,int deh_frame_id)917 static void readframe(myfile_t* f, int deh_frame_id)
918 {
919 state_t * fsp;
920 char s[MAXLINELEN];
921 char *word1,*word2;
922 int value;
923
924 // Syntax: "Frame <num>"
925 int si = deh_frame_to_state(deh_frame_id);
926 if( si == S_NULL )
927 {
928 deh_error("Frame %d don't exist\n", deh_frame_id);
929 return;
930 }
931
932 fsp = & states[ si ];
933
934 do{
935 if(myfgets_nocom(s,sizeof(s),f)!=NULL)
936 {
937 if(s[0]=='\n') break;
938 value=searchvalue(s);
939 // set the value in appropriate field
940 word1=strtok(s," ");
941 word2=strtok(NULL," ");
942
943 if(!strcasecmp(word1,"Sprite"))
944 {
945 // Syntax: Sprite number = <num>
946 if(!strcasecmp(word2,"number")) fsp->sprite =value;
947 // Syntax: Sprite subnumber = <num>
948 else if(!strcasecmp(word2,"subnumber")) fsp->frame =value;
949 }
950 // Syntax: Duration = <num>
951 else if(!strcasecmp(word1,"Duration")) fsp->tics =value;
952 // Syntax: Next frame = <num>
953 else if(!strcasecmp(word1,"Next")) set_state( &fsp->nextstate, value );
954 else if(!strcasecmp(word1,"Unknown"))
955 {
956 // Syntax: Unknown 2 = <num>
957 // MBF uses these for parameters (parm1, parm2)
958 state_ext_t * sep = P_create_state_ext( fsp );
959 if( word2[0] == '1' ) sep->parm1 = value;
960 else if( word2[0] == '2' ) sep->parm2 = value;
961 }
962 else deh_error("Frame %d : unknown word '%s'\n", deh_frame_id, word1);
963 }
964 } while(s[0]!='\n' && !myfeof(f));
965 }
966
967 // Have read "Pointer <xref>"
968 // The xref is a dehacked cross ref number.
969 static
readpointer(myfile_t * f,int xref)970 void readpointer( myfile_t* f, int xref )
971 {
972 char s[MAXLINELEN];
973 char *word2;
974 int i;
975 statenum_t si, sj;
976
977 // Syntax: "Pointer <xref> (Frame <deh_frame_id>)"
978 word2 = strtok(NULL," "); // get keyword "Frame"
979 word2 = strtok(NULL,")");
980 if( ! word2 )
981 {
982 deh_error("Pointer %i (Frame ... ) : missing ')'\n", xref );
983 return;
984 }
985
986 i = atoi(word2);
987 si = deh_frame_to_state(i);
988 if( si == S_NULL )
989 {
990 deh_error("Pointer %i : Frame %d don't exist\n", xref, i);
991 return;
992 }
993
994 // [WDJ] For some reason PrBoom and EE do this in a loop,
995 // and then print out a BEX equivalent line.
996 // Syntax: "Codep Frame <deh_frame_id>"
997 if( myfgets(s,sizeof(s),f) != NULL )
998 {
999 sj = deh_frame_to_state( searchvalue(s) );
1000 states[si].action = deh_actions[sj];
1001 }
1002 }
1003
1004
readsound(myfile_t * f,int deh_sound_id)1005 static void readsound(myfile_t* f, int deh_sound_id)
1006 {
1007 sfxinfo_t * ssp = & S_sfx[ deh_sound_id ];
1008 char s[MAXLINELEN];
1009 char *word;
1010 int value;
1011
1012 do{
1013 if(myfgets_nocom(s,sizeof(s),f)!=NULL)
1014 {
1015 if(s[0]=='\n') break;
1016 value=searchvalue(s);
1017 word=strtok(s," ");
1018 if(!strcasecmp(word,"Offset"))
1019 {
1020 value-=150360;
1021 if(value<=64) value/=8;
1022 else if(value<=260) value=(value+4)/8;
1023 else value=(value+8)/8;
1024 if(value>=-1 && value<sfx_freeslot0-1)
1025 ssp->name=deh_sfxnames[value+1];
1026 else
1027 deh_error("Sound %d : offset out of bound\n", deh_sound_id);
1028 }
1029 else if(!strcasecmp(word,"Zero/One"))
1030 ssp->flags = ( value? SFX_single : 0 );
1031 else if(!strcasecmp(word,"Value")) ssp->priority =value;
1032 else deh_error("Sound %d : unknown word '%s'\n", deh_sound_id,word);
1033 }
1034 } while(s[0]!='\n' && !myfeof(f));
1035 }
1036
1037 // [WDJ] Some strings have been altered, preventing match to text in DEH file.
1038 // This is hash of the original Doom strings.
1039 typedef struct {
1040 uint32_t hash; // computed hash
1041 uint16_t indirect; // id of actual text
1042 } PACKED_ATTR hash_text_t;
1043
1044 // [WDJ] This is constructed from wads where DEH fails.
1045 // To get hash print use -devparm -v.
1046 // > doomlegacy -devparm -v -game doom2 -file xxx.wad 2> xxx.log
1047 static hash_text_t hash_text_table[] =
1048 {
1049 {0x26323511, QUITMSG3_NUM}, // dos -> your os
1050 {0x33033301, QUITMSG4_NUM}, // dos -> your os
1051 {0x8e3b425e, QUIT2MSG1_NUM}, // dos -> shell
1052 {0x0042d2cd, DEATHMSG_TELEFRAG}, // telefraged -> telefragged
1053 {0x106f76c2, DEATHMSG_ROCKET}, // catched -> caught
1054 // because Chex1PatchEngine changes text before newmaps DEH makes its changes
1055 {0xea264ed7, E1TEXT_NUM},
1056 {0xcee03ff5, QUITMSG_NUM},
1057 {0x55b48886, QUITMSG1_NUM},
1058 {0xe980b2e0, QUITMSG2_NUM},
1059 {0x26323511, QUITMSG3_NUM},
1060 {0x33033301, QUITMSG4_NUM},
1061 {0x84311a52, QUITMSG5_NUM},
1062 {0xb6d256a1, QUITMSG6_NUM},
1063 {0x303ea545, QUITMSG7_NUM},
1064 {0x8e3b425e, QUIT2MSG1_NUM},
1065 {0x507e7be5, QUIT2MSG2_NUM},
1066 {0xc5e635e9, QUIT2MSG3_NUM},
1067 {0x70136b3e, QUIT2MSG4_NUM},
1068 {0xe5efb6a8, QUIT2MSG5_NUM},
1069 {0x04fc8b21, QUIT2MSG6_NUM},
1070 {0x68d8f2ce, NIGHTMARE_NUM},
1071 {0x001bcf94, GOTARMOR_NUM},
1072 {0x01bcfc54, GOTMEGA_NUM},
1073 {0x01bc5ffd, GOTHTHBONUS_NUM},
1074 {0x01bc817d, GOTARMBONUS_NUM},
1075 {0x00010e33, GOTSUPER_NUM},
1076 {0x01bc64a4, GOTBLUECARD_NUM},
1077 {0x06f2bfa4, GOTYELWCARD_NUM},
1078 {0x00de4424, GOTREDCARD_NUM},
1079 {0x003795f5, GOTSTIM_NUM},
1080 {0x8e914c80, GOTMEDINEED_NUM},
1081 {0x001bc72a, GOTMEDIKIT_NUM},
1082 {0x00000b0b, GOTBERSERK_NUM},
1083 {0x00e1e245, GOTINVIS_NUM},
1084 {0x06b5b172, GOTSUIT_NUM},
1085 {0x0d3bf614, GOTVISOR_NUM},
1086 {0x000378aa, GOTCLIP_NUM},
1087 {0x0378e27f, GOTCLIPBOX_NUM},
1088 {0x000de4c2, GOTROCKET_NUM},
1089 {0x0378e527, GOTROCKBOX_NUM},
1090 {0x01bc81b0, GOTCELL_NUM},
1091 {0x1bc81c85, GOTCELLBOX_NUM},
1092 {0x06f0ec53, GOTSHELLS_NUM},
1093 {0xde396c53, GOTSHELLBOX_NUM},
1094 {0xde1fc095, GOTBACKPACK_NUM},
1095 {0x048972a1, GOTBFG9000_NUM},
1096 {0x004899e4, GOTCHAINGUN_NUM},
1097 {0x0260d3b2, GOTCHAINSAW_NUM},
1098 {0x1228dc04, GOTLAUNCHER_NUM},
1099 {0x009143bc, GOTPLASMA_NUM},
1100 {0x00245214, GOTSHOTGUN_NUM},
1101 {0x015aa620, STSTR_DQDON_NUM},
1102 {0x02b54c46, STSTR_DQDOFF_NUM},
1103 {0x000b945e, STSTR_FAADDED_NUM},
1104 {0x00824ede, STSTR_KFAADDED_NUM},
1105 {0x000188ff, STSTR_CHOPPERS_NUM},
1106 {0x00007ba0, HUSTR_E1M1_NUM},
1107 {0x001f7c84, HUSTR_E1M2_NUM},
1108 {0x003fc441, HUSTR_E1M3_NUM},
1109 {0x007d8282, HUSTR_E1M4_NUM},
1110 {0x0003f9ac, HUSTR_E1M5_NUM},
1111 //{0x0000199f, ??}, // "GREEN KEY"
1112 {0x00004375, DEATHMSG_SUICIDE},
1113 {0x0042d2cd, DEATHMSG_TELEFRAG},
1114 {0x020d3f9d, DEATHMSG_FIST},
1115 {0x0004296d, DEATHMSG_GUN},
1116 {0x0010c1fd, DEATHMSG_SHOTGUN},
1117 {0x0211416d, DEATHMSG_MACHGUN},
1118 {0x106f76c2, DEATHMSG_ROCKET},
1119 {0x020f86c2, DEATHMSG_GIBROCKET},
1120 {0x00075264, DEATHMSG_PLASMA},
1121 {0x07904664, DEATHMSG_BFGBALL},
1122 {0xe3a3462f, DEATHMSG_CHAINSAW},
1123 {0x000426ad, DEATHMSG_PLAYUNKNOW},
1124 {0x001cd8c3, DEATHMSG_HELLSLIME},
1125 {0x01f47e6f, DEATHMSG_NUKE},
1126 {0x75e39428, DEATHMSG_SUPHELLSLIME},
1127 {0x01ce59b8, DEATHMSG_SPECUNKNOW},
1128 {0x020e19cd, DEATHMSG_BARRELFRAG},
1129 {0x39b61448, DEATHMSG_BARREL},
1130 {0x021835d2, DEATHMSG_POSSESSED},
1131 {0x0861051f, DEATHMSG_SHOTGUY},
1132 {0x0021255e, DEATHMSG_TROOP},
1133 {0x109d6e18, DEATHMSG_SERGEANT},
1134 {0x10b701a8, DEATHMSG_BRUISER},
1135 {0x00000372, DEATHMSG_DEAD},
1136 {0, 0xFFFF} // last has invalid indirect
1137 };
1138
readtext(myfile_t * f,int len1,int len2)1139 static void readtext(myfile_t* f, int len1, int len2 )
1140 {
1141 char s[2001];
1142 char * str2;
1143 int i;
1144
1145 // it is hard to change all the text in doom
1146 // here I implement only vital things
1147 // yes, Text can change some tables like music, sound and sprite name
1148
1149 if(len1+len2 > 2000)
1150 {
1151 deh_error("Text too big\n");
1152 return;
1153 }
1154
1155 if( myfread(s, len1+len2, f) )
1156 {
1157 if( bex_include_notext )
1158 return; // BEX INCLUDE NOTEXT is active, blocks Text replacements
1159
1160 #if 0
1161 // Debugging trigger
1162 if( strncmp(s,"GREATER RUNES", 13) == 0 )
1163 {
1164 GenPrintf(EMSG_errlog, "Text:%s\n", s);
1165 }
1166 #endif
1167
1168 str2 = &s[len1];
1169 s[len1+len2]='\0';
1170
1171 if( devparm > 1 )
1172 {
1173 // Readable listing of DEH text replacement.
1174 // Make copy of str1 so can terminate string.
1175 int len3 = (len1<999)?len1:999;
1176 char str3[1000];
1177 strncpy( str3, s, len3);
1178 str3[len3] = 0;
1179 GenPrintf(EMSG_errlog, "FROM:%s\nTO:%s\n", str3, str2);
1180 }
1181
1182 if((len1 == 4) && (len2 == 4)) // sprite names are always 4 chars
1183 {
1184 // sprite table
1185 for(i=0;i<NUMSPRITES;i++)
1186 {
1187 if(!strncmp(deh_sprnames[i],s,len1))
1188 {
1189 // May be const string, which will segfault on write
1190 deh_replace_string( &sprnames[i], str2, DRS_name );
1191 return;
1192 }
1193 }
1194 }
1195 if((len1 <= 6) && (len2 <= 6)) // sound effect names limited to 6 chars
1196 {
1197 // these names are strings, so compare them correctly
1198 char str1[8]; // make long enough for 0 term too
1199 strncpy( str1, s, len1 ); // copy name to proper string
1200 str1[len1] = '\0';
1201 // sound table
1202 for(i=0;i<sfx_freeslot0;i++)
1203 {
1204 if(!strcmp(deh_sfxnames[i],str1))
1205 {
1206 // sfx name may be Z_Malloc(7) or a const string
1207 // May be const string, which will segfault on write
1208 deh_replace_string( &S_sfx[i].name, str2, DRS_name );
1209 return;
1210 }
1211 }
1212 // music names limited to 6 chars
1213 // music table
1214 for(i=1;i<NUMMUSIC;i++)
1215 {
1216 if( deh_musicname[i] && (!strcmp(deh_musicname[i], str1)) )
1217 {
1218 // May be const string, which will segfault on write
1219 deh_replace_string( &S_music[i].name, str2, DRS_name );
1220 return;
1221 }
1222 }
1223 }
1224 // Limited by buffer size.
1225 // text table
1226 for(i=0;i<SPECIALDEHACKED;i++)
1227 {
1228 if(!strncmp(deh_text[i],s,len1) && strlen(deh_text[i])==(unsigned)len1)
1229 {
1230 // May be const string, which will segfault on write
1231 deh_replace_string( &text[i], str2, DRS_string );
1232 return;
1233 }
1234 }
1235
1236 // special text : text changed in Legacy but with dehacked support
1237 for(i=SPECIALDEHACKED;i<NUMTEXT;i++)
1238 {
1239 int ltxt = strlen(deh_text[i]);
1240
1241 if(len1>ltxt && strstr(s,deh_text[i]))
1242 {
1243 // found text to be replaced
1244 char *t;
1245
1246 // remove space for center the text
1247 t=&s[len1+len2-1];
1248 while(t[0]==' ') { t[0]='\0'; t--; }
1249 // skip the space
1250 while(s[len1]==' ') len1++;
1251
1252 // remove version string identifier
1253 t=strstr(&(s[len1]),"v%i.%i");
1254 if(!t) {
1255 t=strstr(&(s[len1]),"%i.%i");
1256 if(!t) {
1257 t=strstr(&(s[len1]),"%i");
1258 if(!t) {
1259 t=&s[len1]+strlen(&(s[len1]));
1260 }
1261 }
1262 }
1263 t[0]='\0';
1264 // May be const string, which will segfault on write
1265 deh_replace_string( &text[i], &(s[len1]), DRS_string );
1266 return;
1267 }
1268 }
1269
1270 // [WDJ] Lookup by hash
1271 {
1272 uint32_t hash = 0;
1273 for(i=0; i<len1; i++)
1274 {
1275 if( s[i] >= '0' )
1276 {
1277 if( hash&0x80000000 )
1278 hash ++;
1279 hash <<= 1;
1280 // Prevent using signed char as index.
1281 hash += toupper( (unsigned char)s[i] ) - '0';
1282 }
1283 }
1284 for(i=0;;i++)
1285 {
1286 if( hash_text_table[i].indirect >= NUMTEXT ) break; // not found
1287 if( hash_text_table[i].hash == hash )
1288 {
1289 deh_replace_string( &text[hash_text_table[i].indirect], &(s[len1]), DRS_string );
1290 return;
1291 }
1292 }
1293 if( devparm > 1 )
1294 GenPrintf(EMSG_errlog, "Text hash= 0x%08x :", hash);
1295 }
1296
1297 s[len1]='\0';
1298 deh_error("Text not changed :%s\n",s);
1299 }
1300 }
1301
1302 // [WDJ] 8/27/2011 BEX text strings
1303 typedef struct {
1304 char * kstr;
1305 uint16_t text_num;
1306 } bex_text_t;
1307
1308 // must count entries in bex_string_table
1309 uint16_t bex_string_start_table[3] = { 46+15, 46, 0 }; // start=
1310
1311 // BEX entries from boom202s/boomdeh.txt
1312 bex_text_t bex_string_table[] =
1313 {
1314 // start=3 language changes
1315 // BEX that language may change, but PWAD should not change
1316 { "D_DEVSTR", D_DEVSTR_NUM }, // dev mode
1317 { "D_CDROM", D_CDROM_NUM }, // cdrom version
1318 { "LOADNET", LOADNET_NUM }, // only server can load netgame
1319 { "QLOADNET", QLOADNET_NUM }, // cant quickload
1320 { "QSAVESPOT", QSAVESPOT_NUM }, // no quicksave slot
1321 { "SAVEDEAD", SAVEDEAD_NUM }, // cannot save when not playing
1322 { "QSPROMPT", QSPROMPT_NUM }, // quicksave, has %s
1323 { "QLPROMPT", QLPROMPT_NUM }, // quickload, has %s
1324 { "NEWGAME", NEWGAME_NUM }, // cant start newgame
1325 { "SWSTRING", SWSTRING_NUM }, // shareware version
1326 { "MSGOFF", MSGOFF_NUM },
1327 { "MSGON", MSGON_NUM },
1328 { "NETEND", NETEND_NUM }, // cant end netgame
1329 { "ENDGAME", ENDGAME_NUM }, // want to end game ?
1330 { "DOSY", DOSY_NUM }, // quit to DOS, has %s
1331 { "EMPTYSTRING", EMPTYSTRING_NUM }, // savegame empty slot
1332 { "GGSAVED", GGSAVED_NUM }, // game saved
1333 { "HUSTR_MSGU", HUSTR_MSGU_NUM }, // message not sent
1334 { "HUSTR_MESSAGESENT", HUSTR_MESSAGESENT_NUM }, // message sent
1335 { "AMSTR_FOLLOWON", AMSTR_FOLLOWON_NUM }, // Automap follow
1336 { "AMSTR_FOLLOWOFF", AMSTR_FOLLOWOFF_NUM },
1337 { "AMSTR_GRIDON", AMSTR_GRIDON_NUM }, // Automap grid
1338 { "AMSTR_GRIDOFF", AMSTR_GRIDOFF_NUM },
1339 { "AMSTR_MARKEDSPOT", AMSTR_MARKEDSPOT_NUM }, // Automap marks
1340 { "AMSTR_MARKSCLEARED", AMSTR_MARKSCLEARED_NUM },
1341 { "STSTR_MUS", STSTR_MUS_NUM }, // Music
1342 { "STSTR_NOMUS", STSTR_NOMUS_NUM },
1343 { "STSTR_NCON", STSTR_NCON_NUM }, // No Clip
1344 { "STSTR_NCOFF", STSTR_NCOFF_NUM },
1345 { "STSTR_CLEV", STSTR_CLEV_NUM }, // change level
1346 // BEX not used in DoomLegacy, but have strings
1347 { "DETAILHI", DETAILHI_NUM },
1348 { "DETAILLO", DETAILLO_NUM },
1349 { "GAMMALVL0", GAMMALVL0_NUM },
1350 { "GAMMALVL1", GAMMALVL1_NUM },
1351 { "GAMMALVL2", GAMMALVL2_NUM },
1352 { "GAMMALVL3", GAMMALVL3_NUM },
1353 { "GAMMALVL4", GAMMALVL4_NUM },
1354 // BEX not used in DoomLegacy, but have strings, was only used in define of other strings
1355 { "PRESSKEY", PRESSKEY_NUM },
1356 { "PRESSYN", PRESSYN_NUM },
1357 // BEX not present in DoomLegacy
1358 { "RESTARTLEVEL", 9999 },
1359 { "HUSTR_PLRGREEN", 9999 },
1360 { "HUSTR_PLRINDIGO", 9999 },
1361 { "HUSTR_PLRBROWN", 9999 },
1362 { "HUSTR_PLRRED", 9999 },
1363 { "STSTR_COMPON", 9999 }, // Doom compatibility mode
1364 { "STSTR_COMPOFF",9999 },
1365
1366 // start=2 personal changes
1367 { "HUSTR_CHATMACRO0", HUSTR_CHATMACRO0_NUM },
1368 { "HUSTR_CHATMACRO1", HUSTR_CHATMACRO1_NUM },
1369 { "HUSTR_CHATMACRO2", HUSTR_CHATMACRO2_NUM },
1370 { "HUSTR_CHATMACRO3", HUSTR_CHATMACRO3_NUM },
1371 { "HUSTR_CHATMACRO4", HUSTR_CHATMACRO4_NUM },
1372 { "HUSTR_CHATMACRO5", HUSTR_CHATMACRO5_NUM },
1373 { "HUSTR_CHATMACRO6", HUSTR_CHATMACRO6_NUM },
1374 { "HUSTR_CHATMACRO7", HUSTR_CHATMACRO7_NUM },
1375 { "HUSTR_CHATMACRO8", HUSTR_CHATMACRO8_NUM },
1376 { "HUSTR_CHATMACRO9", HUSTR_CHATMACRO9_NUM },
1377 { "HUSTR_TALKTOSELF1", HUSTR_TALKTOSELF1_NUM },
1378 { "HUSTR_TALKTOSELF2", HUSTR_TALKTOSELF2_NUM },
1379 { "HUSTR_TALKTOSELF3", HUSTR_TALKTOSELF3_NUM },
1380 { "HUSTR_TALKTOSELF4", HUSTR_TALKTOSELF4_NUM },
1381 { "HUSTR_TALKTOSELF5", HUSTR_TALKTOSELF5_NUM },
1382
1383 // start=0 normal game changes
1384 { "QUITMSG", QUITMSG_NUM },
1385 { "NIGHTMARE", NIGHTMARE_NUM },
1386 { "GOTARMOR", GOTARMOR_NUM },
1387 { "GOTMEGA", GOTMEGA_NUM },
1388 { "GOTHTHBONUS", GOTHTHBONUS_NUM },
1389 { "GOTARMBONUS", GOTARMBONUS_NUM },
1390 { "GOTSTIM", GOTSTIM_NUM },
1391 { "GOTMEDINEED", GOTMEDINEED_NUM },
1392 { "GOTMEDIKIT", GOTMEDIKIT_NUM },
1393 { "GOTSUPER", GOTSUPER_NUM },
1394 { "GOTBLUECARD", GOTBLUECARD_NUM },
1395 { "GOTYELWCARD", GOTYELWCARD_NUM },
1396 { "GOTREDCARD", GOTREDCARD_NUM },
1397 { "GOTBLUESKUL", GOTBLUESKUL_NUM },
1398 { "GOTYELWSKUL", GOTYELWSKUL_NUM },
1399 { "GOTREDSKULL", GOTREDSKULL_NUM },
1400 { "GOTINVUL", GOTINVUL_NUM },
1401 { "GOTBERSERK", GOTBERSERK_NUM },
1402 { "GOTINVIS", GOTINVIS_NUM },
1403 { "GOTSUIT", GOTSUIT_NUM },
1404 { "GOTMAP", GOTMAP_NUM },
1405 { "GOTVISOR", GOTVISOR_NUM },
1406 { "GOTMSPHERE", GOTMSPHERE_NUM },
1407 { "GOTCLIP", GOTCLIP_NUM },
1408 { "GOTCLIPBOX", GOTCLIPBOX_NUM },
1409 { "GOTROCKET", GOTROCKET_NUM },
1410 { "GOTROCKBOX", GOTROCKBOX_NUM },
1411 { "GOTCELL", GOTCELL_NUM },
1412 { "GOTCELLBOX", GOTCELLBOX_NUM },
1413 { "GOTSHELLS", GOTSHELLS_NUM },
1414 { "GOTSHELLBOX", GOTSHELLBOX_NUM },
1415 { "GOTBACKPACK", GOTBACKPACK_NUM },
1416 { "GOTBFG9000", GOTBFG9000_NUM },
1417 { "GOTCHAINGUN", GOTCHAINGUN_NUM },
1418 { "GOTCHAINSAW", GOTCHAINSAW_NUM },
1419 { "GOTLAUNCHER", GOTLAUNCHER_NUM },
1420 { "GOTPLASMA", GOTPLASMA_NUM },
1421 { "GOTSHOTGUN", GOTSHOTGUN_NUM },
1422 { "GOTSHOTGUN2", GOTSHOTGUN2_NUM },
1423 { "PD_BLUEO", PD_BLUEO_NUM },
1424 { "PD_REDO", PD_REDO_NUM },
1425 { "PD_YELLOWO", PD_YELLOWO_NUM },
1426 { "PD_BLUEK", PD_BLUEK_NUM },
1427 { "PD_REDK", PD_REDK_NUM },
1428 { "PD_YELLOWK", PD_YELLOWK_NUM },
1429 { "PD_BLUEC", PD_BLUEC_NUM },
1430 { "PD_REDC", PD_REDC_NUM },
1431 { "PD_YELLOWC", PD_YELLOWC_NUM },
1432 { "PD_BLUES", PD_BLUES_NUM },
1433 { "PD_REDS", PD_REDS_NUM },
1434 { "PD_YELLOWS", PD_YELLOWS_NUM },
1435 { "PD_ANY", PD_ANY_NUM },
1436 { "PD_ALL3", PD_ALL3_NUM },
1437 { "PD_ALL6", PD_ALL6_NUM },
1438 { "HUSTR_MSGU", HUSTR_MSGU_NUM },
1439 { "HUSTR_E1M1", HUSTR_E1M1_NUM },
1440 { "HUSTR_E1M2", HUSTR_E1M2_NUM },
1441 { "HUSTR_E1M3", HUSTR_E1M3_NUM },
1442 { "HUSTR_E1M4", HUSTR_E1M4_NUM },
1443 { "HUSTR_E1M5", HUSTR_E1M5_NUM },
1444 { "HUSTR_E1M6", HUSTR_E1M6_NUM },
1445 { "HUSTR_E1M7", HUSTR_E1M7_NUM },
1446 { "HUSTR_E1M8", HUSTR_E1M8_NUM },
1447 { "HUSTR_E1M9", HUSTR_E1M9_NUM },
1448 { "HUSTR_E2M1", HUSTR_E2M1_NUM },
1449 { "HUSTR_E2M2", HUSTR_E2M2_NUM },
1450 { "HUSTR_E2M3", HUSTR_E2M3_NUM },
1451 { "HUSTR_E2M4", HUSTR_E2M4_NUM },
1452 { "HUSTR_E2M5", HUSTR_E2M5_NUM },
1453 { "HUSTR_E2M6", HUSTR_E2M6_NUM },
1454 { "HUSTR_E2M7", HUSTR_E2M7_NUM },
1455 { "HUSTR_E2M8", HUSTR_E2M8_NUM },
1456 { "HUSTR_E2M9", HUSTR_E2M9_NUM },
1457 { "HUSTR_E3M1", HUSTR_E3M1_NUM },
1458 { "HUSTR_E3M2", HUSTR_E3M2_NUM },
1459 { "HUSTR_E3M3", HUSTR_E3M3_NUM },
1460 { "HUSTR_E3M4", HUSTR_E3M4_NUM },
1461 { "HUSTR_E3M5", HUSTR_E3M5_NUM },
1462 { "HUSTR_E3M6", HUSTR_E3M6_NUM },
1463 { "HUSTR_E3M7", HUSTR_E3M7_NUM },
1464 { "HUSTR_E3M8", HUSTR_E3M8_NUM },
1465 { "HUSTR_E3M9", HUSTR_E3M9_NUM },
1466 { "HUSTR_E4M1", HUSTR_E4M1_NUM },
1467 { "HUSTR_E4M2", HUSTR_E4M2_NUM },
1468 { "HUSTR_E4M3", HUSTR_E4M3_NUM },
1469 { "HUSTR_E4M4", HUSTR_E4M4_NUM },
1470 { "HUSTR_E4M5", HUSTR_E4M5_NUM },
1471 { "HUSTR_E4M6", HUSTR_E4M6_NUM },
1472 { "HUSTR_E4M7", HUSTR_E4M7_NUM },
1473 { "HUSTR_E4M8", HUSTR_E4M8_NUM },
1474 { "HUSTR_E4M9", HUSTR_E4M9_NUM },
1475 { "HUSTR_1", HUSTR_1_NUM },
1476 { "HUSTR_2", HUSTR_2_NUM },
1477 { "HUSTR_3", HUSTR_3_NUM },
1478 { "HUSTR_4", HUSTR_4_NUM },
1479 { "HUSTR_5", HUSTR_5_NUM },
1480 { "HUSTR_6", HUSTR_6_NUM },
1481 { "HUSTR_7", HUSTR_7_NUM },
1482 { "HUSTR_8", HUSTR_8_NUM },
1483 { "HUSTR_9", HUSTR_9_NUM },
1484 { "HUSTR_10", HUSTR_10_NUM },
1485 { "HUSTR_11", HUSTR_11_NUM },
1486 { "HUSTR_12", HUSTR_12_NUM },
1487 { "HUSTR_13", HUSTR_13_NUM },
1488 { "HUSTR_14", HUSTR_14_NUM },
1489 { "HUSTR_15", HUSTR_15_NUM },
1490 { "HUSTR_16", HUSTR_16_NUM },
1491 { "HUSTR_17", HUSTR_17_NUM },
1492 { "HUSTR_18", HUSTR_18_NUM },
1493 { "HUSTR_19", HUSTR_19_NUM },
1494 { "HUSTR_20", HUSTR_20_NUM },
1495 { "HUSTR_21", HUSTR_21_NUM },
1496 { "HUSTR_22", HUSTR_22_NUM },
1497 { "HUSTR_23", HUSTR_23_NUM },
1498 { "HUSTR_24", HUSTR_24_NUM },
1499 { "HUSTR_25", HUSTR_25_NUM },
1500 { "HUSTR_26", HUSTR_26_NUM },
1501 { "HUSTR_27", HUSTR_27_NUM },
1502 { "HUSTR_28", HUSTR_28_NUM },
1503 { "HUSTR_29", HUSTR_29_NUM },
1504 { "HUSTR_30", HUSTR_30_NUM },
1505 { "HUSTR_31", HUSTR_31_NUM },
1506 { "HUSTR_32", HUSTR_32_NUM },
1507 { "PHUSTR_1", PHUSTR_1_NUM },
1508 { "PHUSTR_2", PHUSTR_2_NUM },
1509 { "PHUSTR_3", PHUSTR_3_NUM },
1510 { "PHUSTR_4", PHUSTR_4_NUM },
1511 { "PHUSTR_5", PHUSTR_5_NUM },
1512 { "PHUSTR_6", PHUSTR_6_NUM },
1513 { "PHUSTR_7", PHUSTR_7_NUM },
1514 { "PHUSTR_8", PHUSTR_8_NUM },
1515 { "PHUSTR_9", PHUSTR_9_NUM },
1516 { "PHUSTR_10", PHUSTR_10_NUM },
1517 { "PHUSTR_11", PHUSTR_11_NUM },
1518 { "PHUSTR_12", PHUSTR_12_NUM },
1519 { "PHUSTR_13", PHUSTR_13_NUM },
1520 { "PHUSTR_14", PHUSTR_14_NUM },
1521 { "PHUSTR_15", PHUSTR_15_NUM },
1522 { "PHUSTR_16", PHUSTR_16_NUM },
1523 { "PHUSTR_17", PHUSTR_17_NUM },
1524 { "PHUSTR_18", PHUSTR_18_NUM },
1525 { "PHUSTR_19", PHUSTR_19_NUM },
1526 { "PHUSTR_20", PHUSTR_20_NUM },
1527 { "PHUSTR_21", PHUSTR_21_NUM },
1528 { "PHUSTR_22", PHUSTR_22_NUM },
1529 { "PHUSTR_23", PHUSTR_23_NUM },
1530 { "PHUSTR_24", PHUSTR_24_NUM },
1531 { "PHUSTR_25", PHUSTR_25_NUM },
1532 { "PHUSTR_26", PHUSTR_26_NUM },
1533 { "PHUSTR_27", PHUSTR_27_NUM },
1534 { "PHUSTR_28", PHUSTR_28_NUM },
1535 { "PHUSTR_29", PHUSTR_29_NUM },
1536 { "PHUSTR_30", PHUSTR_30_NUM },
1537 { "PHUSTR_31", PHUSTR_31_NUM },
1538 { "PHUSTR_32", PHUSTR_32_NUM },
1539 { "THUSTR_1", THUSTR_1_NUM },
1540 { "THUSTR_2", THUSTR_2_NUM },
1541 { "THUSTR_3", THUSTR_3_NUM },
1542 { "THUSTR_4", THUSTR_4_NUM },
1543 { "THUSTR_5", THUSTR_5_NUM },
1544 { "THUSTR_6", THUSTR_6_NUM },
1545 { "THUSTR_7", THUSTR_7_NUM },
1546 { "THUSTR_8", THUSTR_8_NUM },
1547 { "THUSTR_9", THUSTR_9_NUM },
1548 { "THUSTR_10", THUSTR_10_NUM },
1549 { "THUSTR_11", THUSTR_11_NUM },
1550 { "THUSTR_12", THUSTR_12_NUM },
1551 { "THUSTR_13", THUSTR_13_NUM },
1552 { "THUSTR_14", THUSTR_14_NUM },
1553 { "THUSTR_15", THUSTR_15_NUM },
1554 { "THUSTR_16", THUSTR_16_NUM },
1555 { "THUSTR_17", THUSTR_17_NUM },
1556 { "THUSTR_18", THUSTR_18_NUM },
1557 { "THUSTR_19", THUSTR_19_NUM },
1558 { "THUSTR_20", THUSTR_20_NUM },
1559 { "THUSTR_21", THUSTR_21_NUM },
1560 { "THUSTR_22", THUSTR_22_NUM },
1561 { "THUSTR_23", THUSTR_23_NUM },
1562 { "THUSTR_24", THUSTR_24_NUM },
1563 { "THUSTR_25", THUSTR_25_NUM },
1564 { "THUSTR_26", THUSTR_26_NUM },
1565 { "THUSTR_27", THUSTR_27_NUM },
1566 { "THUSTR_28", THUSTR_28_NUM },
1567 { "THUSTR_29", THUSTR_29_NUM },
1568 { "THUSTR_30", THUSTR_30_NUM },
1569 { "THUSTR_31", THUSTR_31_NUM },
1570 { "THUSTR_32", THUSTR_32_NUM },
1571
1572 { "E1TEXT", E1TEXT_NUM },
1573 { "E2TEXT", E2TEXT_NUM },
1574 { "E3TEXT", E3TEXT_NUM },
1575 { "E4TEXT", E4TEXT_NUM },
1576 { "C1TEXT", C1TEXT_NUM },
1577 { "C2TEXT", C2TEXT_NUM },
1578 { "C3TEXT", C3TEXT_NUM },
1579 { "C4TEXT", C4TEXT_NUM },
1580 { "C5TEXT", C5TEXT_NUM },
1581 { "C6TEXT", C6TEXT_NUM },
1582 { "P1TEXT", P1TEXT_NUM },
1583 { "P2TEXT", P2TEXT_NUM },
1584 { "P3TEXT", P3TEXT_NUM },
1585 { "P4TEXT", P4TEXT_NUM },
1586 { "P5TEXT", P5TEXT_NUM },
1587 { "P6TEXT", P6TEXT_NUM },
1588 { "T1TEXT", T1TEXT_NUM },
1589 { "T2TEXT", T2TEXT_NUM },
1590 { "T3TEXT", T3TEXT_NUM },
1591 { "T4TEXT", T4TEXT_NUM },
1592 { "T5TEXT", T5TEXT_NUM },
1593 { "T6TEXT", T6TEXT_NUM },
1594 { "STSTR_DQDON", STSTR_DQDON_NUM }, // Invincible
1595 { "STSTR_DQDOFF", STSTR_DQDOFF_NUM },
1596 { "STSTR_FAADDED", STSTR_FAADDED_NUM }, // Full Ammo
1597 { "STSTR_KFAADDED", STSTR_KFAADDED_NUM }, // Full Ammo Keys
1598 { "STSTR_BEHOLD", STSTR_BEHOLD_NUM }, // Power-up
1599 { "STSTR_BEHOLDX", STSTR_BEHOLDX_NUM }, // Power-up toggle
1600 { "STSTR_CHOPPERS", STSTR_CHOPPERS_NUM }, // Chainsaw
1601
1602 { "CC_ZOMBIE", CC_ZOMBIE_NUM },
1603 { "CC_SHOTGUN", CC_SHOTGUN_NUM },
1604 { "CC_HEAVY", CC_HEAVY_NUM },
1605 { "CC_IMP", CC_IMP_NUM },
1606 { "CC_DEMON", CC_DEMON_NUM },
1607 { "CC_LOST", CC_LOST_NUM },
1608 { "CC_CACO", CC_CACO_NUM },
1609 { "CC_HELL", CC_HELL_NUM },
1610 { "CC_BARON", CC_BARON_NUM },
1611 { "CC_ARACH", CC_ARACH_NUM },
1612 { "CC_PAIN", CC_PAIN_NUM },
1613 { "CC_REVEN", CC_REVEN_NUM },
1614 { "CC_MANCU", CC_MANCU_NUM },
1615 { "CC_ARCH", CC_ARCH_NUM },
1616 { "CC_SPIDER", CC_SPIDER_NUM },
1617 { "CC_CYBER", CC_CYBER_NUM },
1618 { "CC_HERO", CC_HERO_NUM },
1619
1620 { "BGFLATE1", BGFLATE1_NUM },
1621 { "BGFLATE2", BGFLATE2_NUM },
1622 { "BGFLATE3", BGFLATE3_NUM },
1623 { "BGFLATE4", BGFLATE4_NUM },
1624 { "BGFLAT06", BGFLAT06_NUM },
1625 { "BGFLAT11", BGFLAT11_NUM },
1626 { "BGFLAT20", BGFLAT20_NUM },
1627 { "BGFLAT30", BGFLAT30_NUM },
1628 { "BGFLAT15", BGFLAT15_NUM },
1629 { "BGFLAT31", BGFLAT31_NUM },
1630 #ifdef BEX_SAVEGAMENAME
1631 { "SAVEGAMENAME", SAVEGAMENAME_NUM }, // [WDJ] Added 9/5/2011
1632 #else
1633 { "SAVEGAMENAME", 9998 }, // [WDJ] Do not allow, because of security risk
1634 #endif
1635
1636 // BEX not present in DoomLegacy
1637 { "BGCASTCALL", 9998 },
1638 { "STARTUP1", 9998 },
1639 { "STARTUP2", 9998 },
1640 { "STARTUP3", 9998 },
1641 { "STARTUP4", 9998 },
1642 { "STARTUP5", 9998 },
1643
1644 { NULL, 0 } // table term
1645 };
1646
1647
1648 #define BEX_MAX_STRING_LEN 2000
1649 #define BEX_KEYW_LEN 20
1650
1651 // BEX [STRINGS] section
1652 // permission: 0=game, 1=adv, 2=language
bex_strings(myfile_t * f,byte bex_permission)1653 static void bex_strings( myfile_t* f, byte bex_permission )
1654 {
1655 char stxt[BEX_MAX_STRING_LEN+1];
1656 char keyw[BEX_KEYW_LEN];
1657 char sb[MAXLINELEN];
1658 char * stp;
1659 char * word;
1660 char * cp;
1661 int perm_min = bex_string_start_table[bex_permission];
1662 int i;
1663
1664 // string format, no quotes:
1665 // [STRINGS]
1666 // #comment, ** Maybe ** comment within replacement string
1667 // <keyw> = <text>
1668 // <keyw> = <text> backslash
1669 // <text> backslash
1670 // <text>
1671
1672 for(;;) {
1673 if( ! myfgets_nocom(sb, sizeof(sb), f) ) // get line, skipping comments
1674 break; // no more lines
1675 if( sb[0] == '\n' ) continue; // blank line
1676 if( sb[0] == 0 ) break;
1677 cp = strchr(sb,'='); // find after =
1678 word=strtok(sb," ");
1679 if( ! word ) break;
1680 strncpy( keyw, word, BEX_KEYW_LEN-1 ); // because continuation lines use sb
1681 keyw[BEX_KEYW_LEN-1] = '\0';
1682 // Limited by buffer size.
1683 if( cp == NULL ) goto no_text_change;
1684 cp++; // skip '='
1685 stxt[BEX_MAX_STRING_LEN] = '\0'; // protection
1686 stp = &stxt[0];
1687 // Get the new text
1688 do {
1689 while( *cp == ' ' || *cp == '\t' ) cp++; // skip leading space
1690 if( *cp == '\n' ) break; // blank line
1691 if( *cp == 0 ) break;
1692 if( *cp == '\"' ) cp++; // skip leading double quote
1693 while( *cp )
1694 { // copy text upto CR
1695 if( *cp == '\n' ) break;
1696 *stp++ = *cp++;
1697 if( stp >= &stxt[BEX_MAX_STRING_LEN] ) break;
1698 }
1699 // remove trailing space
1700 while( stp > stxt && stp[-1] == ' ')
1701 stp --;
1702 // test for continuation line
1703 if( ! (stp > stxt && stp[-1] == '\\') )
1704 break; // no backslash continuation
1705 stp--; // remove backslash
1706 if( stp > stxt && stp[-1] == '\"' )
1707 stp --; // remove trailing doublequote
1708 // get continuation line to sb, skipping comments.
1709 // [WDJ] questionable, but boom202 code skips comments between continuation lines.
1710 if( ! myfgets_nocom(sb, sizeof(sb), f) )
1711 break; // no more lines
1712 cp = &sb[0];
1713 } while ( *cp );
1714 if( stp > stxt && stp[-1] == '\"' )
1715 stp --; // remove trailing doublequote
1716 *stp++ = '\0'; // term BEX replacement string in stxt
1717
1718 // search text table for keyw
1719 for(i=0; ;i++)
1720 {
1721 if( bex_string_table[i].kstr == NULL ) goto no_text_change;
1722 if(!strcmp(bex_string_table[i].kstr, keyw)) // BEX keyword search
1723 {
1724 int text_index = bex_string_table[i].text_num;
1725 #ifdef BEX_SAVEGAMENAME
1726 // protect file names against attack
1727 if( i == SAVEGAMENAME_NUM )
1728 {
1729 if( filename_reject( stxt, 10 ) ) goto no_text_change;
1730 }
1731 #endif
1732 if( i >= perm_min && text_index < NUMTEXT)
1733 {
1734 // May be const string, which will segfault on write
1735 deh_replace_string( &text[text_index], stxt, DRS_string );
1736 }
1737 else
1738 {
1739 // change blocked, but not an error
1740 }
1741 goto next_keyw;
1742 }
1743 }
1744 no_text_change:
1745 deh_error("Text not changed :%s\n", keyw);
1746
1747 next_keyw:
1748 continue;
1749 }
1750 }
1751
1752 // BEX [PARS] section
bex_pars(myfile_t * f)1753 static void bex_pars( myfile_t* f )
1754 {
1755 char s[MAXLINELEN];
1756 int episode, level, partime;
1757 int nn;
1758
1759 // format:
1760 // [PARS]
1761 // par <episode> <level> <seconds>
1762 // par <map_level> <seconds>
1763
1764 for(;;) {
1765 if( ! myfgets_nocom(s, sizeof(s), f) )
1766 break; // no more lines
1767 if( s[0] == '\n' ) continue; // blank line
1768 if( s[0] == 0 ) break;
1769 if( strncasecmp( s, "par", 3 ) != 0 ) break; // not a par line
1770 nn = sscanf( &s[3], " %i %i %i", &episode, &level, &partime );
1771 if( nn == 3 )
1772 { // Doom1 Episode, level, time format
1773 if( (episode < 1) || (episode > 3) || (level < 1) || (level > 9) )
1774 deh_error( "Bad par E%dM%d\n", episode, level );
1775 else
1776 {
1777 pars[episode][level] = partime;
1778 pars_valid_bex = true;
1779 }
1780 }
1781 else if( nn == 2 )
1782 { // Doom2 map, time format
1783 partime = level;
1784 level = episode;
1785 if( (level < 1) || (level > 32))
1786 deh_error( "Bad PAR MAP%d\n", level );
1787 else
1788 {
1789 cpars[level-1] = partime;
1790 pars_valid_bex = true;
1791 }
1792 }
1793 else
1794 deh_error( "Invalid par format\n" );
1795 }
1796 }
1797
1798
1799 // [WDJ] BEX codeptr strings and function
1800 typedef struct {
1801 const char * kstr;
1802 actionf_t action; // union of action ptrs
1803 } PACKED_ATTR bex_codeptr_t;
1804
1805 // BEX entries from boom202s/boomdeh.txt
1806 bex_codeptr_t bex_action_table[] = {
1807 {"NULL", {NULL}}, // to clear a ptr
1808 {"Light0", {A_Light0}},
1809 {"WeaponReady", {A_WeaponReady}},
1810 {"Lower", {A_Lower}},
1811 {"Raise", {A_Raise}},
1812 {"Punch", {A_Punch}},
1813 {"ReFire", {A_ReFire}},
1814 {"FirePistol", {A_FirePistol}},
1815 {"Light1", {A_Light1}},
1816 {"FireShotgun", {A_FireShotgun}},
1817 {"Light2", {A_Light2}},
1818 {"FireShotgun2", {A_FireShotgun2}},
1819 {"CheckReload", {A_CheckReload}},
1820 {"OpenShotgun2", {A_OpenShotgun2}},
1821 {"LoadShotgun2", {A_LoadShotgun2}},
1822 {"CloseShotgun2", {A_CloseShotgun2}},
1823 {"FireCGun", {A_FireCGun}},
1824 {"GunFlash", {A_GunFlash}},
1825 {"FireMissile", {A_FireMissile}},
1826 {"Saw", {A_Saw}},
1827 {"FirePlasma", {A_FirePlasma}},
1828 {"BFGsound", {A_BFGsound}},
1829 {"FireBFG", {A_FireBFG}},
1830 {"BFGSpray", {A_BFGSpray}},
1831 {"Explode", {A_Explode}},
1832 {"Pain", {A_Pain}},
1833 {"PlayerScream", {A_PlayerScream}},
1834 {"Fall", {A_Fall}},
1835 {"XScream", {A_XScream}},
1836 {"Look", {A_Look}},
1837 {"Chase", {A_Chase}},
1838 {"FaceTarget", {A_FaceTarget}},
1839 {"PosAttack", {A_PosAttack}},
1840 {"Scream", {A_Scream}},
1841 {"SPosAttack", {A_SPosAttack}},
1842 {"VileChase", {A_VileChase}},
1843 {"VileStart", {A_VileStart}},
1844 {"VileTarget", {A_VileTarget}},
1845 {"VileAttack", {A_VileAttack}},
1846 {"StartFire", {A_StartFire}},
1847 {"Fire", {A_Fire}},
1848 {"FireCrackle", {A_FireCrackle}},
1849 {"Tracer", {A_Tracer}},
1850 {"SkelWhoosh", {A_SkelWhoosh}},
1851 {"SkelFist", {A_SkelFist}},
1852 {"SkelMissile", {A_SkelMissile}},
1853 {"FatRaise", {A_FatRaise}},
1854 {"FatAttack1", {A_FatAttack1}},
1855 {"FatAttack2", {A_FatAttack2}},
1856 {"FatAttack3", {A_FatAttack3}},
1857 {"BossDeath", {A_BossDeath}},
1858 {"CPosAttack", {A_CPosAttack}},
1859 {"CPosRefire", {A_CPosRefire}},
1860 {"TroopAttack", {A_TroopAttack}},
1861 {"SargAttack", {A_SargAttack}},
1862 {"HeadAttack", {A_HeadAttack}},
1863 {"BruisAttack", {A_BruisAttack}},
1864 {"SkullAttack", {A_SkullAttack}},
1865 {"Metal", {A_Metal}},
1866 {"SpidRefire", {A_SpidRefire}},
1867 {"BabyMetal", {A_BabyMetal}},
1868 {"BspiAttack", {A_BspiAttack}},
1869 {"Hoof", {A_Hoof}},
1870 {"CyberAttack", {A_CyberAttack}},
1871 {"PainAttack", {A_PainAttack}},
1872 {"PainDie", {A_PainDie}},
1873 {"KeenDie", {A_KeenDie}},
1874 {"BrainPain", {A_BrainPain}},
1875 {"BrainScream", {A_BrainScream}},
1876 {"BrainDie", {A_BrainDie}},
1877 {"BrainAwake", {A_BrainAwake}},
1878 {"BrainSpit", {A_BrainSpit}},
1879 {"SpawnSound", {A_SpawnSound}},
1880 {"SpawnFly", {A_SpawnFly}},
1881 {"BrainExplode", {A_BrainExplode}},
1882
1883 // [WDJ] MBF function ptrs, from MBF, EternityEngine.
1884 {"Detonate", {A_Detonate}}, // Radius damage, variable damage
1885 {"Mushroom", {A_Mushroom}}, // Mushroom explosion
1886 {"Die", {A_Die}}, // MBF, kill an object
1887 {"Spawn", {A_Spawn}}, // SpawnMobj(x,y, parm2, parm1-1)
1888 {"Turn", {A_Turn}}, // Turn by parm1 degrees
1889 {"Face", {A_Face}}, // Turn to face parm1 degrees
1890 {"Scratch", {A_Scratch}}, // Melee attack
1891 {"PlaySound", {A_PlaySound}}, // Play Sound parm1
1892 // if parm2 = 0 then mobj sound, else unassociated sound.
1893 {"RandomJump", {A_RandomJump}}, // Random transition to mobj state parm1
1894 // probability parm2
1895 {"LineEffect", {A_LineEffect}}, // Trigger line type parm1, tag = parm2
1896
1897 {"KeepChasing", {A_KeepChasing}}, // MBF, from EnternityEngine
1898
1899 #if 0
1900 // [WDJ] Code pointers that are BEX available in EternityEngine.
1901 {"Nailbomb", {A_Nailbomb}},
1902 #endif
1903
1904 #if 0
1905 // haleyjd: start new eternity codeptrs
1906 {"StartScript", {A_StartScript}},
1907 {"PlayerStartScript", {A_PlayerStartScript}},
1908 {"SetFlags", {A_SetFlags}},
1909 {"UnSetFlags", {A_UnSetFlags}},
1910 {"BetaSkullAttack", {A_BetaSkullAttack}},
1911 {"GenRefire", {A_GenRefire}},
1912 {"FireGrenade", {A_FireGrenade}},
1913 {"FireCustomBullets", {A_FireCustomBullets}},
1914 {"FirePlayerMissile", {A_FirePlayerMissile}},
1915 {"CustomPlayerMelee", {A_CustomPlayerMelee}},
1916 {"GenTracer", {A_GenTracer}},
1917 {"BFG11KHit", {A_BFG11KHit}},
1918 {"BouncingBFG", {A_BouncingBFG}},
1919 {"BFGBurst", {A_BFGBurst}},
1920 {"FireOldBFG", {A_FireOldBFG}},
1921 {"Stop", {A_Stop}},
1922 {"PlayerThunk", {A_PlayerThunk}},
1923 {"MissileAttack", {A_MissileAttack}},
1924 {"MissileSpread", {A_MissileSpread}},
1925 {"BulletAttack", {A_BulletAttack}},
1926 {"HealthJump", {A_HealthJump}},
1927 {"CounterJump", {A_CounterJump}},
1928 {"CounterSwitch", {A_CounterSwitch}},
1929 {"SetCounter", {A_SetCounter}},
1930 {"CopyCounter", {A_CopyCounter}},
1931 {"CounterOp", {A_CounterOp}},
1932 {"SetTics", {A_SetTics}},
1933 {"AproxDistance", {A_AproxDistance}},
1934 {"ShowMessage", {A_ShowMessage}},
1935 {"RandomWalk", {A_RandomWalk}},
1936 {"TargetJump", {A_TargetJump}},
1937 {"ThingSummon", {A_ThingSummon}},
1938 {"KillChildren", {A_KillChildren}},
1939 {"WeaponCtrJump", {A_WeaponCtrJump}},
1940 {"WeaponCtrSwitch", {A_WeaponCtrSwitch}},
1941 {"WeaponSetCtr", {A_WeaponSetCtr}},
1942 {"WeaponCopyCtr", {A_WeaponCopyCtr}},
1943 {"WeaponCtrOp", {A_WeaponCtrOp}},
1944 {"AmbientThinker", {A_AmbientThinker}},
1945 {"SteamSpawn", {A_SteamSpawn}},
1946 {"EjectCasing", {A_EjectCasing}},
1947 {"CasingThrust", {A_CasingThrust}},
1948 {"JumpIfNoAmmo", {A_JumpIfNoAmmo}},
1949 {"CheckReloadEx", {A_CheckReloadEx}},
1950 #endif
1951
1952 #if 0
1953 // haleyjd 07/13/03: nuke specials
1954 {"PainNukeSpec", {A_PainNukeSpec}},
1955 {"SorcNukeSpec", {A_SorcNukeSpec}},
1956 #endif
1957
1958 // haleyjd: Heretic pointers
1959 // Unique to EternityEngine, parameterized.
1960 // {"SpawnGlitter", {A_SpawnGlitter}},
1961 // {"AccelGlitter", {A_AccelGlitter}},
1962 // {"SpawnAbove", {A_SpawnAbove}},
1963 // {"HticDrop", {A_HticDrop}}, // Deprecated
1964 // {"HticTracer", {A_HticTracer}}, // parameterized
1965 // {"HticExplode", {A_HticExplode}},
1966 // {"HticBossDeath", {A_HticBossDeath}},
1967 // Heretic normal
1968 {"MummyAttack", {A_MummyAttack}},
1969 {"MummyAttack2", {A_MummyAttack2}},
1970 {"MummySoul", {A_MummySoul}},
1971 {"ClinkAttack", {A_ClinkAttack}},
1972 {"BlueSpark", {A_BlueSpark}},
1973 {"GenWizard", {A_GenWizard}},
1974 {"WizardAtk1", {A_WizAtk1}},
1975 {"WizardAtk2", {A_WizAtk2}},
1976 {"WizardAtk3", {A_WizAtk3}},
1977 {"SorcererRise", {A_SorcererRise}},
1978 {"Srcr1Attack", {A_Srcr1Attack}},
1979 {"Srcr2Decide", {A_Srcr2Decide}},
1980 {"Srcr2Attack", {A_Srcr2Attack}},
1981 {"Sor1Chase", {A_Sor1Chase}},
1982 {"Sor1Pain", {A_Sor1Pain}},
1983 {"Sor2DthInit", {A_Sor2DthInit}},
1984 {"Sor2DthLoop", {A_Sor2DthLoop}},
1985 {"PodPain", {A_PodPain}},
1986 {"RemovePod", {A_RemovePod}},
1987 {"MakePod", {A_MakePod}},
1988 {"KnightAttack", {A_KnightAttack}},
1989 {"DripBlood", {A_DripBlood}},
1990 {"BeastAttack", {A_BeastAttack}},
1991 {"BeastPuff", {A_BeastPuff}},
1992 {"SnakeAttack", {A_SnakeAttack}},
1993 {"SnakeAttack2", {A_SnakeAttack2}},
1994 {"VolcanoBlast", {A_VolcanoBlast}},
1995 {"VolcBallImpact", {A_VolcBallImpact}},
1996 {"MinotaurDecide", {A_MinotaurDecide}},
1997 {"MinotaurAtk1", {A_MinotaurAtk1}},
1998 {"MinotaurAtk2", {A_MinotaurAtk2}},
1999 {"MinotaurAtk3", {A_MinotaurAtk3}},
2000 {"MinotaurCharge", {A_MinotaurCharge}},
2001 {"MntrFloorFire", {A_MntrFloorFire}},
2002 // {"LichFire", {A_LichFire}},
2003 // {"LichWhirlwind", {A_LichWhirlwind}},
2004 {"LichAttack", {A_HHeadAttack}},
2005 {"WhirlwindSeek", {A_WhirlwindSeek}},
2006 {"LichIceImpact", {A_HeadIceImpact}},
2007 {"LichFireGrow", {A_HeadFireGrow}},
2008 // {"ImpChargeAtk", {A_ImpChargeAtk}},
2009 {"ImpMeleeAtk", {A_ImpMeAttack}},
2010 {"ImpMissileAtk", {A_ImpMsAttack}}, // Boss Imp
2011 {"ImpDeath", {A_ImpDeath}},
2012 {"ImpXDeath1", {A_ImpXDeath1}},
2013 {"ImpXDeath2", {A_ImpXDeath2}},
2014 {"ImpExplode", {A_ImpExplode}},
2015 {"PhoenixPuff", {A_PhoenixPuff}},
2016 // {"PlayerSkull", {A_PlayerSkull}},
2017 // {"ClearSkin", {A_ClearSkin}},
2018
2019 #if 0
2020 // haleyjd 10/04/08: Hexen pointers
2021 {"SetInvulnerable", {A_SetInvulnerable}},
2022 {"UnSetInvulnerable", {A_UnSetInvulnerable}},
2023 {"SetReflective", {A_SetReflective}},
2024 {"UnSetReflective", {A_UnSetReflective}},
2025 {"PigLook", {A_PigLook}},
2026 {"PigChase", {A_PigChase}},
2027 {"PigAttack", {A_PigAttack}},
2028 {"PigPain", {A_PigPain}},
2029 {"HexenExplode", {A_HexenExplode}},
2030 {"SerpentUnHide", {A_SerpentUnHide}},
2031 {"SerpentHide", {A_SerpentHide}},
2032 {"SerpentChase", {A_SerpentChase}},
2033 {"RaiseFloorClip", {A_RaiseFloorClip}},
2034 {"LowerFloorClip", {A_LowerFloorClip}},
2035 {"SerpentHumpDecide", {A_SerpentHumpDecide}},
2036 {"SerpentWalk", {A_SerpentWalk}},
2037 {"SerpentCheckForAttack", {A_SerpentCheckForAttack}},
2038 {"SerpentChooseAttack", {A_SerpentChooseAttack}},
2039 {"SerpentMeleeAttack", {A_SerpentMeleeAttack}},
2040 {"SerpentMissileAttack", {A_SerpentMissileAttack}},
2041 {"SerpentSpawnGibs", {A_SerpentSpawnGibs}},
2042 {"SubTics", {A_SubTics}},
2043 {"SerpentHeadCheck", {A_SerpentHeadCheck}},
2044 {"CentaurAttack", {A_CentaurAttack}},
2045 {"CentaurAttack2", {A_CentaurAttack2}},
2046 {"DropEquipment", {A_DropEquipment}},
2047 {"CentaurDefend", {A_CentaurDefend}},
2048 {"BishopAttack", {A_BishopAttack}},
2049 {"BishopAttack2", {A_BishopAttack2}},
2050 {"BishopMissileWeave", {A_BishopMissileWeave}},
2051 {"BishopDoBlur", {A_BishopDoBlur}},
2052 {"SpawnBlur", {A_SpawnBlur}},
2053 {"BishopChase", {A_BishopChase}},
2054 {"BishopPainBlur", {A_BishopPainBlur}},
2055 {"DragonInitFlight", {A_DragonInitFlight}},
2056 {"DragonFlight", {A_DragonFlight}},
2057 {"DragonFlap", {A_DragonFlap}},
2058 {"DragonFX2", {A_DragonFX2}},
2059 {"PainCounterBEQ", {A_PainCounterBEQ}},
2060 {"DemonAttack1", {A_DemonAttack1}},
2061 {"DemonAttack2", {A_DemonAttack2}},
2062 {"DemonDeath", {A_DemonDeath}},
2063 {"Demon2Death", {A_Demon2Death}},
2064 {"WraithInit", {A_WraithInit}},
2065 {"WraithRaiseInit", {A_WraithRaiseInit}},
2066 {"WraithRaise", {A_WraithRaise}},
2067 {"WraithMelee", {A_WraithMelee}},
2068 {"WraithMissile", {A_WraithMissile}},
2069 {"WraithFX2", {A_WraithFX2}},
2070 {"WraithFX3", {A_WraithFX3}},
2071 {"WraithFX4", {A_WraithFX4}},
2072 {"WraithLook", {A_WraithLook}},
2073 {"WraithChase", {A_WraithChase}},
2074 {"EttinAttack", {A_EttinAttack}},
2075 {"DropMace", {A_DropMace}},
2076 {"AffritSpawnRock", {A_AffritSpawnRock}},
2077 {"AffritRocks", {A_AffritRocks}},
2078 {"SmBounce", {A_SmBounce}},
2079 {"AffritChase", {A_AffritChase}},
2080 {"AffritSplotch", {A_AffritSplotch}},
2081 {"IceGuyLook", {A_IceGuyLook}},
2082 {"IceGuyChase", {A_IceGuyChase}},
2083 {"IceGuyAttack", {A_IceGuyAttack}},
2084 {"IceGuyDie", {A_IceGuyDie}},
2085 {"IceGuyMissileExplode", {A_IceGuyMissileExplode}},
2086 {"CheckFloor", {A_CheckFloor}},
2087 {"FreezeDeath", {A_FreezeDeath}},
2088 {"IceSetTics", {A_IceSetTics}},
2089 {"IceCheckHeadDone", {A_IceCheckHeadDone}},
2090 {"FreezeDeathChunks", {A_FreezeDeathChunks}},
2091 #endif
2092
2093 { NULL, {NULL} } // table term
2094 };
2095
2096
2097
2098 // BEX [CODEPTR] section
bex_codeptr(myfile_t * f)2099 static void bex_codeptr( myfile_t* f )
2100 {
2101 char funcname[BEX_KEYW_LEN];
2102 char s[MAXLINELEN];
2103 int framenum, nn, i, si;
2104
2105 // format:
2106 // [CODEPTR]
2107 // FRAME <framenum> = <funcname>
2108
2109 for(;;) {
2110 if( ! myfgets_nocom(s, sizeof(s), f) )
2111 break; // no more lines
2112 if( s[0] == '\n' ) continue; // blank line
2113 if( s[0] == 0 ) break;
2114 if( strncasecmp( s, "FRAME", 5 ) != 0 ) break; // not a FRAME line
2115 nn = sscanf( &s[5], "%d = %s", &framenum, funcname );
2116 if( nn != 2 )
2117 {
2118 deh_error( "Bad FRAME syntax\n" );
2119 continue;
2120 }
2121 si = deh_frame_to_state(framenum);
2122 if( si == S_NULL )
2123 {
2124 deh_error( "Bad BEX FRAME number %d\n", framenum );
2125 continue;
2126 }
2127 // search action table
2128 for(i=0; ;i++)
2129 {
2130 if( bex_action_table[i].kstr == NULL ) goto no_action_change;
2131 if(!strcasecmp(bex_action_table[i].kstr, funcname)) // BEX action search
2132 {
2133 // change the sprite behavior at the framenum
2134 states[si].action.acv = bex_action_table[i].action.acv;
2135 goto next_keyw;
2136 }
2137 }
2138 no_action_change:
2139 deh_error("Action not changed : FRAME %d\n", framenum);
2140
2141 next_keyw:
2142 continue;
2143 }
2144 }
2145
2146
2147 // [WDJ] MBF helper, From PrBoom (not in MBF)
2148 // haleyjd 9/22/99
2149 //
2150 // Allows handy substitution of any thing for helper dogs. DEH patches
2151 // are being made frequently for this purpose and it requires a complete
2152 // rewiring of the DOG thing. I feel this is a waste of effort, and so
2153 // have added this new [HELPER] BEX block
2154
2155 // BEX [HELPER] section
bex_helper(myfile_t * f)2156 static void bex_helper( myfile_t* f )
2157 {
2158 char s[MAXLINELEN];
2159 int tn, nn;
2160
2161 // format:
2162 // [HELPER]
2163 // TYPE = <num>
2164
2165 for(;;)
2166 {
2167 if( ! myfgets_nocom(s, sizeof(s), f) )
2168 break; // no more lines
2169
2170 if( s[0] == '\n' ) continue; // blank line
2171 if( s[0] == 0 ) break;
2172 if( strncasecmp( s, "TYPE", 4 ) != 0 ) break; // not a TYPE line
2173 nn = sscanf( &s[4], " = %d", &tn );
2174
2175 if( nn != 1 )
2176 {
2177 deh_error( "Bad TYPE syntax\n" );
2178 continue;
2179 }
2180
2181 // In BEX things are 1..
2182 if( tn <= 0 || tn > ENDDOOM_MT )
2183 {
2184 deh_error( "Bad BEX thing number %d\n", tn );
2185 continue;
2186 }
2187
2188 helper_MT = tn - 1; // mobj MT (in BEX things are 1.. )
2189 }
2190 }
2191
2192
2193 // include another DEH or BEX file
bex_include(char * inclfilename)2194 void bex_include( char * inclfilename )
2195 {
2196 static boolean include_nested = 0;
2197
2198 // myfile_t is local to DEH_LoadDehackedLump
2199
2200 if( include_nested )
2201 {
2202 deh_error( "BEX INCLUDE, only one level allowed\n" );
2203 return;
2204 }
2205 // save state
2206
2207 include_nested = 1;
2208 // DEH_LoadDehackedFile( inclfile ); // do the include file
2209 W_Load_WadFile (inclfilename);
2210 include_nested = 0;
2211
2212 // restore state
2213 }
2214
2215
2216
2217 /*
2218 Ammo type = 2
2219 Deselect frame = 11
2220 Select frame = 12
2221 Bobbing frame = 13
2222 Shooting frame = 17
2223 Firing frame = 10
2224 */
readweapon(myfile_t * f,int deh_weapon_id)2225 static void readweapon(myfile_t *f, int deh_weapon_id)
2226 {
2227 weaponinfo_t * wip = & doomweaponinfo[ deh_weapon_id ];
2228 char s[MAXLINELEN];
2229 char *word;
2230 int value;
2231
2232 do{
2233 if(myfgets(s,sizeof(s),f)!=NULL)
2234 {
2235 if(s[0]=='\n') break;
2236 value=searchvalue(s);
2237 word=strtok(s," ");
2238
2239 if(!strcasecmp(word,"Ammo")) wip->ammo =value;
2240 else if(!strcasecmp(word,"Deselect")) set_state( &wip->upstate, value );
2241 else if(!strcasecmp(word,"Select")) set_state( &wip->downstate, value );
2242 else if(!strcasecmp(word,"Bobbing")) set_state( &wip->readystate, value );
2243 else if(!strcasecmp(word,"Shooting")) { set_state( &wip->atkstate, value );
2244 wip->holdatkstate = wip->atkstate; }
2245 else if(!strcasecmp(word,"Firing")) set_state( &wip->flashstate, value );
2246 else deh_error("Weapon %d : unknown word '%s'\n", deh_weapon_id,word);
2247 }
2248 } while(s[0]!='\n' && !myfeof(f));
2249 }
2250
2251 /*
2252 Ammo 1
2253 Max ammo = 400
2254 Per ammo = 40
2255
2256 where:
2257 Ammo 0 = Bullets
2258 Ammo 1 = Shells (shotgun)
2259 Ammo 2 = Cells
2260 Ammo 3 = Rockets
2261 */
2262
2263 extern int clipammo[];
2264 extern int GetWeaponAmmo[];
2265
readammo(myfile_t * f,int num)2266 static void readammo(myfile_t *f,int num)
2267 {
2268 char s[MAXLINELEN];
2269 char *word;
2270 int value;
2271
2272 do{
2273 if(myfgets(s,sizeof(s),f)!=NULL)
2274 {
2275 if(s[0]=='\n') break;
2276 value=searchvalue(s);
2277 word=strtok(s," ");
2278
2279 if(!strcasecmp(word,"Max")) maxammo[num] =value;
2280 else if(!strcasecmp(word,"Per"))
2281 {
2282 clipammo[num]=value; // amount of ammo for small item
2283 GetWeaponAmmo[num] = 2*value; // per weapon
2284 }
2285 else if(!strcasecmp(word,"Perweapon")) GetWeaponAmmo[num] = 2*value;
2286 else deh_error("Ammo %d : unknown word '%s'\n",num,word);
2287 }
2288 } while(s[0]!='\n' && !myfeof(f));
2289 }
2290
2291 // i don't like that but do you see a other way ?
2292 extern int idfa_armor;
2293 extern int idfa_armor_class;
2294 extern int idkfa_armor;
2295 extern int idkfa_armor_class;
2296 extern int god_health;
2297 extern int initial_health;
2298 extern int initial_bullets;
2299 extern int MAXHEALTH;
2300 extern int max_armor;
2301 extern int green_armor_class;
2302 extern int blue_armor_class;
2303 extern int maxsoul;
2304 extern int soul_health;
2305 extern int mega_health;
2306
2307
readmisc(myfile_t * f)2308 static void readmisc(myfile_t *f)
2309 {
2310 char s[MAXLINELEN];
2311 char *word,*word2;
2312 int value;
2313 do{
2314 if(myfgets(s,sizeof(s),f)!=NULL)
2315 {
2316 if(s[0]=='\n') break;
2317 value=searchvalue(s); // none -> 0
2318 word=strtok(s," ");
2319 word2=strtok(NULL," ");
2320
2321 if(!strcasecmp(word,"Initial"))
2322 {
2323 if(!strcasecmp(word2,"Health")) initial_health=value;
2324 else if(!strcasecmp(word2,"Bullets")) initial_bullets=value;
2325 }
2326 else if(!strcasecmp(word,"Max"))
2327 {
2328 if(!strcasecmp(word2,"Health")) MAXHEALTH=value;
2329 else if(!strcasecmp(word2,"Armor")) max_armor=value;
2330 else if(!strcasecmp(word2,"Soulsphere")) maxsoul=value;
2331 }
2332 else if(!strcasecmp(word,"Green")) green_armor_class=value;
2333 else if(!strcasecmp(word,"Blue")) blue_armor_class=value;
2334 else if(!strcasecmp(word,"Soulsphere")) soul_health=value;
2335 else if(!strcasecmp(word,"Megasphere")) mega_health=value;
2336 else if(!strcasecmp(word,"God")) god_health=value;
2337 else if(!strcasecmp(word,"IDFA"))
2338 {
2339 word2=strtok(NULL," ");
2340 if(!strcmp(word2,"=")) idfa_armor=value;
2341 else if(!strcasecmp(word2,"Class")) idfa_armor_class=value;
2342 }
2343 else if(!strcasecmp(word,"IDKFA"))
2344 {
2345 word2=strtok(NULL," ");
2346 if(!strcmp(word2,"=")) idkfa_armor=value;
2347 else if(!strcasecmp(word2,"Class")) idkfa_armor_class=value;
2348 }
2349 else if(!strcasecmp(word,"BFG")) doomweaponinfo[wp_bfg].ammopershoot = value;
2350 else if(!strcasecmp(word,"Monsters Ignore"))
2351 {
2352 // ZDoom at least
2353 // Looks like a coop setting
2354 switch( value )
2355 {
2356 case 0:
2357 monster_infight_deh = INFT_infight_off; // infight off
2358 break;
2359 default:
2360 monster_infight_deh = INFT_coop; // coop
2361 break;
2362 }
2363 }
2364 else if(!strcasecmp(word,"Monsters"))
2365 {
2366 //DarkWolf95:November 21, 2003: Monsters Infight!
2367 //[WDJ] from prboom the string is "Monsters Infight"
2368 // cannot confirm any specific valid numbers
2369 switch( value )
2370 {
2371 case 0: // previous behavior: default to on
2372 case 1: // if user tries to set it on
2373 monster_infight_deh = INFT_infight; // infight on
2374 break;
2375 case 3: // extended behavior, coop monsters
2376 monster_infight_deh = INFT_coop;
2377 break;
2378 case 221: // value=221 -> on (prboom)
2379 // PrBoom implements full infight
2380 monster_infight_deh = INFT_full_infight; // infight on
2381 break;
2382 case 202: // value=202 -> off (prboom)
2383 default: // off
2384 monster_infight_deh = INFT_infight_off; // infight off
2385 break;
2386 }
2387 }
2388 else deh_error("Misc : unknown word '%s'\n",word);
2389 }
2390 } while(s[0]!='\n' && !myfeof(f));
2391 }
2392
2393 extern byte cheat_mus_seq[];
2394 extern byte cheat_choppers_seq[];
2395 extern byte cheat_god_seq[];
2396 extern byte cheat_ammo_seq[];
2397 extern byte cheat_ammonokey_seq[];
2398 extern byte cheat_noclip_seq[];
2399 extern byte cheat_commercial_noclip_seq[];
2400 extern byte cheat_powerup_seq[7][10];
2401 extern byte cheat_clev_seq[];
2402 extern byte cheat_mypos_seq[];
2403 extern byte cheat_amap_seq[];
2404
change_cheat_code(byte * cheatseq,byte * newcheat)2405 static void change_cheat_code(byte *cheatseq, byte *newcheat)
2406 {
2407 byte *i,*j;
2408
2409 // encrypt data
2410 for(i=newcheat;i[0]!='\0';i++)
2411 i[0]=SCRAMBLE(i[0]);
2412
2413 for(i=cheatseq,j=newcheat;j[0]!='\0' && j[0]!=0xff;i++,j++)
2414 {
2415 if(i[0]==1 || i[0]==0xff) // no more place in the cheat
2416 {
2417 deh_error("Cheat too long\n");
2418 return;
2419 }
2420 else
2421 i[0]=j[0];
2422 }
2423
2424 // newcheatseq < oldcheat
2425 j=i;
2426 // search special cheat with 100
2427 for(;i[0]!=0xff;i++)
2428 {
2429 if(i[0]==1)
2430 {
2431 *j++=1;
2432 *j++=0;
2433 *j++=0;
2434 break;
2435 }
2436 }
2437 *j=0xff;
2438
2439 return;
2440 }
2441
2442 // Read cheat section
readcheat(myfile_t * f)2443 static void readcheat(myfile_t *f)
2444 {
2445 char s[MAXLINELEN];
2446 char *word,*word2;
2447 byte *value;
2448
2449 do{
2450 if(myfgets(s,sizeof(s),f)!=NULL)
2451 {
2452 // for each line "<word> = <value>"
2453 if(s[0]=='\n') break;
2454 strtok(s,"="); // after '='
2455 value = (byte *)strtok(NULL," \n"); // skip the space
2456 strtok(NULL," \n"); // finish the string
2457 word=strtok(s," ");
2458
2459 if(!strcasecmp(word ,"Change")) change_cheat_code(cheat_mus_seq,value);
2460 else if(!strcasecmp(word,"Chainsaw")) change_cheat_code(cheat_choppers_seq,value);
2461 else if(!strcasecmp(word,"God")) change_cheat_code(cheat_god_seq,value);
2462 else if(!strcasecmp(word,"Ammo"))
2463 {
2464 word2=strtok(NULL," ");
2465
2466 if(word2 && !strcmp(word2,"&")) change_cheat_code(cheat_ammo_seq,value);
2467 else change_cheat_code(cheat_ammonokey_seq,value);
2468 }
2469 else if(!strcasecmp(word,"No"))
2470 {
2471 word2=strtok(NULL," ");
2472 if(word2)
2473 word2=strtok(NULL," ");
2474
2475 if(word2 && !strcmp(word2,"1")) change_cheat_code(cheat_noclip_seq,value);
2476 else change_cheat_code(cheat_commercial_noclip_seq,value);
2477
2478 }
2479 else if(!strcasecmp(word,"Invincibility")) change_cheat_code(cheat_powerup_seq[0],value);
2480 else if(!strcasecmp(word,"Berserk")) change_cheat_code(cheat_powerup_seq[1],value);
2481 else if(!strcasecmp(word,"Invisibility")) change_cheat_code(cheat_powerup_seq[2],value);
2482 else if(!strcasecmp(word,"Radiation")) change_cheat_code(cheat_powerup_seq[3],value);
2483 else if(!strcasecmp(word,"Auto-map")) change_cheat_code(cheat_powerup_seq[4],value);
2484 else if(!strcasecmp(word,"Lite-Amp")) change_cheat_code(cheat_powerup_seq[5],value);
2485 else if(!strcasecmp(word,"BEHOLD")) change_cheat_code(cheat_powerup_seq[6],value);
2486 else if(!strcasecmp(word,"Level")) change_cheat_code(cheat_clev_seq,value);
2487 else if(!strcasecmp(word,"Player")) change_cheat_code(cheat_mypos_seq,value);
2488 else if(!strcasecmp(word,"Map")) change_cheat_code(cheat_amap_seq,value);
2489 else deh_error("Cheat : unknown word '%s'\n",word);
2490 }
2491 } while(s[0]!='\n' && !myfeof(f));
2492 }
2493
2494
2495 // permission: 0=game, 1=adv, 2=language
DEH_LoadDehackedFile(myfile_t * f,byte bex_permission)2496 void DEH_LoadDehackedFile(myfile_t* f, byte bex_permission)
2497 {
2498
2499 char s[1000];
2500 char *word,*word2;
2501 int i;
2502
2503 deh_num_error=0;
2504
2505 // it don't test the version of doom
2506 // and version of dehacked file
2507 while(!myfeof(f))
2508 {
2509 myfgets(s,sizeof(s),f);
2510 if(s[0]=='\n' || s[0]=='#') // skip blank lines and comments
2511 continue;
2512
2513 word=strtok(s," "); // first keyword
2514 if(word!=NULL)
2515 {
2516 if( word[0] == '\n' ) // ignore blank line
2517 continue;
2518 if(!strncmp(word, "[STRINGS]", 9))
2519 {
2520 bex_strings(f, bex_permission);
2521 continue;
2522 }
2523 else if(!strncmp(word, "[PARS]", 6))
2524 {
2525 bex_pars(f);
2526 continue;
2527 }
2528 else if(!strncmp(word, "[CODEPTR]", 9))
2529 {
2530 bex_codeptr(f);
2531 continue;
2532 }
2533 else if(!strncmp(word, "[HELPER]", 8))
2534 {
2535 bex_helper(f);
2536 continue;
2537 }
2538 else if(!strncmp(word, "INCLUDE", 7))
2539 {
2540 word=strtok(NULL," ");
2541 if(!strcasecmp( word, "NOTEXT" ))
2542 {
2543 bex_include_notext = 1;
2544 word=strtok(NULL," "); // filename
2545 }
2546 bex_include( word ); // include file
2547 bex_include_notext = 0;
2548 continue;
2549 }
2550 else if(!strncmp(word, "Engine", 6)
2551 || !strncmp(word, "Data", 4)
2552 || !strncmp(word, "IWAD", 4) )
2553 continue; // WhackEd2 data, ignore it
2554
2555 word2=strtok(NULL," "); // id number
2556 if(word2!=NULL)
2557 {
2558 i=atoi(word2);
2559
2560 if(!strcasecmp(word,"Thing"))
2561 {
2562 // "Thing <num>"
2563 if(i<=NUMMOBJTYPES && i>0)
2564 readthing(f,i);
2565 else
2566 deh_error("Thing %d don't exist\n",i);
2567 }
2568 else if(!strcasecmp(word,"Frame"))
2569 {
2570 // "Frame <num>"
2571 readframe(f,i);
2572 }
2573 else if(!strcasecmp(word,"Pointer"))
2574 {
2575 // "Pointer <xref>"
2576 readpointer(f,i);
2577 }
2578 else if(!strcasecmp(word,"Sound"))
2579 {
2580 // "Sound <num>"
2581 if(i<NUMSFX && i>=0)
2582 readsound(f,i);
2583 else
2584 deh_error("Sound %d don't exist\n", i);
2585 }
2586 else if(!strcasecmp(word,"Sprite"))
2587 {
2588 // "Sprite <num>"
2589 // PrBoom does not support the DEH sprite block.
2590 if(i<NUMSPRITES && i>=0)
2591 {
2592 if(myfgets(s,sizeof(s),f)!=NULL)
2593 {
2594 int k;
2595 k=(searchvalue(s)-151328)/8;
2596 if(k>=0 && k<NUMSPRITES)
2597 sprnames[i]=deh_sprnames[k];
2598 else
2599 deh_error("Sprite %i : offset out of bound\n",i);
2600 }
2601 }
2602 else
2603 deh_error("Sprite %d don't exist\n",i);
2604 }
2605 else if(!strcasecmp(word,"Text"))
2606 {
2607 // "Text <num>"
2608 int j;
2609
2610 if((word=strtok(NULL," "))!=NULL)
2611 {
2612 j=atoi(word);
2613 readtext(f,i,j);
2614 }
2615 else
2616 deh_error("Text : missing second number\n");
2617
2618 }
2619 else if(!strcasecmp(word,"Weapon"))
2620 {
2621 // "Weapon <num>"
2622 if(i<NUMWEAPONS && i>=0)
2623 readweapon(f,i);
2624 else
2625 deh_error("Weapon %d don't exist\n",i);
2626 }
2627 else if(!strcasecmp(word,"Ammo"))
2628 {
2629 // "Ammo <num>"
2630 if(i<NUMAMMO && i>=0)
2631 readammo(f,i);
2632 else
2633 deh_error("Ammo %d don't exist\n",i);
2634 }
2635 else if(!strcasecmp(word,"Misc"))
2636 // "Misc <num>"
2637 readmisc(f);
2638 else if(!strcasecmp(word,"Cheat"))
2639 // "Cheat <num>"
2640 readcheat(f);
2641 else if(!strcasecmp(word,"Doom"))
2642 {
2643 // "Doom <num>"
2644 int ver = searchvalue(strtok(NULL,"\n"));
2645 if( ver!=19)
2646 deh_error("Warning : patch from a different doom version (%d), only version 1.9 is supported\n",ver);
2647 }
2648 else if(!strcasecmp(word,"Patch"))
2649 {
2650 // "Patch <num>"
2651 word=strtok(NULL," ");
2652 if(word && !strcasecmp(word,"format"))
2653 {
2654 if(searchvalue(strtok(NULL,"\n"))!=6)
2655 deh_error("Warning : Patch format not supported");
2656 }
2657 }
2658 else deh_error("Unknown word : %s\n",word);
2659 }
2660 else
2661 deh_error("missing argument for '%s'\n",word);
2662 }
2663 else
2664 deh_error("No word in this line:\n%s\n",s);
2665
2666 } // end while
2667
2668 if (deh_num_error>0)
2669 {
2670 CONS_Printf("%d warning(s) in the dehacked file\n",deh_num_error);
2671 if (devparm)
2672 getchar();
2673 }
2674
2675 deh_loaded = true;
2676 if( flags_valid_deh )
2677 Translucency_OnChange(); // ensure translucent updates
2678 }
2679
2680 // read dehacked lump in a wad (there is special trick for for deh
2681 // file that are converted to wad in w_wad.c)
DEH_LoadDehackedLump(lumpnum_t lumpnum)2682 void DEH_LoadDehackedLump( lumpnum_t lumpnum )
2683 {
2684 myfile_t f;
2685
2686 f.size = W_LumpLength(lumpnum);
2687 f.data = Z_Malloc(f.size + 1, PU_IN_USE, 0); // temp
2688 W_ReadLump(lumpnum, f.data);
2689 f.curpos = f.data;
2690 f.data[f.size] = 0;
2691
2692 DEH_LoadDehackedFile(&f, 0);
2693 Z_Free(f.data);
2694 }
2695
2696
2697 // [WDJ] Before any changes, save all comparison info, so that multiple
2698 // DEH files and lumps can be handled without interfering with each other.
2699 // Called once
DEH_Init(void)2700 void DEH_Init(void)
2701 {
2702 int i;
2703 // save value for cross reference
2704 for(i=0;i<NUMSTATES;i++)
2705 deh_actions[i]=states[i].action;
2706 for(i=0;i<NUMSPRITES;i++)
2707 deh_sprnames[i]=sprnames[i];
2708 for(i=0;i<NUMSFX;i++)
2709 deh_sfxnames[i]=S_sfx[i].name;
2710 for(i=1;i<NUMMUSIC;i++)
2711 deh_musicname[i]=S_music[i].name;
2712 for(i=0;i<NUMTEXT;i++)
2713 deh_text[i]=text[i];
2714 monster_infight_deh = INFT_none; // not set
2715 #if defined DEBUG_WINDOWED || defined PARANOIA
2716 // [WDJ] Checks of constants that I could not do at compile-time (enums).
2717 if( (int)STS_TECH2LAMP4 != (int)S_TECH2LAMP4 )
2718 I_Error( "DEH: state S_TECH2LAMP4 error\n" );
2719 if( STH_PODGENERATOR != (S_PODGENERATOR - S_FREETARGMOBJ + STH_FREETARGMOBJ ))
2720 I_Error( "DEH: state S_PODGENERATOR error" );
2721 if( STH_SND_WATERFALL != (S_SND_WATERFALL - S_BLOODYSKULL1 + STH_BLOODYSKULL1 ))
2722 I_Error( "DEH: state S_SND_WATERFALL error\n" );
2723 #endif
2724 }
2725
2726 #ifdef BEX_LANGUAGE
2727 #include <fcntl.h>
2728 #include <unistd.h>
2729
2730 // Load a language BEX file, by name (french, german, etc.)
BEX_load_language(char * langname,byte bex_permission)2731 void BEX_load_language( char * langname, byte bex_permission )
2732 {
2733 int handle;
2734 int bytesread;
2735 struct stat bufstat;
2736 char filename[MAX_WADPATH];
2737 myfile_t f;
2738
2739 f.data = NULL;
2740 if( langname == NULL )
2741 langname = "lang"; // default name
2742 strncpy(filename, langname, MAX_WADPATH);
2743 filename[ MAX_WADPATH - 6 ] = '\0';
2744 strcat( filename, ".bex" );
2745 handle = open (filename,O_RDONLY|O_BINARY,0666);
2746 if( handle == -1)
2747 {
2748 CONS_Printf( "Lang file not found: %s\n", filename );
2749 goto errexit;
2750 }
2751 fstat(handle,&bufstat);
2752 f.size = bufstat.st_size;
2753 f.data = malloc( f.size );
2754 if( f.data == NULL )
2755 {
2756 CONS_Printf("Lang file too large for memory\n" );
2757 goto errexit;
2758 }
2759 bytesread = read (handle, f.data, f.size);
2760 if( bytesread < f.size )
2761 {
2762 CONS_Printf( "Lang file read failed\n" );
2763 goto errexit;
2764 }
2765 f.curpos = f.data;
2766 f.data[f.size] = 0;
2767 DEH_LoadDehackedFile(&f, bex_permission);
2768
2769 errexit:
2770 free( f.data );
2771 }
2772 #endif
2773