7 * low level parse routines common to all types of parsers
10 * Revision 1.4 2002/05/28 06:45:38 theoddone33
13 * Revision 1.3 2002/05/28 06:28:20 theoddone33
14 * Filesystem mods, actually reads some data files now
16 * Revision 1.2 2002/05/07 03:16:48 theoddone33
17 * The Great Newline Fix
19 * Revision 1.1.1.1 2002/05/03 03:28:10 root
23 * 12 8/10/99 6:54p Dave
24 * Mad optimizations. Added paging to the nebula effect.
26 * 11 6/02/99 2:22p Andsager
29 * 10 5/20/99 11:25a Andsager
30 * Added error checking for parsing table files.
32 * 9 2/23/99 11:18a Andsager
33 * Localize launcher using strings.tbl
35 * 8 2/03/99 6:06p Dave
36 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
38 * 7 11/05/98 4:18p Dave
39 * First run nebula support. Beefed up localization a bit. Removed all
40 * conditional compiles for foreign versions. Modified mission file
43 * 6 10/29/98 12:49p Dave
44 * Intermediate checkin for Fred hash table stuff.
46 * 5 10/28/98 11:30a Dave
49 * 4 10/22/98 6:14p Dave
50 * Optimized some #includes in Anim folder. Put in the beginnings of
51 * parse/localization support for externalized strings and tstrings.tbl
53 * 3 10/14/98 1:15p Andsager
56 * 2 10/07/98 10:53a Dave
59 * 1 10/07/98 10:50a Dave
61 * 98 9/21/98 2:37p Adam
62 * fixed font translation bug
64 * 97 7/29/98 9:39a Hoffoss
65 * Fixed bug with stuff_and_malloc_string
67 * 96 6/22/98 11:18a Hoffoss
68 * Fixed bug where mission notes field gets an extra linefeed with each
71 * 95 6/19/98 3:53p Lawrance
72 * account for some new foreign chars
74 * 94 6/12/98 7:37p Hoffoss
75 * Made ß translate to ss, since we don't have this character in our font.
77 * 93 6/12/98 4:52p Hoffoss
78 * Added support for special characters in in forgeign languages.
80 * 92 5/21/98 3:26p Dave
81 * Fixed bug in new split string function. Newline as last character did
84 * 91 5/21/98 3:01p Dave
85 * Make sure split_str handles 0 length strings correctly.
87 * 90 5/21/98 2:14a Hoffoss
88 * Rewrote split_str() to solve a problem and it was too hard for me to
89 * figure out (oow, my head hurts still!)
105 #include "missionparse.h"
108 #include "localize.h"
111 #define ERROR_LENGTH 64
112 #define RS_MAX_TRIES 5
114 char Current_filename[128];
115 char Error_str[ERROR_LENGTH];
117 int Warning_count, Error_count;
118 int fred_parse_flag = 0;
119 int Token_found_flag;
122 char Mission_text[MISSION_TEXT_SIZE];
123 char Mission_text_raw[MISSION_TEXT_SIZE];
127 // Return true if this character is white space, else false.
128 int is_white_space(char ch)
130 return ((ch == ' ') || (ch == '\t') || (ch == EOLN));
133 // Returns true if this character is gray space, else false (gray space is white space except for EOLN).
134 int is_gray_space(char ch)
136 return ((ch == ' ') || (ch == '\t'));
139 // Advance global Mp (mission pointer) past all current white space.
140 // Leaves Mp pointing at first non white space character.
141 void ignore_white_space()
143 while ((*Mp != EOF_CHAR) && is_white_space(*Mp))
147 void ignore_gray_space()
149 while ((*Mp != EOF_CHAR) && is_gray_space(*Mp))
153 // Truncate *str, eliminating all trailing white space.
154 // Eg: "abc " becomes "abc"
155 // "abc abc " becomes "abc abc"
156 // "abc \t" becomes "abc"
157 void drop_trailing_white_space(char *str)
163 while ((i >= 0) && is_white_space(str[i]))
169 // Eliminate any leading whitespace in str
170 void drop_leading_white_space(char *str)
177 while ((i < len) && is_white_space(str[i]))
180 memmove(str, str+i, len-i);
184 // eliminates all leading and trailing white space from a string. Returns pointer passed in.
185 char *drop_white_space(char *str)
190 while (str[s] && is_white_space(str[s]))
195 if (!is_white_space(str[e]))
202 memmove(str, str + s, e - s + 1);
208 // Advances Mp past current token.
211 ignore_white_space();
213 while ((*Mp != EOF_CHAR) && !is_white_space(*Mp))
217 // Display a diagnostic message if Verbose is set.
218 // (Verbose is set if -v command line switch is present.)
219 void diag_printf(char *format, ...)
224 va_start(args, format);
225 vsprintf(buffer, format, args);
228 nprintf(("Parse", "%s", buffer));
231 // Grab and return (a pointer to) a bunch of tokens, terminating at
232 // ERROR_LENGTH chars, or end of line.
239 while (((ch = *pstr++) != EOLN) && (ch != EOF_CHAR) && (count < ERROR_LENGTH-1))
240 Error_str[count++] = ch;
242 Error_str[count] = 0;
246 // Return the line number given by the current mission pointer, ie Mp.
247 // A very slow function (scans all processed text), but who cares how long
248 // an error reporting function takes?
257 p = Mission_text_raw;
258 stoploc = (Mp - Mission_text) + p;
265 if ( !incomment && (*p == COMMENT_CHAR) )
268 if ( !incomment && (*p == '/') && (*(p+1) == '*') ) {
276 if ( multiline && (*(p-1) == '*') && (*p == '/') ) {
282 if ( !multiline && incomment )
291 // Call this function to display an error message.
292 // error_level == 0 means this is just a warning.
293 // !0 means it's an error message.
294 // Prints line number and other useful information.
295 void error_display(int error_level, char *format, ...)
298 char error_text[128];
301 if (error_level == 0) {
302 strcpy(error_text, "Warning");
305 strcpy(error_text, "Error");
309 nprintf((error_text, "%s(%i):%s: ", Current_filename, get_line_num(), error_text));
311 va_start(args, format);
312 vsprintf(buffer, format, args);
314 Assert(strlen(buffer) < 1024);
316 nprintf((error_text, "%s", buffer));
317 Warning(LOCATION, "%s(%i):\n%s: %s", Current_filename, get_line_num(), error_text, buffer);
320 // Advance Mp to the next eoln character.
321 void advance_to_eoln(char *more_terminators)
323 char terminators[128];
325 Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
327 terminators[0] = EOLN;
328 terminators[1] = (char)EOF_CHAR;
329 if (more_terminators != NULL)
330 strcpy(&terminators[2], more_terminators);
334 while (strchr(terminators, *Mp) == NULL)
338 // Advance Mp to the next white space (ignoring white space inside of " marks)
339 void advance_to_next_white()
343 while ((*Mp != EOLN) && (*Mp != EOF_CHAR)) {
345 in_quotes = !in_quotes;
347 if (!in_quotes && is_white_space(*Mp))
353 // Search for specified string, skipping everything up to that point. Returns 1 if found,
354 // 0 if string wasn't found (and hit end of file), or -1 if not found, but end of checking
355 // block was reached.
356 int skip_to_string(char *pstr, char *end)
360 ignore_white_space();
365 while ((*Mp != EOF_CHAR) && strnicmp(pstr, Mp, len)) {
366 if (end && *Mp == '#')
369 if (end && !strnicmp(pstr, Mp, len2))
372 advance_to_eoln(NULL);
373 ignore_white_space();
376 if (!Mp || (*Mp == EOF_CHAR))
383 // Advance to start of either pstr1 or pstr2. Return 0 is successful, otherwise return !0
384 int skip_to_start_of_strings(char *pstr1, char *pstr2)
388 ignore_white_space();
389 len1 = strlen(pstr1);
390 len2 = strlen(pstr2);
392 while ( (*Mp != EOF_CHAR) && strnicmp(pstr1, Mp, len1) && strnicmp(pstr2, Mp, len2) ) {
393 advance_to_eoln(NULL);
394 ignore_white_space();
397 if (!Mp || (*Mp == EOF_CHAR))
403 // Find a required string.
404 // If not found, display an error message, but try up to RS_MAX_TRIES times
405 // to find the string. (This is the groundwork for ignoring non-understood
407 // If unable to find the required string after RS_MAX_TRIES tries, then
408 // abort using longjmp to parse_abort.
409 int required_string(char *pstr)
413 ignore_white_space();
415 while (strnicmp(pstr, Mp, strlen(pstr)) && (count < RS_MAX_TRIES)) {
416 error_display(1, "Required token = [%s], found [%.32s].\n", pstr, next_tokens());
417 advance_to_eoln(NULL);
418 ignore_white_space();
422 if (count == RS_MAX_TRIES) {
423 nprintf(("Error", "Error: Unable to find required token [%s]\n", pstr));
424 Warning(LOCATION, "Error: Unable to find required token [%s]\n", pstr);
425 longjmp(parse_abort, 1);
429 diag_printf("Found required string [%s]\n", token_found = pstr);
433 // similar to optional_string, but just checks if next token is a match.
434 // It doesn't advance Mp.
436 int check_for_string(char *pstr)
438 ignore_white_space();
440 if (!strnicmp(pstr, Mp, strlen(pstr)))
446 // like check for string, but doesn't skip past any whitespace
447 int check_for_string_raw(char *pstr)
449 if (!strnicmp(pstr, Mp, strlen(pstr))){
456 // Find an optional string.
457 // If found, return 1, else return 0.
458 // If found, point past string, else don't update pointer.
459 int optional_string(char *pstr)
461 ignore_white_space();
463 if (!strnicmp(pstr, Mp, strlen(pstr))) {
471 int required_string_fred(char *pstr, char *end)
479 ignore_white_space();
480 while (*Mp != EOF_CHAR && strnicmp(pstr, Mp, strlen(pstr))) {
481 if ((*Mp == '#') || (end && !strnicmp(end, Mp, strlen(end)))) {
486 advance_to_eoln(NULL);
487 ignore_white_space();
490 if (!Mp || (*Mp == EOF_CHAR)) {
491 diag_printf("Required string [%s] not found\n", pstr);
493 Token_found_flag = 0;
498 diag_printf("Found required string [%s]\n", pstr);
499 Token_found_flag = 1;
503 // attempt to find token in buffer. It might not exist, however, in which case we don't need
504 // to do anything. If it is found, then we advance the pointer to just after the token. To
505 // further complicate things, we should only search to a certain point, since we don't want
506 // a token that belongs to another section which might match the token we want. Thus, we
507 // also pass in an ending token, which marks the point we should stop looking at.
508 int optional_string_fred(char *pstr, char *end, char *end2)
516 ignore_white_space();
517 while ((*Mp != EOF_CHAR) && strnicmp(pstr, Mp, strlen(pstr))) {
518 if ((*Mp == '#') || (end && !strnicmp(end, Mp, strlen(end))) ||
519 (end2 && !strnicmp(end2, Mp, strlen(end2)))) {
524 advance_to_eoln(NULL);
525 ignore_white_space();
528 if (!Mp || (*Mp == EOF_CHAR)) {
529 diag_printf("Optional string [%s] not found\n", pstr);
531 Token_found_flag = 0;
536 diag_printf("Found optional string [%s]\n", pstr);
537 Token_found_flag = 1;
541 // Return 0 or 1 for str1 match, str2 match. Return -1 if neither matches.
542 // Does not update Mp if token found. If not found, advances, trying to
543 // find the string. Doesn't advance past the found string.
544 int required_string_either(char *str1, char *str2)
548 ignore_white_space();
550 while (count < RS_MAX_TRIES) {
551 if (strnicmp(str1, Mp, strlen(str1)) == 0) {
552 // Mp += strlen(str1);
553 diag_printf("Found required string [%s]\n", token_found = str1);
555 } else if (strnicmp(str2, Mp, strlen(str2)) == 0) {
556 // Mp += strlen(str2);
557 diag_printf("Found required string [%s]\n", token_found = str2);
561 error_display(1, "Required token = [%s] or [%s], found [%.32s].\n", str1, str2, next_tokens());
563 advance_to_eoln(NULL);
564 ignore_white_space();
568 if (count == RS_MAX_TRIES) {
569 nprintf(("Error", "Error: Unable to find either required token [%s] or [%s]\n", str1, str2));
570 Warning(LOCATION, "Error: Unable to find either required token [%s] or [%s]\n", str1, str2);
571 longjmp(parse_abort, 2);
578 // Return 0 or 1 for str1 match, str2 match. Return -1 if neither matches.
579 // Does not update Mp if token found. If not found, advances, trying to
580 // find the string. Doesn't advance past the found string.
581 int required_string_3(char *str1, char *str2, char *str3)
585 ignore_white_space();
587 while (count < RS_MAX_TRIES) {
588 if (strnicmp(str1, Mp, strlen(str1)) == 0) {
589 // Mp += strlen(str1);
590 diag_printf("Found required string [%s]\n", token_found = str1);
592 } else if (strnicmp(str2, Mp, strlen(str2)) == 0) {
593 // Mp += strlen(str2);
594 diag_printf("Found required string [%s]\n", token_found = str2);
596 } else if (strnicmp(str3, Mp, strlen(str3)) == 0) {
597 diag_printf("Found required string [%s]\n", token_found = str3);
601 error_display(1, "Required token = [%s], [%s] or [%s], found [%.32s].\n", str1, str2, str3, next_tokens());
603 advance_to_eoln(NULL);
604 ignore_white_space();
612 int required_string_either_fred(char *str1, char *str2)
614 ignore_white_space();
616 while (*Mp != EOF_CHAR) {
617 if (!strnicmp(str1, Mp, strlen(str1))) {
618 // Mp += strlen(str1);
619 diag_printf("Found required string [%s]\n", token_found = str1);
620 return fred_parse_flag = 0;
622 } else if (!strnicmp(str2, Mp, strlen(str2))) {
623 // Mp += strlen(str2);
624 diag_printf("Found required string [%s]\n", token_found = str2);
625 return fred_parse_flag = 1;
628 advance_to_eoln(NULL);
629 ignore_white_space();
633 diag_printf("Unable to find either required token [%s] or [%s]\n", str1, str2);
639 // Copy characters from instr to outstr until eoln is found, or until max
640 // characters have been copied (including terminator).
641 void copy_to_eoln(char *outstr, char *more_terminators, char *instr, int max)
645 char terminators[128];
647 Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
649 terminators[0] = EOLN;
650 terminators[1] = (char)EOF_CHAR;
651 if (more_terminators != NULL)
652 strcpy(&terminators[2], more_terminators);
656 while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL) && (count < max)) {
662 error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
667 // Copy characters from instr to outstr until next white space is found, or until max
668 // characters have been copied (including terminator).
669 void copy_to_next_white(char *outstr, char *instr, int max)
675 while (((ch = *instr++)>0) && (ch != EOLN) && (ch != EOF_CHAR) && (count < max)) {
677 in_quotes = !in_quotes;
680 if ( !in_quotes && is_white_space(ch) ) // not in quotes, white space terminates string
687 error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
692 // Copy text until a certain string is matched.
693 // For example, this is used to copy mission notes, scanning until $END NOTES:
695 void copy_text_until(char *outstr, char *instr, char *endstr, int max_chars)
699 foundstr = strstr(instr, endstr);
701 if (foundstr == NULL) {
702 nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
703 longjmp(parse_abort, 3);
706 if (foundstr - instr + strlen(endstr) < (uint) max_chars) {
707 strncpy(outstr, instr, foundstr - instr);
708 outstr[foundstr - instr] = 0;
711 nprintf(("Error", "Error. Too much text (%i chars, %i allowed) before %s\n",
712 foundstr - instr - strlen(endstr), max_chars, endstr));
714 longjmp(parse_abort, 4);
717 diag_printf("Here's the partial wad of text:\n%.30s", outstr);
720 // stuffs a string into a buffer. Can get a string between " marks and stops
721 // when whitespace in encounted -- not to end of line
722 void stuff_string_white(char *pstr)
724 ignore_white_space();
725 copy_to_next_white(pstr, Mp, NAME_LENGTH-1);
726 advance_to_next_white();
729 // Stuff a string into a string buffer.
730 // Supports various FreeSpace primitive types. If 'len' is supplied, it will override
731 // the default string length if using the F_NAME case.
732 void stuff_string(char *pstr, int type, char *terminators, int len)
734 char read_str[2048] = "";
742 final_len = NAME_LENGTH;
745 copy_to_eoln(read_str, terminators, Mp, read_len);
746 drop_trailing_white_space(read_str);
747 advance_to_eoln(terminators);
752 final_len = DATE_LENGTH;
753 copy_to_eoln(read_str, terminators, Mp, read_len);
754 drop_trailing_white_space(read_str);
755 advance_to_eoln(terminators);
759 ignore_white_space();
760 final_len = NOTES_LENGTH;
761 copy_text_until(read_str, Mp, "$End Notes:", read_len);
762 Mp += strlen(read_str);
763 required_string("$End Notes:");
768 final_len = FILESPEC_LENGTH;
769 copy_to_eoln(read_str, terminators, Mp, read_len);
770 drop_trailing_white_space(read_str);
771 advance_to_eoln(terminators);
773 // F_MULTITEXTOLD keeping for backwards compatability with old missions
774 // can be deleted once all missions are using new briefing format
777 ignore_white_space();
778 final_len = NOTES_LENGTH;
779 copy_text_until(read_str, Mp, "$End Briefing Text:", read_len);
780 Mp += strlen(read_str);
781 required_string("$End Briefing Text:");
786 final_len = MULTITEXT_LENGTH;
788 ignore_white_space();
789 copy_text_until(read_str, Mp, "$end_multi_text", read_len);
790 Mp += strlen(read_str);
791 drop_trailing_white_space(read_str);
792 required_string("$end_multi_text");
797 final_len = PATHNAME_LENGTH;
798 copy_to_eoln(read_str, terminators, Mp, read_len);
799 drop_trailing_white_space(read_str);
800 advance_to_eoln(terminators);
805 final_len = MESSAGE_LENGTH;
806 copy_to_eoln(read_str, terminators, Mp, read_len);
807 drop_trailing_white_space(read_str);
808 advance_to_eoln(terminators);
815 // now we want to do any final localization
816 lcl_ext_localize(read_str, pstr, final_len, &tag_id);
818 // if the hash localized text hash table is active and we have a valid external string - hash it
819 if(fhash_active() && (tag_id > -2)){
820 fhash_add_str(pstr, tag_id);
823 diag_printf("Stuffed string = [%.30s]\n", pstr);
826 // stuff a string, but only until the end of a line. don't ignore leading whitespace. close analog of fgets()/cfgets()
827 void stuff_string_line(char *pstr, int len)
829 char read_str[2048] = "";
836 copy_to_eoln(read_str, "\n", Mp, read_len);
837 drop_trailing_white_space(read_str);
841 // now we want to do any final localization
842 lcl_ext_localize(read_str, pstr, final_len, &tag_id);
844 // if the hash localized text hash table is active and we have a valid external string - hash it
845 if(fhash_active() && (tag_id > -2)){
846 fhash_add_str(pstr, tag_id);
849 diag_printf("Stuffed string = [%.30s]\n", pstr);
852 // 1K on the stack? seems to work...
853 // JH: 1k isn't enough! Command briefs can be 16k max, so changed this.
854 #define MAX_TMP_STRING_LENGTH 16384
856 // Exactly the same as stuff string only Malloc's the buffer.
857 // Supports various FreeSpace primitive types. If 'len' is supplied, it will override
858 // the default string length if using the F_NAME case.
859 char *stuff_and_malloc_string( int type, char *terminators, int len)
863 char tmp_result[MAX_TMP_STRING_LENGTH];
865 stuff_string(tmp_result, type, terminators, len);
866 drop_white_space(tmp_result);
868 l = strlen(tmp_result);
869 Assert(l < MAX_TMP_STRING_LENGTH); // Get John!!
873 return strdup(tmp_result);
876 // After reading a multitext string, you can call this function to convert any newlines into
877 // spaces, so it's a one paragraph string (I.e. as in MS-Word).
879 void compact_multitext_string(char *str)
883 for (i=0; i<strlen(str); i++)
888 #define BUF_SIZE 2048
890 // Strip comments from a line of input.
891 int strip_comments_fred(char *readp, int in_comment)
894 char *writep = readp;
896 while ((ch = *readp) != COMMENT_CHAR) {
917 // Strip comments from a line of input.
918 int strip_comments(char *readp, int in_comment)
921 char *writep = readp;
923 while ((ch = *readp) != COMMENT_CHAR) {
930 // time to do some special foreign character conversion
1047 void strip_all_comments( char *readp, char *writep )
1050 //char *writep = readp;
1052 while ( *readp != EOF_CHAR ) {
1054 if ( ch == COMMENT_CHAR ) {
1055 while ( *readp != EOLN )
1060 // get to next character after EOLN
1062 } else if ( (ch == '/') && (readp[1] == '*')) { // Start of multi-line comment
1067 while ( *readp != '*' )
1069 if ( readp[1] == '/' ) {
1083 *writep = (char)EOF_CHAR;
1087 int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
1090 int i, num_chars_read=0;
1094 for ( i = 0; i < max_line_len-1; i++ ) {
1096 if ( (cur - start) >= max_size ) {
1098 if ( lineout > t ) {
1099 return num_chars_read;
1106 } while ( c == 13 );
1109 if ( c=='\n' ) break;
1113 return num_chars_read;
1116 // Read mission text, stripping comments.
1117 // When a comment is found, it is removed. If an entire line
1118 // consisted of a comment, a blank line is left in the input file.
1119 void read_file_text(char *filename, int mode)
1122 char outbuf[BUF_SIZE], *str;
1123 char *mp = Mission_text;
1124 char *mp2 = Mission_text_raw;
1125 int file_is_encrypted = 0, in_comment = 0;
1128 longjmp(parse_abort, 10);
1130 strcpy(Current_filename, filename);
1131 mf = cfopen(filename, "rb", CFILE_NORMAL, mode);
1133 nprintf(("Error", "Wokka! Error opening mission.txt!\n"));
1134 longjmp(parse_abort, 5);
1137 // read the entire file in
1138 int file_len = cfilelength(mf);
1140 // read first 10 bytes to determine if file is encrypted
1141 cfread(Mission_text_raw, min(file_len, 10), 1, mf);
1142 file_is_encrypted = is_encrpyted(Mission_text_raw);
1143 cfseek(mf, 0, CF_SEEK_SET);
1145 if ( file_is_encrypted ) {
1146 int unscrambled_len;
1147 char *scrambled_text;
1148 scrambled_text = (char*)malloc(file_len+1);
1149 Assert(scrambled_text);
1150 cfread(scrambled_text, file_len, 1, mf);
1152 unencrypt(scrambled_text, file_len, Mission_text_raw, &unscrambled_len);
1153 file_len = unscrambled_len;
1154 free(scrambled_text);
1156 cfread(Mission_text_raw, file_len, 1, mf);
1161 // If you hit this assert, it is probably telling you the obvious. The file
1162 // you are trying to read is truly too large. Look at *filename to see the file name.
1163 Assert(file_len < MISSION_TEXT_SIZE);
1165 // strip comments from raw text, reading into Mission_text
1167 int num_chars_read = 0;
1169 mp2 = Mission_text_raw;
1170 while ( (num_chars_read = parse_get_line(outbuf, BUF_SIZE, Mission_text_raw, file_len, mp2)) != 0 ) {
1171 mp2 += num_chars_read;
1174 in_comment = strip_comments_fred(outbuf, in_comment);
1176 in_comment = strip_comments(outbuf, in_comment);
1189 // strcpy(mp, outbuf);
1190 // mp += strlen(outbuf);
1193 *mp = *mp2 = (char)EOF_CHAR;
1195 while (cfgets(outbuf, BUF_SIZE, mf) != NULL) {
1196 if (strlen(outbuf) >= BUF_SIZE-1)
1197 error_display(0, "Input string too long. Max is %i characters.\n%.256s\n", BUF_SIZE, outbuf);
1199 // If you hit this assert, it is probably telling you the obvious. The file
1200 // you are trying to read is truly too large. Look at *filename to see the file name.
1201 Assert(mp2 - Mission_text_raw + strlen(outbuf) < MISSION_TEXT_SIZE);
1202 strcpy(mp2, outbuf);
1203 mp2 += strlen(outbuf);
1205 in_comment = strip_comments(outbuf, in_comment);
1207 mp += strlen(outbuf);
1210 *mp = *mp2 = (char)EOF_CHAR;
1215 void debug_show_mission_text()
1217 char *mp = Mission_text;
1220 while ((ch = *mp++) != EOF_CHAR)
1229 ignore_white_space();
1233 if ((ch != '.') && (ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
1234 error_display(1, "Expecting float, found [%.32s].\n", next_tokens());
1238 return (float)atof(Mp);
1248 ignore_white_space();
1252 if ((ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
1253 error_display(1, "Expecting int, found [%.32s].\n", next_tokens());
1262 // Stuff a floating point value pointed at by Mp.
1263 // Advances past float characters.
1264 void stuff_float(float *f)
1271 Mp += strspn(Mp, "+-0123456789.");
1276 diag_printf("Stuffed float: %f\n", *f);
1279 // Stuff an integer value pointed at by Mp.
1280 // Advances past integer characters.
1281 void stuff_int(int *i)
1288 Mp += strspn(Mp, "+-0123456789");
1293 diag_printf("Stuffed int: %i\n", *i);
1297 // Stuffs a boolean value pointed at by Mp.
1298 // YES/NO (supporting 1/0 now as well)
1300 void stuff_boolean(int *i)
1303 ignore_white_space();
1304 copy_to_eoln(token, NULL, Mp, 512);
1305 drop_trailing_white_space(token);
1306 advance_to_eoln(NULL);
1308 if ( isdigit(token[0]) ) { // using 1/0 instead of YES/NO
1310 int_value = atoi(token);
1317 if ( !stricmp(token, "yes") ) {
1320 else if ( !stricmp(token, "no") ) {
1324 Assert(0); // can't happen
1329 // Stuff an integer value pointed at by Mp.
1330 // Advances past integer characters.
1331 void stuff_byte(ubyte *i)
1342 Mp += strspn(Mp, "+-0123456789");
1347 diag_printf("Stuffed byte: %i\n", *i);
1350 // Stuffs a list of strings
1351 int stuff_string_list(char slp[][NAME_LENGTH], int max_strings)
1354 ignore_white_space();
1357 error_display(1, "Reading string list. Found [%c]. Expecting '('.\n", *Mp);
1358 longjmp(parse_abort, 100);
1363 ignore_white_space();
1365 while (*Mp != ')') {
1366 assert ( count < max_strings );
1367 Assert ( *Mp == '\"' ); // should always be enclosed in quotes
1369 get_string( slp[count++] );
1370 ignore_white_space();
1378 // Stuffs an integer list.
1379 // This is of the form ( i* )
1380 // where i is an integer.
1381 // For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
1382 int stuff_int_list(int *ilp, int max_ints, int lookup_type)
1384 int count = 0, ok_flag = 1, dummy;
1385 ignore_white_space();
1388 error_display(1, "Reading integer list. Found [%c]. Expecting '('.\n", *Mp);
1389 longjmp(parse_abort, 6);
1393 ignore_white_space();
1395 while (*Mp != ')') {
1396 Assert(count < max_ints);
1402 switch (lookup_type) {
1404 num = ship_name_lookup(str); // returns index of Ship[] entry with name
1407 case SHIP_INFO_TYPE:
1409 num = ship_info_lookup(str); // returns index of Ship_info[] entry with name
1414 case WEAPON_POOL_TYPE:
1416 num = weapon_info_lookup(str);
1421 case WEAPON_LIST_TYPE:
1422 num = weapon_info_lookup(str);
1427 case RAW_INTEGER_TYPE:
1432 Error(LOCATION,"Unknown lookup_type in stuff_int_list");
1438 Error(LOCATION, "Unable to find string \"%s\" in stuff_int_list\n\nMany possible sources for this error. Get a programmer!\n", str);
1439 } else if (num == -2) {
1440 if (strlen(str) > 0) {
1441 Warning(LOCATION, "Unable to find WEAPON_LIST_TYPE string \"%s\" in stuff_int_list\n\nMany possible sources for this error. Get a programmer!\n", str);
1445 if (num < 0) // other negatives used to bypass the above error trap, but should be -1
1453 stuff_int(&ilp[count++]);
1458 ignore_white_space();
1466 // Marks an integer list.
1467 // This is of the form ( i* )
1468 // where i is an integer.
1469 // If a specified string is found in the lookup and its value is 7, then the 7th value
1470 // in the array is set.
1471 void mark_int_list(int *ilp, int max_ints, int lookup_type)
1473 ignore_white_space();
1476 error_display(1, "Marking integer list. Found [%c]. Expecting '('.\n", *Mp);
1477 longjmp(parse_abort, 6);
1481 ignore_white_space();
1483 while (*Mp != ')') {
1489 switch(lookup_type) {
1491 num = ship_name_lookup(str); // returns index of Ship[] entry with name
1494 case SHIP_INFO_TYPE:
1495 num = ship_info_lookup(str); // returns index of Ship_info[] entry with name
1498 case WEAPON_LIST_TYPE:
1499 num = weapon_info_lookup(str);
1503 Error(LOCATION,"Unknown lookup_type in stuff_int_list");
1507 if ( (num < 0) || (num >= max_ints) )
1508 Error(LOCATION, "Unable to find string \"%s\" in mark_int_list.\n", str);
1516 Assert((tval >= 0) && (tval < max_ints));
1520 ignore_white_space();
1528 // Stuff a vector, which is 3 floats.
1529 void stuff_vector(vector *vp)
1531 stuff_float(&vp->x);
1532 stuff_float(&vp->y);
1533 stuff_float(&vp->z);
1536 void stuff_parenthesized_vector(vector *vp)
1538 ignore_white_space();
1541 error_display(1, "Reading parenthesized vector. Found [%c]. Expecting '('.\n", *Mp);
1542 longjmp(parse_abort, 11);
1546 ignore_white_space();
1548 error_display(1, "Reading parenthesized vector. Found [%c]. Expecting ')'.\n", *Mp);
1549 longjmp(parse_abort, 12);
1556 // Stuffs vector list.
1557 // This is of the form ( (vector)* )
1558 // where vector is a vector
1559 // For example, ( (1 2 3) (2 3 4) (2 3 5) )
1560 // is a vector list of three vectors.
1561 int stuff_vector_list(vector *vlp, int max_vecs)
1565 ignore_white_space();
1568 error_display(1, "Reading integer list. Found [%c]. Expecting '('.\n", *Mp);
1569 longjmp(parse_abort, 6);
1574 ignore_white_space();
1576 while (*Mp != ')') {
1577 Assert(count < max_vecs);
1578 stuff_parenthesized_vector(&vlp[count++]);
1580 ignore_white_space();
1589 // Stuff a matrix, which is 3 vectors.
1590 void stuff_matrix(matrix *mp)
1592 stuff_vector(&mp->rvec);
1593 stuff_vector(&mp->uvec);
1594 stuff_vector(&mp->fvec);
1598 // Given a string, find it in a string array.
1599 // *descrtiption is only used for diagnostics in case it can't be found.
1600 // *str1 is the string to be found.
1601 // *strlist is the list of strings to search.
1602 // max is the number of entries in *strlist to scan.
1603 int string_lookup(char *str1, char *strlist[], int max, char *description, int say_errors)
1607 for (i=0; i<max; i++) {
1608 Assert(strlen(strlist[i]) != 0);
1610 if (!stricmp(str1, strlist[i]))
1615 error_display(0, "Unable to find [%s] in %s list.\n", str1, description);
1620 // Find a required string (*id), then stuff the text of type f_type that
1621 // follows it at *addr. *strlist[] contains the strings it should try to
1623 void find_and_stuff(char *id, int *addr, int f_type, char *strlist[], int max, char *description)
1627 required_string(id);
1628 stuff_string(token, f_type, NULL);
1629 *addr = string_lookup(token, strlist, max, description, 1);
1632 // Mp points at a string.
1633 // Find the string in the list of strings *strlist[].
1634 // Returns the index of the match, -1 if none.
1635 int match_and_stuff(int f_type, char *strlist[], int max, char *description)
1639 stuff_string(token, f_type, NULL);
1640 return string_lookup(token, strlist, max, description, 0);
1643 void find_and_stuff_or_add(char *id, int *addr, int f_type, char *strlist[], int *total,
1644 int max, char *description)
1649 required_string(id);
1650 stuff_string(token, f_type, NULL);
1652 *addr = string_lookup(token, strlist, *total, description, 0);
1654 if (*addr == -1) // not in list, so lets try and add it.
1656 Assert(*total < max);
1657 strcpy(strlist[*total], token);
1662 // Initialize a parse process.
1671 for (i=0; i<MAX_CARGO; i++)
1672 Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
1674 Total_goal_ship_names = 0;
1686 // Display number of warnings and errors at the end of a parse.
1687 void display_parse_diagnostics()
1689 nprintf(("Parse", "\nParse complete.\n"));
1690 nprintf(("Parse", "%i errors. %i warnings.\n", Error_count, Warning_count));
1693 // Splits a string into 2 lines if the string is wider than max_pixel_w pixels. A null
1694 // terminator is placed where required to make the first line <= max_pixel_w. The remaining
1695 // text is returned (leading whitespace removed). If the line doesn't need to be split,
1696 // NULL is returned.
1697 char *split_str_once(char *src, int max_pixel_w)
1700 int i, w, len, last_was_white = 0;
1703 Assert(max_pixel_w > 0);
1705 gr_get_string_size(&w, NULL, src);
1706 if ( w <= max_pixel_w )
1707 return NULL; // string doesn't require a cut
1710 for (i=0; i<len; i++) {
1711 gr_get_string_size(&w, NULL, src, i);
1712 if ( w > max_pixel_w )
1715 if (src[i] == '\n') { // reached natural end of line
1720 if (is_white_space(src[i])) {
1721 if (!last_was_white)
1737 while (is_white_space(*src))
1741 return NULL; // end of the string anyway
1749 #define SPLIT_STR_BUFFER_SIZE 512
1751 // --------------------------------------------------------------------------------------
1754 // A general function that will split a string into several lines. Lines are allowed up
1755 // to max_pixel_w pixels. Breaks are found in white space.
1757 // Supports \n's in the strings!
1759 // parameters: src => source string to be broken up
1760 // max_pixel_w => max width of line in pixels
1761 // n_chars => output array that will hold number of characters in each line
1762 // p_str => output array of pointers to start of lines within src
1763 // max_lines => limit of number of lines to break src up into
1764 // ignore_char => OPTIONAL parameter (default val -1). Ignore words starting with this character
1765 // This is useful when you want to ignore embedded control information that starts
1766 // with a specific character, like $ or #
1768 // returns: number of lines src is broken into
1769 // -1 is returned when an error occurs
1771 int split_str(char *src, int max_pixel_w, int *n_chars, char **p_str, int max_lines, char ignore_char)
1773 char buffer[SPLIT_STR_BUFFER_SIZE];
1774 char *breakpoint = NULL;
1775 int sw, new_line = 1, line_num = 0, last_was_white = 0;
1776 int ignore_until_whitespace, buf_index;
1778 // check our assumptions..
1779 Assert(src != NULL);
1780 Assert(n_chars != NULL);
1781 Assert(p_str != NULL);
1782 Assert(max_lines > 0);
1783 Assert(max_pixel_w > 0);
1785 memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1787 ignore_until_whitespace = 0;
1789 // get rid of any leading whitespace
1790 while (is_white_space(*src))
1796 // iterate through chars in line, keeping track of most recent "white space" location that can be used
1797 // as a line splitting point if necessary
1798 for (; *src; src++) {
1799 if (line_num >= max_lines)
1800 return line_num; // time to bail out
1802 // starting a new line of text, init stuff for that
1804 p_str[line_num] = NULL;
1805 if (is_gray_space(*src))
1808 p_str[line_num] = src;
1813 // maybe skip leading whitespace
1814 if (ignore_until_whitespace) {
1815 if ( is_white_space(*src) )
1816 ignore_until_whitespace = 0;
1821 // if we have a newline, split the line here
1823 n_chars[line_num] = src - p_str[line_num]; // track length of line
1825 p_str[line_num] = NULL;
1828 memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1833 if (*src == ignore_char) {
1834 ignore_until_whitespace = 1;
1838 if (is_gray_space(*src)) {
1839 if (!last_was_white) // track at first whitespace in a series of whitespace
1845 // indicate next time around that this wasn't a whitespace character
1849 // throw it in our buffer
1850 buffer[buf_index] = *src;
1852 buffer[buf_index] = 0; // null terminate it
1854 gr_get_string_size(&sw, NULL, buffer);
1855 if (sw >= max_pixel_w) {
1859 end = src = breakpoint;
1862 end = src; // force a split here since to whitespace
1863 src--; // reuse this character in next line
1866 n_chars[line_num] = end - p_str[line_num]; // track length of line
1867 Assert(n_chars[line_num]);
1869 p_str[line_num] = NULL;
1872 memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1878 if (p_str[line_num]) {
1879 n_chars[line_num] = src - p_str[line_num]; // track length of line
1880 Assert(n_chars[line_num]);