1 /*
2 *
3 *
4 * Copyright (c) 2003 Johannes Prix
5 * Copyright (c) 2004-2010 Arthur Huillet
6 *
7 * This file is part of Freedroid
8 *
9 * Freedroid is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * Freedroid is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Freedroid; see the file COPYING. If not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22 * MA 02111-1307 USA
23 *
24 */
25
26 /**
27 * This file contains some text code, that is also needed in the
28 * dialog editor.
29 */
30
31 #define _text_public_c
32
33 #include "system.h"
34
35 #include "defs.h"
36 #include "struct.h"
37 #include "proto.h"
38 #include "global.h"
39
40 #include <zlib.h>
41
42 extern int debug_level;
43
44 // A store for alert window or console error messages.
45 // Used in alert_once_window() and error_once_window() to avoid to display
46 // several the same message.
47
48 struct msg_store_item {
49 int when; // ONCE_PER_RUN / ONCE_PER_GAME - see defs.h
50 struct auto_string *msg; // The message already displayed.
51 };
52
53 static struct dynarray *msg_store = NULL;
54
55 /**
56 * This function works a malloc, except that it also checks for
57 * success and terminates in case of "out of memory", so we don't
58 * need to do this always in the code.
59 */
MyMalloc(long Mamount)60 void *MyMalloc(long Mamount)
61 {
62 void *Mptr = NULL;
63
64 // make Gnu-compatible even if on a broken system:
65 if (Mamount == 0)
66 Mamount = 1;
67
68 if ((Mptr = calloc(1, (size_t) Mamount)) == NULL) {
69 fprintf(stderr, " MyMalloc(%ld) did not succeed!\n", Mamount);
70 fflush(stderr);
71 Terminate(EXIT_FAILURE);
72 }
73
74 return Mptr;
75
76 }; // void* MyMalloc ( long Mamount )
77
78 /**
79 * This function works as strdup, except that it also checks for
80 * success and terminates in case of "out of memory", so we don't
81 * need to do this always in the code.
82 */
my_strdup(const char * src)83 char *my_strdup(const char *src)
84 {
85 if (src == NULL)
86 return NULL;
87
88 char *dst = strdup(src);
89 if (dst == NULL) {
90 fprintf(stderr, " my_strdup() did not succeed!\n");
91 fflush(stderr);
92 Terminate(EXIT_FAILURE);
93 }
94
95 return dst;
96 }
97
98 /**
99 * This function is used for debugging purposes. It writes the
100 * given string on the screen, or simply does
101 * nothing according to currently set debug level.
102 */
DebugPrintf(int db_level,const char * fmt,...)103 void DebugPrintf(int db_level, const char *fmt, ...)
104 {
105 va_list args;
106 va_start(args, fmt);
107
108 if (db_level <= debug_level) {
109 vfprintf(stderr, fmt, args);
110 }
111
112 va_end(args);
113 }; // void DebugPrintf ( int db_level, char *fmt, ...)
114
115 /*
116 * Check if a msg is already in the error msg store. If not, register it.
117 * Return TRUE if the msg was not yet in the store, FALSE otherwise.
118 */
_first_use_of_msg(int when,struct auto_string * msg)119 static int _first_use_of_msg(int when, struct auto_string *msg)
120 {
121 // A dynarray is used to store past alerts/errors, to remember which message was
122 // already displayed.
123
124 // On first use, initialize the msg store.
125
126 if (!msg_store)
127 msg_store = dynarray_alloc(10, sizeof(struct msg_store_item));
128
129 // If the message is already in the store, immediately return FALSE
130
131 int i;
132 for (i = 0; i < msg_store->size; i++) {
133 struct msg_store_item *stored_item = (struct msg_store_item *)dynarray_member(msg_store, i, sizeof(struct msg_store_item));
134 if (!strcmp(msg->value, stored_item->msg->value)) {
135 return FALSE;
136 }
137 }
138
139 // The message was not yet registered. Store it and return TRUE
140
141 struct msg_store_item *stored_item = (struct msg_store_item *)MyMalloc(sizeof(struct msg_store_item));
142 stored_item->when = when;
143 stored_item->msg = msg;
144 dynarray_add(msg_store, stored_item, sizeof(struct msg_store_item));
145 free(stored_item);
146
147 return TRUE;
148 }
149
150 /**
151 * Clean the alert/error message store, removing messages which are flagged as ONCE_PER_GAME
152 * so that they can be redisplayed
153 */
clean_error_msg_store()154 void clean_error_msg_store()
155 {
156 if (!msg_store) // msg store not yet used
157 return;
158
159 // We have to remove the item flagged as ONCE_PER_GAME, but we have to keep
160 // the others. We can not delete a dynarray member inside a loop over the
161 // dynarray. We thus create a new dynarray to replace the current one,
162 // containing the kept items.
163
164 struct dynarray *new_store = dynarray_alloc(10, sizeof(struct msg_store_item));
165
166 int i;
167 for (i = 0; i < msg_store->size; i++) {
168 struct msg_store_item *stored_item = (struct msg_store_item *)dynarray_member(msg_store, i, sizeof(struct msg_store_item));
169 if (stored_item->when == ONCE_PER_GAME) {
170 // free memory
171 free_autostr(stored_item->msg);
172 stored_item->msg = NULL;
173 } else {
174 // copy the item in the new store
175 dynarray_add(new_store, stored_item, sizeof(struct msg_store_item));
176 }
177 }
178 dynarray_free(msg_store);
179 free(msg_store);
180 msg_store = new_store;
181 }
182
183 /**
184 * Free all the memory slots used by the msg_store
185 */
free_error_msg_store()186 void free_error_msg_store()
187 {
188 if (!msg_store) // msg store was not used
189 return;
190
191 int i;
192 for (i = 0; i < msg_store->size; i++) {
193 struct msg_store_item *stored_item = (struct msg_store_item *)dynarray_member(msg_store, i, sizeof(struct msg_store_item));
194 free_autostr(stored_item->msg);
195 }
196 dynarray_free(msg_store);
197 free(msg_store);
198 }
199
200 /**
201 * This function should help to simplify and standardize the many error
202 * messages possible in FreedroidRPG.
203 */
error_message(const char * fn,const char * fmt,int error_type,...)204 void error_message(const char *fn, const char *fmt, int error_type, ...)
205 {
206 if (error_type & SILENT)
207 return;
208
209 va_list args;
210 va_start(args, error_type);
211
212 fprintf(stderr, "\n---------------------------------------------------------------------------------\n"
213 "FreedroidRPG %s encountered a problem in function: %s\n", freedroid_version, fn);
214 vfprintf(stderr, fmt, args);
215 fprintf(stderr, "\n");
216
217 va_end(args);
218
219 if (error_type & PLEASE_INFORM) {
220 fprintf(stderr, "\n\n"
221 "If you encounter this message, please inform the FreedroidRPG developers about it!\n"
222 "You can\n"
223 " send an e-mail to freedroid-discussion AT lists.sourceforge.net\n"""
224 " mention it on our IRC channel #freedroid on irc.freenode.net\n"
225 " or report the bug on our tracker at http://bugs.freedroid.org/\n"
226 "\n"
227 "Thank you!\n");
228 }
229
230 if (error_type & IS_FATAL) {
231 fprintf(stderr, "\nFreedroidRPG will terminate now to draw attention to the problems it could\n"
232 "not resolve. We are sorry if that interrupts a major game of yours.\n"
233 "---------------------------------------------------------------------------------\n");
234 } else {
235 fprintf(stderr, "---------------------------------------------------------------------------------\n");
236 }
237
238 if ((error_type & IS_FATAL) && !(error_type & NO_TERMINATE))
239 Terminate(EXIT_FAILURE);
240 }
241
242 /**
243 * Wrapper on error_message() that avoids to redisplay several times the same error.
244 * The first parameter defines if an error should never be redisplayed at
245 * all (ONCE_PER_RUN), or if it has to be redisplayed once if a new savegame was
246 * loaded (ONCE_PER_GAME).
247 */
error_once_message(int when,const char * fn,const char * fmt,int error_type,...)248 void error_once_message(int when, const char *fn, const char *fmt, int error_type, ...)
249 {
250 if (error_type & SILENT)
251 return;
252
253 // Compute the error message
254
255 va_list args;
256 struct auto_string *buffer = alloc_autostr(256);
257 va_start(args, error_type);
258 autostr_vappend(buffer, fmt, args);
259 va_end(args);
260
261 // If the message is already in the store, immediately return
262
263 if (!_first_use_of_msg(when, buffer)) {
264 free_autostr(buffer);
265 return;
266 }
267
268 // This error was not yet displayed.
269
270 error_message(fn, "%s", error_type, buffer->value);
271 }
272
273 /**
274 * In some cases it will be necessary to inform the user of something in
275 * a big important style. Then a popup window is suitable, with a mouse
276 * click to confirm and make it go away again.
277 */
alert_window(const char * text,...)278 void alert_window(const char *text, ...)
279 {
280 if (!SDL_WasInit(SDL_INIT_VIDEO))
281 return;
282
283 va_list args;
284 struct auto_string *buffer = alloc_autostr(256);
285 va_start(args, text);
286 autostr_vappend(buffer, text, args);
287 va_end(args);
288
289 Activate_Conservative_Frame_Computation();
290 StoreMenuBackground(1);
291
292 int w = 440; // arbitrary
293 int h = 60; // arbitrary
294 int x = (GameConfig.screen_width - w) / 2; // center of screen
295 int y = (GameConfig.screen_height - h) / 5 * 2; // 2/5 of screen from top
296
297 SDL_Event e;
298 while (1) {
299 SDL_Delay(1);
300 RestoreMenuBackground(1);
301
302 int r_height = show_backgrounded_text_rectangle(buffer->value, FPS_Display_Font, x, y, w, h);
303 show_backgrounded_text_rectangle(_("Click to continue..."), Red_Font, x, y + r_height, w, 10);
304
305 blit_mouse_cursor();
306 our_SDL_flip_wrapper();
307 save_mouse_state();
308
309 SDL_WaitEvent(&e);
310
311 switch (e.type) {
312 case SDL_QUIT:
313 Terminate(EXIT_SUCCESS);
314 break;
315 case SDL_KEYDOWN:
316 if (e.key.keysym.sym == SDLK_SPACE || e.key.keysym.sym == SDLK_RETURN || e.key.keysym.sym == SDLK_ESCAPE)
317 goto wait_click_and_out;
318 break;
319 case SDL_MOUSEBUTTONDOWN:
320 if (e.button.button == 1)
321 goto wait_click_and_out;
322 break;
323 }
324 }
325
326 wait_click_and_out:
327 while (SpacePressed() || EnterPressed() || EscapePressed() || MouseLeftPressed());
328
329 our_SDL_flip_wrapper();
330 RestoreMenuBackground(1);
331
332 free_autostr(buffer);
333 }
334
335 /**
336 * Wrapper on alert_window() that avoids to redisplay several times the same alert.
337 * The first parameter defines if an alert should never be redisplayed at
338 * all (ONCE_PER_RUN), or if it has to be redisplayed once if a new savegame was
339 * loaded (ONCE_PER_GAME).
340 */
alert_once_window(int when,const char * text,...)341 void alert_once_window(int when, const char *text, ...)
342 {
343 if (!SDL_WasInit(SDL_INIT_VIDEO))
344 return;
345
346 // Compute the alert message
347
348 va_list args;
349 struct auto_string *buffer = alloc_autostr(256);
350 va_start(args, text);
351 autostr_vappend(buffer, text, args);
352 va_end(args);
353
354 // If the message is already in the store, immediately return
355
356 if (!_first_use_of_msg(when, buffer)) {
357 free_autostr(buffer);
358 return;
359 }
360
361 // This alert was not yet displayed.
362
363 alert_window("%s", buffer->value);
364 }
365
366 /**
367 * This function does something similar to memmem. Indeed, it would be
368 * best perhaps if it did exactly the same as memmem, but since we do not
369 * use gnu extensions for compatibility reasons, we go this way.
370 *
371 * Be careful with using this function for searching just one byte!!
372 *
373 * Be careful with using this function for non-string NEEDLE!!
374 *
375 * Haystack can of course have ANY form!!!
376 *
377 */
MyMemmem(char * haystack,size_t haystacklen,char * needle,size_t needlelen)378 void *MyMemmem(char *haystack, size_t haystacklen, char *needle, size_t needlelen)
379 {
380 char *NextFoundPointer;
381 void *MatchPointer;
382 size_t SearchPos = 0;
383
384 while (haystacklen - SearchPos > 0) {
385 // We search for the first match OF THE FIRST CHARACTER of needle
386 //
387 NextFoundPointer = memchr(haystack + SearchPos, needle[0], haystacklen - SearchPos);
388
389 // if not even that was found, we can immediately return and report our failure to find it
390 //
391 if (NextFoundPointer == NULL)
392 return (NULL);
393
394 // Otherwise we see, if also the rest of the strings match this time ASSUMING THEY ARE STRINGS!
395 // In case of a match, we can return immediately
396 //
397 MatchPointer = strstr(NextFoundPointer, needle);
398 if (MatchPointer != NULL)
399 return (MatchPointer);
400
401 // At this point, we know that we had no luck with this one occasion of a first-character-match
402 // and must continue after this one occasion with our search
403 SearchPos = NextFoundPointer - haystack + 1;
404 }
405
406 return (NULL);
407 }; // void *MyMemmem ( ... );
408
409 /**
410 * This function looks for a string begin indicator and takes the string
411 * from after there up to a string end indicator and mallocs memory for
412 * it, copies it there and returns it.
413 * The original source string specified should in no way be modified.
414 * Returns 0 if the prefix string is not present
415 */
ReadAndMallocStringFromDataOptional(char * SearchString,const char * StartIndicationString,const char * EndIndicationString)416 char *ReadAndMallocStringFromDataOptional(char *SearchString, const char *StartIndicationString, const char *EndIndicationString)
417 {
418 char *SearchPointer;
419 char *EndOfStringPointer;
420 char *ReturnString = NULL;
421 int StringLength;
422
423 SearchPointer = strstr(SearchString, StartIndicationString);
424
425 if (!SearchPointer)
426 return 0;
427
428 // Now we move to the beginning
429 SearchPointer += strlen(StartIndicationString);
430
431 // Now we move to the end with the end pointer
432 EndOfStringPointer = strstr(SearchPointer, EndIndicationString);
433 if (!EndOfStringPointer)
434 return 0;
435
436 // Now we allocate memory and copy the string (even empty string)...
437 StringLength = EndOfStringPointer - SearchPointer;
438 ReturnString = MyMalloc(StringLength + 1);
439 strncpy(ReturnString, SearchPointer, StringLength);
440 ReturnString[StringLength] = 0;
441
442 return ReturnString;
443 }
444
445 /**
446 * This function looks for a string begin indicator and takes the string
447 * from after there up to a string end indicator and mallocs memory for
448 * it, copies it there and returns it.
449 * The original source string specified should in no way be modified.
450 * The lack of searched string is fatal.
451 */
ReadAndMallocStringFromData(char * SearchString,const char * StartIndicationString,const char * EndIndicationString)452 char *ReadAndMallocStringFromData(char *SearchString, const char *StartIndicationString, const char *EndIndicationString)
453 {
454 char *result = ReadAndMallocStringFromDataOptional(SearchString, StartIndicationString, EndIndicationString);
455 if (!result) {
456 fprintf(stderr, "\n\nStartIndicationString: '%s'\nEndIndicationString: '%s'\n", StartIndicationString, EndIndicationString);
457 error_message(__FUNCTION__, "\
458 The string that is supposed to prefix an entry in a text data file\n\
459 of FreedroidRPG was not found within this text data file.\n\
460 This indicates some corruption in the data file in question.", PLEASE_INFORM | IS_FATAL);
461 }
462 return result;
463 }; // char* ReadAndMallocStringFromData ( ... )
464
465 /**
466 * This function counts the number of occurrences of a string in a given
467 * other string.
468 */
CountStringOccurences(char * SearchString,const char * TargetString)469 int CountStringOccurences(char *SearchString, const char *TargetString)
470 {
471 int Counter = 0;
472 char *CountPointer;
473
474 CountPointer = SearchString;
475
476 while ((CountPointer = strstr(CountPointer, TargetString)) != NULL) {
477 CountPointer += strlen(TargetString);
478 Counter++;
479 }
480 return (Counter);
481 }; // CountStringOccurences ( char* SearchString , char* TargetString )
482
483 /**
484 * This function read in a file with the specified name, allocated
485 * memory for it of course, looks for the file end string and then
486 * terminates the whole read in file with a 0 character, so that it
487 * can easily be treated like a common string.
488 */
ReadAndMallocAndTerminateFile(const char * filename,const char * File_End_String)489 char *ReadAndMallocAndTerminateFile(const char *filename, const char *File_End_String)
490 {
491 FILE *DataFile;
492 char *Data;
493 char *ReadPointer;
494 long MemoryAmount;
495
496 // Read the whole theme data to memory. We use binary mode, as we
497 // don't want to have to deal with any carriage return/line feed
498 // convention mess on win32 or something...
499 //
500 if ((DataFile = fopen(filename, "rb")) == NULL) {
501 fprintf(stderr, "\n\nfilename: '%s'\n", filename);
502
503 error_message(__FUNCTION__, "\
504 FreedroidRPG was unable to open a given text file,\n\
505 that should be there and should be accessible.\n\
506 This indicates a serious bug in this installation of FreedroidRPG.", PLEASE_INFORM | IS_FATAL);
507 } else {
508 DebugPrintf(1, "\nchar* ReadAndMallocAndTerminateFile ( char* filename ) : Opening file succeeded...");
509 }
510
511 int filelen = FS_filelength(DataFile);
512 MemoryAmount = filelen + 100;
513 Data = (char *)MyMalloc(MemoryAmount);
514
515 if (fread(Data, 1, MemoryAmount, DataFile) < filelen && ferror(DataFile)) {
516 error_message(__FUNCTION__, "\
517 FreedroidRPG was unable to read a given text file, that should be there and\n\
518 should be accessible.\n\
519 Filename: %s", PLEASE_INFORM | IS_FATAL, filename);
520 } else {
521 DebugPrintf(1, "\n%s(): Reading file succeeded...", __FUNCTION__);
522 }
523
524 if (fclose(DataFile) == EOF) {
525 fprintf(stderr, "\n\nfilename: '%s'\n", filename);
526 error_message(__FUNCTION__, "\
527 FreedroidRPG was unable to close a given text file, that should be there and\n\
528 should be accessible.\n\
529 This indicates a strange bug in this installation of Freedroid, that is\n\
530 very likely a problem with the file/directory permissions of the files\n\
531 belonging to FreedroidRPG.", PLEASE_INFORM | IS_FATAL);
532 } else {
533 DebugPrintf(1, "\n%s(): file closed successfully...\n", __FUNCTION__);
534 }
535
536 // NOTE: Since we do not assume to always have pure text files here, we switched to
537 // MyMemmem, so that we can handle 0 entries in the middle of the file content as well
538 if (File_End_String) {
539 if ((ReadPointer = MyMemmem(Data, (size_t) MemoryAmount, (char *)File_End_String, (size_t) strlen(File_End_String))) == NULL) {
540 error_message(__FUNCTION__, "FreedroidRPG was unable to find the string, that should indicate the end of\n"
541 "the given text file within this file.\n"
542 "This indicates a corrupt or outdated data or saved game file."
543 " filename: '%s' - File_End_String: '%s'",
544 PLEASE_INFORM | IS_FATAL, filename, File_End_String);
545 } else {
546 ReadPointer[0] = 0;
547 }
548 } else
549 Data[MemoryAmount - 100] = 0;
550
551 return (Data);
552 }
553
554 /**
555 * This function tries to locate a string in some given data string.
556 * The data string is assumed to be null terminated. Otherwise SEGFAULTS
557 * might happen.
558 *
559 * The return value is a pointer to the first instance where the substring
560 * we are searching is found in the main text.
561 */
LocateStringInData(char * SearchBeginPointer,const char * SearchTextPointer)562 char *LocateStringInData(char *SearchBeginPointer, const char *SearchTextPointer)
563 {
564 char *temp;
565
566 if ((temp = strstr(SearchBeginPointer, SearchTextPointer)) == NULL) {
567 fprintf(stderr, "\n\nSearchTextPointer: '%s'\n", SearchTextPointer);
568 error_message(__FUNCTION__, "\
569 The string that was supposed to be in the text data file could not be found.\n\
570 This indicates a corrupted or seriously outdated game data or saved game file.", PLEASE_INFORM | IS_FATAL);
571 }
572
573 return (temp);
574
575 }; // char* LocateStringInData ( ... )
576
577 /**
578 * This function should analyze a given passage of text, locate an
579 * indicator for a value, and read in the value.
580 */
581 void
ReadValueFromStringWithDefault(char * SearchBeginPointer,const char * ValuePreceedText,const char * FormatString,const char * DefaultValueString,void * TargetValue,char * EndOfSearchSectionPointer)582 ReadValueFromStringWithDefault(char *SearchBeginPointer, const char *ValuePreceedText, const char *FormatString,
583 const char *DefaultValueString, void *TargetValue, char *EndOfSearchSectionPointer)
584 {
585 char OldTerminaterCharValue = 0;
586 const char *SourceLocation;
587
588 // We shortly make a termination char into the string.
589 if (EndOfSearchSectionPointer) {
590 OldTerminaterCharValue = EndOfSearchSectionPointer[0];
591 EndOfSearchSectionPointer[0] = 0;
592 }
593
594 // Now we locate the spot, where we finally will find our value
595 SourceLocation = strstr(SearchBeginPointer, ValuePreceedText);
596 if (SourceLocation)
597 SourceLocation += strlen(ValuePreceedText);
598 else
599 SourceLocation = DefaultValueString;
600
601 // Attention!!!
602 // Now we try to read in the value!!!
603 //
604 if (sscanf(SourceLocation, FormatString, TargetValue) == EOF) {
605 error_message(__FUNCTION__, "sscanf using a certain format string failed!\n"
606 "This indicates a corrupted or seriously outdated game data or saved game file.\n"
607 " FormatString: '%s' - ValuePreceedText: '%s'",
608 PLEASE_INFORM | IS_FATAL, FormatString, ValuePreceedText);
609 }
610 // Now that we are done, we restore the given SearchArea to former glory
611 if (EndOfSearchSectionPointer) {
612 EndOfSearchSectionPointer[0] = OldTerminaterCharValue;
613 }
614 }
615
616 /**
617 * This function should analyze a given passage of text, locate an
618 * indicator for a value, and read in the value.
619 */
620 void
ReadValueFromString(char * SearchBeginPointer,const char * ValuePreceedText,const char * FormatString,void * TargetValue,char * EndOfSearchSectionPointer)621 ReadValueFromString(char *SearchBeginPointer, const char *ValuePreceedText, const char *FormatString, void *TargetValue,
622 char *EndOfSearchSectionPointer)
623 {
624 char OldTerminaterCharValue = 0;
625 char *SourceLocation;
626
627 // We shortly make a termination char into the string if needed
628 if (EndOfSearchSectionPointer) {
629 OldTerminaterCharValue = EndOfSearchSectionPointer[0];
630 EndOfSearchSectionPointer[0] = 0;
631 }
632 // Now we locate the spot, where we finally will find our value
633 SourceLocation = LocateStringInData(SearchBeginPointer, ValuePreceedText);
634 SourceLocation += strlen(ValuePreceedText);
635
636 // Attention!!!
637 // Now we try to read in the value!!!
638 //
639 if (sscanf(SourceLocation, FormatString, TargetValue) == EOF) {
640 error_message(__FUNCTION__, "sscanf using a certain format string failed!\n"
641 "This indicates a corrupted or seriously outdated game data or saved game file.\n"
642 " FormatString: '%s' - ValuePreceedText: '%s'",
643 PLEASE_INFORM | IS_FATAL, FormatString, ValuePreceedText);
644 }
645
646 if (EndOfSearchSectionPointer) {
647 // Now that we are done, we restore the given SearchArea to former glory
648 EndOfSearchSectionPointer[0] = OldTerminaterCharValue;
649 }
650
651 }; // void ReadValueFromString( ... )
652
653 /**
654 * \deprecated Use get_range_from_string.
655 * \brief Read a range of integer values from a string with a default.
656 *
657 * Read the range between two indications. The function don't accept negative value.
658 *
659 * \param SearchString The global string to search the range.
660 * \param StartIndicationString The start indication.
661 * \param EndIndicationString The end indication.
662 * \param min,max The result of the conversion.
663 * \param default_val The value used as default.
664 * \return FALSE if the conversion failed and set min and max to default_val, else return TRUE.
665 */
ReadRangeFromString(char * SearchString,const char * StartIndicationString,const char * EndIndicationString,int * min,int * max,int default_val)666 int ReadRangeFromString(char *SearchString, const char *StartIndicationString, const char *EndIndicationString, int *min, int *max, int default_val)
667 {
668 int result;
669
670 // First, we have to get the range string.
671 char *search_ptr = ReadAndMallocStringFromDataOptional(SearchString, StartIndicationString, EndIndicationString);
672
673 result = get_range_from_string(search_ptr, min, max, default_val);
674
675 //Handle corrupted values.
676 if (*min < 0) {
677 error_message(__FUNCTION__, "\
678 The value read in as a minimum (%d) is a negative number.\n\
679 This most likely means corrupted data.\n\
680 Setting both the maximum and minimum to the default value (%d).", NO_REPORT, *min, default_val);
681 *min = *max = default_val;
682 return FALSE;
683 }
684
685 free(search_ptr);
686
687 return result;
688 }
689
690 /**
691 * \brief Read a range of integer values from a string with a default.
692 * \param str The constant string to convert.
693 * \param min,max The result of the conversion.
694 * \param default_value The value used in case of error or NULL.
695 * \return FALSE if the conversion failed and set min and max to default_val, else return TRUE.
696 */
get_range_from_string(const char * str,int * min,int * max,int default_value)697 int get_range_from_string(const char *str, int *min, int *max, int default_value)
698 {
699 char *ptr;
700
701 if (!str) {
702 // String is NULL, set everything to default value.
703 *min = *max = default_value;
704 return TRUE;
705 }
706
707 *min = strtol(str, &ptr, 10);
708
709 if (str == ptr) {
710 goto read_error;
711 }
712
713 if (*ptr == '\0') {
714 // Conversion succeeded and the string contains only one value.
715 *max = *min;
716 return TRUE;
717 }
718
719 if (*ptr != ':') {
720 goto read_error;
721 }
722
723 // The string contains potentially another value.
724 str = ++ptr;
725 *max = strtol(str, &ptr, 10);
726
727 if (str == ptr) {
728 goto read_error;
729 }
730 // Conversion of max value succeeded
731
732 if (*max >= *min) {
733 return TRUE; // Values are in the good order.
734 }
735
736 error_message(__FUNCTION__, "The value read in as a maximum (%d) is less than the minimum (%d).\n"
737 "This most likely means corrupted data.\n"
738 "Setting both the maximum and minimum to the default value (%d).",
739 NO_REPORT, *max, *min, default_value);
740 *min = *max = default_value;
741 return FALSE;
742
743 read_error:
744 // Failed to read the string.
745 error_message(__FUNCTION__, "The string (\"%s\") cannot be legaly converted to a range of integers.\n"
746 "This most likely means corrupted data.\n"
747 "Setting both the maximum and minimum to the default value (%d).",
748 NO_REPORT, str, default_value);
749 *min = *max = default_value;
750 return FALSE;
751 }
752
753 /**
754 * This function does the rotation of a given vector by a given angle.
755 * The vector is a vector.
756 * The angle given is in DEGREE MEASURE, i.e. 0-360 or so, maybe more or
757 * less than that, but you get what I mean...
758 */
RotateVectorByAngle(moderately_finepoint * vector,float rot_angle)759 void RotateVectorByAngle(moderately_finepoint * vector, float rot_angle)
760 {
761 moderately_finepoint new_vect;
762 float rad_angle;
763
764 if (rot_angle == 0)
765 return;
766
767 rad_angle = rot_angle * (M_PI / 180.0);
768
769 DebugPrintf(2, "\n RAD_ANGLE : %f ", rad_angle);
770 new_vect.x = sin(rad_angle) * vector->y + cos(rad_angle) * vector->x;
771 new_vect.y = cos(rad_angle) * vector->y - sin(rad_angle) * vector->x;
772 vector->x = new_vect.x;
773 vector->y = new_vect.y;
774
775 }; // void RotateVectorByAngle ( ... )
776
777 /*----------------------------------------------------------------------
778 * Copyright (C) 1997-2001 Id Software, Inc., under GPL
779 *
780 * FS_filelength().. (taken from quake2)
781 * contrary to stat() this fct is nice and portable,
782 *----------------------------------------------------------------------*/
FS_filelength(FILE * f)783 int FS_filelength(FILE * f)
784 {
785 int pos;
786 int end;
787
788 pos = ftell(f);
789 fseek(f, 0, SEEK_END);
790 end = ftell(f);
791 fseek(f, pos, SEEK_SET);
792
793 // DebugPrintf ( -4 , "\n%s(): file length: %d." , __FUNCTION__ , end );
794
795 return end;
796 }; // int FS_filelength (FILE *f)
797
798 /*-------------------------------------------------------------------------
799 * Inflate a given stream using zlib
800 *
801 * Takes DataFile file and points DataBuffer to a buffer containing the
802 * uncompressed data. Sets '*size' to the size of said uncompressed data, if size
803 * is not NULL.
804 *
805 * Returns nonzero in case of error.
806 ***/
inflate_stream(FILE * DataFile,unsigned char ** DataBuffer,int * size)807 int inflate_stream(FILE * DataFile, unsigned char **DataBuffer, int *size)
808 {
809 int filelen = FS_filelength(DataFile);
810 unsigned char *src = MyMalloc(filelen + 1);
811 int cursz = 1048576; //start with 1MB
812 unsigned char *temp_dbuffer = malloc(cursz);
813 if (fread(src, filelen, 1, DataFile) != 1) {
814 error_message(__FUNCTION__, "Error reading compressed data stream.", PLEASE_INFORM);
815 free(temp_dbuffer);
816 free(src);
817 return -1;
818 }
819
820 int ret;
821 z_stream strm;
822
823 /* copy paste of public domain tool "zpipe" */
824 /* big thanks to zlib authors */
825 /* allocate inflate state */
826 strm.zalloc = Z_NULL;
827 strm.zfree = Z_NULL;
828 strm.opaque = Z_NULL;
829 strm.avail_in = filelen;
830 strm.next_in = src;
831 strm.avail_out = cursz;
832 strm.next_out = (Bytef *) temp_dbuffer;
833
834 ret = inflateInit2(&strm, 47);
835 if (ret != Z_OK) {
836 error_message(__FUNCTION__, "\
837 zlib was unable to start decompressing a stream.\n\
838 This indicates a serious bug in this installation of FreedroidRPG.", PLEASE_INFORM);
839 free(temp_dbuffer);
840 free(src);
841 return -1;
842 }
843
844 do {
845 if (!strm.avail_out) { //out of memory? increase
846 temp_dbuffer = realloc(temp_dbuffer, cursz + 1048576); //increase size by 1MB
847 strm.next_out = temp_dbuffer + cursz;
848 cursz += 1048576;
849 strm.avail_out += 1048576;
850 }
851 ret = inflate(&strm, Z_NO_FLUSH);
852 switch (ret) {
853 case Z_OK:
854 break;
855 case Z_NEED_DICT:
856 case Z_DATA_ERROR:
857 case Z_MEM_ERROR:
858 (void)inflateEnd(&strm);
859
860 error_message(__FUNCTION__, "\
861 zlib was unable to decompress a stream\n\
862 This indicates a serious bug in this installation of FreedroidRPG.", PLEASE_INFORM);
863 free(temp_dbuffer);
864 free(src);
865 return -1;
866 break;
867 }
868 } while (ret != Z_STREAM_END);
869
870 (*DataBuffer) = (unsigned char *)malloc(strm.total_out + 1);
871 memcpy(*DataBuffer, temp_dbuffer, strm.total_out);
872 (*DataBuffer)[strm.total_out] = 0;
873
874 if (size != NULL)
875 *size = strm.total_out;
876
877 (void)inflateEnd(&strm);
878 free(src);
879 free(temp_dbuffer);
880
881 return 0;
882 }
883
deflate_to_stream(unsigned char * source_buffer,int size,FILE * dest)884 int deflate_to_stream(unsigned char *source_buffer, int size, FILE *dest)
885 {
886 int ret;
887 unsigned have;
888 z_stream strm;
889 #define CHUNK 16384
890 unsigned char out[CHUNK];
891
892 /* allocate deflate state */
893 strm.zalloc = Z_NULL;
894 strm.zfree = Z_NULL;
895 strm.opaque = Z_NULL;
896 strm.avail_in = size;
897 strm.next_in = source_buffer;
898
899 ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
900 if (ret != Z_OK) {
901 error_message(__FUNCTION__, "\
902 zlib was unable to start compressing a string.", PLEASE_INFORM);
903 return -1;
904 }
905
906 /* run deflate() on input until output buffer not full, finish
907 compression if all of source has been read in */
908 do {
909 strm.avail_out = CHUNK;
910 strm.next_out = out;
911 ret = deflate(&strm, Z_FINISH);
912 if (ret == Z_STREAM_ERROR) {
913 error_message(__FUNCTION__, "Stream error while deflating a buffer", IS_FATAL | PLEASE_INFORM);
914 }
915 have = CHUNK - strm.avail_out;
916 if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
917 (void)deflateEnd(&strm);
918 return -1;
919 }
920 } while (strm.avail_out == 0);
921
922 /* clean up and return */
923 (void)deflateEnd(&strm);
924
925 return 0;
926 }
927
928 #undef _text_public_c
929