OpenTTD Source 20260721-master-g25ec12c62d
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(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<uint16_t>(SlReadUint16())); 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 if (_sl.action == SaveLoadAction::Ptrs || _sl.action == SaveLoadAction::Null) return;
1265
1266 /* Automatically calculate the length? */
1267 if (_sl.need_length != NeedLength::None) {
1268 SlSetLength(length * SlCalcConvFileLen(conv));
1269 /* Determine length only? */
1270 if (_sl.need_length == NeedLength::CalcLength) return;
1271 }
1272
1273 SlCopyInternal(object, length, conv);
1274}
1275
1282static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1283{
1284 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1285}
1286
1293static void SlArray(void *array, size_t length, VarType conv)
1294{
1295 switch (_sl.action) {
1297 SlWriteArrayLength(length);
1298 SlCopyInternal(array, length, conv);
1299 return;
1300
1302 case SaveLoadAction::Load: {
1304 size_t sv_length = SlReadArrayLength();
1305 if (conv.mem == VarMemType::Null) {
1306 /* We don't know this field, so we assume the length in the savegame is correct. */
1307 length = sv_length;
1308 } else if (sv_length != length) {
1309 /* If the SLE_ARR changes size, a savegame bump is required
1310 * and the developer should have written conversion lines.
1311 * Error out to make this more visible. */
1312 SlErrorCorrupt("Fixed-length array is of wrong length");
1313 }
1314 }
1315
1316 SlCopyInternal(array, length, conv);
1317 return;
1318 }
1319
1322 return;
1323
1324 default:
1325 NOT_REACHED();
1326 }
1327}
1328
1339static uint32_t ReferenceToInt(const void *obj, SLRefType rt)
1340{
1341 assert(_sl.action == SaveLoadAction::Save);
1342
1343 if (obj == nullptr) return 0;
1344
1345 switch (rt) {
1346 case SLRefType::OldVehicle: // Old vehicles we save as new ones
1347 case SLRefType::Vehicle: return static_cast<const Vehicle *>(obj)->index + 1;
1348 case SLRefType::Station: return static_cast<const Station *>(obj)->index + 1;
1349 case SLRefType::Town: return static_cast<const Town *>(obj)->index + 1;
1350 case SLRefType::RoadStop: return static_cast<const RoadStop *>(obj)->index + 1;
1351 case SLRefType::EngineRenew: return static_cast<const EngineRenew *>(obj)->index + 1;
1352 case SLRefType::CargoPacket: return static_cast<const CargoPacket *>(obj)->index + 1;
1353 case SLRefType::OrderList: return static_cast<const OrderList *>(obj)->index + 1;
1354 case SLRefType::Storage: return static_cast<const PersistentStorage *>(obj)->index + 1;
1355 case SLRefType::LinkGraph: return static_cast<const LinkGraph *>(obj)->index + 1;
1356 case SLRefType::LinkGraphJob: return static_cast<const LinkGraphJob *>(obj)->index + 1;
1357 default: NOT_REACHED();
1358 }
1359}
1360
1371static void *IntToReference(size_t index, SLRefType rt)
1372{
1373 static_assert(sizeof(size_t) <= sizeof(void *));
1374
1375 assert(_sl.action == SaveLoadAction::Ptrs);
1376
1377 /* After version 4.3 SLRefType::OldVehicle is saved as SLRefType::Vehicle,
1378 * and should be loaded like that */
1380 rt = SLRefType::Vehicle;
1381 }
1382
1383 /* No need to look up nullptr pointers, just return immediately */
1384 if (index == (rt == SLRefType::OldVehicle ? 0xFFFF : 0)) return nullptr;
1385
1386 /* Correct index. Old vehicles were saved differently:
1387 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1388 if (rt != SLRefType::OldVehicle) index--;
1389
1390 switch (rt) {
1392 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1393 SlErrorCorrupt("Referencing invalid OrderList");
1394
1396 case SLRefType::Vehicle:
1397 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1398 SlErrorCorrupt("Referencing invalid Vehicle");
1399
1400 case SLRefType::Station:
1401 if (Station::IsValidID(index)) return Station::Get(index);
1402 SlErrorCorrupt("Referencing invalid Station");
1403
1404 case SLRefType::Town:
1405 if (Town::IsValidID(index)) return Town::Get(index);
1406 SlErrorCorrupt("Referencing invalid Town");
1407
1409 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1410 SlErrorCorrupt("Referencing invalid RoadStop");
1411
1413 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1414 SlErrorCorrupt("Referencing invalid EngineRenew");
1415
1417 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1418 SlErrorCorrupt("Referencing invalid CargoPacket");
1419
1420 case SLRefType::Storage:
1421 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1422 SlErrorCorrupt("Referencing invalid PersistentStorage");
1423
1425 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1426 SlErrorCorrupt("Referencing invalid LinkGraph");
1427
1429 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1430 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1431
1432 default: NOT_REACHED();
1433 }
1434}
1435
1441void SlSaveLoadRef(void *ptr, VarType conv)
1442{
1443 switch (_sl.action) {
1445 SlWriteUint32(ReferenceToInt(*static_cast<void **>(ptr), conv.ref));
1446 break;
1449 *static_cast<size_t *>(ptr) = IsSavegameVersionBefore(SaveLoadVersion::MoreCargoPackets) ? SlReadUint16() : SlReadUint32();
1450 break;
1452 *static_cast<void **>(ptr) = IntToReference(*static_cast<size_t *>(ptr), conv.ref);
1453 break;
1455 *static_cast<void **>(ptr) = nullptr;
1456 break;
1457 default: NOT_REACHED();
1458 }
1459}
1460
1464template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1466 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1467public:
1475 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SaveLoadType::Variable)
1476 {
1477 assert(cmd == SaveLoadType::Variable || cmd == SaveLoadType::Reference);
1478
1479 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1480
1481 int type_size = SlGetArrayLength(list->size());
1482 int item_size = SlCalcConvFileLen(cmd == SaveLoadType::Variable ? conv : VarType{VarFileType::U32, {}});
1483 return list->size() * item_size + type_size;
1484 }
1485
1486 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1487 {
1488 switch (cmd) {
1489 case SaveLoadType::Variable: SlSaveLoadConv(item, conv); break;
1490 case SaveLoadType::Reference: SlSaveLoadRef(item, conv); break;
1491 case SaveLoadType::String: SlStdString(item, conv); break;
1492 default:
1493 NOT_REACHED();
1494 }
1495 }
1496
1503 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SaveLoadType::Variable)
1504 {
1505 assert(cmd == SaveLoadType::Variable || cmd == SaveLoadType::Reference || cmd == SaveLoadType::String);
1506
1507 SlStorageT *list = static_cast<SlStorageT *>(storage);
1508
1509 switch (_sl.action) {
1511 SlWriteArrayLength(list->size());
1512
1513 for (auto &item : *list) {
1514 SlSaveLoadMember(cmd, &item, conv);
1515 }
1516 break;
1517
1519 case SaveLoadAction::Load: {
1520 size_t length;
1521 switch (cmd) {
1522 case SaveLoadType::Variable: length = IsSavegameVersionBefore(SaveLoadVersion::SaveloadListLength) ? SlReadUint32() : SlReadArrayLength(); break;
1523 case SaveLoadType::Reference: length = IsSavegameVersionBefore(SaveLoadVersion::MoreCargoPackets) ? SlReadUint16() : IsSavegameVersionBefore(SaveLoadVersion::SaveloadListLength) ? SlReadUint32() : SlReadArrayLength(); break;
1524 case SaveLoadType::String: length = SlReadArrayLength(); break;
1525 default: NOT_REACHED();
1526 }
1527
1528 list->clear();
1529 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1530 list->reserve(length);
1531 }
1532
1533 /* Load each value and push to the end of the storage. */
1534 for (size_t i = 0; i < length; i++) {
1535 Tvar &data = list->emplace_back();
1536 SlSaveLoadMember(cmd, &data, conv);
1537 }
1538 break;
1539 }
1540
1542 for (auto &item : *list) {
1543 SlSaveLoadMember(cmd, &item, conv);
1544 }
1545 break;
1546
1548 list->clear();
1549 break;
1550
1551 default: NOT_REACHED();
1552 }
1553 }
1554};
1555
1562static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1563{
1565}
1566
1572static void SlRefList(void *list, VarType conv)
1573{
1574 /* Automatically calculate the length? */
1575 if (_sl.need_length != NeedLength::None) {
1576 SlSetLength(SlCalcRefListLen(list, conv));
1577 /* Determine length only? */
1578 if (_sl.need_length == NeedLength::CalcLength) return;
1579 }
1580
1582}
1583
1590static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1591{
1593}
1594
1600static void SlRefVector(void *vector, VarType conv)
1601{
1602 /* Automatically calculate the length? */
1603 if (_sl.need_length != NeedLength::None) {
1604 SlSetLength(SlCalcRefVectorLen(vector, conv));
1605 /* Determine length only? */
1606 if (_sl.need_length == NeedLength::CalcLength) return;
1607 }
1608
1610}
1611
1618static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1619{
1620 switch (conv.mem) {
1621 case VarMemType::Bool: NOT_REACHED(); // Not supported
1630
1631 case VarMemType::Str:
1632 /* Strings are a length-prefixed field type in the savegame table format,
1633 * these may not be directly stored in another length-prefixed container type. */
1634 NOT_REACHED();
1635
1636 default: NOT_REACHED();
1637 }
1638}
1639
1645static void SlVector(void *vector, VarType conv)
1646{
1647 switch (conv.mem) {
1648 case VarMemType::Bool: NOT_REACHED(); // Not supported
1657
1658 case VarMemType::Str:
1659 /* Strings are a length-prefixed field type in the savegame table format,
1660 * these may not be directly stored in another length-prefixed container type.
1661 * This is permitted for load-related actions, because invalid fields of this type are present
1662 * from SaveLoadVersion::CompanyAllowList up to SaveLoadVersion::CompanyAllowListV2. */
1663 assert(_sl.action != SaveLoadAction::Save);
1665 break;
1666
1667 default: NOT_REACHED();
1668 }
1669}
1670
1676static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1677{
1678 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1679}
1680
1686static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1687{
1688 size_t length = 0;
1689
1690 for (auto &sld : slt) {
1691 if (!SlIsObjectValidInSavegame(sld)) continue;
1692
1694 length += SlCalcStdStringLen(&sld.name);
1695 }
1696
1697 length += SlCalcConvFileLen(VarTypes::U8); // End-of-list entry.
1698
1699 for (auto &sld : slt) {
1700 if (!SlIsObjectValidInSavegame(sld)) continue;
1701 if (sld.cmd == SaveLoadType::StructList || sld.cmd == SaveLoadType::Struct) {
1702 length += SlCalcTableHeader(sld.handler->GetDescription());
1703 }
1704 }
1705
1706 return length;
1707}
1708
1715size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1716{
1717 size_t length = 0;
1718
1719 /* Need to determine the length and write a length tag. */
1720 for (auto &sld : slt) {
1721 length += SlCalcObjMemberLength(object, sld);
1722 }
1723 return length;
1724}
1725
1726size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1727{
1728 assert(_sl.action == SaveLoadAction::Save);
1729
1730 if (!SlIsObjectValidInSavegame(sld)) return 0;
1731
1732 switch (sld.cmd) {
1735 case SaveLoadType::Array: return SlCalcArrayLen(sld.length, sld.conv);
1738 case SaveLoadType::Vector: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1740 case SaveLoadType::SaveByte: return 1; // a byte is logically of size 1
1741 case SaveLoadType::Null: return SlCalcConvFileLen(sld.conv) * sld.length;
1742
1745 NeedLength old_need_length = _sl.need_length;
1746 size_t old_obj_len = _sl.obj_len;
1747
1748 _sl.need_length = NeedLength::CalcLength;
1749 _sl.obj_len = 0;
1750
1751 /* Pretend that we are saving to collect the object size. Other
1752 * means are difficult, as we don't know the length of the list we
1753 * are about to store. */
1754 sld.handler->Save(const_cast<void *>(object));
1755 size_t length = _sl.obj_len;
1756
1757 _sl.obj_len = old_obj_len;
1758 _sl.need_length = old_need_length;
1759
1760 if (sld.cmd == SaveLoadType::Struct) {
1761 length += SlGetArrayLength(1);
1762 }
1763
1764 return length;
1765 }
1766
1767 default: NOT_REACHED();
1768 }
1769 return 0;
1770}
1771
1772static bool SlObjectMember(void *object, const SaveLoad &sld)
1773{
1774 if (!SlIsObjectValidInSavegame(sld)) return false;
1775
1776 switch (sld.cmd) {
1783 case SaveLoadType::String: {
1784 void *ptr = GetVariableAddress(object, sld);
1785
1786 switch (sld.cmd) {
1787 case SaveLoadType::Variable: SlSaveLoadConv(ptr, sld.conv); break;
1788 case SaveLoadType::Reference: SlSaveLoadRef(ptr, sld.conv); break;
1789 case SaveLoadType::Array: SlArray(ptr, sld.length, sld.conv); break;
1790 case SaveLoadType::ReferenceList: SlRefList(ptr, sld.conv); break;
1791 case SaveLoadType::ReferenceVector: SlRefVector(ptr, sld.conv); break;
1792 case SaveLoadType::Vector: SlVector(ptr, sld.conv); break;
1793 case SaveLoadType::String: SlStdString(ptr, sld.conv); break;
1794 default: NOT_REACHED();
1795 }
1796 break;
1797 }
1798
1799 /* SaveLoadType::SaveByte writes a value to the savegame to identify the type of an object.
1800 * When loading, the value is read explicitly with SlReadByte() to determine which
1801 * object description to use. */
1803 void *ptr = GetVariableAddress(object, sld);
1804
1805 switch (_sl.action) {
1806 case SaveLoadAction::Save: SlWriteByte(*static_cast<uint8_t *>(ptr)); break;
1810 case SaveLoadAction::Null: break;
1811 default: NOT_REACHED();
1812 }
1813 break;
1814 }
1815
1816 case SaveLoadType::Null: {
1817 assert(sld.conv.mem == VarMemType::Null);
1818
1819 switch (_sl.action) {
1822 case SaveLoadAction::Save: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1824 case SaveLoadAction::Null: break;
1825 default: NOT_REACHED();
1826 }
1827 break;
1828 }
1829
1832 switch (_sl.action) {
1833 case SaveLoadAction::Save: {
1834 if (sld.cmd == SaveLoadType::Struct) {
1835 /* Store in the savegame if this struct was written or not. */
1836 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1837 }
1838 sld.handler->Save(object);
1839 break;
1840 }
1841
1845 }
1846 sld.handler->LoadCheck(object);
1847 break;
1848 }
1849
1850 case SaveLoadAction::Load: {
1853 }
1854 sld.handler->Load(object);
1855 break;
1856 }
1857
1859 sld.handler->FixPointers(object);
1860 break;
1861
1862 case SaveLoadAction::Null: break;
1863 default: NOT_REACHED();
1864 }
1865 break;
1866
1867 default: NOT_REACHED();
1868 }
1869 return true;
1870}
1871
1876void SlSetStructListLength(size_t length)
1877{
1878 /* Automatically calculate the length? */
1879 if (_sl.need_length != NeedLength::None) {
1880 SlSetLength(SlGetArrayLength(length));
1881 if (_sl.need_length == NeedLength::CalcLength) return;
1882 }
1883
1884 SlWriteArrayLength(length);
1885}
1886
1892size_t SlGetStructListLength(size_t limit)
1893{
1894 size_t length = SlReadArrayLength();
1895 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1896
1897 return length;
1898}
1899
1905void SlObject(void *object, const SaveLoadTable &slt)
1906{
1907 /* Automatically calculate the length? */
1908 if (_sl.need_length != NeedLength::None) {
1909 SlSetLength(SlCalcObjLength(object, slt));
1910 if (_sl.need_length == NeedLength::CalcLength) return;
1911 }
1912
1913 for (auto &sld : slt) {
1914 SlObjectMember(object, sld);
1915 }
1916}
1917
1923 void Save(void *) const override
1924 {
1925 NOT_REACHED();
1926 }
1927
1928 void Load(void *object) const override
1929 {
1930 size_t length = SlGetStructListLength(UINT32_MAX);
1931 for (; length > 0; length--) {
1932 SlObject(object, this->GetLoadDescription());
1933 }
1934 }
1935
1936 void LoadCheck(void *object) const override
1937 {
1938 this->Load(object);
1939 }
1940
1942 {
1943 return {};
1944 }
1945
1947 {
1948 NOT_REACHED();
1949 }
1950};
1951
1958std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1959{
1960 /* You can only use SlTableHeader if you are a ChunkType::Table or ChunkType::SparseTable. */
1961 assert(_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
1962
1963 switch (_sl.action) {
1965 case SaveLoadAction::Load: {
1966 std::vector<SaveLoad> saveloads;
1967
1968 /* Build a key lookup mapping based on the available fields. */
1969 std::map<std::string, const SaveLoad *> key_lookup;
1970 for (auto &sld : slt) {
1971 if (!SlIsObjectValidInSavegame(sld)) continue;
1972
1973 /* Check that there is only one active SaveLoad for a given name. */
1974 assert(key_lookup.find(sld.name) == key_lookup.end());
1975 key_lookup[sld.name] = &sld;
1976 }
1977
1978 while (true) {
1979 SavegameFileType type{};
1981 if (type.IsEnd()) break;
1982
1983 std::string key;
1985
1986 auto sld_it = key_lookup.find(key);
1987 if (sld_it == key_lookup.end()) {
1988 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1989 Debug(sl, _sl.action == SaveLoadAction::Load ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type.storage);
1990
1991 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1992 SaveLoadType saveload_type;
1993 switch (type.Type()) {
1995 saveload_type = SaveLoadType::String;
1996 break;
1997
1999 saveload_type = SaveLoadType::StructList;
2000 handler = std::make_shared<SlSkipHandler>();
2001 break;
2002
2003 default:
2005 break;
2006 }
2007
2008 /* We don't know this field, so read to nothing. */
2009 saveloads.emplace_back(std::move(key), saveload_type, type.Type() | VarMemType::Null, 1, SaveLoadVersion::MinVersion, SaveLoadVersion::MaxVersion, nullptr, 0, std::move(handler));
2010 continue;
2011 }
2012
2013 /* Validate the type of the field. If it is changed, the
2014 * savegame should have been bumped so we know how to do the
2015 * conversion. If this error triggers, that clearly didn't
2016 * happen and this is a friendly poke to the developer to bump
2017 * the savegame version and add conversion code. */
2018 SavegameFileType correct_type = GetSavegameFileType(*sld_it->second);
2019 if (correct_type.storage != type.storage) {
2020 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type.storage, type.storage);
2021 SlErrorCorrupt("Field type is different than expected");
2022 }
2023 saveloads.emplace_back(*sld_it->second);
2024 }
2025
2026 for (auto &sld : saveloads) {
2028 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
2029 }
2030 }
2031
2032 return saveloads;
2033 }
2034
2035 case SaveLoadAction::Save: {
2036 /* Automatically calculate the length? */
2037 if (_sl.need_length != NeedLength::None) {
2039 if (_sl.need_length == NeedLength::CalcLength) break;
2040 }
2041
2042 for (auto &sld : slt) {
2043 if (!SlIsObjectValidInSavegame(sld)) continue;
2044 /* Make sure we are not storing empty keys. */
2045 assert(!sld.name.empty());
2046
2048 assert(!type.IsEnd());
2049
2051 SlStdString(const_cast<std::string *>(&sld.name), VarTypes::STR);
2052 }
2053
2054 /* Add an end-of-header marker. */
2055 SavegameFileType type{};
2057
2058 /* After the table, write down any sub-tables we might have. */
2059 for (auto &sld : slt) {
2060 if (!SlIsObjectValidInSavegame(sld)) continue;
2062 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
2063 NeedLength old_need_length = _sl.need_length;
2064 _sl.need_length = NeedLength::None;
2065
2066 SlTableHeader(sld.handler->GetDescription());
2067
2068 _sl.need_length = old_need_length;
2069 }
2070 }
2071
2072 break;
2073 }
2074
2075 default: NOT_REACHED();
2076 }
2077
2078 return std::vector<SaveLoad>();
2079}
2080
2094std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
2095{
2096 assert(_sl.action == SaveLoadAction::Load || _sl.action == SaveLoadAction::LoadCheck);
2097 /* ChunkType::Table / ChunkType::SparseTable always have a header. */
2098 if (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable) return SlTableHeader(slt);
2099
2100 std::vector<SaveLoad> saveloads;
2101
2102 /* Build a key lookup mapping based on the available fields. */
2103 std::map<std::string_view, std::vector<const SaveLoad *>> key_lookup;
2104 for (auto &sld : slt) {
2105 /* All entries should have a name; otherwise the entry should just be removed. */
2106 assert(!sld.name.empty());
2107
2108 key_lookup[sld.name].push_back(&sld);
2109 }
2110
2111 for (auto &slc : slct) {
2112 if (slc.name.empty()) {
2113 /* In old savegames there can be data we no longer care for. We
2114 * skip this by simply reading the amount of bytes indicated and
2115 * send those to /dev/null. */
2116 saveloads.emplace_back("", SaveLoadType::Null, VarFileType::U8 | VarMemType::Null, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr);
2117 } else {
2118 auto sld_it = key_lookup.find(slc.name);
2119 /* If this branch triggers, it means that an entry in the
2120 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2121 * you rename a field in one and not in the other? */
2122 if (sld_it == key_lookup.end()) {
2123 /* This isn't an assert, as that leaves no information what
2124 * field was to blame. This way at least we have breadcrumbs. */
2125 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2126 SlErrorCorrupt("Internal error with savegame compatibility");
2127 }
2128 for (auto &sld : sld_it->second) {
2129 saveloads.push_back(*sld);
2130 }
2131 }
2132 }
2133
2134 for (auto &sld : saveloads) {
2135 if (!SlIsObjectValidInSavegame(sld)) continue;
2137 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2138 }
2139 }
2140
2141 return saveloads;
2142}
2143
2149{
2150 SlObject(nullptr, slt);
2151}
2152
2158void SlAutolength(AutolengthProc *proc, int arg)
2159{
2160 assert(_sl.action == SaveLoadAction::Save);
2161
2162 /* Tell it to calculate the length */
2163 _sl.need_length = NeedLength::CalcLength;
2164 _sl.obj_len = 0;
2165 proc(arg);
2166
2167 /* Setup length */
2168 _sl.need_length = NeedLength::WantLength;
2169 SlSetLength(_sl.obj_len);
2170
2171 size_t start_pos = _sl.dumper->GetSize();
2172 size_t expected_offs = start_pos + _sl.obj_len;
2173
2174 /* And write the stuff */
2175 proc(arg);
2176
2177 if (expected_offs != _sl.dumper->GetSize()) {
2178 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2179 }
2180}
2181
2182void ChunkHandler::LoadCheck(size_t len) const
2183{
2184 switch (_sl.chunk_type) {
2185 case ChunkType::Table:
2187 SlTableHeader({});
2188 [[fallthrough]];
2189 case ChunkType::Array:
2191 SlSkipArray();
2192 break;
2193 case ChunkType::Riff:
2194 SlSkipBytes(len);
2195 break;
2196 default:
2197 NOT_REACHED();
2198 }
2199}
2200
2205static void SlLoadChunk(const ChunkHandler &ch)
2206{
2207 uint8_t m = SlReadByte();
2208
2209 _sl.chunk_type = static_cast<ChunkType>(m & to_underlying(ChunkType::FileTypeMask));
2210 _sl.obj_len = 0;
2211 _sl.expect_table_header = (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
2212
2213 /* The header should always be at the start. Read the length; the
2214 * Load() should as first action process the header. */
2215 if (_sl.expect_table_header) {
2216 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2217 }
2218
2219 switch (_sl.chunk_type) {
2220 case ChunkType::Table:
2221 case ChunkType::Array:
2222 _sl.array_index = 0;
2223 ch.Load();
2224 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2225 break;
2228 ch.Load();
2229 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2230 break;
2231 case ChunkType::Riff: {
2232 /* Read length */
2233 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2234 len += SlReadUint16();
2235 _sl.obj_len = len;
2236 size_t start_pos = _sl.reader->GetSize();
2237 size_t endoffs = start_pos + len;
2238 ch.Load();
2239
2240 if (_sl.reader->GetSize() != endoffs) {
2241 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2242 }
2243 break;
2244 }
2245 default:
2246 SlErrorCorrupt("Invalid chunk type");
2247 break;
2248 }
2249
2250 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2251}
2252
2258static void SlLoadCheckChunk(const ChunkHandler &ch)
2259{
2260 uint8_t m = SlReadByte();
2261
2262 _sl.chunk_type = static_cast<ChunkType>(m & to_underlying(ChunkType::FileTypeMask));
2263 _sl.obj_len = 0;
2264 _sl.expect_table_header = (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
2265
2266 /* The header should always be at the start. Read the length; the
2267 * LoadCheck() should as first action process the header. */
2268 if (_sl.expect_table_header) {
2269 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2270 }
2271
2272 switch (_sl.chunk_type) {
2273 case ChunkType::Table:
2274 case ChunkType::Array:
2275 _sl.array_index = 0;
2276 ch.LoadCheck();
2277 break;
2280 ch.LoadCheck();
2281 break;
2282 case ChunkType::Riff: {
2283 /* Read length */
2284 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2285 len += SlReadUint16();
2286 _sl.obj_len = len;
2287 size_t start_pos = _sl.reader->GetSize();
2288 size_t endoffs = start_pos + len;
2289 ch.LoadCheck(len);
2290
2291 if (_sl.reader->GetSize() != endoffs) {
2292 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2293 }
2294 break;
2295 }
2296 default:
2297 SlErrorCorrupt("Invalid chunk type");
2298 break;
2299 }
2300
2301 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2302}
2303
2309static void SlSaveChunk(const ChunkHandler &ch)
2310{
2311 if (ch.type == ChunkType::ReadOnly) return;
2312
2313 for (uint8_t b : ch.id) SlWriteByte(b);
2314 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2315
2316 _sl.chunk_type = ch.type;
2317 _sl.expect_table_header = (_sl.chunk_type == ChunkType::Table || _sl.chunk_type == ChunkType::SparseTable);
2318
2319 _sl.need_length = (_sl.expect_table_header || _sl.chunk_type == ChunkType::Riff) ? NeedLength::WantLength : NeedLength::None;
2320
2321 switch (_sl.chunk_type) {
2322 case ChunkType::Riff:
2323 ch.Save();
2324 break;
2325 case ChunkType::Table:
2326 case ChunkType::Array:
2327 _sl.last_array_index = 0;
2328 SlWriteByte(to_underlying(_sl.chunk_type));
2329 ch.Save();
2330 SlWriteArrayLength(0); // Terminate arrays
2331 break;
2334 SlWriteByte(to_underlying(_sl.chunk_type));
2335 ch.Save();
2336 SlWriteArrayLength(0); // Terminate arrays
2337 break;
2338 default: NOT_REACHED();
2339 }
2340
2341 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2342}
2343
2345static void SlSaveChunks()
2346{
2347 for (auto &ch : ChunkHandlers()) {
2348 SlSaveChunk(ch);
2349 }
2350
2351 /* Terminator */
2352 SlWriteUint32(0);
2353}
2354
2362{
2363 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2364 return nullptr;
2365}
2366
2368static void SlLoadChunks()
2369{
2370 for (ChunkId id = SlReadChunkId(); !id.Empty(); id = SlReadChunkId()) {
2371 Debug(sl, 2, "Loading chunk {}", id.AsString());
2372
2373 const ChunkHandler *ch = SlFindChunkHandler(id);
2374 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2375 SlLoadChunk(*ch);
2376 }
2377}
2378
2381{
2382 for (ChunkId id = SlReadChunkId(); id.Empty(); id = SlReadChunkId()) {
2383 Debug(sl, 2, "Loading chunk {}", id.AsString());
2384
2385 const ChunkHandler *ch = SlFindChunkHandler(id);
2386 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2387 SlLoadCheckChunk(*ch);
2388 }
2389}
2390
2392static void SlFixPointers()
2393{
2394 _sl.action = SaveLoadAction::Ptrs;
2395
2396 for (const ChunkHandler &ch : ChunkHandlers()) {
2397 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2398 ch.FixPointers();
2399 }
2400
2401 assert(_sl.action == SaveLoadAction::Ptrs);
2402}
2403
2404
2407 std::optional<FileHandle> file;
2408 long begin;
2409
2414 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2415 {
2416 }
2417
2419 ~FileReader() override
2420 {
2421 if (this->file.has_value()) {
2422 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2423 }
2424 }
2425
2426 size_t Read(uint8_t *buf, size_t size) override
2427 {
2428 /* We're in the process of shutting down, i.e. in "failure" mode. */
2429 if (!this->file.has_value()) return 0;
2430
2431 return fread(buf, 1, size, *this->file);
2432 }
2433
2434 void Reset() override
2435 {
2436 clearerr(*this->file);
2437 if (fseek(*this->file, this->begin, SEEK_SET)) {
2438 Debug(sl, 1, "Could not reset the file reading");
2439 }
2440 }
2441};
2442
2445 std::optional<FileHandle> file;
2446
2451 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2452 {
2453 }
2454
2456 ~FileWriter() override
2457 {
2458 this->Finish();
2459 }
2460
2461 void Write(const uint8_t *buf, size_t size) override
2462 {
2463 /* We're in the process of shutting down, i.e. in "failure" mode. */
2464 if (!this->file.has_value()) return;
2465
2466 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2467 }
2468
2469 void Finish() override
2470 {
2471 if (this->file.has_value()) {
2472 _game_session_stats.savegame_size = ftell(*this->file);
2473 this->file.reset();
2474 }
2475 }
2476};
2477
2478/*******************************************
2479 ********** START OF LZO CODE **************
2480 *******************************************/
2481
2482#ifdef WITH_LZO
2483
2485static const uint LZO_BUFFER_SIZE = 8192;
2486
2493 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2494 {
2495 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2496 }
2497
2498 size_t Read(uint8_t *buf, size_t ssize) override
2499 {
2500 assert(ssize >= LZO_BUFFER_SIZE);
2501
2502 /* Buffer size is from the LZO docs plus the chunk header size. */
2503 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2504 uint32_t tmp[2];
2505 uint32_t size;
2506 lzo_uint len = ssize;
2507
2508 /* Read header*/
2509 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2510
2511 /* Check if size is bad */
2512 ((uint32_t*)out)[0] = size = tmp[1];
2513
2515 tmp[0] = TO_BE32(tmp[0]);
2516 size = TO_BE32(size);
2517 }
2518
2519 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2520
2521 /* Read block */
2522 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2523
2524 /* Verify checksum */
2525 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2526
2527 /* Decompress */
2528 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2529 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2530 return len;
2531 }
2532};
2533
2540 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2541 {
2542 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2543 }
2544
2545 void Write(const uint8_t *buf, size_t size) override
2546 {
2547 const lzo_bytep in = buf;
2548 /* Buffer size is from the LZO docs plus the chunk header size. */
2549 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2550 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2551 lzo_uint outlen;
2552
2553 do {
2554 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2555 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : static_cast<lzo_uint>(size);
2556 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2557 ((uint32_t*)out)[1] = TO_BE32(static_cast<uint32_t>(outlen));
2558 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2559 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2560
2561 /* Move to next data chunk. */
2562 size -= len;
2563 in += len;
2564 } while (size > 0);
2565 }
2566};
2567
2568#endif /* WITH_LZO */
2569
2570/*********************************************
2571 ******** START OF NOCOMP CODE (uncompressed)*
2572 *********************************************/
2573
2580 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2581 {
2582 }
2583
2584 size_t Read(uint8_t *buf, size_t size) override
2585 {
2586 return this->chain->Read(buf, size);
2587 }
2588};
2589
2596 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2597 {
2598 }
2599
2600 void Write(const uint8_t *buf, size_t size) override
2601 {
2602 this->chain->Write(buf, size);
2603 }
2604};
2605
2606/********************************************
2607 ********** START OF ZLIB CODE **************
2608 ********************************************/
2609
2610#if defined(WITH_ZLIB)
2611
2614 z_stream z{};
2616
2621 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2622 {
2623 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2624 }
2625
2628 {
2629 inflateEnd(&this->z);
2630 }
2631
2632 size_t Read(uint8_t *buf, size_t size) override
2633 {
2634 this->z.next_out = buf;
2635 this->z.avail_out = static_cast<uint>(size);
2636
2637 do {
2638 /* read more bytes from the file? */
2639 if (this->z.avail_in == 0) {
2640 this->z.next_in = this->fread_buf;
2641 this->z.avail_in = static_cast<uint>(this->chain->Read(this->fread_buf, sizeof(this->fread_buf)));
2642 }
2643
2644 /* inflate the data */
2645 int r = inflate(&this->z, 0);
2646 if (r == Z_STREAM_END) break;
2647
2648 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2649 } while (this->z.avail_out != 0);
2650
2651 return size - this->z.avail_out;
2652 }
2653};
2654
2657 z_stream z{};
2659
2665 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain))
2666 {
2667 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2668 }
2669
2672 {
2673 deflateEnd(&this->z);
2674 }
2675
2682 void WriteLoop(const uint8_t *p, size_t len, int mode)
2683 {
2684 uint n;
2685 this->z.next_in = const_cast<uint8_t *>(p); // zlib does not modify the data, but is non-const for legacy reasons
2686 this->z.avail_in = static_cast<uInt>(len);
2687 do {
2688 this->z.next_out = this->fwrite_buf;
2689 this->z.avail_out = sizeof(this->fwrite_buf);
2690
2698 int r = deflate(&this->z, mode);
2699
2700 /* bytes were emitted? */
2701 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2702 this->chain->Write(this->fwrite_buf, n);
2703 }
2704 if (r == Z_STREAM_END) break;
2705
2706 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2707 } while (this->z.avail_in || !this->z.avail_out);
2708 }
2709
2710 void Write(const uint8_t *buf, size_t size) override
2711 {
2712 this->WriteLoop(buf, size, 0);
2713 }
2714
2715 void Finish() override
2716 {
2717 this->WriteLoop(nullptr, 0, Z_FINISH);
2718 this->chain->Finish();
2719 }
2720};
2721
2722#endif /* WITH_ZLIB */
2723
2724/********************************************
2725 ********** START OF LZMA CODE **************
2726 ********************************************/
2727
2728#if defined(WITH_LIBLZMA)
2729
2736static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2737
2740 lzma_stream lzma;
2742
2747 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain)), lzma(_lzma_init)
2748 {
2749 /* Allow saves up to 256 MB uncompressed */
2750 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2751 }
2752
2755 {
2756 lzma_end(&this->lzma);
2757 }
2758
2759 size_t Read(uint8_t *buf, size_t size) override
2760 {
2761 this->lzma.next_out = buf;
2762 this->lzma.avail_out = size;
2763
2764 do {
2765 /* read more bytes from the file? */
2766 if (this->lzma.avail_in == 0) {
2767 this->lzma.next_in = this->fread_buf;
2768 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2769 }
2770
2771 /* inflate the data */
2772 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2773 if (r == LZMA_STREAM_END) break;
2774 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2775 } while (this->lzma.avail_out != 0);
2776
2777 return size - this->lzma.avail_out;
2778 }
2779};
2780
2783 lzma_stream lzma;
2785
2791 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init)
2792 {
2793 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2794 }
2795
2798 {
2799 lzma_end(&this->lzma);
2800 }
2801
2808 void WriteLoop(const uint8_t *p, size_t len, lzma_action action)
2809 {
2810 size_t n;
2811 this->lzma.next_in = p;
2812 this->lzma.avail_in = len;
2813 do {
2814 this->lzma.next_out = this->fwrite_buf;
2815 this->lzma.avail_out = sizeof(this->fwrite_buf);
2816
2817 lzma_ret r = lzma_code(&this->lzma, action);
2818
2819 /* bytes were emitted? */
2820 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2821 this->chain->Write(this->fwrite_buf, n);
2822 }
2823 if (r == LZMA_STREAM_END) break;
2824 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2825 } while (this->lzma.avail_in || !this->lzma.avail_out);
2826 }
2827
2828 void Write(const uint8_t *buf, size_t size) override
2829 {
2830 this->WriteLoop(buf, size, LZMA_RUN);
2831 }
2832
2833 void Finish() override
2834 {
2835 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2836 this->chain->Finish();
2837 }
2838};
2839
2840#endif /* WITH_LIBLZMA */
2841
2842/*******************************************
2843 ************* END OF CODE *****************
2844 *******************************************/
2845
2848
2851 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2852 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2853
2854 std::string_view name;
2856
2860};
2861
2866
2869#if defined(WITH_LZO)
2870 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2872#else
2873 {nullptr, nullptr, "lzo", SAVEGAME_TAG_LZO, 0, 0, 0},
2874#endif
2875 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2877#if defined(WITH_ZLIB)
2878 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2879 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2880 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2882#else
2883 {nullptr, nullptr, "zlib", SAVEGAME_TAG_ZLIB, 0, 0, 0},
2884#endif
2885#if defined(WITH_LIBLZMA)
2886 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2887 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2888 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2889 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2890 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2892#else
2893 {nullptr, nullptr, "lzma", SAVEGAME_TAG_LZMA, 0, 0, 0},
2894#endif
2895};
2896
2903static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(std::string_view full_name)
2904{
2905 /* Find default savegame format, the highest one with which files can be written. */
2906 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2907 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2908
2909 const SaveLoadFormat &def = *it;
2910
2911 if (!full_name.empty()) {
2912 /* Get the ":..." of the compression level out of the way */
2913 size_t separator = full_name.find(':');
2914 bool has_comp_level = separator != std::string::npos;
2915 std::string_view name = has_comp_level ? full_name.substr(0, separator) : full_name;
2916
2917 for (const auto &slf : _saveload_formats) {
2918 if (slf.init_write != nullptr && name == slf.name) {
2919 if (has_comp_level) {
2920 auto complevel = full_name.substr(separator + 1);
2921
2922 /* Get the level and determine whether all went fine. */
2923 auto level = ParseInteger<uint8_t>(complevel);
2924 if (!level.has_value() || *level != Clamp(*level, slf.min_compression, slf.max_compression)) {
2926 GetEncodedString(STR_CONFIG_ERROR),
2927 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2929 } else {
2930 return {slf, *level};
2931 }
2932 }
2933 return {slf, slf.default_compression};
2934 }
2935 }
2936
2938 GetEncodedString(STR_CONFIG_ERROR),
2939 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2941 }
2942 return {def, def.default_compression};
2943}
2944
2945/* actual loader/saver function */
2946void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2947extern bool AfterLoadGame();
2948extern bool LoadOldSaveGame(std::string_view file);
2949
2955static void ResetSettings()
2956{
2957 for (auto &desc : GetSaveLoadSettingTable()) {
2958 const SettingDesc *sd = GetSettingDesc(desc);
2959 if (sd->flags.Test(SettingFlag::NotInSave)) continue;
2961
2963 }
2964}
2965
2966extern void ClearOldOrders();
2967
2972{
2974 ResetTempEngineData();
2975 ClearRailTypeLabelList();
2976 ClearRoadTypeLabelList();
2977 ResetOldWaypoints();
2978 ResetSettings();
2979}
2980
2984static inline void ClearSaveLoadState()
2985{
2986 _sl.dumper = nullptr;
2987 _sl.sf = nullptr;
2988 _sl.reader = nullptr;
2989 _sl.lf = nullptr;
2990}
2991
2993static void SaveFileStart()
2994{
2995 SetMouseCursorBusy(true);
2996
2997 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_SAVELOAD_START);
2998 _sl.saveinprogress = true;
2999}
3000
3002static void SaveFileDone()
3003{
3004 SetMouseCursorBusy(false);
3005
3006 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_SAVELOAD_FINISH);
3007 _sl.saveinprogress = false;
3008
3009#ifdef __EMSCRIPTEN__
3010 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
3011#endif
3012}
3013
3019{
3020 _sl.error_str = str;
3021}
3022
3028{
3029 return GetEncodedString(_sl.action == SaveLoadAction::Save ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
3030}
3031
3037{
3038 return GetEncodedString(_sl.error_str, _sl.extra_msg);
3039}
3040
3047
3054static SaveLoadResult SaveFileToDisk(bool threaded)
3055{
3056 try {
3057 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
3058
3059 /* We have written our stuff to memory, now write it to file! */
3060 _sl.sf->Write(fmt.tag.data(), fmt.tag.size());
3061
3062 uint32_t version = TO_BE32(to_underlying(SAVEGAME_VERSION) << 16);
3063 _sl.sf->Write(reinterpret_cast<uint8_t *>(&version), sizeof(version));
3064
3065 _sl.sf = fmt.init_write(_sl.sf, compression);
3066 _sl.dumper->Flush(_sl.sf);
3067
3069
3070 if (threaded) SetAsyncSaveFinish(SaveFileDone);
3071
3072 return SaveLoadResult::Ok;
3073 } catch (...) {
3075
3077
3078 /* We don't want to shout when saving is just
3079 * cancelled due to a client disconnecting. */
3080 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
3081 Debug(sl, 0, "{} {}", GetSaveLoadErrorType().GetDecodedString(), GetSaveLoadErrorMessage().GetDecodedString());
3082 asfp = SaveFileError;
3083 }
3084
3085 if (threaded) {
3086 SetAsyncSaveFinish(asfp);
3087 } else {
3088 asfp();
3089 }
3090 return SaveLoadResult::Error;
3091 }
3092}
3093
3094void WaitTillSaved()
3095{
3096 if (!_save_thread.joinable()) return;
3097
3098 _save_thread.join();
3099
3100 /* Make sure every other state is handled properly as well. */
3102}
3103
3112static SaveLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
3113{
3114 assert(!_sl.saveinprogress);
3115
3116 _sl.dumper = std::make_unique<MemoryDumper>();
3117 _sl.sf = std::move(writer);
3118
3120
3121 SaveViewportBeforeSaveGame();
3122 SlSaveChunks();
3123
3124 SaveFileStart();
3125
3126 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3127 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3128
3129 SaveLoadResult result = SaveFileToDisk(false);
3130 SaveFileDone();
3131
3132 return result;
3133 }
3134
3135 return SaveLoadResult::Ok;
3136}
3137
3144SaveLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3145{
3146 try {
3147 _sl.action = SaveLoadAction::Save;
3148 return DoSave(std::move(writer), threaded);
3149 } catch (...) {
3151 return SaveLoadResult::Error;
3152 }
3153}
3154
3163static const SaveLoadFormat *DetermineSaveLoadFormat(SaveLoadFormatTag tag, uint32_t raw_version)
3164{
3165 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3166 if (fmt != std::end(_saveload_formats)) {
3167 /* Check version number */
3168 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3169 /* Minor is not used anymore from version 18.0, but it is still needed
3170 * in versions before that (4 cases) which can't be removed easy.
3171 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3172 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3173
3174 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3175
3176 /* Is the version higher than the current? */
3177 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3179 return fmt;
3180 }
3181
3182 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3183 _sl.lf->Reset();
3186
3187 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3188 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3189 if (fmt == std::end(_saveload_formats)) {
3190 /* Who removed the LZO savegame format definition? When built without LZO support,
3191 * the formats must still list it just without a method to read the file.
3192 * The caller of this function has to check for the existence of load function. */
3193 NOT_REACHED();
3194 }
3195 return fmt;
3196}
3197
3204static SaveLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3205{
3206 _sl.lf = std::move(reader);
3207
3208 if (load_check) {
3209 /* Clear previous check data */
3210 _load_check_data.Clear();
3211 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3212 _load_check_data.checkable = true;
3213 }
3214
3215 SaveLoadFormatTag tag{};
3216 if (_sl.lf->Read(tag.data(), tag.size()) != tag.size()) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3217
3218 uint32_t version;
3219 if (_sl.lf->Read(reinterpret_cast<uint8_t*>(&version), sizeof(version)) != sizeof(version)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3220
3221 /* see if we have any loader for this type. */
3222 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(tag, version);
3223
3224 /* loader for this savegame type is not implemented? */
3225 if (fmt->init_load == nullptr) {
3226 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3227 }
3228
3229 _sl.lf = fmt->init_load(_sl.lf);
3230 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3231 _next_offs = 0;
3232
3233 if (!load_check) {
3235
3236 /* Old maps were hardcoded to 256x256 and thus did not contain
3237 * any mapsize information. Pre-initialize to 256x256 to not to
3238 * confuse old games */
3239 InitializeGame(256, 256, true, true);
3240
3241 _gamelog.Reset();
3242
3244 /*
3245 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3246 * shared savegame version 4. Anything before that 'obviously'
3247 * does not have any NewGRFs. Between the introduction and
3248 * savegame version 41 (just before 0.5) the NewGRF settings
3249 * were not stored in the savegame and they were loaded by
3250 * using the settings from the main menu.
3251 * So, to recap:
3252 * - savegame version < 4: do not load any NewGRFs.
3253 * - savegame version >= 41: load NewGRFs from savegame, which is
3254 * already done at this stage by
3255 * overwriting the main menu settings.
3256 * - other savegame versions: use main menu settings.
3257 *
3258 * This means that users *can* crash savegame version 4..40
3259 * savegames if they set incompatible NewGRFs in the main menu,
3260 * but can't crash anymore for savegame version < 4 savegames.
3261 *
3262 * Note: this is done here because AfterLoadGame is also called
3263 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3264 */
3266 }
3267 }
3268
3269 if (load_check) {
3270 /* Load chunks into _load_check_data.
3271 * No pools are loaded. References are not possible, and thus do not need resolving. */
3273 } else {
3274 /* Load chunks and resolve references */
3275 SlLoadChunks();
3276 SlFixPointers();
3277 }
3278
3280
3282
3283 if (load_check) {
3284 /* The only part from AfterLoadGame() we need */
3285 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
3286 } else {
3287 _gamelog.StartAction(GamelogActionType::Load);
3288
3289 /* After loading fix up savegame for any internal changes that
3290 * might have occurred since then. If it fails, load back the old game. */
3291 if (!AfterLoadGame()) {
3292 _gamelog.StopAction();
3294 }
3295
3296 _gamelog.StopAction();
3297 }
3298
3299 return SaveLoadResult::Ok;
3300}
3301
3307SaveLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3308{
3309 try {
3310 _sl.action = SaveLoadAction::Load;
3311 return DoLoad(std::move(reader), false);
3312 } catch (...) {
3315 }
3316}
3317
3328SaveLoadResult SaveOrLoad(std::string_view filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3329{
3330 /* An instance of saving is already active, so don't go saving again */
3331 if (_sl.saveinprogress && fop == SaveLoadOperation::Save && dft == DetailedFileType::GameFile && threaded) {
3332 /* if not an autosave, but a user action, show error message */
3333 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WarningLevel::Error);
3334 return SaveLoadResult::Ok;
3335 }
3336 WaitTillSaved();
3337
3338 try {
3339 /* Load a TTDLX or TTDPatch game */
3342
3343 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3344
3345 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3346 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3347 * Note: this is done here because AfterLoadGame is also called
3348 * for OTTD savegames which have their own NewGRF logic. */
3350 _gamelog.Reset();
3351 if (!LoadOldSaveGame(filename)) return SaveLoadResult::ReInit;
3354 _gamelog.StartAction(GamelogActionType::Load);
3355 if (!AfterLoadGame()) {
3356 _gamelog.StopAction();
3358 }
3359 _gamelog.StopAction();
3360 return SaveLoadResult::Ok;
3361 }
3362
3363 assert(dft == DetailedFileType::GameFile);
3364 switch (fop) {
3367 break;
3368
3370 _sl.action = SaveLoadAction::Load;
3371 break;
3372
3374 _sl.action = SaveLoadAction::Save;
3375 break;
3376
3377 default: NOT_REACHED();
3378 }
3379
3380 auto fh = (fop == SaveLoadOperation::Save) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3381
3382 /* Make it a little easier to load savegames from the console */
3383 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Save);
3384 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Base);
3385 if (!fh.has_value() && fop != SaveLoadOperation::Save) fh = FioFOpenFile(filename, "rb", Subdirectory::Scenario);
3386
3387 if (!fh.has_value()) {
3388 SlError(fop == SaveLoadOperation::Save ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3389 }
3390
3391 if (fop == SaveLoadOperation::Save) { // SAVE game
3392 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3393 if (!_settings_client.gui.threaded_saves) threaded = false;
3394
3395 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3396 }
3397
3398 /* LOAD game */
3399 assert(fop == SaveLoadOperation::Load || fop == SaveLoadOperation::Check);
3400 Debug(desync, 1, "load: {}", filename);
3401 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SaveLoadOperation::Check);
3402 } catch (...) {
3403 /* This code may be executed both for old and new save games. */
3405
3406 if (fop != SaveLoadOperation::Check) Debug(sl, 0, "{} {}", GetSaveLoadErrorType().GetDecodedString(), GetSaveLoadErrorMessage().GetDecodedString());
3407
3408 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3410 }
3411}
3412
3418{
3419 std::string filename;
3420
3421 if (_settings_client.gui.keep_all_autosave) {
3422 filename = GenerateDefaultSaveName() + counter.Extension();
3423 } else {
3424 filename = counter.Filename();
3425 }
3426
3427 Debug(sl, 2, "Autosaving to '{}'", filename);
3429 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WarningLevel::Error);
3430 }
3431}
3432
3433
3439
3445{
3446 /* Check if we have a name for this map, which is the name of the first
3447 * available company. When there's no company available we'll use
3448 * 'Spectator' as "company" name. */
3449 CompanyID cid = _local_company;
3450 if (!Company::IsValidID(cid)) {
3451 for (const Company *c : Company::Iterate()) {
3452 cid = c->index;
3453 break;
3454 }
3455 }
3456
3457 std::array<StringParameter, 4> params{};
3458 auto it = params.begin();
3459 *it++ = cid;
3460
3461 /* We show the current game time differently depending on the timekeeping units used by this game. */
3463 /* Insert time played. */
3464 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3465 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3466 *it++ = play_time / 60 / 60;
3467 *it++ = (play_time / 60) % 60;
3468 } else {
3469 /* Insert current date */
3470 switch (_settings_client.gui.date_format_in_default_names) {
3471 case 0: *it++ = STR_JUST_DATE_LONG; break;
3472 case 1: *it++ = STR_JUST_DATE_TINY; break;
3473 case 2: *it++ = STR_JUST_DATE_ISO; break;
3474 default: NOT_REACHED();
3475 }
3476 *it++ = TimerGameEconomy::date;
3477 }
3478
3479 /* Get the correct string (special string for when there's not company) */
3480 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3481 SanitizeFilename(filename);
3482 return filename;
3483}
3484
3491{
3494 this->ftype = FIOS_TYPE_INVALID;
3495 return;
3496 }
3497
3498 this->file_op = fop;
3499 this->ftype = ft;
3500}
3501
3507{
3508 this->SetMode(item.type);
3509 this->name = item.name;
3510 this->title = item.title;
3511}
3512
3514{
3515 assert(this->load_description.has_value());
3516 return *this->load_description;
3517}
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:651
@ U64
A 64 bit unsigned int.
Definition saveload.h:660
@ Name
old custom name to be converted to a string pointer
Definition saveload.h:664
@ LabelForward
A 4 character Label, stored as-is.
Definition saveload.h:666
@ I8
A 8 bit signed int.
Definition saveload.h:653
@ U8
A 8 bit unsigned int.
Definition saveload.h:654
@ LabelReverse
A 4 character Label, stored in reverse.
Definition saveload.h:665
@ StrQ
string pointer enclosed in quotes
Definition saveload.h:663
@ Null
useful to write zeros in savegame.
Definition saveload.h:661
@ I16
A 16 bit signed int.
Definition saveload.h:655
@ Bool
A boolean value.
Definition saveload.h:652
@ U32
A 32 bit unsigned int.
Definition saveload.h:658
@ I32
A 32 bit signed int.
Definition saveload.h:657
@ I64
A 64 bit signed int.
Definition saveload.h:659
@ Str
string pointer
Definition saveload.h:662
@ U16
A 16 bit unsigned int.
Definition saveload.h:656
VarFileType
The types/structures of data that can be stored in the file.
Definition saveload.h:632
@ String
A string.
Definition saveload.h:645
@ U64
A 64 bit unsigned int.
Definition saveload.h:643
@ I8
A 8 bit signed int.
Definition saveload.h:636
@ U8
A 8 bit unsigned int.
Definition saveload.h:637
@ Struct
An arbitrary structure.
Definition saveload.h:646
@ I16
A 16 bit signed int.
Definition saveload.h:638
@ U32
A 32 bit unsigned int.
Definition saveload.h:641
@ StringID
StringID offset into strings-array.
Definition saveload.h:644
@ I32
A 32 bit signed int.
Definition saveload.h:640
@ I64
A 64 bit signed int.
Definition saveload.h:642
@ U16
A 16 bit unsigned int.
Definition saveload.h:639
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:1343
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:1299
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:511
SaveLoadType
Type of data saved.
Definition saveload.h:743
@ ReferenceList
Save/load a list of SaveLoadType::Reference elements.
Definition saveload.h:752
@ String
Save/load a std::string.
Definition saveload.h:748
@ Array
Save/load a fixed-size array of SaveLoadType::Variable elements.
Definition saveload.h:750
@ Variable
Save/load a variable.
Definition saveload.h:744
@ Vector
Save/load a vector of SaveLoadType::Variable elements.
Definition saveload.h:751
@ Reference
Save/load a reference.
Definition saveload.h:745
@ StructList
Save/load a list of structs.
Definition saveload.h:753
@ Struct
Save/load a struct.
Definition saveload.h:746
@ Null
Save null-bytes and load to nowhere.
Definition saveload.h:756
@ SaveByte
Save (but not load) a byte.
Definition saveload.h:755
@ ReferenceVector
Save/load a vector of SaveLoadType::Reference elements.
Definition saveload.h:758
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:1258
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.
uint32_t 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:764
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:768
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:773
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:770
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:766
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:765
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:767
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:769
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:670
SLRefType ref
The reference type.
Definition saveload.h:674
VarMemType mem
The way of storing data in memory.
Definition saveload.h:672
StringValidationSettings string_validation_settings
Any settings related to validation of the strings.
Definition saveload.h:673
VarFileType file
The way of storing data in the file.
Definition saveload.h:671
static constexpr VarType U16
Store a 16 bits unsigned int.
Definition saveload.h:729
static constexpr VarType U8
Store a 8 bits unsigned int.
Definition saveload.h:727
static constexpr VarType STR
Store string.
Definition saveload.h:735
static constexpr VarType LABEL_REVERSE
Store a Label in reverse.
Definition saveload.h:738
static constexpr VarType I16
Store a 16 bits signed int.
Definition saveload.h:728
static constexpr VarType I8
Store a 8 bits signed int.
Definition saveload.h:726
static constexpr VarType U32
Store a 32 bits unsigned int.
Definition saveload.h:731
static constexpr VarType STRINGID
Store a StringID.
Definition saveload.h:734
static constexpr VarType LABEL_FORWARD
Store a Label as-is.
Definition saveload.h:739
static constexpr VarType I32
Store a 32 bits signed int.
Definition saveload.h:730
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:3315
Window functions not directly related to making/drawing windows.