1
0
Fork 0

(svn r20872) [1.0] -Backport from trunk:

- Fix: Road/water toolbars did not get updated when the first vehicle of their type becomes available [FS#4141] (r20856)
- Fix: Smallmap legend buttons must all be equal in size, even if their contents is not (r20851)
- Fix: Deadlock when aborting map generation on Windows [FS#3707] (r20822)
- Fix: Be a bit more lenient w.r.t. invalid savegames; do not crash on saveload related NOT_REACHEDs, just show the user an error that the savegame is corrupted [FS#3714] (r20819)
- Fix: Make the crash-on-saveload message clearer and more correct [FS#3791] (r20818)
release/1.0
rubidium 2010-10-02 14:51:33 +00:00
parent e9b13f89a3
commit ad4dd864f9
11 changed files with 126 additions and 71 deletions

View File

@ -172,6 +172,7 @@ struct BuildDocksToolbarWindow : Window {
BuildDocksToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
{
this->InitNested(desc, window_number);
this->OnInvalidateData();
if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
}
@ -180,9 +181,17 @@ struct BuildDocksToolbarWindow : Window {
if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false);
}
void OnInvalidateData(int data = 0)
{
this->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_SHIP),
DTW_DEPOT,
DTW_STATION,
DTW_BUOY,
WIDGET_LIST_END);
}
virtual void OnPaint()
{
this->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_SHIP), DTW_DEPOT, DTW_STATION, DTW_BUOY, WIDGET_LIST_END);
this->DrawWidgets();
}

View File

@ -581,6 +581,10 @@ static void AcceptEnginePreview(EngineID eid, CompanyID company)
if (company == _local_company) {
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
}
/* Update the toolbar. */
if (e->type == VEH_ROAD) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_ROAD);
if (e->type == VEH_SHIP) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_WATER);
}
static CompanyID GetBestCompany(uint8 pp)
@ -713,6 +717,10 @@ static void NewVehicleAvailable(Engine *e)
SetDParam(0, GetEngineCategoryName(index));
SetDParam(1, index);
AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NS_NEW_VEHICLES, NR_ENGINE, index);
/* Update the toolbar. */
if (e->type == VEH_ROAD) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_ROAD);
if (e->type == VEH_SHIP) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_WATER);
}
void EnginesMonthlyLoop()

View File

