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