The Battle for Wesnoth  1.17.8+dev
translation.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2022
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.";
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;
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() << ")";
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() << ")";
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";
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  PLAIN_LOG << 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  PLAIN_LOG << 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  if(str.size() > 4) {
713  throw error("A terrain with a string with more "
714  "than 4 characters has been found, the affected terrain is: " + std::string(str));
715  }
716 
717  ter_layer result = 0;
718  // The conversion to int puts the first char
719  // in the highest part of the number.
720  // This will make the wildcard matching
721  // later on a bit easier.
722  for(std::size_t i = 0; i < 4; ++i) {
723  const unsigned char c = (i < str.size()) ? str[i] : 0;
724 
725  // Clearing the lower area is a nop on i == 0
726  // so no need for if statement
727  result <<= 8;
728 
729  // Add the result
730  result += c;
731  }
732 
733  return result;
734 }
735 
736 static terrain_code string_to_number_(std::string_view str, const ter_layer filler) {
737  std::vector<std::string> dummy;
738  return string_to_number_(str, dummy, filler);
739 }
740 
741 static terrain_code string_to_number_(std::string_view str, std::vector<std::string>& start_positions, const ter_layer filler)
742 {
743  terrain_code result;
744 
745  // Strip the spaces around us
746  // unlike the old implementation this also trims newlines.
747  utils::trim(str);
748  if(str.empty()) {
749  return result;
750  }
751 
752  // Split if we have spaces inside
753  std::size_t offset = str.find(' ', 0);
754  while(offset != std::string::npos) {
755  start_positions.push_back(std::string(str.substr(0, offset)));
756  str.remove_prefix(offset + 1);
757  offset = str.find(' ', 0);
758  }
759 
760  offset = str.find('^', 0);
761  if(offset != std::string::npos) {
762  result = terrain_code { string_to_layer_(str.substr(0, offset)), string_to_layer_(str.substr(offset + 1)) };
763  } else {
764  result = terrain_code { string_to_layer_(str), filler };
765 
766  // Ugly hack
767  if(filler == WILDCARD && (result.base == NOT.base ||
768  result.base == STAR.base)) {
769 
770  result.overlay = NO_LAYER;
771  }
772  }
773 
774  return result;
775 }
776 
777 static std::string number_to_string_(terrain_code terrain, const std::vector<std::string>& start_positions)
778 {
779  std::string result = "";
780 
781  // Insert the start position
782  for (const std::string& str : start_positions) {
783  result = str + " " + result;
784  }
785 
786  /*
787  * The initialization of tcode is done to make gcc-4.7 happy. Otherwise it
788  * some uninitialized fields might be used. Its analysis are wrong, but
789  * Initialize to keep it happy.
790  */
791  unsigned char tcode[9] {0};
792  // Insert the terrain tcode
793  tcode[0] = ((terrain.base & 0xFF000000) >> 24);
794  tcode[1] = ((terrain.base & 0x00FF0000) >> 16);
795  tcode[2] = ((terrain.base & 0x0000FF00) >> 8);
796  tcode[3] = (terrain.base & 0x000000FF);
797 
798  if(terrain.overlay != NO_LAYER) {
799  tcode[4] = '^'; //the layer separator
800  tcode[5] = ((terrain.overlay & 0xFF000000) >> 24);
801  tcode[6] = ((terrain.overlay & 0x00FF0000) >> 16);
802  tcode[7] = ((terrain.overlay & 0x0000FF00) >> 8);
803  tcode[8] = (terrain.overlay & 0x000000FF);
804  } else {
805  // If no second layer, the second layer won't be written,
806  // so no need to initialize that part of the array
807  tcode[4] = 0;
808  }
809 
810  for(int i = 0; i < 9; ++i) {
811  if(tcode[i] != 0 && tcode[i] != 0xFF) {
812  result += tcode[i];
813  }
814  if(i == 4 && tcode[i] == 0) {
815  // no layer, stop
816  break;
817  }
818  }
819 
820  return result;
821 }
822 
824 {
825  // Strip the spaces around us
826  const std::string& whitespace = " \t";
827  str.erase(0, str.find_first_not_of(whitespace));
828  if(! str.empty()) {
829  str.erase(str.find_last_not_of(whitespace) + 1);
830  }
831 
832  // Empty string is allowed here, so handle it
833  if(str.empty()) {
834  return terrain_code();
835  }
836 
837  const int number = lexical_cast_default(str, -1);
838  if(number == -1) {
839  // At this point we have a single char
840  // which should be interpreted by the
841  // map builder, so return this number
842  return terrain_code(str[0] << 24, 0);
843  } else {
844  return terrain_code(0, number);
845  }
846 }
847 
848 } // end namespace t_translation
849 
850 #if 0
851 // small helper rule to test the matching rules
852 // building rule
853 // make terrain_translation.o && g++ terrain_translation.o libwesnoth-core.a -lSDL -o terrain_translation
854 int main(int argc, char** argv)
855 {
856  if(argc > 1) {
857 
858  if(std::string(argv[1]) == "match" && argc == 4) {
860 
861  t_translation::ter_list dest = t_translation::read_list(std::string(argv[3]));
862 
863  if(t_translation::terrain_matches(src, dest)) {
864  std::cout << "Match\n" ;
865  } else {
866  std::cout << "No match\n";
867  }
868  }
869  }
870 }
871 
872 #endif
#define ERR_G
Definition: translation.cpp:32
const terrain_code CAVE
#define PLAIN_LOG
Definition: log.hpp:256
const terrain_code FOREST
boost::bimaps::bimap< boost::bimaps::set_of< std::string >, boost::bimaps::multiset_of< coordinate > > starting_positions
const terrain_code DWARVEN_KEEP
int main(int, char **argv)
Definition: sdl2.cpp:5
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.
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:38
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
const ter_layer NO_LAYER
Definition: translation.hpp:40
#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
const terrain_code HILL
const ter_match ALL_MOUNTAINS("!,*^V*,!,M*")
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
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:77
const ter_layer WILDCARD
Definition: translation.hpp:39
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