@ -88,7 +88,6 @@ static void CleanupGeneration()
DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
MarkWholeScreenDirty();
_genworld_mapgen_mutex->EndCritical();
}
/**
@ -172,6 +171,7 @@ static void _GenerateWorld(void *)
IncreaseGeneratingWorldProgress(GWP_GAME_START);
CleanupGeneration();
_genworld_mapgen_mutex->EndCritical();
ShowNewGRFError();

View File

@ -413,11 +413,7 @@ struct BuildRoadToolbarWindow : Window {
RTW_ONE_WAY,
WIDGET_LIST_END);
this->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_ROAD),
RTW_DEPOT,
RTW_BUS_STATION,
RTW_TRUCK_STATION,
WIDGET_LIST_END);
this->OnInvalidateData();
if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
}
@ -427,6 +423,15 @@ struct BuildRoadToolbarWindow : Window {
if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false);
}
void OnInvalidateData(int data = 0)
{
this->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_ROAD),
RTW_DEPOT,
RTW_BUS_STATION,
RTW_TRUCK_STATION,
WIDGET_LIST_END);
}
/**
* Update the remove button lowered state of the road toolbar
*

View File

@ -81,7 +81,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate
SetWaterClass(t, WATER_CLASS_INVALID);
return;
} else {
NOT_REACHED();
SlErrorCorrupt("Invalid water class for dry tile");
}
}
@ -110,7 +110,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate
case WATER_CLASS_SEA: has_water = true; break;
case WATER_CLASS_CANAL: has_canal = true; break;
case WATER_CLASS_RIVER: has_river = true; break;
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid water class for tile");
}
}
break;
@ -338,18 +338,25 @@ static void CDECL HandleSavegameLoadCrash(int signum)
char buffer[8192];
char *p = buffer;
p += seprintf(p, lastof(buffer), "Loading your savegame caused OpenTTD to crash.\n");
for (const GRFConfig *c = _grfconfig; !_saveload_crash_with_missing_newgrfs && c != NULL; c = c->next) {
_saveload_crash_with_missing_newgrfs = HasBit(c->flags, GCF_COMPATIBLE) || c->status == GCS_NOT_FOUND;
}
if (_saveload_crash_with_missing_newgrfs) {
p += seprintf(p, lastof(buffer),
"Loading your savegame caused OpenTTD to crash.\n"
"This is most likely caused by a missing NewGRF or a NewGRF that has been\n"
"loaded as replacement for a missing NewGRF. OpenTTD cannot easily\n"
"determine whether a replacement NewGRF is of a newer or older version.\n"
"It will load a NewGRF with the same GRF ID as the missing NewGRF. This\n"
"means that if the author makes incompatible NewGRFs with the same GRF ID\n"
"OpenTTD cannot magically do the right thing. In most cases OpenTTD will\n"
"load the savegame and not crash, but this is an exception.\n"
"Please load the savegame with the appropriate NewGRFs. When loading a\n"
"savegame still crashes when all NewGRFs are found you should file a\n"
"bug report. The missing NewGRFs are:\n");
"This is most likely caused by a missing NewGRF or a NewGRF that\n"
"has been loaded as replacement for a missing NewGRF. OpenTTD\n"
"cannot easily determine whether a replacement NewGRF is of a newer\n"
"or older version.\n"
"It will load a NewGRF with the same GRF ID as the missing NewGRF.\n"
"This means that if the author makes incompatible NewGRFs with the\n"
"same GRF ID OpenTTD cannot magically do the right thing. In most\n"
"cases OpenTTD will load the savegame and not crash, but this is an\n"
"exception.\n"
"Please load the savegame with the appropriate NewGRFs installed.\n"
"The missing/compatible NewGRFs are:\n");
for (const GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
if (HasBit(c->flags, GCF_COMPATIBLE)) {
@ -357,15 +364,18 @@ static void CDECL HandleSavegameLoadCrash(int signum)
char buf[40];
md5sumToString(buf, lastof(buf), replaced->md5sum);
p += seprintf(p, lastof(buffer), "NewGRF %08X (checksum %s) not found.\n Loaded NewGRF \"%s\" with same GRF ID instead.\n", BSWAP32(c->grfid), buf, c->filename);
_saveload_crash_with_missing_newgrfs = true;
}
if (c->status == GCS_NOT_FOUND) {
char buf[40];
md5sumToString(buf, lastof(buf), c->md5sum);
p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s.\n", BSWAP32(c->grfid), c->filename, buf);
_saveload_crash_with_missing_newgrfs = true;
}
}
} else {
p += seprintf(p, lastof(buffer),
"This is probably caused by a corruption in the savegame.\n"
"Please file a bug report and attach this savegame.\n");
}
ShowInfo(buffer);
@ -840,7 +850,7 @@ bool AfterLoadGame()
case MP_ROAD:
SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2));
switch (GetRoadTileType(t)) {
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid road tile type");
case ROAD_TILE_NORMAL:
SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4));
SB(_m[t].m4, 4, 4, 0);
@ -881,7 +891,7 @@ bool AfterLoadGame()
if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_me[t].m7, 5, 3));
SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert
switch (GetRoadTileType(t)) {
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid road tile type");
case ROAD_TILE_NORMAL:
SB(_me[t].m7, 0, 4, GB(_m[t].m3, 0, 4)); // road works
SB(_m[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground
@ -1000,7 +1010,7 @@ bool AfterLoadGame()
if (dir != DirToDiagDir(v->direction)) continue;
switch (dir) {
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid vehicle direction");
case DIAGDIR_NE: if ((v->x_pos & 0xF) != 0) continue; break;
case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break;
case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break;

View File

@ -178,7 +178,7 @@ static bool LoadObjects()
case SQSL_ARRAY_TABLE_END:
return false;
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid AI data type");
}
}

View File

@ -1098,7 +1098,7 @@ static bool LoadOldVehicleUnion(LoadgameState *ls, int num)
res = LoadChunk(ls, NULL, vehicle_empty_chunk);
} else {
switch (v->type) {
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid vehicle type");
case VEH_TRAIN : res = LoadChunk(ls, v, vehicle_train_chunk); break;
case VEH_ROAD : res = LoadChunk(ls, v, vehicle_road_chunk); break;
case VEH_SHIP : res = LoadChunk(ls, v, vehicle_ship_chunk); break;
@ -1298,7 +1298,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
} else {
/* Read the vehicle type and allocate the right vehicle */
switch (ReadByte(ls)) {
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid vehicle type");
case 0x00 /* VEH_INVALID */: v = NULL; break;
case 0x10 /* VEH_TRAIN */: v = new (_current_vehicle_id) Train(); break;
case 0x11 /* VEH_ROAD */: v = new (_current_vehicle_id) RoadVehicle(); break;

View File

@ -203,6 +203,18 @@ static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
throw std::exception();
}
/**
* Error handler for corrupt savegames. Sets everything up to show the
* error message and to clean up the mess of a partial savegame load.
* @param msg Location the corruption has been spotted.
* @note This function does never return as it throws an exception to
* break out of all the saveload code.
*/
void NORETURN SlErrorCorrupt(const char *msg)
{
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
}
typedef void (*AsyncSaveFinishProc)();
static AsyncSaveFinishProc _async_save_finish = NULL;
static ThreadObject *_save_thread;
@ -242,7 +254,7 @@ void ProcessAsyncSaveFinish()
static void SlReadFill()
{
size_t len = _sl.read_bytes();
if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk");
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
_sl.bufp = _sl.buf;
_sl.bufe = _sl.buf + len;
@ -390,7 +402,7 @@ static uint SlReadSimpleGamma()
if (HasBit(i, 5)) {
i &= ~0x20;
if (HasBit(i, 4))
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma");
SlErrorCorrupt("Unsupported gamma");
i = (i << 8) | SlReadByte();
}
i = (i << 8) | SlReadByte();
@ -462,7 +474,7 @@ int SlIterateArray()
/* After reading in the whole array inside the loop
* we must have read in all the data, so we must be at end of current block. */
if (_next_offs != 0 && SlGetOffs() != _next_offs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
if (_next_offs != 0 && SlGetOffs() != _next_offs) SlErrorCorrupt("Invalid chunk size");
while (true) {
uint length = SlReadArrayLength();
@ -1098,7 +1110,7 @@ void SlAutolength(AutolengthProc *proc, void *arg)
/* And write the stuff */
proc(arg);
if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
if (offs != SlGetOffs()) SlErrorCorrupt("Invalid chunk size");
}
/**
@ -1130,9 +1142,9 @@ static void SlLoadChunk(const ChunkHandler *ch)
_sl.obj_len = len;
endoffs = SlGetOffs() + len;
ch->load_proc();
if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
if (SlGetOffs() != endoffs) SlErrorCorrupt("Invalid chunk size");
} else {
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type");
SlErrorCorrupt("Invalid chunk type");
}
break;
}
@ -1216,7 +1228,7 @@ static void SlLoadChunks()
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
ch = SlFindChunkHandler(id);
if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type");
if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
SlLoadChunk(ch);
}
}
@ -1269,13 +1281,13 @@ static size_t ReadLZO()
size = TO_BE32(size);
}
if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size");
if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
/* Read block */
if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
/* Verify checksum */
if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum");
if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum");
/* Decompress */
lzo1x_decompress(out + sizeof(uint32) * 1, size, _sl.buf, &len, NULL);
@ -1564,38 +1576,38 @@ static void *IntToReference(size_t index, SLRefType rt)
switch (rt) {
case REF_ORDERLIST:
if (OrderList::IsValidID(index)) return OrderList::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid OrderList");
SlErrorCorrupt("Referencing invalid OrderList");
case REF_ORDER:
if (Order::IsValidID(index)) return Order::Get(index);
/* in old versions, invalid order was used to mark end of order list */
if (CheckSavegameVersionOldStyle(5, 2)) return NULL;
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Order");
SlErrorCorrupt("Referencing invalid Order");
case REF_VEHICLE_OLD:
case REF_VEHICLE:
if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Vehicle");
SlErrorCorrupt("Referencing invalid Vehicle");
case REF_STATION:
if (Station::IsValidID(index)) return Station::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Station");
SlErrorCorrupt("Referencing invalid Station");
case REF_TOWN:
if (Town::IsValidID(index)) return Town::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Town");
SlErrorCorrupt("Referencing invalid Town");
case REF_ROADSTOPS:
if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid RoadStop");
SlErrorCorrupt("Referencing invalid RoadStop");
case REF_ENGINE_RENEWS:
if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid EngineRenew");
SlErrorCorrupt("Referencing invalid EngineRenew");
case REF_CARGO_PACKET:
if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid CargoPacket");
SlErrorCorrupt("Referencing invalid CargoPacket");
default: NOT_REACHED();
}
@ -1766,7 +1778,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
{
uint i;
if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
for (i = 0; i != _memory_savegame.Length() - 1; i++) {
_sl.buf = _memory_savegame[i];
fmt->writer(MEMORY_CHUNK_SIZE);
@ -1779,7 +1791,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
}
fmt->uninit_write();
if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
UnInitMem();
fclose(_sl.fh);

View File

@ -333,6 +333,7 @@ void SlGlobList(const SaveLoadGlobVarList *sldg);
void SlArray(void *array, size_t length, VarType conv);
void SlObject(void *object, const SaveLoad *sld);
bool SlObjectMember(void *object, const SaveLoad *sld);
void NORETURN SlErrorCorrupt(const char *msg);
bool SaveloadCrashWithMissingNewGRFs();

View File

@ -151,7 +151,7 @@ void ConvertOldMultiheadToNew()
u->SetWagon();
u->SetFreeWagon();
break;
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid train subtype");
}
}
}
@ -716,7 +716,7 @@ void Load_VEHS()
case VEH_EFFECT: v = new (index) EffectVehicle(); break;
case VEH_DISASTER: v = new (index) DisasterVehicle(); break;
case VEH_INVALID: // Savegame shouldn't contain invalid vehicles
default: NOT_REACHED();
default: SlErrorCorrupt("Invalid vehicle type");
}
SlObject(v, GetVehicleDescription(vtype));

View File

@ -1424,19 +1424,29 @@ static const NWidgetPart _nested_smallmap_bar[] = {
NWidget(NWID_VERTICAL),
/* Top button row. */
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN),
SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP),
SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR),
SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES),
SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES),
SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP), SetFill(1, 1),
EndContainer(),
/* Bottom button row. */
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT),
SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME),
SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES),
SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION),
SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS),
SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP), SetFill(1, 1),
EndContainer(),
NWidget(NWID_SPACER), SetResize(0, 1),
EndContainer(),