OpenTTD Source 20250312-master-gcdcc6b491d
saveload.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
23#include "../stdafx.h"
24#include "../debug.h"
25#include "../station_base.h"
26#include "../thread.h"
27#include "../town.h"
28#include "../network/network.h"
29#include "../window_func.h"
30#include "../strings_func.h"
31#include "../core/endian_func.hpp"
32#include "../vehicle_base.h"
33#include "../company_func.h"
34#include "../timer/timer_game_economy.h"
35#include "../autoreplace_base.h"
36#include "../roadstop_base.h"
37#include "../linkgraph/linkgraph.h"
38#include "../linkgraph/linkgraphjob.h"
39#include "../statusbar_gui.h"
40#include "../fileio_func.h"
41#include "../gamelog.h"
42#include "../string_func.h"
43#include "../fios.h"
44#include "../error.h"
45#include "../strings_type.h"
46#include "../newgrf_railtype.h"
47#include "../newgrf_roadtype.h"
48
49#include <atomic>
50#ifdef __EMSCRIPTEN__
51# include <emscripten.h>
52#endif
53
54#include "table/strings.h"
55
56#include "saveload_internal.h"
57#include "saveload_filter.h"
58
59#include "../safeguards.h"
60
62
65
66uint32_t _ttdp_version;
69std::string _savegame_format;
71
80
81enum NeedLength : uint8_t {
82 NL_NONE = 0,
85};
86
88static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
89
91struct ReadBuffer {
93 uint8_t *bufp;
94 uint8_t *bufe;
95 std::shared_ptr<LoadFilter> reader;
96 size_t read;
97
102 ReadBuffer(std::shared_ptr<LoadFilter> reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
103 {
104 }
105
106 inline uint8_t ReadByte()
107 {
108 if (this->bufp == this->bufe) {
109 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
110 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
111
112 this->read += len;
113 this->bufp = this->buf;
114 this->bufe = this->buf + len;
115 }
116
117 return *this->bufp++;
118 }
119
124 size_t GetSize() const
125 {
126 return this->read - (this->bufe - this->bufp);
127 }
128};
129
130
133 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
134 uint8_t *buf = nullptr;
135 uint8_t *bufe = nullptr;
136
141 inline void WriteByte(uint8_t b)
142 {
143 /* Are we at the end of this chunk? */
144 if (this->buf == this->bufe) {
145 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
146 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
147 }
148
149 *this->buf++ = b;
150 }
151
156 void Flush(std::shared_ptr<SaveFilter> writer)
157 {
158 uint i = 0;
159 size_t t = this->GetSize();
160
161 while (t > 0) {
162 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
163
164 writer->Write(this->blocks[i++].get(), to_write);
165 t -= to_write;
166 }
167
168 writer->Finish();
169 }
170
175 size_t GetSize() const
176 {
177 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
178 }
179};
180
185 uint8_t block_mode;
186 bool error;
187
188 size_t obj_len;
189 int array_index, last_array_index;
191
192 std::unique_ptr<MemoryDumper> dumper;
193 std::shared_ptr<SaveFilter> sf;
194
195 std::unique_ptr<ReadBuffer> reader;
196 std::shared_ptr<LoadFilter> lf;
197
199 std::string extra_msg;
200
202};
203
205
206static const std::vector<ChunkHandlerRef> &ChunkHandlers()
207{
208 /* These define the chunks */
209 extern const ChunkHandlerTable _gamelog_chunk_handlers;
210 extern const ChunkHandlerTable _map_chunk_handlers;
211 extern const ChunkHandlerTable _misc_chunk_handlers;
212 extern const ChunkHandlerTable _name_chunk_handlers;
213 extern const ChunkHandlerTable _cheat_chunk_handlers;
214 extern const ChunkHandlerTable _setting_chunk_handlers;
215 extern const ChunkHandlerTable _company_chunk_handlers;
216 extern const ChunkHandlerTable _engine_chunk_handlers;
217 extern const ChunkHandlerTable _veh_chunk_handlers;
218 extern const ChunkHandlerTable _waypoint_chunk_handlers;
219 extern const ChunkHandlerTable _depot_chunk_handlers;
220 extern const ChunkHandlerTable _order_chunk_handlers;
221 extern const ChunkHandlerTable _town_chunk_handlers;
222 extern const ChunkHandlerTable _sign_chunk_handlers;
223 extern const ChunkHandlerTable _station_chunk_handlers;
224 extern const ChunkHandlerTable _industry_chunk_handlers;
225 extern const ChunkHandlerTable _economy_chunk_handlers;
226 extern const ChunkHandlerTable _subsidy_chunk_handlers;
227 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
228 extern const ChunkHandlerTable _goal_chunk_handlers;
229 extern const ChunkHandlerTable _story_page_chunk_handlers;
230 extern const ChunkHandlerTable _league_chunk_handlers;
231 extern const ChunkHandlerTable _ai_chunk_handlers;
232 extern const ChunkHandlerTable _game_chunk_handlers;
233 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
234 extern const ChunkHandlerTable _newgrf_chunk_handlers;
235 extern const ChunkHandlerTable _group_chunk_handlers;
236 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
237 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
238 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
239 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
240 extern const ChunkHandlerTable _airport_chunk_handlers;
241 extern const ChunkHandlerTable _object_chunk_handlers;
242 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
243 extern const ChunkHandlerTable _water_region_chunk_handlers;
244 extern const ChunkHandlerTable _randomizer_chunk_handlers;
245
247 static const ChunkHandlerTable _chunk_handler_tables[] = {
248 _gamelog_chunk_handlers,
249 _map_chunk_handlers,
250 _misc_chunk_handlers,
251 _name_chunk_handlers,
252 _cheat_chunk_handlers,
253 _setting_chunk_handlers,
254 _veh_chunk_handlers,
255 _waypoint_chunk_handlers,
256 _depot_chunk_handlers,
257 _order_chunk_handlers,
258 _industry_chunk_handlers,
259 _economy_chunk_handlers,
260 _subsidy_chunk_handlers,
261 _cargomonitor_chunk_handlers,
262 _goal_chunk_handlers,
263 _story_page_chunk_handlers,
264 _league_chunk_handlers,
265 _engine_chunk_handlers,
266 _town_chunk_handlers,
267 _sign_chunk_handlers,
268 _station_chunk_handlers,
269 _company_chunk_handlers,
270 _ai_chunk_handlers,
271 _game_chunk_handlers,
272 _animated_tile_chunk_handlers,
273 _newgrf_chunk_handlers,
274 _group_chunk_handlers,
275 _cargopacket_chunk_handlers,
276 _autoreplace_chunk_handlers,
277 _labelmaps_chunk_handlers,
278 _linkgraph_chunk_handlers,
279 _airport_chunk_handlers,
280 _object_chunk_handlers,
281 _persistent_storage_chunk_handlers,
282 _water_region_chunk_handlers,
283 _randomizer_chunk_handlers,
284 };
285
286 static std::vector<ChunkHandlerRef> _chunk_handlers;
287
288 if (_chunk_handlers.empty()) {
289 for (auto &chunk_handler_table : _chunk_handler_tables) {
290 for (auto &chunk_handler : chunk_handler_table) {
291 _chunk_handlers.push_back(chunk_handler);
292 }
293 }
294 }
295
296 return _chunk_handlers;
297}
298
300static void SlNullPointers()
301{
303
304 /* We don't want any savegame conversion code to run
305 * during NULLing; especially those that try to get
306 * pointers from other pools. */
308
309 for (const ChunkHandler &ch : ChunkHandlers()) {
310 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
311 ch.FixPointers();
312 }
313
314 assert(_sl.action == SLA_NULL);
315}
316
325[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
326{
327 /* Distinguish between loading into _load_check_data vs. normal save/load. */
328 if (_sl.action == SLA_LOAD_CHECK) {
329 _load_check_data.error = string;
330 _load_check_data.error_msg = extra_msg;
331 } else {
332 _sl.error_str = string;
333 _sl.extra_msg = extra_msg;
334 }
335
336 /* We have to nullptr all pointers here; we might be in a state where
337 * the pointers are actually filled with indices, which means that
338 * when we access them during cleaning the pool dereferences of
339 * those indices will be made with segmentation faults as result. */
341
342 /* Logging could be active. */
343 _gamelog.StopAnyAction();
344
345 throw std::exception();
346}
347
355[[noreturn]] void SlErrorCorrupt(const std::string &msg)
356{
357 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
358}
359
360
361typedef void (*AsyncSaveFinishProc)();
362static std::atomic<AsyncSaveFinishProc> _async_save_finish;
363static std::thread _save_thread;
364
370{
371 if (_exit_game) return;
372 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
373
374 _async_save_finish.store(proc, std::memory_order_release);
375}
376
381{
382 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
383 if (proc == nullptr) return;
384
385 proc();
386
387 if (_save_thread.joinable()) {
388 _save_thread.join();
389 }
390}
391
396uint8_t SlReadByte()
397{
398 return _sl.reader->ReadByte();
399}
400
405void SlWriteByte(uint8_t b)
406{
407 _sl.dumper->WriteByte(b);
408}
409
410static inline int SlReadUint16()
411{
412 int x = SlReadByte() << 8;
413 return x | SlReadByte();
414}
415
416static inline uint32_t SlReadUint32()
417{
418 uint32_t x = SlReadUint16() << 16;
419 return x | SlReadUint16();
420}
421
422static inline uint64_t SlReadUint64()
423{
424 uint32_t x = SlReadUint32();
425 uint32_t y = SlReadUint32();
426 return (uint64_t)x << 32 | y;
427}
428
429static inline void SlWriteUint16(uint16_t v)
430{
431 SlWriteByte(GB(v, 8, 8));
432 SlWriteByte(GB(v, 0, 8));
433}
434
435static inline void SlWriteUint32(uint32_t v)
436{
437 SlWriteUint16(GB(v, 16, 16));
438 SlWriteUint16(GB(v, 0, 16));
439}
440
441static inline void SlWriteUint64(uint64_t x)
442{
443 SlWriteUint32((uint32_t)(x >> 32));
444 SlWriteUint32((uint32_t)x);
445}
446
456static uint SlReadSimpleGamma()
457{
458 uint i = SlReadByte();
459 if (HasBit(i, 7)) {
460 i &= ~0x80;
461 if (HasBit(i, 6)) {
462 i &= ~0x40;
463 if (HasBit(i, 5)) {
464 i &= ~0x20;
465 if (HasBit(i, 4)) {
466 i &= ~0x10;
467 if (HasBit(i, 3)) {
468 SlErrorCorrupt("Unsupported gamma");
469 }
470 i = SlReadByte(); // 32 bits only.
471 }
472 i = (i << 8) | SlReadByte();
473 }
474 i = (i << 8) | SlReadByte();
475 }
476 i = (i << 8) | SlReadByte();
477 }
478 return i;
479}
480
498static void SlWriteSimpleGamma(size_t i)
499{
500 if (i >= (1 << 7)) {
501 if (i >= (1 << 14)) {
502 if (i >= (1 << 21)) {
503 if (i >= (1 << 28)) {
504 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
505 SlWriteByte((uint8_t)(0xF0));
506 SlWriteByte((uint8_t)(i >> 24));
507 } else {
508 SlWriteByte((uint8_t)(0xE0 | (i >> 24)));
509 }
510 SlWriteByte((uint8_t)(i >> 16));
511 } else {
512 SlWriteByte((uint8_t)(0xC0 | (i >> 16)));
513 }
514 SlWriteByte((uint8_t)(i >> 8));
515 } else {
516 SlWriteByte((uint8_t)(0x80 | (i >> 8)));
517 }
518 }
519 SlWriteByte((uint8_t)i);
520}
521
523static inline uint SlGetGammaLength(size_t i)
524{
525 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
526}
527
528static inline uint SlReadSparseIndex()
529{
530 return SlReadSimpleGamma();
531}
532
533static inline void SlWriteSparseIndex(uint index)
534{
535 SlWriteSimpleGamma(index);
536}
537
538static inline uint SlReadArrayLength()
539{
540 return SlReadSimpleGamma();
541}
542
543static inline void SlWriteArrayLength(size_t length)
544{
545 SlWriteSimpleGamma(length);
546}
547
548static inline uint SlGetArrayLength(size_t length)
549{
550 return SlGetGammaLength(length);
551}
552
556static uint8_t GetSavegameFileType(const SaveLoad &sld)
557{
558 switch (sld.cmd) {
559 case SL_VAR:
560 return GetVarFileType(sld.conv); break;
561
562 case SL_STDSTR:
563 case SL_ARR:
564 case SL_VECTOR:
565 case SL_DEQUE:
566 return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
567
568 case SL_REF:
569 return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
570
571 case SL_REFLIST:
572 case SL_REFVECTOR:
573 return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
574
575 case SL_SAVEBYTE:
576 return SLE_FILE_U8;
577
578 case SL_STRUCT:
579 case SL_STRUCTLIST:
580 return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
581
582 default: NOT_REACHED();
583 }
584}
585
592static inline uint SlCalcConvMemLen(VarType conv)
593{
594 switch (GetVarMemType(conv)) {
595 case SLE_VAR_BL: return sizeof(bool);
596 case SLE_VAR_I8: return sizeof(int8_t);
597 case SLE_VAR_U8: return sizeof(uint8_t);
598 case SLE_VAR_I16: return sizeof(int16_t);
599 case SLE_VAR_U16: return sizeof(uint16_t);
600 case SLE_VAR_I32: return sizeof(int32_t);
601 case SLE_VAR_U32: return sizeof(uint32_t);
602 case SLE_VAR_I64: return sizeof(int64_t);
603 case SLE_VAR_U64: return sizeof(uint64_t);
604 case SLE_VAR_NULL: return 0;
605
606 case SLE_VAR_STR:
607 case SLE_VAR_STRQ:
608 return SlReadArrayLength();
609
610 case SLE_VAR_NAME:
611 default:
612 NOT_REACHED();
613 }
614}
615
622static inline uint8_t SlCalcConvFileLen(VarType conv)
623{
624 switch (GetVarFileType(conv)) {
625 case SLE_FILE_END: return 0;
626 case SLE_FILE_I8: return sizeof(int8_t);
627 case SLE_FILE_U8: return sizeof(uint8_t);
628 case SLE_FILE_I16: return sizeof(int16_t);
629 case SLE_FILE_U16: return sizeof(uint16_t);
630 case SLE_FILE_I32: return sizeof(int32_t);
631 case SLE_FILE_U32: return sizeof(uint32_t);
632 case SLE_FILE_I64: return sizeof(int64_t);
633 case SLE_FILE_U64: return sizeof(uint64_t);
634 case SLE_FILE_STRINGID: return sizeof(uint16_t);
635
636 case SLE_FILE_STRING:
637 return SlReadArrayLength();
638
639 case SLE_FILE_STRUCT:
640 default:
641 NOT_REACHED();
642 }
643}
644
646static inline size_t SlCalcRefLen()
647{
648 return IsSavegameVersionBefore(SLV_69) ? 2 : 4;
649}
650
651void SlSetArrayIndex(uint index)
652{
654 _sl.array_index = index;
655}
656
657static size_t _next_offs;
658
664{
665 /* After reading in the whole array inside the loop
666 * we must have read in all the data, so we must be at end of current block. */
667 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
668 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
669 }
670
671 for (;;) {
672 uint length = SlReadArrayLength();
673 if (length == 0) {
674 assert(!_sl.expect_table_header);
675 _next_offs = 0;
676 return -1;
677 }
678
679 _sl.obj_len = --length;
680 _next_offs = _sl.reader->GetSize() + length;
681
683 _sl.expect_table_header = false;
684 return INT32_MAX;
685 }
686
687 int index;
688 switch (_sl.block_mode) {
689 case CH_SPARSE_TABLE:
690 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
691 case CH_TABLE:
692 case CH_ARRAY: index = _sl.array_index++; break;
693 default:
694 Debug(sl, 0, "SlIterateArray error");
695 return -1; // error
696 }
697
698 if (length != 0) return index;
699 }
700}
701
706{
707 while (SlIterateArray() != -1) {
708 SlSkipBytes(_next_offs - _sl.reader->GetSize());
709 }
710}
711
717void SlSetLength(size_t length)
718{
719 assert(_sl.action == SLA_SAVE);
720
721 switch (_sl.need_length) {
722 case NL_WANTLENGTH:
724 if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) {
725 _sl.expect_table_header = false;
726 SlWriteArrayLength(length + 1);
727 break;
728 }
729
730 switch (_sl.block_mode) {
731 case CH_RIFF:
732 /* Ugly encoding of >16M RIFF chunks
733 * The lower 24 bits are normal
734 * The uppermost 4 bits are bits 24:27 */
735 assert(length < (1 << 28));
736 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
737 break;
738 case CH_TABLE:
739 case CH_ARRAY:
740 assert(_sl.last_array_index <= _sl.array_index);
741 while (++_sl.last_array_index <= _sl.array_index) {
742 SlWriteArrayLength(1);
743 }
744 SlWriteArrayLength(length + 1);
745 break;
746 case CH_SPARSE_TABLE:
747 case CH_SPARSE_ARRAY:
748 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
749 SlWriteSparseIndex(_sl.array_index);
750 break;
751 default: NOT_REACHED();
752 }
753 break;
754
755 case NL_CALCLENGTH:
756 _sl.obj_len += (int)length;
757 break;
758
759 default: NOT_REACHED();
760 }
761}
762
769static void SlCopyBytes(void *ptr, size_t length)
770{
771 uint8_t *p = (uint8_t *)ptr;
772
773 switch (_sl.action) {
774 case SLA_LOAD_CHECK:
775 case SLA_LOAD:
776 for (; length != 0; length--) *p++ = SlReadByte();
777 break;
778 case SLA_SAVE:
779 for (; length != 0; length--) SlWriteByte(*p++);
780 break;
781 default: NOT_REACHED();
782 }
783}
784
787{
788 return _sl.obj_len;
789}
790
798int64_t ReadValue(const void *ptr, VarType conv)
799{
800 switch (GetVarMemType(conv)) {
801 case SLE_VAR_BL: return (*(const bool *)ptr != 0);
802 case SLE_VAR_I8: return *(const int8_t *)ptr;
803 case SLE_VAR_U8: return *(const uint8_t *)ptr;
804 case SLE_VAR_I16: return *(const int16_t *)ptr;
805 case SLE_VAR_U16: return *(const uint16_t*)ptr;
806 case SLE_VAR_I32: return *(const int32_t *)ptr;
807 case SLE_VAR_U32: return *(const uint32_t*)ptr;
808 case SLE_VAR_I64: return *(const int64_t *)ptr;
809 case SLE_VAR_U64: return *(const uint64_t*)ptr;
810 case SLE_VAR_NULL:return 0;
811 default: NOT_REACHED();
812 }
813}
814
822void WriteValue(void *ptr, VarType conv, int64_t val)
823{
824 switch (GetVarMemType(conv)) {
825 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
826 case SLE_VAR_I8: *(int8_t *)ptr = val; break;
827 case SLE_VAR_U8: *(uint8_t *)ptr = val; break;
828 case SLE_VAR_I16: *(int16_t *)ptr = val; break;
829 case SLE_VAR_U16: *(uint16_t*)ptr = val; break;
830 case SLE_VAR_I32: *(int32_t *)ptr = val; break;
831 case SLE_VAR_U32: *(uint32_t*)ptr = val; break;
832 case SLE_VAR_I64: *(int64_t *)ptr = val; break;
833 case SLE_VAR_U64: *(uint64_t*)ptr = val; break;
834 case SLE_VAR_NAME: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(val); break;
835 case SLE_VAR_NULL: break;
836 default: NOT_REACHED();
837 }
838}
839
848static void SlSaveLoadConv(void *ptr, VarType conv)
849{
850 switch (_sl.action) {
851 case SLA_SAVE: {
852 int64_t x = ReadValue(ptr, conv);
853
854 /* Write the value to the file and check if its value is in the desired range */
855 switch (GetVarFileType(conv)) {
856 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
857 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
858 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
860 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
861 case SLE_FILE_I32:
862 case SLE_FILE_U32: SlWriteUint32((uint32_t)x);break;
863 case SLE_FILE_I64:
864 case SLE_FILE_U64: SlWriteUint64(x);break;
865 default: NOT_REACHED();
866 }
867 break;
868 }
869 case SLA_LOAD_CHECK:
870 case SLA_LOAD: {
871 int64_t x;
872 /* Read a value from the file */
873 switch (GetVarFileType(conv)) {
874 case SLE_FILE_I8: x = (int8_t )SlReadByte(); break;
875 case SLE_FILE_U8: x = (uint8_t )SlReadByte(); break;
876 case SLE_FILE_I16: x = (int16_t )SlReadUint16(); break;
877 case SLE_FILE_U16: x = (uint16_t)SlReadUint16(); break;
878 case SLE_FILE_I32: x = (int32_t )SlReadUint32(); break;
879 case SLE_FILE_U32: x = (uint32_t)SlReadUint32(); break;
880 case SLE_FILE_I64: x = (int64_t )SlReadUint64(); break;
881 case SLE_FILE_U64: x = (uint64_t)SlReadUint64(); break;
882 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16_t)SlReadUint16()); break;
883 default: NOT_REACHED();
884 }
885
886 /* Write The value to the struct. These ARE endian safe. */
887 WriteValue(ptr, conv, x);
888 break;
889 }
890 case SLA_PTRS: break;
891 case SLA_NULL: break;
892 default: NOT_REACHED();
893 }
894}
895
903static inline size_t SlCalcStdStringLen(const void *ptr)
904{
905 const std::string *str = reinterpret_cast<const std::string *>(ptr);
906
907 size_t len = str->length();
908 return len + SlGetArrayLength(len); // also include the length of the index
909}
910
911
919void FixSCCEncoded(std::string &str, bool fix_code)
920{
921 if (str.empty()) return;
922
923 /* We need to convert from old escape-style encoding to record separator encoding.
924 * Initial `<SCC_ENCODED><STRINGID>` stays the same.
925 *
926 * `:<SCC_ENCODED><STRINGID>` becomes `<RS><SCC_ENCODED><STRINGID>`
927 * `:<HEX>` becomes `<RS><SCC_ENCODED_NUMERIC><HEX>`
928 * `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
929 */
930 std::string result;
931 auto output = std::back_inserter(result);
932
933 bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
934 bool in_string = false; // Set if we in a string, between double-quotes.
935 bool need_type = true; // Set if a parameter type needs to be emitted.
936
937 for (auto it = std::begin(str); it != std::end(str); /* nothing */) {
938 size_t len = Utf8EncodedCharLen(*it);
939 if (len == 0 || it + len > std::end(str)) break;
940
941 char32_t c;
942 Utf8Decode(&c, &*it);
943 if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
944 Utf8Encode(output, SCC_ENCODED);
945 need_type = false;
946 is_encoded = true;
947 it += len;
948 continue;
949 }
950
951 /* If the first character is not SCC_ENCODED then we don't have to do any conversion. */
952 if (!is_encoded) return;
953
954 if (c == '"') {
955 in_string = !in_string;
956 if (in_string && need_type) {
957 /* Started a new string parameter. */
959 need_type = false;
960 }
961 it += len;
962 continue;
963 }
964
965 if (!in_string && c == ':') {
966 *output = SCC_RECORD_SEPARATOR;
967 need_type = true;
968 it += len;
969 continue;
970 }
971 if (need_type) {
972 /* Started a new numeric parameter. */
974 need_type = false;
975 }
976
977 Utf8Encode(output, c);
978 it += len;
979 }
980
981 str = result;
982}
983
990void SlReadString(std::string &str, size_t length)
991{
992 str.resize(length);
993 SlCopyBytes(str.data(), length);
994}
995
1001static void SlStdString(void *ptr, VarType conv)
1002{
1003 std::string *str = reinterpret_cast<std::string *>(ptr);
1004
1005 switch (_sl.action) {
1006 case SLA_SAVE: {
1007 size_t len = str->length();
1008 SlWriteArrayLength(len);
1009 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->c_str())), len);
1010 break;
1011 }
1012
1013 case SLA_LOAD_CHECK:
1014 case SLA_LOAD: {
1015 size_t len = SlReadArrayLength();
1016 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1017 SlSkipBytes(len);
1018 return;
1019 }
1020
1021 SlReadString(*str, len);
1022
1024 if ((conv & SLF_ALLOW_CONTROL) != 0) {
1027 }
1028 if ((conv & SLF_ALLOW_NEWLINE) != 0) {
1030 }
1031 *str = StrMakeValid(*str, settings);
1032 }
1033
1034 case SLA_PTRS: break;
1035 case SLA_NULL: break;
1036 default: NOT_REACHED();
1037 }
1038}
1039
1048static void SlCopyInternal(void *object, size_t length, VarType conv)
1049{
1050 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1051 assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes
1052 SlSkipBytes(length * SlCalcConvFileLen(conv));
1053 return;
1054 }
1055
1056 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
1057 * as a byte-type. So detect this, and adjust object size accordingly */
1058 if (_sl.action != SLA_SAVE && _sl_version == 0) {
1059 /* all objects except difficulty settings */
1060 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
1061 conv == SLE_INT32 || conv == SLE_UINT32) {
1062 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
1063 return;
1064 }
1065 /* used for conversion of Money 32bit->64bit */
1066 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
1067 for (uint i = 0; i < length; i++) {
1068 ((int64_t*)object)[i] = (int32_t)std::byteswap(SlReadUint32());
1069 }
1070 return;
1071 }
1072 }
1073
1074 /* If the size of elements is 1 byte both in file and memory, no special
1075 * conversion is needed, use specialized copy-copy function to speed up things */
1076 if (conv == SLE_INT8 || conv == SLE_UINT8) {
1077 SlCopyBytes(object, length);
1078 } else {
1079 uint8_t *a = (uint8_t*)object;
1080 uint8_t mem_size = SlCalcConvMemLen(conv);
1081
1082 for (; length != 0; length --) {
1083 SlSaveLoadConv(a, conv);
1084 a += mem_size; // get size
1085 }
1086 }
1087}
1088
1097void SlCopy(void *object, size_t length, VarType conv)
1098{
1099 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
1100
1101 /* Automatically calculate the length? */
1102 if (_sl.need_length != NL_NONE) {
1103 SlSetLength(length * SlCalcConvFileLen(conv));
1104 /* Determine length only? */
1105 if (_sl.need_length == NL_CALCLENGTH) return;
1106 }
1107
1108 SlCopyInternal(object, length, conv);
1109}
1110
1116static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1117{
1118 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1119}
1120
1127static void SlArray(void *array, size_t length, VarType conv)
1128{
1129 switch (_sl.action) {
1130 case SLA_SAVE:
1131 SlWriteArrayLength(length);
1132 SlCopyInternal(array, length, conv);
1133 return;
1134
1135 case SLA_LOAD_CHECK:
1136 case SLA_LOAD: {
1138 size_t sv_length = SlReadArrayLength();
1139 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1140 /* We don't know this field, so we assume the length in the savegame is correct. */
1141 length = sv_length;
1142 } else if (sv_length != length) {
1143 /* If the SLE_ARR changes size, a savegame bump is required
1144 * and the developer should have written conversion lines.
1145 * Error out to make this more visible. */
1146 SlErrorCorrupt("Fixed-length array is of wrong length");
1147 }
1148 }
1149
1150 SlCopyInternal(array, length, conv);
1151 return;
1152 }
1153
1154 case SLA_PTRS:
1155 case SLA_NULL:
1156 return;
1157
1158 default:
1159 NOT_REACHED();
1160 }
1161}
1162
1173static size_t ReferenceToInt(const void *obj, SLRefType rt)
1174{
1175 assert(_sl.action == SLA_SAVE);
1176
1177 if (obj == nullptr) return 0;
1178
1179 switch (rt) {
1180 case REF_VEHICLE_OLD: // Old vehicles we save as new ones
1181 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
1182 case REF_STATION: return ((const Station*)obj)->index + 1;
1183 case REF_TOWN: return ((const Town*)obj)->index + 1;
1184 case REF_ORDER: return ((const Order*)obj)->index + 1;
1185 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
1186 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
1187 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
1188 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
1189 case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
1190 case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
1191 case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
1192 default: NOT_REACHED();
1193 }
1194}
1195
1206static void *IntToReference(size_t index, SLRefType rt)
1207{
1208 static_assert(sizeof(size_t) <= sizeof(void *));
1209
1210 assert(_sl.action == SLA_PTRS);
1211
1212 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
1213 * and should be loaded like that */
1214 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) {
1215 rt = REF_VEHICLE;
1216 }
1217
1218 /* No need to look up nullptr pointers, just return immediately */
1219 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr;
1220
1221 /* Correct index. Old vehicles were saved differently:
1222 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1223 if (rt != REF_VEHICLE_OLD) index--;
1224
1225 switch (rt) {
1226 case REF_ORDERLIST:
1227 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1228 SlErrorCorrupt("Referencing invalid OrderList");
1229
1230 case REF_ORDER:
1231 if (Order::IsValidID(index)) return Order::Get(index);
1232 /* in old versions, invalid order was used to mark end of order list */
1233 if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr;
1234 SlErrorCorrupt("Referencing invalid Order");
1235
1236 case REF_VEHICLE_OLD:
1237 case REF_VEHICLE:
1238 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1239 SlErrorCorrupt("Referencing invalid Vehicle");
1240
1241 case REF_STATION:
1242 if (Station::IsValidID(index)) return Station::Get(index);
1243 SlErrorCorrupt("Referencing invalid Station");
1244
1245 case REF_TOWN:
1246 if (Town::IsValidID(index)) return Town::Get(index);
1247 SlErrorCorrupt("Referencing invalid Town");
1248
1249 case REF_ROADSTOPS:
1250 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1251 SlErrorCorrupt("Referencing invalid RoadStop");
1252
1253 case REF_ENGINE_RENEWS:
1254 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1255 SlErrorCorrupt("Referencing invalid EngineRenew");
1256
1257 case REF_CARGO_PACKET:
1258 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1259 SlErrorCorrupt("Referencing invalid CargoPacket");
1260
1261 case REF_STORAGE:
1262 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1263 SlErrorCorrupt("Referencing invalid PersistentStorage");
1264
1265 case REF_LINK_GRAPH:
1266 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1267 SlErrorCorrupt("Referencing invalid LinkGraph");
1268
1269 case REF_LINK_GRAPH_JOB:
1270 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1271 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1272
1273 default: NOT_REACHED();
1274 }
1275}
1276
1282void SlSaveLoadRef(void *ptr, VarType conv)
1283{
1284 switch (_sl.action) {
1285 case SLA_SAVE:
1286 SlWriteUint32((uint32_t)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
1287 break;
1288 case SLA_LOAD_CHECK:
1289 case SLA_LOAD:
1290 *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32();
1291 break;
1292 case SLA_PTRS:
1293 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
1294 break;
1295 case SLA_NULL:
1296 *(void **)ptr = nullptr;
1297 break;
1298 default: NOT_REACHED();
1299 }
1300}
1301
1305template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1307 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1308public:
1315 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1316 {
1317 assert(cmd == SL_VAR || cmd == SL_REF);
1318
1319 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1320
1321 int type_size = SlGetArrayLength(list->size());
1322 int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32);
1323 return list->size() * item_size + type_size;
1324 }
1325
1326 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1327 {
1328 switch (cmd) {
1329 case SL_VAR: SlSaveLoadConv(item, conv); break;
1330 case SL_REF: SlSaveLoadRef(item, conv); break;
1331 case SL_STDSTR: SlStdString(item, conv); break;
1332 default:
1333 NOT_REACHED();
1334 }
1335 }
1336
1343 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1344 {
1345 assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
1346
1347 SlStorageT *list = static_cast<SlStorageT *>(storage);
1348
1349 switch (_sl.action) {
1350 case SLA_SAVE:
1351 SlWriteArrayLength(list->size());
1352
1353 for (auto &item : *list) {
1354 SlSaveLoadMember(cmd, &item, conv);
1355 }
1356 break;
1357
1358 case SLA_LOAD_CHECK:
1359 case SLA_LOAD: {
1360 size_t length;
1361 switch (cmd) {
1362 case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1363 case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1364 case SL_STDSTR: length = SlReadArrayLength(); break;
1365 default: NOT_REACHED();
1366 }
1367
1368 list->clear();
1369 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1370 list->reserve(length);
1371 }
1372
1373 /* Load each value and push to the end of the storage. */
1374 for (size_t i = 0; i < length; i++) {
1375 Tvar &data = list->emplace_back();
1376 SlSaveLoadMember(cmd, &data, conv);
1377 }
1378 break;
1379 }
1380
1381 case SLA_PTRS:
1382 for (auto &item : *list) {
1383 SlSaveLoadMember(cmd, &item, conv);
1384 }
1385 break;
1386
1387 case SLA_NULL:
1388 list->clear();
1389 break;
1390
1391 default: NOT_REACHED();
1392 }
1393 }
1394};
1395
1401static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1402{
1404}
1405
1411static void SlRefList(void *list, VarType conv)
1412{
1413 /* Automatically calculate the length? */
1414 if (_sl.need_length != NL_NONE) {
1415 SlSetLength(SlCalcRefListLen(list, conv));
1416 /* Determine length only? */
1417 if (_sl.need_length == NL_CALCLENGTH) return;
1418 }
1419
1421}
1422
1428static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1429{
1431}
1432
1438static void SlRefVector(void *vector, VarType conv)
1439{
1440 /* Automatically calculate the length? */
1441 if (_sl.need_length != NL_NONE) {
1442 SlSetLength(SlCalcRefVectorLen(vector, conv));
1443 /* Determine length only? */
1444 if (_sl.need_length == NL_CALCLENGTH) return;
1445 }
1446
1448}
1449
1455static inline size_t SlCalcDequeLen(const void *deque, VarType conv)
1456{
1457 switch (GetVarMemType(conv)) {
1458 case SLE_VAR_BL: return SlStorageHelper<std::deque, bool>::SlCalcLen(deque, conv);
1459 case SLE_VAR_I8: return SlStorageHelper<std::deque, int8_t>::SlCalcLen(deque, conv);
1460 case SLE_VAR_U8: return SlStorageHelper<std::deque, uint8_t>::SlCalcLen(deque, conv);
1461 case SLE_VAR_I16: return SlStorageHelper<std::deque, int16_t>::SlCalcLen(deque, conv);
1462 case SLE_VAR_U16: return SlStorageHelper<std::deque, uint16_t>::SlCalcLen(deque, conv);
1463 case SLE_VAR_I32: return SlStorageHelper<std::deque, int32_t>::SlCalcLen(deque, conv);
1464 case SLE_VAR_U32: return SlStorageHelper<std::deque, uint32_t>::SlCalcLen(deque, conv);
1465 case SLE_VAR_I64: return SlStorageHelper<std::deque, int64_t>::SlCalcLen(deque, conv);
1466 case SLE_VAR_U64: return SlStorageHelper<std::deque, uint64_t>::SlCalcLen(deque, conv);
1467
1468 case SLE_VAR_STR:
1469 /* Strings are a length-prefixed field type in the savegame table format,
1470 * these may not be directly stored in another length-prefixed container type. */
1471 NOT_REACHED();
1472
1473 default: NOT_REACHED();
1474 }
1475}
1476
1482static void SlDeque(void *deque, VarType conv)
1483{
1484 switch (GetVarMemType(conv)) {
1485 case SLE_VAR_BL: SlStorageHelper<std::deque, bool>::SlSaveLoad(deque, conv); break;
1486 case SLE_VAR_I8: SlStorageHelper<std::deque, int8_t>::SlSaveLoad(deque, conv); break;
1487 case SLE_VAR_U8: SlStorageHelper<std::deque, uint8_t>::SlSaveLoad(deque, conv); break;
1488 case SLE_VAR_I16: SlStorageHelper<std::deque, int16_t>::SlSaveLoad(deque, conv); break;
1489 case SLE_VAR_U16: SlStorageHelper<std::deque, uint16_t>::SlSaveLoad(deque, conv); break;
1490 case SLE_VAR_I32: SlStorageHelper<std::deque, int32_t>::SlSaveLoad(deque, conv); break;
1491 case SLE_VAR_U32: SlStorageHelper<std::deque, uint32_t>::SlSaveLoad(deque, conv); break;
1492 case SLE_VAR_I64: SlStorageHelper<std::deque, int64_t>::SlSaveLoad(deque, conv); break;
1493 case SLE_VAR_U64: SlStorageHelper<std::deque, uint64_t>::SlSaveLoad(deque, conv); break;
1494
1495 case SLE_VAR_STR:
1496 /* Strings are a length-prefixed field type in the savegame table format,
1497 * these may not be directly stored in another length-prefixed container type.
1498 * This is permitted for load-related actions, because invalid fields of this type are present
1499 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1500 assert(_sl.action != SLA_SAVE);
1502 break;
1503
1504 default: NOT_REACHED();
1505 }
1506}
1507
1513static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1514{
1515 switch (GetVarMemType(conv)) {
1516 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1517 case SLE_VAR_I8: return SlStorageHelper<std::vector, int8_t>::SlCalcLen(vector, conv);
1518 case SLE_VAR_U8: return SlStorageHelper<std::vector, uint8_t>::SlCalcLen(vector, conv);
1519 case SLE_VAR_I16: return SlStorageHelper<std::vector, int16_t>::SlCalcLen(vector, conv);
1520 case SLE_VAR_U16: return SlStorageHelper<std::vector, uint16_t>::SlCalcLen(vector, conv);
1521 case SLE_VAR_I32: return SlStorageHelper<std::vector, int32_t>::SlCalcLen(vector, conv);
1522 case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
1523 case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
1524 case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
1525
1526 case SLE_VAR_STR:
1527 /* Strings are a length-prefixed field type in the savegame table format,
1528 * these may not be directly stored in another length-prefixed container type. */
1529 NOT_REACHED();
1530
1531 default: NOT_REACHED();
1532 }
1533}
1534
1540static void SlVector(void *vector, VarType conv)
1541{
1542 switch (GetVarMemType(conv)) {
1543 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1544 case SLE_VAR_I8: SlStorageHelper<std::vector, int8_t>::SlSaveLoad(vector, conv); break;
1545 case SLE_VAR_U8: SlStorageHelper<std::vector, uint8_t>::SlSaveLoad(vector, conv); break;
1546 case SLE_VAR_I16: SlStorageHelper<std::vector, int16_t>::SlSaveLoad(vector, conv); break;
1547 case SLE_VAR_U16: SlStorageHelper<std::vector, uint16_t>::SlSaveLoad(vector, conv); break;
1548 case SLE_VAR_I32: SlStorageHelper<std::vector, int32_t>::SlSaveLoad(vector, conv); break;
1549 case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
1550 case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
1551 case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
1552
1553 case SLE_VAR_STR:
1554 /* Strings are a length-prefixed field type in the savegame table format,
1555 * these may not be directly stored in another length-prefixed container type.
1556 * This is permitted for load-related actions, because invalid fields of this type are present
1557 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1558 assert(_sl.action != SLA_SAVE);
1560 break;
1561
1562 default: NOT_REACHED();
1563 }
1564}
1565
1567static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1568{
1569 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1570}
1571
1577static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1578{
1579 size_t length = 0;
1580
1581 for (auto &sld : slt) {
1582 if (!SlIsObjectValidInSavegame(sld)) continue;
1583
1584 length += SlCalcConvFileLen(SLE_UINT8);
1585 length += SlCalcStdStringLen(&sld.name);
1586 }
1587
1588 length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
1589
1590 for (auto &sld : slt) {
1591 if (!SlIsObjectValidInSavegame(sld)) continue;
1592 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1593 length += SlCalcTableHeader(sld.handler->GetDescription());
1594 }
1595 }
1596
1597 return length;
1598}
1599
1606size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1607{
1608 size_t length = 0;
1609
1610 /* Need to determine the length and write a length tag. */
1611 for (auto &sld : slt) {
1612 length += SlCalcObjMemberLength(object, sld);
1613 }
1614 return length;
1615}
1616
1617size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1618{
1619 assert(_sl.action == SLA_SAVE);
1620
1621 if (!SlIsObjectValidInSavegame(sld)) return 0;
1622
1623 switch (sld.cmd) {
1624 case SL_VAR: return SlCalcConvFileLen(sld.conv);
1625 case SL_REF: return SlCalcRefLen();
1626 case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
1627 case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
1628 case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
1629 case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
1630 case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1631 case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
1632 case SL_SAVEBYTE: return 1; // a byte is logically of size 1
1633 case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
1634
1635 case SL_STRUCT:
1636 case SL_STRUCTLIST: {
1637 NeedLength old_need_length = _sl.need_length;
1638 size_t old_obj_len = _sl.obj_len;
1639
1641 _sl.obj_len = 0;
1642
1643 /* Pretend that we are saving to collect the object size. Other
1644 * means are difficult, as we don't know the length of the list we
1645 * are about to store. */
1646 sld.handler->Save(const_cast<void *>(object));
1647 size_t length = _sl.obj_len;
1648
1649 _sl.obj_len = old_obj_len;
1650 _sl.need_length = old_need_length;
1651
1652 if (sld.cmd == SL_STRUCT) {
1653 length += SlGetArrayLength(1);
1654 }
1655
1656 return length;
1657 }
1658
1659 default: NOT_REACHED();
1660 }
1661 return 0;
1662}
1663
1664static bool SlObjectMember(void *object, const SaveLoad &sld)
1665{
1666 if (!SlIsObjectValidInSavegame(sld)) return false;
1667
1668 VarType conv = GB(sld.conv, 0, 8);
1669 switch (sld.cmd) {
1670 case SL_VAR:
1671 case SL_REF:
1672 case SL_ARR:
1673 case SL_REFLIST:
1674 case SL_REFVECTOR:
1675 case SL_DEQUE:
1676 case SL_VECTOR:
1677 case SL_STDSTR: {
1678 void *ptr = GetVariableAddress(object, sld);
1679
1680 switch (sld.cmd) {
1681 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
1682 case SL_REF: SlSaveLoadRef(ptr, conv); break;
1683 case SL_ARR: SlArray(ptr, sld.length, conv); break;
1684 case SL_REFLIST: SlRefList(ptr, conv); break;
1685 case SL_REFVECTOR: SlRefVector(ptr, conv); break;
1686 case SL_DEQUE: SlDeque(ptr, conv); break;
1687 case SL_VECTOR: SlVector(ptr, conv); break;
1688 case SL_STDSTR: SlStdString(ptr, sld.conv); break;
1689 default: NOT_REACHED();
1690 }
1691 break;
1692 }
1693
1694 /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object.
1695 * When loading, the value is read explicitly with SlReadByte() to determine which
1696 * object description to use. */
1697 case SL_SAVEBYTE: {
1698 void *ptr = GetVariableAddress(object, sld);
1699
1700 switch (_sl.action) {
1701 case SLA_SAVE: SlWriteByte(*(uint8_t *)ptr); break;
1702 case SLA_LOAD_CHECK:
1703 case SLA_LOAD:
1704 case SLA_PTRS:
1705 case SLA_NULL: break;
1706 default: NOT_REACHED();
1707 }
1708 break;
1709 }
1710
1711 case SL_NULL: {
1712 assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
1713
1714 switch (_sl.action) {
1715 case SLA_LOAD_CHECK:
1716 case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
1717 case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1718 case SLA_PTRS:
1719 case SLA_NULL: break;
1720 default: NOT_REACHED();
1721 }
1722 break;
1723 }
1724
1725 case SL_STRUCT:
1726 case SL_STRUCTLIST:
1727 switch (_sl.action) {
1728 case SLA_SAVE: {
1729 if (sld.cmd == SL_STRUCT) {
1730 /* Store in the savegame if this struct was written or not. */
1731 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1732 }
1733 sld.handler->Save(object);
1734 break;
1735 }
1736
1737 case SLA_LOAD_CHECK: {
1740 }
1741 sld.handler->LoadCheck(object);
1742 break;
1743 }
1744
1745 case SLA_LOAD: {
1748 }
1749 sld.handler->Load(object);
1750 break;
1751 }
1752
1753 case SLA_PTRS:
1754 sld.handler->FixPointers(object);
1755 break;
1756
1757 case SLA_NULL: break;
1758 default: NOT_REACHED();
1759 }
1760 break;
1761
1762 default: NOT_REACHED();
1763 }
1764 return true;
1765}
1766
1771void SlSetStructListLength(size_t length)
1772{
1773 /* Automatically calculate the length? */
1774 if (_sl.need_length != NL_NONE) {
1775 SlSetLength(SlGetArrayLength(length));
1776 if (_sl.need_length == NL_CALCLENGTH) return;
1777 }
1778
1779 SlWriteArrayLength(length);
1780}
1781
1787size_t SlGetStructListLength(size_t limit)
1788{
1789 size_t length = SlReadArrayLength();
1790 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1791
1792 return length;
1793}
1794
1800void SlObject(void *object, const SaveLoadTable &slt)
1801{
1802 /* Automatically calculate the length? */
1803 if (_sl.need_length != NL_NONE) {
1804 SlSetLength(SlCalcObjLength(object, slt));
1805 if (_sl.need_length == NL_CALCLENGTH) return;
1806 }
1807
1808 for (auto &sld : slt) {
1809 SlObjectMember(object, sld);
1810 }
1811}
1812
1818 void Save(void *) const override
1819 {
1820 NOT_REACHED();
1821 }
1822
1823 void Load(void *object) const override
1824 {
1825 size_t length = SlGetStructListLength(UINT32_MAX);
1826 for (; length > 0; length--) {
1827 SlObject(object, this->GetLoadDescription());
1828 }
1829 }
1830
1831 void LoadCheck(void *object) const override
1832 {
1833 this->Load(object);
1834 }
1835
1836 virtual SaveLoadTable GetDescription() const override
1837 {
1838 return {};
1839 }
1840
1842 {
1843 NOT_REACHED();
1844 }
1845};
1846
1853std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1854{
1855 /* You can only use SlTableHeader if you are a CH_TABLE. */
1856 assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
1857
1858 switch (_sl.action) {
1859 case SLA_LOAD_CHECK:
1860 case SLA_LOAD: {
1861 std::vector<SaveLoad> saveloads;
1862
1863 /* Build a key lookup mapping based on the available fields. */
1864 std::map<std::string, const SaveLoad *> key_lookup;
1865 for (auto &sld : slt) {
1866 if (!SlIsObjectValidInSavegame(sld)) continue;
1867
1868 /* Check that there is only one active SaveLoad for a given name. */
1869 assert(key_lookup.find(sld.name) == key_lookup.end());
1870 key_lookup[sld.name] = &sld;
1871 }
1872
1873 while (true) {
1874 uint8_t type = 0;
1875 SlSaveLoadConv(&type, SLE_UINT8);
1876 if (type == SLE_FILE_END) break;
1877
1878 std::string key;
1879 SlStdString(&key, SLE_STR);
1880
1881 auto sld_it = key_lookup.find(key);
1882 if (sld_it == key_lookup.end()) {
1883 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1884 Debug(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type);
1885
1886 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1887 SaveLoadType saveload_type;
1888 switch (type & SLE_FILE_TYPE_MASK) {
1889 case SLE_FILE_STRING:
1890 /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
1891 saveload_type = SL_STDSTR;
1892 break;
1893
1894 case SLE_FILE_STRUCT:
1895 /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */
1896 saveload_type = SL_STRUCTLIST;
1897 handler = std::make_shared<SlSkipHandler>();
1898 break;
1899
1900 default:
1901 saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
1902 break;
1903 }
1904
1905 /* We don't know this field, so read to nothing. */
1906 saveloads.push_back({key, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, handler});
1907 continue;
1908 }
1909
1910 /* Validate the type of the field. If it is changed, the
1911 * savegame should have been bumped so we know how to do the
1912 * conversion. If this error triggers, that clearly didn't
1913 * happen and this is a friendly poke to the developer to bump
1914 * the savegame version and add conversion code. */
1915 uint8_t correct_type = GetSavegameFileType(*sld_it->second);
1916 if (correct_type != type) {
1917 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type, type);
1918 SlErrorCorrupt("Field type is different than expected");
1919 }
1920 saveloads.push_back(*sld_it->second);
1921 }
1922
1923 for (auto &sld : saveloads) {
1924 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1925 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
1926 }
1927 }
1928
1929 return saveloads;
1930 }
1931
1932 case SLA_SAVE: {
1933 /* Automatically calculate the length? */
1934 if (_sl.need_length != NL_NONE) {
1936 if (_sl.need_length == NL_CALCLENGTH) break;
1937 }
1938
1939 for (auto &sld : slt) {
1940 if (!SlIsObjectValidInSavegame(sld)) continue;
1941 /* Make sure we are not storing empty keys. */
1942 assert(!sld.name.empty());
1943
1944 uint8_t type = GetSavegameFileType(sld);
1945 assert(type != SLE_FILE_END);
1946
1947 SlSaveLoadConv(&type, SLE_UINT8);
1948 SlStdString(const_cast<std::string *>(&sld.name), SLE_STR);
1949 }
1950
1951 /* Add an end-of-header marker. */
1952 uint8_t type = SLE_FILE_END;
1953 SlSaveLoadConv(&type, SLE_UINT8);
1954
1955 /* After the table, write down any sub-tables we might have. */
1956 for (auto &sld : slt) {
1957 if (!SlIsObjectValidInSavegame(sld)) continue;
1958 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1959 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
1960 NeedLength old_need_length = _sl.need_length;
1962
1963 SlTableHeader(sld.handler->GetDescription());
1964
1965 _sl.need_length = old_need_length;
1966 }
1967 }
1968
1969 break;
1970 }
1971
1972 default: NOT_REACHED();
1973 }
1974
1975 return std::vector<SaveLoad>();
1976}
1977
1991std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
1992{
1993 assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
1994 /* CH_TABLE / CH_SPARSE_TABLE always have a header. */
1995 if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt);
1996
1997 std::vector<SaveLoad> saveloads;
1998
1999 /* Build a key lookup mapping based on the available fields. */
2000 std::map<std::string, std::vector<const SaveLoad *>> key_lookup;
2001 for (auto &sld : slt) {
2002 /* All entries should have a name; otherwise the entry should just be removed. */
2003 assert(!sld.name.empty());
2004
2005 key_lookup[sld.name].push_back(&sld);
2006 }
2007
2008 for (auto &slc : slct) {
2009 if (slc.name.empty()) {
2010 /* In old savegames there can be data we no longer care for. We
2011 * skip this by simply reading the amount of bytes indicated and
2012 * send those to /dev/null. */
2013 saveloads.push_back({"", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr});
2014 } else {
2015 auto sld_it = key_lookup.find(slc.name);
2016 /* If this branch triggers, it means that an entry in the
2017 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2018 * you rename a field in one and not in the other? */
2019 if (sld_it == key_lookup.end()) {
2020 /* This isn't an assert, as that leaves no information what
2021 * field was to blame. This way at least we have breadcrumbs. */
2022 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2023 SlErrorCorrupt("Internal error with savegame compatibility");
2024 }
2025 for (auto &sld : sld_it->second) {
2026 saveloads.push_back(*sld);
2027 }
2028 }
2029 }
2030
2031 for (auto &sld : saveloads) {
2032 if (!SlIsObjectValidInSavegame(sld)) continue;
2033 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
2034 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2035 }
2036 }
2037
2038 return saveloads;
2039}
2040
2046{
2047 SlObject(nullptr, slt);
2048}
2049
2055void SlAutolength(AutolengthProc *proc, int arg)
2056{
2057 assert(_sl.action == SLA_SAVE);
2058
2059 /* Tell it to calculate the length */
2061 _sl.obj_len = 0;
2062 proc(arg);
2063
2064 /* Setup length */
2067
2068 size_t start_pos = _sl.dumper->GetSize();
2069 size_t expected_offs = start_pos + _sl.obj_len;
2070
2071 /* And write the stuff */
2072 proc(arg);
2073
2074 if (expected_offs != _sl.dumper->GetSize()) {
2075 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2076 }
2077}
2078
2079void ChunkHandler::LoadCheck(size_t len) const
2080{
2081 switch (_sl.block_mode) {
2082 case CH_TABLE:
2083 case CH_SPARSE_TABLE:
2084 SlTableHeader({});
2085 [[fallthrough]];
2086 case CH_ARRAY:
2087 case CH_SPARSE_ARRAY:
2088 SlSkipArray();
2089 break;
2090 case CH_RIFF:
2091 SlSkipBytes(len);
2092 break;
2093 default:
2094 NOT_REACHED();
2095 }
2096}
2097
2102static void SlLoadChunk(const ChunkHandler &ch)
2103{
2104 uint8_t m = SlReadByte();
2105
2107 _sl.obj_len = 0;
2108 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2109
2110 /* The header should always be at the start. Read the length; the
2111 * Load() should as first action process the header. */
2114 }
2115
2116 switch (_sl.block_mode) {
2117 case CH_TABLE:
2118 case CH_ARRAY:
2119 _sl.array_index = 0;
2120 ch.Load();
2121 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2122 break;
2123 case CH_SPARSE_TABLE:
2124 case CH_SPARSE_ARRAY:
2125 ch.Load();
2126 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2127 break;
2128 case CH_RIFF: {
2129 /* Read length */
2130 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2131 len += SlReadUint16();
2132 _sl.obj_len = len;
2133 size_t start_pos = _sl.reader->GetSize();
2134 size_t endoffs = start_pos + len;
2135 ch.Load();
2136
2137 if (_sl.reader->GetSize() != endoffs) {
2138 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2139 }
2140 break;
2141 }
2142 default:
2143 SlErrorCorrupt("Invalid chunk type");
2144 break;
2145 }
2146
2147 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2148}
2149
2155static void SlLoadCheckChunk(const ChunkHandler &ch)
2156{
2157 uint8_t m = SlReadByte();
2158
2160 _sl.obj_len = 0;
2161 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2162
2163 /* The header should always be at the start. Read the length; the
2164 * LoadCheck() should as first action process the header. */
2167 }
2168
2169 switch (_sl.block_mode) {
2170 case CH_TABLE:
2171 case CH_ARRAY:
2172 _sl.array_index = 0;
2173 ch.LoadCheck();
2174 break;
2175 case CH_SPARSE_TABLE:
2176 case CH_SPARSE_ARRAY:
2177 ch.LoadCheck();
2178 break;
2179 case CH_RIFF: {
2180 /* Read length */
2181 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2182 len += SlReadUint16();
2183 _sl.obj_len = len;
2184 size_t start_pos = _sl.reader->GetSize();
2185 size_t endoffs = start_pos + len;
2186 ch.LoadCheck(len);
2187
2188 if (_sl.reader->GetSize() != endoffs) {
2189 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2190 }
2191 break;
2192 }
2193 default:
2194 SlErrorCorrupt("Invalid chunk type");
2195 break;
2196 }
2197
2198 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2199}
2200
2206static void SlSaveChunk(const ChunkHandler &ch)
2207{
2208 if (ch.type == CH_READONLY) return;
2209
2210 SlWriteUint32(ch.id);
2211 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2212
2213 _sl.block_mode = ch.type;
2214 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2215
2217
2218 switch (_sl.block_mode) {
2219 case CH_RIFF:
2220 ch.Save();
2221 break;
2222 case CH_TABLE:
2223 case CH_ARRAY:
2226 ch.Save();
2227 SlWriteArrayLength(0); // Terminate arrays
2228 break;
2229 case CH_SPARSE_TABLE:
2230 case CH_SPARSE_ARRAY:
2232 ch.Save();
2233 SlWriteArrayLength(0); // Terminate arrays
2234 break;
2235 default: NOT_REACHED();
2236 }
2237
2238 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2239}
2240
2242static void SlSaveChunks()
2243{
2244 for (auto &ch : ChunkHandlers()) {
2245 SlSaveChunk(ch);
2246 }
2247
2248 /* Terminator */
2249 SlWriteUint32(0);
2250}
2251
2258static const ChunkHandler *SlFindChunkHandler(uint32_t id)
2259{
2260 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2261 return nullptr;
2262}
2263
2265static void SlLoadChunks()
2266{
2267 uint32_t id;
2268 const ChunkHandler *ch;
2269
2270 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2271 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2272
2273 ch = SlFindChunkHandler(id);
2274 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2275 SlLoadChunk(*ch);
2276 }
2277}
2278
2281{
2282 uint32_t id;
2283 const ChunkHandler *ch;
2284
2285 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2286 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2287
2288 ch = SlFindChunkHandler(id);
2289 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2290 SlLoadCheckChunk(*ch);
2291 }
2292}
2293
2295static void SlFixPointers()
2296{
2298
2299 for (const ChunkHandler &ch : ChunkHandlers()) {
2300 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2301 ch.FixPointers();
2302 }
2303
2304 assert(_sl.action == SLA_PTRS);
2305}
2306
2307
2310 std::optional<FileHandle> file;
2311 long begin;
2312
2317 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2318 {
2319 }
2320
2323 {
2324 if (this->file.has_value()) {
2325 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2326 }
2327 }
2328
2329 size_t Read(uint8_t *buf, size_t size) override
2330 {
2331 /* We're in the process of shutting down, i.e. in "failure" mode. */
2332 if (!this->file.has_value()) return 0;
2333
2334 return fread(buf, 1, size, *this->file);
2335 }
2336
2337 void Reset() override
2338 {
2339 clearerr(*this->file);
2340 if (fseek(*this->file, this->begin, SEEK_SET)) {
2341 Debug(sl, 1, "Could not reset the file reading");
2342 }
2343 }
2344};
2345
2348 std::optional<FileHandle> file;
2349
2354 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2355 {
2356 }
2357
2360 {
2361 this->Finish();
2362 }
2363
2364 void Write(uint8_t *buf, size_t size) override
2365 {
2366 /* We're in the process of shutting down, i.e. in "failure" mode. */
2367 if (!this->file.has_value()) return;
2368
2369 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2370 }
2371
2372 void Finish() override
2373 {
2374 if (this->file.has_value()) {
2375 _game_session_stats.savegame_size = ftell(*this->file);
2376 this->file.reset();
2377 }
2378 }
2379};
2380
2381/*******************************************
2382 ********** START OF LZO CODE **************
2383 *******************************************/
2384
2385#ifdef WITH_LZO
2386#include <lzo/lzo1x.h>
2387
2389static const uint LZO_BUFFER_SIZE = 8192;
2390
2397 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain)
2398 {
2399 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2400 }
2401
2402 size_t Read(uint8_t *buf, size_t ssize) override
2403 {
2404 assert(ssize >= LZO_BUFFER_SIZE);
2405
2406 /* Buffer size is from the LZO docs plus the chunk header size. */
2407 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2408 uint32_t tmp[2];
2409 uint32_t size;
2410 lzo_uint len = ssize;
2411
2412 /* Read header*/
2413 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2414
2415 /* Check if size is bad */
2416 ((uint32_t*)out)[0] = size = tmp[1];
2417
2418 if (_sl_version != SL_MIN_VERSION) {
2419 tmp[0] = TO_BE32(tmp[0]);
2420 size = TO_BE32(size);
2421 }
2422
2423 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2424
2425 /* Read block */
2426 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2427
2428 /* Verify checksum */
2429 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2430
2431 /* Decompress */
2432 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2433 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2434 return len;
2435 }
2436};
2437
2444 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(chain)
2445 {
2446 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2447 }
2448
2449 void Write(uint8_t *buf, size_t size) override
2450 {
2451 const lzo_bytep in = buf;
2452 /* Buffer size is from the LZO docs plus the chunk header size. */
2453 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2454 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2455 lzo_uint outlen;
2456
2457 do {
2458 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2459 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
2460 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2461 ((uint32_t*)out)[1] = TO_BE32((uint32_t)outlen);
2462 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2463 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2464
2465 /* Move to next data chunk. */
2466 size -= len;
2467 in += len;
2468 } while (size > 0);
2469 }
2470};
2471
2472#endif /* WITH_LZO */
2473
2474/*********************************************
2475 ******** START OF NOCOMP CODE (uncompressed)*
2476 *********************************************/
2477
2484 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain)
2485 {
2486 }
2487
2488 size_t Read(uint8_t *buf, size_t size) override
2489 {
2490 return this->chain->Read(buf, size);
2491 }
2492};
2493
2500 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(chain)
2501 {
2502 }
2503
2504 void Write(uint8_t *buf, size_t size) override
2505 {
2506 this->chain->Write(buf, size);
2507 }
2508};
2509
2510/********************************************
2511 ********** START OF ZLIB CODE **************
2512 ********************************************/
2513
2514#if defined(WITH_ZLIB)
2515#include <zlib.h>
2516
2519 z_stream z;
2521
2526 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain)
2527 {
2528 memset(&this->z, 0, sizeof(this->z));
2529 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2530 }
2531
2534 {
2535 inflateEnd(&this->z);
2536 }
2537
2538 size_t Read(uint8_t *buf, size_t size) override
2539 {
2540 this->z.next_out = buf;
2541 this->z.avail_out = (uint)size;
2542
2543 do {
2544 /* read more bytes from the file? */
2545 if (this->z.avail_in == 0) {
2546 this->z.next_in = this->fread_buf;
2547 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2548 }
2549
2550 /* inflate the data */
2551 int r = inflate(&this->z, 0);
2552 if (r == Z_STREAM_END) break;
2553
2554 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2555 } while (this->z.avail_out != 0);
2556
2557 return size - this->z.avail_out;
2558 }
2559};
2560
2563 z_stream z;
2565
2571 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(chain)
2572 {
2573 memset(&this->z, 0, sizeof(this->z));
2574 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2575 }
2576
2579 {
2580 deflateEnd(&this->z);
2581 }
2582
2589 void WriteLoop(uint8_t *p, size_t len, int mode)
2590 {
2591 uint n;
2592 this->z.next_in = p;
2593 this->z.avail_in = (uInt)len;
2594 do {
2595 this->z.next_out = this->fwrite_buf;
2596 this->z.avail_out = sizeof(this->fwrite_buf);
2597
2605 int r = deflate(&this->z, mode);
2606
2607 /* bytes were emitted? */
2608 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2609 this->chain->Write(this->fwrite_buf, n);
2610 }
2611 if (r == Z_STREAM_END) break;
2612
2613 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2614 } while (this->z.avail_in || !this->z.avail_out);
2615 }
2616
2617 void Write(uint8_t *buf, size_t size) override
2618 {
2619 this->WriteLoop(buf, size, 0);
2620 }
2621
2622 void Finish() override
2623 {
2624 this->WriteLoop(nullptr, 0, Z_FINISH);
2625 this->chain->Finish();
2626 }
2627};
2628
2629#endif /* WITH_ZLIB */
2630
2631/********************************************
2632 ********** START OF LZMA CODE **************
2633 ********************************************/
2634
2635#if defined(WITH_LIBLZMA)
2636#include <lzma.h>
2637
2644static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2645
2648 lzma_stream lzma;
2650
2655 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(chain), lzma(_lzma_init)
2656 {
2657 /* Allow saves up to 256 MB uncompressed */
2658 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2659 }
2660
2663 {
2664 lzma_end(&this->lzma);
2665 }
2666
2667 size_t Read(uint8_t *buf, size_t size) override
2668 {
2669 this->lzma.next_out = buf;
2670 this->lzma.avail_out = size;
2671
2672 do {
2673 /* read more bytes from the file? */
2674 if (this->lzma.avail_in == 0) {
2675 this->lzma.next_in = this->fread_buf;
2676 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2677 }
2678
2679 /* inflate the data */
2680 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2681 if (r == LZMA_STREAM_END) break;
2682 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2683 } while (this->lzma.avail_out != 0);
2684
2685 return size - this->lzma.avail_out;
2686 }
2687};
2688
2691 lzma_stream lzma;
2693
2699 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(chain), lzma(_lzma_init)
2700 {
2701 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2702 }
2703
2706 {
2707 lzma_end(&this->lzma);
2708 }
2709
2716 void WriteLoop(uint8_t *p, size_t len, lzma_action action)
2717 {
2718 size_t n;
2719 this->lzma.next_in = p;
2720 this->lzma.avail_in = len;
2721 do {
2722 this->lzma.next_out = this->fwrite_buf;
2723 this->lzma.avail_out = sizeof(this->fwrite_buf);
2724
2725 lzma_ret r = lzma_code(&this->lzma, action);
2726
2727 /* bytes were emitted? */
2728 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2729 this->chain->Write(this->fwrite_buf, n);
2730 }
2731 if (r == LZMA_STREAM_END) break;
2732 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2733 } while (this->lzma.avail_in || !this->lzma.avail_out);
2734 }
2735
2736 void Write(uint8_t *buf, size_t size) override
2737 {
2738 this->WriteLoop(buf, size, LZMA_RUN);
2739 }
2740
2741 void Finish() override
2742 {
2743 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2744 this->chain->Finish();
2745 }
2746};
2747
2748#endif /* WITH_LIBLZMA */
2749
2750/*******************************************
2751 ************* END OF CODE *****************
2752 *******************************************/
2753
2756 const char *name;
2757 uint32_t tag;
2758
2759 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2760 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2761
2765};
2766
2767static const uint32_t SAVEGAME_TAG_LZO = TO_BE32('OTTD');
2768static const uint32_t SAVEGAME_TAG_NONE = TO_BE32('OTTN');
2769static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32('OTTZ');
2770static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32('OTTX');
2771
2774#if defined(WITH_LZO)
2775 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2776 {"lzo", SAVEGAME_TAG_LZO, CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
2777#else
2778 {"lzo", SAVEGAME_TAG_LZO, nullptr, nullptr, 0, 0, 0},
2779#endif
2780 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2781 {"none", SAVEGAME_TAG_NONE, CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
2782#if defined(WITH_ZLIB)
2783 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2784 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2785 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2786 {"zlib", SAVEGAME_TAG_ZLIB, CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
2787#else
2788 {"zlib", SAVEGAME_TAG_ZLIB, nullptr, nullptr, 0, 0, 0},
2789#endif
2790#if defined(WITH_LIBLZMA)
2791 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2792 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2793 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2794 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2795 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2796 {"lzma", SAVEGAME_TAG_LZMA, CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
2797#else
2798 {"lzma", SAVEGAME_TAG_LZMA, nullptr, nullptr, 0, 0, 0},
2799#endif
2800};
2801
2808static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(const std::string &full_name)
2809{
2810 /* Find default savegame format, the highest one with which files can be written. */
2811 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2812 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2813
2814 const SaveLoadFormat &def = *it;
2815
2816 if (!full_name.empty()) {
2817 /* Get the ":..." of the compression level out of the way */
2818 size_t separator = full_name.find(':');
2819 bool has_comp_level = separator != std::string::npos;
2820 const std::string name(full_name, 0, has_comp_level ? separator : full_name.size());
2821
2822 for (const auto &slf : _saveload_formats) {
2823 if (slf.init_write != nullptr && name.compare(slf.name) == 0) {
2824 if (has_comp_level) {
2825 const std::string complevel(full_name, separator + 1);
2826
2827 /* Get the level and determine whether all went fine. */
2828 size_t processed;
2829 long level = std::stol(complevel, &processed, 10);
2830 if (processed == 0 || level != Clamp(level, slf.min_compression, slf.max_compression)) {
2832 GetEncodedString(STR_CONFIG_ERROR),
2833 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2834 WL_CRITICAL);
2835 } else {
2836 return {slf, ClampTo<uint8_t>(level)};
2837 }
2838 }
2839 return {slf, slf.default_compression};
2840 }
2841 }
2842
2844 GetEncodedString(STR_CONFIG_ERROR),
2845 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2846 WL_CRITICAL);
2847 }
2848 return {def, def.default_compression};
2849}
2850
2851/* actual loader/saver function */
2852void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2853extern bool AfterLoadGame();
2854extern bool LoadOldSaveGame(const std::string &file);
2855
2860{
2861 ResetTempEngineData();
2862 ClearRailTypeLabelList();
2863 ClearRoadTypeLabelList();
2864 ResetOldWaypoints();
2865}
2866
2870static inline void ClearSaveLoadState()
2871{
2872 _sl.dumper = nullptr;
2873 _sl.sf = nullptr;
2874 _sl.reader = nullptr;
2875 _sl.lf = nullptr;
2876}
2877
2879static void SaveFileStart()
2880{
2881 SetMouseCursorBusy(true);
2882
2884 _sl.saveinprogress = true;
2885}
2886
2888static void SaveFileDone()
2889{
2890 SetMouseCursorBusy(false);
2891
2893 _sl.saveinprogress = false;
2894
2895#ifdef __EMSCRIPTEN__
2896 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
2897#endif
2898}
2899
2902{
2903 _sl.error_str = str;
2904}
2905
2908{
2909 return GetEncodedString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
2910}
2911
2917
2924
2930{
2931 try {
2932 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
2933
2934 /* We have written our stuff to memory, now write it to file! */
2935 uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
2936 _sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
2937
2938 _sl.sf = fmt.init_write(_sl.sf, compression);
2939 _sl.dumper->Flush(_sl.sf);
2940
2942
2943 if (threaded) SetAsyncSaveFinish(SaveFileDone);
2944
2945 return SL_OK;
2946 } catch (...) {
2948
2950
2951 /* We don't want to shout when saving is just
2952 * cancelled due to a client disconnecting. */
2953 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
2954 /* Skip the "colour" character */
2955 Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
2956 asfp = SaveFileError;
2957 }
2958
2959 if (threaded) {
2960 SetAsyncSaveFinish(asfp);
2961 } else {
2962 asfp();
2963 }
2964 return SL_ERROR;
2965 }
2966}
2967
2968void WaitTillSaved()
2969{
2970 if (!_save_thread.joinable()) return;
2971
2972 _save_thread.join();
2973
2974 /* Make sure every other state is handled properly as well. */
2976}
2977
2986static SaveOrLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
2987{
2988 assert(!_sl.saveinprogress);
2989
2990 _sl.dumper = std::make_unique<MemoryDumper>();
2991 _sl.sf = writer;
2992
2994
2995 SaveViewportBeforeSaveGame();
2996 SlSaveChunks();
2997
2998 SaveFileStart();
2999
3000 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3001 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3002
3003 SaveOrLoadResult result = SaveFileToDisk(false);
3004 SaveFileDone();
3005
3006 return result;
3007 }
3008
3009 return SL_OK;
3010}
3011
3018SaveOrLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3019{
3020 try {
3022 return DoSave(writer, threaded);
3023 } catch (...) {
3025 return SL_ERROR;
3026 }
3027}
3028
3037static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
3038{
3039 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3040 if (fmt != std::end(_saveload_formats)) {
3041 /* Check version number */
3042 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3043 /* Minor is not used anymore from version 18.0, but it is still needed
3044 * in versions before that (4 cases) which can't be removed easy.
3045 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3046 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3047
3048 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3049
3050 /* Is the version higher than the current? */
3051 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3052 if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
3053 return fmt;
3054 }
3055
3056 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3057 _sl.lf->Reset();
3060
3061 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3062 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3063 if (fmt == std::end(_saveload_formats)) {
3064 /* Who removed the LZO savegame format definition? When built without LZO support,
3065 * the formats must still list it just without a method to read the file.
3066 * The caller of this function has to check for the existence of load function. */
3067 NOT_REACHED();
3068 }
3069 return fmt;
3070}
3071
3078static SaveOrLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3079{
3080 _sl.lf = reader;
3081
3082 if (load_check) {
3083 /* Clear previous check data */
3085 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3087 }
3088
3089 uint32_t hdr[2];
3090 if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3091
3092 /* see if we have any loader for this type. */
3093 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
3094
3095 /* loader for this savegame type is not implemented? */
3096 if (fmt->init_load == nullptr) {
3097 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3098 }
3099
3100 _sl.lf = fmt->init_load(_sl.lf);
3101 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3102 _next_offs = 0;
3103
3104 if (!load_check) {
3106
3107 /* Old maps were hardcoded to 256x256 and thus did not contain
3108 * any mapsize information. Pre-initialize to 256x256 to not to
3109 * confuse old games */
3110 InitializeGame(256, 256, true, true);
3111
3112 _gamelog.Reset();
3113
3115 /*
3116 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3117 * shared savegame version 4. Anything before that 'obviously'
3118 * does not have any NewGRFs. Between the introduction and
3119 * savegame version 41 (just before 0.5) the NewGRF settings
3120 * were not stored in the savegame and they were loaded by
3121 * using the settings from the main menu.
3122 * So, to recap:
3123 * - savegame version < 4: do not load any NewGRFs.
3124 * - savegame version >= 41: load NewGRFs from savegame, which is
3125 * already done at this stage by
3126 * overwriting the main menu settings.
3127 * - other savegame versions: use main menu settings.
3128 *
3129 * This means that users *can* crash savegame version 4..40
3130 * savegames if they set incompatible NewGRFs in the main menu,
3131 * but can't crash anymore for savegame version < 4 savegames.
3132 *
3133 * Note: this is done here because AfterLoadGame is also called
3134 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3135 */
3137 }
3138 }
3139
3140 if (load_check) {
3141 /* Load chunks into _load_check_data.
3142 * No pools are loaded. References are not possible, and thus do not need resolving. */
3144 } else {
3145 /* Load chunks and resolve references */
3146 SlLoadChunks();
3147 SlFixPointers();
3148 }
3149
3151
3153
3154 if (load_check) {
3155 /* The only part from AfterLoadGame() we need */
3157 } else {
3159
3160 /* After loading fix up savegame for any internal changes that
3161 * might have occurred since then. If it fails, load back the old game. */
3162 if (!AfterLoadGame()) {
3164 return SL_REINIT;
3165 }
3166
3168 }
3169
3170 return SL_OK;
3171}
3172
3178SaveOrLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3179{
3180 try {
3182 return DoLoad(reader, false);
3183 } catch (...) {
3185 return SL_REINIT;
3186 }
3187}
3188
3198SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3199{
3200 /* An instance of saving is already active, so don't go saving again */
3201 if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) {
3202 /* if not an autosave, but a user action, show error message */
3203 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WL_ERROR);
3204 return SL_OK;
3205 }
3206 WaitTillSaved();
3207
3208 try {
3209 /* Load a TTDLX or TTDPatch game */
3210 if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) {
3212
3213 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3214
3215 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3216 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3217 * Note: this is done here because AfterLoadGame is also called
3218 * for OTTD savegames which have their own NewGRF logic. */
3220 _gamelog.Reset();
3221 if (!LoadOldSaveGame(filename)) return SL_REINIT;
3225 if (!AfterLoadGame()) {
3227 return SL_REINIT;
3228 }
3230 return SL_OK;
3231 }
3232
3233 assert(dft == DFT_GAME_FILE);
3234 switch (fop) {
3235 case SLO_CHECK:
3237 break;
3238
3239 case SLO_LOAD:
3241 break;
3242
3243 case SLO_SAVE:
3245 break;
3246
3247 default: NOT_REACHED();
3248 }
3249
3250 auto fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3251
3252 /* Make it a little easier to load savegames from the console */
3253 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
3254 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
3255 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
3256
3257 if (!fh.has_value()) {
3258 SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3259 }
3260
3261 if (fop == SLO_SAVE) { // SAVE game
3262 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3263 if (!_settings_client.gui.threaded_saves) threaded = false;
3264
3265 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3266 }
3267
3268 /* LOAD game */
3269 assert(fop == SLO_LOAD || fop == SLO_CHECK);
3270 Debug(desync, 1, "load: {}", filename);
3271 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SLO_CHECK);
3272 } catch (...) {
3273 /* This code may be executed both for old and new save games. */
3275
3276 /* Skip the "colour" character */
3277 if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
3278
3279 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3280 return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;
3281 }
3282}
3283
3290{
3291 std::string filename;
3292
3294 filename = GenerateDefaultSaveName() + counter.Extension();
3295 } else {
3296 filename = counter.Filename();
3297 }
3298
3299 Debug(sl, 2, "Autosaving to '{}'", filename);
3300 if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) {
3301 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WL_ERROR);
3302 }
3303}
3304
3305
3308{
3310}
3311
3316{
3317 /* Check if we have a name for this map, which is the name of the first
3318 * available company. When there's no company available we'll use
3319 * 'Spectator' as "company" name. */
3321 if (!Company::IsValidID(cid)) {
3322 for (const Company *c : Company::Iterate()) {
3323 cid = c->index;
3324 break;
3325 }
3326 }
3327
3328 std::array<StringParameter, 4> params{};
3329 auto it = params.begin();
3330 *it++ = cid;
3331
3332 /* We show the current game time differently depending on the timekeeping units used by this game. */
3334 /* Insert time played. */
3335 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3336 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3337 *it++ = play_time / 60 / 60;
3338 *it++ = (play_time / 60) % 60;
3339 } else {
3340 /* Insert current date */
3342 case 0: *it++ = STR_JUST_DATE_LONG; break;
3343 case 1: *it++ = STR_JUST_DATE_TINY; break;
3344 case 2: *it++ = STR_JUST_DATE_ISO; break;
3345 default: NOT_REACHED();
3346 }
3347 *it++ = TimerGameEconomy::date;
3348 }
3349
3350 /* Get the correct string (special string for when there's not company) */
3351 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3352 SanitizeFilename(filename);
3353 return filename;
3354}
3355
3364
3372{
3373 if (aft == FT_INVALID || aft == FT_NONE) {
3374 this->file_op = SLO_INVALID;
3375 this->detail_ftype = DFT_INVALID;
3376 this->abstract_ftype = FT_INVALID;
3377 return;
3378 }
3379
3380 this->file_op = fop;
3381 this->detail_ftype = dft;
3382 this->abstract_ftype = aft;
3383}
3384
3390{
3391 this->SetMode(item.type);
3392 this->name = item.name;
3393 this->title = item.title;
3394}
3395
3397{
3398 assert(this->load_description.has_value());
3399 return *this->load_description;
3400}
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.
Container for an encoded string, created by GetEncodedString.
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.
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, const CommandCost &cc)
Display an error message in a window.
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition fileio.cpp:1003
std::optional< FileHandle > FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:243
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:53
@ SLO_CHECK
Load file for checking and/or preview.
Definition fileio_type.h:54
@ SLO_SAVE
File is being saved.
Definition fileio_type.h:56
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:55
@ SLO_INVALID
Unknown file operation.
Definition fileio_type.h:58
DetailedFileType GetDetailedFileType(FiosType fios_type)
Extract the detailed file type from a FiosType.
AbstractFileType GetAbstractFileType(FiosType fios_type)
Extract the abstract file type from a FiosType.
Definition fileio_type.h:97
FiosType
Elements of a file system that are recognized.
Definition fileio_type.h:73
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:29
@ DFT_GAME_FILE
Save game or scenario file.
Definition fileio_type.h:32
@ DFT_INVALID
Unknown or invalid file.
Definition fileio_type.h:49
@ DFT_OLD_GAME_FILE
Old save game or scenario file.
Definition fileio_type.h:31
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ SCENARIO_DIR
Base directory for all scenarios.
@ BASE_DIR
Base directory for all subdirectories.
@ SAVE_DIR
Base directory for all savegames.
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
AbstractFileType
The different abstract types of files that the system knows about.
Definition fileio_type.h:16
@ FT_NONE
nothing to do
Definition fileio_type.h:17
@ FT_INVALID
Invalid or unknown file type.
Definition fileio_type.h:23
LoadCheckData _load_check_data
Data loaded from save during SL_LOAD_CHECK.
Definition fios_gui.cpp:41
fluid_settings_t * settings
FluidSynth settings handle.
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
@ GLAT_LOAD
Game loaded.
Definition gamelog.h:18
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp: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
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:325
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:556
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:380
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:66
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:69
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:67
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:206
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:68
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:498
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:70
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:362
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:363
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:592
std::vector< SaveLoad > SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
Load a table header in a savegame compatible way.
void SlWriteByte(uint8_t b)
Wrapper for writing a byte to the dumper.
Definition saveload.cpp:405
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:822
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:361
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:663
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:369
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:786
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:646
NeedLength
Definition saveload.cpp:81
@ NL_WANTLENGTH
writing length and data
Definition saveload.cpp:83
@ NL_NONE
not working in NeedLength mode
Definition saveload.cpp:82
@ NL_CALCLENGTH
need to calculate the length
Definition saveload.cpp:84
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:300
static void SlStdString(void *ptr, VarType conv)
Save/Load a std::string.
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:355
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:705
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:903
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:456
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:73
@ SLA_LOAD
loading
Definition saveload.cpp:74
@ SLA_NULL
null all pointers (on loading error)
Definition saveload.cpp:77
@ SLA_SAVE
saving
Definition saveload.cpp:75
@ SLA_LOAD_CHECK
partial loading into _load_check_data
Definition saveload.cpp:78
@ SLA_PTRS
fixing pointers
Definition saveload.cpp:76
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:769
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:63
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:848
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:64
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:717
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:396
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:204
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:622
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:798
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:919
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:88
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:990
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:523
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.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:57
@ SBI_SAVELOAD_FINISH
finished saving
@ SBI_SAVELOAD_START
started saving
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:277
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
Definition string.cpp:125
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition string.cpp:437
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition string.cpp:478
int8_t Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
StringValidationSettings
Settings for the string validation.
Definition string_type.h:44
@ SVS_ALLOW_CONTROL_CODE
Allow the special control codes.
Definition string_type.h:48
@ SVS_ALLOW_NEWLINE
Allow newlines; replaces '\r ' with ' ' during processing.
Definition string_type.h:47
@ SVS_REPLACE_WITH_QUESTION_MARK
Replace the unknown/bad bits with question marks.
Definition string_type.h:46
void 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:338
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
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:77
A savegame name automatically numbered.
Definition fios.h:128
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:47
GRFConfigList grfconfig
NewGrf configuration from save.
Definition fios.h:46
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:132
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:134
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:141
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:133
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:135
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:175
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:156
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:91
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:93
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:102
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:96
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:124
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:95
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:92
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:94
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:182
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:195
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:193
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:192
StringID error_str
the translatable error message to show
Definition saveload.cpp:198
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:183
std::string extra_msg
the error message
Definition saveload.cpp:199
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:184
uint8_t block_mode
???
Definition saveload.cpp:185
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:201
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:196
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:190
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:188
bool error
did an error occur or not
Definition saveload.cpp:186
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:189
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
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