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