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