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