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