1 /*
2    Copyright (C) 2006 - 2018 by Mark de Wever <koraq@xs4all.nl>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 /**
16  *  @file
17  *  Routines for terrain-conversion.
18  */
19 
20 #define GETTEXT_DOMAIN "wesnoth-lib"
21 
22 #include "gettext.hpp"
23 #include "lexical_cast.hpp"
24 #include "log.hpp"
25 #include "terrain/translation.hpp"
26 #include "serialization/string_utils.hpp"
27 #include "wml_exception.hpp"
28 
29 
30 #define ERR_G LOG_STREAM(err, lg::general())
31 #define WRN_G LOG_STREAM(warn, lg::general())
32 
33 namespace t_translation {
34 
max_map_size()35 	int max_map_size() {
36 		return 1000; //TODO make this overridable by the user without having to rebuild
37 	}
38 
39 /***************************************************************************************/
40 // forward declaration of internal functions
41 
42 	// The low level convertors,
43 	// These function are the ones which know about the internal format.
44 	// All other functions are unaware of the internal format.
45 
46 	/**
47 	 * Get the mask for a single layer.
48 	 *
49 	 * @param terrain   1 layer of a terrain, might have a wildcard.
50 	 *
51 	 * @return          Mask for that layer.
52 	 */
53 	static ter_layer get_layer_mask_(ter_layer terrain); //inlined
54 
55 	/**
56 	 * Gets a mask for a terrain, this mask is used for wildcard matching.
57 	 *
58 	 * @param terrain   The terrain which might have a wildcard.
59 	 *
60 	 * @return          The mask for this terrain.
61 	 */
62 	static terrain_code get_mask_(const terrain_code& terrain);
63 
64 	static ter_layer string_to_layer_(const char* begin, const char* end);
65 
66 	/**
67 	 * Converts a string to a layer.
68 	 *
69 	 * @param str       The terrain string to convert, but needs to be
70 	 *                  sanitized so no spaces and only the terrain to convert.
71 	 *
72 	 * @return          The converted layer.
73 	 */
string_to_layer_(const std::string & str)74 	static ter_layer string_to_layer_(const std::string& str)
75 	{
76 		return string_to_layer_(str.c_str(), str.c_str() + str.size());
77 	}
78 
79 	/**
80 	 * Converts a terrain string to a number.
81 	 * @param str               The terrain string with an optional number.
82 	 * @param start_position    Returns the start_position, the caller should
83 	 *                          set it on -1 and it's only changed it there is
84 	 *                          a starting position found.
85 	 * @param filler            If the terrain has only 1 layer then the filler
86 	 *                          will be used as the second layer.
87 	 *
88 	 * @return                  The terrain code found in the string if no
89 	 *                          valid terrain is found VOID will be returned.
90 	 */
91 	static terrain_code string_to_number_(std::string str, std::string& start_position, const ter_layer filler);
92 	static terrain_code string_to_number_(const std::string& str, const ter_layer filler = NO_LAYER);
93 
94 	/**
95 	 * Converts a terrain number to a string
96 	 *
97 	 * @param terrain               The terrain number to convert.
98 	 * @param start_position        The starting position, if smaller than 0
99 	 *                              it's ignored else it's written.
100 	 *
101 	 * @return                      The converted string, if no starting
102 	 *                              position given it's padded to 4 chars else
103 	 *                              padded to 7 chars.
104 	 */
105 	static std::string number_to_string_(terrain_code terrain, const std::string& start_position = "");
106 
107 	/**
108 	 * Converts a terrain string to a number for the builder.
109 	 * The translation rules differ from the normal conversion rules
110 	 *
111 	 * @param str   The terrain string.
112 	 *
113 	 * @return      Number for the builder map.
114 	 */
115 	static terrain_code string_to_builder_number_(std::string str);
116 
117 /***************************************************************************************/
118 
119 const terrain_code OFF_MAP_USER = string_to_number_("_off^_usr");
120 
121 const terrain_code VOID_TERRAIN = string_to_number_("_s");
122 const terrain_code FOGGED = string_to_number_("_f");
123 
124 const terrain_code HUMAN_CASTLE = string_to_number_("Ch");
125 const terrain_code HUMAN_KEEP = string_to_number_("Kh");
126 const terrain_code SHALLOW_WATER = string_to_number_("Ww");
127 const terrain_code DEEP_WATER = string_to_number_("Wo");
128 const terrain_code GRASS_LAND = string_to_number_("Gg");
129 const terrain_code FOREST = string_to_number_("Gg^Ff");
130 const terrain_code MOUNTAIN = string_to_number_("Mm");
131 const terrain_code HILL = string_to_number_("Hh");
132 
133 const terrain_code CAVE_WALL = string_to_number_("Xu");
134 const terrain_code CAVE = string_to_number_("Uu");
135 const terrain_code UNDERGROUND_VILLAGE = string_to_number_("Uu^Vu");
136 const terrain_code DWARVEN_CASTLE = string_to_number_("Cud");
137 const terrain_code DWARVEN_KEEP = string_to_number_("Kud");
138 
139 const terrain_code PLUS = string_to_number_("+");
140 const terrain_code MINUS = string_to_number_("-");
141 const terrain_code NOT = string_to_number_("!");
142 const terrain_code STAR = string_to_number_("*");
143 const terrain_code BASE = string_to_number_("_bas");
144 
145 const ter_match ALL_OFF_MAP("_off^_usr,*^_fme");
146 const ter_match ALL_FORESTS("F*,*^F*");
147 const ter_match ALL_HILLS("!,*^V*,!,H*");
148 const ter_match ALL_MOUNTAINS("!,*^V*,!,M*"); //excluding impassable mountains
149 const ter_match ALL_SWAMPS("!,*^V*,*^B*,!,S*"); //excluding swamp villages and bridges
150 
151 /***************************************************************************************/
152 
terrain_code(const std::string & b,ter_layer o)153 terrain_code::terrain_code(const std::string& b, ter_layer o) :
154 	base(string_to_layer_(b)), overlay(o)
155 {}
156 
terrain_code(const std::string & b,const std::string & o)157 terrain_code::terrain_code(const std::string& b, const std::string& o) :
158 	base(string_to_layer_(b)), overlay(string_to_layer_(o))
159 {}
160 
ter_match()161 ter_match::ter_match() :
162 	terrain(),
163 	mask(),
164 	masked_terrain(),
165 	has_wildcard(false),
166 	is_empty(true)
167 {}
168 
ter_match(const std::string & str,const ter_layer filler)169 ter_match::ter_match(const std::string& str, const ter_layer filler) :
170 	terrain(t_translation::read_list(str, filler)),
171 	mask(),
172 	masked_terrain(),
173 	has_wildcard(t_translation::has_wildcard(terrain)),
174 	is_empty(terrain.empty())
175 
176 {
177 	mask.resize(terrain.size());
178 	masked_terrain.resize(terrain.size());
179 
180 	for(size_t i = 0; i < terrain.size(); i++) {
181 		mask[i] = t_translation::get_mask_(terrain[i]);
182 		masked_terrain[i] = mask[i] & terrain[i];
183 	}
184 }
185 
ter_match(const terrain_code & tcode)186 ter_match::ter_match(const terrain_code& tcode):
187 	terrain(ter_list(1, tcode)),
188 	mask(),
189 	masked_terrain(),
190 	has_wildcard(t_translation::has_wildcard(terrain)),
191 	is_empty(terrain.empty())
192 {
193 	mask.resize(terrain.size());
194 	masked_terrain.resize(terrain.size());
195 
196 	for(size_t i = 0; i < terrain.size(); i++) {
197 		mask[i] = t_translation::get_mask_(terrain[i]);
198 		masked_terrain[i] = mask[i] & terrain[i];
199 	}
200 }
201 
read_terrain_code(const std::string & str,const ter_layer filler)202 terrain_code read_terrain_code(const std::string& str, const ter_layer filler)
203 {
204 	return string_to_number_(str, filler);
205 }
206 
write_terrain_code(const terrain_code & tcode)207 std::string write_terrain_code(const terrain_code& tcode)
208 {
209 	return number_to_string_(tcode);
210 }
211 
read_list(utils::string_view str,const ter_layer filler)212 ter_list read_list(utils::string_view str, const ter_layer filler)
213 {
214 	// Handle an empty string
215 	ter_list result;
216 
217 	if(str.empty()) {
218 		return result;
219 	}
220 
221 	size_t offset = 0;
222 	while(offset < str.length()) {
223 
224 		// Get a terrain chunk
225 		const std::string separators = ",";
226 		const size_t pos_separator = str.find_first_of(separators, offset);
227 		const std::string terrain = str.substr(offset, pos_separator - offset).to_string();
228 
229 		// Process the chunk
230 		const terrain_code tile = string_to_number_(terrain, filler);
231 
232 		// Add the resulting terrain number
233 		result.push_back(tile);
234 
235 		// Evaluate the separator
236 		if(pos_separator == std::string::npos) {
237 			offset =  str.length();
238 		} else {
239 			offset = pos_separator + 1;
240 		}
241 	}
242 
243 	return result;
244 }
245 
write_list(const ter_list & list)246 std::string write_list(const ter_list& list)
247 {
248 	std::stringstream result;
249 
250 	ter_list::const_iterator itor = list.begin();
251 	for( ; itor != list.end(); ++itor) {
252 		if(itor == list.begin()) {
253 			result << number_to_string_(*itor);
254 		} else {
255 			result << ", " << number_to_string_(*itor);
256 		}
257 	}
258 
259 	return result.str();
260 }
261 
get_map_size(const char * begin,const char * end)262 static std::pair<int, int> get_map_size(const char* begin, const char* end)
263 {
264 	int w = 1;
265 	int h = 0;
266 	for (const char* it = begin; it != end;) {
267 		int cur_w = 1;
268 		++h;
269 
270 
271 		for (;it != end && (*it != '\n' && *it != '\r'); ++it) {
272 			if (*it == ',') {
273 				++cur_w;
274 			}
275 		}
276 		w = std::max(w, cur_w);
277 
278 		while (it != end && (*it == '\n' || *it == '\r')) {
279 			++it;
280 		}
281 
282 	}
283 	return{ w, h };
284 }
285 
read_game_map(const std::string & str,starting_positions & starting_positions,coordinate border_offset)286 ter_map read_game_map(const std::string& str, starting_positions& starting_positions, coordinate border_offset)
287 {
288 	size_t offset = 0;
289 	int x = 0, y = 0, width = 0;
290 
291 	// Skip the leading newlines
292 	while(offset < str.length() && utils::isnewline(str[offset])) {
293 		++offset;
294 	}
295 
296 	// Did we get an empty map?
297 	if((offset + 1) >= str.length()) {
298 		return ter_map();
299 	}
300 
301 	auto map_size = get_map_size(&str[offset], str.c_str() + str.size());
302 	ter_map result(map_size.first, map_size.second);
303 
304 	while(offset < str.length()) {
305 
306 		// Get a terrain chunk
307 		const std::string separators = ",\n\r";
308 		const size_t pos_separator = str.find_first_of(separators, offset);
309 		const std::string terrain = str.substr(offset, pos_separator - offset);
310 
311 		// Process the chunk
312 		std::string starting_position;
313 		// The gamemap never has a wildcard
314 		const terrain_code tile = string_to_number_(terrain, starting_position, NO_LAYER);
315 
316 		// Add to the resulting starting position
317 		if(!starting_position.empty()) {
318 			if (starting_positions.left.find(starting_position) != starting_positions.left.end()) {
319 				WRN_G << "Starting position " << starting_position << " is redefined." << std::endl;
320 			}
321 			starting_positions.insert(starting_positions::value_type(starting_position, coordinate(x - border_offset.x, y - border_offset.y)));
322 		}
323 
324 		if(result.w <= x || result.h <= y) {
325 			throw error("Map not a rectangle.");
326 		}
327 
328 		// Add the resulting terrain number
329 		result.get(x, y) = tile;
330 
331 		// Evaluate the separator
332 		if(pos_separator == std::string::npos || utils::isnewline(str[pos_separator])) {
333 			// the first line we set the with the other lines we check the width
334 			if(y == 0) {
335 				// x contains the offset in the map
336 				width = x + 1;
337 			} else {
338 				if((x + 1) != width ) {
339 					ERR_G << "Map not a rectangle error occurred at line offset " << y << " position offset " << x << std::endl;
340 					throw error("Map not a rectangle.");
341 				}
342 				if (y > max_map_size()) {
343 					ERR_G << "Map size exceeds limit (y > " << max_map_size() << ")" << std::endl;
344 					throw error("Map height limit exceeded.");
345 				}
346 			}
347 
348 			// Prepare next iteration
349 			++y;
350 			x = 0;
351 
352 			// Avoid in infinite loop if the last line ends without an EOL
353 			if(pos_separator == std::string::npos) {
354 				offset = str.length();
355 
356 			} else {
357 
358 				offset = pos_separator + 1;
359 				// Skip the following newlines
360 				while(offset < str.length() && utils::isnewline(str[offset])) {
361 					++offset;
362 				}
363 			}
364 
365 		} else {
366 			++x;
367 			offset = pos_separator + 1;
368 			if (x > max_map_size()) {
369 				ERR_G << "Map size exceeds limit (x > " << max_map_size() << ")" << std::endl;
370 				throw error("Map width limit exceeded.");
371 			}
372 		}
373 
374 	}
375 
376 	if(x != 0 && (x + 1) != width) {
377 		ERR_G << "Map not a rectangle error occurred at the end" << std::endl;
378 		throw error("Map not a rectangle.");
379 	}
380 
381 	return result;
382 }
383 
write_game_map(const ter_map & map,const starting_positions & starting_positions,coordinate border_offset)384 std::string write_game_map(const ter_map& map, const starting_positions& starting_positions, coordinate border_offset)
385 {
386 	std::stringstream str;
387 
388 	for(int y = 0; y < map.h; ++y) {
389 		for(int x = 0; x < map.w; ++x) {
390 
391 			// If the current location is a starting position,
392 			// it needs to be added to the terrain.
393 			// After it's found it can't be found again,
394 			// so the location is removed from the map.
395 			auto itor = starting_positions.right.find(coordinate(x - border_offset.x, y - border_offset.y));
396 			std::string starting_position;
397 			if (itor != starting_positions.right.end()) {
398 				starting_position = itor->second;
399 			}
400 			// Add the separator
401 			if(x != 0) {
402 				str << ", ";
403 			}
404 			str << number_to_string_(map[x][y], starting_position);
405 		}
406 
407 		if (y < map.h -1)
408 			str << "\n";
409 	}
410 
411 	return str.str();
412 }
413 
terrain_matches(const terrain_code & src,const terrain_code & dest)414 bool terrain_matches(const terrain_code& src, const terrain_code& dest)
415 {
416 	return terrain_matches(src, ter_list(1, dest));
417 }
418 
terrain_matches(const terrain_code & src,const ter_list & dest)419 bool terrain_matches(const terrain_code& src, const ter_list& dest)
420 {
421 	// NOTE we impose some code duplication.
422 	// It could have been rewritten to get a match structure
423 	// and then call the version with the match structure.
424 	// IMO that's some extra overhead to this function
425 	// which is not required. Hence the two versions
426 	if(dest.empty()) {
427 		return false;
428 	}
429 
430 #if 0
431 	std::cerr << std::hex << "src = " << src.base << "^" << src.overlay << "\t"
432 		<< src_mask.base << "^" << src_mask.overlay << "\t"
433 		<< masked_src.base << "^" << masked_src.overlay << "\t"
434 		<< src_has_wildcard << "\n";
435 #endif
436 
437 	bool result = true;
438 	ter_list::const_iterator itor = dest.begin();
439 
440 	// Try to match the terrains if matched jump out of the loop.
441 	for(; itor != dest.end(); ++itor) {
442 
443 		// Match wildcard
444 		if(*itor == STAR) {
445 			return result;
446 		}
447 
448 		// Match inverse symbol
449 		if(*itor == NOT) {
450 			result = !result;
451 			continue;
452 		}
453 
454 		// Full match
455 		if(src == *itor) {
456 			return result;
457 		}
458 
459 		// Does the destination wildcard match
460 		const terrain_code dest_mask = get_mask_(*itor);
461 		const terrain_code masked_dest = (*itor & dest_mask);
462 		const bool dest_has_wildcard = has_wildcard(*itor);
463 #if 0
464 		std::cerr << std::hex << "dest= "
465 			<< itor->base << "^" << itor->overlay  << "\t"
466 			<< dest_mask.base << "^" << dest_mask.overlay << "\t"
467 			<< masked_dest.base << "^" << masked_dest.overlay << "\t"
468 			<< dest_has_wildcard << "\n";
469 #endif
470 		if(dest_has_wildcard &&
471 				(src.base & dest_mask.base) == masked_dest.base &&
472 				(src.overlay & dest_mask.overlay) == masked_dest.overlay) {
473 			return result;
474 		}
475 
476 /* Test code */ /*
477 		if(src_has_wildcard && dest_has_wildcard && (
478 				(
479 					get_layer_mask_(itor->base) != NO_LAYER &&
480 					get_layer_mask_(src.overlay) != NO_LAYER &&
481 					(src.base & dest_mask.base) == masked_dest.base &&
482 					(itor->overlay & src_mask.overlay) == masked_src.overlay
483 				) || (
484 					get_layer_mask_(itor->overlay) != NO_LAYER &&
485 					get_layer_mask_(src.base) != NO_LAYER &&
486 					(src.overlay & dest_mask.overlay) == masked_dest.overlay &&
487 					(itor->base & src_mask.base) == masked_src.base
488 				))) {
489 
490 			return result;
491 		}
492 */
493 	}
494 
495 	// No match, return the inverse of the result
496 	return !result;
497 }
498 
499 // This routine is used for the terrain building,
500 // so it's one of the delays while loading a map.
501 // This routine is optimized a bit at the loss of readability.
terrain_matches(const terrain_code & src,const ter_match & dest)502 bool terrain_matches(const terrain_code& src, const ter_match& dest)
503 {
504 	if(dest.is_empty) {
505 		return false;
506 	}
507 
508 	bool result = true;
509 
510 	// Try to match the terrains if matched jump out of the loop.
511 	// We loop on the dest.terrain since the iterator is faster than operator[].
512 	// The i holds the value for operator[].
513 	// Since dest.mask and dest.masked_terrain need to be in sync,
514 	// they are less often looked up, so no iterator for them.
515 	size_t i = 0;
516 	ter_list::const_iterator end = dest.terrain.end();
517 	for(ter_list::const_iterator terrain_itor = dest.terrain.begin();
518 			terrain_itor != end;
519 			++i, ++terrain_itor) {
520 
521 		// Match wildcard
522 		if(*terrain_itor == STAR) {
523 			return result;
524 		}
525 
526 		// Match inverse symbol
527 		if(*terrain_itor == NOT) {
528 			result = !result;
529 			continue;
530 		}
531 
532 		// Full match
533 		if(*terrain_itor == src) {
534 			return result;
535 		}
536 
537 		// Does the destination wildcard match
538 		if(dest.has_wildcard &&
539 				(src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
540 				(src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay) {
541 			return result;
542 		}
543 
544 /* Test code */ /*
545 		if(src_has_wildcard && has_wildcard(*terrain_itor) && (
546 				(
547 					get_layer_mask_(terrain_itor->base) != NO_LAYER &&
548 					get_layer_mask_(src.overlay) != NO_LAYER &&
549 					(src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
550 					(terrain_itor->overlay & src_mask.overlay) == masked_src.overlay
551 				) || (
552 					get_layer_mask_(terrain_itor->overlay) != NO_LAYER &&
553 					get_layer_mask_(src.base) != NO_LAYER &&
554 					(src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay &&
555 					(terrain_itor->base & src_mask.base) == masked_src.base
556 				))) {
557 
558 			return result;
559 		}
560 */
561 	}
562 
563 	// No match, return the inverse of the result
564 	return !result;
565 }
566 
has_wildcard(const terrain_code & tcode)567 bool has_wildcard(const terrain_code& tcode)
568 {
569 	if(tcode.overlay == NO_LAYER) {
570 		return get_layer_mask_(tcode.base) != NO_LAYER;
571 	} else {
572 		return get_layer_mask_(tcode.base) != NO_LAYER || get_layer_mask_(tcode.overlay) != NO_LAYER;
573 	}
574 }
575 
has_wildcard(const ter_list & list)576 bool has_wildcard(const ter_list& list)
577 {
578 	if(list.empty()) {
579 		return false;
580 	}
581 
582 	// Test all items for a wildcard
583 	ter_list::const_iterator itor = list.begin();
584 	for(; itor != list.end(); ++itor) {
585 		if(has_wildcard(*itor)) {
586 			return true;
587 		}
588 	}
589 
590 	// No wildcard found
591 	return false;
592 }
593 
read_builder_map(const std::string & str)594 ter_map read_builder_map(const std::string& str)
595 {
596 	boost::multi_array<int, sizeof(ter_map)> a;
597 
598 	size_t offset = 0;
599 	// Skip the leading newlines
600 	while(offset < str.length() && utils::isnewline(str[offset])) {
601 		++offset;
602 	}
603 	// Did we get an empty map?
604 	if((offset + 1) >= str.length()) {
605 		return ter_map();
606 	}
607 
608 	auto map_size = get_map_size(&str[offset], str.c_str() + str.size());
609 	ter_map result(map_size.second, map_size.first, terrain_code(t_translation::TB_DOT, ter_layer()));
610 
611 	int x = 0, y = 0;
612 	while(offset < str.length()) {
613 
614 		// Get a terrain chunk
615 		const std::string separators = ",\n\r";
616 		const size_t pos_separator = str.find_first_of(separators, offset);
617 		std::string terrain = "";
618 		// Make sure we didn't hit an empty chunk
619 		// which is allowed
620 		if(pos_separator != offset) {
621 			terrain = str.substr(offset, pos_separator - offset);
622 		}
623 
624 		// Process the chunk
625 		const terrain_code tile = string_to_builder_number_(terrain);
626 
627 		// Make space for the new item
628 		if (result.h <= x || result.w <= y) {
629 			throw error("Map not a rectangle.");
630 		}
631 
632 		// Add the resulting terrain number,
633 		result.get(y, x) = tile;
634 
635 		// evaluate the separator
636 		if(pos_separator == std::string::npos) {
637 			// Probably not required to change the value,
638 			// but be sure the case should be handled at least.
639 			// I'm not sure how it is defined in the standard,
640 			// but here it's defined at max u32 which with +1 gives 0
641 			// and make a nice infinite loop.
642 			offset = str.length();
643 		} else if(utils::isnewline(str[pos_separator])) {
644 			// Prepare next iteration
645 			++y;
646 			x = 0;
647 
648 			offset =  pos_separator + 1;
649 			// Skip the following newlines
650 			while(offset < str.length() && utils::isnewline(str[offset])) {
651 				++offset;
652 			}
653 
654 		} else {
655 			++x;
656 			offset = pos_separator + 1;
657 		}
658 
659 	}
660 
661 	return result;
662 }
663 
664 /***************************************************************************************/
665 // Internal
666 
get_layer_mask_(ter_layer terrain)667 inline ter_layer get_layer_mask_(ter_layer terrain)
668 {
669 	// Test for the star 0x2A in every position
670 	// and return the appropriate mask
671 /*
672  *	This is what the code intents to do, but in order to gain some more
673  *	speed it's changed to the code below, which does the same but faster.
674  *	This routine is used often in the builder and the speedup is noticeable. */
675 	if((terrain & 0xFF000000) == 0x2A000000) return 0x00000000;
676 	if((terrain & 0x00FF0000) == 0x002A0000) return 0xFF000000;
677 	if((terrain & 0x0000FF00) == 0x00002A00) return 0xFFFF0000;
678 	if((terrain & 0x000000FF) == 0x0000002A) return 0xFFFFFF00;
679 
680 /*
681 	uint8_t *ptr = (uint8_t *) &terrain;
682 
683 	if(ptr[3] == 0x2A) return 0x00000000;
684 	if(ptr[2] == 0x2A) return 0xFF000000;
685 	if(ptr[1] == 0x2A) return 0xFFFF0000;
686 	if(ptr[0] == 0x2A) return 0xFFFFFF00;
687 */
688 	// no star found return the default
689 	return 0xFFFFFFFF;
690 }
691 
get_mask_(const terrain_code & terrain)692 static terrain_code get_mask_(const terrain_code& terrain)
693 {
694 	if(terrain.overlay == NO_LAYER) {
695 		return terrain_code(get_layer_mask_(terrain.base), 0xFFFFFFFF);
696 	} else {
697 		return terrain_code(get_layer_mask_(terrain.base), get_layer_mask_(terrain.overlay));
698 	}
699 }
700 
string_to_layer_(const char * begin,const char * end)701 static ter_layer string_to_layer_(const char* begin, const char* end)
702 {
703 	size_t size = end - begin;
704 	if (begin == end) {
705 		return NO_LAYER;
706 	}
707 	ter_layer result = 0;
708 
709 	// Validate the string
710 	VALIDATE(size <= 4, _("A terrain with a string with more "
711 		"than 4 characters has been found, the affected terrain is :") + std::string(begin, end));
712 
713 	// The conversion to int puts the first char
714 	// in the highest part of the number.
715 	// This will make the wildcard matching
716 	// later on a bit easier.
717 	for(size_t i = 0; i < 4; ++i) {
718 		const unsigned char c = (i < size) ? begin[i] : 0;
719 
720 		// Clearing the lower area is a nop on i == 0
721 		// so no need for if statement
722 		result <<= 8;
723 
724 		// Add the result
725 		result += c;
726 	}
727 
728 	return result;
729 }
730 
string_to_number_(const std::string & str,const ter_layer filler)731 static terrain_code string_to_number_(const std::string& str, const ter_layer filler) {
732 	std::string dummy;
733 	return string_to_number_(str, dummy, filler);
734 }
735 
string_to_number_(std::string str,std::string & start_position,const ter_layer filler)736 static terrain_code string_to_number_(std::string str, std::string& start_position, const ter_layer filler)
737 {
738 	const char* c_str = str.c_str();
739 	terrain_code result;
740 
741 	// Strip the spaces around us
742 	const std::string& whitespace = " \t";
743 	size_t begin = str.find_first_not_of(whitespace);
744 	size_t end = str.find_last_not_of(whitespace) + 1;
745 	if(begin == std::string::npos) {
746 		return result;
747 	}
748 
749 	// Split if we have 1 space inside
750 	size_t offset = str.find(' ', begin);
751 	if(offset < end) {
752 		start_position = str.substr(begin, offset - begin);
753 		begin = offset + 1;
754 	}
755 
756 	offset = str.find('^', 0);
757 	if(offset !=  std::string::npos) {
758 		result = terrain_code { string_to_layer_(c_str + begin, c_str + offset), string_to_layer_(c_str + offset + 1, c_str + end) };
759 	} else {
760 		result = terrain_code { string_to_layer_(c_str + begin, c_str + end), filler };
761 
762 		// Ugly hack
763 		if(filler == WILDCARD && (result.base == NOT.base ||
764 				result.base == STAR.base)) {
765 
766 			result.overlay = NO_LAYER;
767 		}
768 	}
769 
770 	return result;
771 }
772 
number_to_string_(terrain_code terrain,const std::string & start_position)773 static std::string number_to_string_(terrain_code terrain, const std::string& start_position)
774 {
775 	std::string result = "";
776 
777 	// Insert the start position
778 	if(!start_position.empty()) {
779 		result = start_position + " ";
780 	}
781 
782 	/*
783 	 * The initialization of tcode is done to make gcc-4.7 happy. Otherwise it
784 	 * some uninitialized fields might be used. Its analysis are wrong, but
785 	 * Initialize to keep it happy.
786 	 */
787 	unsigned char tcode[9] {0};
788 	// Insert the terrain tcode
789 	tcode[0] = ((terrain.base & 0xFF000000) >> 24);
790 	tcode[1] = ((terrain.base & 0x00FF0000) >> 16);
791 	tcode[2] = ((terrain.base & 0x0000FF00) >> 8);
792 	tcode[3] =  (terrain.base & 0x000000FF);
793 
794 	if(terrain.overlay != NO_LAYER) {
795 		tcode[4] = '^'; //the layer separator
796 		tcode[5] = ((terrain.overlay & 0xFF000000) >> 24);
797 		tcode[6] = ((terrain.overlay & 0x00FF0000) >> 16);
798 		tcode[7] = ((terrain.overlay & 0x0000FF00) >> 8);
799 		tcode[8] =  (terrain.overlay & 0x000000FF);
800 	} else {
801 		// If no second layer, the second layer won't be written,
802 		// so no need to initialize that part of the array
803 		tcode[4] = 0;
804 	}
805 
806 	for(int i = 0; i < 9; ++i) {
807 		if(tcode[i] != 0 && tcode[i] != 0xFF) {
808 			result += tcode[i];
809 		}
810 		if(i == 4 && tcode[i] == 0) {
811 			// no layer, stop
812 			break;
813 		}
814 	}
815 
816 	return result;
817 }
818 
string_to_builder_number_(std::string str)819 static terrain_code string_to_builder_number_(std::string str)
820 {
821 	// Strip the spaces around us
822 	const std::string& whitespace = " \t";
823 	str.erase(0, str.find_first_not_of(whitespace));
824 	if(! str.empty()) {
825 		str.erase(str.find_last_not_of(whitespace) + 1);
826 	}
827 
828 	// Empty string is allowed here, so handle it
829 	if(str.empty()) {
830 		return terrain_code();
831 	}
832 
833 	const int number = lexical_cast_default(str, -1);
834 	if(number == -1) {
835 		// At this point we have a single char
836 		// which should be interpreted by the
837 		// map builder, so return this number
838 		return terrain_code(str[0] << 24, 0);
839 	} else {
840 		return terrain_code(0, number);
841 	}
842 }
843 
844 } // end namespace t_translation
845 
846 #if 0
847 // small helper rule to test the matching rules
848 // building rule
849 // make terrain_translation.o &&  g++ terrain_translation.o libwesnoth-core.a -lSDL -o terrain_translation
850 int main(int argc, char** argv)
851 {
852 	if(argc > 1) {
853 
854 		if(std::string(argv[1]) == "match" && argc == 4) {
855 			t_translation::terrain_code src = t_translation::read_terrain_code(std::string(argv[2]));
856 
857 			t_translation::ter_list dest = t_translation::read_list(std::string(argv[3]));
858 
859 			if(t_translation::terrain_matches(src, dest)) {
860 				std::cout << "Match\n" ;
861 			} else {
862 				std::cout << "No match\n";
863 			}
864 		}
865 	}
866 }
867 
868 #endif
869