Index: src/settings.cpp =================================================================== --- src/settings.cpp (revision 11415) +++ src/settings.cpp (working copy) @@ -50,6 +50,7 @@ #endif #include "spritecache.h" #include "transparency.h" +#include "map.h" /** The patch values that are used for new games and/or modified in config file */ Patches _patches_newgame; @@ -1356,8 +1357,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 11415) +++ src/lang/english.txt (working copy) @@ -305,6 +305,8 @@ STR_512 :512 STR_1024 :1024 STR_2048 :2048 +STR_4096 :4096 +STR_8192 :8192 STR_MAPSIZE :{BLACK}Map size: STR_BY :{BLACK}* STR_0148_GAME_OPTIONS :{BLACK}Game Options @@ -3257,6 +3259,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 11415) +++ src/genworld_gui.cpp (working copy) @@ -25,6 +25,7 @@ #include "thread.h" #include "date.h" #include "newgrf_config.h" +#include "map.h" /** * In what 'mode' the GenerateLandscapeWindowProc is. @@ -183,6 +184,9 @@ { WIDGETS_END}, }; +/** Allowed map sizes, common for scenario editor and new map GUI */ +static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, STR_4096, STR_8192, INVALID_STRING_ID}; + void StartGeneratingLandscape(glwp_modes mode) { DeleteAllNonVitalWindows(); @@ -207,9 +211,29 @@ if (confirmed) StartGeneratingLandscape((glwp_modes)w->window_number); } +/** + Check if map size set lies in allowed boundaries. + If size is outside limits, warning is printed out + @return true if size is ok, false otherwise. +*/ +static bool CheckMapSize() +{ + 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) { + SetDParam(0, MAX_MAP_TILES); + SetDParam(1, tiles); + ShowErrorMessage(INVALID_STRING_ID, STR_MAP_TOO_MANY_TILES_MESSAGE, 0, 0); + return false; + } + return true; +} + + 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}; @@ -266,9 +290,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, mapsizes[_patches_newgame.map_x - MIN_MAP_SIZE_BITS], TC_BLACK); DrawString(168, 91 + y, STR_BY, TC_FROMSTRING); - DrawString(182, 91 + y, mapsizes[_patches_newgame.map_y - 6], TC_BLACK); + DrawString(182, 91 + y, mapsizes[_patches_newgame.map_y - MIN_MAP_SIZE_BITS], TC_BLACK); DrawString( 12, 113 + y, STR_NUMBER_OF_TOWNS, TC_FROMSTRING); DrawString( 12, 131 + y, STR_NUMBER_OF_INDUSTRIES, TC_FROMSTRING); @@ -339,10 +363,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, mapsizes, _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, mapsizes, _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); @@ -360,6 +384,7 @@ UpdatePatches(); + if (!CheckMapSize()) break; if (_patches.town_layout == TL_NO_ROADS) { ShowQuery( STR_TOWN_LAYOUT_WARNING_CAPTION, @@ -602,8 +627,6 @@ 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}; - switch (e->event) { case WE_CREATE: LowerWindowWidget(w, _opt_newgame.landscape + CSCEN_TEMPERATE); break; @@ -620,9 +643,9 @@ DrawWindowWidgets(w); DrawStringRightAligned(211, 97, STR_MAPSIZE, TC_FROMSTRING); - DrawString( 221, 97, mapsizes[_patches_newgame.map_x - 6], TC_BLACK); + DrawString( 221, 97, mapsizes[_patches_newgame.map_x - MIN_MAP_SIZE_BITS], TC_BLACK); DrawStringCentered( 272, 97, STR_BY, TC_FROMSTRING); - DrawString( 284, 97, mapsizes[_patches_newgame.map_y - 6], TC_BLACK); + DrawString( 284, 97, mapsizes[_patches_newgame.map_y - MIN_MAP_SIZE_BITS], TC_BLACK); DrawStringRightAligned(211, 115, STR_DATE, TC_FROMSTRING); SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); @@ -640,15 +663,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, mapsizes, _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, mapsizes, _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 11415) +++ src/saveload.cpp (working copy) @@ -1109,8 +1109,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.h =================================================================== --- src/map.h (revision 11415) +++ src/map.h (working copy) @@ -8,6 +8,19 @@ #include "stdafx.h" #include "direction.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 + extern uint _map_tile_mask; /** Index: src/map.cpp =================================================================== --- src/map.cpp (revision 11415) +++ src/map.cpp (working copy) @@ -33,16 +33,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/openttd.cpp =================================================================== --- src/openttd.cpp (revision 11415) +++ src/openttd.cpp (working copy) @@ -754,7 +754,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();