00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #define GETTEXT_DOMAIN "wesnoth-lib"
00022
00023 #include "global.hpp"
00024 #include "gettext.hpp"
00025 #include "log.hpp"
00026 #include "terrain_translation.hpp"
00027 #include "serialization/string_utils.hpp"
00028 #include "util.hpp"
00029 #include "wml_exception.hpp"
00030
00031
00032 #define ERR_G LOG_STREAM(err, lg::general)
00033 #define WRN_G LOG_STREAM(warn, lg::general)
00034
00035 namespace t_translation {
00036
00037 size_t max_map_size() {
00038 return 1000;
00039 }
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055 static t_layer get_layer_mask_(t_layer terrain);
00056
00057
00058
00059
00060
00061
00062
00063
00064 static t_terrain get_mask_(const t_terrain& terrain);
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 static t_layer string_to_layer_(const std::string& str);
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088 static t_terrain string_to_number_(std::string str, int& start_position, const t_layer filler);
00089 static t_terrain string_to_number_(const std::string& str, const t_layer filler = NO_LAYER);
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104 static std::string number_to_string_(t_terrain terrain, const int start_position = -1);
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114 static t_terrain string_to_builder_number_(std::string str);
00115
00116
00117
00118 const t_terrain OFF_MAP_USER = string_to_number_("_off^_usr");
00119
00120 const t_terrain VOID_TERRAIN = string_to_number_("_s");
00121 const t_terrain FOGGED = string_to_number_("_f");
00122
00123 const t_terrain HUMAN_CASTLE = string_to_number_("Ch");
00124 const t_terrain HUMAN_KEEP = string_to_number_("Kh");
00125 const t_terrain SHALLOW_WATER = string_to_number_("Ww");
00126 const t_terrain DEEP_WATER = string_to_number_("Wo");
00127 const t_terrain GRASS_LAND = string_to_number_("Gg");
00128 const t_terrain FOREST = string_to_number_("Gg^Ff");
00129 const t_terrain MOUNTAIN = string_to_number_("Mm");
00130 const t_terrain HILL = string_to_number_("Hh");
00131
00132 const t_terrain CAVE_WALL = string_to_number_("Xu");
00133 const t_terrain CAVE = string_to_number_("Uu");
00134 const t_terrain UNDERGROUND_VILLAGE = string_to_number_("Uu^Vu");
00135 const t_terrain DWARVEN_CASTLE = string_to_number_("Cud");
00136 const t_terrain DWARVEN_KEEP = string_to_number_("Kud");
00137
00138 const t_terrain PLUS = string_to_number_("+");
00139 const t_terrain MINUS = string_to_number_("-");
00140 const t_terrain NOT = string_to_number_("!");
00141 const t_terrain STAR = string_to_number_("*");
00142 const t_terrain BASE = string_to_number_("_bas");
00143
00144 const t_match ALL_FORESTS("F*,*^F*");
00145 const t_match ALL_HILLS("!,*^V*,!,H*");
00146 const t_match ALL_MOUNTAINS("!,*^V*,!,M*");
00147 const t_match ALL_SWAMPS("!,*^V*,*^B*,!,S*");
00148
00149
00150
00151 t_terrain::t_terrain(const std::string& b, t_layer o) :
00152 base(string_to_layer_(b)), overlay(o)
00153 {}
00154
00155 t_terrain::t_terrain(const std::string& b, const std::string& o) :
00156 base(string_to_layer_(b)), overlay(string_to_layer_(o))
00157 {}
00158
00159 t_match::t_match() :
00160 terrain(),
00161 mask(),
00162 masked_terrain(),
00163 has_wildcard(false),
00164 is_empty(true)
00165 {}
00166
00167 t_match::t_match(const std::string& str, const t_layer filler) :
00168 terrain(t_translation::read_list(str, filler)),
00169 mask(),
00170 masked_terrain(),
00171 has_wildcard(t_translation::has_wildcard(terrain)),
00172 is_empty(terrain.empty())
00173
00174 {
00175 mask.resize(terrain.size());
00176 masked_terrain.resize(terrain.size());
00177
00178 for(size_t i = 0; i < terrain.size(); i++) {
00179 mask[i] = t_translation::get_mask_(terrain[i]);
00180 masked_terrain[i] = mask[i] & terrain[i];
00181 }
00182 }
00183
00184 t_match::t_match(const t_terrain& tcode):
00185 terrain(t_list(1, tcode)),
00186 mask(),
00187 masked_terrain(),
00188 has_wildcard(t_translation::has_wildcard(terrain)),
00189 is_empty(terrain.empty())
00190 {
00191 mask.resize(terrain.size());
00192 masked_terrain.resize(terrain.size());
00193
00194 for(size_t i = 0; i < terrain.size(); i++) {
00195 mask[i] = t_translation::get_mask_(terrain[i]);
00196 masked_terrain[i] = mask[i] & terrain[i];
00197 }
00198 }
00199
00200 coordinate::coordinate()
00201 : x(0)
00202 , y(0)
00203 {
00204 }
00205
00206 coordinate::coordinate(const size_t x_, const size_t y_)
00207 : x(x_)
00208 , y(y_)
00209 {
00210 }
00211
00212 t_terrain read_terrain_code(const std::string& str, const t_layer filler)
00213 {
00214 return string_to_number_(str, filler);
00215 }
00216
00217 std::string write_terrain_code(const t_terrain& tcode)
00218 {
00219 return number_to_string_(tcode);
00220 }
00221
00222 t_list read_list(const std::string& str, const t_layer filler)
00223 {
00224
00225 t_list result;
00226
00227 if(str.empty()) {
00228 return result;
00229 }
00230
00231 size_t offset = 0;
00232 while(offset < str.length()) {
00233
00234
00235 const std::string separators = ",";
00236 const size_t pos_separator = str.find_first_of(separators, offset);
00237 const std::string terrain = str.substr(offset, pos_separator - offset);
00238
00239
00240 const t_terrain tile = string_to_number_(terrain, filler);
00241
00242
00243 result.push_back(tile);
00244
00245
00246 if(pos_separator == std::string::npos) {
00247 offset = str.length();
00248 } else {
00249 offset = pos_separator + 1;
00250 }
00251 }
00252
00253 return result;
00254 }
00255
00256 std::string write_list(const t_list& list)
00257 {
00258 std::stringstream result;
00259
00260 t_list::const_iterator itor = list.begin();
00261 for( ; itor != list.end(); ++itor) {
00262 if(itor == list.begin()) {
00263 result << number_to_string_(*itor);
00264 } else {
00265 result << ", " << number_to_string_(*itor);
00266 }
00267 }
00268
00269 return result.str();
00270 }
00271
00272 t_map read_game_map(const std::string& str, std::map<int, coordinate>& starting_positions)
00273 {
00274 t_map result;
00275
00276 size_t offset = 0;
00277 size_t x = 0, y = 0, width = 0;
00278
00279
00280 while(offset < str.length() && utils::isnewline(str[offset])) {
00281 ++offset;
00282 }
00283
00284
00285 if((offset + 1) >= str.length()) {
00286 return result;
00287 }
00288
00289 while(offset < str.length()) {
00290
00291
00292 const std::string separators = ",\n\r";
00293 const size_t pos_separator = str.find_first_of(separators, offset);
00294 const std::string terrain = str.substr(offset, pos_separator - offset);
00295
00296
00297 int starting_position = -1;
00298
00299 const t_terrain tile = string_to_number_(terrain, starting_position, NO_LAYER);
00300
00301
00302 if(starting_position != -1) {
00303 if(starting_positions.find(starting_position) != starting_positions.end()) {
00304
00305 WRN_G << "Starting position " << starting_position << " is redefined.\n";
00306 starting_positions[starting_position].x = x;
00307 starting_positions[starting_position].y = y;
00308 } else {
00309
00310 const struct coordinate coord(x, y);
00311 starting_positions.insert(std::pair<int, coordinate>(starting_position, coord));
00312 }
00313 }
00314
00315
00316
00317
00318
00319
00320 if(result.size() <= x) {
00321 result.resize(x + 1);
00322 }
00323 if(result[x].size() <= y) {
00324 result[x].resize(y + 1);
00325 }
00326
00327
00328 result[x][y] = tile;
00329
00330
00331 if(pos_separator == std::string::npos || utils::isnewline(str[pos_separator])) {
00332
00333 if(y == 0) {
00334
00335 width = x + 1;
00336 } else {
00337 if((x + 1) != width ) {
00338 ERR_G << "Map not a rectangle error occurred at line offset " << y << " position offset " << x << "\n";
00339 throw error("Map not a rectangle.");
00340 }
00341 if (y > max_map_size()) {
00342 ERR_G << "Map size exceeds limit (y > " << max_map_size() << ")\n";
00343 throw error("Map height limit exceeded.");
00344 }
00345 }
00346
00347
00348 ++y;
00349 x = 0;
00350
00351
00352 if(pos_separator == std::string::npos) {
00353 offset = str.length();
00354
00355 } else {
00356
00357 offset = pos_separator + 1;
00358
00359 while(offset < str.length() && utils::isnewline(str[offset])) {
00360 ++offset;
00361 }
00362 }
00363
00364 } else {
00365 ++x;
00366 offset = pos_separator + 1;
00367 if (x > max_map_size()) {
00368 ERR_G << "Map size exceeds limit (x > " << max_map_size() << ")\n";
00369 throw error("Map width limit exceeded.");
00370 }
00371 }
00372
00373 }
00374
00375 if(x != 0 && (x + 1) != width) {
00376 ERR_G << "Map not a rectangle error occurred at the end\n";
00377 throw error("Map not a rectangle.");
00378 }
00379
00380 return result;
00381 }
00382
00383 std::string write_game_map(const t_map& map, std::map<int, coordinate> starting_positions)
00384 {
00385 std::stringstream str;
00386
00387 for(size_t y = 0; y < map[0].size(); ++y) {
00388 for(size_t x = 0; x < map.size(); ++x) {
00389
00390
00391
00392
00393
00394 std::map<int, coordinate>::iterator itor = starting_positions.begin();
00395 int starting_position = -1;
00396 for(; itor != starting_positions.end(); ++itor) {
00397 if(itor->second.x == x && itor->second.y == y) {
00398 starting_position = itor->first;
00399 starting_positions.erase(itor);
00400 break;
00401 }
00402 }
00403
00404
00405 if(x != 0) {
00406 str << ", ";
00407 }
00408 str << number_to_string_(map[x][y], starting_position);
00409 }
00410
00411 if (y < map[0].size() -1)
00412 str << "\n";
00413 }
00414
00415 return str.str();
00416 }
00417
00418 bool terrain_matches(const t_terrain& src, const t_terrain& dest)
00419 {
00420 return terrain_matches(src, t_list(1, dest));
00421 }
00422
00423 bool terrain_matches(const t_terrain& src, const t_list& dest)
00424 {
00425
00426
00427
00428
00429
00430 if(dest.empty()) {
00431 return false;
00432 }
00433
00434 #if 0
00435 std::cerr << std::hex << "src = " << src.base << "^" << src.overlay << "\t"
00436 << src_mask.base << "^" << src_mask.overlay << "\t"
00437 << masked_src.base << "^" << masked_src.overlay << "\t"
00438 << src_has_wildcard << "\n";
00439 #endif
00440
00441 bool result = true;
00442 t_list::const_iterator itor = dest.begin();
00443
00444
00445 for(; itor != dest.end(); ++itor) {
00446
00447
00448 if(*itor == STAR) {
00449 return result;
00450 }
00451
00452
00453 if(*itor == NOT) {
00454 result = !result;
00455 continue;
00456 }
00457
00458
00459 if(src == *itor) {
00460 return result;
00461 }
00462
00463
00464 const t_terrain dest_mask = get_mask_(*itor);
00465 const t_terrain masked_dest = (*itor & dest_mask);
00466 const bool dest_has_wildcard = has_wildcard(*itor);
00467 #if 0
00468 std::cerr << std::hex << "dest= "
00469 << itor->base << "^" << itor->overlay << "\t"
00470 << dest_mask.base << "^" << dest_mask.overlay << "\t"
00471 << masked_dest.base << "^" << masked_dest.overlay << "\t"
00472 << dest_has_wildcard << "\n";
00473 #endif
00474 if(dest_has_wildcard &&
00475 (src.base & dest_mask.base) == masked_dest.base &&
00476 (src.overlay & dest_mask.overlay) == masked_dest.overlay) {
00477 return result;
00478 }
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497 }
00498
00499
00500 return !result;
00501 }
00502
00503
00504
00505
00506 bool terrain_matches(const t_terrain& src, const t_match& dest)
00507 {
00508 if(dest.is_empty) {
00509 return false;
00510 }
00511
00512 bool result = true;
00513
00514
00515
00516
00517
00518
00519 size_t i = 0;
00520 t_list::const_iterator end = dest.terrain.end();
00521 for(t_list::const_iterator terrain_itor = dest.terrain.begin();
00522 terrain_itor != end;
00523 ++i, ++terrain_itor) {
00524
00525
00526 if(*terrain_itor == STAR) {
00527 return result;
00528 }
00529
00530
00531 if(*terrain_itor == NOT) {
00532 result = !result;
00533 continue;
00534 }
00535
00536
00537 if(*terrain_itor == src) {
00538 return result;
00539 }
00540
00541
00542 if(dest.has_wildcard &&
00543 (src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
00544 (src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay) {
00545 return result;
00546 }
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565 }
00566
00567
00568 return !result;
00569 }
00570
00571 bool has_wildcard(const t_terrain& tcode)
00572 {
00573 if(tcode.overlay == NO_LAYER) {
00574 return get_layer_mask_(tcode.base) != NO_LAYER;
00575 } else {
00576 return get_layer_mask_(tcode.base) != NO_LAYER || get_layer_mask_(tcode.overlay) != NO_LAYER;
00577 }
00578 }
00579
00580 bool has_wildcard(const t_list& list)
00581 {
00582 if(list.empty()) {
00583 return false;
00584 }
00585
00586
00587 t_list::const_iterator itor = list.begin();
00588 for(; itor != list.end(); ++itor) {
00589 if(has_wildcard(*itor)) {
00590 return true;
00591 }
00592 }
00593
00594
00595 return false;
00596 }
00597
00598 t_map read_builder_map(const std::string& str)
00599 {
00600 size_t offset = 0;
00601 t_map result;
00602
00603
00604 while(offset < str.length() && utils::isnewline(str[offset])) {
00605 ++offset;
00606 }
00607
00608
00609 if((offset + 1) >= str.length()) {
00610 return result;
00611 }
00612
00613 size_t x = 0, y = 0;
00614 while(offset < str.length()) {
00615
00616
00617 const std::string separators = ",\n\r";
00618 const size_t pos_separator = str.find_first_of(separators, offset);
00619
00620 std::string terrain = "";
00621
00622
00623 if(pos_separator != offset) {
00624 terrain = str.substr(offset, pos_separator - offset);
00625 }
00626
00627
00628 const t_terrain tile = string_to_builder_number_(terrain);
00629
00630
00631 if(result.size() <= y) {
00632 result.resize(y + 1);
00633 }
00634 if(result[y].size() <= x) {
00635 result[y].resize(x + 1);
00636 }
00637
00638
00639 result[y][x] = tile;
00640
00641
00642 if(pos_separator == std::string::npos) {
00643
00644
00645
00646
00647
00648 offset = str.length();
00649 } else if(utils::isnewline(str[pos_separator])) {
00650
00651 ++y;
00652 x = 0;
00653
00654 offset = pos_separator + 1;
00655
00656 while(offset < str.length() && utils::isnewline(str[offset])) {
00657 ++offset;
00658 }
00659
00660 } else {
00661 ++x;
00662 offset = pos_separator + 1;
00663 }
00664
00665 }
00666
00667 return result;
00668 }
00669
00670
00671
00672
00673 inline t_layer get_layer_mask_(t_layer terrain)
00674 {
00675
00676
00677
00678
00679
00680
00681 if((terrain & 0xFF000000) == 0x2A000000) return 0x00000000;
00682 if((terrain & 0x00FF0000) == 0x002A0000) return 0xFF000000;
00683 if((terrain & 0x0000FF00) == 0x00002A00) return 0xFFFF0000;
00684 if((terrain & 0x000000FF) == 0x0000002A) return 0xFFFFFF00;
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695 return 0xFFFFFFFF;
00696 }
00697
00698 static t_terrain get_mask_(const t_terrain& terrain)
00699 {
00700 if(terrain.overlay == NO_LAYER) {
00701 return t_terrain(get_layer_mask_(terrain.base), 0xFFFFFFFF);
00702 } else {
00703 return t_terrain(get_layer_mask_(terrain.base), get_layer_mask_(terrain.overlay));
00704 }
00705 }
00706
00707 static t_layer string_to_layer_(const std::string& str)
00708 {
00709 if (str.empty())
00710 return NO_LAYER;
00711
00712 t_layer result = 0;
00713
00714
00715 VALIDATE(str.size() <= 4, _("A terrain with a string with more "
00716 "than 4 characters has been found, the affected terrain is :") + str);
00717
00718
00719
00720
00721
00722 for(size_t i = 0; i < 4; ++i) {
00723 const unsigned char c = (i < str.length()) ? str[i] : 0;
00724
00725
00726
00727 result <<= 8;
00728
00729
00730 result += c;
00731 }
00732
00733 return result;
00734 }
00735
00736 static t_terrain string_to_number_(const std::string& str, const t_layer filler) {
00737 int dummy = -1;
00738 return string_to_number_(str, dummy, filler);
00739 }
00740
00741 static t_terrain string_to_number_(std::string str, int& start_position, const t_layer filler)
00742 {
00743 t_terrain result;
00744
00745
00746 const std::string& whitespace = " \t";
00747 str.erase(0, str.find_first_not_of(whitespace));
00748 str.erase(str.find_last_not_of(whitespace) + 1);
00749 if(str.empty()) {
00750 return result;
00751 }
00752
00753
00754 size_t offset = str.find(' ', 0);
00755 if(offset != std::string::npos) {
00756 try {
00757 start_position = lexical_cast<int>(str.substr(0, offset));
00758 } catch(bad_lexical_cast&) {
00759 return VOID_TERRAIN;
00760 }
00761 str.erase(0, offset + 1);
00762 }
00763
00764 offset = str.find('^', 0);
00765 if(offset != std::string::npos) {
00766 const std::string base_str(str, 0, offset);
00767 const std::string overlay_str(str, offset + 1, str.size());
00768 result = t_terrain(base_str, overlay_str);
00769 } else {
00770 result = t_terrain(str, filler);
00771
00772
00773 if(filler == WILDCARD && (result.base == NOT.base ||
00774 result.base == STAR.base)) {
00775
00776 result.overlay = NO_LAYER;
00777 }
00778 }
00779
00780 return result;
00781 }
00782
00783 static std::string number_to_string_(t_terrain terrain, const int start_position)
00784 {
00785 std::string result = "";
00786
00787
00788 if(start_position > 0) {
00789 result = str_cast(start_position) + " ";
00790 }
00791
00792
00793
00794
00795
00796
00797 unsigned char tcode[9] = {0};
00798
00799 tcode[0] = ((terrain.base & 0xFF000000) >> 24);
00800 tcode[1] = ((terrain.base & 0x00FF0000) >> 16);
00801 tcode[2] = ((terrain.base & 0x0000FF00) >> 8);
00802 tcode[3] = (terrain.base & 0x000000FF);
00803
00804 if(terrain.overlay != NO_LAYER) {
00805 tcode[4] = '^';
00806 tcode[5] = ((terrain.overlay & 0xFF000000) >> 24);
00807 tcode[6] = ((terrain.overlay & 0x00FF0000) >> 16);
00808 tcode[7] = ((terrain.overlay & 0x0000FF00) >> 8);
00809 tcode[8] = (terrain.overlay & 0x000000FF);
00810 } else {
00811
00812
00813 tcode[4] = 0;
00814 }
00815
00816 for(int i = 0; i < 9; ++i) {
00817 if(tcode[i] != 0 && tcode[i] != 0xFF) {
00818 result += tcode[i];
00819 }
00820 if(i == 4 && tcode[i] == 0) {
00821
00822 break;
00823 }
00824 }
00825
00826 return result;
00827 }
00828
00829 static t_terrain string_to_builder_number_(std::string str)
00830 {
00831
00832 const std::string& whitespace = " \t";
00833 str.erase(0, str.find_first_not_of(whitespace));
00834 if(! str.empty()) {
00835 str.erase(str.find_last_not_of(whitespace) + 1);
00836 }
00837
00838
00839 if(str.empty()) {
00840 return t_terrain();
00841 }
00842
00843 const int number = lexical_cast_default(str, -1);
00844 if(number == -1) {
00845
00846
00847
00848 return t_terrain(str[0] << 24, 0);
00849 } else {
00850 return t_terrain(0, number);
00851 }
00852 }
00853
00854 }
00855
00856 #if 0
00857
00858
00859
00860 int main(int argc, char** argv)
00861 {
00862 if(argc > 1) {
00863
00864 if(std::string(argv[1]) == "match" && argc == 4) {
00865 t_translation::t_terrain src = t_translation::read_terrain_code(std::string(argv[2]));
00866
00867 t_translation::t_list dest = t_translation::read_list(std::string(argv[3]));
00868
00869 if(t_translation::terrain_matches(src, dest)) {
00870 std::cout << "Match\n" ;
00871 } else {
00872 std::cout << "No match\n";
00873 }
00874 }
00875 }
00876 }
00877
00878 #endif
00879