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

OpenTTD Source 15.3

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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
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 "../core/string_builder.hpp"
33#include "../core/string_consumer.hpp"
34#include "../vehicle_base.h"
35#include "../company_func.h"
36#include "../timer/timer_game_economy.h"
37#include "../autoreplace_base.h"
38#include "../roadstop_base.h"
39#include "../linkgraph/linkgraph.h"
40#include "../linkgraph/linkgraphjob.h"
41#include "../statusbar_gui.h"
42#include "../fileio_func.h"
43#include "../gamelog.h"
44#include "../string_func.h"
45#include "../fios.h"
46#include "../error.h"
47#include "../strings_type.h"
48#include "../newgrf_railtype.h"
49#include "../newgrf_roadtype.h"
50#include "../settings_internal.h"
51#include "saveload_internal.h"
52#include "saveload_filter.h"
53
54#include <atomic>
55#ifdef __EMSCRIPTEN__
56# include <emscripten.h>
57#endif
58
59#ifdef WITH_LZO
60#include <lzo/lzo1x.h>
61#endif
62
63#if defined(WITH_ZLIB)
64#include <zlib.h>
65#endif /* WITH_ZLIB */
66
67#if defined(WITH_LIBLZMA)
68#include <lzma.h>
69#endif /* WITH_LIBLZMA */
70
71#include "table/strings.h"
72
73#include "../safeguards.h"
74
75extern const SaveLoadVersion SAVEGAME_VERSION = (SaveLoadVersion)(SL_MAX_VERSION - 1);
76
77 SavegameType _savegame_type;
78 FileToSaveLoad _file_to_saveload;
79
80uint32_t _ttdp_version;
81 SaveLoadVersion _sl_version;
82uint8_t _sl_minor_version;
83std::string _savegame_format;
84 bool _do_autosave;
85
87 enum SaveLoadAction : uint8_t {
88 SLA_LOAD,
89 SLA_SAVE,
90 SLA_PTRS,
91 SLA_NULL,
92 SLA_LOAD_CHECK,
93};
94
95 enum NeedLength : uint8_t {
96 NL_NONE = 0,
97 NL_WANTLENGTH = 1,
98 NL_CALCLENGTH = 2,
99};
100
102 static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
103
105 struct ReadBuffer {
106 uint8_t buf[MEMORY_CHUNK_SIZE];
107 uint8_t *bufp = nullptr;
108 uint8_t *bufe = nullptr;
109 std::shared_ptr<LoadFilter> reader{};
110 size_t read = 0;
111
116 ReadBuffer(std::shared_ptr<LoadFilter> reader) : reader(std::move(reader))
117 {
118 }
119
120 inline uint8_t ReadByte()
121 {
122 if (this->bufp == this->bufe) {
123 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
124 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
125
126 this->read += len;
127 this->bufp = this->buf;
128 this->bufe = this->buf + len;
129 }
130
131 return *this->bufp++;
132 }
133
138 size_t GetSize() const
139 {
140 return this->read - (this->bufe - this->bufp);
141 }
142};
143
144
146 struct MemoryDumper {
147 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
148 uint8_t *buf = nullptr;
149 uint8_t *bufe = nullptr;
150
155 inline void WriteByte(uint8_t b)
156 {
157 /* Are we at the end of this chunk? */
158 if (this->buf == this->bufe) {
159 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
160 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
161 }
162
163 *this->buf++ = b;
164 }
165
170 void Flush(std::shared_ptr<SaveFilter> writer)
171 {
172 uint i = 0;
173 size_t t = this->GetSize();
174
175 while (t > 0) {
176 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
177
178 writer->Write(this->blocks[i++].get(), to_write);
179 t -= to_write;
180 }
181
182 writer->Finish();
183 }
184
189 size_t GetSize() const
190 {
191 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
192 }
193};
194
196 struct SaveLoadParams {
197 SaveLoadAction action;
198 NeedLength need_length;
199 uint8_t block_mode;
200 bool error;
201
202 size_t obj_len;
203 int array_index, last_array_index;
204 bool expect_table_header;
205
206 std::unique_ptr<MemoryDumper> dumper;
207 std::shared_ptr<SaveFilter> sf;
208
209 std::unique_ptr<ReadBuffer> reader;
210 std::shared_ptr<LoadFilter> lf;
211
212 StringID error_str;
213 std::string extra_msg;
214
215 bool saveinprogress;
216};
217
218 static SaveLoadParams _sl;
219
220 static const std::vector<ChunkHandlerRef> &ChunkHandlers()
221{
222 /* These define the chunks */
223 extern const ChunkHandlerTable _gamelog_chunk_handlers;
224 extern const ChunkHandlerTable _map_chunk_handlers;
225 extern const ChunkHandlerTable _misc_chunk_handlers;
226 extern const ChunkHandlerTable _name_chunk_handlers;
227 extern const ChunkHandlerTable _cheat_chunk_handlers;
228 extern const ChunkHandlerTable _setting_chunk_handlers;
229 extern const ChunkHandlerTable _company_chunk_handlers;
230 extern const ChunkHandlerTable _engine_chunk_handlers;
231 extern const ChunkHandlerTable _veh_chunk_handlers;
232 extern const ChunkHandlerTable _waypoint_chunk_handlers;
233 extern const ChunkHandlerTable _depot_chunk_handlers;
234 extern const ChunkHandlerTable _order_chunk_handlers;
235 extern const ChunkHandlerTable _town_chunk_handlers;
236 extern const ChunkHandlerTable _sign_chunk_handlers;
237 extern const ChunkHandlerTable _station_chunk_handlers;
238 extern const ChunkHandlerTable _industry_chunk_handlers;
239 extern const ChunkHandlerTable _economy_chunk_handlers;
240 extern const ChunkHandlerTable _subsidy_chunk_handlers;
241 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
242 extern const ChunkHandlerTable _goal_chunk_handlers;
243 extern const ChunkHandlerTable _story_page_chunk_handlers;
244 extern const ChunkHandlerTable _league_chunk_handlers;
245 extern const ChunkHandlerTable _ai_chunk_handlers;
246 extern const ChunkHandlerTable _game_chunk_handlers;
247 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
248 extern const ChunkHandlerTable _newgrf_chunk_handlers;
249 extern const ChunkHandlerTable _group_chunk_handlers;
250 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
251 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
252 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
253 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
254 extern const ChunkHandlerTable _airport_chunk_handlers;
255 extern const ChunkHandlerTable _object_chunk_handlers;
256 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
257 extern const ChunkHandlerTable _water_region_chunk_handlers;
258 extern const ChunkHandlerTable _randomizer_chunk_handlers;
259
261 static const ChunkHandlerTable _chunk_handler_tables[] = {
262 _gamelog_chunk_handlers,
263 _map_chunk_handlers,
264 _misc_chunk_handlers,
265 _name_chunk_handlers,
266 _cheat_chunk_handlers,
267 _setting_chunk_handlers,
268 _veh_chunk_handlers,
269 _waypoint_chunk_handlers,
270 _depot_chunk_handlers,
271 _order_chunk_handlers,
272 _industry_chunk_handlers,
273 _economy_chunk_handlers,
274 _subsidy_chunk_handlers,
275 _cargomonitor_chunk_handlers,
276 _goal_chunk_handlers,
277 _story_page_chunk_handlers,
278 _league_chunk_handlers,
279 _engine_chunk_handlers,
280 _town_chunk_handlers,
281 _sign_chunk_handlers,
282 _station_chunk_handlers,
283 _company_chunk_handlers,
284 _ai_chunk_handlers,
285 _game_chunk_handlers,
286 _animated_tile_chunk_handlers,
287 _newgrf_chunk_handlers,
288 _group_chunk_handlers,
289 _cargopacket_chunk_handlers,
290 _autoreplace_chunk_handlers,
291 _labelmaps_chunk_handlers,
292 _linkgraph_chunk_handlers,
293 _airport_chunk_handlers,
294 _object_chunk_handlers,
295 _persistent_storage_chunk_handlers,
296 _water_region_chunk_handlers,
297 _randomizer_chunk_handlers,
298 };
299
300 static std::vector<ChunkHandlerRef> _chunk_handlers;
301
302 if (_chunk_handlers.empty()) {
303 for (auto &chunk_handler_table : _chunk_handler_tables) {
304 for (auto &chunk_handler : chunk_handler_table) {
305 _chunk_handlers.push_back(chunk_handler);
306 }
307 }
308 }
309
310 return _chunk_handlers;
311}
312
314 static void SlNullPointers()
315{
316 _sl.action = SLA_NULL;
317
318 /* We don't want any savegame conversion code to run
319 * during NULLing; especially those that try to get
320 * pointers from other pools. */
321 _sl_version = SAVEGAME_VERSION;
322
323 for (const ChunkHandler &ch : ChunkHandlers()) {
324 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
325 ch.FixPointers();
326 }
327
328 assert(_sl.action == SLA_NULL);
329}
330
339[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
340{
341 /* Distinguish between loading into _load_check_data vs. normal save/load. */
342 if (_sl.action == SLA_LOAD_CHECK) {
343 _load_check_data.error = string;
344 _load_check_data.error_msg = extra_msg;
345 } else {
346 _sl.error_str = string;
347 _sl.extra_msg = extra_msg;
348 }
349
350 /* We have to nullptr all pointers here; we might be in a state where
351 * the pointers are actually filled with indices, which means that
352 * when we access them during cleaning the pool dereferences of
353 * those indices will be made with segmentation faults as result. */
354 if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
355
356 /* Logging could be active. */
357 _gamelog.StopAnyAction();
358
359 throw std::exception();
360}
361
369[[noreturn]] void SlErrorCorrupt(const std::string &msg)
370{
371 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
372}
373
374
375 typedef void (*AsyncSaveFinishProc)();
376 static std::atomic<AsyncSaveFinishProc> _async_save_finish;
377 static std::thread _save_thread;
378
383 static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
384{
385 if (_exit_game) return;
386 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
387
388 _async_save_finish.store(proc, std::memory_order_release);
389}
390
394 void ProcessAsyncSaveFinish()
395{
396 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
397 if (proc == nullptr) return;
398
399 proc();
400
401 if (_save_thread.joinable()) {
402 _save_thread.join();
403 }
404}
405
410uint8_t SlReadByte()
411{
412 return _sl.reader->ReadByte();
413}
414
419 void SlWriteByte(uint8_t b)
420{
421 _sl.dumper->WriteByte(b);
422}
423
424static inline int SlReadUint16()
425{
426 int x = SlReadByte() << 8;
427 return x | SlReadByte();
428}
429
430static inline uint32_t SlReadUint32()
431{
432 uint32_t x = SlReadUint16() << 16;
433 return x | SlReadUint16();
434}
435
436static inline uint64_t SlReadUint64()
437{
438 uint32_t x = SlReadUint32();
439 uint32_t y = SlReadUint32();
440 return (uint64_t)x << 32 | y;
441}
442
443static inline void SlWriteUint16(uint16_t v)
444{
445 SlWriteByte(GB(v, 8, 8));
446 SlWriteByte(GB(v, 0, 8));
447}
448
449static inline void SlWriteUint32(uint32_t v)
450{
451 SlWriteUint16(GB(v, 16, 16));
452 SlWriteUint16(GB(v, 0, 16));
453}
454
455static inline void SlWriteUint64(uint64_t x)
456{
457 SlWriteUint32((uint32_t)(x >> 32));
458 SlWriteUint32((uint32_t)x);
459}
460
470 static uint SlReadSimpleGamma()
471{
472 uint i = SlReadByte();
473 if (HasBit(i, 7)) {
474 i &= ~0x80;
475 if (HasBit(i, 6)) {
476 i &= ~0x40;
477 if (HasBit(i, 5)) {
478 i &= ~0x20;
479 if (HasBit(i, 4)) {
480 i &= ~0x10;
481 if (HasBit(i, 3)) {
482 SlErrorCorrupt("Unsupported gamma");
483 }
484 i = SlReadByte(); // 32 bits only.
485 }
486 i = (i << 8) | SlReadByte();
487 }
488 i = (i << 8) | SlReadByte();
489 }
490 i = (i << 8) | SlReadByte();
491 }
492 return i;
493}
494
512 static void SlWriteSimpleGamma(size_t i)
513{
514 if (i >= (1 << 7)) {
515 if (i >= (1 << 14)) {
516 if (i >= (1 << 21)) {
517 if (i >= (1 << 28)) {
518 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
519 SlWriteByte((uint8_t)(0xF0));
520 SlWriteByte((uint8_t)(i >> 24));
521 } else {
522 SlWriteByte((uint8_t)(0xE0 | (i >> 24)));
523 }
524 SlWriteByte((uint8_t)(i >> 16));
525 } else {
526 SlWriteByte((uint8_t)(0xC0 | (i >> 16)));
527 }
528 SlWriteByte((uint8_t)(i >> 8));
529 } else {
530 SlWriteByte((uint8_t)(0x80 | (i >> 8)));
531 }
532 }
533 SlWriteByte((uint8_t)i);
534}
535
537 static inline uint SlGetGammaLength(size_t i)
538{
539 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
540}
541
542static inline uint SlReadSparseIndex()
543{
544 return SlReadSimpleGamma();
545}
546
547static inline void SlWriteSparseIndex(uint index)
548{
549 SlWriteSimpleGamma(index);
550}
551
552static inline uint SlReadArrayLength()
553{
554 return SlReadSimpleGamma();
555}
556
557static inline void SlWriteArrayLength(size_t length)
558{
559 SlWriteSimpleGamma(length);
560}
561
562static inline uint SlGetArrayLength(size_t length)
563{
564 return SlGetGammaLength(length);
565}
566
570 static uint8_t GetSavegameFileType(const SaveLoad &sld)
571{
572 switch (sld.cmd) {
573 case SL_VAR:
574 return GetVarFileType(sld.conv); break;
575
576 case SL_STDSTR:
577 case SL_ARR:
578 case SL_VECTOR:
579 case SL_DEQUE:
580 return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
581
582 case SL_REF:
583 return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
584
585 case SL_REFLIST:
586 case SL_REFVECTOR:
587 return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
588
589 case SL_SAVEBYTE:
590 return SLE_FILE_U8;
591
592 case SL_STRUCT:
593 case SL_STRUCTLIST:
594 return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
595
596 default: NOT_REACHED();
597 }
598}
599
606 static inline uint SlCalcConvMemLen(VarType conv)
607{
608 switch (GetVarMemType(conv)) {
609 case SLE_VAR_BL: return sizeof(bool);
610 case SLE_VAR_I8: return sizeof(int8_t);
611 case SLE_VAR_U8: return sizeof(uint8_t);
612 case SLE_VAR_I16: return sizeof(int16_t);
613 case SLE_VAR_U16: return sizeof(uint16_t);
614 case SLE_VAR_I32: return sizeof(int32_t);
615 case SLE_VAR_U32: return sizeof(uint32_t);
616 case SLE_VAR_I64: return sizeof(int64_t);
617 case SLE_VAR_U64: return sizeof(uint64_t);
618 case SLE_VAR_NULL: return 0;
619
620 case SLE_VAR_STR:
621 case SLE_VAR_STRQ:
622 return SlReadArrayLength();
623
624 case SLE_VAR_NAME:
625 default:
626 NOT_REACHED();
627 }
628}
629
636 static inline uint8_t SlCalcConvFileLen(VarType conv)
637{
638 switch (GetVarFileType(conv)) {
639 case SLE_FILE_END: return 0;
640 case SLE_FILE_I8: return sizeof(int8_t);
641 case SLE_FILE_U8: return sizeof(uint8_t);
642 case SLE_FILE_I16: return sizeof(int16_t);
643 case SLE_FILE_U16: return sizeof(uint16_t);
644 case SLE_FILE_I32: return sizeof(int32_t);
645 case SLE_FILE_U32: return sizeof(uint32_t);
646 case SLE_FILE_I64: return sizeof(int64_t);
647 case SLE_FILE_U64: return sizeof(uint64_t);
648 case SLE_FILE_STRINGID: return sizeof(uint16_t);
649
650 case SLE_FILE_STRING:
651 return SlReadArrayLength();
652
653 case SLE_FILE_STRUCT:
654 default:
655 NOT_REACHED();
656 }
657}
658
660 static inline size_t SlCalcRefLen()
661{
662 return IsSavegameVersionBefore(SLV_69) ? 2 : 4;
663}
664
665void SlSetArrayIndex(uint index)
666{
667 _sl.need_length = NL_WANTLENGTH;
668 _sl.array_index = index;
669}
670
671static size_t _next_offs;
672
677 int SlIterateArray()
678{
679 /* After reading in the whole array inside the loop
680 * we must have read in all the data, so we must be at end of current block. */
681 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
682 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
683 }
684
685 for (;;) {
686 uint length = SlReadArrayLength();
687 if (length == 0) {
688 assert(!_sl.expect_table_header);
689 _next_offs = 0;
690 return -1;
691 }
692
693 _sl.obj_len = --length;
694 _next_offs = _sl.reader->GetSize() + length;
695
696 if (_sl.expect_table_header) {
697 _sl.expect_table_header = false;
698 return INT32_MAX;
699 }
700
701 int index;
702 switch (_sl.block_mode) {
703 case CH_SPARSE_TABLE:
704 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
705 case CH_TABLE:
706 case CH_ARRAY: index = _sl.array_index++; break;
707 default:
708 Debug(sl, 0, "SlIterateArray error");
709 return -1; // error
710 }
711
712 if (length != 0) return index;
713 }
714}
715
719 void SlSkipArray()
720{
721 while (SlIterateArray() != -1) {
722 SlSkipBytes(_next_offs - _sl.reader->GetSize());
723 }
724}
725
731 void SlSetLength(size_t length)
732{
733 assert(_sl.action == SLA_SAVE);
734
735 switch (_sl.need_length) {
736 case NL_WANTLENGTH:
737 _sl.need_length = NL_NONE;
738 if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) {
739 _sl.expect_table_header = false;
740 SlWriteArrayLength(length + 1);
741 break;
742 }
743
744 switch (_sl.block_mode) {
745 case CH_RIFF:
746 /* Ugly encoding of >16M RIFF chunks
747 * The lower 24 bits are normal
748 * The uppermost 4 bits are bits 24:27 */
749 assert(length < (1 << 28));
750 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
751 break;
752 case CH_TABLE:
753 case CH_ARRAY:
754 assert(_sl.last_array_index <= _sl.array_index);
755 while (++_sl.last_array_index <= _sl.array_index) {
756 SlWriteArrayLength(1);
757 }
758 SlWriteArrayLength(length + 1);
759 break;
760 case CH_SPARSE_TABLE:
761 case CH_SPARSE_ARRAY:
762 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
763 SlWriteSparseIndex(_sl.array_index);
764 break;
765 default: NOT_REACHED();
766 }
767 break;
768
769 case NL_CALCLENGTH:
770 _sl.obj_len += (int)length;
771 break;
772
773 default: NOT_REACHED();
774 }
775}
776
783 static void SlCopyBytes(void *ptr, size_t length)
784{
785 uint8_t *p = (uint8_t *)ptr;
786
787 switch (_sl.action) {
788 case SLA_LOAD_CHECK:
789 case SLA_LOAD:
790 for (; length != 0; length--) *p++ = SlReadByte();
791 break;
792 case SLA_SAVE:
793 for (; length != 0; length--) SlWriteByte(*p++);
794 break;
795 default: NOT_REACHED();
796 }
797}
798
800 size_t SlGetFieldLength()
801{
802 return _sl.obj_len;
803}
804
812int64_t ReadValue(const void *ptr, VarType conv)
813{
814 switch (GetVarMemType(conv)) {
815 case SLE_VAR_BL: return (*(const bool *)ptr != 0);
816 case SLE_VAR_I8: return *(const int8_t *)ptr;
817 case SLE_VAR_U8: return *(const uint8_t *)ptr;
818 case SLE_VAR_I16: return *(const int16_t *)ptr;
819 case SLE_VAR_U16: return *(const uint16_t*)ptr;
820 case SLE_VAR_I32: return *(const int32_t *)ptr;
821 case SLE_VAR_U32: return *(const uint32_t*)ptr;
822 case SLE_VAR_I64: return *(const int64_t *)ptr;
823 case SLE_VAR_U64: return *(const uint64_t*)ptr;
824 case SLE_VAR_NULL:return 0;
825 default: NOT_REACHED();
826 }
827}
828
836 void WriteValue(void *ptr, VarType conv, int64_t val)
837{
838 switch (GetVarMemType(conv)) {
839 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
840 case SLE_VAR_I8: *(int8_t *)ptr = val; break;
841 case SLE_VAR_U8: *(uint8_t *)ptr = val; break;
842 case SLE_VAR_I16: *(int16_t *)ptr = val; break;
843 case SLE_VAR_U16: *(uint16_t*)ptr = val; break;
844 case SLE_VAR_I32: *(int32_t *)ptr = val; break;
845 case SLE_VAR_U32: *(uint32_t*)ptr = val; break;
846 case SLE_VAR_I64: *(int64_t *)ptr = val; break;
847 case SLE_VAR_U64: *(uint64_t*)ptr = val; break;
848 case SLE_VAR_NAME: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(val); break;
849 case SLE_VAR_NULL: break;
850 default: NOT_REACHED();
851 }
852}
853
862 static void SlSaveLoadConv(void *ptr, VarType conv)
863{
864 switch (_sl.action) {
865 case SLA_SAVE: {
866 int64_t x = ReadValue(ptr, conv);
867
868 /* Write the value to the file and check if its value is in the desired range */
869 switch (GetVarFileType(conv)) {
870 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
871 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
872 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
873 case SLE_FILE_STRINGID:
874 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
875 case SLE_FILE_I32:
876 case SLE_FILE_U32: SlWriteUint32((uint32_t)x);break;
877 case SLE_FILE_I64:
878 case SLE_FILE_U64: SlWriteUint64(x);break;
879 default: NOT_REACHED();
880 }
881 break;
882 }
883 case SLA_LOAD_CHECK:
884 case SLA_LOAD: {
885 int64_t x;
886 /* Read a value from the file */
887 switch (GetVarFileType(conv)) {
888 case SLE_FILE_I8: x = (int8_t )SlReadByte(); break;
889 case SLE_FILE_U8: x = (uint8_t )SlReadByte(); break;
890 case SLE_FILE_I16: x = (int16_t )SlReadUint16(); break;
891 case SLE_FILE_U16: x = (uint16_t)SlReadUint16(); break;
892 case SLE_FILE_I32: x = (int32_t )SlReadUint32(); break;
893 case SLE_FILE_U32: x = (uint32_t)SlReadUint32(); break;
894 case SLE_FILE_I64: x = (int64_t )SlReadUint64(); break;
895 case SLE_FILE_U64: x = (uint64_t)SlReadUint64(); break;
896 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16_t)SlReadUint16()); break;
897 default: NOT_REACHED();
898 }
899
900 /* Write The value to the struct. These ARE endian safe. */
901 WriteValue(ptr, conv, x);
902 break;
903 }
904 case SLA_PTRS: break;
905 case SLA_NULL: break;
906 default: NOT_REACHED();
907 }
908}
909
917 static inline size_t SlCalcStdStringLen(const void *ptr)
918{
919 const std::string *str = reinterpret_cast<const std::string *>(ptr);
920
921 size_t len = str->length();
922 return len + SlGetArrayLength(len); // also include the length of the index
923}
924
925
933 void FixSCCEncoded(std::string &str, bool fix_code)
934{
935 if (str.empty()) return;
936
937 /* We need to convert from old escape-style encoding to record separator encoding.
938 * Initial `<SCC_ENCODED><STRINGID>` stays the same.
939 *
940 * `:<SCC_ENCODED><STRINGID>` becomes `<RS><SCC_ENCODED><STRINGID>`
941 * `:<HEX>` becomes `<RS><SCC_ENCODED_NUMERIC><HEX>`
942 * `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
943 */
944 std::string result;
945 StringBuilder builder(result);
946
947 bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
948 bool in_string = false; // Set if we in a string, between double-quotes.
949 bool need_type = true; // Set if a parameter type needs to be emitted.
950
951 StringConsumer consumer(str);
952 while (consumer.AnyBytesLeft()) {
953 char32_t c;
954 if (auto r = consumer.TryReadUtf8(); r.has_value()) {
955 c = *r;
956 } else {
957 break;
958 }
959 if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
960 builder.PutUtf8(SCC_ENCODED);
961 need_type = false;
962 is_encoded = true;
963 continue;
964 }
965
966 /* If the first character is not SCC_ENCODED then we don't have to do any conversion. */
967 if (!is_encoded) return;
968
969 if (c == '"') {
970 in_string = !in_string;
971 if (in_string && need_type) {
972 /* Started a new string parameter. */
973 builder.PutUtf8(SCC_ENCODED_STRING);
974 need_type = false;
975 }
976 continue;
977 }
978
979 if (!in_string && c == ':') {
980 builder.PutUtf8(SCC_RECORD_SEPARATOR);
981 need_type = true;
982 continue;
983 }
984 if (need_type) {
985 /* Started a new numeric parameter. */
986 builder.PutUtf8(SCC_ENCODED_NUMERIC);
987 need_type = false;
988 }
989
990 builder.PutUtf8(c);
991 }
992
993 str = std::move(result);
994}
995
1000 void FixSCCEncodedNegative(std::string &str)
1001{
1002 if (str.empty()) return;
1003
1004 StringConsumer consumer(str);
1005
1006 /* Check whether this is an encoded string */
1007 if (!consumer.ReadUtf8If(SCC_ENCODED)) return;
1008
1009 std::string result;
1010 StringBuilder builder(result);
1011 builder.PutUtf8(SCC_ENCODED);
1012 while (consumer.AnyBytesLeft()) {
1013 /* Copy until next record */
1014 builder.Put(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::READ_ONE_SEPARATOR));
1015
1016 /* Check whether this is a numeric parameter */
1017 if (!consumer.ReadUtf8If(SCC_ENCODED_NUMERIC)) continue;
1018 builder.PutUtf8(SCC_ENCODED_NUMERIC);
1019
1020 /* First try unsigned */
1021 if (auto u = consumer.TryReadIntegerBase<uint64_t>(16); u.has_value()) {
1022 builder.PutIntegerBase<uint64_t>(*u, 16);
1023 } else {
1024 /* Read as signed, store as unsigned */
1025 auto s = consumer.ReadIntegerBase<int64_t>(16);
1026 builder.PutIntegerBase<uint64_t>(static_cast<uint64_t>(s), 16);
1027 }
1028 }
1029
1030 str = std::move(result);
1031}
1032
1039 void SlReadString(std::string &str, size_t length)
1040{
1041 str.resize(length);
1042 SlCopyBytes(str.data(), length);
1043}
1044
1050 static void SlStdString(void *ptr, VarType conv)
1051{
1052 std::string *str = reinterpret_cast<std::string *>(ptr);
1053
1054 switch (_sl.action) {
1055 case SLA_SAVE: {
1056 size_t len = str->length();
1057 SlWriteArrayLength(len);
1058 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->data())), len);
1059 break;
1060 }
1061
1062 case SLA_LOAD_CHECK:
1063 case SLA_LOAD: {
1064 size_t len = SlReadArrayLength();
1065 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1066 SlSkipBytes(len);
1067 return;
1068 }
1069
1070 SlReadString(*str, len);
1071
1072 StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark;
1073 if ((conv & SLF_ALLOW_CONTROL) != 0) {
1074 settings.Set(StringValidationSetting::AllowControlCode);
1075 if (IsSavegameVersionBefore(SLV_ENCODED_STRING_FORMAT)) FixSCCEncoded(*str, IsSavegameVersionBefore(SLV_169));
1076 if (IsSavegameVersionBefore(SLV_FIX_SCC_ENCODED_NEGATIVE)) FixSCCEncodedNegative(*str);
1077 }
1078 if ((conv & SLF_ALLOW_NEWLINE) != 0) {
1079 settings.Set(StringValidationSetting::AllowNewline);
1080 }
1081 if ((conv & SLF_REPLACE_TABCRLF) != 0) {
1082 settings.Set(StringValidationSetting::ReplaceTabCrNlWithSpace);
1083 }
1084 StrMakeValidInPlace(*str, settings);
1085 }
1086
1087 case SLA_PTRS: break;
1088 case SLA_NULL: break;
1089 default: NOT_REACHED();
1090 }
1091}
1092
1101 static void SlCopyInternal(void *object, size_t length, VarType conv)
1102{
1103 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1104 assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes
1105 SlSkipBytes(length * SlCalcConvFileLen(conv));
1106 return;
1107 }
1108
1109 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
1110 * as a byte-type. So detect this, and adjust object size accordingly */
1111 if (_sl.action != SLA_SAVE && _sl_version == 0) {
1112 /* all objects except difficulty settings */
1113 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
1114 conv == SLE_INT32 || conv == SLE_UINT32) {
1115 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
1116 return;
1117 }
1118 /* used for conversion of Money 32bit->64bit */
1119 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
1120 for (uint i = 0; i < length; i++) {
1121 ((int64_t*)object)[i] = (int32_t)std::byteswap(SlReadUint32());
1122 }
1123 return;
1124 }
1125 }
1126
1127 /* If the size of elements is 1 byte both in file and memory, no special
1128 * conversion is needed, use specialized copy-copy function to speed up things */
1129 if (conv == SLE_INT8 || conv == SLE_UINT8) {
1130 SlCopyBytes(object, length);
1131 } else {
1132 uint8_t *a = (uint8_t*)object;
1133 uint8_t mem_size = SlCalcConvMemLen(conv);
1134
1135 for (; length != 0; length --) {
1136 SlSaveLoadConv(a, conv);
1137 a += mem_size; // get size
1138 }
1139 }
1140}
1141
1150 void SlCopy(void *object, size_t length, VarType conv)
1151{
1152 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
1153
1154 /* Automatically calculate the length? */
1155 if (_sl.need_length != NL_NONE) {
1156 SlSetLength(length * SlCalcConvFileLen(conv));
1157 /* Determine length only? */
1158 if (_sl.need_length == NL_CALCLENGTH) return;
1159 }
1160
1161 SlCopyInternal(object, length, conv);
1162}
1163
1169 static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1170{
1171 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1172}
1173
1180 static void SlArray(void *array, size_t length, VarType conv)
1181{
1182 switch (_sl.action) {
1183 case SLA_SAVE:
1184 SlWriteArrayLength(length);
1185 SlCopyInternal(array, length, conv);
1186 return;
1187
1188 case SLA_LOAD_CHECK:
1189 case SLA_LOAD: {
1190 if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
1191 size_t sv_length = SlReadArrayLength();
1192 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1193 /* We don't know this field, so we assume the length in the savegame is correct. */
1194 length = sv_length;
1195 } else if (sv_length != length) {
1196 /* If the SLE_ARR changes size, a savegame bump is required
1197 * and the developer should have written conversion lines.
1198 * Error out to make this more visible. */
1199 SlErrorCorrupt("Fixed-length array is of wrong length");
1200 }
1201 }
1202
1203 SlCopyInternal(array, length, conv);
1204 return;
1205 }
1206
1207 case SLA_PTRS:
1208 case SLA_NULL:
1209 return;
1210
1211 default:
1212 NOT_REACHED();
1213 }
1214}
1215
1226 static size_t ReferenceToInt(const void *obj, SLRefType rt)
1227{
1228 assert(_sl.action == SLA_SAVE);
1229
1230 if (obj == nullptr) return 0;
1231
1232 switch (rt) {
1233 case REF_VEHICLE_OLD: // Old vehicles we save as new ones
1234 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
1235 case REF_STATION: return ((const Station*)obj)->index + 1;
1236 case REF_TOWN: return ((const Town*)obj)->index + 1;
1237 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
1238 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
1239 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
1240 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
1241 case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
1242 case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
1243 case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
1244 default: NOT_REACHED();
1245 }
1246}
1247
1258 static void *IntToReference(size_t index, SLRefType rt)
1259{
1260 static_assert(sizeof(size_t) <= sizeof(void *));
1261
1262 assert(_sl.action == SLA_PTRS);
1263
1264 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
1265 * and should be loaded like that */
1266 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) {
1267 rt = REF_VEHICLE;
1268 }
1269
1270 /* No need to look up nullptr pointers, just return immediately */
1271 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr;
1272
1273 /* Correct index. Old vehicles were saved differently:
1274 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1275 if (rt != REF_VEHICLE_OLD) index--;
1276
1277 switch (rt) {
1278 case REF_ORDERLIST:
1279 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1280 SlErrorCorrupt("Referencing invalid OrderList");
1281
1282 case REF_VEHICLE_OLD:
1283 case REF_VEHICLE:
1284 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1285 SlErrorCorrupt("Referencing invalid Vehicle");
1286
1287 case REF_STATION:
1288 if (Station::IsValidID(index)) return Station::Get(index);
1289 SlErrorCorrupt("Referencing invalid Station");
1290
1291 case REF_TOWN:
1292 if (Town::IsValidID(index)) return Town::Get(index);
1293 SlErrorCorrupt("Referencing invalid Town");
1294
1295 case REF_ROADSTOPS:
1296 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1297 SlErrorCorrupt("Referencing invalid RoadStop");
1298
1299 case REF_ENGINE_RENEWS:
1300 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1301 SlErrorCorrupt("Referencing invalid EngineRenew");
1302
1303 case REF_CARGO_PACKET:
1304 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1305 SlErrorCorrupt("Referencing invalid CargoPacket");
1306
1307 case REF_STORAGE:
1308 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1309 SlErrorCorrupt("Referencing invalid PersistentStorage");
1310
1311 case REF_LINK_GRAPH:
1312 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1313 SlErrorCorrupt("Referencing invalid LinkGraph");
1314
1315 case REF_LINK_GRAPH_JOB:
1316 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1317 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1318
1319 default: NOT_REACHED();
1320 }
1321}
1322
1328 void SlSaveLoadRef(void *ptr, VarType conv)
1329{
1330 switch (_sl.action) {
1331 case SLA_SAVE:
1332 SlWriteUint32((uint32_t)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
1333 break;
1334 case SLA_LOAD_CHECK:
1335 case SLA_LOAD:
1336 *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32();
1337 break;
1338 case SLA_PTRS:
1339 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
1340 break;
1341 case SLA_NULL:
1342 *(void **)ptr = nullptr;
1343 break;
1344 default: NOT_REACHED();
1345 }
1346}
1347
1351template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1352 class SlStorageHelper {
1353 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1354public:
1361 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1362 {
1363 assert(cmd == SL_VAR || cmd == SL_REF);
1364
1365 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1366
1367 int type_size = SlGetArrayLength(list->size());
1368 int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32);
1369 return list->size() * item_size + type_size;
1370 }
1371
1372 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1373 {
1374 switch (cmd) {
1375 case SL_VAR: SlSaveLoadConv(item, conv); break;
1376 case SL_REF: SlSaveLoadRef(item, conv); break;
1377 case SL_STDSTR: SlStdString(item, conv); break;
1378 default:
1379 NOT_REACHED();
1380 }
1381 }
1382
1389 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1390 {
1391 assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
1392
1393 SlStorageT *list = static_cast<SlStorageT *>(storage);
1394
1395 switch (_sl.action) {
1396 case SLA_SAVE:
1397 SlWriteArrayLength(list->size());
1398
1399 for (auto &item : *list) {
1400 SlSaveLoadMember(cmd, &item, conv);
1401 }
1402 break;
1403
1404 case SLA_LOAD_CHECK:
1405 case SLA_LOAD: {
1406 size_t length;
1407 switch (cmd) {
1408 case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1409 case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1410 case SL_STDSTR: length = SlReadArrayLength(); break;
1411 default: NOT_REACHED();
1412 }
1413
1414 list->clear();
1415 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1416 list->reserve(length);
1417 }
1418
1419 /* Load each value and push to the end of the storage. */
1420 for (size_t i = 0; i < length; i++) {
1421 Tvar &data = list->emplace_back();
1422 SlSaveLoadMember(cmd, &data, conv);
1423 }
1424 break;
1425 }
1426
1427 case SLA_PTRS:
1428 for (auto &item : *list) {
1429 SlSaveLoadMember(cmd, &item, conv);
1430 }
1431 break;
1432
1433 case SLA_NULL:
1434 list->clear();
1435 break;
1436
1437 default: NOT_REACHED();
1438 }
1439 }
1440};
1441
1447 static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1448{
1449 return SlStorageHelper<std::list, void *>::SlCalcLen(list, conv, SL_REF);
1450}
1451
1457 static void SlRefList(void *list, VarType conv)
1458{
1459 /* Automatically calculate the length? */
1460 if (_sl.need_length != NL_NONE) {
1461 SlSetLength(SlCalcRefListLen(list, conv));
1462 /* Determine length only? */
1463 if (_sl.need_length == NL_CALCLENGTH) return;
1464 }
1465
1466 SlStorageHelper<std::list, void *>::SlSaveLoad(list, conv, SL_REF);
1467}
1468
1474 static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1475{
1476 return SlStorageHelper<std::vector, void *>::SlCalcLen(vector, conv, SL_REF);
1477}
1478
1484 static void SlRefVector(void *vector, VarType conv)
1485{
1486 /* Automatically calculate the length? */
1487 if (_sl.need_length != NL_NONE) {
1488 SlSetLength(SlCalcRefVectorLen(vector, conv));
1489 /* Determine length only? */
1490 if (_sl.need_length == NL_CALCLENGTH) return;
1491 }
1492
1493 SlStorageHelper<std::vector, void *>::SlSaveLoad(vector, conv, SL_REF);
1494}
1495
1501 static inline size_t SlCalcDequeLen(const void *deque, VarType conv)
1502{
1503 switch (GetVarMemType(conv)) {
1504 case SLE_VAR_BL: return SlStorageHelper<std::deque, bool>::SlCalcLen(deque, conv);
1505 case SLE_VAR_I8: return SlStorageHelper<std::deque, int8_t>::SlCalcLen(deque, conv);
1506 case SLE_VAR_U8: return SlStorageHelper<std::deque, uint8_t>::SlCalcLen(deque, conv);
1507 case SLE_VAR_I16: return SlStorageHelper<std::deque, int16_t>::SlCalcLen(deque, conv);
1508 case SLE_VAR_U16: return SlStorageHelper<std::deque, uint16_t>::SlCalcLen(deque, conv);
1509 case SLE_VAR_I32: return SlStorageHelper<std::deque, int32_t>::SlCalcLen(deque, conv);
1510 case SLE_VAR_U32: return SlStorageHelper<std::deque, uint32_t>::SlCalcLen(deque, conv);
1511 case SLE_VAR_I64: return SlStorageHelper<std::deque, int64_t>::SlCalcLen(deque, conv);
1512 case SLE_VAR_U64: return SlStorageHelper<std::deque, uint64_t>::SlCalcLen(deque, conv);
1513
1514 case SLE_VAR_STR:
1515 /* Strings are a length-prefixed field type in the savegame table format,
1516 * these may not be directly stored in another length-prefixed container type. */
1517 NOT_REACHED();
1518
1519 default: NOT_REACHED();
1520 }
1521}
1522
1528 static void SlDeque(void *deque, VarType conv)
1529{
1530 switch (GetVarMemType(conv)) {
1531 case SLE_VAR_BL: SlStorageHelper<std::deque, bool>::SlSaveLoad(deque, conv); break;
1532 case SLE_VAR_I8: SlStorageHelper<std::deque, int8_t>::SlSaveLoad(deque, conv); break;
1533 case SLE_VAR_U8: SlStorageHelper<std::deque, uint8_t>::SlSaveLoad(deque, conv); break;
1534 case SLE_VAR_I16: SlStorageHelper<std::deque, int16_t>::SlSaveLoad(deque, conv); break;
1535 case SLE_VAR_U16: SlStorageHelper<std::deque, uint16_t>::SlSaveLoad(deque, conv); break;
1536 case SLE_VAR_I32: SlStorageHelper<std::deque, int32_t>::SlSaveLoad(deque, conv); break;
1537 case SLE_VAR_U32: SlStorageHelper<std::deque, uint32_t>::SlSaveLoad(deque, conv); break;
1538 case SLE_VAR_I64: SlStorageHelper<std::deque, int64_t>::SlSaveLoad(deque, conv); break;
1539 case SLE_VAR_U64: SlStorageHelper<std::deque, uint64_t>::SlSaveLoad(deque, conv); break;
1540
1541 case SLE_VAR_STR:
1542 /* Strings are a length-prefixed field type in the savegame table format,
1543 * these may not be directly stored in another length-prefixed container type.
1544 * This is permitted for load-related actions, because invalid fields of this type are present
1545 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1546 assert(_sl.action != SLA_SAVE);
1547 SlStorageHelper<std::deque, std::string>::SlSaveLoad(deque, conv, SL_STDSTR);
1548 break;
1549
1550 default: NOT_REACHED();
1551 }
1552}
1553
1559 static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1560{
1561 switch (GetVarMemType(conv)) {
1562 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1563 case SLE_VAR_I8: return SlStorageHelper<std::vector, int8_t>::SlCalcLen(vector, conv);
1564 case SLE_VAR_U8: return SlStorageHelper<std::vector, uint8_t>::SlCalcLen(vector, conv);
1565 case SLE_VAR_I16: return SlStorageHelper<std::vector, int16_t>::SlCalcLen(vector, conv);
1566 case SLE_VAR_U16: return SlStorageHelper<std::vector, uint16_t>::SlCalcLen(vector, conv);
1567 case SLE_VAR_I32: return SlStorageHelper<std::vector, int32_t>::SlCalcLen(vector, conv);
1568 case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
1569 case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
1570 case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
1571
1572 case SLE_VAR_STR:
1573 /* Strings are a length-prefixed field type in the savegame table format,
1574 * these may not be directly stored in another length-prefixed container type. */
1575 NOT_REACHED();
1576
1577 default: NOT_REACHED();
1578 }
1579}
1580
1586 static void SlVector(void *vector, VarType conv)
1587{
1588 switch (GetVarMemType(conv)) {
1589 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1590 case SLE_VAR_I8: SlStorageHelper<std::vector, int8_t>::SlSaveLoad(vector, conv); break;
1591 case SLE_VAR_U8: SlStorageHelper<std::vector, uint8_t>::SlSaveLoad(vector, conv); break;
1592 case SLE_VAR_I16: SlStorageHelper<std::vector, int16_t>::SlSaveLoad(vector, conv); break;
1593 case SLE_VAR_U16: SlStorageHelper<std::vector, uint16_t>::SlSaveLoad(vector, conv); break;
1594 case SLE_VAR_I32: SlStorageHelper<std::vector, int32_t>::SlSaveLoad(vector, conv); break;
1595 case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
1596 case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
1597 case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
1598
1599 case SLE_VAR_STR:
1600 /* Strings are a length-prefixed field type in the savegame table format,
1601 * these may not be directly stored in another length-prefixed container type.
1602 * This is permitted for load-related actions, because invalid fields of this type are present
1603 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1604 assert(_sl.action != SLA_SAVE);
1605 SlStorageHelper<std::vector, std::string>::SlSaveLoad(vector, conv, SL_STDSTR);
1606 break;
1607
1608 default: NOT_REACHED();
1609 }
1610}
1611
1613 static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1614{
1615 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1616}
1617
1623 static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1624{
1625 size_t length = 0;
1626
1627 for (auto &sld : slt) {
1628 if (!SlIsObjectValidInSavegame(sld)) continue;
1629
1630 length += SlCalcConvFileLen(SLE_UINT8);
1631 length += SlCalcStdStringLen(&sld.name);
1632 }
1633
1634 length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
1635
1636 for (auto &sld : slt) {
1637 if (!SlIsObjectValidInSavegame(sld)) continue;
1638 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1639 length += SlCalcTableHeader(sld.handler->GetDescription());
1640 }
1641 }
1642
1643 return length;
1644}
1645
1652 size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1653{
1654 size_t length = 0;
1655
1656 /* Need to determine the length and write a length tag. */
1657 for (auto &sld : slt) {
1658 length += SlCalcObjMemberLength(object, sld);
1659 }
1660 return length;
1661}
1662
1663size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1664{
1665 assert(_sl.action == SLA_SAVE);
1666
1667 if (!SlIsObjectValidInSavegame(sld)) return 0;
1668
1669 switch (sld.cmd) {
1670 case SL_VAR: return SlCalcConvFileLen(sld.conv);
1671 case SL_REF: return SlCalcRefLen();
1672 case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
1673 case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
1674 case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
1675 case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
1676 case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1677 case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
1678 case SL_SAVEBYTE: return 1; // a byte is logically of size 1
1679 case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
1680
1681 case SL_STRUCT:
1682 case SL_STRUCTLIST: {
1683 NeedLength old_need_length = _sl.need_length;
1684 size_t old_obj_len = _sl.obj_len;
1685
1686 _sl.need_length = NL_CALCLENGTH;
1687 _sl.obj_len = 0;
1688
1689 /* Pretend that we are saving to collect the object size. Other
1690 * means are difficult, as we don't know the length of the list we
1691 * are about to store. */
1692 sld.handler->Save(const_cast<void *>(object));
1693 size_t length = _sl.obj_len;
1694
1695 _sl.obj_len = old_obj_len;
1696 _sl.need_length = old_need_length;
1697
1698 if (sld.cmd == SL_STRUCT) {
1699 length += SlGetArrayLength(1);
1700 }
1701
1702 return length;
1703 }
1704
1705 default: NOT_REACHED();
1706 }
1707 return 0;
1708}
1709
1710static bool SlObjectMember(void *object, const SaveLoad &sld)
1711{
1712 if (!SlIsObjectValidInSavegame(sld)) return false;
1713
1714 VarType conv = GB(sld.conv, 0, 8);
1715 switch (sld.cmd) {
1716 case SL_VAR:
1717 case SL_REF:
1718 case SL_ARR:
1719 case SL_REFLIST:
1720 case SL_REFVECTOR:
1721 case SL_DEQUE:
1722 case SL_VECTOR:
1723 case SL_STDSTR: {
1724 void *ptr = GetVariableAddress(object, sld);
1725
1726 switch (sld.cmd) {
1727 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
1728 case SL_REF: SlSaveLoadRef(ptr, conv); break;
1729 case SL_ARR: SlArray(ptr, sld.length, conv); break;
1730 case SL_REFLIST: SlRefList(ptr, conv); break;
1731 case SL_REFVECTOR: SlRefVector(ptr, conv); break;
1732 case SL_DEQUE: SlDeque(ptr, conv); break;
1733 case SL_VECTOR: SlVector(ptr, conv); break;
1734 case SL_STDSTR: SlStdString(ptr, sld.conv); break;
1735 default: NOT_REACHED();
1736 }
1737 break;
1738 }
1739
1740 /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object.
1741 * When loading, the value is read explicitly with SlReadByte() to determine which
1742 * object description to use. */
1743 case SL_SAVEBYTE: {
1744 void *ptr = GetVariableAddress(object, sld);
1745
1746 switch (_sl.action) {
1747 case SLA_SAVE: SlWriteByte(*(uint8_t *)ptr); break;
1748 case SLA_LOAD_CHECK:
1749 case SLA_LOAD:
1750 case SLA_PTRS:
1751 case SLA_NULL: break;
1752 default: NOT_REACHED();
1753 }
1754 break;
1755 }
1756
1757 case SL_NULL: {
1758 assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
1759
1760 switch (_sl.action) {
1761 case SLA_LOAD_CHECK:
1762 case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
1763 case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1764 case SLA_PTRS:
1765 case SLA_NULL: break;
1766 default: NOT_REACHED();
1767 }
1768 break;
1769 }
1770
1771 case SL_STRUCT:
1772 case SL_STRUCTLIST:
1773 switch (_sl.action) {
1774 case SLA_SAVE: {
1775 if (sld.cmd == SL_STRUCT) {
1776 /* Store in the savegame if this struct was written or not. */
1777 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1778 }
1779 sld.handler->Save(object);
1780 break;
1781 }
1782
1783 case SLA_LOAD_CHECK: {
1784 if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
1785 SlGetStructListLength(1);
1786 }
1787 sld.handler->LoadCheck(object);
1788 break;
1789 }
1790
1791 case SLA_LOAD: {
1792 if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
1793 SlGetStructListLength(1);
1794 }
1795 sld.handler->Load(object);
1796 break;
1797 }
1798
1799 case SLA_PTRS:
1800 sld.handler->FixPointers(object);
1801 break;
1802
1803 case SLA_NULL: break;
1804 default: NOT_REACHED();
1805 }
1806 break;
1807
1808 default: NOT_REACHED();
1809 }
1810 return true;
1811}
1812
1817 void SlSetStructListLength(size_t length)
1818{
1819 /* Automatically calculate the length? */
1820 if (_sl.need_length != NL_NONE) {
1821 SlSetLength(SlGetArrayLength(length));
1822 if (_sl.need_length == NL_CALCLENGTH) return;
1823 }
1824
1825 SlWriteArrayLength(length);
1826}
1827
1833 size_t SlGetStructListLength(size_t limit)
1834{
1835 size_t length = SlReadArrayLength();
1836 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1837
1838 return length;
1839}
1840
1846 void SlObject(void *object, const SaveLoadTable &slt)
1847{
1848 /* Automatically calculate the length? */
1849 if (_sl.need_length != NL_NONE) {
1850 SlSetLength(SlCalcObjLength(object, slt));
1851 if (_sl.need_length == NL_CALCLENGTH) return;
1852 }
1853
1854 for (auto &sld : slt) {
1855 SlObjectMember(object, sld);
1856 }
1857}
1858
1863 class SlSkipHandler : public SaveLoadHandler {
1864 void Save(void *) const override
1865 {
1866 NOT_REACHED();
1867 }
1868
1869 void Load(void *object) const override
1870 {
1871 size_t length = SlGetStructListLength(UINT32_MAX);
1872 for (; length > 0; length--) {
1873 SlObject(object, this->GetLoadDescription());
1874 }
1875 }
1876
1877 void LoadCheck(void *object) const override
1878 {
1879 this->Load(object);
1880 }
1881
1882 virtual SaveLoadTable GetDescription() const override
1883 {
1884 return {};
1885 }
1886
1887 virtual SaveLoadCompatTable GetCompatDescription() const override
1888 {
1889 NOT_REACHED();
1890 }
1891};
1892
1899std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1900{
1901 /* You can only use SlTableHeader if you are a CH_TABLE. */
1902 assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
1903
1904 switch (_sl.action) {
1905 case SLA_LOAD_CHECK:
1906 case SLA_LOAD: {
1907 std::vector<SaveLoad> saveloads;
1908
1909 /* Build a key lookup mapping based on the available fields. */
1910 std::map<std::string, const SaveLoad *> key_lookup;
1911 for (auto &sld : slt) {
1912 if (!SlIsObjectValidInSavegame(sld)) continue;
1913
1914 /* Check that there is only one active SaveLoad for a given name. */
1915 assert(key_lookup.find(sld.name) == key_lookup.end());
1916 key_lookup[sld.name] = &sld;
1917 }
1918
1919 while (true) {
1920 uint8_t type = 0;
1921 SlSaveLoadConv(&type, SLE_UINT8);
1922 if (type == SLE_FILE_END) break;
1923
1924 std::string key;
1925 SlStdString(&key, SLE_STR);
1926
1927 auto sld_it = key_lookup.find(key);
1928 if (sld_it == key_lookup.end()) {
1929 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1930 Debug(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type);
1931
1932 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1933 SaveLoadType saveload_type;
1934 switch (type & SLE_FILE_TYPE_MASK) {
1935 case SLE_FILE_STRING:
1936 /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
1937 saveload_type = SL_STDSTR;
1938 break;
1939
1940 case SLE_FILE_STRUCT:
1941 /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */
1942 saveload_type = SL_STRUCTLIST;
1943 handler = std::make_shared<SlSkipHandler>();
1944 break;
1945
1946 default:
1947 saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
1948 break;
1949 }
1950
1951 /* We don't know this field, so read to nothing. */
1952 saveloads.emplace_back(std::move(key), saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, std::move(handler));
1953 continue;
1954 }
1955
1956 /* Validate the type of the field. If it is changed, the
1957 * savegame should have been bumped so we know how to do the
1958 * conversion. If this error triggers, that clearly didn't
1959 * happen and this is a friendly poke to the developer to bump
1960 * the savegame version and add conversion code. */
1961 uint8_t correct_type = GetSavegameFileType(*sld_it->second);
1962 if (correct_type != type) {
1963 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type, type);
1964 SlErrorCorrupt("Field type is different than expected");
1965 }
1966 saveloads.emplace_back(*sld_it->second);
1967 }
1968
1969 for (auto &sld : saveloads) {
1970 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1971 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
1972 }
1973 }
1974
1975 return saveloads;
1976 }
1977
1978 case SLA_SAVE: {
1979 /* Automatically calculate the length? */
1980 if (_sl.need_length != NL_NONE) {
1981 SlSetLength(SlCalcTableHeader(slt));
1982 if (_sl.need_length == NL_CALCLENGTH) break;
1983 }
1984
1985 for (auto &sld : slt) {
1986 if (!SlIsObjectValidInSavegame(sld)) continue;
1987 /* Make sure we are not storing empty keys. */
1988 assert(!sld.name.empty());
1989
1990 uint8_t type = GetSavegameFileType(sld);
1991 assert(type != SLE_FILE_END);
1992
1993 SlSaveLoadConv(&type, SLE_UINT8);
1994 SlStdString(const_cast<std::string *>(&sld.name), SLE_STR);
1995 }
1996
1997 /* Add an end-of-header marker. */
1998 uint8_t type = SLE_FILE_END;
1999 SlSaveLoadConv(&type, SLE_UINT8);
2000
2001 /* After the table, write down any sub-tables we might have. */
2002 for (auto &sld : slt) {
2003 if (!SlIsObjectValidInSavegame(sld)) continue;
2004 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
2005 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
2006 NeedLength old_need_length = _sl.need_length;
2007 _sl.need_length = NL_NONE;
2008
2009 SlTableHeader(sld.handler->GetDescription());
2010
2011 _sl.need_length = old_need_length;
2012 }
2013 }
2014
2015 break;
2016 }
2017
2018 default: NOT_REACHED();
2019 }
2020
2021 return std::vector<SaveLoad>();
2022}
2023
2037std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
2038{
2039 assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
2040 /* CH_TABLE / CH_SPARSE_TABLE always have a header. */
2041 if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt);
2042
2043 std::vector<SaveLoad> saveloads;
2044
2045 /* Build a key lookup mapping based on the available fields. */
2046 std::map<std::string, std::vector<const SaveLoad *>> key_lookup;
2047 for (auto &sld : slt) {
2048 /* All entries should have a name; otherwise the entry should just be removed. */
2049 assert(!sld.name.empty());
2050
2051 key_lookup[sld.name].push_back(&sld);
2052 }
2053
2054 for (auto &slc : slct) {
2055 if (slc.name.empty()) {
2056 /* In old savegames there can be data we no longer care for. We
2057 * skip this by simply reading the amount of bytes indicated and
2058 * send those to /dev/null. */
2059 saveloads.emplace_back("", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr);
2060 } else {
2061 auto sld_it = key_lookup.find(slc.name);
2062 /* If this branch triggers, it means that an entry in the
2063 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2064 * you rename a field in one and not in the other? */
2065 if (sld_it == key_lookup.end()) {
2066 /* This isn't an assert, as that leaves no information what
2067 * field was to blame. This way at least we have breadcrumbs. */
2068 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2069 SlErrorCorrupt("Internal error with savegame compatibility");
2070 }
2071 for (auto &sld : sld_it->second) {
2072 saveloads.push_back(*sld);
2073 }
2074 }
2075 }
2076
2077 for (auto &sld : saveloads) {
2078 if (!SlIsObjectValidInSavegame(sld)) continue;
2079 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
2080 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2081 }
2082 }
2083
2084 return saveloads;
2085}
2086
2091 void SlGlobList(const SaveLoadTable &slt)
2092{
2093 SlObject(nullptr, slt);
2094}
2095
2101 void SlAutolength(AutolengthProc *proc, int arg)
2102{
2103 assert(_sl.action == SLA_SAVE);
2104
2105 /* Tell it to calculate the length */
2106 _sl.need_length = NL_CALCLENGTH;
2107 _sl.obj_len = 0;
2108 proc(arg);
2109
2110 /* Setup length */
2111 _sl.need_length = NL_WANTLENGTH;
2112 SlSetLength(_sl.obj_len);
2113
2114 size_t start_pos = _sl.dumper->GetSize();
2115 size_t expected_offs = start_pos + _sl.obj_len;
2116
2117 /* And write the stuff */
2118 proc(arg);
2119
2120 if (expected_offs != _sl.dumper->GetSize()) {
2121 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2122 }
2123}
2124
2125 void ChunkHandler::LoadCheck(size_t len) const
2126{
2127 switch (_sl.block_mode) {
2128 case CH_TABLE:
2129 case CH_SPARSE_TABLE:
2130 SlTableHeader({});
2131 [[fallthrough]];
2132 case CH_ARRAY:
2133 case CH_SPARSE_ARRAY:
2134 SlSkipArray();
2135 break;
2136 case CH_RIFF:
2137 SlSkipBytes(len);
2138 break;
2139 default:
2140 NOT_REACHED();
2141 }
2142}
2143
2148 static void SlLoadChunk(const ChunkHandler &ch)
2149{
2150 uint8_t m = SlReadByte();
2151
2152 _sl.block_mode = m & CH_TYPE_MASK;
2153 _sl.obj_len = 0;
2154 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2155
2156 /* The header should always be at the start. Read the length; the
2157 * Load() should as first action process the header. */
2158 if (_sl.expect_table_header) {
2159 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2160 }
2161
2162 switch (_sl.block_mode) {
2163 case CH_TABLE:
2164 case CH_ARRAY:
2165 _sl.array_index = 0;
2166 ch.Load();
2167 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2168 break;
2169 case CH_SPARSE_TABLE:
2170 case CH_SPARSE_ARRAY:
2171 ch.Load();
2172 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2173 break;
2174 case CH_RIFF: {
2175 /* Read length */
2176 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2177 len += SlReadUint16();
2178 _sl.obj_len = len;
2179 size_t start_pos = _sl.reader->GetSize();
2180 size_t endoffs = start_pos + len;
2181 ch.Load();
2182
2183 if (_sl.reader->GetSize() != endoffs) {
2184 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2185 }
2186 break;
2187 }
2188 default:
2189 SlErrorCorrupt("Invalid chunk type");
2190 break;
2191 }
2192
2193 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2194}
2195
2201 static void SlLoadCheckChunk(const ChunkHandler &ch)
2202{
2203 uint8_t m = SlReadByte();
2204
2205 _sl.block_mode = m & CH_TYPE_MASK;
2206 _sl.obj_len = 0;
2207 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2208
2209 /* The header should always be at the start. Read the length; the
2210 * LoadCheck() should as first action process the header. */
2211 if (_sl.expect_table_header) {
2212 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2213 }
2214
2215 switch (_sl.block_mode) {
2216 case CH_TABLE:
2217 case CH_ARRAY:
2218 _sl.array_index = 0;
2219 ch.LoadCheck();
2220 break;
2221 case CH_SPARSE_TABLE:
2222 case CH_SPARSE_ARRAY:
2223 ch.LoadCheck();
2224 break;
2225 case CH_RIFF: {
2226 /* Read length */
2227 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2228 len += SlReadUint16();
2229 _sl.obj_len = len;
2230 size_t start_pos = _sl.reader->GetSize();
2231 size_t endoffs = start_pos + len;
2232 ch.LoadCheck(len);
2233
2234 if (_sl.reader->GetSize() != endoffs) {
2235 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2236 }
2237 break;
2238 }
2239 default:
2240 SlErrorCorrupt("Invalid chunk type");
2241 break;
2242 }
2243
2244 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2245}
2246
2252 static void SlSaveChunk(const ChunkHandler &ch)
2253{
2254 if (ch.type == CH_READONLY) return;
2255
2256 SlWriteUint32(ch.id);
2257 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2258
2259 _sl.block_mode = ch.type;
2260 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2261
2262 _sl.need_length = (_sl.expect_table_header || _sl.block_mode == CH_RIFF) ? NL_WANTLENGTH : NL_NONE;
2263
2264 switch (_sl.block_mode) {
2265 case CH_RIFF:
2266 ch.Save();
2267 break;
2268 case CH_TABLE:
2269 case CH_ARRAY:
2270 _sl.last_array_index = 0;
2271 SlWriteByte(_sl.block_mode);
2272 ch.Save();
2273 SlWriteArrayLength(0); // Terminate arrays
2274 break;
2275 case CH_SPARSE_TABLE:
2276 case CH_SPARSE_ARRAY:
2277 SlWriteByte(_sl.block_mode);
2278 ch.Save();
2279 SlWriteArrayLength(0); // Terminate arrays
2280 break;
2281 default: NOT_REACHED();
2282 }
2283
2284 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2285}
2286
2288 static void SlSaveChunks()
2289{
2290 for (auto &ch : ChunkHandlers()) {
2291 SlSaveChunk(ch);
2292 }
2293
2294 /* Terminator */
2295 SlWriteUint32(0);
2296}
2297
2304 static const ChunkHandler *SlFindChunkHandler(uint32_t id)
2305{
2306 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2307 return nullptr;
2308}
2309
2311 static void SlLoadChunks()
2312{
2313 uint32_t id;
2314 const ChunkHandler *ch;
2315
2316 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2317 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2318
2319 ch = SlFindChunkHandler(id);
2320 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2321 SlLoadChunk(*ch);
2322 }
2323}
2324
2326 static void SlLoadCheckChunks()
2327{
2328 uint32_t id;
2329 const ChunkHandler *ch;
2330
2331 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2332 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2333
2334 ch = SlFindChunkHandler(id);
2335 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2336 SlLoadCheckChunk(*ch);
2337 }
2338}
2339
2341 static void SlFixPointers()
2342{
2343 _sl.action = SLA_PTRS;
2344
2345 for (const ChunkHandler &ch : ChunkHandlers()) {
2346 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2347 ch.FixPointers();
2348 }
2349
2350 assert(_sl.action == SLA_PTRS);
2351}
2352
2353
2355 struct FileReader : LoadFilter {
2356 std::optional<FileHandle> file;
2357 long begin;
2358
2363 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2364 {
2365 }
2366
2368 ~FileReader()
2369 {
2370 if (this->file.has_value()) {
2371 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2372 }
2373 }
2374
2375 size_t Read(uint8_t *buf, size_t size) override
2376 {
2377 /* We're in the process of shutting down, i.e. in "failure" mode. */
2378 if (!this->file.has_value()) return 0;
2379
2380 return fread(buf, 1, size, *this->file);
2381 }
2382
2383 void Reset() override
2384 {
2385 clearerr(*this->file);
2386 if (fseek(*this->file, this->begin, SEEK_SET)) {
2387 Debug(sl, 1, "Could not reset the file reading");
2388 }
2389 }
2390};
2391
2393 struct FileWriter : SaveFilter {
2394 std::optional<FileHandle> file;
2395
2400 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2401 {
2402 }
2403
2405 ~FileWriter()
2406 {
2407 this->Finish();
2408 }
2409
2410 void Write(uint8_t *buf, size_t size) override
2411 {
2412 /* We're in the process of shutting down, i.e. in "failure" mode. */
2413 if (!this->file.has_value()) return;
2414
2415 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2416 }
2417
2418 void Finish() override
2419 {
2420 if (this->file.has_value()) {
2421 _game_session_stats.savegame_size = ftell(*this->file);
2422 this->file.reset();
2423 }
2424 }
2425};
2426
2427/*******************************************
2428 ********** START OF LZO CODE **************
2429 *******************************************/
2430
2431#ifdef WITH_LZO
2432
2434 static const uint LZO_BUFFER_SIZE = 8192;
2435
2437 struct LZOLoadFilter : LoadFilter {
2442 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2443 {
2444 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2445 }
2446
2447 size_t Read(uint8_t *buf, size_t ssize) override
2448 {
2449 assert(ssize >= LZO_BUFFER_SIZE);
2450
2451 /* Buffer size is from the LZO docs plus the chunk header size. */
2452 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2453 uint32_t tmp[2];
2454 uint32_t size;
2455 lzo_uint len = ssize;
2456
2457 /* Read header*/
2458 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2459
2460 /* Check if size is bad */
2461 ((uint32_t*)out)[0] = size = tmp[1];
2462
2463 if (_sl_version != SL_MIN_VERSION) {
2464 tmp[0] = TO_BE32(tmp[0]);
2465 size = TO_BE32(size);
2466 }
2467
2468 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2469
2470 /* Read block */
2471 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2472
2473 /* Verify checksum */
2474 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2475
2476 /* Decompress */
2477 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2478 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2479 return len;
2480 }
2481};
2482
2484 struct LZOSaveFilter : SaveFilter {
2489 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2490 {
2491 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2492 }
2493
2494 void Write(uint8_t *buf, size_t size) override
2495 {
2496 const lzo_bytep in = buf;
2497 /* Buffer size is from the LZO docs plus the chunk header size. */
2498 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2499 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2500 lzo_uint outlen;
2501
2502 do {
2503 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2504 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
2505 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2506 ((uint32_t*)out)[1] = TO_BE32((uint32_t)outlen);
2507 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2508 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2509
2510 /* Move to next data chunk. */
2511 size -= len;
2512 in += len;
2513 } while (size > 0);
2514 }
2515};
2516
2517#endif /* WITH_LZO */
2518
2519/*********************************************
2520 ******** START OF NOCOMP CODE (uncompressed)*
2521 *********************************************/
2522
2524 struct NoCompLoadFilter : LoadFilter {
2529 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2530 {
2531 }
2532
2533 size_t Read(uint8_t *buf, size_t size) override
2534 {
2535 return this->chain->Read(buf, size);
2536 }
2537};
2538
2540 struct NoCompSaveFilter : SaveFilter {
2545 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2546 {
2547 }
2548
2549 void Write(uint8_t *buf, size_t size) override
2550 {
2551 this->chain->Write(buf, size);
2552 }
2553};
2554
2555/********************************************
2556 ********** START OF ZLIB CODE **************
2557 ********************************************/
2558
2559#if defined(WITH_ZLIB)
2560
2562 struct ZlibLoadFilter : LoadFilter {
2563 z_stream z{};
2564 uint8_t fread_buf[MEMORY_CHUNK_SIZE];
2565
2570 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2571 {
2572 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2573 }
2574
2576 ~ZlibLoadFilter()
2577 {
2578 inflateEnd(&this->z);
2579 }
2580
2581 size_t Read(uint8_t *buf, size_t size) override
2582 {
2583 this->z.next_out = buf;
2584 this->z.avail_out = (uint)size;
2585
2586 do {
2587 /* read more bytes from the file? */
2588 if (this->z.avail_in == 0) {
2589 this->z.next_in = this->fread_buf;
2590 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2591 }
2592
2593 /* inflate the data */
2594 int r = inflate(&this->z, 0);
2595 if (r == Z_STREAM_END) break;
2596
2597 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2598 } while (this->z.avail_out != 0);
2599
2600 return size - this->z.avail_out;
2601 }
2602};
2603
2605 struct ZlibSaveFilter : SaveFilter {
2606 z_stream z{};
2607 uint8_t fwrite_buf[MEMORY_CHUNK_SIZE];
2608
2614 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain))
2615 {
2616 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2617 }
2618
2620 ~ZlibSaveFilter()
2621 {
2622 deflateEnd(&this->z);
2623 }
2624
2631 void WriteLoop(uint8_t *p, size_t len, int mode)
2632 {
2633 uint n;
2634 this->z.next_in = p;
2635 this->z.avail_in = (uInt)len;
2636 do {
2637 this->z.next_out = this->fwrite_buf;
2638 this->z.avail_out = sizeof(this->fwrite_buf);
2639
2647 int r = deflate(&this->z, mode);
2648
2649 /* bytes were emitted? */
2650 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2651 this->chain->Write(this->fwrite_buf, n);
2652 }
2653 if (r == Z_STREAM_END) break;
2654
2655 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2656 } while (this->z.avail_in || !this->z.avail_out);
2657 }
2658
2659 void Write(uint8_t *buf, size_t size) override
2660 {
2661 this->WriteLoop(buf, size, 0);
2662 }
2663
2664 void Finish() override
2665 {
2666 this->WriteLoop(nullptr, 0, Z_FINISH);
2667 this->chain->Finish();
2668 }
2669};
2670
2671#endif /* WITH_ZLIB */
2672
2673/********************************************
2674 ********** START OF LZMA CODE **************
2675 ********************************************/
2676
2677#if defined(WITH_LIBLZMA)
2678
2685 static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2686
2688 struct LZMALoadFilter : LoadFilter {
2689 lzma_stream lzma;
2690 uint8_t fread_buf[MEMORY_CHUNK_SIZE];
2691
2696 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain)), lzma(_lzma_init)
2697 {
2698 /* Allow saves up to 256 MB uncompressed */
2699 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2700 }
2701
2703 ~LZMALoadFilter()
2704 {
2705 lzma_end(&this->lzma);
2706 }
2707
2708 size_t Read(uint8_t *buf, size_t size) override
2709 {
2710 this->lzma.next_out = buf;
2711 this->lzma.avail_out = size;
2712
2713 do {
2714 /* read more bytes from the file? */
2715 if (this->lzma.avail_in == 0) {
2716 this->lzma.next_in = this->fread_buf;
2717 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2718 }
2719
2720 /* inflate the data */
2721 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2722 if (r == LZMA_STREAM_END) break;
2723 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2724 } while (this->lzma.avail_out != 0);
2725
2726 return size - this->lzma.avail_out;
2727 }
2728};
2729
2731 struct LZMASaveFilter : SaveFilter {
2732 lzma_stream lzma;
2733 uint8_t fwrite_buf[MEMORY_CHUNK_SIZE];
2734
2740 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init)
2741 {
2742 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2743 }
2744
2746 ~LZMASaveFilter()
2747 {
2748 lzma_end(&this->lzma);
2749 }
2750
2757 void WriteLoop(uint8_t *p, size_t len, lzma_action action)
2758 {
2759 size_t n;
2760 this->lzma.next_in = p;
2761 this->lzma.avail_in = len;
2762 do {
2763 this->lzma.next_out = this->fwrite_buf;
2764 this->lzma.avail_out = sizeof(this->fwrite_buf);
2765
2766 lzma_ret r = lzma_code(&this->lzma, action);
2767
2768 /* bytes were emitted? */
2769 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2770 this->chain->Write(this->fwrite_buf, n);
2771 }
2772 if (r == LZMA_STREAM_END) break;
2773 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2774 } while (this->lzma.avail_in || !this->lzma.avail_out);
2775 }
2776
2777 void Write(uint8_t *buf, size_t size) override
2778 {
2779 this->WriteLoop(buf, size, LZMA_RUN);
2780 }
2781
2782 void Finish() override
2783 {
2784 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2785 this->chain->Finish();
2786 }
2787};
2788
2789#endif /* WITH_LIBLZMA */
2790
2791/*******************************************
2792 ************* END OF CODE *****************
2793 *******************************************/
2794
2796 struct SaveLoadFormat {
2797 std::string_view name;
2798 uint32_t tag;
2799
2800 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2801 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2802
2803 uint8_t min_compression;
2804 uint8_t default_compression;
2805 uint8_t max_compression;
2806};
2807
2808static const uint32_t SAVEGAME_TAG_LZO = TO_BE32('OTTD');
2809static const uint32_t SAVEGAME_TAG_NONE = TO_BE32('OTTN');
2810static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32('OTTZ');
2811static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32('OTTX');
2812
2814 static const SaveLoadFormat _saveload_formats[] = {
2815#if defined(WITH_LZO)
2816 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2817 {"lzo", SAVEGAME_TAG_LZO, CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
2818#else
2819 {"lzo", SAVEGAME_TAG_LZO, nullptr, nullptr, 0, 0, 0},
2820#endif
2821 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2822 {"none", SAVEGAME_TAG_NONE, CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
2823#if defined(WITH_ZLIB)
2824 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2825 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2826 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2827 {"zlib", SAVEGAME_TAG_ZLIB, CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
2828#else
2829 {"zlib", SAVEGAME_TAG_ZLIB, nullptr, nullptr, 0, 0, 0},
2830#endif
2831#if defined(WITH_LIBLZMA)
2832 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2833 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2834 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2835 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2836 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2837 {"lzma", SAVEGAME_TAG_LZMA, CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
2838#else
2839 {"lzma", SAVEGAME_TAG_LZMA, nullptr, nullptr, 0, 0, 0},
2840#endif
2841};
2842
2849 static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(std::string_view full_name)
2850{
2851 /* Find default savegame format, the highest one with which files can be written. */
2852 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2853 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2854
2855 const SaveLoadFormat &def = *it;
2856
2857 if (!full_name.empty()) {
2858 /* Get the ":..." of the compression level out of the way */
2859 size_t separator = full_name.find(':');
2860 bool has_comp_level = separator != std::string::npos;
2861 std::string_view name = has_comp_level ? full_name.substr(0, separator) : full_name;
2862
2863 for (const auto &slf : _saveload_formats) {
2864 if (slf.init_write != nullptr && name == slf.name) {
2865 if (has_comp_level) {
2866 auto complevel = full_name.substr(separator + 1);
2867
2868 /* Get the level and determine whether all went fine. */
2869 auto level = ParseInteger<uint8_t>(complevel);
2870 if (!level.has_value() || *level != Clamp(*level, slf.min_compression, slf.max_compression)) {
2871 ShowErrorMessage(
2872 GetEncodedString(STR_CONFIG_ERROR),
2873 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2874 WL_CRITICAL);
2875 } else {
2876 return {slf, *level};
2877 }
2878 }
2879 return {slf, slf.default_compression};
2880 }
2881 }
2882
2883 ShowErrorMessage(
2884 GetEncodedString(STR_CONFIG_ERROR),
2885 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2886 WL_CRITICAL);
2887 }
2888 return {def, def.default_compression};
2889}
2890
2891/* actual loader/saver function */
2892void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2893extern bool AfterLoadGame();
2894extern bool LoadOldSaveGame(std::string_view file);
2895
2901 static void ResetSettings()
2902{
2903 for (auto &desc : GetSaveLoadSettingTable()) {
2904 const SettingDesc *sd = GetSettingDesc(desc);
2905 if (sd->flags.Test(SettingFlag::NotInSave)) continue;
2906 if (sd->flags.Test(SettingFlag::NoNetworkSync) && _networking && !_network_server) continue;
2907
2908 sd->ResetToDefault(&_settings_game);
2909 }
2910}
2911
2912extern void ClearOldOrders();
2913
2917 static void ResetSaveloadData()
2918{
2919 ClearOldOrders();
2920 ResetTempEngineData();
2921 ClearRailTypeLabelList();
2922 ClearRoadTypeLabelList();
2923 ResetOldWaypoints();
2924 ResetSettings();
2925}
2926
2930 static inline void ClearSaveLoadState()
2931{
2932 _sl.dumper = nullptr;
2933 _sl.sf = nullptr;
2934 _sl.reader = nullptr;
2935 _sl.lf = nullptr;
2936}
2937
2939 static void SaveFileStart()
2940{
2941 SetMouseCursorBusy(true);
2942
2943 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
2944 _sl.saveinprogress = true;
2945}
2946
2948 static void SaveFileDone()
2949{
2950 SetMouseCursorBusy(false);
2951
2952 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
2953 _sl.saveinprogress = false;
2954
2955#ifdef __EMSCRIPTEN__
2956 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
2957#endif
2958}
2959
2961 void SetSaveLoadError(StringID str)
2962{
2963 _sl.error_str = str;
2964}
2965
2967 EncodedString GetSaveLoadErrorType()
2968{
2969 return GetEncodedString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
2970}
2971
2973 EncodedString GetSaveLoadErrorMessage()
2974{
2975 return GetEncodedString(_sl.error_str, _sl.extra_msg);
2976}
2977
2979 static void SaveFileError()
2980{
2981 ShowErrorMessage(GetSaveLoadErrorType(), GetSaveLoadErrorMessage(), WL_ERROR);
2982 SaveFileDone();
2983}
2984
2989 static SaveOrLoadResult SaveFileToDisk(bool threaded)
2990{
2991 try {
2992 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
2993
2994 /* We have written our stuff to memory, now write it to file! */
2995 uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
2996 _sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
2997
2998 _sl.sf = fmt.init_write(_sl.sf, compression);
2999 _sl.dumper->Flush(_sl.sf);
3000
3001 ClearSaveLoadState();
3002
3003 if (threaded) SetAsyncSaveFinish(SaveFileDone);
3004
3005 return SL_OK;
3006 } catch (...) {
3007 ClearSaveLoadState();
3008
3009 AsyncSaveFinishProc asfp = SaveFileDone;
3010
3011 /* We don't want to shout when saving is just
3012 * cancelled due to a client disconnecting. */
3013 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
3014 /* Skip the "colour" character */
3015 Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
3016 asfp = SaveFileError;
3017 }
3018
3019 if (threaded) {
3020 SetAsyncSaveFinish(asfp);
3021 } else {
3022 asfp();
3023 }
3024 return SL_ERROR;
3025 }
3026}
3027
3028void WaitTillSaved()
3029{
3030 if (!_save_thread.joinable()) return;
3031
3032 _save_thread.join();
3033
3034 /* Make sure every other state is handled properly as well. */
3035 ProcessAsyncSaveFinish();
3036}
3037
3046 static SaveOrLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
3047{
3048 assert(!_sl.saveinprogress);
3049
3050 _sl.dumper = std::make_unique<MemoryDumper>();
3051 _sl.sf = std::move(writer);
3052
3053 _sl_version = SAVEGAME_VERSION;
3054
3055 SaveViewportBeforeSaveGame();
3056 SlSaveChunks();
3057
3058 SaveFileStart();
3059
3060 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3061 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3062
3063 SaveOrLoadResult result = SaveFileToDisk(false);
3064 SaveFileDone();
3065
3066 return result;
3067 }
3068
3069 return SL_OK;
3070}
3071
3078 SaveOrLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3079{
3080 try {
3081 _sl.action = SLA_SAVE;
3082 return DoSave(std::move(writer), threaded);
3083 } catch (...) {
3084 ClearSaveLoadState();
3085 return SL_ERROR;
3086 }
3087}
3088
3097 static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
3098{
3099 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3100 if (fmt != std::end(_saveload_formats)) {
3101 /* Check version number */
3102 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3103 /* Minor is not used anymore from version 18.0, but it is still needed
3104 * in versions before that (4 cases) which can't be removed easy.
3105 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3106 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3107
3108 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3109
3110 /* Is the version higher than the current? */
3111 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3112 if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
3113 return fmt;
3114 }
3115
3116 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3117 _sl.lf->Reset();
3118 _sl_version = SL_MIN_VERSION;
3119 _sl_minor_version = 0;
3120
3121 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3122 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3123 if (fmt == std::end(_saveload_formats)) {
3124 /* Who removed the LZO savegame format definition? When built without LZO support,
3125 * the formats must still list it just without a method to read the file.
3126 * The caller of this function has to check for the existence of load function. */
3127 NOT_REACHED();
3128 }
3129 return fmt;
3130}
3131
3138 static SaveOrLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3139{
3140 _sl.lf = std::move(reader);
3141
3142 if (load_check) {
3143 /* Clear previous check data */
3144 _load_check_data.Clear();
3145 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3146 _load_check_data.checkable = true;
3147 }
3148
3149 uint32_t hdr[2];
3150 if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3151
3152 /* see if we have any loader for this type. */
3153 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
3154
3155 /* loader for this savegame type is not implemented? */
3156 if (fmt->init_load == nullptr) {
3157 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3158 }
3159
3160 _sl.lf = fmt->init_load(_sl.lf);
3161 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3162 _next_offs = 0;
3163
3164 if (!load_check) {
3165 ResetSaveloadData();
3166
3167 /* Old maps were hardcoded to 256x256 and thus did not contain
3168 * any mapsize information. Pre-initialize to 256x256 to not to
3169 * confuse old games */
3170 InitializeGame(256, 256, true, true);
3171
3172 _gamelog.Reset();
3173
3174 if (IsSavegameVersionBefore(SLV_4)) {
3175 /*
3176 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3177 * shared savegame version 4. Anything before that 'obviously'
3178 * does not have any NewGRFs. Between the introduction and
3179 * savegame version 41 (just before 0.5) the NewGRF settings
3180 * were not stored in the savegame and they were loaded by
3181 * using the settings from the main menu.
3182 * So, to recap:
3183 * - savegame version < 4: do not load any NewGRFs.
3184 * - savegame version >= 41: load NewGRFs from savegame, which is
3185 * already done at this stage by
3186 * overwriting the main menu settings.
3187 * - other savegame versions: use main menu settings.
3188 *
3189 * This means that users *can* crash savegame version 4..40
3190 * savegames if they set incompatible NewGRFs in the main menu,
3191 * but can't crash anymore for savegame version < 4 savegames.
3192 *
3193 * Note: this is done here because AfterLoadGame is also called
3194 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3195 */
3196 ClearGRFConfigList(_grfconfig);
3197 }
3198 }
3199
3200 if (load_check) {
3201 /* Load chunks into _load_check_data.
3202 * No pools are loaded. References are not possible, and thus do not need resolving. */
3203 SlLoadCheckChunks();
3204 } else {
3205 /* Load chunks and resolve references */
3206 SlLoadChunks();
3207 SlFixPointers();
3208 }
3209
3210 ClearSaveLoadState();
3211
3212 _savegame_type = SGT_OTTD;
3213
3214 if (load_check) {
3215 /* The only part from AfterLoadGame() we need */
3216 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
3217 } else {
3218 _gamelog.StartAction(GLAT_LOAD);
3219
3220 /* After loading fix up savegame for any internal changes that
3221 * might have occurred since then. If it fails, load back the old game. */
3222 if (!AfterLoadGame()) {
3223 _gamelog.StopAction();
3224 return SL_REINIT;
3225 }
3226
3227 _gamelog.StopAction();
3228 }
3229
3230 return SL_OK;
3231}
3232
3238 SaveOrLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3239{
3240 try {
3241 _sl.action = SLA_LOAD;
3242 return DoLoad(std::move(reader), false);
3243 } catch (...) {
3244 ClearSaveLoadState();
3245 return SL_REINIT;
3246 }
3247}
3248
3258 SaveOrLoadResult SaveOrLoad(std::string_view filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3259{
3260 /* An instance of saving is already active, so don't go saving again */
3261 if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) {
3262 /* if not an autosave, but a user action, show error message */
3263 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WL_ERROR);
3264 return SL_OK;
3265 }
3266 WaitTillSaved();
3267
3268 try {
3269 /* Load a TTDLX or TTDPatch game */
3270 if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) {
3271 ResetSaveloadData();
3272
3273 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3274
3275 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3276 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3277 * Note: this is done here because AfterLoadGame is also called
3278 * for OTTD savegames which have their own NewGRF logic. */
3279 ClearGRFConfigList(_grfconfig);
3280 _gamelog.Reset();
3281 if (!LoadOldSaveGame(filename)) return SL_REINIT;
3282 _sl_version = SL_MIN_VERSION;
3283 _sl_minor_version = 0;
3284 _gamelog.StartAction(GLAT_LOAD);
3285 if (!AfterLoadGame()) {
3286 _gamelog.StopAction();
3287 return SL_REINIT;
3288 }
3289 _gamelog.StopAction();
3290 return SL_OK;
3291 }
3292
3293 assert(dft == DFT_GAME_FILE);
3294 switch (fop) {
3295 case SLO_CHECK:
3296 _sl.action = SLA_LOAD_CHECK;
3297 break;
3298
3299 case SLO_LOAD:
3300 _sl.action = SLA_LOAD;
3301 break;
3302
3303 case SLO_SAVE:
3304 _sl.action = SLA_SAVE;
3305 break;
3306
3307 default: NOT_REACHED();
3308 }
3309
3310 auto fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3311
3312 /* Make it a little easier to load savegames from the console */
3313 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
3314 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
3315 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
3316
3317 if (!fh.has_value()) {
3318 SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3319 }
3320
3321 if (fop == SLO_SAVE) { // SAVE game
3322 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3323 if (!_settings_client.gui.threaded_saves) threaded = false;
3324
3325 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3326 }
3327
3328 /* LOAD game */
3329 assert(fop == SLO_LOAD || fop == SLO_CHECK);
3330 Debug(desync, 1, "load: {}", filename);
3331 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SLO_CHECK);
3332 } catch (...) {
3333 /* This code may be executed both for old and new save games. */
3334 ClearSaveLoadState();
3335
3336 /* Skip the "colour" character */
3337 if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
3338
3339 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3340 return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;
3341 }
3342}
3343
3349 void DoAutoOrNetsave(FiosNumberedSaveName &counter)
3350{
3351 std::string filename;
3352
3353 if (_settings_client.gui.keep_all_autosave) {
3354 filename = GenerateDefaultSaveName() + counter.Extension();
3355 } else {
3356 filename = counter.Filename();
3357 }
3358
3359 Debug(sl, 2, "Autosaving to '{}'", filename);
3360 if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) {
3361 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WL_ERROR);
3362 }
3363}
3364
3365
3367 void DoExitSave()
3368{
3369 SaveOrLoad("exit.sav", SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR);
3370}
3371
3375std::string GenerateDefaultSaveName()
3376{
3377 /* Check if we have a name for this map, which is the name of the first
3378 * available company. When there's no company available we'll use
3379 * 'Spectator' as "company" name. */
3380 CompanyID cid = _local_company;
3381 if (!Company::IsValidID(cid)) {
3382 for (const Company *c : Company::Iterate()) {
3383 cid = c->index;
3384 break;
3385 }
3386 }
3387
3388 std::array<StringParameter, 4> params{};
3389 auto it = params.begin();
3390 *it++ = cid;
3391
3392 /* We show the current game time differently depending on the timekeeping units used by this game. */
3393 if (TimerGameEconomy::UsingWallclockUnits()) {
3394 /* Insert time played. */
3395 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3396 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3397 *it++ = play_time / 60 / 60;
3398 *it++ = (play_time / 60) % 60;
3399 } else {
3400 /* Insert current date */
3401 switch (_settings_client.gui.date_format_in_default_names) {
3402 case 0: *it++ = STR_JUST_DATE_LONG; break;
3403 case 1: *it++ = STR_JUST_DATE_TINY; break;
3404 case 2: *it++ = STR_JUST_DATE_ISO; break;
3405 default: NOT_REACHED();
3406 }
3407 *it++ = TimerGameEconomy::date;
3408 }
3409
3410 /* Get the correct string (special string for when there's not company) */
3411 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3412 SanitizeFilename(filename);
3413 return filename;
3414}
3415
3421 void FileToSaveLoad::SetMode(const FiosType &ft, SaveLoadOperation fop)
3422{
3423 if (ft.abstract == FT_INVALID || ft.abstract == FT_NONE) {
3424 this->file_op = SLO_INVALID;
3425 this->ftype = FIOS_TYPE_INVALID;
3426 return;
3427 }
3428
3429 this->file_op = fop;
3430 this->ftype = ft;
3431}
3432
3437 void FileToSaveLoad::Set(const FiosItem &item)
3438{
3439 this->SetMode(item.type);
3440 this->name = item.name;
3441 this->title = item.title;
3442}
3443
3444 SaveLoadTable SaveLoadHandler::GetLoadDescription() const
3445{
3446 assert(this->load_description.has_value());
3447 return *this->load_description;
3448}
GB
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
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
BaseBitSet::Test
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Definition base_bitset_type.hpp:141
BaseBitSet::Set
constexpr Timpl & Set()
Set all bits.
Definition base_bitset_type.hpp:40
BaseStringBuilder::PutUtf8
void PutUtf8(char32_t c)
Append UTF.8 char.
Definition string_builder.cpp:113
BaseStringBuilder::Put
void Put(std::string_view str)
Append string.
Definition string_builder.hpp:34
BaseStringBuilder::PutIntegerBase
void PutIntegerBase(T value, int base)
Append integer 'value' in given number 'base'.
Definition string_builder.hpp:52
EncodedString
Container for an encoded string, created by GetEncodedString.
Definition strings_type.h:98
EnumBitSet
Enum-as-bit-set wrapper.
Definition enum_type.hpp:177
FileHandle
Definition fileio_type.h:132
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:536
SaveLoadHandler::GetLoadDescription
SaveLoadTable GetLoadDescription() const
Get the description for how to load the chunk.
Definition saveload.cpp:3444
SlSkipHandler
Handler that is assigned when there is a struct read in the savegame which is not known to the code.
Definition saveload.cpp:1863
SlSkipHandler::GetDescription
virtual SaveLoadTable GetDescription() const override
Get the description of the fields in the savegame.
Definition saveload.cpp:1882
SlSkipHandler::GetCompatDescription
virtual SaveLoadCompatTable GetCompatDescription() const override
Get the pre-header description of the fields in the savegame.
Definition saveload.cpp:1887
SlSkipHandler::LoadCheck
void LoadCheck(void *object) const override
Similar to load, but used only to validate savegames.
Definition saveload.cpp:1877
SlSkipHandler::Load
void Load(void *object) const override
Load the object from disk.
Definition saveload.cpp:1869
SlSkipHandler::Save
void Save(void *) const override
Save the object to disk.
Definition saveload.cpp:1864
SlStorageHelper
Template class to help with list-like types.
Definition saveload.cpp:1352
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:1389
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:1361
StringBuilder
Compose data into a growing std::string.
Definition string_builder.hpp:65
StringConsumer
Parse data from a string / buffer.
Definition string_consumer.hpp:27
StringConsumer::TryReadIntegerBase
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
Definition string_consumer.hpp:874
StringConsumer::READ_ONE_SEPARATOR
@ READ_ONE_SEPARATOR
Read one separator, and include it in the result.
Definition string_consumer.hpp:694
StringConsumer::AnyBytesLeft
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
Definition string_consumer.hpp:70
StringConsumer::TryReadUtf8
std::optional< char32_t > TryReadUtf8()
Try to read a UTF-8 character, and then advance reader.
Definition string_consumer.hpp:412
StringConsumer::ReadIntegerBase
T ReadIntegerBase(int base, T def=0, bool clamp=false)
Read and parse an integer in number 'base', and advance the reader.
Definition string_consumer.hpp:891
StringConsumer::ReadUtf8If
bool ReadUtf8If(char32_t c)
Check whether the next UTF-8 char matches 'c', and skip it.
Definition string_consumer.hpp:501
StringConsumer::ReadUntilUtf8
std::string_view ReadUntilUtf8(char32_t c, SeparatorUsage sep)
Read data until the first occurrence of UTF-8 char 'c', and advance reader.
Definition string_consumer.cpp:162
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:98
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:55
SCC_ENCODED
@ SCC_ENCODED
Encoded string marker and sub-string parameter.
Definition control_codes.h:27
SCC_ENCODED_NUMERIC
@ SCC_ENCODED_NUMERIC
Encoded numeric parameter.
Definition control_codes.h:29
SCC_ENCODED_STRING
@ SCC_ENCODED_STRING
Encoded string parameter.
Definition control_codes.h:30
Debug
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
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
ShowErrorMessage
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
Definition error_gui.cpp:296
SanitizeFilename
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition fileio.cpp:1004
FioFOpenFile
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:242
SaveLoadOperation
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:52
SLO_CHECK
@ SLO_CHECK
Load file for checking and/or preview.
Definition fileio_type.h:53
SLO_SAVE
@ SLO_SAVE
File is being saved.
Definition fileio_type.h:55
SLO_LOAD
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:54
SLO_INVALID
@ SLO_INVALID
Unknown file operation.
Definition fileio_type.h:57
DetailedFileType
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:28
DFT_GAME_FILE
@ DFT_GAME_FILE
Save game or scenario file.
Definition fileio_type.h:31
DFT_OLD_GAME_FILE
@ DFT_OLD_GAME_FILE
Old save game or scenario file.
Definition fileio_type.h:30
Subdirectory
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition fileio_type.h:88
SCENARIO_DIR
@ SCENARIO_DIR
Base directory for all scenarios.
Definition fileio_type.h:92
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition fileio_type.h:89
SAVE_DIR
@ SAVE_DIR
Base directory for all savegames.
Definition fileio_type.h:90
AUTOSAVE_DIR
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition fileio_type.h:91
FT_NONE
@ FT_NONE
nothing to do
Definition fileio_type.h:18
FT_INVALID
@ FT_INVALID
Invalid or unknown file type.
Definition fileio_type.h:24
_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:25
_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:1690
_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
_networking
bool _networking
are we in networking mode?
Definition network.cpp:67
_network_server
bool _network_server
network-server is active
Definition network.cpp:68
_grfconfig
GRFConfigList _grfconfig
First item in list of current GRF set up.
Definition newgrf_config.cpp:154
IsGoodGRFConfigList
GRFListCompatibility IsGoodGRFConfigList(GRFConfigList &grfconfig)
Check if all GRFs in the GRF config from a savegame can be loaded.
Definition newgrf_config.cpp:425
ClearGRFConfigList
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
Definition newgrf_config.cpp:324
SlRefVector
static void SlRefVector(void *vector, VarType conv)
Save/Load a vector.
Definition saveload.cpp:1484
LZO_BUFFER_SIZE
static const uint LZO_BUFFER_SIZE
Buffer size for the LZO compressor.
Definition saveload.cpp:2434
SlError
void SlError(StringID string, const std::string &extra_msg)
Error handler.
Definition saveload.cpp:339
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:2304
GetSavegameFileType
static uint8_t GetSavegameFileType(const SaveLoad &sld)
Return the type as saved/loaded inside the savegame.
Definition saveload.cpp:570
DoSave
static SaveOrLoadResult DoSave(std::shared_ptr< SaveFilter > writer, bool threaded)
Actually perform the saving of the savegame.
Definition saveload.cpp:3046
ProcessAsyncSaveFinish
void ProcessAsyncSaveFinish()
Handle async save finishes.
Definition saveload.cpp:394
FixSCCEncodedNegative
void FixSCCEncodedNegative(std::string &str)
Scan the string for SCC_ENCODED_NUMERIC with negative values, and reencode them as uint64_t.
Definition saveload.cpp:1000
_lzma_init
static const lzma_stream _lzma_init
Have a copy of an initialised LZMA stream.
Definition saveload.cpp:2685
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:1258
_ttdp_version
uint32_t _ttdp_version
version of TTDP savegame (if applicable)
Definition saveload.cpp:80
_saveload_formats
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
Definition saveload.cpp:2814
_savegame_format
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:83
SaveFileDone
static void SaveFileDone()
Update the gui accordingly when saving is done and release locks on saveload.
Definition saveload.cpp:2948
_sl_version
SaveLoadVersion _sl_version
the major savegame version identifier
Definition saveload.cpp:81
ChunkHandlers
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:220
_sl_minor_version
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:82
SlCalcRefVectorLen
static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
Return the size in bytes of a vector.
Definition saveload.cpp:1474
LoadWithFilter
SaveOrLoadResult LoadWithFilter(std::shared_ptr< LoadFilter > reader)
Load the game using a (reader) filter.
Definition saveload.cpp:3238
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:2989
ResetSaveloadData
static void ResetSaveloadData()
Clear temporary data that is passed between various saveload phases.
Definition saveload.cpp:2917
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:1226
SaveWithFilter
SaveOrLoadResult SaveWithFilter(std::shared_ptr< SaveFilter > writer, bool threaded)
Save the game using a (writer) filter.
Definition saveload.cpp:3078
SlWriteSimpleGamma
static void SlWriteSimpleGamma(size_t i)
Write the header descriptor of an object or an array.
Definition saveload.cpp:512
SlCalcTableHeader
static size_t SlCalcTableHeader(const SaveLoadTable &slt)
Calculate the size of the table header.
Definition saveload.cpp:1623
ClearSaveLoadState
static void ClearSaveLoadState()
Clear/free saveload state.
Definition saveload.cpp:2930
_do_autosave
bool _do_autosave
are we doing an autosave at the moment?
Definition saveload.cpp:84
_async_save_finish
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:376
_save_thread
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:377
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:606
SlCompatTableHeader
std::vector< SaveLoad > SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
Load a table header in a savegame compatible way.
Definition saveload.cpp:2037
ResetSettings
static void ResetSettings()
Reset all settings to their default, so any settings missing in the savegame are their default,...
Definition saveload.cpp:2901
SlWriteByte
void SlWriteByte(uint8_t b)
Wrapper for writing a byte to the dumper.
Definition saveload.cpp:419
SlGetStructListLength
size_t SlGetStructListLength(size_t limit)
Get the length of this list; if it exceeds the limit, error out.
Definition saveload.cpp:1833
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:1169
WriteValue
void WriteValue(void *ptr, VarType conv, int64_t val)
Write the value of a setting.
Definition saveload.cpp:836
AsyncSaveFinishProc
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:375
SlIterateArray
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:677
SetAsyncSaveFinish
static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
Called by save thread to tell we finished saving.
Definition saveload.cpp:383
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:2961
SlCopy
void SlCopy(void *object, size_t length, VarType conv)
Copy a list of SL_VARs to/from a savegame.
Definition saveload.cpp:1150
SlGetFieldLength
size_t SlGetFieldLength()
Get the length of the current object.
Definition saveload.cpp:800
DoAutoOrNetsave
void DoAutoOrNetsave(FiosNumberedSaveName &counter)
Create an autosave or netsave.
Definition saveload.cpp:3349
SlCalcRefLen
static size_t SlCalcRefLen()
Return the size in bytes of a reference (pointer)
Definition saveload.cpp:660
NeedLength
NeedLength
Definition saveload.cpp:95
NL_WANTLENGTH
@ NL_WANTLENGTH
writing length and data
Definition saveload.cpp:97
NL_NONE
@ NL_NONE
not working in NeedLength mode
Definition saveload.cpp:96
NL_CALCLENGTH
@ NL_CALCLENGTH
need to calculate the length
Definition saveload.cpp:98
SaveFileStart
static void SaveFileStart()
Update the gui accordingly when starting saving and set locks on saveload.
Definition saveload.cpp:2939
SlNullPointers
static void SlNullPointers()
Null all pointers (convert index -> nullptr)
Definition saveload.cpp:314
SlStdString
static void SlStdString(void *ptr, VarType conv)
Save/Load a std::string.
Definition saveload.cpp:1050
DoLoad
static SaveOrLoadResult DoLoad(std::shared_ptr< LoadFilter > reader, bool load_check)
Actually perform the loading of a "non-old" savegame.
Definition saveload.cpp:3138
SlCalcRefListLen
static size_t SlCalcRefListLen(const void *list, VarType conv)
Return the size in bytes of a list.
Definition saveload.cpp:1447
SlIsObjectValidInSavegame
static bool SlIsObjectValidInSavegame(const SaveLoad &sld)
Are we going to save this object or not?
Definition saveload.cpp:1613
GetSaveLoadErrorType
EncodedString GetSaveLoadErrorType()
Return the appropriate initial string for an error depending on whether we are saving or loading.
Definition saveload.cpp:2967
SlSaveLoadRef
void SlSaveLoadRef(void *ptr, VarType conv)
Handle conversion for references.
Definition saveload.cpp:1328
SlFixPointers
static void SlFixPointers()
Fix all pointers (convert index -> pointer)
Definition saveload.cpp:2341
SlErrorCorrupt
void SlErrorCorrupt(const std::string &msg)
Error handler for corrupt savegames.
Definition saveload.cpp:369
SlSkipArray
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:719
SlLoadChunk
static void SlLoadChunk(const ChunkHandler &ch)
Load a chunk of data (eg vehicles, stations, etc.)
Definition saveload.cpp:2148
SlLoadCheckChunks
static void SlLoadCheckChunks()
Load all chunks for savegame checking.
Definition saveload.cpp:2326
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:917
SlReadSimpleGamma
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:470
SaveLoadAction
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:87
SLA_LOAD
@ SLA_LOAD
loading
Definition saveload.cpp:88
SLA_NULL
@ SLA_NULL
null all pointers (on loading error)
Definition saveload.cpp:91
SLA_SAVE
@ SLA_SAVE
saving
Definition saveload.cpp:89
SLA_LOAD_CHECK
@ SLA_LOAD_CHECK
partial loading into _load_check_data
Definition saveload.cpp:92
SLA_PTRS
@ SLA_PTRS
fixing pointers
Definition saveload.cpp:90
SlCopyBytes
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:783
SlCopyInternal
static void SlCopyInternal(void *object, size_t length, VarType conv)
Internal function to save/Load a list of SL_VARs.
Definition saveload.cpp:1101
SaveOrLoad
SaveOrLoadResult SaveOrLoad(std::string_view 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:3258
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:1180
_savegame_type
SavegameType _savegame_type
type of savegame we are loading
Definition saveload.cpp:77
SlSaveLoadConv
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:862
_file_to_saveload
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:78
SlLoadCheckChunk
static void SlLoadCheckChunk(const ChunkHandler &ch)
Load a chunk of data for checking savegames.
Definition saveload.cpp:2201
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:731
SlReadByte
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:410
ClearOldOrders
void ClearOldOrders()
Clear all old orders.
Definition order_sl.cpp:114
_sl
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:218
DoExitSave
void DoExitSave()
Do a save when exiting the game (_settings_client.gui.autosave_on_exit)
Definition saveload.cpp:3367
SlLoadChunks
static void SlLoadChunks()
Load all chunks.
Definition saveload.cpp:2311
SlDeque
static void SlDeque(void *deque, VarType conv)
Save/load a std::deque.
Definition saveload.cpp:1528
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:636
SlSaveChunk
static void SlSaveChunk(const ChunkHandler &ch)
Save a chunk of data (eg.
Definition saveload.cpp:2252
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:1652
SlObject
void SlObject(void *object, const SaveLoadTable &slt)
Main SaveLoad function.
Definition saveload.cpp:1846
GetSaveLoadErrorMessage
EncodedString GetSaveLoadErrorMessage()
Return the description of the error.
Definition saveload.cpp:2973
SlTableHeader
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
Definition saveload.cpp:1899
AfterLoadGame
bool AfterLoadGame()
Perform a (large) amount of savegame conversion magic in order to load older savegames and to fill th...
Definition afterload.cpp:560
ReadValue
int64_t ReadValue(const void *ptr, VarType conv)
Return a signed-long version of the value of a setting.
Definition saveload.cpp:812
SlVector
static void SlVector(void *vector, VarType conv)
Save/load a std::vector.
Definition saveload.cpp:1586
SaveFileError
static void SaveFileError()
Show a gui message when saving has failed.
Definition saveload.cpp:2979
SlGlobList
void SlGlobList(const SaveLoadTable &slt)
Save or Load (a list of) global variables.
Definition saveload.cpp:2091
GetSavegameFormat
static std::pair< const SaveLoadFormat &, uint8_t > GetSavegameFormat(std::string_view full_name)
Return the savegameformat of the game.
Definition saveload.cpp:2849
FixSCCEncoded
void FixSCCEncoded(std::string &str, bool fix_code)
Scan the string for old values of SCC_ENCODED and fix it to it's new, value.
Definition saveload.cpp:933
SlSaveChunks
static void SlSaveChunks()
Save all chunks.
Definition saveload.cpp:2288
GenerateDefaultSaveName
std::string GenerateDefaultSaveName()
Get the default name for a savegame or screenshot.
Definition saveload.cpp:3375
MEMORY_CHUNK_SIZE
static const size_t MEMORY_CHUNK_SIZE
Save in chunks of 128 KiB.
Definition saveload.cpp:102
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:3097
SlAutolength
void SlAutolength(AutolengthProc *proc, int arg)
Do something of which I have no idea what it is :P.
Definition saveload.cpp:2101
SlReadString
void SlReadString(std::string &str, size_t length)
Read the given amount of bytes from the buffer into the string.
Definition saveload.cpp:1039
SlSetStructListLength
void SlSetStructListLength(size_t length)
Set the length of this list.
Definition saveload.cpp:1817
SlGetGammaLength
static uint SlGetGammaLength(size_t i)
Return how many bytes used to encode a gamma value.
Definition saveload.cpp:537
SlCalcVectorLen
static size_t SlCalcVectorLen(const void *vector, VarType conv)
Return the size in bytes of a std::vector.
Definition saveload.cpp:1559
SlRefList
static void SlRefList(void *list, VarType conv)
Save/Load a list.
Definition saveload.cpp:1457
SlCalcDequeLen
static size_t SlCalcDequeLen(const void *deque, VarType conv)
Return the size in bytes of a std::deque.
Definition saveload.cpp:1501
SavegameType
SavegameType
Types of save games.
Definition saveload.h:438
SGT_OTTD
@ SGT_OTTD
OTTD savegame.
Definition saveload.h:442
SaveOrLoadResult
SaveOrLoadResult
Save or load result codes.
Definition saveload.h:420
SL_OK
@ SL_OK
completed successfully
Definition saveload.h:421
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:423
SLE_VAR_NULL
@ SLE_VAR_NULL
useful to write zeros in savegame.
Definition saveload.h:667
SLE_FILE_END
@ SLE_FILE_END
Used to mark end-of-header in tables.
Definition saveload.h:640
SLE_FILE_TYPE_MASK
@ SLE_FILE_TYPE_MASK
Mask to get the file-type (and not any flags).
Definition saveload.h:654
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:655
SLF_REPLACE_TABCRLF
@ SLF_REPLACE_TABCRLF
Replace tabs, cr and lf in the string with spaces.
Definition saveload.h:704
SLF_ALLOW_NEWLINE
@ SLF_ALLOW_NEWLINE
Allow new lines in the strings.
Definition saveload.h:703
SLF_ALLOW_CONTROL
@ SLF_ALLOW_CONTROL
Allow control codes in the strings.
Definition saveload.h:702
SLE_VAR_STR
@ SLE_VAR_STR
string pointer
Definition saveload.h:668
SLE_VAR_NAME
@ SLE_VAR_NAME
old custom name to be converted to a string pointer
Definition saveload.h:670
SLE_VAR_STRQ
@ SLE_VAR_STRQ
string pointer enclosed in quotes
Definition saveload.h:669
SLE_FILE_STRINGID
@ SLE_FILE_STRINGID
StringID offset into strings-array.
Definition saveload.h:649
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:1362
GetVarFileType
constexpr VarType GetVarFileType(VarType type)
Get the FileType of a setting.
Definition saveload.h:777
SLRefType
SLRefType
Type of reference (SLE_REF, SLE_CONDREF).
Definition saveload.h:615
REF_VEHICLE_OLD
@ REF_VEHICLE_OLD
Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
Definition saveload.h:619
REF_LINK_GRAPH_JOB
@ REF_LINK_GRAPH_JOB
Load/save a reference to a link graph job.
Definition saveload.h:626
REF_TOWN
@ REF_TOWN
Load/save a reference to a town.
Definition saveload.h:618
REF_LINK_GRAPH
@ REF_LINK_GRAPH
Load/save a reference to a link graph.
Definition saveload.h:625
REF_CARGO_PACKET
@ REF_CARGO_PACKET
Load/save a reference to a cargo packet.
Definition saveload.h:622
REF_ENGINE_RENEWS
@ REF_ENGINE_RENEWS
Load/save a reference to an engine renewal (autoreplace).
Definition saveload.h:621
REF_STATION
@ REF_STATION
Load/save a reference to a station.
Definition saveload.h:617
REF_ORDERLIST
@ REF_ORDERLIST
Load/save a reference to an orderlist.
Definition saveload.h:623
REF_STORAGE
@ REF_STORAGE
Load/save a reference to a persistent storage.
Definition saveload.h:624
REF_VEHICLE
@ REF_VEHICLE
Load/save a reference to a vehicle.
Definition saveload.h:616
REF_ROADSTOPS
@ REF_ROADSTOPS
Load/save a reference to a bus/truck stop.
Definition saveload.h:620
GetVariableAddress
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1316
ChunkHandlerTable
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:527
SaveLoadType
SaveLoadType
Type of data saved.
Definition saveload.h:710
SL_NULL
@ SL_NULL
Save null-bytes and load to nowhere.
Definition saveload.h:724
SL_STRUCTLIST
@ SL_STRUCTLIST
Save/load a list of structs.
Definition saveload.h:721
SL_STDSTR
@ SL_STDSTR
Save/load a std::string.
Definition saveload.h:715
SL_REF
@ SL_REF
Save/load a reference.
Definition saveload.h:712
SL_SAVEBYTE
@ SL_SAVEBYTE
Save (but not load) a byte.
Definition saveload.h:723
SL_DEQUE
@ SL_DEQUE
Save/load a deque of SL_VAR elements.
Definition saveload.h:718
SL_STRUCT
@ SL_STRUCT
Save/load a struct.
Definition saveload.h:713
SL_VECTOR
@ SL_VECTOR
Save/load a vector of SL_VAR elements.
Definition saveload.h:719
SL_REFVECTOR
@ SL_REFVECTOR
Save/load a vector of SL_REF elements.
Definition saveload.h:726
SL_REFLIST
@ SL_REFLIST
Save/load a list of SL_REF elements.
Definition saveload.h:720
SL_ARR
@ SL_ARR
Save/load a fixed-size array of SL_VAR elements.
Definition saveload.h:717
SL_VAR
@ SL_VAR
Save/load a variable.
Definition saveload.h:711
SaveLoadCompatTable
std::span< const struct SaveLoadCompat > SaveLoadCompatTable
A table of SaveLoadCompat entries.
Definition saveload.h:533
IsSavegameVersionBefore
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1278
GetVarMemType
constexpr VarType GetVarMemType(VarType type)
Get the NumberType of a setting.
Definition saveload.h:766
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_FIX_SCC_ENCODED_NEGATIVE
@ SLV_FIX_SCC_ENCODED_NEGATIVE
353 PR#14049 Fix encoding of negative parameters.
Definition saveload.h:403
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:416
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_ENCODED_STRING_FORMAT
@ SLV_ENCODED_STRING_FORMAT
350 PR#13499 Encoded String format changed.
Definition saveload.h:400
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:1899
SaveLoadTable
std::span< const struct SaveLoad > SaveLoadTable
A table of SaveLoad entries.
Definition saveload.h:530
CH_TYPE_MASK
@ CH_TYPE_MASK
All ChunkType values have to be within this mask.
Definition saveload.h:473
CH_READONLY
@ CH_READONLY
Chunk is never saved.
Definition saveload.h:474
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:28
CopyFromOldName
std::string CopyFromOldName(StringID id)
Copy and convert old custom names to UTF-8.
Definition strings_sl.cpp:59
_settings_game
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
GetSaveLoadSettingTable
SettingTable GetSaveLoadSettingTable()
Create a single table with all settings that should be stored/loaded in the savegame.
Definition settings.cpp:1678
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
SettingFlag::NotInSave
@ NotInSave
Do not save with savegame, basically client-based.
SettingFlag::NoNetworkSync
@ NoNetworkSync
Do not synchronize over network (but it is saved if SettingFlag::NotInSave is not set).
GetSettingDesc
static constexpr const SettingDesc * GetSettingDesc(const SettingVariant &desc)
Helper to convert the type of the iterated settings description to a pointer to it.
Definition settings_internal.h:384
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:271
StrMakeValidInPlace
void StrMakeValidInPlace(char *str, StringValidationSettings settings)
Scans the string for invalid characters and replaces them with a question mark '?' (if not ignored).
Definition string.cpp:157
StringValidationSetting::ReplaceWithQuestionMark
@ ReplaceWithQuestionMark
Replace the unknown/bad bits with question marks.
StringValidationSetting::AllowControlCode
@ AllowControlCode
Allow the special control codes.
StringValidationSetting::AllowNewline
@ AllowNewline
Allow newlines; replaces '\r ' with ' ' during processing.
StringValidationSetting::ReplaceTabCrNlWithSpace
@ ReplaceTabCrNlWithSpace
Replace tabs ('\t'), carriage returns ('\r') and newlines (' ') with spaces.
GetStringWithArgs
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
GetEncodedString
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition strings_type.h:19
CargoPacket
Container for cargo from the same location and time.
Definition cargopacket.h:41
ChunkHandler
Handlers and description of chunk.
Definition saveload.h:478
ChunkHandler::type
ChunkType type
Type of the chunk.
Definition saveload.h:480
ChunkHandler::LoadCheck
virtual void LoadCheck(size_t len=0) const
Load the chunk for game preview.
Definition saveload.cpp:2125
ChunkHandler::Load
virtual void Load() const =0
Load the chunk.
ChunkHandler::id
uint32_t id
Unique ID (4 letters).
Definition saveload.h:479
ChunkHandler::Save
virtual void Save() const
Save the chunk.
Definition saveload.h:490
ClientSettings::gui
GUISettings gui
settings related to the GUI
Definition settings_type.h:650
Company
Definition company_base.h:127
EngineRenew
Struct to store engine replacements.
Definition autoreplace_base.h:33
FileReader
Yes, simply reading from a file.
Definition saveload.cpp:2355
FileReader::~FileReader
~FileReader()
Make sure everything is cleaned up.
Definition saveload.cpp:2368
FileReader::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2375
FileReader::Reset
void Reset() override
Reset this filter to read from the beginning of the file.
Definition saveload.cpp:2383
FileReader::FileReader
FileReader(FileHandle &&file)
Create the file reader, so it reads from a specific file.
Definition saveload.cpp:2363
FileReader::begin
long begin
The begin of the file.
Definition saveload.cpp:2357
FileReader::file
std::optional< FileHandle > file
The file to read from.
Definition saveload.cpp:2356
FileToSaveLoad
Deals with the type of the savegame, independent of extension.
Definition saveload.h:427
FileToSaveLoad::SetMode
void SetMode(const FiosType &ft, SaveLoadOperation fop=SLO_LOAD)
Set the mode and file type of the file to save or load.
Definition saveload.cpp:3421
FileToSaveLoad::ftype
FiosType ftype
File type.
Definition saveload.h:429
FileToSaveLoad::file_op
SaveLoadOperation file_op
File operation to perform.
Definition saveload.h:428
FileToSaveLoad::name
std::string name
Name of the file.
Definition saveload.h:430
FileToSaveLoad::title
EncodedString title
Internal name of the game.
Definition saveload.h:431
FileToSaveLoad::Set
void Set(const FiosItem &item)
Set the title of the file.
Definition saveload.cpp:3437
FileWriter
Yes, simply writing to a file.
Definition saveload.cpp:2393
FileWriter::file
std::optional< FileHandle > file
The file to write to.
Definition saveload.cpp:2394
FileWriter::FileWriter
FileWriter(FileHandle &&file)
Create the file writer, so it writes to a specific file.
Definition saveload.cpp:2400
FileWriter::Finish
void Finish() override
Prepare everything to finish writing the savegame.
Definition saveload.cpp:2418
FileWriter::~FileWriter
~FileWriter()
Make sure everything is cleaned up.
Definition saveload.cpp:2405
FileWriter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2410
FiosItem
Deals with finding savegames.
Definition fios.h:78
FiosNumberedSaveName
A savegame name automatically numbered.
Definition fios.h:128
FiosNumberedSaveName::Filename
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
Definition fios.cpp:738
FiosNumberedSaveName::Extension
std::string Extension()
Generate an extension for a savegame name.
Definition fios.cpp:748
FiosType
Elements of a file system that are recognized.
Definition fileio_type.h:63
FiosType::abstract
AbstractFileType abstract
Abstract file type.
Definition fileio_type.h:64
GUISettings::keep_all_autosave
bool keep_all_autosave
name the autosave in a different way
Definition settings_type.h:196
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:199
GUISettings::threaded_saves
bool threaded_saves
should we do threaded saves?
Definition settings_type.h:195
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:2688
LZMALoadFilter::~LZMALoadFilter
~LZMALoadFilter()
Clean everything up.
Definition saveload.cpp:2703
LZMALoadFilter::lzma
lzma_stream lzma
Stream state that we are reading from.
Definition saveload.cpp:2689
LZMALoadFilter::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2708
LZMALoadFilter::fread_buf
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
Definition saveload.cpp:2690
LZMALoadFilter::LZMALoadFilter
LZMALoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2696
LZMASaveFilter
Filter using LZMA compression.
Definition saveload.cpp:2731
LZMASaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2777
LZMASaveFilter::Finish
void Finish() override
Prepare everything to finish writing the savegame.
Definition saveload.cpp:2782
LZMASaveFilter::WriteLoop
void WriteLoop(uint8_t *p, size_t len, lzma_action action)
Helper loop for writing the data.
Definition saveload.cpp:2757
LZMASaveFilter::LZMASaveFilter
LZMASaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
Definition saveload.cpp:2740
LZMASaveFilter::lzma
lzma_stream lzma
Stream state that we are writing to.
Definition saveload.cpp:2732
LZMASaveFilter::fwrite_buf
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
Definition saveload.cpp:2733
LZMASaveFilter::~LZMASaveFilter
~LZMASaveFilter()
Clean up what we allocated.
Definition saveload.cpp:2746
LZOLoadFilter
Filter using LZO compression.
Definition saveload.cpp:2437
LZOLoadFilter::LZOLoadFilter
LZOLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2442
LZOLoadFilter::Read
size_t Read(uint8_t *buf, size_t ssize) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2447
LZOSaveFilter
Filter using LZO compression.
Definition saveload.cpp:2484
LZOSaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2494
LZOSaveFilter::LZOSaveFilter
LZOSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
Definition saveload.cpp:2489
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 string parameters 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::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:48
LoadCheckData::grfconfig
GRFConfigList grfconfig
NewGrf configuration from save.
Definition fios.h:47
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:146
MemoryDumper::buf
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:148
MemoryDumper::WriteByte
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:155
MemoryDumper::blocks
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:147
MemoryDumper::bufe
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:149
MemoryDumper::GetSize
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:189
MemoryDumper::Flush
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:170
NoCompLoadFilter
Filter without any compression.
Definition saveload.cpp:2524
NoCompLoadFilter::NoCompLoadFilter
NoCompLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2529
NoCompLoadFilter::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2533
NoCompSaveFilter
Filter without any compression.
Definition saveload.cpp:2540
NoCompSaveFilter::NoCompSaveFilter
NoCompSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
Definition saveload.cpp:2545
NoCompSaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2549
OrderList
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:274
PersistentStorage
Class for pooled persistent storage of data.
Definition newgrf_storage.h:199
PoolID< uint8_t, struct CompanyIDTag, 0xF, 0xFF >
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:436
Pool::PoolItem<&_orderlist_pool >::Get
static Titem * Get(auto index)
Returns Titem with given index.
Definition pool_type.hpp:387
Pool::PoolItem<&_orderlist_pool >::IsValidID
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition pool_type.hpp:376
ReadBuffer
A buffer for reading (and buffering) savegame data.
Definition saveload.cpp:105
ReadBuffer::bufp
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:107
ReadBuffer::ReadBuffer
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:116
ReadBuffer::read
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:110
ReadBuffer::GetSize
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:138
ReadBuffer::reader
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:109
ReadBuffer::buf
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:106
ReadBuffer::bufe
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:108
RoadStop
A Stop for a Road Vehicle.
Definition roadstop_base.h:21
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:2796
SaveLoadFormat::tag
uint32_t tag
the 4-letter tag by which it is identified in the savegame
Definition saveload.cpp:2798
SaveLoadFormat::min_compression
uint8_t min_compression
the minimum compression level of this format
Definition saveload.cpp:2803
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:2801
SaveLoadFormat::default_compression
uint8_t default_compression
the default compression level of this format
Definition saveload.cpp:2804
SaveLoadFormat::init_load
std::shared_ptr< LoadFilter >(* init_load)(std::shared_ptr< LoadFilter > chain)
Constructor for the load filter.
Definition saveload.cpp:2800
SaveLoadFormat::name
std::string_view name
name of the compressor/decompressor (debug-only)
Definition saveload.cpp:2797
SaveLoadFormat::max_compression
uint8_t max_compression
the maximum compression level of this format
Definition saveload.cpp:2805
SaveLoadParams
The saveload struct, containing reader-writer functions, buffer, version, etc.
Definition saveload.cpp:196
SaveLoadParams::reader
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:209
SaveLoadParams::sf
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:207
SaveLoadParams::dumper
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:206
SaveLoadParams::error_str
StringID error_str
the translatable error message to show
Definition saveload.cpp:212
SaveLoadParams::action
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:197
SaveLoadParams::extra_msg
std::string extra_msg
the error message
Definition saveload.cpp:213
SaveLoadParams::need_length
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:198
SaveLoadParams::block_mode
uint8_t block_mode
???
Definition saveload.cpp:199
SaveLoadParams::saveinprogress
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:215
SaveLoadParams::lf
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:210
SaveLoadParams::expect_table_header
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:204
SaveLoadParams::obj_len
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:202
SaveLoadParams::error
bool error
did an error occur or not
Definition saveload.cpp:200
SaveLoadParams::last_array_index
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:203
SaveLoad
SaveLoad type struct.
Definition saveload.h:732
SaveLoad::length
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:736
SaveLoad::handler
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:741
SaveLoad::version_to
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:738
SaveLoad::cmd
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:734
SaveLoad::name
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:733
SaveLoad::conv
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:735
SaveLoad::version_from
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:737
SettingDesc
Properties of config file settings.
Definition settings_internal.h:71
SettingDesc::flags
SettingFlags flags
Handles how a setting would show up in the GUI (text/currency, etc.).
Definition settings_internal.h:76
SettingDesc::ResetToDefault
virtual void ResetToDefault(void *object) const =0
Reset the setting to its default value.
SpecializedStation< Station, false >::IsValidID
static bool IsValidID(auto index)
Tests whether given index is a valid index for station of this type.
Definition base_station_base.h:239
SpecializedStation< Station, false >::Get
static Station * Get(auto index)
Gets station with given index.
Definition base_station_base.h:248
Station
Station data structure.
Definition station_base.h:520
Town
Town data structure.
Definition town.h:63
Vehicle
Vehicle data structure.
Definition vehicle_base.h:205
ZlibLoadFilter
Filter using Zlib compression.
Definition saveload.cpp:2562
ZlibLoadFilter::Read
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Definition saveload.cpp:2581
ZlibLoadFilter::ZlibLoadFilter
ZlibLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Definition saveload.cpp:2570
ZlibLoadFilter::fread_buf
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
Definition saveload.cpp:2564
ZlibLoadFilter::~ZlibLoadFilter
~ZlibLoadFilter()
Clean everything up.
Definition saveload.cpp:2576
ZlibLoadFilter::z
z_stream z
Stream state we are reading from.
Definition saveload.cpp:2563
ZlibSaveFilter
Filter using Zlib compression.
Definition saveload.cpp:2605
ZlibSaveFilter::WriteLoop
void WriteLoop(uint8_t *p, size_t len, int mode)
Helper loop for writing the data.
Definition saveload.cpp:2631
ZlibSaveFilter::z
z_stream z
Stream state we are writing to.
Definition saveload.cpp:2606
ZlibSaveFilter::fwrite_buf
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
Definition saveload.cpp:2607
ZlibSaveFilter::Finish
void Finish() override
Prepare everything to finish writing the savegame.
Definition saveload.cpp:2664
ZlibSaveFilter::~ZlibSaveFilter
~ZlibSaveFilter()
Clean up what we allocated.
Definition saveload.cpp:2620
ZlibSaveFilter::ZlibSaveFilter
ZlibSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
Definition saveload.cpp:2614
ZlibSaveFilter::Write
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Definition saveload.cpp:2659
CSleep
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Definition thread.h:24
StartNewThread
bool StartNewThread(std::thread *thr, std::string_view 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:3297
WC_STATUS_BAR
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:69