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