OpenTTD Source: src/saveload/saveload.cpp Source File

OpenTTD Source 15.0-beta1

saveload.cpp
Go to the documentation of this file.
1/*
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/>.
6 */
7
23#include "../stdafx.h"
24#include "../debug.h"
25#include "../station_base.h"
26#include "../thread.h"
27#include "../town.h"
28#include "../network/network.h"
29#include "../window_func.h"
30#include "../strings_func.h"
31#include "../core/endian_func.hpp"
32#include "../vehicle_base.h"
33#include "../company_func.h"
34#include "../timer/timer_game_economy.h"
35#include "../autoreplace_base.h"
36#include "../roadstop_base.h"
37#include "../linkgraph/linkgraph.h"
38#include "../linkgraph/linkgraphjob.h"
39#include "../statusbar_gui.h"
40#include "../fileio_func.h"
41#include "../gamelog.h"
42#include "../string_func.h"
43#include "../fios.h"
44#include "../error.h"
45#include <atomic>
46#ifdef __EMSCRIPTEN__
47# include <emscripten.h>
48#endif
49
50#include "table/strings.h"
51
52#include "saveload_internal.h"
53#include "saveload_filter.h"
54
55#include "../safeguards.h"
56
57extern const SaveLoadVersion SAVEGAME_VERSION = (SaveLoadVersion)(SL_MAX_VERSION - 1);
58
59 SavegameType _savegame_type;
60 FileToSaveLoad _file_to_saveload;
61
62uint32_t _ttdp_version;
63 SaveLoadVersion _sl_version;
64uint8_t _sl_minor_version;
65std::string _savegame_format;
66 bool _do_autosave;
67
69 enum SaveLoadAction {
70 SLA_LOAD,
71 SLA_SAVE,
72 SLA_PTRS,
73 SLA_NULL,
74 SLA_LOAD_CHECK,
75};
76
77 enum NeedLength {
78 NL_NONE = 0,
79 NL_WANTLENGTH = 1,
80 NL_CALCLENGTH = 2,
81};
82
84 static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
85
87 struct ReadBuffer {
88 uint8_t buf[MEMORY_CHUNK_SIZE];
89 uint8_t *bufp;
90 uint8_t *bufe;
91 std::shared_ptr<LoadFilter> reader;
92 size_t read;
93
98 ReadBuffer(std::shared_ptr<LoadFilter> reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
99 {
100 }
101
102 inline uint8_t ReadByte()
103 {
104 if (this->bufp == this->bufe) {
105 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
106 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
107
108 this->read += len;
109 this->bufp = this->buf;
110 this->bufe = this->buf + len;
111 }
112
113 return *this->bufp++;
114 }
115
120 size_t GetSize() const
121 {
122 return this->read - (this->bufe - this->bufp);
123 }
124};
125
126
128 struct MemoryDumper {
129 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
130 uint8_t *buf = nullptr;
131 uint8_t *bufe = nullptr;
132
137 inline void WriteByte(uint8_t b)
138 {
139 /* Are we at the end of this chunk? */
140 if (this->buf == this->bufe) {
141 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
142 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
143 }
144
145 *this->buf++ = b;
146 }
147
152 void Flush(std::shared_ptr<SaveFilter> writer)
153 {
154 uint i = 0;
155 size_t t = this->GetSize();
156
157 while (t > 0) {
158 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
159
160 writer->Write(this->blocks[i++].get(), to_write);
161 t -= to_write;
162 }
163
164 writer->Finish();
165 }
166
171 size_t GetSize() const
172 {
173 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
174 }
175};
176
178 struct SaveLoadParams {
179 SaveLoadAction action;
180 NeedLength need_length;
181 uint8_t block_mode;
182 bool error;
183
184 size_t obj_len;
185 int array_index, last_array_index;
186 bool expect_table_header;
187
188 std::unique_ptr<MemoryDumper> dumper;
189 std::shared_ptr<SaveFilter> sf;
190
191 std::unique_ptr<ReadBuffer> reader;
192 std::shared_ptr<LoadFilter> lf;
193
194 StringID error_str;
195 std::string extra_msg;
196
197 bool saveinprogress;
198};
199
200 static SaveLoadParams _sl;
201
202 static const std::vector<ChunkHandlerRef> &ChunkHandlers()
203{
204 /* These define the chunks */
205 extern const ChunkHandlerTable _gamelog_chunk_handlers;
206 extern const ChunkHandlerTable _map_chunk_handlers;
207 extern const ChunkHandlerTable _misc_chunk_handlers;
208 extern const ChunkHandlerTable _name_chunk_handlers;
209 extern const ChunkHandlerTable _cheat_chunk_handlers;
210 extern const ChunkHandlerTable _setting_chunk_handlers;
211 extern const ChunkHandlerTable _company_chunk_handlers;
212 extern const ChunkHandlerTable _engine_chunk_handlers;
213 extern const ChunkHandlerTable _veh_chunk_handlers;
214 extern const ChunkHandlerTable _waypoint_chunk_handlers;
215 extern const ChunkHandlerTable _depot_chunk_handlers;
216 extern const ChunkHandlerTable _order_chunk_handlers;
217 extern const ChunkHandlerTable _town_chunk_handlers;
218 extern const ChunkHandlerTable _sign_chunk_handlers;
219 extern const ChunkHandlerTable _station_chunk_handlers;
220 extern const ChunkHandlerTable _industry_chunk_handlers;
221 extern const ChunkHandlerTable _economy_chunk_handlers;
222 extern const ChunkHandlerTable _subsidy_chunk_handlers;
223 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
224 extern const ChunkHandlerTable _goal_chunk_handlers;
225 extern const ChunkHandlerTable _story_page_chunk_handlers;
226 extern const ChunkHandlerTable _league_chunk_handlers;
227 extern const ChunkHandlerTable _ai_chunk_handlers;
228 extern const ChunkHandlerTable _game_chunk_handlers;
229 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
230 extern const ChunkHandlerTable _newgrf_chunk_handlers;
231 extern const ChunkHandlerTable _group_chunk_handlers;
232 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
233 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
234 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
235 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
236 extern const ChunkHandlerTable _airport_chunk_handlers;
237 extern const ChunkHandlerTable _object_chunk_handlers;
238 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
239 extern const ChunkHandlerTable _water_region_chunk_handlers;
240 extern const ChunkHandlerTable _randomizer_chunk_handlers;
241
243 static const ChunkHandlerTable _chunk_handler_tables[] = {
244 _gamelog_chunk_handlers,
245 _map_chunk_handlers,
246 _misc_chunk_handlers,
247 _name_chunk_handlers,
248 _cheat_chunk_handlers,
249 _setting_chunk_handlers,
250 _veh_chunk_handlers,
251 _waypoint_chunk_handlers,
252 _depot_chunk_handlers,
253 _order_chunk_handlers,
254 _industry_chunk_handlers,
255 _economy_chunk_handlers,
256 _subsidy_chunk_handlers,
257 _cargomonitor_chunk_handlers,
258 _goal_chunk_handlers,
259 _story_page_chunk_handlers,
260 _league_chunk_handlers,
261 _engine_chunk_handlers,
262 _town_chunk_handlers,
263 _sign_chunk_handlers,
264 _station_chunk_handlers,
265 _company_chunk_handlers,
266 _ai_chunk_handlers,
267 _game_chunk_handlers,
268 _animated_tile_chunk_handlers,
269 _newgrf_chunk_handlers,
270 _group_chunk_handlers,
271 _cargopacket_chunk_handlers,
272 _autoreplace_chunk_handlers,
273 _labelmaps_chunk_handlers,
274 _linkgraph_chunk_handlers,
275 _airport_chunk_handlers,
276 _object_chunk_handlers,
277 _persistent_storage_chunk_handlers,
278 _water_region_chunk_handlers,
279 _randomizer_chunk_handlers,
280 };
281
282 static std::vector<ChunkHandlerRef> _chunk_handlers;
283
284 if (_chunk_handlers.empty()) {
285 for (auto &chunk_handler_table : _chunk_handler_tables) {
286 for (auto &chunk_handler : chunk_handler_table) {
287 _chunk_handlers.push_back(chunk_handler);
288 }
289 }
290 }
291
292 return _chunk_handlers;
293}
294
296 static void SlNullPointers()
297{
298 _sl.action = SLA_NULL;
299
300 /* We don't want any savegame conversion code to run
301 * during NULLing; especially those that try to get
302 * pointers from other pools. */
303 _sl_version = SAVEGAME_VERSION;
304
305 for (const ChunkHandler &ch : ChunkHandlers()) {
306 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
307 ch.FixPointers();
308 }
309
310 assert(_sl.action == SLA_NULL);
311}
312
321[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
322{
323 /* Distinguish between loading into _load_check_data vs. normal save/load. */
324 if (_sl.action == SLA_LOAD_CHECK) {
325 _load_check_data.error = string;
326 _load_check_data.error_msg = extra_msg;
327 } else {
328 _sl.error_str = string;
329 _sl.extra_msg = extra_msg;
330 }
331
332 /* We have to nullptr all pointers here; we might be in a state where
333 * the pointers are actually filled with indices, which means that
334 * when we access them during cleaning the pool dereferences of
335 * those indices will be made with segmentation faults as result. */
336 if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
337
338 /* Logging could be active. */
339 _gamelog.StopAnyAction();
340
341 throw std::exception();
342}
343
351[[noreturn]] void SlErrorCorrupt(const std::string &msg)
352{
353 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
354}
355
356
357 typedef void (*AsyncSaveFinishProc)();
358 static std::atomic<AsyncSaveFinishProc> _async_save_finish;
359 static std::thread _save_thread;
360
365 static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
366{
367 if (_exit_game) return;
368 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
369
370 _async_save_finish.store(proc, std::memory_order_release);
371}
372
376 void ProcessAsyncSaveFinish()
377{
378 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
379 if (proc == nullptr) return;
380
381 proc();
382
383 if (_save_thread.joinable()) {
384 _save_thread.join();
385 }
386}
387
392uint8_t SlReadByte()
393{
394 return _sl.reader->ReadByte();
395}
396
401 void SlWriteByte(uint8_t b)
402{
403 _sl.dumper->WriteByte(b);
404}
405
406static inline int SlReadUint16()
407{
408 int x = SlReadByte() << 8;
409 return x | SlReadByte();
410}
411
412static inline uint32_t SlReadUint32()
413{
414 uint32_t x = SlReadUint16() << 16;
415 return x | SlReadUint16();
416}
417
418static inline uint64_t SlReadUint64()
419{
420 uint32_t x = SlReadUint32();
421 uint32_t y = SlReadUint32();
422 return (uint64_t)x << 32 | y;
423}
424
425static inline void SlWriteUint16(uint16_t v)
426{
427 SlWriteByte(GB(v, 8, 8));
428 SlWriteByte(GB(v, 0, 8));
429}
430
431static inline void SlWriteUint32(uint32_t v)
432{
433 SlWriteUint16(GB(v, 16, 16));
434 SlWriteUint16(GB(v, 0, 16));
435}
436
437static inline void SlWriteUint64(uint64_t x)
438{
439 SlWriteUint32((uint32_t)(x >> 32));
440 SlWriteUint32((uint32_t)x);
441}
442
452 static uint SlReadSimpleGamma()
453{
454 uint i = SlReadByte();
455 if (HasBit(i, 7)) {
456 i &= ~0x80;
457 if (HasBit(i, 6)) {
458 i &= ~0x40;
459 if (HasBit(i, 5)) {
460 i &= ~0x20;
461 if (HasBit(i, 4)) {
462 i &= ~0x10;
463 if (HasBit(i, 3)) {
464 SlErrorCorrupt("Unsupported gamma");
465 }
466 i = SlReadByte(); // 32 bits only.
467 }
468 i = (i << 8) | SlReadByte();
469 }
470 i = (i << 8) | SlReadByte();
471 }
472 i = (i << 8) | SlReadByte();
473 }
474 return i;
475}
476
494 static void SlWriteSimpleGamma(size_t i)
495{
496 if (i >= (1 << 7)) {
497 if (i >= (1 << 14)) {
498 if (i >= (1 << 21)) {
499 if (i >= (1 << 28)) {
500 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
501 SlWriteByte((uint8_t)(0xF0));
502 SlWriteByte((uint8_t)(i >> 24));
503 } else {
504 SlWriteByte((uint8_t)(0xE0 | (i >> 24)));
505 }
506 SlWriteByte((uint8_t)(i >> 16));
507 } else {
508 SlWriteByte((uint8_t)(0xC0 | (i >> 16)));
509 }
510 SlWriteByte((uint8_t)(i >> 8));
511 } else {
512 SlWriteByte((uint8_t)(0x80 | (i >> 8)));
513 }
514 }
515 SlWriteByte((uint8_t)i);
516}
517
519 static inline uint SlGetGammaLength(size_t i)
520{
521 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
522}
523
524static inline uint SlReadSparseIndex()
525{
526 return SlReadSimpleGamma();
527}
528
529static inline void SlWriteSparseIndex(uint index)
530{
531 SlWriteSimpleGamma(index);
532}
533
534static inline uint SlReadArrayLength()
535{
536 return SlReadSimpleGamma();
537}
538
539static inline void SlWriteArrayLength(size_t length)
540{
541 SlWriteSimpleGamma(length);
542}
543
544static inline uint SlGetArrayLength(size_t length)
545{
546 return SlGetGammaLength(length);
547}
548
552 static uint8_t GetSavegameFileType(const SaveLoad &sld)
553{
554 switch (sld.cmd) {
555 case SL_VAR:
556 return GetVarFileType(sld.conv); break;
557
558 case SL_STDSTR:
559 case SL_ARR:
560 case SL_VECTOR:
561 case SL_DEQUE:
562 return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
563
564 case SL_REF:
565 return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
566
567 case SL_REFLIST:
568 case SL_REFVECTOR:
569 return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
570
571 case SL_SAVEBYTE:
572 return SLE_FILE_U8;
573
574 case SL_STRUCT:
575 case SL_STRUCTLIST:
576 return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
577
578 default: NOT_REACHED();
579 }
580}
581
588 static inline uint SlCalcConvMemLen(VarType conv)
589{
590 switch (GetVarMemType(conv)) {
591 case SLE_VAR_BL: return sizeof(bool);
592 case SLE_VAR_I8: return sizeof(int8_t);
593 case SLE_VAR_U8: return sizeof(uint8_t);
594 case SLE_VAR_I16: return sizeof(int16_t);
595 case SLE_VAR_U16: return sizeof(uint16_t);
596 case SLE_VAR_I32: return sizeof(int32_t);
597 case SLE_VAR_U32: return sizeof(uint32_t);
598 case SLE_VAR_I64: return sizeof(int64_t);
599 case SLE_VAR_U64: return sizeof(uint64_t);
600 case SLE_VAR_NULL: return 0;
601
602 case SLE_VAR_STR:
603 case SLE_VAR_STRQ:
604 return SlReadArrayLength();
605
606 case SLE_VAR_NAME:
607 default:
608 NOT_REACHED();
609 }
610}
611
618 static inline uint8_t SlCalcConvFileLen(VarType conv)
619{
620 switch (GetVarFileType(conv)) {
621 case SLE_FILE_END: return 0;
622 case SLE_FILE_I8: return sizeof(int8_t);
623 case SLE_FILE_U8: return sizeof(uint8_t);
624 case SLE_FILE_I16: return sizeof(int16_t);
625 case SLE_FILE_U16: return sizeof(uint16_t);
626 case SLE_FILE_I32: return sizeof(int32_t);
627 case SLE_FILE_U32: return sizeof(uint32_t);
628 case SLE_FILE_I64: return sizeof(int64_t);
629 case SLE_FILE_U64: return sizeof(uint64_t);
630 case SLE_FILE_STRINGID: return sizeof(uint16_t);
631
632 case SLE_FILE_STRING:
633 return SlReadArrayLength();
634
635 case SLE_FILE_STRUCT:
636 default:
637 NOT_REACHED();
638 }
639}
640
642 static inline size_t SlCalcRefLen()
643{
644 return IsSavegameVersionBefore(SLV_69) ? 2 : 4;
645}
646
647void SlSetArrayIndex(uint index)
648{
649 _sl.need_length = NL_WANTLENGTH;
650 _sl.array_index = index;
651}
652
653static size_t _next_offs;
654
659 int SlIterateArray()
660{
661 /* After reading in the whole array inside the loop
662 * we must have read in all the data, so we must be at end of current block. */
663 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
664 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
665 }
666
667 for (;;) {
668 uint length = SlReadArrayLength();
669 if (length == 0) {
670 assert(!_sl.expect_table_header);
671 _next_offs = 0;
672 return -1;
673 }
674
675 _sl.obj_len = --length;
676 _next_offs = _sl.reader->GetSize() + length;
677
678 if (_sl.expect_table_header) {
679 _sl.expect_table_header = false;
680 return INT32_MAX;
681 }
682
683 int index;
684 switch (_sl.block_mode) {
685 case CH_SPARSE_TABLE:
686 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
687 case CH_TABLE:
688 case CH_ARRAY: index = _sl.array_index++; break;
689 default:
690 Debug(sl, 0, "SlIterateArray error");
691 return -1; // error
692 }
693
694 if (length != 0) return index;
695 }
696}
697
701 void SlSkipArray()
702{
703 while (SlIterateArray() != -1) {
704 SlSkipBytes(_next_offs - _sl.reader->GetSize());
705 }
706}
707
713 void SlSetLength(size_t length)
714{
715 assert(_sl.action == SLA_SAVE);
716
717 switch (_sl.need_length) {
718 case NL_WANTLENGTH:
719 _sl.need_length = NL_NONE;
720 if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) {
721 _sl.expect_table_header = false;
722 SlWriteArrayLength(length + 1);
723 break;
724 }
725
726 switch (_sl.block_mode) {
727 case CH_RIFF:
728 /* Ugly encoding of >16M RIFF chunks
729 * The lower 24 bits are normal
730 * The uppermost 4 bits are bits 24:27 */
731 assert(length < (1 << 28));
732 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
733 break;
734 case CH_TABLE:
735 case CH_ARRAY:
736 assert(_sl.last_array_index <= _sl.array_index);
737 while (++_sl.last_array_index <= _sl.array_index) {
738 SlWriteArrayLength(1);
739 }
740 SlWriteArrayLength(length + 1);
741 break;
742 case CH_SPARSE_TABLE:
743 case CH_SPARSE_ARRAY:
744 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
745 SlWriteSparseIndex(_sl.array_index);
746 break;
747 default: NOT_REACHED();
748 }
749 break;
750
751 case NL_CALCLENGTH:
752 _sl.obj_len += (int)length;
753 break;
754
755 default: NOT_REACHED();
756 }
757}
758
765 static void SlCopyBytes(void *ptr, size_t length)
766{
767 uint8_t *p = (uint8_t *)ptr;
768
769 switch (_sl.action) {
770 case SLA_LOAD_CHECK:
771 case SLA_LOAD:
772 for (; length != 0; length--) *p++ = SlReadByte();
773 break;
774 case SLA_SAVE:
775 for (; length != 0; length--) SlWriteByte(*p++);
776 break;
777 default: NOT_REACHED();
778 }
779}
780
782 size_t SlGetFieldLength()
783{
784 return _sl.obj_len;
785}
786
794int64_t ReadValue(const void *ptr, VarType conv)
795{
796 switch (GetVarMemType(conv)) {
797 case SLE_VAR_BL: return (*(const bool *)ptr != 0);
798 case SLE_VAR_I8: return *(const int8_t *)ptr;
799 case SLE_VAR_U8: return *(const uint8_t *)ptr;
800 case SLE_VAR_I16: return *(const int16_t *)ptr;
801 case SLE_VAR_U16: return *(const uint16_t*)ptr;
802 case SLE_VAR_I32: return *(const int32_t *)ptr;
803 case SLE_VAR_U32: return *(const uint32_t*)ptr;
804 case SLE_VAR_I64: return *(const int64_t *)ptr;
805 case SLE_VAR_U64: return *(const uint64_t*)ptr;
806 case SLE_VAR_NULL:return 0;
807 default: NOT_REACHED();
808 }
809}
810
818 void WriteValue(void *ptr, VarType conv, int64_t val)
819{
820 switch (GetVarMemType(conv)) {
821 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
822 case SLE_VAR_I8: *(int8_t *)ptr = val; break;
823 case SLE_VAR_U8: *(uint8_t *)ptr = val; break;
824 case SLE_VAR_I16: *(int16_t *)ptr = val; break;
825 case SLE_VAR_U16: *(uint16_t*)ptr = val; break;
826 case SLE_VAR_I32: *(int32_t *)ptr = val; break;
827 case SLE_VAR_U32: *(uint32_t*)ptr = val; break;
828 case SLE_VAR_I64: *(int64_t *)ptr = val; break;
829 case SLE_VAR_U64: *(uint64_t*)ptr = val; break;
830 case SLE_VAR_NAME: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(val); break;
831 case SLE_VAR_NULL: break;
832 default: NOT_REACHED();
833 }
834}
835
844 static void SlSaveLoadConv(void *ptr, VarType conv)
845{
846 switch (_sl.action) {
847 case SLA_SAVE: {
848 int64_t x = ReadValue(ptr, conv);
849
850 /* Write the value to the file and check if its value is in the desired range */
851 switch (GetVarFileType(conv)) {
852 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
853 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
854 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
855 case SLE_FILE_STRINGID:
856 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
857 case SLE_FILE_I32:
858 case SLE_FILE_U32: SlWriteUint32((uint32_t)x);break;
859 case SLE_FILE_I64:
860 case SLE_FILE_U64: SlWriteUint64(x);break;
861 default: NOT_REACHED();
862 }
863 break;
864 }
865 case SLA_LOAD_CHECK:
866 case SLA_LOAD: {
867 int64_t x;
868 /* Read a value from the file */
869 switch (GetVarFileType(conv)) {
870 case SLE_FILE_I8: x = (int8_t )SlReadByte(); break;
871 case SLE_FILE_U8: x = (uint8_t )SlReadByte(); break;
872 case SLE_FILE_I16: x = (int16_t )SlReadUint16(); break;
873 case SLE_FILE_U16: x = (uint16_t)SlReadUint16(); break;
874 case SLE_FILE_I32: x = (int32_t )SlReadUint32(); break;
875 case SLE_FILE_U32: x = (uint32_t)SlReadUint32(); break;
876 case SLE_FILE_I64: x = (int64_t )SlReadUint64(); break;
877 case SLE_FILE_U64: x = (uint64_t)SlReadUint64(); break;
878 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16_t)SlReadUint16()); break;
879 default: NOT_REACHED();
880 }
881
882 /* Write The value to the struct. These ARE endian safe. */
883 WriteValue(ptr, conv, x);
884 break;
885 }
886 case SLA_PTRS: break;
887 case SLA_NULL: break;
888 default: NOT_REACHED();
889 }
890}
891
899 static inline size_t SlCalcStdStringLen(const void *ptr)
900{
901 const std::string *str = reinterpret_cast<const std::string *>(ptr);
902
903 size_t len = str->length();
904 return len + SlGetArrayLength(len); // also include the length of the index
905}
906
907
915 static void FixSCCEncoded(std::string &str)
916{
917 for (size_t i = 0; i < str.size(); /* nothing. */) {
918 size_t len = Utf8EncodedCharLen(str[i]);
919 if (len == 0 || i + len > str.size()) break;
920
921 char32_t c;
922 Utf8Decode(&c, &str[i]);
923 if (c == 0xE028 || c == 0xE02A) Utf8Encode(&str[i], SCC_ENCODED);
924 i += len;
925 }
926}
927
933 static void SlStdString(void *ptr, VarType conv)
934{
935 std::string *str = reinterpret_cast<std::string *>(ptr);
936
937 switch (_sl.action) {
938 case SLA_SAVE: {
939 size_t len = str->length();
940 SlWriteArrayLength(len);
941 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->c_str())), len);
942 break;
943 }
944
945 case SLA_LOAD_CHECK:
946 case SLA_LOAD: {
947 size_t len = SlReadArrayLength();
948 if (GetVarMemType(conv) == SLE_VAR_NULL) {
949 SlSkipBytes(len);
950 return;
951 }
952
953 str->resize(len);
954 SlCopyBytes(str->data(), len);
955
956 StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK;
957 if ((conv & SLF_ALLOW_CONTROL) != 0) {
958 settings = settings | SVS_ALLOW_CONTROL_CODE;
959 if (IsSavegameVersionBefore(SLV_169)) FixSCCEncoded(*str);
960 }
961 if ((conv & SLF_ALLOW_NEWLINE) != 0) {
962 settings = settings | SVS_ALLOW_NEWLINE;
963 }
964 *str = StrMakeValid(*str, settings);
965 }
966
967 case SLA_PTRS: break;
968 case SLA_NULL: break;
969 default: NOT_REACHED();
970 }
971}
972
981 static void SlCopyInternal(void *object, size_t length, VarType conv)
982{
983 if (GetVarMemType(conv) == SLE_VAR_NULL) {
984 assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes
985 SlSkipBytes(length * SlCalcConvFileLen(conv));
986 return;
987 }
988
989 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
990 * as a byte-type. So detect this, and adjust object size accordingly */
991 if (_sl.action != SLA_SAVE && _sl_version == 0) {
992 /* all objects except difficulty settings */
993 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
994 conv == SLE_INT32 || conv == SLE_UINT32) {
995 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
996 return;
997 }
998 /* used for conversion of Money 32bit->64bit */
999 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
1000 for (uint i = 0; i < length; i++) {
1001 ((int64_t*)object)[i] = (int32_t)BSWAP32(SlReadUint32());
1002 }
1003 return;
1004 }
1005 }
1006
1007 /* If the size of elements is 1 byte both in file and memory, no special
1008 * conversion is needed, use specialized copy-copy function to speed up things */
1009 if (conv == SLE_INT8 || conv == SLE_UINT8) {
1010 SlCopyBytes(object, length);
1011 } else {
1012 uint8_t *a = (uint8_t*)object;
1013 uint8_t mem_size = SlCalcConvMemLen(conv);
1014
1015 for (; length != 0; length --) {
1016 SlSaveLoadConv(a, conv);
1017 a += mem_size; // get size
1018 }
1019 }
1020}
1021
1030 void SlCopy(void *object, size_t length, VarType conv)
1031{
1032 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
1033
1034 /* Automatically calculate the length? */
1035 if (_sl.need_length != NL_NONE) {
1036 SlSetLength(length * SlCalcConvFileLen(conv));
1037 /* Determine length only? */
1038 if (_sl.need_length == NL_CALCLENGTH) return;
1039 }
1040
1041 SlCopyInternal(object, length, conv);
1042}
1043
1049 static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1050{
1051 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1052}
1053
1060 static void SlArray(void *array, size_t length, VarType conv)
1061{
1062 switch (_sl.action) {
1063 case SLA_SAVE:
1064 SlWriteArrayLength(length);
1065 SlCopyInternal(array, length, conv);
1066 return;
1067
1068 case SLA_LOAD_CHECK:
1069 case SLA_LOAD: {
1070 if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
1071 size_t sv_length = SlReadArrayLength();
1072 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1073 /* We don't know this field, so we assume the length in the savegame is correct. */
1074 length = sv_length;
1075 } else if (sv_length != length) {
1076 /* If the SLE_ARR changes size, a savegame bump is required
1077 * and the developer should have written conversion lines.
1078 * Error out to make this more visible. */
1079 SlErrorCorrupt("Fixed-length array is of wrong length");
1080 }
1081 }
1082
1083 SlCopyInternal(array, length, conv);
1084 return;
1085 }
1086
1087 case SLA_PTRS:
1088 case SLA_NULL:
1089 return;
1090
1091 default:
1092 NOT_REACHED();
1093 }
1094}
1095
1106 static size_t ReferenceToInt(const void *obj, SLRefType rt)
1107{
1108 assert(_sl.action == SLA_SAVE);
1109
1110 if (obj == nullptr) return 0;
1111
1112 switch (rt) {
1113 case REF_VEHICLE_OLD: // Old vehicles we save as new ones
1114 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
1115 case REF_STATION: return ((const Station*)obj)->index + 1;
1116 case REF_TOWN: return ((const Town*)obj)->index + 1;
1117 case REF_ORDER: return ((const Order*)obj)->index + 1;
1118 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
1119 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
1120 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
1121 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
1122 case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
1123 case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
1124 case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
1125 default: NOT_REACHED();
1126 }
1127}
1128
1139 static void *IntToReference(size_t index, SLRefType rt)
1140{
1141 static_assert(sizeof(size_t) <= sizeof(void *));
1142
1143 assert(_sl.action == SLA_PTRS);
1144
1145 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
1146 * and should be loaded like that */
1147 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) {
1148 rt = REF_VEHICLE;
1149 }
1150
1151 /* No need to look up nullptr pointers, just return immediately */
1152 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr;
1153
1154 /* Correct index. Old vehicles were saved differently:
1155 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1156 if (rt != REF_VEHICLE_OLD) index--;
1157
1158 switch (rt) {
1159 case REF_ORDERLIST:
1160 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1161 SlErrorCorrupt("Referencing invalid OrderList");
1162
1163 case REF_ORDER:
1164 if (Order::IsValidID(index)) return Order::Get(index);
1165 /* in old versions, invalid order was used to mark end of order list */
1166 if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr;
1167 SlErrorCorrupt("Referencing invalid Order");
1168
1169 case REF_VEHICLE_OLD:
1170 case REF_VEHICLE:
1171 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1172 SlErrorCorrupt("Referencing invalid Vehicle");
1173
1174 case REF_STATION:
1175 if (Station::IsValidID(index)) return Station::Get(index);
1176 SlErrorCorrupt("Referencing invalid Station");
1177
1178 case REF_TOWN:
1179 if (Town::IsValidID(index)) return Town::Get(index);
1180 SlErrorCorrupt("Referencing invalid Town");
1181
1182 case REF_ROADSTOPS:
1183 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1184 SlErrorCorrupt("Referencing invalid RoadStop");
1185
1186 case REF_ENGINE_RENEWS:
1187 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1188 SlErrorCorrupt("Referencing invalid EngineRenew");
1189
1190 case REF_CARGO_PACKET:
1191 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1192 SlErrorCorrupt("Referencing invalid CargoPacket");
1193
1194 case REF_STORAGE:
1195 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1196 SlErrorCorrupt("Referencing invalid PersistentStorage");
1197
1198 case REF_LINK_GRAPH:
1199 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1200 SlErrorCorrupt("Referencing invalid LinkGraph");
1201
1202 case REF_LINK_GRAPH_JOB:
1203 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1204 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1205
1206 default: NOT_REACHED();
1207 }
1208}
1209
1215 void SlSaveLoadRef(void *ptr, VarType conv)
1216{
1217 switch (_sl.action) {
1218 case SLA_SAVE:
1219 SlWriteUint32((uint32_t)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
1220 break;
1221 case SLA_LOAD_CHECK:
1222 case SLA_LOAD:
1223 *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32();
1224 break;
1225 case SLA_PTRS:
1226 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
1227 break;
1228 case SLA_NULL:
1229 *(void **)ptr = nullptr;
1230 break;
1231 default: NOT_REACHED();
1232 }
1233}
1234
1238template <template<typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1239 class SlStorageHelper {
1240 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1241public:
1248 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1249 {
1250 assert(cmd == SL_VAR || cmd == SL_REF);
1251
1252 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1253
1254 int type_size = SlGetArrayLength(list->size());
1255 int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32);
1256 return list->size() * item_size + type_size;
1257 }
1258
1259 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1260 {
1261 switch (cmd) {
1262 case SL_VAR: SlSaveLoadConv(item, conv); break;
1263 case SL_REF: SlSaveLoadRef(item, conv); break;
1264 case SL_STDSTR: SlStdString(item, conv); break;
1265 default:
1266 NOT_REACHED();
1267 }
1268 }
1269
1276 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1277 {
1278 assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
1279
1280 SlStorageT *list = static_cast<SlStorageT *>(storage);
1281
1282 switch (_sl.action) {
1283 case SLA_SAVE:
1284 SlWriteArrayLength(list->size());
1285
1286 for (auto &item : *list) {
1287 SlSaveLoadMember(cmd, &item, conv);
1288 }
1289 break;
1290
1291 case SLA_LOAD_CHECK:
1292 case SLA_LOAD: {
1293 size_t length;
1294 switch (cmd) {
1295 case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1296 case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1297 case SL_STDSTR: length = SlReadArrayLength(); break;
1298 default: NOT_REACHED();
1299 }
1300
1301 list->clear();
1302 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1303 list->reserve(length);
1304 }
1305
1306 /* Load each value and push to the end of the storage. */
1307 for (size_t i = 0; i < length; i++) {
1308 Tvar &data = list->emplace_back();
1309 SlSaveLoadMember(cmd, &data, conv);
1310 }
1311 break;
1312 }
1313
1314 case SLA_PTRS:
1315 for (auto &item : *list) {
1316 SlSaveLoadMember(cmd, &item, conv);
1317 }
1318 break;
1319
1320 case SLA_NULL:
1321 list->clear();
1322 break;
1323
1324 default: NOT_REACHED();
1325 }
1326 }
1327};
1328
1334 static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1335{
1336 return SlStorageHelper<std::list, void *>::SlCalcLen(list, conv, SL_REF);
1337}
1338
1344 static void SlRefList(void *list, VarType conv)
1345{
1346 /* Automatically calculate the length? */
1347 if (_sl.need_length != NL_NONE) {
1348 SlSetLength(SlCalcRefListLen(list, conv));
1349 /* Determine length only? */
1350 if (_sl.need_length == NL_CALCLENGTH) return;
1351 }
1352
1353 SlStorageHelper<std::list, void *>::SlSaveLoad(list, conv, SL_REF);
1354}
1355
1361 static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1362{
1363 return SlStorageHelper<std::vector, void *>::SlCalcLen(vector, conv, SL_REF);
1364}
1365
1371 static void SlRefVector(void *vector, VarType conv)
1372{
1373 /* Automatically calculate the length? */
1374 if (_sl.need_length != NL_NONE) {
1375 SlSetLength(SlCalcRefVectorLen(vector, conv));
1376 /* Determine length only? */
1377 if (_sl.need_length == NL_CALCLENGTH) return;
1378 }
1379
1380 SlStorageHelper<std::vector, void *>::SlSaveLoad(vector, conv, SL_REF);
1381}
1382
1388 static inline size_t SlCalcDequeLen(const void *deque, VarType conv)
1389{
1390 switch (GetVarMemType(conv)) {
1391 case SLE_VAR_BL: return SlStorageHelper<std::deque, bool>::SlCalcLen(deque, conv);
1392 case SLE_VAR_I8: return SlStorageHelper<std::deque, int8_t>::SlCalcLen(deque, conv);
1393 case SLE_VAR_U8: return SlStorageHelper<std::deque, uint8_t>::SlCalcLen(deque, conv);
1394 case SLE_VAR_I16: return SlStorageHelper<std::deque, int16_t>::SlCalcLen(deque, conv);
1395 case SLE_VAR_U16: return SlStorageHelper<std::deque, uint16_t>::SlCalcLen(deque, conv);
1396 case SLE_VAR_I32: return SlStorageHelper<std::deque, int32_t>::SlCalcLen(deque, conv);
1397 case SLE_VAR_U32: return SlStorageHelper<std::deque, uint32_t>::SlCalcLen(deque, conv);
1398 case SLE_VAR_I64: return SlStorageHelper<std::deque, int64_t>::SlCalcLen(deque, conv);
1399 case SLE_VAR_U64: return SlStorageHelper<std::deque, uint64_t>::SlCalcLen(deque, conv);
1400
1401 case SLE_VAR_STR:
1402 /* Strings are a length-prefixed field type in the savegame table format,
1403 * these may not be directly stored in another length-prefixed container type. */
1404 NOT_REACHED();
1405
1406 default: NOT_REACHED();
1407 }
1408}
1409
1415 static void SlDeque(void *deque, VarType conv)
1416{
1417 switch (GetVarMemType(conv)) {
1418 case SLE_VAR_BL: SlStorageHelper<std::deque, bool>::SlSaveLoad(deque, conv); break;
1419 case SLE_VAR_I8: SlStorageHelper<std::deque, int8_t>::SlSaveLoad(deque, conv); break;
1420 case SLE_VAR_U8: SlStorageHelper<std::deque, uint8_t>::SlSaveLoad(deque, conv); break;
1421 case SLE_VAR_I16: SlStorageHelper<std::deque, int16_t>::SlSaveLoad(deque, conv); break;
1422 case SLE_VAR_U16: SlStorageHelper<std::deque, uint16_t>::SlSaveLoad(deque, conv); break;
1423 case SLE_VAR_I32: SlStorageHelper<std::deque, int32_t>::SlSaveLoad(deque, conv); break;
1424 case SLE_VAR_U32: SlStorageHelper<std::deque, uint32_t>::SlSaveLoad(deque, conv); break;
1425 case SLE_VAR_I64: SlStorageHelper<std::deque, int64_t>::SlSaveLoad(deque, conv); break;
1426 case SLE_VAR_U64: SlStorageHelper<std::deque, uint64_t>::SlSaveLoad(deque, conv); break;
1427
1428 case SLE_VAR_STR:
1429 /* Strings are a length-prefixed field type in the savegame table format,
1430 * these may not be directly stored in another length-prefixed container type.
1431 * This is permitted for load-related actions, because invalid fields of this type are present
1432 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1433 assert(_sl.action != SLA_SAVE);
1434 SlStorageHelper<std::deque, std::string>::SlSaveLoad(deque, conv, SL_STDSTR);
1435 break;
1436
1437 default: NOT_REACHED();
1438 }
1439}
1440
1446 static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1447{
1448 switch (GetVarMemType(conv)) {
1449 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1450 case SLE_VAR_I8: return SlStorageHelper<std::vector, int8_t>::SlCalcLen(vector, conv);
1451 case SLE_VAR_U8: return SlStorageHelper<std::vector, uint8_t>::SlCalcLen(vector, conv);
1452 case SLE_VAR_I16: return SlStorageHelper<std::vector, int16_t>::SlCalcLen(vector, conv);
1453 case SLE_VAR_U16: return SlStorageHelper<std::vector, uint16_t>::SlCalcLen(vector, conv);
1454 case SLE_VAR_I32: return SlStorageHelper<std::vector, int32_t>::SlCalcLen(vector, conv);
1455 case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
1456 case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
1457 case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
1458
1459 case SLE_VAR_STR:
1460 /* Strings are a length-prefixed field type in the savegame table format,
1461 * these may not be directly stored in another length-prefixed container type. */
1462 NOT_REACHED();
1463
1464 default: NOT_REACHED();
1465 }
1466}
1467
1473 static void SlVector(void *vector, VarType conv)
1474{
1475 switch (GetVarMemType(conv)) {
1476 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1477 case SLE_VAR_I8: SlStorageHelper<std::vector, int8_t>::SlSaveLoad(vector, conv); break;
1478 case SLE_VAR_U8: SlStorageHelper<std::vector, uint8_t>::SlSaveLoad(vector, conv); break;
1479 case SLE_VAR_I16: SlStorageHelper<std::vector, int16_t>::SlSaveLoad(vector, conv); break;
1480 case SLE_VAR_U16: SlStorageHelper<std::vector, uint16_t>::SlSaveLoad(vector, conv); break;
1481 case SLE_VAR_I32: SlStorageHelper<std::vector, int32_t>::SlSaveLoad(vector, conv); break;
1482 case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
1483 case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
1484 case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
1485
1486 case SLE_VAR_STR:
1487 /* Strings are a length-prefixed field type in the savegame table format,
1488 * these may not be directly stored in another length-prefixed container type.
1489 * This is permitted for load-related actions, because invalid fields of this type are present
1490 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1491 assert(_sl.action != SLA_SAVE);
1492 SlStorageHelper<std::vector, std::string>::SlSaveLoad(vector, conv, SL_STDSTR);
1493 break;
1494
1495 default: NOT_REACHED();
1496 }
1497}
1498
1500 static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1501{
1502 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1503}
1504
1510 static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1511{
1512 size_t length = 0;
1513
1514 for (auto &sld : slt) {
1515 if (!SlIsObjectValidInSavegame(sld)) continue;
1516
1517 length += SlCalcConvFileLen(SLE_UINT8);
1518 length += SlCalcStdStringLen(&sld.name);
1519 }
1520
1521 length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
1522
1523 for (auto &sld : slt) {
1524 if (!SlIsObjectValidInSavegame(sld)) continue;
1525 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1526 length += SlCalcTableHeader(sld.handler->GetDescription());
1527 }
1528 }
1529
1530 return length;
1531}
1532
1539 size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1540{
1541 size_t length = 0;
1542
1543 /* Need to determine the length and write a length tag. */
1544 for (auto &sld : slt) {
1545 length += SlCalcObjMemberLength(object, sld);
1546 }
1547 return length;
1548}
1549
1550size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1551{
1552 assert(_sl.action == SLA_SAVE);
1553
1554 if (!SlIsObjectValidInSavegame(sld)) return 0;
1555
1556 switch (sld.cmd) {
1557 case SL_VAR: return SlCalcConvFileLen(sld.conv);
1558 case SL_REF: return SlCalcRefLen();
1559 case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
1560 case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
1561 case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
1562 case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
1563 case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1564 case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
1565 case SL_SAVEBYTE: return 1; // a byte is logically of size 1
1566 case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
1567
1568 case SL_STRUCT:
1569 case SL_STRUCTLIST: {
1570 NeedLength old_need_length = _sl.need_length;
1571 size_t old_obj_len = _sl.obj_len;
1572
1573 _sl.need_length = NL_CALCLENGTH;
1574 _sl.obj_len = 0;
1575
1576 /* Pretend that we are saving to collect the object size. Other
1577 * means are difficult, as we don't know the length of the list we
1578 * are about to store. */
1579 sld.handler->Save(const_cast<void *>(object));
1580 size_t length = _sl.obj_len;
1581
1582 _sl.obj_len = old_obj_len;
1583 _sl.need_length = old_need_length;
1584
1585 if (sld.cmd == SL_STRUCT) {
1586 length += SlGetArrayLength(1);
1587 }
1588
1589 return length;
1590 }
1591
1592 default: NOT_REACHED();
1593 }
1594 return 0;
1595}
1596
1597static bool SlObjectMember(void *object, const SaveLoad &sld)
1598{
1599 if (!SlIsObjectValidInSavegame(sld)) return false;
1600
1601 VarType conv = GB(sld.conv, 0, 8);
1602 switch (sld.cmd) {
1603 case SL_VAR:
1604 case SL_REF:
1605 case SL_ARR:
1606 case SL_REFLIST:
1607 case SL_REFVECTOR:
1608 case SL_DEQUE:
1609 case SL_VECTOR:
1610 case SL_STDSTR: {
1611 void *ptr = GetVariableAddress(object, sld);
1612
1613 switch (sld.cmd) {
1614 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
1615 case SL_REF: SlSaveLoadRef(ptr, conv); break;
1616 case SL_ARR: SlArray(ptr, sld.length, conv); break;
1617 case SL_REFLIST: SlRefList(ptr, conv); break;
1618 case SL_REFVECTOR: SlRefVector(ptr, conv); break;
1619 case SL_DEQUE: SlDeque(ptr, conv); break;
1620 case SL_VECTOR: SlVector(ptr, conv); break;
1621 case SL_STDSTR: SlStdString(ptr, sld.conv); break;
1622 default: NOT_REACHED();
1623 }
1624 break;
1625 }
1626
1627 /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object.
1628 * When loading, the value is read explicitly with SlReadByte() to determine which
1629 * object description to use. */
1630 case SL_SAVEBYTE: {
1631 void *ptr = GetVariableAddress(object, sld);
1632
1633 switch (_sl.action) {
1634 case SLA_SAVE: SlWriteByte(*(uint8_t *)ptr); break;
1635 case SLA_LOAD_CHECK:
1636 case SLA_LOAD:
1637 case SLA_PTRS:
1638 case SLA_NULL: break;
1639 default: NOT_REACHED();
1640 }
1641 break;
1642 }
1643
1644 case SL_NULL: {
1645 assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
1646
1647 switch (_sl.action) {
1648 case SLA_LOAD_CHECK:
1649 case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
1650 case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1651 case SLA_PTRS:
1652 case SLA_NULL: break;
1653 default: NOT_REACHED();
1654 }
1655 break;
1656 }
1657
1658 case SL_STRUCT:
1659 case SL_STRUCTLIST:
1660 switch (_sl.action) {
1661 case SLA_SAVE: {
1662 if (sld.cmd == SL_STRUCT) {
1663 /* Store in the savegame if this struct was written or not. */
1664 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1665 }
1666 sld.handler->Save(object);
1667 break;
1668 }
1669
1670 case SLA_LOAD_CHECK: {
1671 if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
1672 SlGetStructListLength(1);
1673 }
1674 sld.handler->LoadCheck(object);
1675 break;
1676 }
1677
1678 case SLA_LOAD: {
1679 if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
1680 SlGetStructListLength(1);
1681 }
1682 sld.handler->Load(object);
1683 break;
1684 }
1685
1686 case SLA_PTRS:
1687 sld.handler->FixPointers(object);
1688 break;
1689
1690 case SLA_NULL: break;
1691 default: NOT_REACHED();
1692 }
1693 break;
1694
1695 default: NOT_REACHED();
1696 }
1697 return true;
1698}
1699
1704 void SlSetStructListLength(size_t length)
1705{
1706 /* Automatically calculate the length? */
1707 if (_sl.need_length != NL_NONE) {
1708 SlSetLength(SlGetArrayLength(length));
1709 if (_sl.need_length == NL_CALCLENGTH) return;
1710 }
1711
1712 SlWriteArrayLength(length);
1713}
1714
1720 size_t SlGetStructListLength(size_t limit)
1721{
1722 size_t length = SlReadArrayLength();
1723 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1724
1725 return length;
1726}
1727
1733 void SlObject(void *object, const SaveLoadTable &slt)
1734{
1735 /* Automatically calculate the length? */
1736 if (_sl.need_length != NL_NONE) {
1737 SlSetLength(SlCalcObjLength(object, slt));
1738 if (_sl.need_length == NL_CALCLENGTH) return;
1739 }
1740
1741 for (auto &sld : slt) {
1742 SlObjectMember(object, sld);
1743 }
1744}
1745
1750 class SlSkipHandler : public SaveLoadHandler {
1751 void Save(void *) const override
1752 {
1753 NOT_REACHED();
1754 }
1755
1756 void Load(void *object) const override
1757 {
1758 size_t length = SlGetStructListLength(UINT32_MAX);
1759 for (; length > 0; length--) {
1760 SlObject(object, this->GetLoadDescription());
1761 }
1762 }
1763
1764 void LoadCheck(void *object) const override
1765 {
1766 this->Load(object);
1767 }
1768
1769 virtual SaveLoadTable GetDescription() const override
1770 {
1771 return {};
1772 }
1773
1774 virtual SaveLoadCompatTable GetCompatDescription() const override
1775 {
1776 NOT_REACHED();
1777 }
1778};
1779
1786std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1787{
1788 /* You can only use SlTableHeader if you are a CH_TABLE. */
1789 assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
1790
1791 switch (_sl.action) {
1792 case SLA_LOAD_CHECK:
1793 case SLA_LOAD: {
1794 std::vector<SaveLoad> saveloads;
1795
1796 /* Build a key lookup mapping based on the available fields. */
1797 std::map<std::string, const SaveLoad *> key_lookup;
1798 for (auto &sld : slt) {
1799 if (!SlIsObjectValidInSavegame(sld)) continue;
1800
1801 /* Check that there is only one active SaveLoad for a given name. */
1802 assert(key_lookup.find(sld.name) == key_lookup.end());
1803 key_lookup[sld.name] = &sld;
1804 }
1805
1806 while (true) {
1807 uint8_t type = 0;
1808 SlSaveLoadConv(&type, SLE_UINT8);
1809 if (type == SLE_FILE_END) break;
1810
1811 std::string key;
1812 SlStdString(&key, SLE_STR);
1813
1814 auto sld_it = key_lookup.find(key);
1815 if (sld_it == key_lookup.end()) {
1816 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1817 Debug(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type);
1818
1819 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1820 SaveLoadType saveload_type;
1821 switch (type & SLE_FILE_TYPE_MASK) {
1822 case SLE_FILE_STRING:
1823 /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
1824 saveload_type = SL_STDSTR;
1825 break;
1826
1827 case SLE_FILE_STRUCT:
1828 /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */
1829 saveload_type = SL_STRUCTLIST;
1830 handler = std::make_shared<SlSkipHandler>();
1831 break;
1832
1833 default:
1834 saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
1835 break;
1836 }
1837
1838 /* We don't know this field, so read to nothing. */
1839 saveloads.push_back({key, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, handler});
1840 continue;
1841 }
1842
1843 /* Validate the type of the field. If it is changed, the
1844 * savegame should have been bumped so we know how to do the
1845 * conversion. If this error triggers, that clearly didn't
1846 * happen and this is a friendly poke to the developer to bump
1847 * the savegame version and add conversion code. */
1848 uint8_t correct_type = GetSavegameFileType(*sld_it->second);
1849 if (correct_type != type) {
1850 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type, type);
1851 SlErrorCorrupt("Field type is different than expected");
1852 }
1853 saveloads.push_back(*sld_it->second);
1854 }
1855
1856 for (auto &sld : saveloads) {
1857 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1858 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
1859 }
1860 }
1861
1862 return saveloads;
1863 }
1864
1865 case SLA_SAVE: {
1866 /* Automatically calculate the length? */
1867 if (_sl.need_length != NL_NONE) {
1868 SlSetLength(SlCalcTableHeader(slt));
1869 if (_sl.need_length == NL_CALCLENGTH) break;
1870 }
1871
1872 for (auto &sld : slt) {
1873 if (!SlIsObjectValidInSavegame(sld)) continue;
1874 /* Make sure we are not storing empty keys. */
1875 assert(!sld.name.empty());
1876
1877 uint8_t type = GetSavegameFileType(sld);
1878 assert(type != SLE_FILE_END);
1879
1880 SlSaveLoadConv(&type, SLE_UINT8);
1881 SlStdString(const_cast<std::string *>(&sld.name), SLE_STR);
1882 }
1883
1884 /* Add an end-of-header marker. */
1885 uint8_t type = SLE_FILE_END;
1886 SlSaveLoadConv(&type, SLE_UINT8);
1887
1888 /* After the table, write down any sub-tables we might have. */
1889 for (auto &sld : slt) {
1890 if (!SlIsObjectValidInSavegame(sld)) continue;
1891 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1892 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
1893 NeedLength old_need_length = _sl.need_length;
1894 _sl.need_length = NL_NONE;
1895
1896 SlTableHeader(sld.handler->GetDescription());
1897
1898 _sl.need_length = old_need_length;
1899 }
1900 }
1901
1902 break;
1903 }
1904
1905 default: NOT_REACHED();
1906 }
1907
1908 return std::vector<SaveLoad>();
1909}
1910
1924std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
1925{
1926 assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
1927 /* CH_TABLE / CH_SPARSE_TABLE always have a header. */
1928 if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt);
1929
1930 std::vector<SaveLoad> saveloads;
1931
1932 /* Build a key lookup mapping based on the available fields. */
1933 std::map<std::string, std::vector<const SaveLoad *>> key_lookup;
1934 for (auto &sld : slt) {
1935 /* All entries should have a name; otherwise the entry should just be removed. */
1936 assert(!sld.name.empty());
1937
1938 key_lookup[sld.name].push_back(&sld);
1939 }
1940
1941 for (auto &slc : slct) {
1942 if (slc.name.empty()) {
1943 /* In old savegames there can be data we no longer care for. We
1944 * skip this by simply reading the amount of bytes indicated and
1945 * send those to /dev/null. */
1946 saveloads.push_back({"", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr});
1947 } else {
1948 auto sld_it = key_lookup.find(slc.name);
1949 /* If this branch triggers, it means that an entry in the
1950 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
1951 * you rename a field in one and not in the other? */
1952 if (sld_it == key_lookup.end()) {
1953 /* This isn't an assert, as that leaves no information what
1954 * field was to blame. This way at least we have breadcrumbs. */
1955 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
1956 SlErrorCorrupt("Internal error with savegame compatibility");
1957 }
1958 for (auto &sld : sld_it->second) {
1959 saveloads.push_back(*sld);
1960 }
1961 }
1962 }
1963
1964 for (auto &sld : saveloads) {
1965 if (!SlIsObjectValidInSavegame(sld)) continue;
1966 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1967 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
1968 }
1969 }
1970
1971 return saveloads;
1972}
1973
1978 void SlGlobList(const SaveLoadTable &slt)
1979{
1980 SlObject(nullptr, slt);
1981}
1982
1988 void SlAutolength(AutolengthProc *proc, int arg)
1989{
1990 assert(_sl.action == SLA_SAVE);
1991
1992 /* Tell it to calculate the length */
1993 _sl.need_length = NL_CALCLENGTH;
1994 _sl.obj_len = 0;
1995 proc(arg);
1996
1997 /* Setup length */
1998 _sl.need_length = NL_WANTLENGTH;
1999 SlSetLength(_sl.obj_len);
2000
2001 size_t start_pos = _sl.dumper->GetSize();
2002 size_t expected_offs = start_pos + _sl.obj_len;
2003
2004 /* And write the stuff */
2005 proc(arg);
2006
2007 if (expected_offs != _sl.dumper->GetSize()) {
2008 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2009 }
2010}
2011
2012 void ChunkHandler::LoadCheck(size_t len) const
2013{
2014 switch (_sl.block_mode) {
2015 case CH_TABLE:
2016 case CH_SPARSE_TABLE:
2017 SlTableHeader({});
2018 [[fallthrough]];
2019 case CH_ARRAY:
2020 case CH_SPARSE_ARRAY:
2021 SlSkipArray();
2022 break;
2023 case CH_RIFF:
2024 SlSkipBytes(len);
2025 break;
2026 default:
2027 NOT_REACHED();
2028 }
2029}
2030
2035 static void SlLoadChunk(const ChunkHandler &ch)
2036{
2037 uint8_t m = SlReadByte();
2038
2039 _sl.block_mode = m & CH_TYPE_MASK;
2040 _sl.obj_len = 0;
2041 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2042
2043 /* The header should always be at the start. Read the length; the
2044 * Load() should as first action process the header. */
2045 if (_sl.expect_table_header) {
2046 SlIterateArray();
2047 }
2048
2049 switch (_sl.block_mode) {
2050 case CH_TABLE:
2051 case CH_ARRAY:
2052 _sl.array_index = 0;
2053 ch.Load();
2054 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2055 break;
2056 case CH_SPARSE_TABLE:
2057 case CH_SPARSE_ARRAY:
2058 ch.Load();
2059 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2060 break;
2061 case CH_RIFF: {
2062 /* Read length */
2063 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2064 len += SlReadUint16();
2065 _sl.obj_len = len;
2066 size_t start_pos = _sl.reader->GetSize();
2067 size_t endoffs = start_pos + len;
2068 ch.Load();
2069
2070 if (_sl.reader->GetSize() != endoffs) {
2071 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2072 }
2073 break;
2074 }
2075 default:
2076 SlErrorCorrupt("Invalid chunk type");
2077 break;
2078 }
2079
2080 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2081}
2082
2088 static void SlLoadCheckChunk(const ChunkHandler &ch)
2089{
2090 uint8_t m = SlReadByte();
2091
2092 _sl.block_mode = m & CH_TYPE_MASK;
2093 _sl.obj_len = 0;
2094 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2095
2096 /* The header should always be at the start. Read the length; the
2097 * LoadCheck() should as first action process the header. */
2098 if (_sl.expect_table_header) {
2099 SlIterateArray();
2100 }
2101
2102 switch (_sl.block_mode) {
2103 case CH_TABLE:
2104 case CH_ARRAY:
2105 _sl.array_index = 0;
2106 ch.LoadCheck();
2107 break;
2108 case CH_SPARSE_TABLE:
2109 case CH_SPARSE_ARRAY:
2110 ch.LoadCheck();
2111 break;
2112 case CH_RIFF: {
2113 /* Read length */
2114 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2115 len += SlReadUint16();
2116 _sl.obj_len = len;
2117 size_t start_pos = _sl.reader->GetSize();
2118 size_t endoffs = start_pos + len;
2119 ch.LoadCheck(len);
2120
2121 if (_sl.reader->GetSize() != endoffs) {
2122 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2123 }
2124 break;
2125 }
2126 default:
2127 SlErrorCorrupt("Invalid chunk type");
2128 break;
2129 }
2130
2131 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2132}
2133
2139 static void SlSaveChunk(const ChunkHandler &ch)
2140{
2141 if (ch.type == CH_READONLY) return;
2142
2143 SlWriteUint32(ch.id);
2144 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2145
2146 _sl.block_mode = ch.type;
2147 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2148
2149 _sl.need_length = (_sl.expect_table_header || _sl.block_mode == CH_RIFF) ? NL_WANTLENGTH : NL_NONE;
2150
2151 switch (_sl.block_mode) {
2152 case CH_RIFF:
2153 ch.Save();
2154 break;
2155 case CH_TABLE:
2156 case CH_ARRAY:
2157 _sl.last_array_index = 0;
2158 SlWriteByte(_sl.block_mode);
2159 ch.Save();
2160 SlWriteArrayLength(0); // Terminate arrays
2161 break;
2162 case CH_SPARSE_TABLE:
2163 case CH_SPARSE_ARRAY:
2164 SlWriteByte(_sl.block_mode);
2165 ch.Save();
2166 SlWriteArrayLength(0); // Terminate arrays
2167 break;
2168 default: NOT_REACHED();
2169 }
2170
2171 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2172}
2173
2175 static void SlSaveChunks()
2176{
2177 for (auto &ch : ChunkHandlers()) {
2178 SlSaveChunk(ch);
2179 }
2180
2181 /* Terminator */
2182 SlWriteUint32(0);
2183}
2184
2191 static const ChunkHandler *SlFindChunkHandler(uint32_t id)
2192{
2193 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2194 return nullptr;
2195}
2196
2198 static void SlLoadChunks()
2199{
2200 uint32_t id;
2201 const ChunkHandler *ch;
2202
2203 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2204 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2205
2206 ch = SlFindChunkHandler(id);
2207 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2208 SlLoadChunk(*ch);
2209 }
2210}
2211
2213 static void SlLoadCheckChunks()
2214{
2215 uint32_t id;
2216 const ChunkHandler *ch;
2217
2218 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2219 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2220
2221 ch = SlFindChunkHandler(id);
2222 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2223 SlLoadCheckChunk(*ch);
2224 }
2225}
2226
2228 static void SlFixPointers()
2229{
2230 _sl.action = SLA_PTRS;
2231
2232 for (const ChunkHandler &ch : ChunkHandlers()) {
2233 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2234 ch.FixPointers();
2235 }
2236
2237 assert(_sl.action == SLA_PTRS);
2238}
2239
2240
2242 struct FileReader : LoadFilter {
2243 std::optional<FileHandle> file;
2244 long begin;
2245
2250 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2251 {
2252 }
2253
2255 ~FileReader()
2256 {
2257 if (this->file.has_value()) {
2258 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2259 }
2260 }
2261
2262 size_t Read(uint8_t *buf, size_t size) override
2263 {
2264 /* We're in the process of shutting down, i.e. in "failure" mode. */
2265 if (!this->file.has_value()) return 0;
2266
2267 return fread(buf, 1, size, *this->file);
2268 }
2269
2270 void Reset() override
2271 {
2272 clearerr(*this->file);
2273 if (fseek(*this->file, this->begin, SEEK_SET)) {
2274 Debug(sl, 1, "Could not reset the file reading");
2275 }
2276 }
2277};
2278
2280 struct FileWriter : SaveFilter {
2281 std::optional<FileHandle> file;
2282
2287 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2288 {
2289 }
2290
2292 ~FileWriter()
2293 {
2294 this->Finish();
2295 }
2296
2297 void Write(uint8_t *buf, size_t size) override
2298 {
2299 /* We're in the process of shutting down, i.e. in "failure" mode. */
2300 if (!this->file.has_value()) return;
2301
2302 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2303 }
2304
2305 void Finish() override
2306 {
2307 if (this->file.has_value()) {
2308 _game_session_stats.savegame_size = ftell(*this->file);
2309 this->file.reset();
2310 }
2311 }
2312};
2313
2314/*******************************************
2315 ********** START OF LZO CODE **************
2316 *******************************************/
2317
2318#ifdef WITH_LZO
2319#include <lzo/lzo1x.h>
2320
2322 static const uint LZO_BUFFER_SIZE = 8192;
2323
2325 struct LZOLoadFilter : LoadFilter {
2330 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain)
2331 {
2332 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2333 }
2334
2335 size_t Read(uint8_t *buf, size_t ssize) override
2336 {
2337 assert(ssize >= LZO_BUFFER_SIZE);
2338
2339 /* Buffer size is from the LZO docs plus the chunk header size. */
2340 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2341 uint32_t tmp[2];
2342 uint32_t size;
2343 lzo_uint len = ssize;
2344
2345 /* Read header*/
2346 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2347
2348 /* Check if size is bad */
2349 ((uint32_t*)out)[0] = size = tmp[1];
2350
2351 if (_sl_version != SL_MIN_VERSION) {
2352 tmp[0] = TO_BE32(tmp[0]);
2353 size = TO_BE32(size);
2354 }
2355
2356 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2357
2358 /* Read block */
2359 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2360
2361 /* Verify checksum */
2362 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2363
2364 /* Decompress */
2365 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2366 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2367 return len;
2368 }
2369};
2370
2372 struct LZOSaveFilter : SaveFilter {
2377 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(chain)
2378 {
2379 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2380 }
2381
2382 void Write(uint8_t *buf, size_t size) override
2383 {
2384 const lzo_bytep in = buf;
2385 /* Buffer size is from the LZO docs plus the chunk header size. */
2386 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2387 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2388 lzo_uint outlen;
2389
2390 do {
2391 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2392 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
2393 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2394 ((uint32_t*)out)[1] = TO_BE32((uint32_t)outlen);
2395 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2396 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2397
2398 /* Move to next data chunk. */
2399 size -= len;
2400 in += len;
2401 } while (size > 0);
2402 }
2403};
2404
2405#endif /* WITH_LZO */
2406
2407/*********************************************
2408 ******** START OF NOCOMP CODE (uncompressed)*
2409 *********************************************/
2410
2412 struct NoCompLoadFilter : LoadFilter {
2417 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain)
2418 {
2419 }
2420
2421 size_t Read(uint8_t *buf, size_t size) override
2422 {
2423 return this->chain->Read(buf, size);
2424 }
2425};
2426
2428 struct NoCompSaveFilter : SaveFilter {
2433 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(chain)
2434 {
2435 }
2436
2437 void Write(uint8_t *buf, size_t size) override
2438 {
2439 this->chain->Write(buf, size);
2440 }
2441};
2442
2443/********************************************
2444 ********** START OF ZLIB CODE **************
2445 ********************************************/
2446
2447#if defined(WITH_ZLIB)
2448#include <zlib.h>
2449
2451 struct ZlibLoadFilter : LoadFilter {
2452 z_stream z;
2453 uint8_t fread_buf[MEMORY_CHUNK_SIZE];
2454
2459 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain)
2460 {
2461 memset(&this->z, 0, sizeof(this->z));
2462 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2463 }
2464
2466 ~ZlibLoadFilter()
2467 {
2468 inflateEnd(&this->z);
2469 }
2470
2471 size_t Read(uint8_t *buf, size_t size) override
2472 {
2473 this->z.next_out = buf;
2474 this->z.avail_out = (uint)size;
2475
2476 do {
2477 /* read more bytes from the file? */
2478 if (this->z.avail_in == 0) {
2479 this->z.next_in = this->fread_buf;
2480 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2481 }
2482
2483 /* inflate the data */
2484 int r = inflate(&this->z, 0);
2485 if (r == Z_STREAM_END) break;
2486
2487 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2488 } while (this->z.avail_out != 0);
2489
2490 return size - this->z.avail_out;
2491 }
2492};
2493
2495 struct ZlibSaveFilter : SaveFilter {
2496 z_stream z;
2497 uint8_t fwrite_buf[MEMORY_CHUNK_SIZE];
2498
2504 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(chain)
2505 {
2506 memset(&this->z, 0, sizeof(this->z));
2507 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2508 }
2509
2511 ~ZlibSaveFilter()
2512 {
2513 deflateEnd(&this->z);
2514 }
2515
2522 void WriteLoop(uint8_t *p, size_t len, int mode)
2523 {
2524 uint n;
2525 this->z.next_in = p;
2526 this->z.avail_in = (uInt)len;
2527 do {
2528 this->z.next_out = this->fwrite_buf;
2529 this->z.avail_out = sizeof(this->fwrite_buf);
2530
2538 int r = deflate(&this->z, mode);
2539
2540 /* bytes were emitted? */
2541 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2542 this->chain->Write(this->fwrite_buf, n);
2543 }
2544 if (r == Z_STREAM_END) break;
2545
2546 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2547 } while (this->z.avail_in || !this->z.avail_out);
2548 }
2549
2550 void Write(uint8_t *buf, size_t size) override
2551 {
2552 this->WriteLoop(buf, size, 0);
2553 }
2554
2555 void Finish() override
2556 {
2557 this->WriteLoop(nullptr, 0, Z_FINISH);
2558 this->chain->Finish();
2559 }
2560};
2561
2562#endif /* WITH_ZLIB */
2563
2564/********************************************
2565 ********** START OF LZMA CODE **************
2566 ********************************************/
2567
2568#if defined(WITH_LIBLZMA)
2569#include <lzma.h>
2570
2577 static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2578
2580 struct LZMALoadFilter : LoadFilter {
2581 lzma_stream lzma;
2582 uint8_t fread_buf[MEMORY_CHUNK_SIZE];
2583
2588 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain), lzma(_lzma_init)
2589 {
2590 /* Allow saves up to 256 MB uncompressed */
2591 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2592 }
2593
2595 ~LZMALoadFilter()
2596 {
2597 lzma_end(&this->lzma);
2598 }
2599
2600 size_t Read(uint8_t *buf, size_t size) override
2601 {
2602 this->lzma.next_out = buf;
2603 this->lzma.avail_out = size;
2604
2605 do {
2606 /* read more bytes from the file? */
2607 if (this->lzma.avail_in == 0) {
2608 this->lzma.next_in = this->fread_buf;
2609 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2610 }
2611
2612 /* inflate the data */
2613 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2614 if (r == LZMA_STREAM_END) break;
2615 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2616 } while (this->lzma.avail_out != 0);
2617
2618 return size - this->lzma.avail_out;
2619 }
2620};
2621
2623 struct LZMASaveFilter : SaveFilter {
2624 lzma_stream lzma;
2625 uint8_t fwrite_buf[MEMORY_CHUNK_SIZE];
2626
2632 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(chain), lzma(_lzma_init)
2633 {
2634 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2635 }
2636
2638 ~LZMASaveFilter()
2639 {
2640 lzma_end(&this->lzma);
2641 }
2642
2649 void WriteLoop(uint8_t *p, size_t len, lzma_action action)
2650 {
2651 size_t n;
2652 this->lzma.next_in = p;
2653 this->lzma.avail_in = len;
2654 do {
2655 this->lzma.next_out = this->fwrite_buf;
2656 this->lzma.avail_out = sizeof(this->fwrite_buf);
2657
2658 lzma_ret r = lzma_code(&this->lzma, action);
2659
2660 /* bytes were emitted? */
2661 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2662 this->chain->Write(this->fwrite_buf, n);
2663 }
2664 if (r == LZMA_STREAM_END) break;
2665 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2666 } while (this->lzma.avail_in || !this->lzma.avail_out);
2667 }
2668
2669 void Write(uint8_t *buf, size_t size) override
2670 {
2671 this->WriteLoop(buf, size, LZMA_RUN);
2672 }
2673
2674 void Finish() override
2675 {
2676 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2677 this->chain->Finish();
2678 }
2679};
2680
2681#endif /* WITH_LIBLZMA */
2682
2683/*******************************************
2684 ************* END OF CODE *****************
2685 *******************************************/
2686
2688 struct SaveLoadFormat {
2689 const char *name;
2690 uint32_t tag;
2691
2692 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2693 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2694
2695 uint8_t min_compression;
2696 uint8_t default_compression;
2697 uint8_t max_compression;
2698};
2699
2700static const uint32_t SAVEGAME_TAG_LZO = TO_BE32X('OTTD');
2701static const uint32_t SAVEGAME_TAG_NONE = TO_BE32X('OTTN');
2702static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32X('OTTZ');
2703static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32X('OTTX');
2704
2706 static const SaveLoadFormat _saveload_formats[] = {
2707#if defined(WITH_LZO)
2708 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2709 {"lzo", SAVEGAME_TAG_LZO, CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
2710#else
2711 {"lzo", SAVEGAME_TAG_LZO, nullptr, nullptr, 0, 0, 0},
2712#endif
2713 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2714 {"none", SAVEGAME_TAG_NONE, CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
2715#if defined(WITH_ZLIB)
2716 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2717 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2718 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2719 {"zlib", SAVEGAME_TAG_ZLIB, CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
2720#else
2721 {"zlib", SAVEGAME_TAG_ZLIB, nullptr, nullptr, 0, 0, 0},
2722#endif
2723#if defined(WITH_LIBLZMA)
2724 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2725 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2726 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2727 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2728 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2729 {"lzma", SAVEGAME_TAG_LZMA, CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
2730#else
2731 {"lzma", SAVEGAME_TAG_LZMA, nullptr, nullptr, 0, 0, 0},
2732#endif
2733};
2734
2741 static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(const std::string &full_name)
2742{
2743 /* Find default savegame format, the highest one with which files can be written. */
2744 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2745 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2746
2747 const SaveLoadFormat &def = *it;
2748
2749 if (!full_name.empty()) {
2750 /* Get the ":..." of the compression level out of the way */
2751 size_t separator = full_name.find(':');
2752 bool has_comp_level = separator != std::string::npos;
2753 const std::string name(full_name, 0, has_comp_level ? separator : full_name.size());
2754
2755 for (const auto &slf : _saveload_formats) {
2756 if (slf.init_write != nullptr && name.compare(slf.name) == 0) {
2757 if (has_comp_level) {
2758 const std::string complevel(full_name, separator + 1);
2759
2760 /* Get the level and determine whether all went fine. */
2761 size_t processed;
2762 long level = std::stol(complevel, &processed, 10);
2763 if (processed == 0 || level != Clamp(level, slf.min_compression, slf.max_compression)) {
2764 SetDParamStr(0, complevel);
2765 ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, WL_CRITICAL);
2766 } else {
2767 return {slf, ClampTo<uint8_t>(level)};
2768 }
2769 }
2770 return {slf, slf.default_compression};
2771 }
2772 }
2773
2774 SetDParamStr(0, name);
2775 SetDParamStr(1, def.name);
2776 ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, WL_CRITICAL);
2777 }
2778 return {def, def.default_compression};
2779}
2780
2781/* actual loader/saver function */
2782void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2783extern bool AfterLoadGame();
2784extern bool LoadOldSaveGame(const std::string &file);
2785
2789 static void ResetSaveloadData()
2790{
2791 ResetTempEngineData();
2792 ResetLabelMaps();
2793 ResetOldWaypoints();
2794}
2795
2799 static inline void ClearSaveLoadState()
2800{
2801 _sl.dumper = nullptr;
2802 _sl.sf = nullptr;
2803 _sl.reader = nullptr;
2804 _sl.lf = nullptr;
2805}
2806
2808 static void SaveFileStart()
2809{
2810 SetMouseCursorBusy(true);
2811
2812 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
2813 _sl.saveinprogress = true;
2814}
2815
2817 static void SaveFileDone()
2818{
2819 SetMouseCursorBusy(false);
2820
2821 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
2822 _sl.saveinprogress = false;
2823
2824#ifdef __EMSCRIPTEN__
2825 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
2826#endif
2827}
2828
2830 void SetSaveLoadError(StringID str)
2831{
2832 _sl.error_str = str;
2833}
2834
2836 StringID GetSaveLoadErrorType()
2837{
2838 return _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED;
2839}
2840
2842 StringID GetSaveLoadErrorMessage()
2843{
2844 SetDParamStr(0, _sl.extra_msg);
2845 return _sl.error_str;
2846}
2847
2849 static void SaveFileError()
2850{
2851 ShowErrorMessage(GetSaveLoadErrorType(), GetSaveLoadErrorMessage(), WL_ERROR);
2852 SaveFileDone();
2853}
2854
2859 static SaveOrLoadResult SaveFileToDisk(bool threaded)
2860{
2861 try {
2862 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
2863
2864 /* We have written our stuff to memory, now write it to file! */
2865 uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
2866 _sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
2867
2868 _sl.sf = fmt.init_write(_sl.sf, compression);
2869 _sl.dumper->Flush(_sl.sf);
2870
2871 ClearSaveLoadState();
2872
2873 if (threaded) SetAsyncSaveFinish(SaveFileDone);
2874
2875 return SL_OK;
2876 } catch (...) {
2877 ClearSaveLoadState();
2878
2879 AsyncSaveFinishProc asfp = SaveFileDone;
2880
2881 /* We don't want to shout when saving is just
2882 * cancelled due to a client disconnecting. */
2883 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
2884 /* Skip the "colour" character */
2885 Debug(sl, 0, "{}", GetString(GetSaveLoadErrorType()).substr(3) + GetString(GetSaveLoadErrorMessage()));
2886 asfp = SaveFileError;
2887 }
2888
2889 if (threaded) {
2890 SetAsyncSaveFinish(asfp);
2891 } else {
2892 asfp();
2893 }
2894 return SL_ERROR;
2895 }
2896}
2897
2898void WaitTillSaved()
2899{
2900 if (!_save_thread.joinable()) return;
2901
2902 _save_thread.join();
2903
2904 /* Make sure every other state is handled properly as well. */
2905 ProcessAsyncSaveFinish();
2906}
2907
2916 static SaveOrLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
2917{
2918 assert(!_sl.saveinprogress);
2919
2920 _sl.dumper = std::make_unique<MemoryDumper>();
2921 _sl.sf = writer;
2922
2923 _sl_version = SAVEGAME_VERSION;
2924
2925 SaveViewportBeforeSaveGame();
2926 SlSaveChunks();
2927
2928 SaveFileStart();
2929
2930 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
2931 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
2932
2933 SaveOrLoadResult result = SaveFileToDisk(false);
2934 SaveFileDone();
2935
2936 return result;
2937 }
2938
2939 return SL_OK;
2940}
2941
2948 SaveOrLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
2949{
2950 try {
2951 _sl.action = SLA_SAVE;
2952 return DoSave(writer, threaded);
2953 } catch (...) {
2954 ClearSaveLoadState();
2955 return SL_ERROR;
2956 }
2957}
2958
2967 static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
2968{
2969 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
2970 if (fmt != std::end(_saveload_formats)) {
2971 /* Check version number */
2972 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
2973 /* Minor is not used anymore from version 18.0, but it is still needed
2974 * in versions before that (4 cases) which can't be removed easy.
2975 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
2976 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
2977
2978 Debug(sl, 1, "Loading savegame version {}", _sl_version);
2979
2980 /* Is the version higher than the current? */
2981 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
2982 if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
2983 return fmt;
2984 }
2985
2986 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
2987 _sl.lf->Reset();
2988 _sl_version = SL_MIN_VERSION;
2989 _sl_minor_version = 0;
2990
2991 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
2992 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
2993 if (fmt == std::end(_saveload_formats)) {
2994 /* Who removed the LZO savegame format definition? When built without LZO support,
2995 * the formats must still list it just without a method to read the file.
2996 * The caller of this function has to check for the existence of load function. */
2997 NOT_REACHED();
2998 }
2999 return fmt;
3000}
3001
3008 static SaveOrLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3009{
3010 _sl.lf = reader;
3011
3012 if (load_check) {
3013 /* Clear previous check data */
3014 _load_check_data.Clear();
3015 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3016 _load_check_data.checkable = true;
3017 }
3018
3019 uint32_t hdr[2];
3020 if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3021
3022 /* see if we have any loader for this type. */
3023 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
3024
3025 /* loader for this savegame type is not implemented? */
3026 if (fmt->init_load == nullptr) {
3027 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3028 }
3029
3030 _sl.lf = fmt->init_load(_sl.lf);
3031 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3032 _next_offs = 0;
3033
3034 if (!load_check) {
3035 ResetSaveloadData();
3036
3037 /* Old maps were hardcoded to 256x256 and thus did not contain
3038 * any mapsize information. Pre-initialize to 256x256 to not to
3039 * confuse old games */
3040 InitializeGame(256, 256, true, true);
3041
3042 _gamelog.Reset();
3043
3044 if (IsSavegameVersionBefore(SLV_4)) {
3045 /*
3046 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3047 * shared savegame version 4. Anything before that 'obviously'
3048 * does not have any NewGRFs. Between the introduction and
3049 * savegame version 41 (just before 0.5) the NewGRF settings
3050 * were not stored in the savegame and they were loaded by
3051 * using the settings from the main menu.
3052 * So, to recap:
3053 * - savegame version < 4: do not load any NewGRFs.
3054 * - savegame version >= 41: load NewGRFs from savegame, which is
3055 * already done at this stage by
3056 * overwriting the main menu settings.
3057 * - other savegame versions: use main menu settings.
3058 *
3059 * This means that users *can* crash savegame version 4..40
3060 * savegames if they set incompatible NewGRFs in the main menu,
3061 * but can't crash anymore for savegame version < 4 savegames.
3062 *
3063 * Note: this is done here because AfterLoadGame is also called
3064 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3065 */
3066 ClearGRFConfigList(&_grfconfig);
3067 }
3068 }
3069
3070 if (load_check) {
3071 /* Load chunks into _load_check_data.
3072 * No pools are loaded. References are not possible, and thus do not need resolving. */
3073 SlLoadCheckChunks();
3074 } else {
3075 /* Load chunks and resolve references */
3076 SlLoadChunks();
3077 SlFixPointers();
3078 }
3079
3080 ClearSaveLoadState();
3081
3082 _savegame_type = SGT_OTTD;
3083
3084 if (load_check) {
3085 /* The only part from AfterLoadGame() we need */
3086 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
3087 } else {
3088 _gamelog.StartAction(GLAT_LOAD);
3089
3090 /* After loading fix up savegame for any internal changes that
3091 * might have occurred since then. If it fails, load back the old game. */
3092 if (!AfterLoadGame()) {
3093 _gamelog.StopAction();
3094 return SL_REINIT;
3095 }
3096
3097 _gamelog.StopAction();
3098 }
3099
3100 return SL_OK;
3101}
3102
3108 SaveOrLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3109{
3110 try {
3111 _sl.action = SLA_LOAD;
3112 return DoLoad(reader, false);
3113 } catch (...) {
3114 ClearSaveLoadState();
3115 return SL_REINIT;
3116 }
3117}
3118
3128 SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3129{
3130 /* An instance of saving is already active, so don't go saving again */
3131 if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) {
3132 /* if not an autosave, but a user action, show error message */
3133 if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR);
3134 return SL_OK;
3135 }
3136 WaitTillSaved();
3137
3138 try {
3139 /* Load a TTDLX or TTDPatch game */
3140 if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) {
3141 ResetSaveloadData();
3142
3143 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3144
3145 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3146 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3147 * Note: this is done here because AfterLoadGame is also called
3148 * for OTTD savegames which have their own NewGRF logic. */
3149 ClearGRFConfigList(&_grfconfig);
3150 _gamelog.Reset();
3151 if (!LoadOldSaveGame(filename)) return SL_REINIT;
3152 _sl_version = SL_MIN_VERSION;
3153 _sl_minor_version = 0;
3154 _gamelog.StartAction(GLAT_LOAD);
3155 if (!AfterLoadGame()) {
3156 _gamelog.StopAction();
3157 return SL_REINIT;
3158 }
3159 _gamelog.StopAction();
3160 return SL_OK;
3161 }
3162
3163 assert(dft == DFT_GAME_FILE);
3164 switch (fop) {
3165 case SLO_CHECK:
3166 _sl.action = SLA_LOAD_CHECK;
3167 break;
3168
3169 case SLO_LOAD:
3170 _sl.action = SLA_LOAD;
3171 break;
3172
3173 case SLO_SAVE:
3174 _sl.action = SLA_SAVE;
3175 break;
3176
3177 default: NOT_REACHED();
3178 }
3179
3180 auto fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3181
3182 /* Make it a little easier to load savegames from the console */
3183 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
3184 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
3185 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
3186
3187 if (!fh.has_value()) {
3188 SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3189 }
3190
3191 if (fop == SLO_SAVE) { // SAVE game
3192 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3193 if (!_settings_client.gui.threaded_saves) threaded = false;
3194
3195 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3196 }
3197
3198 /* LOAD game */
3199 assert(fop == SLO_LOAD || fop == SLO_CHECK);
3200 Debug(desync, 1, "load: {}", filename);
3201 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SLO_CHECK);
3202 } catch (...) {
3203 /* This code may be executed both for old and new save games. */
3204 ClearSaveLoadState();
3205
3206 /* Skip the "colour" character */
3207 if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetString(GetSaveLoadErrorType()).substr(3) + GetString(GetSaveLoadErrorMessage()));
3208
3209 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3210 return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;
3211 }
3212}
3213
3219 void DoAutoOrNetsave(FiosNumberedSaveName &counter)
3220{
3221 std::string filename;
3222
3223 if (_settings_client.gui.keep_all_autosave) {
3224 filename = GenerateDefaultSaveName() + counter.Extension();
3225 } else {
3226 filename = counter.Filename();
3227 }
3228
3229 Debug(sl, 2, "Autosaving to '{}'", filename);
3230 if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) {
3231 ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR);
3232 }
3233}
3234
3235
3237 void DoExitSave()
3238{
3239 SaveOrLoad("exit.sav", SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR);
3240}
3241
3245std::string GenerateDefaultSaveName()
3246{
3247 /* Check if we have a name for this map, which is the name of the first
3248 * available company. When there's no company available we'll use
3249 * 'Spectator' as "company" name. */
3250 CompanyID cid = _local_company;
3251 if (!Company::IsValidID(cid)) {
3252 for (const Company *c : Company::Iterate()) {
3253 cid = c->index;
3254 break;
3255 }
3256 }
3257
3258 SetDParam(0, cid);
3259
3260 /* We show the current game time differently depending on the timekeeping units used by this game. */
3261 if (TimerGameEconomy::UsingWallclockUnits()) {
3262 /* Insert time played. */
3263 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3264 SetDParam(1, STR_SAVEGAME_DURATION_REALTIME);
3265 SetDParam(2, play_time / 60 / 60);
3266 SetDParam(3, (play_time / 60) % 60);
3267 } else {
3268 /* Insert current date */
3269 switch (_settings_client.gui.date_format_in_default_names) {
3270 case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
3271 case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
3272 case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
3273 default: NOT_REACHED();
3274 }
3275 SetDParam(2, TimerGameEconomy::date);
3276 }
3277
3278 /* Get the correct string (special string for when there's not company) */
3279 std::string filename = GetString(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT);
3280 SanitizeFilename(filename);
3281 return filename;
3282}
3283
3288 void FileToSaveLoad::SetMode(FiosType ft)
3289{
3290 this->SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft));
3291}
3292
3299 void FileToSaveLoad::SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft)
3300{
3301 if (aft == FT_INVALID || aft == FT_NONE) {
3302 this->file_op = SLO_INVALID;
3303 this->detail_ftype = DFT_INVALID;
3304 this->abstract_ftype = FT_INVALID;
3305 return;
3306 }
3307
3308 this->file_op = fop;
3309 this->detail_ftype = dft;
3310 this->abstract_ftype = aft;
3311}
3312
3317 void FileToSaveLoad::Set(const FiosItem &item)
3318{
3319 this->SetMode(item.type);
3320 this->name = item.name;
3321 this->title = item.title;
3322}
3323
3324 SaveLoadTable SaveLoadHandler::GetLoadDescription() const
3325{
3326 assert(this->load_description.has_value());
3327 return *this->load_description;
3328}
HasBit
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
BSWAP32
static uint32_t BSWAP32(uint32_t x)
Perform a 32 bits endianness bitswap on x.
Definition bitmath_func.hpp:364
GB
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Definition bitmath_func.hpp:32
FileHandle
Definition fileio_type.h:158
Gamelog::StartAction
void StartAction(GamelogActionType at)
Stores information about new action, but doesn't allocate it Action is allocated only when there is a...
Definition gamelog.cpp:65
Gamelog::Reset
void Reset()
Resets and frees all memory allocated - used before loading or starting a new game.
Definition gamelog.cpp:94
Gamelog::StopAction
void StopAction()
Stops logging of any changes.
Definition gamelog.cpp:74
LinkGraphJob
Class for calculation jobs to be run on link graphs.
Definition linkgraphjob.h:29
LinkGraph
A connected component of a link graph.
Definition linkgraph.h:37
SaveLoadHandler
Handler for saving/loading an object to/from disk.
Definition saveload.h:521
SaveLoadHandler::GetLoadDescription
SaveLoadTable GetLoadDescription() const
Get the description for how to load the chunk.
Definition saveload.cpp:3324
SlSkipHandler
Handler that is assigned when there is a struct read in the savegame which is not known to the code.
Definition saveload.cpp:1750
SlSkipHandler::GetDescription
virtual SaveLoadTable GetDescription() const override
Get the description of the fields in the savegame.
Definition saveload.cpp:1769
SlSkipHandler::GetCompatDescription
virtual SaveLoadCompatTable GetCompatDescription() const override
Get the pre-header description of the fields in the savegame.
Definition saveload.cpp:1774
SlSkipHandler::LoadCheck
void LoadCheck(void *object) const override
Similar to load, but used only to validate savegames.
Definition saveload.cpp:1764
SlSkipHandler::Load
void Load(void *object) const override
Load the object from disk.
Definition saveload.cpp:1756
SlSkipHandler::Save
void Save(void *) const override
Save the object to disk.
Definition saveload.cpp:1751
SlStorageHelper
Template class to help with list-like types.
Definition saveload.cpp:1239
SlStorageHelper::SlSaveLoad
static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd=SL_VAR)
Internal templated helper to save/load a list-like type.
Definition saveload.cpp:1276
SlStorageHelper::SlCalcLen
static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd=SL_VAR)
Internal templated helper to return the size in bytes of a list-like type.
Definition saveload.cpp:1248
Ticks::TICKS_PER_SECOND
static constexpr TimerGameTick::Ticks TICKS_PER_SECOND
Estimation of how many ticks fit in a single second.
Definition timer_game_tick.h:76
TimerGameEconomy::date
static Date date
Current date in days (day counter).
Definition timer_game_economy.h:38
TimerGameEconomy::UsingWallclockUnits
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
Definition timer_game_economy.cpp:97
TimerGameEconomy::date_fract
static DateFract date_fract
Fractional part of the day.
Definition timer_game_economy.h:39
TimerGameTick::counter
static TickCounter counter
Monotonic counter, in ticks, since start of game.
Definition timer_game_tick.h:60
_local_company
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition company_cmd.cpp:52
Owner
Owner
Enum for all companies/owners.
Definition company_type.h:18
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition debug.h:37
ShowErrorMessage
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
Definition error_gui.cpp:367
WL_ERROR
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
WL_CRITICAL
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
SanitizeFilename
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition fileio.cpp:1003
FioFOpenFile
std::optional< FileHandle > FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:242
GetDetailedFileType
DetailedFileType GetDetailedFileType(FiosType fios_type)
Extract the detailed file type from a FiosType.
Definition fileio_type.h:107
GetAbstractFileType
AbstractFileType GetAbstractFileType(FiosType fios_type)
Extract the abstract file type from a FiosType.
Definition fileio_type.h:97
AbstractFileType
AbstractFileType
The different abstract types of files that the system knows about.
Definition fileio_type.h:16
FT_NONE
@ FT_NONE
nothing to do
Definition fileio_type.h:17
FT_INVALID
@ FT_INVALID
Invalid or unknown file type.
Definition fileio_type.h:23
FiosType
FiosType
Elements of a file system that are recognized.
Definition fileio_type.h:73
SaveLoadOperation
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:53
SLO_CHECK
@ SLO_CHECK
Load file for checking and/or preview.
Definition fileio_type.h:54
SLO_SAVE
@ SLO_SAVE
File is being saved.
Definition fileio_type.h:56
SLO_LOAD
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:55
SLO_INVALID
@ SLO_INVALID
Unknown file operation.
Definition fileio_type.h:58
DetailedFileType
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:29
DFT_GAME_FILE
@ DFT_GAME_FILE
Save game or scenario file.
Definition fileio_type.h:32
DFT_INVALID
@ DFT_INVALID
Unknown or invalid file.
Definition fileio_type.h:49
DFT_OLD_GAME_FILE
@ DFT_OLD_GAME_FILE
Old save game or scenario file.
Definition fileio_type.h:31
Subdirectory
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition fileio_type.h:115
SCENARIO_DIR
@ SCENARIO_DIR
Base directory for all scenarios.
Definition fileio_type.h:119
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition fileio_type.h:116
SAVE_DIR
@ SAVE_DIR
Base directory for all savegames.
Definition fileio_type.h:117
AUTOSAVE_DIR
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition fileio_type.h:118
_load_check_data
LoadCheckData _load_check_data
Data loaded from save during SL_LOAD_CHECK.
Definition fios_gui.cpp:41
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition fluidsynth.cpp:21
_gamelog
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
GLAT_LOAD
@ GLAT_LOAD
Game loaded.
Definition gamelog.h:18
SetMouseCursorBusy
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1670
_game_session_stats
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:51
Clamp
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
ClearGRFConfigList
void ClearGRFConfigList(GRFConfig **config)
Clear a GRF Config list, freeing all nodes.
Definition newgrf_config.cpp:336
_grfconfig
GRFConfig * _grfconfig
First item in list of current GRF set up.
Definition newgrf_config.cpp:157
IsGoodGRFConfigList
GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig)
Check if all GRFs in the GRF config from a savegame can be loaded.
Definition newgrf_config.cpp:449
SlRefVector
static void SlRefVector(void *vector, VarType conv)
Save/Load a vector.
Definition saveload.cpp:1371
LZO_BUFFER_SIZE
static const uint LZO_BUFFER_SIZE
Buffer size for the LZO compressor.
Definition saveload.cpp:2322
SlError
void SlError(StringID string, const std::string &extra_msg)
Error handler.
Definition saveload.cpp:321
SlFindChunkHandler
static const ChunkHandler * SlFindChunkHandler(uint32_t id)
Find the ChunkHandler that will be used for processing the found chunk in the savegame or in memory.
Definition saveload.cpp:2191
GetSavegameFileType
static uint8_t GetSavegameFileType(const SaveLoad &sld)
Return the type as saved/loaded inside the savegame.
Definition saveload.cpp:552
DoSave
static SaveOrLoadResult DoSave(std::shared_ptr< SaveFilter > writer, bool threaded)
Actually perform the saving of the savegame.
Definition saveload.cpp:2916
ProcessAsyncSaveFinish
void ProcessAsyncSaveFinish()
Handle async save finishes.
Definition saveload.cpp:376
_lzma_init
static const lzma_stream _lzma_init
Have a copy of an initialised LZMA stream.
Definition saveload.cpp:2577
IntToReference
static void * IntToReference(size_t index, SLRefType rt)
Pointers cannot be loaded from a savegame, so this function gets the index from the savegame and retu...
Definition saveload.cpp:1139
_ttdp_version
uint32_t _ttdp_version
version of TTDP savegame (if applicable)
Definition saveload.cpp:62
_saveload_formats
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
Definition saveload.cpp:2706
_savegame_format
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:65
SaveFileDone
static void SaveFileDone()
Update the gui accordingly when saving is done and release locks on saveload.
Definition saveload.cpp:2817
GetSaveLoadErrorMessage
StringID GetSaveLoadErrorMessage()
Return the description of the error.
Definition saveload.cpp:2842
_sl_version
SaveLoadVersion _sl_version
the major savegame version identifier
Definition saveload.cpp:63
ChunkHandlers
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:202
_sl_minor_version
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:64
SlCalcRefVectorLen
static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
Return the size in bytes of a vector.
Definition saveload.cpp:1361
LoadWithFilter
SaveOrLoadResult LoadWithFilter(std::shared_ptr< LoadFilter > reader)
Load the game using a (reader) filter.
Definition saveload.cpp:3108
SaveFileToDisk
static SaveOrLoadResult SaveFileToDisk(bool threaded)
We have written the whole game into memory, _memory_savegame, now find and appropriate compressor and...
Definition saveload.cpp:2859
ResetSaveloadData
static void ResetSaveloadData()
Clear temporary data that is passed between various saveload phases.
Definition saveload.cpp:2789
ReferenceToInt
static size_t ReferenceToInt(const void *obj, SLRefType rt)
Pointers cannot be saved to a savegame, so this functions gets the index of the item,...
Definition saveload.cpp:1106
SaveWithFilter
SaveOrLoadResult SaveWithFilter(std::shared_ptr< SaveFilter > writer, bool threaded)
Save the game using a (writer) filter.
Definition saveload.cpp:2948
SlWriteSimpleGamma
static void SlWriteSimpleGamma(size_t i)
Write the header descriptor of an object or an array.
Definition saveload.cpp:494
SlCalcTableHeader
static size_t SlCalcTableHeader(const SaveLoadTable &slt)
Calculate the size of the table header.
Definition saveload.cpp:1510
ClearSaveLoadState
static void ClearSaveLoadState()
Clear/free saveload state.
Definition saveload.cpp:2799
_do_autosave
bool _do_autosave
are we doing an autosave at the moment?
Definition saveload.cpp:66
GetSaveLoadErrorType
StringID GetSaveLoadErrorType()
Return the appropriate initial string for an error depending on whether we are saving or loading.
Definition saveload.cpp:2836
_async_save_finish
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:358
_save_thread
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:359
SlCalcConvMemLen
static uint SlCalcConvMemLen(VarType conv)
Return the size in bytes of a certain type of normal/atomic variable as it appears in memory.
Definition saveload.cpp:588
SlCompatTableHeader
std::vector< SaveLoad > SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
Load a table header in a savegame compatible way.
Definition saveload.cpp:1924
SlWriteByte
void SlWriteByte(uint8_t b)
Wrapper for writing a byte to the dumper.
Definition saveload.cpp:401
SlGetStructListLength
size_t SlGetStructListLength(size_t limit)
Get the length of this list; if it exceeds the limit, error out.
Definition saveload.cpp:1720
SaveLoadAction
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:69
SLA_LOAD
@ SLA_LOAD
loading
Definition saveload.cpp:70
SLA_NULL
@ SLA_NULL
null all pointers (on loading error)
Definition saveload.cpp:73
SLA_SAVE
@ SLA_SAVE
saving
Definition saveload.cpp:71
SLA_LOAD_CHECK
@ SLA_LOAD_CHECK
partial loading into _load_check_data
Definition saveload.cpp:74
SLA_PTRS
@ SLA_PTRS
fixing pointers
Definition saveload.cpp:72
SlCalcArrayLen
static size_t SlCalcArrayLen(size_t length, VarType conv)
Return the size in bytes of a certain type of atomic array.
Definition saveload.cpp:1049
WriteValue
void WriteValue(void *ptr, VarType conv, int64_t val)
Write the value of a setting.
Definition saveload.cpp:818
AsyncSaveFinishProc
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:357
SlIterateArray
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:659
SaveOrLoad
SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
Main Save or Load function where the high-level saveload functions are handled.
Definition saveload.cpp:3128
SetAsyncSaveFinish
static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
Called by save thread to tell we finished saving.
Definition saveload.cpp:365
SetSaveLoadError
void SetSaveLoadError(StringID str)
Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friend...
Definition saveload.cpp:2830
SlCopy
void SlCopy(void *object, size_t length, VarType conv)
Copy a list of SL_VARs to/from a savegame.
Definition saveload.cpp:1030
GetSavegameFormat
static std::pair< const SaveLoadFormat &, uint8_t > GetSavegameFormat(const std::string &full_name)
Return the savegameformat of the game.
Definition saveload.cpp:2741
SlGetFieldLength
size_t SlGetFieldLength()
Get the length of the current object.
Definition saveload.cpp:782
DoAutoOrNetsave
void DoAutoOrNetsave(FiosNumberedSaveName &counter)
Create an autosave or netsave.
Definition saveload.cpp:3219
SlCalcRefLen
static size_t SlCalcRefLen()
Return the size in bytes of a reference (pointer)
Definition saveload.cpp:642
SaveFileStart
static void SaveFileStart()
Update the gui accordingly when starting saving and set locks on saveload.
Definition saveload.cpp:2808
SlNullPointers
static void SlNullPointers()
Null all pointers (convert index -> nullptr)
Definition saveload.cpp:296
SlStdString
static void SlStdString(void *ptr, VarType conv)
Save/Load a std::string.
Definition saveload.cpp:933
DoLoad
static SaveOrLoadResult DoLoad(std::shared_ptr< LoadFilter > reader, bool load_check)
Actually perform the loading of a "non-old" savegame.
Definition saveload.cpp:3008
SlCalcRefListLen
static size_t SlCalcRefListLen(const void *list, VarType conv)
Return the size in bytes of a list.
Definition saveload.cpp:1334
SlIsObjectValidInSavegame
static bool SlIsObjectValidInSavegame(const SaveLoad &sld)
Are we going to save this object or not?
Definition saveload.cpp:1500
SlSaveLoadRef
void SlSaveLoadRef(void *ptr, VarType conv)
Handle conversion for references.
Definition saveload.cpp:1215
SlFixPointers
static void SlFixPointers()
Fix all pointers (convert index -> pointer)
Definition saveload.cpp:2228
SlErrorCorrupt
void SlErrorCorrupt(const std::string &msg)
Error handler for corrupt savegames.
Definition saveload.cpp:351
SlSkipArray
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:701
SlLoadChunk
static void SlLoadChunk(const ChunkHandler &ch)
Load a chunk of data (eg vehicles, stations, etc.)
Definition saveload.cpp:2035
SlLoadCheckChunks
static void SlLoadCheckChunks()
Load all chunks for savegame checking.
Definition saveload.cpp:2213
SlCalcStdStringLen
static size_t SlCalcStdStringLen(const void *ptr)
Calculate the gross length of the string that it will occupy in the savegame.
Definition saveload.cpp:899
SlReadSimpleGamma
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:452
SlCopyBytes
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:765
NeedLength
NeedLength
Definition saveload.cpp:77
NL_WANTLENGTH
@ NL_WANTLENGTH
writing length and data
Definition saveload.cpp:79
NL_NONE
@ NL_NONE
not working in NeedLength mode
Definition saveload.cpp:78
NL_CALCLENGTH
@ NL_CALCLENGTH
need to calculate the length
Definition saveload.cpp:80
SlCopyInternal
static void SlCopyInternal(void *object, size_t length, VarType conv)
Internal function to save/Load a list of SL_VARs.
Definition saveload.cpp:981
SlArray
static void SlArray(void *array, size_t length, VarType conv)
Save/Load the length of the array followed by the array of SL_VAR elements.
Definition saveload.cpp:1060
_savegame_type
SavegameType _savegame_type
type of savegame we are loading
Definition saveload.cpp:59
SlSaveLoadConv
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:844
_file_to_saveload
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:60
SlLoadCheckChunk
static void SlLoadCheckChunk(const ChunkHandler &ch)
Load a chunk of data for checking savegames.
Definition saveload.cpp:2088
SlSetLength
void SlSetLength(size_t length)
Sets the length of either a RIFF object or the number of items in an array.
Definition saveload.cpp:713
SlReadByte
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:392
_sl
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:200
DoExitSave
void DoExitSave()
Do a save when exiting the game (_settings_client.gui.autosave_on_exit)
Definition saveload.cpp:3237
SlLoadChunks
static void SlLoadChunks()
Load all chunks.
Definition saveload.cpp:2198
SlDeque
static void SlDeque(void *deque, VarType conv)
Save/load a std::deque.
Definition saveload.cpp:1415
SlCalcConvFileLen
static uint8_t SlCalcConvFileLen(VarType conv)
Return the size in bytes of a certain type of normal/atomic variable as it appears in a saved game.
Definition saveload.cpp:618
SlSaveChunk
static void SlSaveChunk(const ChunkHandler &ch)
Save a chunk of data (eg.
Definition saveload.cpp:2139
SAVEGAME_VERSION
const SaveLoadVersion SAVEGAME_VERSION
Current savegame version of OpenTTD.
SlCalcObjLength
size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
Calculate the size of an object.
Definition saveload.cpp:1539
SlObject
void SlObject(void *object, const SaveLoadTable &slt)
Main SaveLoad function.
Definition saveload.cpp:1733
SlTableHeader
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
Definition saveload.cpp:1786
AfterLoadGame
bool AfterLoadGame()
Perform a (large) amount of savegame conversion magic in order to load older savegames and to fill th...
Definition afterload.cpp:566
ReadValue
int64_t ReadValue(const void *ptr, VarType conv)
Return a signed-long version of the value of a setting.
Definition saveload.cpp:794
SlVector
static void SlVector(void *vector, VarType conv)
Save/load a std::vector.
Definition saveload.cpp:1473
SaveFileError
static void SaveFileError()
Show a gui message when saving has failed.
Definition saveload.cpp:2849
SlGlobList
void SlGlobList(const SaveLoadTable &slt)
Save or Load (a list of) global variables.
Definition saveload.cpp:1978
SlSaveChunks
static void SlSaveChunks()
Save all chunks.
Definition saveload.cpp:2175
GenerateDefaultSaveName
std::string GenerateDefaultSaveName()
Get the default name for a savegame or screenshot.
Definition saveload.cpp:3245
MEMORY_CHUNK_SIZE
static const size_t MEMORY_CHUNK_SIZE
Save in chunks of 128 KiB.
Definition saveload.cpp:84
DetermineSaveLoadFormat
static const SaveLoadFormat * DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
Determines the SaveLoadFormat that is connected to the given tag.
Definition saveload.cpp:2967
SlAutolength
void SlAutolength(AutolengthProc *proc, int arg)
Do something of which I have no idea what it is :P.
Definition saveload.cpp:1988
FixSCCEncoded
static void FixSCCEncoded(std::string &str)
Scan the string for old values of SCC_ENCODED and fix it to it's new, value.
Definition saveload.cpp:915
SlSetStructListLength
void SlSetStructListLength(size_t length)
Set the length of this list.
Definition saveload.cpp:1704
SlGetGammaLength
static uint SlGetGammaLength(size_t i)
Return how many bytes used to encode a gamma value.
Definition saveload.cpp:519
SlCalcVectorLen
static size_t SlCalcVectorLen(const void *vector, VarType conv)
Return the size in bytes of a std::vector.
Definition saveload.cpp:1446
SlRefList
static void SlRefList(void *list, VarType conv)
Save/Load a list.
Definition saveload.cpp:1344
SlCalcDequeLen
static size_t SlCalcDequeLen(const void *deque, VarType conv)
Return the size in bytes of a std::deque.
Definition saveload.cpp:1388
SLE_VAR_NULL
@ SLE_VAR_NULL
useful to write zeros in savegame.
Definition saveload.h:653
SLE_FILE_END
@ SLE_FILE_END
Used to mark end-of-header in tables.
Definition saveload.h:626
SLE_FILE_TYPE_MASK
@ SLE_FILE_TYPE_MASK
Mask to get the file-type (and not any flags).
Definition saveload.h:640
SLE_FILE_HAS_LENGTH_FIELD
@ SLE_FILE_HAS_LENGTH_FIELD
Bit stored in savegame to indicate field has a length field for each entry.
Definition saveload.h:641
SLF_ALLOW_NEWLINE
@ SLF_ALLOW_NEWLINE
Allow new lines in the strings.
Definition saveload.h:689
SLF_ALLOW_CONTROL
@ SLF_ALLOW_CONTROL
Allow control codes in the strings.
Definition saveload.h:688
SLE_VAR_STR
@ SLE_VAR_STR
string pointer
Definition saveload.h:654
SLE_VAR_NAME
@ SLE_VAR_NAME
old custom name to be converted to a char pointer
Definition saveload.h:656
SLE_VAR_STRQ
@ SLE_VAR_STRQ
string pointer enclosed in quotes
Definition saveload.h:655
SLE_FILE_STRINGID
@ SLE_FILE_STRINGID
StringID offset into strings-array.
Definition saveload.h:635
SaveOrLoadResult
SaveOrLoadResult
Save or load result codes.
Definition saveload.h:403
SL_OK
@ SL_OK
completed successfully
Definition saveload.h:404
SL_REINIT
@ SL_REINIT
error that was caught in the middle of updating game state, need to clear it. (can only happen during...
Definition saveload.h:406
CH_TYPE_MASK
@ CH_TYPE_MASK
All ChunkType values have to be within this mask.
Definition saveload.h:458
CH_READONLY
@ CH_READONLY
Chunk is never saved.
Definition saveload.h:459
SlSkipBytes
void SlSkipBytes(size_t length)
Read in bytes from the file/data structure but don't do anything with them, discarding them in effect...
Definition saveload.h:1345
SavegameType
SavegameType
Types of save games.
Definition saveload.h:423
SGT_OTTD
@ SGT_OTTD
OTTD savegame.
Definition saveload.h:427
GetVarFileType
constexpr VarType GetVarFileType(VarType type)
Get the FileType of a setting.
Definition saveload.h:762
GetVariableAddress
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1301
ChunkHandlerTable
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:512
SaveLoadType
SaveLoadType
Type of data saved.
Definition saveload.h:695
SL_NULL
@ SL_NULL
Save null-bytes and load to nowhere.
Definition saveload.h:709
SL_STRUCTLIST
@ SL_STRUCTLIST
Save/load a list of structs.
Definition saveload.h:706
SL_STDSTR
@ SL_STDSTR
Save/load a std::string.
Definition saveload.h:700
SL_REF
@ SL_REF
Save/load a reference.
Definition saveload.h:697
SL_SAVEBYTE
@ SL_SAVEBYTE
Save (but not load) a byte.
Definition saveload.h:708
SL_DEQUE
@ SL_DEQUE
Save/load a deque of SL_VAR elements.
Definition saveload.h:703
SL_STRUCT
@ SL_STRUCT
Save/load a struct.
Definition saveload.h:698
SL_VECTOR
@ SL_VECTOR
Save/load a vector of SL_VAR elements.
Definition saveload.h:704
SL_REFVECTOR
@ SL_REFVECTOR
Save/load a vector of SL_REF elements.
Definition saveload.h:711
SL_REFLIST
@ SL_REFLIST
Save/load a list of SL_REF elements.
Definition saveload.h:705
SL_ARR
@ SL_ARR
Save/load a fixed-size array of SL_VAR elements.
Definition saveload.h:702
SL_VAR
@ SL_VAR
Save/load a variable.
Definition saveload.h:696
SaveLoadCompatTable
std::span< const struct SaveLoadCompat > SaveLoadCompatTable
A table of SaveLoadCompat entries.
Definition saveload.h:518
IsSavegameVersionBefore
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1263
GetVarMemType
constexpr VarType GetVarMemType(VarType type)
Get the NumberType of a setting.
Definition saveload.h:751
SaveLoadVersion
SaveLoadVersion
SaveLoad versions Previous savegame versions, the trunk revision where they were introduced and the r...
Definition saveload.h:30
SLV_69
@ SLV_69
69 10319
Definition saveload.h:125
SLV_4
@ SLV_4
4.0 1 4.1 122 0.3.3, 0.3.4 4.2 1222 0.3.5 4.3 1417 4.4 1426
Definition saveload.h:37
SLV_SAVELOAD_LIST_LENGTH
@ SLV_SAVELOAD_LIST_LENGTH
293 PR#9374 Consistency in list length with SL_STRUCT / SL_STRUCTLIST / SL_DEQUE / SL_REFLIST.
Definition saveload.h:331
SLV_START_PATCHPACKS
@ SLV_START_PATCHPACKS
220 First known patchpack to use a version just above ours.
Definition saveload.h:321
SL_MAX_VERSION
@ SL_MAX_VERSION
Highest possible saveload version.
Definition saveload.h:399
SL_MIN_VERSION
@ SL_MIN_VERSION
First savegame version.
Definition saveload.h:31
SLV_END_PATCHPACKS
@ SLV_END_PATCHPACKS
286 Last known patchpack to use a version just above ours.
Definition saveload.h:322
SLV_5
@ SLV_5
5.0 1429 5.1 1440 5.2 1525 0.3.6
Definition saveload.h:43
SLV_169
@ SLV_169
169 23816
Definition saveload.h:245
SlTableHeader
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
Definition saveload.cpp:1786
SLRefType
SLRefType
Type of reference (SLE_REF, SLE_CONDREF).
Definition saveload.h:600
REF_VEHICLE_OLD
@ REF_VEHICLE_OLD
Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
Definition saveload.h:605
REF_LINK_GRAPH_JOB
@ REF_LINK_GRAPH_JOB
Load/save a reference to a link graph job.
Definition saveload.h:612
REF_TOWN
@ REF_TOWN
Load/save a reference to a town.
Definition saveload.h:604
REF_LINK_GRAPH
@ REF_LINK_GRAPH
Load/save a reference to a link graph.
Definition saveload.h:611
REF_CARGO_PACKET
@ REF_CARGO_PACKET
Load/save a reference to a cargo packet.
Definition saveload.h:608
REF_ENGINE_RENEWS
@ REF_ENGINE_RENEWS
Load/save a reference to an engine renewal (autoreplace).
Definition saveload.h:607
REF_STATION
@ REF_STATION
Load/save a reference to a station.
Definition saveload.h:603
REF_ORDER
@ REF_ORDER
Load/save a reference to an order.
Definition saveload.h:601
REF_ORDERLIST
@ REF_ORDERLIST
Load/save a reference to an orderlist.
Definition saveload.h:609
REF_STORAGE
@ REF_STORAGE
Load/save a reference to a persistent storage.
Definition saveload.h:610
REF_VEHICLE
@ REF_VEHICLE
Load/save a reference to a vehicle.
Definition saveload.h:602
REF_ROADSTOPS
@ REF_ROADSTOPS
Load/save a reference to a bus/truck stop.
Definition saveload.h:606
SaveLoadTable
std::span< const struct SaveLoad > SaveLoadTable
A table of SaveLoad entries.
Definition saveload.h:515
saveload_filter.h
Declaration of filters used for saving and loading savegames.
saveload_internal.h
Declaration of functions used in more save/load files.
RemapOldStringID
StringID RemapOldStringID(StringID s)
Remap a string ID from the old format to the new format.
Definition strings_sl.cpp:30
CopyFromOldName
std::string CopyFromOldName(StringID id)
Copy and convert old custom names to UTF-8.
Definition strings_sl.cpp:61
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
SBI_SAVELOAD_FINISH
@ SBI_SAVELOAD_FINISH
finished saving
Definition statusbar_gui.h:16
SBI_SAVELOAD_START
@ SBI_SAVELOAD_START
started saving
Definition statusbar_gui.h:15
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:280
StrMakeValid
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
Definition string.cpp:107
Utf8Decode
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition string.cpp:419
Utf8Encode
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition string.cpp:460
Utf8EncodedCharLen
int8_t Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
Definition string_func.h:124
StringValidationSettings
StringValidationSettings
Settings for the string validation.
Definition string_type.h:44
SVS_ALLOW_CONTROL_CODE
@ SVS_ALLOW_CONTROL_CODE
Allow the special control codes.
Definition string_type.h:48
SVS_ALLOW_NEWLINE
@ SVS_ALLOW_NEWLINE
Allow newlines; replaces '\r ' with ' ' during processing.
Definition string_type.h:47
SVS_REPLACE_WITH_QUESTION_MARK
@ SVS_REPLACE_WITH_QUESTION_MARK
Replace the unknown/bad bits with question marks.
Definition string_type.h:46
SetDParam
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
GetString
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition strings.cpp:333
SetDParamStr
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition strings.cpp:371
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition strings_type.h:16
INVALID_STRING_ID
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Definition strings_type.h:17
CargoPacket
Container for cargo from the same location and time.
Definition cargopacket.h:40
ChunkHandler
Handlers and description of chunk.
Definition saveload.h:463
ChunkHandler::type
ChunkType type
Type of the chunk.
Definition saveload.h:465
ChunkHandler::LoadCheck
virtual void LoadCheck(size_t len=0) const
Load the chunk for game preview.
Definition saveload.cpp:2012
ChunkHandler::Load
virtual void Load() const =0
Load the chunk.
ChunkHandler::id
uint32_t id
Unique ID (4 letters).
Definition saveload.h:464
ChunkHandler::Save
virtual void Save() const
Save the chunk.
Definition saveload.h:475
ClientSettings::gui
GUISettings gui
settings related to the GUI
Definition settings_type.h:610
Company
Definition company_base.h:133
EngineRenew
Struct to store engine replacements.
Definition autoreplace_base.h:33
FileReader
Yes, simply reading from a file.
Definition saveload.cpp:2242
FileReader::~FileReader
~FileReader()
Make sure everything is cleaned up.
Definition saveload.cpp:2255
FileReader::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2262
FileReader::Reset
void Reset() override
Reset this filter to read from the beginning of the file.
Definition saveload.cpp:2270
FileReader::FileReader
FileReader(FileHandle &&file)
Create the file reader, so it reads from a specific file.
Definition saveload.cpp:2250
FileReader::begin
long begin
The begin of the file.
Definition saveload.cpp:2244
FileReader::file
std::optional< FileHandle > file
The file to read from.
Definition saveload.cpp:2243
FileToSaveLoad
Deals with the type of the savegame, independent of extension.
Definition saveload.h:410
FileToSaveLoad::abstract_ftype
AbstractFileType abstract_ftype
Abstract type of file (scenario, heightmap, etc).
Definition saveload.h:413
FileToSaveLoad::SetMode
void SetMode(FiosType ft)
Set the mode and file type of the file to save or load based on the type of file entry at the file sy...
Definition saveload.cpp:3288
FileToSaveLoad::detail_ftype
DetailedFileType detail_ftype
Concrete file type (PNG, BMP, old save, etc).
Definition saveload.h:412
FileToSaveLoad::title
std::string title
Internal name of the game.
Definition saveload.h:415
FileToSaveLoad::file_op
SaveLoadOperation file_op
File operation to perform.
Definition saveload.h:411
FileToSaveLoad::name
std::string name
Name of the file.
Definition saveload.h:414
FileToSaveLoad::Set
void Set(const FiosItem &item)
Set the title of the file.
Definition saveload.cpp:3317
FileWriter
Yes, simply writing to a file.
Definition saveload.cpp:2280
FileWriter::file
std::optional< FileHandle > file
The file to write to.
Definition saveload.cpp:2281
FileWriter::FileWriter
FileWriter(FileHandle &&file)
Create the file writer, so it writes to a specific file.
Definition saveload.cpp:2287
FileWriter::Finish
void Finish() override
Prepare everything to finish writing the savegame.
Definition saveload.cpp:2305
FileWriter::~FileWriter
~FileWriter()
Make sure everything is cleaned up.
Definition saveload.cpp:2292
FileWriter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2297
FiosItem
Deals with finding savegames.
Definition fios.h:79
FiosNumberedSaveName
A savegame name automatically numbered.
Definition fios.h:130
FiosNumberedSaveName::Filename
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
Definition fios.cpp:764
FiosNumberedSaveName::Extension
std::string Extension()
Generate an extension for a savegame name.
Definition fios.cpp:774
GUISettings::keep_all_autosave
bool keep_all_autosave
name the autosave in a different way
Definition settings_type.h:170
GUISettings::date_format_in_default_names
uint8_t date_format_in_default_names
should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) ...
Definition settings_type.h:173
GUISettings::threaded_saves
bool threaded_saves
should we do threaded saves?
Definition settings_type.h:169
GameSessionStats::savegame_size
std::optional< size_t > savegame_size
Size of the last saved savegame in bytes, or std::nullopt if not saved yet.
Definition openttd.h:58
LZMALoadFilter
Filter without any compression.
Definition saveload.cpp:2580
LZMALoadFilter::~LZMALoadFilter
~LZMALoadFilter()
Clean everything up.
Definition saveload.cpp:2595
LZMALoadFilter::lzma
lzma_stream lzma
Stream state that we are reading from.
Definition saveload.cpp:2581
LZMALoadFilter::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2600
LZMALoadFilter::fread_buf
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
Definition saveload.cpp:2582
LZMALoadFilter::LZMALoadFilter
LZMALoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2588
LZMASaveFilter
Filter using LZMA compression.
Definition saveload.cpp:2623
LZMASaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2669
LZMASaveFilter::Finish
void Finish() override
Prepare everything to finish writing the savegame.
Definition saveload.cpp:2674
LZMASaveFilter::WriteLoop
void WriteLoop(uint8_t *p, size_t len, lzma_action action)
Helper loop for writing the data.
Definition saveload.cpp:2649
LZMASaveFilter::LZMASaveFilter
LZMASaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
Definition saveload.cpp:2632
LZMASaveFilter::lzma
lzma_stream lzma
Stream state that we are writing to.
Definition saveload.cpp:2624
LZMASaveFilter::fwrite_buf
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
Definition saveload.cpp:2625
LZMASaveFilter::~LZMASaveFilter
~LZMASaveFilter()
Clean up what we allocated.
Definition saveload.cpp:2638
LZOLoadFilter
Filter using LZO compression.
Definition saveload.cpp:2325
LZOLoadFilter::LZOLoadFilter
LZOLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2330
LZOLoadFilter::Read
size_t Read(uint8_t *buf, size_t ssize) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2335
LZOSaveFilter
Filter using LZO compression.
Definition saveload.cpp:2372
LZOSaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2382
LZOSaveFilter::LZOSaveFilter
LZOSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
Definition saveload.cpp:2377
LoadCheckData::checkable
bool checkable
True if the savegame could be checked by SL_LOAD_CHECK. (Old savegames are not checkable....
Definition fios.h:34
LoadCheckData::error_msg
std::string error_msg
Data to pass to SetDParamStr when displaying error.
Definition fios.h:36
LoadCheckData::error
StringID error
Error message from loading. INVALID_STRING_ID if no error.
Definition fios.h:35
LoadCheckData::grfconfig
GRFConfig * grfconfig
NewGrf configuration from save.
Definition fios.h:45
LoadCheckData::Clear
void Clear()
Reset read data.
Definition fios_gui.cpp:49
LoadCheckData::grf_compatibility
GRFListCompatibility grf_compatibility
Summary state of NewGrfs, whether missing files or only compatible found.
Definition fios.h:46
LoadFilter
Interface for filtering a savegame till it is loaded.
Definition saveload_filter.h:14
LoadFilter::chain
std::shared_ptr< LoadFilter > chain
Chained to the (savegame) filters.
Definition saveload_filter.h:16
MemoryDumper
Container for dumping the savegame (quickly) to memory.
Definition saveload.cpp:128
MemoryDumper::buf
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:130
MemoryDumper::WriteByte
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:137
MemoryDumper::blocks
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:129
MemoryDumper::bufe
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:131
MemoryDumper::GetSize
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:171
MemoryDumper::Flush
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:152
NoCompLoadFilter
Filter without any compression.
Definition saveload.cpp:2412
NoCompLoadFilter::NoCompLoadFilter
NoCompLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2417
NoCompLoadFilter::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2421
NoCompSaveFilter
Filter without any compression.
Definition saveload.cpp:2428
NoCompSaveFilter::NoCompSaveFilter
NoCompSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
Definition saveload.cpp:2433
NoCompSaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2437
OrderList
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:259
Order
Definition order_base.h:36
PersistentStorage
Class for pooled persistent storage of data.
Definition newgrf_storage.h:199
Pool::PoolItem<&_orderlist_pool >::IsValidID
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition pool_type.hpp:328
Pool::PoolItem<&_company_pool >::Iterate
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition pool_type.hpp:388
Pool::PoolItem<&_orderlist_pool >::Get
static Titem * Get(size_t index)
Returns Titem with given index.
Definition pool_type.hpp:339
ReadBuffer
A buffer for reading (and buffering) savegame data.
Definition saveload.cpp:87
ReadBuffer::bufp
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:89
ReadBuffer::ReadBuffer
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:98
ReadBuffer::read
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:92
ReadBuffer::GetSize
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:120
ReadBuffer::reader
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:91
ReadBuffer::buf
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:88
ReadBuffer::bufe
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:90
RoadStop
A Stop for a Road Vehicle.
Definition roadstop_base.h:22
SaveFilter
Interface for filtering a savegame till it is written.
Definition saveload_filter.h:57
SaveFilter::chain
std::shared_ptr< SaveFilter > chain
Chained to the (savegame) filters.
Definition saveload_filter.h:59
SaveLoadFormat
The format for a reader/writer type of a savegame.
Definition saveload.cpp:2688
SaveLoadFormat::name
const char * name
name of the compressor/decompressor (debug-only)
Definition saveload.cpp:2689
SaveLoadFormat::tag
uint32_t tag
the 4-letter tag by which it is identified in the savegame
Definition saveload.cpp:2690
SaveLoadFormat::min_compression
uint8_t min_compression
the minimum compression level of this format
Definition saveload.cpp:2695
SaveLoadFormat::init_write
std::shared_ptr< SaveFilter >(* init_write)(std::shared_ptr< SaveFilter > chain, uint8_t compression)
Constructor for the save filter.
Definition saveload.cpp:2693
SaveLoadFormat::default_compression
uint8_t default_compression
the default compression level of this format
Definition saveload.cpp:2696
SaveLoadFormat::init_load
std::shared_ptr< LoadFilter >(* init_load)(std::shared_ptr< LoadFilter > chain)
Constructor for the load filter.
Definition saveload.cpp:2692
SaveLoadFormat::max_compression
uint8_t max_compression
the maximum compression level of this format
Definition saveload.cpp:2697
SaveLoadParams
The saveload struct, containing reader-writer functions, buffer, version, etc.
Definition saveload.cpp:178
SaveLoadParams::reader
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:191
SaveLoadParams::sf
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:189
SaveLoadParams::dumper
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:188
SaveLoadParams::error_str
StringID error_str
the translatable error message to show
Definition saveload.cpp:194
SaveLoadParams::action
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:179
SaveLoadParams::extra_msg
std::string extra_msg
the error message
Definition saveload.cpp:195
SaveLoadParams::need_length
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:180
SaveLoadParams::block_mode
uint8_t block_mode
???
Definition saveload.cpp:181
SaveLoadParams::saveinprogress
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:197
SaveLoadParams::lf
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:192
SaveLoadParams::expect_table_header
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:186
SaveLoadParams::obj_len
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:184
SaveLoadParams::error
bool error
did an error occur or not
Definition saveload.cpp:182
SaveLoadParams::last_array_index
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:185
SaveLoad
SaveLoad type struct.
Definition saveload.h:717
SaveLoad::length
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:721
SaveLoad::handler
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:726
SaveLoad::version_to
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:723
SaveLoad::cmd
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:719
SaveLoad::name
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:718
SaveLoad::conv
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:720
SaveLoad::version_from
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:722
SpecializedStation< Station, false >::Get
static Station * Get(size_t index)
Gets station with given index.
Definition base_station_base.h:254
SpecializedStation< Station, false >::IsValidID
static bool IsValidID(size_t index)
Tests whether given index is a valid index for station of this type.
Definition base_station_base.h:245
Station
Station data structure.
Definition station_base.h:439
Town
Town data structure.
Definition town.h:54
Vehicle
Vehicle data structure.
Definition vehicle_base.h:244
ZlibLoadFilter
Filter using Zlib compression.
Definition saveload.cpp:2451
ZlibLoadFilter::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2471
ZlibLoadFilter::ZlibLoadFilter
ZlibLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2459
ZlibLoadFilter::fread_buf
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
Definition saveload.cpp:2453
ZlibLoadFilter::~ZlibLoadFilter
~ZlibLoadFilter()
Clean everything up.
Definition saveload.cpp:2466
ZlibLoadFilter::z
z_stream z
Stream state we are reading from.
Definition saveload.cpp:2452
ZlibSaveFilter
Filter using Zlib compression.
Definition saveload.cpp:2495
ZlibSaveFilter::WriteLoop
void WriteLoop(uint8_t *p, size_t len, int mode)
Helper loop for writing the data.
Definition saveload.cpp:2522
ZlibSaveFilter::z
z_stream z
Stream state we are writing to.
Definition saveload.cpp:2496
ZlibSaveFilter::fwrite_buf
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
Definition saveload.cpp:2497
ZlibSaveFilter::Finish
void Finish() override
Prepare everything to finish writing the savegame.
Definition saveload.cpp:2555
ZlibSaveFilter::~ZlibSaveFilter
~ZlibSaveFilter()
Clean up what we allocated.
Definition saveload.cpp:2511
ZlibSaveFilter::ZlibSaveFilter
ZlibSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
Definition saveload.cpp:2504
ZlibSaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2550
CSleep
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Definition thread.h:24
StartNewThread
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
InvalidateWindowData
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3219
WC_STATUS_BAR
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:64