OpenTTD Source 15.0-beta2
saveload.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
23#include "../stdafx.h"
24#include "../debug.h"
25#include "../station_base.h"
26#include "../thread.h"
27#include "../town.h"
28#include "../network/network.h"
29#include "../window_func.h"
30#include "../strings_func.h"
31#include "../core/endian_func.hpp"
32#include "../core/string_builder.hpp"
33#include "../vehicle_base.h"
34#include "../company_func.h"
35#include "../timer/timer_game_economy.h"
36#include "../autoreplace_base.h"
37#include "../roadstop_base.h"
38#include "../linkgraph/linkgraph.h"
39#include "../linkgraph/linkgraphjob.h"
40#include "../statusbar_gui.h"
41#include "../fileio_func.h"
42#include "../gamelog.h"
43#include "../string_func.h"
44#include "../fios.h"
45#include "../error.h"
46#include "../strings_type.h"
47#include "../newgrf_railtype.h"
48#include "../newgrf_roadtype.h"
49#include "../settings_internal.h"
50#include "saveload_internal.h"
51#include "saveload_filter.h"
52
53#include <atomic>
54#ifdef __EMSCRIPTEN__
55# include <emscripten.h>
56#endif
57
58#include "table/strings.h"
59
60#include "../safeguards.h"
61
63
66
67uint32_t _ttdp_version;
70std::string _savegame_format;
72
81
82enum NeedLength : uint8_t {
83 NL_NONE = 0,
86};
87
89static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
90
92struct ReadBuffer {
94 uint8_t *bufp = nullptr;
95 uint8_t *bufe = nullptr;
96 std::shared_ptr<LoadFilter> reader{};
97 size_t read = 0;
98
103 ReadBuffer(std::shared_ptr<LoadFilter> reader) : reader(std::move(reader))
104 {
105 }
106
107 inline uint8_t ReadByte()
108 {
109 if (this->bufp == this->bufe) {
110 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
111 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
112
113 this->read += len;
114 this->bufp = this->buf;
115 this->bufe = this->buf + len;
116 }
117
118 return *this->bufp++;
119 }
120
125 size_t GetSize() const
126 {
127 return this->read - (this->bufe - this->bufp);
128 }
129};
130
131
134 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
135 uint8_t *buf = nullptr;
136 uint8_t *bufe = nullptr;
137
142 inline void WriteByte(uint8_t b)
143 {
144 /* Are we at the end of this chunk? */
145 if (this->buf == this->bufe) {
146 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
147 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
148 }
149
150 *this->buf++ = b;
151 }
152
157 void Flush(std::shared_ptr<SaveFilter> writer)
158 {
159 uint i = 0;
160 size_t t = this->GetSize();
161
162 while (t > 0) {
163 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
164
165 writer->Write(this->blocks[i++].get(), to_write);
166 t -= to_write;
167 }
168
169 writer->Finish();
170 }
171
176 size_t GetSize() const
177 {
178 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
179 }
180};
181
186 uint8_t block_mode;
187 bool error;
188
189 size_t obj_len;
190 int array_index, last_array_index;
192
193 std::unique_ptr<MemoryDumper> dumper;
194 std::shared_ptr<SaveFilter> sf;
195
196 std::unique_ptr<ReadBuffer> reader;
197 std::shared_ptr<LoadFilter> lf;
198
200 std::string extra_msg;
201
203};
204
206
207static const std::vector<ChunkHandlerRef> &ChunkHandlers()
208{
209 /* These define the chunks */
210 extern const ChunkHandlerTable _gamelog_chunk_handlers;
211 extern const ChunkHandlerTable _map_chunk_handlers;
212 extern const ChunkHandlerTable _misc_chunk_handlers;
213 extern const ChunkHandlerTable _name_chunk_handlers;
214 extern const ChunkHandlerTable _cheat_chunk_handlers;
215 extern const ChunkHandlerTable _setting_chunk_handlers;
216 extern const ChunkHandlerTable _company_chunk_handlers;
217 extern const ChunkHandlerTable _engine_chunk_handlers;
218 extern const ChunkHandlerTable _veh_chunk_handlers;
219 extern const ChunkHandlerTable _waypoint_chunk_handlers;
220 extern const ChunkHandlerTable _depot_chunk_handlers;
221 extern const ChunkHandlerTable _order_chunk_handlers;
222 extern const ChunkHandlerTable _town_chunk_handlers;
223 extern const ChunkHandlerTable _sign_chunk_handlers;
224 extern const ChunkHandlerTable _station_chunk_handlers;
225 extern const ChunkHandlerTable _industry_chunk_handlers;
226 extern const ChunkHandlerTable _economy_chunk_handlers;
227 extern const ChunkHandlerTable _subsidy_chunk_handlers;
228 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
229 extern const ChunkHandlerTable _goal_chunk_handlers;
230 extern const ChunkHandlerTable _story_page_chunk_handlers;
231 extern const ChunkHandlerTable _league_chunk_handlers;
232 extern const ChunkHandlerTable _ai_chunk_handlers;
233 extern const ChunkHandlerTable _game_chunk_handlers;
234 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
235 extern const ChunkHandlerTable _newgrf_chunk_handlers;
236 extern const ChunkHandlerTable _group_chunk_handlers;
237 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
238 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
239 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
240 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
241 extern const ChunkHandlerTable _airport_chunk_handlers;
242 extern const ChunkHandlerTable _object_chunk_handlers;
243 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
244 extern const ChunkHandlerTable _water_region_chunk_handlers;
245 extern const ChunkHandlerTable _randomizer_chunk_handlers;
246
248 static const ChunkHandlerTable _chunk_handler_tables[] = {
249 _gamelog_chunk_handlers,
250 _map_chunk_handlers,
251 _misc_chunk_handlers,
252 _name_chunk_handlers,
253 _cheat_chunk_handlers,
254 _setting_chunk_handlers,
255 _veh_chunk_handlers,
256 _waypoint_chunk_handlers,
257 _depot_chunk_handlers,
258 _order_chunk_handlers,
259 _industry_chunk_handlers,
260 _economy_chunk_handlers,
261 _subsidy_chunk_handlers,
262 _cargomonitor_chunk_handlers,
263 _goal_chunk_handlers,
264 _story_page_chunk_handlers,
265 _league_chunk_handlers,
266 _engine_chunk_handlers,
267 _town_chunk_handlers,
268 _sign_chunk_handlers,
269 _station_chunk_handlers,
270 _company_chunk_handlers,
271 _ai_chunk_handlers,
272 _game_chunk_handlers,
273 _animated_tile_chunk_handlers,
274 _newgrf_chunk_handlers,
275 _group_chunk_handlers,
276 _cargopacket_chunk_handlers,
277 _autoreplace_chunk_handlers,
278 _labelmaps_chunk_handlers,
279 _linkgraph_chunk_handlers,
280 _airport_chunk_handlers,
281 _object_chunk_handlers,
282 _persistent_storage_chunk_handlers,
283 _water_region_chunk_handlers,
284 _randomizer_chunk_handlers,
285 };
286
287 static std::vector<ChunkHandlerRef> _chunk_handlers;
288
289 if (_chunk_handlers.empty()) {
290 for (auto &chunk_handler_table : _chunk_handler_tables) {
291 for (auto &chunk_handler : chunk_handler_table) {
292 _chunk_handlers.push_back(chunk_handler);
293 }
294 }
295 }
296
297 return _chunk_handlers;
298}
299
301static void SlNullPointers()
302{
304
305 /* We don't want any savegame conversion code to run
306 * during NULLing; especially those that try to get
307 * pointers from other pools. */
309
310 for (const ChunkHandler &ch : ChunkHandlers()) {
311 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
312 ch.FixPointers();
313 }
314
315 assert(_sl.action == SLA_NULL);
316}
317
326[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
327{
328 /* Distinguish between loading into _load_check_data vs. normal save/load. */
329 if (_sl.action == SLA_LOAD_CHECK) {
330 _load_check_data.error = string;
331 _load_check_data.error_msg = extra_msg;
332 } else {
333 _sl.error_str = string;
334 _sl.extra_msg = extra_msg;
335 }
336
337 /* We have to nullptr all pointers here; we might be in a state where
338 * the pointers are actually filled with indices, which means that
339 * when we access them during cleaning the pool dereferences of
340 * those indices will be made with segmentation faults as result. */
342
343 /* Logging could be active. */
344 _gamelog.StopAnyAction();
345
346 throw std::exception();
347}
348
356[[noreturn]] void SlErrorCorrupt(const std::string &msg)
357{
358 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
359}
360
361
362typedef void (*AsyncSaveFinishProc)();
363static std::atomic<AsyncSaveFinishProc> _async_save_finish;
364static std::thread _save_thread;
365
371{
372 if (_exit_game) return;
373 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
374
375 _async_save_finish.store(proc, std::memory_order_release);
376}
377
382{
383 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
384 if (proc == nullptr) return;
385
386 proc();
387
388 if (_save_thread.joinable()) {
389 _save_thread.join();
390 }
391}
392
397uint8_t SlReadByte()
398{
399 return _sl.reader->ReadByte();
400}
401
406void SlWriteByte(uint8_t b)
407{
408 _sl.dumper->WriteByte(b);
409}
410
411static inline int SlReadUint16()
412{
413 int x = SlReadByte() << 8;
414 return x | SlReadByte();
415}
416
417static inline uint32_t SlReadUint32()
418{
419 uint32_t x = SlReadUint16() << 16;
420 return x | SlReadUint16();
421}
422
423static inline uint64_t SlReadUint64()
424{
425 uint32_t x = SlReadUint32();
426 uint32_t y = SlReadUint32();
427 return (uint64_t)x << 32 | y;
428}
429
430static inline void SlWriteUint16(uint16_t v)
431{
432 SlWriteByte(GB(v, 8, 8));
433 SlWriteByte(GB(v, 0, 8));
434}
435
436static inline void SlWriteUint32(uint32_t v)
437{
438 SlWriteUint16(GB(v, 16, 16));
439 SlWriteUint16(GB(v, 0, 16));
440}
441
442static inline void SlWriteUint64(uint64_t x)
443{
444 SlWriteUint32((uint32_t)(x >> 32));
445 SlWriteUint32((uint32_t)x);
446}
447
457static uint SlReadSimpleGamma()
458{
459 uint i = SlReadByte();
460 if (HasBit(i, 7)) {
461 i &= ~0x80;
462 if (HasBit(i, 6)) {
463 i &= ~0x40;
464 if (HasBit(i, 5)) {
465 i &= ~0x20;
466 if (HasBit(i, 4)) {
467 i &= ~0x10;
468 if (HasBit(i, 3)) {
469 SlErrorCorrupt("Unsupported gamma");
470 }
471 i = SlReadByte(); // 32 bits only.
472 }
473 i = (i << 8) | SlReadByte();
474 }
475 i = (i << 8) | SlReadByte();
476 }
477 i = (i << 8) | SlReadByte();
478 }
479 return i;
480}
481
499static void SlWriteSimpleGamma(size_t i)
500{
501 if (i >= (1 << 7)) {
502 if (i >= (1 << 14)) {
503 if (i >= (1 << 21)) {
504 if (i >= (1 << 28)) {
505 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
506 SlWriteByte((uint8_t)(0xF0));
507 SlWriteByte((uint8_t)(i >> 24));
508 } else {
509 SlWriteByte((uint8_t)(0xE0 | (i >> 24)));
510 }
511 SlWriteByte((uint8_t)(i >> 16));
512 } else {
513 SlWriteByte((uint8_t)(0xC0 | (i >> 16)));
514 }
515 SlWriteByte((uint8_t)(i >> 8));
516 } else {
517 SlWriteByte((uint8_t)(0x80 | (i >> 8)));
518 }
519 }
520 SlWriteByte((uint8_t)i);
521}
522
524static inline uint SlGetGammaLength(size_t i)
525{
526 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
527}
528
529static inline uint SlReadSparseIndex()
530{
531 return SlReadSimpleGamma();
532}
533
534static inline void SlWriteSparseIndex(uint index)
535{
536 SlWriteSimpleGamma(index);
537}
538
539static inline uint SlReadArrayLength()
540{
541 return SlReadSimpleGamma();
542}
543
544static inline void SlWriteArrayLength(size_t length)
545{
546 SlWriteSimpleGamma(length);
547}
548
549static inline uint SlGetArrayLength(size_t length)
550{
551 return SlGetGammaLength(length);
552}
553
557static uint8_t GetSavegameFileType(const SaveLoad &sld)
558{
559 switch (sld.cmd) {
560 case SL_VAR:
561 return GetVarFileType(sld.conv); break;
562
563 case SL_STDSTR:
564 case SL_ARR:
565 case SL_VECTOR:
566 case SL_DEQUE:
567 return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
568
569 case SL_REF:
570 return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
571
572 case SL_REFLIST:
573 case SL_REFVECTOR:
574 return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
575
576 case SL_SAVEBYTE:
577 return SLE_FILE_U8;
578
579 case SL_STRUCT:
580 case SL_STRUCTLIST:
581 return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
582
583 default: NOT_REACHED();
584 }
585}
586
593static inline uint SlCalcConvMemLen(VarType conv)
594{
595 switch (GetVarMemType(conv)) {
596 case SLE_VAR_BL: return sizeof(bool);
597 case SLE_VAR_I8: return sizeof(int8_t);
598 case SLE_VAR_U8: return sizeof(uint8_t);
599 case SLE_VAR_I16: return sizeof(int16_t);
600 case SLE_VAR_U16: return sizeof(uint16_t);
601 case SLE_VAR_I32: return sizeof(int32_t);
602 case SLE_VAR_U32: return sizeof(uint32_t);
603 case SLE_VAR_I64: return sizeof(int64_t);
604 case SLE_VAR_U64: return sizeof(uint64_t);
605 case SLE_VAR_NULL: return 0;
606
607 case SLE_VAR_STR:
608 case SLE_VAR_STRQ:
609 return SlReadArrayLength();
610
611 case SLE_VAR_NAME:
612 default:
613 NOT_REACHED();
614 }
615}
616
623static inline uint8_t SlCalcConvFileLen(VarType conv)
624{
625 switch (GetVarFileType(conv)) {
626 case SLE_FILE_END: return 0;
627 case SLE_FILE_I8: return sizeof(int8_t);
628 case SLE_FILE_U8: return sizeof(uint8_t);
629 case SLE_FILE_I16: return sizeof(int16_t);
630 case SLE_FILE_U16: return sizeof(uint16_t);
631 case SLE_FILE_I32: return sizeof(int32_t);
632 case SLE_FILE_U32: return sizeof(uint32_t);
633 case SLE_FILE_I64: return sizeof(int64_t);
634 case SLE_FILE_U64: return sizeof(uint64_t);
635 case SLE_FILE_STRINGID: return sizeof(uint16_t);
636
637 case SLE_FILE_STRING:
638 return SlReadArrayLength();
639
640 case SLE_FILE_STRUCT:
641 default:
642 NOT_REACHED();
643 }
644}
645
647static inline size_t SlCalcRefLen()
648{
649 return IsSavegameVersionBefore(SLV_69) ? 2 : 4;
650}
651
652void SlSetArrayIndex(uint index)
653{
655 _sl.array_index = index;
656}
657
658static size_t _next_offs;
659
665{
666 /* After reading in the whole array inside the loop
667 * we must have read in all the data, so we must be at end of current block. */
668 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
669 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
670 }
671
672 for (;;) {
673 uint length = SlReadArrayLength();
674 if (length == 0) {
675 assert(!_sl.expect_table_header);
676 _next_offs = 0;
677 return -1;
678 }
679
680 _sl.obj_len = --length;
681 _next_offs = _sl.reader->GetSize() + length;
682
684 _sl.expect_table_header = false;
685 return INT32_MAX;
686 }
687
688 int index;
689 switch (_sl.block_mode) {
690 case CH_SPARSE_TABLE:
691 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
692 case CH_TABLE:
693 case CH_ARRAY: index = _sl.array_index++; break;
694 default:
695 Debug(sl, 0, "SlIterateArray error");
696 return -1; // error
697 }
698
699 if (length != 0) return index;
700 }
701}
702
707{
708 while (SlIterateArray() != -1) {
709 SlSkipBytes(_next_offs - _sl.reader->GetSize());
710 }
711}
712
718void SlSetLength(size_t length)
719{
720 assert(_sl.action == SLA_SAVE);
721
722 switch (_sl.need_length) {
723 case NL_WANTLENGTH:
725 if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) {
726 _sl.expect_table_header = false;
727 SlWriteArrayLength(length + 1);
728 break;
729 }
730
731 switch (_sl.block_mode) {
732 case CH_RIFF:
733 /* Ugly encoding of >16M RIFF chunks
734 * The lower 24 bits are normal
735 * The uppermost 4 bits are bits 24:27 */
736 assert(length < (1 << 28));
737 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
738 break;
739 case CH_TABLE:
740 case CH_ARRAY:
741 assert(_sl.last_array_index <= _sl.array_index);
742 while (++_sl.last_array_index <= _sl.array_index) {
743 SlWriteArrayLength(1);
744 }
745 SlWriteArrayLength(length + 1);
746 break;
747 case CH_SPARSE_TABLE:
748 case CH_SPARSE_ARRAY:
749 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
750 SlWriteSparseIndex(_sl.array_index);
751 break;
752 default: NOT_REACHED();
753 }
754 break;
755
756 case NL_CALCLENGTH:
757 _sl.obj_len += (int)length;
758 break;
759
760 default: NOT_REACHED();
761 }
762}
763
770static void SlCopyBytes(void *ptr, size_t length)
771{
772 uint8_t *p = (uint8_t *)ptr;
773
774 switch (_sl.action) {
775 case SLA_LOAD_CHECK:
776 case SLA_LOAD:
777 for (; length != 0; length--) *p++ = SlReadByte();
778 break;
779 case SLA_SAVE:
780 for (; length != 0; length--) SlWriteByte(*p++);
781 break;
782 default: NOT_REACHED();
783 }
784}
785
788{
789 return _sl.obj_len;
790}
791
799int64_t ReadValue(const void *ptr, VarType conv)
800{
801 switch (GetVarMemType(conv)) {
802 case SLE_VAR_BL: return (*(const bool *)ptr != 0);
803 case SLE_VAR_I8: return *(const int8_t *)ptr;
804 case SLE_VAR_U8: return *(const uint8_t *)ptr;
805 case SLE_VAR_I16: return *(const int16_t *)ptr;
806 case SLE_VAR_U16: return *(const uint16_t*)ptr;
807 case SLE_VAR_I32: return *(const int32_t *)ptr;
808 case SLE_VAR_U32: return *(const uint32_t*)ptr;
809 case SLE_VAR_I64: return *(const int64_t *)ptr;
810 case SLE_VAR_U64: return *(const uint64_t*)ptr;
811 case SLE_VAR_NULL:return 0;
812 default: NOT_REACHED();
813 }
814}
815
823void WriteValue(void *ptr, VarType conv, int64_t val)
824{
825 switch (GetVarMemType(conv)) {
826 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
827 case SLE_VAR_I8: *(int8_t *)ptr = val; break;
828 case SLE_VAR_U8: *(uint8_t *)ptr = val; break;
829 case SLE_VAR_I16: *(int16_t *)ptr = val; break;
830 case SLE_VAR_U16: *(uint16_t*)ptr = val; break;
831 case SLE_VAR_I32: *(int32_t *)ptr = val; break;
832 case SLE_VAR_U32: *(uint32_t*)ptr = val; break;
833 case SLE_VAR_I64: *(int64_t *)ptr = val; break;
834 case SLE_VAR_U64: *(uint64_t*)ptr = val; break;
835 case SLE_VAR_NAME: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(val); break;
836 case SLE_VAR_NULL: break;
837 default: NOT_REACHED();
838 }
839}
840
849static void SlSaveLoadConv(void *ptr, VarType conv)
850{
851 switch (_sl.action) {
852 case SLA_SAVE: {
853 int64_t x = ReadValue(ptr, conv);
854
855 /* Write the value to the file and check if its value is in the desired range */
856 switch (GetVarFileType(conv)) {
857 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
858 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
859 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
861 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
862 case SLE_FILE_I32:
863 case SLE_FILE_U32: SlWriteUint32((uint32_t)x);break;
864 case SLE_FILE_I64:
865 case SLE_FILE_U64: SlWriteUint64(x);break;
866 default: NOT_REACHED();
867 }
868 break;
869 }
870 case SLA_LOAD_CHECK:
871 case SLA_LOAD: {
872 int64_t x;
873 /* Read a value from the file */
874 switch (GetVarFileType(conv)) {
875 case SLE_FILE_I8: x = (int8_t )SlReadByte(); break;
876 case SLE_FILE_U8: x = (uint8_t )SlReadByte(); break;
877 case SLE_FILE_I16: x = (int16_t )SlReadUint16(); break;
878 case SLE_FILE_U16: x = (uint16_t)SlReadUint16(); break;
879 case SLE_FILE_I32: x = (int32_t )SlReadUint32(); break;
880 case SLE_FILE_U32: x = (uint32_t)SlReadUint32(); break;
881 case SLE_FILE_I64: x = (int64_t )SlReadUint64(); break;
882 case SLE_FILE_U64: x = (uint64_t)SlReadUint64(); break;
883 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16_t)SlReadUint16()); break;
884 default: NOT_REACHED();
885 }
886
887 /* Write The value to the struct. These ARE endian safe. */
888 WriteValue(ptr, conv, x);
889 break;
890 }
891 case SLA_PTRS: break;
892 case SLA_NULL: break;
893 default: NOT_REACHED();
894 }
895}
896
904static inline size_t SlCalcStdStringLen(const void *ptr)
905{
906 const std::string *str = reinterpret_cast<const std::string *>(ptr);
907
908 size_t len = str->length();
909 return len + SlGetArrayLength(len); // also include the length of the index
910}
911
912
920void FixSCCEncoded(std::string &str, bool fix_code)
921{
922 if (str.empty()) return;
923
924 /* We need to convert from old escape-style encoding to record separator encoding.
925 * Initial `<SCC_ENCODED><STRINGID>` stays the same.
926 *
927 * `:<SCC_ENCODED><STRINGID>` becomes `<RS><SCC_ENCODED><STRINGID>`
928 * `:<HEX>` becomes `<RS><SCC_ENCODED_NUMERIC><HEX>`
929 * `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
930 */
931 std::string result;
932 StringBuilder builder(result);
933
934 bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
935 bool in_string = false; // Set if we in a string, between double-quotes.
936 bool need_type = true; // Set if a parameter type needs to be emitted.
937
938 for (auto it = std::begin(str); it != std::end(str); /* nothing */) {
939 size_t len = Utf8EncodedCharLen(*it);
940 if (len == 0 || it + len > std::end(str)) break;
941
942 char32_t c;
943 Utf8Decode(&c, &*it);
944 it += len;
945 if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
946 builder.PutUtf8(SCC_ENCODED);
947 need_type = false;
948 is_encoded = true;
949 continue;
950 }
951
952 /* If the first character is not SCC_ENCODED then we don't have to do any conversion. */
953 if (!is_encoded) return;
954
955 if (c == '"') {
956 in_string = !in_string;
957 if (in_string && need_type) {
958 /* Started a new string parameter. */
960 need_type = false;
961 }
962 continue;
963 }
964
965 if (!in_string && c == ':') {
966 builder.PutUtf8(SCC_RECORD_SEPARATOR);
967 need_type = true;
968 continue;
969 }
970 if (need_type) {
971 /* Started a new numeric parameter. */
973 need_type = false;
974 }
975
976 builder.PutUtf8(c);
977 }
978
979 str = std::move(result);
980}
981
988void SlReadString(std::string &str, size_t length)
989{
990 str.resize(length);
991 SlCopyBytes(str.data(), length);
992}
993
999static void SlStdString(void *ptr, VarType conv)
1000{
1001 std::string *str = reinterpret_cast<std::string *>(ptr);
1002
1003 switch (_sl.action) {
1004 case SLA_SAVE: {
1005 size_t len = str->length();
1006 SlWriteArrayLength(len);
1007 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->c_str())), len);
1008 break;
1009 }
1010
1011 case SLA_LOAD_CHECK:
1012 case SLA_LOAD: {
1013 size_t len = SlReadArrayLength();
1014 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1015 SlSkipBytes(len);
1016 return;
1017 }
1018
1019 SlReadString(*str, len);
1020
1022 if ((conv & SLF_ALLOW_CONTROL) != 0) {
1025 }
1026 if ((conv & SLF_ALLOW_NEWLINE) != 0) {
1028 }
1030 }
1031
1032 case SLA_PTRS: break;
1033 case SLA_NULL: break;
1034 default: NOT_REACHED();
1035 }
1036}
1037
1046static void SlCopyInternal(void *object, size_t length, VarType conv)
1047{
1048 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1049 assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes
1050 SlSkipBytes(length * SlCalcConvFileLen(conv));
1051 return;
1052 }
1053
1054 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
1055 * as a byte-type. So detect this, and adjust object size accordingly */
1056 if (_sl.action != SLA_SAVE && _sl_version == 0) {
1057 /* all objects except difficulty settings */
1058 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
1059 conv == SLE_INT32 || conv == SLE_UINT32) {
1060 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
1061 return;
1062 }
1063 /* used for conversion of Money 32bit->64bit */
1064 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
1065 for (uint i = 0; i < length; i++) {
1066 ((int64_t*)object)[i] = (int32_t)std::byteswap(SlReadUint32());
1067 }
1068 return;
1069 }
1070 }
1071
1072 /* If the size of elements is 1 byte both in file and memory, no special
1073 * conversion is needed, use specialized copy-copy function to speed up things */
1074 if (conv == SLE_INT8 || conv == SLE_UINT8) {
1075 SlCopyBytes(object, length);
1076 } else {
1077 uint8_t *a = (uint8_t*)object;
1078 uint8_t mem_size = SlCalcConvMemLen(conv);
1079
1080 for (; length != 0; length --) {
1081 SlSaveLoadConv(a, conv);
1082 a += mem_size; // get size
1083 }
1084 }
1085}
1086
1095void SlCopy(void *object, size_t length, VarType conv)
1096{
1097 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
1098
1099 /* Automatically calculate the length? */
1100 if (_sl.need_length != NL_NONE) {
1101 SlSetLength(length * SlCalcConvFileLen(conv));
1102 /* Determine length only? */
1103 if (_sl.need_length == NL_CALCLENGTH) return;
1104 }
1105
1106 SlCopyInternal(object, length, conv);
1107}
1108
1114static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1115{
1116 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1117}
1118
1125static void SlArray(void *array, size_t length, VarType conv)
1126{
1127 switch (_sl.action) {
1128 case SLA_SAVE:
1129 SlWriteArrayLength(length);
1130 SlCopyInternal(array, length, conv);
1131 return;
1132
1133 case SLA_LOAD_CHECK:
1134 case SLA_LOAD: {
1136 size_t sv_length = SlReadArrayLength();
1137 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1138 /* We don't know this field, so we assume the length in the savegame is correct. */
1139 length = sv_length;
1140 } else if (sv_length != length) {
1141 /* If the SLE_ARR changes size, a savegame bump is required
1142 * and the developer should have written conversion lines.
1143 * Error out to make this more visible. */
1144 SlErrorCorrupt("Fixed-length array is of wrong length");
1145 }
1146 }
1147
1148 SlCopyInternal(array, length, conv);
1149 return;
1150 }
1151
1152 case SLA_PTRS:
1153 case SLA_NULL:
1154 return;
1155
1156 default:
1157 NOT_REACHED();
1158 }
1159}
1160
1171static size_t ReferenceToInt(const void *obj, SLRefType rt)
1172{
1173 assert(_sl.action == SLA_SAVE);
1174
1175 if (obj == nullptr) return 0;
1176
1177 switch (rt) {
1178 case REF_VEHICLE_OLD: // Old vehicles we save as new ones
1179 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
1180 case REF_STATION: return ((const Station*)obj)->index + 1;
1181 case REF_TOWN: return ((const Town*)obj)->index + 1;
1182 case REF_ORDER: return ((const Order*)obj)->index + 1;
1183 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
1184 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
1185 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
1186 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
1187 case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
1188 case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
1189 case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
1190 default: NOT_REACHED();
1191 }
1192}
1193
1204static void *IntToReference(size_t index, SLRefType rt)
1205{
1206 static_assert(sizeof(size_t) <= sizeof(void *));
1207
1208 assert(_sl.action == SLA_PTRS);
1209
1210 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
1211 * and should be loaded like that */
1212 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) {
1213 rt = REF_VEHICLE;
1214 }
1215
1216 /* No need to look up nullptr pointers, just return immediately */
1217 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr;
1218
1219 /* Correct index. Old vehicles were saved differently:
1220 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1221 if (rt != REF_VEHICLE_OLD) index--;
1222
1223 switch (rt) {
1224 case REF_ORDERLIST:
1225 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1226 SlErrorCorrupt("Referencing invalid OrderList");
1227
1228 case REF_ORDER:
1229 if (Order::IsValidID(index)) return Order::Get(index);
1230 /* in old versions, invalid order was used to mark end of order list */
1231 if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr;
1232 SlErrorCorrupt("Referencing invalid Order");
1233
1234 case REF_VEHICLE_OLD:
1235 case REF_VEHICLE:
1236 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1237 SlErrorCorrupt("Referencing invalid Vehicle");
1238
1239 case REF_STATION:
1240 if (Station::IsValidID(index)) return Station::Get(index);
1241 SlErrorCorrupt("Referencing invalid Station");
1242
1243 case REF_TOWN:
1244 if (Town::IsValidID(index)) return Town::Get(index);
1245 SlErrorCorrupt("Referencing invalid Town");
1246
1247 case REF_ROADSTOPS:
1248 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1249 SlErrorCorrupt("Referencing invalid RoadStop");
1250
1251 case REF_ENGINE_RENEWS:
1252 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1253 SlErrorCorrupt("Referencing invalid EngineRenew");
1254
1255 case REF_CARGO_PACKET:
1256 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1257 SlErrorCorrupt("Referencing invalid CargoPacket");
1258
1259 case REF_STORAGE:
1260 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1261 SlErrorCorrupt("Referencing invalid PersistentStorage");
1262
1263 case REF_LINK_GRAPH:
1264 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1265 SlErrorCorrupt("Referencing invalid LinkGraph");
1266
1267 case REF_LINK_GRAPH_JOB:
1268 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1269 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1270
1271 default: NOT_REACHED();
1272 }
1273}
1274
1280void SlSaveLoadRef(void *ptr, VarType conv)
1281{
1282 switch (_sl.action) {
1283 case SLA_SAVE:
1284 SlWriteUint32((uint32_t)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
1285 break;
1286 case SLA_LOAD_CHECK:
1287 case SLA_LOAD:
1288 *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32();
1289 break;
1290 case SLA_PTRS:
1291 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
1292 break;
1293 case SLA_NULL:
1294 *(void **)ptr = nullptr;
1295 break;
1296 default: NOT_REACHED();
1297 }
1298}
1299
1303template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1305 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1306public:
1313 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1314 {
1315 assert(cmd == SL_VAR || cmd == SL_REF);
1316
1317 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1318
1319 int type_size = SlGetArrayLength(list->size());
1320 int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32);
1321 return list->size() * item_size + type_size;
1322 }
1323
1324 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1325 {
1326 switch (cmd) {
1327 case SL_VAR: SlSaveLoadConv(item, conv); break;
1328 case SL_REF: SlSaveLoadRef(item, conv); break;
1329 case SL_STDSTR: SlStdString(item, conv); break;
1330 default:
1331 NOT_REACHED();
1332 }
1333 }
1334
1341 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1342 {
1343 assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
1344
1345 SlStorageT *list = static_cast<SlStorageT *>(storage);
1346
1347 switch (_sl.action) {
1348 case SLA_SAVE:
1349 SlWriteArrayLength(list->size());
1350
1351 for (auto &item : *list) {
1352 SlSaveLoadMember(cmd, &item, conv);
1353 }
1354 break;
1355
1356 case SLA_LOAD_CHECK:
1357 case SLA_LOAD: {
1358 size_t length;
1359 switch (cmd) {
1360 case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1361 case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1362 case SL_STDSTR: length = SlReadArrayLength(); break;
1363 default: NOT_REACHED();
1364 }
1365
1366 list->clear();
1367 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1368 list->reserve(length);
1369 }
1370
1371 /* Load each value and push to the end of the storage. */
1372 for (size_t i = 0; i < length; i++) {
1373 Tvar &data = list->emplace_back();
1374 SlSaveLoadMember(cmd, &data, conv);
1375 }
1376 break;
1377 }
1378
1379 case SLA_PTRS:
1380 for (auto &item : *list) {
1381 SlSaveLoadMember(cmd, &item, conv);
1382 }
1383 break;
1384
1385 case SLA_NULL:
1386 list->clear();
1387 break;
1388
1389 default: NOT_REACHED();
1390 }
1391 }
1392};
1393
1399static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1400{
1402}
1403
1409static void SlRefList(void *list, VarType conv)
1410{
1411 /* Automatically calculate the length? */
1412 if (_sl.need_length != NL_NONE) {
1413 SlSetLength(SlCalcRefListLen(list, conv));
1414 /* Determine length only? */
1415 if (_sl.need_length == NL_CALCLENGTH) return;
1416 }
1417
1419}
1420
1426static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1427{
1429}
1430
1436static void SlRefVector(void *vector, VarType conv)
1437{
1438 /* Automatically calculate the length? */
1439 if (_sl.need_length != NL_NONE) {
1440 SlSetLength(SlCalcRefVectorLen(vector, conv));
1441 /* Determine length only? */
1442 if (_sl.need_length == NL_CALCLENGTH) return;
1443 }
1444
1446}
1447
1453static inline size_t SlCalcDequeLen(const void *deque, VarType conv)
1454{
1455 switch (GetVarMemType(conv)) {
1456 case SLE_VAR_BL: return SlStorageHelper<std::deque, bool>::SlCalcLen(deque, conv);
1457 case SLE_VAR_I8: return SlStorageHelper<std::deque, int8_t>::SlCalcLen(deque, conv);
1458 case SLE_VAR_U8: return SlStorageHelper<std::deque, uint8_t>::SlCalcLen(deque, conv);
1459 case SLE_VAR_I16: return SlStorageHelper<std::deque, int16_t>::SlCalcLen(deque, conv);
1460 case SLE_VAR_U16: return SlStorageHelper<std::deque, uint16_t>::SlCalcLen(deque, conv);
1461 case SLE_VAR_I32: return SlStorageHelper<std::deque, int32_t>::SlCalcLen(deque, conv);
1462 case SLE_VAR_U32: return SlStorageHelper<std::deque, uint32_t>::SlCalcLen(deque, conv);
1463 case SLE_VAR_I64: return SlStorageHelper<std::deque, int64_t>::SlCalcLen(deque, conv);
1464 case SLE_VAR_U64: return SlStorageHelper<std::deque, uint64_t>::SlCalcLen(deque, conv);
1465
1466 case SLE_VAR_STR:
1467 /* Strings are a length-prefixed field type in the savegame table format,
1468 * these may not be directly stored in another length-prefixed container type. */
1469 NOT_REACHED();
1470
1471 default: NOT_REACHED();
1472 }
1473}
1474
1480static void SlDeque(void *deque, VarType conv)
1481{
1482 switch (GetVarMemType(conv)) {
1483 case SLE_VAR_BL: SlStorageHelper<std::deque, bool>::SlSaveLoad(deque, conv); break;
1484 case SLE_VAR_I8: SlStorageHelper<std::deque, int8_t>::SlSaveLoad(deque, conv); break;
1485 case SLE_VAR_U8: SlStorageHelper<std::deque, uint8_t>::SlSaveLoad(deque, conv); break;
1486 case SLE_VAR_I16: SlStorageHelper<std::deque, int16_t>::SlSaveLoad(deque, conv); break;
1487 case SLE_VAR_U16: SlStorageHelper<std::deque, uint16_t>::SlSaveLoad(deque, conv); break;
1488 case SLE_VAR_I32: SlStorageHelper<std::deque, int32_t>::SlSaveLoad(deque, conv); break;
1489 case SLE_VAR_U32: SlStorageHelper<std::deque, uint32_t>::SlSaveLoad(deque, conv); break;
1490 case SLE_VAR_I64: SlStorageHelper<std::deque, int64_t>::SlSaveLoad(deque, conv); break;
1491 case SLE_VAR_U64: SlStorageHelper<std::deque, uint64_t>::SlSaveLoad(deque, conv); break;
1492
1493 case SLE_VAR_STR:
1494 /* Strings are a length-prefixed field type in the savegame table format,
1495 * these may not be directly stored in another length-prefixed container type.
1496 * This is permitted for load-related actions, because invalid fields of this type are present
1497 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1498 assert(_sl.action != SLA_SAVE);
1500 break;
1501
1502 default: NOT_REACHED();
1503 }
1504}
1505
1511static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1512{
1513 switch (GetVarMemType(conv)) {
1514 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1515 case SLE_VAR_I8: return SlStorageHelper<std::vector, int8_t>::SlCalcLen(vector, conv);
1516 case SLE_VAR_U8: return SlStorageHelper<std::vector, uint8_t>::SlCalcLen(vector, conv);
1517 case SLE_VAR_I16: return SlStorageHelper<std::vector, int16_t>::SlCalcLen(vector, conv);
1518 case SLE_VAR_U16: return SlStorageHelper<std::vector, uint16_t>::SlCalcLen(vector, conv);
1519 case SLE_VAR_I32: return SlStorageHelper<std::vector, int32_t>::SlCalcLen(vector, conv);
1520 case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
1521 case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
1522 case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
1523
1524 case SLE_VAR_STR:
1525 /* Strings are a length-prefixed field type in the savegame table format,
1526 * these may not be directly stored in another length-prefixed container type. */
1527 NOT_REACHED();
1528
1529 default: NOT_REACHED();
1530 }
1531}
1532
1538static void SlVector(void *vector, VarType conv)
1539{
1540 switch (GetVarMemType(conv)) {
1541 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1542 case SLE_VAR_I8: SlStorageHelper<std::vector, int8_t>::SlSaveLoad(vector, conv); break;
1543 case SLE_VAR_U8: SlStorageHelper<std::vector, uint8_t>::SlSaveLoad(vector, conv); break;
1544 case SLE_VAR_I16: SlStorageHelper<std::vector, int16_t>::SlSaveLoad(vector, conv); break;
1545 case SLE_VAR_U16: SlStorageHelper<std::vector, uint16_t>::SlSaveLoad(vector, conv); break;
1546 case SLE_VAR_I32: SlStorageHelper<std::vector, int32_t>::SlSaveLoad(vector, conv); break;
1547 case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
1548 case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
1549 case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
1550
1551 case SLE_VAR_STR:
1552 /* Strings are a length-prefixed field type in the savegame table format,
1553 * these may not be directly stored in another length-prefixed container type.
1554 * This is permitted for load-related actions, because invalid fields of this type are present
1555 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1556 assert(_sl.action != SLA_SAVE);
1558 break;
1559
1560 default: NOT_REACHED();
1561 }
1562}
1563
1565static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1566{
1567 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1568}
1569
1575static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1576{
1577 size_t length = 0;
1578
1579 for (auto &sld : slt) {
1580 if (!SlIsObjectValidInSavegame(sld)) continue;
1581
1582 length += SlCalcConvFileLen(SLE_UINT8);
1583 length += SlCalcStdStringLen(&sld.name);
1584 }
1585
1586 length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
1587
1588 for (auto &sld : slt) {
1589 if (!SlIsObjectValidInSavegame(sld)) continue;
1590 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1591 length += SlCalcTableHeader(sld.handler->GetDescription());
1592 }
1593 }
1594
1595 return length;
1596}
1597
1604size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1605{
1606 size_t length = 0;
1607
1608 /* Need to determine the length and write a length tag. */
1609 for (auto &sld : slt) {
1610 length += SlCalcObjMemberLength(object, sld);
1611 }
1612 return length;
1613}
1614
1615size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1616{
1617 assert(_sl.action == SLA_SAVE);
1618
1619 if (!SlIsObjectValidInSavegame(sld)) return 0;
1620
1621 switch (sld.cmd) {
1622 case SL_VAR: return SlCalcConvFileLen(sld.conv);
1623 case SL_REF: return SlCalcRefLen();
1624 case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
1625 case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
1626 case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
1627 case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
1628 case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1629 case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
1630 case SL_SAVEBYTE: return 1; // a byte is logically of size 1
1631 case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
1632
1633 case SL_STRUCT:
1634 case SL_STRUCTLIST: {
1635 NeedLength old_need_length = _sl.need_length;
1636 size_t old_obj_len = _sl.obj_len;
1637
1639 _sl.obj_len = 0;
1640
1641 /* Pretend that we are saving to collect the object size. Other
1642 * means are difficult, as we don't know the length of the list we
1643 * are about to store. */
1644 sld.handler->Save(const_cast<void *>(object));
1645 size_t length = _sl.obj_len;
1646
1647 _sl.obj_len = old_obj_len;
1648 _sl.need_length = old_need_length;
1649
1650 if (sld.cmd == SL_STRUCT) {
1651 length += SlGetArrayLength(1);
1652 }
1653
1654 return length;
1655 }
1656
1657 default: NOT_REACHED();
1658 }
1659 return 0;
1660}
1661
1662static bool SlObjectMember(void *object, const SaveLoad &sld)
1663{
1664 if (!SlIsObjectValidInSavegame(sld)) return false;
1665
1666 VarType conv = GB(sld.conv, 0, 8);
1667 switch (sld.cmd) {
1668 case SL_VAR:
1669 case SL_REF:
1670 case SL_ARR:
1671 case SL_REFLIST:
1672 case SL_REFVECTOR:
1673 case SL_DEQUE:
1674 case SL_VECTOR:
1675 case SL_STDSTR: {
1676 void *ptr = GetVariableAddress(object, sld);
1677
1678 switch (sld.cmd) {
1679 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
1680 case SL_REF: SlSaveLoadRef(ptr, conv); break;
1681 case SL_ARR: SlArray(ptr, sld.length, conv); break;
1682 case SL_REFLIST: SlRefList(ptr, conv); break;
1683 case SL_REFVECTOR: SlRefVector(ptr, conv); break;
1684 case SL_DEQUE: SlDeque(ptr, conv); break;
1685 case SL_VECTOR: SlVector(ptr, conv); break;
1686 case SL_STDSTR: SlStdString(ptr, sld.conv); break;
1687 default: NOT_REACHED();
1688 }
1689 break;
1690 }
1691
1692 /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object.
1693 * When loading, the value is read explicitly with SlReadByte() to determine which
1694 * object description to use. */
1695 case SL_SAVEBYTE: {
1696 void *ptr = GetVariableAddress(object, sld);
1697
1698 switch (_sl.action) {
1699 case SLA_SAVE: SlWriteByte(*(uint8_t *)ptr); break;
1700 case SLA_LOAD_CHECK:
1701 case SLA_LOAD:
1702 case SLA_PTRS:
1703 case SLA_NULL: break;
1704 default: NOT_REACHED();
1705 }
1706 break;
1707 }
1708
1709 case SL_NULL: {
1710 assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
1711
1712 switch (_sl.action) {
1713 case SLA_LOAD_CHECK:
1714 case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
1715 case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1716 case SLA_PTRS:
1717 case SLA_NULL: break;
1718 default: NOT_REACHED();
1719 }
1720 break;
1721 }
1722
1723 case SL_STRUCT:
1724 case SL_STRUCTLIST:
1725 switch (_sl.action) {
1726 case SLA_SAVE: {
1727 if (sld.cmd == SL_STRUCT) {
1728 /* Store in the savegame if this struct was written or not. */
1729 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1730 }
1731 sld.handler->Save(object);
1732 break;
1733 }
1734
1735 case SLA_LOAD_CHECK: {
1738 }
1739 sld.handler->LoadCheck(object);
1740 break;
1741 }
1742
1743 case SLA_LOAD: {
1746 }
1747 sld.handler->Load(object);
1748 break;
1749 }
1750
1751 case SLA_PTRS:
1752 sld.handler->FixPointers(object);
1753 break;
1754
1755 case SLA_NULL: break;
1756 default: NOT_REACHED();
1757 }
1758 break;
1759
1760 default: NOT_REACHED();
1761 }
1762 return true;
1763}
1764
1769void SlSetStructListLength(size_t length)
1770{
1771 /* Automatically calculate the length? */
1772 if (_sl.need_length != NL_NONE) {
1773 SlSetLength(SlGetArrayLength(length));
1774 if (_sl.need_length == NL_CALCLENGTH) return;
1775 }
1776
1777 SlWriteArrayLength(length);
1778}
1779
1785size_t SlGetStructListLength(size_t limit)
1786{
1787 size_t length = SlReadArrayLength();
1788 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1789
1790 return length;
1791}
1792
1798void SlObject(void *object, const SaveLoadTable &slt)
1799{
1800 /* Automatically calculate the length? */
1801 if (_sl.need_length != NL_NONE) {
1802 SlSetLength(SlCalcObjLength(object, slt));
1803 if (_sl.need_length == NL_CALCLENGTH) return;
1804 }
1805
1806 for (auto &sld : slt) {
1807 SlObjectMember(object, sld);
1808 }
1809}
1810
1816 void Save(void *) const override
1817 {
1818 NOT_REACHED();
1819 }
1820
1821 void Load(void *object) const override
1822 {
1823 size_t length = SlGetStructListLength(UINT32_MAX);
1824 for (; length > 0; length--) {
1825 SlObject(object, this->GetLoadDescription());
1826 }
1827 }
1828
1829 void LoadCheck(void *object) const override
1830 {
1831 this->Load(object);
1832 }
1833
1834 virtual SaveLoadTable GetDescription() const override
1835 {
1836 return {};
1837 }
1838
1840 {
1841 NOT_REACHED();
1842 }
1843};
1844
1851std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1852{
1853 /* You can only use SlTableHeader if you are a CH_TABLE. */
1854 assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
1855
1856 switch (_sl.action) {
1857 case SLA_LOAD_CHECK:
1858 case SLA_LOAD: {
1859 std::vector<SaveLoad> saveloads;
1860
1861 /* Build a key lookup mapping based on the available fields. */
1862 std::map<std::string, const SaveLoad *> key_lookup;
1863 for (auto &sld : slt) {
1864 if (!SlIsObjectValidInSavegame(sld)) continue;
1865
1866 /* Check that there is only one active SaveLoad for a given name. */
1867 assert(key_lookup.find(sld.name) == key_lookup.end());
1868 key_lookup[sld.name] = &sld;
1869 }
1870
1871 while (true) {
1872 uint8_t type = 0;
1873 SlSaveLoadConv(&type, SLE_UINT8);
1874 if (type == SLE_FILE_END) break;
1875
1876 std::string key;
1877 SlStdString(&key, SLE_STR);
1878
1879 auto sld_it = key_lookup.find(key);
1880 if (sld_it == key_lookup.end()) {
1881 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1882 Debug(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type);
1883
1884 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1885 SaveLoadType saveload_type;
1886 switch (type & SLE_FILE_TYPE_MASK) {
1887 case SLE_FILE_STRING:
1888 /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
1889 saveload_type = SL_STDSTR;
1890 break;
1891
1892 case SLE_FILE_STRUCT:
1893 /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */
1894 saveload_type = SL_STRUCTLIST;
1895 handler = std::make_shared<SlSkipHandler>();
1896 break;
1897
1898 default:
1899 saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
1900 break;
1901 }
1902
1903 /* We don't know this field, so read to nothing. */
1904 saveloads.emplace_back(std::move(key), saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, std::move(handler));
1905 continue;
1906 }
1907
1908 /* Validate the type of the field. If it is changed, the
1909 * savegame should have been bumped so we know how to do the
1910 * conversion. If this error triggers, that clearly didn't
1911 * happen and this is a friendly poke to the developer to bump
1912 * the savegame version and add conversion code. */
1913 uint8_t correct_type = GetSavegameFileType(*sld_it->second);
1914 if (correct_type != type) {
1915 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type, type);
1916 SlErrorCorrupt("Field type is different than expected");
1917 }
1918 saveloads.emplace_back(*sld_it->second);
1919 }
1920
1921 for (auto &sld : saveloads) {
1922 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1923 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
1924 }
1925 }
1926
1927 return saveloads;
1928 }
1929
1930 case SLA_SAVE: {
1931 /* Automatically calculate the length? */
1932 if (_sl.need_length != NL_NONE) {
1934 if (_sl.need_length == NL_CALCLENGTH) break;
1935 }
1936
1937 for (auto &sld : slt) {
1938 if (!SlIsObjectValidInSavegame(sld)) continue;
1939 /* Make sure we are not storing empty keys. */
1940 assert(!sld.name.empty());
1941
1942 uint8_t type = GetSavegameFileType(sld);
1943 assert(type != SLE_FILE_END);
1944
1945 SlSaveLoadConv(&type, SLE_UINT8);
1946 SlStdString(const_cast<std::string *>(&sld.name), SLE_STR);
1947 }
1948
1949 /* Add an end-of-header marker. */
1950 uint8_t type = SLE_FILE_END;
1951 SlSaveLoadConv(&type, SLE_UINT8);
1952
1953 /* After the table, write down any sub-tables we might have. */
1954 for (auto &sld : slt) {
1955 if (!SlIsObjectValidInSavegame(sld)) continue;
1956 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1957 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
1958 NeedLength old_need_length = _sl.need_length;
1960
1961 SlTableHeader(sld.handler->GetDescription());
1962
1963 _sl.need_length = old_need_length;
1964 }
1965 }
1966
1967 break;
1968 }
1969
1970 default: NOT_REACHED();
1971 }
1972
1973 return std::vector<SaveLoad>();
1974}
1975
1989std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
1990{
1991 assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
1992 /* CH_TABLE / CH_SPARSE_TABLE always have a header. */
1993 if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt);
1994
1995 std::vector<SaveLoad> saveloads;
1996
1997 /* Build a key lookup mapping based on the available fields. */
1998 std::map<std::string, std::vector<const SaveLoad *>> key_lookup;
1999 for (auto &sld : slt) {
2000 /* All entries should have a name; otherwise the entry should just be removed. */
2001 assert(!sld.name.empty());
2002
2003 key_lookup[sld.name].push_back(&sld);
2004 }
2005
2006 for (auto &slc : slct) {
2007 if (slc.name.empty()) {
2008 /* In old savegames there can be data we no longer care for. We
2009 * skip this by simply reading the amount of bytes indicated and
2010 * send those to /dev/null. */
2011 saveloads.emplace_back("", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr);
2012 } else {
2013 auto sld_it = key_lookup.find(slc.name);
2014 /* If this branch triggers, it means that an entry in the
2015 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2016 * you rename a field in one and not in the other? */
2017 if (sld_it == key_lookup.end()) {
2018 /* This isn't an assert, as that leaves no information what
2019 * field was to blame. This way at least we have breadcrumbs. */
2020 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2021 SlErrorCorrupt("Internal error with savegame compatibility");
2022 }
2023 for (auto &sld : sld_it->second) {
2024 saveloads.push_back(*sld);
2025 }
2026 }
2027 }
2028
2029 for (auto &sld : saveloads) {
2030 if (!SlIsObjectValidInSavegame(sld)) continue;
2031 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
2032 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2033 }
2034 }
2035
2036 return saveloads;
2037}
2038
2044{
2045 SlObject(nullptr, slt);
2046}
2047
2053void SlAutolength(AutolengthProc *proc, int arg)
2054{
2055 assert(_sl.action == SLA_SAVE);
2056
2057 /* Tell it to calculate the length */
2059 _sl.obj_len = 0;
2060 proc(arg);
2061
2062 /* Setup length */
2065
2066 size_t start_pos = _sl.dumper->GetSize();
2067 size_t expected_offs = start_pos + _sl.obj_len;
2068
2069 /* And write the stuff */
2070 proc(arg);
2071
2072 if (expected_offs != _sl.dumper->GetSize()) {
2073 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2074 }
2075}
2076
2077void ChunkHandler::LoadCheck(size_t len) const
2078{
2079 switch (_sl.block_mode) {
2080 case CH_TABLE:
2081 case CH_SPARSE_TABLE:
2082 SlTableHeader({});
2083 [[fallthrough]];
2084 case CH_ARRAY:
2085 case CH_SPARSE_ARRAY:
2086 SlSkipArray();
2087 break;
2088 case CH_RIFF:
2089 SlSkipBytes(len);
2090 break;
2091 default:
2092 NOT_REACHED();
2093 }
2094}
2095
2100static void SlLoadChunk(const ChunkHandler &ch)
2101{
2102 uint8_t m = SlReadByte();
2103
2105 _sl.obj_len = 0;
2106 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2107
2108 /* The header should always be at the start. Read the length; the
2109 * Load() should as first action process the header. */
2111 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2112 }
2113
2114 switch (_sl.block_mode) {
2115 case CH_TABLE:
2116 case CH_ARRAY:
2117 _sl.array_index = 0;
2118 ch.Load();
2119 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2120 break;
2121 case CH_SPARSE_TABLE:
2122 case CH_SPARSE_ARRAY:
2123 ch.Load();
2124 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2125 break;
2126 case CH_RIFF: {
2127 /* Read length */
2128 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2129 len += SlReadUint16();
2130 _sl.obj_len = len;
2131 size_t start_pos = _sl.reader->GetSize();
2132 size_t endoffs = start_pos + len;
2133 ch.Load();
2134
2135 if (_sl.reader->GetSize() != endoffs) {
2136 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2137 }
2138 break;
2139 }
2140 default:
2141 SlErrorCorrupt("Invalid chunk type");
2142 break;
2143 }
2144
2145 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2146}
2147
2153static void SlLoadCheckChunk(const ChunkHandler &ch)
2154{
2155 uint8_t m = SlReadByte();
2156
2158 _sl.obj_len = 0;
2159 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2160
2161 /* The header should always be at the start. Read the length; the
2162 * LoadCheck() should as first action process the header. */
2164 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2165 }
2166
2167 switch (_sl.block_mode) {
2168 case CH_TABLE:
2169 case CH_ARRAY:
2170 _sl.array_index = 0;
2171 ch.LoadCheck();
2172 break;
2173 case CH_SPARSE_TABLE:
2174 case CH_SPARSE_ARRAY:
2175 ch.LoadCheck();
2176 break;
2177 case CH_RIFF: {
2178 /* Read length */
2179 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2180 len += SlReadUint16();
2181 _sl.obj_len = len;
2182 size_t start_pos = _sl.reader->GetSize();
2183 size_t endoffs = start_pos + len;
2184 ch.LoadCheck(len);
2185
2186 if (_sl.reader->GetSize() != endoffs) {
2187 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2188 }
2189 break;
2190 }
2191 default:
2192 SlErrorCorrupt("Invalid chunk type");
2193 break;
2194 }
2195
2196 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2197}
2198
2204static void SlSaveChunk(const ChunkHandler &ch)
2205{
2206 if (ch.type == CH_READONLY) return;
2207
2208 SlWriteUint32(ch.id);
2209 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2210
2211 _sl.block_mode = ch.type;
2212 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2213
2215
2216 switch (_sl.block_mode) {
2217 case CH_RIFF:
2218 ch.Save();
2219 break;
2220 case CH_TABLE:
2221 case CH_ARRAY:
2224 ch.Save();
2225 SlWriteArrayLength(0); // Terminate arrays
2226 break;
2227 case CH_SPARSE_TABLE:
2228 case CH_SPARSE_ARRAY:
2230 ch.Save();
2231 SlWriteArrayLength(0); // Terminate arrays
2232 break;
2233 default: NOT_REACHED();
2234 }
2235
2236 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2237}
2238
2240static void SlSaveChunks()
2241{
2242 for (auto &ch : ChunkHandlers()) {
2243 SlSaveChunk(ch);
2244 }
2245
2246 /* Terminator */
2247 SlWriteUint32(0);
2248}
2249
2256static const ChunkHandler *SlFindChunkHandler(uint32_t id)
2257{
2258 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2259 return nullptr;
2260}
2261
2263static void SlLoadChunks()
2264{
2265 uint32_t id;
2266 const ChunkHandler *ch;
2267
2268 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2269 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2270
2271 ch = SlFindChunkHandler(id);
2272 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2273 SlLoadChunk(*ch);
2274 }
2275}
2276
2279{
2280 uint32_t id;
2281 const ChunkHandler *ch;
2282
2283 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2284 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2285
2286 ch = SlFindChunkHandler(id);
2287 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2288 SlLoadCheckChunk(*ch);
2289 }
2290}
2291
2293static void SlFixPointers()
2294{
2296
2297 for (const ChunkHandler &ch : ChunkHandlers()) {
2298 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2299 ch.FixPointers();
2300 }
2301
2302 assert(_sl.action == SLA_PTRS);
2303}
2304
2305
2308 std::optional<FileHandle> file;
2309 long begin;
2310
2315 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2316 {
2317 }
2318
2321 {
2322 if (this->file.has_value()) {
2323 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2324 }
2325 }
2326
2327 size_t Read(uint8_t *buf, size_t size) override
2328 {
2329 /* We're in the process of shutting down, i.e. in "failure" mode. */
2330 if (!this->file.has_value()) return 0;
2331
2332 return fread(buf, 1, size, *this->file);
2333 }
2334
2335 void Reset() override
2336 {
2337 clearerr(*this->file);
2338 if (fseek(*this->file, this->begin, SEEK_SET)) {
2339 Debug(sl, 1, "Could not reset the file reading");
2340 }
2341 }
2342};
2343
2346 std::optional<FileHandle> file;
2347
2352 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2353 {
2354 }
2355
2358 {
2359 this->Finish();
2360 }
2361
2362 void Write(uint8_t *buf, size_t size) override
2363 {
2364 /* We're in the process of shutting down, i.e. in "failure" mode. */
2365 if (!this->file.has_value()) return;
2366
2367 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2368 }
2369
2370 void Finish() override
2371 {
2372 if (this->file.has_value()) {
2373 _game_session_stats.savegame_size = ftell(*this->file);
2374 this->file.reset();
2375 }
2376 }
2377};
2378
2379/*******************************************
2380 ********** START OF LZO CODE **************
2381 *******************************************/
2382
2383#ifdef WITH_LZO
2384#include <lzo/lzo1x.h>
2385
2387static const uint LZO_BUFFER_SIZE = 8192;
2388
2395 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2396 {
2397 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2398 }
2399
2400 size_t Read(uint8_t *buf, size_t ssize) override
2401 {
2402 assert(ssize >= LZO_BUFFER_SIZE);
2403
2404 /* Buffer size is from the LZO docs plus the chunk header size. */
2405 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2406 uint32_t tmp[2];
2407 uint32_t size;
2408 lzo_uint len = ssize;
2409
2410 /* Read header*/
2411 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2412
2413 /* Check if size is bad */
2414 ((uint32_t*)out)[0] = size = tmp[1];
2415
2416 if (_sl_version != SL_MIN_VERSION) {
2417 tmp[0] = TO_BE32(tmp[0]);
2418 size = TO_BE32(size);
2419 }
2420
2421 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2422
2423 /* Read block */
2424 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2425
2426 /* Verify checksum */
2427 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2428
2429 /* Decompress */
2430 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2431 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2432 return len;
2433 }
2434};
2435
2442 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2443 {
2444 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2445 }
2446
2447 void Write(uint8_t *buf, size_t size) override
2448 {
2449 const lzo_bytep in = buf;
2450 /* Buffer size is from the LZO docs plus the chunk header size. */
2451 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2452 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2453 lzo_uint outlen;
2454
2455 do {
2456 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2457 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
2458 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2459 ((uint32_t*)out)[1] = TO_BE32((uint32_t)outlen);
2460 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2461 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2462
2463 /* Move to next data chunk. */
2464 size -= len;
2465 in += len;
2466 } while (size > 0);
2467 }
2468};
2469
2470#endif /* WITH_LZO */
2471
2472/*********************************************
2473 ******** START OF NOCOMP CODE (uncompressed)*
2474 *********************************************/
2475
2482 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2483 {
2484 }
2485
2486 size_t Read(uint8_t *buf, size_t size) override
2487 {
2488 return this->chain->Read(buf, size);
2489 }
2490};
2491
2498 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2499 {
2500 }
2501
2502 void Write(uint8_t *buf, size_t size) override
2503 {
2504 this->chain->Write(buf, size);
2505 }
2506};
2507
2508/********************************************
2509 ********** START OF ZLIB CODE **************
2510 ********************************************/
2511
2512#if defined(WITH_ZLIB)
2513#include <zlib.h>
2514
2517 z_stream z;
2519
2524 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2525 {
2526 memset(&this->z, 0, sizeof(this->z));
2527 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2528 }
2529
2532 {
2533 inflateEnd(&this->z);
2534 }
2535
2536 size_t Read(uint8_t *buf, size_t size) override
2537 {
2538 this->z.next_out = buf;
2539 this->z.avail_out = (uint)size;
2540
2541 do {
2542 /* read more bytes from the file? */
2543 if (this->z.avail_in == 0) {
2544 this->z.next_in = this->fread_buf;
2545 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2546 }
2547
2548 /* inflate the data */
2549 int r = inflate(&this->z, 0);
2550 if (r == Z_STREAM_END) break;
2551
2552 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2553 } while (this->z.avail_out != 0);
2554
2555 return size - this->z.avail_out;
2556 }
2557};
2558
2561 z_stream z;
2563
2569 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain))
2570 {
2571 memset(&this->z, 0, sizeof(this->z));
2572 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2573 }
2574
2577 {
2578 deflateEnd(&this->z);
2579 }
2580
2587 void WriteLoop(uint8_t *p, size_t len, int mode)
2588 {
2589 uint n;
2590 this->z.next_in = p;
2591 this->z.avail_in = (uInt)len;
2592 do {
2593 this->z.next_out = this->fwrite_buf;
2594 this->z.avail_out = sizeof(this->fwrite_buf);
2595
2603 int r = deflate(&this->z, mode);
2604
2605 /* bytes were emitted? */
2606 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2607 this->chain->Write(this->fwrite_buf, n);
2608 }
2609 if (r == Z_STREAM_END) break;
2610
2611 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2612 } while (this->z.avail_in || !this->z.avail_out);
2613 }
2614
2615 void Write(uint8_t *buf, size_t size) override
2616 {
2617 this->WriteLoop(buf, size, 0);
2618 }
2619
2620 void Finish() override
2621 {
2622 this->WriteLoop(nullptr, 0, Z_FINISH);
2623 this->chain->Finish();
2624 }
2625};
2626
2627#endif /* WITH_ZLIB */
2628
2629/********************************************
2630 ********** START OF LZMA CODE **************
2631 ********************************************/
2632
2633#if defined(WITH_LIBLZMA)
2634#include <lzma.h>
2635
2642static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2643
2646 lzma_stream lzma;
2648
2653 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain)), lzma(_lzma_init)
2654 {
2655 /* Allow saves up to 256 MB uncompressed */
2656 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2657 }
2658
2661 {
2662 lzma_end(&this->lzma);
2663 }
2664
2665 size_t Read(uint8_t *buf, size_t size) override
2666 {
2667 this->lzma.next_out = buf;
2668 this->lzma.avail_out = size;
2669
2670 do {
2671 /* read more bytes from the file? */
2672 if (this->lzma.avail_in == 0) {
2673 this->lzma.next_in = this->fread_buf;
2674 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2675 }
2676
2677 /* inflate the data */
2678 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2679 if (r == LZMA_STREAM_END) break;
2680 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2681 } while (this->lzma.avail_out != 0);
2682
2683 return size - this->lzma.avail_out;
2684 }
2685};
2686
2689 lzma_stream lzma;
2691
2697 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init)
2698 {
2699 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2700 }
2701
2704 {
2705 lzma_end(&this->lzma);
2706 }
2707
2714 void WriteLoop(uint8_t *p, size_t len, lzma_action action)
2715 {
2716 size_t n;
2717 this->lzma.next_in = p;
2718 this->lzma.avail_in = len;
2719 do {
2720 this->lzma.next_out = this->fwrite_buf;
2721 this->lzma.avail_out = sizeof(this->fwrite_buf);
2722
2723 lzma_ret r = lzma_code(&this->lzma, action);
2724
2725 /* bytes were emitted? */
2726 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2727 this->chain->Write(this->fwrite_buf, n);
2728 }
2729 if (r == LZMA_STREAM_END) break;
2730 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2731 } while (this->lzma.avail_in || !this->lzma.avail_out);
2732 }
2733
2734 void Write(uint8_t *buf, size_t size) override
2735 {
2736 this->WriteLoop(buf, size, LZMA_RUN);
2737 }
2738
2739 void Finish() override
2740 {
2741 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2742 this->chain->Finish();
2743 }
2744};
2745
2746#endif /* WITH_LIBLZMA */
2747
2748/*******************************************
2749 ************* END OF CODE *****************
2750 *******************************************/
2751
2754 const char *name;
2755 uint32_t tag;
2756
2757 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2758 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2759
2763};
2764
2765static const uint32_t SAVEGAME_TAG_LZO = TO_BE32('OTTD');
2766static const uint32_t SAVEGAME_TAG_NONE = TO_BE32('OTTN');
2767static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32('OTTZ');
2768static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32('OTTX');
2769
2772#if defined(WITH_LZO)
2773 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2774 {"lzo", SAVEGAME_TAG_LZO, CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
2775#else
2776 {"lzo", SAVEGAME_TAG_LZO, nullptr, nullptr, 0, 0, 0},
2777#endif
2778 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2779 {"none", SAVEGAME_TAG_NONE, CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
2780#if defined(WITH_ZLIB)
2781 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2782 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2783 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2784 {"zlib", SAVEGAME_TAG_ZLIB, CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
2785#else
2786 {"zlib", SAVEGAME_TAG_ZLIB, nullptr, nullptr, 0, 0, 0},
2787#endif
2788#if defined(WITH_LIBLZMA)
2789 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2790 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2791 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2792 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2793 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2794 {"lzma", SAVEGAME_TAG_LZMA, CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
2795#else
2796 {"lzma", SAVEGAME_TAG_LZMA, nullptr, nullptr, 0, 0, 0},
2797#endif
2798};
2799
2806static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(const std::string &full_name)
2807{
2808 /* Find default savegame format, the highest one with which files can be written. */
2809 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2810 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2811
2812 const SaveLoadFormat &def = *it;
2813
2814 if (!full_name.empty()) {
2815 /* Get the ":..." of the compression level out of the way */
2816 size_t separator = full_name.find(':');
2817 bool has_comp_level = separator != std::string::npos;
2818 const std::string name(full_name, 0, has_comp_level ? separator : full_name.size());
2819
2820 for (const auto &slf : _saveload_formats) {
2821 if (slf.init_write != nullptr && name.compare(slf.name) == 0) {
2822 if (has_comp_level) {
2823 const std::string complevel(full_name, separator + 1);
2824
2825 /* Get the level and determine whether all went fine. */
2826 size_t processed;
2827 long level = std::stol(complevel, &processed, 10);
2828 if (processed == 0 || level != Clamp(level, slf.min_compression, slf.max_compression)) {
2830 GetEncodedString(STR_CONFIG_ERROR),
2831 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2832 WL_CRITICAL);
2833 } else {
2834 return {slf, ClampTo<uint8_t>(level)};
2835 }
2836 }
2837 return {slf, slf.default_compression};
2838 }
2839 }
2840
2842 GetEncodedString(STR_CONFIG_ERROR),
2843 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2844 WL_CRITICAL);
2845 }
2846 return {def, def.default_compression};
2847}
2848
2849/* actual loader/saver function */
2850void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2851extern bool AfterLoadGame();
2852extern bool LoadOldSaveGame(const std::string &file);
2853
2859static void ResetSettings()
2860{
2861 for (auto &desc : GetSaveLoadSettingTable()) {
2862 const SettingDesc *sd = GetSettingDesc(desc);
2863 if (sd->flags.Test(SettingFlag::NotInSave)) continue;
2865
2867 }
2868}
2869
2874{
2875 ResetTempEngineData();
2876 ClearRailTypeLabelList();
2877 ClearRoadTypeLabelList();
2878 ResetOldWaypoints();
2879 ResetSettings();
2880}
2881
2885static inline void ClearSaveLoadState()
2886{
2887 _sl.dumper = nullptr;
2888 _sl.sf = nullptr;
2889 _sl.reader = nullptr;
2890 _sl.lf = nullptr;
2891}
2892
2894static void SaveFileStart()
2895{
2896 SetMouseCursorBusy(true);
2897
2899 _sl.saveinprogress = true;
2900}
2901
2903static void SaveFileDone()
2904{
2905 SetMouseCursorBusy(false);
2906
2908 _sl.saveinprogress = false;
2909
2910#ifdef __EMSCRIPTEN__
2911 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
2912#endif
2913}
2914
2917{
2918 _sl.error_str = str;
2919}
2920
2923{
2924 return GetEncodedString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
2925}
2926
2932
2939
2945{
2946 try {
2947 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
2948
2949 /* We have written our stuff to memory, now write it to file! */
2950 uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
2951 _sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
2952
2953 _sl.sf = fmt.init_write(_sl.sf, compression);
2954 _sl.dumper->Flush(_sl.sf);
2955
2957
2958 if (threaded) SetAsyncSaveFinish(SaveFileDone);
2959
2960 return SL_OK;
2961 } catch (...) {
2963
2965
2966 /* We don't want to shout when saving is just
2967 * cancelled due to a client disconnecting. */
2968 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
2969 /* Skip the "colour" character */
2970 Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
2971 asfp = SaveFileError;
2972 }
2973
2974 if (threaded) {
2975 SetAsyncSaveFinish(asfp);
2976 } else {
2977 asfp();
2978 }
2979 return SL_ERROR;
2980 }
2981}
2982
2983void WaitTillSaved()
2984{
2985 if (!_save_thread.joinable()) return;
2986
2987 _save_thread.join();
2988
2989 /* Make sure every other state is handled properly as well. */
2991}
2992
3001static SaveOrLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
3002{
3003 assert(!_sl.saveinprogress);
3004
3005 _sl.dumper = std::make_unique<MemoryDumper>();
3006 _sl.sf = std::move(writer);
3007
3009
3010 SaveViewportBeforeSaveGame();
3011 SlSaveChunks();
3012
3013 SaveFileStart();
3014
3015 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3016 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3017
3018 SaveOrLoadResult result = SaveFileToDisk(false);
3019 SaveFileDone();
3020
3021 return result;
3022 }
3023
3024 return SL_OK;
3025}
3026
3033SaveOrLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3034{
3035 try {
3037 return DoSave(std::move(writer), threaded);
3038 } catch (...) {
3040 return SL_ERROR;
3041 }
3042}
3043
3052static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
3053{
3054 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3055 if (fmt != std::end(_saveload_formats)) {
3056 /* Check version number */
3057 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3058 /* Minor is not used anymore from version 18.0, but it is still needed
3059 * in versions before that (4 cases) which can't be removed easy.
3060 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3061 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3062
3063 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3064
3065 /* Is the version higher than the current? */
3066 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3067 if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
3068 return fmt;
3069 }
3070
3071 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3072 _sl.lf->Reset();
3075
3076 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3077 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3078 if (fmt == std::end(_saveload_formats)) {
3079 /* Who removed the LZO savegame format definition? When built without LZO support,
3080 * the formats must still list it just without a method to read the file.
3081 * The caller of this function has to check for the existence of load function. */
3082 NOT_REACHED();
3083 }
3084 return fmt;
3085}
3086
3093static SaveOrLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3094{
3095 _sl.lf = std::move(reader);
3096
3097 if (load_check) {
3098 /* Clear previous check data */
3100 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3102 }
3103
3104 uint32_t hdr[2];
3105 if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3106
3107 /* see if we have any loader for this type. */
3108 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
3109
3110 /* loader for this savegame type is not implemented? */
3111 if (fmt->init_load == nullptr) {
3112 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3113 }
3114
3115 _sl.lf = fmt->init_load(_sl.lf);
3116 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3117 _next_offs = 0;
3118
3119 if (!load_check) {
3121
3122 /* Old maps were hardcoded to 256x256 and thus did not contain
3123 * any mapsize information. Pre-initialize to 256x256 to not to
3124 * confuse old games */
3125 InitializeGame(256, 256, true, true);
3126
3127 _gamelog.Reset();
3128
3130 /*
3131 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3132 * shared savegame version 4. Anything before that 'obviously'
3133 * does not have any NewGRFs. Between the introduction and
3134 * savegame version 41 (just before 0.5) the NewGRF settings
3135 * were not stored in the savegame and they were loaded by
3136 * using the settings from the main menu.
3137 * So, to recap:
3138 * - savegame version < 4: do not load any NewGRFs.
3139 * - savegame version >= 41: load NewGRFs from savegame, which is
3140 * already done at this stage by
3141 * overwriting the main menu settings.
3142 * - other savegame versions: use main menu settings.
3143 *
3144 * This means that users *can* crash savegame version 4..40
3145 * savegames if they set incompatible NewGRFs in the main menu,
3146 * but can't crash anymore for savegame version < 4 savegames.
3147 *
3148 * Note: this is done here because AfterLoadGame is also called
3149 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3150 */
3152 }
3153 }
3154
3155 if (load_check) {
3156 /* Load chunks into _load_check_data.
3157 * No pools are loaded. References are not possible, and thus do not need resolving. */
3159 } else {
3160 /* Load chunks and resolve references */
3161 SlLoadChunks();
3162 SlFixPointers();
3163 }
3164
3166
3168
3169 if (load_check) {
3170 /* The only part from AfterLoadGame() we need */
3172 } else {
3174
3175 /* After loading fix up savegame for any internal changes that
3176 * might have occurred since then. If it fails, load back the old game. */
3177 if (!AfterLoadGame()) {
3179 return SL_REINIT;
3180 }
3181
3183 }
3184
3185 return SL_OK;
3186}
3187
3193SaveOrLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3194{
3195 try {
3197 return DoLoad(std::move(reader), false);
3198 } catch (...) {
3200 return SL_REINIT;
3201 }
3202}
3203
3213SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3214{
3215 /* An instance of saving is already active, so don't go saving again */
3216 if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) {
3217 /* if not an autosave, but a user action, show error message */
3218 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WL_ERROR);
3219 return SL_OK;
3220 }
3221 WaitTillSaved();
3222
3223 try {
3224 /* Load a TTDLX or TTDPatch game */
3225 if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) {
3227
3228 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3229
3230 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3231 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3232 * Note: this is done here because AfterLoadGame is also called
3233 * for OTTD savegames which have their own NewGRF logic. */
3235 _gamelog.Reset();
3236 if (!LoadOldSaveGame(filename)) return SL_REINIT;
3240 if (!AfterLoadGame()) {
3242 return SL_REINIT;
3243 }
3245 return SL_OK;
3246 }
3247
3248 assert(dft == DFT_GAME_FILE);
3249 switch (fop) {
3250 case SLO_CHECK:
3252 break;
3253
3254 case SLO_LOAD:
3256 break;
3257
3258 case SLO_SAVE:
3260 break;
3261
3262 default: NOT_REACHED();
3263 }
3264
3265 auto fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3266
3267 /* Make it a little easier to load savegames from the console */
3268 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
3269 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
3270 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
3271
3272 if (!fh.has_value()) {
3273 SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3274 }
3275
3276 if (fop == SLO_SAVE) { // SAVE game
3277 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3278 if (!_settings_client.gui.threaded_saves) threaded = false;
3279
3280 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3281 }
3282
3283 /* LOAD game */
3284 assert(fop == SLO_LOAD || fop == SLO_CHECK);
3285 Debug(desync, 1, "load: {}", filename);
3286 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SLO_CHECK);
3287 } catch (...) {
3288 /* This code may be executed both for old and new save games. */
3290
3291 /* Skip the "colour" character */
3292 if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
3293
3294 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3295 return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;
3296 }
3297}
3298
3305{
3306 std::string filename;
3307
3309 filename = GenerateDefaultSaveName() + counter.Extension();
3310 } else {
3311 filename = counter.Filename();
3312 }
3313
3314 Debug(sl, 2, "Autosaving to '{}'", filename);
3315 if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) {
3316 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WL_ERROR);
3317 }
3318}
3319
3320
3323{
3325}
3326
3331{
3332 /* Check if we have a name for this map, which is the name of the first
3333 * available company. When there's no company available we'll use
3334 * 'Spectator' as "company" name. */
3336 if (!Company::IsValidID(cid)) {
3337 for (const Company *c : Company::Iterate()) {
3338 cid = c->index;
3339 break;
3340 }
3341 }
3342
3343 std::array<StringParameter, 4> params{};
3344 auto it = params.begin();
3345 *it++ = cid;
3346
3347 /* We show the current game time differently depending on the timekeeping units used by this game. */
3349 /* Insert time played. */
3350 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3351 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3352 *it++ = play_time / 60 / 60;
3353 *it++ = (play_time / 60) % 60;
3354 } else {
3355 /* Insert current date */
3357 case 0: *it++ = STR_JUST_DATE_LONG; break;
3358 case 1: *it++ = STR_JUST_DATE_TINY; break;
3359 case 2: *it++ = STR_JUST_DATE_ISO; break;
3360 default: NOT_REACHED();
3361 }
3362 *it++ = TimerGameEconomy::date;
3363 }
3364
3365 /* Get the correct string (special string for when there's not company) */
3366 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3367 SanitizeFilename(filename);
3368 return filename;
3369}
3370
3379
3387{
3388 if (aft == FT_INVALID || aft == FT_NONE) {
3389 this->file_op = SLO_INVALID;
3390 this->detail_ftype = DFT_INVALID;
3391 this->abstract_ftype = FT_INVALID;
3392 return;
3393 }
3394
3395 this->file_op = fop;
3396 this->detail_ftype = dft;
3397 this->abstract_ftype = aft;
3398}
3399
3405{
3406 this->SetMode(item.type);
3407 this->name = item.name;
3408 this->title = item.title;
3409}
3410
3412{
3413 assert(this->load_description.has_value());
3414 return *this->load_description;
3415}
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
void PutUtf8(char32_t c)
Append UTF.8 char.
Container for an encoded string, created by GetEncodedString.
Enum-as-bit-set wrapper.
void StartAction(GamelogActionType at)
Stores information about new action, but doesn't allocate it Action is allocated only when there is a...
Definition gamelog.cpp:65
void Reset()
Resets and frees all memory allocated - used before loading or starting a new game.
Definition gamelog.cpp:94
void StopAction()
Stops logging of any changes.
Definition gamelog.cpp:74
Class for calculation jobs to be run on link graphs.
A connected component of a link graph.
Definition linkgraph.h:37
Handler for saving/loading an object to/from disk.
Definition saveload.h:526
SaveLoadTable GetLoadDescription() const
Get the description for how to load the chunk.
Handler that is assigned when there is a struct read in the savegame which is not known to the code.
virtual SaveLoadTable GetDescription() const override
Get the description of the fields in the savegame.
virtual SaveLoadCompatTable GetCompatDescription() const override
Get the pre-header description of the fields in the savegame.
void LoadCheck(void *object) const override
Similar to load, but used only to validate savegames.
void Load(void *object) const override
Load the object from disk.
void Save(void *) const override
Save the object to disk.
Template class to help with list-like types.
static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd=SL_VAR)
Internal templated helper to save/load a list-like type.
static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd=SL_VAR)
Internal templated helper to return the size in bytes of a list-like type.
Compose data into a growing std::string.
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.
@ SCC_ENCODED
Encoded string marker and sub-string parameter.
@ SCC_ENCODED_NUMERIC
Encoded numeric parameter.
@ SCC_ENCODED_STRING
Encoded string parameter.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
void ShowErrorMessage(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:1002
std::optional< FileHandle > FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:242
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:53
@ SLO_CHECK
Load file for checking and/or preview.
Definition fileio_type.h:54
@ SLO_SAVE
File is being saved.
Definition fileio_type.h:56
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:55
@ SLO_INVALID
Unknown file operation.
Definition fileio_type.h:58
DetailedFileType GetDetailedFileType(FiosType fios_type)
Extract the detailed file type from a FiosType.
AbstractFileType GetAbstractFileType(FiosType fios_type)
Extract the abstract file type from a FiosType.
Definition fileio_type.h:97
FiosType
Elements of a file system that are recognized.
Definition fileio_type.h:73
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:29
@ DFT_GAME_FILE
Save game or scenario file.
Definition fileio_type.h:32
@ DFT_INVALID
Unknown or invalid file.
Definition fileio_type.h:49
@ DFT_OLD_GAME_FILE
Old save game or scenario file.
Definition fileio_type.h:31
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ SCENARIO_DIR
Base directory for all scenarios.
@ BASE_DIR
Base directory for all subdirectories.
@ SAVE_DIR
Base directory for all savegames.
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
AbstractFileType
The different abstract types of files that the system knows about.
Definition fileio_type.h:16
@ FT_NONE
nothing to do
Definition fileio_type.h:17
@ FT_INVALID
Invalid or unknown file type.
Definition fileio_type.h:23
LoadCheckData _load_check_data
Data loaded from save during SL_LOAD_CHECK.
Definition fios_gui.cpp:41
fluid_settings_t * settings
FluidSynth settings handle.
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
@ GLAT_LOAD
Game loaded.
Definition gamelog.h:18
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1641
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:51
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _networking
are we in networking mode?
Definition network.cpp:66
bool _network_server
network-server is active
Definition network.cpp:67
GRFConfigList _grfconfig
First item in list of current GRF set up.
GRFListCompatibility IsGoodGRFConfigList(GRFConfigList &grfconfig)
Check if all GRFs in the GRF config from a savegame can be loaded.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
static void SlRefVector(void *vector, VarType conv)
Save/Load a vector.
static const uint LZO_BUFFER_SIZE
Buffer size for the LZO compressor.
void SlError(StringID string, const std::string &extra_msg)
Error handler.
Definition saveload.cpp:326
static const ChunkHandler * SlFindChunkHandler(uint32_t id)
Find the ChunkHandler that will be used for processing the found chunk in the savegame or in memory.
static uint8_t GetSavegameFileType(const SaveLoad &sld)
Return the type as saved/loaded inside the savegame.
Definition saveload.cpp:557
static SaveOrLoadResult DoSave(std::shared_ptr< SaveFilter > writer, bool threaded)
Actually perform the saving of the savegame.
void ProcessAsyncSaveFinish()
Handle async save finishes.
Definition saveload.cpp:381
static const lzma_stream _lzma_init
Have a copy of an initialised LZMA stream.
static void * IntToReference(size_t index, SLRefType rt)
Pointers cannot be loaded from a savegame, so this function gets the index from the savegame and retu...
uint32_t _ttdp_version
version of TTDP savegame (if applicable)
Definition saveload.cpp:67
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:70
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:68
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:207
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:69
static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
Return the size in bytes of a vector.
SaveOrLoadResult LoadWithFilter(std::shared_ptr< LoadFilter > reader)
Load the game using a (reader) filter.
static SaveOrLoadResult SaveFileToDisk(bool threaded)
We have written the whole game into memory, _memory_savegame, now find and appropriate compressor and...
static void ResetSaveloadData()
Clear temporary data that is passed between various saveload phases.
static size_t ReferenceToInt(const void *obj, SLRefType rt)
Pointers cannot be saved to a savegame, so this functions gets the index of the item,...
SaveOrLoadResult SaveWithFilter(std::shared_ptr< SaveFilter > writer, bool threaded)
Save the game using a (writer) filter.
static void SlWriteSimpleGamma(size_t i)
Write the header descriptor of an object or an array.
Definition saveload.cpp:499
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:71
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:363
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:364
static uint SlCalcConvMemLen(VarType conv)
Return the size in bytes of a certain type of normal/atomic variable as it appears in memory.
Definition saveload.cpp:593
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:406
size_t SlGetStructListLength(size_t limit)
Get the length of this list; if it exceeds the limit, error out.
static size_t SlCalcArrayLen(size_t length, VarType conv)
Return the size in bytes of a certain type of atomic array.
void WriteValue(void *ptr, VarType conv, int64_t val)
Write the value of a setting.
Definition saveload.cpp:823
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:362
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:664
SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
Main Save or Load function where the high-level saveload functions are handled.
static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
Called by save thread to tell we finished saving.
Definition saveload.cpp:370
void SetSaveLoadError(StringID str)
Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friend...
void SlCopy(void *object, size_t length, VarType conv)
Copy a list of SL_VARs to/from a savegame.
static std::pair< const SaveLoadFormat &, uint8_t > GetSavegameFormat(const std::string &full_name)
Return the savegameformat of the game.
size_t SlGetFieldLength()
Get the length of the current object.
Definition saveload.cpp:787
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:647
NeedLength
Definition saveload.cpp:82
@ NL_WANTLENGTH
writing length and data
Definition saveload.cpp:84
@ NL_NONE
not working in NeedLength mode
Definition saveload.cpp:83
@ NL_CALCLENGTH
need to calculate the length
Definition saveload.cpp:85
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:301
static void SlStdString(void *ptr, VarType conv)
Save/Load a std::string.
Definition saveload.cpp:999
static SaveOrLoadResult DoLoad(std::shared_ptr< LoadFilter > reader, bool load_check)
Actually perform the loading of a "non-old" savegame.
static size_t SlCalcRefListLen(const void *list, VarType conv)
Return the size in bytes of a list.
static bool SlIsObjectValidInSavegame(const SaveLoad &sld)
Are we going to save this object or not?
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:356
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:706
static void SlLoadChunk(const ChunkHandler &ch)
Load a chunk of data (eg vehicles, stations, etc.)
static void SlLoadCheckChunks()
Load all chunks for savegame checking.
static size_t SlCalcStdStringLen(const void *ptr)
Calculate the gross length of the string that it will occupy in the savegame.
Definition saveload.cpp:904
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:457
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:74
@ SLA_LOAD
loading
Definition saveload.cpp:75
@ SLA_NULL
null all pointers (on loading error)
Definition saveload.cpp:78
@ SLA_SAVE
saving
Definition saveload.cpp:76
@ SLA_LOAD_CHECK
partial loading into _load_check_data
Definition saveload.cpp:79
@ SLA_PTRS
fixing pointers
Definition saveload.cpp:77
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:770
static void SlCopyInternal(void *object, size_t length, VarType conv)
Internal function to save/Load a list of SL_VARs.
static void SlArray(void *array, size_t length, VarType conv)
Save/Load the length of the array followed by the array of SL_VAR elements.
SavegameType _savegame_type
type of savegame we are loading
Definition saveload.cpp:64
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:849
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:65
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:718
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:397
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:205
void DoExitSave()
Do a save when exiting the game (_settings_client.gui.autosave_on_exit)
static void SlLoadChunks()
Load all chunks.
static void SlDeque(void *deque, VarType conv)
Save/load a std::deque.
static uint8_t SlCalcConvFileLen(VarType conv)
Return the size in bytes of a certain type of normal/atomic variable as it appears in a saved game.
Definition saveload.cpp:623
static void SlSaveChunk(const ChunkHandler &ch)
Save a chunk of data (eg.
const SaveLoadVersion SAVEGAME_VERSION
Current savegame version of OpenTTD.
size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
Calculate the size of an object.
void SlObject(void *object, const SaveLoadTable &slt)
Main SaveLoad function.
EncodedString GetSaveLoadErrorMessage()
Return the description of the error.
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
bool AfterLoadGame()
Perform a (large) amount of savegame conversion magic in order to load older savegames and to fill th...
int64_t ReadValue(const void *ptr, VarType conv)
Return a signed-long version of the value of a setting.
Definition saveload.cpp:799
static void SlVector(void *vector, VarType conv)
Save/load a std::vector.
static void SaveFileError()
Show a gui message when saving has failed.
void SlGlobList(const SaveLoadTable &slt)
Save or Load (a list of) global variables.
void FixSCCEncoded(std::string &str, bool fix_code)
Scan the string for old values of SCC_ENCODED and fix it to it's new, value.
Definition saveload.cpp:920
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:89
static const SaveLoadFormat * DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
Determines the SaveLoadFormat that is connected to the given tag.
void SlAutolength(AutolengthProc *proc, int arg)
Do something of which I have no idea what it is :P.
void SlReadString(std::string &str, size_t length)
Read the given amount of bytes from the buffer into the string.
Definition saveload.cpp:988
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:524
static size_t SlCalcVectorLen(const void *vector, VarType conv)
Return the size in bytes of a std::vector.
static void SlRefList(void *list, VarType conv)
Save/Load a list.
static size_t SlCalcDequeLen(const void *deque, VarType conv)
Return the size in bytes of a std::deque.
SavegameType
Types of save games.
Definition saveload.h:428
@ SGT_OTTD
OTTD savegame.
Definition saveload.h:432
SaveOrLoadResult
Save or load result codes.
Definition saveload.h:408
@ SL_OK
completed successfully
Definition saveload.h:409
@ SL_REINIT
error that was caught in the middle of updating game state, need to clear it. (can only happen during...
Definition saveload.h:411
@ SLE_VAR_NULL
useful to write zeros in savegame.
Definition saveload.h:658
@ SLE_FILE_END
Used to mark end-of-header in tables.
Definition saveload.h:631
@ SLE_FILE_TYPE_MASK
Mask to get the file-type (and not any flags).
Definition saveload.h:645
@ SLE_FILE_HAS_LENGTH_FIELD
Bit stored in savegame to indicate field has a length field for each entry.
Definition saveload.h:646
@ SLF_ALLOW_NEWLINE
Allow new lines in the strings.
Definition saveload.h:694
@ SLF_ALLOW_CONTROL
Allow control codes in the strings.
Definition saveload.h:693
@ SLE_VAR_STR
string pointer
Definition saveload.h:659
@ SLE_VAR_NAME
old custom name to be converted to a char pointer
Definition saveload.h:661
@ SLE_VAR_STRQ
string pointer enclosed in quotes
Definition saveload.h:660
@ SLE_FILE_STRINGID
StringID offset into strings-array.
Definition saveload.h:640
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:1352
constexpr VarType GetVarFileType(VarType type)
Get the FileType of a setting.
Definition saveload.h:767
SLRefType
Type of reference (SLE_REF, SLE_CONDREF).
Definition saveload.h:605
@ REF_VEHICLE_OLD
Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
Definition saveload.h:610
@ REF_LINK_GRAPH_JOB
Load/save a reference to a link graph job.
Definition saveload.h:617
@ REF_TOWN
Load/save a reference to a town.
Definition saveload.h:609
@ REF_LINK_GRAPH
Load/save a reference to a link graph.
Definition saveload.h:616
@ REF_CARGO_PACKET
Load/save a reference to a cargo packet.
Definition saveload.h:613
@ REF_ENGINE_RENEWS
Load/save a reference to an engine renewal (autoreplace).
Definition saveload.h:612
@ REF_STATION
Load/save a reference to a station.
Definition saveload.h:608
@ REF_ORDER
Load/save a reference to an order.
Definition saveload.h:606
@ REF_ORDERLIST
Load/save a reference to an orderlist.
Definition saveload.h:614
@ REF_STORAGE
Load/save a reference to a persistent storage.
Definition saveload.h:615
@ REF_VEHICLE
Load/save a reference to a vehicle.
Definition saveload.h:607
@ REF_ROADSTOPS
Load/save a reference to a bus/truck stop.
Definition saveload.h:611
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1306
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:517
SaveLoadType
Type of data saved.
Definition saveload.h:700
@ SL_NULL
Save null-bytes and load to nowhere.
Definition saveload.h:714
@ SL_STRUCTLIST
Save/load a list of structs.
Definition saveload.h:711
@ SL_STDSTR
Save/load a std::string.
Definition saveload.h:705
@ SL_REF
Save/load a reference.
Definition saveload.h:702
@ SL_SAVEBYTE
Save (but not load) a byte.
Definition saveload.h:713
@ SL_DEQUE
Save/load a deque of SL_VAR elements.
Definition saveload.h:708
@ SL_STRUCT
Save/load a struct.
Definition saveload.h:703
@ SL_VECTOR
Save/load a vector of SL_VAR elements.
Definition saveload.h:709
@ SL_REFVECTOR
Save/load a vector of SL_REF elements.
Definition saveload.h:716
@ SL_REFLIST
Save/load a list of SL_REF elements.
Definition saveload.h:710
@ SL_ARR
Save/load a fixed-size array of SL_VAR elements.
Definition saveload.h:707
@ SL_VAR
Save/load a variable.
Definition saveload.h:701
std::span< const struct SaveLoadCompat > SaveLoadCompatTable
A table of SaveLoadCompat entries.
Definition saveload.h:523
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1268
constexpr VarType GetVarMemType(VarType type)
Get the NumberType of a setting.
Definition saveload.h:756
SaveLoadVersion
SaveLoad versions Previous savegame versions, the trunk revision where they were introduced and the r...
Definition saveload.h:30
@ SLV_69
69 10319
Definition saveload.h:125
@ SLV_4
4.0 1 4.1 122 0.3.3, 0.3.4 4.2 1222 0.3.5 4.3 1417 4.4 1426
Definition saveload.h:37
@ SLV_SAVELOAD_LIST_LENGTH
293 PR#9374 Consistency in list length with SL_STRUCT / SL_STRUCTLIST / SL_DEQUE / SL_REFLIST.
Definition saveload.h:331
@ SLV_START_PATCHPACKS
220 First known patchpack to use a version just above ours.
Definition saveload.h:321
@ SL_MAX_VERSION
Highest possible saveload version.
Definition saveload.h:404
@ SL_MIN_VERSION
First savegame version.
Definition saveload.h:31
@ SLV_END_PATCHPACKS
286 Last known patchpack to use a version just above ours.
Definition saveload.h:322
@ SLV_ENCODED_STRING_FORMAT
350 PR#13499 Encoded String format changed.
Definition saveload.h:400
@ SLV_5
5.0 1429 5.1 1440 5.2 1525 0.3.6
Definition saveload.h:43
@ SLV_169
169 23816
Definition saveload.h:245
std::vector< SaveLoad > SlTableHeader(const SaveLoadTable &slt)
Save or Load a table header.
std::span< const struct SaveLoad > SaveLoadTable
A table of SaveLoad entries.
Definition saveload.h:520
@ CH_TYPE_MASK
All ChunkType values have to be within this mask.
Definition saveload.h:463
@ CH_READONLY
Chunk is never saved.
Definition saveload.h:464
Declaration of filters used for saving and loading savegames.
Declaration of functions used in more save/load files.
StringID RemapOldStringID(StringID s)
Remap a string ID from the old format to the new format.
std::string CopyFromOldName(StringID id)
Copy and convert old custom names to UTF-8.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:59
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:58
@ 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.
@ SBI_SAVELOAD_FINISH
finished saving
@ SBI_SAVELOAD_START
started saving
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
void StrMakeValidInPlace(char *str, StringValidationSettings settings)
Scans the string for invalid characters and replaces them with a question mark '?' (if not ignored).
Definition string.cpp:194
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition string.cpp:441
int8_t Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
@ ReplaceWithQuestionMark
Replace the unknown/bad bits with question marks.
@ AllowControlCode
Allow the special control codes.
@ AllowNewline
Allow newlines; replaces '\r ' with ' ' during processing.
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:91
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Container for cargo from the same location and time.
Definition cargopacket.h:41
Handlers and description of chunk.
Definition saveload.h:468
ChunkType type
Type of the chunk.
Definition saveload.h:470
virtual void LoadCheck(size_t len=0) const
Load the chunk for game preview.
virtual void Load() const =0
Load the chunk.
uint32_t id
Unique ID (4 letters).
Definition saveload.h:469
virtual void Save() const
Save the chunk.
Definition saveload.h:480
GUISettings gui
settings related to the GUI
Struct to store engine replacements.
Yes, simply reading from a file.
~FileReader()
Make sure everything is cleaned up.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
void Reset() override
Reset this filter to read from the beginning of the file.
FileReader(FileHandle &&file)
Create the file reader, so it reads from a specific file.
long begin
The begin of the file.
std::optional< FileHandle > file
The file to read from.
Deals with the type of the savegame, independent of extension.
Definition saveload.h:415
AbstractFileType abstract_ftype
Abstract type of file (scenario, heightmap, etc).
Definition saveload.h:418
void SetMode(FiosType ft)
Set the mode and file type of the file to save or load based on the type of file entry at the file sy...
DetailedFileType detail_ftype
Concrete file type (PNG, BMP, old save, etc).
Definition saveload.h:417
std::string title
Internal name of the game.
Definition saveload.h:420
SaveLoadOperation file_op
File operation to perform.
Definition saveload.h:416
std::string name
Name of the file.
Definition saveload.h:419
void Set(const FiosItem &item)
Set the title of the file.
Yes, simply writing to a file.
std::optional< FileHandle > file
The file to write to.
FileWriter(FileHandle &&file)
Create the file writer, so it writes to a specific file.
void Finish() override
Prepare everything to finish writing the savegame.
~FileWriter()
Make sure everything is cleaned up.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Deals with finding savegames.
Definition fios.h:78
A savegame name automatically numbered.
Definition fios.h:129
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
Definition fios.cpp:757
std::string Extension()
Generate an extension for a savegame name.
Definition fios.cpp:767
bool keep_all_autosave
name the autosave in a different way
uint8_t date_format_in_default_names
should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) ...
bool threaded_saves
should we do threaded saves?
std::optional< size_t > savegame_size
Size of the last saved savegame in bytes, or std::nullopt if not saved yet.
Definition openttd.h:58
Filter without any compression.
~LZMALoadFilter()
Clean everything up.
lzma_stream lzma
Stream state that we are reading from.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
LZMALoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Filter using LZMA compression.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
void Finish() override
Prepare everything to finish writing the savegame.
void WriteLoop(uint8_t *p, size_t len, lzma_action action)
Helper loop for writing the data.
LZMASaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
lzma_stream lzma
Stream state that we are writing to.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
~LZMASaveFilter()
Clean up what we allocated.
Filter using LZO compression.
LZOLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
size_t Read(uint8_t *buf, size_t ssize) override
Read a given number of bytes from the savegame.
Filter using LZO compression.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
LZOSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
bool checkable
True if the savegame could be checked by SL_LOAD_CHECK. (Old savegames are not checkable....
Definition fios.h:34
std::string error_msg
Data to pass to string parameters when displaying error.
Definition fios.h:36
StringID error
Error message from loading. INVALID_STRING_ID if no error.
Definition fios.h:35
void Clear()
Reset read data.
Definition fios_gui.cpp:49
GRFListCompatibility grf_compatibility
Summary state of NewGrfs, whether missing files or only compatible found.
Definition fios.h:48
GRFConfigList grfconfig
NewGrf configuration from save.
Definition fios.h:47
Interface for filtering a savegame till it is loaded.
std::shared_ptr< LoadFilter > chain
Chained to the (savegame) filters.
Container for dumping the savegame (quickly) to memory.
Definition saveload.cpp:133
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:135
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:142
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:134
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:136
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:176
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:157
Filter without any compression.
NoCompLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
Filter without any compression.
NoCompSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:258
Class for pooled persistent storage of data.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
A buffer for reading (and buffering) savegame data.
Definition saveload.cpp:92
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:94
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:103
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:97
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:125
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:96
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:93
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:95
A Stop for a Road Vehicle.
Interface for filtering a savegame till it is written.
std::shared_ptr< SaveFilter > chain
Chained to the (savegame) filters.
The format for a reader/writer type of a savegame.
const char * name
name of the compressor/decompressor (debug-only)
uint32_t tag
the 4-letter tag by which it is identified in the savegame
uint8_t min_compression
the minimum compression level of this format
std::shared_ptr< SaveFilter >(* init_write)(std::shared_ptr< SaveFilter > chain, uint8_t compression)
Constructor for the save filter.
uint8_t default_compression
the default compression level of this format
std::shared_ptr< LoadFilter >(* init_load)(std::shared_ptr< LoadFilter > chain)
Constructor for the load filter.
uint8_t max_compression
the maximum compression level of this format
The saveload struct, containing reader-writer functions, buffer, version, etc.
Definition saveload.cpp:183
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:196
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:194
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:193
StringID error_str
the translatable error message to show
Definition saveload.cpp:199
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:184
std::string extra_msg
the error message
Definition saveload.cpp:200
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:185
uint8_t block_mode
???
Definition saveload.cpp:186
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:202
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:197
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:191
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:189
bool error
did an error occur or not
Definition saveload.cpp:187
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:190
SaveLoad type struct.
Definition saveload.h:722
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:726
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:731
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:728
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:724
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:723
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:725
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:727
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 bool IsValidID(auto index)
Tests whether given index is a valid index for station of this type.
static Station * Get(auto index)
Gets station with given index.
Station data structure.
Town data structure.
Definition town.h:52
Vehicle data structure.
Filter using Zlib compression.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
ZlibLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
~ZlibLoadFilter()
Clean everything up.
z_stream z
Stream state we are reading from.
Filter using Zlib compression.
void WriteLoop(uint8_t *p, size_t len, int mode)
Helper loop for writing the data.
z_stream z
Stream state we are writing to.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
void Finish() override
Prepare everything to finish writing the savegame.
~ZlibSaveFilter()
Clean up what we allocated.
ZlibSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Definition thread.h:24
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3224
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:66