FS2_Open
Open source remastering of the Freespace 2 engine
parselo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
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 the
6  * source.
7  *
8 */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <assert.h>
14 #include <stdarg.h>
15 #include <setjmp.h>
16 
17 #include "ctype.h"
18 #include "globalincs/version.h"
19 #include "localization/fhash.h"
20 #include "localization/localize.h"
21 #include "mission/missionparse.h"
22 #include "parse/encrypt.h"
23 #include "parse/parselo.h"
24 #include "parse/sexp.h"
25 #include "ship/ship.h"
26 #include "weapon/weapon.h"
27 
28 
29 
30 #define ERROR_LENGTH 64
31 #define RS_MAX_TRIES 5
32 
33 // to know that a modular table is currently being parsed
34 bool Parsing_modular_table = false;
35 
38 char Current_filename_sub[MAX_PATH_LEN]; //Last attempted file to load, don't know if ex or not.
45 
46 char *Mission_text = NULL;
47 char *Mission_text_raw = NULL;
48 char *Mp = NULL, *Mp_save = NULL;
49 const char *token_found;
50 
51 static int Parsing_paused = 0;
52 
53 // text allocation stuff
54 void allocate_mission_text(int size);
55 static int Mission_text_size = 0;
56 
57 
58 // Return true if this character is white space, else false.
59 int is_white_space(char ch)
60 {
61  return ((ch == ' ') || (ch == '\t') || (ch == EOLN));
62 }
63 
64 // Returns true if this character is gray space, else false (gray space is white space except for EOLN).
65 int is_gray_space(char ch)
66 {
67  return ((ch == ' ') || (ch == '\t'));
68 }
69 
70 int is_parenthesis(char ch)
71 {
72  return ((ch == '(') || (ch == ')'));
73 }
74 
75 // Advance global Mp (mission pointer) past all current white space.
76 // Leaves Mp pointing at first non white space character.
78 {
79  while ((*Mp != EOF_CHAR) && is_white_space(*Mp))
80  Mp++;
81 }
82 
84 {
85  while ((*Mp != EOF_CHAR) && is_gray_space(*Mp))
86  Mp++;
87 }
88 
89 // Truncate *str, eliminating all trailing white space.
90 // Eg: "abc " becomes "abc"
91 // "abc abc " becomes "abc abc"
92 // "abc \t" becomes "abc"
94 {
95  int i = strlen(str) - 1;
96 
97  while ((i >= 0) && is_white_space(str[i]))
98  i--;
99 
100  str[i+1] = 0;
101 }
102 
103 // Ditto for SCP_string
105 {
106  int i = str.length() - 1;
107 
108  while ((i >= 0) && is_white_space(str[i]))
109  i--;
110 
111  str.resize(i+1);
112 }
113 
114 // Eliminate any leading whitespace in str
116 {
117  int len, first;
118 
119  len = strlen(str);
120  first = 0;
121 
122  // find first non-whitespace
123  while ((first < len) && is_white_space(str[first]))
124  first++;
125 
126  // quick out
127  if (first == 0)
128  return;
129 
130  memmove(str, str+first, len-first);
131  str[len-first] = 0;
132 }
133 
134 // Ditto for SCP_string
136 {
137  int len, first, i;
138 
139  len = str.length();
140  first = 0;
141 
142  // find first non-whitespace
143  while ((first < len) && is_white_space(str[first]))
144  first++;
145 
146  // quick out
147  if (first == 0)
148  return;
149 
150  // copy chars to beginning of string
151  for (i = 0; (first + i) < len; i++)
152  str[i] = str[first + i];
153 
154  // since i is now off the end of the for loop, it represents the new length
155  str.resize(i);
156 }
157 
158 // eliminates all leading and trailing white space from a string. Returns pointer passed in.
159 char *drop_white_space(char *str)
160 {
161  int s, e;
162 
163  s = 0;
164  while (str[s] && is_white_space(str[s]))
165  s++;
166 
167  e = strlen(str) - 1;
168  while (e > s) {
169  if (!is_white_space(str[e]))
170  break;
171 
172  e--;
173  }
174 
175  if (e > s)
176  memmove(str, str + s, e - s + 1);
177 
178  str[e - s + 1] = 0;
179  return str;
180 }
181 
182 // ditto for SCP_string
184 {
185  int len, newlen, first, last, i;
186 
187  len = str.length();
188  first = 0;
189  last = len - 1;
190 
191  // find first non-whitespace
192  while ((first < len) && is_white_space(str[first]))
193  first++;
194 
195  // find last non-whitespace
196  while ((last > first) && is_white_space(str[last]))
197  last--;
198 
199  newlen = last - first + 1;
200 
201  // quick out
202  if (newlen <= 0)
203  {
204  str = "";
205  return;
206  }
207 
208  if (first != 0)
209  {
210  // copy chars to beginning of string
211  for (i = 0; i < newlen; i++)
212  str[i] = str[first + i];
213  }
214 
215  str.resize(newlen);
216 }
217 
218 // Advances Mp past current token.
220 {
222 
223  while ((*Mp != EOF_CHAR) && !is_white_space(*Mp))
224  Mp++;
225 }
226 
227 // Display a diagnostic message if Verbose is set.
228 // (Verbose is set if -v command line switch is present.)
229 void diag_printf(char *format, ...)
230 {
231 #ifndef NDEBUG
233  va_list args;
234 
235  va_start(args, format);
236  vsprintf(buffer, format, args);
237  va_end(args);
238 
239  nprintf(("Parse", "%s", buffer.c_str()));
240 #endif
241 }
242 
243 // Grab and return (a pointer to) a bunch of tokens, terminating at
244 // ERROR_LENGTH chars, or end of line.
245 char *next_tokens()
246 {
247  int count = 0;
248  char *pstr = Mp;
249  char ch;
250 
251  while (((ch = *pstr++) != EOLN) && (ch != EOF_CHAR) && (count < ERROR_LENGTH-1))
252  Error_str[count++] = ch;
253 
254  Error_str[count] = 0;
255  return Error_str;
256 }
257 
258 // Return the line number given by the current mission pointer, ie Mp.
259 // A very slow function (scans all processed text), but who cares how long
260 // an error reporting function takes?
262 {
263  int count = 1;
264  int incomment = 0;
265  int multiline = 0;
266  char *stoploc;
267  char *p;
268 
269  p = Mission_text;
270  stoploc = Mp;
271 
272  while (p < stoploc)
273  {
274  if (*p == EOF_CHAR)
275  Assert(0);
276 
277  if ( !incomment && (*p == COMMENT_CHAR) )
278  incomment = 1;
279 
280  if ( !incomment && (*p == '/') && (*(p+1) == '*') ) {
281  multiline = 1;
282  incomment = 1;
283  }
284 
285  if ( incomment )
286  stoploc++;
287 
288  if ( multiline && (*(p-1) == '*') && (*p == '/') ) {
289  multiline = 0;
290  incomment = 0;
291  }
292 
293  if (*p++ == EOLN) {
294  if ( !multiline && incomment )
295  incomment = 0;
296  count++;
297  }
298  }
299 
300  return count;
301 }
302 
303 // Call this function to display an error message.
304 // error_level == 0 means this is just a warning.
305 // !0 means it's an error message.
306 // Prints line number and other useful information.
307 extern int Cmdline_noparseerrors;
308 void error_display(int error_level, char *format, ...)
309 {
310  char type[8];
311  SCP_string error_text;
312  va_list args;
313 
314  if (error_level == 0) {
315  strcpy_s(type, "Warning");
316  Warning_count++;
317  } else {
318  strcpy_s(type, "Error");
319  Error_count++;
320  }
321 
322  va_start(args, format);
323  vsprintf(error_text, format, args);
324  va_end(args);
325 
326  nprintf((type, "%s(line %i): %s: %s\n", Current_filename, get_line_num(), type, error_text.c_str()));
327 
328  if(error_level == 0 || Cmdline_noparseerrors)
329  Warning(LOCATION, "%s(line %i):\n%s: %s", Current_filename, get_line_num(), type, error_text.c_str());
330  else
331  Error(LOCATION, "%s(line %i):\n%s: %s", Current_filename, get_line_num(), type, error_text.c_str());
332 }
333 
334 // Advance Mp to the next eoln character.
335 void advance_to_eoln(char *more_terminators)
336 {
337  char terminators[128];
338 
339  Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
340 
341  terminators[0] = EOLN;
342  terminators[1] = EOF_CHAR;
343  terminators[2] = 0;
344  if (more_terminators != NULL)
345  strcat_s(terminators, more_terminators);
346  else
347  terminators[2] = 0;
348 
349  while (strchr(terminators, *Mp) == NULL)
350  Mp++;
351 }
352 
353 // Advance Mp to the next white space (ignoring white space inside of " marks)
355 {
356  int in_quotes = 0;
357 
358  while ((*Mp != EOLN) && (*Mp != EOF_CHAR)) {
359  if (*Mp == '\"')
360  in_quotes = !in_quotes;
361 
362  if (!in_quotes && is_white_space(*Mp))
363  break;
364 
365  if (!in_quotes && is_parenthesis(*Mp))
366  break;
367 
368  Mp++;
369  }
370 }
371 
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(char *pstr, char *end)
376 {
377  int len, len2 = 0;
378 
380  len = strlen(pstr);
381  if (end)
382  len2 = strlen(end);
383 
384  while ((*Mp != EOF_CHAR) && strnicmp(pstr, Mp, len)) {
385  if (end && *Mp == '#')
386  return 0;
387 
388  if (end && !strnicmp(end, Mp, len2))
389  return -1;
390 
391  advance_to_eoln(NULL);
393  }
394 
395  if (!Mp || (*Mp == EOF_CHAR))
396  return 0;
397 
398  Mp += strlen(pstr);
399  return 1;
400 }
401 
402 // Goober5000
403 // Advance to start of pstr. Return 0 is successful, otherwise return !0
404 int skip_to_start_of_string(char *pstr, char *end)
405 {
406  int len, endlen;
407 
409  len = strlen(pstr);
410  if(end)
411  endlen = strlen(end);
412  else
413  endlen = 0;
414 
415  while ( (*Mp != EOF_CHAR) && strnicmp(pstr, Mp, len) ) {
416  if (end && *Mp == '#')
417  return 0;
418 
419  if (end && !strnicmp(end, Mp, endlen))
420  return 0;
421 
422  advance_to_eoln(NULL);
424  }
425 
426  if (!Mp || (*Mp == EOF_CHAR))
427  return 0;
428 
429  return 1;
430 }
431 
432 // Advance to start of either pstr1 or pstr2. Return 0 is successful, otherwise return !0
433 int skip_to_start_of_string_either(char *pstr1, char *pstr2, char *end)
434 {
435  int len1, len2, endlen;
436 
438  len1 = strlen(pstr1);
439  len2 = strlen(pstr2);
440  if(end)
441  endlen = strlen(end);
442  else
443  endlen = 0;
444 
445  while ( (*Mp != EOF_CHAR) && strnicmp(pstr1, Mp, len1) && strnicmp(pstr2, Mp, len2) ) {
446  if (end && *Mp == '#')
447  return 0;
448 
449  if (end && !strnicmp(end, Mp, endlen))
450  return 0;
451 
452  advance_to_eoln(NULL);
454  }
455 
456  if (!Mp || (*Mp == EOF_CHAR))
457  return 0;
458 
459  return 1;
460 }
461 
462 // Find a required string.
463 // If not found, display an error message, but try up to RS_MAX_TRIES times
464 // to find the string. (This is the groundwork for ignoring non-understood
465 // lines.
466 // If unable to find the required string after RS_MAX_TRIES tries, then
467 // abort using longjmp to parse_abort.
468 int required_string(const char *pstr)
469 {
470  int count = 0;
471 
473 
474  while (strnicmp(pstr, Mp, strlen(pstr)) && (count < RS_MAX_TRIES)) {
475  error_display(1, "Missing required token: [%s]. Found [%.32s] instead.\n", pstr, next_tokens());
476  advance_to_eoln(NULL);
478  count++;
479  }
480 
481  if (count == RS_MAX_TRIES) {
482  nprintf(("Error", "Error: Unable to find required token [%s]\n", pstr));
483  Warning(LOCATION, "Error: Unable to find required token [%s]\n", pstr);
484  throw parse::ParseException("Required string not found");
485  }
486 
487  Mp += strlen(pstr);
488  diag_printf("Found required string [%s]\n", token_found = pstr);
489  return 1;
490 }
491 
493 {
495 
496  if (*Mp == EOF_CHAR)
497  return 1;
498 
499  return 0;
500 }
501 
506 {
508 
509  if(*Mp == EOLN)
510  return 1;
511  else
512  return 0;
513 }
514 
515 // similar to optional_string, but just checks if next token is a match.
516 // It doesn't advance Mp except to skip past white space.
517 int check_for_string(const char *pstr)
518 {
520 
521  if (!strnicmp(pstr, Mp, strlen(pstr)))
522  return 1;
523 
524  return 0;
525 }
526 
527 // like check for string, but doesn't skip past any whitespace
528 int check_for_string_raw(const char *pstr)
529 {
530  if (!strnicmp(pstr, Mp, strlen(pstr)))
531  return 1;
532 
533  return 0;
534 }
535 
536 // Find an optional string.
537 // If found, return 1, else return 0.
538 // If found, point past string, else don't update pointer.
539 int optional_string(const char *pstr)
540 {
542 
543  if (!strnicmp(pstr, Mp, strlen(pstr))) {
544  Mp += strlen(pstr);
545  return 1;
546  }
547 
548  return 0;
549 }
550 
551 int optional_string_either(char *str1, char *str2)
552 {
554 
555  if ( !strnicmp(str1, Mp, strlen(str1)) ) {
556  Mp += strlen(str1);
557  return 0;
558  } else if ( !strnicmp(str2, Mp, strlen(str2)) ) {
559  Mp += strlen(str2);
560  return 1;
561  }
562 
563  return -1;
564 }
565 
566 // generic parallel to required_string_one_of
567 int optional_string_one_of(int arg_count, ...)
568 {
569  Assertion(arg_count > 0, "optional_string_one_of() called with arg_count of %d; get a coder!\n", arg_count);
570  int idx, found = -1;
571  char *pstr;
572  va_list vl;
573 
575 
576  va_start(vl, arg_count);
577  for (idx = 0; idx < arg_count; idx++)
578  {
579  pstr = va_arg(vl, char*);
580 
581  if ( !strnicmp(pstr, Mp, strlen(pstr)) )
582  {
583  Mp += strlen(pstr);
584  found = idx;
585  break;
586  }
587  }
588  va_end(vl);
589 
590  return found;
591 }
592 
593 int required_string_fred(char *pstr, char *end)
594 {
595  char *backup = Mp;
596 
597  token_found = pstr;
598  if (fred_parse_flag)
599  return 0;
600 
602  while (*Mp != EOF_CHAR && strnicmp(pstr, Mp, strlen(pstr))) {
603  if ((*Mp == '#') || (end && !strnicmp(end, Mp, strlen(end)))) {
604  Mp = NULL;
605  break;
606  }
607 
608  advance_to_eoln(NULL);
610  }
611 
612  if (!Mp || (*Mp == EOF_CHAR)) {
613  diag_printf("Required string [%s] not found\n", pstr);
614  Mp = backup;
615  Token_found_flag = 0;
616  return 0;
617  }
618 
619  Mp += strlen(pstr);
620  diag_printf("Found required string [%s]\n", pstr);
621  Token_found_flag = 1;
622  return 1;
623 }
624 
625 // attempt to find token in buffer. It might not exist, however, in which case we don't need
626 // to do anything. If it is found, then we advance the pointer to just after the token. To
627 // further complicate things, we should only search to a certain point, since we don't want
628 // a token that belongs to another section which might match the token we want. Thus, we
629 // also pass in an ending token, which marks the point we should stop looking at.
630 int optional_string_fred(char *pstr, char *end, char *end2)
631 {
632  char *mp_save = Mp;
633 
634  token_found = pstr;
635  if (fred_parse_flag)
636  return 0;
637 
639  while ((*Mp != EOF_CHAR) && strnicmp(pstr, Mp, strlen(pstr))) {
640  if ((*Mp == '#') || (end && !strnicmp(end, Mp, strlen(end))) ||
641  (end2 && !strnicmp(end2, Mp, strlen(end2)))) {
642  Mp = NULL;
643  break;
644  }
645 
646  advance_to_eoln(NULL);
648  }
649 
650  if (!Mp || (*Mp == EOF_CHAR)) {
651  diag_printf("Optional string [%s] not found\n", pstr);
652  Mp = mp_save;
653  Token_found_flag = 0;
654  return 0;
655  }
656 
657  Mp += strlen(pstr);
658  diag_printf("Found optional string [%s]\n", pstr);
659  Token_found_flag = 1;
660  return 1;
661 }
662 
673 int required_string_either(char *str1, char *str2)
674 {
676 
677  for (int count = 0; count < RS_MAX_TRIES; ++count) {
678  if (strnicmp(str1, Mp, strlen(str1)) == 0) {
679  // Mp += strlen(str1);
680  diag_printf("Found required string [%s]\n", token_found = str1);
681  return 0;
682  } else if (strnicmp(str2, Mp, strlen(str2)) == 0) {
683  // Mp += strlen(str2);
684  diag_printf("Found required string [%s]\n", token_found = str2);
685  return 1;
686  }
687 
688  error_display(1, "Required token = [%s] or [%s], found [%.32s].\n", str1, str2, next_tokens());
689 
690  advance_to_eoln(NULL);
692  }
693 
694  nprintf(("Error", "Error: Unable to find either required token [%s] or [%s]\n", str1, str2));
695  Warning(LOCATION, "Error: Unable to find either required token [%s] or [%s]\n", str1, str2);
696  throw parse::ParseException("Required string not found");
697  return -1; // Dead code, but some compilers still complain about it
698 }
699 
708 int required_string_one_of(int arg_count, ...)
709 {
710  Assertion(arg_count > 0, "required_string_one_of() called with arg_count of %d; get a coder!\n", arg_count);
711  int count = 0;
712  int idx;
713  char *expected;
714  SCP_string message = "";
715  va_list vl;
716 
718 
719  while (count < RS_MAX_TRIES) {
720  va_start(vl, arg_count);
721  for (idx = 0; idx < arg_count; idx++) {
722  expected = va_arg(vl, char*);
723  if (strnicmp(expected, Mp, strlen(expected)) == 0) {
724  diag_printf("Found required string [%s]", token_found = expected);
725  va_end(vl);
726  return idx;
727  }
728  }
729  va_end(vl);
730 
731  if (!message.compare("")) {
732  va_start(vl, arg_count);
733  message = "Required token = ";
734  for (idx = 0; idx < arg_count; idx++) {
735  message += "[";
736  message += va_arg(vl, char*);
737  message += "]";
738  if (arg_count == 2 && idx == 0) {
739  message += " or ";
740  } else if (idx == arg_count - 2) {
741  message += ", or ";
742  } else if (idx < arg_count - 2) {
743  message += ", ";
744  }
745  }
746  va_end(vl);
747  }
748 
749  error_display(1, "%s, found [%.32s]\n", message.c_str(), next_tokens());
750  advance_to_eoln(NULL);
752  count++;
753  }
754 
755  return -1;
756 }
757 
758 int required_string_either_fred(char *str1, char *str2)
759 {
761 
762  while (*Mp != EOF_CHAR) {
763  if (!strnicmp(str1, Mp, strlen(str1))) {
764  // Mp += strlen(str1);
765  diag_printf("Found required string [%s]\n", token_found = str1);
766  return fred_parse_flag = 0;
767 
768  } else if (!strnicmp(str2, Mp, strlen(str2))) {
769  // Mp += strlen(str2);
770  diag_printf("Found required string [%s]\n", token_found = str2);
771  return fred_parse_flag = 1;
772  }
773 
774  advance_to_eoln(NULL);
776  }
777 
778  if (*Mp == EOF_CHAR)
779  diag_printf("Unable to find either required token [%s] or [%s]\n", str1, str2);
780 
781  return -1;
782 }
783 
784 // Copy characters from instr to outstr until eoln is found, or until max
785 // characters have been copied (including terminator).
786 void copy_to_eoln(char *outstr, char *more_terminators, char *instr, int max)
787 {
788  int count = 0;
789  char ch;
790  char terminators[128];
791 
792  Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
793 
794  terminators[0] = EOLN;
795  terminators[1] = EOF_CHAR;
796  terminators[2] = 0;
797  if (more_terminators != NULL)
798  strcat_s(terminators, more_terminators);
799  else
800  terminators[2] = 0;
801 
802  while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL) && (count < max)) {
803  *outstr++ = ch;
804  count++;
805  }
806 
807  if (count >= max)
808  error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
809 
810  *outstr = 0;
811 }
812 
813 // Ditto for SCP_string.
814 void copy_to_eoln(SCP_string &outstr, char *more_terminators, char *instr)
815 {
816  char ch;
817  char terminators[128];
818 
819  Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
820 
821  terminators[0] = EOLN;
822  terminators[1] = EOF_CHAR;
823  terminators[2] = 0;
824  if (more_terminators != NULL)
825  strcat_s(terminators, more_terminators);
826  else
827  terminators[2] = 0;
828 
829  outstr = "";
830  while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL)) {
831  outstr.append(1, ch);
832  }
833 }
834 
835 // Copy characters from instr to outstr until next white space is found, or until max
836 // characters have been copied (including terminator).
837 void copy_to_next_white(char *outstr, char *instr, int max)
838 {
839  int count = 0;
840  int in_quotes = 0;
841  char ch;
842 
843  while (((ch = *instr++)>0) && (ch != EOLN) && (ch != EOF_CHAR) && (count < max)) {
844  if ( ch == '\"' ) {
845  in_quotes = !in_quotes;
846  continue;
847  }
848 
849  if ( !in_quotes && is_white_space(ch) ) // not in quotes, white space terminates string
850  break;
851 
852  if ( !in_quotes && is_parenthesis(ch) ) // not in quotes, parentheses are important for parsing so we don't want to copy them
853  break;
854 
855  *outstr++ = ch;
856  count++;
857  }
858 
859  if (count >= max)
860  error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
861 
862  *outstr = 0;
863 }
864 
865 // Ditto for SCP_string.
866 void copy_to_next_white(SCP_string &outstr, char *instr)
867 {
868  int in_quotes = 0;
869  char ch;
870 
871  outstr = "";
872  while (((ch = *instr++)>0) && (ch != EOLN) && (ch != EOF_CHAR)) {
873  if ( ch == '\"' ) {
874  in_quotes = !in_quotes;
875  continue;
876  }
877 
878  if ( !in_quotes && is_white_space(ch) ) // not in quotes, white space terminates string
879  break;
880 
881  if ( !in_quotes && is_parenthesis(ch) ) // not in quotes, parentheses are important for parsing so we don't want to copy them
882  break;
883 
884  outstr.append(1, ch);
885  }
886 }
887 
888 //Returns a null-terminated character string allocated with vm_malloc() with the data
889 char* alloc_text_until(char* instr, char* endstr)
890 {
891  Assert(instr && endstr);
892  char *foundstr = stristr(instr, endstr);
893 
894  if(foundstr == NULL)
895  {
896  Error(LOCATION, "Missing [%s] in file", endstr);
897  throw parse::ParseException("End string not found");
898  }
899  else
900  {
901  if ( (foundstr - instr) <= 0 ) {
902  Int3(); // since this really shouldn't ever happen
903  return NULL;
904  }
905 
906  char* rstr = NULL;
907  rstr = (char*) vm_malloc((foundstr - instr + 1)*sizeof(char));
908 
909  if(rstr != NULL) {
910  strncpy(rstr, instr, foundstr-instr);
911  rstr[foundstr-instr] = '\0';
912  } else {
913  Error(LOCATION, "Could not allocate enough memory in alloc_text_until");
914  }
915 
916  return rstr;
917  }
918 }
919 
920 // Copy text until a certain string is matched.
921 // For example, this is used to copy mission notes, scanning until $END NOTES:
922 // is found.
923 void copy_text_until(char *outstr, char *instr, char *endstr, int max_chars)
924 {
925  char *foundstr;
926  Assert(outstr && instr && endstr);
927 
928  foundstr = stristr(instr, endstr);
929 
930  if (foundstr == NULL) {
931  nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
932  throw parse::ParseException("End string not found");
933  }
934 
935  if (foundstr - instr + strlen(endstr) < (uint) max_chars) {
936  strncpy(outstr, instr, foundstr - instr);
937  outstr[foundstr - instr] = 0;
938 
939  } else {
940  nprintf(("Error", "Error. Too much text (" SIZE_T_ARG " chars, %i allowed) before %s\n",
941  foundstr - instr - strlen(endstr), max_chars, endstr));
942 
943  throw parse::ParseException("Too much text found");
944  }
945 
946  diag_printf("Here's the partial wad of text:\n%.30s\n", outstr);
947 }
948 
949 // Ditto for SCP_string.
950 void copy_text_until(SCP_string &outstr, char *instr, char *endstr)
951 {
952  char *foundstr;
953  Assert(instr && endstr);
954 
955  foundstr = stristr(instr, endstr);
956 
957  if (foundstr == NULL) {
958  nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
959  throw parse::ParseException("End string not found");
960  }
961 
962  outstr.assign(instr, foundstr - instr);
963 
964  diag_printf("Here's the partial wad of text:\n%.30s\n", outstr.c_str());
965 }
966 
967 // stuffs a string into a buffer. Can get a string between " marks and stops
968 // when whitespace is encounted -- not to end of line
969 void stuff_string_white(char *outstr, int len)
970 {
971  if(!len)
972  len = NAME_LENGTH-1;
973 
975  copy_to_next_white(outstr, Mp, len);
977 }
978 
979 // ditto for SCP_string
981 {
983  copy_to_next_white(outstr, Mp);
985 }
986 
987 // Goober5000
988 void stuff_string_until(char *outstr, char *endstr, int len)
989 {
990  if(!len)
991  len = NAME_LENGTH-1;
992 
994  copy_text_until(outstr, Mp, endstr, len);
995  Mp += strlen(outstr);
997 }
998 
999 // Goober5000
1000 void stuff_string_until(SCP_string &outstr, char *endstr)
1001 {
1003  copy_text_until(outstr, Mp, endstr);
1004  Mp += outstr.length();
1005  drop_trailing_white_space(outstr);
1006 }
1007 
1008 //WMC
1009 //Used for allocating large blocks, eg of Python code
1010 //Returns a null-terminated string allocated with vm_malloc(),
1011 //or NULL on failure
1012 //Does depth checks for the start and end strings
1013 //extra_chars indicates extra malloc space that should be allocated.
1014 char* alloc_block(char* startstr, char* endstr, int extra_chars)
1015 {
1016  Assert(startstr != NULL && endstr != NULL);
1017  Assert(stricmp(startstr, endstr));
1018 
1019  char* rval = NULL;
1020  uint elen = strlen(endstr);
1021  uint slen = strlen(startstr);
1022  uint flen = 0;
1023 
1024  //Skip the opening thing and any extra stuff
1025  required_string(startstr);
1027 
1028  //Allocate it
1029  char* pos = Mp;
1030 
1031  //Depth checking
1032  int level = 1;
1033  while(*pos != EOF_CHAR)
1034  {
1035  if(!strnicmp(pos, startstr, slen))
1036  {
1037  level++;
1038  }
1039  else if(!strnicmp(pos, endstr, elen))
1040  {
1041  level--;
1042  }
1043 
1044  if(level<=0)
1045  {
1046  break;
1047  }
1048 
1049  pos++;
1050  }
1051 
1052  //Check that we left the file
1053  if(level > 0)
1054  {
1055  Error(LOCATION, "Unclosed pair of \"%s\" and \"%s\" on line %d in file", startstr, endstr, get_line_num());
1056  throw parse::ParseException("End string not found");
1057  }
1058  else
1059  {
1060  //Set final length for faster calcs
1061  flen = pos-Mp;
1062 
1063  //Allocate the memory
1064  //WMC - Don't forget the null character that's added later on.
1065  rval = (char*) vm_malloc((flen + extra_chars + 1)*sizeof(char));
1066 
1067  //Copy the text (if memory was allocated)
1068  if(rval != NULL) {
1069  strncpy(rval, Mp, flen);
1070  rval[flen] = '\0';
1071  } else {
1072  return NULL;
1073  }
1074  }
1075 
1076  //Skip the copied stuff
1077  Mp += flen;
1078  required_string(endstr);
1079  return rval;
1080 }
1081 
1082 // Karajorma - Stuffs the provided char array with either the contents of a quoted string or the name of a string
1083 // variable. Returns PARSING_FOUND_STRING if a string was found or PARSING_FOUND_VARIABLE if a variable was present.
1084 int get_string_or_variable (char *str)
1085 {
1086  int result = -1;
1087 
1089 
1090  // Variable
1091  if (*Mp == '@')
1092  {
1093  Mp++;
1094  stuff_string_white(str);
1095  int sexp_variable_index = get_index_sexp_variable_name(str);
1096 
1097  // We only want String variables
1098  Assertion (sexp_variable_index != -1, "Didn't find variable name \"%s\"", str);
1099  Assert (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING);
1100 
1101  result = PARSING_FOUND_VARIABLE;
1102  }
1103  // Quoted string
1104  else if (*Mp == '"')
1105  {
1106  get_string(str);
1107  result = PARSING_FOUND_STRING;
1108  }
1109  else
1110  {
1111  get_string(str);
1112  Error(LOCATION, "Invalid entry \"%s\" found in get_string_or_variable. Must be a quoted string or a string variable name.", str);
1113  }
1114 
1115  return result;
1116 }
1117 
1118 // ditto for SCP_string
1120 {
1121  int result = -1;
1122 
1124 
1125  // Variable
1126  if (*Mp == '@')
1127  {
1128  Mp++;
1129  stuff_string_white(str);
1130  int sexp_variable_index = get_index_sexp_variable_name(str);
1131 
1132  // We only want String variables
1133  Assertion (sexp_variable_index != -1, "Didn't find variable name \"%s\"", str.c_str());
1134  Assert (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING);
1135 
1136  result = PARSING_FOUND_VARIABLE;
1137  }
1138  // Quoted string
1139  else if (*Mp == '"')
1140  {
1141  get_string(str);
1142  result = PARSING_FOUND_STRING;
1143  }
1144  else
1145  {
1146  get_string(str);
1147  Error(LOCATION, "Invalid entry \"%s\" found in get_string_or_variable. Must be a quoted string or a string variable name.", str.c_str());
1148  }
1149 
1150  return result;
1151 }
1152 
1157 int get_string(char *str, int max)
1158 {
1159  int len;
1160 
1161  len = strcspn(Mp + 1, "\"");
1162 
1163  if (max >= 0 && len >= max)
1164  error_display(0, "String too long. Length = %i. Max is %i.\n", len, max);
1165 
1166  strncpy(str, Mp + 1, len);
1167  str[len] = 0;
1168 
1169  Mp += len + 2;
1170  return len;
1171 }
1172 
1177 {
1178  int len;
1179 
1180  len = strcspn(Mp + 1, "\"");
1181  str.assign(Mp + 1, len);
1182 
1183  Mp += len + 2;
1184 }
1185 
1186 // Stuff a string into a string buffer.
1187 // Supports various FreeSpace primitive types. If 'len' is supplied, it will override
1188 // the default string length if using the F_NAME case.
1189 void stuff_string(char *outstr, int type, int len, char *terminators)
1190 {
1191  char read_str[PARSE_BUF_SIZE] = "";
1192  int read_len = PARSE_BUF_SIZE;
1193  int final_len = len - 1;
1194  int tag_id;
1195 
1196  // make sure we have enough room
1197  Assert( final_len > 0 );
1198 
1199  // make sure it's zero'd out
1200  memset( outstr, 0, len );
1201 
1202  switch (type) {
1203  case F_RAW:
1204  case F_LNAME:
1205  case F_NAME:
1206  case F_DATE:
1207  case F_FILESPEC:
1208  case F_PATHNAME:
1209  case F_MESSAGE:
1211  copy_to_eoln(read_str, terminators, Mp, read_len);
1212  drop_trailing_white_space(read_str);
1213  advance_to_eoln(terminators);
1214  break;
1215 
1216  case F_NOTES:
1218  copy_text_until(read_str, Mp, "$End Notes:", read_len);
1219  Mp += strlen(read_str);
1220  required_string("$End Notes:");
1221  break;
1222 
1223  // F_MULTITEXTOLD keeping for backwards compatability with old missions
1224  // can be deleted once all missions are using new briefing format
1225 
1226  case F_MULTITEXTOLD:
1228  copy_text_until(read_str, Mp, "$End Briefing Text:", read_len);
1229  Mp += strlen(read_str);
1230  required_string("$End Briefing Text:");
1231  break;
1232 
1233  case F_MULTITEXT:
1235  copy_text_until(read_str, Mp, "$end_multi_text", read_len);
1236  Mp += strlen(read_str);
1237  drop_trailing_white_space(read_str);
1238  required_string("$end_multi_text");
1239  break;
1240 
1241  default:
1242  Error(LOCATION, "Unhandled string type %d in stuff_string!", type);
1243  }
1244 
1245  if (type == F_FILESPEC) {
1246  // Make sure that the passed string looks like a good filename
1247  if (strlen(read_str) == 0) {
1248  // Empty file name is not valid!
1249  error_display(1, "A file name was expected but no name was supplied!\n");
1250  }
1251  }
1252 
1253  // now we want to do any final localization
1254  if(type != F_RAW && type != F_LNAME)
1255  {
1256  lcl_ext_localize(read_str, outstr, final_len, &tag_id);
1257 
1258  // if the hash localized text hash table is active and we have a valid external string - hash it
1259  if(fhash_active() && (tag_id > -2)){
1260  fhash_add_str(outstr, tag_id);
1261  }
1262  }
1263  else
1264  {
1265  if ( strlen(read_str) > (uint)final_len )
1266  error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", read_str, strlen(read_str), final_len);
1267 
1268  strncpy(outstr, read_str, final_len);
1269  }
1270 
1271  diag_printf("Stuffed string = [%.30s]\n", outstr);
1272 }
1273 
1274 // Stuff a string into a string buffer.
1275 // Supports various FreeSpace primitive types.
1276 void stuff_string(SCP_string &outstr, int type, char *terminators)
1277 {
1278  SCP_string read_str;
1279  int tag_id;
1280 
1281  // make sure it's zero'd out
1282  outstr = "";
1283 
1284  switch (type) {
1285  case F_RAW:
1286  case F_LNAME:
1287  case F_NAME:
1288  case F_DATE:
1289  case F_FILESPEC:
1290  case F_PATHNAME:
1291  case F_MESSAGE:
1293  copy_to_eoln(read_str, terminators, Mp);
1294  drop_trailing_white_space(read_str);
1295  advance_to_eoln(terminators);
1296  break;
1297 
1298  case F_NOTES:
1300  copy_text_until(read_str, Mp, "$End Notes:");
1301  Mp += read_str.length();
1302  required_string("$End Notes:");
1303  break;
1304 
1305  // F_MULTITEXTOLD keeping for backwards compatability with old missions
1306  // can be deleted once all missions are using new briefing format
1307 
1308  case F_MULTITEXTOLD:
1310  copy_text_until(read_str, Mp, "$End Briefing Text:");
1311  Mp += read_str.length();
1312  required_string("$End Briefing Text:");
1313  break;
1314 
1315  case F_MULTITEXT:
1317  copy_text_until(read_str, Mp, "$end_multi_text");
1318  Mp += read_str.length();
1319  drop_trailing_white_space(read_str);
1320  required_string("$end_multi_text");
1321  break;
1322 
1323  default:
1324  Error(LOCATION, "Unhandled string type %d in stuff_string!", type);
1325  }
1326 
1327  if (type == F_FILESPEC) {
1328  // Make sure that the passed string looks like a good filename
1329  if (read_str.empty()) {
1330  // Empty file name is not valid!
1331  error_display(1, "A file name was expected but no name was supplied!\n");
1332  }
1333  }
1334 
1335  // now we want to do any final localization
1336  if(type != F_RAW && type != F_LNAME)
1337  {
1338  lcl_ext_localize(read_str, outstr, &tag_id);
1339 
1340  // if the hash localized text hash table is active and we have a valid external string - hash it
1341  if(fhash_active() && (tag_id > -2)){
1342  fhash_add_str(outstr.c_str(), tag_id);
1343  }
1344  }
1345  else
1346  {
1347  outstr = read_str;
1348  }
1349 
1350  diag_printf("Stuffed string = [%.30s]\n", outstr.c_str());
1351 }
1352 
1353 // stuff a string, but only until the end of a line. don't ignore leading whitespace. close analog of fgets()/cfgets()
1354 void stuff_string_line(char *outstr, int len)
1355 {
1356  char read_str[PARSE_BUF_SIZE] = "";
1357  int read_len = PARSE_BUF_SIZE;
1358  int final_len = len - 1;
1359  int tag_id;
1360 
1361  Assert( final_len > 0 );
1362 
1363  // read in a line
1364  copy_to_eoln(read_str, "\n", Mp, read_len);
1365  drop_trailing_white_space(read_str);
1366  advance_to_eoln("");
1367  Mp++;
1368 
1369  // now we want to do any final localization
1370  lcl_ext_localize(read_str, outstr, final_len, &tag_id);
1371 
1372  // if the hash localized text hash table is active and we have a valid external string - hash it
1373  if(fhash_active() && (tag_id > -2)){
1374  fhash_add_str(outstr, tag_id);
1375  }
1376 
1377  diag_printf("Stuffed string = [%.30s]\n", outstr);
1378 }
1379 
1380 // ditto for SCP_string
1382 {
1383  SCP_string read_str;
1384  int tag_id;
1385 
1386  // read in a line
1387  copy_to_eoln(read_str, "\n", Mp);
1388  drop_trailing_white_space(read_str);
1389  advance_to_eoln("");
1390  Mp++;
1391 
1392  // now we want to do any final localization
1393  lcl_ext_localize(read_str, outstr, &tag_id);
1394 
1395  // if the hash localized text hash table is active and we have a valid external string - hash it
1396  if(fhash_active() && (tag_id > -2)){
1397  fhash_add_str(outstr.c_str(), tag_id);
1398  }
1399 
1400  diag_printf("Stuffed string = [%.30s]\n", outstr.c_str());
1401 }
1402 
1403 // Exactly the same as stuff string only Malloc's the buffer.
1404 // Supports various FreeSpace primitive types. If 'len' is supplied, it will override
1405 // the default string length if using the F_NAME case.
1406 char *stuff_and_malloc_string(int type, char *terminators)
1407 {
1408  SCP_string tmp_result;
1409 
1410  stuff_string(tmp_result, type, terminators);
1411  drop_white_space(tmp_result);
1412 
1413  if (tmp_result.empty())
1414  return NULL;
1415 
1416  return vm_strdup(tmp_result.c_str());
1417 }
1418 
1419 void stuff_malloc_string(char **dest, int type, char *terminators)
1420 {
1421  Assert(dest != NULL); //wtf?
1422 
1423  char *new_val = stuff_and_malloc_string(type, terminators);
1424 
1425  if(new_val != NULL)
1426  {
1427  if((*dest) != NULL) {
1428  vm_free(*dest);
1429  }
1430 
1431  (*dest) = new_val;
1432  }
1433 }
1434 
1435 // After reading a multitext string, you can call this function to convert any newlines into
1436 // spaces, so it's a one paragraph string (I.e. as in MS-Word).
1437 //
1439 {
1440  unsigned int i;
1441  unsigned int len = strlen(str);
1442  int num_cr = 0;
1443 
1444  for (i=0; i<len; i++)
1445  {
1446  // skip CR
1447  // convert LF to space
1448  // copy characters backwards if any CRs previously encountered
1449  if (str[i] == '\r')
1450  num_cr++;
1451  else if (str[i] == '\n')
1452  str[i-num_cr] = ' ';
1453  else if (num_cr > 0)
1454  str[i-num_cr] = str[i];
1455  }
1456 
1457  if (num_cr > 0)
1458  str[len-num_cr] = 0;
1459 }
1460 
1461 // ditto for SCP_string
1463 {
1464  unsigned int i;
1465  unsigned int len = str.length();
1466  int num_cr = 0;
1467 
1468  for (i=0; i<len; i++)
1469  {
1470  // skip CR
1471  // convert LF to space
1472  // copy characters backwards if any CRs previously encountered
1473  if (str[i] == '\r')
1474  num_cr++;
1475  else if (str[i] == '\n')
1476  str[i-num_cr] = ' ';
1477  else if (num_cr > 0)
1478  str[i-num_cr] = str[i];
1479  }
1480 
1481  if (num_cr > 0)
1482  str.resize(len-num_cr);
1483 }
1484 
1485 // Converts a character from Windows-1252 to CP437.
1487 {
1488  // time to do some special foreign character conversion
1489  switch (ch) {
1490  case -57:
1491  ch = 128;
1492  break;
1493 
1494  case -4:
1495  ch = 129;
1496  break;
1497 
1498  case -23:
1499  ch = 130;
1500  break;
1501 
1502  case -30:
1503  ch = 131;
1504  break;
1505 
1506  case -28:
1507  ch = 132;
1508  break;
1509 
1510  case -32:
1511  ch = 133;
1512  break;
1513 
1514  case -27:
1515  ch = 134;
1516  break;
1517 
1518  case -25:
1519  ch = 135;
1520  break;
1521 
1522  case -22:
1523  ch = 136;
1524  break;
1525 
1526  case -21:
1527  ch = 137;
1528  break;
1529 
1530  case -24:
1531  ch = 138;
1532  break;
1533 
1534  case -17:
1535  ch = 139;
1536  break;
1537 
1538  case -18:
1539  ch = 140;
1540  break;
1541 
1542  case -20:
1543  ch = 141;
1544  break;
1545 
1546  case -60:
1547  ch = 142;
1548  break;
1549 
1550  case -59:
1551  ch = 143;
1552  break;
1553 
1554  case -55:
1555  ch = 144;
1556  break;
1557 
1558  case -26:
1559  ch = 145;
1560  break;
1561 
1562  case -58:
1563  ch = 146;
1564  break;
1565 
1566  case -12:
1567  ch = 147;
1568  break;
1569 
1570  case -10:
1571  ch = 148;
1572  break;
1573 
1574  case -14:
1575  ch = 149;
1576  break;
1577 
1578  case -5:
1579  ch = 150;
1580  break;
1581 
1582  case -7:
1583  ch = 151;
1584  break;
1585 
1586  case -1:
1587  ch = 152;
1588  break;
1589 
1590  case -42:
1591  ch = 153;
1592  break;
1593 
1594  case -36:
1595  ch = 154;
1596  break;
1597 
1598  case -94:
1599  ch = 155;
1600  break;
1601 
1602  case -93:
1603  ch = 156;
1604  break;
1605 
1606  case -91:
1607  ch = 157;
1608  break;
1609 
1610  case -125:
1611  ch = 159;
1612  break;
1613 
1614  case -31:
1615  ch = 160;
1616  break;
1617 
1618  case -19:
1619  ch = 161;
1620  break;
1621 
1622  case -13:
1623  ch = 162;
1624  break;
1625 
1626  case -6:
1627  ch = 163;
1628  break;
1629 
1630  case -15:
1631  ch = 164;
1632  break;
1633 
1634  case -47:
1635  ch = 165;
1636  break;
1637 
1638  case -86:
1639  ch = 166;
1640  break;
1641 
1642  case -70:
1643  ch = 167;
1644  break;
1645 
1646  case -65:
1647  ch = 168;
1648  break;
1649 
1650  case -84:
1651  ch = 170;
1652  break;
1653 
1654  case -67:
1655  ch = 171;
1656  break;
1657 
1658  case -68:
1659  ch = 172;
1660  break;
1661 
1662  case -95:
1663  ch = 173;
1664  break;
1665 
1666  case -85:
1667  ch = 174;
1668  break;
1669 
1670  case -69:
1671  ch = 175;
1672  break;
1673 
1674  case -33:
1675  ch = 225;
1676  break;
1677 
1678  case -75:
1679  ch = 230;
1680  break;
1681 
1682  case -79:
1683  ch = 241;
1684  break;
1685 
1686  case -9:
1687  ch = 246;
1688  break;
1689 
1690  case -80:
1691  ch = 248;
1692  break;
1693 
1694  case -73:
1695  ch = 250;
1696  break;
1697 
1698  case -78:
1699  ch = 253;
1700  break;
1701 
1702  case -96:
1703  ch = 255;
1704  break;
1705  }
1706 
1707  return ch;
1708 }
1709 
1710 // Goober5000
1712 {
1713  char *ch;
1714  if (Fred_running)
1715  return;
1716 
1717  if (Lcl_pl)
1718  return;
1719 
1720  for (ch = line; *ch != '\0'; ch++)
1721  *ch = (char) maybe_convert_foreign_character(*ch);
1722 }
1723 
1724 // Goober5000
1726 {
1727  if (Fred_running)
1728  return;
1729 
1730  if (Lcl_pl)
1731  return;
1732 
1733  for (SCP_string::iterator ii = line.begin(); ii != line.end(); ++ii)
1734  *ii = (char) maybe_convert_foreign_character(*ii);
1735 }
1736 
1737 // Goober5000
1738 bool get_number_before_separator(int &number, int &number_chars, const char *text, char separator)
1739 {
1740  char buf[8];
1741  const char *ch = text;
1742  int len = 0;
1743 
1744  while (true)
1745  {
1746  // didn't find separator
1747  if (*ch == '\0' || len == 8)
1748  return false;
1749 
1750  // found separator
1751  if (*ch == separator)
1752  break;
1753 
1754  // found nondigit
1755  if (!isdigit(*ch))
1756  return false;
1757 
1758  // copying in progress
1759  buf[len] = *ch;
1760  len++;
1761  ch++;
1762  }
1763 
1764  // got an integer
1765  buf[len] = '\0';
1766  number = atoi(buf);
1767  number_chars = len;
1768  return true;
1769 }
1770 
1771 // Goober5000
1772 bool get_number_before_separator(int &number, int &number_chars, const SCP_string &text, SCP_string::iterator text_pos, char separator)
1773 {
1774  char buf[8];
1775  SCP_string::iterator ch = text_pos;
1776  int len = 0;
1777 
1778  while (true)
1779  {
1780  // didn't find separator
1781  if (ch == text.end() || len == 8)
1782  return false;
1783 
1784  // found separator
1785  if (*ch == separator)
1786  break;
1787 
1788  // found nondigit
1789  if (!isdigit(*ch))
1790  return false;
1791 
1792  // copying in progress
1793  buf[len] = *ch;
1794  len++;
1795  ++ch;
1796  }
1797 
1798  // got an integer
1799  buf[len] = '\0';
1800  number = atoi(buf);
1801  number_chars = len;
1802  return true;
1803 }
1804 
1805 bool matches_version_specific_tag(const char *line_start, bool &compatible_version, int &tag_len)
1806 {
1807  // special version-specific comment
1808  // formatted like e.g. ;;FSO 3.7.0;;
1809  // Should now support anything from ;;FSO 3;; to ;;FSO 3.7.3.20151106;; -MageKing17
1810  if (strnicmp(line_start, ";;FSO ", 6))
1811  return false;
1812 
1813  int major, minor, build, revis;
1814  int s_num = scan_fso_version_string(line_start, &major, &minor, &build, &revis);
1815 
1816  if (s_num == 0)
1817  return false;
1818 
1819  // hack for releases
1820  if (s_num == 4 && FS_VERSION_REVIS < 1000) {
1821  s_num = 3;
1822  }
1823 
1824  const char *ch = line_start + 6;
1825  while ((*ch) != ';') {
1826  Assertion((*ch) != '\0', "String that was already guaranteed to end with semicolons did not end with semicolons; it's possible we have fallen into an alternate universe. Failing string: [%s]\n", line_start);
1827  ch++;
1828  }
1829  ch++;
1830  Assertion((*ch) == ';', "String that was guaranteed to have double semicolons did not; it's possible we have fallen into an alternate universe. Failing string: [%s]\n", line_start);
1831  ch++;
1832 
1833  tag_len = ch - line_start;
1834  compatible_version = true;
1835 
1836  // check whether major, minor, and build line up with this version
1837  if (major > FS_VERSION_MAJOR)
1838  {
1839  compatible_version = false;
1840  }
1841  else if (major == FS_VERSION_MAJOR && s_num > 1)
1842  {
1843  if (minor > FS_VERSION_MINOR)
1844  {
1845  compatible_version = false;
1846  }
1847  else if (minor == FS_VERSION_MINOR && s_num > 2)
1848  {
1849  if (build > FS_VERSION_BUILD)
1850  {
1851  compatible_version = false;
1852  }
1853  else if (build == FS_VERSION_BUILD && s_num > 3)
1854  {
1855  if (revis > FS_VERSION_REVIS)
1856  {
1857  compatible_version = false;
1858  }
1859  }
1860  }
1861  }
1862 
1863  // true for tag match
1864  return true;
1865 }
1866 
1867 // Strip comments from a line of input.
1868 // Goober5000 - rewritten for the second time
1869 void strip_comments(char *line, bool &in_quote, bool &in_multiline_comment_a, bool &in_multiline_comment_b)
1870 {
1871  char *writep = line;
1872  char *readp = line;
1873 
1874  // copy all characters from read to write, unless they're commented
1875  while (*readp != '\r' && *readp != '\n' && *readp != '\0')
1876  {
1877  // only check for comments if not quoting
1878  if (!in_quote)
1879  {
1880  bool compatible_version;
1881  int tag_len;
1882 
1883  // see what sort of comment characters we recognize
1884  if (!strncmp(readp, "/*", 2))
1885  {
1886  // comment styles are mutually exclusive
1887  if (!in_multiline_comment_b)
1888  in_multiline_comment_a = true;
1889  }
1890  else if (!strncmp(readp, "!*", 2))
1891  {
1892  // comment styles are mutually exclusive
1893  if (!in_multiline_comment_a)
1894  in_multiline_comment_b = true;
1895  }
1896  else if (!strncmp(readp, "*/", 2))
1897  {
1898  if (in_multiline_comment_a)
1899  {
1900  in_multiline_comment_a = false;
1901  readp += 2;
1902  continue;
1903  }
1904  }
1905  else if (!strncmp(readp, "*!", 2))
1906  {
1907  if (in_multiline_comment_b)
1908  {
1909  in_multiline_comment_b = false;
1910  readp += 2;
1911  continue;
1912  }
1913  }
1914  // special version-specific comment
1915  // formatted like e.g. ;;FSO 3.7.0;;
1916  else if (matches_version_specific_tag(readp, compatible_version, tag_len))
1917  {
1918  // comment passes, so advance pass the tag and keep reading
1919  if (compatible_version)
1920  {
1921  readp += tag_len;
1922  continue;
1923  }
1924  // comment does not pass, so ignore the line
1925  else
1926  {
1927  break;
1928  }
1929  }
1930  // standard comment
1931  else if (*readp == ';')
1932  {
1933  break;
1934  }
1935  }
1936 
1937  // maybe toggle quoting
1938  if (*readp == '\"')
1939  in_quote = !in_quote;
1940 
1941  // if not inside a comment, copy the characters
1942  if (!in_multiline_comment_a && !in_multiline_comment_b)
1943  {
1944  if (writep != readp)
1945  *writep = *readp;
1946 
1947  writep++;
1948  }
1949 
1950  // read the next character
1951  readp++;
1952  }
1953 
1954  // if we moved any characters, or if we haven't reached the end of the string, then mark end-of-line and terminate string
1955  if (writep != readp || *readp != '\0')
1956  {
1957  writep[0] = EOLN;
1958  writep[1] = '\0';
1959  }
1960 }
1961 
1962 int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
1963 {
1964  char * t = lineout;
1965  int i, num_chars_read=0;
1966  char c;
1967 
1968 
1969  for ( i = 0; i < max_line_len-1; i++ ) {
1970  do {
1971  if ( (cur - start) >= max_size ) {
1972  *lineout = 0;
1973  if ( lineout > t ) {
1974  return num_chars_read;
1975  } else {
1976  return 0;
1977  }
1978  }
1979  c = *cur++;
1980  num_chars_read++;
1981  } while ( c == 13 );
1982 
1983  *lineout++ = c;
1984  if ( c=='\n' ) break;
1985  }
1986 
1987  *lineout++ = 0;
1988  return num_chars_read;
1989 }
1990 
1991 // Read mission text, stripping comments.
1992 // When a comment is found, it is removed. If an entire line
1993 // consisted of a comment, a blank line is left in the input file.
1994 // Goober5000 - added ability to read somewhere other than Mission_text
1995 void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
1996 {
1997  // copy the filename
1998  if (!filename)
1999  throw parse::ParseException("Invalid filename");
2000 
2001  strcpy_s(Current_filename_sub, filename);
2002 
2003  // if we are paused then processed_text and raw_text must not be NULL!!
2004  if ( Parsing_paused && ((processed_text == NULL) || (raw_text == NULL)) ) {
2005  Error(LOCATION, "ERROR: Neither processed_text nor raw_text may be NULL when parsing is paused!!\n");
2006  }
2007 
2008  // read the raw text
2009  read_raw_file_text(filename, mode, raw_text);
2010 
2011  if (processed_text == NULL)
2012  processed_text = Mission_text;
2013 
2014  if (raw_text == NULL)
2015  raw_text = Mission_text_raw;
2016 
2017  // process it (strip comments)
2018  process_raw_file_text(processed_text, raw_text);
2019 }
2020 
2021 // Goober5000
2022 void read_file_text_from_array(const char *array, char *processed_text, char *raw_text)
2023 {
2024  // we have no filename, so copy a substitute
2025  strcpy_s(Current_filename_sub, "internal default file");
2026 
2027  // if we are paused then processed_text and raw_text must not be NULL!!
2028  if ( Parsing_paused && ((processed_text == NULL) || (raw_text == NULL)) ) {
2029  Error(LOCATION, "ERROR: Neither \"processed_text\" nor \"raw_text\" may be NULL when parsing is paused!!\n");
2030  }
2031 
2032  // make sure to do this before anything else
2033  allocate_mission_text( strlen(array) + 1 );
2034 
2035  // if we have no raw buffer, set it as the default raw text area
2036  if (raw_text == NULL)
2037  raw_text = Mission_text_raw;
2038 
2039  // copy text in the array (but only if the raw text and the array are not the same)
2040  if (raw_text != array)
2041  strcpy(raw_text, array);
2042 
2043  if (processed_text == NULL)
2044  processed_text = Mission_text;
2045 
2046  // process the text
2047  process_raw_file_text(processed_text, raw_text);
2048 }
2049 
2050 // Goober5000
2051 int is_unicode(char *text)
2052 {
2053  if (!strncmp(text, "\xEF\xBB\xBF", 3)) // UTF-8
2054  return 1;
2055 
2056  if (!strncmp(text, "\xFE\xFF", 2)) // UTF-16 big-endian
2057  return 1;
2058 
2059  if (!strncmp(text, "\xFF\xFE", 2)) // UTF-16 little-endian
2060  return 1;
2061 
2062  if (!strncmp(text, "\x00\x00\xFE\xFF", 4)) // UTF-32 big-endian
2063  return 1;
2064 
2065  if (!strncmp(text, "\xFF\xFE\x00\x00", 4)) // UTF-32 little-endian
2066  return 1;
2067 
2068  return 0;
2069 }
2070 
2072 {
2073  Assert( !Parsing_paused );
2074 
2075  if (Mission_text != NULL) {
2077  Mission_text = NULL;
2078  }
2079 
2080  if (Mission_text_raw != NULL) {
2082  Mission_text_raw = NULL;
2083  }
2084 
2085  Mission_text_size = 0;
2086 }
2087 
2089 {
2090  Assert( size > 0 );
2091 
2092  if (size <= Mission_text_size)
2093  return;
2094 
2095 
2096  static ubyte parse_atexit = 0;
2097 
2098  if (!parse_atexit) {
2099  atexit(stop_parse);
2100  parse_atexit = 1;
2101  }
2102 
2103  if (Mission_text != NULL) {
2105  Mission_text = NULL;
2106  }
2107 
2108  if (Mission_text_raw != NULL) {
2110  Mission_text_raw = NULL;
2111  }
2112 
2113  Mission_text = (char *) vm_malloc_q(sizeof(char) * size);
2114  Mission_text_raw = (char *) vm_malloc_q(sizeof(char) * size);
2115 
2116  if ( (Mission_text == NULL) || (Mission_text_raw == NULL) ) {
2117  Error(LOCATION, "Unable to allocate enough memory for Mission_text! Aborting...\n");
2118  }
2119 
2120  memset( Mission_text, 0, sizeof(char) * size );
2121  memset( Mission_text_raw, 0, sizeof(char) * size);
2122 
2123  Mission_text_size = size;
2124 }
2125 
2126 // Goober5000
2127 void read_raw_file_text(const char *filename, int mode, char *raw_text)
2128 {
2129  CFILE *mf;
2130  int file_is_encrypted;
2131  int file_is_unicode;
2132 
2133  Assert(filename);
2134 
2135  mf = cfopen(filename, "rb", CFILE_NORMAL, mode);
2136  if (mf == NULL)
2137  {
2138  nprintf(("Error", "Wokka! Error opening file (%s)!\n", filename));
2139  throw parse::ParseException("Failed to open file");
2140  }
2141 
2142  // read the entire file in
2143  int file_len = cfilelength(mf);
2144 
2145  if(!file_len) {
2146  nprintf(("Error", "Oh noes!! File is empty! (%s)!\n", filename));
2147  throw parse::ParseException("Failed to open file");
2148  }
2149 
2150  // allocate, or reallocate, memory for Mission_text and Mission_text_raw based on size we need now
2151  allocate_mission_text( file_len + 1 );
2152 
2153  // NOTE: this always has to be done *after* the allocate_mission_text() call!!
2154  if (raw_text == NULL)
2155  raw_text = Mission_text_raw;
2156 
2157  // read first 10 bytes to determine if file is encrypted
2158  cfread(raw_text, MIN(file_len, 10), 1, mf);
2159  file_is_encrypted = is_encrypted(raw_text);
2160  cfseek(mf, 0, CF_SEEK_SET);
2161 
2162  // Goober5000 - also determine if file is Unicode
2163  file_is_unicode = is_unicode(raw_text);
2164  if ( file_is_unicode )
2165  {
2166  //This is probably fatal, so let's abort right here and now.
2167  Error(LOCATION, "%s is in Unicode/UTF format and cannot be read by FreeSpace Open. Please convert it to ASCII/ANSI\n", filename);
2168  }
2169 
2170  if ( file_is_encrypted )
2171  {
2172  int unscrambled_len;
2173  char *scrambled_text;
2174  scrambled_text = (char*)vm_malloc(file_len+1);
2175  Assert(scrambled_text);
2176  cfread(scrambled_text, file_len, 1, mf);
2177  // unscramble text
2178  unencrypt(scrambled_text, file_len, raw_text, &unscrambled_len);
2179  file_len = unscrambled_len;
2180  vm_free(scrambled_text);
2181  }
2182  else
2183  {
2184  cfread(raw_text, file_len, 1, mf);
2185  }
2186 
2187  //WMC - Slap a NULL character on here for the odd error where we forgot a #End
2188  raw_text[file_len] = '\0';
2189 
2190  cfclose(mf);
2191 }
2192 
2193 // Goober5000
2194 void process_raw_file_text(char *processed_text, char *raw_text)
2195 {
2196  char *mp;
2197  char *mp_raw;
2198  char outbuf[PARSE_BUF_SIZE], *str;
2199  bool in_quote = false;
2200  bool in_multiline_comment_a = false;
2201  bool in_multiline_comment_b = false;
2202  int raw_text_len = strlen(raw_text);
2203 
2204  if (processed_text == NULL)
2205  processed_text = Mission_text;
2206 
2207  if (raw_text == NULL)
2208  raw_text = Mission_text_raw;
2209 
2210  Assert( processed_text != NULL );
2211  Assert( raw_text != NULL );
2212 
2213  mp = processed_text;
2214  mp_raw = raw_text;
2215 
2216  // strip comments from raw text, reading into file_text
2217  int num_chars_read = 0;
2218  while ( (num_chars_read = parse_get_line(outbuf, PARSE_BUF_SIZE, raw_text, raw_text_len, mp_raw)) != 0 ) {
2219  mp_raw += num_chars_read;
2220 
2221  // stupid hacks to make retail data work with fixed parser, per Mantis #3072
2222  if (!strcmp(outbuf, "1402, \"Sie haben IPX-Protokoll als Protokoll ausgew\xE4hlt, aber dieses Protokoll ist auf Ihrer Maschine nicht installiert.\".\"\n")) {
2223  outbuf[121] = ' ';
2224  outbuf[122] = ' ';
2225  } else if (!strcmp(outbuf, "1117, \"\\r\\n\"Aucun web browser trouva. Del\xE0 isn't on emm\xE9nagea ou if \\r\\non est emm\xE9nagea, ca isn't set pour soient la default browser.\\r\\n\\r\\n\"\n")) {
2226  char *ch = &outbuf[11];
2227  do {
2228  *ch = *(ch+1);
2229  ++ch;
2230  } while (*ch);
2231  } else if (!strcmp(outbuf, "1337, \"(fr)Loading\"\n")) {
2232  outbuf[3] = '6';
2233  } else if (!strcmp(outbuf, "3966, \"Es sieht so aus, als habe Staffel Kappa Zugriff auf die GTVA-Zugangscodes f\xFCr das System gehabt. Das ist ein ernstes Sicherheitsleck. Ihre IFF-Kennung erschien als \"verb\xFCndet\", so da\xDF sie sich dem Konvoi ungehindert n\xE4hern konnten. Zum Gl\xFC\x63k flogen Sie und Alpha 2 Geleitschutz und lie\xDF\x65n den Schwindel auffliegen, bevor Kappa ihren Befehl ausf\xFChren konnte.\"\n")) {
2234  outbuf[171] = '\'';
2235  outbuf[181] = '\'';
2236  }
2237 
2238  strip_comments(outbuf, in_quote, in_multiline_comment_a, in_multiline_comment_b);
2239 
2241 
2242  str = outbuf;
2243  while (*str) {
2244  if (*str == (Lcl_pl ? -33 : -31)) {
2245  *mp++ = 's';
2246  *mp++ = 's';
2247  str++;
2248 
2249  } else {
2250  *mp++ = *str++;
2251  }
2252  }
2253 
2254 // strcpy_s(mp, outbuf);
2255 // mp += strlen(outbuf);
2256  }
2257 
2258  *mp = *mp_raw = EOF_CHAR;
2259 /*
2260  while (cfgets(outbuf, PARSE_BUF_SIZE, mf) != NULL) {
2261  if (strlen(outbuf) >= PARSE_BUF_SIZE-1)
2262  error_display(0, "Input string too long. Max is %i characters.\n%.256s\n", PARSE_BUF_SIZE, outbuf);
2263 
2264  // If you hit this assert, it is probably telling you the obvious. The file
2265  // you are trying to read is truly too large. Look at *filename to see the file name.
2266  Assert(mp_raw - file_text_raw + strlen(outbuf) < MISSION_TEXT_SIZE);
2267  strcpy_s(mp_raw, outbuf);
2268  mp_raw += strlen(outbuf);
2269 
2270  in_comment = strip_comments(outbuf, in_comment);
2271  strcpy_s(mp, outbuf);
2272  mp += strlen(outbuf);
2273  }
2274 
2275  *mp = *mp_raw = EOF_CHAR;
2276 */
2277 
2278 }
2279 
2281 {
2282  char *mp = Mission_text;
2283  char ch;
2284 
2285  while ((ch = *mp++) != EOF_CHAR)
2286  printf("%c", ch);
2287 }
2288 
2289 float atof2()
2290 {
2291  char ch;
2292 
2293  my_errno = 0;
2295 
2296  ch = *Mp;
2297 
2298  if ((ch != '.') && (ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
2299  error_display(1, "Expecting float, found [%.32s].\n", next_tokens());
2300  my_errno = 1;
2301  return 0.0f;
2302  } else
2303  return (float)atof(Mp);
2304 
2305 }
2306 
2307 int atoi2()
2308 {
2309  char ch;
2310 
2311  my_errno = 0;
2312 
2314 
2315  ch = *Mp;
2316 
2317  if ((ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
2318  error_display(1, "Expecting int, found [%.32s].\n", next_tokens());
2319  my_errno = 1;
2320  return 0;
2321  } else
2322  return atoi(Mp);
2323 
2324 }
2325 
2326 // Stuff a floating point value pointed at by Mp.
2327 // Advances past float characters.
2328 void stuff_float(float *f)
2329 {
2330  *f = atof2();
2331 
2332  if (my_errno)
2333  skip_token();
2334  else
2335  Mp += strspn(Mp, "+-0123456789.");
2336 
2337  if (*Mp ==',')
2338  Mp++;
2339 
2340  diag_printf("Stuffed float: %f\n", *f);
2341 }
2342 
2343 int stuff_float_optional(float *f, bool raw)
2344 {
2345  int skip_len;
2346  bool comma = false;
2347 
2348  if (!raw)
2350 
2351  skip_len = strspn(Mp, "+-0123456789.");
2352  if(*(Mp+skip_len) == ',') {
2353  comma = true;
2354  }
2355 
2356  if(skip_len == 0)
2357  {
2358  if(comma) {
2359  Mp++;
2360  return 1;
2361  } else {
2362  return 0;
2363  }
2364  }
2365 
2366  stuff_float(f);
2367  return 2;
2368 }
2369 
2370 // Stuff an integer value pointed at by Mp.
2371 // Advances past integer characters.
2372 void stuff_int(int *i)
2373 {
2374  *i = atoi2();
2375 
2376  if (my_errno)
2377  skip_token();
2378  else
2379  Mp += strspn(Mp, "+-0123456789");
2380 
2381  if (*Mp ==',')
2382  Mp++;
2383 
2384  diag_printf("Stuffed int: %i\n", *i);
2385 }
2386 
2387 int stuff_int_optional(int *i, bool raw)
2388 {
2389  int skip_len;
2390  bool comma = false;
2391 
2392  if (!raw)
2394 
2395  skip_len = strspn(Mp, "+-0123456789");
2396  if(*(Mp+skip_len) == ',') {
2397  comma = true;
2398  }
2399 
2400  if(skip_len == 0)
2401  {
2402  if(comma) {
2403  Mp++;
2404  return 1;
2405  } else {
2406  return 0;
2407  }
2408  }
2409 
2410  stuff_int(i);
2411  return 2;
2412 }
2413 
2414 int stuff_int_or_variable (int &i, bool positive_value = false);
2415 int stuff_int_or_variable (int *ilp, int count, bool positive_value = false);
2416 
2417 // Stuffs an int value or the value of a number variable. Returns the index of the variable or NOT_SET_BY_SEXP_VARIABLE.
2418 int stuff_int_or_variable (int &i, bool positive_value)
2419 {
2421 
2422  if (*Mp == '@')
2423  {
2424  Mp++;
2425  int value = -1;
2426  char str[128];
2427  stuff_string(str, F_NAME, sizeof(str));
2428 
2429  index = get_index_sexp_variable_name(str);
2430 
2431  if (index > -1 && index < MAX_SEXP_VARIABLES)
2432  {
2434  {
2435  value = atoi(Sexp_variables[index].text);
2436  }
2437  else
2438  {
2439  Error(LOCATION, "Invalid variable type \"%s\" found in mission. Variable must be a number variable!", str);
2440  }
2441  }
2442  else
2443  {
2444 
2445  Error(LOCATION, "Invalid variable name \"%s\" found.", str);
2446  }
2447 
2448  // zero negative values if requested
2449  if (positive_value && value < 0)
2450  {
2451  value = 0;
2452  }
2453 
2454 
2455  // Record the value of the index for FreeSpace
2456  i = value;
2457  }
2458  else
2459  {
2460  stuff_int(&i);
2461  }
2462  return index;
2463 }
2464 
2465 // Stuff an integer value pointed at by Mp.If a variable is found instead stuff the value of that variable and record the
2466 // index of the variable in the following slot.
2467 int stuff_int_or_variable (int *ilp, int count, bool positive_value)
2468 {
2469  if (*Mp == '@')
2470  {
2471  Mp++;
2472  int value = -1;
2473  char str[128];
2474  stuff_string(str, F_NAME, sizeof(str));
2475 
2477 
2478  if (index > -1 && index < MAX_SEXP_VARIABLES)
2479  {
2481  {
2482  value = atoi(Sexp_variables[index].text);
2483  }
2484  else
2485  {
2486  Error(LOCATION, "Invalid variable type \"%s\" found in mission. Variable must be a number variable!", str);
2487  }
2488  }
2489  else
2490  {
2491 
2492  Error(LOCATION, "Invalid variable name \"%s\" found.", str);
2493  }
2494 
2495  // zero negative values if requested
2496  if (positive_value && value < 0)
2497  {
2498  value = 0;
2499  }
2500 
2501 
2502  // Record the value of the index for FreeSpace
2503  ilp[count++] = value;
2504  // Record the index itself because we may need it later.
2505  ilp[count++] = index;
2506  }
2507  else
2508  {
2509  stuff_int(&ilp[count++]);
2510  // Since we have a numerical value we don't have a SEXP variable index to add for next slot.
2511  ilp[count++] = NOT_SET_BY_SEXP_VARIABLE;
2512  }
2513  return count;
2514 }
2515 
2516 
2517 //Stuffs boolean value.
2518 //Passes things off to stuff_boolean(bool)
2519 void stuff_boolean(int *i, bool a_to_eol)
2520 {
2521  bool tempb;
2522  stuff_boolean(&tempb, a_to_eol);
2523  if(tempb)
2524  *i = 1;
2525  else
2526  *i = 0;
2527 }
2528 
2529 void stuff_boolean_flag(int *i, int flag, bool a_to_eol)
2530 {
2531  bool temp;
2532  stuff_boolean(&temp, a_to_eol);
2533  if(temp)
2534  *i |= flag;
2535  else
2536  *i &= ~(flag);
2537 }
2538 
2539 // Stuffs a boolean value pointed at by Mp.
2540 // YES/NO (supporting 1/0 now as well)
2541 // Now supports localization :) -WMC
2542 
2543 void stuff_boolean(bool *b, bool a_to_eol)
2544 {
2545  char token[32];
2546  stuff_string_white(token, sizeof(token)/sizeof(char));
2547  if(a_to_eol)
2548  advance_to_eoln(NULL);
2549 
2550  if( isdigit(token[0]))
2551  {
2552  if(token[0] != '0')
2553  *b = true;
2554  else
2555  *b = false;
2556  }
2557  else
2558  {
2559  if(!stricmp(token, "yes")
2560  || !stricmp(token, "true")
2561  || !stricmp(token, "ja") //German
2562  || !stricmp(token, "Oui") //French
2563  || !stricmp(token, "si") //Spanish
2564  || !stricmp(token, "ita vero") //Latin
2565  || !stricmp(token, "HIja'") || !stricmp(token, "HISlaH")) //Klingon
2566  {
2567  *b = true;
2568  }
2569  else if(!stricmp(token, "no")
2570  || !stricmp(token, "false")
2571  || !stricmp(token, "nein") //German
2572  || !stricmp(token, "Non") //French
2573  //I don't know spanish for "no"
2574  //But according to altavista, spanish for "No" is "no"
2575  //Go figure.
2576  || !stricmp(token, "minime") //Latin
2577  || !stricmp(token, "ghobe'")) //Klingon
2578  {
2579  *b = false;
2580  }
2581  else
2582  {
2583  *b = false;
2584  Warning(LOCATION, "Boolean '%s' type unknown; assuming 'no/false'",token);
2585  }
2586  }
2587 
2588  diag_printf("Stuffed bool: %s\n", (b) ? NOX("true") : NOX("false"));
2589 }
2590 
2591 int stuff_bool_list(bool *blp, int max_bools)
2592 {
2593  int count = 0;
2594  bool trash_buf = false;
2595 
2597 
2598  if (*Mp != '(') {
2599  error_display(1, "Reading boolean list. Found [%c]. Expecting '('.\n", *Mp);
2600  throw parse::ParseException("Syntax error");
2601  }
2602 
2603  Mp++;
2604 
2606 
2607  while(*Mp != ')')
2608  {
2609  if(count < max_bools)
2610  {
2611  stuff_boolean(&blp[count++], false);
2613 
2614  //Since Bobb has set a precedent, allow commas for bool lists -WMC
2615  if(*Mp == ',')
2616  {
2617  Mp++;
2619  }
2620  }
2621  else
2622  {
2623  trash_buf = true;
2624  break;
2625  }
2626  }
2627 
2628  if(trash_buf)
2629  {
2630  error_display(0, "Boolean list has more than allowed arguments; max is %d. Arguments over max will be ignored.", max_bools);
2631  while(*Mp != ')')
2632  {
2633  stuff_boolean(&trash_buf, false);
2635  }
2636  }
2637 
2638  Mp++;
2639 
2640  return count;
2641 }
2642 
2643 
2644 // Stuff an integer value pointed at by Mp.
2645 // Advances past integer characters.
2647 {
2648  int temp;
2649 
2650  temp = atoi2();
2651 
2652  *i = (ubyte)temp;
2653 
2654  if (my_errno)
2655  skip_token();
2656  else
2657  Mp += strspn(Mp, "+-0123456789");
2658 
2659  if (*Mp == ',')
2660  Mp++;
2661 
2662  diag_printf("Stuffed byte: %i\n", *i);
2663 }
2664 
2665 int parse_string_flag_list(int *dest, flag_def_list defs[], int defs_size)
2666 {
2667  Assert(dest!=NULL); //wtf?
2668 
2669  char (*slp)[NAME_LENGTH] = (char(*)[32])new char[defs_size*NAME_LENGTH];
2670  int num_strings = stuff_string_list(slp, defs_size);
2671  int i, j;
2672 
2673  for(i = 0; i < num_strings; i++)
2674  {
2675  for(j = 0; j < defs_size; j++)
2676  {
2677  if(!stricmp(slp[i], defs[j].name)) {
2678  (*dest) |= defs[j].def;
2679  }
2680  }
2681  }
2682 
2683  delete[] slp; //>_>
2684  //nobody saw that right
2685 
2686  return num_strings;
2687 }
2688 
2690 {
2691  //_asm int 3;
2692  slp.clear();
2693 
2695 
2696  if ( *Mp != '(' ) {
2697  error_display(1, "Reading string list. Found [%c]. Expecting '('.\n", *Mp);
2698  throw parse::ParseException("Syntax error");
2699  }
2700 
2701  Mp++;
2702 
2704 
2705  SCP_string buf;
2706 
2707  while (*Mp != ')') {
2708  if(*Mp != '\"') {
2709  error_display(0, "Missing quotation marks in string list.");
2710  }
2711  //Assert ( *Mp == '\"' ); // should always be enclosed in quotes
2712 
2713  buf = "";
2714  get_string( buf );
2715  slp.push_back( buf );
2717  }
2718 
2719  Mp++;
2720 
2721  return slp.size();
2722 }
2723 
2724 // Stuffs a list of strings
2725 int stuff_string_list(char slp[][NAME_LENGTH], int max_strings)
2726 {
2727  int count = 0;
2729 
2730  if ( *Mp != '(' ) {
2731  error_display(1, "Reading string list. Found [%c]. Expecting '('.\n", *Mp);
2732  throw parse::ParseException("Syntax error");
2733  }
2734 
2735  Mp++;
2736 
2738 
2739  while (*Mp != ')') {
2740  Assert ( count < max_strings );
2741  if(*Mp != '\"') {
2742  error_display(0, "Missing quotation marks in string list.");
2743  }
2744  //Assert ( *Mp == '\"' ); // should always be enclosed in quotes
2745 
2746  if (count < max_strings) {
2747  get_string( slp[count++] );
2748  } else {
2749  char trash[NAME_LENGTH];
2750  get_string( trash );
2751  }
2753  }
2754 
2755  Mp++;
2756 
2757  return count;
2758 }
2759 
2760 const char* get_lookup_type_name(int lookup_type)
2761 {
2762  switch (lookup_type) {
2763  case SHIP_TYPE:
2764  return "Ships";
2765  case SHIP_INFO_TYPE:
2766  return "Ship Classes";
2767  case WEAPON_POOL_TYPE:
2768  return "Weapon Pool";
2769  case WEAPON_LIST_TYPE:
2770  return "Weapon Types";
2771  case RAW_INTEGER_TYPE:
2772  return "Untyped integer list";
2773  }
2774 
2775  return "Unknown lookup type, tell a coder!";
2776 }
2777 
2778 // Stuffs an integer list.
2779 // This is of the form ( i* )
2780 // where i is an integer.
2781 // For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
2782 int stuff_int_list(int *ilp, int max_ints, int lookup_type)
2783 {
2784  int count = 0, ok_flag = 1, dummy;
2786 
2787  if (*Mp != '(') {
2788  error_display(1, "Reading integer list. Found [%c]. Expecting '('.\n", *Mp);
2789  throw parse::ParseException("Syntax error");
2790  }
2791 
2792  Mp++;
2794 
2795  while (*Mp != ')') {
2796  Assertion(count < max_ints, "Too many entries in integer list. Expected %d, found %d.\nList type was %s", max_ints, count+1, get_lookup_type_name(lookup_type));
2797  if (*Mp == '"') {
2798  int num = 0;
2799  char str[128];
2800 
2801  get_string(str);
2802  switch (lookup_type) {
2803  case SHIP_TYPE:
2804  num = ship_name_lookup(str); // returns index of Ship[] entry with name
2805  break;
2806 
2807  case SHIP_INFO_TYPE:
2808  ok_flag = 1;
2809  num = ship_info_lookup(str); // returns index of Ship_info[] entry with name
2810  if (num < 0)
2811  ok_flag = 0;
2812  break;
2813 
2814  case WEAPON_POOL_TYPE:
2815  ok_flag = 1;
2816  num = weapon_info_lookup(str);
2817  if (num < 0)
2818  ok_flag = 0;
2819  break;
2820 
2821  case WEAPON_LIST_TYPE:
2822  num = weapon_info_lookup(str);
2823  if (num < 0)
2824  num = -2;
2825  break;
2826 
2827  case RAW_INTEGER_TYPE:
2828  num = atoi(str);
2829  break;
2830 
2831  default:
2832  Error(LOCATION,"Unknown lookup_type in stuff_int_list");
2833  break;
2834  }
2835 
2836  if (ok_flag) {
2837  if (num == -1) {
2838  Error(LOCATION, "Unable to find string \"%s\" in stuff_int_list\n\nMany possible sources for this error. Get a programmer!\n", str);
2839  } else if (num == -2) {
2840  if (str[0] != '\0') {
2841  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);
2842  }
2843  }
2844 
2845  if (num < 0) // other negatives used to bypass the above error trap, but should be -1
2846  num = -1;
2847 
2848  if (count < max_ints) {
2849  ilp[count++] = num;
2850  }
2851  }
2852 
2853  } else {
2854  if (ok_flag && (count < max_ints))
2855  stuff_int(&ilp[count++]);
2856  else
2857  stuff_int(&dummy);
2858  }
2859 
2861  }
2862 
2863  Mp++;
2864 
2865  return count;
2866 }
2867 
2868 // helper for the next function. Removes a broken entry from ship or weapon lists and advances to the next one
2870 {
2871  int dummy;
2872 
2873  // clean out the broken entry
2875  stuff_int_or_variable(dummy);
2877 }
2878 
2879 // Karajorma - Stuffs an int list by parsing a list of ship or weapon choices.
2880 // Unlike stuff_int_list it can deal with variables and it also has better error reporting.
2881 int stuff_loadout_list (int *ilp, int max_ints, int lookup_type)
2882 {
2883  int count = 0;
2884  int index, sexp_variable_index, variable_found;
2885  char str[128];
2886 
2888 
2889  if (*Mp != '(') {
2890  error_display(1, "Reading loadout list. Found [%c]. Expecting '('.\n", *Mp);
2891  throw parse::ParseException("Syntax error");
2892  }
2893 
2894  Mp++;
2896 
2897  while (*Mp != ')') {
2898  if (count >= max_ints) {
2899  Error(LOCATION, "Loadout contains too many entries.\n");
2900  }
2901 
2902  index = -1;
2903  sexp_variable_index = NOT_SET_BY_SEXP_VARIABLE;
2904  variable_found = get_string_or_variable (str);
2905 
2906  // if we've got a variable get the variable index and copy it's value into str so that regardless of whether we found
2907  // a variable or not it now holds the name of the ship or weapon we're interested in.
2908  if (variable_found) {
2909  Assert (lookup_type != CAMPAIGN_LOADOUT_SHIP_LIST );
2910  sexp_variable_index = get_index_sexp_variable_name(str);
2911 
2912  if(sexp_variable_index<0) {
2913  Error(LOCATION, "Invalid SEXP variable name \"%s\" found in stuff_loadout_list.", str);
2914  }
2915 
2916  strcpy_s (str, Sexp_variables[sexp_variable_index].text);
2917  }
2918 
2919  switch (lookup_type) {
2922  index = ship_info_lookup(str);
2923  break;
2924 
2927  index = weapon_info_lookup(str);
2928  break;
2929 
2930  default:
2931  Int3();
2932  }
2933 
2934  // Complain if this isn't a valid ship or weapon and we are loading a mission. Campaign files can be loading containing
2935  // no ships from the current tables (when swapping mods) so don't report that as an error.
2936  if (index < 0 && (lookup_type == MISSION_LOADOUT_SHIP_LIST || lookup_type == MISSION_LOADOUT_WEAPON_LIST)) {
2937  // print a warning in debug mode
2938  Warning(LOCATION, "Invalid type \"%s\" found in loadout of mission file...skipping", str);
2939  // increment counter for release FRED builds.
2941 
2943  continue;
2944  }
2945 
2946  // similarly, complain if this is a valid ship or weapon class that the player can't use
2947  if ((lookup_type == MISSION_LOADOUT_SHIP_LIST) && (!(Ship_info[index].flags & SIF_PLAYER_SHIP)) ) {
2949  Warning(LOCATION, "Ship type \"%s\" found in loadout of mission file. This class is not marked as a player ship...skipping", str);
2950  continue;
2951  }
2952  else if ((lookup_type == MISSION_LOADOUT_WEAPON_LIST) && (!(Weapon_info[index].wi_flags & WIF_PLAYER_ALLOWED)) ) {
2954  nprintf(("Warning", "Warning: Weapon type %s found in loadout of mission file. This class is not marked as a player allowed weapon...skipping\n", str));
2955  if ( !Is_standalone )
2956  Warning(LOCATION, "Weapon type \"%s\" found in loadout of mission file. This class is not marked as a player allowed weapon...skipping", str);
2957  continue;
2958  }
2959 
2960  // we've found a real item. Add its index to the list.
2961  if (count < max_ints) {
2962  ilp[count++] = index;
2963  }
2964 
2966 
2967  // Campaign lists need go no further
2968  if (lookup_type == CAMPAIGN_LOADOUT_SHIP_LIST || lookup_type == CAMPAIGN_LOADOUT_WEAPON_LIST) {
2969  continue;
2970  }
2971 
2972  // record the index of the variable that gave us this item if any
2973  if (count < max_ints) {
2974  ilp[count++] = sexp_variable_index;
2975  }
2976 
2977  // Now read in the number of this type available. The number must be positive
2978  count = stuff_int_or_variable(ilp, count, true);
2979 
2981  }
2982 
2983  Mp++;
2984  return count;
2985 }
2986 
2987 //Stuffs an integer list like stuff_int_list.
2988 int stuff_float_list(float* flp, int max_floats)
2989 {
2990  int count = 0;
2992 
2993  if (*Mp != '(') {
2994  error_display(1, "Reading float list. Found [%c]. Expecting '('.\n", *Mp);
2995  throw parse::ParseException("Syntax error");
2996  }
2997 
2998  Mp++;
3000  while(*Mp != ')')
3001  {
3002  Assert(count < max_floats);
3003  if (count < max_floats) {
3004  stuff_float(&flp[count++]);
3005  } else {
3006  float dummy;
3007  stuff_float(&dummy);
3008  }
3010  }
3011 
3012  Mp++;
3013 
3014  return count;
3015 }
3016 
3017 // Marks an integer list.
3018 // This is of the form ( i* )
3019 // where i is an integer.
3020 // If a specified string is found in the lookup and its value is 7, then the 7th value
3021 // in the array is set.
3022 void mark_int_list(int *ilp, int max_ints, int lookup_type)
3023 {
3025 
3026  if (*Mp != '(') {
3027  error_display(1, "Marking integer list. Found [%c]. Expecting '('.\n", *Mp);
3028  throw parse::ParseException("Syntax error");
3029  }
3030 
3031  Mp++;
3033 
3034  while (*Mp != ')') {
3035  if (*Mp == '"') {
3036  int num = 0;
3037  char str[128];
3038 
3039  get_string(str);
3040  switch(lookup_type) {
3041  case SHIP_TYPE:
3042  num = ship_name_lookup(str); // returns index of Ship[] entry with name
3043  break;
3044 
3045  case SHIP_INFO_TYPE:
3046  num = ship_info_lookup(str); // returns index of Ship_info[] entry with name
3047  break;
3048 
3049  case WEAPON_LIST_TYPE:
3050  num = weapon_info_lookup(str);
3051  break;
3052 
3053  default:
3054  Error(LOCATION,"Unknown lookup_type in stuff_int_list");
3055  break;
3056  }
3057 
3058  if ( (num < 0) || (num >= max_ints) )
3059  Error(LOCATION, "Unable to find string \"%s\" in mark_int_list.\n", str);
3060 
3061 // ilp[num] = 1;
3062 
3063  } else {
3064  int tval;
3065 
3066  stuff_int(&tval);
3067  Assert((tval >= 0) && (tval < max_ints));
3068  if (tval >= 0 && tval < max_ints) {
3069  ilp[tval] = 1;
3070  }
3071  }
3072 
3074  }
3075 
3076  Mp++;
3077 
3078 }
3079 
3080 
3081 // Stuff a vec3d struct, which is 3 floats.
3083 {
3084  stuff_float(&vp->xyz.x);
3085  stuff_float(&vp->xyz.y);
3086  stuff_float(&vp->xyz.z);
3087 }
3088 
3090 {
3092 
3093  if (*Mp != '(') {
3094  error_display(1, "Reading parenthesized vec3d. Found [%c]. Expecting '('.\n", *Mp);
3095  throw parse::ParseException("Syntax error");
3096  } else {
3097  Mp++;
3098  stuff_vec3d(vp);
3100  if (*Mp != ')') {
3101  error_display(1, "Reading parenthesized vec3d. Found [%c]. Expecting ')'.\n", *Mp);
3102  throw parse::ParseException("Syntax error");
3103  }
3104  Mp++;
3105  }
3106 
3107 }
3108 
3109 // Stuffs vec3d list. *vlp is an array of vec3ds.
3110 // This is of the form ( (vec3d)* )
3111 // (where * is a kleene star, not a pointer indirection)
3112 // For example, ( (1 2 3) (2 3 4) (2 3 5) )
3113 // is a list of three vec3ds.
3114 int stuff_vec3d_list(vec3d *vlp, int max_vecs)
3115 {
3116  int count = 0;
3117 
3119 
3120  if (*Mp != '(') {
3121  error_display(1, "Reading vec3d list. Found [%c]. Expecting '('.\n", *Mp);
3122  throw parse::ParseException("Syntax error");
3123  }
3124 
3125  Mp++;
3126 
3128 
3129  while (*Mp != ')') {
3130  Assert(count < max_vecs);
3131  if (count < max_vecs) {
3132  stuff_parenthesized_vec3d(&vlp[count++]);
3133  } else {
3134  vec3d temp;
3136  }
3137 
3139  }
3140 
3141  Mp++;
3142 
3143  return count;
3144 }
3145 
3146 // ditto the above, but a vector of vec3ds...
3148 {
3150 
3151  if (*Mp != '(') {
3152  error_display(1, "Reading vec3d list. Found [%c]. Expecting '('.\n", *Mp);
3153  throw parse::ParseException("Syntax error");
3154  }
3155 
3156  Mp++;
3157 
3159 
3160  while (*Mp != ')') {
3161  vec3d temp;
3163  vec_list.push_back(temp);
3164 
3166  }
3167 
3168  Mp++;
3169 
3170  return vec_list.size();
3171 }
3172 
3173 // Stuff a matrix, which is 3 vec3ds.
3175 {
3176  stuff_vec3d(&mp->vec.rvec);
3177  stuff_vec3d(&mp->vec.uvec);
3178  stuff_vec3d(&mp->vec.fvec);
3179 }
3180 
3181 
3182 // Given a string, find it in a string array.
3183 // *description is only used for diagnostics in case it can't be found.
3184 // *str1 is the string to be found.
3185 // *strlist is the list of strings to search.
3186 // max is the number of entries in *strlist to scan.
3187 int string_lookup(char *str1, char *strlist[], int max, char *description, int say_errors)
3188 {
3189  int i;
3190 
3191  for (i=0; i<max; i++) {
3192  Assert(strlen(strlist[i]) != 0); //-V805
3193 
3194  if (!stricmp(str1, strlist[i]))
3195  return i;
3196  }
3197 
3198  if (say_errors)
3199  error_display(0, "Unable to find [%s] in %s list.\n", str1, description);
3200 
3201  return -1;
3202 }
3203 
3204 // Find a required string (*id), then stuff the text of type f_type that
3205 // follows it at *addr. *strlist[] contains the strings it should try to
3206 // match.
3207 void find_and_stuff(char *id, int *addr, int f_type, char *strlist[], int max, char *description)
3208 {
3209  char token[128];
3210  int checking_ship_classes = (stricmp(id, "$class:") == 0);
3211 
3212  // Goober5000 - don't say errors when we're checking classes because 1) we have more checking to do; and 2) we will say a redundant error later
3213  required_string(id);
3214  stuff_string(token, f_type, sizeof(token));
3215  *addr = string_lookup(token, strlist, max, description, !checking_ship_classes);
3216 
3217  // Goober5000 - handle certain FSPort idiosyncracies with ship classes
3218  if (*addr < 0 && checking_ship_classes)
3219  {
3220  int idx = ship_info_lookup(token);
3221 
3222  if (idx >= 0)
3223  *addr = string_lookup(Ship_info[idx].name, strlist, max, description, 0);
3224  else
3225  *addr = -1;
3226  }
3227 }
3228 
3229 void find_and_stuff_optional(char *id, int *addr, int f_type, char *strlist[], int max, char *description)
3230 {
3231  char token[128];
3232 
3233  if(optional_string(id))
3234  {
3235  stuff_string(token, f_type, sizeof(token));
3236  *addr = string_lookup(token, strlist, max, description, 1);
3237  }
3238 }
3239 
3240 // Mp points at a string.
3241 // Find the string in the list of strings *strlist[].
3242 // Returns the index of the match, -1 if none.
3243 int match_and_stuff(int f_type, char *strlist[], int max, char *description)
3244 {
3245  char token[128];
3246 
3247  stuff_string(token, f_type, sizeof(token));
3248  return string_lookup(token, strlist, max, description, 0);
3249 }
3250 
3251 void find_and_stuff_or_add(char *id, int *addr, int f_type, char *strlist[], int *total,
3252  int max, char *description)
3253 {
3254  char token[128];
3255 
3256  *addr = -1;
3257  required_string(id);
3258  stuff_string(token, f_type, sizeof(token));
3259  if (*total)
3260  *addr = string_lookup(token, strlist, *total, description, 0);
3261 
3262  if (*addr == -1) // not in list, so lets try and add it.
3263  {
3264  Assert(*total < max);
3265  strcpy(strlist[*total], token);
3266  *addr = (*total)++;
3267  }
3268 }
3269 
3270 // pause current parsing so that some else can be parsed without interfering
3271 // with the currently parsing file
3273 {
3274  Assert( !Parsing_paused );
3275  if (Parsing_paused)
3276  return;
3277 
3278  Mp_save = Mp;
3279 
3282 
3284 
3285  Parsing_paused = 1;
3286 }
3287 
3288 // unpause parsing to continue with previously parsing file
3290 {
3291  Assert( Parsing_paused );
3292  if (!Parsing_paused)
3293  return;
3294 
3295  Mp = Mp_save;
3296 
3299 
3301 
3302  Parsing_paused = 0;
3303 }
3304 
3305 void reset_parse(char *text)
3306 {
3307  if (text != NULL) {
3308  Mp = text;
3309  } else {
3310  Mp = Mission_text;
3311  }
3312 
3313  Warning_count = 0;
3314  Error_count = 0;
3315 
3317 }
3318 
3319 // Display number of warnings and errors at the end of a parse.
3321 {
3322  nprintf(("Parse", "\nParse complete.\n"));
3323  nprintf(("Parse", "%i errors. %i warnings.\n", Error_count, Warning_count));
3324 }
3325 
3326 // Splits a string into 2 lines if the string is wider than max_pixel_w pixels. A null
3327 // terminator is placed where required to make the first line <= max_pixel_w. The remaining
3328 // text is returned (leading whitespace removed). If the line doesn't need to be split,
3329 // NULL is returned.
3330 char *split_str_once(char *src, int max_pixel_w)
3331 {
3332  char *brk = NULL;
3333  int i, w, len, last_was_white = 0;
3334 
3335  Assert(src);
3336  Assert(max_pixel_w > 0);
3337 
3338  gr_get_string_size(&w, NULL, src);
3339  if ( (w <= max_pixel_w) && !strstr(src, "\n") ) {
3340  return NULL; // string doesn't require a cut
3341  }
3342 
3343  len = strlen(src);
3344  for (i=0; i<len; i++) {
3345  gr_get_string_size(&w, NULL, src, i);
3346  if ( w > max_pixel_w )
3347  break;
3348 
3349  if (src[i] == '\n') { // reached natural end of line
3350  src[i] = 0;
3351  return src + i + 1;
3352  }
3353 
3354  if (is_white_space(src[i])) {
3355  if (!last_was_white)
3356  brk = src + i;
3357 
3358  last_was_white = 1;
3359 
3360  } else {
3361  last_was_white = 0;
3362  }
3363  }
3364 
3365  // if we are over max pixel width and weren't able to come up with a good non-word
3366  // split then just return the original src text and the calling function should
3367  // have to handle the result
3368  if ( (w > max_pixel_w) && ((i == 0) || !brk) ) {
3369  return src;
3370  }
3371 
3372  if (!brk) {
3373  brk = src + i;
3374  }
3375 
3376  *brk = 0;
3377  src = brk + 1;
3378  while (is_white_space(*src))
3379  src++;
3380 
3381  if (!*src)
3382  return NULL; // end of the string anyway
3383 
3384  if (*src == '\n')
3385  src++;
3386 
3387  return src;
3388 }
3389 
3390 #define SPLIT_STR_BUFFER_SIZE 512
3391 
3392 // --------------------------------------------------------------------------------------
3393 // split_str()
3394 //
3395 // A general function that will split a string into several lines. Lines are allowed up
3396 // to max_pixel_w pixels. Breaks are found in white space.
3397 //
3398 // Supports \n's in the strings!
3399 //
3400 // parameters: src => source string to be broken up
3401 // max_pixel_w => max width of line in pixels
3402 // n_chars => output array that will hold number of characters in each line
3403 // p_str => output array of pointers to start of lines within src
3404 // max_lines => limit of number of lines to break src up into
3405 // ignore_char => OPTIONAL parameter (default val -1). Ignore words starting with this character
3406 // This is useful when you want to ignore embedded control information that starts
3407 // with a specific character, like $ or #
3408 //
3409 // returns: number of lines src is broken into
3410 // -1 is returned when an error occurs
3411 //
3412 int split_str(const char *src, int max_pixel_w, int *n_chars, const char **p_str, int max_lines, char ignore_char)
3413 {
3415  const char *breakpoint = NULL;
3416  int sw, new_line = 1, line_num = 0, last_was_white = 0;
3417  int ignore_until_whitespace, buf_index;
3418 
3419  // check our assumptions..
3420  Assert(src != NULL);
3421  Assert(n_chars != NULL);
3422  Assert(p_str != NULL);
3423  Assert(max_lines > 0);
3424  Assert(max_pixel_w > 0);
3425 
3426  memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
3427  buf_index = 0;
3428  ignore_until_whitespace = 0;
3429 
3430  // get rid of any leading whitespace
3431  while (is_white_space(*src))
3432  src++;
3433 
3434  new_line = 1;
3435  p_str[0] = NULL;
3436 
3437  // iterate through chars in line, keeping track of most recent "white space" location that can be used
3438  // as a line splitting point if necessary
3439  for (; *src; src++) {
3440  if (line_num >= max_lines)
3441  return line_num; // time to bail out
3442 
3443  // starting a new line of text, init stuff for that
3444  if (new_line) {
3445  p_str[line_num] = NULL;
3446  if (is_gray_space(*src))
3447  continue;
3448 
3449  p_str[line_num] = src;
3450  breakpoint = NULL;
3451  new_line = 0;
3452  }
3453 
3454  // maybe skip leading whitespace
3455  if (ignore_until_whitespace) {
3456  if ( is_white_space(*src) )
3457  ignore_until_whitespace = 0;
3458 
3459  continue;
3460  }
3461 
3462  // if we have a newline, split the line here
3463  if (*src == '\n') {
3464  n_chars[line_num] = src - p_str[line_num]; // track length of line
3465  line_num++;
3466  if (line_num < max_lines) {
3467  p_str[line_num] = NULL;
3468  }
3469  new_line = 1;
3470 
3471  memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
3472  buf_index = 0;
3473  continue;
3474  }
3475 
3476  if (*src == ignore_char) {
3477  ignore_until_whitespace = 1;
3478  continue;
3479  }
3480 
3481  if (is_gray_space(*src)) {
3482  if (!last_was_white) // track at first whitespace in a series of whitespace
3483  breakpoint = src;
3484 
3485  last_was_white = 1;
3486 
3487  } else {
3488  // indicate next time around that this wasn't a whitespace character
3489  last_was_white = 0;
3490  }
3491 
3492  Assertion(buf_index < SPLIT_STR_BUFFER_SIZE - 1, "buffer overflow in split_str: screen width causes this text to be longer than %d characters!", SPLIT_STR_BUFFER_SIZE - 1);
3493 
3494  // throw it in our buffer
3495  buffer[buf_index] = *src;
3496  buf_index++;
3497  buffer[buf_index] = 0; // null terminate it
3498 
3499  gr_get_string_size(&sw, NULL, buffer);
3500  if (sw >= max_pixel_w) {
3501  const char *end;
3502 
3503  if (breakpoint) {
3504  end = src = breakpoint;
3505 
3506  } else {
3507  end = src; // force a split here since to whitespace
3508  src--; // reuse this character in next line
3509  }
3510 
3511  n_chars[line_num] = end - p_str[line_num]; // track length of line
3512  Assert(n_chars[line_num]);
3513  line_num++;
3514  if (line_num < max_lines) {
3515  p_str[line_num] = NULL;
3516  }
3517  new_line = 1;
3518 
3519  memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
3520  buf_index = 0;
3521  continue;
3522  }
3523  } // end for
3524 
3525  if (!new_line && p_str[line_num]) {
3526  n_chars[line_num] = src - p_str[line_num]; // track length of line
3527  Assert(n_chars[line_num]);
3528  line_num++;
3529  }
3530 
3531  return line_num;
3532 }
3533 
3534 int split_str(const char *src, int max_pixel_w, SCP_vector<int> &n_chars, SCP_vector<const char*> &p_str, char ignore_char)
3535 {
3537  const char *breakpoint = NULL;
3538  int sw, new_line = 1, line_num = 0, last_was_white = 0;
3539  int ignore_until_whitespace = 0, buf_index = 0;
3540 
3541  // check our assumptions..
3542  Assert(src != NULL);
3543  Assert(max_pixel_w > 0);
3544 
3545  memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
3546 
3547  // get rid of any leading whitespace
3548  while (is_white_space(*src))
3549  src++;
3550 
3551  p_str.clear();
3552 
3553  // iterate through chars in line, keeping track of most recent "white space" location that can be used
3554  // as a line splitting point if necessary
3555  for (; *src; src++) {
3556 
3557  // starting a new line of text, init stuff for that
3558  if (new_line) {
3559  if (is_gray_space(*src))
3560  continue;
3561 
3562  p_str.push_back(src);
3563  breakpoint = NULL;
3564  new_line = 0;
3565  }
3566 
3567  // maybe skip leading whitespace
3568  if (ignore_until_whitespace) {
3569  if ( is_white_space(*src) ) {
3570  ignore_until_whitespace = 0;
3571 
3572  // don't eat the newline
3573  if (*src == EOLN)
3574  src--;
3575  }
3576 
3577  continue;
3578  }
3579 
3580  // if we have a newline, split the line here
3581  if (*src == '\n') {
3582  n_chars.push_back(src - p_str.at(line_num)); // track length of line
3583  line_num++;
3584  new_line = 1;
3585 
3586  memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
3587  buf_index = 0;
3588  continue;
3589  }
3590 
3591  if (*src == ignore_char) {
3592  ignore_until_whitespace = 1;
3593  continue;
3594  }
3595 
3596  if (is_gray_space(*src)) {
3597  if (!last_was_white) // track at first whitespace in a series of whitespace
3598  breakpoint = src;
3599 
3600  last_was_white = 1;
3601 
3602  } else {
3603  // indicate next time around that this wasn't a whitespace character
3604  last_was_white = 0;
3605  }
3606 
3607  Assertion(buf_index < SPLIT_STR_BUFFER_SIZE - 1, "buffer overflow in split_str: screen width causes this text to be longer than %d characters!", SPLIT_STR_BUFFER_SIZE - 1);
3608 
3609  // throw it in our buffer
3610  buffer[buf_index] = *src;
3611  buf_index++;
3612  buffer[buf_index] = 0; // null terminate it
3613 
3614  gr_get_string_size(&sw, NULL, buffer);
3615  if (sw >= max_pixel_w) {
3616  const char *end;
3617 
3618  if (breakpoint) {
3619  end = src = breakpoint;
3620 
3621  } else {
3622  end = src; // force a split here since to whitespace
3623  src--; // reuse this character in next line
3624  }
3625 
3626  n_chars.push_back(end - p_str.at(line_num)); // track length of line
3627  Assert(n_chars.at(line_num));
3628  line_num++;
3629  new_line = 1;
3630 
3631  memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
3632  buf_index = 0;
3633  continue;
3634  }
3635  } // end for
3636 
3637  if (!new_line && p_str.at(line_num)) {
3638  n_chars.push_back(src - p_str.at(line_num)); // track length of line
3639  Assert(n_chars.at(line_num));
3640  line_num++;
3641  }
3642 
3643  return line_num;
3644 }
3645 
3646 // Goober5000
3647 // accounts for the dumb communications != communication, etc.
3648 int subsystem_stricmp(const char *str1, const char *str2)
3649 {
3650  Assert(str1 && str2);
3651 
3652  // ensure len-1 will be valid
3653  if (!*str1 || !*str2)
3654  return stricmp(str1, str2);
3655 
3656  // calc lengths
3657  int len1 = strlen(str1);
3658  int len2 = strlen(str2);
3659 
3660  // get rid of trailing s on s1?
3661  if (tolower(*(str1+len1-1) == 's'))
3662  len1--;
3663 
3664  // get rid of trailing s on s2?
3665  if (tolower(*(str2+len2-1) == 's'))
3666  len2--;
3667 
3668  // once we remove the trailing s on both names, they should be the same length
3669  if (len1 > len2)
3670  return 1;
3671  if (len1 < len2)
3672  return -1;
3673 
3674  // now do the comparison
3675  return strnicmp(str1, str2, len1);
3676 }
3677 
3678 // Goober5000
3679 // current algorithm adapted from http://www.codeproject.com/string/stringsearch.asp
3680 const char *stristr(const char *str, const char *substr)
3681 {
3682  // check for null and insanity
3683  Assert(str);
3684  Assert(substr);
3685  if (str == NULL || substr == NULL || *substr == '\0')
3686  return NULL;
3687 
3688  // save both a lowercase and an uppercase version of the first character of substr
3689  char substr_ch_lower = (char)tolower(*substr);
3690  char substr_ch_upper = (char)toupper(*substr);
3691 
3692  // find the maximum distance to search
3693  const char *upper_bound = str + strlen(str) - strlen(substr);
3694 
3695  // loop through every character of str
3696  for (const char *start = str; start <= upper_bound; start++)
3697  {
3698  // check first character of substr
3699  if ((*start == substr_ch_upper) || (*start == substr_ch_lower))
3700  {
3701  // first character matched, so check the rest
3702  for (const char *str_ch = start+1, *substr_ch = substr+1; *substr_ch != '\0'; str_ch++, substr_ch++)
3703  {
3704  // character match?
3705  if (*str_ch == *substr_ch)
3706  continue;
3707 
3708  // converted character match?
3709  if (tolower(*str_ch) == tolower(*substr_ch))
3710  continue;
3711 
3712  // mismatch
3713  goto stristr_continue_outer_loop;
3714  }
3715 
3716  // finished inner loop with success!
3717  return start;
3718  }
3719 
3720 stristr_continue_outer_loop:
3721  /* NO-OP */ ;
3722  }
3723 
3724  // no match
3725  return NULL;
3726 }
3727 
3728 // non-const version
3729 char *stristr(char *str, const char *substr)
3730 {
3731  // check for null and insanity
3732  Assert(str);
3733  Assert(substr);
3734  if (str == NULL || substr == NULL || *substr == '\0')
3735  return NULL;
3736 
3737  // save both a lowercase and an uppercase version of the first character of substr
3738  char substr_ch_lower = (char)tolower(*substr);
3739  char substr_ch_upper = (char)toupper(*substr);
3740 
3741  // find the maximum distance to search
3742  const char *upper_bound = str + strlen(str) - strlen(substr);
3743 
3744  // loop through every character of str
3745  for (char *start = str; start <= upper_bound; start++)
3746  {
3747  // check first character of substr
3748  if ((*start == substr_ch_upper) || (*start == substr_ch_lower))
3749  {
3750  // first character matched, so check the rest
3751  for (const char *str_ch = start+1, *substr_ch = substr+1; *substr_ch != '\0'; str_ch++, substr_ch++)
3752  {
3753  // character match?
3754  if (*str_ch == *substr_ch)
3755  continue;
3756 
3757  // converted character match?
3758  if (tolower(*str_ch) == tolower(*substr_ch))
3759  continue;
3760 
3761  // mismatch
3762  goto stristr_continue_outer_loop;
3763  }
3764 
3765  // finished inner loop with success!
3766  return start;
3767  }
3768 
3769 stristr_continue_outer_loop:
3770  /* NO-OP */ ;
3771  }
3772 
3773  // no match
3774  return NULL;
3775 }
3776 
3777 // Goober5000
3778 bool can_construe_as_integer(const char *text)
3779 {
3780  // trivial case; evaluates to 0
3781  if (*text == '\0')
3782  return true;
3783 
3784  // number sign or digit for first char
3785  if ((*text != '+') && (*text != '-') && !isdigit(*text))
3786  return false;
3787 
3788  // check digits for rest
3789  for (const char *p = text + 1; *p != '\0'; p++)
3790  {
3791  if (!isdigit(*p))
3792  return false;
3793  }
3794 
3795  return true;
3796 }
3797 
3798 // Goober5000
3799 // yoinked gratefully from dbugfile.cpp
3800 void vsprintf(SCP_string &dest, const char *format, va_list ap)
3801 {
3802  va_list copy;
3803 
3804 #if defined(_MSC_VER) && _MSC_VER < 1800
3805  // Only Visual Studio >= 2013 supports va_copy
3806  // This isn't portable but should work for Visual Studio
3807  copy = ap;
3808 #else
3809  va_copy(copy, ap);
3810 #endif
3811 
3812  int needed_length = vsnprintf(nullptr, 0, format, copy);
3813  va_end(copy);
3814 
3815  if (needed_length < 0) {
3816  // Error
3817  return;
3818  }
3819 
3820  dest.resize(static_cast<size_t>(needed_length));
3821  vsnprintf(&dest[0], dest.size() + 1, format, ap);
3822 }
3823 
3824 void sprintf(SCP_string &dest, const char *format, ...)
3825 {
3826  va_list args;
3827  va_start(args, format);
3828  vsprintf(dest, format, args);
3829  va_end(args);
3830 }
3831 
3832 // Goober5000
3834 {
3835  char *p;
3836  Assert(src);
3837 
3839  if (p)
3840  {
3841  while ((p != src) && (*(p-1) == ' '))
3842  p--;
3843 
3844  *p = '\0';
3845  return true;
3846  }
3847 
3848  return false;
3849 }
3850 
3851 // Goober5000
3853 {
3855  if (index >= 0)
3856  {
3857  while (index > 0 && src[index-1] == ' ')
3858  index--;
3859 
3860  src.resize(index);
3861  return true;
3862  }
3863 
3864  return false;
3865 }
3866 
3867 // Goober5000
3869 {
3870  Assert(src);
3871  return strchr(src, '#');
3872 }
3873 
3874 // Goober5000
3875 const char *get_pointer_to_first_hash_symbol(const char *src)
3876 {
3877  Assert(src);
3878  return strchr(src, '#');
3879 }
3880 
3881 // Goober5000
3883 {
3884  size_t pos = src.find('#');
3885  return (pos == SCP_string::npos) ? -1 : pos;
3886 }
3887 
3888 // Goober5000
3889 int replace_one(char *str, char *oldstr, char *newstr, uint max_len, int range)
3890 {
3891  Assert(str && oldstr && newstr);
3892 
3893  // search
3894  char *ch = stristr(str, oldstr);
3895 
3896  // found?
3897  if (ch)
3898  {
3899  // not found within bounds?
3900  if ((range > 0) && ((ch - str) > range))
3901  {
3902  return 0;
3903  }
3904 
3905  // determine if replacement will exceed max len
3906  if (strlen(str) + strlen(newstr) - strlen(oldstr) > max_len)
3907  {
3908  return -1;
3909  }
3910 
3911  // allocate temp string to hold extra stuff
3912  char *temp = (char *) vm_malloc(sizeof(char) * max_len);
3913 
3914  // ensure allocation was successful
3915  if (temp)
3916  {
3917  // save remainder of string
3918  strcpy_s(temp, sizeof(char)*max_len, ch + strlen(oldstr));
3919 
3920  // replace
3921  strcpy(ch, newstr);
3922 
3923  // append rest of string
3924  strcpy(ch + strlen(newstr), temp);
3925  }
3926 
3927  // free temp string
3928  vm_free(temp);
3929  }
3930  // not found
3931  else
3932  {
3933  return 0;
3934  }
3935 
3936  // return pos of replacement
3937  return (ch - str);
3938 }
3939 
3940 // Goober5000
3941 int replace_all(char *str, char *oldstr, char *newstr, uint max_len, int range)
3942 {
3943  int val, tally = 0;
3944 
3945  while ((val = replace_one(str, oldstr, newstr, max_len, range)) > 0)
3946  {
3947  tally++;
3948 
3949  // adjust range (if we have one), because the text length might have changed
3950  if (range) {
3951  range += strlen(newstr) - strlen(oldstr);
3952  }
3953  }
3954 
3955  return (val < 0) ? val : tally;
3956 }
3957 
3958 SCP_string& replace_one(SCP_string& context, const SCP_string& from, const SCP_string& to)
3959 {
3960  size_t foundHere;
3961  if ((foundHere = context.find(from, 0)) != SCP_string::npos)
3962  {
3963  context.replace(foundHere, from.length(), to);
3964  }
3965  return context;
3966 }
3967 
3968 SCP_string& replace_one(SCP_string& context, const char* from, const char* to)
3969 {
3970  size_t foundHere;
3971  if ((foundHere = context.find(from, 0)) != SCP_string::npos)
3972  {
3973  context.replace(foundHere, strlen(from), to);
3974  }
3975  return context;
3976 }
3977 
3978 // http://www.cppreference.com/wiki/string/replace
3979 SCP_string& replace_all(SCP_string& context, const SCP_string& from, const SCP_string& to)
3980 {
3981  size_t from_len = from.length();
3982  size_t to_len = to.length();
3983 
3984  size_t lookHere = 0;
3985  size_t foundHere;
3986  while ((foundHere = context.find(from, lookHere)) != SCP_string::npos)
3987  {
3988  context.replace(foundHere, from_len, to);
3989  lookHere = foundHere + to_len;
3990  }
3991  return context;
3992 }
3993 
3994 // http://www.cppreference.com/wiki/string/replace
3995 SCP_string& replace_all(SCP_string& context, const char* from, const char* to)
3996 {
3997  size_t from_len = strlen(from);
3998  size_t to_len = strlen(to);
3999 
4000  size_t lookHere = 0;
4001  size_t foundHere;
4002  while ((foundHere = context.find(from, lookHere)) != SCP_string::npos)
4003  {
4004  context.replace(foundHere, from_len, to);
4005  lookHere = foundHere + to_len;
4006  }
4007  return context;
4008 }
4009 
4010 // WMC
4011 // Compares two strings, ignoring (last) extension
4012 // Returns 0 if equal, nonzero if not
4013 int strextcmp(const char *s1, const char *s2)
4014 {
4015  // sanity check
4016  Assert( (s1 != NULL) && (s2 != NULL) );
4017 
4018  // find last '.' in both strings
4019  char *s1_end = (char *)strrchr(s1, '.');
4020  char *s2_end = (char *)strrchr(s2, '.');
4021 
4022  // get length
4023  size_t s1_len, s2_len;
4024 
4025  if (s1_end != NULL)
4026  s1_len = (s1_end - s1);
4027  else
4028  s1_len = strlen(s1);
4029 
4030  if (s2_end != NULL)
4031  s2_len = (s2_end - s2);
4032  else
4033  s2_len = strlen(s2);
4034 
4035  // if the lengths aren't the same then it's deffinitely not the same name
4036  if (s2_len != s1_len)
4037  return 1;
4038 
4039  return strnicmp(s1, s2, s1_len);
4040 }
4041 
4042 // Goober5000
4043 bool drop_extension(char *str)
4044 {
4045  char *p = strrchr(str, '.');
4046  if (p != NULL)
4047  {
4048  *p = 0;
4049  return true;
4050  }
4051 
4052  return false;
4053 }
4054 
4055 // Goober5000
4057 {
4058  size_t pos = str.rfind('.');
4059  if (pos != SCP_string::npos)
4060  {
4061  str.resize(pos);
4062  return true;
4063  }
4064 
4065  return false;
4066 }
4067 
4068 //WMC
4069 void backspace(char* src)
4070 {
4071  Assert(src!= NULL); //this would be bad
4072 
4073  char *dest = src;
4074  src++;
4075 
4076  while(*src != '\0') {
4077  *dest++ = *src++;
4078  }
4079 
4080  *dest = '\0';
4081 }
4082 
4083 // Goober5000
4084 void format_integer_with_commas(char *buf, int integer, bool use_comma_with_four_digits)
4085 {
4086  int old_pos, new_pos, triad_count;
4087  char backward_buf[32];
4088 
4089  // print an initial string of just the digits
4090  sprintf(buf, "%d", integer);
4091 
4092  // no commas needed?
4093  if ((integer < 1000) || (integer < 10000 && !use_comma_with_four_digits))
4094  return;
4095 
4096  // scan the string backwards, writing commas after every third digit
4097  new_pos = 0;
4098  triad_count = 0;
4099  for (old_pos = strlen(buf) - 1; old_pos >= 0; old_pos--)
4100  {
4101  backward_buf[new_pos] = buf[old_pos];
4102  new_pos++;
4103  triad_count++;
4104 
4105  if (triad_count == 3 && old_pos > 0)
4106  {
4107  backward_buf[new_pos] = ',';
4108  new_pos++;
4109  triad_count = 0;
4110  }
4111  }
4112  backward_buf[new_pos] = '\0';
4113 
4114  // now reverse the string
4115  new_pos = 0;
4116  for (old_pos = strlen(backward_buf) - 1; old_pos >= 0; old_pos--)
4117  {
4118  buf[new_pos] = backward_buf[old_pos];
4119  new_pos++;
4120  }
4121  buf[new_pos] = '\0';
4122 }
4123 
4124 // Goober5000
4125 // there's probably a better way to do this, but this way works and is clear and short
4126 int scan_fso_version_string(const char *text, int *major, int *minor, int *build, int *revis)
4127 {
4128  int val;
4129 
4130  val = sscanf(text, ";;FSO %i.%i.%i.%i;;", major, minor, build, revis);
4131  if (val == 4)
4132  return val;
4133 
4134  *revis = 0;
4135  val = sscanf(text, ";;FSO %i.%i.%i;;", major, minor, build);
4136  if (val == 3)
4137  return val;
4138 
4139  *build = *revis = 0;
4140  val = sscanf(text, ";;FSO %i.%i;;", major, minor);
4141  if (val == 2)
4142  return val;
4143 
4144  *minor = *build = *revis = 0;
4145  val = sscanf(text, ";;FSO %i;;", major);
4146  if (val == 1)
4147  return val;
4148 
4149  *major = *minor = *build = *revis = 0;
4150  return 0;
4151 }
4152 
4153 // Goober5000 - used for long Warnings, Errors, and FRED error messages with SEXPs
4154 void truncate_message_lines(SCP_string &text, int num_allowed_lines)
4155 {
4156  Assert(num_allowed_lines > 0);
4157  size_t find_from = 0;
4158 
4159  while (find_from < text.size())
4160  {
4161  if (num_allowed_lines <= 0)
4162  {
4163  text.resize(find_from);
4164  text.append("[...]");
4165  break;
4166  }
4167 
4168  size_t pos = text.find('\n', find_from);
4169  if (pos == SCP_string::npos)
4170  break;
4171 
4172  num_allowed_lines--;
4173  find_from = pos + 1;
4174  }
4175 }
4176 
4177 // Goober5000 - ugh, I can't see why they didn't just use stuff_*_list for these;
4178 // the only differece is the lack of parentheses
4179 
4180 // from aicode.cpp
4181 // Stuff a list of floats at *plist.
4182 void parse_float_list(float *plist, int size)
4183 {
4184  int i;
4185 
4186  for (i=0; i<size; i++)
4187  {
4188  stuff_float(&plist[i]);
4189  }
4190 }
4191 
4192 // from aicode.cpp
4193 // Stuff a list of ints at *plist.
4194 void parse_int_list(int *ilist, int size)
4195 {
4196  int i;
4197 
4198  for (i=0; i<size; i++)
4199  {
4200  stuff_int(&ilist[i]);
4201  }
4202 }
4203 
4204 // parse a modular table of type "name_check" and parse it using the specified function callback
4205 int parse_modular_table(const char *name_check, void (*parse_callback)(const char *filename), int path_type, int sort_type)
4206 {
4207  SCP_vector<SCP_string> tbl_file_names;
4208  int i, num_files = 0;
4209 
4210  if ( (name_check == NULL) || (parse_callback == NULL) || ((*name_check) != '*') ) {
4211  Assertion(false, "parse_modular_table() called with invalid arguments; get a coder!\n");
4212  return 0;
4213  }
4214 
4215  num_files = cf_get_file_list(tbl_file_names, path_type, name_check, sort_type);
4216 
4217  Parsing_modular_table = true;
4218 
4219  for (i = 0; i < num_files; i++){
4220  tbl_file_names[i] += ".tbm";
4221  mprintf(("TBM => Starting parse of '%s' ...\n", tbl_file_names[i].c_str()));
4222  (*parse_callback)(tbl_file_names[i].c_str());
4223  }
4224 
4225  Parsing_modular_table = false;
4226 
4227  return num_files;
4228 }
GLenum GLsizei GLenum format
Definition: Gl.h:1509
GLuint64EXT * result
Definition: Glext.h:10775
char * Mission_text
Definition: parselo.cpp:46
void advance_to_eoln(char *more_terminators)
Definition: parselo.cpp:335
#define CAMPAIGN_LOADOUT_WEAPON_LIST
Definition: parselo.h:61
#define vm_malloc_q(size)
Definition: pstypes.h:554
#define WEAPON_LIST_TYPE
Definition: parselo.h:51
int ship_info_lookup(const char *token)
Definition: ship.cpp:12772
#define CFILE_NORMAL
Definition: cfile.h:89
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
int stuff_string_list(SCP_vector< SCP_string > &slp)
Definition: parselo.cpp:2689
#define PARSE_BUF_SIZE
Definition: parselo.h:47
char Error_str[ERROR_LENGTH]
Definition: parselo.cpp:39
#define MIN(a, b)
Definition: pstypes.h:296
char Current_filename_sub[MAX_PATH_LEN]
Definition: parselo.cpp:38
#define vp(p)
Definition: modelsinc.h:70
void format_integer_with_commas(char *buf, int integer, bool use_comma_with_four_digits)
Definition: parselo.cpp:4084
int check_for_string(const char *pstr)
Definition: parselo.cpp:517
void copy_text_until(char *outstr, char *instr, char *endstr, int max_chars)
Definition: parselo.cpp:923
int parse_string_flag_list(int *dest, flag_def_list defs[], int defs_size)
Definition: parselo.cpp:2665
#define SIF_PLAYER_SHIP
Definition: ship.h:875
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
int stuff_int_optional(int *i, bool raw)
Definition: parselo.cpp:2387
int fhash_active()
Definition: fhash.cpp:73
int scan_fso_version_string(const char *text, int *major, int *minor, int *build, int *revis)
Definition: parselo.cpp:4126
int cfread(void *buf, int elsize, int nelem, CFILE *fp)
int maybe_convert_foreign_character(int ch)
Definition: parselo.cpp:1486
void drop_trailing_white_space(char *str)
Definition: parselo.cpp:93
void unencrypt(char *scrambled_text, int scrambled_len, char *text, int *text_len)
Definition: encrypt.cpp:177
int my_errno
Definition: parselo.cpp:40
GLsizei const GLfloat * value
Definition: Glext.h:5646
int required_string_one_of(int arg_count,...)
Checks for one of any of the given required strings.
Definition: parselo.cpp:708
GLuint index
Definition: Glext.h:5608
void copy_to_eoln(char *outstr, char *more_terminators, char *instr, int max)
Definition: parselo.cpp:786
int Fred_running
Definition: fred.cpp:44
int replace_all(char *str, char *oldstr, char *newstr, uint max_len, int range)
Definition: parselo.cpp:3941
char * stuff_and_malloc_string(int type, char *terminators)
Definition: parselo.cpp:1406
int check_for_string_raw(const char *pstr)
Definition: parselo.cpp:528
void parse_int_list(int *ilist, int size)
Definition: parselo.cpp:4194
void stuff_malloc_string(char **dest, int type, char *terminators)
Definition: parselo.cpp:1419
int weapon_info_lookup(const char *name=NULL)
Definition: weapons.cpp:467
void stuff_string_line(char *outstr, int len)
Definition: parselo.cpp:1354
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
Assert(pm!=NULL)
int Cmdline_noparseerrors
Definition: cmdline.cpp:492
Definition: pstypes.h:88
#define mprintf(args)
Definition: pstypes.h:238
void stuff_boolean_flag(int *i, int flag, bool a_to_eol)
Definition: parselo.cpp:2529
#define EOLN
Definition: parselo.h:32
int subsystem_stricmp(const char *str1, const char *str2)
Definition: parselo.cpp:3648
int stuff_float_optional(float *f, bool raw)
Definition: parselo.cpp:2343
char * drop_white_space(char *str)
Definition: parselo.cpp:159
int Lcl_pl
Definition: localize.cpp:49
struct vec3d::@225::@227 xyz
#define FS_VERSION_MAJOR
Definition: version.h:37
GLclampf f
Definition: Glext.h:7097
void compact_multitext_string(char *str)
Definition: parselo.cpp:1438
#define SIZE_T_ARG
Definition: clang.h:61
float atof2()
Definition: parselo.cpp:2289
Definition: cfile.h:28
#define Assertion(expr, msg,...)
Definition: clang.h:41
#define CF_SEEK_SET
Definition: cfile.h:24
#define RS_MAX_TRIES
Definition: parselo.cpp:31
GLenum mode
Definition: Glext.h:5794
sexp_variable Sexp_variables[MAX_SEXP_VARIABLES]
Definition: sexp.cpp:846
int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
Definition: parselo.cpp:1962
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: Glext.h:5156
#define F_MULTITEXTOLD
Definition: parselo.h:38
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
void stuff_matrix(matrix *mp)
Definition: parselo.cpp:3174
int replace_one(char *str, char *oldstr, char *newstr, uint max_len, int range)
Definition: parselo.cpp:3889
GLsizeiptr size
Definition: Glext.h:5496
#define Int3()
Definition: pstypes.h:292
int get_index_sexp_variable_name(const char *text)
Definition: sexp.cpp:29324
int required_string_either(char *str1, char *str2)
Checks for one of two required strings.
Definition: parselo.cpp:673
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
int match_and_stuff(int f_type, char *strlist[], int max, char *description)
Definition: parselo.cpp:3243
int Num_unknown_loadout_classes
#define SPLIT_STR_BUFFER_SIZE
Definition: parselo.cpp:3390
GLenum type
Definition: Gl.h:1492
void stuff_float(float *f)
Definition: parselo.cpp:2328
int get_line_num()
Definition: parselo.cpp:261
void copy_to_next_white(char *outstr, char *instr, int max)
Definition: parselo.cpp:837
void sprintf(SCP_string &dest, const char *format,...)
Definition: parselo.cpp:3824
void backspace(char *src)
Definition: parselo.cpp:4069
#define PARSING_FOUND_VARIABLE
Definition: parselo.h:255
#define F_NOTES
Definition: parselo.h:36
int strextcmp(const char *s1, const char *s2)
Definition: parselo.cpp:4013
void debug_show_mission_text()
Definition: parselo.cpp:2280
GLenum GLint * range
Definition: Glext.h:7096
int Token_found_flag
Definition: parselo.cpp:44
char Current_filename[MAX_PATH_LEN]
Definition: parselo.cpp:36
struct matrix::@228::@230 vec
unsigned int uint
Definition: pstypes.h:64
void stuff_string_until(char *outstr, char *endstr, int len)
Definition: parselo.cpp:988
void vsprintf(SCP_string &dest, const char *format, va_list ap)
Definition: parselo.cpp:3800
#define cfopen(...)
Definition: cfile.h:134
#define F_DATE
Definition: parselo.h:35
#define nprintf(args)
Definition: pstypes.h:239
void ignore_gray_space()
Definition: parselo.cpp:83
int optional_string_fred(char *pstr, char *end, char *end2)
Definition: parselo.cpp:630
#define SHIP_TYPE
Definition: parselo.h:49
int fred_parse_flag
Definition: parselo.cpp:43
void parse_float_list(float *plist, int size)
Definition: parselo.cpp:4182
#define PARSING_FOUND_STRING
Definition: parselo.h:254
char * filename
#define strnicmp(s1, s2, n)
Definition: config.h:272
void stuff_string(char *outstr, int type, int len, char *terminators)
Definition: parselo.cpp:1189
#define w(p)
Definition: modelsinc.h:68
#define vm_strdup(ptr)
Definition: pstypes.h:549
int required_string(const char *pstr)
Definition: parselo.cpp:468
GLuint buffer
Definition: Glext.h:5492
char * alloc_text_until(char *instr, char *endstr)
Definition: parselo.cpp:889
#define FS_VERSION_MINOR
Definition: version.h:38
int stuff_int_or_variable(int &i, bool positive_value=false)
Definition: parselo.cpp:2418
GLdouble s
Definition: Glext.h:5321
int is_unicode(char *text)
Definition: parselo.cpp:2051
#define WEAPON_POOL_TYPE
Definition: parselo.h:53
char * get_pointer_to_first_hash_symbol(char *src)
Definition: parselo.cpp:3868
bool can_construe_as_integer(const char *text)
Definition: parselo.cpp:3778
int split_str(const char *src, int max_pixel_w, int *n_chars, const char **p_str, int max_lines, char ignore_char)
Definition: parselo.cpp:3412
int optional_string(const char *pstr)
Definition: parselo.cpp:539
#define MISSION_LOADOUT_SHIP_LIST
Definition: parselo.h:58
void find_and_stuff(char *id, int *addr, int f_type, char *strlist[], int max, char *description)
Definition: parselo.cpp:3207
void stuff_parenthesized_vec3d(vec3d *vp)
Definition: parselo.cpp:3089
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
Definition: parselo.cpp:1995
int skip_to_start_of_string_either(char *pstr1, char *pstr2, char *end)
Definition: parselo.cpp:433
int stuff_loadout_list(int *ilp, int max_ints, int lookup_type)
Definition: parselo.cpp:2881
bool Parsing_modular_table
Definition: parselo.cpp:34
int idx
Definition: multiui.cpp:761
void process_raw_file_text(char *processed_text, char *raw_text)
Definition: parselo.cpp:2194
void unpause_parse()
Definition: parselo.cpp:3289
void pause_parse()
Definition: parselo.cpp:3272
void allocate_mission_text(int size)
Definition: parselo.cpp:2088
GLdouble GLdouble t
Definition: Glext.h:5329
int get_string(char *str, int max)
Definition: parselo.cpp:1157
const char * get_lookup_type_name(int lookup_type)
Definition: parselo.cpp:2760
bool matches_version_specific_tag(const char *line_start, bool &compatible_version, int &tag_len)
Definition: parselo.cpp:1805
#define COMMENT_CHAR
Definition: parselo.h:30
bool get_number_before_separator(int &number, int &number_chars, const char *text, char separator)
Definition: parselo.cpp:1738
const char * token_found
Definition: parselo.cpp:49
unsigned char ubyte
Definition: pstypes.h:62
void stuff_vec3d(vec3d *vp)
Definition: parselo.cpp:3082
void truncate_message_lines(SCP_string &text, int num_allowed_lines)
Definition: parselo.cpp:4154
void read_file_text_from_array(const char *array, char *processed_text, char *raw_text)
Definition: parselo.cpp:2022
void strip_comments(char *line, bool &in_quote, bool &in_multiline_comment_a, bool &in_multiline_comment_b)
Definition: parselo.cpp:1869
#define F_PATHNAME
Definition: parselo.h:40
void stuff_boolean(int *i, bool a_to_eol)
Definition: parselo.cpp:2519
#define F_MESSAGE
Definition: parselo.h:42
#define NOX(s)
Definition: pstypes.h:473
bool end_string_at_first_hash_symbol(char *src)
Definition: parselo.cpp:3833
GLuint start
Definition: Gl.h:1502
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
GLbitfield flags
Definition: Glext.h:6722
#define vm_malloc(size)
Definition: pstypes.h:547
#define SHIP_INFO_TYPE
Definition: parselo.h:50
void reset_parse(char *text)
Definition: parselo.cpp:3305
const char * stristr(const char *str, const char *substr)
Definition: parselo.cpp:3680
GLuint const GLchar * name
Definition: Glext.h:5608
GLuint GLfloat * val
Definition: Glext.h:6741
void read_raw_file_text(const char *filename, int mode, char *raw_text)
Definition: parselo.cpp:2127
#define MAX_PATH_LEN
Definition: pstypes.h:325
GLboolean GLboolean GLboolean b
Definition: Glext.h:5781
int required_string_fred(char *pstr, char *end)
Definition: parselo.cpp:593
void stuff_int(int *i)
Definition: parselo.cpp:2372
int stuff_int_list(int *ilp, int max_ints, int lookup_type)
Definition: parselo.cpp:2782
int get_string_or_variable(char *str)
Definition: parselo.cpp:1084
int Error_count_save
Definition: parselo.cpp:42
GLuint GLuint num
Definition: Glext.h:9089
void display_parse_diagnostics()
Definition: parselo.cpp:3320
#define WIF_PLAYER_ALLOWED
Definition: weapon.h:68
#define strcat_s(...)
Definition: safe_strings.h:68
#define FS_VERSION_REVIS
Definition: version.h:40
#define EOF_CHAR
Definition: parselo.h:31
#define NAME_LENGTH
Definition: globals.h:15
int Error_count
Definition: parselo.cpp:41
void stop_parse()
Definition: parselo.cpp:2071
int optional_string_either(char *str1, char *str2)
Definition: parselo.cpp:551
int check_for_eoln()
Definition: parselo.cpp:505
int is_parenthesis(char ch)
Definition: parselo.cpp:70
void gr_get_string_size(int *w, int *h, const char *text, int len=9999)
Definition: font.cpp:196
char * Mp_save
Definition: parselo.cpp:48
char * split_str_once(char *src, int max_pixel_w)
Definition: parselo.cpp:3330
GLint first
Definition: Gl.h:1491
void find_and_stuff_or_add(char *id, int *addr, int f_type, char *strlist[], int *total, int max, char *description)
Definition: parselo.cpp:3251
void mark_int_list(int *ilp, int max_ints, int lookup_type)
Definition: parselo.cpp:3022
char * Mission_text_raw
Definition: parselo.cpp:47
GLfloat GLfloat p
Definition: Glext.h:8373
cfbp line_num
Definition: cfile.cpp:1072
int stuff_vec3d_list(vec3d *vlp, int max_vecs)
Definition: parselo.cpp:3114
#define F_NAME
Definition: parselo.h:34
GLenum src
Definition: Glext.h:5917
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
#define LOCATION
Definition: pstypes.h:245
#define ERROR_LENGTH
Definition: parselo.cpp:30
int is_encrypted(char *scrambled_text)
Definition: encrypt.cpp:407
void fhash_add_str(const char *str, int id)
Definition: fhash.cpp:107
hull_check pos
Definition: lua.cpp:5050
void error_display(int error_level, char *format,...)
Definition: parselo.cpp:308
void ignore_white_space()
Definition: parselo.cpp:77
int is_gray_space(char ch)
Definition: parselo.cpp:65
void advance_to_next_white()
Definition: parselo.cpp:354
void drop_leading_white_space(char *str)
Definition: parselo.cpp:115
int Warning_count_save
Definition: parselo.cpp:42
#define NOT_SET_BY_SEXP_VARIABLE
Definition: parselo.h:56
GLint GLsizei count
Definition: Gl.h:1491
int parse_modular_table(const char *name_check, void(*parse_callback)(const char *filename), int path_type, int sort_type)
Definition: parselo.cpp:4205
void skip_token()
Definition: parselo.cpp:219
#define SEXP_VARIABLE_NUMBER
Definition: sexp.h:873
GLenum GLsizei len
Definition: Glext.h:6283
#define FS_VERSION_BUILD
Definition: version.h:39
int check_for_eof()
Definition: parselo.cpp:492
#define F_FILESPEC
Definition: parselo.h:37
int temp
Definition: lua.cpp:4996
void stuff_ubyte(ubyte *i)
Definition: parselo.cpp:2646
#define MISSION_LOADOUT_WEAPON_LIST
Definition: parselo.h:59
void find_and_stuff_optional(char *id, int *addr, int f_type, char *strlist[], int max, char *description)
Definition: parselo.cpp:3229
int optional_string_one_of(int arg_count,...)
Definition: parselo.cpp:567
int stuff_bool_list(bool *blp, int max_bools)
Definition: parselo.cpp:2591
void stuff_string_white(char *outstr, int len)
Definition: parselo.cpp:969
int is_white_space(char ch)
Definition: parselo.cpp:59
int ship_name_lookup(const char *name, int inc_players)
Definition: ship.cpp:12900
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id)
Definition: localize.cpp:831
int string_lookup(char *str1, char *strlist[], int max, char *description, int say_errors)
Definition: parselo.cpp:3187
void clean_loadout_list_entry()
Definition: parselo.cpp:2869
bool drop_extension(char *str)
Definition: parselo.cpp:4043
GLint level
Definition: Glext.h:5180
int cfclose(CFILE *cfile)
Definition: cfile.cpp:895
GLenum const GLvoid * addr
Definition: Glext.h:9092
int stuff_float_list(float *flp, int max_floats)
Definition: parselo.cpp:2988
void diag_printf(char *format,...)
Definition: parselo.cpp:229
int atoi2()
Definition: parselo.cpp:2307
int cf_get_file_list(SCP_vector< SCP_string > &list, int pathtype, const char *filter, int sort=CF_SORT_NONE, SCP_vector< file_list_info > *info=NULL)
#define CAMPAIGN_LOADOUT_SHIP_LIST
Definition: parselo.h:60
char Current_filename_save[MAX_PATH_LEN]
Definition: parselo.cpp:37
#define F_MULTITEXT
Definition: parselo.h:43
#define stricmp(s1, s2)
Definition: config.h:271
char * Mp
Definition: parselo.cpp:48
char * alloc_block(char *startstr, char *endstr, int extra_chars)
Definition: parselo.cpp:1014
int skip_to_string(char *pstr, char *end)
Definition: parselo.cpp:375
int cfilelength(CFILE *cfile)
Definition: cfile.cpp:1393
int get_index_of_first_hash_symbol(SCP_string &src)
Definition: parselo.cpp:3882
const GLubyte * c
Definition: Glext.h:8376
int required_string_either_fred(char *str1, char *str2)
Definition: parselo.cpp:758
#define SEXP_VARIABLE_STRING
Definition: sexp.h:874
int skip_to_start_of_string(char *pstr, char *end)
Definition: parselo.cpp:404
char * next_tokens()
Definition: parselo.cpp:245
GLuint GLuint end
Definition: Gl.h:1502
#define MAX_SEXP_VARIABLES
Definition: sexp.h:23
#define RAW_INTEGER_TYPE
Definition: parselo.h:52
#define F_RAW
Definition: parselo.h:44
int Warning_count
Definition: parselo.cpp:41
#define strcpy_s(...)
Definition: safe_strings.h:67
int Is_standalone
Definition: systemvars.cpp:59
void maybe_convert_foreign_characters(char *line)
Definition: parselo.cpp:1711
#define F_LNAME
Definition: parselo.h:45
int cfseek(CFILE *fp, int offset, int where)