Index: src/settings.cpp =================================================================== --- src/settings.cpp (revision 11719) +++ src/settings.cpp (working copy) @@ -47,6 +47,7 @@ #endif #include "spritecache.h" #include "transparency.h" +#include "map_type.h" /** The patch values that are used for new games and/or modified in config file */ Patches _patches_newgame; @@ -1353,8 +1354,8 @@ SDT_VAR(Patches, window_snap_radius, SLE_UINT8, S,D0, 10, 1, 32, 0, STR_CONFIG_PATCHES_SNAP_RADIUS, NULL), SDT_BOOL(Patches, invisible_trees, S, 0, false, STR_CONFIG_PATCHES_INVISIBLE_TREES, RedrawScreen), SDT_BOOL(Patches, population_in_label, S, 0, true, STR_CONFIG_PATCHES_POPULATION_IN_LABEL, PopulationInLabelActive), - SDT_VAR(Patches, map_x, SLE_UINT8, S, 0, 8, 6, 11, 0, STR_CONFIG_PATCHES_MAP_X, NULL), - SDT_VAR(Patches, map_y, SLE_UINT8, S, 0, 8, 6, 11, 0, STR_CONFIG_PATCHES_MAP_Y, NULL), + SDT_VAR(Patches, map_x, SLE_UINT8, S, 0, 8, MIN_MAP_SIZE_BITS, MAX_MAP_SIZE_BITS, 0, STR_CONFIG_PATCHES_MAP_X, NULL), + SDT_VAR(Patches, map_y, SLE_UINT8, S, 0, 8, MIN_MAP_SIZE_BITS, MAX_MAP_SIZE_BITS, 0, STR_CONFIG_PATCHES_MAP_Y, NULL), SDT_BOOL(Patches, link_terraform_toolbar, S, 0, false, STR_CONFIG_PATCHES_LINK_TERRAFORM_TOOLBAR,NULL), SDT_VAR(Patches, liveries, SLE_UINT8, S,MS, 2, 0, 2, 0, STR_CONFIG_PATCHES_LIVERIES, RedrawScreen), SDT_BOOL(Patches, prefer_teamchat, S, 0, false, STR_CONFIG_PATCHES_PREFER_TEAMCHAT, NULL), Index: src/lang/english.txt =================================================================== --- src/lang/english.txt (revision 11719) +++ src/lang/english.txt (working copy) @@ -299,12 +299,6 @@ STR_MULTIPLAYER :{BLACK}Multiplayer STR_SCENARIO_EDITOR :{BLACK}Scenario Editor -STR_64 :64 -STR_128 :128 -STR_256 :256 -STR_512 :512 -STR_1024 :1024 -STR_2048 :2048 STR_MAPSIZE :{BLACK}Map size: STR_BY :{BLACK}* STR_0148_GAME_OPTIONS :{BLACK}Game Options @@ -3270,6 +3264,7 @@ STR_HEIGHTMAP_SCALE_WARNING_MESSAGE :{YELLOW}Resizing source map too much is not recommended. Continue with the generation? STR_TOWN_LAYOUT_WARNING_CAPTION :{WHITE}Town layout warning STR_TOWN_LAYOUT_WARNING_MESSAGE :{YELLOW}The town layout "no more roads" is not recommended. Continue with the generation? +STR_MAP_TOO_MANY_TILES_MESSAGE :{YELLOW}Too many tiles in map. Maximum number of tiles is {NUM}, you have selected {NUM} STR_SNOW_LINE_HEIGHT_NUM :{NUM} STR_HEIGHTMAP_NAME :{BLACK}Heightmap name: STR_HEIGHTMAP_SIZE :{BLACK}Size: {ORANGE}{NUM} x {NUM} Index: src/genworld_gui.cpp =================================================================== --- src/genworld_gui.cpp (revision 11719) +++ src/genworld_gui.cpp (working copy) @@ -26,6 +26,7 @@ #include "date_func.h" #include "sound_func.h" #include "fios.h" +#include "map_type.h" /** * In what 'mode' the GenerateLandscapeWindowProc is. @@ -208,9 +209,44 @@ if (confirmed) StartGeneratingLandscape((glwp_modes)w->window_number); } +/** + Check if map size set lies in allowed boundaries. + @param printWarning If set to true, messagebox with warning is printed out if size is outside limits. + @return true if size is ok, false otherwise. +*/ +static bool CheckMapSize(bool printWarning = true) +{ + uint64 map_x = 1U << _patches_newgame.map_x; + uint64 map_y = 1U << _patches_newgame.map_y; + uint64 tiles = map_x * map_y; + + if (_patches_newgame.map_x + _patches_newgame.map_y > MAX_MAP_TILES_BITS) { + if (printWarning) { + SetDParam(0, MAX_MAP_TILES); + SetDParam(1, tiles); + ShowErrorMessage(INVALID_STRING_ID, STR_MAP_TOO_MANY_TILES_MESSAGE, 0, 0); + } + return false; + } + return true; +} + +/** + Build list of string ID's containing all available map sizes +*/ +static StringID *BuildMapDropdown() +{ + int base = SPECSTR_MAPSIZE_START; + int num = MAX_MAP_SIZE_BITS - MIN_MAP_SIZE_BITS + 1; + static StringID buf[SPECSTR_MAPSIZE_END - SPECSTR_MAPSIZE_START + 2]; + StringID *p = buf; + while (--num>=0) *p++ = base++; + *p = INVALID_STRING_ID; + return buf; +} + static void GenerateLandscapeWndProc(Window *w, WindowEvent *e) { - static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID}; static const StringID elevations[] = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID}; static const StringID sea_lakes[] = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID}; static const StringID smoothness[] = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID}; @@ -224,6 +260,7 @@ static querystr_d _genseed_query; static char _genseed_buffer[11]; + uint16 mapsize_color = CheckMapSize(false) ? TC_BLACK : TC_RED; glwp_modes mode = (glwp_modes)w->window_number; uint y; @@ -267,9 +304,9 @@ y = (mode == GLWP_HEIGHTMAP) ? 22 : 0; DrawString( 12, 91 + y, STR_MAPSIZE, TC_FROMSTRING); - DrawString(119, 91 + y, mapsizes[_patches_newgame.map_x - 6], TC_BLACK); + DrawString(119, 91 + y, SPECSTR_MAPSIZE_START + _patches_newgame.map_x - MIN_MAP_SIZE_BITS, mapsize_color); DrawString(168, 91 + y, STR_BY, TC_FROMSTRING); - DrawString(182, 91 + y, mapsizes[_patches_newgame.map_y - 6], TC_BLACK); + DrawString(182, 91 + y, SPECSTR_MAPSIZE_START + _patches_newgame.map_y - MIN_MAP_SIZE_BITS, mapsize_color); DrawString( 12, 113 + y, STR_NUMBER_OF_TOWNS, TC_FROMSTRING); DrawString( 12, 131 + y, STR_NUMBER_OF_INDUSTRIES, TC_FROMSTRING); @@ -340,10 +377,10 @@ SetNewLandscapeType(e->we.click.widget - GLAND_TEMPERATE); break; case GLAND_MAPSIZE_X_TEXT: case GLAND_MAPSIZE_X_PULLDOWN: // Mapsize X - ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, GLAND_MAPSIZE_X_PULLDOWN, 0, 0); + ShowDropDownMenu(w, BuildMapDropdown(), _patches_newgame.map_x - MIN_MAP_SIZE_BITS, GLAND_MAPSIZE_X_PULLDOWN, 0, 0); break; case GLAND_MAPSIZE_Y_TEXT: case GLAND_MAPSIZE_Y_PULLDOWN: // Mapsize Y - ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, GLAND_MAPSIZE_Y_PULLDOWN, 0, 0); + ShowDropDownMenu(w, BuildMapDropdown(), _patches_newgame.map_y - MIN_MAP_SIZE_BITS, GLAND_MAPSIZE_Y_PULLDOWN, 0, 0); break; case GLAND_TOWN_TEXT: case GLAND_TOWN_PULLDOWN: // Number of towns ShowDropDownMenu(w, num_towns, _opt_newgame.diff.number_towns, GLAND_TOWN_PULLDOWN, 0, 0); @@ -361,6 +398,7 @@ UpdatePatches(); + if (!CheckMapSize()) break; if (_patches.town_layout == TL_NO_ROADS) { ShowQuery( STR_TOWN_LAYOUT_WARNING_CAPTION, @@ -603,7 +641,7 @@ static void CreateScenarioWndProc(Window *w, WindowEvent *e) { - static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID}; + uint16 mapsize_color = CheckMapSize(false) ? TC_BLACK : TC_RED; switch (e->event) { case WE_CREATE: w->LowerWidget(_opt_newgame.landscape + CSCEN_TEMPERATE); break; @@ -621,9 +659,9 @@ DrawWindowWidgets(w); DrawStringRightAligned(211, 97, STR_MAPSIZE, TC_FROMSTRING); - DrawString( 221, 97, mapsizes[_patches_newgame.map_x - 6], TC_BLACK); + DrawString( 221, 97, SPECSTR_MAPSIZE_START + _patches_newgame.map_x - MIN_MAP_SIZE_BITS, mapsize_color); DrawStringCentered( 272, 97, STR_BY, TC_FROMSTRING); - DrawString( 284, 97, mapsizes[_patches_newgame.map_y - 6], TC_BLACK); + DrawString( 284, 97, SPECSTR_MAPSIZE_START + _patches_newgame.map_y - MIN_MAP_SIZE_BITS, mapsize_color); DrawStringRightAligned(211, 115, STR_DATE, TC_FROMSTRING); SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); @@ -641,15 +679,17 @@ SetNewLandscapeType(e->we.click.widget - CSCEN_TEMPERATE); break; case CSCEN_MAPSIZE_X_TEXT: case CSCEN_MAPSIZE_X_PULLDOWN: // Mapsize X - ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, CSCEN_MAPSIZE_X_PULLDOWN, 0, 0); + ShowDropDownMenu(w, BuildMapDropdown(), _patches_newgame.map_x - MIN_MAP_SIZE_BITS, CSCEN_MAPSIZE_X_PULLDOWN, 0, 0); break; case CSCEN_MAPSIZE_Y_TEXT: case CSCEN_MAPSIZE_Y_PULLDOWN: // Mapsize Y - ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, CSCEN_MAPSIZE_Y_PULLDOWN, 0, 0); + ShowDropDownMenu(w, BuildMapDropdown(), _patches_newgame.map_y - MIN_MAP_SIZE_BITS, CSCEN_MAPSIZE_Y_PULLDOWN, 0, 0); break; case CSCEN_EMPTY_WORLD: // Empty world / flat world + if (!CheckMapSize()) break; StartGeneratingLandscape(GLWP_SCENARIO); break; case CSCEN_RANDOM_WORLD: // Generate + if (!CheckMapSize()) break; ShowGenerateLandscape(); break; case CSCEN_START_DATE_DOWN: case CSCEN_START_DATE_UP: // Year buttons Index: src/saveload.cpp =================================================================== --- src/saveload.cpp (revision 11719) +++ src/saveload.cpp (working copy) @@ -1112,8 +1112,11 @@ CursorID cursor; }; -/* A maximum size of of 128K * 500 = 64.000KB savegames */ -STATIC_OLD_POOL(Savegame, byte, 17, 500, NULL, NULL) +/* + * A maximum size of of 128K * 500 * N = 64000 KiB savegames + * N = 1 for 2048*2048 or appropriately more for larger sizes + */ +STATIC_OLD_POOL(Savegame, byte, 17, 500 * (MAX_MAP_TILES / 2048) / 2048, NULL, NULL) static ThreadedSave _ts; static bool InitMem() Index: src/map_type.h =================================================================== --- src/map_type.h (revision 11719) +++ src/map_type.h (working copy) @@ -5,6 +5,19 @@ #ifndef MAP_TYPE_H #define MAP_TYPE_H +/** Minimal size of map is equal to 2 ^ MIN_MAP_SIZE_BITS */ +#define MIN_MAP_SIZE_BITS 6 +/** Maximal size of map is equal to 2 ^ MAX_MAP_SIZE_BITS */ +#define MAX_MAP_SIZE_BITS 13 +/** Maximal number of tiles in a map is equal to 2 ^ MAX_MAP_TILES_BITS. */ +#define MAX_MAP_TILES_BITS 22 +/** Minimal map size. */ +#define MIN_MAP_SIZE (1 << MIN_MAP_SIZE_BITS) // = 64 +/** Maximal map size. */ +#define MAX_MAP_SIZE (1 << MAX_MAP_SIZE_BITS) // = 8192 +/** Maximal number of tiles in a map. */ +#define MAX_MAP_TILES (1 << MAX_MAP_TILES_BITS) // = 2048 * 2048 + /** * Data that is stored per tile. Also used TileExtended for this. * Look at docs/landscape.html for the exact meaning of the members. Index: src/strings.cpp =================================================================== --- src/strings.cpp (revision 11719) +++ src/strings.cpp (working copy) @@ -1149,6 +1149,13 @@ return strecpy(buff, GetScreenshotFormatDesc(i), last); } + /* map size */ + if (IsInsideMM(ind, (SPECSTR_MAPSIZE_START - 0x70E4), (SPECSTR_MAPSIZE_END - 0x70E4) + 1)) { + int i = ind - (SPECSTR_MAPSIZE_START - 0x70E4); + snprintf(buff, last - buff + 1, "%d", 1 << (MIN_MAP_SIZE_BITS + i)); + return buff; + } + assert(0); return NULL; } Index: src/map.cpp =================================================================== --- src/map.cpp (revision 11719) +++ src/map.cpp (working copy) @@ -32,16 +32,18 @@ */ void AllocateMap(uint size_x, uint size_y) { + DEBUG(map, 2, "Min/max map size %d/%d, max map tiles %d", MIN_MAP_SIZE, MAX_MAP_SIZE, MAX_MAP_TILES); + DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y); + /* Make sure that the map size is within the limits and that * the x axis size is a power of 2. */ - if (size_x < 64 || size_x > 2048 || - size_y < 64 || size_y > 2048 || + if (size_x < MIN_MAP_SIZE || + size_x * size_y > MAX_MAP_TILES || + size_y < MIN_MAP_SIZE || (size_x & (size_x - 1)) != 0 || (size_y & (size_y - 1)) != 0) error("Invalid map size"); - DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y); - _map_log_x = FindFirstBit(size_x); _map_size_x = size_x; _map_size_y = size_y; Index: src/strings_type.h =================================================================== --- src/strings_type.h (revision 11719) +++ src/strings_type.h (working copy) @@ -81,6 +81,10 @@ SPECSTR_SCREENSHOT_START = SPECSTR_RESOLUTION_END + 1, SPECSTR_SCREENSHOT_END = SPECSTR_SCREENSHOT_START + 0x1F, + // reserve 32 strings for map sizes + SPECSTR_MAPSIZE_START = SPECSTR_SCREENSHOT_END + 1, + SPECSTR_MAPSIZE_END = SPECSTR_MAPSIZE_START + 0x1F, + // Used to implement SetDParamStr STR_SPEC_DYNSTRING = 0xF800, STR_SPEC_USERSTRING = 0xF808, Index: src/openttd.cpp =================================================================== --- src/openttd.cpp (revision 11719) +++ src/openttd.cpp (working copy) @@ -732,7 +732,20 @@ ResetGRFConfig(true); GenerateWorldSetCallback(&MakeNewEditorWorldDone); - GenerateWorld(GW_EMPTY, 1 << _patches.map_x, 1 << _patches.map_y); + + /* + * Too large size may be stored in config. + * Check the size and fall back to minimal size if the size is invalid + */ + uint64 map_x = 1U << _patches.map_x; + uint64 map_y = 1U << _patches.map_y; + + if (_patches.map_x + _patches.map_y > MAX_MAP_TILES_BITS) { + map_x = MIN_MAP_SIZE; + map_y = MIN_MAP_SIZE; + } + + GenerateWorld(GW_EMPTY, map_x, map_y); } void StartupPlayers();