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