1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name util.cpp - General utilites. */
12 //
13 //      (c) Copyright 1998-2019 by Lutz Sammer, Jimmy Salmon and Andrettin
14 //
15 //      This program is free software; you can redistribute it and/or modify
16 //      it under the terms of the GNU General Public License as published by
17 //      the Free Software Foundation; only version 2 of the License.
18 //
19 //      This program is distributed in the hope that it will be useful,
20 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //      GNU General Public License for more details.
23 //
24 //      You should have received a copy of the GNU General Public License
25 //      along with this program; if not, write to the Free Software
26 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 //      02111-1307, USA.
28 //
29 
30 #include "stratagus.h"
31 
32 #include "util.h"
33 
34 //Wyrmgus start
35 #include "network.h"
36 //Wyrmgus end
37 
38 #include <boost/tokenizer.hpp>
39 
40 #include <ctype.h>
41 #include <cctype>
42 #include <errno.h>
43 #include <map>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 //Wyrmgus start
48 #include <time.h>
49 //Wyrmgus end
50 
51 #ifdef WIN32
52 #include <windows.h>
53 #endif
54 
55 #ifdef USE_STACKTRACE
56 #include <stdexcept>
57 #include <stacktrace/call_stack.hpp>
58 #include <stacktrace/stack_exception.hpp>
59 #endif
60 
61 #ifdef USE_X11
62 #include <X11/Xlib.h>
63 #include <X11/Xatom.h>
64 #endif
65 
66 /*----------------------------------------------------------------------------
67 --  Random
68 ----------------------------------------------------------------------------*/
69 
70 unsigned SyncRandSeed;				/// sync random seed value.
71 
72 /**
73 **  Inititalize sync rand seed.
74 */
InitSyncRand()75 void InitSyncRand()
76 {
77 	if (!IsNetworkGame()) { //if isn't a network game, make the seed vary according to the date and time
78 		time_t time_curr;
79 		time(&time_curr);
80 		SyncRandSeed = static_cast<unsigned>(time_curr);
81 	} else {
82 		SyncRandSeed = 0x87654321;
83 	}
84 }
85 
86 /**
87 **  Synchronized random number.
88 **
89 **  @note This random value must be same on all machines in network game.
90 **  Very simple random generations, enough for us.
91 */
SyncRand()92 int SyncRand()
93 {
94 	int val;
95 
96 	val = SyncRandSeed >> 16;
97 
98 	SyncRandSeed = SyncRandSeed * (0x12345678 * 4 + 1) + 1;
99 
100 	return val;
101 }
102 
103 /**
104 **  Synchronized random number.
105 **
106 **  @param max  Max value of random number to return
107 */
SyncRand(int max)108 int SyncRand(int max)
109 {
110 	return SyncRand() % max;
111 }
112 
113 
114 
MyRand()115 int MyRand()
116 {
117 	return rand();
118 }
119 
120 /*----------------------------------------------------------------------------
121 --  Math
122 ----------------------------------------------------------------------------*/
123 
124 /**
125 **  Compute a square root using ints
126 **
127 **  Uses John Halleck's method, see
128 **  http://www.cc.utah.edu/~nahaj/factoring/isqrt.legalize.c.html
129 **
130 **  @param num  Calculate the square root of this number
131 **
132 **  @return     The integer square root.
133 */
isqrt(long num)134 long isqrt(long num)
135 {
136 	long squaredbit;
137 	long remainder;
138 	long root;
139 
140 	if (num < 1) {
141 		return 0;
142 	}
143 
144 	//
145 	//  Load the binary constant 01 00 00 ... 00, where the number
146 	//  of zero bits to the right of the single one bit
147 	//  is even, and the one bit is as far left as is consistent
148 	//  with that condition.)
149 	//
150 	//  This portable load replaces the loop that used to be
151 	//  here, and was donated by  legalize@xmission.com
152 	//
153 	squaredbit  = (long)((((unsigned long)~0L) >> 1) & ~(((unsigned long)~0L) >> 2));
154 
155 	// Form bits of the answer.
156 	remainder = num;
157 	root = 0;
158 	while (squaredbit > 0) {
159 		if (remainder >= (squaredbit | root)) {
160 			remainder -= (squaredbit | root);
161 			root >>= 1;
162 			root |= squaredbit;
163 		} else {
164 			root >>= 1;
165 		}
166 		squaredbit >>= 2;
167 	}
168 
169 	return root;
170 }
171 
172 
173 /*----------------------------------------------------------------------------
174 --  Strings
175 ----------------------------------------------------------------------------*/
176 
177 #ifndef HAVE_STRCPYS
strcpy_s(char * dst,size_t dstsize,const char * src)178 errno_t strcpy_s(char *dst, size_t dstsize, const char *src)
179 {
180 	if (dst == nullptr || src == nullptr) {
181 		return EINVAL;
182 	}
183 	if (strlen(src) >= dstsize) {
184 		return ERANGE;
185 	}
186 	strcpy(dst, src);
187 	return 0;
188 }
189 #endif
190 
191 #ifndef HAVE_STRNLEN
strnlen(const char * str,size_t strsize)192 size_t strnlen(const char *str, size_t strsize)
193 {
194 	size_t len = 0;
195 	while (len < strsize) {
196 		if (*str == '\0') {
197 			break;
198 		}
199 		++str;
200 		++len;
201 	}
202 	return len;
203 }
204 #endif
205 
206 #ifndef HAVE_STRNCPYS
strncpy_s(char * dst,size_t dstsize,const char * src,size_t count)207 errno_t strncpy_s(char *dst, size_t dstsize, const char *src, size_t count)
208 {
209 	if (dst == nullptr || src == nullptr || dstsize == 0) {
210 		return EINVAL;
211 	}
212 
213 	size_t mincount;
214 	if (count == _TRUNCATE) {
215 		mincount = strnlen(src, dstsize);
216 	} else {
217 		mincount = strnlen(src, count);
218 	}
219 	if (mincount >= dstsize) {
220 		if (count != _TRUNCATE) {
221 			dst[0] = '\0';
222 			return EINVAL;
223 		} else {
224 			mincount = dstsize - 1;
225 		}
226 	}
227 	for (size_t i = 0; i < mincount; ++i) {
228 		*dst++ = *src++;
229 	}
230 	*dst = '\0';
231 	return 0;
232 }
233 #endif
234 
235 #ifndef HAVE_STRCATS
strcat_s(char * dst,size_t dstsize,const char * src)236 errno_t strcat_s(char *dst, size_t dstsize, const char *src)
237 {
238 	if (dst == nullptr || src == nullptr) {
239 		return EINVAL;
240 	}
241 	char *enddst = dst;
242 	size_t count = dstsize;
243 	while (count > 0 && *enddst != '\0') {
244 		++enddst;
245 		count--;
246 	}
247 	if (count == 0) {
248 		return EINVAL;
249 	}
250 	if (strlen(src) >= count) {
251 		return ERANGE;
252 	}
253 	strcpy(enddst, src);
254 	return 0;
255 }
256 #endif
257 
258 #if !defined(HAVE_STRCASESTR)
259 /**
260 **  Case insensitive version of strstr
261 **
262 **  @param a  String to search in
263 **  @param b  Substring to search for
264 **
265 **  @return   Pointer to first occurrence of b or null if not found.
266 */
strcasestr(const char * a,const char * b)267 char *strcasestr(const char *a, const char *b)
268 {
269 	int x;
270 
271 	if (!a || !*a || !b || !*b || strlen(a) < strlen(b)) {
272 		return nullptr;
273 	}
274 
275 	x = 0;
276 	while (*a) {
277 		if (a[x] && (tolower(a[x]) == tolower(b[x]))) {
278 			++x;
279 		} else if (b[x]) {
280 			++a;
281 			x = 0;
282 		} else {
283 			return (char *)a;
284 		}
285 	}
286 
287 	return nullptr;
288 }
289 #endif // !HAVE_STRCASESTR
290 
SplitString(const std::string & str,const char * separators)291 std::vector<std::string> SplitString(const std::string &str, const char *separators)
292 {
293 	std::vector<std::string> output;
294 
295 	boost::char_separator<char> separator(separators);
296 
297 	boost::tokenizer<boost::char_separator<char>> tokens(str, separator);
298 
299 	for (boost::tokenizer<boost::char_separator<char>>::iterator iterator = tokens.begin(); iterator != tokens.end(); ++iterator) {
300 		output.push_back(*iterator);
301 	}
302 
303 	return output;
304 }
305 
StringToBool(const std::string & str)306 bool StringToBool(const std::string &str)
307 {
308 	return str == "true" || str == "1";
309 }
310 
IsStringNumber(const std::string & str)311 bool IsStringNumber(const std::string &str)
312 {
313 	for (size_t i = 0; i < str.length(); ++i) {
314 		if (!std::isdigit(str[i]) && (i != 0 || str[i] != '-')) {
315 			return false;
316 		}
317 	}
318 
319 	return true;
320 }
321 
IsStringBool(const std::string & str)322 bool IsStringBool(const std::string &str)
323 {
324 	return str == "true" || str == "false";
325 }
326 
327 static std::map<unsigned, std::string> RomanConversionTable = {{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}};
328 
NumberToRomanNumeral(unsigned number)329 std::string NumberToRomanNumeral(unsigned number)
330 {
331 	std::string numeral;
332 
333 	for (std::map<unsigned, std::string>::const_reverse_iterator iterator = RomanConversionTable.rbegin(); iterator != RomanConversionTable.rend(); ++iterator) {
334 		while (number >= iterator->first) {
335 			numeral += iterator->second;
336 			number -= iterator->first;
337 		}
338 	}
339 
340 	return numeral;
341 }
342 
343 /**
344 **	@brief	Format a number using commas
345 **
346 **	@param	number	Number to be formatted
347 **
348 **	@return	The formatted number as a string
349 */
FormatNumber(const int number)350 std::string FormatNumber(const int number)
351 {
352 	std::string str;
353 	const char sep = ',';
354 	int n = abs(number);
355 
356 	int loop = 0;
357 	while (n > 0 || loop == 0) {
358 		if (loop > 0 && loop % 3 == 0) {
359 			str.insert(0, 1, sep);
360 		}
361 		const char c = n % 10 + 48;
362 		str.insert(0, 1, c);
363 		n /= 10;
364 		loop++;
365 	}
366 
367 	if (number < 0) {
368 		str.insert(0, 1, '-');
369 	}
370 
371 	return str;
372 }
373 
SnakeCaseToPascalCase(const std::string & str)374 std::string SnakeCaseToPascalCase(const std::string &str)
375 {
376 	if (str.empty()) {
377 		return str;
378 	}
379 
380 	std::string result(str);
381 
382 	result[0] = toupper(result[0]);
383 
384 	size_t pos = 0;
385 	while ((pos = result.find('_', pos)) != std::string::npos) {
386 		result.replace(pos, 1, "");
387 		if (pos < result.length()) {
388 			result[pos] = toupper(result[pos]);
389 		}
390 	}
391 
392 	return result;
393 }
394 
395 /*----------------------------------------------------------------------------
396 --  Getopt
397 ----------------------------------------------------------------------------*/
398 
399 #ifndef HAVE_GETOPT
400 
401 /**
402 **  Standard implementation of getopt(3).
403 **
404 **  One extension: If the first character of the optionsstring is a ':'
405 **  the error return for 'argument required' is a ':' not a '?'.
406 **  This makes it easier to differentiate between an 'illegal option' and
407 **  an 'argument required' error.
408 */
409 
410 #include <string.h>
411 
412 int opterr = 1;
413 int optind = 1;
414 int optopt;
415 char *optarg;
416 
getopt_err(const char * argv0,const char * str,char opt)417 static void getopt_err(const char *argv0, const char *str, char opt)
418 {
419 	if (opterr) {
420 		const char *x;
421 
422 		while ((x = strchr(argv0, '/'))) {
423 			argv0 = x + 1;
424 		}
425 
426 		fprintf(stderr, "%s%s%c\n", argv0, str, opt);
427 	}
428 }
429 
getopt(int argc,char * const * argv,const char * opts)430 int getopt(int argc, char *const *argv, const char *opts)
431 {
432 	static int sp = 1;
433 	register int c;
434 	register const char *cp;
435 
436 	optarg = nullptr;
437 
438 	if (sp == 1) {
439 		if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') {
440 			return EOF;
441 		} else if (!strcmp(argv[optind], "--")) {
442 			optind++;
443 			return EOF;
444 		}
445 	}
446 	optopt = c = argv[optind][sp];
447 	if (c == ':' || (cp = strchr(opts, c)) == nullptr) {
448 		getopt_err(argv[0], ": illegal option -", (char)c);
449 		cp = "xx"; /* make the next if false */
450 		c = '?';
451 	}
452 	if (*++cp == ':') {
453 		if (argv[optind][++sp] != '\0') {
454 			optarg = &argv[optind++][sp];
455 		} else if (++optind < argc) {
456 			optarg = argv[optind++];
457 		} else {
458 			getopt_err(argv[0], ": option requires an argument -", (char)c);
459 			c = (*opts == ':') ? ':' : '?';
460 		}
461 		sp = 1;
462 	} else if (argv[optind][++sp] == '\0') {
463 		optind++;
464 		sp = 1;
465 	}
466 	return c;
467 }
468 
469 #endif
470 
471 
472 /*----------------------------------------------------------------------------
473 --  Clipboard
474 ----------------------------------------------------------------------------*/
475 
476 /**
477 ** Paste text from the clipboard
478 */
GetClipboard(std::string & str)479 int GetClipboard(std::string &str)
480 {
481 #if defined(USE_WIN32) || defined(USE_X11)
482 	int i;
483 	unsigned char *clipboard;
484 #ifdef USE_WIN32
485 	HGLOBAL handle;
486 #elif defined(USE_X11)
487 	Display *display;
488 	Window window;
489 	Atom rettype;
490 	unsigned long nitem;
491 	unsigned long dummy;
492 	int retform;
493 	XEvent event;
494 #endif
495 
496 #ifdef USE_WIN32
497 	if (!IsClipboardFormatAvailable(CF_TEXT) || !OpenClipboard(nullptr)) {
498 		return -1;
499 	}
500 	handle = GetClipboardData(CF_TEXT);
501 	if (!handle) {
502 		CloseClipboard();
503 		return -1;
504 	}
505 	clipboard = (unsigned char *)GlobalLock(handle);
506 	if (!clipboard) {
507 		CloseClipboard();
508 		return -1;
509 	}
510 #elif defined(USE_X11)
511 	if (!(display = XOpenDisplay(nullptr))) {
512 		return -1;
513 	}
514 
515 	// Creates a non maped temporary X window to hold the selection
516 	if (!(window = XCreateSimpleWindow(display,
517 									   DefaultRootWindow(display), 0, 0, 1, 1, 0, 0, 0))) {
518 		XCloseDisplay(display);
519 		return -1;
520 	}
521 
522 	XConvertSelection(display, XA_PRIMARY, XA_STRING, XA_STRING,
523 					  window, CurrentTime);
524 
525 	XNextEvent(display, &event);
526 
527 	if (event.type != SelectionNotify || event.xselection.property != XA_STRING) {
528 		return -1;
529 	}
530 
531 	XGetWindowProperty(display, window, XA_STRING, 0, 1024, False,
532 					   XA_STRING, &rettype, &retform, &nitem, &dummy, &clipboard);
533 
534 	XDestroyWindow(display, window);
535 	XCloseDisplay(display);
536 
537 	if (rettype != XA_STRING || retform != 8) {
538 		if (clipboard != nullptr) {
539 			XFree(clipboard);
540 		}
541 		clipboard = nullptr;
542 	}
543 
544 	if (clipboard == nullptr) {
545 		return -1;
546 	}
547 #endif
548 	// Only allow ascii characters
549 	for (i = 0; clipboard[i] != '\0'; ++i) {
550 		if (clipboard[i] < 32 || clipboard[i] > 126) {
551 			return -1;
552 		}
553 	}
554 	str = (char *)clipboard;
555 #ifdef USE_WIN32
556 	GlobalUnlock(handle);
557 	CloseClipboard();
558 #elif defined(USE_X11)
559 	if (clipboard != nullptr) {
560 		XFree(clipboard);
561 	}
562 #endif
563 	return 0;
564 #else
565 	return -1;
566 #endif
567 }
568 
569 
570 /*----------------------------------------------------------------------------
571 --  UTF8
572 ----------------------------------------------------------------------------*/
573 
UTF8GetPrev(const std::string & text,int curpos)574 int UTF8GetPrev(const std::string &text, int curpos)
575 {
576 	--curpos;
577 	if (curpos < 0) {
578 		return curpos;
579 	}
580 	while (curpos >= 0) {
581 		if ((text[curpos] & 0xC0) != 0x80) {
582 			return curpos;
583 		}
584 		--curpos;
585 	}
586 	if (curpos < 0) {
587 		fprintf(stderr, "Invalid UTF8.\n");
588 	}
589 	return 0;
590 }
591 
UTF8GetNext(const std::string & text,int curpos)592 int UTF8GetNext(const std::string &text, int curpos)
593 {
594 	if (curpos == (int)text.size()) {
595 		return curpos + 1;
596 	}
597 	char c = text[curpos];
598 	if (!(c & 0x80)) {
599 		return curpos + 1;
600 	}
601 	if ((c & 0xE0) == 0xC0) {
602 		return curpos + 2;
603 	}
604 	if ((c & 0xF0) == 0xE0) {
605 		return curpos + 3;
606 	}
607 	fprintf(stderr, "Invalid UTF8.\n");
608 	return text.size();
609 }
610 
611 
612 /*----------------------------------------------------------------------------
613 --  others
614 ----------------------------------------------------------------------------*/
615 
PrintLocation(const char * file,int line,const char * funcName)616 void PrintLocation(const char *file, int line, const char *funcName)
617 {
618 	fprintf(stdout, "%s:%d: %s: ", file, line, funcName);
619 }
620 
AbortAt(const char * file,int line,const char * funcName,const char * conditionStr)621 void AbortAt(const char *file, int line, const char *funcName, const char *conditionStr)
622 {
623 	char buf[1024];
624 	snprintf(buf, 1024, "Assertion failed at %s:%d: %s: %s\n", file, line, funcName, conditionStr);
625 #ifdef USE_STACKTRACE
626 	throw stacktrace::stack_runtime_error((const char*)buf);
627 #else
628 	fprintf(stderr, "%s\n", buf);
629 #endif
630 	fflush(stdout);
631 	fflush(stderr);
632 	abort();
633 }
634 
PrintOnStdOut(const char * format,...)635 void PrintOnStdOut(const char *format, ...)
636 {
637 	va_list valist;
638 	va_start(valist, format);
639 	vprintf(format, valist);
640 	va_end(valist);
641 	fflush(stdout);
642 }
643 
644 //Wyrmgus start
645 #include "character.h" //for personal name generation
646 #include "iocompat.h" //for getting a file's last modified date
647 #include "iolib.h" //for getting a file's last modified date
648 #include "player.h" //for personal name generation
649 #include "unit/unittype.h" //for personal name generation
650 #include "upgrade/upgrade.h" //for personal name generation
651 
FindAndReplaceString(const std::string & text,const std::string & find,const std::string & replace)652 std::string FindAndReplaceString(const std::string &text, const std::string &find, const std::string &replace)
653 {
654 	std::string result(text);
655 
656 	size_t pos = 0;
657 	while ((pos = result.find(find, pos)) != std::string::npos) {
658 		result.replace(pos, find.length(), replace);
659 		pos += replace.length();
660 	}
661 
662 	return result;
663 }
664 
FindAndReplaceStringEnding(const std::string & text,const std::string & find,const std::string & replace)665 std::string FindAndReplaceStringEnding(const std::string &text, const std::string &find, const std::string &replace)
666 {
667 	std::string result(text);
668 
669 	size_t pos = text.find(find, text.length() - find.length());
670 	if (pos != std::string::npos) {
671 		result.replace(pos, find.length(), replace);
672 	}
673 
674 	return result;
675 }
676 
FindAndReplaceStringBeginning(const std::string & text,const std::string & find,const std::string & replace)677 std::string FindAndReplaceStringBeginning(const std::string &text, const std::string &find, const std::string &replace)
678 {
679 	std::string result(text);
680 
681 	size_t pos = text.find(find, 0);
682 	if (pos != std::string::npos && pos == 0) {
683 		result.replace(pos, find.length(), replace);
684 	}
685 
686 	return result;
687 }
688 
GetFileLastModified(const std::string & file_name)689 int GetFileLastModified(const std::string &file_name)
690 {
691 	std::string library_file_name = LibraryFileName(file_name.c_str());
692 
693 	struct stat tmp;
694 
695 	stat(library_file_name.c_str(), &tmp);
696 
697 	int date = tmp.st_mtime;
698 
699 	return date;
700 }
701 
TransliterateText(const std::string & text)702 std::string TransliterateText(const std::string &text) //convert special characters into ones more legible for English-speakers
703 {
704 	std::string result(text);
705 
706 	result = FindAndReplaceString(result, "Ā́", "A");
707 	result = FindAndReplaceString(result, "ā́", "a");
708 	result = FindAndReplaceString(result, "Ā", "A");
709 	result = FindAndReplaceString(result, "ā", "a");
710 	result = FindAndReplaceString(result, "Ấ", "A");
711 	result = FindAndReplaceString(result, "ấ", "a");
712 	result = FindAndReplaceString(result, "Ȧ́", "A");
713 	result = FindAndReplaceString(result, "ȧ́", "a");
714 	result = FindAndReplaceString(result, "Á", "A");
715 	result = FindAndReplaceString(result, "á", "a");
716 	result = FindAndReplaceString(result, "À", "A");
717 	result = FindAndReplaceString(result, "à", "a");
718 	result = FindAndReplaceString(result, "Ã", "A");
719 	result = FindAndReplaceString(result, "ã", "a");
720 	result = FindAndReplaceString(result, "Ä", "A");
721 	result = FindAndReplaceString(result, "ä", "a");
722 	result = FindAndReplaceString(result, "Ā", "A");
723 	result = FindAndReplaceString(result, "ā", "a");
724 	result = FindAndReplaceString(result, "Â", "A");
725 	result = FindAndReplaceString(result, "â", "a");
726 	result = FindAndReplaceString(result, "Ą", "A");
727 	result = FindAndReplaceString(result, "ą", "a");
728 	result = FindAndReplaceString(result, "ᶏ", "a");
729 	result = FindAndReplaceString(result, "Å", "A");
730 	result = FindAndReplaceString(result, "å", "a");
731 	result = FindAndReplaceString(result, "Ă", "A");
732 	result = FindAndReplaceString(result, "ă", "a");
733 	result = FindAndReplaceString(result, "Æ̂", "Ae");
734 	result = FindAndReplaceString(result, "æ̂", "ae");
735 	result = FindAndReplaceString(result, "Æ", "Ae");
736 	result = FindAndReplaceString(result, "æ", "ae");
737 	result = FindAndReplaceString(result, "Ǣ", "Ae");
738 	result = FindAndReplaceString(result, "ǣ", "ae");
739 	result = FindAndReplaceString(result, "Ǽ", "Ae");
740 	result = FindAndReplaceString(result, "ǽ", "ae");
741 	result = FindAndReplaceString(result, "Ƀ", "B");
742 	result = FindAndReplaceString(result, "ƀ", "b");
743 	result = FindAndReplaceString(result, "Č", "C");
744 	result = FindAndReplaceString(result, "č", "c");
745 	result = FindAndReplaceString(result, "Ð", "D");
746 	result = FindAndReplaceString(result, "ð", "d");
747 	result = FindAndReplaceString(result, "Ḍ", "D");
748 	result = FindAndReplaceString(result, "ḍ", "d");
749 	result = FindAndReplaceString(result, "Đ", "D");
750 	result = FindAndReplaceString(result, "đ", "d");
751 	result = FindAndReplaceString(result, "ẟ", "d"); //not the same character as "δ"
752 	result = FindAndReplaceString(result, "Ę̄", "E");
753 	result = FindAndReplaceString(result, "ę̄", "e");
754 	result = FindAndReplaceString(result, "Ḗ", "E");
755 	result = FindAndReplaceString(result, "ḗ", "e");
756 	result = FindAndReplaceString(result, "Ė́", "E");
757 	result = FindAndReplaceString(result, "ė́", "e");
758 	result = FindAndReplaceString(result, "Ë̃", "E");
759 	result = FindAndReplaceString(result, "ë̃", "e");
760 	result = FindAndReplaceString(result, "Ë̂", "E");
761 	result = FindAndReplaceString(result, "ë̂", "e");
762 	result = FindAndReplaceString(result, "É", "E");
763 	result = FindAndReplaceString(result, "é", "e");
764 	result = FindAndReplaceString(result, "È", "E");
765 	result = FindAndReplaceString(result, "è", "e");
766 	result = FindAndReplaceString(result, "Ē", "E");
767 	result = FindAndReplaceString(result, "ē", "e");
768 	result = FindAndReplaceString(result, "Ê", "E");
769 	result = FindAndReplaceString(result, "ê", "e");
770 	result = FindAndReplaceString(result, "Ě", "E");
771 	result = FindAndReplaceString(result, "ě", "e");
772 	result = FindAndReplaceString(result, "Ė", "E");
773 	result = FindAndReplaceString(result, "ė", "e");
774 	result = FindAndReplaceString(result, "Ë", "E");
775 	result = FindAndReplaceString(result, "ë", "e");
776 	result = FindAndReplaceString(result, "Ę", "E");
777 	result = FindAndReplaceString(result, "ę", "e");
778 	result = FindAndReplaceString(result, "Ĕ", "E");
779 	result = FindAndReplaceString(result, "ĕ", "e");
780 	result = FindAndReplaceString(result, "Ə", "E");
781 	result = FindAndReplaceString(result, "ə", "e");
782 	result = FindAndReplaceString(result, "Ǝ", "E");
783 	result = FindAndReplaceString(result, "ǝ", "e");
784 	result = FindAndReplaceString(result, "ϵ", "e");
785 	result = FindAndReplaceString(result, "Ĝ", "G");
786 	result = FindAndReplaceString(result, "ĝ", "g");
787 	result = FindAndReplaceString(result, "Ī̆", "I");
788 	result = FindAndReplaceString(result, "ī̆", "i");
789 	result = FindAndReplaceString(result, "Î́", "I");
790 	result = FindAndReplaceString(result, "î́", "i");
791 	result = FindAndReplaceString(result, "Ī́", "I");
792 	result = FindAndReplaceString(result, "ī́", "i");
793 	result = FindAndReplaceString(result, "Ī", "I");
794 	result = FindAndReplaceString(result, "ī", "i");
795 	result = FindAndReplaceString(result, "I̊", "I");
796 	result = FindAndReplaceString(result, "i̊", "i");
797 	result = FindAndReplaceString(result, "Í", "I");
798 	result = FindAndReplaceString(result, "í", "i");
799 	result = FindAndReplaceString(result, "Ì", "I");
800 	result = FindAndReplaceString(result, "ì", "i");
801 	result = FindAndReplaceString(result, "Ī", "I");
802 	result = FindAndReplaceString(result, "ī", "i");
803 	result = FindAndReplaceString(result, "Î", "I");
804 	result = FindAndReplaceString(result, "î", "i");
805 	result = FindAndReplaceString(result, "Ĭ", "I");
806 	result = FindAndReplaceString(result, "ĭ", "i");
807 	result = FindAndReplaceString(result, "Ĩ", "I");
808 	result = FindAndReplaceString(result, "ĩ", "i");
809 	result = FindAndReplaceString(result, "Ḱ", "K");
810 	result = FindAndReplaceString(result, "ḱ", "k");
811 	result = FindAndReplaceString(result, "L̥", "L");
812 	result = FindAndReplaceString(result, "l̥", "l");
813 	result = FindAndReplaceString(result, "Ɫ", "L");
814 	result = FindAndReplaceString(result, "ɫ", "l");
815 	result = FindAndReplaceString(result, "Ň", "N");
816 	result = FindAndReplaceString(result, "ň", "n");
817 	result = FindAndReplaceString(result, "Ṇ", "N");
818 	result = FindAndReplaceString(result, "ṇ", "n");
819 	result = FindAndReplaceString(result, "Ṅ", "N");
820 	result = FindAndReplaceString(result, "ṅ", "n");
821 	result = FindAndReplaceString(result, "Ṓ", "O");
822 	result = FindAndReplaceString(result, "ṓ", "o");
823 	result = FindAndReplaceString(result, "Ŏ", "O");
824 	result = FindAndReplaceString(result, "ŏ", "o");
825 	result = FindAndReplaceString(result, "Ø", "Ö"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
826 	result = FindAndReplaceString(result, "ø", "ö"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
827 	result = FindAndReplaceString(result, "Ǫ", "O"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
828 	result = FindAndReplaceString(result, "ǫ", "o"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
829 	result = FindAndReplaceString(result, "Ö", "O");
830 	result = FindAndReplaceString(result, "ö", "o");
831 	result = FindAndReplaceString(result, "Ó", "O");
832 	result = FindAndReplaceString(result, "ó", "o");
833 	result = FindAndReplaceString(result, "Ò", "O");
834 	result = FindAndReplaceString(result, "ò", "o");
835 	result = FindAndReplaceString(result, "Ō", "O");
836 	result = FindAndReplaceString(result, "ō", "o");
837 	result = FindAndReplaceString(result, "Ô", "O");
838 	result = FindAndReplaceString(result, "ô", "o");
839 	result = FindAndReplaceString(result, "Ǒ", "O");
840 	result = FindAndReplaceString(result, "ǒ", "o");
841 	result = FindAndReplaceString(result, "Œ", "Oe");
842 	result = FindAndReplaceString(result, "œ", "oe");
843 	result = FindAndReplaceString(result, "Ṛ́", "R");
844 	result = FindAndReplaceString(result, "ṛ́", "r");
845 	result = FindAndReplaceString(result, "Ŗ́", "R");
846 	result = FindAndReplaceString(result, "ŗ́", "r");
847 	result = FindAndReplaceString(result, "R̄", "R");
848 	result = FindAndReplaceString(result, "r̄", "r");
849 	result = FindAndReplaceString(result, "Ř", "R");
850 	result = FindAndReplaceString(result, "ř", "r");
851 	result = FindAndReplaceString(result, "Ṛ", "R");
852 	result = FindAndReplaceString(result, "ṛ", "r");
853 	result = FindAndReplaceString(result, "Ŕ", "R");
854 	result = FindAndReplaceString(result, "ŕ", "r");
855 	result = FindAndReplaceString(result, "Ṙ", "R");
856 	result = FindAndReplaceString(result, "ṙ", "r");
857 	result = FindAndReplaceString(result, "Š", "S");
858 	result = FindAndReplaceString(result, "š", "s");
859 	result = FindAndReplaceString(result, "Ș", "S");
860 	result = FindAndReplaceString(result, "ș", "s");
861 	result = FindAndReplaceString(result, "Ś", "S");
862 	result = FindAndReplaceString(result, "ś", "s");
863 	result = FindAndReplaceString(result, "ß", "ss"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
864 	result = FindAndReplaceString(result, "Ṭ", "T");
865 	result = FindAndReplaceString(result, "ṭ", "t");
866 	result = FindAndReplaceString(result, "Ț", "T");
867 	result = FindAndReplaceString(result, "ț", "t");
868 	result = FindAndReplaceString(result, "ÞÞ", "Þ"); //replace double thorns with a single one
869 	result = FindAndReplaceString(result, "þþ", "þ"); //replace double thorns with a single one
870 	result = FindAndReplaceString(result, "Þ", "Th"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
871 	result = FindAndReplaceString(result, "þ", "th"); //Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
872 	result = FindAndReplaceString(result, "Ū́", "U");
873 	result = FindAndReplaceString(result, "ū́", "u");
874 	result = FindAndReplaceString(result, "Ů̃", "U");
875 	result = FindAndReplaceString(result, "ů̃", "u");
876 	result = FindAndReplaceString(result, "Ü", "U");
877 	result = FindAndReplaceString(result, "ü", "u");
878 	result = FindAndReplaceString(result, "Ú", "U");
879 	result = FindAndReplaceString(result, "ú", "u");
880 	result = FindAndReplaceString(result, "Ù", "U");
881 	result = FindAndReplaceString(result, "ù", "u");
882 	result = FindAndReplaceString(result, "Ū", "U");
883 	result = FindAndReplaceString(result, "ū", "u");
884 	result = FindAndReplaceString(result, "Û", "U");
885 	result = FindAndReplaceString(result, "û", "u");
886 	result = FindAndReplaceString(result, "Ŭ", "U");
887 	result = FindAndReplaceString(result, "ŭ", "u");
888 	result = FindAndReplaceString(result, "Ů", "U");
889 	result = FindAndReplaceString(result, "ů", "u");
890 	result = FindAndReplaceString(result, "Ũ", "U");
891 	result = FindAndReplaceString(result, "ũ", "u");
892 	result = FindAndReplaceString(result, "Ṷ", "U");
893 	result = FindAndReplaceString(result, "ṷ", "u");
894 	result = FindAndReplaceString(result, "ʷ", "w");
895 	result = FindAndReplaceString(result, "Ȳ", "Y");
896 	result = FindAndReplaceString(result, "ȳ", "y");
897 	result = FindAndReplaceString(result, "Ŷ", "Y");
898 	result = FindAndReplaceString(result, "ŷ", "y");
899 	result = FindAndReplaceString(result, "Ỹ", "Y");
900 	result = FindAndReplaceString(result, "ỹ", "y");
901 	result = FindAndReplaceString(result, "Ý", "Y");
902 	result = FindAndReplaceString(result, "ý", "y");
903 	result = FindAndReplaceString(result, "Ž", "Z");
904 	result = FindAndReplaceString(result, "ž", "z");
905 	result = FindAndReplaceString(result, "Z̨", "Z");
906 	result = FindAndReplaceString(result, "z̨", "z");
907 	result = FindAndReplaceString(result, "Ż", "Z");
908 	result = FindAndReplaceString(result, "ż", "z");
909 
910 	result = FindAndReplaceString(result, "ʔ", "'"); // glottal stop
911 
912 	//replace endings in -r after consonants (which happens in the nominative for Old Norse); Source: Henry Adams Bellows (transl.), "The Poetic Edda", p. xxviii.
913 	result = FindAndReplaceStringEnding(result, "dr", "d");
914 	result = FindAndReplaceString(result, "dr ", "d ");
915 	result = FindAndReplaceStringEnding(result, "fr", "f");
916 	result = FindAndReplaceString(result, "fr ", "f ");
917 	result = FindAndReplaceStringEnding(result, "gr", "g");
918 	result = FindAndReplaceString(result, "gr ", "g ");
919 	result = FindAndReplaceStringEnding(result, "kr", "k");
920 	result = FindAndReplaceString(result, "kr ", "k ");
921 	result = FindAndReplaceStringEnding(result, "mr", "m");
922 	result = FindAndReplaceString(result, "mr ", "m ");
923 	result = FindAndReplaceStringEnding(result, "nr", "n");
924 	result = FindAndReplaceString(result, "nr ", "n ");
925 	result = FindAndReplaceStringEnding(result, "pr", "p");
926 	result = FindAndReplaceString(result, "pr ", "p ");
927 	result = FindAndReplaceStringEnding(result, "rr", "r");
928 	result = FindAndReplaceString(result, "rr ", "r ");
929 	result = FindAndReplaceStringEnding(result, "tr", "t");
930 	result = FindAndReplaceString(result, "tr ", "t ");
931 
932 	//Greek characters
933 	result = FindAndReplaceString(result, "Ἄ", "A");
934 	result = FindAndReplaceString(result, "ἄ", "a");
935 	result = FindAndReplaceString(result, "Ά", "A");
936 	result = FindAndReplaceString(result, "ά", "a");
937 	result = FindAndReplaceString(result, "Ἀ", "A");
938 	result = FindAndReplaceString(result, "ἀ", "a");
939 	result = FindAndReplaceString(result, "Α", "A");
940 	result = FindAndReplaceString(result, "α", "a");
941 	result = FindAndReplaceString(result, "Β", "B");
942 	result = FindAndReplaceString(result, "β", "b");
943 	result = FindAndReplaceString(result, "Χ", "Ch");
944 	result = FindAndReplaceString(result, "χ", "ch");
945 	result = FindAndReplaceString(result, "Δ", "D");
946 	result = FindAndReplaceString(result, "δ", "d");
947 	result = FindAndReplaceString(result, "Ἑ", "E");
948 	result = FindAndReplaceString(result, "ἑ", "e");
949 	result = FindAndReplaceString(result, "Ἔ", "E");
950 	result = FindAndReplaceString(result, "ἔ", "e");
951 	result = FindAndReplaceString(result, "Έ", "E");
952 	result = FindAndReplaceString(result, "έ", "e");
953 	result = FindAndReplaceString(result, "Ε", "E");
954 	result = FindAndReplaceString(result, "ε", "e");
955 	result = FindAndReplaceString(result, "Η", "E");
956 	result = FindAndReplaceString(result, "η", "e");
957 	result = FindAndReplaceString(result, "Γ", "G");
958 	result = FindAndReplaceString(result, "γ", "g");
959 	result = FindAndReplaceString(result, "Ῑ́", "I");
960 	result = FindAndReplaceString(result, "ῑ́", "i");
961 	result = FindAndReplaceString(result, "Ί", "I");
962 	result = FindAndReplaceString(result, "ί", "i");
963 	result = FindAndReplaceString(result, "ῖ", "i");
964 	result = FindAndReplaceString(result, "Ι", "I");
965 	result = FindAndReplaceString(result, "ι", "i");
966 	result = FindAndReplaceString(result, "Ή", "I");
967 	result = FindAndReplaceString(result, "ή", "i");
968 	result = FindAndReplaceString(result, "Κ", "K");
969 	result = FindAndReplaceString(result, "κ", "k");
970 	result = FindAndReplaceString(result, "Λ", "L");
971 	result = FindAndReplaceString(result, "λ", "l");
972 	result = FindAndReplaceString(result, "Μ", "M");
973 	result = FindAndReplaceString(result, "μ", "m");
974 	result = FindAndReplaceString(result, "Ν", "N");
975 	result = FindAndReplaceString(result, "ν", "n");
976 	result = FindAndReplaceString(result, "Ὄ", "O");
977 	result = FindAndReplaceString(result, "ὄ", "o");
978 	result = FindAndReplaceString(result, "Ὅ", "O");
979 	result = FindAndReplaceString(result, "ὅ", "o");
980 	result = FindAndReplaceString(result, "Ό", "O");
981 	result = FindAndReplaceString(result, "ό", "o");
982 	result = FindAndReplaceString(result, "Ὀ", "O");
983 	result = FindAndReplaceString(result, "ὀ", "o");
984 	result = FindAndReplaceString(result, "Ὁ", "O");
985 	result = FindAndReplaceString(result, "ὁ", "o");
986 	result = FindAndReplaceString(result, "Ο", "O");
987 	result = FindAndReplaceString(result, "ο", "o");
988 	result = FindAndReplaceString(result, "Ώ", "O");
989 	result = FindAndReplaceString(result, "ώ", "o");
990 	result = FindAndReplaceString(result, "Ω", "O");
991 	result = FindAndReplaceString(result, "ω", "o");
992 	result = FindAndReplaceString(result, "Π", "P");
993 	result = FindAndReplaceString(result, "π", "p");
994 	result = FindAndReplaceString(result, "Φ", "Ph");
995 	result = FindAndReplaceString(result, "φ", "ph");
996 	result = FindAndReplaceString(result, "Ψ", "Ps");
997 	result = FindAndReplaceString(result, "ψ", "ps");
998 	result = FindAndReplaceString(result, "Ρ", "R");
999 	result = FindAndReplaceString(result, "ρ", "r");
1000 	result = FindAndReplaceString(result, "Σ", "S");
1001 	result = FindAndReplaceString(result, "σ", "s");
1002 	result = FindAndReplaceString(result, "ς", "s");
1003 	result = FindAndReplaceString(result, "Τ", "T");
1004 	result = FindAndReplaceString(result, "τ", "t");
1005 	result = FindAndReplaceString(result, "Θ", "Th");
1006 	result = FindAndReplaceString(result, "θ", "th");
1007 	result = FindAndReplaceString(result, "Ξ", "X");
1008 	result = FindAndReplaceString(result, "ξ", "x");
1009 	result = FindAndReplaceString(result, "Ύ", "Y");
1010 	result = FindAndReplaceString(result, "ύ", "y");
1011 	result = FindAndReplaceString(result, "Ὑ", "Y");
1012 	result = FindAndReplaceString(result, "ὑ", "y");
1013 	result = FindAndReplaceString(result, "Υ", "Y");
1014 	result = FindAndReplaceString(result, "υ", "y");
1015 	result = FindAndReplaceString(result, "Ζ", "Z");
1016 	result = FindAndReplaceString(result, "ζ", "z");
1017 
1018 	//remove large clusters of the same letters
1019 	result = FindAndReplaceString(result, "nnn", "nn");
1020 
1021 	return result;
1022 }
1023 
CapitalizeString(const std::string & text)1024 std::string CapitalizeString(const std::string &text)
1025 {
1026 	if (text.empty()) {
1027 		return text;
1028 	}
1029 
1030 	std::string result(text);
1031 
1032 	result[0] = toupper(result[0]);
1033 
1034 	// replace special characters which may not have been uppered with the previous method
1035 	result = FindAndReplaceStringBeginning(result, "ā", "Ā");
1036 	result = FindAndReplaceStringBeginning(result, "â", "Â");
1037 	result = FindAndReplaceStringBeginning(result, "æ", "Æ");
1038 	result = FindAndReplaceStringBeginning(result, "ǣ", "Ǣ");
1039 	result = FindAndReplaceStringBeginning(result, "ǽ", "Ǽ");
1040 	result = FindAndReplaceStringBeginning(result, "ð", "Ð");
1041 	result = FindAndReplaceStringBeginning(result, "ḍ", "Ḍ");
1042 	result = FindAndReplaceStringBeginning(result, "ē", "Ē");
1043 	result = FindAndReplaceStringBeginning(result, "ê", "Ê");
1044 	result = FindAndReplaceStringBeginning(result, "ě", "Ě");
1045 	result = FindAndReplaceStringBeginning(result, "ī", "Ī");
1046 	result = FindAndReplaceStringBeginning(result, "î", "Î");
1047 	result = FindAndReplaceStringBeginning(result, "ĭ", "Ĭ");
1048 	result = FindAndReplaceStringBeginning(result, "ī̆", "Ī̆");
1049 	result = FindAndReplaceStringBeginning(result, "ō", "Ō");
1050 	result = FindAndReplaceStringBeginning(result, "ô", "Ô");
1051 	result = FindAndReplaceStringBeginning(result, "ø", "Ø");
1052 	result = FindAndReplaceStringBeginning(result, "ǫ", "Ǫ");
1053 	result = FindAndReplaceStringBeginning(result, "ș", "Ș");
1054 	result = FindAndReplaceStringBeginning(result, "ț", "Ț");
1055 	result = FindAndReplaceStringBeginning(result, "þ", "Þ");
1056 	result = FindAndReplaceStringBeginning(result, "ū", "Ū");
1057 	result = FindAndReplaceStringBeginning(result, "û", "Û");
1058 	result = FindAndReplaceStringBeginning(result, "ŭ", "Ŭ");
1059 	result = FindAndReplaceStringBeginning(result, "ȳ", "Ȳ");
1060 	result = FindAndReplaceStringBeginning(result, "ž", "Ž");
1061 
1062 	//Greek characters
1063 	result = FindAndReplaceStringBeginning(result, "α", "Α");
1064 	result = FindAndReplaceStringBeginning(result, "χ", "Χ");
1065 	result = FindAndReplaceStringBeginning(result, "έ", "Έ");
1066 	result = FindAndReplaceStringBeginning(result, "ι", "Ι");
1067 	result = FindAndReplaceStringBeginning(result, "μ", "Μ");
1068 	result = FindAndReplaceStringBeginning(result, "ν", "Ν");
1069 	result = FindAndReplaceStringBeginning(result, "ο", "Ο");
1070 	result = FindAndReplaceStringBeginning(result, "ό", "Ό");
1071 	result = FindAndReplaceStringBeginning(result, "σ", "Σ");
1072 	result = FindAndReplaceStringBeginning(result, "θ", "Θ");
1073 	result = FindAndReplaceStringBeginning(result, "ύ", "Ύ");
1074 
1075 	return result;
1076 }
1077 
DecapitalizeString(const std::string & text)1078 std::string DecapitalizeString(const std::string &text)
1079 {
1080 	if (text.empty()) {
1081 		return text;
1082 	}
1083 
1084 	std::string result(text);
1085 
1086 	result[0] = tolower(result[0]);
1087 
1088 	// replace special characters which may not have been lowered with the previous method
1089 	result = FindAndReplaceStringBeginning(result, "Ā", "ā");
1090 	result = FindAndReplaceStringBeginning(result, "Â", "â");
1091 	result = FindAndReplaceStringBeginning(result, "Æ", "æ");
1092 	result = FindAndReplaceStringBeginning(result, "Ǣ", "ǣ");
1093 	result = FindAndReplaceStringBeginning(result, "Ǽ", "ǽ");
1094 	result = FindAndReplaceStringBeginning(result, "Ç", "ç");
1095 	result = FindAndReplaceStringBeginning(result, "Ð", "ð");
1096 	result = FindAndReplaceStringBeginning(result, "Ḍ", "ḍ");
1097 	result = FindAndReplaceStringBeginning(result, "Ē", "ē");
1098 	result = FindAndReplaceStringBeginning(result, "Ê", "ê");
1099 	result = FindAndReplaceStringBeginning(result, "Ě", "ě");
1100 	result = FindAndReplaceStringBeginning(result, "Ī", "ī");
1101 	result = FindAndReplaceStringBeginning(result, "Î", "î");
1102 	result = FindAndReplaceStringBeginning(result, "Ĭ", "ĭ");
1103 	result = FindAndReplaceStringBeginning(result, "Ī̆", "ī̆");
1104 	result = FindAndReplaceStringBeginning(result, "Ō", "ō");
1105 	result = FindAndReplaceStringBeginning(result, "Ô", "ô");
1106 	result = FindAndReplaceStringBeginning(result, "Ø", "ø");
1107 	result = FindAndReplaceStringBeginning(result, "Ǫ", "ǫ");
1108 	result = FindAndReplaceStringBeginning(result, "Þ", "þ");
1109 	result = FindAndReplaceStringBeginning(result, "Ū", "ū");
1110 	result = FindAndReplaceStringBeginning(result, "Û", "û");
1111 	result = FindAndReplaceStringBeginning(result, "Ŭ", "ŭ");
1112 	result = FindAndReplaceStringBeginning(result, "Ȳ", "ȳ");
1113 	result = FindAndReplaceStringBeginning(result, "Ž", "ž");
1114 
1115 	//Greek characters
1116 	result = FindAndReplaceStringBeginning(result, "Α", "α");
1117 	result = FindAndReplaceStringBeginning(result, "Χ", "χ");
1118 	result = FindAndReplaceStringBeginning(result, "Έ", "έ");
1119 	result = FindAndReplaceStringBeginning(result, "Ι", "ι");
1120 	result = FindAndReplaceStringBeginning(result, "Μ", "μ");
1121 	result = FindAndReplaceStringBeginning(result, "Ν", "ν");
1122 	result = FindAndReplaceStringBeginning(result, "Ο", "ο");
1123 	result = FindAndReplaceStringBeginning(result, "Ό", "ό");
1124 	result = FindAndReplaceStringBeginning(result, "Σ", "σ");
1125 	result = FindAndReplaceStringBeginning(result, "Θ", "θ");
1126 	result = FindAndReplaceStringBeginning(result, "Ύ", "ύ");
1127 
1128 	return result;
1129 }
1130 
FullyCapitalizeString(const std::string & text)1131 std::string FullyCapitalizeString(const std::string &text)
1132 {
1133 	std::string result(text);
1134 
1135 	result = CapitalizeString(result);
1136 
1137 	size_t pos = 0;
1138 	while ((pos = result.find(" ", pos)) != std::string::npos) {
1139 		std::string replace = CapitalizeString(result.substr(pos + 1, 1));
1140 		result.replace(pos + 1, 1, replace);
1141 		pos += replace.length();
1142 	}
1143 
1144 	return result;
1145 }
1146 
FullyDecapitalizeString(const std::string & text)1147 std::string FullyDecapitalizeString(const std::string &text)
1148 {
1149 	std::string result(text);
1150 
1151 	result = DecapitalizeString(result);
1152 
1153 	size_t pos = 0;
1154 	while ((pos = result.find(" ", pos)) != std::string::npos) {
1155 		std::string replace = DecapitalizeString(result.substr(pos + 1, 1));
1156 		result.replace(pos + 1, 1, replace);
1157 		pos += replace.length();
1158 	}
1159 
1160 	return result;
1161 }
1162 
GetPluralForm(const std::string & name)1163 std::string GetPluralForm(const std::string &name)
1164 {
1165 	if (name == "Einherjar" || name == "Wose") {
1166 		return name; // no difference
1167 	}
1168 
1169 	std::string result(name);
1170 
1171 	if (result != "Monkey") {
1172 		result = FindAndReplaceStringEnding(result, "y", "ie");
1173 	}
1174 
1175 	if (result.substr(result.size() - 2, 2) == "os" || result.substr(result.size() - 2, 2) == "us" || result.substr(result.size() - 1, 1) == "x") {
1176 		result += "es";
1177 	}
1178 
1179 	if (result.substr(result.size() - 1, 1) != "s") {
1180 		result += "s";
1181 	}
1182 
1183 	result = FindAndReplaceString(result, "Barracks", "Barrackses");
1184 	result = FindAndReplaceString(result, "Dwarfs", "Dwarves");
1185 	result = FindAndReplaceString(result, "Elfs", "Elves");
1186 	result = FindAndReplaceString(result, "Ostrichs", "Ostriches");
1187 	result = FindAndReplaceString(result, "Thiefs", "Thieves");
1188 	result = FindAndReplaceString(result, "Wolfs", "Wolves");
1189 	if (result != "Humans") {
1190 		result = FindAndReplaceStringEnding(result, "mans", "men");
1191 	}
1192 
1193 	return result;
1194 }
1195 
IdentToName(const std::string & text)1196 std::string IdentToName(const std::string &text)
1197 {
1198 	std::string result(text);
1199 
1200 	result = FindAndReplaceString(result, "-", " ");
1201 	result = FullyCapitalizeString(result);
1202 
1203 	return result;
1204 }
1205 
NameToIdent(const std::string & text)1206 std::string NameToIdent(const std::string &text)
1207 {
1208 	std::string result(text);
1209 
1210 	result = FullyDecapitalizeString(result);
1211 	result = FindAndReplaceString(result, " ", "-");
1212 	result = FindAndReplaceString(result, "'", "");
1213 
1214 	return result;
1215 }
1216 
SeparateCapitalizedStringElements(const std::string & text)1217 std::string SeparateCapitalizedStringElements(const std::string &text)
1218 {
1219 	std::string result(text);
1220 
1221 	for (size_t pos = 1; pos < result.length(); ++pos) {
1222 		if (isupper(result[pos])) {
1223 			result.replace(pos, 1, " " + result.substr(pos, 1));
1224 			pos += 1;
1225 		}
1226 	}
1227 	return result;
1228 }
1229 
GeneratePersonalName(const std::string & unit_type_ident)1230 std::string GeneratePersonalName(const std::string &unit_type_ident)
1231 {
1232 	int unit_type_id = UnitTypeIdByIdent(unit_type_ident);
1233 	return UnitTypes[unit_type_id]->GeneratePersonalName(nullptr, UnitTypes[unit_type_id]->DefaultStat.Variables[GENDER_INDEX].Value);
1234 }
1235 //Wyrmgus end
1236