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