diff --git a/src/saveload/labelmaps_sl.cpp b/src/saveload/labelmaps_sl.cpp index a91571c6f6..a354f34174 100644 --- a/src/saveload/labelmaps_sl.cpp +++ b/src/saveload/labelmaps_sl.cpp @@ -13,12 +13,22 @@ #include "compat/labelmaps_sl_compat.h" #include "saveload_internal.h" +#include "../rail.h" +#include "../road.h" #include "../station_map.h" #include "../tunnelbridge_map.h" #include "../safeguards.h" -static std::vector _railtype_list; +/** Container for a label for rail or road type conversion. */ +template +struct LabelObject { + T label = {}; ///< Label of rail or road type. + uint8_t subtype = 0; ///< Subtype of type (road or tram). +}; + +static std::vector> _railtype_list; +static std::vector> _roadtype_list; /** * Test if any saved rail type labels are different to the currently loaded @@ -30,7 +40,7 @@ static void ConvertRailTypes() bool needs_conversion = false; for (auto it = std::begin(_railtype_list); it != std::end(_railtype_list); ++it) { - RailType rt = GetRailTypeByLabel(*it); + RailType rt = GetRailTypeByLabel(it->label); if (rt == INVALID_RAILTYPE) { rt = RAILTYPE_RAIL; } @@ -38,7 +48,7 @@ static void ConvertRailTypes() railtype_conversion_map.push_back(rt); /* Conversion is needed if the rail type is in a different position than the list. */ - if (*it != 0 && rt != std::distance(std::begin(_railtype_list), it)) needs_conversion = true; + if (it->label != 0 && rt != std::distance(std::begin(_railtype_list), it)) needs_conversion = true; } if (!needs_conversion) return; @@ -72,58 +82,146 @@ static void ConvertRailTypes() } } +/** + * Test if any saved road type labels are different to the currently loaded + * road types. Road types stored in the map will be converted if necessary. + */ +static void ConvertRoadTypes() +{ + std::vector roadtype_conversion_map; + bool needs_conversion = false; + for (auto it = std::begin(_roadtype_list); it != std::end(_roadtype_list); ++it) { + RoadType rt = GetRoadTypeByLabel(it->label); + if (rt == INVALID_ROADTYPE || GetRoadTramType(rt) != it->subtype) { + rt = it->subtype ? ROADTYPE_TRAM : ROADTYPE_ROAD; + } + + roadtype_conversion_map.push_back(rt); + + /* Conversion is needed if the road type is in a different position than the list. */ + if (it->label != 0 && rt != std::distance(std::begin(_roadtype_list), it)) needs_conversion = true; + } + if (!needs_conversion) return; + + for (TileIndex t : Map::Iterate()) { + switch (GetTileType(t)) { + case MP_ROAD: + if (RoadType rt = GetRoadTypeRoad(t); rt != INVALID_ROADTYPE) SetRoadTypeRoad(t, roadtype_conversion_map[rt]); + if (RoadType rt = GetRoadTypeTram(t); rt != INVALID_ROADTYPE) SetRoadTypeTram(t, roadtype_conversion_map[rt]); + break; + + case MP_STATION: + if (IsStationRoadStop(t) || IsRoadWaypoint(t)) { + if (RoadType rt = GetRoadTypeRoad(t); rt != INVALID_ROADTYPE) SetRoadTypeRoad(t, roadtype_conversion_map[rt]); + if (RoadType rt = GetRoadTypeTram(t); rt != INVALID_ROADTYPE) SetRoadTypeTram(t, roadtype_conversion_map[rt]); + } + break; + + case MP_TUNNELBRIDGE: + if (GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD) { + if (RoadType rt = GetRoadTypeRoad(t); rt != INVALID_ROADTYPE) SetRoadTypeRoad(t, roadtype_conversion_map[rt]); + if (RoadType rt = GetRoadTypeTram(t); rt != INVALID_ROADTYPE) SetRoadTypeTram(t, roadtype_conversion_map[rt]); + } + break; + + default: + break; + } + } +} + +/** Perform rail type and road type conversion if necessary. */ void AfterLoadLabelMaps() { ConvertRailTypes(); + ConvertRoadTypes(); } void ResetLabelMaps() { _railtype_list.clear(); + _roadtype_list.clear(); } -/** Container for a label for SaveLoad system */ -struct LabelObject { - uint32_t label; -}; - -static const SaveLoad _label_object_desc[] = { - SLE_VAR(LabelObject, label, SLE_UINT32), -}; - struct RAILChunkHandler : ChunkHandler { RAILChunkHandler() : ChunkHandler('RAIL', CH_TABLE) {} + static inline const SaveLoad description[] = { + SLE_VAR(LabelObject, label, SLE_UINT32), + }; + void Save() const override { - SlTableHeader(_label_object_desc); - - LabelObject lo; + SlTableHeader(description); + LabelObject lo; for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) { lo.label = GetRailTypeInfo(r)->label; SlSetArrayIndex(r); - SlObject(&lo, _label_object_desc); + SlObject(&lo, description); } } void Load() const override { - const std::vector slt = SlCompatTableHeader(_label_object_desc, _label_object_sl_compat); + const std::vector slt = SlCompatTableHeader(description, _label_object_sl_compat); - LabelObject lo; + _railtype_list.reserve(RAILTYPE_END); + + LabelObject lo; while (SlIterateArray() != -1) { SlObject(&lo, slt); - _railtype_list.push_back((RailTypeLabel)lo.label); + _railtype_list.push_back(lo); + } + } +}; + +struct ROTTChunkHandler : ChunkHandler { + ROTTChunkHandler() : ChunkHandler('ROTT', CH_TABLE) {} + + static inline const SaveLoad description[] = { + SLE_VAR(LabelObject, label, SLE_UINT32), + SLE_VAR(LabelObject, subtype, SLE_UINT8), + }; + + void Save() const override + { + SlTableHeader(description); + + LabelObject lo; + for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) { + const RoadTypeInfo *rti = GetRoadTypeInfo(r); + lo.label = rti->label; + lo.subtype = GetRoadTramType(r); + + SlSetArrayIndex(r); + SlObject(&lo, description); + } + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(description, _label_object_sl_compat); + + _roadtype_list.reserve(ROADTYPE_END); + + LabelObject lo; + + while (SlIterateArray() != -1) { + SlObject(&lo, slt); + _roadtype_list.push_back(lo); } } }; static const RAILChunkHandler RAIL; +static const ROTTChunkHandler ROTT; + static const ChunkHandlerRef labelmaps_chunk_handlers[] = { RAIL, + ROTT, }; extern const ChunkHandlerTable _labelmaps_chunk_handlers(labelmaps_chunk_handlers); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index e9d10a595a..81847ca521 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -389,6 +389,7 @@ enum SaveLoadVersion : uint16_t { SLV_COMPANY_ALLOW_LIST_V2, ///< 341 PR#12908 Fixed savegame format for saving of list of client keys that are allowed to join this company. SLV_WATER_TILE_TYPE, ///< 342 PR#13030 Simplify water tile type. SLV_PRODUCTION_HISTORY, ///< 343 PR#10541 Industry production history. + SLV_ROAD_TYPE_LABEL_MAP, ///< 344 PR#13021 Add road type label map to allow upgrade/conversion of road types. SL_MAX_VERSION, ///< Highest possible saveload version };