1 //----------------------------------------------------------------------------
2 // EDGE Data Definition Files Code (Main)
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18
19 #include "local.h"
20
21 #include <limits.h>
22 #include <vector>
23
24 #include "epi/path.h"
25 #include "epi/str_format.h"
26
27 #include "colormap.h"
28
29 #include "src/p_action.h"
30
31
32 // FIXME: unwanted link to engine code (switch to epi::angle_c)
33 extern float M_Tan(angle_t ang) GCCATTR((const));
34
35
36 #define DEBUG_DDFREAD 0
37
38 static int engine_version;
39 static std::string ddf_where;
40
41 bool strict_errors = false;
42 bool lax_errors = false;
43 bool no_warnings = false;
44
45
46 //
47 // DDF_Error
48 //
49 // -AJA- 1999/10/27: written.
50 //
51 int cur_ddf_line_num;
52 std::string cur_ddf_filename;
53 std::string cur_ddf_entryname;
54 std::string cur_ddf_linedata;
55
DDF_Error(const char * err,...)56 void DDF_Error(const char *err, ...)
57 {
58 va_list argptr;
59 char buffer[2048];
60 char *pos;
61
62 buffer[2047] = 0;
63
64 // put actual error message on first line
65 va_start(argptr, err);
66 vsprintf(buffer, err, argptr);
67 va_end(argptr);
68
69 pos = buffer + strlen(buffer);
70
71 if (!cur_ddf_filename.empty())
72 {
73 sprintf(pos, "Error occurred near line %d of %s\n",
74 cur_ddf_line_num, cur_ddf_filename.c_str());
75
76 pos += strlen(pos);
77 }
78
79 if (!cur_ddf_entryname.empty())
80 {
81 sprintf(pos, "Error occurred in entry: %s\n",
82 cur_ddf_entryname.c_str());
83
84 pos += strlen(pos);
85 }
86
87 if (!cur_ddf_linedata.empty())
88 {
89 sprintf(pos, "Line contents: %s\n",
90 cur_ddf_linedata.c_str());
91
92 pos += strlen(pos);
93 }
94
95 // check for buffer overflow
96 if (buffer[2047] != 0)
97 I_Error("Buffer overflow in DDF_Error\n");
98
99 // add a blank line for readability under DOS/Linux.
100 I_Printf("\n");
101
102 I_Error("%s", buffer);
103 }
104
DDF_Warning(const char * err,...)105 void DDF_Warning(const char *err, ...)
106 {
107 va_list argptr;
108 char buffer[1024];
109
110 if (no_warnings)
111 return;
112
113 va_start(argptr, err);
114 vsprintf(buffer, err, argptr);
115 va_end(argptr);
116
117 I_Warning("%s", buffer);
118
119 if (!cur_ddf_filename.empty())
120 {
121 I_Printf(" problem occurred near line %d of %s\n",
122 cur_ddf_line_num, cur_ddf_filename.c_str());
123 }
124
125 if (!cur_ddf_entryname.empty())
126 {
127 I_Printf(" problem occurred in entry: %s\n",
128 cur_ddf_entryname.c_str());
129 }
130
131 if (!cur_ddf_linedata.empty())
132 {
133 I_Printf(" with line contents: %s\n",
134 cur_ddf_linedata.c_str());
135 }
136
137 /// I_Printf("\n");
138 }
139
DDF_WarnError(const char * err,...)140 void DDF_WarnError(const char *err, ...)
141 {
142 va_list argptr;
143 char buffer[1024];
144
145 va_start(argptr, err);
146 vsprintf(buffer, err, argptr);
147 va_end(argptr);
148
149 if (strict_errors)
150 DDF_Error("%s", buffer);
151 else
152 DDF_Warning("%s", buffer);
153 }
154
155
DDF_Init(int _engine_ver)156 void DDF_Init(int _engine_ver)
157 {
158 engine_version = _engine_ver;
159
160 DDF_StateInit();
161 DDF_LanguageInit();
162 DDF_SFXInit();
163 DDF_ColmapInit();
164 DDF_ImageInit();
165 DDF_FontInit();
166 DDF_StyleInit();
167 DDF_AttackInit();
168 DDF_WeaponInit();
169 DDF_MobjInit();
170 DDF_LinedefInit();
171 DDF_SectorInit();
172 DDF_SwitchInit();
173 DDF_AnimInit();
174 DDF_GameInit();
175 DDF_LevelInit();
176 DDF_MusicPlaylistInit();
177 }
178
DDF_SetWhere(const std::string & dir)179 void DDF_SetWhere(const std::string& dir)
180 {
181 ddf_where = dir;
182 }
183
184
185 class define_c
186 {
187 public:
188 // Note: these pointers only point inside the currently
189 // loaded memfile. Hence there is no need to
190 // explicitly free them.
191 char *name;
192 char *value;
193
194 public:
define_c()195 define_c() : name(NULL), value(NULL)
196 { }
197
define_c(char * _N,char * _V)198 define_c(char *_N, char *_V) : name(_N), value(_V)
199 { }
200
~define_c()201 ~define_c()
202 { }
203 };
204
205 // -AJA- 1999/09/12: Made these static. The variable `defines' was
206 // clashing with the one in rad_trig.c. Ugh.
207
208 static std::vector<define_c> defines;
209
DDF_MainAddDefine(char * name,char * value)210 static void DDF_MainAddDefine(char *name, char *value)
211 {
212 for (int i = 0; i < (int)defines.size(); i++)
213 {
214 if (stricmp(defines[i].name, name) == 0)
215 {
216 DDF_Error("Redefinition of '%s'\n", name);
217 return;
218 }
219 }
220
221 defines.push_back(define_c(name, value));
222 }
223
DDF_MainGetDefine(const char * name)224 static const char *DDF_MainGetDefine(const char *name)
225 {
226 for (int i = 0; i < (int)defines.size(); i++)
227 {
228 if (stricmp(defines[i].name, name) == 0)
229 return defines[i].value;
230 }
231 return name; // un-defined.
232 }
233
234 //
235 // DDF_MainCleanup
236 //
237 // This goes through the information loaded via DDF and matchs any
238 // info stored as references.
239 //
DDF_CleanUp(void)240 void DDF_CleanUp(void)
241 {
242 DDF_LanguageCleanUp();
243 DDF_ImageCleanUp();
244 DDF_FontCleanUp();
245 DDF_StyleCleanUp();
246 DDF_MobjCleanUp();
247 DDF_AttackCleanUp();
248 DDF_StateCleanUp();
249 DDF_LinedefCleanUp();
250 DDF_SFXCleanUp();
251 DDF_ColmapCleanUp();
252 DDF_WeaponCleanUp();
253 DDF_SectorCleanUp();
254 DDF_SwitchCleanUp();
255 DDF_AnimCleanUp();
256 DDF_GameCleanUp();
257 DDF_LevelCleanUp();
258 DDF_MusicPlaylistCleanUp();
259 }
260
261 static const char *tag_conversion_table[] =
262 {
263 "ANIMATIONS", "DDFANIM",
264 "ATTACKS", "DDFATK",
265 "COLOURMAPS", "DDFCOLM",
266 "FONTS", "DDFFONT",
267 "GAMES", "DDFGAME",
268 "IMAGES", "DDFIMAGE",
269 "LANGUAGES", "DDFLANG",
270 "LEVELS", "DDFLEVL",
271 "LINES", "DDFLINE",
272 "PLAYLISTS", "DDFPLAY",
273 "SECTORS", "DDFSECT",
274 "SOUNDS", "DDFSFX",
275 "STYLES", "DDFSTYLE",
276 "SWITCHES", "DDFSWTH",
277 "THINGS", "DDFTHING",
278 "WEAPONS", "DDFWEAP",
279
280 NULL, NULL
281 };
282
DDF_GetLumpNameForFile(const char * filename,char * lumpname)283 void DDF_GetLumpNameForFile(const char *filename, char *lumpname)
284 {
285 FILE *fp = fopen(filename, "r");
286
287 if (!fp)
288 I_Error("Couldn't open DDF file: %s\n", filename);
289
290 bool in_comment = false;
291
292 for (;;)
293 {
294 int ch = fgetc(fp);
295
296 if (ch == EOF || ferror(fp))
297 break;
298
299 if (ch == '/' || ch == '#') // skip directives too
300 {
301 in_comment = true;
302 continue;
303 }
304
305 if (in_comment)
306 {
307 if (ch == '\n' || ch == '\r')
308 in_comment = false;
309 continue;
310 }
311
312 if (ch == '[')
313 break;
314
315 if (ch != '<')
316 continue;
317
318 // found start of <XYZ> tag, read it in
319
320 char tag_buf[40];
321 int len = 0;
322
323 for (;;)
324 {
325 ch = fgetc(fp);
326
327 if (ch == EOF || ferror(fp) || ch == '>')
328 break;
329
330 tag_buf[len++] = toupper(ch);
331
332 if (len+2 >= (int)sizeof(tag_buf))
333 break;
334 }
335
336 tag_buf[len] = 0;
337
338 if (len > 0)
339 {
340 for (int i = 0; tag_conversion_table[i]; i += 2)
341 {
342 if (strcmp(tag_buf, tag_conversion_table[i]) == 0)
343 {
344 strcpy(lumpname, tag_conversion_table[i+1]);
345 fclose(fp);
346
347 return; // SUCCESS!
348 }
349 }
350
351 fclose(fp);
352 I_Error("Unknown marker <%s> in DDF file: %s\n", tag_buf, filename);
353 }
354 break;
355 }
356
357 fclose(fp);
358 I_Error("Missing <..> marker in DDF file: %s\n", filename);
359 }
360
361 // -KM- 1998/12/16 This loads the ddf file into memory for parsing.
362 // -AJA- Returns NULL if no such file exists (with a warning).
363
DDF_MainCacheFile(readinfo_t * readinfo)364 static void *DDF_MainCacheFile(readinfo_t * readinfo)
365 {
366 FILE *file;
367 char *memfile;
368 size_t size;
369
370 if (!readinfo->filename)
371 I_Error("DDF_MainReadFile: No file to read\n");
372
373 std::string filename(epi::PATH_Join(ddf_where.c_str(), readinfo->filename));
374
375 file = fopen(filename.c_str(), "rb");
376 if (file == NULL)
377 {
378 I_Warning("DDF_MainReadFile: Unable to open: '%s'\n", filename.c_str());
379 return NULL;
380 }
381
382 #if (DEBUG_DDFREAD)
383 I_Debugf("\nDDF Parser Output:\n");
384 #endif
385
386 // get to the end of the file
387 fseek(file, 0, SEEK_END);
388
389 // get the size
390 size = ftell(file);
391
392 // reset to beginning
393 fseek(file, 0, SEEK_SET);
394
395 // malloc the size
396 memfile = new char[size + 1];
397
398 fread(memfile, sizeof(char), size, file);
399 memfile[size] = 0;
400
401 // close the file
402 fclose(file);
403
404 readinfo->memsize = size;
405 return (void *)memfile;
406 }
407
DDF_ParseVersion(const char * str,int len)408 static void DDF_ParseVersion(const char *str, int len)
409 {
410 if (len <= 0 || ! isspace(*str))
411 DDF_Error("Badly formed #VERSION directive.\n");
412
413 for (; isspace(*str) && len > 0; str++, len--)
414 { }
415
416 if (len < 4 || ! isdigit(str[0]) || str[1] != '.' ||
417 ! isdigit(str[2]) || ! isdigit(str[3]))
418 {
419 DDF_Error("Badly formed #VERSION directive.\n");
420 }
421
422 int ddf_version = ((str[0] - '0') * 100) |
423 ((str[2] - '0') * 10) |
424 (str[3] - '0');
425
426 if (ddf_version < 123)
427 DDF_Error("Illegal #VERSION number: %s\n", str);
428
429 if (ddf_version > engine_version)
430 DDF_Error("This version of EDGE cannot handle this DDF.\n");
431 }
432
433
434 //
435 // Description of the DDF Parser:
436 //
437 // The DDF Parser is a simple reader that is very limited in error checking,
438 // however it can adapt to most tasks, as is required for the variety of stuff
439 // need to be loaded in order to configure the EDGE Engine.
440 //
441 // The parser will read an ascii file, character by character an interpret each
442 // depending in which mode it is in; Unless an error is encountered or a called
443 // procedure stops the parser, it will read everything until EOF is encountered.
444 //
445 // When the parser function is called, a pointer to a readinfo_t is passed and
446 // contains all the info needed, it contains:
447 //
448 // * filename - filename to be read, returns error if NULL
449 // * DDF_MainCheckName - function called when a def has been just been started
450 // * DDF_MainCheckCmd - function called when we need to check a command
451 // * DDF_MainCreateEntry - function called when a def has been completed
452 // * DDF_MainFinishingCode - function called when EOF is read
453 // * currentcmdlist - Current list of commands
454 //
455 // Also when commands are referenced, they use currentcmdlist, which is a pointer
456 // to a list of entries, the entries are formatted like this:
457 //
458 // * name - name of command
459 // * routine - function called to interpret info
460 // * numeric - void pointer to an value (possibly used by routine)
461 //
462 // name is compared with the read command, to see if it matchs.
463 // routine called to interpret info, if command name matches read command.
464 // numeric is used if a numeric value needs to be changed, by routine.
465 //
466 // The different parser modes are:
467 // waiting_newdef
468 // reading_newdef
469 // reading_command
470 // reading_data
471 // reading_remark
472 // reading_string
473 //
474 // 'waiting_newdef' is only set at the start of the code, At this point every
475 // character with the exception of DEFSTART is ignored. When DEFSTART is
476 // encounted, the parser will switch to reading_newdef. DEFSTART the parser
477 // will only switches modes and sets firstgo to false.
478 //
479 // 'reading_newdef' reads all alphanumeric characters and the '_' character - which
480 // substitudes for a space character (whitespace is ignored) - until DEFSTOP is read.
481 // DEFSTOP passes the read string to DDF_MainCheckName and then clears the string.
482 // Mode reading_command is now set. All read stuff is passed to char *buffer.
483 //
484 // 'reading_command' picks out all the alphabetic characters and passes them to
485 // buffer as soon as COMMANDREAD is encountered; DDF_MainReadCmd looks through
486 // for a matching command, if none is found a fatal error is returned. If a matching
487 // command is found, this function returns a command reference number to command ref
488 // and sets the mode to reading_data. if DEFSTART is encountered the procedure will
489 // clear the buffer, run DDF_MainCreateEntry (called this as it reflects that in Items
490 // & Scenery if starts a new mobj type, in truth it can do anything procedure wise) and
491 // then switch mode to reading_newdef.
492 //
493 // 'reading_data' passes alphanumeric characters, plus a few other characters that
494 // are also needed. It continues to feed buffer until a SEPARATOR or a TERMINATOR is
495 // found. The difference between SEPARATOR and TERMINATOR is that a TERMINATOR refs
496 // the cmdlist to find the routine to use and then sets the mode to reading_command,
497 // whereas SEPARATOR refs the cmdlist to find the routine and a looks for more data
498 // on the same command. This is how the multiple states and specials are defined.
499 //
500 // 'reading_remark' does not process any chars except REMARKSTOP, everything else is
501 // ignored. This mode is only set when REMARKSTART is found, when this happens the
502 // current mode is held in formerstatus, which is restored when REMARKSTOP is found.
503 //
504 // 'reading_string' is set when the parser is going through data (reading_data) and
505 // encounters STRINGSTART and only stops on a STRINGSTOP. When reading_string,
506 // everything that is an ASCII char is read (which the exception of STRINGSTOP) and
507 // passed to the buffer. REMARKS are ignored in when reading_string and the case is
508 // take notice of here.
509 //
510 // The maximum size of BUFFER is set in the BUFFERSIZE define.
511 //
512 // DDF_MainReadFile & DDF_MainProcessChar handle the main processing of the file, all
513 // the procedures in the other DDF files (which the exceptions of the Inits) are
514 // called directly or indirectly. DDF_MainReadFile handles to opening, closing and
515 // calling of procedures, DDF_MainProcessChar makes sense from the character read
516 // from the file.
517 //
518
519 //
520 // DDF_MainProcessChar
521 //
522 // 1998/08/10 Added String reading code.
523 //
DDF_MainProcessChar(char character,std::string & token,int status)524 static readchar_t DDF_MainProcessChar(char character, std::string& token, int status)
525 {
526 //int len;
527
528 // -ACB- 1998/08/11 Used for detecting formatting in a string
529 static bool formatchar = false;
530
531 // With the exception of reading_string, whitespace is ignored.
532 if (status != reading_string)
533 {
534 if (isspace(character))
535 return nothing;
536 }
537 else // check for formatting char in a string
538 {
539 if (!formatchar && character == '\\')
540 {
541 formatchar = true;
542 return nothing;
543 }
544 }
545
546 // -AJA- 1999/09/26: Handle unmatched '}' better.
547 if (status != reading_string && character == '{')
548 return remark_start;
549
550 if (status == reading_remark && character == '}')
551 return remark_stop;
552
553 if (status != reading_string && character == '}')
554 DDF_Error("DDF: Encountered '}' without previous '{'.\n");
555
556 switch (status)
557 {
558 case reading_remark:
559 return nothing;
560
561 // -ES- 2000/02/29 Added tag check.
562 case waiting_tag:
563 if (character == '<')
564 return tag_start;
565 else
566 DDF_Error("DDF: File must start with a tag!\n");
567 break;
568
569 case reading_tag:
570 if (character == '>')
571 return tag_stop;
572 else
573 {
574 token += (character);
575 return ok_char;
576 }
577
578 case waiting_newdef:
579 if (character == '[')
580 return def_start;
581 else
582 return nothing;
583
584 case reading_newdef:
585 if (character == ']')
586 {
587 return def_stop;
588 }
589 else if ((isalnum(character)) || (character == '_') ||
590 (character == ':') || (character == '+'))
591 {
592 token += toupper(character);
593 return ok_char;
594 }
595 return nothing;
596
597 case reading_command:
598 if (character == '=')
599 {
600 return command_read;
601 }
602 else if (character == ';')
603 {
604 return property_read;
605 }
606 else if (character == '[')
607 {
608 return def_start;
609 }
610 else if (isalnum(character) || character == '_' ||
611 character == '(' || character == ')' ||
612 character == '.')
613 {
614 token += toupper(character);
615 return ok_char;
616 }
617 return nothing;
618
619 // -ACB- 1998/08/10 Check for string start
620 case reading_data:
621 if (character == '\"')
622 return string_start;
623
624 if (character == ';')
625 return terminator;
626
627 if (character == ',')
628 return separator;
629
630 if (character == '(')
631 {
632 token += (character);
633 return group_start;
634 }
635
636 if (character == ')')
637 {
638 token += (character);
639 return group_stop;
640 }
641
642 // Sprite Data - more than a few exceptions....
643 if (isalnum(character) || character == '_' || character == '-' ||
644 character == ':' || character == '.' || character == '[' ||
645 character == ']' || character == '\\' || character == '!' ||
646 character == '#' || character == '%' || character == '+' ||
647 character == '@' || character == '?')
648 {
649 token += toupper(character);
650 return ok_char;
651 }
652 else if (isprint(character))
653 DDF_WarnError("DDF: Illegal character '%c' found.\n", character);
654
655 break;
656
657 case reading_string: // -ACB- 1998/08/10 New string handling
658 // -KM- 1999/01/29 Fixed nasty bug where \" would be recognised as
659 // string end over quote mark. One of the level text used this.
660 if (formatchar)
661 {
662 // -ACB- 1998/08/11 Formatting check: Carriage-return.
663 if (character == 'n')
664 {
665 token += ('\n');
666 formatchar = false;
667 return ok_char;
668 }
669 else if (character == '\"') // -KM- 1998/10/29 Also recognise quote
670 {
671 token += ('\"');
672 formatchar = false;
673 return ok_char;
674 }
675 else if (character == '\\') // -ACB- 1999/11/24 Double backslash means directory
676 {
677 token += ('\\');
678 formatchar = false;
679 return ok_char;
680 }
681 else // -ACB- 1999/11/24 Any other characters are treated in the norm
682 {
683 token += (character);
684 formatchar = false;
685 return ok_char;
686 }
687
688 }
689 else if (character == '\"')
690 {
691 return string_stop;
692 }
693 else if (character == '\n')
694 {
695 cur_ddf_line_num--;
696 DDF_WarnError("Unclosed string detected.\n");
697
698 cur_ddf_line_num++;
699 return nothing;
700 }
701 // -KM- 1998/10/29 Removed ascii check, allow foreign characters (?)
702 // -ES- HEY! Swedish is not foreign!
703 else
704 {
705 token += (character);
706 return ok_char;
707 }
708
709 default: // doh!
710 I_Error("DDF_MainProcessChar: INTERNAL ERROR: "
711 "Bad status value %d !\n", status);
712 break;
713 }
714
715 return nothing;
716 }
717
718 //
719 // DDF_MainReadFile
720 //
721 // -ACB- 1998/08/10 Added the string reading code
722 // -ACB- 1998/09/28 DDF_ReadFunction Localised here
723 // -AJA- 1999/10/02 Recursive { } comments.
724 // -ES- 2000/02/29 Added
725 //
DDF_MainReadFile(readinfo_t * readinfo)726 bool DDF_MainReadFile(readinfo_t * readinfo)
727 {
728 std::string token;
729 std::string current_cmd;
730
731 char *name;
732 char *value = NULL;
733 char character;
734 char *memfile;
735 char *memfileptr;
736 int status, formerstatus;
737 int response;
738 int size;
739 int comment_level;
740 int bracket_level;
741 bool firstgo;
742
743 int current_index = 0;
744 int entry_count = 0;
745
746 #if (DEBUG_DDFREAD)
747 char charcount = 0;
748 #endif
749
750 status = waiting_tag;
751 formerstatus = readstatus_invalid;
752 comment_level = 0;
753 bracket_level = 0;
754 firstgo = true;
755
756 cur_ddf_line_num = 1;
757
758 if (!readinfo->memfile && !readinfo->filename)
759 I_Error("DDF_MainReadFile: No file to read\n");
760
761 if (!readinfo->memfile)
762 {
763 readinfo->memfile = (char*)DDF_MainCacheFile(readinfo);
764
765 // no file ? No worries, we'll get it from edge.wad...
766 if (!readinfo->memfile)
767 return false;
768
769 cur_ddf_filename = std::string(readinfo->filename);
770 }
771 else
772 {
773 cur_ddf_filename = std::string(readinfo->lumpname);
774 }
775
776 memfileptr = memfile = readinfo->memfile;
777 size = readinfo->memsize;
778
779 // -ACB- 1998/09/12 Copy file to memory: Read until end. Speed optimisation.
780 while (memfileptr < &memfile[size])
781 {
782 // -KM- 1998/12/16 Added #define command to ddf files.
783 if (!strnicmp(memfileptr, "#DEFINE", 7))
784 {
785 bool line = false;
786
787 memfileptr += 8;
788 name = memfileptr;
789
790 while (*memfileptr != ' ' && memfileptr < &memfile[size])
791 memfileptr++;
792
793 if (memfileptr < &memfile[size])
794 {
795 *memfileptr++ = 0;
796 value = memfileptr;
797 }
798 else
799 {
800 DDF_Error("#DEFINE '%s' as what?!\n", name);
801 }
802
803 while (memfileptr < &memfile[size])
804 {
805 if (*memfileptr == '\r')
806 *memfileptr = ' ';
807 if (*memfileptr == '\\')
808 line = true;
809 if (*memfileptr == '\n' && !line)
810 break;
811 memfileptr++;
812 }
813
814 if (*memfileptr == '\n')
815 cur_ddf_line_num++;
816
817 *memfileptr++ = 0;
818
819 DDF_MainAddDefine(name, value);
820
821 token.clear();
822 continue;
823 }
824
825 // -AJA- 1999/10/27: Not the greatest place for it, but detect //
826 // comments here and ignore them. Ow the pain of long
827 // identifier names... Ow the pain of &memfile[size] :-)
828
829 if (comment_level == 0 && status != reading_string &&
830 memfileptr+1 < &memfile[size] &&
831 memfileptr[0] == '/' && memfileptr[1] == '/')
832 {
833 while (memfileptr < &memfile[size] && *memfileptr != '\n')
834 memfileptr++;
835
836 if (memfileptr >= &memfile[size])
837 break;
838 }
839
840 character = *memfileptr++;
841
842 if (character == '\n')
843 {
844 int l_len;
845
846 cur_ddf_line_num++;
847
848 // -AJA- 2000/03/21: determine linedata. Ouch.
849 for (l_len=0; &memfileptr[l_len] < &memfile[size] &&
850 memfileptr[l_len] != '\n' && memfileptr[l_len] != '\r'; l_len++)
851 { }
852
853
854 cur_ddf_linedata = std::string(memfileptr, l_len);
855
856 // -AJA- 2001/05/21: handle directives (lines beginning with #).
857 // This code is more hackitude -- to be fixed when the whole
858 // parsing code gets the overhaul it needs.
859
860 if (strnicmp(memfileptr, "#CLEARALL", 9) == 0)
861 {
862 if (! firstgo)
863 DDF_Error("#CLEARALL cannot be used inside an entry !\n");
864
865 (* readinfo->clear_all)();
866
867 memfileptr += l_len;
868 continue;
869 }
870
871 if (strnicmp(memfileptr, "#VERSION", 8) == 0)
872 {
873 if (! firstgo)
874 DDF_Error("#VERSION cannot be used inside an entry !\n");
875
876 DDF_ParseVersion(memfileptr + 8, l_len - 8);
877
878 memfileptr += l_len;
879 continue;
880 }
881 }
882
883 response = DDF_MainProcessChar(character, token, status);
884
885 switch (response)
886 {
887 case remark_start:
888 if (comment_level == 0)
889 {
890 formerstatus = status;
891 status = reading_remark;
892 }
893 comment_level++;
894 break;
895
896 case remark_stop:
897 comment_level--;
898 if (comment_level == 0)
899 {
900 status = formerstatus;
901 }
902 break;
903
904 case command_read:
905 if (! token.empty())
906 current_cmd = token.c_str();
907 else
908 current_cmd.clear();
909
910 SYS_ASSERT(current_index == 0);
911
912 token.clear();
913 status = reading_data;
914 break;
915
916 case tag_start:
917 status = reading_tag;
918 break;
919
920 case tag_stop:
921 if (stricmp(token.c_str(), readinfo->tag) != 0)
922 DDF_Error("Start tag <%s> expected, found <%s>!\n",
923 readinfo->tag, token.c_str());
924
925 status = waiting_newdef;
926 token.clear();
927 break;
928
929 case def_start:
930 if (bracket_level > 0)
931 DDF_Error("Unclosed () brackets detected.\n");
932
933 entry_count++;
934
935 if (firstgo)
936 {
937 firstgo = false;
938 status = reading_newdef;
939 }
940 else
941 {
942 cur_ddf_linedata.clear();
943
944 // finish off previous entry
945 (* readinfo->finish_entry)();
946
947 token.clear();
948
949 status = reading_newdef;
950
951 cur_ddf_entryname.clear();
952 }
953 break;
954
955 case def_stop:
956 cur_ddf_entryname = epi::STR_Format("[%s]", token.c_str());
957
958 // -AJA- 2009/07/27: extend an existing entry
959 if (token[0] == '+' && token[1] == '+')
960 (* readinfo->start_entry)(token.c_str()+2, true);
961 else
962 (* readinfo->start_entry)(token.c_str(), false);
963
964 token.clear();
965 status = reading_command;
966 break;
967
968 // -AJA- 2000/10/02: support for () brackets
969 case group_start:
970 if (status == reading_data || status == reading_command)
971 bracket_level++;
972 break;
973
974 case group_stop:
975 if (status == reading_data || status == reading_command)
976 {
977 bracket_level--;
978 if (bracket_level < 0)
979 DDF_Error("Unexpected `)' bracket.\n");
980 }
981 break;
982
983 case separator:
984 if (bracket_level > 0)
985 {
986 token += (',');
987 break;
988 }
989
990 if (current_cmd.empty())
991 DDF_Error("Unexpected comma `,'.\n");
992
993 if (firstgo)
994 DDF_WarnError("Command %s used outside of any entry\n",
995 current_cmd.c_str());
996 else
997 {
998 (* readinfo->parse_field)(current_cmd.c_str(),
999 DDF_MainGetDefine(token.c_str()), current_index, false);
1000 current_index++;
1001 }
1002
1003 token.clear();
1004 break;
1005
1006 // -ACB- 1998/08/10 String Handling
1007 case string_start:
1008 status = reading_string;
1009 break;
1010
1011 // -ACB- 1998/08/10 String Handling
1012 case string_stop:
1013 status = reading_data;
1014 break;
1015
1016 case terminator:
1017 if (current_cmd.empty())
1018 DDF_Error("Unexpected semicolon `;'.\n");
1019
1020 if (bracket_level > 0)
1021 DDF_Error("Missing ')' bracket in ddf command.\n");
1022
1023 (* readinfo->parse_field)(current_cmd.c_str(),
1024 DDF_MainGetDefine(token.c_str()), current_index, true);
1025 current_index = 0;
1026
1027 token.clear();
1028 status = reading_command;
1029 break;
1030
1031 case property_read:
1032 DDF_WarnError("Badly formed command: Unexpected semicolon `;'\n");
1033 break;
1034
1035 case nothing:
1036 break;
1037
1038 case ok_char:
1039 #if (DEBUG_DDFREAD)
1040 charcount++;
1041 I_Debugf("%c", character);
1042 if (charcount == 75)
1043 {
1044 charcount = 0;
1045 I_Debugf("\n");
1046 }
1047 #endif
1048 break;
1049
1050 default:
1051 break;
1052 }
1053 }
1054
1055 current_cmd.clear();
1056 cur_ddf_linedata.clear();
1057
1058 // -AJA- 1999/10/21: check for unclosed comments
1059 if (comment_level > 0)
1060 DDF_Error("Unclosed comments detected.\n");
1061
1062 if (bracket_level > 0)
1063 DDF_Error("Unclosed () brackets detected.\n");
1064
1065 if (status == reading_tag)
1066 DDF_Error("Unclosed <> brackets detected.\n");
1067
1068 if (status == reading_newdef)
1069 DDF_Error("Unclosed [] brackets detected.\n");
1070
1071 if (status == reading_data || status == reading_string)
1072 DDF_WarnError("Unfinished DDF command on last line.\n");
1073
1074 // if firstgo is true, nothing was defined
1075 if (!firstgo)
1076 (* readinfo->finish_entry)();
1077
1078 cur_ddf_entryname.clear();
1079 cur_ddf_filename.clear();
1080
1081 defines.clear();
1082
1083 if (readinfo->filename)
1084 delete[] memfile;
1085
1086 return true;
1087 }
1088
1089 #if 0
1090 static XXX *parse_type;
1091
1092 bool DDF_Load(epi::file_c *f)
1093 {
1094 byte *data = f->LoadIntoMemory();
1095 if (! data)
1096 return false;
1097
1098 for (;;)
1099 {
1100 get token;
1101
1102 if (token == EOF) break;
1103
1104 if (token == WHITESPACE | COMMENTS) continue;
1105
1106 if (token == DIRECTIVE && entries == 0) handle directive;
1107
1108 if (token == TAG) set type
1109
1110 if (token == ENTRY) begin entry
1111
1112 else parse command to current entry
1113 }
1114
1115 return true;
1116 }
1117 #endif
1118
1119
1120 //
1121 // DDF_MainGetNumeric
1122 //
1123 // Get numeric value directly from the file
1124 //
DDF_MainGetNumeric(const char * info,void * storage)1125 void DDF_MainGetNumeric(const char *info, void *storage)
1126 {
1127 int *dest = (int *)storage;
1128
1129 SYS_ASSERT(info && storage);
1130
1131 if (isalpha(info[0]))
1132 {
1133 DDF_WarnError("Bad numeric value: %s\n", info);
1134 return;
1135 }
1136
1137 // -KM- 1999/01/29 strtol accepts hex and decimal.
1138 *dest = strtol(info, NULL, 0); // straight conversion - no messin'
1139 }
1140
1141 //
1142 // DDF_MainGetBoolean
1143 //
1144 // Get true/false from the file
1145 //
1146 // -KM- 1998/09/01 Gets a true/false value
1147 //
DDF_MainGetBoolean(const char * info,void * storage)1148 void DDF_MainGetBoolean(const char *info, void *storage)
1149 {
1150 bool *dest = (bool *)storage;
1151
1152 SYS_ASSERT(info && storage);
1153
1154 if ((stricmp(info, "TRUE") == 0) || (strcmp(info, "1") == 0))
1155 {
1156 *dest = true;
1157 return;
1158 }
1159
1160 if ((stricmp(info, "FALSE") == 0) || (strcmp(info, "0") == 0))
1161 {
1162 *dest = false;
1163 return;
1164 }
1165
1166 DDF_Error("Bad boolean value: %s\n", info);
1167 }
1168
1169 //
1170 // DDF_MainGetString
1171 //
1172 // Get String value directly from the file
1173 //
1174 // -KM- 1998/07/31 Needed a string argument. Based on DDF_MainGetNumeric.
1175 // -AJA- 2000/02/09: Free any existing string (potential memory leak).
1176 // -ACB- 2004/07/26: Use epi::strent_c
1177 //
DDF_MainGetString(const char * info,void * storage)1178 void DDF_MainGetString(const char *info, void *storage)
1179 {
1180 epi::strent_c *dest = (epi::strent_c *)storage;
1181
1182 SYS_ASSERT(info && storage);
1183
1184 dest->Set(info);
1185 }
1186
1187
1188 //
1189 // DDF_MainParseField
1190 //
1191 // Check if the command exists, and call the parser function if it
1192 // does (and return true), otherwise return false.
1193 //
DDF_MainParseField(const commandlist_t * commands,const char * field,const char * contents,byte * obj_base)1194 bool DDF_MainParseField(const commandlist_t *commands,
1195 const char *field, const char *contents,
1196 byte *obj_base)
1197 {
1198 SYS_ASSERT(obj_base);
1199
1200 for (int i=0; commands[i].name; i++)
1201 {
1202 const char * name = commands[i].name;
1203
1204 if (name[0] == '!')
1205 name++;
1206
1207 // handle subfields
1208 if (name[0] == '*')
1209 {
1210 name++;
1211
1212 int len = strlen(name);
1213 SYS_ASSERT(len > 0);
1214
1215 if (strncmp(field, name, len) == 0 && field[len] == '.' &&
1216 isalnum(field[len+1]))
1217 {
1218 // recursively parse the sub-field
1219 return DDF_MainParseField(commands[i].sub_comms,
1220 field + len + 1, contents,
1221 obj_base + commands[i].offset);
1222 }
1223
1224 continue;
1225 }
1226
1227 if (DDF_CompareName(field, name) != 0)
1228 continue;
1229
1230 // found it, so call parse routine
1231 SYS_ASSERT(commands[i].parse_command);
1232
1233 (* commands[i].parse_command)(contents, obj_base + commands[i].offset);
1234
1235 return true;
1236 }
1237
1238 return false;
1239 }
1240
1241
DDF_MainGetLumpName(const char * info,void * storage)1242 void DDF_MainGetLumpName(const char *info, void *storage)
1243 {
1244 // Gets the string and checks the length is valid for a lump.
1245
1246 SYS_ASSERT(info && storage);
1247
1248 lumpname_c *LN = (lumpname_c *)storage;
1249
1250 if (strlen(info) == 9)
1251 DDF_WarnError("Name %s too long (should be 8 characters or less)\n", info);
1252 else if (strlen(info) > 9)
1253 DDF_Error("Name %s too long (must be 8 characters or less)\n", info);
1254
1255 LN->Set(info);
1256 }
1257
1258
DDF_MainRefAttack(const char * info,void * storage)1259 void DDF_MainRefAttack(const char *info, void *storage)
1260 {
1261 atkdef_c **dest = (atkdef_c **)storage;
1262
1263 SYS_ASSERT(info && storage);
1264
1265 *dest = (atkdef_c*)atkdefs.Lookup(info);
1266 if (*dest == NULL)
1267 DDF_WarnError("Unknown Attack: %s\n", info);
1268 }
1269
1270
DDF_MainLookupDirector(const mobjtype_c * info,const char * ref)1271 int DDF_MainLookupDirector(const mobjtype_c *info, const char *ref)
1272 {
1273 const char *p = strchr(ref, ':');
1274
1275 int len = p ? (p - ref) : strlen(ref);
1276
1277 if (len <= 0)
1278 DDF_Error("Bad Director `%s' : Nothing after divide\n", ref);
1279
1280 std::string director(ref, len);
1281
1282 int state = DDF_StateFindLabel(info->state_grp, director.c_str());
1283 int offset = p ? MAX(0, atoi(p + 1) - 1) : 0;
1284
1285 // FIXME: check for overflow
1286 return state + offset;
1287 }
1288
1289
DDF_MainGetFloat(const char * info,void * storage)1290 void DDF_MainGetFloat(const char *info, void *storage)
1291 {
1292 float *dest = (float *)storage;
1293
1294 SYS_ASSERT(info && storage);
1295
1296 if (strchr(info, '%') != NULL)
1297 {
1298 DDF_MainGetPercentAny(info, storage);
1299 return;
1300 }
1301
1302 if (sscanf(info, "%f", dest) != 1)
1303 DDF_Error("Bad floating point value: %s\n", info);
1304 }
1305
1306 // -AJA- 1999/09/11: Added DDF_MainGetAngle and DDF_MainGetSlope.
1307
DDF_MainGetAngle(const char * info,void * storage)1308 void DDF_MainGetAngle(const char *info, void *storage)
1309 {
1310 SYS_ASSERT(info && storage);
1311
1312 angle_t *dest = (angle_t *)storage;
1313
1314 float val;
1315
1316 if (sscanf(info, "%f", &val) != 1)
1317 DDF_Error("Bad angle value: %s\n", info);
1318
1319 if ((int) val == 360)
1320 val = 359.5;
1321 else if (val > 360.0f)
1322 DDF_WarnError("Angle '%s' too large (must be less than 360)\n", info);
1323
1324 *dest = FLOAT_2_ANG(val);
1325 }
1326
DDF_MainGetSlope(const char * info,void * storage)1327 void DDF_MainGetSlope(const char *info, void *storage)
1328 {
1329 float val;
1330 float *dest = (float *)storage;
1331
1332 SYS_ASSERT(info && storage);
1333
1334 if (sscanf(info, "%f", &val) != 1)
1335 DDF_Error("Bad slope value: %s\n", info);
1336
1337 if (val > +89.5f)
1338 val = +89.5f;
1339 if (val < -89.5f)
1340 val = -89.5f;
1341
1342 *dest = M_Tan(FLOAT_2_ANG(val));
1343 }
1344
1345
DoGetFloat(const char * info,void * storage)1346 static void DoGetFloat(const char *info, void *storage)
1347 {
1348 float *dest = (float *)storage;
1349
1350 SYS_ASSERT(info && storage);
1351
1352 if (sscanf(info, "%f", dest) != 1)
1353 DDF_Error("Bad floating point value: %s\n", info);
1354 }
1355
1356
1357 //
1358 // DDF_MainGetPercent
1359 //
1360 // Reads percentages (0%..100%).
1361 //
DDF_MainGetPercent(const char * info,void * storage)1362 void DDF_MainGetPercent(const char *info, void *storage)
1363 {
1364 percent_t *dest = (percent_t *)storage;
1365 char s[101];
1366 char *p;
1367 float f;
1368
1369 // check that the string is valid
1370 Z_StrNCpy(s, info, 100);
1371 for (p = s; isdigit(*p) || *p == '.'; p++)
1372 { /* do nothing */ }
1373
1374 // the number must be followed by %
1375 if (*p != '%')
1376 {
1377 DDF_WarnError("Bad percent value '%s': Should be a number followed by %%\n", info);
1378 // -AJA- 2001/01/27: backwards compatibility
1379 DoGetFloat(s, &f);
1380 *dest = MAX(0, MIN(1, f));
1381 return;
1382 }
1383
1384 *p = 0;
1385
1386 DoGetFloat(s, &f);
1387 if (f < 0.0f || f > 100.0f)
1388 DDF_Error("Bad percent value '%s': Must be between 0%% and 100%%\n", s);
1389
1390 *dest = f / 100.0f;
1391 }
1392
1393 //
1394 // DDF_MainGetPercentAny
1395 //
1396 // Like the above routine, but allows percentages outside of the
1397 // 0-100% range (which is useful in same instances).
1398 //
DDF_MainGetPercentAny(const char * info,void * storage)1399 void DDF_MainGetPercentAny(const char *info, void *storage)
1400 {
1401 percent_t *dest = (percent_t *)storage;
1402 char s[101];
1403 char *p;
1404 float f;
1405
1406 // check that the string is valid
1407 Z_StrNCpy(s, info, 100);
1408 for (p = s; isdigit(*p) || *p == '.'; p++)
1409 { /* do nothing */ }
1410
1411 // the number must be followed by %
1412 if (*p != '%')
1413 {
1414 DDF_WarnError("Bad percent value '%s': Should be a number followed by %%\n", info);
1415 // -AJA- 2001/01/27: backwards compatibility
1416 DoGetFloat(s, dest);
1417 return;
1418 }
1419
1420 *p = 0;
1421
1422 DoGetFloat(s, &f);
1423
1424 *dest = f / 100.0f;
1425 }
1426
1427 // -KM- 1998/09/27 You can end a number with T to specify tics; ie 35T
1428 // means 35 tics while 3.5 means 3.5 seconds.
1429
DDF_MainGetTime(const char * info,void * storage)1430 void DDF_MainGetTime(const char *info, void *storage)
1431 {
1432 float val;
1433 int *dest = (int *)storage;
1434
1435 SYS_ASSERT(info && storage);
1436
1437 // -ES- 1999/09/14 MAXT means that time should be maximal.
1438 if (!stricmp(info, "maxt"))
1439 {
1440 *dest = INT_MAX; // -ACB- 1999/09/22 Standards, Please.
1441 return;
1442 }
1443
1444 if (strchr(info, 'T'))
1445 {
1446 DDF_MainGetNumeric(info, storage);
1447 return;
1448 }
1449
1450 if (sscanf(info, "%f", &val) != 1)
1451 DDF_Error("Bad time value: %s\n", info);
1452
1453 *dest = (int)(val * (float)TICRATE);
1454 }
1455
1456 //
1457 // DDF_DummyFunction
1458 //
DDF_DummyFunction(const char * info,void * storage)1459 void DDF_DummyFunction(const char *info, void *storage)
1460 {
1461 /* does nothing */
1462 }
1463
1464 //
1465 // DDF_MainGetColourmap
1466 //
DDF_MainGetColourmap(const char * info,void * storage)1467 void DDF_MainGetColourmap(const char *info, void *storage)
1468 {
1469 const colourmap_c **result = (const colourmap_c **)storage;
1470
1471 *result = colourmaps.Lookup(info);
1472 if (*result == NULL)
1473 DDF_Error("DDF_MainGetColourmap: No such colourmap '%s'\n", info);
1474
1475 }
1476
1477 //
1478 // DDF_MainGetRGB
1479 //
DDF_MainGetRGB(const char * info,void * storage)1480 void DDF_MainGetRGB(const char *info, void *storage)
1481 {
1482 rgbcol_t *result = (rgbcol_t *)storage;
1483 int r, g, b;
1484
1485 SYS_ASSERT(info && storage);
1486
1487 if (DDF_CompareName(info, "NONE") == 0)
1488 {
1489 *result = RGB_NO_VALUE;
1490 return;
1491 }
1492
1493 if (sscanf(info, " #%2x%2x%2x ", &r, &g, &b) != 3)
1494 DDF_Error("Bad RGB colour value: %s\n", info);
1495
1496 *result = (r << 16) | (g << 8) | b;
1497
1498 // silently change if matches the "none specified" value
1499 if (*result == RGB_NO_VALUE)
1500 *result ^= RGB_MAKE(1,1,1);
1501 }
1502
1503 //
1504 // DDF_MainGetWhenAppear
1505 //
1506 // Syntax: [ '!' ] [ SKILL ] ':' [ NETMODE ]
1507 //
1508 // SKILL = digit { ':' digit } | digit '-' digit.
1509 // NETMODE = 'sp' | 'coop' | 'dm'.
1510 //
1511 // When no skill was specified, it's as though all were specified.
1512 // Same for the netmode.
1513 //
1514 // -AJA- 2004/10/28: Dodgy-est crap ever, now with ranges and negation.
1515 //
DDF_MainGetWhenAppear(const char * info,void * storage)1516 void DDF_MainGetWhenAppear(const char *info, void *storage)
1517 {
1518 when_appear_e *result = (when_appear_e *)storage;
1519
1520 *result = WNAP_None;
1521
1522 bool negate = (info[0] == '!');
1523
1524 const char *range = strstr(info, "-");
1525
1526 if (range)
1527 {
1528 if (range <= info || range[+1] == 0 ||
1529 range[-1] < '1' || range[-1] > '5' ||
1530 range[+1] < '1' || range[+1] > '5' ||
1531 range[-1] > range[+1])
1532 {
1533 DDF_Error("Bad range in WHEN_APPEAR value: %s\n", info);
1534 return;
1535 }
1536
1537 for (char sk = '1'; sk <= '5'; sk++)
1538 if (range[-1] <= sk && sk <= range[+1])
1539 *result = (when_appear_e)(*result | (WNAP_SkillLevel1 << (sk - '1')));
1540 }
1541 else
1542 {
1543 if (strstr(info, "1"))
1544 *result = (when_appear_e)(*result | WNAP_SkillLevel1);
1545
1546 if (strstr(info, "2"))
1547 *result = (when_appear_e)(*result | WNAP_SkillLevel2);
1548
1549 if (strstr(info, "3"))
1550 *result = (when_appear_e)(*result | WNAP_SkillLevel3);
1551
1552 if (strstr(info, "4"))
1553 *result = (when_appear_e)(*result | WNAP_SkillLevel4);
1554
1555 if (strstr(info, "5"))
1556 *result = (when_appear_e)(*result | WNAP_SkillLevel5);
1557 }
1558
1559 if (strstr(info, "SP") || strstr(info, "sp"))
1560 *result = (when_appear_e)(*result| WNAP_Single);
1561
1562 if (strstr(info, "COOP") || strstr(info, "coop"))
1563 *result = (when_appear_e)(*result | WNAP_Coop);
1564
1565 if (strstr(info, "DM") || strstr(info, "dm"))
1566 *result = (when_appear_e)(*result | WNAP_DeathMatch);
1567
1568 // allow more human readable strings...
1569
1570 if (negate)
1571 *result = (when_appear_e)(*result ^ (WNAP_SkillBits | WNAP_NetBits));
1572
1573 if ((*result & WNAP_SkillBits) == 0)
1574 *result = (when_appear_e)(*result | WNAP_SkillBits);
1575
1576 if ((*result & WNAP_NetBits) == 0)
1577 *result = (when_appear_e)(*result | WNAP_NetBits);
1578 }
1579
1580 #if 0 // DEBUGGING ONLY
1581 void Test_ParseWhenAppear(void)
1582 {
1583 when_appear_e val;
1584
1585 DDF_MainGetWhenAppear("1", &val); printf("WNAP '1' --> 0x%04x\n", val);
1586 DDF_MainGetWhenAppear("3", &val); printf("WNAP '3' --> 0x%04x\n", val);
1587 DDF_MainGetWhenAppear("5", &val); printf("WNAP '5' --> 0x%04x\n", val);
1588 DDF_MainGetWhenAppear("7", &val); printf("WNAP '7' --> 0x%04x\n", val);
1589
1590 DDF_MainGetWhenAppear("1:2", &val); printf("WNAP '1:2' --> 0x%04x\n", val);
1591 DDF_MainGetWhenAppear("5:3:1", &val); printf("WNAP '5:3:1' --> 0x%04x\n", val);
1592
1593 DDF_MainGetWhenAppear("1-3", &val); printf("WNAP '1-3' --> 0x%04x\n", val);
1594 DDF_MainGetWhenAppear("4-5", &val); printf("WNAP '4-5' --> 0x%04x\n", val);
1595 DDF_MainGetWhenAppear("0-2", &val); printf("WNAP '0-2' --> 0x%04x\n", val);
1596 DDF_MainGetWhenAppear("3-6", &val); printf("WNAP '3-6' --> 0x%04x\n", val);
1597 DDF_MainGetWhenAppear("5-1", &val); printf("WNAP '5-1' --> 0x%04x\n", val);
1598
1599 DDF_MainGetWhenAppear("sp", &val); printf("WNAP 'sp' --> 0x%04x\n", val);
1600 DDF_MainGetWhenAppear("coop", &val); printf("WNAP 'coop' --> 0x%04x\n", val);
1601 DDF_MainGetWhenAppear("dm", &val); printf("WNAP 'dm' --> 0x%04x\n", val);
1602 DDF_MainGetWhenAppear("sp:coop", &val); printf("WNAP 'sp:coop' --> 0x%04x\n", val);
1603 DDF_MainGetWhenAppear("sp:dm", &val); printf("WNAP 'sp:dm' --> 0x%04x\n", val);
1604 DDF_MainGetWhenAppear("sp:coop:dm", &val); printf("WNAP 'sp:coop:dm' --> 0x%04x\n", val);
1605
1606 DDF_MainGetWhenAppear("sp:3", &val); printf("WNAP 'sp:3' --> 0x%04x\n", val);
1607 DDF_MainGetWhenAppear("sp:3:4", &val); printf("WNAP 'sp:3:4' --> 0x%04x\n", val);
1608 DDF_MainGetWhenAppear("sp:3-5", &val); printf("WNAP 'sp:3-5' --> 0x%04x\n", val);
1609
1610 DDF_MainGetWhenAppear("sp:dm:3", &val); printf("WNAP 'sp:dm:3' --> 0x%04x\n", val);
1611 DDF_MainGetWhenAppear("sp:dm:3:4", &val); printf("WNAP 'sp:dm:3:4' --> 0x%04x\n", val);
1612 DDF_MainGetWhenAppear("sp:dm:3-5", &val); printf("WNAP 'sp:dm:3-5' --> 0x%04x\n", val);
1613
1614 DDF_MainGetWhenAppear("DM:COOP:SP:3", &val); printf("WNAP 'DM:COOP:SP:3' --> 0x%04x\n", val);
1615 DDF_MainGetWhenAppear("DM:COOP:SP:3:4", &val); printf("WNAP 'DM:COOP:SP:3:4' --> 0x%04x\n", val);
1616 DDF_MainGetWhenAppear("DM:COOP:SP:3-5", &val); printf("WNAP 'DM:COOP:SP:3-5' --> 0x%04x\n", val);
1617
1618 printf("------------------------------------------------------------\n");
1619
1620 DDF_MainGetWhenAppear("!1", &val); printf("WNAP '!1' --> 0x%04x\n", val);
1621 DDF_MainGetWhenAppear("!3", &val); printf("WNAP '!3' --> 0x%04x\n", val);
1622 DDF_MainGetWhenAppear("!5", &val); printf("WNAP '!5' --> 0x%04x\n", val);
1623 DDF_MainGetWhenAppear("!7", &val); printf("WNAP '!7' --> 0x%04x\n", val);
1624
1625 DDF_MainGetWhenAppear("!1:2", &val); printf("WNAP '!1:2' --> 0x%04x\n", val);
1626 DDF_MainGetWhenAppear("!5:3:1", &val); printf("WNAP '!5:3:1' --> 0x%04x\n", val);
1627
1628 DDF_MainGetWhenAppear("!1-3", &val); printf("WNAP '!1-3' --> 0x%04x\n", val);
1629 DDF_MainGetWhenAppear("!4-5", &val); printf("WNAP '!4-5' --> 0x%04x\n", val);
1630 DDF_MainGetWhenAppear("!0-2", &val); printf("WNAP '!0-2' --> 0x%04x\n", val);
1631 DDF_MainGetWhenAppear("!3-6", &val); printf("WNAP '!3-6' --> 0x%04x\n", val);
1632 DDF_MainGetWhenAppear("!5-1", &val); printf("WNAP '!5-1' --> 0x%04x\n", val);
1633
1634 DDF_MainGetWhenAppear("!sp", &val); printf("WNAP '!sp' --> 0x%04x\n", val);
1635 DDF_MainGetWhenAppear("!coop", &val); printf("WNAP '!coop' --> 0x%04x\n", val);
1636 DDF_MainGetWhenAppear("!dm", &val); printf("WNAP '!dm' --> 0x%04x\n", val);
1637 DDF_MainGetWhenAppear("!sp:coop", &val); printf("WNAP '!sp:coop' --> 0x%04x\n", val);
1638 DDF_MainGetWhenAppear("!sp:dm", &val); printf("WNAP '!sp:dm' --> 0x%04x\n", val);
1639 DDF_MainGetWhenAppear("!sp:coop:dm", &val); printf("WNAP '!sp:coop:dm' --> 0x%04x\n", val);
1640
1641 DDF_MainGetWhenAppear("!sp:3", &val); printf("WNAP '!sp:3' --> 0x%04x\n", val);
1642 DDF_MainGetWhenAppear("!sp:3:4", &val); printf("WNAP '!sp:3:4' --> 0x%04x\n", val);
1643 DDF_MainGetWhenAppear("!sp:3-5", &val); printf("WNAP '!sp:3-5' --> 0x%04x\n", val);
1644
1645 DDF_MainGetWhenAppear("!sp:dm:3", &val); printf("WNAP '!sp:dm:3' --> 0x%04x\n", val);
1646 DDF_MainGetWhenAppear("!sp:dm:3:4", &val); printf("WNAP '!sp:dm:3:4' --> 0x%04x\n", val);
1647 DDF_MainGetWhenAppear("!sp:dm:3-5", &val); printf("WNAP '!sp:dm:3-5' --> 0x%04x\n", val);
1648
1649 DDF_MainGetWhenAppear("!DM:COOP:SP:3", &val); printf("WNAP '!DM:COOP:SP:3' --> 0x%04x\n", val);
1650 DDF_MainGetWhenAppear("!DM:COOP:SP:3:4", &val); printf("WNAP '!DM:COOP:SP:3:4' --> 0x%04x\n", val);
1651 DDF_MainGetWhenAppear("!DM:COOP:SP:3-5", &val); printf("WNAP '!DM:COOP:SP:3-5' --> 0x%04x\n", val);
1652 }
1653 #endif
1654
1655 //
1656 // DDF_MainGetBitSet
1657 //
DDF_MainGetBitSet(const char * info,void * storage)1658 void DDF_MainGetBitSet(const char *info, void *storage)
1659 {
1660 bitset_t *result = (bitset_t *)storage;
1661 int start, end;
1662
1663 SYS_ASSERT(info && storage);
1664
1665 // allow a numeric value
1666 if (sscanf(info, " %i ", result) == 1)
1667 return;
1668
1669 *result = BITSET_EMPTY;
1670
1671 for (; *info; info++)
1672 {
1673 if (*info < 'A' || *info > 'Z')
1674 continue;
1675
1676 start = end = (*info) - 'A';
1677
1678 // handle ranges
1679 if (info[1] == '-' && 'A' <= info[2] && info[2] <= 'Z' &&
1680 info[2] >= info[0])
1681 {
1682 end = info[2] - 'A';
1683 }
1684
1685 for (; start <= end; start++)
1686 (*result) |= (1 << start);
1687 }
1688 }
1689
FindSpecialFlag(const char * prefix,const char * name,const specflags_t * flag_set)1690 static int FindSpecialFlag(const char *prefix, const char *name,
1691 const specflags_t *flag_set)
1692 {
1693 int i;
1694 char try_name[512];
1695
1696 for (i=0; flag_set[i].name; i++)
1697 {
1698 const char *current = flag_set[i].name;
1699
1700 if (current[0] == '!')
1701 current++;
1702
1703 sprintf(try_name, "%s%s", prefix, current);
1704
1705 if (DDF_CompareName(name, try_name) == 0)
1706 return i;
1707 }
1708
1709 return -1;
1710 }
1711
DDF_MainCheckSpecialFlag(const char * name,const specflags_t * flag_set,int * flag_value,bool allow_prefixes,bool allow_user)1712 checkflag_result_e DDF_MainCheckSpecialFlag(const char *name,
1713 const specflags_t *flag_set, int *flag_value,
1714 bool allow_prefixes, bool allow_user)
1715 {
1716 int index;
1717 int negate = 0;
1718 int user = 0;
1719
1720 // try plain name...
1721 index = FindSpecialFlag("", name, flag_set);
1722
1723 if (allow_prefixes)
1724 {
1725 // try name with ENABLE_ prefix...
1726 if (index == -1)
1727 {
1728 index = FindSpecialFlag("ENABLE_", name, flag_set);
1729 }
1730
1731 // try name with NO_ prefix...
1732 if (index == -1)
1733 {
1734 negate = 1;
1735 index = FindSpecialFlag("NO_", name, flag_set);
1736 }
1737
1738 // try name with NOT_ prefix...
1739 if (index == -1)
1740 {
1741 negate = 1;
1742 index = FindSpecialFlag("NOT_", name, flag_set);
1743 }
1744
1745 // try name with DISABLE_ prefix...
1746 if (index == -1)
1747 {
1748 negate = 1;
1749 index = FindSpecialFlag("DISABLE_", name, flag_set);
1750 }
1751
1752 // try name with USER_ prefix...
1753 if (index == -1 && allow_user)
1754 {
1755 user = 1;
1756 negate = 0;
1757 index = FindSpecialFlag("USER_", name, flag_set);
1758 }
1759 }
1760
1761 if (index < 0)
1762 return CHKF_Unknown;
1763
1764 (*flag_value) = flag_set[index].flags;
1765
1766 if (flag_set[index].negative)
1767 negate = !negate;
1768
1769 if (user)
1770 return CHKF_User;
1771
1772 if (negate)
1773 return CHKF_Negative;
1774
1775 return CHKF_Positive;
1776 }
1777
1778 //
1779 // DDF_DecodeBrackets
1780 //
1781 // Decode a keyword followed by something in () brackets. Buf_len gives
1782 // the maximum size of the output buffers. The outer keyword is required
1783 // to be non-empty, though the inside can be empty. Returns false if
1784 // cannot be parsed (e.g. no brackets). Handles strings.
1785 //
DDF_MainDecodeBrackets(const char * info,char * outer,char * inner,int buf_len)1786 bool DDF_MainDecodeBrackets(const char *info, char *outer, char *inner,
1787 int buf_len)
1788 {
1789 const char *pos = info;
1790
1791 while (*pos && *pos != '(')
1792 pos++;
1793
1794 if (*pos == 0 || pos == info)
1795 return false;
1796
1797 if (pos - info >= buf_len) // overflow
1798 return false;
1799
1800 strncpy(outer, info, pos - info);
1801 outer[pos - info] = 0;
1802
1803 pos++; // skip the '('
1804
1805 info = pos;
1806
1807 bool in_string = false;
1808
1809 while (*pos && (in_string || *pos != ')'))
1810 {
1811 // handle escaped quotes
1812 if (pos[0] == '\\' && pos[1] == '"')
1813 {
1814 pos += 2;
1815 continue;
1816 }
1817
1818 if (*pos == '"')
1819 in_string = ! in_string;
1820
1821 pos++;
1822 }
1823
1824 if (*pos == 0)
1825 return false;
1826
1827 if (pos - info >= buf_len) // overflow
1828 return false;
1829
1830 strncpy(inner, info, pos - info);
1831 inner[pos - info] = 0;
1832
1833 return true;
1834 }
1835
1836 //
1837 // DDF_MainDecodeList
1838 //
1839 // Find the dividing character. Returns NULL if not found.
1840 // Handles strings and brackets unless simple is true.
1841 //
DDF_MainDecodeList(const char * info,char divider,bool simple)1842 const char *DDF_MainDecodeList(const char *info, char divider, bool simple)
1843 {
1844 int brackets = 0;
1845 bool in_string = false;
1846
1847 const char *pos = info;
1848
1849 for (;;)
1850 {
1851 if (*pos == 0)
1852 break;
1853
1854 if (brackets == 0 && !in_string && *pos == divider)
1855 return pos;
1856
1857 // handle escaped quotes
1858 if (! simple)
1859 {
1860 if (pos[0] == '\\' && pos[1] == '"')
1861 {
1862 pos += 2;
1863 continue;
1864 }
1865
1866 if (*pos == '"')
1867 in_string = ! in_string;
1868
1869 if (!in_string && *pos == '(')
1870 brackets++;
1871
1872 if (!in_string && *pos == ')')
1873 {
1874 brackets--;
1875 if (brackets < 0)
1876 DDF_Error("Too many ')' found: %s\n", info);
1877 }
1878 }
1879
1880 pos++;
1881 }
1882
1883 if (in_string)
1884 DDF_Error("Unterminated string found: %s\n", info);
1885
1886 if (brackets != 0)
1887 DDF_Error("Unclosed brackets found: %s\n", info);
1888
1889 return NULL;
1890 }
1891
1892 // DDF OBJECTS
1893
1894
1895 // ---> mobj_strref class
1896
GetRef()1897 const mobjtype_c *mobj_strref_c::GetRef()
1898 {
1899 if (def)
1900 return def;
1901
1902 def = mobjtypes.Lookup(name.c_str());
1903
1904 return def;
1905 }
1906
1907 // ---> damage class
1908
1909 //
1910 // damage_c Constructor
1911 //
damage_c()1912 damage_c::damage_c()
1913 {
1914 }
1915
1916 //
1917 // damage_c Copy constructor
1918 //
damage_c(damage_c & rhs)1919 damage_c::damage_c(damage_c &rhs)
1920 {
1921 Copy(rhs);
1922 }
1923
1924 //
1925 // damage_c Destructor
1926 //
~damage_c()1927 damage_c::~damage_c()
1928 {
1929 }
1930
1931 //
1932 // damage_c::Copy
1933 //
Copy(damage_c & src)1934 void damage_c::Copy(damage_c &src)
1935 {
1936 nominal = src.nominal;
1937 linear_max = src.linear_max;
1938 error = src.error;
1939 delay = src.delay;
1940
1941 obituary = src.obituary;
1942 pain = src.pain;
1943 death = src.death;
1944 overkill = src.overkill;
1945
1946 no_armour = src.no_armour;
1947 }
1948
1949 //
1950 // damage_c::Default
1951 //
Default(damage_c::default_e def)1952 void damage_c::Default(damage_c::default_e def)
1953 {
1954 obituary.clear();
1955
1956 switch (def)
1957 {
1958 case DEFAULT_MobjChoke:
1959 {
1960 nominal = 6.0f;
1961 linear_max = 14.0f;
1962 error = -1.0f;
1963 delay = 2 * TICRATE;
1964 obituary = "OB_DROWN";
1965 no_armour = true;
1966 break;
1967 }
1968
1969 case DEFAULT_Sector:
1970 {
1971 nominal = 0.0f;
1972 linear_max = -1.0f;
1973 error = -1.0f;
1974 delay = 31;
1975 no_armour = false;
1976 break;
1977 }
1978
1979 case DEFAULT_Attack:
1980 case DEFAULT_Mobj:
1981 default:
1982 {
1983 nominal = 0.0f;
1984 linear_max = -1.0f;
1985 error = -1.0f;
1986 delay = 0;
1987 no_armour = false;
1988 break;
1989 }
1990 }
1991
1992 pain.Default();
1993 death.Default();
1994 overkill.Default();
1995 }
1996
1997 //
1998 // damage_c assignment operator
1999 //
operator =(damage_c & rhs)2000 damage_c& damage_c::operator=(damage_c &rhs)
2001 {
2002 if (&rhs != this)
2003 Copy(rhs);
2004
2005 return *this;
2006 }
2007
2008 // ---> label offset class
2009
2010 //
2011 // label_offset_c Constructor
2012 //
label_offset_c()2013 label_offset_c::label_offset_c()
2014 {
2015 offset = 0;
2016 }
2017
2018 //
2019 // label_offset_c Copy constructor
2020 //
label_offset_c(label_offset_c & rhs)2021 label_offset_c::label_offset_c(label_offset_c &rhs)
2022 {
2023 Copy(rhs);
2024 }
2025
2026 //
2027 // label_offset_c Destructor
2028 //
~label_offset_c()2029 label_offset_c::~label_offset_c()
2030 {
2031 }
2032
2033 //
2034 // label_offset_c::Copy
2035 //
Copy(label_offset_c & src)2036 void label_offset_c::Copy(label_offset_c &src)
2037 {
2038 label = src.label;
2039 offset = src.offset;
2040 }
2041
2042 //
2043 // label_offset_c::Default
2044 //
Default()2045 void label_offset_c::Default()
2046 {
2047 label.clear();
2048 offset = 0;
2049 }
2050
2051
2052 //
2053 // label_offset_c assignment operator
2054 //
operator =(label_offset_c & rhs)2055 label_offset_c& label_offset_c::operator=(label_offset_c& rhs)
2056 {
2057 if (&rhs != this)
2058 Copy(rhs);
2059
2060 return *this;
2061 }
2062
2063 // ---> dlight_info class
2064
dlight_info_c()2065 dlight_info_c::dlight_info_c()
2066 {
2067 Default();
2068 }
2069
dlight_info_c(dlight_info_c & rhs)2070 dlight_info_c::dlight_info_c(dlight_info_c &rhs)
2071 {
2072 Copy(rhs);
2073 }
2074
Copy(dlight_info_c & src)2075 void dlight_info_c::Copy(dlight_info_c &src)
2076 {
2077 type = src.type;
2078 shape = src.shape;
2079 radius = src.radius;
2080 colour = src.colour;
2081 height = src.height;
2082 leaky = src.leaky;
2083
2084 cache_data = NULL;
2085 }
2086
Default()2087 void dlight_info_c::Default()
2088 {
2089 type = DLITE_None;
2090 radius = 32;
2091 colour = RGB_MAKE(255, 255, 255);
2092 height = PERCENT_MAKE(50);
2093 leaky = false;
2094
2095 shape.Set("DLIGHT_EXP");
2096
2097 cache_data = NULL;
2098 }
2099
operator =(dlight_info_c & rhs)2100 dlight_info_c& dlight_info_c::operator= (dlight_info_c &rhs)
2101 {
2102 CHECK_SELF_ASSIGN(rhs);
2103
2104 Copy(rhs);
2105
2106 return *this;
2107 }
2108
2109 // ---> weakness_info class
2110
weakness_info_c()2111 weakness_info_c::weakness_info_c()
2112 {
2113 Default();
2114 }
2115
weakness_info_c(weakness_info_c & rhs)2116 weakness_info_c::weakness_info_c(weakness_info_c &rhs)
2117 {
2118 Copy(rhs);
2119 }
2120
Copy(weakness_info_c & src)2121 void weakness_info_c::Copy(weakness_info_c &src)
2122 {
2123 height[0] = src.height[0];
2124 height[1] = src.height[1];
2125 angle[0] = src.angle[0];
2126 angle[1] = src.angle[1];
2127
2128 classes = src.classes;
2129 multiply = src.multiply;
2130 painchance = src.painchance;
2131 }
2132
Default()2133 void weakness_info_c::Default()
2134 {
2135 height[0] = PERCENT_MAKE( 0);
2136 height[1] = PERCENT_MAKE(100);
2137
2138 angle[0] = ANG0;
2139 angle[1] = ANG_MAX;
2140
2141 classes = BITSET_EMPTY;
2142 multiply = 2.5;
2143 painchance = -1; // disabled
2144 }
2145
operator =(weakness_info_c & rhs)2146 weakness_info_c& weakness_info_c::operator=(weakness_info_c &rhs)
2147 {
2148 CHECK_SELF_ASSIGN(rhs);
2149
2150 Copy(rhs);
2151
2152 return *this;
2153 }
2154
2155 //--- editor settings ---
2156 // vi:ts=4:sw=4:noexpandtab
2157