2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
15 * low level parse routines common to all types of parsers
18 * Revision 1.7 2003/05/25 02:30:43 taylor
21 * Revision 1.6 2002/06/17 06:15:25 relnev
22 * ryan's struct patch (and cr removal)
24 * Revision 1.5 2002/06/09 04:41:25 relnev
25 * added copyright header
27 * Revision 1.4 2002/05/28 06:45:38 theoddone33
30 * Revision 1.3 2002/05/28 06:28:20 theoddone33
31 * Filesystem mods, actually reads some data files now
33 * Revision 1.2 2002/05/07 03:16:48 theoddone33
34 * The Great Newline Fix
36 * Revision 1.1.1.1 2002/05/03 03:28:10 root
40 * 12 8/10/99 6:54p Dave
41 * Mad optimizations. Added paging to the nebula effect.
43 * 11 6/02/99 2:22p Andsager
46 * 10 5/20/99 11:25a Andsager
47 * Added error checking for parsing table files.
49 * 9 2/23/99 11:18a Andsager
50 * Localize launcher using strings.tbl
52 * 8 2/03/99 6:06p Dave
53 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
55 * 7 11/05/98 4:18p Dave
56 * First run nebula support. Beefed up localization a bit. Removed all
57 * conditional compiles for foreign versions. Modified mission file
60 * 6 10/29/98 12:49p Dave
61 * Intermediate checkin for Fred hash table stuff.
63 * 5 10/28/98 11:30a Dave
66 * 4 10/22/98 6:14p Dave
67 * Optimized some #includes in Anim folder. Put in the beginnings of
68 * parse/localization support for externalized strings and tstrings.tbl
70 * 3 10/14/98 1:15p Andsager
73 * 2 10/07/98 10:53a Dave
76 * 1 10/07/98 10:50a Dave
78 * 98 9/21/98 2:37p Adam
79 * fixed font translation bug
81 * 97 7/29/98 9:39a Hoffoss
82 * Fixed bug with stuff_and_malloc_string
84 * 96 6/22/98 11:18a Hoffoss
85 * Fixed bug where mission notes field gets an extra linefeed with each
88 * 95 6/19/98 3:53p Lawrance
89 * account for some new foreign chars
91 * 94 6/12/98 7:37p Hoffoss
92 * Made � translate to ss, since we don't have this character in our font.
94 * 93 6/12/98 4:52p Hoffoss
95 * Added support for special characters in in forgeign languages.
97 * 92 5/21/98 3:26p Dave
98 * Fixed bug in new split string function. Newline as last character did
101 * 91 5/21/98 3:01p Dave
102 * Make sure split_str handles 0 length strings correctly.
104 * 90 5/21/98 2:14a Hoffoss
105 * Rewrote split_str() to solve a problem and it was too hard for me to
106 * figure out (oow, my head hurts still!)
121 #include "missionparse.h"
124 #include "localize.h"
127 #define ERROR_LENGTH 64
128 #define RS_MAX_TRIES 5
130 char Current_filename[128];
131 char Error_str[ERROR_LENGTH];
133 int Warning_count, Error_count;
134 int fred_parse_flag = 0;
135 int Token_found_flag;
137 char Mission_text[MISSION_TEXT_SIZE];
138 char Mission_text_raw[MISSION_TEXT_SIZE];
140 const char *token_found;
142 // Return true if this character is white space, else false.
143 int is_white_space(char ch)
145 return ((ch == ' ') || (ch == '\t') || (ch == EOLN));
148 // Returns true if this character is gray space, else false (gray space is white space except for EOLN).
149 int is_gray_space(char ch)
151 return ((ch == ' ') || (ch == '\t'));
154 // Advance global Mp (mission pointer) past all current white space.
155 // Leaves Mp pointing at first non white space character.
156 void ignore_white_space()
158 while ((*Mp != EOF_CHAR) && is_white_space(*Mp))
162 void ignore_gray_space()
164 while ((*Mp != EOF_CHAR) && is_gray_space(*Mp))
168 // Truncate *str, eliminating all trailing white space.
169 // Eg: "abc " becomes "abc"
170 // "abc abc " becomes "abc abc"
171 // "abc \t" becomes "abc"
172 void drop_trailing_white_space(char *str)
178 while ((i >= 0) && is_white_space(str[i]))
184 // Eliminate any leading whitespace in str
185 void drop_leading_white_space(char *str)
192 while ((i < len) && is_white_space(str[i]))
195 memmove(str, str+i, len-i);
199 // eliminates all leading and trailing white space from a string. Returns pointer passed in.
200 char *drop_white_space(char *str)
205 while (str[s] && is_white_space(str[s]))
210 if (!is_white_space(str[e]))
217 memmove(str, str + s, e - s + 1);
223 // Advances Mp past current token.
226 ignore_white_space();
228 while ((*Mp != EOF_CHAR) && !is_white_space(*Mp))
232 // Display a diagnostic message if Verbose is set.
233 // (Verbose is set if -v command line switch is present.)
234 void diag_printf(const char *format, ...)
239 va_start(args, format);
240 SDL_vsnprintf(buffer, SDL_arraysize(buffer), format, args);
243 nprintf(("Parse", "%s", buffer));
246 // Grab and return (a pointer to) a bunch of tokens, terminating at
247 // ERROR_LENGTH chars, or end of line.
254 while (((ch = *pstr++) != EOLN) && (ch != EOF_CHAR) && (count < ERROR_LENGTH-1))
255 Error_str[count++] = ch;
257 Error_str[count] = 0;
261 // Return the line number given by the current mission pointer, ie Mp.
262 // A very slow function (scans all processed text), but who cares how long
263 // an error reporting function takes?
272 p = Mission_text_raw;
273 stoploc = (Mp - Mission_text) + p;
280 if ( !incomment && (*p == COMMENT_CHAR) )
283 if ( !incomment && (*p == '/') && (*(p+1) == '*') ) {
291 if ( multiline && (*(p-1) == '*') && (*p == '/') ) {
297 if ( !incomment && (*p == '/') && (*(p+1) == '/') )
302 if ( !multiline && incomment )
311 // Call this function to display an error message.
312 // error_level == 0 means this is just a warning.
313 // !0 means it's an error message.
314 // Prints line number and other useful information.
315 void error_display(int error_level, const char *format, ...)
318 char error_text[128];
321 if (error_level == 0) {
322 SDL_strlcpy(error_text, "Warning", SDL_arraysize(error_text));
325 SDL_strlcpy(error_text, "Error", SDL_arraysize(error_text));
329 nprintf((error_text, "%s(%i):%s: ", Current_filename, get_line_num(), error_text));
331 va_start(args, format);
332 SDL_vsnprintf(buffer, SDL_arraysize(buffer), format, args);
335 nprintf((error_text, "%s", buffer));
336 Warning(LOCATION, "%s(%i):\n%s: %s", Current_filename, get_line_num(), error_text, buffer);
339 // Advance Mp to the next eoln character.
340 void advance_to_eoln(const char *more_terminators)
342 char terminators[128];
344 SDL_assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
346 terminators[0] = EOLN;
347 terminators[1] = (char)EOF_CHAR;
348 if (more_terminators != NULL)
349 SDL_strlcpy(&terminators[2], more_terminators, SDL_arraysize(terminators));
353 while (SDL_strchr(terminators, *Mp) == NULL)
357 // Advance Mp to the next white space (ignoring white space inside of " marks)
358 void advance_to_next_white()
362 while ((*Mp != EOLN) && (*Mp != EOF_CHAR)) {
364 in_quotes = !in_quotes;
366 if (!in_quotes && is_white_space(*Mp))
372 // Search for specified string, skipping everything up to that point. Returns 1 if found,
373 // 0 if string wasn't found (and hit end of file), or -1 if not found, but end of checking
374 // block was reached.
375 int skip_to_string(const char *pstr, const char *end)
379 ignore_white_space();
384 while ((*Mp != EOF_CHAR) && SDL_strncasecmp(pstr, Mp, len)) {
385 if (end && *Mp == '#')
388 if (end && !SDL_strncasecmp(pstr, Mp, len2))
391 advance_to_eoln(NULL);
392 ignore_white_space();
395 if (!Mp || (*Mp == EOF_CHAR))
402 // Advance to start of either pstr1 or pstr2. Return 0 is successful, otherwise return !0
403 int skip_to_start_of_strings(const char *pstr1, const char *pstr2)
407 ignore_white_space();
408 len1 = strlen(pstr1);
409 len2 = strlen(pstr2);
411 while ( (*Mp != EOF_CHAR) && SDL_strncasecmp(pstr1, Mp, len1) && SDL_strncasecmp(pstr2, Mp, len2) ) {
412 advance_to_eoln(NULL);
413 ignore_white_space();
416 if (!Mp || (*Mp == EOF_CHAR))
422 // Find a required string.
423 // If not found, display an error message, but try up to RS_MAX_TRIES times
424 // to find the string. (This is the groundwork for ignoring non-understood
426 // If unable to find the required string after RS_MAX_TRIES tries, then
427 // abort using longjmp to parse_abort.
428 int required_string(const char *pstr)
432 ignore_white_space();
434 while (SDL_strncasecmp(pstr, Mp, strlen(pstr)) && (count < RS_MAX_TRIES)) {
435 error_display(1, "Required token = [%s], found [%.32s].\n", pstr, next_tokens());
436 advance_to_eoln(NULL);
437 ignore_white_space();
441 if (count == RS_MAX_TRIES) {
442 nprintf(("Error", "Error: Unable to find required token [%s]\n", pstr));
443 Warning(LOCATION, "Error: Unable to find required token [%s]\n", pstr);
444 throw PARSE_ERROR_MISSING_TOKEN;
448 diag_printf("Found required string [%s]\n", token_found = pstr);
452 // similar to optional_string, but just checks if next token is a match.
453 // It doesn't advance Mp.
455 int check_for_string(const char *pstr)
457 ignore_white_space();
459 if (!SDL_strncasecmp(pstr, Mp, strlen(pstr)))
465 // like check for string, but doesn't skip past any whitespace
466 int check_for_string_raw(const char *pstr)
468 if (!SDL_strncasecmp(pstr, Mp, strlen(pstr))){
475 // Find an optional string.
476 // If found, return 1, else return 0.
477 // If found, point past string, else don't update pointer.
478 int optional_string(const char *pstr)
480 ignore_white_space();
482 if (!SDL_strncasecmp(pstr, Mp, strlen(pstr))) {
490 int required_string_fred(const char *pstr, const char *end)
498 ignore_white_space();
499 while (*Mp != EOF_CHAR && SDL_strncasecmp(pstr, Mp, strlen(pstr))) {
500 if ((*Mp == '#') || (end && !SDL_strncasecmp(end, Mp, strlen(end)))) {
505 advance_to_eoln(NULL);
506 ignore_white_space();
509 if (!Mp || (*Mp == EOF_CHAR)) {
510 diag_printf("Required string [%s] not found\n", pstr);
512 Token_found_flag = 0;
517 diag_printf("Found required string [%s]\n", pstr);
518 Token_found_flag = 1;
522 // attempt to find token in buffer. It might not exist, however, in which case we don't need
523 // to do anything. If it is found, then we advance the pointer to just after the token. To
524 // further complicate things, we should only search to a certain point, since we don't want
525 // a token that belongs to another section which might match the token we want. Thus, we
526 // also pass in an ending token, which marks the point we should stop looking at.
527 int optional_string_fred(const char *pstr, const char *end, const char *end2)
535 ignore_white_space();
536 while ((*Mp != EOF_CHAR) && SDL_strncasecmp(pstr, Mp, strlen(pstr))) {
537 if ((*Mp == '#') || (end && !SDL_strncasecmp(end, Mp, strlen(end))) ||
538 (end2 && !SDL_strncasecmp(end2, Mp, strlen(end2)))) {
543 advance_to_eoln(NULL);
544 ignore_white_space();
547 if (!Mp || (*Mp == EOF_CHAR)) {
548 diag_printf("Optional string [%s] not found\n", pstr);
550 Token_found_flag = 0;
555 diag_printf("Found optional string [%s]\n", pstr);
556 Token_found_flag = 1;
560 // Return 0 or 1 for str1 match, str2 match. Return -1 if neither matches.
561 // Does not update Mp if token found. If not found, advances, trying to
562 // find the string. Doesn't advance past the found string.
563 int required_string_either(const char *str1, const char *str2)
567 ignore_white_space();
569 while (count < RS_MAX_TRIES) {
570 if (SDL_strncasecmp(str1, Mp, strlen(str1)) == 0) {
571 // Mp += strlen(str1);
572 diag_printf("Found required string [%s]\n", token_found = str1);
574 } else if (SDL_strncasecmp(str2, Mp, strlen(str2)) == 0) {
575 // Mp += strlen(str2);
576 diag_printf("Found required string [%s]\n", token_found = str2);
580 error_display(1, "Required token = [%s] or [%s], found [%.32s].\n", str1, str2, next_tokens());
582 advance_to_eoln(NULL);
583 ignore_white_space();
587 if (count == RS_MAX_TRIES) {
588 nprintf(("Error", "Error: Unable to find either required token [%s] or [%s]\n", str1, str2));
589 Warning(LOCATION, "Error: Unable to find either required token [%s] or [%s]\n", str1, str2);
590 throw PARSE_ERROR_MISSING_TOKEN_EITHER;
597 // Return 0 or 1 for str1 match, str2 match. Return -1 if neither matches.
598 // Does not update Mp if token found. If not found, advances, trying to
599 // find the string. Doesn't advance past the found string.
600 int required_string_3(const char *str1, const char *str2, const char *str3)
604 ignore_white_space();
606 while (count < RS_MAX_TRIES) {
607 if (SDL_strncasecmp(str1, Mp, strlen(str1)) == 0) {
608 // Mp += strlen(str1);
609 diag_printf("Found required string [%s]\n", token_found = str1);
611 } else if (SDL_strncasecmp(str2, Mp, strlen(str2)) == 0) {
612 // Mp += strlen(str2);
613 diag_printf("Found required string [%s]\n", token_found = str2);
615 } else if (SDL_strncasecmp(str3, Mp, strlen(str3)) == 0) {
616 diag_printf("Found required string [%s]\n", token_found = str3);
620 error_display(1, "Required token = [%s], [%s] or [%s], found [%.32s].\n", str1, str2, str3, next_tokens());
622 advance_to_eoln(NULL);
623 ignore_white_space();
631 int required_string_either_fred(const char *str1, const char *str2)
633 ignore_white_space();
635 while (*Mp != EOF_CHAR) {
636 if (!SDL_strncasecmp(str1, Mp, strlen(str1))) {
637 // Mp += strlen(str1);
638 diag_printf("Found required string [%s]\n", token_found = str1);
639 return fred_parse_flag = 0;
641 } else if (!SDL_strncasecmp(str2, Mp, strlen(str2))) {
642 // Mp += strlen(str2);
643 diag_printf("Found required string [%s]\n", token_found = str2);
644 return fred_parse_flag = 1;
647 advance_to_eoln(NULL);
648 ignore_white_space();
652 diag_printf("Unable to find either required token [%s] or [%s]\n", str1, str2);
658 // Copy characters from instr to outstr until eoln is found, or until max
659 // characters have been copied (including terminator).
660 void copy_to_eoln(char *outstr, const char *more_terminators, const char *instr, int max)
664 char terminators[128];
666 SDL_assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
668 terminators[0] = EOLN;
669 terminators[1] = (char)EOF_CHAR;
670 if (more_terminators != NULL)
671 SDL_strlcpy(&terminators[2], more_terminators, SDL_arraysize(terminators));
675 while (((ch = *instr++) != 0) && (SDL_strchr(terminators, ch) == NULL) && (count < max)) {
681 error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
686 // Copy characters from instr to outstr until next white space is found, or until max
687 // characters have been copied (including terminator).
688 void copy_to_next_white(char *outstr, const char *instr, int max)
694 while (((ch = *instr++)>0) && (ch != EOLN) && (ch != EOF_CHAR) && (count < max)) {
696 in_quotes = !in_quotes;
699 if ( !in_quotes && is_white_space(ch) ) // not in quotes, white space terminates string
706 error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
711 // Copy text until a certain string is matched.
712 // For example, this is used to copy mission notes, scanning until $END NOTES:
714 void copy_text_until(char *outstr, const char *instr, const char *endstr, int max_chars)
716 const char *foundstr;
718 foundstr = strstr(instr, endstr);
720 if (foundstr == NULL) {
721 nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
722 throw PARSE_ERROR_MISSING_STRING;
725 if (foundstr - instr + strlen(endstr) < (uint) max_chars) {
726 SDL_strlcpy(outstr, instr, foundstr - instr + 1);
729 nprintf(("Error", "Error. Too much text (%i chars, %i allowed) before %s\n",
730 foundstr - instr - strlen(endstr), max_chars, endstr));
732 throw PARSE_ERROR_TOO_LONG;
735 diag_printf("Here's the partial wad of text:\n%.30s", outstr);
738 // stuffs a string into a buffer. Can get a string between " marks and stops
739 // when whitespace in encounted -- not to end of line
740 void stuff_string_white(char *pstr)
742 ignore_white_space();
743 copy_to_next_white(pstr, Mp, NAME_LENGTH-1);
744 advance_to_next_white();
747 // Stuff a string into a string buffer.
748 // Supports various FreeSpace primitive types. If 'len' is supplied, it will override
749 // the default string length if using the F_NAME case.
750 void stuff_string(char *pstr, int type, const char *terminators, int len)
752 char read_str[2048] = "";
760 final_len = NAME_LENGTH;
763 copy_to_eoln(read_str, terminators, Mp, read_len);
764 drop_trailing_white_space(read_str);
765 advance_to_eoln(terminators);
770 final_len = DATE_LENGTH;
771 copy_to_eoln(read_str, terminators, Mp, read_len);
772 drop_trailing_white_space(read_str);
773 advance_to_eoln(terminators);
777 ignore_white_space();
778 final_len = NOTES_LENGTH;
779 copy_text_until(read_str, Mp, "$End Notes:", read_len);
780 Mp += strlen(read_str);
781 required_string("$End Notes:");
786 final_len = FILESPEC_LENGTH;
787 copy_to_eoln(read_str, terminators, Mp, read_len);
788 drop_trailing_white_space(read_str);
789 advance_to_eoln(terminators);
791 // F_MULTITEXTOLD keeping for backwards compatability with old missions
792 // can be deleted once all missions are using new briefing format
795 ignore_white_space();
796 final_len = NOTES_LENGTH;
797 copy_text_until(read_str, Mp, "$End Briefing Text:", read_len);
798 Mp += strlen(read_str);
799 required_string("$End Briefing Text:");
804 final_len = MULTITEXT_LENGTH;
806 ignore_white_space();
807 copy_text_until(read_str, Mp, "$end_multi_text", read_len);
808 Mp += strlen(read_str);
809 drop_trailing_white_space(read_str);
810 required_string("$end_multi_text");
815 final_len = PATHNAME_LENGTH;
816 copy_to_eoln(read_str, terminators, Mp, read_len);
817 drop_trailing_white_space(read_str);
818 advance_to_eoln(terminators);
823 final_len = MESSAGE_LENGTH;
824 copy_to_eoln(read_str, terminators, Mp, read_len);
825 drop_trailing_white_space(read_str);
826 advance_to_eoln(terminators);
833 // now we want to do any final localization
834 lcl_ext_localize(read_str, pstr, final_len, &tag_id);
836 // if the hash localized text hash table is active and we have a valid external string - hash it
837 if(fhash_active() && (tag_id > -2)){
838 fhash_add_str(pstr, tag_id);
841 diag_printf("Stuffed string = [%.30s]\n", pstr);
844 // stuff a string, but only until the end of a line. don't ignore leading whitespace. close analog of fgets()/cfgets()
845 void stuff_string_line(char *pstr, int len)
847 char read_str[2048] = "";
854 copy_to_eoln(read_str, "\n", Mp, read_len);
855 drop_trailing_white_space(read_str);
859 // now we want to do any final localization
860 lcl_ext_localize(read_str, pstr, final_len, &tag_id);
862 // if the hash localized text hash table is active and we have a valid external string - hash it
863 if(fhash_active() && (tag_id > -2)){
864 fhash_add_str(pstr, tag_id);
867 diag_printf("Stuffed string = [%.30s]\n", pstr);
870 // 1K on the stack? seems to work...
871 // JH: 1k isn't enough! Command briefs can be 16k max, so changed this.
872 #define MAX_TMP_STRING_LENGTH 16384
874 // Exactly the same as stuff string only Malloc's the buffer.
875 // Supports various FreeSpace primitive types. If 'len' is supplied, it will override
876 // the default string length if using the F_NAME case.
877 char *stuff_and_malloc_string( int type, const char *terminators, int len)
881 char tmp_result[MAX_TMP_STRING_LENGTH];
883 stuff_string(tmp_result, type, terminators, len);
884 drop_white_space(tmp_result);
886 l = strlen(tmp_result);
887 SDL_assert(l < MAX_TMP_STRING_LENGTH); // Get John!!
891 return strdup(tmp_result);
894 // After reading a multitext string, you can call this function to convert any newlines into
895 // spaces, so it's a one paragraph string (I.e. as in MS-Word).
897 void compact_multitext_string(char *str)
901 for (i=0; i<strlen(str); i++)
906 #define BUF_SIZE 2048
908 // Strip comments from a line of input.
909 int strip_comments_fred(char *readp, int in_comment)
912 char *writep = readp;
914 while ((ch = *readp) != COMMENT_CHAR) {
935 // Strip comments from a line of input.
936 int strip_comments(char *readp, int in_comment)
939 char *writep = readp;
941 while ((ch = *readp) != COMMENT_CHAR) {
948 if (!in_comment && (*readp == '/') && (*(readp+1) == '/')) {
955 // time to do some special foreign character conversion
1072 void strip_all_comments( char *readp, char *writep )
1075 //char *writep = readp;
1077 while ( *readp && *readp != EOF_CHAR ) {
1079 if ( ch == COMMENT_CHAR ) {
1080 while ( *readp != EOLN )
1085 // get to next character after EOLN
1087 } else if ( (ch == '/') && (readp[1] == '*')) { // Start of multi-line comment
1092 while ( *readp != '*' )
1094 if ( readp[1] == '/' ) {
1108 *writep = (char)EOF_CHAR;
1112 int parse_get_line(char *lineout, int max_line_len, const char *start, int max_size, const char *cur)
1115 int i, num_chars_read=0;
1119 for ( i = 0; i < max_line_len-1; i++ ) {
1121 if ( (cur - start) >= max_size ) {
1123 if ( lineout > t ) {
1124 return num_chars_read;
1131 } while ( c == 13 );
1134 if ( c=='\n' ) break;
1138 return num_chars_read;
1141 // Read mission text, stripping comments.
1142 // When a comment is found, it is removed. If an entire line
1143 // consisted of a comment, a blank line is left in the input file.
1144 void read_file_text(const char *filename, int mode)
1147 char outbuf[BUF_SIZE], *str;
1148 char *mp = Mission_text;
1149 char *mp2 = Mission_text_raw;
1150 int file_is_encrypted = 0, in_comment = 0;
1153 throw PARSE_ERROR_EMPTY_FILENAME;
1155 SDL_strlcpy(Current_filename, filename, SDL_arraysize(Current_filename));
1156 mf = cfopen(filename, "rb", CFILE_NORMAL, mode);
1158 nprintf(("Error", "Wokka! Error opening mission.txt!\n"));
1159 throw PARSE_ERROR_FILE_NOT_FOUND;
1162 // read the entire file in
1163 int file_len = cfilelength(mf);
1165 // read first 10 bytes to determine if file is encrypted
1166 cfread(Mission_text_raw, SDL_min(file_len, 10), 1, mf);
1167 file_is_encrypted = is_encrpyted(Mission_text_raw);
1168 cfseek(mf, 0, CF_SEEK_SET);
1170 if ( file_is_encrypted ) {
1171 int unscrambled_len;
1172 char *scrambled_text;
1173 scrambled_text = (char*)malloc(file_len+1);
1174 SDL_assert(scrambled_text);
1175 cfread(scrambled_text, file_len, 1, mf);
1177 unencrypt(scrambled_text, file_len, Mission_text_raw, &unscrambled_len);
1178 file_len = unscrambled_len;
1179 free(scrambled_text);
1181 cfread(Mission_text_raw, file_len, 1, mf);
1186 // If you hit this assert, it is probably telling you the obvious. The file
1187 // you are trying to read is truly too large. Look at *filename to see the file name.
1188 SDL_assert(file_len < MISSION_TEXT_SIZE);
1190 // strip comments from raw text, reading into Mission_text
1192 int num_chars_read = 0;
1195 mp2 = Mission_text_raw;
1197 strip_all_comments(Mission_text_raw, mp2);
1199 while ( (num_chars_read = parse_get_line(outbuf, BUF_SIZE, Mission_text_raw, file_len, mp2)) != 0 ) {
1200 mp2 += num_chars_read;
1203 in_comment = strip_comments_fred(outbuf, in_comment);
1205 in_comment = strip_comments(outbuf, in_comment);
1218 // strcpy(mp, outbuf);
1219 // mp += strlen(outbuf);
1222 *mp = *mp2 = (char)EOF_CHAR;
1224 while (cfgets(outbuf, BUF_SIZE, mf) != NULL) {
1225 if (strlen(outbuf) >= BUF_SIZE-1)
1226 error_display(0, "Input string too long. Max is %i characters.\n%.256s\n", BUF_SIZE, outbuf);
1228 // If you hit this assert, it is probably telling you the obvious. The file
1229 // you are trying to read is truly too large. Look at *filename to see the file name.
1230 SDL_assert(mp2 - Mission_text_raw + strlen(outbuf) < MISSION_TEXT_SIZE);
1231 strcpy(mp2, outbuf);
1232 mp2 += strlen(outbuf);
1234 in_comment = strip_comments(outbuf, in_comment);
1236 mp += strlen(outbuf);
1239 *mp = *mp2 = (char)EOF_CHAR;
1244 void debug_show_mission_text()
1246 char *mp = Mission_text;
1249 while ((ch = *mp++) != EOF_CHAR)
1258 ignore_white_space();
1262 if ((ch != '.') && (ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
1263 error_display(1, "Expecting float, found [%.32s].\n", next_tokens());
1267 return (float)atof(Mp);
1277 ignore_white_space();
1281 if ((ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
1282 error_display(1, "Expecting int, found [%.32s].\n", next_tokens());
1291 // Stuff a floating point value pointed at by Mp.
1292 // Advances past float characters.
1293 void stuff_float(float *f)
1300 Mp += strspn(Mp, "+-0123456789.");
1305 diag_printf("Stuffed float: %f\n", *f);
1308 // Stuff an integer value pointed at by Mp.
1309 // Advances past integer characters.
1310 void stuff_int(int *i)
1317 Mp += strspn(Mp, "+-0123456789");
1322 diag_printf("Stuffed int: %i\n", *i);
1326 // Stuffs a boolean value pointed at by Mp.
1327 // YES/NO (supporting 1/0 now as well)
1329 void stuff_boolean(int *i)
1332 ignore_white_space();
1333 copy_to_eoln(token, NULL, Mp, 512);
1334 drop_trailing_white_space(token);
1335 advance_to_eoln(NULL);
1337 if ( isdigit(token[0]) ) { // using 1/0 instead of YES/NO
1339 int_value = atoi(token);
1346 if ( !SDL_strcasecmp(token, "yes") ) {
1349 else if ( !SDL_strcasecmp(token, "no") ) {
1353 SDL_assert(0); // can't happen
1358 // Stuff an integer value pointed at by Mp.
1359 // Advances past integer characters.
1360 void stuff_byte(ubyte *i)
1371 Mp += strspn(Mp, "+-0123456789");
1376 diag_printf("Stuffed byte: %i\n", *i);
1379 // Stuffs a list of strings
1380 int stuff_string_list(char slp[][NAME_LENGTH], int max_strings)
1383 ignore_white_space();
1386 error_display(1, "Reading string list. Found [%c]. Expecting '('.\n", *Mp);
1387 throw PARSE_ERROR_STRING_LIST;
1392 ignore_white_space();
1394 while (*Mp != ')') {
1395 assert ( count < max_strings );
1396 SDL_assert ( *Mp == '\"' ); // should always be enclosed in quotes
1398 get_string( slp[count++] );
1399 ignore_white_space();
1407 // Stuffs an integer list.
1408 // This is of the form ( i* )
1409 // where i is an integer.
1410 // For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
1411 int stuff_int_list(int *ilp, int max_ints, int lookup_type)
1413 int count = 0, ok_flag = 1, dummy;
1414 ignore_white_space();
1417 error_display(1, "Reading integer list. Found [%c]. Expecting '('.\n", *Mp);
1418 throw PARSE_ERROR_INT_LIST;
1422 ignore_white_space();
1424 while (*Mp != ')') {
1425 SDL_assert(count < max_ints);
1431 switch (lookup_type) {
1433 num = ship_name_lookup(str); // returns index of Ship[] entry with name
1436 case SHIP_INFO_TYPE:
1438 num = ship_info_lookup(str); // returns index of Ship_info[] entry with name
1443 case WEAPON_POOL_TYPE:
1445 num = weapon_info_lookup(str);
1450 case WEAPON_LIST_TYPE:
1451 num = weapon_info_lookup(str);
1456 case RAW_INTEGER_TYPE:
1461 Error(LOCATION,"Unknown lookup_type in stuff_int_list");
1467 Error(LOCATION, "Unable to find string \"%s\" in stuff_int_list\n\nMany possible sources for this error. Get a programmer!\n", str);
1468 } else if (num == -2) {
1469 if (strlen(str) > 0) {
1470 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);
1474 if (num < 0) // other negatives used to bypass the above error trap, but should be -1
1482 stuff_int(&ilp[count++]);
1487 ignore_white_space();
1495 // Marks an integer list.
1496 // This is of the form ( i* )
1497 // where i is an integer.
1498 // If a specified string is found in the lookup and its value is 7, then the 7th value
1499 // in the array is set.
1500 void mark_int_list(int *ilp, int max_ints, int lookup_type)
1502 ignore_white_space();
1505 error_display(1, "Marking integer list. Found [%c]. Expecting '('.\n", *Mp);
1506 throw PARSE_ERROR_INT_LIST;
1510 ignore_white_space();
1512 while (*Mp != ')') {
1518 switch(lookup_type) {
1520 num = ship_name_lookup(str); // returns index of Ship[] entry with name
1523 case SHIP_INFO_TYPE:
1524 num = ship_info_lookup(str); // returns index of Ship_info[] entry with name
1527 case WEAPON_LIST_TYPE:
1528 num = weapon_info_lookup(str);
1532 Error(LOCATION,"Unknown lookup_type in stuff_int_list");
1536 if ( (num < 0) || (num >= max_ints) )
1537 Error(LOCATION, "Unable to find string \"%s\" in mark_int_list.\n", str);
1545 SDL_assert((tval >= 0) && (tval < max_ints));
1549 ignore_white_space();
1557 // Stuff a vector, which is 3 floats.
1558 void stuff_vector(vector *vp)
1560 stuff_float(&vp->xyz.x);
1561 stuff_float(&vp->xyz.y);
1562 stuff_float(&vp->xyz.z);
1565 void stuff_parenthesized_vector(vector *vp)
1567 ignore_white_space();
1570 error_display(1, "Reading parenthesized vector. Found [%c]. Expecting '('.\n", *Mp);
1571 throw PARSE_ERROR_VECTOR_PSTART;
1575 ignore_white_space();
1577 error_display(1, "Reading parenthesized vector. Found [%c]. Expecting ')'.\n", *Mp);
1578 throw PARSE_ERROR_VECTOR_PEND;
1585 // Stuffs vector list.
1586 // This is of the form ( (vector)* )
1587 // where vector is a vector
1588 // For example, ( (1 2 3) (2 3 4) (2 3 5) )
1589 // is a vector list of three vectors.
1590 int stuff_vector_list(vector *vlp, int max_vecs)
1594 ignore_white_space();
1597 error_display(1, "Reading integer list. Found [%c]. Expecting '('.\n", *Mp);
1598 throw PARSE_ERROR_INT_LIST;
1603 ignore_white_space();
1605 while (*Mp != ')') {
1606 SDL_assert(count < max_vecs);
1607 stuff_parenthesized_vector(&vlp[count++]);
1609 ignore_white_space();
1618 // Stuff a matrix, which is 3 vectors.
1619 void stuff_matrix(matrix *mp)
1621 stuff_vector(&mp->v.rvec);
1622 stuff_vector(&mp->v.uvec);
1623 stuff_vector(&mp->v.fvec);
1627 // Given a string, find it in a string array.
1628 // *descrtiption is only used for diagnostics in case it can't be found.
1629 // *str1 is the string to be found.
1630 // *strlist is the list of strings to search.
1631 // max is the number of entries in *strlist to scan.
1632 int string_lookup(const char *str1, const char *strlist[], int max, const char *description, int say_errors)
1636 for (i=0; i<max; i++) {
1637 SDL_assert(strlen(strlist[i]) != 0);
1639 if (!SDL_strcasecmp(str1, strlist[i]))
1644 error_display(0, "Unable to find [%s] in %s list.\n", str1, description);
1649 // Find a required string (*id), then stuff the text of type f_type that
1650 // follows it at *addr. *strlist[] contains the strings it should try to
1652 void find_and_stuff(const char *id, int *addr, int f_type, const char *strlist[], int max, const char *description)
1656 required_string(id);
1657 stuff_string(token, f_type, NULL);
1658 *addr = string_lookup(token, strlist, max, description, 1);
1661 // Mp points at a string.
1662 // Find the string in the list of strings *strlist[].
1663 // Returns the index of the match, -1 if none.
1664 int match_and_stuff(int f_type, const char *strlist[], int max, const char *description)
1668 stuff_string(token, f_type, NULL);
1669 return string_lookup(token, strlist, max, description, 0);
1672 void find_and_stuff_or_add(const char *id, int *addr, int f_type, char *strlist[], const int max_strlen,
1673 int *total, int max, const char *description)
1678 required_string(id);
1679 stuff_string(token, f_type, NULL);
1681 *addr = string_lookup(token, (const char **)strlist, *total, description, 0);
1683 if (*addr == -1) // not in list, so lets try and add it.
1685 SDL_assert(*total < max);
1686 SDL_strlcpy(strlist[*total], token, max_strlen);
1691 // Initialize a parse process.
1700 for (i=0; i<MAX_CARGO; i++)
1701 Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
1703 Total_goal_ship_names = 0;
1715 // Display number of warnings and errors at the end of a parse.
1716 void display_parse_diagnostics()
1718 nprintf(("Parse", "\nParse complete.\n"));
1719 nprintf(("Parse", "%i errors. %i warnings.\n", Error_count, Warning_count));
1722 // Splits a string into 2 lines if the string is wider than max_pixel_w pixels. A null
1723 // terminator is placed where required to make the first line <= max_pixel_w. The remaining
1724 // text is returned (leading whitespace removed). If the line doesn't need to be split,
1725 // NULL is returned.
1726 char *split_str_once(char *src, int max_pixel_w)
1729 int i, w, len, last_was_white = 0;
1732 SDL_assert(max_pixel_w > 0);
1734 gr_get_string_size(&w, NULL, src);
1735 if ( w <= max_pixel_w )
1736 return NULL; // string doesn't require a cut
1739 for (i=0; i<len; i++) {
1740 gr_get_string_size(&w, NULL, src, i);
1741 if ( w > max_pixel_w )
1744 if (src[i] == '\n') { // reached natural end of line
1749 if (is_white_space(src[i])) {
1750 if (!last_was_white)
1766 while (is_white_space(*src))
1770 return NULL; // end of the string anyway
1778 #define SPLIT_STR_BUFFER_SIZE 512
1780 // --------------------------------------------------------------------------------------
1783 // A general function that will split a string into several lines. Lines are allowed up
1784 // to max_pixel_w pixels. Breaks are found in white space.
1786 // Supports \n's in the strings!
1788 // parameters: src => source string to be broken up
1789 // max_pixel_w => max width of line in pixels
1790 // n_chars => output array that will hold number of characters in each line
1791 // p_str => output array of pointers to start of lines within src
1792 // max_lines => limit of number of lines to break src up into
1793 // ignore_char => OPTIONAL parameter (default val -1). Ignore words starting with this character
1794 // This is useful when you want to ignore embedded control information that starts
1795 // with a specific character, like $ or #
1797 // returns: number of lines src is broken into
1798 // -1 is returned when an error occurs
1800 int split_str(const char *src, int max_pixel_w, int *n_chars, char **p_str, int max_lines, char ignore_char)
1802 char buffer[SPLIT_STR_BUFFER_SIZE];
1803 char *breakpoint = NULL;
1804 int sw, new_line = 1, line_num = 0, last_was_white = 0;
1805 int ignore_until_whitespace, buf_index;
1807 // check our assumptions..
1808 SDL_assert(src != NULL);
1809 SDL_assert(n_chars != NULL);
1810 SDL_assert(p_str != NULL);
1811 SDL_assert(max_lines > 0);
1812 SDL_assert(max_pixel_w > 0);
1814 memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1816 ignore_until_whitespace = 0;
1818 // get rid of any leading whitespace
1819 while (is_white_space(*src))
1825 // iterate through chars in line, keeping track of most recent "white space" location that can be used
1826 // as a line splitting point if necessary
1827 for (; *src; src++) {
1828 if (line_num >= max_lines)
1829 return line_num; // time to bail out
1831 // starting a new line of text, init stuff for that
1833 p_str[line_num] = NULL;
1834 if (is_gray_space(*src))
1837 p_str[line_num] = (char*)src;
1842 // maybe skip leading whitespace
1843 if (ignore_until_whitespace) {
1844 if ( is_white_space(*src) )
1845 ignore_until_whitespace = 0;
1850 // if we have a newline, split the line here
1852 n_chars[line_num] = src - p_str[line_num]; // track length of line
1854 p_str[line_num] = NULL;
1857 memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1862 if (*src == ignore_char) {
1863 ignore_until_whitespace = 1;
1867 if (is_gray_space(*src)) {
1868 if (!last_was_white) // track at first whitespace in a series of whitespace
1869 breakpoint = (char*)src;
1874 // indicate next time around that this wasn't a whitespace character
1878 // throw it in our buffer
1879 buffer[buf_index] = *src;
1881 buffer[buf_index] = 0; // null terminate it
1883 gr_get_string_size(&sw, NULL, buffer);
1884 if (sw >= max_pixel_w) {
1891 end = (char*)src; // force a split here since to whitespace
1892 src--; // reuse this character in next line
1895 n_chars[line_num] = end - p_str[line_num]; // track length of line
1896 SDL_assert(n_chars[line_num]);
1898 p_str[line_num] = NULL;
1901 memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1907 if (p_str[line_num]) {
1908 n_chars[line_num] = src - p_str[line_num]; // track length of line
1909 SDL_assert(n_chars[line_num]);