terrain_translation.cpp

Go to the documentation of this file.
00001 /* $Id: terrain_translation.cpp 25548 2008-04-04 18:43:57Z mog $ */
00002 /*
00003    Copyright (C) 2006 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file terrain_translation.cpp
00016 //! Routines for terrain-conversion.
00017 
00018 #include "global.hpp"
00019 #include "gettext.hpp"
00020 #include "log.hpp"
00021 #include "terrain_translation.hpp"
00022 #include "serialization/string_utils.hpp"
00023 #include "wml_exception.hpp"
00024 
00025 #include <iostream>
00026 
00027 #define ERR_G  LOG_STREAM(err, general)
00028 #define WRN_G  LOG_STREAM(warn, general)
00029 
00030 namespace t_translation {
00031 
00032 /***************************************************************************************/
00033 // forward declaration of internal functions
00034 
00035     // The low level convertors,
00036     // These function are the ones which know about the internal format.
00037     // All other functions are unaware of the internal format.
00038 
00039     /**
00040      * Get the mask for a single layer.
00041      *
00042      * @param terrain   1 layer of a terrain, might have a wildcard
00043      *
00044      * @return          mask for that layer
00045      */
00046     static t_layer get_layer_mask_(t_layer terrain); //inlined
00047 
00048     /**
00049      * Gets a mask for a terrain, this mask is used for wildcard matching.
00050      *
00051      * @param terrain   the terrain which might have a wildcard
00052      *
00053      * @return          the mask for this terrain
00054      */
00055     static t_terrain get_mask_(const t_terrain& terrain);
00056 
00057     /**
00058      * Converts a string to a layer.
00059      *
00060      * @param str       the terrain string to convert, but needs to be sanitized
00061      *                  so no spaces and only the terrain to convert
00062      *
00063      * @return          the converted layer
00064      */
00065     static t_layer string_to_layer_(const std::string& str);
00066 
00067     /**
00068      * Converts a terrain string to a number.
00069      * @param str               the terrain string with an optional number
00070      * @param start_position    returns the start_position, the caller should set it on -1
00071      *                      and it's only changed it there is a starting position found
00072      * @param filler            if the terrain has only 1 layer then the filler will be used
00073      *                          as the second layer.
00074      *
00075      * @return                  the terrain code found in the string
00076      */
00077     static t_terrain string_to_number_(std::string str, int& start_position, const t_layer filler);
00078     static t_terrain string_to_number_(const std::string& str, const t_layer filler = NO_LAYER);
00079 
00080     /**
00081      * Converts a terrain number to a string
00082      *
00083      * @param terrain               the terrain number to convert
00084      * @param start_position        the starting position, if smaller than 0 it's
00085      *                              ignored else it's written
00086      * @param min_size              padds the results with spaces if required,
00087      *                              until the result has a length of min_size.
00088      * @return                      the converted string, if no starting position
00089      *                              given it's padded to 4 chars else padded to 7 chars
00090      */
00091     static std::string number_to_string_(t_terrain terrain, const int start_position, const size_t min_size);
00092     static std::string number_to_string_(t_terrain terrain, const int start_position = -1);
00093 
00094     /**
00095      * Converts a terrain string to a number for the builder.
00096      * The translation rules differ from the normal conversion rules
00097      *
00098      * @param str   the terrain string
00099      *
00100      * @return      number for the builder map
00101      */
00102     static t_terrain string_to_builder_number_(std::string str);
00103 
00104 /***************************************************************************************/
00105 
00106 const t_terrain OFF_MAP_USER = string_to_number_("_off^_usr");
00107 
00108 const t_terrain VOID_TERRAIN = string_to_number_("_s");
00109 const t_terrain FOGGED = string_to_number_("_f");
00110 
00111 const t_terrain HUMAN_CASTLE = string_to_number_("Ch");
00112 const t_terrain HUMAN_KEEP = string_to_number_("Kh");
00113 const t_terrain SHALLOW_WATER = string_to_number_("Ww");
00114 const t_terrain DEEP_WATER = string_to_number_("Wo");
00115 const t_terrain GRASS_LAND = string_to_number_("Gg");
00116 const t_terrain FOREST = string_to_number_("Gg^Ff");
00117 const t_terrain MOUNTAIN = string_to_number_("Mm");
00118 const t_terrain HILL = string_to_number_("Hh");
00119 
00120 const t_terrain CAVE_WALL = string_to_number_("Xu");
00121 const t_terrain CAVE = string_to_number_("Uu");
00122 const t_terrain UNDERGROUND_VILLAGE = string_to_number_("Uu^Vu");
00123 const t_terrain DWARVEN_CASTLE = string_to_number_("Cud");
00124 const t_terrain DWARVEN_KEEP = string_to_number_("Kud");
00125 
00126 const t_terrain PLUS = string_to_number_("+");
00127 const t_terrain MINUS = string_to_number_("-");
00128 const t_terrain NOT = string_to_number_("!");
00129 const t_terrain STAR = string_to_number_("*");
00130 const t_terrain BASE = string_to_number_("_bas");
00131 
00132 /***************************************************************************************/
00133 
00134 t_terrain::t_terrain(const std::string& b) :
00135     base(string_to_layer_(b)), overlay(NO_LAYER)
00136 {}
00137 
00138 t_terrain::t_terrain(const std::string& b, const t_layer o) :
00139     base(string_to_layer_(b)), overlay(o)
00140 {}
00141 
00142 t_terrain::t_terrain(const std::string& b, const std::string& o) :
00143     base(string_to_layer_(b)), overlay(string_to_layer_(o))
00144 {}
00145 
00146 t_match::t_match() :
00147     terrain(),
00148     mask(),
00149     masked_terrain(),
00150     has_wildcard(false),
00151     is_empty(true)
00152 {}
00153 
00154 t_match::t_match(const std::string& str, const t_layer filler) :
00155     terrain(t_translation::read_list(str, filler)),
00156     mask(),
00157     masked_terrain(),
00158     has_wildcard(t_translation::has_wildcard(terrain)),
00159     is_empty(terrain.empty())
00160 
00161 {
00162     mask.resize(terrain.size());
00163     masked_terrain.resize(terrain.size());
00164 
00165     for(size_t i = 0; i < terrain.size(); i++) {
00166         mask[i] = t_translation::get_mask_(terrain[i]);
00167         masked_terrain[i] = mask[i] & terrain[i];
00168     }
00169 }
00170 
00171 t_match::t_match(const t_terrain& tcode):
00172     terrain(t_list(1, tcode)),
00173     mask(),
00174     masked_terrain(),
00175     has_wildcard(t_translation::has_wildcard(terrain)),
00176     is_empty(terrain.empty())
00177 {
00178     mask.resize(terrain.size());
00179     masked_terrain.resize(terrain.size());
00180 
00181     for(size_t i = 0; i < terrain.size(); i++) {
00182         mask[i] = t_translation::get_mask_(terrain[i]);
00183         masked_terrain[i] = mask[i] & terrain[i];
00184     }
00185 }
00186 
00187 t_terrain read_terrain_code(const std::string& str, const t_layer filler)
00188 {
00189     return string_to_number_(str, filler);
00190 }
00191 
00192 std::string write_terrain_code(const t_terrain& tcode)
00193 {
00194     return number_to_string_(tcode);
00195 }
00196 
00197 t_list read_list(const std::string& str, const t_layer filler)
00198 {
00199     // Handle an empty string
00200     t_list result;
00201 
00202     if(str.empty()) {
00203         return result;
00204     }
00205 
00206     size_t offset = 0;
00207     while(offset < str.length()) {
00208 
00209         // Get a terrain chunk
00210         const std::string separators = ",";
00211         const size_t pos_separator = str.find_first_of(separators, offset);
00212         const std::string terrain = str.substr(offset, pos_separator - offset);
00213 
00214         // Process the chunk
00215         const t_terrain tile = string_to_number_(terrain, filler);
00216 
00217         // Add the resulting terrain number
00218         result.push_back(tile);
00219 
00220         // Evaluate the separator
00221         if(pos_separator == std::string::npos) {
00222             offset =  str.length();
00223         } else {
00224             offset = pos_separator + 1;
00225         }
00226     }
00227 
00228     return result;
00229 }
00230 
00231 std::string write_list(const t_list& list)
00232 {
00233     std::stringstream result;
00234 
00235     t_list::const_iterator itor = list.begin();
00236     for( ; itor != list.end(); ++itor) {
00237         if(itor == list.begin()) {
00238             result << number_to_string_(*itor);
00239         } else {
00240             result << ", " << number_to_string_(*itor);
00241         }
00242     }
00243 
00244     return result.str();
00245 }
00246 
00247 t_map read_game_map(const std::string& str, std::map<int, coordinate>& starting_positions)
00248 {
00249     t_map result;
00250 
00251     size_t offset = 0;
00252     size_t x = 0, y = 0, width = 0;
00253 
00254     // Skip the leading newlines
00255     while(offset < str.length() && utils::isnewline(str[offset])) {
00256         ++offset;
00257     }
00258 
00259     // Did we get an empty map?
00260     if((offset + 1) >= str.length()) {
00261         return result;
00262     }
00263 
00264     while(offset < str.length()) {
00265 
00266         // Get a terrain chunk
00267         const std::string separators = ",\n\r";
00268         const size_t pos_separator = str.find_first_of(separators, offset);
00269         const std::string terrain = str.substr(offset, pos_separator - offset);
00270 
00271         // Process the chunk
00272         int starting_position = -1;
00273         // The gamemap never has a wildcard
00274         const t_terrain tile = string_to_number_(terrain, starting_position, NO_LAYER);
00275 
00276         // Add to the resulting starting position
00277         if(starting_position != -1) {
00278             if(starting_positions.find(starting_position) != starting_positions.end()) {
00279                 // Redefine existion position
00280                 WRN_G << "Starting position " << starting_position << " is redefined.\n";
00281                 starting_positions[starting_position].x = x;
00282                 starting_positions[starting_position].y = y;
00283             } else {
00284                 // Add new position
00285                 const struct coordinate coord = {x, y};
00286                 starting_positions.insert(std::pair<int, coordinate>(starting_position, coord));
00287             }
00288         }
00289 
00290         // Make space for the new item
00291         // NOTE we increase the vector every loop for every x and y.
00292         // Profiling with an increase of y with 256 items didn't show
00293         // an significant speed increase.
00294         // So didn't rework the system to allocate larger vectors at once.
00295         if(result.size() <= x) {
00296             result.resize(x + 1);
00297         }
00298         if(result[x].size() <= y) {
00299             result[x].resize(y + 1);
00300         }
00301 
00302         // Add the resulting terrain number
00303         result[x][y] = tile;
00304 
00305         // Evaluate the separator
00306         if(utils::isnewline(str[pos_separator]) || pos_separator == std::string::npos) {
00307             // the first line we set the with the other lines we check the width
00308             if(y == 0) {
00309                 // x contains the offset in the map
00310                 width = x + 1;
00311             } else {
00312                 if((x + 1) != width ) {
00313                     ERR_G << "Map not a rectangle error occured at line offset " << y << " position offset " << x << "\n";
00314                     throw error("Map not a rectangle.");
00315                 }
00316             }
00317 
00318             // Prepare next iteration
00319             ++y;
00320             x = 0;
00321 
00322             // Avoid in infinite loop if the last line ends without an EOL
00323             if(pos_separator == std::string::npos) {
00324                 offset = str.length();
00325 
00326             } else {
00327 
00328                 offset = pos_separator + 1;
00329                 // Skip the following newlines
00330                 while(offset < str.length() && utils::isnewline(str[offset])) {
00331                     ++offset;
00332                 }
00333             }
00334 
00335         } else {
00336             ++x;
00337             offset = pos_separator + 1;
00338         }
00339 
00340     }
00341 
00342     if(x != 0 && (x + 1) != width) {
00343         ERR_G << "Map not a rectangle error occured at the end\n";
00344         throw error("Map not a rectangle.");
00345     }
00346 
00347     return result;
00348 }
00349 
00350 std::string write_game_map(const t_map& map, std::map<int, coordinate> starting_positions)
00351 {
00352     std::stringstream str;
00353 
00354     for(size_t y = 0; y < map[0].size(); ++y) {
00355         for(size_t x = 0; x < map.size(); ++x) {
00356 
00357             // If the current location is a starting position,
00358             // it needs to be added to the terrain.
00359             // After it's found it can't be found again,
00360             // so the location is removed from the map.
00361             std::map<int, coordinate>::iterator itor = starting_positions.begin();
00362             int starting_position = -1;
00363             for(; itor != starting_positions.end(); ++itor) {
00364                 if(itor->second.x == x && itor->second.y == y) {
00365                     starting_position = itor->first;
00366                     starting_positions.erase(itor);
00367                     break;
00368                 }
00369             }
00370 
00371             // Add the separator
00372             if(x != 0) {
00373                 str << ", ";
00374             }
00375             str << number_to_string_(map[x][y], starting_position, 12);
00376         }
00377 
00378         str << "\n";
00379     }
00380 
00381     return str.str();
00382 }
00383 
00384 bool terrain_matches(const t_terrain& src, const t_terrain& dest)
00385 {
00386     return terrain_matches(src, t_list(1, dest));
00387 }
00388 
00389 bool terrain_matches(const t_terrain& src, const t_list& dest)
00390 {
00391     // NOTE we impose some code duplication.
00392     // It could have been rewritten to get a match structure
00393     // and then call the version with the match structure.
00394     // IMO that's some extra overhead to this function
00395     // which is not required. Hence the two versions
00396     if(dest.empty()) {
00397         return false;
00398     }
00399 
00400 #if 0
00401     std::cerr << std::hex << "src = " << src.base << "^" << src.overlay << "\t"
00402         << src_mask.base << "^" << src_mask.overlay << "\t"
00403         << masked_src.base << "^" << masked_src.overlay << "\t"
00404         << src_has_wildcard << "\n";
00405 #endif
00406 
00407     bool result = true;
00408     t_list::const_iterator itor = dest.begin();
00409 
00410     // Try to match the terrains if matched jump out of the loop.
00411     for(; itor != dest.end(); ++itor) {
00412 
00413         // Match wildcard
00414         if(*itor == STAR) {
00415             return result;
00416         }
00417 
00418         // Match inverse symbol
00419         if(*itor == NOT) {
00420             result = !result;
00421             continue;
00422         }
00423 
00424         // Full match
00425         if(src == *itor) {
00426             return result;
00427         }
00428 
00429         // Does the destination wildcard match
00430         const t_terrain dest_mask = get_mask_(*itor);
00431         const t_terrain masked_dest = (*itor & dest_mask);
00432         const bool dest_has_wildcard = has_wildcard(*itor);
00433 #if 0
00434         std::cerr << std::hex << "dest= "
00435             << itor->base << "^" << itor->overlay  << "\t"
00436             << dest_mask.base << "^" << dest_mask.overlay << "\t"
00437             << masked_dest.base << "^" << masked_dest.overlay << "\t"
00438             << dest_has_wildcard << "\n";
00439 #endif
00440         if(dest_has_wildcard &&
00441                 (src.base & dest_mask.base) == masked_dest.base &&
00442                 (src.overlay & dest_mask.overlay) == masked_dest.overlay) {
00443             return result;
00444         }
00445 
00446 /* Test code */ /*
00447         if(src_has_wildcard && dest_has_wildcard && (
00448                 (
00449                     get_layer_mask_(itor->base) != NO_LAYER &&
00450                     get_layer_mask_(src.overlay) != NO_LAYER &&
00451                     (src.base & dest_mask.base) == masked_dest.base &&
00452                     (itor->overlay & src_mask.overlay) == masked_src.overlay
00453                 ) || (
00454                     get_layer_mask_(itor->overlay) != NO_LAYER &&
00455                     get_layer_mask_(src.base) != NO_LAYER &&
00456                     (src.overlay & dest_mask.overlay) == masked_dest.overlay &&
00457                     (itor->base & src_mask.base) == masked_src.base
00458                 ))) {
00459 
00460             return result;
00461         }
00462 */
00463     }
00464 
00465     // No match, return the inverse of the result
00466     return !result;
00467 }
00468 
00469 // This routine is used for the terrain building,
00470 // so it's one of the delays while loading a map.
00471 // This routine is optimized a bit at the loss of readability.
00472 bool terrain_matches(const t_terrain& src, const t_match& dest)
00473 {
00474     if(dest.is_empty) {
00475         return false;
00476     }
00477 
00478     bool result = true;
00479 
00480     // Try to match the terrains if matched jump out of the loop.
00481     // We loop on the dest.terrain since the iterator is faster than operator[].
00482     // The i holds the value for operator[].
00483     // Since dest.mask and dest.masked_terrain need to be in sync,
00484     // they are less often looked up, so no iterator for them.
00485     size_t i = 0;
00486     t_list::const_iterator end = dest.terrain.end();
00487     for(t_list::const_iterator terrain_itor = dest.terrain.begin();
00488             terrain_itor != end;
00489             ++i, ++terrain_itor) {
00490 
00491         // Match wildcard
00492         if(*terrain_itor == STAR) {
00493             return result;
00494         }
00495 
00496         // Match inverse symbol
00497         if(*terrain_itor == NOT) {
00498             result = !result;
00499             continue;
00500         }
00501 
00502         // Full match
00503         if(*terrain_itor == src) {
00504             return result;
00505         }
00506 
00507         // Does the destination wildcard match
00508         if(dest.has_wildcard &&
00509                 (src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
00510                 (src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay) {
00511             return result;
00512         }
00513 
00514 /* Test code */ /*
00515         if(src_has_wildcard && has_wildcard(*terrain_itor) && (
00516                 (
00517                     get_layer_mask_(terrain_itor->base) != NO_LAYER &&
00518                     get_layer_mask_(src.overlay) != NO_LAYER &&
00519                     (src.base & dest.mask[i].base) == dest.masked_terrain[i].base &&
00520                     (terrain_itor->overlay & src_mask.overlay) == masked_src.overlay
00521                 ) || (
00522                     get_layer_mask_(terrain_itor->overlay) != NO_LAYER &&
00523                     get_layer_mask_(src.base) != NO_LAYER &&
00524                     (src.overlay & dest.mask[i].overlay) == dest.masked_terrain[i].overlay &&
00525                     (terrain_itor->base & src_mask.base) == masked_src.base
00526                 ))) {
00527 
00528             return result;
00529         }
00530 */
00531     }
00532 
00533     // No match, return the inverse of the result
00534     return !result;
00535 }
00536 
00537 bool has_wildcard(const t_terrain& tcode)
00538 {
00539     if(tcode.overlay == NO_LAYER) {
00540         return get_layer_mask_(tcode.base) != NO_LAYER;
00541     } else {
00542         return get_layer_mask_(tcode.base) != NO_LAYER || get_layer_mask_(tcode.overlay) != NO_LAYER;
00543     }
00544 }
00545 
00546 bool has_wildcard(const t_list& list)
00547 {
00548     if(list.empty()) {
00549         return false;
00550     }
00551 
00552     // Test all items for a wildcard
00553     t_list::const_iterator itor = list.begin();
00554     for(; itor != list.end(); ++itor) {
00555         if(has_wildcard(*itor)) {
00556             return true;
00557         }
00558     }
00559 
00560     // No wildcard found
00561     return false;
00562 }
00563 
00564 t_map read_builder_map(const std::string& str)
00565 {
00566     size_t offset = 0;
00567     t_map result;
00568 
00569     // Skip the leading newlines
00570     while(offset < str.length() && utils::isnewline(str[offset])) {
00571         ++offset;
00572     }
00573 
00574     // Did we get an empty map?
00575     if((offset + 1) >= str.length()) {
00576         return result;
00577     }
00578 
00579     size_t x = 0, y = 0;
00580     while(offset < str.length()) {
00581 
00582         // Get a terrain chunk
00583         const std::string separators = ",\n\r";
00584         const size_t pos_separator = str.find_first_of(separators, offset);
00585 
00586         std::string terrain = "";
00587         // Make sure we didn't hit an empty chunk
00588         // which is allowed
00589         if(pos_separator != offset) {
00590             terrain = str.substr(offset, pos_separator - offset);
00591         }
00592 
00593         // Process the chunk
00594         const t_terrain tile = string_to_builder_number_(terrain);
00595 
00596         // Make space for the new item
00597         if(result.size() <= y) {
00598             result.resize(y + 1);
00599         }
00600         if(result[y].size() <= x) {
00601             result[y].resize(x + 1);
00602         }
00603 
00604         // Add the resulting terrain number,
00605         result[y][x] = tile;
00606 
00607         // evaluate the separator
00608         if(pos_separator == std::string::npos) {
00609             // Probably not required to change the value,
00610             // but be sure the case should be handled at least.
00611             // I'm not sure how it is defined in the standard,
00612             // but here it's defined at max u32 which with +1 gives 0
00613             // and make a nice infinite loop.
00614             offset = str.length();
00615         } else if(utils::isnewline(str[pos_separator])) {
00616             // Prepare next iteration
00617             ++y;
00618             x = 0;
00619 
00620             offset =  pos_separator + 1;
00621             // Skip the following newlines
00622             while(offset < str.length() && utils::isnewline(str[offset])) {
00623                 ++offset;
00624             }
00625 
00626         } else {
00627             ++x;
00628             offset = pos_separator + 1;
00629         }
00630 
00631     }
00632 
00633     return result;
00634 }
00635 
00636 /***************************************************************************************/
00637 // Internal
00638 
00639 inline t_layer get_layer_mask_(t_layer terrain)
00640 {
00641     // Test for the star 0x2A in every position
00642     // and return the appropriate mask
00643 /*
00644  *  This is what the code intents to do, but in order to gain some more
00645  *  speed it's changed to the code below, which does the same but faster.
00646  *  This routine is used often in the builder and the speedup is noticable. */
00647     if((terrain & 0xFF000000) == 0x2A000000) return 0x00000000;
00648     if((terrain & 0x00FF0000) == 0x002A0000) return 0xFF000000;
00649     if((terrain & 0x0000FF00) == 0x00002A00) return 0xFFFF0000;
00650     if((terrain & 0x000000FF) == 0x0000002A) return 0xFFFFFF00;
00651 
00652 /*
00653     Uint8 *ptr = (Uint8 *) &terrain;
00654 
00655     if(ptr[3] == 0x2A) return 0x00000000;
00656     if(ptr[2] == 0x2A) return 0xFF000000;
00657     if(ptr[1] == 0x2A) return 0xFFFF0000;
00658     if(ptr[0] == 0x2A) return 0xFFFFFF00;
00659 */
00660     // no star found return the default
00661     return 0xFFFFFFFF;
00662 }
00663 
00664 static t_terrain get_mask_(const t_terrain& terrain)
00665 {
00666     if(terrain.overlay == NO_LAYER) {
00667         return t_terrain(get_layer_mask_(terrain.base), 0xFFFFFFFF);
00668     } else {
00669         return t_terrain(get_layer_mask_(terrain.base), get_layer_mask_(terrain.overlay));
00670     }
00671 }
00672 
00673 static t_layer string_to_layer_(const std::string& str)
00674 {
00675     if (str.size() == 0)
00676         return NO_LAYER;
00677 
00678     t_layer result = 0;
00679 
00680     // Validate the string (Note at the moment the error is caught at another
00681     // location and sending a lg::wml_error() but that can be replaced later)
00682     VALIDATE(str.size() <= 4, _("A terrain with a string with more "
00683         "than 4 characters has been found, the affected terrain is :") + str);
00684 
00685     // The conversion to int puts the first char
00686     // in the highest part of the number.
00687     // This will make the wildcard matching
00688     // later on a bit easier.
00689     for(size_t i = 0; i < 4; ++i) {
00690         const unsigned char c = (i < str.length()) ? str[i] : 0;
00691 
00692         // Clearing the lower area is a nop on i == 0
00693         // so no need for if statement
00694         result <<= 8;
00695 
00696         // Add the result
00697         result += c;
00698     }
00699 
00700     return result;
00701 }
00702 
00703 static t_terrain string_to_number_(const std::string& str, const t_layer filler) {
00704     int dummy = -1;
00705     return string_to_number_(str, dummy, filler);
00706 }
00707 
00708 static t_terrain string_to_number_(std::string str, int& start_position, const t_layer filler)
00709 {
00710     t_terrain result;
00711 
00712     // Need to store the orginal string for the error handling.
00713     // This has been made to avoid the assertion failure
00714     // which happens often and is not too user friendly.
00715     const std::string input(str);
00716 
00717     // Strip the spaces around us
00718     const std::string& whitespace = " \t";
00719     str.erase(0, str.find_first_not_of(whitespace));
00720     str.erase(str.find_last_not_of(whitespace) + 1);
00721     if(str.empty()) {
00722         return result;
00723     }
00724 
00725     // Split if we have 1 space inside
00726     size_t offset = str.find(' ', 0);
00727     if(offset != std::string::npos) {
00728         try {
00729             start_position = lexical_cast<int>(str.substr(0, offset));
00730         } catch(bad_lexical_cast&) {
00731             goto terrain_error;
00732         }
00733         str.erase(0, offset + 1);
00734     }
00735 
00736     offset = str.find('^', 0);
00737     if(offset !=  std::string::npos) {
00738         // If either string is longer than 4 characters bail out
00739         if(offset > 4 || (str.size() - offset) > 5) {
00740             goto terrain_error;
00741         }
00742         const std::string base_str(str, 0, offset);
00743         const std::string overlay_str(str, offset + 1, str.size());
00744         result = t_terrain(base_str, overlay_str);
00745     } else {
00746         // If the string is longer than 4 characters bail out
00747         if(str.size() > 4) {
00748             goto terrain_error;
00749         }
00750         result = t_terrain(str, filler);
00751 
00752         // Ugly hack
00753         if(filler == WILDCARD && (result.base == NOT.base ||
00754                 result.base == STAR.base)) {
00755 
00756             result.overlay = NO_LAYER;
00757         }
00758     }
00759 
00760     return result;
00761 
00762 terrain_error:
00763     // When this string is removed, also test
00764     // whether the gettext include is still required
00765     //! @todo 1.5 remove this test and let the wml_exception to the test
00766     lg::wml_error << _("Invalid terrain found probably an 1.2 terrain format, "
00767         "terrain = ") << input << '\n';
00768     return VOID_TERRAIN;
00769 }
00770 
00771 static std::string number_to_string_(t_terrain terrain, const int start_position)
00772 {
00773     std::string result = "";
00774 
00775     // Insert the start position
00776     if(start_position > 0) {
00777         result = str_cast(start_position) + " ";
00778     }
00779 
00780     // Insert the terrain tcode
00781     unsigned char tcode[9];
00782     tcode[0] = ((terrain.base & 0xFF000000) >> 24);
00783     tcode[1] = ((terrain.base & 0x00FF0000) >> 16);
00784     tcode[2] = ((terrain.base & 0x0000FF00) >> 8);
00785     tcode[3] =  (terrain.base & 0x000000FF);
00786 
00787     if(terrain.overlay != NO_LAYER) {
00788         tcode[4] = '^'; //the layer separator
00789         tcode[5] = ((terrain.overlay & 0xFF000000) >> 24);
00790         tcode[6] = ((terrain.overlay & 0x00FF0000) >> 16);
00791         tcode[7] = ((terrain.overlay & 0x0000FF00) >> 8);
00792         tcode[8] =  (terrain.overlay & 0x000000FF);
00793     } else {
00794         // If no second layer, the second layer won't be written,
00795         // so no need to initialize that part of the array
00796         tcode[4] = 0;
00797     }
00798 
00799     for(int i = 0; i < 9; ++i) {
00800         if(tcode[i] != 0 && tcode[i] != 0xFF) {
00801             result += tcode[i];
00802         }
00803         if(i == 4 && tcode[i] == 0) {
00804             // no layer, stop
00805             break;
00806         }
00807     }
00808 
00809     return result;
00810 }
00811 
00812 static std::string number_to_string_(t_terrain terrain, const int start_position, const size_t min_size)
00813 {
00814     std::string result = number_to_string_(terrain, start_position);
00815     if(result.size() < min_size) {
00816         result.resize(min_size, ' ');
00817     }
00818 
00819     return result;
00820 }
00821 
00822 static t_terrain string_to_builder_number_(std::string str)
00823 {
00824     // Strip the spaces around us
00825     const std::string& whitespace = " \t";
00826     str.erase(0, str.find_first_not_of(whitespace));
00827     if(! str.empty()) {
00828         str.erase(str.find_last_not_of(whitespace) + 1);
00829     }
00830 
00831     // Empty string is allowed here, so handle it
00832     if(str.empty()) {
00833         return t_terrain();
00834     }
00835 
00836     const int number = lexical_cast_default(str, -1);
00837     if(number == -1) {
00838         // At this point we have a single char
00839         // which should be interpreted by the
00840         // map builder, so return this number
00841         return t_terrain(str[0] << 24, 0);
00842     } else {
00843         return t_terrain(0, number);
00844     }
00845 }
00846 
00847 } // end namespace t_translation
00848 
00849 #if 0
00850 // small helper rule to test the matching rules
00851 // building rule
00852 // make terrain_translation.o &&  g++ terrain_translation.o libwesnoth-core.a -lSDL -o terrain_translation
00853 int main(int argc, char** argv)
00854 {
00855     if(argc > 1) {
00856 
00857         if(std::string(argv[1]) == "match" && argc == 4) {
00858             t_translation::t_terrain src = t_translation::read_terrain_code(std::string(argv[2]));
00859 
00860             t_translation::t_list dest = t_translation::read_list(std::string(argv[3]));
00861 
00862             if(t_translation::terrain_matches(src, dest)) {
00863                 std::cout << "Match\n" ;
00864             } else {
00865                 std::cout << "No match\n";
00866             }
00867         }
00868     }
00869 }
00870 
00871 #endif
00872 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs