The Battle for Wesnoth  1.19.0-dev
translation.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
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 the terrain has only 1 layer then the filler
86  * will be used as the second layer.
87  *
88  * @return The terrain code found in the string if no
89  * valid terrain is found VOID will be returned.
90  */
91  static terrain_code string_to_number_(std::string_view str, std::vector<std::string>& start_positions, const ter_layer filler);
92  static terrain_code string_to_number_(std::string_view str, const ter_layer filler = NO_LAYER);
93 
94  /**
95  * Converts a terrain number to a string
96  *
97  * @param terrain The terrain number to convert.
98  * @param start_position The starting position, if smaller than 0
99  * it's ignored else it's written.
100  *
101  * @return The converted string, if no starting
102  * position given it's padded to 4 chars else
103  * padded to 7 chars.
104  */
105  static std::string number_to_string_(terrain_code terrain, const std::vector<std::string>& start_position = std::vector<std::string>());
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) :
164 {}
165 
167  terrain(),
168  mask(),
169  masked_terrain(),
170  has_wildcard(false),
171  is_empty(true)
172 {}
173 
174 ter_match::ter_match(std::string_view str, const ter_layer filler) :
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++) {
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++) {
203  masked_terrain[i] = mask[i] & terrain[i];
204  }
205 }
206 
207 terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
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 
217 ter_list read_list(std::string_view str, const ter_layer filler)
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  std::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 == std::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  std::string_view terrain = str.substr(offset, pos_separator - offset);
315 
316  // Process the chunk
317  std::vector<std::string> sp;
318  // The gamemap never has a wildcard
319  const terrain_code tile = string_to_number_(terrain, sp, NO_LAYER);
320 
321  // Add to the resulting starting position
322  for(const auto& starting_position : sp) {
323  if (starting_positions.left.find(starting_position) != starting_positions.left.end()) {
324  WRN_G << "Starting position " << starting_position << " is redefined.";
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;
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() << ")";
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() << ")";
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";
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  std::vector<std::string> sp;
401 
402  for(const auto& pair : starting_positions.right.equal_range(coordinate(x - border_offset.x, y - border_offset.y))) {
403  sp.push_back(pair.second);
404  }
405  // Add the separator
406  if(x != 0) {
407  str << ", ";
408  }
409  str << number_to_string_(map[x][y], sp);
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  PLAIN_LOG << 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  PLAIN_LOG << 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  std::size_t offset = 0;
602  // Skip the leading newlines
603  while(offset < str.length() && utils::isnewline(str[offset])) {
604  ++offset;
605  }
606  // Did we get an empty map?
607  if((offset + 1) >= str.length()) {
608  return ter_map();
609  }
610 
611  auto map_size = get_map_size(&str[offset], str.c_str() + str.size());
612  ter_map result(map_size.second, map_size.first, terrain_code(t_translation::TB_DOT, ter_layer()));
613 
614  int x = 0, y = 0;
615  while(offset < str.length()) {
616 
617  // Get a terrain chunk
618  const std::string separators = ",\n\r";
619  const std::size_t pos_separator = str.find_first_of(separators, offset);
620  std::string terrain = "";
621  // Make sure we didn't hit an empty chunk
622  // which is allowed
623  if(pos_separator != offset) {
624  terrain = str.substr(offset, pos_separator - offset);
625  }
626 
627  // Process the chunk
628  const terrain_code tile = string_to_builder_number_(terrain);
629 
630  // Make space for the new item
631  if (result.h <= x || result.w <= y) {
632  throw error("Map not a rectangle.");
633  }
634 
635  // Add the resulting terrain number,
636  result.get(y, x) = tile;
637 
638  // evaluate the separator
639  if(pos_separator == std::string::npos) {
640  // Probably not required to change the value,
641  // but be sure the case should be handled at least.
642  // I'm not sure how it is defined in the standard,
643  // but here it's defined at max u32 which with +1 gives 0
644  // and make a nice infinite loop.
645  offset = str.length();
646  } else if(utils::isnewline(str[pos_separator])) {
647  // Prepare next iteration
648  ++y;
649  x = 0;
650 
651  offset = pos_separator + 1;
652  // Skip the following newlines
653  while(offset < str.length() && utils::isnewline(str[offset])) {
654  ++offset;
655  }
656 
657  } else {
658  ++x;
659  offset = pos_separator + 1;
660  }
661 
662  }
663 
664  return result;
665 }
666 
667 /***************************************************************************************/
668 // Internal
669 
671 {
672  // Test for the star 0x2A in every position
673  // and return the appropriate mask
674 /*
675  * This is what the code intents to do, but in order to gain some more
676  * speed it's changed to the code below, which does the same but faster.
677  * This routine is used often in the builder and the speedup is noticeable. */
678  if((terrain & 0xFF000000) == 0x2A000000) return 0x00000000;
679  if((terrain & 0x00FF0000) == 0x002A0000) return 0xFF000000;
680  if((terrain & 0x0000FF00) == 0x00002A00) return 0xFFFF0000;
681  if((terrain & 0x000000FF) == 0x0000002A) return 0xFFFFFF00;
682 
683 /*
684  uint8_t *ptr = (uint8_t *) &terrain;
685 
686  if(ptr[3] == 0x2A) return 0x00000000;
687  if(ptr[2] == 0x2A) return 0xFF000000;
688  if(ptr[1] == 0x2A) return 0xFFFF0000;
689  if(ptr[0] == 0x2A) return 0xFFFFFF00;
690 */
691  // no star found return the default
692  return 0xFFFFFFFF;
693 }
694 
695 static terrain_code get_mask_(const terrain_code& terrain)
696 {
697  if(terrain.overlay == NO_LAYER) {
698  return terrain_code(get_layer_mask_(terrain.base), 0xFFFFFFFF);
699  } else {
700  return terrain_code(get_layer_mask_(terrain.base), get_layer_mask_(terrain.overlay));
701  }
702 }
703 
704 static ter_layer string_to_layer_(std::string_view str)
705 {
706  if(str.empty()) {
707  return NO_LAYER;
708  }
709 
710  if(str.size() > 4) {
711  throw error("A terrain with a string with more "
712  "than 4 characters has been found, the affected terrain is: " + std::string(str));
713  }
714 
715  ter_layer result = 0;
716  // The conversion to int puts the first char
717  // in the highest part of the number.
718  // This will make the wildcard matching
719  // later on a bit easier.
720  for(std::size_t i = 0; i < 4; ++i) {
721  const unsigned char c = (i < str.size()) ? str[i] : 0;
722 
723  // Clearing the lower area is a nop on i == 0
724  // so no need for if statement
725  result <<= 8;
726 
727  // Add the result
728  result += c;
729  }
730 
731  return result;
732 }
733 
734 static terrain_code string_to_number_(std::string_view str, const ter_layer filler) {
735  std::vector<std::string> dummy;
736  return string_to_number_(str, dummy, filler);
737 }
738 
739 static terrain_code string_to_number_(std::string_view str, std::vector<std::string>& start_positions, const ter_layer filler)
740 {
741  terrain_code result;
742 
743  // Strip the spaces around us
744  // unlike the old implementation this also trims newlines.
745  utils::trim(str);
746  if(str.empty()) {
747  return result;
748  }
749 
750  // Split if we have spaces inside
751  std::size_t offset = str.find(' ', 0);
752  while(offset != std::string::npos) {
753  start_positions.push_back(std::string(str.substr(0, offset)));
754  str.remove_prefix(offset + 1);
755  offset = str.find(' ', 0);
756  }
757 
758  offset = str.find('^', 0);
759  if(offset != std::string::npos) {
760  result = terrain_code { string_to_layer_(str.substr(0, offset)), string_to_layer_(str.substr(offset + 1)) };
761  } else {
762  result = terrain_code { string_to_layer_(str), filler };
763 
764  // Ugly hack
765  if(filler == WILDCARD && (result.base == NOT.base ||
766  result.base == STAR.base)) {
767 
768  result.overlay = NO_LAYER;
769  }
770  }
771 
772  return result;
773 }
774 
775 static std::string number_to_string_(terrain_code terrain, const std::vector<std::string>& start_positions)
776 {
777  std::string result = "";
778 
779  // Insert the start position
780  for (const std::string& str : start_positions) {
781  result = str + " " + result;
782  }
783 
784  /*
785  * The initialization of tcode is done to make gcc-4.7 happy. Otherwise it
786  * some uninitialized fields might be used. Its analysis are wrong, but
787  * Initialize to keep it happy.
788  */
789  unsigned char tcode[9] {0};
790  // Insert the terrain tcode
791  tcode[0] = ((terrain.base & 0xFF000000) >> 24);
792  tcode[1] = ((terrain.base & 0x00FF0000) >> 16);
793  tcode[2] = ((terrain.base & 0x0000FF00) >> 8);
794  tcode[3] = (terrain.base & 0x000000FF);
795 
796  if(terrain.overlay != NO_LAYER) {
797  tcode[4] = '^'; //the layer separator
798  tcode[5] = ((terrain.overlay & 0xFF000000) >> 24);
799  tcode[6] = ((terrain.overlay & 0x00FF0000) >> 16);
800  tcode[7] = ((terrain.overlay & 0x0000FF00) >> 8);
801  tcode[8] = (terrain.overlay & 0x000000FF);
802  } else {
803  // If no second layer, the second layer won't be written,
804  // so no need to initialize that part of the array
805  tcode[4] = 0;
806  }
807 
808  for(int i = 0; i < 9; ++i) {
809  if(tcode[i] != 0 && tcode[i] != 0xFF) {
810  result += tcode[i];
811  }
812  if(i == 4 && tcode[i] == 0) {
813  // no layer, stop
814  break;
815  }
816  }
817 
818  return result;
819 }
820 
822 {
823  // Strip the spaces around us
824  const std::string& whitespace = " \t";
825  str.erase(0, str.find_first_not_of(whitespace));
826  if(! str.empty()) {
827  str.erase(str.find_last_not_of(whitespace) + 1);
828  }
829 
830  // Empty string is allowed here, so handle it
831  if(str.empty()) {
832  return terrain_code();
833  }
834 
835  const int number = lexical_cast_default(str, -1);
836  if(number == -1) {
837  // At this point we have a single char
838  // which should be interpreted by the
839  // map builder, so return this number
840  return terrain_code(str[0] << 24, 0);
841  } else {
842  return terrain_code(0, number);
843  }
844 }
845 
846 } // end namespace t_translation
847 
848 #if 0
849 // small helper rule to test the matching rules
850 // building rule
851 // make terrain_translation.o && g++ terrain_translation.o libwesnoth-core.a -lSDL -o terrain_translation
852 int main(int argc, char** argv)
853 {
854  if(argc > 1) {
855 
856  if(std::string(argv[1]) == "match" && argc == 4) {
858 
859  t_translation::ter_list dest = t_translation::read_list(std::string(argv[3]));
860 
861  if(t_translation::terrain_matches(src, dest)) {
862  std::cout << "Match\n" ;
863  } else {
864  std::cout << "No match\n";
865  }
866  }
867  }
868 }
869 
870 #endif
std::size_t i
Definition: function.cpp:968
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:295
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
const ter_layer WILDCARD
Definition: translation.hpp:39
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 **argv)
Definition: sdl2.cpp:19
Encapsulates the map of the game.
Definition: location.hpp:38
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