OpenTTD Source 20260731-master-g77ba2b244a
saveload.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
22
23#include "../stdafx.h"
24#include "../debug.h"
25#include "../station_base.h"
26#include "../thread.h"
27#include "../town.h"
28#include "../network/network.h"
29#include "../window_func.h"
30#include "../strings_func.h"
34#include "../vehicle_base.h"
35#include "../company_func.h"
37#include "../autoreplace_base.h"
38#include "../roadstop_base.h"
41#include "../statusbar_gui.h"
42#include "../fileio_func.h"
43#include "../gamelog.h"
44#include "../string_func.h"
45#include "../fios.h"
46#include "../error.h"
47#include "../strings_type.h"
48#include "../newgrf_railtype.h"
49#include "../newgrf_roadtype.h"
51#include "saveload_internal.h"
52#include "saveload_filter.h"
53
54#include <atomic>
55#ifdef __EMSCRIPTEN__
56# include <emscripten.h>
57#endif
58
59#ifdef WITH_LZO
60#include <lzo/lzo1x.h>
61#endif
62
63#if defined(WITH_ZLIB)
64#include <zlib.h>
65#endif /* WITH_ZLIB */
66
67#if defined(WITH_LIBLZMA)
68#include <lzma.h>
69#endif /* WITH_LIBLZMA */
70
71#include "table/strings.h"
72
73#include "../safeguards.h"
74
76
79
80uint32_t _ttdp_version;
83std::string _savegame_format;
85
87enum class SaveLoadAction : uint8_t {
93};
94
95enum class NeedLength : uint8_t {
99};
100
102static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
103
107 uint8_t *bufp = nullptr;
108 uint8_t *bufe = nullptr;
109 std::shared_ptr<LoadFilter> reader{};
110 size_t read = 0;
111
116 ReadBuffer(std::shared_ptr<LoadFilter> reader) : reader(std::move(reader))
117 {
118 }
119
120 inline uint8_t ReadByte()
121 {
122 if (this->bufp == this->bufe) {
123 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
124 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
125
126 this->read += len;
127 this->bufp = this->buf;
128 this->bufe = this->buf + len;
129 }
130
131 return *this->bufp++;
132 }
133
138 size_t GetSize() const
139 {
140 return this->read - (this->bufe - this->bufp);
141 }
142};
143
144
147 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
148 uint8_t *buf = nullptr;
149 uint8_t *bufe = nullptr;
150
155 inline void WriteByte(uint8_t b)
156 {
157 /* Are we at the end of this chunk? */
158 if (this->buf == this->bufe) {
159 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
160 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
161 }
162
163 *this->buf++ = b;
164 }
165
170 void Flush(std::shared_ptr<SaveFilter> writer)
171 {
172 uint i = 0;
173 size_t t = this->GetSize();
174
175 while (t > 0) {
176 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
177
178 writer->Write(this->blocks[i++].get(), to_write);
179 t -= to_write;
180 }
181
182 writer->Finish();
183 }
184
189 size_t GetSize() const
190 {
191 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
192 }
193};
194
200 bool error;
201
202 size_t obj_len;
203 int array_index, last_array_index;
205
206 std::unique_ptr<MemoryDumper> dumper;
207 std::shared_ptr<SaveFilter> sf;
208
209 std::unique_ptr<ReadBuffer> reader;
210 std::shared_ptr<LoadFilter> lf;
211
213 std::string extra_msg;
214
216};
217
219
220static const std::vector<ChunkHandlerRef> &ChunkHandlers()
221{
222 /* These define the chunks */
223 extern const ChunkHandlerTable _gamelog_chunk_handlers;
224 extern const ChunkHandlerTable _map_chunk_handlers;
225 extern const ChunkHandlerTable _misc_chunk_handlers;
226 extern const ChunkHandlerTable _name_chunk_handlers;
227 extern const ChunkHandlerTable _cheat_chunk_handlers;
228 extern const ChunkHandlerTable _setting_chunk_handlers;
229 extern const ChunkHandlerTable _company_chunk_handlers;
230 extern const ChunkHandlerTable _engine_chunk_handlers;
231 extern const ChunkHandlerTable _veh_chunk_handlers;
232 extern const ChunkHandlerTable _waypoint_chunk_handlers;
233 extern const ChunkHandlerTable _depot_chunk_handlers;
234 extern const ChunkHandlerTable _order_chunk_handlers;
235 extern const ChunkHandlerTable _town_chunk_handlers;
236 extern const ChunkHandlerTable _sign_chunk_handlers;
237 extern const ChunkHandlerTable _station_chunk_handlers;
238 extern const ChunkHandlerTable _industry_chunk_handlers;
239 extern const ChunkHandlerTable _economy_chunk_handlers;
240 extern const ChunkHandlerTable _subsidy_chunk_handlers;
241 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
242 extern const ChunkHandlerTable _goal_chunk_handlers;
243 extern const ChunkHandlerTable _story_page_chunk_handlers;
244 extern const ChunkHandlerTable _league_chunk_handlers;
245 extern const ChunkHandlerTable _ai_chunk_handlers;
246 extern const ChunkHandlerTable _game_chunk_handlers;
247 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
248 extern const ChunkHandlerTable _newgrf_chunk_handlers;
249 extern const ChunkHandlerTable _group_chunk_handlers;
250 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
251 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
252 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
253 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
254 extern const ChunkHandlerTable _airport_chunk_handlers;
255 extern const ChunkHandlerTable _object_chunk_handlers;
256 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
257 extern const ChunkHandlerTable _water_region_chunk_handlers;
258 extern const ChunkHandlerTable _randomizer_chunk_handlers;
259
261 static const ChunkHandlerTable _chunk_handler_tables[] = {
262 _gamelog_chunk_handlers,
263 _map_chunk_handlers,
264 _misc_chunk_handlers,
265 _name_chunk_handlers,
266 _cheat_chunk_handlers,
267 _setting_chunk_handlers,
268 _veh_chunk_handlers,
269 _waypoint_chunk_handlers,
270 _depot_chunk_handlers,
271 _order_chunk_handlers,
272 _industry_chunk_handlers,
273 _economy_chunk_handlers,
274 _subsidy_chunk_handlers,
275 _cargomonitor_chunk_handlers,
276 _goal_chunk_handlers,
277 _story_page_chunk_handlers,
278 _league_chunk_handlers,
279 _engine_chunk_handlers,
280 _town_chunk_handlers,
281 _sign_chunk_handlers,
282 _station_chunk_handlers,
283 _company_chunk_handlers,
284 _ai_chunk_handlers,
285 _game_chunk_handlers,
286 _animated_tile_chunk_handlers,
287 _newgrf_chunk_handlers,
288 _group_chunk_handlers,
289 _cargopacket_chunk_handlers,
290 _autoreplace_chunk_handlers,
291 _labelmaps_chunk_handlers,
292 _linkgraph_chunk_handlers,
293 _airport_chunk_handlers,
294 _object_chunk_handlers,
295 _persistent_storage_chunk_handlers,
296 _water_region_chunk_handlers,
297 _randomizer_chunk_handlers,
298 };
299
300 static std::vector<ChunkHandlerRef> _chunk_handlers;
301
302 if (_chunk_handlers.empty()) {
303 for (auto &chunk_handler_table : _chunk_handler_tables) {
304 for (auto &chunk_handler : chunk_handler_table) {
305 _chunk_handlers.push_back(chunk_handler);
306 }
307 }
308 }
309
310 return _chunk_handlers;
311}
312
314static void SlNullPointers()
315{
316 _sl.action = SaveLoadAction::Null;
317
318 /* We don't want any savegame conversion code to run
319 * during NULLing; especially those that try to get
320 * pointers from other pools. */
322
323 for (const ChunkHandler &ch : ChunkHandlers()) {
324 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
325 ch.FixPointers();
326 }
327
328 assert(_sl.action == SaveLoadAction::Null);
329}
330
339[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
340{
341 /* Distinguish between loading into _load_check_data vs. normal save/load. */
342 if (_sl.action == SaveLoadAction::LoadCheck) {
343 _load_check_data.error = string;
344 _load_check_data.error_msg = extra_msg;
345 } else {
346 _sl.error_str = string;
347 _sl.extra_msg = extra_msg;
348 }
349
350 /* We have to nullptr all pointers here; we might be in a state where
351 * the pointers are actually filled with indices, which means that
352 * when we access them during cleaning the pool dereferences of
353 * those indices will be made with segmentation faults as result. */
354 if (_sl.action == SaveLoadAction::Load || _sl.action == SaveLoadAction::Ptrs) SlNullPointers();
355
356 /* Logging could be active. */
357 _gamelog.StopAnyAction();
358
359 throw std::exception();
360}
361
369[[noreturn]] void SlErrorCorrupt(const std::string &msg)
370{
371 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
372}
373
374
375typedef void (*AsyncSaveFinishProc)();
376static std::atomic<AsyncSaveFinishProc> _async_save_finish;
377static std::thread _save_thread;
378
384{
385 if (_exit_game) return;
386 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
387
388 _async_save_finish.store(proc, std::memory_order_release);
389}
390
395{
396 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
397 if (proc == nullptr) return;
398
399 proc();
400
401 if (_save_thread.joinable()) {
402 _save_thread.join();
403 }
404}
405
410uint8_t SlReadByte()
411{
412 return _sl.reader->ReadByte();
413}
414
419void SlWriteByte(uint8_t b)
420{
421 _sl.dumper->WriteByte(b);
422}
423
424static inline int SlReadUint16()
425{
426 int x = SlReadByte() << 8;
427 return x | SlReadByte();
428}
429
430static inline uint32_t SlReadUint32()
431{
432 uint32_t x = SlReadUint16() << 16;
433 return x | SlReadUint16();
434}
435
436static inline uint64_t SlReadUint64()
437{
438 uint32_t x = SlReadUint32();
439 uint32_t y = SlReadUint32();
440 return static_cast<uint64_t>(x) << 32 | y;
441}
442
443static inline void SlWriteUint16(uint16_t v)
444{
445 SlWriteByte(GB(v, 8, 8));
446 SlWriteByte(GB(v, 0, 8));
447}
448
449static inline void SlWriteUint32(uint32_t v)
450{
451 SlWriteUint16(GB(v, 16, 16));
452 SlWriteUint16(GB(v, 0, 16));
453}
454
455static inline void SlWriteUint64(uint64_t x)
456{
457 SlWriteUint32(static_cast<uint32_t>(x >> 32));
458 SlWriteUint32(static_cast<uint32_t>(x));
459}
460
465static inline ChunkId SlReadChunkId()
466{
467 ChunkId label{};
468 for (uint8_t &b : label) b = SlReadByte();
469 return label;
470}
471
481static uint SlReadSimpleGamma()
482{
483 uint i = SlReadByte();
484 if (HasBit(i, 7)) {
485 i &= ~0x80;
486 if (HasBit(i, 6)) {
487 i &= ~0x40;
488 if (HasBit(i, 5)) {
489 i &= ~0x20;
490 if (HasBit(i, 4)) {
491 i &= ~0x10;
492 if (HasBit(i, 3)) {
493 SlErrorCorrupt("Unsupported gamma");
494 }
495 i = SlReadByte(); // 32 bits only.
496 }
497 i = (i << 8) | SlReadByte();
498 }
499 i = (i << 8) | SlReadByte();
500 }
501 i = (i << 8) | SlReadByte();
502 }
503 return i;
504}
505
522
523static void SlWriteSimpleGamma(size_t i)
524{
525 if (i >= (1 << 7)) {
526 if (i >= (1 << 14)) {
527 if (i >= (1 << 21)) {
528 if (i >= (1 << 28)) {
529 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
530 SlWriteByte(static_cast<uint8_t>(0xF0));
531 SlWriteByte(static_cast<uint8_t>(i >> 24));
532 } else {
533 SlWriteByte(static_cast<uint8_t>(0xE0 | (i >> 24)));
534 }
535 SlWriteByte(static_cast<uint8_t>(i >> 16));
536 } else {
537 SlWriteByte(static_cast<uint8_t>(0xC0 | (i >> 16)));
538 }
539 SlWriteByte(static_cast<uint8_t>(i >> 8));
540 } else {
541 SlWriteByte(static_cast<uint8_t>(0x80 | (i >> 8)));
542 }
543 }
544 SlWriteByte(static_cast<uint8_t>(i));
545}
546
552static inline uint SlGetGammaLength(size_t i)
553{
554 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
555}
556
557static inline uint SlReadSparseIndex()
558{
559 return SlReadSimpleGamma();
560}
561
562static inline void SlWriteSparseIndex(uint index)
563{
564 SlWriteSimpleGamma(index);
565}
566
567static inline uint SlReadArrayLength()
568{
569 return SlReadSimpleGamma();
570}
571
572static inline void SlWriteArrayLength(size_t length)
573{
574 SlWriteSimpleGamma(length);
575}
576
577static inline uint SlGetArrayLength(size_t length)
578{
579 return SlGetGammaLength(length);
580}
581
587 static constexpr uint8_t HAS_FIELD_LENGTH_BIT = 4;
588 uint8_t storage{};
589
592
598 SavegameFileType(VarFileType file_type, bool has_field_length = false) : storage(to_underlying(file_type))
599 {
600 /* 0 is not allowed as it's the end-of-table marker, larger is not allowed due to the field length bit. */
601 assert(IsInsideMM(to_underlying(file_type), 1, 1 << HAS_FIELD_LENGTH_BIT));
602 AssignBit(this->storage, HAS_FIELD_LENGTH_BIT, has_field_length);
603 }
604
609 constexpr bool IsEnd() const { return storage == 0; }
610
615 constexpr bool HasFieldLength() const
616 {
617 assert(!this->IsEnd());
618 return HasBit(storage, HAS_FIELD_LENGTH_BIT);
619 }
620
625 constexpr VarFileType Type() const
626 {
627 assert(!this->IsEnd());
628 return static_cast<VarFileType>(GB(storage, 0, HAS_FIELD_LENGTH_BIT));
629 }
630};
631
638{
639 switch (sld.cmd) {
641 return sld.conv.file;
642
646 return { sld.conv.file, true };
647
650
654
656 return VarFileType::U8;
657
660 return { VarFileType::Struct, true };
661
662 default: NOT_REACHED();
663 }
664}
665
672static inline uint SlCalcConvMemLen(VarMemType conv)
673{
674 switch (conv) {
675 case VarMemType::Bool: return sizeof(bool);
676 case VarMemType::I8: return sizeof(int8_t);
677 case VarMemType::U8: return sizeof(uint8_t);
678 case VarMemType::I16: return sizeof(int16_t);
679 case VarMemType::U16: return sizeof(uint16_t);
680 case VarMemType::I32: return sizeof(int32_t);
681 case VarMemType::U32: return sizeof(uint32_t);
682 case VarMemType::I64: return sizeof(int64_t);
683 case VarMemType::U64: return sizeof(uint64_t);
684 case VarMemType::Null: return 0;
685 case VarMemType::LabelReverse: return sizeof(BaseLabel);
686 case VarMemType::LabelForward: return sizeof(BaseLabel);
687
688 case VarMemType::Str:
689 case VarMemType::StrQ:
690 return SlReadArrayLength();
691
692 case VarMemType::Name:
693 default:
694 NOT_REACHED();
695 }
696}
697
704static inline uint8_t SlCalcConvFileLen(VarType conv)
705{
706 switch (conv.file) {
707 case VarFileType::I8: return sizeof(int8_t);
708 case VarFileType::U8: return sizeof(uint8_t);
709 case VarFileType::I16: return sizeof(int16_t);
710 case VarFileType::U16: return sizeof(uint16_t);
711 case VarFileType::I32: return sizeof(int32_t);
712 case VarFileType::U32: return sizeof(uint32_t);
713 case VarFileType::I64: return sizeof(int64_t);
714 case VarFileType::U64: return sizeof(uint64_t);
715 case VarFileType::StringID: return sizeof(uint16_t);
716
718 return SlReadArrayLength();
719
721 default:
722 NOT_REACHED();
723 }
724}
725
730static inline size_t SlCalcRefLen()
731{
733}
734
735void SlSetArrayIndex(uint index)
736{
737 _sl.need_length = NeedLength::WantLength;
738 _sl.array_index = index;
739}
740
741static size_t _next_offs;
742
748{
749 /* After reading in the whole array inside the loop
750 * we must have read in all the data, so we must be at end of current block. */
751 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
752 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
753 }
754
755 for (;;) {
756 uint length = SlReadArrayLength();
757 if (length == 0) {
758 assert(!_sl.expect_table_header);
759 _next_offs = 0;
760 return -1;
761 }
762
763 _sl.obj_len = --length;
764 _next_offs = _sl.reader->GetSize() + length;
765
766 if (_sl.expect_table_header) {
767 _sl.expect_table_header = false;
768 return INT32_MAX;
769 }
770
771 int index;
772 switch (_sl.chunk_type) {
774 case ChunkType::SparseArray: index = static_cast<int>(SlReadSparseIndex()); break;
775 case ChunkType::Table:
776 case ChunkType::Array: index = _sl.array_index++; break;
777 default:
778 Debug(sl, 0, "SlIterateArray error");
779 return -1; // error
780 }
781
782 if (length != 0) return index;
783 }
784}
785
790{
791 while (SlIterateArray() != -1) {
792 SlSkipBytes(_next_offs - _sl.reader->GetSize());
793 }
794}
795
801void SlSetLength(size_t length)
802{
803 assert(_sl.action == SaveLoadAction::Save);
804
805 switch (_sl.need_length) {
807 _sl.need_length = NeedLength::None;
808 if ((_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable) && _sl.expect_table_header) {
809 _sl.expect_table_header = false;
810 SlWriteArrayLength(length + 1);
811 break;
812 }
813
814 switch (_sl.chunk_type) {
815 case ChunkType::Riff:
816 /* Ugly encoding of >16M RIFF chunks
817 * The lower 24 bits are normal
818 * The uppermost 4 bits are bits 24:27 */
819 assert(length < (1 << 28));
820 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
821 break;
822 case ChunkType::Table:
823 case ChunkType::Array:
824 assert(_sl.last_array_index <= _sl.array_index);
825 while (++_sl.last_array_index <= _sl.array_index) {
826 SlWriteArrayLength(1);
827 }
828 SlWriteArrayLength(length + 1);
829 break;
832 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
833 SlWriteSparseIndex(_sl.array_index);
834 break;
835 default: NOT_REACHED();
836 }
837 break;
838
840 _sl.obj_len += static_cast<int>(length);
841 break;
842
843 default: NOT_REACHED();
844 }
845}
846
853static void SlCopyBytes(void *ptr, size_t length)
854{
855 uint8_t *p = static_cast<uint8_t *>(ptr);
856
857 switch (_sl.action) {
860 for (; length != 0; length--) *p++ = SlReadByte();
861 break;
863 for (; length != 0; length--) SlWriteByte(*p++);
864 break;
865 default: NOT_REACHED();
866 }
867}
868
874{
875 return _sl.obj_len;
876}
877
885int64_t ReadValue(const void *ptr, VarMemType conv)
886{
887 switch (conv) {
888 case VarMemType::Bool: return (*static_cast<const bool *>(ptr) != 0);
889 case VarMemType::I8: return *static_cast<const int8_t *>(ptr);
890 case VarMemType::U8: return *static_cast<const uint8_t *>(ptr);
891 case VarMemType::I16: return *static_cast<const int16_t *>(ptr);
892 case VarMemType::U16: return *static_cast<const uint16_t *>(ptr);
893 case VarMemType::I32: return *static_cast<const int32_t *>(ptr);
894 case VarMemType::U32: return *static_cast<const uint32_t *>(ptr);
895 case VarMemType::I64: return *static_cast<const int64_t *>(ptr);
896 case VarMemType::U64: return *static_cast<const uint64_t *>(ptr);
897 case VarMemType::Null: return 0;
898 default: NOT_REACHED();
899 }
900}
901
909void WriteValue(void *ptr, VarMemType conv, int64_t val)
910{
911 switch (conv) {
912 case VarMemType::Bool: *static_cast<bool *>(ptr) = (val != 0); break;
913 case VarMemType::I8: *static_cast<int8_t *>(ptr) = val; break;
914 case VarMemType::U8: *static_cast<uint8_t *>(ptr) = val; break;
915 case VarMemType::I16: *static_cast<int16_t *>(ptr) = val; break;
916 case VarMemType::U16: *static_cast<uint16_t *>(ptr) = val; break;
917 case VarMemType::I32: *static_cast<int32_t *>(ptr) = val; break;
918 case VarMemType::U32: *static_cast<uint32_t *>(ptr) = val; break;
919 case VarMemType::I64: *static_cast<int64_t *>(ptr) = val; break;
920 case VarMemType::U64: *static_cast<uint64_t *>(ptr) = val; break;
921 case VarMemType::Name: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(static_cast<StringID>(val)); break;
922 case VarMemType::Null: break;
923 default: NOT_REACHED();
924 }
925}
926
935static void SlSaveLoadConv(void *ptr, VarType conv)
936{
937 switch (_sl.action) {
939 if (conv == VarTypes::LABEL_REVERSE) {
940 BaseLabel *label = static_cast<BaseLabel *>(ptr);
941 for (auto it = label->rbegin(); it != label->rend(); it++) SlWriteByte(*it);
942 break;
943 }
944 if (conv == VarTypes::LABEL_FORWARD) {
945 BaseLabel *label = static_cast<BaseLabel *>(ptr);
946 for (auto it = label->begin(); it != label->end(); it++) SlWriteByte(*it);
947 break;
948 }
949
950 int64_t x = ReadValue(ptr, conv.mem);
951
952 /* Write the value to the file and check if its value is in the desired range */
953 switch (conv.file) {
954 case VarFileType::I8:
955 assert(x >= -128 && x <= 127);
956 SlWriteByte(x);
957 break;
958
959 case VarFileType::U8:
960 assert(x >= 0 && x <= 255);
961 SlWriteByte(x);
962 break;
963
964 case VarFileType::I16:
965 assert(x >= -32768 && x <= 32767);
966 SlWriteUint16(x);
967 break;
968
970 case VarFileType::U16:
971 assert(x >= 0 && x <= 65535);
972 SlWriteUint16(x);
973 break;
974
975 case VarFileType::I32:
976 case VarFileType::U32:
977 SlWriteUint32(static_cast<uint32_t>(x));
978 break;
979
980 case VarFileType::I64:
981 case VarFileType::U64:
982 SlWriteUint64(x);
983 break;
984
985 default: NOT_REACHED();
986 }
987 break;
988 }
991 if (conv == VarTypes::LABEL_REVERSE) {
992 BaseLabel *label = static_cast<BaseLabel *>(ptr);
993 for (auto it = label->rbegin(); it != label->rend(); it++) *it = SlReadByte();
994 break;
995 }
996 if (conv == VarTypes::LABEL_FORWARD) {
997 BaseLabel *label = static_cast<BaseLabel *>(ptr);
998 for (auto it = label->begin(); it != label->end(); it++) *it = SlReadByte();
999 break;
1000 }
1001
1002 int64_t x;
1003 /* Read a value from the file */
1004 switch (conv.file) {
1005 case VarFileType::I8: x = static_cast<int8_t>(SlReadByte()); break;
1006 case VarFileType::U8: x = static_cast<uint8_t>(SlReadByte()); break;
1007 case VarFileType::I16: x = static_cast<int16_t>(SlReadUint16()); break;
1008 case VarFileType::U16: x = static_cast<uint16_t>(SlReadUint16()); break;
1009 case VarFileType::I32: x = static_cast<int32_t>(SlReadUint32()); break;
1010 case VarFileType::U32: x = static_cast<uint32_t>(SlReadUint32()); break;
1011 case VarFileType::I64: x = static_cast<int64_t>(SlReadUint64()); break;
1012 case VarFileType::U64: x = static_cast<uint64_t>(SlReadUint64()); break;
1013 case VarFileType::StringID: x = RemapOldStringID(static_cast<StringID>(SlReadUint16())).base(); break;
1014 default: NOT_REACHED();
1015 }
1016
1017 /* Write The value to the struct. These ARE endian safe. */
1018 WriteValue(ptr, conv.mem, x);
1019 break;
1020 }
1021 case SaveLoadAction::Ptrs: break;
1022 case SaveLoadAction::Null: break;
1023 default: NOT_REACHED();
1024 }
1025}
1026
1034static inline size_t SlCalcStdStringLen(const void *ptr)
1035{
1036 const std::string *str = reinterpret_cast<const std::string *>(ptr);
1037
1038 size_t len = str->length();
1039 return len + SlGetArrayLength(len); // also include the length of the index
1040}
1041
1042
1051void FixSCCEncoded(std::string &str, bool fix_code)
1052{
1053 if (str.empty()) return;
1054
1055 /* We need to convert from old escape-style encoding to record separator encoding.
1056 * Initial `<SCC_ENCODED><STRINGID>` stays the same.
1057 *
1058 * `:<SCC_ENCODED><STRINGID>` becomes `<RS><SCC_ENCODED><STRINGID>`
1059 * `:<HEX>` becomes `<RS><SCC_ENCODED_NUMERIC><HEX>`
1060 * `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
1061 */
1062 std::string result;
1063 StringBuilder builder(result);
1064
1065 bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
1066 bool in_string = false; // Set if we in a string, between double-quotes.
1067 bool need_type = true; // Set if a parameter type needs to be emitted.
1068
1069 StringConsumer consumer(str);
1070 while (consumer.AnyBytesLeft()) {
1071 char32_t c;
1072 if (auto r = consumer.TryReadUtf8(); r.has_value()) {
1073 c = *r;
1074 } else {
1075 break;
1076 }
1077 if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
1078 builder.PutUtf8(SCC_ENCODED);
1079 need_type = false;
1080 is_encoded = true;
1081 continue;
1082 }
1083
1084 /* If the first character is not SCC_ENCODED then we don't have to do any conversion. */
1085 if (!is_encoded) return;
1086
1087 if (c == '"') {
1088 in_string = !in_string;
1089 if (in_string && need_type) {
1090 /* Started a new string parameter. */
1091 builder.PutUtf8(SCC_ENCODED_STRING);
1092 need_type = false;
1093 }
1094 continue;
1095 }
1096
1097 if (!in_string && c == ':') {
1098 builder.PutUtf8(SCC_RECORD_SEPARATOR);
1099 need_type = true;
1100 continue;
1101 }
1102 if (need_type) {
1103 /* Started a new numeric parameter. */
1105 need_type = false;
1106 }
1107
1108 builder.PutUtf8(c);
1109 }
1110
1111 str = std::move(result);
1112}
1113
1118void FixSCCEncodedNegative(std::string &str)
1119{
1120 if (str.empty()) return;
1121
1122 StringConsumer consumer(str);
1123
1124 /* Check whether this is an encoded string */
1125 if (!consumer.ReadUtf8If(SCC_ENCODED)) return;
1126
1127 std::string result;
1128 StringBuilder builder(result);
1129 builder.PutUtf8(SCC_ENCODED);
1130 while (consumer.AnyBytesLeft()) {
1131 /* Copy until next record */
1132 builder.Put(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::READ_ONE_SEPARATOR));
1133
1134 /* Check whether this is a numeric parameter */
1135 if (!consumer.ReadUtf8If(SCC_ENCODED_NUMERIC)) continue;
1137
1138 /* First try unsigned */
1139 if (auto u = consumer.TryReadIntegerBase<uint64_t>(16); u.has_value()) {
1140 builder.PutIntegerBase<uint64_t>(*u, 16);
1141 } else {
1142 /* Read as signed, store as unsigned */
1143 auto s = consumer.ReadIntegerBase<int64_t>(16);
1144 builder.PutIntegerBase<uint64_t>(static_cast<uint64_t>(s), 16);
1145 }
1146 }
1147
1148 str = std::move(result);
1149}
1150
1157void SlReadString(std::string &str, size_t length)
1158{
1159 str.resize(length);
1160 SlCopyBytes(str.data(), length);
1161}
1162
1168static void SlStdString(void *ptr, VarType conv)
1169{
1170 std::string *str = reinterpret_cast<std::string *>(ptr);
1171
1172 switch (_sl.action) {
1173 case SaveLoadAction::Save: {
1174 size_t len = str->length();
1175 SlWriteArrayLength(len);
1176 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->data())), len);
1177 break;
1178 }
1179
1181 case SaveLoadAction::Load: {
1182 size_t len = SlReadArrayLength();
1183 if (conv.mem == VarMemType::Null) {
1184 SlSkipBytes(len);
1185 return;
1186 }
1187
1188 SlReadString(*str, len);
1189
1195 }
1197 }
1198
1199 case SaveLoadAction::Ptrs: break;
1200 case SaveLoadAction::Null: break;
1201 default: NOT_REACHED();
1202 }
1203}
1204
1213static void SlCopyInternal(void *object, size_t length, VarType conv)
1214{
1215 if (conv.mem == VarMemType::Null) {
1216 assert(_sl.action != SaveLoadAction::Save); // Use SaveLoadType::Null if you want to write null-bytes
1217 SlSkipBytes(length * SlCalcConvFileLen(conv));
1218 return;
1219 }
1220
1221 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
1222 * as a byte-type. So detect this, and adjust object size accordingly */
1224 /* all objects except difficulty settings */
1225 if (conv == VarTypes::I16 || conv == VarTypes::U16 || conv == VarTypes::STRINGID ||
1226 conv == VarTypes::I32 || conv == VarTypes::U32) {
1227 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
1228 return;
1229 }
1230 /* used for conversion of Money 32bit->64bit */
1231 if (conv == (VarFileType::I32 | VarMemType::I64)) {
1232 for (uint i = 0; i < length; i++) {
1233 static_cast<int64_t *>(object)[i] = std::byteswap(SlReadUint32());
1234 }
1235 return;
1236 }
1237 }
1238
1239 /* If the size of elements is 1 byte both in file and memory, no special
1240 * conversion is needed, use specialized copy-copy function to speed up things */
1241 if (conv == VarTypes::I8 || conv == VarTypes::U8) {
1242 SlCopyBytes(object, length);
1243 } else {
1244 uint8_t *a = static_cast<uint8_t *>(object);
1245 uint8_t mem_size = SlCalcConvMemLen(conv.mem);
1246
1247 for (; length != 0; length --) {
1248 SlSaveLoadConv(a, conv);
1249 a += mem_size; // get size
1250 }
1251 }
1252}
1253
1262void SlCopy(void *object, size_t length, VarType conv)
1263{
1264 assert(object != nullptr); // Use SlSkipBytes instead
1265 if (_sl.action == SaveLoadAction::Ptrs || _sl.action == SaveLoadAction::Null) return;
1266
1267 /* Automatically calculate the length? */
1268 if (_sl.need_length != NeedLength::None) {
1269 SlSetLength(length * SlCalcConvFileLen(conv));
1270 /* Determine length only? */
1271 if (_sl.need_length == NeedLength::CalcLength) return;
1272 }
1273
1274 SlCopyInternal(object, length, conv);
1275}
1276
1283static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1284{
1285 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1286}
1287
1294static void SlArray(void *array, size_t length, VarType conv)
1295{
1296 switch (_sl.action) {
1298 SlWriteArrayLength(length);
1299 SlCopyInternal(array, length, conv);
1300 return;
1301
1303 case SaveLoadAction::Load: {
1305 size_t sv_length = SlReadArrayLength();
1306 if (conv.mem == VarMemType::Null) {
1307 /* We don't know this field, so we assume the length in the savegame is correct. */
1308 length = sv_length;
1309 } else if (sv_length != length) {
1310 /* If the SLE_ARR changes size, a savegame bump is required
1311 * and the developer should have written conversion lines.
1312 * Error out to make this more visible. */
1313 SlErrorCorrupt("Fixed-length array is of wrong length");
1314 }
1315 }
1316
1317 SlCopyInternal(array, length, conv);
1318 return;
1319 }
1320
1323 return;
1324
1325 default:
1326 NOT_REACHED();
1327 }
1328}
1329
1340static uint32_t ReferenceToInt(const void *obj, SLRefType rt)
1341{
1342 assert(_sl.action == SaveLoadAction::Save);
1343
1344 if (obj == nullptr) return 0;
1345
1346 switch (rt) {
1347 case SLRefType::OldVehicle: // Old vehicles we save as new ones
1348 case SLRefType::Vehicle: return static_cast<const Vehicle *>(obj)->index + 1;
1349 case SLRefType::Station: return static_cast<const Station *>(obj)->index + 1;
1350 case SLRefType::Town: return static_cast<const Town *>(obj)->index + 1;
1351 case SLRefType::RoadStop: return static_cast<const RoadStop *>(obj)->index + 1;
1352 case SLRefType::EngineRenew: return static_cast<const EngineRenew *>(obj)->index + 1;
1353 case SLRefType::CargoPacket: return static_cast<const CargoPacket *>(obj)->index + 1;
1354 case SLRefType::OrderList: return static_cast<const OrderList *>(obj)->index + 1;
1355 case SLRefType::Storage: return static_cast<const PersistentStorage *>(obj)->index + 1;
1356 case SLRefType::LinkGraph: return static_cast<const LinkGraph *>(obj)->index + 1;
1357 case SLRefType::LinkGraphJob: return static_cast<const LinkGraphJob *>(obj)->index + 1;
1358 default: NOT_REACHED();
1359 }
1360}
1361
1372static void *IntToReference(size_t index, SLRefType rt)
1373{
1374 static_assert(sizeof(size_t) <= sizeof(void *));
1375
1376 assert(_sl.action == SaveLoadAction::Ptrs);
1377
1378 /* After version 4.3 SLRefType::OldVehicle is saved as SLRefType::Vehicle,
1379 * and should be loaded like that */
1381 rt = SLRefType::Vehicle;
1382 }
1383
1384 /* No need to look up nullptr pointers, just return immediately */
1385 if (index == (rt == SLRefType::OldVehicle ? 0xFFFF : 0)) return nullptr;
1386
1387 /* Correct index. Old vehicles were saved differently:
1388 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1389 if (rt != SLRefType::OldVehicle) index--;
1390
1391 switch (rt) {
1393 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1394 SlErrorCorrupt("Referencing invalid OrderList");
1395
1397 case SLRefType::Vehicle:
1398 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1399 SlErrorCorrupt("Referencing invalid Vehicle");
1400
1401 case SLRefType::Station:
1402 if (Station::IsValidID(index)) return Station::Get(index);
1403 SlErrorCorrupt("Referencing invalid Station");
1404
1405 case SLRefType::Town:
1406 if (Town::IsValidID(index)) return Town::Get(index);
1407 SlErrorCorrupt("Referencing invalid Town");
1408
1410 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1411 SlErrorCorrupt("Referencing invalid RoadStop");
1412
1414 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1415 SlErrorCorrupt("Referencing invalid EngineRenew");
1416
1418 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1419 SlErrorCorrupt("Referencing invalid CargoPacket");
1420
1421 case SLRefType::Storage:
1422 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1423 SlErrorCorrupt("Referencing invalid PersistentStorage");
1424
1426 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1427 SlErrorCorrupt("Referencing invalid LinkGraph");
1428
1430 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1431 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1432
1433 default: NOT_REACHED();
1434 }
1435}
1436
1442void SlSaveLoadRef(void *ptr, VarType conv)
1443{
1444 switch (_sl.action) {
1446 SlWriteUint32(ReferenceToInt(*static_cast<void **>(ptr), conv.ref));
1447 break;
1450 *static_cast<size_t *>(ptr) = IsSavegameVersionBefore(SaveLoadVersion::MoreCargoPackets) ? SlReadUint16() : SlReadUint32();
1451 break;
1453 *static_cast<void **>(ptr) = IntToReference(*static_cast<size_t *>(ptr), conv.ref);
1454 break;
1456 *static_cast<void **>(ptr) = nullptr;
1457 break;
1458 default: NOT_REACHED();
1459 }
1460}
1461
1465template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1467 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1468public:
1476 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SaveLoadType::Variable)
1477 {
1478 assert(cmd == SaveLoadType::Variable || cmd == SaveLoadType::Reference);
1479
1480 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1481
1482 int type_size = SlGetArrayLength(list->size());
1483 int item_size = SlCalcConvFileLen(cmd == SaveLoadType::Variable ? conv : VarType{VarFileType::U32, {}});
1484 return list->size() * item_size + type_size;
1485 }
1486
1487 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1488 {
1489 switch (cmd) {
1490 case SaveLoadType::Variable: SlSaveLoadConv(item, conv); break;
1491 case SaveLoadType::Reference: SlSaveLoadRef(item, conv); break;
1492 case SaveLoadType::String: SlStdString(item, conv); break;
1493 default:
1494 NOT_REACHED();
1495 }
1496 }
1497
1504 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SaveLoadType::Variable)
1505 {
1506 assert(cmd == SaveLoadType::Variable || cmd == SaveLoadType::Reference || cmd == SaveLoadType::String);
1507
1508 SlStorageT *list = static_cast<SlStorageT *>(storage);
1509
1510 switch (_sl.action) {
1512 SlWriteArrayLength(list->size());
1513
1514 for (auto &item : *list) {
1515 SlSaveLoadMember(cmd, &item, conv);
1516 }
1517 break;
1518
1520 case SaveLoadAction::Load: {
1521 size_t length;
1522 switch (cmd) {
1523 case SaveLoadType::Variable: length = IsSavegameVersionBefore(SaveLoadVersion::SaveloadListLength) ? SlReadUint32() : SlReadArrayLength(); break;
1524 case SaveLoadType::Reference: length = IsSavegameVersionBefore(SaveLoadVersion::MoreCargoPackets) ? SlReadUint16() : IsSavegameVersionBefore(SaveLoadVersion::SaveloadListLength) ? SlReadUint32() : SlReadArrayLength(); break;
1525 case SaveLoadType::String: length = SlReadArrayLength(); break;
1526 default: NOT_REACHED();
1527 }
1528
1529 list->clear();
1530 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1531 list->reserve(length);
1532 }
1533
1534 /* Load each value and push to the end of the storage. */
1535 for (size_t i = 0; i < length; i++) {
1536 Tvar &data = list->emplace_back();
1537 SlSaveLoadMember(cmd, &data, conv);
1538 }
1539 break;
1540 }
1541
1543 for (auto &item : *list) {
1544 SlSaveLoadMember(cmd, &item, conv);
1545 }
1546 break;
1547
1549 list->clear();
1550 break;
1551
1552 default: NOT_REACHED();
1553 }
1554 }
1555};
1556
1563static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1564{
1566}
1567
1573static void SlRefList(void *list, VarType conv)
1574{
1575 /* Automatically calculate the length? */
1576 if (_sl.need_length != NeedLength::None) {
1577 SlSetLength(SlCalcRefListLen(list, conv));
1578 /* Determine length only? */
1579 if (_sl.need_length == NeedLength::CalcLength) return;
1580 }
1581
1583}
1584
1591static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1592{
1594}
1595
1601static void SlRefVector(void *vector, VarType conv)
1602{
1603 /* Automatically calculate the length? */
1604 if (_sl.need_length != NeedLength::None) {
1605 SlSetLength(SlCalcRefVectorLen(vector, conv));
1606 /* Determine length only? */
1607 if (_sl.need_length == NeedLength::CalcLength) return;
1608 }
1609
1611}
1612
1619static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1620{
1621 switch (conv.mem) {
1622 case VarMemType::Bool: NOT_REACHED(); // Not supported
1631
1632 case VarMemType::Str:
1633 /* Strings are a length-prefixed field type in the savegame table format,
1634 * these may not be directly stored in another length-prefixed container type. */
1635 NOT_REACHED();
1636
1637 default: NOT_REACHED();
1638 }
1639}
1640
1646static void SlVector(void *vector, VarType conv)
1647{
1648 switch (conv.mem) {
1649 case VarMemType::Bool: NOT_REACHED(); // Not supported
1658
1659 case VarMemType::Str:
1660 /* Strings are a length-prefixed field type in the savegame table format,
1661 * these may not be directly stored in another length-prefixed container type.
1662 * This is permitted for load-related actions, because invalid fields of this type are present
1663 * from SaveLoadVersion::CompanyAllowList up to SaveLoadVersion::CompanyAllowListV2. */
1664 assert(_sl.action != SaveLoadAction::Save);
1666 break;
1667
1668 default: NOT_REACHED();
1669 }
1670}
1671
1677static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1678{
1679 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1680}
1681
1687static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1688{
1689 size_t length = 0;
1690
1691 for (auto &sld : slt) {
1692 if (!SlIsObjectValidInSavegame(sld)) continue;
1693
1695 length += SlCalcStdStringLen(&sld.name);
1696 }
1697
1698 length += SlCalcConvFileLen(VarTypes::U8); // End-of-list entry.
1699
1700 for (auto &sld : slt) {
1701 if (!SlIsObjectValidInSavegame(sld)) continue;
1702 if (sld.cmd == SaveLoadType::StructList || sld.cmd == SaveLoadType::Struct) {
1703 length += SlCalcTableHeader(sld.handler->GetDescription());
1704 }
1705 }
1706
1707 return length;
1708}
1709
1716size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1717{
1718 size_t length = 0;
1719
1720 /* Need to determine the length and write a length tag. */
1721 for (auto &sld : slt) {
1722 length += SlCalcObjMemberLength(object, sld);
1723 }
1724 return length;
1725}
1726
1727size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1728{
1729 assert(_sl.action == SaveLoadAction::Save);
1730
1731 if (!SlIsObjectValidInSavegame(sld)) return 0;
1732
1733 switch (sld.cmd) {
1736 case SaveLoadType::Array: return SlCalcArrayLen(sld.length, sld.conv);
1739 case SaveLoadType::Vector: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1741 case SaveLoadType::SaveByte: return 1; // a byte is logically of size 1
1742 case SaveLoadType::Null: return SlCalcConvFileLen(sld.conv) * sld.length;
1743
1746 NeedLength old_need_length = _sl.need_length;
1747 size_t old_obj_len = _sl.obj_len;
1748
1749 _sl.need_length = NeedLength::CalcLength;
1750 _sl.obj_len = 0;
1751
1752 /* Pretend that we are saving to collect the object size. Other
1753 * means are difficult, as we don't know the length of the list we
1754 * are about to store. */
1755 sld.handler->Save(const_cast<void *>(object));
1756 size_t length = _sl.obj_len;
1757
1758 _sl.obj_len = old_obj_len;
1759 _sl.need_length = old_need_length;
1760
1761 if (sld.cmd == SaveLoadType::Struct) {
1762 length += SlGetArrayLength(1);
1763 }
1764
1765 return length;
1766 }
1767
1768 default: NOT_REACHED();
1769 }
1770 return 0;
1771}
1772
1773static bool SlObjectMember(void *object, const SaveLoad &sld)
1774{
1775 if (!SlIsObjectValidInSavegame(sld)) return false;
1776
1777 switch (sld.cmd) {
1784 case SaveLoadType::String: {
1785 void *ptr = GetVariableAddress(object, sld);
1786
1787 switch (sld.cmd) {
1788 case SaveLoadType::Variable: SlSaveLoadConv(ptr, sld.conv); break;
1789 case SaveLoadType::Reference: SlSaveLoadRef(ptr, sld.conv); break;
1790 case SaveLoadType::Array: SlArray(ptr, sld.length, sld.conv); break;
1791 case SaveLoadType::ReferenceList: SlRefList(ptr, sld.conv); break;
1792 case SaveLoadType::ReferenceVector: SlRefVector(ptr, sld.conv); break;
1793 case SaveLoadType::Vector: SlVector(ptr, sld.conv); break;
1794 case SaveLoadType::String: SlStdString(ptr, sld.conv); break;
1795 default: NOT_REACHED();
1796 }
1797 break;
1798 }
1799
1800 /* SaveLoadType::SaveByte writes a value to the savegame to identify the type of an object.
1801 * When loading, the value is read explicitly with SlReadByte() to determine which
1802 * object description to use. */
1804 void *ptr = GetVariableAddress(object, sld);
1805
1806 switch (_sl.action) {
1807 case SaveLoadAction::Save: SlWriteByte(*static_cast<uint8_t *>(ptr)); break;
1811 case SaveLoadAction::Null: break;
1812 default: NOT_REACHED();
1813 }
1814 break;
1815 }
1816
1817 case SaveLoadType::Null: {
1818 assert(sld.conv.mem == VarMemType::Null);
1819
1820 switch (_sl.action) {
1823 case SaveLoadAction::Save: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1825 case SaveLoadAction::Null: break;
1826 default: NOT_REACHED();
1827 }
1828 break;
1829 }
1830
1833 switch (_sl.action) {
1834 case SaveLoadAction::Save: {
1835 if (sld.cmd == SaveLoadType::Struct) {
1836 /* Store in the savegame if this struct was written or not. */
1837 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1838 }
1839 sld.handler->Save(object);
1840 break;
1841 }
1842
1846 }
1847 sld.handler->LoadCheck(object);
1848 break;
1849 }
1850
1851 case SaveLoadAction::Load: {
1854 }
1855 sld.handler->Load(object);
1856 break;
1857 }
1858
1860 sld.handler->FixPointers(object);
1861 break;
1862
1863 case SaveLoadAction::Null: break;
1864 default: NOT_REACHED();
1865 }
1866 break;
1867
1868 default: NOT_REACHED();
1869 }
1870 return true;
1871}
1872
1877void SlSetStructListLength(size_t length)
1878{
1879 /* Automatically calculate the length? */
1880 if (_sl.need_length != NeedLength::None) {
1881 SlSetLength(SlGetArrayLength(length));
1882 if (_sl.need_length == NeedLength::CalcLength) return;
1883 }
1884
1885 SlWriteArrayLength(length);
1886}
1887
1893size_t SlGetStructListLength(size_t limit)
1894{
1895 size_t length = SlReadArrayLength();
1896 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1897
1898 return length;
1899}
1900
1906void SlObject(void *object, const SaveLoadTable &slt)
1907{
1908 /* Automatically calculate the length? */
1909 if (_sl.need_length != NeedLength::None) {
1910 SlSetLength(SlCalcObjLength(object, slt));
1911 if (_sl.need_length == NeedLength::CalcLength) return;
1912 }
1913
1914 for (auto &sld : slt) {
1915 SlObjectMember(object, sld);
1916 }
1917}
1918
1924 void Save(void *) const override
1925 {
1926 NOT_REACHED();
1927 }
1928
1929 void Load(void *object) const override
1930 {
1931 size_t length = SlGetStructListLength(UINT32_MAX);
1932 for (; length > 0; length--) {
1933 SlObject(object, this->GetLoadDescription());
1934 }
1935 }
1936
1937 void LoadCheck(void *object) const override
1938 {
1939 this->Load(object);
1940 }
1941
1943 {
1944 return {};
1945 }
1946
1948 {
1949 NOT_REACHED();
1950 }
1951};
1952
1959std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1960{
1961 /* You can only use SlTableHeader if you are a ChunkType::Table or ChunkType::SparseTable. */
1962 assert(_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
1963
1964 switch (_sl.action) {
1966 case SaveLoadAction::Load: {
1967 std::vector<SaveLoad> saveloads;
1968
1969 /* Build a key lookup mapping based on the available fields. */
1970 std::map<std::string, const SaveLoad *> key_lookup;
1971 for (auto &sld : slt) {
1972 if (!SlIsObjectValidInSavegame(sld)) continue;
1973
1974 /* Check that there is only one active SaveLoad for a given name. */
1975 assert(key_lookup.find(sld.name) == key_lookup.end());
1976 key_lookup[sld.name] = &sld;
1977 }
1978
1979 while (true) {
1980 SavegameFileType type{};
1982 if (type.IsEnd()) break;
1983
1984 std::string key;
1986
1987 auto sld_it = key_lookup.find(key);
1988 if (sld_it == key_lookup.end()) {
1989 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1990 Debug(sl, _sl.action == SaveLoadAction::Load ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type.storage);
1991
1992 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1993 SaveLoadType saveload_type;
1994 switch (type.Type()) {
1996 saveload_type = SaveLoadType::String;
1997 break;
1998
2000 saveload_type = SaveLoadType::StructList;
2001 handler = std::make_shared<SlSkipHandler>();
2002 break;
2003
2004 default:
2006 break;
2007 }
2008
2009 /* We don't know this field, so read to nothing. */
2010 saveloads.emplace_back(std::move(key), saveload_type, type.Type() | VarMemType::Null, 1, SaveLoadVersion::MinVersion, SaveLoadVersion::MaxVersion, nullptr, 0, std::move(handler));
2011 continue;
2012 }
2013
2014 /* Validate the type of the field. If it is changed, the
2015 * savegame should have been bumped so we know how to do the
2016 * conversion. If this error triggers, that clearly didn't
2017 * happen and this is a friendly poke to the developer to bump
2018 * the savegame version and add conversion code. */
2019 SavegameFileType correct_type = GetSavegameFileType(*sld_it->second);
2020 if (correct_type.storage != type.storage) {
2021 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type.storage, type.storage);
2022 SlErrorCorrupt("Field type is different than expected");
2023 }
2024 saveloads.emplace_back(*sld_it->second);
2025 }
2026
2027 for (auto &sld : saveloads) {
2029 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
2030 }
2031 }
2032
2033 return saveloads;
2034 }
2035
2036 case SaveLoadAction::Save: {
2037 /* Automatically calculate the length? */
2038 if (_sl.need_length != NeedLength::None) {
2040 if (_sl.need_length == NeedLength::CalcLength) break;
2041 }
2042
2043 for (auto &sld : slt) {
2044 if (!SlIsObjectValidInSavegame(sld)) continue;
2045 /* Make sure we are not storing empty keys. */
2046 assert(!sld.name.empty());
2047
2049 assert(!type.IsEnd());
2050
2052 SlStdString(const_cast<std::string *>(&sld.name), VarTypes::STR);
2053 }
2054
2055 /* Add an end-of-header marker. */
2056 SavegameFileType type{};
2058
2059 /* After the table, write down any sub-tables we might have. */
2060 for (auto &sld : slt) {
2061 if (!SlIsObjectValidInSavegame(sld)) continue;
2063 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
2064 NeedLength old_need_length = _sl.need_length;
2065 _sl.need_length = NeedLength::None;
2066
2067 SlTableHeader(sld.handler->GetDescription());
2068
2069 _sl.need_length = old_need_length;
2070 }
2071 }
2072
2073 break;
2074 }
2075
2076 default: NOT_REACHED();
2077 }
2078
2079 return std::vector<SaveLoad>();
2080}
2081
2095std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
2096{
2097 assert(_sl.action == SaveLoadAction::Load || _sl.action == SaveLoadAction::LoadCheck);
2098 /* ChunkType::Table / ChunkType::SparseTable always have a header. */
2099 if (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable) return SlTableHeader(slt);
2100
2101 std::vector<SaveLoad> saveloads;
2102
2103 /* Build a key lookup mapping based on the available fields. */
2104 std::map<std::string_view, std::vector<const SaveLoad *>> key_lookup;
2105 for (auto &sld : slt) {
2106 /* All entries should have a name; otherwise the entry should just be removed. */
2107 assert(!sld.name.empty());
2108
2109 key_lookup[sld.name].push_back(&sld);
2110 }
2111
2112 for (auto &slc : slct) {
2113 if (slc.name.empty()) {
2114 /* In old savegames there can be data we no longer care for. We
2115 * skip this by simply reading the amount of bytes indicated and
2116 * send those to /dev/null. */
2117 saveloads.emplace_back("", SaveLoadType::Null, VarFileType::U8 | VarMemType::Null, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr);
2118 } else {
2119 auto sld_it = key_lookup.find(slc.name);
2120 /* If this branch triggers, it means that an entry in the
2121 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2122 * you rename a field in one and not in the other? */
2123 if (sld_it == key_lookup.end()) {
2124 /* This isn't an assert, as that leaves no information what
2125 * field was to blame. This way at least we have breadcrumbs. */
2126 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2127 SlErrorCorrupt("Internal error with savegame compatibility");
2128 }
2129 for (auto &sld : sld_it->second) {
2130 saveloads.push_back(*sld);
2131 }
2132 }
2133 }
2134
2135 for (auto &sld : saveloads) {
2136 if (!SlIsObjectValidInSavegame(sld)) continue;
2138 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2139 }
2140 }
2141
2142 return saveloads;
2143}
2144
2150{
2151 SlObject(nullptr, slt);
2152}
2153
2159void SlAutolength(AutolengthProc *proc, int arg)
2160{
2161 assert(_sl.action == SaveLoadAction::Save);
2162
2163 /* Tell it to calculate the length */
2164 _sl.need_length = NeedLength::CalcLength;
2165 _sl.obj_len = 0;
2166 proc(arg);
2167
2168 /* Setup length */
2169 _sl.need_length = NeedLength::WantLength;
2170 SlSetLength(_sl.obj_len);
2171
2172 size_t start_pos = _sl.dumper->GetSize();
2173 size_t expected_offs = start_pos + _sl.obj_len;
2174
2175 /* And write the stuff */
2176 proc(arg);
2177
2178 if (expected_offs != _sl.dumper->GetSize()) {
2179 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2180 }
2181}
2182
2183void ChunkHandler::LoadCheck(size_t len) const
2184{
2185 switch (_sl.chunk_type) {
2186 case ChunkType::Table:
2188 SlTableHeader({});
2189 [[fallthrough]];
2190 case ChunkType::Array:
2192 SlSkipArray();
2193 break;
2194 case ChunkType::Riff:
2195 SlSkipBytes(len);
2196 break;
2197 default:
2198 NOT_REACHED();
2199 }
2200}
2201
2206static void SlLoadChunk(const ChunkHandler &ch)
2207{
2208 uint8_t m = SlReadByte();
2209
2210 _sl.chunk_type = static_cast<ChunkType>(m & to_underlying(ChunkType::FileTypeMask));
2211 _sl.obj_len = 0;
2212 _sl.expect_table_header = (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
2213
2214 /* The header should always be at the start. Read the length; the
2215 * Load() should as first action process the header. */
2216 if (_sl.expect_table_header) {
2217 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2218 }
2219
2220 switch (_sl.chunk_type) {
2221 case ChunkType::Table:
2222 case ChunkType::Array:
2223 _sl.array_index = 0;
2224 ch.Load();
2225 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2226 break;
2229 ch.Load();
2230 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2231 break;
2232 case ChunkType::Riff: {
2233 /* Read length */
2234 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2235 len += SlReadUint16();
2236 _sl.obj_len = len;
2237 size_t start_pos = _sl.reader->GetSize();
2238 size_t endoffs = start_pos + len;
2239 ch.Load();
2240
2241 if (_sl.reader->GetSize() != endoffs) {
2242 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2243 }
2244 break;
2245 }
2246 default:
2247 SlErrorCorrupt("Invalid chunk type");
2248 break;
2249 }
2250
2251 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2252}
2253
2259static void SlLoadCheckChunk(const ChunkHandler &ch)
2260{
2261 uint8_t m = SlReadByte();
2262
2263 _sl.chunk_type = static_cast<ChunkType>(m & to_underlying(ChunkType::FileTypeMask));
2264 _sl.obj_len = 0;
2265 _sl.expect_table_header = (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
2266
2267 /* The header should always be at the start. Read the length; the
2268 * LoadCheck() should as first action process the header. */
2269 if (_sl.expect_table_header) {
2270 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2271 }
2272
2273 switch (_sl.chunk_type) {
2274 case ChunkType::Table:
2275 case ChunkType::Array:
2276 _sl.array_index = 0;
2277 ch.LoadCheck();
2278 break;
2281 ch.LoadCheck();
2282 break;
2283 case ChunkType::Riff: {
2284 /* Read length */
2285 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2286 len += SlReadUint16();
2287 _sl.obj_len = len;
2288 size_t start_pos = _sl.reader->GetSize();
2289 size_t endoffs = start_pos + len;
2290 ch.LoadCheck(len);
2291
2292 if (_sl.reader->GetSize() != endoffs) {
2293 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2294 }
2295 break;
2296 }
2297 default:
2298 SlErrorCorrupt("Invalid chunk type");
2299 break;
2300 }
2301
2302 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2303}
2304
2310static void SlSaveChunk(const ChunkHandler &ch)
2311{
2312 if (ch.type == ChunkType::ReadOnly) return;
2313
2314 for (uint8_t b : ch.id) SlWriteByte(b);
2315 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2316
2317 _sl.chunk_type = ch.type;
2318 _sl.expect_table_header = (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
2319
2320 _sl.need_length = (_sl.expect_table_header || _sl.chunk_type == ChunkType::Riff) ? NeedLength::WantLength : NeedLength::None;
2321
2322 switch (_sl.chunk_type) {
2323 case ChunkType::Riff:
2324 ch.Save();
2325 break;
2326 case ChunkType::Table:
2327 case ChunkType::Array:
2328 _sl.last_array_index = 0;
2329 SlWriteByte(to_underlying(_sl.chunk_type));
2330 ch.Save();
2331 SlWriteArrayLength(0); // Terminate arrays
2332 break;
2335 SlWriteByte(to_underlying(_sl.chunk_type));
2336 ch.Save();
2337 SlWriteArrayLength(0); // Terminate arrays
2338 break;
2339 default: NOT_REACHED();
2340 }
2341
2342 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2343}
2344
2346static void SlSaveChunks()
2347{
2348 for (auto &ch : ChunkHandlers()) {
2349 SlSaveChunk(ch);
2350 }
2351
2352 /* Terminator */
2353 SlWriteUint32(0);
2354}
2355
2363{
2364 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2365 return nullptr;
2366}
2367
2369static void SlLoadChunks()
2370{
2371 for (ChunkId id = SlReadChunkId(); !id.Empty(); id = SlReadChunkId()) {
2372 Debug(sl, 2, "Loading chunk {}", id.AsString());
2373
2374 const ChunkHandler *ch = SlFindChunkHandler(id);
2375 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2376 SlLoadChunk(*ch);
2377 }
2378}
2379
2382{
2383 for (ChunkId id = SlReadChunkId(); !id.Empty(); id = SlReadChunkId()) {
2384 Debug(sl, 2, "Loading chunk {}", id.AsString());
2385
2386 const ChunkHandler *ch = SlFindChunkHandler(id);
2387 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2388 SlLoadCheckChunk(*ch);
2389 }
2390}
2391
2393static void SlFixPointers()
2394{
2395 _sl.action = SaveLoadAction::Ptrs;
2396
2397 for (const ChunkHandler &ch : ChunkHandlers()) {
2398 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2399 ch.FixPointers();
2400 }
2401
2402 assert(_sl.action == SaveLoadAction::Ptrs);
2403}
2404
2405
2408 std::optional<FileHandle> file;
2409 long begin;
2410
2415 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2416 {
2417 }
2418
2420 ~FileReader() override
2421 {
2422 if (this->file.has_value()) {
2423 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2424 }
2425 }
2426
2427 size_t Read(uint8_t *buf, size_t size) override
2428 {
2429 /* We're in the process of shutting down, i.e. in "failure" mode. */
2430 if (!this->file.has_value()) return 0;
2431
2432 return fread(buf, 1, size, *this->file);
2433 }
2434
2435 void Reset() override
2436 {
2437 clearerr(*this->file);
2438 if (fseek(*this->file, this->begin, SEEK_SET)) {
2439 Debug(sl, 1, "Could not reset the file reading");
2440 }
2441 }
2442};
2443
2446 std::optional<FileHandle> file;
2447
2452 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2453 {
2454 }
2455
2457 ~FileWriter() override
2458 {
2459 this->Finish();
2460 }
2461
2462 void Write(const uint8_t *buf, size_t size) override
2463 {
2464 /* We're in the process of shutting down, i.e. in "failure" mode. */
2465 if (!this->file.has_value()) return;
2466
2467 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2468 }
2469
2470 void Finish() override
2471 {
2472 if (this->file.has_value()) {
2473 _game_session_stats.savegame_size = ftell(*this->file);
2474 this->file.reset();
2475 }
2476 }
2477};
2478
2479/*******************************************
2480 ********** START OF LZO CODE **************
2481 *******************************************/
2482
2483#ifdef WITH_LZO
2484
2486static const uint LZO_BUFFER_SIZE = 8192;
2487
2494 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2495 {
2496 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2497 }
2498
2499 size_t Read(uint8_t *buf, size_t ssize) override
2500 {
2501 assert(ssize >= LZO_BUFFER_SIZE);
2502
2503 /* Buffer size is from the LZO docs plus the chunk header size. */
2504 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2505 uint32_t tmp[2];
2506 uint32_t size;
2507 lzo_uint len = ssize;
2508
2509 /* Read header*/
2510 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2511
2512 /* Check if size is bad */
2513 ((uint32_t*)out)[0] = size = tmp[1];
2514
2516 tmp[0] = TO_BE32(tmp[0]);
2517 size = TO_BE32(size);
2518 }
2519
2520 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2521
2522 /* Read block */
2523 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2524
2525 /* Verify checksum */
2526 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2527
2528 /* Decompress */
2529 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2530 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2531 return len;
2532 }
2533};
2534
2541 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2542 {
2543 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2544 }
2545
2546 void Write(const uint8_t *buf, size_t size) override
2547 {
2548 const lzo_bytep in = buf;
2549 /* Buffer size is from the LZO docs plus the chunk header size. */
2550 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2551 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2552 lzo_uint outlen;
2553
2554 do {
2555 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2556 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : static_cast<lzo_uint>(size);
2557 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2558 ((uint32_t*)out)[1] = TO_BE32(static_cast<uint32_t>(outlen));
2559 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2560 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2561
2562 /* Move to next data chunk. */
2563 size -= len;
2564 in += len;
2565 } while (size > 0);
2566 }
2567};
2568
2569#endif /* WITH_LZO */
2570
2571/*********************************************
2572 ******** START OF NOCOMP CODE (uncompressed)*
2573 *********************************************/
2574
2581 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2582 {
2583 }
2584
2585 size_t Read(uint8_t *buf, size_t size) override
2586 {
2587 return this->chain->Read(buf, size);
2588 }
2589};
2590
2597 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2598 {
2599 }
2600
2601 void Write(const uint8_t *buf, size_t size) override
2602 {
2603 this->chain->Write(buf, size);
2604 }
2605};
2606
2607/********************************************
2608 ********** START OF ZLIB CODE **************
2609 ********************************************/
2610
2611#if defined(WITH_ZLIB)
2612
2615 z_stream z{};
2617
2622 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2623 {
2624 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2625 }
2626
2629 {
2630 inflateEnd(&this->z);
2631 }
2632
2633 size_t Read(uint8_t *buf, size_t size) override
2634 {
2635 this->z.next_out = buf;
2636 this->z.avail_out = static_cast<uint>(size);
2637
2638 do {
2639 /* read more bytes from the file? */
2640 if (this->z.avail_in == 0) {
2641 this->z.next_in = this->fread_buf;
2642 this->z.avail_in = static_cast<uint>(this->chain->Read(this->fread_buf, sizeof(this->fread_buf)));
2643 }
2644
2645 /* inflate the data */
2646 int r = inflate(&this->z, 0);
2647 if (r == Z_STREAM_END) break;
2648
2649 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2650 } while (this->z.avail_out != 0);
2651
2652 return size - this->z.avail_out;
2653 }
2654};
2655
2658 z_stream z{};
2660
2666 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain))
2667 {
2668 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2669 }
2670
2673 {
2674 deflateEnd(&this->z);
2675 }
2676
2683 void WriteLoop(const uint8_t *p, size_t len, int mode)
2684 {
2685 uint n;
2686 this->z.next_in = const_cast<uint8_t *>(p); // zlib does not modify the data, but is non-const for legacy reasons
2687 this->z.avail_in = static_cast<uInt>(len);
2688 do {
2689 this->z.next_out = this->fwrite_buf;
2690 this->z.avail_out = sizeof(this->fwrite_buf);
2691
2699 int r = deflate(&this->z, mode);
2700
2701 /* bytes were emitted? */
2702 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2703 this->chain->Write(this->fwrite_buf, n);
2704 }
2705 if (r == Z_STREAM_END) break;
2706
2707 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2708 } while (this->z.avail_in || !this->z.avail_out);
2709 }
2710
2711 void Write(const uint8_t *buf, size_t size) override
2712 {
2713 this->WriteLoop(buf, size, 0);
2714 }
2715
2716 void Finish() override
2717 {
2718 this->WriteLoop(nullptr, 0, Z_FINISH);
2719 this->chain->Finish();
2720 }
2721};
2722
2723#endif /* WITH_ZLIB */
2724
2725/********************************************
2726 ********** START OF LZMA CODE **************
2727 ********************************************/
2728
2729#if defined(WITH_LIBLZMA)
2730
2737static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2738
2741 lzma_stream lzma;
2743
2748 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain)), lzma(_lzma_init)
2749 {
2750 /* Allow saves up to 256 MB uncompressed */
2751 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2752 }
2753
2756 {
2757 lzma_end(&this->lzma);
2758 }
2759
2760 size_t Read(uint8_t *buf, size_t size) override
2761 {
2762 this->lzma.next_out = buf;
2763 this->lzma.avail_out = size;
2764
2765 do {
2766 /* read more bytes from the file? */
2767 if (this->lzma.avail_in == 0) {
2768 this->lzma.next_in = this->fread_buf;
2769 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2770 }
2771
2772 /* inflate the data */
2773 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2774 if (r == LZMA_STREAM_END) break;
2775 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2776 } while (this->lzma.avail_out != 0);
2777
2778 return size - this->lzma.avail_out;
2779 }
2780};
2781
2784 lzma_stream lzma;
2786
2792 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init)
2793 {
2794 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2795 }
2796
2799 {
2800 lzma_end(&this->lzma);
2801 }
2802
2809 void WriteLoop(const uint8_t *p, size_t len, lzma_action action)
2810 {
2811 size_t n;
2812 this->lzma.next_in = p;
2813 this->lzma.avail_in = len;
2814 do {
2815 this->lzma.next_out = this->fwrite_buf;
2816 this->lzma.avail_out = sizeof(this->fwrite_buf);
2817
2818 lzma_ret r = lzma_code(&this->lzma, action);
2819
2820 /* bytes were emitted? */
2821 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2822 this->chain->Write(this->fwrite_buf, n);
2823 }
2824 if (r == LZMA_STREAM_END) break;
2825 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2826 } while (this->lzma.avail_in || !this->lzma.avail_out);
2827 }
2828
2829 void Write(const uint8_t *buf, size_t size) override
2830 {
2831 this->WriteLoop(buf, size, LZMA_RUN);
2832 }
2833
2834 void Finish() override
2835 {
2836 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2837 this->chain->Finish();
2838 }
2839};
2840
2841#endif /* WITH_LIBLZMA */
2842
2843/*******************************************
2844 ************* END OF CODE *****************
2845 *******************************************/
2846
2849
2852 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2853 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2854
2855 std::string_view name;
2857
2861};
2862
2867
2870#if defined(WITH_LZO)
2871 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2873#else
2874 {nullptr, nullptr, "lzo", SAVEGAME_TAG_LZO, 0, 0, 0},
2875#endif
2876 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2878#if defined(WITH_ZLIB)
2879 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2880 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2881 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2883#else
2884 {nullptr, nullptr, "zlib", SAVEGAME_TAG_ZLIB, 0, 0, 0},
2885#endif
2886#if defined(WITH_LIBLZMA)
2887 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2888 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2889 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2890 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2891 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2893#else
2894 {nullptr, nullptr, "lzma", SAVEGAME_TAG_LZMA, 0, 0, 0},
2895#endif
2896};
2897
2904static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(std::string_view full_name)
2905{
2906 /* Find default savegame format, the highest one with which files can be written. */
2907 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2908 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2909
2910 const SaveLoadFormat &def = *it;
2911
2912 if (!full_name.empty()) {
2913 /* Get the ":..." of the compression level out of the way */
2914 size_t separator = full_name.find(':');
2915 bool has_comp_level = separator != std::string::npos;
2916 std::string_view name = has_comp_level ? full_name.substr(0, separator) : full_name;
2917
2918 for (const auto &slf : _saveload_formats) {
2919 if (slf.init_write != nullptr && name == slf.name) {
2920 if (has_comp_level) {
2921 auto complevel = full_name.substr(separator + 1);
2922
2923 /* Get the level and determine whether all went fine. */
2924 auto level = ParseInteger<uint8_t>(complevel);
2925 if (!level.has_value() || *level != Clamp(*level, slf.min_compression, slf.max_compression)) {
2927 GetEncodedString(STR_CONFIG_ERROR),
2928 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2930 } else {
2931 return {slf, *level};
2932 }
2933 }
2934 return {slf, slf.default_compression};
2935 }
2936 }
2937
2939 GetEncodedString(STR_CONFIG_ERROR),
2940 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2942 }
2943 return {def, def.default_compression};
2944}
2945
2946/* actual loader/saver function */
2947void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2948extern bool AfterLoadGame();
2949extern bool LoadOldSaveGame(std::string_view file);
2950
2956static void ResetSettings()
2957{
2958 for (auto &desc : GetSaveLoadSettingTable()) {
2959 const SettingDesc *sd = GetSettingDesc(desc);
2960 if (sd->flags.Test(SettingFlag::NotInSave)) continue;
2962
2964 }
2965}
2966
2967extern void ClearOldOrders();
2968
2973{
2975 ResetTempEngineData();
2976 ClearRailTypeLabelList();
2977 ClearRoadTypeLabelList();
2978 ResetOldWaypoints();
2979 ResetSettings();
2980}
2981
2985static inline void ClearSaveLoadState()
2986{
2987 _sl.dumper = nullptr;
2988 _sl.sf = nullptr;
2989 _sl.reader = nullptr;
2990 _sl.lf = nullptr;
2991}
2992
2994static void SaveFileStart()
2995{
2996 SetMouseCursorBusy(true);
2997
2998 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_SAVELOAD_START);
2999 _sl.saveinprogress = true;
3000}
3001
3003static void SaveFileDone()
3004{
3005 SetMouseCursorBusy(false);
3006
3007 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_SAVELOAD_FINISH);
3008 _sl.saveinprogress = false;
3009
3010#ifdef __EMSCRIPTEN__
3011 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
3012#endif
3013}
3014
3020{
3021 _sl.error_str = str;
3022}
3023
3029{
3030 return GetEncodedString(_sl.action == SaveLoadAction::Save ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
3031}
3032
3038{
3039 return GetEncodedString(_sl.error_str, _sl.extra_msg);
3040}
3041
3048
3055static SaveLoadResult SaveFileToDisk(bool threaded)
3056{
3057 try {
3058 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
3059
3060 /* We have written our stuff to memory, now write it to file! */
3061 _sl.sf->Write(fmt.tag.data(), fmt.tag.size());
3062
3063 uint32_t version = TO_BE32(to_underlying(SAVEGAME_VERSION) << 16);
3064 _sl.sf->Write(reinterpret_cast<uint8_t *>(&version), sizeof(version));
3065
3066 _sl.sf = fmt.init_write(_sl.sf, compression);
3067 _sl.dumper->Flush(_sl.sf);
3068
3070
3071 if (threaded) SetAsyncSaveFinish(SaveFileDone);
3072
3073 return SaveLoadResult::Ok;
3074 } catch (...) {
3076
3078
3079 /* We don't want to shout when saving is just
3080 * cancelled due to a client disconnecting. */
3081 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
3082 Debug(sl, 0, "{} {}", GetSaveLoadErrorType().GetDecodedString(), GetSaveLoadErrorMessage().GetDecodedString());
3083 asfp = SaveFileError;
3084 }
3085
3086 if (threaded) {
3087 SetAsyncSaveFinish(asfp);
3088 } else {
3089 asfp();
3090 }
3091 return SaveLoadResult::Error;
3092 }
3093}
3094
3095void WaitTillSaved()
3096{
3097 if (!_save_thread.joinable()) return;
3098
3099 _save_thread.join();
3100
3101 /* Make sure every other state is handled properly as well. */
3103}
3104
3113static SaveLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
3114{
3115 assert(!_sl.saveinprogress);
3116
3117 _sl.dumper = std::make_unique<MemoryDumper>();
3118 _sl.sf = std::move(writer);
3119
3121
3122 SaveViewportBeforeSaveGame();
3123 SlSaveChunks();
3124
3125 SaveFileStart();
3126
3127 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3128 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3129
3130 SaveLoadResult result = SaveFileToDisk(false);
3131 SaveFileDone();
3132
3133 return result;
3134 }
3135
3136 return SaveLoadResult::Ok;
3137}
3138
3145SaveLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3146{
3147 try {
3148 _sl.action = SaveLoadAction::Save;
3149 return DoSave(std::move(writer), threaded);
3150 } catch (...) {
3152 return SaveLoadResult::Error;
3153 }
3154}
3155
3164static const SaveLoadFormat *DetermineSaveLoadFormat(SaveLoadFormatTag tag, uint32_t raw_version)
3165{
3166 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3167 if (fmt != std::end(_saveload_formats)) {
3168 /* Check version number */
3169 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3170 /* Minor is not used anymore from version 18.0, but it is still needed
3171 * in versions before that (4 cases) which can't be removed easy.
3172 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3173 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3174
3175 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3176
3177 /* Is the version higher than the current? */
3178 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3180 return fmt;
3181 }
3182
3183 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3184 _sl.lf->Reset();
3187
3188 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3189 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3190 if (fmt == std::end(_saveload_formats)) {
3191 /* Who removed the LZO savegame format definition? When built without LZO support,
3192 * the formats must still list it just without a method to read the file.
3193 * The caller of this function has to check for the existence of load function. */
3194 NOT_REACHED();
3195 }
3196 return fmt;
3197}
3198
3205static SaveLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3206{
3207 _sl.lf = std::move(reader);
3208
3209 if (load_check) {
3210 /* Clear previous check data */
3211 _load_check_data.Clear();
3212 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3213 _load_check_data.checkable = true;
3214 }
3215
3216 SaveLoadFormatTag tag{};
3217 if (_sl.lf->Read(tag.data(), tag.size()) != tag.size()) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3218
3219 uint32_t version;
3220 if (_sl.lf->Read(reinterpret_cast<uint8_t*>(&version), sizeof(version)) != sizeof(version)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3221
3222 /* see if we have any loader for this type. */
3223 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(tag, version);
3224
3225 /* loader for this savegame type is not implemented? */
3226 if (fmt->init_load == nullptr) {
3227 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3228 }
3229
3230 _sl.lf = fmt->init_load(_sl.lf);
3231 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3232 _next_offs = 0;
3233
3234 if (!load_check) {
3236
3237 /* Old maps were hardcoded to 256x256 and thus did not contain
3238 * any mapsize information. Pre-initialize to 256x256 to not to
3239 * confuse old games */
3240 InitializeGame(256, 256, true, true);
3241
3242 _gamelog.Reset();
3243
3245 /*
3246 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3247 * shared savegame version 4. Anything before that 'obviously'
3248 * does not have any NewGRFs. Between the introduction and
3249 * savegame version 41 (just before 0.5) the NewGRF settings
3250 * were not stored in the savegame and they were loaded by
3251 * using the settings from the main menu.
3252 * So, to recap:
3253 * - savegame version < 4: do not load any NewGRFs.
3254 * - savegame version >= 41: load NewGRFs from savegame, which is
3255 * already done at this stage by
3256 * overwriting the main menu settings.
3257 * - other savegame versions: use main menu settings.
3258 *
3259 * This means that users *can* crash savegame version 4..40
3260 * savegames if they set incompatible NewGRFs in the main menu,
3261 * but can't crash anymore for savegame version < 4 savegames.
3262 *
3263 * Note: this is done here because AfterLoadGame is also called
3264 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3265 */
3267 }
3268 }
3269
3270 if (load_check) {
3271 /* Load chunks into _load_check_data.
3272 * No pools are loaded. References are not possible, and thus do not need resolving. */
3274 } else {
3275 /* Load chunks and resolve references */
3276 SlLoadChunks();
3277 SlFixPointers();
3278 }
3279
3281
3283
3284 if (load_check) {
3285 /* The only part from AfterLoadGame() we need */
3286 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
3287 } else {
3288 _gamelog.StartAction(GamelogActionType::Load);
3289
3290 /* After loading fix up savegame for any internal changes that
3291 * might have occurred since then. If it fails, load back the old game. */
3292 if (!AfterLoadGame()) {
3293 _gamelog.StopAction();
3295 }
3296
3297 _gamelog.StopAction();
3298 }
3299
3300 return SaveLoadResult::Ok;
3301}
3302
3308SaveLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3309{
3310 try {
3311 _sl.action = SaveLoadAction::Load;
3312 return DoLoad(std::move(reader), false);
3313 } catch (...) {
3316 }
3317}
3318
3329SaveLoadResult SaveOrLoad(std::string_view filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3330{
3331 /* An instance of saving is already active, so don't go saving again */
3332 if (_sl.saveinprogress && fop == SaveLoadOperation::Save && dft == DetailedFileType::GameFile && threaded) {
3333 /* if not an autosave, but a user action, show error message */
3334 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WarningLevel::Error);
3335 return SaveLoadResult::Ok;
3336 }
3337 WaitTillSaved();
3338
3339 try {
3340 /* Load a TTDLX or TTDPatch game */
3343
3344 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3345
3346 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3347 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3348 * Note: this is done here because AfterLoadGame is also called
3349 * for OTTD savegames which have their own NewGRF logic. */
3351 _gamelog.Reset();
3352 if (!LoadOldSaveGame(filename)) return SaveLoadResult::ReInit;
3355 _gamelog.StartAction(GamelogActionType::Load);
3356 if (!AfterLoadGame()) {
3357 _gamelog.StopAction();
3359 }
3360 _gamelog.StopAction();
3361 return SaveLoadResult::Ok;
3362 }
3363
3364 assert(dft == DetailedFileType::GameFile);
3365 switch (fop) {
3368 break;
3369
3371 _sl.action = SaveLoadAction::Load;
3372 break;
3373
3375 _sl.action = SaveLoadAction::Save;
3376 break;
3377
3378 default: NOT_REACHED();
3379 }
3380
3381 auto fh = (fop == SaveLoadOperation::Save) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3382
3383 /* Make it a little easier to load savegames from the console */
3384 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Save);
3385 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Base);
3386 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Scenario);
3387
3388 if (!fh.has_value()) {
3389 SlError(fop == SaveLoadOperation::Save ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3390 }
3391
3392 if (fop == SaveLoadOperation::Save) { // SAVE game
3393 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3394 if (!_settings_client.gui.threaded_saves) threaded = false;
3395
3396 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3397 }
3398
3399 /* LOAD game */
3400 assert(fop == SaveLoadOperation::Load || fop == SaveLoadOperation::Check);
3401 Debug(desync, 1, "load: {}", filename);
3402 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SaveLoadOperation::Check);
3403 } catch (...) {
3404 /* This code may be executed both for old and new save games. */
3406
3407 if (fop != SaveLoadOperation::Check) Debug(sl, 0, "{} {}", GetSaveLoadErrorType().GetDecodedString(), GetSaveLoadErrorMessage().GetDecodedString());
3408
3409 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3411 }
3412}
3413
3419{
3420 std::string filename;
3421
3422 if (_settings_client.gui.keep_all_autosave) {
3423 filename = GenerateDefaultSaveName() + counter.Extension();
3424 } else {
3425 filename = counter.Filename();
3426 }
3427
3428 Debug(sl, 2, "Autosaving to '{}'", filename);
3430 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WarningLevel::Error);
3431 }
3432}
3433
3434
3440
3446{
3447 /* Check if we have a name for this map, which is the name of the first
3448 * available company. When there's no company available we'll use
3449 * 'Spectator' as "company" name. */
3450 CompanyID cid = _local_company;
3451 if (!Company::IsValidID(cid)) {
3452 for (const Company *c : Company::Iterate()) {
3453 cid = c->index;
3454 break;
3455 }
3456 }
3457
3458 std::array<StringParameter, 4> params{};
3459 auto it = params.begin();
3460 *it++ = cid;
3461
3462 /* We show the current game time differently depending on the timekeeping units used by this game. */
3464 /* Insert time played. */
3465 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3466 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3467 *it++ = play_time / 60 / 60;
3468 *it++ = (play_time / 60) % 60;
3469 } else {
3470 /* Insert current date */
3471 switch (_settings_client.gui.date_format_in_default_names) {
3472 case 0: *it++ = STR_JUST_DATE_LONG; break;
3473 case 1: *it++ = STR_JUST_DATE_TINY; break;
3474 case 2: *it++ = STR_JUST_DATE_ISO; break;
3475 default: NOT_REACHED();
3476 }
3477 *it++ = TimerGameEconomy::date;
3478 }
3479
3480 /* Get the correct string (special string for when there's not company) */
3481 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3482 SanitizeFilename(filename);
3483 return filename;
3484}
3485
3492{
3495 this->ftype = FIOS_TYPE_INVALID;
3496 return;
3497 }
3498
3499 this->file_op = fop;
3500 this->ftype = ft;
3501}
3502
3508{
3509 this->SetMode(item.type);
3510 this->name = item.name;
3511 this->title = item.title;
3512}
3513
3515{
3516 assert(this->load_description.has_value());
3517 return *this->load_description;
3518}
Base class for autoreplaces/autorenews.
constexpr T AssignBit(T &x, const uint8_t y, bool value)
Assigns a bit in a variable.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr enable_if_t< is_integral_v< T >, T > byteswap(T x) noexcept
Custom implementation of std::byteswap; remove once we build with C++23.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
void PutUtf8(char32_t c)
Append UTF-8 char.
void Put(std::string_view str)
Append string.
void PutIntegerBase(T value, int base)
Append integer 'value' in given number 'base'.
Container for an encoded string, created by GetEncodedString.
Class for calculation jobs to be run on link graphs.
A connected component of a link graph.
Definition linkgraph.h:37
Handler for saving/loading an object to/from disk.
Definition saveload.h:517
std::optional< std::vector< SaveLoad > > load_description
Description derived from savegame being loaded.
Definition saveload.h:519
SaveLoadTable GetLoadDescription() const
Get the description for how to load the chunk.
Handler that is assigned when there is a struct read in the savegame which is not known to the code.
SaveLoadCompatTable GetCompatDescription() const override
Get the pre-header description of the fields in the savegame.
SaveLoadTable GetDescription() const override
Get the description of the fields in the savegame.
void LoadCheck(void *object) const override
Similar to load, but used only to validate savegames.
void Load(void *object) const override
Load the object from disk.
void Save(void *) const override
Save the object to disk.
Template class to help with list-like types.
static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd=SaveLoadType::Variable)
Internal templated helper to return the size in bytes of a list-like type.
static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd=SaveLoadType::Variable)
Internal templated helper to save/load a list-like type.
Compose data into a growing std::string.
Parse data from a string / buffer.
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
@ READ_ONE_SEPARATOR
Read one separator, and include it in the result.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
std::optional< char32_t > TryReadUtf8()
Try to read a UTF-8 character, and then advance reader.
T ReadIntegerBase(int base, T def=0, bool clamp=false)
Read and parse an integer in number 'base', and advance the reader.
bool ReadUtf8If(char32_t c)
Check whether the next UTF-8 char matches 'c', and skip it.
std::string_view ReadUntilUtf8(char32_t c, SeparatorUsage sep)
Read data until the first occurrence of UTF-8 char 'c', and advance reader.
static constexpr TimerGameTick::Ticks TICKS_PER_SECOND
Estimation of how many ticks fit in a single second.
static Date date
Current date in days (day counter).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static DateFract date_fract
Fractional part of the day.
static TickCounter counter
Monotonic counter, in ticks, since start of game.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Functions related to companies.
@ SCC_ENCODED
Encoded string marker and sub-string parameter.
@ SCC_ENCODED_NUMERIC
Encoded numeric parameter.
@ SCC_ENCODED_STRING
Encoded string parameter.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Function to handling different endian machines.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ Critical
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
@ Error
Errors (eg. saving/loading failed).
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition fileio.cpp:1057
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:249
Functions for standard in/out file operations.
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:52
@ Check
Load file for checking and/or preview.
Definition fileio_type.h:53
@ Invalid
Unknown file operation.
Definition fileio_type.h:57
@ Save
File is being saved.
Definition fileio_type.h:55
@ Load
File is being loaded.
Definition fileio_type.h:54
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:28
@ OldGameFile
Old save game or scenario file.
Definition fileio_type.h:30
@ GameFile
Save game or scenario file.
Definition fileio_type.h:31
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition fileio_type.h:88
@ Base
Base directory for all subdirectories.
Definition fileio_type.h:89
@ Autosave
Subdirectory of save for autosaves.
Definition fileio_type.h:91
@ Scenario
Base directory for all scenarios.
Definition fileio_type.h:92
@ Save
Base directory for all savegames.
Definition fileio_type.h:90
@ Invalid
Invalid or unknown file type.
Definition fileio_type.h:24
@ None
nothing to do
Definition fileio_type.h:18
Declarations for savegames operations.
LoadCheckData _load_check_data
Data loaded from save during SL_LOAD_CHECK.
Definition fios_gui.cpp:41
fluid_settings_t * settings
FluidSynth settings handle.
uint32_t _ttdp_version
version of TTDP savegame (if applicable)
Definition saveload.cpp:80
SaveLoadVersion _sl_version
the major savegame version identifier
Definition saveload.cpp:81
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:82
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
SavegameType _savegame_type
type of savegame we are loading
Definition saveload.cpp:77
const SaveLoadVersion SAVEGAME_VERSION
current savegame version
Functions to be called to log fundamental changes to the game.
@ Load
Game loaded.
Definition gamelog.h:19
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1694
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:52
Declaration of link graph classes used for cargo distribution.
Declaration of link graph job classes used for cargo distribution.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _networking
are we in networking mode?
Definition network.cpp:67
bool _network_server
network-server is active
Definition network.cpp:68
Basic functions/variables used all over the place.
GRFConfigList _grfconfig
First item in list of current GRF set up.
GRFListCompatibility IsGoodGRFConfigList(GRFConfigList &grfconfig)
Check if all GRFs in the GRF config from a savegame can be loaded.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
NewGRF handling of rail types.
NewGRF handling of road types.
uint8_t ReadByte(LoadgameState &ls)
Reads a byte from the buffer and decompress if needed.
Definition oldloader.cpp:86
Base class for roadstops.
A number of safeguards to prevent using unsafe methods.
static void SlRefVector(void *vector, VarType conv)
Save/Load a vector.
static const uint LZO_BUFFER_SIZE
Buffer size for the LZO compressor.
void SlError(StringID string, const std::string &extra_msg)
Error handler.
Definition saveload.cpp:339
void ProcessAsyncSaveFinish()
Handle async save finishes.
Definition saveload.cpp:394
void FixSCCEncodedNegative(std::string &str)
Scan the string for SCC_ENCODED_NUMERIC with negative values, and reencode them as uint64_t.
static const lzma_stream _lzma_init
Have a copy of an initialised LZMA stream.
static void * IntToReference(size_t index, SLRefType rt)
Pointers cannot be loaded from a savegame, so this function gets the index from the savegame and retu...
static SaveLoadResult DoSave(std::shared_ptr< SaveFilter > writer, bool threaded)
Actually perform the saving of the savegame.
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:83
static void SaveFileDone()
Update the gui accordingly when saving is done and release locks on saveload.
SaveLoadVersion _sl_version
the major savegame version identifier
Definition saveload.cpp:81
static SavegameFileType GetSavegameFileType(const SaveLoad &sld)
Return the type as saved/loaded inside the savegame.
Definition saveload.cpp:637
SaveLoadResult SaveOrLoad(std::string_view filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
Main Save or Load function where the high-level saveload functions are handled.
static uint32_t ReferenceToInt(const void *obj, SLRefType rt)
Pointers cannot be saved to a savegame, so this functions gets the index of the item,...
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:220
static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
Return the size in bytes of a vector.
static void ResetSaveloadData()
Clear temporary data that is passed between various saveload phases.
static void SlWriteSimpleGamma(size_t i)
Write the header descriptor of an object or an array.
Definition saveload.cpp:523
static size_t SlCalcTableHeader(const SaveLoadTable &slt)
Calculate the size of the table header.
static void ClearSaveLoadState()
Clear/free saveload state.
bool _do_autosave
are we doing an autosave at the moment?
Definition saveload.cpp:84
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:376
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:377
std::vector< SaveLoad > SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
Load a table header in a savegame compatible way.
static void ResetSettings()
Reset all settings to their default, so any settings missing in the savegame are their default,...
void SlWriteByte(uint8_t b)
Wrapper for writing a byte to the dumper.
Definition saveload.cpp:419
size_t SlGetStructListLength(size_t limit)
Get the length of this list; if it exceeds the limit, error out.
Label< struct SaveLoadFormatLabelTag > SaveLoadFormatTag
Unique 4-letter tag for the different saveload formats.
static SaveLoadResult DoLoad(std::shared_ptr< LoadFilter > reader, bool load_check)
Actually perform the loading of a "non-old" savegame.
SaveLoadResult SaveWithFilter(std::shared_ptr< SaveFilter > writer, bool threaded)
Save the game using a (writer) filter.
static size_t SlCalcArrayLen(size_t length, VarType conv)
Return the size in bytes of a certain type of atomic array.
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:375
static const SaveLoadFormatTag SAVEGAME_TAG_LZMA
Tag for a game with lzma compression.
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:747
static const SaveLoadFormatTag SAVEGAME_TAG_LZO
Tag for a game compressed with LZO.
static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
Called by save thread to tell we finished saving.
Definition saveload.cpp:383
void SetSaveLoadError(StringID str)
Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friend...
void SlCopy(void *object, size_t length, VarType conv)
Copy a list of SaveLoadType::Variables to/from a savegame.
size_t SlGetFieldLength()
Get the length of the current object.
Definition saveload.cpp:873
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:730
NeedLength
Definition saveload.cpp:95
@ WantLength
writing length and data
Definition saveload.cpp:97
@ None
not working in NeedLength mode
Definition saveload.cpp:96
@ CalcLength
need to calculate the length
Definition saveload.cpp:98
static void SaveFileStart()
Update the gui accordingly when starting saving and set locks on saveload.
static void SlNullPointers()
Null all pointers (convert index -> nullptr).
Definition saveload.cpp:314
static void SlStdString(void *ptr, VarType conv)
Save/Load a std::string.
static size_t SlCalcRefListLen(const void *list, VarType conv)
Return the size in bytes of a list.
static bool SlIsObjectValidInSavegame(const SaveLoad &sld)
Are we going to save this object or not?
EncodedString GetSaveLoadErrorType()
Return the appropriate initial string for an error depending on whether we are saving or loading.
void SlSaveLoadRef(void *ptr, VarType conv)
Handle conversion for references.
static void SlFixPointers()
Fix all pointers (convert index -> pointer).
void SlErrorCorrupt(const std::string &msg)
Error handler for corrupt savegames.
Definition saveload.cpp:369
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:789
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.
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:481
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:87
@ Ptrs
fixing pointers
Definition saveload.cpp:90
@ Null
null all pointers (on loading error)
Definition saveload.cpp:91
@ LoadCheck
partial loading into _load_check_data
Definition saveload.cpp:92
@ Load
loading
Definition saveload.cpp:88
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:853
static ChunkId SlReadChunkId()
Read the ChunkId.
Definition saveload.cpp:465
static void SlCopyInternal(void *object, size_t length, VarType conv)
Internal function to save/Load a list of SaveLoadType::Variables.
static void SlArray(void *array, size_t length, VarType conv)
Save/Load the length of the array followed by the array of SaveLoadType::Variable elements.
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:935
static const SaveLoadFormatTag SAVEGAME_TAG_NONE
Tag for a game without compression.
void WriteValue(void *ptr, VarMemType conv, int64_t val)
Write the value of a setting.
Definition saveload.cpp:909
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:78
static void SlLoadCheckChunk(const ChunkHandler &ch)
Load a chunk of data for checking savegames.
void SlSetLength(size_t length)
Sets the length of either a RIFF object or the number of items in an array.
Definition saveload.cpp:801
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:410
void ClearOldOrders()
Clear all old orders.
Definition order_sl.cpp:114
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:218
void DoExitSave()
Do a save when exiting the game (_settings_client.gui.autosave_on_exit).
static void SlLoadChunks()
Load all chunks.
static uint8_t SlCalcConvFileLen(VarType conv)
Return the size in bytes of a certain type of normal/atomic variable as it appears in a saved game.
Definition saveload.cpp:704
static void SlSaveChunk(const ChunkHandler &ch)
Save a chunk of data (eg.
size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
Calculate the size of an object.
void SlObject(void *object, const SaveLoadTable &slt)
Main SaveLoad function.
EncodedString GetSaveLoadErrorMessage()
Return the description of the error.
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
bool AfterLoadGame()
Perform a (large) amount of savegame conversion magic in order to load older savegames and to fill th...
SaveLoadResult LoadWithFilter(std::shared_ptr< LoadFilter > reader)
Load the game using a (reader) filter.
static void SlVector(void *vector, VarType conv)
Save/load a std::vector.
static void SaveFileError()
Show a gui message when saving has failed.
static SaveLoadResult SaveFileToDisk(bool threaded)
We have written the whole game into memory, _memory_savegame, now find and appropriate compressor and...
void SlGlobList(const SaveLoadTable &slt)
Save or Load (a list of) global variables.
static std::pair< const SaveLoadFormat &, uint8_t > GetSavegameFormat(std::string_view full_name)
Return the savegameformat of the game.
static uint SlCalcConvMemLen(VarMemType conv)
Return the size in bytes of a certain type of normal/atomic variable as it appears in memory.
Definition saveload.cpp:672
void FixSCCEncoded(std::string &str, bool fix_code)
Scan the string for old values of SCC_ENCODED and fix it to it's new, value.
static void SlSaveChunks()
Save all chunks.
std::string GenerateDefaultSaveName()
Get the default name for a savegame or screenshot.
static const size_t MEMORY_CHUNK_SIZE
Save in chunks of 128 KiB.
Definition saveload.cpp:102
int64_t ReadValue(const void *ptr, VarMemType conv)
Return a signed-long version of the value of a setting.
Definition saveload.cpp:885
void SlAutolength(AutolengthProc *proc, int arg)
Do something of which I have no idea what it is :P.
void SlReadString(std::string &str, size_t length)
Read the given amount of bytes from the buffer into the string.
static const SaveLoadFormatTag SAVEGAME_TAG_ZLIB
Tag for a game with zlib compression.
static const ChunkHandler * SlFindChunkHandler(ChunkId id)
Find the ChunkHandler that will be used for processing the found chunk in the savegame or in memory.
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:552
static const SaveLoadFormat * DetermineSaveLoadFormat(SaveLoadFormatTag tag, uint32_t raw_version)
Determines the SaveLoadFormat that is connected to the given tag.
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.
VarMemType
The types/structures of data we have in memory.
Definition saveload.h:653
@ U64
A 64 bit unsigned int.
Definition saveload.h:662
@ Name
old custom name to be converted to a string pointer
Definition saveload.h:666
@ LabelForward
A 4 character Label, stored as-is.
Definition saveload.h:668
@ I8
A 8 bit signed int.
Definition saveload.h:655
@ U8
A 8 bit unsigned int.
Definition saveload.h:656
@ LabelReverse
A 4 character Label, stored in reverse.
Definition saveload.h:667
@ StrQ
string pointer enclosed in quotes
Definition saveload.h:665
@ Null
useful to write zeros in savegame.
Definition saveload.h:663
@ I16
A 16 bit signed int.
Definition saveload.h:657
@ Bool
A boolean value.
Definition saveload.h:654
@ U32
A 32 bit unsigned int.
Definition saveload.h:660
@ I32
A 32 bit signed int.
Definition saveload.h:659
@ I64
A 64 bit signed int.
Definition saveload.h:661
@ Str
string pointer
Definition saveload.h:664
@ U16
A 16 bit unsigned int.
Definition saveload.h:658
VarFileType
The types/structures of data that can be stored in the file.
Definition saveload.h:632
@ String
A string.
Definition saveload.h:647
@ U64
A 64 bit unsigned int.
Definition saveload.h:645
@ I8
A 8 bit signed int.
Definition saveload.h:636
@ U8
A 8 bit unsigned int.
Definition saveload.h:638
@ Struct
An arbitrary structure.
Definition saveload.h:648
@ I16
A 16 bit signed int.
Definition saveload.h:639
@ U32
A 32 bit unsigned int.
Definition saveload.h:642
@ StringID
StringID offset into strings-array.
Definition saveload.h:646
@ I32
A 32 bit signed int.
Definition saveload.h:641
@ I64
A 64 bit signed int.
Definition saveload.h:644
@ U16
A 16 bit unsigned int.
Definition saveload.h:640
SavegameType
Types of save games.
Definition saveload.h:428
@ OTTD
OTTD savegame.
Definition saveload.h:432
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:1353
SLRefType
Type of reference (SLE_REF, SLE_CONDREF).
Definition saveload.h:617
@ LinkGraph
Load/save a reference to a link graph.
Definition saveload.h:627
@ CargoPacket
Load/save a reference to a cargo packet.
Definition saveload.h:624
@ OrderList
Load/save a reference to an orderlist.
Definition saveload.h:625
@ Station
Load/save a reference to a station.
Definition saveload.h:619
@ OldVehicle
Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
Definition saveload.h:621
@ Storage
Load/save a reference to a persistent storage.
Definition saveload.h:626
@ EngineRenew
Load/save a reference to an engine renewal (autoreplace).
Definition saveload.h:623
@ Town
Load/save a reference to a town.
Definition saveload.h:620
@ LinkGraphJob
Load/save a reference to a link graph job.
Definition saveload.h:628
@ Vehicle
Load/save a reference to a vehicle.
Definition saveload.h:618
@ RoadStop
Load/save a reference to a bus/truck stop.
Definition saveload.h:622
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1309
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:511
SaveLoadType
Type of data saved.
Definition saveload.h:745
@ ReferenceList
Save/load a list of SaveLoadType::Reference elements.
Definition saveload.h:754
@ String
Save/load a std::string.
Definition saveload.h:750
@ Array
Save/load a fixed-size array of SaveLoadType::Variable elements.
Definition saveload.h:752
@ Variable
Save/load a variable.
Definition saveload.h:746
@ Vector
Save/load a vector of SaveLoadType::Variable elements.
Definition saveload.h:753
@ Reference
Save/load a reference.
Definition saveload.h:747
@ StructList
Save/load a list of structs.
Definition saveload.h:755
@ Struct
Save/load a struct.
Definition saveload.h:748
@ Null
Save null-bytes and load to nowhere.
Definition saveload.h:758
@ SaveByte
Save (but not load) a byte.
Definition saveload.h:757
@ ReferenceVector
Save/load a vector of SaveLoadType::Reference elements.
Definition saveload.h:760
std::span< const struct SaveLoadCompat > SaveLoadCompatTable
A table of SaveLoadCompat entries.
Definition saveload.h:514
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1268
SaveLoadVersion
SaveLoad versions Previous savegame versions, the trunk revision where they were introduced and the r...
Definition saveload.h:33
@ EndPatchpacks
Saveload version: 286 Last known patchpack to use a version just above ours.
Definition saveload.h:325
@ MoveSccEncoded
Saveload version: 169, SVN revision: 23816 Move SCC_ENCODED to the first StringControlCode.
Definition saveload.h:249
@ TownTolerancePauseMode
Saveload version: 4.0, SVN revision: 1 Town council tolerance and pause mode.
Definition saveload.h:41
@ MoreCargoPackets
Saveload version: 69, SVN revision: 10319 Allow more than ~65k cargo packets.
Definition saveload.h:129
@ EncodedStringFormat
Saveload version: 350, GitHub pull request: 13499 Encoded String format changed.
Definition saveload.h:403
@ FixSccEncodedNegative
Saveload version: 353, GitHub pull request: 14049 Fix encoding of negative parameters.
Definition saveload.h:406
@ MinVersion
First savegame version.
Definition saveload.h:34
@ SaveloadListLength
Saveload version: 293, GitHub pull request: 9374 Consistency in list length with SaveLoadType::Struc...
Definition saveload.h:334
@ MaxVersion
Highest possible saveload version.
Definition saveload.h:424
@ StartPatchpacks
Saveload version: 220 First known patchpack to use a version just above ours.
Definition saveload.h:324
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
ChunkType
Type of a chunk.
Definition saveload.h:441
@ SparseTable
A SparseArray with a header describing the elements.
Definition saveload.h:446
@ ReadOnly
Chunk is never saved.
Definition saveload.h:449
@ Array
Contiguous array of elements starting at index 0.
Definition saveload.h:443
@ Table
An Array with a header describing the elements.
Definition saveload.h:445
@ FileTypeMask
All ChunkType values that are saved in the file have to be within this mask.
Definition saveload.h:448
@ Riff
4 bits store the chunk type, 28 bits the number of bytes.
Definition saveload.h:442
@ SparseArray
Array of elements with index for each element.
Definition saveload.h:444
Label< struct ChunkIdTag > ChunkId
Label/unique identifier for each of the chunks in the savegame.
Definition saveload.h:453
void SlErrorCorruptFmt(const fmt::format_string< Args... > format, Args &&... fmt_args)
Issue an SlErrorCorrupt with a format string.
Declaration of filters used for saving and loading savegames.
std::shared_ptr< SaveFilter > CreateSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Instantiator for a save filter.
std::shared_ptr< LoadFilter > CreateLoadFilter(std::shared_ptr< LoadFilter > chain)
Instantiator for a load filter.
Declaration of functions used in more save/load files.
StringID RemapOldStringID(StringID s)
Remap a string ID from the old format to the new format.
std::string CopyFromOldName(StringID id)
Copy and convert old custom names to UTF-8.
SaveLoadResult
Save or load result codes.
@ Error
error that was caught before internal structures were modified
@ Ok
completed successfully
@ ReInit
error that was caught in the middle of updating game state, need to clear it. (can only happen during...
std::span< const struct SaveLoad > SaveLoadTable
A table of SaveLoad entries.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
SettingTable GetSaveLoadSettingTable()
Create a single table with all settings that should be stored/loaded in the savegame.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Functions and types used internally for the settings configurations.
@ NotInSave
Do not save with savegame, basically client-based.
@ NoNetworkSync
Do not synchronize over network (but it is saved if SettingFlag::NotInSave is not set).
static constexpr const SettingDesc * GetSettingDesc(const SettingVariant &desc)
Helper to convert the type of the iterated settings description to a pointer to it.
Base classes/functions for stations.
Functions, definitions and such used only by the GUI.
@ SBI_SAVELOAD_FINISH
finished saving
@ SBI_SAVELOAD_START
started saving
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:261
void StrMakeValidInPlace(char *str, StringValidationSettings settings)
Scans the string for invalid characters and replaces them with a question mark '?
Definition string.cpp:157
Compose strings from textual and binary data.
Parse strings.
static std::optional< T > ParseInteger(std::string_view arg, int base=10, bool clamp=false)
Change a string into its number representation.
Functions related to low-level strings.
@ ReplaceWithQuestionMark
Replace the unknown/bad bits with question marks.
Definition string_type.h:45
@ AllowControlCode
Allow the special control codes.
Definition string_type.h:47
EnumBitSet< StringValidationSetting, uint8_t > StringValidationSettings
Bitset of StringValidationSetting elements.
Definition string_type.h:57
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
Types related to strings.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > StringID
Numeric value that represents a string, independent of the selected language.
Base for a four character label/tag/id.
constexpr bool Empty() const
Check whether the label is empty.
Container for cargo from the same location and time.
Definition cargopacket.h:41
Handlers and description of chunk.
Definition saveload.h:456
ChunkType type
Type of the chunk.
Definition saveload.h:458
virtual void LoadCheck(size_t len=0) const
Load the chunk for game preview.
ChunkId id
Unique ID (4 letters).
Definition saveload.h:457
std::string GetName() const
Get the name of this chunk.
Definition saveload.h:501
virtual void Load() const =0
Load the chunk.
virtual void Save() const
Save the chunk.
Definition saveload.h:474
Struct to store engine replacements.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
void Reset() override
Reset this filter to read from the beginning of the file.
~FileReader() override
Make sure everything is cleaned up.
FileReader(FileHandle &&file)
Create the file reader, so it reads from a specific file.
long begin
The begin of the file.
std::optional< FileHandle > file
The file to read from.
Deals with the type of the savegame, independent of extension.
void SetMode(const FiosType &ft, SaveLoadOperation fop=SaveLoadOperation::Load)
Set the mode and file type of the file to save or load.
FiosType ftype
File type.
SaveLoadOperation file_op
File operation to perform.
std::string name
Name of the file.
EncodedString title
Internal name of the game.
void Set(const FiosItem &item)
Set the mode, title and name of the file.
std::optional< FileHandle > file
The file to write to.
void Write(const uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
~FileWriter() override
Make sure everything is cleaned up.
FileWriter(FileHandle &&file)
Create the file writer, so it writes to a specific file.
void Finish() override
Prepare everything to finish writing the savegame.
Deals with finding savegames.
Definition fios.h:78
A savegame name automatically numbered.
Definition fios.h:119
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
Definition fios.cpp:723
std::string Extension()
Generate an extension for a savegame name.
Definition fios.cpp:733
Elements of a file system that are recognized.
Definition fileio_type.h:63
AbstractFileType abstract
Abstract file type.
Definition fileio_type.h:64
lzma_stream lzma
Stream state that we are reading from.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
~LZMALoadFilter() override
Clean everything up.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
LZMALoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
~LZMASaveFilter() override
Clean up what we allocated.
void WriteLoop(const uint8_t *p, size_t len, lzma_action action)
Helper loop for writing the data.
void Write(const 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.
LZMASaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
lzma_stream lzma
Stream state that we are writing to.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
LZOLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
size_t Read(uint8_t *buf, size_t ssize) override
Read a given number of bytes from the savegame.
void Write(const 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.
A four character label/tag/id.
std::shared_ptr< LoadFilter > chain
Chained to the (savegame) filters.
LoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Container for dumping the savegame (quickly) to memory.
Definition saveload.cpp:146
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:148
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:155
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:147
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:149
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:189
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:170
NoCompLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
void Write(const uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
NoCompSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:384
Class for pooled persistent storage of data.
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static OrderList * Get(auto index)
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:107
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:116
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:110
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:138
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:109
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:106
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:108
A Stop for a Road Vehicle.
SaveFilter(std::shared_ptr< SaveFilter > chain)
Initialise this filter.
std::shared_ptr< SaveFilter > chain
Chained to the (savegame) filters.
The format for a reader/writer type of a savegame.
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.
SaveLoadFormatTag tag
the 4-letter tag by which it is identified in the savegame
std::string_view name
name of the compressor/decompressor (debug-only)
uint8_t max_compression
the maximum compression level of this format
The saveload struct, containing reader-writer functions, buffer, version, etc.
Definition saveload.cpp:196
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:209
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:207
ChunkType chunk_type
The type of chunk we are reading or writing.
Definition saveload.cpp:199
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:206
StringID error_str
the translatable error message to show
Definition saveload.cpp:212
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:197
std::string extra_msg
the error message
Definition saveload.cpp:213
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:198
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:215
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:210
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:204
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:202
bool error
did an error occur or not
Definition saveload.cpp:200
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:203
SaveLoad type struct.
Definition saveload.h:765
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:778
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:783
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:780
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:776
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:775
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:777
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:779
Container/wrapper for the file type that is used in tables in the save game.
Definition saveload.cpp:586
uint8_t storage
Actual storage of the file type.
Definition saveload.cpp:588
constexpr VarFileType Type() const
Get the VarType for this field.
Definition saveload.cpp:625
SavegameFileType(VarFileType file_type, bool has_field_length=false)
Create the type.
Definition saveload.cpp:598
constexpr bool HasFieldLength() const
Does this field have a length?
Definition saveload.cpp:615
constexpr bool IsEnd() const
Is this the end-of-table marker?
Definition saveload.cpp:609
static constexpr uint8_t HAS_FIELD_LENGTH_BIT
Set this bit to denote the type has a field length.
Definition saveload.cpp:587
SavegameFileType()
Create an end-of-table marker.
Definition saveload.cpp:591
Properties of config file settings.
SettingFlags flags
Handles how a setting would show up in the GUI (text/currency, etc.).
virtual void ResetToDefault(void *object) const =0
Reset the setting to its default value.
static Station * Get(auto index)
Station data structure.
Town data structure.
Definition town.h:64
Container of a variable's characteristics about a variable's storage.
Definition saveload.h:672
SLRefType ref
The reference type.
Definition saveload.h:676
VarMemType mem
The way of storing data in memory.
Definition saveload.h:674
StringValidationSettings string_validation_settings
Any settings related to validation of the strings.
Definition saveload.h:675
VarFileType file
The way of storing data in the file.
Definition saveload.h:673
static constexpr VarType U16
Store a 16 bits unsigned int.
Definition saveload.h:731
static constexpr VarType U8
Store a 8 bits unsigned int.
Definition saveload.h:729
static constexpr VarType STR
Store string.
Definition saveload.h:737
static constexpr VarType LABEL_REVERSE
Store a Label in reverse.
Definition saveload.h:740
static constexpr VarType I16
Store a 16 bits signed int.
Definition saveload.h:730
static constexpr VarType I8
Store a 8 bits signed int.
Definition saveload.h:728
static constexpr VarType U32
Store a 32 bits unsigned int.
Definition saveload.h:733
static constexpr VarType STRINGID
Store a StringID.
Definition saveload.h:736
static constexpr VarType LABEL_FORWARD
Store a Label as-is.
Definition saveload.h:741
static constexpr VarType I32
Store a 32 bits signed int.
Definition saveload.h:732
Vehicle data structure.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
ZlibLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
~ZlibLoadFilter() override
Clean everything up.
z_stream z
Stream state we are reading from.
z_stream z
Stream state we are writing to.
void Write(const uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
~ZlibSaveFilter() override
Clean up what we allocated.
void Finish() override
Prepare everything to finish writing the savegame.
ZlibSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
void WriteLoop(const uint8_t *p, size_t len, int mode)
Helper loop for writing the data.
Base of all threads.
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Definition thread.h:24
bool StartNewThread(std::thread *thr, std::string_view name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
Definition of the game-economy-timer.
Base of the town class.
Base class for all vehicles.
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3318
Window functions not directly related to making/drawing windows.