The Battle for Wesnoth  1.15.2+dev
translation.cpp
Go to the documentation of this file.
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"
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 
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  */
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_(utils::string_view str, std::string& start_position, const ter_layer filler);
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 
120 
121 /**
122  * VOID_TERRAIN is used for shrouded hexes. It's also used by terrain_type's
123  * default constructor, but that's only used as the sentinel value for when
124  * terrain_type_data::get_terrain_info() is asked for an unknown terrain code.
125  */
128 
137 
143 
149 
150 const ter_match ALL_OFF_MAP("_off^_usr,*^_fme");
151 const ter_match ALL_FORESTS("F*,*^F*");
152 const ter_match ALL_HILLS("!,*^V*,!,H*");
153 const ter_match ALL_MOUNTAINS("!,*^V*,!,M*"); //excluding impassable mountains
154 const ter_match ALL_SWAMPS("!,*^V*,*^B*,!,S*"); //excluding swamp villages and bridges
155 
156 /***************************************************************************************/
157 
158 terrain_code::terrain_code(const std::string& b, ter_layer o) :
159  base(string_to_layer_(b)), overlay(o)
160 {}
161 
162 terrain_code::terrain_code(const std::string& b, const std::string& o) :
163  base(string_to_layer_(b)), overlay(string_to_layer_(o))
164 {}
165 
167  terrain(),
168  mask(),
169  masked_terrain(),
170  has_wildcard(false),
171  is_empty(true)
172 {}
173 
175  terrain(t_translation::read_list(str, filler)),
176  mask(),
177  masked_terrain(),
179  is_empty(terrain.empty())
180 
181 {
182  mask.resize(terrain.size());
183  masked_terrain.resize(terrain.size());
184 
185  for(std::size_t i = 0; i < terrain.size(); i++) {
186  mask[i] = t_translation::get_mask_(terrain[i]);
187  masked_terrain[i] = mask[i] & terrain[i];
188  }
189 }
190 
192  terrain(ter_list(1, tcode)),
193  mask(),
194  masked_terrain(),
196  is_empty(terrain.empty())
197 {
198  mask.resize(terrain.size());
199  masked_terrain.resize(terrain.size());
200 
201  for(std::size_t i = 0; i < terrain.size(); i++) {
202  mask[i] = t_translation::get_mask_(terrain[i]);
203  masked_terrain[i] = mask[i] & terrain[i];
204  }
205 }
206 
208 {
209  return string_to_number_(str, filler);
210 }
211 
212 std::string write_terrain_code(const terrain_code& tcode)
213 {
214  return number_to_string_(tcode);
215 }
216 
218 {
219  // Handle an empty string
220  ter_list result;
221 
222  if(str.empty()) {
223  return result;
224  }
225 
226  std::size_t offset = 0;
227  while(offset < str.length()) {
228 
229  // Get a terrain chunk
230  const std::string separators = ",";
231  const size_t pos_separator = str.find_first_of(separators, offset);
232  utils::string_view terrain = str.substr(offset, pos_separator - offset);
233 
234  // Process the chunk
235  const terrain_code tile = string_to_number_(terrain, filler);
236 
237  // Add the resulting terrain number
238  result.push_back(tile);
239 
240  // Evaluate the separator
241  if(pos_separator == utils::string_view::npos) {
242  offset = str.length();
243  } else {
244  offset = pos_separator + 1;
245  }
246  }
247 
248  return result;
249 }
250 
251 std::string write_list(const ter_list& list)
252 {
253  std::stringstream result;
254 
255  ter_list::const_iterator itor = list.begin();
256  for( ; itor != list.end(); ++itor) {
257  if(itor == list.begin()) {
258  result << number_to_string_(*itor);
259  } else {
260  result << ", " << number_to_string_(*itor);
261  }
262  }
263 
264  return result.str();
265 }
266 
267 static std::pair<int, int> get_map_size(const char* begin, const char* end)
268 {
269  int w = 1;
270  int h = 0;
271  for (const char* it = begin; it != end;) {
272  int cur_w = 1;
273  ++h;
274 
275 
276  for (;it != end && (*it != '\n' && *it != '\r'); ++it) {
277  if (*it == ',') {
278  ++cur_w;
279  }
280  }
281  w = std::max(w, cur_w);
282 
283  while (it != end && (*it == '\n' || *it == '\r')) {
284  ++it;
285  }
286 
287  }
288  return{ w, h };
289 }
290 
292 {
293  std::size_t offset = 0;
294  int x = 0, y = 0, width = 0;
295 
296  // Skip the leading newlines
297  while(!str.empty() && utils::isnewline(str.front())) {
298  str.remove_prefix(1);
299  }
300 
301  // Did we get an empty map?
302  if(str.length() <= 1) {
303  return ter_map();
304  }
305 
306  auto map_size = get_map_size(&str[0], &str[0] + str.size());
307  ter_map result(map_size.first, map_size.second);
308 
309  while(offset < str.length()) {
310 
311  // Get a terrain chunk
312  const std::string separators = ",\n\r";
313  const std::size_t pos_separator = str.find_first_of(separators, offset);
314  utils::string_view terrain = str.substr(offset, pos_separator - offset);
315 
316  // Process the chunk
317  std::string starting_position;
318  // The gamemap never has a wildcard
319  const terrain_code tile = string_to_number_(terrain, starting_position, NO_LAYER);
320 
321  // Add to the resulting starting position
322  if(!starting_position.empty()) {
323  if (starting_positions.left.find(starting_position) != starting_positions.left.end()) {
324  WRN_G << "Starting position " << starting_position << " is redefined." << std::endl;
325  }
326  starting_positions.insert(starting_positions::value_type(starting_position, coordinate(x - border_offset.x, y - border_offset.y)));
327  }
328 
329  if(result.w <= x || result.h <= y) {
330  throw error("Map not a rectangle.");
331  }
332 
333  // Add the resulting terrain number
334  result.get(x, y) = tile;
335 
336  // Evaluate the separator
337  if(pos_separator == std::string::npos || utils::isnewline(str[pos_separator])) {
338  // the first line we set the with the other lines we check the width
339  if(y == 0) {
340  // x contains the offset in the map
341  width = x + 1;
342  } else {
343  if((x + 1) != width ) {
344  ERR_G << "Map not a rectangle error occurred at line offset " << y << " position offset " << x << std::endl;
345  throw error("Map not a rectangle.");
346  }
347  if (y > max_map_size()) {
348  ERR_G << "Map size exceeds limit (y > " << max_map_size() << ")" << std::endl;
349  throw error("Map height limit exceeded.");
350  }
351  }
352 
353  // Prepare next iteration
354  ++y;
355  x = 0;
356 
357  // Avoid in infinite loop if the last line ends without an EOL
358  if(pos_separator == std::string::npos) {
359  offset = str.length();
360 
361  } else {
362 
363  offset = pos_separator + 1;
364  // Skip the following newlines
365  while(offset < str.length() && utils::isnewline(str[offset])) {
366  ++offset;
367  }
368  }
369 
370  } else {
371  ++x;
372  offset = pos_separator + 1;
373  if (x > max_map_size()) {
374  ERR_G << "Map size exceeds limit (x > " << max_map_size() << ")" << std::endl;
375  throw error("Map width limit exceeded.");
376  }
377  }
378 
379  }
380 
381  if(x != 0 && (x + 1) != width) {
382  ERR_G << "Map not a rectangle error occurred at the end" << std::endl;
383  throw error("Map not a rectangle.");
384  }
385 
386  return result;
387 }
388 
389 std::string write_game_map(const ter_map& map, const starting_positions& starting_positions, coordinate border_offset)
390 {
391  std::stringstream str;
392 
393  for(int y = 0; y < map.h; ++y) {
394  for(int x = 0; x < map.w; ++x) {
395 
396  // If the current location is a starting position,
397  // it needs to be added to the terrain.
398  // After it's found it can't be found again,
399  // so the location is removed from the map.
400  auto itor = starting_positions.right.find(coordinate(x - border_offset.x, y - border_offset.y));
401  std::string starting_position;
402  if (itor != starting_positions.right.end()) {
403  starting_position = itor->second;
404  }
405  // Add the separator
406  if(x != 0) {
407  str << ", ";
408  }
409  str << number_to_string_(map[x][y], starting_position);
410  }
411 
412  if (y < map.h -1)
413  str << "\n";
414  }
415 
416  return str.str();
417 }
418 
419 bool terrain_matches(const terrain_code& src, const terrain_code& dest)
420 {
421  return terrain_matches(src, ter_list(1, dest));
422 }
423 
424 bool terrain_matches(const terrain_code& src, const ter_list& dest)
425 {
426  // NOTE we impose some code duplication.
427  // It could have been rewritten to get a match structure
428  // and then call the version with the match structure.
429  // IMO that's some extra overhead to this function
430  // which is not required. Hence the two versions
431  if(dest.empty()) {
432  return false;
433  }
434 
435 #if 0
436  std::cerr << std::hex << "src = " << src.base << "^" << src.overlay << "\t"
437  << src_mask.base << "^" << src_mask.overlay << "\t"
438  << masked_src.base << "^" << masked_src.overlay << "\t"
439  << src_has_wildcard << "\n";
440 #endif
441 
442  bool result = true;
443  ter_list::const_iterator itor = dest.begin();
444 
445  // Try to match the terrains if matched jump out of the loop.
446  for(; itor != dest.end(); ++itor) {
447 
448  // Match wildcard
449  if(*itor == STAR) {
450  return result;
451  }
452 
453  // Match inverse symbol
454  if(*itor == NOT) {
455  result = !result;
456  continue;
457  }
458 
459  // Full match
460  if(src == *itor) {
461  return result;
462  }
463 
464  // Does the destination wildcard match
465  const terrain_code dest_mask = get_mask_(*itor);
466  const terrain_code masked_dest = (*itor & dest_mask);
467  const bool dest_has_wildcard = has_wildcard(*itor);
468 #if 0
469  std::cerr << std::hex << "dest= "
470  << itor->base << "^" << itor->overlay << "\t"
471  << dest_mask.base << "^" << dest_mask.overlay << "\t"
472  << masked_dest.base << "^" << masked_dest.overlay << "\t"
473  << dest_has_wildcard << "\n";
474 #endif
475  if(dest_has_wildcard &&
476  (src.base & dest_mask.base) == masked_dest.base &&
477  (src.overlay & dest_mask.overlay) == masked_dest.overlay) {
478  return result;
479  }
480 
481 /* Test code */ /*
482  if(src_has_wildcard && dest_has_wildcard && (
483  (
484  get_layer_mask_(itor->base) != NO_LAYER &&
485  get_layer_mask_(src.overlay) != NO_LAYER &&
486  (src.base & dest_mask.base) == masked_dest.base &&
487  (itor->overlay & src_mask.overlay) == masked_src.overlay
488  ) || (
489  get_layer_mask_(itor->overlay) != NO_LAYER &&
490  get_layer_mask_(src.base) != NO_LAYER &&
491  (src.overlay & dest_mask.overlay) == masked_dest.overlay &&
492  (itor->base & src_mask.base) == masked_src.base
493  ))) {
494 
495  return result;
496  }
497 */
498  }
499 
500  // No match, return the inverse of the result
501  return !result;
502 }
503 
504 // This routine is used for the terrain building,
505 // so it's one of the delays while loading a map.
506 // This routine is optimized a bit at the loss of readability.
507 bool terrain_matches(const terrain_code& src, const ter_match& dest)
508 {
509  if(dest.is_empty) {
510  return false;
511  }
512 
513  bool result = true;
514 
515  // Try to match the terrains if matched jump out of the loop.
516  // We loop on the dest.terrain since the iterator is faster than operator[].
517  // The i holds the value for operator[].
518  // Since dest.mask and dest.masked_terrain need to be in sync,
519  // they are less often looked up, so no iterator for them.
520  std::size_t i = 0;
521  ter_list::const_iterator end = dest.terrain.end();
522  for(ter_list::const_iterator terrain_itor = dest.terrain.begin();
523  terrain_itor != end;
524  ++i, ++terrain_itor) {
525 
526  // Match wildcard
527  if(*terrain_itor == STAR) {
528  return result;
529  }
530 
531  // Match inverse symbol
532  if(*terrain_itor == NOT) {
533  result = !result;
534  continue;
535  }
536 
537  // Full match
538  if(*terrain_itor == src) {
539  return result;
540  }
541 
542  // Does the destination wildcard match
543  if(dest.has_wildcard &&
544  (src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
545  (src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay) {
546  return result;
547  }
548 
549 /* Test code */ /*
550  if(src_has_wildcard && has_wildcard(*terrain_itor) && (
551  (
552  get_layer_mask_(terrain_itor->base) != NO_LAYER &&
553  get_layer_mask_(src.overlay) != NO_LAYER &&
554  (src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
555  (terrain_itor->overlay & src_mask.overlay) == masked_src.overlay
556  ) || (
557  get_layer_mask_(terrain_itor->overlay) != NO_LAYER &&
558  get_layer_mask_(src.base) != NO_LAYER &&
559  (src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay &&
560  (terrain_itor->base & src_mask.base) == masked_src.base
561  ))) {
562 
563  return result;
564  }
565 */
566  }
567 
568  // No match, return the inverse of the result
569  return !result;
570 }
571 
572 bool has_wildcard(const terrain_code& tcode)
573 {
574  if(tcode.overlay == NO_LAYER) {
575  return get_layer_mask_(tcode.base) != NO_LAYER;
576  } else {
577  return get_layer_mask_(tcode.base) != NO_LAYER || get_layer_mask_(tcode.overlay) != NO_LAYER;
578  }
579 }
580 
581 bool has_wildcard(const ter_list& list)
582 {
583  if(list.empty()) {
584  return false;
585  }
586 
587  // Test all items for a wildcard
588  ter_list::const_iterator itor = list.begin();
589  for(; itor != list.end(); ++itor) {
590  if(has_wildcard(*itor)) {
591  return true;
592  }
593  }
594 
595  // No wildcard found
596  return false;
597 }
598 
599 ter_map read_builder_map(const std::string& str)
600 {
601  boost::multi_array<int, sizeof(ter_map)> a;
602 
603  std::size_t offset = 0;
604  // Skip the leading newlines
605  while(offset < str.length() && utils::isnewline(str[offset])) {
606  ++offset;
607  }
608  // Did we get an empty map?
609  if((offset + 1) >= str.length()) {
610  return ter_map();
611  }
612 
613  auto map_size = get_map_size(&str[offset], str.c_str() + str.size());
614  ter_map result(map_size.second, map_size.first, terrain_code(t_translation::TB_DOT, ter_layer()));
615 
616  int x = 0, y = 0;
617  while(offset < str.length()) {
618 
619  // Get a terrain chunk
620  const std::string separators = ",\n\r";
621  const std::size_t pos_separator = str.find_first_of(separators, offset);
622  std::string terrain = "";
623  // Make sure we didn't hit an empty chunk
624  // which is allowed
625  if(pos_separator != offset) {
626  terrain = str.substr(offset, pos_separator - offset);
627  }
628 
629  // Process the chunk
630  const terrain_code tile = string_to_builder_number_(terrain);
631 
632  // Make space for the new item
633  if (result.h <= x || result.w <= y) {
634  throw error("Map not a rectangle.");
635  }
636 
637  // Add the resulting terrain number,
638  result.get(y, x) = tile;
639 
640  // evaluate the separator
641  if(pos_separator == std::string::npos) {
642  // Probably not required to change the value,
643  // but be sure the case should be handled at least.
644  // I'm not sure how it is defined in the standard,
645  // but here it's defined at max u32 which with +1 gives 0
646  // and make a nice infinite loop.
647  offset = str.length();
648  } else if(utils::isnewline(str[pos_separator])) {
649  // Prepare next iteration
650  ++y;
651  x = 0;
652 
653  offset = pos_separator + 1;
654  // Skip the following newlines
655  while(offset < str.length() && utils::isnewline(str[offset])) {
656  ++offset;
657  }
658 
659  } else {
660  ++x;
661  offset = pos_separator + 1;
662  }
663 
664  }
665 
666  return result;
667 }
668 
669 /***************************************************************************************/
670 // Internal
671 
673 {
674  // Test for the star 0x2A in every position
675  // and return the appropriate mask
676 /*
677  * This is what the code intents to do, but in order to gain some more
678  * speed it's changed to the code below, which does the same but faster.
679  * This routine is used often in the builder and the speedup is noticeable. */
680  if((terrain & 0xFF000000) == 0x2A000000) return 0x00000000;
681  if((terrain & 0x00FF0000) == 0x002A0000) return 0xFF000000;
682  if((terrain & 0x0000FF00) == 0x00002A00) return 0xFFFF0000;
683  if((terrain & 0x000000FF) == 0x0000002A) return 0xFFFFFF00;
684 
685 /*
686  uint8_t *ptr = (uint8_t *) &terrain;
687 
688  if(ptr[3] == 0x2A) return 0x00000000;
689  if(ptr[2] == 0x2A) return 0xFF000000;
690  if(ptr[1] == 0x2A) return 0xFFFF0000;
691  if(ptr[0] == 0x2A) return 0xFFFFFF00;
692 */
693  // no star found return the default
694  return 0xFFFFFFFF;
695 }
696 
697 static terrain_code get_mask_(const terrain_code& terrain)
698 {
699  if(terrain.overlay == NO_LAYER) {
700  return terrain_code(get_layer_mask_(terrain.base), 0xFFFFFFFF);
701  } else {
702  return terrain_code(get_layer_mask_(terrain.base), get_layer_mask_(terrain.overlay));
703  }
704 }
705 
706 static ter_layer string_to_layer_(const char* begin, const char* end)
707 {
708  std::size_t size = end - begin;
709  if (begin == end) {
710  return NO_LAYER;
711  }
712  ter_layer result = 0;
713 
714  // Validate the string
715  VALIDATE(size <= 4, _("A terrain with a string with more "
716  "than 4 characters has been found, the affected terrain is :") + std::string(begin, end));
717 
718  // The conversion to int puts the first char
719  // in the highest part of the number.
720  // This will make the wildcard matching
721  // later on a bit easier.
722  for(std::size_t i = 0; i < 4; ++i) {
723  const unsigned char c = (i < size) ? begin[i] : 0;
724 
725  // Clearing the lower area is a nop on i == 0
726  // so no need for if statement
727  result <<= 8;
728 
729  // Add the result
730  result += c;
731  }
732 
733  return result;
734 }
735 
737  std::string dummy;
738  return string_to_number_(str, dummy, filler);
739 }
740 
741 static terrain_code string_to_number_(utils::string_view str, std::string& start_position, const ter_layer filler)
742 {
743  const char* c_str = &str[0];
744  terrain_code result;
745 
746  // Strip the spaces around us
747  const std::string& whitespace = " \t";
748  std::size_t begin = str.find_first_not_of(whitespace);
749  std::size_t end = str.find_last_not_of(whitespace) + 1;
750  if(begin == std::string::npos) {
751  return result;
752  }
753 
754  // Split if we have 1 space inside
755  std::size_t offset = str.find(' ', begin);
756  if(offset < end) {
757  start_position = std::string(str.substr(begin, offset - begin));
758  begin = offset + 1;
759  }
760 
761  offset = str.find('^', 0);
762  if(offset != std::string::npos) {
763  result = terrain_code { string_to_layer_(c_str + begin, c_str + offset), string_to_layer_(c_str + offset + 1, c_str + end) };
764  } else {
765  result = terrain_code { string_to_layer_(c_str + begin, c_str + end), filler };
766 
767  // Ugly hack
768  if(filler == WILDCARD && (result.base == NOT.base ||
769  result.base == STAR.base)) {
770 
771  result.overlay = NO_LAYER;
772  }
773  }
774 
775  return result;
776 }
777 
778 static std::string number_to_string_(terrain_code terrain, const std::string& start_position)
779 {
780  std::string result = "";
781 
782  // Insert the start position
783  if(!start_position.empty()) {
784  result = start_position + " ";
785  }
786 
787  /*
788  * The initialization of tcode is done to make gcc-4.7 happy. Otherwise it
789  * some uninitialized fields might be used. Its analysis are wrong, but
790  * Initialize to keep it happy.
791  */
792  unsigned char tcode[9] {0};
793  // Insert the terrain tcode
794  tcode[0] = ((terrain.base & 0xFF000000) >> 24);
795  tcode[1] = ((terrain.base & 0x00FF0000) >> 16);
796  tcode[2] = ((terrain.base & 0x0000FF00) >> 8);
797  tcode[3] = (terrain.base & 0x000000FF);
798 
799  if(terrain.overlay != NO_LAYER) {
800  tcode[4] = '^'; //the layer separator
801  tcode[5] = ((terrain.overlay & 0xFF000000) >> 24);
802  tcode[6] = ((terrain.overlay & 0x00FF0000) >> 16);
803  tcode[7] = ((terrain.overlay & 0x0000FF00) >> 8);
804  tcode[8] = (terrain.overlay & 0x000000FF);
805  } else {
806  // If no second layer, the second layer won't be written,
807  // so no need to initialize that part of the array
808  tcode[4] = 0;
809  }
810 
811  for(int i = 0; i < 9; ++i) {
812  if(tcode[i] != 0 && tcode[i] != 0xFF) {
813  result += tcode[i];
814  }
815  if(i == 4 && tcode[i] == 0) {
816  // no layer, stop
817  break;
818  }
819  }
820 
821  return result;
822 }
823 
825 {
826  // Strip the spaces around us
827  const std::string& whitespace = " \t";
828  str.erase(0, str.find_first_not_of(whitespace));
829  if(! str.empty()) {
830  str.erase(str.find_last_not_of(whitespace) + 1);
831  }
832 
833  // Empty string is allowed here, so handle it
834  if(str.empty()) {
835  return terrain_code();
836  }
837 
838  const int number = lexical_cast_default(str, -1);
839  if(number == -1) {
840  // At this point we have a single char
841  // which should be interpreted by the
842  // map builder, so return this number
843  return terrain_code(str[0] << 24, 0);
844  } else {
845  return terrain_code(0, number);
846  }
847 }
848 
849 } // end namespace t_translation
850 
851 #if 0
852 // small helper rule to test the matching rules
853 // building rule
854 // make terrain_translation.o && g++ terrain_translation.o libwesnoth-core.a -lSDL -o terrain_translation
855 int main(int argc, char** argv)
856 {
857  if(argc > 1) {
858 
859  if(std::string(argv[1]) == "match" && argc == 4) {
861 
862  t_translation::ter_list dest = t_translation::read_list(std::string(argv[3]));
863 
864  if(t_translation::terrain_matches(src, dest)) {
865  std::cout << "Match\n" ;
866  } else {
867  std::cout << "No match\n";
868  }
869  }
870  }
871 }
872 
873 #endif
#define ERR_G
Definition: translation.cpp:30
const terrain_code CAVE
BOOST_CXX14_CONSTEXPR size_type find(basic_string_view s, size_type pos=0) const BOOST_NOEXCEPT
BOOST_CXX14_CONSTEXPR size_type find_last_not_of(basic_string_view s, size_type pos=npos) const BOOST_NOEXCEPT
const terrain_code FOREST
int dummy
Definition: lstrlib.cpp:1125
static ter_layer string_to_layer_(const char *begin, const char *end)
boost::bimaps::bimap< boost::bimaps::set_of< std::string >, boost::bimaps::multiset_of< coordinate > > starting_positions
const terrain_code DWARVEN_KEEP
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
New lexcical_cast header.
#define a
BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n)
To lexical_cast_default(From value, To fallback=To())
Lexical cast converts one type to another with a fallback.
uint32_t ter_layer
Definition: translation.hpp:39
BOOST_CXX14_CONSTEXPR basic_string_view substr(size_type pos, size_type n=npos) const
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
const ter_layer NO_LAYER
Definition: translation.hpp:41
#define h
BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT
const terrain_code HUMAN_KEEP
const terrain_code HILL
const ter_match ALL_MOUNTAINS("!,*^V*,!,M*")
const terrain_code VOID_TERRAIN
VOID_TERRAIN is used for shrouded hexes.
#define b
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
int max_map_size()
Return the maximum allowed map size (in either dimension), the maximum map area is, therefore, this value squared.
Definition: translation.cpp:35
const terrain_code MOUNTAIN
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
const terrain_code FOGGED
const terrain_code STAR
ter_map read_game_map(utils::string_view str, starting_positions &starting_positions, coordinate border_offset)
Reads a gamemap string into a 2D vector.
const terrain_code DWARVEN_CASTLE
BOOST_CXX14_CONSTEXPR size_type find_first_not_of(basic_string_view s, size_type pos=0) const BOOST_NOEXCEPT
const terrain_code MINUS
static terrain_code string_to_number_(utils::string_view str, std::string &start_position, const ter_layer filler)
Converts a terrain string to a number.
const terrain_code PLUS
BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT
const terrain_code NOT
#define WRN_G
Definition: translation.cpp:31
const ter_layer TB_DOT
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
const ter_match ALL_HILLS("!,*^V*,!,H*")
const terrain_code OFF_MAP_USER
static terrain_code string_to_builder_number_(std::string str)
Converts a terrain string to a number for the builder.
std::string write_terrain_code(const terrain_code &tcode)
Writes a single terrain code to a string.
const terrain_code HUMAN_CASTLE
BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT
Encapsulates the map of the game.
Definition: location.hpp:42
static ter_layer get_layer_mask_(ter_layer terrain)
Get the mask for a single layer.
const terrain_code BASE
std::size_t i
Definition: function.cpp:933
const ter_match ALL_SWAMPS("!,*^V*,*^B*,!,S*")
int main()
ter_map read_builder_map(const std::string &str)
Reads a builder map.
terrain_code read_terrain_code(utils::string_view str, const ter_layer filler)
Reads a single terrain from a string.
static std::string number_to_string_(terrain_code terrain, const std::string &start_position="")
Converts a terrain number to a string.
std::string write_list(const ter_list &list)
Writes a list of terrains to a string, only writes the new format.
int w
static BOOST_CONSTEXPR_OR_CONST size_type npos
Definition: string_view.hpp:84
static std::pair< int, int > get_map_size(const char *begin, const char *end)
const terrain_code CAVE_WALL
bool has_wildcard(const terrain_code &tcode)
Tests whether a terrain code contains a wildcard.
bool isnewline(const char c)
std::string write_game_map(const ter_map &map, const starting_positions &starting_positions, coordinate border_offset)
Write a gamemap in to a vector string.
const terrain_code DEEP_WATER
const ter_match ALL_OFF_MAP
BOOST_CONSTEXPR const_reference front() const
const terrain_code UNDERGROUND_VILLAGE
ter_list read_list(utils::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
Standard logging facilities (interface).
std::vector< terrain_code > ter_list
Definition: translation.hpp:78
const ter_layer WILDCARD
Definition: translation.hpp:40
static terrain_code get_mask_(const terrain_code &terrain)
Gets a mask for a terrain, this mask is used for wildcard matching.
const terrain_code SHALLOW_WATER
BOOST_CXX14_CONSTEXPR size_type find_first_of(basic_string_view s, size_type pos=0) const BOOST_NOEXCEPT
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
mock_char c
This structure can be used for matching terrain strings.
const terrain_code GRASS_LAND
const ter_match ALL_FORESTS