terrain_translation.cpp

Go to the documentation of this file.
00001 /* $Id: terrain_translation.cpp 53861 2012-04-08 17:35:14Z mordante $ */
00002 /*
00003    Copyright (C) 2006 - 2012 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  *  @file
00018  *  Routines for terrain-conversion.
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; //TODO make this overridable by the user without having to rebuild
00039     }
00040 
00041 /***************************************************************************************/
00042 // forward declaration of internal functions
00043 
00044     // The low level convertors,
00045     // These function are the ones which know about the internal format.
00046     // All other functions are unaware of the internal format.
00047 
00048     /**
00049      * Get the mask for a single layer.
00050      *
00051      * @param terrain   1 layer of a terrain, might have a wildcard.
00052      *
00053      * @return          Mask for that layer.
00054      */
00055     static t_layer get_layer_mask_(t_layer terrain); //inlined
00056 
00057     /**
00058      * Gets a mask for a terrain, this mask is used for wildcard matching.
00059      *
00060      * @param terrain   The terrain which might have a wildcard.
00061      *
00062      * @return          The mask for this terrain.
00063      */
00064     static t_terrain get_mask_(const t_terrain& terrain);
00065 
00066     /**
00067      * Converts a string to a layer.
00068      *
00069      * @param str       The terrain string to convert, but needs to be
00070      *                  sanitized so no spaces and only the terrain to convert.
00071      *
00072      * @return          The converted layer.
00073      */
00074     static t_layer string_to_layer_(const std::string& str);
00075 
00076     /**
00077      * Converts a terrain string to a number.
00078      * @param str               The terrain string with an optional number.
00079      * @param start_position    Returns the start_position, the caller should
00080      *                          set it on -1 and it's only changed it there is
00081      *                          a starting position found.
00082      * @param filler            If the terrain has only 1 layer then the filler
00083      *                          will be used as the second layer.
00084      *
00085      * @return                  The terrain code found in the string if no
00086      *                          valid terrain is found VOID will be returned.
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      * Converts a terrain number to a string
00093      *
00094      * @param terrain               The terrain number to convert.
00095      * @param start_position        The starting position, if smaller than 0
00096      *                              it's ignored else it's written.
00097      * @param min_size              Padds the results with spaces if required,
00098      *                              until the result has a length of min_size.
00099      *
00100      * @return                      The converted string, if no starting
00101      *                              position given it's padded to 4 chars else
00102      *                              padded to 7 chars.
00103      */
00104     static std::string number_to_string_(t_terrain terrain, const int start_position = -1);
00105 
00106     /**
00107      * Converts a terrain string to a number for the builder.
00108      * The translation rules differ from the normal conversion rules
00109      *
00110      * @param str   The terrain string.
00111      *
00112      * @return      Number for the builder map.
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*"); //excluding impassable mountains
00147 const t_match ALL_SWAMPS("!,*^V*,*^B*,!,S*"); //excluding swamp villages and bridges
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     // Handle an empty string
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         // Get a terrain chunk
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         // Process the chunk
00240         const t_terrain tile = string_to_number_(terrain, filler);
00241 
00242         // Add the resulting terrain number
00243         result.push_back(tile);
00244 
00245         // Evaluate the separator
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     // Skip the leading newlines
00280     while(offset < str.length() && utils::isnewline(str[offset])) {
00281         ++offset;
00282     }
00283 
00284     // Did we get an empty map?
00285     if((offset + 1) >= str.length()) {
00286         return result;
00287     }
00288 
00289     while(offset < str.length()) {
00290 
00291         // Get a terrain chunk
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         // Process the chunk
00297         int starting_position = -1;
00298         // The gamemap never has a wildcard
00299         const t_terrain tile = string_to_number_(terrain, starting_position, NO_LAYER);
00300 
00301         // Add to the resulting starting position
00302         if(starting_position != -1) {
00303             if(starting_positions.find(starting_position) != starting_positions.end()) {
00304                 // Redefine existion position
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                 // Add new position
00310                 const struct coordinate coord(x, y);
00311                 starting_positions.insert(std::pair<int, coordinate>(starting_position, coord));
00312             }
00313         }
00314 
00315         // Make space for the new item
00316         // NOTE we increase the vector every loop for every x and y.
00317         // Profiling with an increase of y with 256 items didn't show
00318         // an significant speed increase.
00319         // So didn't rework the system to allocate larger vectors at once.
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         // Add the resulting terrain number
00328         result[x][y] = tile;
00329 
00330         // Evaluate the separator
00331         if(pos_separator == std::string::npos || utils::isnewline(str[pos_separator])) {
00332             // the first line we set the with the other lines we check the width
00333             if(y == 0) {
00334                 // x contains the offset in the map
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             // Prepare next iteration
00348             ++y;
00349             x = 0;
00350 
00351             // Avoid in infinite loop if the last line ends without an EOL
00352             if(pos_separator == std::string::npos) {
00353                 offset = str.length();
00354 
00355             } else {
00356 
00357                 offset = pos_separator + 1;
00358                 // Skip the following newlines
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             // If the current location is a starting position,
00391             // it needs to be added to the terrain.
00392             // After it's found it can't be found again,
00393             // so the location is removed from the map.
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             // Add the separator
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     // NOTE we impose some code duplication.
00426     // It could have been rewritten to get a match structure
00427     // and then call the version with the match structure.
00428     // IMO that's some extra overhead to this function
00429     // which is not required. Hence the two versions
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     // Try to match the terrains if matched jump out of the loop.
00445     for(; itor != dest.end(); ++itor) {
00446 
00447         // Match wildcard
00448         if(*itor == STAR) {
00449             return result;
00450         }
00451 
00452         // Match inverse symbol
00453         if(*itor == NOT) {
00454             result = !result;
00455             continue;
00456         }
00457 
00458         // Full match
00459         if(src == *itor) {
00460             return result;
00461         }
00462 
00463         // Does the destination wildcard match
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 /* Test code */ /*
00481         if(src_has_wildcard && dest_has_wildcard && (
00482                 (
00483                     get_layer_mask_(itor->base) != NO_LAYER &&
00484                     get_layer_mask_(src.overlay) != NO_LAYER &&
00485                     (src.base & dest_mask.base) == masked_dest.base &&
00486                     (itor->overlay & src_mask.overlay) == masked_src.overlay
00487                 ) || (
00488                     get_layer_mask_(itor->overlay) != NO_LAYER &&
00489                     get_layer_mask_(src.base) != NO_LAYER &&
00490                     (src.overlay & dest_mask.overlay) == masked_dest.overlay &&
00491                     (itor->base & src_mask.base) == masked_src.base
00492                 ))) {
00493 
00494             return result;
00495         }
00496 */
00497     }
00498 
00499     // No match, return the inverse of the result
00500     return !result;
00501 }
00502 
00503 // This routine is used for the terrain building,
00504 // so it's one of the delays while loading a map.
00505 // This routine is optimized a bit at the loss of readability.
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     // Try to match the terrains if matched jump out of the loop.
00515     // We loop on the dest.terrain since the iterator is faster than operator[].
00516     // The i holds the value for operator[].
00517     // Since dest.mask and dest.masked_terrain need to be in sync,
00518     // they are less often looked up, so no iterator for them.
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         // Match wildcard
00526         if(*terrain_itor == STAR) {
00527             return result;
00528         }
00529 
00530         // Match inverse symbol
00531         if(*terrain_itor == NOT) {
00532             result = !result;
00533             continue;
00534         }
00535 
00536         // Full match
00537         if(*terrain_itor == src) {
00538             return result;
00539         }
00540 
00541         // Does the destination wildcard match
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 /* Test code */ /*
00549         if(src_has_wildcard && has_wildcard(*terrain_itor) && (
00550                 (
00551                     get_layer_mask_(terrain_itor->base) != NO_LAYER &&
00552                     get_layer_mask_(src.overlay) != NO_LAYER &&
00553                     (src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
00554                     (terrain_itor->overlay & src_mask.overlay) == masked_src.overlay
00555                 ) || (
00556                     get_layer_mask_(terrain_itor->overlay) != NO_LAYER &&
00557                     get_layer_mask_(src.base) != NO_LAYER &&
00558                     (src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay &&
00559                     (terrain_itor->base & src_mask.base) == masked_src.base
00560                 ))) {
00561 
00562             return result;
00563         }
00564 */
00565     }
00566 
00567     // No match, return the inverse of the result
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     // Test all items for a wildcard
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     // No wildcard found
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     // Skip the leading newlines
00604     while(offset < str.length() && utils::isnewline(str[offset])) {
00605         ++offset;
00606     }
00607 
00608     // Did we get an empty map?
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         // Get a terrain chunk
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         // Make sure we didn't hit an empty chunk
00622         // which is allowed
00623         if(pos_separator != offset) {
00624             terrain = str.substr(offset, pos_separator - offset);
00625         }
00626 
00627         // Process the chunk
00628         const t_terrain tile = string_to_builder_number_(terrain);
00629 
00630         // Make space for the new item
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         // Add the resulting terrain number,
00639         result[y][x] = tile;
00640 
00641         // evaluate the separator
00642         if(pos_separator == std::string::npos) {
00643             // Probably not required to change the value,
00644             // but be sure the case should be handled at least.
00645             // I'm not sure how it is defined in the standard,
00646             // but here it's defined at max u32 which with +1 gives 0
00647             // and make a nice infinite loop.
00648             offset = str.length();
00649         } else if(utils::isnewline(str[pos_separator])) {
00650             // Prepare next iteration
00651             ++y;
00652             x = 0;
00653 
00654             offset =  pos_separator + 1;
00655             // Skip the following newlines
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 // Internal
00672 
00673 inline t_layer get_layer_mask_(t_layer terrain)
00674 {
00675     // Test for the star 0x2A in every position
00676     // and return the appropriate mask
00677 /*
00678  *  This is what the code intents to do, but in order to gain some more
00679  *  speed it's changed to the code below, which does the same but faster.
00680  *  This routine is used often in the builder and the speedup is noticeable. */
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     Uint8 *ptr = (Uint8 *) &terrain;
00688 
00689     if(ptr[3] == 0x2A) return 0x00000000;
00690     if(ptr[2] == 0x2A) return 0xFF000000;
00691     if(ptr[1] == 0x2A) return 0xFFFF0000;
00692     if(ptr[0] == 0x2A) return 0xFFFFFF00;
00693 */
00694     // no star found return the default
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     // Validate the string
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     // The conversion to int puts the first char
00719     // in the highest part of the number.
00720     // This will make the wildcard matching
00721     // later on a bit easier.
00722     for(size_t i = 0; i < 4; ++i) {
00723         const unsigned char c = (i < str.length()) ? str[i] : 0;
00724 
00725         // Clearing the lower area is a nop on i == 0
00726         // so no need for if statement
00727         result <<= 8;
00728 
00729         // Add the result
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     // Strip the spaces around us
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     // Split if we have 1 space inside
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         // Ugly hack
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     // Insert the start position
00788     if(start_position > 0) {
00789         result = str_cast(start_position) + " ";
00790     }
00791 
00792     /*
00793      * The initialisation of tcode is done to make gcc-4.7 happy. Otherwise it
00794      * some uninitialised fields might be used. Its analysis are wrong, but
00795      * initialise to keep it happy.
00796      */
00797     unsigned char tcode[9] = {0};
00798     // Insert the terrain tcode
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] = '^'; //the layer separator
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         // If no second layer, the second layer won't be written,
00812         // so no need to initialize that part of the array
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             // no layer, stop
00822             break;
00823         }
00824     }
00825 
00826     return result;
00827 }
00828 
00829 static t_terrain string_to_builder_number_(std::string str)
00830 {
00831     // Strip the spaces around us
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     // Empty string is allowed here, so handle it
00839     if(str.empty()) {
00840         return t_terrain();
00841     }
00842 
00843     const int number = lexical_cast_default(str, -1);
00844     if(number == -1) {
00845         // At this point we have a single char
00846         // which should be interpreted by the
00847         // map builder, so return this number
00848         return t_terrain(str[0] << 24, 0);
00849     } else {
00850         return t_terrain(0, number);
00851     }
00852 }
00853 
00854 } // end namespace t_translation
00855 
00856 #if 0
00857 // small helper rule to test the matching rules
00858 // building rule
00859 // make terrain_translation.o &&  g++ terrain_translation.o libwesnoth-core.a -lSDL -o terrain_translation
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:11 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs