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