diff --git a/.gitignore b/.gitignore index 3d0376aa94..38765312bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.vs +/.cache/* /build* CMakeSettings.json docs/aidocs/* diff --git a/media/baseset/opntitle_testing.dat b/media/baseset/opntitle_testing.dat new file mode 100644 index 0000000000..b43b3f6cd3 Binary files /dev/null and b/media/baseset/opntitle_testing.dat differ diff --git a/src/company_base.h b/src/company_base.h index 814665777b..8173fdda4e 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -96,6 +96,7 @@ struct CompanyProperties { uint8_t months_empty = 0; ///< NOSAVE: Number of months this company has not had a client in multiplayer. uint8_t months_of_bankruptcy; ///< Number of months that the company is unable to pay its debts CompanyMask bankrupt_asked; ///< which companies were asked about buying it? + uint16_t old_bankrupt_asked; int16_t bankrupt_timeout; ///< If bigger than \c 0, amount of time to wait for an answer on an offer to buy this company. Money bankrupt_value; diff --git a/src/engine_base.h b/src/engine_base.h index de552bd9a7..4f545a7bd3 100644 --- a/src/engine_base.h +++ b/src/engine_base.h @@ -15,6 +15,7 @@ #include "core/pool_type.hpp" #include "newgrf_commons.h" #include "timer/timer_game_calendar.h" +#include struct WagonOverride { std::vector engines; @@ -47,11 +48,19 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> { uint16_t duration_phase_2; ///< Second reliability phase in months, keeping #reliability_max. uint16_t duration_phase_3; ///< Third reliability phase in months, decaying to #reliability_final. uint8_t flags; ///< Flags of the engine. @see EngineFlags + CompanyMask preview_asked; ///< Bit for each company which has already been offered a preview. + uint16_t old_preview_asked; + CompanyID preview_company; ///< Company which is currently being offered a preview \c INVALID_COMPANY means no company. uint8_t preview_wait; ///< Daily countdown timer for timeout of offering the engine to the #preview_company company. + CompanyMask company_avail; ///< Bit for each company whether the engine is available for that company. + uint16_t old_company_avail; + CompanyMask company_hidden; ///< Bit for each company whether the engine is normally hidden in the build gui for that company. + uint16_t old_company_hidden; + uint8_t original_image_index; ///< Original vehicle image index, thus the image index of the overridden vehicle VehicleType type; ///< %Vehicle type, ie #VEH_ROAD, #VEH_TRAIN, etc. diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index e092960f66..bf1d2c1486 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -63,11 +63,14 @@ #include "../timer/timer_game_economy.h" #include "../timer/timer_game_tick.h" +#include "saveload/saveload.h" #include "saveload_internal.h" #include #include "../safeguards.h" +#include "tile_map.h" +#include "tile_type.h" extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY); @@ -163,7 +166,7 @@ static void ConvertTownOwner() [[fallthrough]]; case MP_TUNNELBRIDGE: - if (tile.m1() & 0x80) SetTileOwner(tile, OWNER_TOWN); + if (tile.m1() & 0x80) OldSetTileOwner(tile, OWNER_TOWN); break; default: break; @@ -421,7 +424,7 @@ static void CDECL HandleSavegameLoadCrash(int signum) */ static void FixOwnerOfRailTrack(Tile t) { - assert(!Company::IsValidID(GetTileOwner(t)) && (IsLevelCrossingTile(t) || IsPlainRailTile(t))); + assert(!Company::IsValidID(OldGetTileOwner(t)) && (IsLevelCrossingTile(t) || IsPlainRailTile(t))); /* remove leftover rail piece from crossing (from very old savegames) */ Train *v = nullptr; @@ -434,7 +437,7 @@ static void FixOwnerOfRailTrack(Tile t) if (v != nullptr) { /* when there is a train on crossing (it could happen in TTD), set owner of crossing to train owner */ - SetTileOwner(t, v->owner); + OldSetTileOwner(t, v->owner); return; } @@ -443,15 +446,15 @@ static void FixOwnerOfRailTrack(Tile t) TileIndex tt = t + TileOffsByDiagDir(dd); if (GetTileTrackStatus(t, TRANSPORT_RAIL, 0, dd) != 0 && GetTileTrackStatus(tt, TRANSPORT_RAIL, 0, ReverseDiagDir(dd)) != 0 && - Company::IsValidID(GetTileOwner(tt))) { - SetTileOwner(t, GetTileOwner(tt)); + Company::IsValidID(OldGetTileOwner(tt))) { + OldSetTileOwner(t, OldGetTileOwner(tt)); return; } } if (IsLevelCrossingTile(t)) { /* else change the crossing to normal road (road vehicles won't care) */ - Owner road = GetRoadOwner(t, RTT_ROAD); // TODO: m9 + Owner road = GetRoadOwner(t, RTT_ROAD); Owner tram = GetRoadOwner(t, RTT_TRAM); RoadBits bits = GetCrossingRoadBits(t); bool hasroad = HasBit(t.m7(), 6); @@ -459,7 +462,7 @@ static void FixOwnerOfRailTrack(Tile t) /* MakeRoadNormal */ SetTileType(t, MP_ROAD); - SetTileOwner(t, road); + OldSetTileOwner(t, road); t.m3() = (hasroad ? bits : 0); t.m5() = (hastram ? bits : 0) | ROAD_TILE_NORMAL << 6; SB(t.m6(), 2, 4, 0); @@ -471,6 +474,7 @@ static void FixOwnerOfRailTrack(Tile t) MakeClear(t, CLEAR_GRASS, 0); } + /** * Fixes inclination of a vehicle. Older OpenTTD versions didn't update the bits correctly. * @param v vehicle @@ -656,8 +660,8 @@ bool AfterLoadGame() * walk through the whole map.. */ if (IsSavegameVersionBefore(SLV_4, 3)) { for (auto t : Map::Iterate()) { - if (IsTileType(t, MP_WATER) && GetTileOwner(t) >= OLD_MAX_COMPANIES) { - SetTileOwner(t, OWNER_WATER); + if (IsTileType(t, MP_WATER) && OldGetTileOwner(t) >= OLD_MAX_COMPANIES) { + OldSetTileOwner(t, OWNER_WATER); } } } @@ -858,7 +862,7 @@ bool AfterLoadGame() default: break; case MP_WATER: - if (GetWaterTileType(t) == WATER_TILE_LOCK && GetTileOwner(t) == OWNER_WATER) SetTileOwner(t, OWNER_NONE); + if (GetWaterTileType(t) == WATER_TILE_LOCK && OldGetTileOwner(t) == OWNER_WATER) OldSetTileOwner(t, OWNER_NONE); break; case MP_STATION: { @@ -913,7 +917,7 @@ bool AfterLoadGame() BaseStation *bst = BaseStation::GetByTile(t); /* Sanity check */ - if (!IsBuoy(t) && bst->owner != GetTileOwner(t)) SlErrorCorrupt("Wrong owner for station tile"); + if (!IsBuoy(t) && bst->owner != OldGetTileOwner(t)) SlErrorCorrupt("Wrong owner for station tile"); /* Set up station spread */ bst->rect.BeforeAddTile(t, StationRect::ADD_FORCE); @@ -986,7 +990,7 @@ bool AfterLoadGame() case MP_ROAD: t.m4() |= (t.m2() << 4); - if ((GB(t.m5(), 4, 2) == ROAD_TILE_CROSSING ? (Owner)t.m3() : GetTileOwner(t)) == OLD_OWNER_TOWN) { + if ((GB(t.m5(), 4, 2) == ROAD_TILE_CROSSING ? (Owner)t.m3() : OldGetTileOwner(t)) == OLD_OWNER_TOWN) { SetTownIndex(t, CalcClosestTownFromTile(t)->index); } else { SetTownIndex(t, 0); @@ -1139,7 +1143,7 @@ bool AfterLoadGame() if (!IsRoadStop(t)) break; if (fix_roadtypes) SB(t.m7(), 6, 2, (RoadTypes)GB(t.m3(), 0, 3)); - SB(t.m7(), 0, 5, HasBit(t.m6(), 2) ? OWNER_TOWN : GetTileOwner(t)); + SB(t.m7(), 0, 5, HasBit(t.m6(), 2) ? OWNER_TOWN : OldGetTileOwner(t)); SB(t.m3(), 4, 4, t.m1()); t.m4() = 0; break; @@ -1149,7 +1153,7 @@ bool AfterLoadGame() if (((old_bridge && IsBridge(t)) ? (TransportType)GB(t.m5(), 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { if (fix_roadtypes) SB(t.m7(), 6, 2, (RoadTypes)GB(t.m3(), 0, 3)); - Owner o = GetTileOwner(t); + Owner o = OldGetTileOwner(t); SB(t.m7(), 0, 5, o); // road owner SB(t.m3(), 4, 4, o == OLD_OWNER_NONE ? OWNER_TOWN : o); // tram owner } @@ -1208,7 +1212,7 @@ bool AfterLoadGame() if (GB(t.m5(), 3, 2) == TRANSPORT_RAIL) { MakeRailNormal( t, - GetTileOwner(t), + OldGetTileOwner(t), axis == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X, GetRailType(t) ); @@ -1231,10 +1235,10 @@ bool AfterLoadGame() if (!IsTileFlat(t)) { MakeShore(t); } else { - if (GetTileOwner(t) == OWNER_WATER) { + if (OldGetTileOwner(t) == OWNER_WATER) { MakeSea(t); } else { - MakeCanal(t, GetTileOwner(t), Random()); + MakeCanal(t, OldGetTileOwner(t), Random()); } } } @@ -1568,7 +1572,7 @@ bool AfterLoadGame() * be OWNER_NONE. So replace OWNER_NONE with OWNER_WATER. */ if (IsSavegameVersionBefore(SLV_46)) { for (Waypoint *wp : Waypoint::Iterate()) { - if ((wp->facilities & FACIL_DOCK) != 0 && IsTileOwner(wp->xy, OLD_OWNER_NONE) && TileHeight(wp->xy) == 0) SetTileOwner(wp->xy, OWNER_WATER); + if ((wp->facilities & FACIL_DOCK) != 0 && IsTileOwner(wp->xy, OLD_OWNER_NONE) && TileHeight(wp->xy) == 0) OldSetTileOwner(wp->xy, OWNER_WATER); } } @@ -1678,9 +1682,9 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { if (IsTileType(t, MP_WATER) && GetWaterTileType(t) == WATER_TILE_CLEAR && - GetTileOwner(t) == OWNER_WATER && + OldGetTileOwner(t) == OWNER_WATER && TileHeight(t) != 0) { - SetTileOwner(t, OWNER_NONE); + OldSetTileOwner(t, OWNER_NONE); } } } @@ -1828,7 +1832,7 @@ bool AfterLoadGame() if (IsTileType(t, MP_WATER)) { if (GetWaterClass(t) != WATER_CLASS_RIVER) { if (IsWater(t)) { - Owner o = GetTileOwner(t); + Owner o = OldGetTileOwner(t); if (o == OWNER_WATER) { MakeSea(t); } else { @@ -1864,7 +1868,7 @@ bool AfterLoadGame() } if (IsBuoyTile(t) || IsDriveThroughStopTile(t) || IsTileType(t, MP_WATER)) { - Owner o = GetTileOwner(t); + Owner o = OldGetTileOwner(t); if (o < OLD_MAX_COMPANIES && !Company::IsValidID(o)) { Backup cur_company(_current_company, o); ChangeTileOwner(t, o, INVALID_OWNER); @@ -1883,10 +1887,10 @@ bool AfterLoadGame() if (o < OLD_MAX_COMPANIES && !Company::IsValidID(o)) SetRoadOwner(t, rtt, OWNER_NONE); } if (IsLevelCrossing(t)) { - if (!Company::IsValidID(GetTileOwner(t))) FixOwnerOfRailTrack(t); + if (!Company::IsValidID(OldGetTileOwner(t))) FixOwnerOfRailTrack(t); } } else if (IsPlainRailTile(t)) { - if (!Company::IsValidID(GetTileOwner(t))) FixOwnerOfRailTrack(t); + if (!Company::IsValidID(OldGetTileOwner(t))) FixOwnerOfRailTrack(t); } } } @@ -2473,7 +2477,7 @@ bool AfterLoadGame() /* Add (random) colour to all objects. */ if (IsSavegameVersionBefore(SLV_148)) { for (Object *o : Object::Iterate()) { - Owner owner = GetTileOwner(o->location.tile); + Owner owner = OldGetTileOwner(o->location.tile); o->colour = (owner == OLD_OWNER_NONE) ? static_cast(GB(Random(), 0, 4)) : Company::Get(owner)->livery->colour1; } } @@ -2841,7 +2845,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_172)) { for (auto t : Map::Iterate()) { if (!IsBayRoadStopTile(t)) continue; - Owner o = GetTileOwner(t); + Owner o = OldGetTileOwner(t); SetRoadOwner(t, RTT_ROAD, o); SetRoadOwner(t, RTT_TRAM, o); } @@ -3262,6 +3266,15 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_SCRIPT_RANDOMIZER)) { ScriptObject::InitializeRandomizers(); } + if (IsSavegameVersionBefore(SLV_MORE_COMPANIES)) { + for (auto t : Map::Iterate()) { + if (IsValidTile(t) + && !IsTileType(t, MP_HOUSE) + && !IsTileType(t, MP_INDUSTRY)) { + SetTileOwner(t, OldGetTileOwner(t)); + } + } + } for (Company *c : Company::Iterate()) { UpdateCompanyLiveries(c); diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index a4e5a35e7b..e56a674b40 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -475,9 +475,11 @@ static const SaveLoad _company_desc[] = { SLE_CONDVAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), - SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8), - // MYTODO: Fix all the compat issues - SLE_CONDCOMPMASK(CompanyProperties, bankrupt_asked, SLE_UINT8, COMPANY_SIZE_BITS, SLV_179, SL_MAX_VERSION), + SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8), +SLE_CONDVARNAME(CompanyProperties, old_bankrupt_asked, "bankrupt_asked", SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), +SLE_CONDVARNAME(CompanyProperties, old_bankrupt_asked, "bankrupt_asked", SLE_UINT16, SLV_104, SLV_MORE_COMPANIES), +SLE_CONDCOMPMASK(CompanyProperties, bankrupt_asked, MAX_COMPANIES, SLV_MORE_COMPANIES, SL_MAX_VERSION), + SLE_VAR(CompanyProperties, bankrupt_timeout, SLE_INT16), SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65), SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_INT64, SLV_65, SL_MAX_VERSION), @@ -520,6 +522,9 @@ struct PLYRChunkHandler : ChunkHandler { Company *c = new (index) Company(); SlObject(c, slt); _company_colours[index] = c->colour; + if (IsSavegameVersionBefore(SLV_MORE_COMPANIES)) { + c->bankrupt_asked = owner_from_int(c->old_bankrupt_asked); + } } } diff --git a/src/saveload/engine_sl.cpp b/src/saveload/engine_sl.cpp index 599803f74d..d6e78d6638 100644 --- a/src/saveload/engine_sl.cpp +++ b/src/saveload/engine_sl.cpp @@ -20,24 +20,32 @@ #include "../safeguards.h" static const SaveLoad _engine_desc[] = { - SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Engine, intro_date, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Engine, age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_VAR(Engine, reliability, SLE_UINT16), - SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16), - SLE_VAR(Engine, reliability_start, SLE_UINT16), - SLE_VAR(Engine, reliability_max, SLE_UINT16), - SLE_VAR(Engine, reliability_final, SLE_UINT16), - SLE_VAR(Engine, duration_phase_1, SLE_UINT16), - SLE_VAR(Engine, duration_phase_2, SLE_UINT16), - SLE_VAR(Engine, duration_phase_3, SLE_UINT16), - SLE_VAR(Engine, flags, SLE_UINT8), - SLE_CONDCOMPMASK(Engine, preview_asked, SLE_UINT8, COMPANY_SIZE_BITS, SLV_179, SL_MAX_VERSION), - SLE_CONDVAR(Engine, preview_company, SLE_UINT8, SLV_179, SL_MAX_VERSION), - SLE_VAR(Engine, preview_wait, SLE_UINT8), -// MYTODO: Fix all the compatibility here - SLE_CONDCOMPMASK(Engine, company_avail, SLE_UINT8, COMPANY_SIZE_BITS, SLV_179, SL_MAX_VERSION), + SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Engine, intro_date, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Engine, age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_VAR(Engine, reliability, SLE_UINT16), + SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16), + SLE_VAR(Engine, reliability_start, SLE_UINT16), + SLE_VAR(Engine, reliability_max, SLE_UINT16), + SLE_VAR(Engine, reliability_final, SLE_UINT16), + SLE_VAR(Engine, duration_phase_1, SLE_UINT16), + SLE_VAR(Engine, duration_phase_2, SLE_UINT16), + SLE_VAR(Engine, duration_phase_3, SLE_UINT16), + SLE_VAR(Engine, flags, SLE_UINT8), + + SLE_CONDVARNAME(Engine, old_preview_asked, "preview_asked", SLE_UINT16, SLV_179, SLV_MORE_COMPANIES), +SLE_CONDCOMPMASK(Engine, preview_asked, MAX_COMPANIES, SLV_MORE_COMPANIES, SL_MAX_VERSION), + + SLE_CONDVAR(Engine, preview_company, SLE_UINT8, SLV_179, SL_MAX_VERSION), + SLE_VAR(Engine, preview_wait, SLE_UINT8), + + SLE_CONDVARNAME(Engine, old_company_avail, "company_avail", SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), + SLE_CONDVARNAME(Engine, old_company_avail, "company_avail", SLE_UINT16, SLV_104, SLV_MORE_COMPANIES), +SLE_CONDCOMPMASK(Engine, company_avail, MAX_COMPANIES, SLV_MORE_COMPANIES, SL_MAX_VERSION), + +SLE_CONDCOMPMASK(Engine, company_hidden, MAX_COMPANIES, SLV_MORE_COMPANIES, SL_MAX_VERSION), + SLE_CONDVARNAME(Engine, old_company_hidden, "company_hidden", SLE_UINT16, SLV_193, SLV_MORE_COMPANIES), SLE_CONDSSTR(Engine, name, SLE_STR, SLV_84, SL_MAX_VERSION), }; @@ -111,6 +119,11 @@ struct ENGNChunkHandler : ChunkHandler { e->preview_company = INVALID_COMPANY; e->preview_asked = MAX_UVALUE(CompanyMask); } + if (IsSavegameVersionBefore(SLV_MORE_COMPANIES)) { + e->preview_asked = owner_from_int(e->old_preview_asked); + e->company_avail = owner_from_int(e->old_company_avail); + e->company_hidden = owner_from_int(e->old_company_hidden); + } } } }; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 7e524cfa6e..d3c1a7f308 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -44,8 +44,10 @@ #include "../fios.h" #include "../error.h" #include "company_type.h" +#include "core/bitmath_func.hpp" #include #include +#include #include #include #ifdef __EMSCRIPTEN__ @@ -1114,61 +1116,56 @@ static void SlArray(void *array, size_t length, VarType conv) // MYTODO: Put this somewhere it belongs -std::vector bitset_to_bytes(const CompanyMask& bs) +std::vector bitset_to_bytes(const CompanyMask& mask) { - int N = COMPANY_SIZE_BITS; - std::vector result((N + 7) >> 3); - for (int j=0; j>3] |= (bs[j] << (j & 7)); + std::vector result((MAX_COMPANIES + 7) >> 3); + for (int j = 0; j < MAX_COMPANIES; j++) + result[j>>3] |= (mask[j] << (j & 7)); return result; } -CompanyMask bitset_from_bytes(const std::vector& buf) -{ - size_t N = COMPANY_SIZE_BITS; - assert(buf.size() == ((N + 7) >> 3)); +CompanyMask bitset_from_bytes(const std::vector& buf) { CompanyMask result; - for (int j=0; j>3] >> (j & 7)) & 1); return result; } +CompanyMask owner_from_int(uint16_t old_owner) { + CompanyMask result; + for (int i = 0; i < 16; i++) { + result[i] = GB(old_owner, i, 1) & 1; + } + return result; +} + /** * Save/Load the length of the bitset followed by the array of SL_VAR bits. * @param array The array being manipulated * @param length The length of the bitset in bytes, */ -static void SlCompanyMask(void *array, size_t length, VarType conv) +static void SlCompanyMask(void *array, size_t byte_length, VarType conv) { switch (_sl.action) { case SLA_SAVE: { CompanyMask *bs = static_cast(array); - // We don't save the number of bits in the company mask, - // because it's incompatible with other versions anyway std::vector bytes = bitset_to_bytes(*bs); uint8_t *bytes_arr = &bytes[0]; - - SlWriteArrayLength(bytes.size()); SlArray(bytes_arr, bytes.size(), conv); - return; } case SLA_LOAD_CHECK: case SLA_LOAD: { + assert(byte_length == (MAX_COMPANIES + 7) >> 3); - std::vector buff(length); + std::vector buff((MAX_COMPANIES + 7) >> 3); + SlArray(&buff[0], byte_length, conv); - SlArray(&buff[0], length, conv); - - CompanyMask res = bitset_from_bytes(buff); CompanyMask *bs = static_cast(array); - for (int i = 0; i < COMPANY_SIZE_BITS; i++) { - (*bs)[i] = res[i]; - } - + *bs = bitset_from_bytes(buff); // we don't want to write direc return; } @@ -2248,10 +2245,12 @@ static void SlLoadCheckChunks() const ChunkHandler *ch; for (id = SlReadUint32(); id != 0; id = SlReadUint32()) { - Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id); + Debug(sl, 2, "Loading chunk (for checking) {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id); ch = SlFindChunkHandler(id); - if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + if (ch == nullptr) { + SlErrorCorrupt("Unknown chunk type"); + } SlLoadCheckChunk(*ch); } } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index f9b8b06905..28c128b0ed 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -381,6 +381,7 @@ enum SaveLoadVersion : uint16_t { SLV_COMPANY_ALLOW_LIST, ///< 335 PR#12337 Saving of list of client keys that are allowed to join this company. SLV_GROUP_NUMBERS, ///< 336 PR#12297 Add per-company group numbers. + SLV_MORE_COMPANIES, /// Added more companies MYTODO: Fix this comment SL_MAX_VERSION, ///< Highest possible saveload version }; @@ -1003,16 +1004,16 @@ inline constexpr bool SlCheckVarSize(SaveLoadType cmd, VarType type, size_t leng */ #define SLE_ARRNAME(base, variable, name, type, length) SLE_CONDARRNAME(base, variable, name, type, length, SL_MIN_VERSION, SL_MAX_VERSION) + /** - * Storage of a fixed-size array of #SL_VAR elements in some savegame versions. - * @param base Name of the class or struct containing the array. + * Storage of a company mask bitset of #MAX_COMPANIES bits in some savegame versions. + * @param base Name of the class or struct containing the company mask. * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the array. + * @param bit_length Number of bits in the serialized form. * @param from First savegame version that has the array. * @param to Last savegame version that has the array. */ -#define SLE_CONDCOMPMASK(base, variable, type, length, from, to) SLE_GENERAL(SL_COMPANY_MASK, base, variable, type, length, from, to, 0) +#define SLE_CONDCOMPMASK(base, variable, bit_length, from, to) SLE_GENERAL(SL_COMPANY_MASK, base, variable, SLE_CHAR, (bit_length + 7) >> 3, from, to, 0) /** * Storage of a \c std::string in every savegame version. @@ -1313,6 +1314,7 @@ void SlCopy(void *object, size_t length, VarType conv); std::vector SlTableHeader(const SaveLoadTable &slt); std::vector SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct); void SlObject(void *object, const SaveLoadTable &slt); +CompanyMask owner_from_int(uint16_t old_owner); bool SaveloadCrashWithMissingNewGRFs(); diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index 2dc3c1162c..ea024634f7 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -215,11 +215,14 @@ static const SaveLoad _town_desc[] = { SLE_CONDSSTR(Town, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), SLE_VAR(Town, flags, SLE_UINT8), - // MYTODO: Fix all the compat -SLE_CONDCOMPMASK(Town, statues, SLE_UINT8, COMPANY_SIZE_BITS, SLV_179, SL_MAX_VERSION), +SLE_CONDVARNAME(Town, old_statues, "statues", SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), +SLE_CONDVARNAME(Town, old_statues, "statues", SLE_UINT16, SLV_104, SLV_MORE_COMPANIES), +SLE_CONDCOMPMASK(Town, statues, MAX_COMPANIES, SLV_MORE_COMPANIES, SL_MAX_VERSION), + +SLE_CONDCOMPMASK(Town, have_ratings, MAX_COMPANIES, SLV_MORE_COMPANIES, SL_MAX_VERSION), +SLE_CONDVARNAME(Town, old_have_ratings, "have_ratings", SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), +SLE_CONDVARNAME(Town, old_have_ratings, "have_ratings", SLE_UINT16, SLV_104, SLV_MORE_COMPANIES), - // MYTODO: Fix all the compat -SLE_CONDCOMPMASK(Town, have_ratings, SLE_UINT8, COMPANY_SIZE_BITS, SLV_179, SL_MAX_VERSION), SLE_CONDARR(Town, ratings, SLE_INT16, 8, SL_MIN_VERSION, SLV_104), SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104), @@ -306,6 +309,10 @@ struct CITYChunkHandler : ChunkHandler { if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) { SlErrorCorrupt("Invalid town name generator"); } + if (IsSavegameVersionBefore(SLV_MORE_COMPANIES)) { + t->statues = owner_from_int(t->old_statues); + t->have_ratings = owner_from_int(t->old_have_ratings); + } } } diff --git a/src/town.h b/src/town.h index ce930bb180..d8512d1db1 100644 --- a/src/town.h +++ b/src/town.h @@ -16,6 +16,7 @@ #include "subsidy_type.h" #include "newgrf_storage.h" #include "cargotype.h" +#include template struct BuildingCounts { @@ -68,9 +69,11 @@ struct Town : TownPool::PoolItem<&_town_pool> { uint16_t noise_reached; ///< level of noise that all the airports are generating CompanyMask statues; ///< which companies have a statue? + uint16_t old_statues; /* Company ratings. */ CompanyMask have_ratings; ///< which companies have a rating + uint16_t old_have_ratings; uint8_t unwanted[MAX_COMPANIES]; ///< how many months companies aren't wanted by towns (bribe) CompanyID exclusivity; ///< which company has exclusivity uint8_t exclusive_counter; ///< months till the exclusivity expires