OpenTTD Source 20260531-master-g0e951f3528
saveload.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
22
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"
34#include "../vehicle_base.h"
35#include "../company_func.h"
37#include "../autoreplace_base.h"
38#include "../roadstop_base.h"
41#include "../statusbar_gui.h"
42#include "../fileio_func.h"
43#include "../gamelog.h"
44#include "../string_func.h"
45#include "../fios.h"
46#include "../error.h"
47#include "../strings_type.h"
48#include "../newgrf_railtype.h"
49#include "../newgrf_roadtype.h"
51#include "saveload_internal.h"
52#include "saveload_filter.h"
53
54#include <atomic>
55#ifdef __EMSCRIPTEN__
56# include <emscripten.h>
57#endif
58
59#ifdef WITH_LZO
60#include <lzo/lzo1x.h>
61#endif
62
63#if defined(WITH_ZLIB)
64#include <zlib.h>
65#endif /* WITH_ZLIB */
66
67#if defined(WITH_LIBLZMA)
68#include <lzma.h>
69#endif /* WITH_LIBLZMA */
70
71#include "table/strings.h"
72
73#include "../safeguards.h"
74
76
79
80uint32_t _ttdp_version;
83std::string _savegame_format;
85
94
95enum NeedLength : uint8_t {
96 NL_NONE = 0,
99};
100
102static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
103
107 uint8_t *bufp = nullptr;
108 uint8_t *bufe = nullptr;
109 std::shared_ptr<LoadFilter> reader{};
110 size_t read = 0;
111
116 ReadBuffer(std::shared_ptr<LoadFilter> reader) : reader(std::move(reader))
117 {
118 }
119
120 inline uint8_t ReadByte()
121 {
122 if (this->bufp == this->bufe) {
123 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
124 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
125
126 this->read += len;
127 this->bufp = this->buf;
128 this->bufe = this->buf + len;
129 }
130
131 return *this->bufp++;
132 }
133
138 size_t GetSize() const
139 {
140 return this->read - (this->bufe - this->bufp);
141 }
142};
143
144
147 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
148 uint8_t *buf = nullptr;
149 uint8_t *bufe = nullptr;
150
155 inline void WriteByte(uint8_t b)
156 {
157 /* Are we at the end of this chunk? */
158 if (this->buf == this->bufe) {
159 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
160 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
161 }
162
163 *this->buf++ = b;
164 }
165
170 void Flush(std::shared_ptr<SaveFilter> writer)
171 {
172 uint i = 0;
173 size_t t = this->GetSize();
174
175 while (t > 0) {
176 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
177
178 writer->Write(this->blocks[i++].get(), to_write);
179 t -= to_write;
180 }
181
182 writer->Finish();
183 }
184
189 size_t GetSize() const
190 {
191 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
192 }
193};
194
199 uint8_t block_mode;
200 bool error;
201
202 size_t obj_len;
203 int array_index, last_array_index;
205
206 std::unique_ptr<MemoryDumper> dumper;
207 std::shared_ptr<SaveFilter> sf;
208
209 std::unique_ptr<ReadBuffer> reader;
210 std::shared_ptr<LoadFilter> lf;
211
213 std::string extra_msg;
214
216};
217
219
220static const std::vector<ChunkHandlerRef> &ChunkHandlers()
221{
222 /* These define the chunks */
223 extern const ChunkHandlerTable _gamelog_chunk_handlers;
224 extern const ChunkHandlerTable _map_chunk_handlers;
225 extern const ChunkHandlerTable _misc_chunk_handlers;
226 extern const ChunkHandlerTable _name_chunk_handlers;
227 extern const ChunkHandlerTable _cheat_chunk_handlers;
228 extern const ChunkHandlerTable _setting_chunk_handlers;
229 extern const ChunkHandlerTable _company_chunk_handlers;
230 extern const ChunkHandlerTable _engine_chunk_handlers;
231 extern const ChunkHandlerTable _veh_chunk_handlers;
232 extern const ChunkHandlerTable _waypoint_chunk_handlers;
233 extern const ChunkHandlerTable _depot_chunk_handlers;
234 extern const ChunkHandlerTable _order_chunk_handlers;
235 extern const ChunkHandlerTable _town_chunk_handlers;
236 extern const ChunkHandlerTable _sign_chunk_handlers;
237 extern const ChunkHandlerTable _station_chunk_handlers;
238 extern const ChunkHandlerTable _industry_chunk_handlers;
239 extern const ChunkHandlerTable _economy_chunk_handlers;
240 extern const ChunkHandlerTable _subsidy_chunk_handlers;
241 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
242 extern const ChunkHandlerTable _goal_chunk_handlers;
243 extern const ChunkHandlerTable _story_page_chunk_handlers;
244 extern const ChunkHandlerTable _league_chunk_handlers;
245 extern const ChunkHandlerTable _ai_chunk_handlers;
246 extern const ChunkHandlerTable _game_chunk_handlers;
247 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
248 extern const ChunkHandlerTable _newgrf_chunk_handlers;
249 extern const ChunkHandlerTable _group_chunk_handlers;
250 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
251 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
252 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
253 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
254 extern const ChunkHandlerTable _airport_chunk_handlers;
255 extern const ChunkHandlerTable _object_chunk_handlers;
256 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
257 extern const ChunkHandlerTable _water_region_chunk_handlers;
258 extern const ChunkHandlerTable _randomizer_chunk_handlers;
259
261 static const ChunkHandlerTable _chunk_handler_tables[] = {
262 _gamelog_chunk_handlers,
263 _map_chunk_handlers,
264 _misc_chunk_handlers,
265 _name_chunk_handlers,
266 _cheat_chunk_handlers,
267 _setting_chunk_handlers,
268 _veh_chunk_handlers,
269 _waypoint_chunk_handlers,
270 _depot_chunk_handlers,
271 _order_chunk_handlers,
272 _industry_chunk_handlers,
273 _economy_chunk_handlers,
274 _subsidy_chunk_handlers,
275 _cargomonitor_chunk_handlers,
276 _goal_chunk_handlers,
277 _story_page_chunk_handlers,
278 _league_chunk_handlers,
279 _engine_chunk_handlers,
280 _town_chunk_handlers,
281 _sign_chunk_handlers,
282 _station_chunk_handlers,
283 _company_chunk_handlers,
284 _ai_chunk_handlers,
285 _game_chunk_handlers,
286 _animated_tile_chunk_handlers,
287 _newgrf_chunk_handlers,
288 _group_chunk_handlers,
289 _cargopacket_chunk_handlers,
290 _autoreplace_chunk_handlers,
291 _labelmaps_chunk_handlers,
292 _linkgraph_chunk_handlers,
293 _airport_chunk_handlers,
294 _object_chunk_handlers,
295 _persistent_storage_chunk_handlers,
296 _water_region_chunk_handlers,
297 _randomizer_chunk_handlers,
298 };
299
300 static std::vector<ChunkHandlerRef> _chunk_handlers;
301
302 if (_chunk_handlers.empty()) {
303 for (auto &chunk_handler_table : _chunk_handler_tables) {
304 for (auto &chunk_handler : chunk_handler_table) {
305 _chunk_handlers.push_back(chunk_handler);
306 }
307 }
308 }
309
310 return _chunk_handlers;
311}
312
314static void SlNullPointers()
315{
316 _sl.action = SLA_NULL;
317
318 /* We don't want any savegame conversion code to run
319 * during NULLing; especially those that try to get
320 * pointers from other pools. */
322
323 for (const ChunkHandler &ch : ChunkHandlers()) {
324 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
325 ch.FixPointers();
326 }
327
328 assert(_sl.action == SLA_NULL);
329}
330
339[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
340{
341 /* Distinguish between loading into _load_check_data vs. normal save/load. */
342 if (_sl.action == SLA_LOAD_CHECK) {
343 _load_check_data.error = string;
344 _load_check_data.error_msg = extra_msg;
345 } else {
346 _sl.error_str = string;
347 _sl.extra_msg = extra_msg;
348 }
349
350 /* We have to nullptr all pointers here; we might be in a state where
351 * the pointers are actually filled with indices, which means that
352 * when we access them during cleaning the pool dereferences of
353 * those indices will be made with segmentation faults as result. */
354 if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
355
356 /* Logging could be active. */
357 _gamelog.StopAnyAction();
358
359 throw std::exception();
360}
361
369[[noreturn]] void SlErrorCorrupt(const std::string &msg)
370{
371 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
372}
373
374
375typedef void (*AsyncSaveFinishProc)();
376static std::atomic<AsyncSaveFinishProc> _async_save_finish;
377static std::thread _save_thread;
378
384{
385 if (_exit_game) return;
386 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
387
388 _async_save_finish.store(proc, std::memory_order_release);
389}
390
395{
396 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
397 if (proc == nullptr) return;
398
399 proc();
400
401 if (_save_thread.joinable()) {
402 _save_thread.join();
403 }
404}
405
410uint8_t SlReadByte()
411{
412 return _sl.reader->ReadByte();
413}
414
419void SlWriteByte(uint8_t b)
420{
421 _sl.dumper->WriteByte(b);
422}
423
424static inline int SlReadUint16()
425{
426 int x = SlReadByte() << 8;
427 return x | SlReadByte();
428}
429
430static inline uint32_t SlReadUint32()
431{
432 uint32_t x = SlReadUint16() << 16;
433 return x | SlReadUint16();
434}
435
436static inline uint64_t SlReadUint64()
437{
438 uint32_t x = SlReadUint32();
439 uint32_t y = SlReadUint32();
440 return static_cast<uint64_t>(x) << 32 | y;
441}
442
443static inline void SlWriteUint16(uint16_t v)
444{
445 SlWriteByte(GB(v, 8, 8));
446 SlWriteByte(GB(v, 0, 8));
447}
448
449static inline void SlWriteUint32(uint32_t v)
450{
451 SlWriteUint16(GB(v, 16, 16));
452 SlWriteUint16(GB(v, 0, 16));
453}
454
455static inline void SlWriteUint64(uint64_t x)
456{
457 SlWriteUint32(static_cast<uint32_t>(x >> 32));
458 SlWriteUint32(static_cast<uint32_t>(x));
459}
460
470static uint SlReadSimpleGamma()
471{
472 uint i = SlReadByte();
473 if (HasBit(i, 7)) {
474 i &= ~0x80;
475 if (HasBit(i, 6)) {
476 i &= ~0x40;
477 if (HasBit(i, 5)) {
478 i &= ~0x20;
479 if (HasBit(i, 4)) {
480 i &= ~0x10;
481 if (HasBit(i, 3)) {
482 SlErrorCorrupt("Unsupported gamma");
483 }
484 i = SlReadByte(); // 32 bits only.
485 }
486 i = (i << 8) | SlReadByte();
487 }
488 i = (i << 8) | SlReadByte();
489 }
490 i = (i << 8) | SlReadByte();
491 }
492 return i;
493}
494
511
512static void SlWriteSimpleGamma(size_t i)
513{
514 if (i >= (1 << 7)) {
515 if (i >= (1 << 14)) {
516 if (i >= (1 << 21)) {
517 if (i >= (1 << 28)) {
518 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
519 SlWriteByte(static_cast<uint8_t>(0xF0));
520 SlWriteByte(static_cast<uint8_t>(i >> 24));
521 } else {
522 SlWriteByte(static_cast<uint8_t>(0xE0 | (i >> 24)));
523 }
524 SlWriteByte(static_cast<uint8_t>(i >> 16));
525 } else {
526 SlWriteByte(static_cast<uint8_t>(0xC0 | (i >> 16)));
527 }
528 SlWriteByte(static_cast<uint8_t>(i >> 8));
529 } else {
530 SlWriteByte(static_cast<uint8_t>(0x80 | (i >> 8)));
531 }
532 }
533 SlWriteByte(static_cast<uint8_t>(i));
534}
535
541static inline uint SlGetGammaLength(size_t i)
542{
543 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
544}
545
546static inline uint SlReadSparseIndex()
547{
548 return SlReadSimpleGamma();
549}
550
551static inline void SlWriteSparseIndex(uint index)
552{
553 SlWriteSimpleGamma(index);
554}
555
556static inline uint SlReadArrayLength()
557{
558 return SlReadSimpleGamma();
559}
560
561static inline void SlWriteArrayLength(size_t length)
562{
563 SlWriteSimpleGamma(length);
564}
565
566static inline uint SlGetArrayLength(size_t length)
567{
568 return SlGetGammaLength(length);
569}
570
576static uint8_t GetSavegameFileType(const SaveLoad &sld)
577{
578 switch (sld.cmd) {
579 case SL_VAR:
580 return GetVarFileType(sld.conv); break;
581
582 case SL_STDSTR:
583 case SL_ARR:
584 case SL_VECTOR:
585 return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
586
587 case SL_REF:
588 return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
589
590 case SL_REFLIST:
591 case SL_REFVECTOR:
592 return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
593
594 case SL_SAVEBYTE:
595 return SLE_FILE_U8;
596
597 case SL_STRUCT:
598 case SL_STRUCTLIST:
599 return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
600
601 default: NOT_REACHED();
602 }
603}
604
611static inline uint SlCalcConvMemLen(VarType conv)
612{
613 switch (GetVarMemType(conv)) {
614 case SLE_VAR_BL: return sizeof(bool);
615 case SLE_VAR_I8: return sizeof(int8_t);
616 case SLE_VAR_U8: return sizeof(uint8_t);
617 case SLE_VAR_I16: return sizeof(int16_t);
618 case SLE_VAR_U16: return sizeof(uint16_t);
619 case SLE_VAR_I32: return sizeof(int32_t);
620 case SLE_VAR_U32: return sizeof(uint32_t);
621 case SLE_VAR_I64: return sizeof(int64_t);
622 case SLE_VAR_U64: return sizeof(uint64_t);
623 case SLE_VAR_NULL: return 0;
624
625 case SLE_VAR_STR:
626 case SLE_VAR_STRQ:
627 return SlReadArrayLength();
628
629 case SLE_VAR_NAME:
630 default:
631 NOT_REACHED();
632 }
633}
634
641static inline uint8_t SlCalcConvFileLen(VarType conv)
642{
643 switch (GetVarFileType(conv)) {
644 case SLE_FILE_END: return 0;
645 case SLE_FILE_I8: return sizeof(int8_t);
646 case SLE_FILE_U8: return sizeof(uint8_t);
647 case SLE_FILE_I16: return sizeof(int16_t);
648 case SLE_FILE_U16: return sizeof(uint16_t);
649 case SLE_FILE_I32: return sizeof(int32_t);
650 case SLE_FILE_U32: return sizeof(uint32_t);
651 case SLE_FILE_I64: return sizeof(int64_t);
652 case SLE_FILE_U64: return sizeof(uint64_t);
653 case SLE_FILE_STRINGID: return sizeof(uint16_t);
654
655 case SLE_FILE_STRING:
656 return SlReadArrayLength();
657
658 case SLE_FILE_STRUCT:
659 default:
660 NOT_REACHED();
661 }
662}
663
668static inline size_t SlCalcRefLen()
669{
670 return IsSavegameVersionBefore(SLV_69) ? 2 : 4;
671}
672
673void SlSetArrayIndex(uint index)
674{
675 _sl.need_length = NL_WANTLENGTH;
676 _sl.array_index = index;
677}
678
679static size_t _next_offs;
680
686{
687 /* After reading in the whole array inside the loop
688 * we must have read in all the data, so we must be at end of current block. */
689 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
690 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
691 }
692
693 for (;;) {
694 uint length = SlReadArrayLength();
695 if (length == 0) {
696 assert(!_sl.expect_table_header);
697 _next_offs = 0;
698 return -1;
699 }
700
701 _sl.obj_len = --length;
702 _next_offs = _sl.reader->GetSize() + length;
703
704 if (_sl.expect_table_header) {
705 _sl.expect_table_header = false;
706 return INT32_MAX;
707 }
708
709 int index;
710 switch (_sl.block_mode) {
711 case CH_SPARSE_TABLE:
712 case CH_SPARSE_ARRAY: index = static_cast<int>(SlReadSparseIndex()); break;
713 case CH_TABLE:
714 case CH_ARRAY: index = _sl.array_index++; break;
715 default:
716 Debug(sl, 0, "SlIterateArray error");
717 return -1; // error
718 }
719
720 if (length != 0) return index;
721 }
722}
723
728{
729 while (SlIterateArray() != -1) {
730 SlSkipBytes(_next_offs - _sl.reader->GetSize());
731 }
732}
733
739void SlSetLength(size_t length)
740{
741 assert(_sl.action == SLA_SAVE);
742
743 switch (_sl.need_length) {
744 case NL_WANTLENGTH:
745 _sl.need_length = NL_NONE;
746 if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) {
747 _sl.expect_table_header = false;
748 SlWriteArrayLength(length + 1);
749 break;
750 }
751
752 switch (_sl.block_mode) {
753 case CH_RIFF:
754 /* Ugly encoding of >16M RIFF chunks
755 * The lower 24 bits are normal
756 * The uppermost 4 bits are bits 24:27 */
757 assert(length < (1 << 28));
758 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
759 break;
760 case CH_TABLE:
761 case CH_ARRAY:
762 assert(_sl.last_array_index <= _sl.array_index);
763 while (++_sl.last_array_index <= _sl.array_index) {
764 SlWriteArrayLength(1);
765 }
766 SlWriteArrayLength(length + 1);
767 break;
768 case CH_SPARSE_TABLE:
769 case CH_SPARSE_ARRAY:
770 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
771 SlWriteSparseIndex(_sl.array_index);
772 break;
773 default: NOT_REACHED();
774 }
775 break;
776
777 case NL_CALCLENGTH:
778 _sl.obj_len += static_cast<int>(length);
779 break;
780
781 default: NOT_REACHED();
782 }
783}
784
791static void SlCopyBytes(void *ptr, size_t length)
792{
793 uint8_t *p = static_cast<uint8_t *>(ptr);
794
795 switch (_sl.action) {
796 case SLA_LOAD_CHECK:
797 case SLA_LOAD:
798 for (; length != 0; length--) *p++ = SlReadByte();
799 break;
800 case SLA_SAVE:
801 for (; length != 0; length--) SlWriteByte(*p++);
802 break;
803 default: NOT_REACHED();
804 }
805}
806
812{
813 return _sl.obj_len;
814}
815
823int64_t ReadValue(const void *ptr, VarType conv)
824{
825 switch (GetVarMemType(conv)) {
826 case SLE_VAR_BL: return (*static_cast<const bool *>(ptr) != 0);
827 case SLE_VAR_I8: return *static_cast<const int8_t *>(ptr);
828 case SLE_VAR_U8: return *static_cast<const uint8_t *>(ptr);
829 case SLE_VAR_I16: return *static_cast<const int16_t *>(ptr);
830 case SLE_VAR_U16: return *static_cast<const uint16_t *>(ptr);
831 case SLE_VAR_I32: return *static_cast<const int32_t *>(ptr);
832 case SLE_VAR_U32: return *static_cast<const uint32_t *>(ptr);
833 case SLE_VAR_I64: return *static_cast<const int64_t *>(ptr);
834 case SLE_VAR_U64: return *static_cast<const uint64_t *>(ptr);
835 case SLE_VAR_NULL: return 0;
836 default: NOT_REACHED();
837 }
838}
839
847void WriteValue(void *ptr, VarType conv, int64_t val)
848{
849 switch (GetVarMemType(conv)) {
850 case SLE_VAR_BL: *static_cast<bool *>(ptr) = (val != 0); break;
851 case SLE_VAR_I8: *static_cast<int8_t *>(ptr) = val; break;
852 case SLE_VAR_U8: *static_cast<uint8_t *>(ptr) = val; break;
853 case SLE_VAR_I16: *static_cast<int16_t *>(ptr) = val; break;
854 case SLE_VAR_U16: *static_cast<uint16_t *>(ptr) = val; break;
855 case SLE_VAR_I32: *static_cast<int32_t *>(ptr) = val; break;
856 case SLE_VAR_U32: *static_cast<uint32_t *>(ptr) = val; break;
857 case SLE_VAR_I64: *static_cast<int64_t *>(ptr) = val; break;
858 case SLE_VAR_U64: *static_cast<uint64_t *>(ptr) = val; break;
859 case SLE_VAR_NAME: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(val); break;
860 case SLE_VAR_NULL: break;
861 default: NOT_REACHED();
862 }
863}
864
873static void SlSaveLoadConv(void *ptr, VarType conv)
874{
875 switch (_sl.action) {
876 case SLA_SAVE: {
877 int64_t x = ReadValue(ptr, conv);
878
879 /* Write the value to the file and check if its value is in the desired range */
880 switch (GetVarFileType(conv)) {
881 case SLE_FILE_I8:
882 assert(x >= -128 && x <= 127);
883 SlWriteByte(x);
884 break;
885
886 case SLE_FILE_U8:
887 assert(x >= 0 && x <= 255);
888 SlWriteByte(x);
889 break;
890
891 case SLE_FILE_I16:
892 assert(x >= -32768 && x <= 32767);
893 SlWriteUint16(x);
894 break;
895
897 case SLE_FILE_U16:
898 assert(x >= 0 && x <= 65535);
899 SlWriteUint16(x);
900 break;
901
902 case SLE_FILE_I32:
903 case SLE_FILE_U32:
904 SlWriteUint32(static_cast<uint32_t>(x));
905 break;
906
907 case SLE_FILE_I64:
908 case SLE_FILE_U64:
909 SlWriteUint64(x);
910 break;
911
912 default: NOT_REACHED();
913 }
914 break;
915 }
916 case SLA_LOAD_CHECK:
917 case SLA_LOAD: {
918 int64_t x;
919 /* Read a value from the file */
920 switch (GetVarFileType(conv)) {
921 case SLE_FILE_I8: x = static_cast<int8_t>(SlReadByte()); break;
922 case SLE_FILE_U8: x = static_cast<uint8_t>(SlReadByte()); break;
923 case SLE_FILE_I16: x = static_cast<int16_t>(SlReadUint16()); break;
924 case SLE_FILE_U16: x = static_cast<uint16_t>(SlReadUint16()); break;
925 case SLE_FILE_I32: x = static_cast<int32_t>(SlReadUint32()); break;
926 case SLE_FILE_U32: x = static_cast<uint32_t>(SlReadUint32()); break;
927 case SLE_FILE_I64: x = static_cast<int64_t>(SlReadUint64()); break;
928 case SLE_FILE_U64: x = static_cast<uint64_t>(SlReadUint64()); break;
929 case SLE_FILE_STRINGID: x = RemapOldStringID(static_cast<uint16_t>(SlReadUint16())); break;
930 default: NOT_REACHED();
931 }
932
933 /* Write The value to the struct. These ARE endian safe. */
934 WriteValue(ptr, conv, x);
935 break;
936 }
937 case SLA_PTRS: break;
938 case SLA_NULL: break;
939 default: NOT_REACHED();
940 }
941}
942
950static inline size_t SlCalcStdStringLen(const void *ptr)
951{
952 const std::string *str = reinterpret_cast<const std::string *>(ptr);
953
954 size_t len = str->length();
955 return len + SlGetArrayLength(len); // also include the length of the index
956}
957
958
967void FixSCCEncoded(std::string &str, bool fix_code)
968{
969 if (str.empty()) return;
970
971 /* We need to convert from old escape-style encoding to record separator encoding.
972 * Initial `<SCC_ENCODED><STRINGID>` stays the same.
973 *
974 * `:<SCC_ENCODED><STRINGID>` becomes `<RS><SCC_ENCODED><STRINGID>`
975 * `:<HEX>` becomes `<RS><SCC_ENCODED_NUMERIC><HEX>`
976 * `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
977 */
978 std::string result;
979 StringBuilder builder(result);
980
981 bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
982 bool in_string = false; // Set if we in a string, between double-quotes.
983 bool need_type = true; // Set if a parameter type needs to be emitted.
984
985 StringConsumer consumer(str);
986 while (consumer.AnyBytesLeft()) {
987 char32_t c;
988 if (auto r = consumer.TryReadUtf8(); r.has_value()) {
989 c = *r;
990 } else {
991 break;
992 }
993 if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
994 builder.PutUtf8(SCC_ENCODED);
995 need_type = false;
996 is_encoded = true;
997 continue;
998 }
999
1000 /* If the first character is not SCC_ENCODED then we don't have to do any conversion. */
1001 if (!is_encoded) return;
1002
1003 if (c == '"') {
1004 in_string = !in_string;
1005 if (in_string && need_type) {
1006 /* Started a new string parameter. */
1007 builder.PutUtf8(SCC_ENCODED_STRING);
1008 need_type = false;
1009 }
1010 continue;
1011 }
1012
1013 if (!in_string && c == ':') {
1014 builder.PutUtf8(SCC_RECORD_SEPARATOR);
1015 need_type = true;
1016 continue;
1017 }
1018 if (need_type) {
1019 /* Started a new numeric parameter. */
1021 need_type = false;
1022 }
1023
1024 builder.PutUtf8(c);
1025 }
1026
1027 str = std::move(result);
1028}
1029
1034void FixSCCEncodedNegative(std::string &str)
1035{
1036 if (str.empty()) return;
1037
1038 StringConsumer consumer(str);
1039
1040 /* Check whether this is an encoded string */
1041 if (!consumer.ReadUtf8If(SCC_ENCODED)) return;
1042
1043 std::string result;
1044 StringBuilder builder(result);
1045 builder.PutUtf8(SCC_ENCODED);
1046 while (consumer.AnyBytesLeft()) {
1047 /* Copy until next record */
1048 builder.Put(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::READ_ONE_SEPARATOR));
1049
1050 /* Check whether this is a numeric parameter */
1051 if (!consumer.ReadUtf8If(SCC_ENCODED_NUMERIC)) continue;
1053
1054 /* First try unsigned */
1055 if (auto u = consumer.TryReadIntegerBase<uint64_t>(16); u.has_value()) {
1056 builder.PutIntegerBase<uint64_t>(*u, 16);
1057 } else {
1058 /* Read as signed, store as unsigned */
1059 auto s = consumer.ReadIntegerBase<int64_t>(16);
1060 builder.PutIntegerBase<uint64_t>(static_cast<uint64_t>(s), 16);
1061 }
1062 }
1063
1064 str = std::move(result);
1065}
1066
1073void SlReadString(std::string &str, size_t length)
1074{
1075 str.resize(length);
1076 SlCopyBytes(str.data(), length);
1077}
1078
1084static void SlStdString(void *ptr, VarType conv)
1085{
1086 std::string *str = reinterpret_cast<std::string *>(ptr);
1087
1088 switch (_sl.action) {
1089 case SLA_SAVE: {
1090 size_t len = str->length();
1091 SlWriteArrayLength(len);
1092 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->data())), len);
1093 break;
1094 }
1095
1096 case SLA_LOAD_CHECK:
1097 case SLA_LOAD: {
1098 size_t len = SlReadArrayLength();
1099 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1100 SlSkipBytes(len);
1101 return;
1102 }
1103
1104 SlReadString(*str, len);
1105
1107 if ((conv & SLF_ALLOW_CONTROL) != 0) {
1111 }
1112 if ((conv & SLF_ALLOW_NEWLINE) != 0) {
1114 }
1115 if ((conv & SLF_REPLACE_TABCRLF) != 0) {
1117 }
1119 }
1120
1121 case SLA_PTRS: break;
1122 case SLA_NULL: break;
1123 default: NOT_REACHED();
1124 }
1125}
1126
1135static void SlCopyInternal(void *object, size_t length, VarType conv)
1136{
1137 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1138 assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes
1139 SlSkipBytes(length * SlCalcConvFileLen(conv));
1140 return;
1141 }
1142
1143 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
1144 * as a byte-type. So detect this, and adjust object size accordingly */
1145 if (_sl.action != SLA_SAVE && _sl_version == 0) {
1146 /* all objects except difficulty settings */
1147 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
1148 conv == SLE_INT32 || conv == SLE_UINT32) {
1149 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
1150 return;
1151 }
1152 /* used for conversion of Money 32bit->64bit */
1153 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
1154 for (uint i = 0; i < length; i++) {
1155 static_cast<int64_t *>(object)[i] = std::byteswap(SlReadUint32());
1156 }
1157 return;
1158 }
1159 }
1160
1161 /* If the size of elements is 1 byte both in file and memory, no special
1162 * conversion is needed, use specialized copy-copy function to speed up things */
1163 if (conv == SLE_INT8 || conv == SLE_UINT8) {
1164 SlCopyBytes(object, length);
1165 } else {
1166 uint8_t *a = static_cast<uint8_t *>(object);
1167 uint8_t mem_size = SlCalcConvMemLen(conv);
1168
1169 for (; length != 0; length --) {
1170 SlSaveLoadConv(a, conv);
1171 a += mem_size; // get size
1172 }
1173 }
1174}
1175
1184void SlCopy(void *object, size_t length, VarType conv)
1185{
1186 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
1187
1188 /* Automatically calculate the length? */
1189 if (_sl.need_length != NL_NONE) {
1190 SlSetLength(length * SlCalcConvFileLen(conv));
1191 /* Determine length only? */
1192 if (_sl.need_length == NL_CALCLENGTH) return;
1193 }
1194
1195 SlCopyInternal(object, length, conv);
1196}
1197
1204static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1205{
1206 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1207}
1208
1215static void SlArray(void *array, size_t length, VarType conv)
1216{
1217 switch (_sl.action) {
1218 case SLA_SAVE:
1219 SlWriteArrayLength(length);
1220 SlCopyInternal(array, length, conv);
1221 return;
1222
1223 case SLA_LOAD_CHECK:
1224 case SLA_LOAD: {
1226 size_t sv_length = SlReadArrayLength();
1227 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1228 /* We don't know this field, so we assume the length in the savegame is correct. */
1229 length = sv_length;
1230 } else if (sv_length != length) {
1231 /* If the SLE_ARR changes size, a savegame bump is required
1232 * and the developer should have written conversion lines.
1233 * Error out to make this more visible. */
1234 SlErrorCorrupt("Fixed-length array is of wrong length");
1235 }
1236 }
1237
1238 SlCopyInternal(array, length, conv);
1239 return;
1240 }
1241
1242 case SLA_PTRS:
1243 case SLA_NULL:
1244 return;
1245
1246 default:
1247 NOT_REACHED();
1248 }
1249}
1250
1261static uint32_t ReferenceToInt(const void *obj, SLRefType rt)
1262{
1263 assert(_sl.action == SLA_SAVE);
1264
1265 if (obj == nullptr) return 0;
1266
1267 switch (rt) {
1268 case REF_VEHICLE_OLD: // Old vehicles we save as new ones
1269 case REF_VEHICLE: return static_cast<const Vehicle *>(obj)->index + 1;
1270 case REF_STATION: return static_cast<const Station *>(obj)->index + 1;
1271 case REF_TOWN: return static_cast<const Town *>(obj)->index + 1;
1272 case REF_ROADSTOPS: return static_cast<const RoadStop *>(obj)->index + 1;
1273 case REF_ENGINE_RENEWS: return static_cast<const EngineRenew *>(obj)->index + 1;
1274 case REF_CARGO_PACKET: return static_cast<const CargoPacket *>(obj)->index + 1;
1275 case REF_ORDERLIST: return static_cast<const OrderList *>(obj)->index + 1;
1276 case REF_STORAGE: return static_cast<const PersistentStorage *>(obj)->index + 1;
1277 case REF_LINK_GRAPH: return static_cast<const LinkGraph *>(obj)->index + 1;
1278 case REF_LINK_GRAPH_JOB: return static_cast<const LinkGraphJob *>(obj)->index + 1;
1279 default: NOT_REACHED();
1280 }
1281}
1282
1293static void *IntToReference(size_t index, SLRefType rt)
1294{
1295 static_assert(sizeof(size_t) <= sizeof(void *));
1296
1297 assert(_sl.action == SLA_PTRS);
1298
1299 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
1300 * and should be loaded like that */
1301 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) {
1302 rt = REF_VEHICLE;
1303 }
1304
1305 /* No need to look up nullptr pointers, just return immediately */
1306 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr;
1307
1308 /* Correct index. Old vehicles were saved differently:
1309 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1310 if (rt != REF_VEHICLE_OLD) index--;
1311
1312 switch (rt) {
1313 case REF_ORDERLIST:
1314 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1315 SlErrorCorrupt("Referencing invalid OrderList");
1316
1317 case REF_VEHICLE_OLD:
1318 case REF_VEHICLE:
1319 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1320 SlErrorCorrupt("Referencing invalid Vehicle");
1321
1322 case REF_STATION:
1323 if (Station::IsValidID(index)) return Station::Get(index);
1324 SlErrorCorrupt("Referencing invalid Station");
1325
1326 case REF_TOWN:
1327 if (Town::IsValidID(index)) return Town::Get(index);
1328 SlErrorCorrupt("Referencing invalid Town");
1329
1330 case REF_ROADSTOPS:
1331 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1332 SlErrorCorrupt("Referencing invalid RoadStop");
1333
1334 case REF_ENGINE_RENEWS:
1335 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1336 SlErrorCorrupt("Referencing invalid EngineRenew");
1337
1338 case REF_CARGO_PACKET:
1339 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1340 SlErrorCorrupt("Referencing invalid CargoPacket");
1341
1342 case REF_STORAGE:
1343 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1344 SlErrorCorrupt("Referencing invalid PersistentStorage");
1345
1346 case REF_LINK_GRAPH:
1347 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1348 SlErrorCorrupt("Referencing invalid LinkGraph");
1349
1350 case REF_LINK_GRAPH_JOB:
1351 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1352 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1353
1354 default: NOT_REACHED();
1355 }
1356}
1357
1363void SlSaveLoadRef(void *ptr, VarType conv)
1364{
1365 switch (_sl.action) {
1366 case SLA_SAVE:
1367 SlWriteUint32(ReferenceToInt(*static_cast<void **>(ptr), static_cast<SLRefType>(conv)));
1368 break;
1369 case SLA_LOAD_CHECK:
1370 case SLA_LOAD:
1371 *static_cast<size_t *>(ptr) = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32();
1372 break;
1373 case SLA_PTRS:
1374 *static_cast<void **>(ptr) = IntToReference(*static_cast<size_t *>(ptr), static_cast<SLRefType>(conv));
1375 break;
1376 case SLA_NULL:
1377 *static_cast<void **>(ptr) = nullptr;
1378 break;
1379 default: NOT_REACHED();
1380 }
1381}
1382
1386template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1388 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1389public:
1397 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1398 {
1399 assert(cmd == SL_VAR || cmd == SL_REF);
1400
1401 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1402
1403 int type_size = SlGetArrayLength(list->size());
1404 int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : static_cast<VarType>(SLE_FILE_U32));
1405 return list->size() * item_size + type_size;
1406 }
1407
1408 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1409 {
1410 switch (cmd) {
1411 case SL_VAR: SlSaveLoadConv(item, conv); break;
1412 case SL_REF: SlSaveLoadRef(item, conv); break;
1413 case SL_STDSTR: SlStdString(item, conv); break;
1414 default:
1415 NOT_REACHED();
1416 }
1417 }
1418
1425 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1426 {
1427 assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
1428
1429 SlStorageT *list = static_cast<SlStorageT *>(storage);
1430
1431 switch (_sl.action) {
1432 case SLA_SAVE:
1433 SlWriteArrayLength(list->size());
1434
1435 for (auto &item : *list) {
1436 SlSaveLoadMember(cmd, &item, conv);
1437 }
1438 break;
1439
1440 case SLA_LOAD_CHECK:
1441 case SLA_LOAD: {
1442 size_t length;
1443 switch (cmd) {
1444 case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1445 case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1446 case SL_STDSTR: length = SlReadArrayLength(); break;
1447 default: NOT_REACHED();
1448 }
1449
1450 list->clear();
1451 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1452 list->reserve(length);
1453 }
1454
1455 /* Load each value and push to the end of the storage. */
1456 for (size_t i = 0; i < length; i++) {
1457 Tvar &data = list->emplace_back();
1458 SlSaveLoadMember(cmd, &data, conv);
1459 }
1460 break;
1461 }
1462
1463 case SLA_PTRS:
1464 for (auto &item : *list) {
1465 SlSaveLoadMember(cmd, &item, conv);
1466 }
1467 break;
1468
1469 case SLA_NULL:
1470 list->clear();
1471 break;
1472
1473 default: NOT_REACHED();
1474 }
1475 }
1476};
1477
1484static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1485{
1487}
1488
1494static void SlRefList(void *list, VarType conv)
1495{
1496 /* Automatically calculate the length? */
1497 if (_sl.need_length != NL_NONE) {
1498 SlSetLength(SlCalcRefListLen(list, conv));
1499 /* Determine length only? */
1500 if (_sl.need_length == NL_CALCLENGTH) return;
1501 }
1502
1504}
1505
1512static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1513{
1515}
1516
1522static void SlRefVector(void *vector, VarType conv)
1523{
1524 /* Automatically calculate the length? */
1525 if (_sl.need_length != NL_NONE) {
1526 SlSetLength(SlCalcRefVectorLen(vector, conv));
1527 /* Determine length only? */
1528 if (_sl.need_length == NL_CALCLENGTH) return;
1529 }
1530
1532}
1533
1540static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1541{
1542 switch (GetVarMemType(conv)) {
1543 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1544 case SLE_VAR_I8: return SlStorageHelper<std::vector, int8_t>::SlCalcLen(vector, conv);
1545 case SLE_VAR_U8: return SlStorageHelper<std::vector, uint8_t>::SlCalcLen(vector, conv);
1546 case SLE_VAR_I16: return SlStorageHelper<std::vector, int16_t>::SlCalcLen(vector, conv);
1547 case SLE_VAR_U16: return SlStorageHelper<std::vector, uint16_t>::SlCalcLen(vector, conv);
1548 case SLE_VAR_I32: return SlStorageHelper<std::vector, int32_t>::SlCalcLen(vector, conv);
1549 case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
1550 case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
1551 case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
1552
1553 case SLE_VAR_STR:
1554 /* Strings are a length-prefixed field type in the savegame table format,
1555 * these may not be directly stored in another length-prefixed container type. */
1556 NOT_REACHED();
1557
1558 default: NOT_REACHED();
1559 }
1560}
1561
1567static void SlVector(void *vector, VarType conv)
1568{
1569 switch (GetVarMemType(conv)) {
1570 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1571 case SLE_VAR_I8: SlStorageHelper<std::vector, int8_t>::SlSaveLoad(vector, conv); break;
1572 case SLE_VAR_U8: SlStorageHelper<std::vector, uint8_t>::SlSaveLoad(vector, conv); break;
1573 case SLE_VAR_I16: SlStorageHelper<std::vector, int16_t>::SlSaveLoad(vector, conv); break;
1574 case SLE_VAR_U16: SlStorageHelper<std::vector, uint16_t>::SlSaveLoad(vector, conv); break;
1575 case SLE_VAR_I32: SlStorageHelper<std::vector, int32_t>::SlSaveLoad(vector, conv); break;
1576 case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
1577 case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
1578 case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
1579
1580 case SLE_VAR_STR:
1581 /* Strings are a length-prefixed field type in the savegame table format,
1582 * these may not be directly stored in another length-prefixed container type.
1583 * This is permitted for load-related actions, because invalid fields of this type are present
1584 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1585 assert(_sl.action != SLA_SAVE);
1587 break;
1588
1589 default: NOT_REACHED();
1590 }
1591}
1592
1598static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1599{
1600 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1601}
1602
1608static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1609{
1610 size_t length = 0;
1611
1612 for (auto &sld : slt) {
1613 if (!SlIsObjectValidInSavegame(sld)) continue;
1614
1615 length += SlCalcConvFileLen(SLE_UINT8);
1616 length += SlCalcStdStringLen(&sld.name);
1617 }
1618
1619 length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
1620
1621 for (auto &sld : slt) {
1622 if (!SlIsObjectValidInSavegame(sld)) continue;
1623 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1624 length += SlCalcTableHeader(sld.handler->GetDescription());
1625 }
1626 }
1627
1628 return length;
1629}
1630
1637size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1638{
1639 size_t length = 0;
1640
1641 /* Need to determine the length and write a length tag. */
1642 for (auto &sld : slt) {
1643 length += SlCalcObjMemberLength(object, sld);
1644 }
1645 return length;
1646}
1647
1648size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1649{
1650 assert(_sl.action == SLA_SAVE);
1651
1652 if (!SlIsObjectValidInSavegame(sld)) return 0;
1653
1654 switch (sld.cmd) {
1655 case SL_VAR: return SlCalcConvFileLen(sld.conv);
1656 case SL_REF: return SlCalcRefLen();
1657 case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
1658 case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
1659 case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
1660 case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1661 case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
1662 case SL_SAVEBYTE: return 1; // a byte is logically of size 1
1663 case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
1664
1665 case SL_STRUCT:
1666 case SL_STRUCTLIST: {
1667 NeedLength old_need_length = _sl.need_length;
1668 size_t old_obj_len = _sl.obj_len;
1669
1670 _sl.need_length = NL_CALCLENGTH;
1671 _sl.obj_len = 0;
1672
1673 /* Pretend that we are saving to collect the object size. Other
1674 * means are difficult, as we don't know the length of the list we
1675 * are about to store. */
1676 sld.handler->Save(const_cast<void *>(object));
1677 size_t length = _sl.obj_len;
1678
1679 _sl.obj_len = old_obj_len;
1680 _sl.need_length = old_need_length;
1681
1682 if (sld.cmd == SL_STRUCT) {
1683 length += SlGetArrayLength(1);
1684 }
1685
1686 return length;
1687 }
1688
1689 default: NOT_REACHED();
1690 }
1691 return 0;
1692}
1693
1694static bool SlObjectMember(void *object, const SaveLoad &sld)
1695{
1696 if (!SlIsObjectValidInSavegame(sld)) return false;
1697
1698 VarType conv = GB(sld.conv, 0, 8);
1699 switch (sld.cmd) {
1700 case SL_VAR:
1701 case SL_REF:
1702 case SL_ARR:
1703 case SL_REFLIST:
1704 case SL_REFVECTOR:
1705 case SL_VECTOR:
1706 case SL_STDSTR: {
1707 void *ptr = GetVariableAddress(object, sld);
1708
1709 switch (sld.cmd) {
1710 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
1711 case SL_REF: SlSaveLoadRef(ptr, conv); break;
1712 case SL_ARR: SlArray(ptr, sld.length, conv); break;
1713 case SL_REFLIST: SlRefList(ptr, conv); break;
1714 case SL_REFVECTOR: SlRefVector(ptr, conv); break;
1715 case SL_VECTOR: SlVector(ptr, conv); break;
1716 case SL_STDSTR: SlStdString(ptr, sld.conv); break;
1717 default: NOT_REACHED();
1718 }
1719 break;
1720 }
1721
1722 /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object.
1723 * When loading, the value is read explicitly with SlReadByte() to determine which
1724 * object description to use. */
1725 case SL_SAVEBYTE: {
1726 void *ptr = GetVariableAddress(object, sld);
1727
1728 switch (_sl.action) {
1729 case SLA_SAVE: SlWriteByte(*static_cast<uint8_t *>(ptr)); break;
1730 case SLA_LOAD_CHECK:
1731 case SLA_LOAD:
1732 case SLA_PTRS:
1733 case SLA_NULL: break;
1734 default: NOT_REACHED();
1735 }
1736 break;
1737 }
1738
1739 case SL_NULL: {
1740 assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
1741
1742 switch (_sl.action) {
1743 case SLA_LOAD_CHECK:
1744 case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
1745 case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1746 case SLA_PTRS:
1747 case SLA_NULL: break;
1748 default: NOT_REACHED();
1749 }
1750 break;
1751 }
1752
1753 case SL_STRUCT:
1754 case SL_STRUCTLIST:
1755 switch (_sl.action) {
1756 case SLA_SAVE: {
1757 if (sld.cmd == SL_STRUCT) {
1758 /* Store in the savegame if this struct was written or not. */
1759 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1760 }
1761 sld.handler->Save(object);
1762 break;
1763 }
1764
1765 case SLA_LOAD_CHECK: {
1768 }
1769 sld.handler->LoadCheck(object);
1770 break;
1771 }
1772
1773 case SLA_LOAD: {
1776 }
1777 sld.handler->Load(object);
1778 break;
1779 }
1780
1781 case SLA_PTRS:
1782 sld.handler->FixPointers(object);
1783 break;
1784
1785 case SLA_NULL: break;
1786 default: NOT_REACHED();
1787 }
1788 break;
1789
1790 default: NOT_REACHED();
1791 }
1792 return true;
1793}
1794
1799void SlSetStructListLength(size_t length)
1800{
1801 /* Automatically calculate the length? */
1802 if (_sl.need_length != NL_NONE) {
1803 SlSetLength(SlGetArrayLength(length));
1804 if (_sl.need_length == NL_CALCLENGTH) return;
1805 }
1806
1807 SlWriteArrayLength(length);
1808}
1809
1815size_t SlGetStructListLength(size_t limit)
1816{
1817 size_t length = SlReadArrayLength();
1818 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1819
1820 return length;
1821}
1822
1828void SlObject(void *object, const SaveLoadTable &slt)
1829{
1830 /* Automatically calculate the length? */
1831 if (_sl.need_length != NL_NONE) {
1832 SlSetLength(SlCalcObjLength(object, slt));
1833 if (_sl.need_length == NL_CALCLENGTH) return;
1834 }
1835
1836 for (auto &sld : slt) {
1837 SlObjectMember(object, sld);
1838 }
1839}
1840
1846 void Save(void *) const override
1847 {
1848 NOT_REACHED();
1849 }
1850
1851 void Load(void *object) const override
1852 {
1853 size_t length = SlGetStructListLength(UINT32_MAX);
1854 for (; length > 0; length--) {
1855 SlObject(object, this->GetLoadDescription());
1856 }
1857 }
1858
1859 void LoadCheck(void *object) const override
1860 {
1861 this->Load(object);
1862 }
1863
1865 {
1866 return {};
1867 }
1868
1870 {
1871 NOT_REACHED();
1872 }
1873};
1874
1881std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1882{
1883 /* You can only use SlTableHeader if you are a CH_TABLE. */
1884 assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
1885
1886 switch (_sl.action) {
1887 case SLA_LOAD_CHECK:
1888 case SLA_LOAD: {
1889 std::vector<SaveLoad> saveloads;
1890
1891 /* Build a key lookup mapping based on the available fields. */
1892 std::map<std::string, const SaveLoad *> key_lookup;
1893 for (auto &sld : slt) {
1894 if (!SlIsObjectValidInSavegame(sld)) continue;
1895
1896 /* Check that there is only one active SaveLoad for a given name. */
1897 assert(key_lookup.find(sld.name) == key_lookup.end());
1898 key_lookup[sld.name] = &sld;
1899 }
1900
1901 while (true) {
1902 uint8_t type = 0;
1903 SlSaveLoadConv(&type, SLE_UINT8);
1904 if (type == SLE_FILE_END) break;
1905
1906 std::string key;
1907 SlStdString(&key, SLE_STR);
1908
1909 auto sld_it = key_lookup.find(key);
1910 if (sld_it == key_lookup.end()) {
1911 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1912 Debug(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type);
1913
1914 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1915 SaveLoadType saveload_type;
1916 switch (type & SLE_FILE_TYPE_MASK) {
1917 case SLE_FILE_STRING:
1918 /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
1919 saveload_type = SL_STDSTR;
1920 break;
1921
1922 case SLE_FILE_STRUCT:
1923 /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */
1924 saveload_type = SL_STRUCTLIST;
1925 handler = std::make_shared<SlSkipHandler>();
1926 break;
1927
1928 default:
1929 saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
1930 break;
1931 }
1932
1933 /* We don't know this field, so read to nothing. */
1934 saveloads.emplace_back(std::move(key), saveload_type, (static_cast<VarType>(type) & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, std::move(handler));
1935 continue;
1936 }
1937
1938 /* Validate the type of the field. If it is changed, the
1939 * savegame should have been bumped so we know how to do the
1940 * conversion. If this error triggers, that clearly didn't
1941 * happen and this is a friendly poke to the developer to bump
1942 * the savegame version and add conversion code. */
1943 uint8_t correct_type = GetSavegameFileType(*sld_it->second);
1944 if (correct_type != type) {
1945 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type, type);
1946 SlErrorCorrupt("Field type is different than expected");
1947 }
1948 saveloads.emplace_back(*sld_it->second);
1949 }
1950
1951 for (auto &sld : saveloads) {
1952 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1953 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
1954 }
1955 }
1956
1957 return saveloads;
1958 }
1959
1960 case SLA_SAVE: {
1961 /* Automatically calculate the length? */
1962 if (_sl.need_length != NL_NONE) {
1964 if (_sl.need_length == NL_CALCLENGTH) break;
1965 }
1966
1967 for (auto &sld : slt) {
1968 if (!SlIsObjectValidInSavegame(sld)) continue;
1969 /* Make sure we are not storing empty keys. */
1970 assert(!sld.name.empty());
1971
1972 uint8_t type = GetSavegameFileType(sld);
1973 assert(type != SLE_FILE_END);
1974
1975 SlSaveLoadConv(&type, SLE_UINT8);
1976 SlStdString(const_cast<std::string *>(&sld.name), SLE_STR);
1977 }
1978
1979 /* Add an end-of-header marker. */
1980 uint8_t type = SLE_FILE_END;
1981 SlSaveLoadConv(&type, SLE_UINT8);
1982
1983 /* After the table, write down any sub-tables we might have. */
1984 for (auto &sld : slt) {
1985 if (!SlIsObjectValidInSavegame(sld)) continue;
1986 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1987 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
1988 NeedLength old_need_length = _sl.need_length;
1989 _sl.need_length = NL_NONE;
1990
1991 SlTableHeader(sld.handler->GetDescription());
1992
1993 _sl.need_length = old_need_length;
1994 }
1995 }
1996
1997 break;
1998 }
1999
2000 default: NOT_REACHED();
2001 }
2002
2003 return std::vector<SaveLoad>();
2004}
2005
2019std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
2020{
2021 assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
2022 /* CH_TABLE / CH_SPARSE_TABLE always have a header. */
2023 if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt);
2024
2025 std::vector<SaveLoad> saveloads;
2026
2027 /* Build a key lookup mapping based on the available fields. */
2028 std::map<std::string, std::vector<const SaveLoad *>> key_lookup;
2029 for (auto &sld : slt) {
2030 /* All entries should have a name; otherwise the entry should just be removed. */
2031 assert(!sld.name.empty());
2032
2033 key_lookup[sld.name].push_back(&sld);
2034 }
2035
2036 for (auto &slc : slct) {
2037 if (slc.name.empty()) {
2038 /* In old savegames there can be data we no longer care for. We
2039 * skip this by simply reading the amount of bytes indicated and
2040 * send those to /dev/null. */
2041 saveloads.emplace_back("", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr);
2042 } else {
2043 auto sld_it = key_lookup.find(slc.name);
2044 /* If this branch triggers, it means that an entry in the
2045 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2046 * you rename a field in one and not in the other? */
2047 if (sld_it == key_lookup.end()) {
2048 /* This isn't an assert, as that leaves no information what
2049 * field was to blame. This way at least we have breadcrumbs. */
2050 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2051 SlErrorCorrupt("Internal error with savegame compatibility");
2052 }
2053 for (auto &sld : sld_it->second) {
2054 saveloads.push_back(*sld);
2055 }
2056 }
2057 }
2058
2059 for (auto &sld : saveloads) {
2060 if (!SlIsObjectValidInSavegame(sld)) continue;
2061 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
2062 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2063 }
2064 }
2065
2066 return saveloads;
2067}
2068
2074{
2075 SlObject(nullptr, slt);
2076}
2077
2083void SlAutolength(AutolengthProc *proc, int arg)
2084{
2085 assert(_sl.action == SLA_SAVE);
2086
2087 /* Tell it to calculate the length */
2088 _sl.need_length = NL_CALCLENGTH;
2089 _sl.obj_len = 0;
2090 proc(arg);
2091
2092 /* Setup length */
2093 _sl.need_length = NL_WANTLENGTH;
2094 SlSetLength(_sl.obj_len);
2095
2096 size_t start_pos = _sl.dumper->GetSize();
2097 size_t expected_offs = start_pos + _sl.obj_len;
2098
2099 /* And write the stuff */
2100 proc(arg);
2101
2102 if (expected_offs != _sl.dumper->GetSize()) {
2103 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2104 }
2105}
2106
2107void ChunkHandler::LoadCheck(size_t len) const
2108{
2109 switch (_sl.block_mode) {
2110 case CH_TABLE:
2111 case CH_SPARSE_TABLE:
2112 SlTableHeader({});
2113 [[fallthrough]];
2114 case CH_ARRAY:
2115 case CH_SPARSE_ARRAY:
2116 SlSkipArray();
2117 break;
2118 case CH_RIFF:
2119 SlSkipBytes(len);
2120 break;
2121 default:
2122 NOT_REACHED();
2123 }
2124}
2125
2130static void SlLoadChunk(const ChunkHandler &ch)
2131{
2132 uint8_t m = SlReadByte();
2133
2134 _sl.block_mode = m & CH_TYPE_MASK;
2135 _sl.obj_len = 0;
2136 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2137
2138 /* The header should always be at the start. Read the length; the
2139 * Load() should as first action process the header. */
2140 if (_sl.expect_table_header) {
2141 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2142 }
2143
2144 switch (_sl.block_mode) {
2145 case CH_TABLE:
2146 case CH_ARRAY:
2147 _sl.array_index = 0;
2148 ch.Load();
2149 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2150 break;
2151 case CH_SPARSE_TABLE:
2152 case CH_SPARSE_ARRAY:
2153 ch.Load();
2154 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2155 break;
2156 case CH_RIFF: {
2157 /* Read length */
2158 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2159 len += SlReadUint16();
2160 _sl.obj_len = len;
2161 size_t start_pos = _sl.reader->GetSize();
2162 size_t endoffs = start_pos + len;
2163 ch.Load();
2164
2165 if (_sl.reader->GetSize() != endoffs) {
2166 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2167 }
2168 break;
2169 }
2170 default:
2171 SlErrorCorrupt("Invalid chunk type");
2172 break;
2173 }
2174
2175 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2176}
2177
2183static void SlLoadCheckChunk(const ChunkHandler &ch)
2184{
2185 uint8_t m = SlReadByte();
2186
2187 _sl.block_mode = m & CH_TYPE_MASK;
2188 _sl.obj_len = 0;
2189 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2190
2191 /* The header should always be at the start. Read the length; the
2192 * LoadCheck() should as first action process the header. */
2193 if (_sl.expect_table_header) {
2194 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2195 }
2196
2197 switch (_sl.block_mode) {
2198 case CH_TABLE:
2199 case CH_ARRAY:
2200 _sl.array_index = 0;
2201 ch.LoadCheck();
2202 break;
2203 case CH_SPARSE_TABLE:
2204 case CH_SPARSE_ARRAY:
2205 ch.LoadCheck();
2206 break;
2207 case CH_RIFF: {
2208 /* Read length */
2209 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2210 len += SlReadUint16();
2211 _sl.obj_len = len;
2212 size_t start_pos = _sl.reader->GetSize();
2213 size_t endoffs = start_pos + len;
2214 ch.LoadCheck(len);
2215
2216 if (_sl.reader->GetSize() != endoffs) {
2217 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2218 }
2219 break;
2220 }
2221 default:
2222 SlErrorCorrupt("Invalid chunk type");
2223 break;
2224 }
2225
2226 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2227}
2228
2234static void SlSaveChunk(const ChunkHandler &ch)
2235{
2236 if (ch.type == CH_READONLY) return;
2237
2238 SlWriteUint32(ch.id);
2239 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2240
2241 _sl.block_mode = ch.type;
2242 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2243
2244 _sl.need_length = (_sl.expect_table_header || _sl.block_mode == CH_RIFF) ? NL_WANTLENGTH : NL_NONE;
2245
2246 switch (_sl.block_mode) {
2247 case CH_RIFF:
2248 ch.Save();
2249 break;
2250 case CH_TABLE:
2251 case CH_ARRAY:
2252 _sl.last_array_index = 0;
2253 SlWriteByte(_sl.block_mode);
2254 ch.Save();
2255 SlWriteArrayLength(0); // Terminate arrays
2256 break;
2257 case CH_SPARSE_TABLE:
2258 case CH_SPARSE_ARRAY:
2259 SlWriteByte(_sl.block_mode);
2260 ch.Save();
2261 SlWriteArrayLength(0); // Terminate arrays
2262 break;
2263 default: NOT_REACHED();
2264 }
2265
2266 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2267}
2268
2270static void SlSaveChunks()
2271{
2272 for (auto &ch : ChunkHandlers()) {
2273 SlSaveChunk(ch);
2274 }
2275
2276 /* Terminator */
2277 SlWriteUint32(0);
2278}
2279
2286static const ChunkHandler *SlFindChunkHandler(uint32_t id)
2287{
2288 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2289 return nullptr;
2290}
2291
2293static void SlLoadChunks()
2294{
2295 uint32_t id;
2296 const ChunkHandler *ch;
2297
2298 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2299 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2300
2301 ch = SlFindChunkHandler(id);
2302 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2303 SlLoadChunk(*ch);
2304 }
2305}
2306
2309{
2310 uint32_t id;
2311 const ChunkHandler *ch;
2312
2313 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2314 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2315
2316 ch = SlFindChunkHandler(id);
2317 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2318 SlLoadCheckChunk(*ch);
2319 }
2320}
2321
2323static void SlFixPointers()
2324{
2325 _sl.action = SLA_PTRS;
2326
2327 for (const ChunkHandler &ch : ChunkHandlers()) {
2328 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2329 ch.FixPointers();
2330 }
2331
2332 assert(_sl.action == SLA_PTRS);
2333}
2334
2335
2338 std::optional<FileHandle> file;
2339 long begin;
2340
2345 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2346 {
2347 }
2348
2350 ~FileReader() override
2351 {
2352 if (this->file.has_value()) {
2353 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2354 }
2355 }
2356
2357 size_t Read(uint8_t *buf, size_t size) override
2358 {
2359 /* We're in the process of shutting down, i.e. in "failure" mode. */
2360 if (!this->file.has_value()) return 0;
2361
2362 return fread(buf, 1, size, *this->file);
2363 }
2364
2365 void Reset() override
2366 {
2367 clearerr(*this->file);
2368 if (fseek(*this->file, this->begin, SEEK_SET)) {
2369 Debug(sl, 1, "Could not reset the file reading");
2370 }
2371 }
2372};
2373
2376 std::optional<FileHandle> file;
2377
2382 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2383 {
2384 }
2385
2387 ~FileWriter() override
2388 {
2389 this->Finish();
2390 }
2391
2392 void Write(uint8_t *buf, size_t size) override
2393 {
2394 /* We're in the process of shutting down, i.e. in "failure" mode. */
2395 if (!this->file.has_value()) return;
2396
2397 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2398 }
2399
2400 void Finish() override
2401 {
2402 if (this->file.has_value()) {
2403 _game_session_stats.savegame_size = ftell(*this->file);
2404 this->file.reset();
2405 }
2406 }
2407};
2408
2409/*******************************************
2410 ********** START OF LZO CODE **************
2411 *******************************************/
2412
2413#ifdef WITH_LZO
2414
2416static const uint LZO_BUFFER_SIZE = 8192;
2417
2424 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2425 {
2426 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2427 }
2428
2429 size_t Read(uint8_t *buf, size_t ssize) override
2430 {
2431 assert(ssize >= LZO_BUFFER_SIZE);
2432
2433 /* Buffer size is from the LZO docs plus the chunk header size. */
2434 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2435 uint32_t tmp[2];
2436 uint32_t size;
2437 lzo_uint len = ssize;
2438
2439 /* Read header*/
2440 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2441
2442 /* Check if size is bad */
2443 ((uint32_t*)out)[0] = size = tmp[1];
2444
2445 if (_sl_version != SL_MIN_VERSION) {
2446 tmp[0] = TO_BE32(tmp[0]);
2447 size = TO_BE32(size);
2448 }
2449
2450 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2451
2452 /* Read block */
2453 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2454
2455 /* Verify checksum */
2456 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2457
2458 /* Decompress */
2459 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2460 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2461 return len;
2462 }
2463};
2464
2471 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2472 {
2473 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2474 }
2475
2476 void Write(uint8_t *buf, size_t size) override
2477 {
2478 const lzo_bytep in = buf;
2479 /* Buffer size is from the LZO docs plus the chunk header size. */
2480 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2481 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2482 lzo_uint outlen;
2483
2484 do {
2485 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2486 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : static_cast<lzo_uint>(size);
2487 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2488 ((uint32_t*)out)[1] = TO_BE32(static_cast<uint32_t>(outlen));
2489 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2490 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2491
2492 /* Move to next data chunk. */
2493 size -= len;
2494 in += len;
2495 } while (size > 0);
2496 }
2497};
2498
2499#endif /* WITH_LZO */
2500
2501/*********************************************
2502 ******** START OF NOCOMP CODE (uncompressed)*
2503 *********************************************/
2504
2511 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2512 {
2513 }
2514
2515 size_t Read(uint8_t *buf, size_t size) override
2516 {
2517 return this->chain->Read(buf, size);
2518 }
2519};
2520
2527 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2528 {
2529 }
2530
2531 void Write(uint8_t *buf, size_t size) override
2532 {
2533 this->chain->Write(buf, size);
2534 }
2535};
2536
2537/********************************************
2538 ********** START OF ZLIB CODE **************
2539 ********************************************/
2540
2541#if defined(WITH_ZLIB)
2542
2545 z_stream z{};
2547
2552 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2553 {
2554 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2555 }
2556
2559 {
2560 inflateEnd(&this->z);
2561 }
2562
2563 size_t Read(uint8_t *buf, size_t size) override
2564 {
2565 this->z.next_out = buf;
2566 this->z.avail_out = static_cast<uint>(size);
2567
2568 do {
2569 /* read more bytes from the file? */
2570 if (this->z.avail_in == 0) {
2571 this->z.next_in = this->fread_buf;
2572 this->z.avail_in = static_cast<uint>(this->chain->Read(this->fread_buf, sizeof(this->fread_buf)));
2573 }
2574
2575 /* inflate the data */
2576 int r = inflate(&this->z, 0);
2577 if (r == Z_STREAM_END) break;
2578
2579 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2580 } while (this->z.avail_out != 0);
2581
2582 return size - this->z.avail_out;
2583 }
2584};
2585
2588 z_stream z{};
2590
2596 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain))
2597 {
2598 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2599 }
2600
2603 {
2604 deflateEnd(&this->z);
2605 }
2606
2613 void WriteLoop(uint8_t *p, size_t len, int mode)
2614 {
2615 uint n;
2616 this->z.next_in = p;
2617 this->z.avail_in = static_cast<uInt>(len);
2618 do {
2619 this->z.next_out = this->fwrite_buf;
2620 this->z.avail_out = sizeof(this->fwrite_buf);
2621
2629 int r = deflate(&this->z, mode);
2630
2631 /* bytes were emitted? */
2632 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2633 this->chain->Write(this->fwrite_buf, n);
2634 }
2635 if (r == Z_STREAM_END) break;
2636
2637 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2638 } while (this->z.avail_in || !this->z.avail_out);
2639 }
2640
2641 void Write(uint8_t *buf, size_t size) override
2642 {
2643 this->WriteLoop(buf, size, 0);
2644 }
2645
2646 void Finish() override
2647 {
2648 this->WriteLoop(nullptr, 0, Z_FINISH);
2649 this->chain->Finish();
2650 }
2651};
2652
2653#endif /* WITH_ZLIB */
2654
2655/********************************************
2656 ********** START OF LZMA CODE **************
2657 ********************************************/
2658
2659#if defined(WITH_LIBLZMA)
2660
2667static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2668
2671 lzma_stream lzma;
2673
2678 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain)), lzma(_lzma_init)
2679 {
2680 /* Allow saves up to 256 MB uncompressed */
2681 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2682 }
2683
2686 {
2687 lzma_end(&this->lzma);
2688 }
2689
2690 size_t Read(uint8_t *buf, size_t size) override
2691 {
2692 this->lzma.next_out = buf;
2693 this->lzma.avail_out = size;
2694
2695 do {
2696 /* read more bytes from the file? */
2697 if (this->lzma.avail_in == 0) {
2698 this->lzma.next_in = this->fread_buf;
2699 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2700 }
2701
2702 /* inflate the data */
2703 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2704 if (r == LZMA_STREAM_END) break;
2705 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2706 } while (this->lzma.avail_out != 0);
2707
2708 return size - this->lzma.avail_out;
2709 }
2710};
2711
2714 lzma_stream lzma;
2716
2722 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init)
2723 {
2724 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2725 }
2726
2729 {
2730 lzma_end(&this->lzma);
2731 }
2732
2739 void WriteLoop(uint8_t *p, size_t len, lzma_action action)
2740 {
2741 size_t n;
2742 this->lzma.next_in = p;
2743 this->lzma.avail_in = len;
2744 do {
2745 this->lzma.next_out = this->fwrite_buf;
2746 this->lzma.avail_out = sizeof(this->fwrite_buf);
2747
2748 lzma_ret r = lzma_code(&this->lzma, action);
2749
2750 /* bytes were emitted? */
2751 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2752 this->chain->Write(this->fwrite_buf, n);
2753 }
2754 if (r == LZMA_STREAM_END) break;
2755 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2756 } while (this->lzma.avail_in || !this->lzma.avail_out);
2757 }
2758
2759 void Write(uint8_t *buf, size_t size) override
2760 {
2761 this->WriteLoop(buf, size, LZMA_RUN);
2762 }
2763
2764 void Finish() override
2765 {
2766 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2767 this->chain->Finish();
2768 }
2769};
2770
2771#endif /* WITH_LIBLZMA */
2772
2773/*******************************************
2774 ************* END OF CODE *****************
2775 *******************************************/
2776
2779 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2780 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2781
2782 std::string_view name;
2783 uint32_t tag;
2784
2788};
2789
2790static const uint32_t SAVEGAME_TAG_LZO = TO_BE32('OTTD');
2791static const uint32_t SAVEGAME_TAG_NONE = TO_BE32('OTTN');
2792static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32('OTTZ');
2793static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32('OTTX');
2794
2797#if defined(WITH_LZO)
2798 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2799 {CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, "lzo", SAVEGAME_TAG_LZO, 0, 0, 0},
2800#else
2801 {nullptr, nullptr, "lzo", SAVEGAME_TAG_LZO, 0, 0, 0},
2802#endif
2803 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2804 {CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, "none", SAVEGAME_TAG_NONE, 0, 0, 0},
2805#if defined(WITH_ZLIB)
2806 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2807 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2808 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2809 {CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, "zlib", SAVEGAME_TAG_ZLIB, 0, 6, 9},
2810#else
2811 {nullptr, nullptr, "zlib", SAVEGAME_TAG_ZLIB, 0, 0, 0},
2812#endif
2813#if defined(WITH_LIBLZMA)
2814 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2815 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2816 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2817 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2818 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2819 {CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, "lzma", SAVEGAME_TAG_LZMA, 0, 2, 9},
2820#else
2821 {nullptr, nullptr, "lzma", SAVEGAME_TAG_LZMA, 0, 0, 0},
2822#endif
2823};
2824
2831static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(std::string_view full_name)
2832{
2833 /* Find default savegame format, the highest one with which files can be written. */
2834 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2835 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2836
2837 const SaveLoadFormat &def = *it;
2838
2839 if (!full_name.empty()) {
2840 /* Get the ":..." of the compression level out of the way */
2841 size_t separator = full_name.find(':');
2842 bool has_comp_level = separator != std::string::npos;
2843 std::string_view name = has_comp_level ? full_name.substr(0, separator) : full_name;
2844
2845 for (const auto &slf : _saveload_formats) {
2846 if (slf.init_write != nullptr && name == slf.name) {
2847 if (has_comp_level) {
2848 auto complevel = full_name.substr(separator + 1);
2849
2850 /* Get the level and determine whether all went fine. */
2851 auto level = ParseInteger<uint8_t>(complevel);
2852 if (!level.has_value() || *level != Clamp(*level, slf.min_compression, slf.max_compression)) {
2854 GetEncodedString(STR_CONFIG_ERROR),
2855 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2857 } else {
2858 return {slf, *level};
2859 }
2860 }
2861 return {slf, slf.default_compression};
2862 }
2863 }
2864
2866 GetEncodedString(STR_CONFIG_ERROR),
2867 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2869 }
2870 return {def, def.default_compression};
2871}
2872
2873/* actual loader/saver function */
2874void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2875extern bool AfterLoadGame();
2876extern bool LoadOldSaveGame(std::string_view file);
2877
2883static void ResetSettings()
2884{
2885 for (auto &desc : GetSaveLoadSettingTable()) {
2886 const SettingDesc *sd = GetSettingDesc(desc);
2887 if (sd->flags.Test(SettingFlag::NotInSave)) continue;
2889
2891 }
2892}
2893
2894extern void ClearOldOrders();
2895
2900{
2902 ResetTempEngineData();
2903 ClearRailTypeLabelList();
2904 ClearRoadTypeLabelList();
2905 ResetOldWaypoints();
2906 ResetSettings();
2907}
2908
2912static inline void ClearSaveLoadState()
2913{
2914 _sl.dumper = nullptr;
2915 _sl.sf = nullptr;
2916 _sl.reader = nullptr;
2917 _sl.lf = nullptr;
2918}
2919
2921static void SaveFileStart()
2922{
2923 SetMouseCursorBusy(true);
2924
2925 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_SAVELOAD_START);
2926 _sl.saveinprogress = true;
2927}
2928
2930static void SaveFileDone()
2931{
2932 SetMouseCursorBusy(false);
2933
2934 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_SAVELOAD_FINISH);
2935 _sl.saveinprogress = false;
2936
2937#ifdef __EMSCRIPTEN__
2938 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
2939#endif
2940}
2941
2947{
2948 _sl.error_str = str;
2949}
2950
2956{
2957 return GetEncodedString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
2958}
2959
2965{
2966 return GetEncodedString(_sl.error_str, _sl.extra_msg);
2967}
2968
2975
2982static SaveLoadResult SaveFileToDisk(bool threaded)
2983{
2984 try {
2985 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
2986
2987 /* We have written our stuff to memory, now write it to file! */
2988 uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
2989 _sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
2990
2991 _sl.sf = fmt.init_write(_sl.sf, compression);
2992 _sl.dumper->Flush(_sl.sf);
2993
2995
2996 if (threaded) SetAsyncSaveFinish(SaveFileDone);
2997
2998 return SaveLoadResult::Ok;
2999 } catch (...) {
3001
3003
3004 /* We don't want to shout when saving is just
3005 * cancelled due to a client disconnecting. */
3006 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
3007 Debug(sl, 0, "{} {}", GetSaveLoadErrorType().GetDecodedString(), GetSaveLoadErrorMessage().GetDecodedString());
3008 asfp = SaveFileError;
3009 }
3010
3011 if (threaded) {
3012 SetAsyncSaveFinish(asfp);
3013 } else {
3014 asfp();
3015 }
3016 return SaveLoadResult::Error;
3017 }
3018}
3019
3020void WaitTillSaved()
3021{
3022 if (!_save_thread.joinable()) return;
3023
3024 _save_thread.join();
3025
3026 /* Make sure every other state is handled properly as well. */
3028}
3029
3038static SaveLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
3039{
3040 assert(!_sl.saveinprogress);
3041
3042 _sl.dumper = std::make_unique<MemoryDumper>();
3043 _sl.sf = std::move(writer);
3044
3046
3047 SaveViewportBeforeSaveGame();
3048 SlSaveChunks();
3049
3050 SaveFileStart();
3051
3052 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3053 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3054
3055 SaveLoadResult result = SaveFileToDisk(false);
3056 SaveFileDone();
3057
3058 return result;
3059 }
3060
3061 return SaveLoadResult::Ok;
3062}
3063
3070SaveLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3071{
3072 try {
3073 _sl.action = SLA_SAVE;
3074 return DoSave(std::move(writer), threaded);
3075 } catch (...) {
3077 return SaveLoadResult::Error;
3078 }
3079}
3080
3089static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
3090{
3091 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3092 if (fmt != std::end(_saveload_formats)) {
3093 /* Check version number */
3094 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3095 /* Minor is not used anymore from version 18.0, but it is still needed
3096 * in versions before that (4 cases) which can't be removed easy.
3097 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3098 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3099
3100 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3101
3102 /* Is the version higher than the current? */
3103 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3104 if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
3105 return fmt;
3106 }
3107
3108 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3109 _sl.lf->Reset();
3112
3113 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3114 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3115 if (fmt == std::end(_saveload_formats)) {
3116 /* Who removed the LZO savegame format definition? When built without LZO support,
3117 * the formats must still list it just without a method to read the file.
3118 * The caller of this function has to check for the existence of load function. */
3119 NOT_REACHED();
3120 }
3121 return fmt;
3122}
3123
3130static SaveLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3131{
3132 _sl.lf = std::move(reader);
3133
3134 if (load_check) {
3135 /* Clear previous check data */
3136 _load_check_data.Clear();
3137 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3138 _load_check_data.checkable = true;
3139 }
3140
3141 uint32_t hdr[2];
3142 if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3143
3144 /* see if we have any loader for this type. */
3145 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
3146
3147 /* loader for this savegame type is not implemented? */
3148 if (fmt->init_load == nullptr) {
3149 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3150 }
3151
3152 _sl.lf = fmt->init_load(_sl.lf);
3153 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3154 _next_offs = 0;
3155
3156 if (!load_check) {
3158
3159 /* Old maps were hardcoded to 256x256 and thus did not contain
3160 * any mapsize information. Pre-initialize to 256x256 to not to
3161 * confuse old games */
3162 InitializeGame(256, 256, true, true);
3163
3164 _gamelog.Reset();
3165
3167 /*
3168 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3169 * shared savegame version 4. Anything before that 'obviously'
3170 * does not have any NewGRFs. Between the introduction and
3171 * savegame version 41 (just before 0.5) the NewGRF settings
3172 * were not stored in the savegame and they were loaded by
3173 * using the settings from the main menu.
3174 * So, to recap:
3175 * - savegame version < 4: do not load any NewGRFs.
3176 * - savegame version >= 41: load NewGRFs from savegame, which is
3177 * already done at this stage by
3178 * overwriting the main menu settings.
3179 * - other savegame versions: use main menu settings.
3180 *
3181 * This means that users *can* crash savegame version 4..40
3182 * savegames if they set incompatible NewGRFs in the main menu,
3183 * but can't crash anymore for savegame version < 4 savegames.
3184 *
3185 * Note: this is done here because AfterLoadGame is also called
3186 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3187 */
3189 }
3190 }
3191
3192 if (load_check) {
3193 /* Load chunks into _load_check_data.
3194 * No pools are loaded. References are not possible, and thus do not need resolving. */
3196 } else {
3197 /* Load chunks and resolve references */
3198 SlLoadChunks();
3199 SlFixPointers();
3200 }
3201
3203
3205
3206 if (load_check) {
3207 /* The only part from AfterLoadGame() we need */
3208 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
3209 } else {
3210 _gamelog.StartAction(GLAT_LOAD);
3211
3212 /* After loading fix up savegame for any internal changes that
3213 * might have occurred since then. If it fails, load back the old game. */
3214 if (!AfterLoadGame()) {
3215 _gamelog.StopAction();
3217 }
3218
3219 _gamelog.StopAction();
3220 }
3221
3222 return SaveLoadResult::Ok;
3223}
3224
3230SaveLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3231{
3232 try {
3233 _sl.action = SLA_LOAD;
3234 return DoLoad(std::move(reader), false);
3235 } catch (...) {
3238 }
3239}
3240
3251SaveLoadResult SaveOrLoad(std::string_view filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3252{
3253 /* An instance of saving is already active, so don't go saving again */
3254 if (_sl.saveinprogress && fop == SaveLoadOperation::Save && dft == DetailedFileType::GameFile && threaded) {
3255 /* if not an autosave, but a user action, show error message */
3256 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WarningLevel::Error);
3257 return SaveLoadResult::Ok;
3258 }
3259 WaitTillSaved();
3260
3261 try {
3262 /* Load a TTDLX or TTDPatch game */
3265
3266 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3267
3268 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3269 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3270 * Note: this is done here because AfterLoadGame is also called
3271 * for OTTD savegames which have their own NewGRF logic. */
3273 _gamelog.Reset();
3274 if (!LoadOldSaveGame(filename)) return SaveLoadResult::ReInit;
3277 _gamelog.StartAction(GLAT_LOAD);
3278 if (!AfterLoadGame()) {
3279 _gamelog.StopAction();
3281 }
3282 _gamelog.StopAction();
3283 return SaveLoadResult::Ok;
3284 }
3285
3286 assert(dft == DetailedFileType::GameFile);
3287 switch (fop) {
3289 _sl.action = SLA_LOAD_CHECK;
3290 break;
3291
3293 _sl.action = SLA_LOAD;
3294 break;
3295
3297 _sl.action = SLA_SAVE;
3298 break;
3299
3300 default: NOT_REACHED();
3301 }
3302
3303 auto fh = (fop == SaveLoadOperation::Save) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3304
3305 /* Make it a little easier to load savegames from the console */
3306 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Save);
3307 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Base);
3308 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Scenario);
3309
3310 if (!fh.has_value()) {
3311 SlError(fop == SaveLoadOperation::Save ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3312 }
3313
3314 if (fop == SaveLoadOperation::Save) { // SAVE game
3315 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3316 if (!_settings_client.gui.threaded_saves) threaded = false;
3317
3318 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3319 }
3320
3321 /* LOAD game */
3322 assert(fop == SaveLoadOperation::Load || fop == SaveLoadOperation::Check);
3323 Debug(desync, 1, "load: {}", filename);
3324 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SaveLoadOperation::Check);
3325 } catch (...) {
3326 /* This code may be executed both for old and new save games. */
3328
3329 if (fop != SaveLoadOperation::Check) Debug(sl, 0, "{} {}", GetSaveLoadErrorType().GetDecodedString(), GetSaveLoadErrorMessage().GetDecodedString());
3330
3331 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3333 }
3334}
3335
3341{
3342 std::string filename;
3343
3344 if (_settings_client.gui.keep_all_autosave) {
3345 filename = GenerateDefaultSaveName() + counter.Extension();
3346 } else {
3347 filename = counter.Filename();
3348 }
3349
3350 Debug(sl, 2, "Autosaving to '{}'", filename);
3352 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WarningLevel::Error);
3353 }
3354}
3355
3356
3362
3368{
3369 /* Check if we have a name for this map, which is the name of the first
3370 * available company. When there's no company available we'll use
3371 * 'Spectator' as "company" name. */
3372 CompanyID cid = _local_company;
3373 if (!Company::IsValidID(cid)) {
3374 for (const Company *c : Company::Iterate()) {
3375 cid = c->index;
3376 break;
3377 }
3378 }
3379
3380 std::array<StringParameter, 4> params{};
3381 auto it = params.begin();
3382 *it++ = cid;
3383
3384 /* We show the current game time differently depending on the timekeeping units used by this game. */
3386 /* Insert time played. */
3387 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3388 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3389 *it++ = play_time / 60 / 60;
3390 *it++ = (play_time / 60) % 60;
3391 } else {
3392 /* Insert current date */
3393 switch (_settings_client.gui.date_format_in_default_names) {
3394 case 0: *it++ = STR_JUST_DATE_LONG; break;
3395 case 1: *it++ = STR_JUST_DATE_TINY; break;
3396 case 2: *it++ = STR_JUST_DATE_ISO; break;
3397 default: NOT_REACHED();
3398 }
3399 *it++ = TimerGameEconomy::date;
3400 }
3401
3402 /* Get the correct string (special string for when there's not company) */
3403 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3404 SanitizeFilename(filename);
3405 return filename;
3406}
3407
3414{
3417 this->ftype = FIOS_TYPE_INVALID;
3418 return;
3419 }
3420
3421 this->file_op = fop;
3422 this->ftype = ft;
3423}
3424
3430{
3431 this->SetMode(item.type);
3432 this->name = item.name;
3433 this->title = item.title;
3434}
3435
3437{
3438 assert(this->load_description.has_value());
3439 return *this->load_description;
3440}
Base class for autoreplaces/autorenews.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr enable_if_t< is_integral_v< T >, T > byteswap(T x) noexcept
Custom implementation of std::byteswap; remove once we build with C++23.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
void PutUtf8(char32_t c)
Append UTF-8 char.
void Put(std::string_view str)
Append string.
void PutIntegerBase(T value, int base)
Append integer 'value' in given number 'base'.
Container for an encoded string, created by GetEncodedString.
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:541
std::optional< std::vector< SaveLoad > > load_description
Description derived from savegame being loaded.
Definition saveload.h:543
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.
SaveLoadCompatTable GetCompatDescription() const override
Get the pre-header description of the fields in the savegame.
SaveLoadTable GetDescription() const override
Get the 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.
Compose data into a growing std::string.
Parse data from a string / buffer.
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
@ READ_ONE_SEPARATOR
Read one separator, and include it in the result.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
std::optional< char32_t > TryReadUtf8()
Try to read a UTF-8 character, and then advance reader.
T ReadIntegerBase(int base, T def=0, bool clamp=false)
Read and parse an integer in number 'base', and advance the reader.
bool ReadUtf8If(char32_t c)
Check whether the next UTF-8 char matches 'c', and skip it.
std::string_view ReadUntilUtf8(char32_t c, SeparatorUsage sep)
Read data until the first occurrence of UTF-8 char 'c', and advance reader.
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.
Functions related to companies.
@ SCC_ENCODED
Encoded string marker and sub-string parameter.
@ SCC_ENCODED_NUMERIC
Encoded numeric parameter.
@ SCC_ENCODED_STRING
Encoded string parameter.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Function to handling different endian machines.
Functions related to errors.
@ Critical
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
@ Error
Errors (eg. saving/loading failed).
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition fileio.cpp:1057
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:249
Functions for standard in/out file operations.
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:52
@ Check
Load file for checking and/or preview.
Definition fileio_type.h:53
@ Invalid
Unknown file operation.
Definition fileio_type.h:57
@ Save
File is being saved.
Definition fileio_type.h:55
@ Load
File is being loaded.
Definition fileio_type.h:54
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:28
@ OldGameFile
Old save game or scenario file.
Definition fileio_type.h:30
@ GameFile
Save game or scenario file.
Definition fileio_type.h:31
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition fileio_type.h:88
@ Base
Base directory for all subdirectories.
Definition fileio_type.h:89
@ Autosave
Subdirectory of save for autosaves.
Definition fileio_type.h:91
@ Scenario
Base directory for all scenarios.
Definition fileio_type.h:92
@ Save
Base directory for all savegames.
Definition fileio_type.h:90
@ Invalid
Invalid or unknown file type.
Definition fileio_type.h:24
@ None
nothing to do
Definition fileio_type.h:18
Declarations for savegames operations.
LoadCheckData _load_check_data
Data loaded from save during SL_LOAD_CHECK.
Definition fios_gui.cpp:41
fluid_settings_t * settings
FluidSynth settings handle.
uint32_t _ttdp_version
version of TTDP savegame (if applicable)
Definition saveload.cpp:80
SaveLoadVersion _sl_version
the major savegame version identifier
Definition saveload.cpp:81
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:82
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
SavegameType _savegame_type
type of savegame we are loading
Definition saveload.cpp:77
const SaveLoadVersion SAVEGAME_VERSION
current savegame version
Functions to be called to log fundamental changes to the game.
@ GLAT_LOAD
Game loaded.
Definition gamelog.h:18
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1694
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:52
Declaration of link graph classes used for cargo distribution.
Declaration of link graph job classes used for cargo distribution.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _networking
are we in networking mode?
Definition network.cpp:67
bool _network_server
network-server is active
Definition network.cpp:68
Basic functions/variables used all over the place.
GRFConfigList _grfconfig
First item in list of current GRF set up.
GRFListCompatibility IsGoodGRFConfigList(GRFConfigList &grfconfig)
Check if all GRFs in the GRF config from a savegame can be loaded.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
NewGRF handling of rail types.
NewGRF handling of road types.
uint8_t ReadByte(LoadgameState &ls)
Reads a byte from the buffer and decompress if needed.
Definition oldloader.cpp:86
Base class for roadstops.
A number of safeguards to prevent using unsafe methods.
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:339
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:576
void ProcessAsyncSaveFinish()
Handle async save finishes.
Definition saveload.cpp:394
void FixSCCEncodedNegative(std::string &str)
Scan the string for SCC_ENCODED_NUMERIC with negative values, and reencode them as uint64_t.
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...
static SaveLoadResult DoSave(std::shared_ptr< SaveFilter > writer, bool threaded)
Actually perform the saving of the savegame.
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:83
static void SaveFileDone()
Update the gui accordingly when saving is done and release locks on saveload.
SaveLoadVersion _sl_version
the major savegame version identifier
Definition saveload.cpp:81
SaveLoadResult SaveOrLoad(std::string_view filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
Main Save or Load function where the high-level saveload functions are handled.
static uint32_t ReferenceToInt(const void *obj, SLRefType rt)
Pointers cannot be saved to a savegame, so this functions gets the index of the item,...
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:220
static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
Return the size in bytes of a vector.
static void ResetSaveloadData()
Clear temporary data that is passed between various saveload phases.
static void SlWriteSimpleGamma(size_t i)
Write the header descriptor of an object or an array.
Definition saveload.cpp:512
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:84
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:376
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:377
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:611
std::vector< SaveLoad > SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
Load a table header in a savegame compatible way.
static void ResetSettings()
Reset all settings to their default, so any settings missing in the savegame are their default,...
void SlWriteByte(uint8_t b)
Wrapper for writing a byte to the dumper.
Definition saveload.cpp:419
size_t SlGetStructListLength(size_t limit)
Get the length of this list; if it exceeds the limit, error out.
static SaveLoadResult DoLoad(std::shared_ptr< LoadFilter > reader, bool load_check)
Actually perform the loading of a "non-old" savegame.
SaveLoadResult SaveWithFilter(std::shared_ptr< SaveFilter > writer, bool threaded)
Save the game using a (writer) filter.
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:847
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:375
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:685
static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
Called by save thread to tell we finished saving.
Definition saveload.cpp:383
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.
size_t SlGetFieldLength()
Get the length of the current object.
Definition saveload.cpp:811
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:668
NeedLength
Definition saveload.cpp:95
@ NL_WANTLENGTH
writing length and data
Definition saveload.cpp:97
@ NL_NONE
not working in NeedLength mode
Definition saveload.cpp:96
@ NL_CALCLENGTH
need to calculate the length
Definition saveload.cpp:98
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:314
static void SlStdString(void *ptr, VarType conv)
Save/Load a std::string.
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?
EncodedString GetSaveLoadErrorType()
Return the appropriate initial string for an error depending on whether we are saving or loading.
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:369
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:727
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:950
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:470
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:87
@ SLA_LOAD
loading
Definition saveload.cpp:88
@ SLA_NULL
null all pointers (on loading error)
Definition saveload.cpp:91
@ SLA_SAVE
saving
Definition saveload.cpp:89
@ SLA_LOAD_CHECK
partial loading into _load_check_data
Definition saveload.cpp:92
@ SLA_PTRS
fixing pointers
Definition saveload.cpp:90
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:791
static void SlCopyInternal(void *object, size_t length, VarType conv)
Internal function to save/Load a list of SL_VARs.
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.
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:873
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:78
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:739
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:410
void ClearOldOrders()
Clear all old orders.
Definition order_sl.cpp:114
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:218
void DoExitSave()
Do a save when exiting the game (_settings_client.gui.autosave_on_exit).
static void SlLoadChunks()
Load all chunks.
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:641
static void SlSaveChunk(const ChunkHandler &ch)
Save a chunk of data (eg.
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.
EncodedString GetSaveLoadErrorMessage()
Return the description of the error.
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:823
SaveLoadResult LoadWithFilter(std::shared_ptr< LoadFilter > reader)
Load the game using a (reader) filter.
static void SlVector(void *vector, VarType conv)
Save/load a std::vector.
static void SaveFileError()
Show a gui message when saving has failed.
static SaveLoadResult SaveFileToDisk(bool threaded)
We have written the whole game into memory, _memory_savegame, now find and appropriate compressor and...
void SlGlobList(const SaveLoadTable &slt)
Save or Load (a list of) global variables.
static std::pair< const SaveLoadFormat &, uint8_t > GetSavegameFormat(std::string_view full_name)
Return the savegameformat of the game.
void FixSCCEncoded(std::string &str, bool fix_code)
Scan the string for old values of SCC_ENCODED and fix it to it's new, value.
Definition saveload.cpp:967
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:102
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.
void SlReadString(std::string &str, size_t length)
Read the given amount of bytes from the buffer into the string.
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:541
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.
SavegameType
Types of save games.
Definition saveload.h:442
@ SGT_OTTD
OTTD savegame.
Definition saveload.h:446
@ SLE_VAR_NULL
useful to write zeros in savegame.
Definition saveload.h:693
@ SLE_FILE_END
Used to mark end-of-header in tables.
Definition saveload.h:666
@ SLE_FILE_TYPE_MASK
Mask to get the file-type (and not any flags).
Definition saveload.h:680
@ SLE_FILE_HAS_LENGTH_FIELD
Bit stored in savegame to indicate field has a length field for each entry.
Definition saveload.h:681
@ SLF_REPLACE_TABCRLF
Replace tabs, cr and lf in the string with spaces.
Definition saveload.h:730
@ SLF_ALLOW_NEWLINE
Allow new lines in the strings.
Definition saveload.h:729
@ SLF_ALLOW_CONTROL
Allow control codes in the strings.
Definition saveload.h:728
@ SLE_VAR_STR
string pointer
Definition saveload.h:694
@ SLE_VAR_NAME
old custom name to be converted to a string pointer
Definition saveload.h:696
@ SLE_VAR_STRQ
string pointer enclosed in quotes
Definition saveload.h:695
@ SLE_FILE_STRINGID
StringID offset into strings-array.
Definition saveload.h:675
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:1379
constexpr VarType GetVarFileType(VarType type)
Get the FileType of a setting.
Definition saveload.h:802
SLRefType
Type of reference (SLE_REF, SLE_CONDREF).
Definition saveload.h:641
@ REF_VEHICLE_OLD
Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
Definition saveload.h:645
@ REF_LINK_GRAPH_JOB
Load/save a reference to a link graph job.
Definition saveload.h:652
@ REF_TOWN
Load/save a reference to a town.
Definition saveload.h:644
@ REF_LINK_GRAPH
Load/save a reference to a link graph.
Definition saveload.h:651
@ REF_CARGO_PACKET
Load/save a reference to a cargo packet.
Definition saveload.h:648
@ REF_ENGINE_RENEWS
Load/save a reference to an engine renewal (autoreplace).
Definition saveload.h:647
@ REF_STATION
Load/save a reference to a station.
Definition saveload.h:643
@ REF_ORDERLIST
Load/save a reference to an orderlist.
Definition saveload.h:649
@ REF_STORAGE
Load/save a reference to a persistent storage.
Definition saveload.h:650
@ REF_VEHICLE
Load/save a reference to a vehicle.
Definition saveload.h:642
@ REF_ROADSTOPS
Load/save a reference to a bus/truck stop.
Definition saveload.h:646
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1333
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:532
SaveLoadType
Type of data saved.
Definition saveload.h:736
@ SL_NULL
Save null-bytes and load to nowhere.
Definition saveload.h:749
@ SL_STRUCTLIST
Save/load a list of structs.
Definition saveload.h:746
@ SL_STDSTR
Save/load a std::string.
Definition saveload.h:741
@ SL_REF
Save/load a reference.
Definition saveload.h:738
@ SL_SAVEBYTE
Save (but not load) a byte.
Definition saveload.h:748
@ SL_STRUCT
Save/load a struct.
Definition saveload.h:739
@ SL_VECTOR
Save/load a vector of SL_VAR elements.
Definition saveload.h:744
@ SL_REFVECTOR
Save/load a vector of SL_REF elements.
Definition saveload.h:751
@ SL_REFLIST
Save/load a list of SL_REF elements.
Definition saveload.h:745
@ SL_ARR
Save/load a fixed-size array of SL_VAR elements.
Definition saveload.h:743
@ SL_VAR
Save/load a variable.
Definition saveload.h:737
std::span< const struct SaveLoadCompat > SaveLoadCompatTable
A table of SaveLoadCompat entries.
Definition saveload.h:538
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1292
SaveLoadResult
Save or load result codes.
Definition saveload.h:424
@ Error
error that was caught before internal structures were modified
Definition saveload.h:426
@ Ok
completed successfully
Definition saveload.h:425
@ ReInit
error that was caught in the middle of updating game state, need to clear it. (can only happen during...
Definition saveload.h:427
constexpr VarType GetVarMemType(VarType type)
Get the NumberType of a setting.
Definition saveload.h:791
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_FIX_SCC_ENCODED_NEGATIVE
353 PR#14049 Fix encoding of negative parameters.
Definition saveload.h:403
@ 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:420
@ 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_ENCODED_STRING_FORMAT
350 PR#13499 Encoded String format changed.
Definition saveload.h:400
@ SLV_169
169 23816
Definition saveload.h:245
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
std::span< const struct SaveLoad > SaveLoadTable
A table of SaveLoad entries.
Definition saveload.h:535
@ CH_TYPE_MASK
All ChunkType values have to be within this mask.
Definition saveload.h:477
@ CH_READONLY
Chunk is never saved.
Definition saveload.h:478
void SlErrorCorruptFmt(const fmt::format_string< Args... > format, Args &&... fmt_args)
Issue an SlErrorCorrupt with a format string.
Declaration of filters used for saving and loading savegames.
std::shared_ptr< SaveFilter > CreateSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Instantiator for a save filter.
std::shared_ptr< LoadFilter > CreateLoadFilter(std::shared_ptr< LoadFilter > chain)
Instantiator for a load filter.
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.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
SettingTable GetSaveLoadSettingTable()
Create a single table with all settings that should be stored/loaded in the savegame.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Functions and types used internally for the settings configurations.
@ NotInSave
Do not save with savegame, basically client-based.
@ NoNetworkSync
Do not synchronize over network (but it is saved if SettingFlag::NotInSave is not set).
static constexpr const SettingDesc * GetSettingDesc(const SettingVariant &desc)
Helper to convert the type of the iterated settings description to a pointer to it.
Base classes/functions for stations.
Functions, definitions and such used only by the GUI.
@ SBI_SAVELOAD_FINISH
finished saving
@ SBI_SAVELOAD_START
started saving
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
void StrMakeValidInPlace(char *str, StringValidationSettings settings)
Scans the string for invalid characters and replaces them with a question mark '?
Definition string.cpp:157
Compose strings from textual and binary data.
Parse strings.
static std::optional< T > ParseInteger(std::string_view arg, int base=10, bool clamp=false)
Change a string into its number representation.
Functions related to low-level strings.
@ ReplaceWithQuestionMark
Replace the unknown/bad bits with question marks.
Definition string_type.h:45
@ AllowControlCode
Allow the special control codes.
Definition string_type.h:47
@ AllowNewline
Allow newlines; replaces '\r ' with ' ' during processing.
Definition string_type.h:46
@ ReplaceTabCrNlWithSpace
Replace tabs ('\t'), carriage returns ('\r') and newlines (' ') with spaces.
Definition string_type.h:53
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
Types related to strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Container for cargo from the same location and time.
Definition cargopacket.h:41
Handlers and description of chunk.
Definition saveload.h:482
ChunkType type
Type of the chunk.
Definition saveload.h:484
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:483
virtual void Save() const
Save the chunk.
Definition saveload.h:495
Struct to store engine replacements.
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() override
Make sure everything is cleaned up.
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:431
void SetMode(const FiosType &ft, SaveLoadOperation fop=SaveLoadOperation::Load)
Set the mode and file type of the file to save or load.
FiosType ftype
File type.
Definition saveload.h:433
SaveLoadOperation file_op
File operation to perform.
Definition saveload.h:432
std::string name
Name of the file.
Definition saveload.h:434
EncodedString title
Internal name of the game.
Definition saveload.h:435
void Set(const FiosItem &item)
Set the mode, title and name of the file.
std::optional< FileHandle > file
The file to write to.
~FileWriter() override
Make sure everything is cleaned up.
FileWriter(FileHandle &&file)
Create the file writer, so it writes to a specific file.
void Finish() override
Prepare everything to finish writing the savegame.
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:78
A savegame name automatically numbered.
Definition fios.h:119
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
Definition fios.cpp:723
std::string Extension()
Generate an extension for a savegame name.
Definition fios.cpp:733
Elements of a file system that are recognized.
Definition fileio_type.h:63
AbstractFileType abstract
Abstract file type.
Definition fileio_type.h:64
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.
~LZMALoadFilter() override
Clean everything up.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
LZMALoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
~LZMASaveFilter() override
Clean up what we allocated.
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.
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.
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.
std::shared_ptr< LoadFilter > chain
Chained to the (savegame) filters.
LoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Container for dumping the savegame (quickly) to memory.
Definition saveload.cpp:146
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:148
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:155
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:147
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:149
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:189
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:170
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.
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:384
Class for pooled persistent storage of data.
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static OrderList * Get(auto index)
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:107
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:116
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:110
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:138
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:109
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:106
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:108
A Stop for a Road Vehicle.
SaveFilter(std::shared_ptr< SaveFilter > chain)
Initialise this filter.
std::shared_ptr< SaveFilter > chain
Chained to the (savegame) filters.
The format for a reader/writer type of a savegame.
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.
std::string_view name
name of the compressor/decompressor (debug-only)
uint8_t max_compression
the maximum compression level of this format
The saveload struct, containing reader-writer functions, buffer, version, etc.
Definition saveload.cpp:196
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:209
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:207
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:206
StringID error_str
the translatable error message to show
Definition saveload.cpp:212
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:197
std::string extra_msg
the error message
Definition saveload.cpp:213
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:198
uint8_t block_mode
???
Definition saveload.cpp:199
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:215
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:210
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:204
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:202
bool error
did an error occur or not
Definition saveload.cpp:200
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:203
SaveLoad type struct.
Definition saveload.h:757
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:761
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:766
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:763
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:759
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:758
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:760
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:762
Properties of config file settings.
SettingFlags flags
Handles how a setting would show up in the GUI (text/currency, etc.).
virtual void ResetToDefault(void *object) const =0
Reset the setting to its default value.
static Station * Get(auto index)
Station data structure.
Town data structure.
Definition town.h:63
Vehicle data structure.
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() override
Clean everything up.
z_stream z
Stream state we are reading from.
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.
~ZlibSaveFilter() override
Clean up what we allocated.
void Finish() override
Prepare everything to finish writing the savegame.
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.
Base of all threads.
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Definition thread.h:24
bool StartNewThread(std::thread *thr, std::string_view name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
Definition of the game-economy-timer.
Base of the town class.
Base class for all vehicles.
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:3322
Window functions not directly related to making/drawing windows.