2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file industry_sl.cpp Code handling saving and loading of industries */
10 #include "../stdafx.h"
13 #include "compat/industry_sl_compat.h"
15 #include "../industry.h"
16 #include "newgrf_sl.h"
18 #include "../safeguards.h"
20 static OldPersistentStorage _old_ind_persistent_storage
;
22 class SlIndustryAccepted
: public DefaultSaveLoadHandler
<SlIndustryAccepted
, Industry
> {
24 inline static const SaveLoad description
[] = {
25 SLE_VAR(Industry::AcceptedCargo
, cargo
, SLE_UINT8
),
26 SLE_VAR(Industry::AcceptedCargo
, waiting
, SLE_UINT16
),
27 SLE_VAR(Industry::AcceptedCargo
, last_accepted
, SLE_INT32
),
29 inline const static SaveLoadCompatTable compat_description
= _industry_accepts_sl_compat
;
31 void Save(Industry
*i
) const override
33 SlSetStructListLength(i
->accepted
.size());
35 for (auto &a
: i
->accepted
) {
36 SlObject(&a
, this->GetDescription());
40 void Load(Industry
*i
) const override
42 size_t len
= SlGetStructListLength(INDUSTRY_NUM_INPUTS
);
44 i
->accepted
.reserve(len
);
45 for (size_t index
= 0; index
< len
; ++index
) {
46 auto &a
= i
->accepted
.emplace_back();
47 SlObject(&a
, this->GetLoadDescription());
51 /* Old array structure used for savegames before SLV_INDUSTRY_CARGO_REORGANISE. */
52 static inline std::array
<CargoID
, INDUSTRY_NUM_INPUTS
> old_cargo
;
53 static inline std::array
<uint16_t, INDUSTRY_NUM_INPUTS
> old_waiting
;
54 static inline std::array
<TimerGameEconomy::Date
, INDUSTRY_NUM_INPUTS
> old_last_accepted
;
56 static void ResetOldStructure()
58 SlIndustryAccepted::old_cargo
.fill(INVALID_CARGO
);
59 SlIndustryAccepted::old_waiting
.fill(0);
60 SlIndustryAccepted::old_last_accepted
.fill(0);
64 class SlIndustryProducedHistory
: public DefaultSaveLoadHandler
<SlIndustryProducedHistory
, Industry::ProducedCargo
> {
66 inline static const SaveLoad description
[] = {
67 SLE_VAR(Industry::ProducedHistory
, production
, SLE_UINT16
),
68 SLE_VAR(Industry::ProducedHistory
, transported
, SLE_UINT16
),
70 inline const static SaveLoadCompatTable compat_description
= _industry_produced_history_sl_compat
;
72 void Save(Industry::ProducedCargo
*p
) const override
74 if (!IsValidCargoID(p
->cargo
)) {
75 /* Don't save any history if cargo slot isn't used. */
76 SlSetStructListLength(0);
80 SlSetStructListLength(p
->history
.size());
82 for (auto &h
: p
->history
) {
83 SlObject(&h
, this->GetDescription());
87 void Load(Industry::ProducedCargo
*p
) const override
89 size_t len
= SlGetStructListLength(p
->history
.size());
91 for (auto &h
: p
->history
) {
92 if (--len
> p
->history
.size()) break; // unsigned so wraps after hitting zero.
93 SlObject(&h
, this->GetDescription());
98 class SlIndustryProduced
: public DefaultSaveLoadHandler
<SlIndustryProduced
, Industry
> {
100 inline static const SaveLoad description
[] = {
101 SLE_VAR(Industry::ProducedCargo
, cargo
, SLE_UINT8
),
102 SLE_VAR(Industry::ProducedCargo
, waiting
, SLE_UINT16
),
103 SLE_VAR(Industry::ProducedCargo
, rate
, SLE_UINT8
),
104 SLEG_STRUCTLIST("history", SlIndustryProducedHistory
),
106 inline const static SaveLoadCompatTable compat_description
= _industry_produced_sl_compat
;
108 void Save(Industry
*i
) const override
110 SlSetStructListLength(i
->produced
.size());
112 for (auto &p
: i
->produced
) {
113 SlObject(&p
, this->GetDescription());
117 void Load(Industry
*i
) const override
119 size_t len
= SlGetStructListLength(INDUSTRY_NUM_OUTPUTS
);
121 i
->produced
.reserve(len
);
122 for (size_t index
= 0; index
< len
; ++index
) {
123 auto &p
= i
->produced
.emplace_back();
124 SlObject(&p
, this->GetLoadDescription());
128 /* Old array structure used for savegames before SLV_INDUSTRY_CARGO_REORGANISE. */
129 static inline std::array
<CargoID
, INDUSTRY_NUM_OUTPUTS
> old_cargo
;
130 static inline std::array
<uint16_t, INDUSTRY_NUM_OUTPUTS
> old_waiting
;
131 static inline std::array
<uint8_t, INDUSTRY_NUM_OUTPUTS
> old_rate
;
132 static inline std::array
<uint16_t, INDUSTRY_NUM_OUTPUTS
> old_this_month_production
;
133 static inline std::array
<uint16_t, INDUSTRY_NUM_OUTPUTS
> old_this_month_transported
;
134 static inline std::array
<uint16_t, INDUSTRY_NUM_OUTPUTS
> old_last_month_production
;
135 static inline std::array
<uint16_t, INDUSTRY_NUM_OUTPUTS
> old_last_month_transported
;
137 static void ResetOldStructure()
139 SlIndustryProduced::old_cargo
.fill(INVALID_CARGO
);
140 SlIndustryProduced::old_waiting
.fill(0);
141 SlIndustryProduced::old_rate
.fill(0);
142 SlIndustryProduced::old_this_month_production
.fill(0);
143 SlIndustryProduced::old_this_month_transported
.fill(0);
144 SlIndustryProduced::old_last_month_production
.fill(0);
145 SlIndustryProduced::old_this_month_production
.fill(0);
149 static const SaveLoad _industry_desc
[] = {
150 SLE_CONDVAR(Industry
, location
.tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
151 SLE_CONDVAR(Industry
, location
.tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
152 SLE_VAR(Industry
, location
.w
, SLE_FILE_U8
| SLE_VAR_U16
),
153 SLE_VAR(Industry
, location
.h
, SLE_FILE_U8
| SLE_VAR_U16
),
154 SLE_REF(Industry
, town
, REF_TOWN
),
155 SLE_CONDREF(Industry
, neutral_station
, REF_STATION
, SLV_SERVE_NEUTRAL_INDUSTRIES
, SL_MAX_VERSION
),
156 SLEG_CONDARR("produced_cargo", SlIndustryProduced::old_cargo
, SLE_UINT8
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SLV_78
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
157 SLEG_CONDARR("produced_cargo", SlIndustryProduced::old_cargo
, SLE_UINT8
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
158 SLEG_CONDARR("incoming_cargo_waiting", SlIndustryAccepted::old_waiting
, SLE_UINT16
, INDUSTRY_ORIGINAL_NUM_INPUTS
, SLV_70
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
159 SLEG_CONDARR("incoming_cargo_waiting", SlIndustryAccepted::old_waiting
, SLE_UINT16
, INDUSTRY_NUM_INPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
160 SLEG_CONDARR("produced_cargo_waiting", SlIndustryProduced::old_waiting
, SLE_UINT16
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SL_MIN_VERSION
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
161 SLEG_CONDARR("produced_cargo_waiting", SlIndustryProduced::old_waiting
, SLE_UINT16
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
162 SLEG_CONDARR("production_rate", SlIndustryProduced::old_rate
, SLE_UINT8
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SL_MIN_VERSION
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
163 SLEG_CONDARR("production_rate", SlIndustryProduced::old_rate
, SLE_UINT8
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
164 SLEG_CONDARR("accepts_cargo", SlIndustryAccepted::old_cargo
, SLE_UINT8
, INDUSTRY_ORIGINAL_NUM_INPUTS
, SLV_78
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
165 SLEG_CONDARR("accepts_cargo", SlIndustryAccepted::old_cargo
, SLE_UINT8
, INDUSTRY_NUM_INPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
166 SLE_VAR(Industry
, prod_level
, SLE_UINT8
),
167 SLEG_CONDARR("this_month_production", SlIndustryProduced::old_this_month_production
, SLE_UINT16
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SL_MIN_VERSION
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
168 SLEG_CONDARR("this_month_production", SlIndustryProduced::old_this_month_production
, SLE_UINT16
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
169 SLEG_CONDARR("this_month_transported", SlIndustryProduced::old_this_month_transported
, SLE_UINT16
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SL_MIN_VERSION
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
170 SLEG_CONDARR("this_month_transported", SlIndustryProduced::old_this_month_transported
, SLE_UINT16
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
171 SLEG_CONDARR("last_month_production", SlIndustryProduced::old_last_month_production
, SLE_UINT16
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SL_MIN_VERSION
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
172 SLEG_CONDARR("last_month_production", SlIndustryProduced::old_last_month_production
, SLE_UINT16
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
173 SLEG_CONDARR("last_month_transported", SlIndustryProduced::old_last_month_transported
, SLE_UINT16
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
, SL_MIN_VERSION
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
174 SLEG_CONDARR("last_month_transported", SlIndustryProduced::old_last_month_transported
, SLE_UINT16
, INDUSTRY_NUM_OUTPUTS
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
176 SLE_VAR(Industry
, counter
, SLE_UINT16
),
178 SLE_VAR(Industry
, type
, SLE_UINT8
),
179 SLE_VAR(Industry
, owner
, SLE_UINT8
),
180 SLE_VAR(Industry
, random_colour
, SLE_UINT8
),
181 SLE_CONDVAR(Industry
, last_prod_year
, SLE_FILE_U8
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_31
),
182 SLE_CONDVAR(Industry
, last_prod_year
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
183 SLE_VAR(Industry
, was_cargo_delivered
, SLE_UINT8
),
184 SLE_CONDVAR(Industry
, ctlflags
, SLE_UINT8
, SLV_GS_INDUSTRY_CONTROL
, SL_MAX_VERSION
),
186 SLE_CONDVAR(Industry
, founder
, SLE_UINT8
, SLV_70
, SL_MAX_VERSION
),
187 SLE_CONDVAR(Industry
, construction_date
, SLE_INT32
, SLV_70
, SL_MAX_VERSION
),
188 SLE_CONDVAR(Industry
, construction_type
, SLE_UINT8
, SLV_70
, SL_MAX_VERSION
),
189 SLEG_CONDVAR("last_cargo_accepted_at[0]", SlIndustryAccepted::old_last_accepted
[0], SLE_INT32
, SLV_70
, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
),
190 SLEG_CONDARR("last_cargo_accepted_at", SlIndustryAccepted::old_last_accepted
, SLE_INT32
, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS
, SLV_INDUSTRY_CARGO_REORGANISE
),
191 SLE_CONDVAR(Industry
, selected_layout
, SLE_UINT8
, SLV_73
, SL_MAX_VERSION
),
192 SLE_CONDVAR(Industry
, exclusive_supplier
, SLE_UINT8
, SLV_GS_INDUSTRY_CONTROL
, SL_MAX_VERSION
),
193 SLE_CONDVAR(Industry
, exclusive_consumer
, SLE_UINT8
, SLV_GS_INDUSTRY_CONTROL
, SL_MAX_VERSION
),
195 SLEG_CONDARR("storage", _old_ind_persistent_storage
.storage
, SLE_UINT32
, 16, SLV_76
, SLV_161
),
196 SLE_CONDREF(Industry
, psa
, REF_STORAGE
, SLV_161
, SL_MAX_VERSION
),
198 SLE_CONDVAR(Industry
, random
, SLE_UINT16
, SLV_82
, SL_MAX_VERSION
),
199 SLE_CONDSSTR(Industry
, text
, SLE_STR
| SLF_ALLOW_CONTROL
, SLV_INDUSTRY_TEXT
, SL_MAX_VERSION
),
201 SLEG_CONDSTRUCTLIST("accepted", SlIndustryAccepted
, SLV_INDUSTRY_CARGO_REORGANISE
, SL_MAX_VERSION
),
202 SLEG_CONDSTRUCTLIST("produced", SlIndustryProduced
, SLV_INDUSTRY_CARGO_REORGANISE
, SL_MAX_VERSION
),
205 struct INDYChunkHandler
: ChunkHandler
{
206 INDYChunkHandler() : ChunkHandler('INDY', CH_TABLE
) {}
208 void Save() const override
210 SlTableHeader(_industry_desc
);
212 /* Write the industries */
213 for (Industry
*ind
: Industry::Iterate()) {
214 SlSetArrayIndex(ind
->index
);
215 SlObject(ind
, _industry_desc
);
219 void LoadMoveAcceptsProduced(Industry
*i
, uint inputs
, uint outputs
) const
221 i
->accepted
.reserve(inputs
);
222 for (uint j
= 0; j
!= inputs
; ++j
) {
223 auto &a
= i
->accepted
.emplace_back();
224 a
.cargo
= SlIndustryAccepted::old_cargo
[j
];
225 a
.waiting
= SlIndustryAccepted::old_waiting
[j
];
226 a
.last_accepted
= SlIndustryAccepted::old_last_accepted
[j
];
229 i
->produced
.reserve(outputs
);
230 for (uint j
= 0; j
!= outputs
; ++j
) {
231 auto &p
= i
->produced
.emplace_back();
232 p
.cargo
= SlIndustryProduced::old_cargo
[j
];
233 p
.waiting
= SlIndustryProduced::old_waiting
[j
];
234 p
.rate
= SlIndustryProduced::old_rate
[j
];
235 p
.history
[THIS_MONTH
].production
= SlIndustryProduced::old_this_month_production
[j
];
236 p
.history
[THIS_MONTH
].transported
= SlIndustryProduced::old_this_month_transported
[j
];
237 p
.history
[LAST_MONTH
].production
= SlIndustryProduced::old_last_month_production
[j
];
238 p
.history
[LAST_MONTH
].transported
= SlIndustryProduced::old_last_month_transported
[j
];
242 void Load() const override
244 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_industry_desc
, _industry_sl_compat
);
248 SlIndustryAccepted::ResetOldStructure();
249 SlIndustryProduced::ResetOldStructure();
250 Industry::ResetIndustryCounts();
252 while ((index
= SlIterateArray()) != -1) {
253 Industry
*i
= new (index
) Industry();
256 /* Before savegame version 161, persistent storages were not stored in a pool. */
257 if (IsSavegameVersionBefore(SLV_161
) && !IsSavegameVersionBefore(SLV_76
)) {
258 /* Store the old persistent storage. The GRFID will be added later. */
259 assert(PersistentStorage::CanAllocateItem());
260 i
->psa
= new PersistentStorage(0, 0, 0);
261 std::copy(std::begin(_old_ind_persistent_storage
.storage
), std::end(_old_ind_persistent_storage
.storage
), std::begin(i
->psa
->storage
));
263 if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS
)) {
264 LoadMoveAcceptsProduced(i
, INDUSTRY_ORIGINAL_NUM_INPUTS
, INDUSTRY_ORIGINAL_NUM_OUTPUTS
);
265 } else if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE
)) {
266 LoadMoveAcceptsProduced(i
, INDUSTRY_NUM_INPUTS
, INDUSTRY_NUM_OUTPUTS
);
268 Industry::IncIndustryTypeCount(i
->type
);
272 void FixPointers() const override
274 for (Industry
*i
: Industry::Iterate()) {
275 SlObject(i
, _industry_desc
);
280 struct IIDSChunkHandler
: NewGRFMappingChunkHandler
{
281 IIDSChunkHandler() : NewGRFMappingChunkHandler('IIDS', _industry_mngr
) {}
284 struct TIDSChunkHandler
: NewGRFMappingChunkHandler
{
285 TIDSChunkHandler() : NewGRFMappingChunkHandler('TIDS', _industile_mngr
) {}
288 /** Description of the data to save and load in #IndustryBuildData. */
289 static const SaveLoad _industry_builder_desc
[] = {
290 SLEG_VAR("wanted_inds", _industry_builder
.wanted_inds
, SLE_UINT32
),
293 /** Industry builder. */
294 struct IBLDChunkHandler
: ChunkHandler
{
295 IBLDChunkHandler() : ChunkHandler('IBLD', CH_TABLE
) {}
297 void Save() const override
299 SlTableHeader(_industry_builder_desc
);
302 SlGlobList(_industry_builder_desc
);
305 void Load() const override
307 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_industry_builder_desc
, _industry_builder_sl_compat
);
309 if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY
) && SlIterateArray() == -1) return;
311 if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY
) && SlIterateArray() != -1) SlErrorCorrupt("Too many IBLD entries");
315 /** Description of the data to save and load in #IndustryTypeBuildData. */
316 static const SaveLoad _industrytype_builder_desc
[] = {
317 SLE_VAR(IndustryTypeBuildData
, probability
, SLE_UINT32
),
318 SLE_VAR(IndustryTypeBuildData
, min_number
, SLE_UINT8
),
319 SLE_VAR(IndustryTypeBuildData
, target_count
, SLE_UINT16
),
320 SLE_VAR(IndustryTypeBuildData
, max_wait
, SLE_UINT16
),
321 SLE_VAR(IndustryTypeBuildData
, wait_count
, SLE_UINT16
),
324 /** Industry-type build data. */
325 struct ITBLChunkHandler
: ChunkHandler
{
326 ITBLChunkHandler() : ChunkHandler('ITBL', CH_TABLE
) {}
328 void Save() const override
330 SlTableHeader(_industrytype_builder_desc
);
332 for (int i
= 0; i
< NUM_INDUSTRYTYPES
; i
++) {
334 SlObject(_industry_builder
.builddata
+ i
, _industrytype_builder_desc
);
338 void Load() const override
340 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_industrytype_builder_desc
, _industrytype_builder_sl_compat
);
342 for (IndustryType it
= 0; it
< NUM_INDUSTRYTYPES
; it
++) {
343 _industry_builder
.builddata
[it
].Reset();
346 while ((index
= SlIterateArray()) != -1) {
347 if ((uint
)index
>= NUM_INDUSTRYTYPES
) SlErrorCorrupt("Too many industry builder datas");
348 SlObject(_industry_builder
.builddata
+ index
, slt
);
353 static const INDYChunkHandler INDY
;
354 static const IIDSChunkHandler IIDS
;
355 static const TIDSChunkHandler TIDS
;
356 static const IBLDChunkHandler IBLD
;
357 static const ITBLChunkHandler ITBL
;
358 static const ChunkHandlerRef industry_chunk_handlers
[] = {
366 extern const ChunkHandlerTable
_industry_chunk_handlers(industry_chunk_handlers
);