OpenTTD Source 20250426-master-gbb1d561369
saveload.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
23#include "../stdafx.h"
24#include "../debug.h"
25#include "../station_base.h"
26#include "../thread.h"
27#include "../town.h"
28#include "../network/network.h"
29#include "../window_func.h"
30#include "../strings_func.h"
31#include "../core/endian_func.hpp"
32#include "../core/string_builder.hpp"
33#include "../core/string_consumer.hpp"
34#include "../vehicle_base.h"
35#include "../company_func.h"
36#include "../timer/timer_game_economy.h"
37#include "../autoreplace_base.h"
38#include "../roadstop_base.h"
39#include "../linkgraph/linkgraph.h"
40#include "../linkgraph/linkgraphjob.h"
41#include "../statusbar_gui.h"
42#include "../fileio_func.h"
43#include "../gamelog.h"
44#include "../string_func.h"
45#include "../fios.h"
46#include "../error.h"
47#include "../strings_type.h"
48#include "../newgrf_railtype.h"
49#include "../newgrf_roadtype.h"
50#include "../settings_internal.h"
51#include "saveload_internal.h"
52#include "saveload_filter.h"
53
54#include <atomic>
55#ifdef __EMSCRIPTEN__
56# include <emscripten.h>
57#endif
58
59#include "table/strings.h"
60
61#include "../safeguards.h"
62
64
67
68uint32_t _ttdp_version;
71std::string _savegame_format;
73
82
83enum NeedLength : uint8_t {
84 NL_NONE = 0,
87};
88
90static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
91
93struct ReadBuffer {
95 uint8_t *bufp = nullptr;
96 uint8_t *bufe = nullptr;
97 std::shared_ptr<LoadFilter> reader{};
98 size_t read = 0;
99
104 ReadBuffer(std::shared_ptr<LoadFilter> reader) : reader(std::move(reader))
105 {
106 }
107
108 inline uint8_t ReadByte()
109 {
110 if (this->bufp == this->bufe) {
111 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
112 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
113
114 this->read += len;
115 this->bufp = this->buf;
116 this->bufe = this->buf + len;
117 }
118
119 return *this->bufp++;
120 }
121
126 size_t GetSize() const
127 {
128 return this->read - (this->bufe - this->bufp);
129 }
130};
131
132
135 std::vector<std::unique_ptr<uint8_t[]>> blocks{};
136 uint8_t *buf = nullptr;
137 uint8_t *bufe = nullptr;
138
143 inline void WriteByte(uint8_t b)
144 {
145 /* Are we at the end of this chunk? */
146 if (this->buf == this->bufe) {
147 this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
148 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
149 }
150
151 *this->buf++ = b;
152 }
153
158 void Flush(std::shared_ptr<SaveFilter> writer)
159 {
160 uint i = 0;
161 size_t t = this->GetSize();
162
163 while (t > 0) {
164 size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
165
166 writer->Write(this->blocks[i++].get(), to_write);
167 t -= to_write;
168 }
169
170 writer->Finish();
171 }
172
177 size_t GetSize() const
178 {
179 return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
180 }
181};
182
187 uint8_t block_mode;
188 bool error;
189
190 size_t obj_len;
191 int array_index, last_array_index;
193
194 std::unique_ptr<MemoryDumper> dumper;
195 std::shared_ptr<SaveFilter> sf;
196
197 std::unique_ptr<ReadBuffer> reader;
198 std::shared_ptr<LoadFilter> lf;
199
201 std::string extra_msg;
202
204};
205
207
208static const std::vector<ChunkHandlerRef> &ChunkHandlers()
209{
210 /* These define the chunks */
211 extern const ChunkHandlerTable _gamelog_chunk_handlers;
212 extern const ChunkHandlerTable _map_chunk_handlers;
213 extern const ChunkHandlerTable _misc_chunk_handlers;
214 extern const ChunkHandlerTable _name_chunk_handlers;
215 extern const ChunkHandlerTable _cheat_chunk_handlers;
216 extern const ChunkHandlerTable _setting_chunk_handlers;
217 extern const ChunkHandlerTable _company_chunk_handlers;
218 extern const ChunkHandlerTable _engine_chunk_handlers;
219 extern const ChunkHandlerTable _veh_chunk_handlers;
220 extern const ChunkHandlerTable _waypoint_chunk_handlers;
221 extern const ChunkHandlerTable _depot_chunk_handlers;
222 extern const ChunkHandlerTable _order_chunk_handlers;
223 extern const ChunkHandlerTable _town_chunk_handlers;
224 extern const ChunkHandlerTable _sign_chunk_handlers;
225 extern const ChunkHandlerTable _station_chunk_handlers;
226 extern const ChunkHandlerTable _industry_chunk_handlers;
227 extern const ChunkHandlerTable _economy_chunk_handlers;
228 extern const ChunkHandlerTable _subsidy_chunk_handlers;
229 extern const ChunkHandlerTable _cargomonitor_chunk_handlers;
230 extern const ChunkHandlerTable _goal_chunk_handlers;
231 extern const ChunkHandlerTable _story_page_chunk_handlers;
232 extern const ChunkHandlerTable _league_chunk_handlers;
233 extern const ChunkHandlerTable _ai_chunk_handlers;
234 extern const ChunkHandlerTable _game_chunk_handlers;
235 extern const ChunkHandlerTable _animated_tile_chunk_handlers;
236 extern const ChunkHandlerTable _newgrf_chunk_handlers;
237 extern const ChunkHandlerTable _group_chunk_handlers;
238 extern const ChunkHandlerTable _cargopacket_chunk_handlers;
239 extern const ChunkHandlerTable _autoreplace_chunk_handlers;
240 extern const ChunkHandlerTable _labelmaps_chunk_handlers;
241 extern const ChunkHandlerTable _linkgraph_chunk_handlers;
242 extern const ChunkHandlerTable _airport_chunk_handlers;
243 extern const ChunkHandlerTable _object_chunk_handlers;
244 extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
245 extern const ChunkHandlerTable _water_region_chunk_handlers;
246 extern const ChunkHandlerTable _randomizer_chunk_handlers;
247
249 static const ChunkHandlerTable _chunk_handler_tables[] = {
250 _gamelog_chunk_handlers,
251 _map_chunk_handlers,
252 _misc_chunk_handlers,
253 _name_chunk_handlers,
254 _cheat_chunk_handlers,
255 _setting_chunk_handlers,
256 _veh_chunk_handlers,
257 _waypoint_chunk_handlers,
258 _depot_chunk_handlers,
259 _order_chunk_handlers,
260 _industry_chunk_handlers,
261 _economy_chunk_handlers,
262 _subsidy_chunk_handlers,
263 _cargomonitor_chunk_handlers,
264 _goal_chunk_handlers,
265 _story_page_chunk_handlers,
266 _league_chunk_handlers,
267 _engine_chunk_handlers,
268 _town_chunk_handlers,
269 _sign_chunk_handlers,
270 _station_chunk_handlers,
271 _company_chunk_handlers,
272 _ai_chunk_handlers,
273 _game_chunk_handlers,
274 _animated_tile_chunk_handlers,
275 _newgrf_chunk_handlers,
276 _group_chunk_handlers,
277 _cargopacket_chunk_handlers,
278 _autoreplace_chunk_handlers,
279 _labelmaps_chunk_handlers,
280 _linkgraph_chunk_handlers,
281 _airport_chunk_handlers,
282 _object_chunk_handlers,
283 _persistent_storage_chunk_handlers,
284 _water_region_chunk_handlers,
285 _randomizer_chunk_handlers,
286 };
287
288 static std::vector<ChunkHandlerRef> _chunk_handlers;
289
290 if (_chunk_handlers.empty()) {
291 for (auto &chunk_handler_table : _chunk_handler_tables) {
292 for (auto &chunk_handler : chunk_handler_table) {
293 _chunk_handlers.push_back(chunk_handler);
294 }
295 }
296 }
297
298 return _chunk_handlers;
299}
300
302static void SlNullPointers()
303{
305
306 /* We don't want any savegame conversion code to run
307 * during NULLing; especially those that try to get
308 * pointers from other pools. */
310
311 for (const ChunkHandler &ch : ChunkHandlers()) {
312 Debug(sl, 3, "Nulling pointers for {}", ch.GetName());
313 ch.FixPointers();
314 }
315
316 assert(_sl.action == SLA_NULL);
317}
318
327[[noreturn]] void SlError(StringID string, const std::string &extra_msg)
328{
329 /* Distinguish between loading into _load_check_data vs. normal save/load. */
330 if (_sl.action == SLA_LOAD_CHECK) {
331 _load_check_data.error = string;
332 _load_check_data.error_msg = extra_msg;
333 } else {
334 _sl.error_str = string;
335 _sl.extra_msg = extra_msg;
336 }
337
338 /* We have to nullptr all pointers here; we might be in a state where
339 * the pointers are actually filled with indices, which means that
340 * when we access them during cleaning the pool dereferences of
341 * those indices will be made with segmentation faults as result. */
343
344 /* Logging could be active. */
345 _gamelog.StopAnyAction();
346
347 throw std::exception();
348}
349
357[[noreturn]] void SlErrorCorrupt(const std::string &msg)
358{
359 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
360}
361
362
363typedef void (*AsyncSaveFinishProc)();
364static std::atomic<AsyncSaveFinishProc> _async_save_finish;
365static std::thread _save_thread;
366
372{
373 if (_exit_game) return;
374 while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10);
375
376 _async_save_finish.store(proc, std::memory_order_release);
377}
378
383{
384 AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel);
385 if (proc == nullptr) return;
386
387 proc();
388
389 if (_save_thread.joinable()) {
390 _save_thread.join();
391 }
392}
393
398uint8_t SlReadByte()
399{
400 return _sl.reader->ReadByte();
401}
402
407void SlWriteByte(uint8_t b)
408{
409 _sl.dumper->WriteByte(b);
410}
411
412static inline int SlReadUint16()
413{
414 int x = SlReadByte() << 8;
415 return x | SlReadByte();
416}
417
418static inline uint32_t SlReadUint32()
419{
420 uint32_t x = SlReadUint16() << 16;
421 return x | SlReadUint16();
422}
423
424static inline uint64_t SlReadUint64()
425{
426 uint32_t x = SlReadUint32();
427 uint32_t y = SlReadUint32();
428 return (uint64_t)x << 32 | y;
429}
430
431static inline void SlWriteUint16(uint16_t v)
432{
433 SlWriteByte(GB(v, 8, 8));
434 SlWriteByte(GB(v, 0, 8));
435}
436
437static inline void SlWriteUint32(uint32_t v)
438{
439 SlWriteUint16(GB(v, 16, 16));
440 SlWriteUint16(GB(v, 0, 16));
441}
442
443static inline void SlWriteUint64(uint64_t x)
444{
445 SlWriteUint32((uint32_t)(x >> 32));
446 SlWriteUint32((uint32_t)x);
447}
448
458static uint SlReadSimpleGamma()
459{
460 uint i = SlReadByte();
461 if (HasBit(i, 7)) {
462 i &= ~0x80;
463 if (HasBit(i, 6)) {
464 i &= ~0x40;
465 if (HasBit(i, 5)) {
466 i &= ~0x20;
467 if (HasBit(i, 4)) {
468 i &= ~0x10;
469 if (HasBit(i, 3)) {
470 SlErrorCorrupt("Unsupported gamma");
471 }
472 i = SlReadByte(); // 32 bits only.
473 }
474 i = (i << 8) | SlReadByte();
475 }
476 i = (i << 8) | SlReadByte();
477 }
478 i = (i << 8) | SlReadByte();
479 }
480 return i;
481}
482
500static void SlWriteSimpleGamma(size_t i)
501{
502 if (i >= (1 << 7)) {
503 if (i >= (1 << 14)) {
504 if (i >= (1 << 21)) {
505 if (i >= (1 << 28)) {
506 assert(i <= UINT32_MAX); // We can only support 32 bits for now.
507 SlWriteByte((uint8_t)(0xF0));
508 SlWriteByte((uint8_t)(i >> 24));
509 } else {
510 SlWriteByte((uint8_t)(0xE0 | (i >> 24)));
511 }
512 SlWriteByte((uint8_t)(i >> 16));
513 } else {
514 SlWriteByte((uint8_t)(0xC0 | (i >> 16)));
515 }
516 SlWriteByte((uint8_t)(i >> 8));
517 } else {
518 SlWriteByte((uint8_t)(0x80 | (i >> 8)));
519 }
520 }
521 SlWriteByte((uint8_t)i);
522}
523
525static inline uint SlGetGammaLength(size_t i)
526{
527 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)) + (i >= (1 << 28));
528}
529
530static inline uint SlReadSparseIndex()
531{
532 return SlReadSimpleGamma();
533}
534
535static inline void SlWriteSparseIndex(uint index)
536{
537 SlWriteSimpleGamma(index);
538}
539
540static inline uint SlReadArrayLength()
541{
542 return SlReadSimpleGamma();
543}
544
545static inline void SlWriteArrayLength(size_t length)
546{
547 SlWriteSimpleGamma(length);
548}
549
550static inline uint SlGetArrayLength(size_t length)
551{
552 return SlGetGammaLength(length);
553}
554
558static uint8_t GetSavegameFileType(const SaveLoad &sld)
559{
560 switch (sld.cmd) {
561 case SL_VAR:
562 return GetVarFileType(sld.conv); break;
563
564 case SL_STDSTR:
565 case SL_ARR:
566 case SL_VECTOR:
567 case SL_DEQUE:
568 return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
569
570 case SL_REF:
571 return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
572
573 case SL_REFLIST:
574 case SL_REFVECTOR:
575 return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
576
577 case SL_SAVEBYTE:
578 return SLE_FILE_U8;
579
580 case SL_STRUCT:
581 case SL_STRUCTLIST:
582 return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
583
584 default: NOT_REACHED();
585 }
586}
587
594static inline uint SlCalcConvMemLen(VarType conv)
595{
596 switch (GetVarMemType(conv)) {
597 case SLE_VAR_BL: return sizeof(bool);
598 case SLE_VAR_I8: return sizeof(int8_t);
599 case SLE_VAR_U8: return sizeof(uint8_t);
600 case SLE_VAR_I16: return sizeof(int16_t);
601 case SLE_VAR_U16: return sizeof(uint16_t);
602 case SLE_VAR_I32: return sizeof(int32_t);
603 case SLE_VAR_U32: return sizeof(uint32_t);
604 case SLE_VAR_I64: return sizeof(int64_t);
605 case SLE_VAR_U64: return sizeof(uint64_t);
606 case SLE_VAR_NULL: return 0;
607
608 case SLE_VAR_STR:
609 case SLE_VAR_STRQ:
610 return SlReadArrayLength();
611
612 case SLE_VAR_NAME:
613 default:
614 NOT_REACHED();
615 }
616}
617
624static inline uint8_t SlCalcConvFileLen(VarType conv)
625{
626 switch (GetVarFileType(conv)) {
627 case SLE_FILE_END: return 0;
628 case SLE_FILE_I8: return sizeof(int8_t);
629 case SLE_FILE_U8: return sizeof(uint8_t);
630 case SLE_FILE_I16: return sizeof(int16_t);
631 case SLE_FILE_U16: return sizeof(uint16_t);
632 case SLE_FILE_I32: return sizeof(int32_t);
633 case SLE_FILE_U32: return sizeof(uint32_t);
634 case SLE_FILE_I64: return sizeof(int64_t);
635 case SLE_FILE_U64: return sizeof(uint64_t);
636 case SLE_FILE_STRINGID: return sizeof(uint16_t);
637
638 case SLE_FILE_STRING:
639 return SlReadArrayLength();
640
641 case SLE_FILE_STRUCT:
642 default:
643 NOT_REACHED();
644 }
645}
646
648static inline size_t SlCalcRefLen()
649{
650 return IsSavegameVersionBefore(SLV_69) ? 2 : 4;
651}
652
653void SlSetArrayIndex(uint index)
654{
656 _sl.array_index = index;
657}
658
659static size_t _next_offs;
660
666{
667 /* After reading in the whole array inside the loop
668 * we must have read in all the data, so we must be at end of current block. */
669 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) {
670 SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position {}, actually at {}", _next_offs, _sl.reader->GetSize());
671 }
672
673 for (;;) {
674 uint length = SlReadArrayLength();
675 if (length == 0) {
676 assert(!_sl.expect_table_header);
677 _next_offs = 0;
678 return -1;
679 }
680
681 _sl.obj_len = --length;
682 _next_offs = _sl.reader->GetSize() + length;
683
685 _sl.expect_table_header = false;
686 return INT32_MAX;
687 }
688
689 int index;
690 switch (_sl.block_mode) {
691 case CH_SPARSE_TABLE:
692 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
693 case CH_TABLE:
694 case CH_ARRAY: index = _sl.array_index++; break;
695 default:
696 Debug(sl, 0, "SlIterateArray error");
697 return -1; // error
698 }
699
700 if (length != 0) return index;
701 }
702}
703
708{
709 while (SlIterateArray() != -1) {
710 SlSkipBytes(_next_offs - _sl.reader->GetSize());
711 }
712}
713
719void SlSetLength(size_t length)
720{
721 assert(_sl.action == SLA_SAVE);
722
723 switch (_sl.need_length) {
724 case NL_WANTLENGTH:
726 if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) {
727 _sl.expect_table_header = false;
728 SlWriteArrayLength(length + 1);
729 break;
730 }
731
732 switch (_sl.block_mode) {
733 case CH_RIFF:
734 /* Ugly encoding of >16M RIFF chunks
735 * The lower 24 bits are normal
736 * The uppermost 4 bits are bits 24:27 */
737 assert(length < (1 << 28));
738 SlWriteUint32((uint32_t)((length & 0xFFFFFF) | ((length >> 24) << 28)));
739 break;
740 case CH_TABLE:
741 case CH_ARRAY:
742 assert(_sl.last_array_index <= _sl.array_index);
743 while (++_sl.last_array_index <= _sl.array_index) {
744 SlWriteArrayLength(1);
745 }
746 SlWriteArrayLength(length + 1);
747 break;
748 case CH_SPARSE_TABLE:
749 case CH_SPARSE_ARRAY:
750 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
751 SlWriteSparseIndex(_sl.array_index);
752 break;
753 default: NOT_REACHED();
754 }
755 break;
756
757 case NL_CALCLENGTH:
758 _sl.obj_len += (int)length;
759 break;
760
761 default: NOT_REACHED();
762 }
763}
764
771static void SlCopyBytes(void *ptr, size_t length)
772{
773 uint8_t *p = (uint8_t *)ptr;
774
775 switch (_sl.action) {
776 case SLA_LOAD_CHECK:
777 case SLA_LOAD:
778 for (; length != 0; length--) *p++ = SlReadByte();
779 break;
780 case SLA_SAVE:
781 for (; length != 0; length--) SlWriteByte(*p++);
782 break;
783 default: NOT_REACHED();
784 }
785}
786
789{
790 return _sl.obj_len;
791}
792
800int64_t ReadValue(const void *ptr, VarType conv)
801{
802 switch (GetVarMemType(conv)) {
803 case SLE_VAR_BL: return (*(const bool *)ptr != 0);
804 case SLE_VAR_I8: return *(const int8_t *)ptr;
805 case SLE_VAR_U8: return *(const uint8_t *)ptr;
806 case SLE_VAR_I16: return *(const int16_t *)ptr;
807 case SLE_VAR_U16: return *(const uint16_t*)ptr;
808 case SLE_VAR_I32: return *(const int32_t *)ptr;
809 case SLE_VAR_U32: return *(const uint32_t*)ptr;
810 case SLE_VAR_I64: return *(const int64_t *)ptr;
811 case SLE_VAR_U64: return *(const uint64_t*)ptr;
812 case SLE_VAR_NULL:return 0;
813 default: NOT_REACHED();
814 }
815}
816
824void WriteValue(void *ptr, VarType conv, int64_t val)
825{
826 switch (GetVarMemType(conv)) {
827 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
828 case SLE_VAR_I8: *(int8_t *)ptr = val; break;
829 case SLE_VAR_U8: *(uint8_t *)ptr = val; break;
830 case SLE_VAR_I16: *(int16_t *)ptr = val; break;
831 case SLE_VAR_U16: *(uint16_t*)ptr = val; break;
832 case SLE_VAR_I32: *(int32_t *)ptr = val; break;
833 case SLE_VAR_U32: *(uint32_t*)ptr = val; break;
834 case SLE_VAR_I64: *(int64_t *)ptr = val; break;
835 case SLE_VAR_U64: *(uint64_t*)ptr = val; break;
836 case SLE_VAR_NAME: *reinterpret_cast<std::string *>(ptr) = CopyFromOldName(val); break;
837 case SLE_VAR_NULL: break;
838 default: NOT_REACHED();
839 }
840}
841
850static void SlSaveLoadConv(void *ptr, VarType conv)
851{
852 switch (_sl.action) {
853 case SLA_SAVE: {
854 int64_t x = ReadValue(ptr, conv);
855
856 /* Write the value to the file and check if its value is in the desired range */
857 switch (GetVarFileType(conv)) {
858 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
859 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
860 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
862 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
863 case SLE_FILE_I32:
864 case SLE_FILE_U32: SlWriteUint32((uint32_t)x);break;
865 case SLE_FILE_I64:
866 case SLE_FILE_U64: SlWriteUint64(x);break;
867 default: NOT_REACHED();
868 }
869 break;
870 }
871 case SLA_LOAD_CHECK:
872 case SLA_LOAD: {
873 int64_t x;
874 /* Read a value from the file */
875 switch (GetVarFileType(conv)) {
876 case SLE_FILE_I8: x = (int8_t )SlReadByte(); break;
877 case SLE_FILE_U8: x = (uint8_t )SlReadByte(); break;
878 case SLE_FILE_I16: x = (int16_t )SlReadUint16(); break;
879 case SLE_FILE_U16: x = (uint16_t)SlReadUint16(); break;
880 case SLE_FILE_I32: x = (int32_t )SlReadUint32(); break;
881 case SLE_FILE_U32: x = (uint32_t)SlReadUint32(); break;
882 case SLE_FILE_I64: x = (int64_t )SlReadUint64(); break;
883 case SLE_FILE_U64: x = (uint64_t)SlReadUint64(); break;
884 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16_t)SlReadUint16()); break;
885 default: NOT_REACHED();
886 }
887
888 /* Write The value to the struct. These ARE endian safe. */
889 WriteValue(ptr, conv, x);
890 break;
891 }
892 case SLA_PTRS: break;
893 case SLA_NULL: break;
894 default: NOT_REACHED();
895 }
896}
897
905static inline size_t SlCalcStdStringLen(const void *ptr)
906{
907 const std::string *str = reinterpret_cast<const std::string *>(ptr);
908
909 size_t len = str->length();
910 return len + SlGetArrayLength(len); // also include the length of the index
911}
912
913
921void FixSCCEncoded(std::string &str, bool fix_code)
922{
923 if (str.empty()) return;
924
925 /* We need to convert from old escape-style encoding to record separator encoding.
926 * Initial `<SCC_ENCODED><STRINGID>` stays the same.
927 *
928 * `:<SCC_ENCODED><STRINGID>` becomes `<RS><SCC_ENCODED><STRINGID>`
929 * `:<HEX>` becomes `<RS><SCC_ENCODED_NUMERIC><HEX>`
930 * `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
931 */
932 std::string result;
933 StringBuilder builder(result);
934
935 bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
936 bool in_string = false; // Set if we in a string, between double-quotes.
937 bool need_type = true; // Set if a parameter type needs to be emitted.
938
939 StringConsumer consumer(str);
940 while (consumer.AnyBytesLeft()) {
941 char32_t c;
942 if (auto r = consumer.TryReadUtf8(); r.has_value()) {
943 c = *r;
944 } else {
945 break;
946 }
947 if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
948 builder.PutUtf8(SCC_ENCODED);
949 need_type = false;
950 is_encoded = true;
951 continue;
952 }
953
954 /* If the first character is not SCC_ENCODED then we don't have to do any conversion. */
955 if (!is_encoded) return;
956
957 if (c == '"') {
958 in_string = !in_string;
959 if (in_string && need_type) {
960 /* Started a new string parameter. */
962 need_type = false;
963 }
964 continue;
965 }
966
967 if (!in_string && c == ':') {
968 builder.PutUtf8(SCC_RECORD_SEPARATOR);
969 need_type = true;
970 continue;
971 }
972 if (need_type) {
973 /* Started a new numeric parameter. */
975 need_type = false;
976 }
977
978 builder.PutUtf8(c);
979 }
980
981 str = std::move(result);
982}
983
988void FixSCCEncodedNegative(std::string &str)
989{
990 if (str.empty()) return;
991
992 StringConsumer consumer(str);
993
994 /* Check whether this is an encoded string */
995 if (!consumer.ReadUtf8If(SCC_ENCODED)) return;
996
997 std::string result;
998 StringBuilder builder(result);
999 builder.PutUtf8(SCC_ENCODED);
1000 while (consumer.AnyBytesLeft()) {
1001 /* Copy until next record */
1002 builder.Put(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::READ_ONE_SEPARATOR));
1003
1004 /* Check whether this is a numeric parameter */
1005 if (!consumer.ReadUtf8If(SCC_ENCODED_NUMERIC)) continue;
1007
1008 /* First try unsigned */
1009 if (auto u = consumer.TryReadIntegerBase<uint64_t>(16); u.has_value()) {
1010 builder.PutIntegerBase<uint64_t>(*u, 16);
1011 } else {
1012 /* Read as signed, store as unsigned */
1013 auto s = consumer.ReadIntegerBase<int64_t>(16);
1014 builder.PutIntegerBase<uint64_t>(static_cast<uint64_t>(s), 16);
1015 }
1016 }
1017
1018 str = std::move(result);
1019}
1020
1027void SlReadString(std::string &str, size_t length)
1028{
1029 str.resize(length);
1030 SlCopyBytes(str.data(), length);
1031}
1032
1038static void SlStdString(void *ptr, VarType conv)
1039{
1040 std::string *str = reinterpret_cast<std::string *>(ptr);
1041
1042 switch (_sl.action) {
1043 case SLA_SAVE: {
1044 size_t len = str->length();
1045 SlWriteArrayLength(len);
1046 SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->c_str())), len);
1047 break;
1048 }
1049
1050 case SLA_LOAD_CHECK:
1051 case SLA_LOAD: {
1052 size_t len = SlReadArrayLength();
1053 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1054 SlSkipBytes(len);
1055 return;
1056 }
1057
1058 SlReadString(*str, len);
1059
1061 if ((conv & SLF_ALLOW_CONTROL) != 0) {
1065 }
1066 if ((conv & SLF_ALLOW_NEWLINE) != 0) {
1068 }
1070 }
1071
1072 case SLA_PTRS: break;
1073 case SLA_NULL: break;
1074 default: NOT_REACHED();
1075 }
1076}
1077
1086static void SlCopyInternal(void *object, size_t length, VarType conv)
1087{
1088 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1089 assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes
1090 SlSkipBytes(length * SlCalcConvFileLen(conv));
1091 return;
1092 }
1093
1094 /* NOTICE - handle some buggy stuff, in really old versions everything was saved
1095 * as a byte-type. So detect this, and adjust object size accordingly */
1096 if (_sl.action != SLA_SAVE && _sl_version == 0) {
1097 /* all objects except difficulty settings */
1098 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
1099 conv == SLE_INT32 || conv == SLE_UINT32) {
1100 SlCopyBytes(object, length * SlCalcConvFileLen(conv));
1101 return;
1102 }
1103 /* used for conversion of Money 32bit->64bit */
1104 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
1105 for (uint i = 0; i < length; i++) {
1106 ((int64_t*)object)[i] = (int32_t)std::byteswap(SlReadUint32());
1107 }
1108 return;
1109 }
1110 }
1111
1112 /* If the size of elements is 1 byte both in file and memory, no special
1113 * conversion is needed, use specialized copy-copy function to speed up things */
1114 if (conv == SLE_INT8 || conv == SLE_UINT8) {
1115 SlCopyBytes(object, length);
1116 } else {
1117 uint8_t *a = (uint8_t*)object;
1118 uint8_t mem_size = SlCalcConvMemLen(conv);
1119
1120 for (; length != 0; length --) {
1121 SlSaveLoadConv(a, conv);
1122 a += mem_size; // get size
1123 }
1124 }
1125}
1126
1135void SlCopy(void *object, size_t length, VarType conv)
1136{
1137 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
1138
1139 /* Automatically calculate the length? */
1140 if (_sl.need_length != NL_NONE) {
1141 SlSetLength(length * SlCalcConvFileLen(conv));
1142 /* Determine length only? */
1143 if (_sl.need_length == NL_CALCLENGTH) return;
1144 }
1145
1146 SlCopyInternal(object, length, conv);
1147}
1148
1154static inline size_t SlCalcArrayLen(size_t length, VarType conv)
1155{
1156 return SlCalcConvFileLen(conv) * length + SlGetArrayLength(length);
1157}
1158
1165static void SlArray(void *array, size_t length, VarType conv)
1166{
1167 switch (_sl.action) {
1168 case SLA_SAVE:
1169 SlWriteArrayLength(length);
1170 SlCopyInternal(array, length, conv);
1171 return;
1172
1173 case SLA_LOAD_CHECK:
1174 case SLA_LOAD: {
1176 size_t sv_length = SlReadArrayLength();
1177 if (GetVarMemType(conv) == SLE_VAR_NULL) {
1178 /* We don't know this field, so we assume the length in the savegame is correct. */
1179 length = sv_length;
1180 } else if (sv_length != length) {
1181 /* If the SLE_ARR changes size, a savegame bump is required
1182 * and the developer should have written conversion lines.
1183 * Error out to make this more visible. */
1184 SlErrorCorrupt("Fixed-length array is of wrong length");
1185 }
1186 }
1187
1188 SlCopyInternal(array, length, conv);
1189 return;
1190 }
1191
1192 case SLA_PTRS:
1193 case SLA_NULL:
1194 return;
1195
1196 default:
1197 NOT_REACHED();
1198 }
1199}
1200
1211static size_t ReferenceToInt(const void *obj, SLRefType rt)
1212{
1213 assert(_sl.action == SLA_SAVE);
1214
1215 if (obj == nullptr) return 0;
1216
1217 switch (rt) {
1218 case REF_VEHICLE_OLD: // Old vehicles we save as new ones
1219 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
1220 case REF_STATION: return ((const Station*)obj)->index + 1;
1221 case REF_TOWN: return ((const Town*)obj)->index + 1;
1222 case REF_ORDER: return ((const Order*)obj)->index + 1;
1223 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
1224 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
1225 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
1226 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
1227 case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
1228 case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
1229 case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
1230 default: NOT_REACHED();
1231 }
1232}
1233
1244static void *IntToReference(size_t index, SLRefType rt)
1245{
1246 static_assert(sizeof(size_t) <= sizeof(void *));
1247
1248 assert(_sl.action == SLA_PTRS);
1249
1250 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
1251 * and should be loaded like that */
1252 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) {
1253 rt = REF_VEHICLE;
1254 }
1255
1256 /* No need to look up nullptr pointers, just return immediately */
1257 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr;
1258
1259 /* Correct index. Old vehicles were saved differently:
1260 * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
1261 if (rt != REF_VEHICLE_OLD) index--;
1262
1263 switch (rt) {
1264 case REF_ORDERLIST:
1265 if (OrderList::IsValidID(index)) return OrderList::Get(index);
1266 SlErrorCorrupt("Referencing invalid OrderList");
1267
1268 case REF_ORDER:
1269 if (Order::IsValidID(index)) return Order::Get(index);
1270 /* in old versions, invalid order was used to mark end of order list */
1271 if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr;
1272 SlErrorCorrupt("Referencing invalid Order");
1273
1274 case REF_VEHICLE_OLD:
1275 case REF_VEHICLE:
1276 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
1277 SlErrorCorrupt("Referencing invalid Vehicle");
1278
1279 case REF_STATION:
1280 if (Station::IsValidID(index)) return Station::Get(index);
1281 SlErrorCorrupt("Referencing invalid Station");
1282
1283 case REF_TOWN:
1284 if (Town::IsValidID(index)) return Town::Get(index);
1285 SlErrorCorrupt("Referencing invalid Town");
1286
1287 case REF_ROADSTOPS:
1288 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
1289 SlErrorCorrupt("Referencing invalid RoadStop");
1290
1291 case REF_ENGINE_RENEWS:
1292 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
1293 SlErrorCorrupt("Referencing invalid EngineRenew");
1294
1295 case REF_CARGO_PACKET:
1296 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
1297 SlErrorCorrupt("Referencing invalid CargoPacket");
1298
1299 case REF_STORAGE:
1300 if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index);
1301 SlErrorCorrupt("Referencing invalid PersistentStorage");
1302
1303 case REF_LINK_GRAPH:
1304 if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index);
1305 SlErrorCorrupt("Referencing invalid LinkGraph");
1306
1307 case REF_LINK_GRAPH_JOB:
1308 if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
1309 SlErrorCorrupt("Referencing invalid LinkGraphJob");
1310
1311 default: NOT_REACHED();
1312 }
1313}
1314
1320void SlSaveLoadRef(void *ptr, VarType conv)
1321{
1322 switch (_sl.action) {
1323 case SLA_SAVE:
1324 SlWriteUint32((uint32_t)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
1325 break;
1326 case SLA_LOAD_CHECK:
1327 case SLA_LOAD:
1328 *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32();
1329 break;
1330 case SLA_PTRS:
1331 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
1332 break;
1333 case SLA_NULL:
1334 *(void **)ptr = nullptr;
1335 break;
1336 default: NOT_REACHED();
1337 }
1338}
1339
1343template <template <typename, typename> typename Tstorage, typename Tvar, typename Tallocator = std::allocator<Tvar>>
1345 typedef Tstorage<Tvar, Tallocator> SlStorageT;
1346public:
1353 static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1354 {
1355 assert(cmd == SL_VAR || cmd == SL_REF);
1356
1357 const SlStorageT *list = static_cast<const SlStorageT *>(storage);
1358
1359 int type_size = SlGetArrayLength(list->size());
1360 int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32);
1361 return list->size() * item_size + type_size;
1362 }
1363
1364 static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv)
1365 {
1366 switch (cmd) {
1367 case SL_VAR: SlSaveLoadConv(item, conv); break;
1368 case SL_REF: SlSaveLoadRef(item, conv); break;
1369 case SL_STDSTR: SlStdString(item, conv); break;
1370 default:
1371 NOT_REACHED();
1372 }
1373 }
1374
1381 static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
1382 {
1383 assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
1384
1385 SlStorageT *list = static_cast<SlStorageT *>(storage);
1386
1387 switch (_sl.action) {
1388 case SLA_SAVE:
1389 SlWriteArrayLength(list->size());
1390
1391 for (auto &item : *list) {
1392 SlSaveLoadMember(cmd, &item, conv);
1393 }
1394 break;
1395
1396 case SLA_LOAD_CHECK:
1397 case SLA_LOAD: {
1398 size_t length;
1399 switch (cmd) {
1400 case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1401 case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
1402 case SL_STDSTR: length = SlReadArrayLength(); break;
1403 default: NOT_REACHED();
1404 }
1405
1406 list->clear();
1407 if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
1408 list->reserve(length);
1409 }
1410
1411 /* Load each value and push to the end of the storage. */
1412 for (size_t i = 0; i < length; i++) {
1413 Tvar &data = list->emplace_back();
1414 SlSaveLoadMember(cmd, &data, conv);
1415 }
1416 break;
1417 }
1418
1419 case SLA_PTRS:
1420 for (auto &item : *list) {
1421 SlSaveLoadMember(cmd, &item, conv);
1422 }
1423 break;
1424
1425 case SLA_NULL:
1426 list->clear();
1427 break;
1428
1429 default: NOT_REACHED();
1430 }
1431 }
1432};
1433
1439static inline size_t SlCalcRefListLen(const void *list, VarType conv)
1440{
1442}
1443
1449static void SlRefList(void *list, VarType conv)
1450{
1451 /* Automatically calculate the length? */
1452 if (_sl.need_length != NL_NONE) {
1453 SlSetLength(SlCalcRefListLen(list, conv));
1454 /* Determine length only? */
1455 if (_sl.need_length == NL_CALCLENGTH) return;
1456 }
1457
1459}
1460
1466static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
1467{
1469}
1470
1476static void SlRefVector(void *vector, VarType conv)
1477{
1478 /* Automatically calculate the length? */
1479 if (_sl.need_length != NL_NONE) {
1480 SlSetLength(SlCalcRefVectorLen(vector, conv));
1481 /* Determine length only? */
1482 if (_sl.need_length == NL_CALCLENGTH) return;
1483 }
1484
1486}
1487
1493static inline size_t SlCalcDequeLen(const void *deque, VarType conv)
1494{
1495 switch (GetVarMemType(conv)) {
1496 case SLE_VAR_BL: return SlStorageHelper<std::deque, bool>::SlCalcLen(deque, conv);
1497 case SLE_VAR_I8: return SlStorageHelper<std::deque, int8_t>::SlCalcLen(deque, conv);
1498 case SLE_VAR_U8: return SlStorageHelper<std::deque, uint8_t>::SlCalcLen(deque, conv);
1499 case SLE_VAR_I16: return SlStorageHelper<std::deque, int16_t>::SlCalcLen(deque, conv);
1500 case SLE_VAR_U16: return SlStorageHelper<std::deque, uint16_t>::SlCalcLen(deque, conv);
1501 case SLE_VAR_I32: return SlStorageHelper<std::deque, int32_t>::SlCalcLen(deque, conv);
1502 case SLE_VAR_U32: return SlStorageHelper<std::deque, uint32_t>::SlCalcLen(deque, conv);
1503 case SLE_VAR_I64: return SlStorageHelper<std::deque, int64_t>::SlCalcLen(deque, conv);
1504 case SLE_VAR_U64: return SlStorageHelper<std::deque, uint64_t>::SlCalcLen(deque, conv);
1505
1506 case SLE_VAR_STR:
1507 /* Strings are a length-prefixed field type in the savegame table format,
1508 * these may not be directly stored in another length-prefixed container type. */
1509 NOT_REACHED();
1510
1511 default: NOT_REACHED();
1512 }
1513}
1514
1520static void SlDeque(void *deque, VarType conv)
1521{
1522 switch (GetVarMemType(conv)) {
1523 case SLE_VAR_BL: SlStorageHelper<std::deque, bool>::SlSaveLoad(deque, conv); break;
1524 case SLE_VAR_I8: SlStorageHelper<std::deque, int8_t>::SlSaveLoad(deque, conv); break;
1525 case SLE_VAR_U8: SlStorageHelper<std::deque, uint8_t>::SlSaveLoad(deque, conv); break;
1526 case SLE_VAR_I16: SlStorageHelper<std::deque, int16_t>::SlSaveLoad(deque, conv); break;
1527 case SLE_VAR_U16: SlStorageHelper<std::deque, uint16_t>::SlSaveLoad(deque, conv); break;
1528 case SLE_VAR_I32: SlStorageHelper<std::deque, int32_t>::SlSaveLoad(deque, conv); break;
1529 case SLE_VAR_U32: SlStorageHelper<std::deque, uint32_t>::SlSaveLoad(deque, conv); break;
1530 case SLE_VAR_I64: SlStorageHelper<std::deque, int64_t>::SlSaveLoad(deque, conv); break;
1531 case SLE_VAR_U64: SlStorageHelper<std::deque, uint64_t>::SlSaveLoad(deque, conv); break;
1532
1533 case SLE_VAR_STR:
1534 /* Strings are a length-prefixed field type in the savegame table format,
1535 * these may not be directly stored in another length-prefixed container type.
1536 * This is permitted for load-related actions, because invalid fields of this type are present
1537 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1538 assert(_sl.action != SLA_SAVE);
1540 break;
1541
1542 default: NOT_REACHED();
1543 }
1544}
1545
1551static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
1552{
1553 switch (GetVarMemType(conv)) {
1554 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1555 case SLE_VAR_I8: return SlStorageHelper<std::vector, int8_t>::SlCalcLen(vector, conv);
1556 case SLE_VAR_U8: return SlStorageHelper<std::vector, uint8_t>::SlCalcLen(vector, conv);
1557 case SLE_VAR_I16: return SlStorageHelper<std::vector, int16_t>::SlCalcLen(vector, conv);
1558 case SLE_VAR_U16: return SlStorageHelper<std::vector, uint16_t>::SlCalcLen(vector, conv);
1559 case SLE_VAR_I32: return SlStorageHelper<std::vector, int32_t>::SlCalcLen(vector, conv);
1560 case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
1561 case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
1562 case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
1563
1564 case SLE_VAR_STR:
1565 /* Strings are a length-prefixed field type in the savegame table format,
1566 * these may not be directly stored in another length-prefixed container type. */
1567 NOT_REACHED();
1568
1569 default: NOT_REACHED();
1570 }
1571}
1572
1578static void SlVector(void *vector, VarType conv)
1579{
1580 switch (GetVarMemType(conv)) {
1581 case SLE_VAR_BL: NOT_REACHED(); // Not supported
1582 case SLE_VAR_I8: SlStorageHelper<std::vector, int8_t>::SlSaveLoad(vector, conv); break;
1583 case SLE_VAR_U8: SlStorageHelper<std::vector, uint8_t>::SlSaveLoad(vector, conv); break;
1584 case SLE_VAR_I16: SlStorageHelper<std::vector, int16_t>::SlSaveLoad(vector, conv); break;
1585 case SLE_VAR_U16: SlStorageHelper<std::vector, uint16_t>::SlSaveLoad(vector, conv); break;
1586 case SLE_VAR_I32: SlStorageHelper<std::vector, int32_t>::SlSaveLoad(vector, conv); break;
1587 case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
1588 case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
1589 case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
1590
1591 case SLE_VAR_STR:
1592 /* Strings are a length-prefixed field type in the savegame table format,
1593 * these may not be directly stored in another length-prefixed container type.
1594 * This is permitted for load-related actions, because invalid fields of this type are present
1595 * from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
1596 assert(_sl.action != SLA_SAVE);
1598 break;
1599
1600 default: NOT_REACHED();
1601 }
1602}
1603
1605static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
1606{
1607 return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
1608}
1609
1615static size_t SlCalcTableHeader(const SaveLoadTable &slt)
1616{
1617 size_t length = 0;
1618
1619 for (auto &sld : slt) {
1620 if (!SlIsObjectValidInSavegame(sld)) continue;
1621
1622 length += SlCalcConvFileLen(SLE_UINT8);
1623 length += SlCalcStdStringLen(&sld.name);
1624 }
1625
1626 length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
1627
1628 for (auto &sld : slt) {
1629 if (!SlIsObjectValidInSavegame(sld)) continue;
1630 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1631 length += SlCalcTableHeader(sld.handler->GetDescription());
1632 }
1633 }
1634
1635 return length;
1636}
1637
1644size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt)
1645{
1646 size_t length = 0;
1647
1648 /* Need to determine the length and write a length tag. */
1649 for (auto &sld : slt) {
1650 length += SlCalcObjMemberLength(object, sld);
1651 }
1652 return length;
1653}
1654
1655size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
1656{
1657 assert(_sl.action == SLA_SAVE);
1658
1659 if (!SlIsObjectValidInSavegame(sld)) return 0;
1660
1661 switch (sld.cmd) {
1662 case SL_VAR: return SlCalcConvFileLen(sld.conv);
1663 case SL_REF: return SlCalcRefLen();
1664 case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
1665 case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
1666 case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
1667 case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
1668 case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
1669 case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
1670 case SL_SAVEBYTE: return 1; // a byte is logically of size 1
1671 case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
1672
1673 case SL_STRUCT:
1674 case SL_STRUCTLIST: {
1675 NeedLength old_need_length = _sl.need_length;
1676 size_t old_obj_len = _sl.obj_len;
1677
1679 _sl.obj_len = 0;
1680
1681 /* Pretend that we are saving to collect the object size. Other
1682 * means are difficult, as we don't know the length of the list we
1683 * are about to store. */
1684 sld.handler->Save(const_cast<void *>(object));
1685 size_t length = _sl.obj_len;
1686
1687 _sl.obj_len = old_obj_len;
1688 _sl.need_length = old_need_length;
1689
1690 if (sld.cmd == SL_STRUCT) {
1691 length += SlGetArrayLength(1);
1692 }
1693
1694 return length;
1695 }
1696
1697 default: NOT_REACHED();
1698 }
1699 return 0;
1700}
1701
1702static bool SlObjectMember(void *object, const SaveLoad &sld)
1703{
1704 if (!SlIsObjectValidInSavegame(sld)) return false;
1705
1706 VarType conv = GB(sld.conv, 0, 8);
1707 switch (sld.cmd) {
1708 case SL_VAR:
1709 case SL_REF:
1710 case SL_ARR:
1711 case SL_REFLIST:
1712 case SL_REFVECTOR:
1713 case SL_DEQUE:
1714 case SL_VECTOR:
1715 case SL_STDSTR: {
1716 void *ptr = GetVariableAddress(object, sld);
1717
1718 switch (sld.cmd) {
1719 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
1720 case SL_REF: SlSaveLoadRef(ptr, conv); break;
1721 case SL_ARR: SlArray(ptr, sld.length, conv); break;
1722 case SL_REFLIST: SlRefList(ptr, conv); break;
1723 case SL_REFVECTOR: SlRefVector(ptr, conv); break;
1724 case SL_DEQUE: SlDeque(ptr, conv); break;
1725 case SL_VECTOR: SlVector(ptr, conv); break;
1726 case SL_STDSTR: SlStdString(ptr, sld.conv); break;
1727 default: NOT_REACHED();
1728 }
1729 break;
1730 }
1731
1732 /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object.
1733 * When loading, the value is read explicitly with SlReadByte() to determine which
1734 * object description to use. */
1735 case SL_SAVEBYTE: {
1736 void *ptr = GetVariableAddress(object, sld);
1737
1738 switch (_sl.action) {
1739 case SLA_SAVE: SlWriteByte(*(uint8_t *)ptr); break;
1740 case SLA_LOAD_CHECK:
1741 case SLA_LOAD:
1742 case SLA_PTRS:
1743 case SLA_NULL: break;
1744 default: NOT_REACHED();
1745 }
1746 break;
1747 }
1748
1749 case SL_NULL: {
1750 assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
1751
1752 switch (_sl.action) {
1753 case SLA_LOAD_CHECK:
1754 case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
1755 case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
1756 case SLA_PTRS:
1757 case SLA_NULL: break;
1758 default: NOT_REACHED();
1759 }
1760 break;
1761 }
1762
1763 case SL_STRUCT:
1764 case SL_STRUCTLIST:
1765 switch (_sl.action) {
1766 case SLA_SAVE: {
1767 if (sld.cmd == SL_STRUCT) {
1768 /* Store in the savegame if this struct was written or not. */
1769 SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
1770 }
1771 sld.handler->Save(object);
1772 break;
1773 }
1774
1775 case SLA_LOAD_CHECK: {
1778 }
1779 sld.handler->LoadCheck(object);
1780 break;
1781 }
1782
1783 case SLA_LOAD: {
1786 }
1787 sld.handler->Load(object);
1788 break;
1789 }
1790
1791 case SLA_PTRS:
1792 sld.handler->FixPointers(object);
1793 break;
1794
1795 case SLA_NULL: break;
1796 default: NOT_REACHED();
1797 }
1798 break;
1799
1800 default: NOT_REACHED();
1801 }
1802 return true;
1803}
1804
1809void SlSetStructListLength(size_t length)
1810{
1811 /* Automatically calculate the length? */
1812 if (_sl.need_length != NL_NONE) {
1813 SlSetLength(SlGetArrayLength(length));
1814 if (_sl.need_length == NL_CALCLENGTH) return;
1815 }
1816
1817 SlWriteArrayLength(length);
1818}
1819
1825size_t SlGetStructListLength(size_t limit)
1826{
1827 size_t length = SlReadArrayLength();
1828 if (length > limit) SlErrorCorrupt("List exceeds storage size");
1829
1830 return length;
1831}
1832
1838void SlObject(void *object, const SaveLoadTable &slt)
1839{
1840 /* Automatically calculate the length? */
1841 if (_sl.need_length != NL_NONE) {
1842 SlSetLength(SlCalcObjLength(object, slt));
1843 if (_sl.need_length == NL_CALCLENGTH) return;
1844 }
1845
1846 for (auto &sld : slt) {
1847 SlObjectMember(object, sld);
1848 }
1849}
1850
1856 void Save(void *) const override
1857 {
1858 NOT_REACHED();
1859 }
1860
1861 void Load(void *object) const override
1862 {
1863 size_t length = SlGetStructListLength(UINT32_MAX);
1864 for (; length > 0; length--) {
1865 SlObject(object, this->GetLoadDescription());
1866 }
1867 }
1868
1869 void LoadCheck(void *object) const override
1870 {
1871 this->Load(object);
1872 }
1873
1874 virtual SaveLoadTable GetDescription() const override
1875 {
1876 return {};
1877 }
1878
1880 {
1881 NOT_REACHED();
1882 }
1883};
1884
1891std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
1892{
1893 /* You can only use SlTableHeader if you are a CH_TABLE. */
1894 assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
1895
1896 switch (_sl.action) {
1897 case SLA_LOAD_CHECK:
1898 case SLA_LOAD: {
1899 std::vector<SaveLoad> saveloads;
1900
1901 /* Build a key lookup mapping based on the available fields. */
1902 std::map<std::string, const SaveLoad *> key_lookup;
1903 for (auto &sld : slt) {
1904 if (!SlIsObjectValidInSavegame(sld)) continue;
1905
1906 /* Check that there is only one active SaveLoad for a given name. */
1907 assert(key_lookup.find(sld.name) == key_lookup.end());
1908 key_lookup[sld.name] = &sld;
1909 }
1910
1911 while (true) {
1912 uint8_t type = 0;
1913 SlSaveLoadConv(&type, SLE_UINT8);
1914 if (type == SLE_FILE_END) break;
1915
1916 std::string key;
1917 SlStdString(&key, SLE_STR);
1918
1919 auto sld_it = key_lookup.find(key);
1920 if (sld_it == key_lookup.end()) {
1921 /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */
1922 Debug(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '{}' of type 0x{:02x} not found, skipping", key, type);
1923
1924 std::shared_ptr<SaveLoadHandler> handler = nullptr;
1925 SaveLoadType saveload_type;
1926 switch (type & SLE_FILE_TYPE_MASK) {
1927 case SLE_FILE_STRING:
1928 /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
1929 saveload_type = SL_STDSTR;
1930 break;
1931
1932 case SLE_FILE_STRUCT:
1933 /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */
1934 saveload_type = SL_STRUCTLIST;
1935 handler = std::make_shared<SlSkipHandler>();
1936 break;
1937
1938 default:
1939 saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
1940 break;
1941 }
1942
1943 /* We don't know this field, so read to nothing. */
1944 saveloads.emplace_back(std::move(key), saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, std::move(handler));
1945 continue;
1946 }
1947
1948 /* Validate the type of the field. If it is changed, the
1949 * savegame should have been bumped so we know how to do the
1950 * conversion. If this error triggers, that clearly didn't
1951 * happen and this is a friendly poke to the developer to bump
1952 * the savegame version and add conversion code. */
1953 uint8_t correct_type = GetSavegameFileType(*sld_it->second);
1954 if (correct_type != type) {
1955 Debug(sl, 1, "Field type for '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, correct_type, type);
1956 SlErrorCorrupt("Field type is different than expected");
1957 }
1958 saveloads.emplace_back(*sld_it->second);
1959 }
1960
1961 for (auto &sld : saveloads) {
1962 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1963 sld.handler->load_description = SlTableHeader(sld.handler->GetDescription());
1964 }
1965 }
1966
1967 return saveloads;
1968 }
1969
1970 case SLA_SAVE: {
1971 /* Automatically calculate the length? */
1972 if (_sl.need_length != NL_NONE) {
1974 if (_sl.need_length == NL_CALCLENGTH) break;
1975 }
1976
1977 for (auto &sld : slt) {
1978 if (!SlIsObjectValidInSavegame(sld)) continue;
1979 /* Make sure we are not storing empty keys. */
1980 assert(!sld.name.empty());
1981
1982 uint8_t type = GetSavegameFileType(sld);
1983 assert(type != SLE_FILE_END);
1984
1985 SlSaveLoadConv(&type, SLE_UINT8);
1986 SlStdString(const_cast<std::string *>(&sld.name), SLE_STR);
1987 }
1988
1989 /* Add an end-of-header marker. */
1990 uint8_t type = SLE_FILE_END;
1991 SlSaveLoadConv(&type, SLE_UINT8);
1992
1993 /* After the table, write down any sub-tables we might have. */
1994 for (auto &sld : slt) {
1995 if (!SlIsObjectValidInSavegame(sld)) continue;
1996 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
1997 /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */
1998 NeedLength old_need_length = _sl.need_length;
2000
2001 SlTableHeader(sld.handler->GetDescription());
2002
2003 _sl.need_length = old_need_length;
2004 }
2005 }
2006
2007 break;
2008 }
2009
2010 default: NOT_REACHED();
2011 }
2012
2013 return std::vector<SaveLoad>();
2014}
2015
2029std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
2030{
2031 assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
2032 /* CH_TABLE / CH_SPARSE_TABLE always have a header. */
2033 if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt);
2034
2035 std::vector<SaveLoad> saveloads;
2036
2037 /* Build a key lookup mapping based on the available fields. */
2038 std::map<std::string, std::vector<const SaveLoad *>> key_lookup;
2039 for (auto &sld : slt) {
2040 /* All entries should have a name; otherwise the entry should just be removed. */
2041 assert(!sld.name.empty());
2042
2043 key_lookup[sld.name].push_back(&sld);
2044 }
2045
2046 for (auto &slc : slct) {
2047 if (slc.name.empty()) {
2048 /* In old savegames there can be data we no longer care for. We
2049 * skip this by simply reading the amount of bytes indicated and
2050 * send those to /dev/null. */
2051 saveloads.emplace_back("", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr);
2052 } else {
2053 auto sld_it = key_lookup.find(slc.name);
2054 /* If this branch triggers, it means that an entry in the
2055 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
2056 * you rename a field in one and not in the other? */
2057 if (sld_it == key_lookup.end()) {
2058 /* This isn't an assert, as that leaves no information what
2059 * field was to blame. This way at least we have breadcrumbs. */
2060 Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
2061 SlErrorCorrupt("Internal error with savegame compatibility");
2062 }
2063 for (auto &sld : sld_it->second) {
2064 saveloads.push_back(*sld);
2065 }
2066 }
2067 }
2068
2069 for (auto &sld : saveloads) {
2070 if (!SlIsObjectValidInSavegame(sld)) continue;
2071 if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
2072 sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription());
2073 }
2074 }
2075
2076 return saveloads;
2077}
2078
2084{
2085 SlObject(nullptr, slt);
2086}
2087
2093void SlAutolength(AutolengthProc *proc, int arg)
2094{
2095 assert(_sl.action == SLA_SAVE);
2096
2097 /* Tell it to calculate the length */
2099 _sl.obj_len = 0;
2100 proc(arg);
2101
2102 /* Setup length */
2105
2106 size_t start_pos = _sl.dumper->GetSize();
2107 size_t expected_offs = start_pos + _sl.obj_len;
2108
2109 /* And write the stuff */
2110 proc(arg);
2111
2112 if (expected_offs != _sl.dumper->GetSize()) {
2113 SlErrorCorruptFmt("Invalid chunk size when writing autolength block, expected {}, got {}", _sl.obj_len, _sl.dumper->GetSize() - start_pos);
2114 }
2115}
2116
2117void ChunkHandler::LoadCheck(size_t len) const
2118{
2119 switch (_sl.block_mode) {
2120 case CH_TABLE:
2121 case CH_SPARSE_TABLE:
2122 SlTableHeader({});
2123 [[fallthrough]];
2124 case CH_ARRAY:
2125 case CH_SPARSE_ARRAY:
2126 SlSkipArray();
2127 break;
2128 case CH_RIFF:
2129 SlSkipBytes(len);
2130 break;
2131 default:
2132 NOT_REACHED();
2133 }
2134}
2135
2140static void SlLoadChunk(const ChunkHandler &ch)
2141{
2142 uint8_t m = SlReadByte();
2143
2145 _sl.obj_len = 0;
2146 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2147
2148 /* The header should always be at the start. Read the length; the
2149 * Load() should as first action process the header. */
2151 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2152 }
2153
2154 switch (_sl.block_mode) {
2155 case CH_TABLE:
2156 case CH_ARRAY:
2157 _sl.array_index = 0;
2158 ch.Load();
2159 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2160 break;
2161 case CH_SPARSE_TABLE:
2162 case CH_SPARSE_ARRAY:
2163 ch.Load();
2164 if (_next_offs != 0) SlErrorCorrupt("Invalid array length");
2165 break;
2166 case CH_RIFF: {
2167 /* Read length */
2168 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2169 len += SlReadUint16();
2170 _sl.obj_len = len;
2171 size_t start_pos = _sl.reader->GetSize();
2172 size_t endoffs = start_pos + len;
2173 ch.Load();
2174
2175 if (_sl.reader->GetSize() != endoffs) {
2176 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2177 }
2178 break;
2179 }
2180 default:
2181 SlErrorCorrupt("Invalid chunk type");
2182 break;
2183 }
2184
2185 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2186}
2187
2193static void SlLoadCheckChunk(const ChunkHandler &ch)
2194{
2195 uint8_t m = SlReadByte();
2196
2198 _sl.obj_len = 0;
2199 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2200
2201 /* The header should always be at the start. Read the length; the
2202 * LoadCheck() should as first action process the header. */
2204 if (SlIterateArray() != INT32_MAX) SlErrorCorrupt("Table chunk without header");
2205 }
2206
2207 switch (_sl.block_mode) {
2208 case CH_TABLE:
2209 case CH_ARRAY:
2210 _sl.array_index = 0;
2211 ch.LoadCheck();
2212 break;
2213 case CH_SPARSE_TABLE:
2214 case CH_SPARSE_ARRAY:
2215 ch.LoadCheck();
2216 break;
2217 case CH_RIFF: {
2218 /* Read length */
2219 size_t len = (SlReadByte() << 16) | ((m >> 4) << 24);
2220 len += SlReadUint16();
2221 _sl.obj_len = len;
2222 size_t start_pos = _sl.reader->GetSize();
2223 size_t endoffs = start_pos + len;
2224 ch.LoadCheck(len);
2225
2226 if (_sl.reader->GetSize() != endoffs) {
2227 SlErrorCorruptFmt("Invalid chunk size in RIFF in {} - expected {}, got {}", ch.GetName(), len, _sl.reader->GetSize() - start_pos);
2228 }
2229 break;
2230 }
2231 default:
2232 SlErrorCorrupt("Invalid chunk type");
2233 break;
2234 }
2235
2236 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2237}
2238
2244static void SlSaveChunk(const ChunkHandler &ch)
2245{
2246 if (ch.type == CH_READONLY) return;
2247
2248 SlWriteUint32(ch.id);
2249 Debug(sl, 2, "Saving chunk {}", ch.GetName());
2250
2251 _sl.block_mode = ch.type;
2252 _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
2253
2255
2256 switch (_sl.block_mode) {
2257 case CH_RIFF:
2258 ch.Save();
2259 break;
2260 case CH_TABLE:
2261 case CH_ARRAY:
2264 ch.Save();
2265 SlWriteArrayLength(0); // Terminate arrays
2266 break;
2267 case CH_SPARSE_TABLE:
2268 case CH_SPARSE_ARRAY:
2270 ch.Save();
2271 SlWriteArrayLength(0); // Terminate arrays
2272 break;
2273 default: NOT_REACHED();
2274 }
2275
2276 if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
2277}
2278
2280static void SlSaveChunks()
2281{
2282 for (auto &ch : ChunkHandlers()) {
2283 SlSaveChunk(ch);
2284 }
2285
2286 /* Terminator */
2287 SlWriteUint32(0);
2288}
2289
2296static const ChunkHandler *SlFindChunkHandler(uint32_t id)
2297{
2298 for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch;
2299 return nullptr;
2300}
2301
2303static void SlLoadChunks()
2304{
2305 uint32_t id;
2306 const ChunkHandler *ch;
2307
2308 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2309 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2310
2311 ch = SlFindChunkHandler(id);
2312 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2313 SlLoadChunk(*ch);
2314 }
2315}
2316
2319{
2320 uint32_t id;
2321 const ChunkHandler *ch;
2322
2323 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
2324 Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
2325
2326 ch = SlFindChunkHandler(id);
2327 if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
2328 SlLoadCheckChunk(*ch);
2329 }
2330}
2331
2333static void SlFixPointers()
2334{
2336
2337 for (const ChunkHandler &ch : ChunkHandlers()) {
2338 Debug(sl, 3, "Fixing pointers for {}", ch.GetName());
2339 ch.FixPointers();
2340 }
2341
2342 assert(_sl.action == SLA_PTRS);
2343}
2344
2345
2348 std::optional<FileHandle> file;
2349 long begin;
2350
2355 FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
2356 {
2357 }
2358
2361 {
2362 if (this->file.has_value()) {
2363 _game_session_stats.savegame_size = ftell(*this->file) - this->begin;
2364 }
2365 }
2366
2367 size_t Read(uint8_t *buf, size_t size) override
2368 {
2369 /* We're in the process of shutting down, i.e. in "failure" mode. */
2370 if (!this->file.has_value()) return 0;
2371
2372 return fread(buf, 1, size, *this->file);
2373 }
2374
2375 void Reset() override
2376 {
2377 clearerr(*this->file);
2378 if (fseek(*this->file, this->begin, SEEK_SET)) {
2379 Debug(sl, 1, "Could not reset the file reading");
2380 }
2381 }
2382};
2383
2386 std::optional<FileHandle> file;
2387
2392 FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
2393 {
2394 }
2395
2398 {
2399 this->Finish();
2400 }
2401
2402 void Write(uint8_t *buf, size_t size) override
2403 {
2404 /* We're in the process of shutting down, i.e. in "failure" mode. */
2405 if (!this->file.has_value()) return;
2406
2407 if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
2408 }
2409
2410 void Finish() override
2411 {
2412 if (this->file.has_value()) {
2413 _game_session_stats.savegame_size = ftell(*this->file);
2414 this->file.reset();
2415 }
2416 }
2417};
2418
2419/*******************************************
2420 ********** START OF LZO CODE **************
2421 *******************************************/
2422
2423#ifdef WITH_LZO
2424#include <lzo/lzo1x.h>
2425
2427static const uint LZO_BUFFER_SIZE = 8192;
2428
2435 LZOLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2436 {
2437 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2438 }
2439
2440 size_t Read(uint8_t *buf, size_t ssize) override
2441 {
2442 assert(ssize >= LZO_BUFFER_SIZE);
2443
2444 /* Buffer size is from the LZO docs plus the chunk header size. */
2445 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2446 uint32_t tmp[2];
2447 uint32_t size;
2448 lzo_uint len = ssize;
2449
2450 /* Read header*/
2451 if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
2452
2453 /* Check if size is bad */
2454 ((uint32_t*)out)[0] = size = tmp[1];
2455
2456 if (_sl_version != SL_MIN_VERSION) {
2457 tmp[0] = TO_BE32(tmp[0]);
2458 size = TO_BE32(size);
2459 }
2460
2461 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
2462
2463 /* Read block */
2464 if (this->chain->Read(out + sizeof(uint32_t), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2465
2466 /* Verify checksum */
2467 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32_t))) SlErrorCorrupt("Bad checksum");
2468
2469 /* Decompress */
2470 int ret = lzo1x_decompress_safe(out + sizeof(uint32_t) * 1, size, buf, &len, nullptr);
2471 if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
2472 return len;
2473 }
2474};
2475
2482 LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2483 {
2484 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2485 }
2486
2487 void Write(uint8_t *buf, size_t size) override
2488 {
2489 const lzo_bytep in = buf;
2490 /* Buffer size is from the LZO docs plus the chunk header size. */
2491 uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
2492 uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
2493 lzo_uint outlen;
2494
2495 do {
2496 /* Compress up to LZO_BUFFER_SIZE bytes at once. */
2497 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
2498 lzo1x_1_compress(in, len, out + sizeof(uint32_t) * 2, &outlen, wrkmem);
2499 ((uint32_t*)out)[1] = TO_BE32((uint32_t)outlen);
2500 ((uint32_t*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32_t), outlen + sizeof(uint32_t)));
2501 this->chain->Write(out, outlen + sizeof(uint32_t) * 2);
2502
2503 /* Move to next data chunk. */
2504 size -= len;
2505 in += len;
2506 } while (size > 0);
2507 }
2508};
2509
2510#endif /* WITH_LZO */
2511
2512/*********************************************
2513 ******** START OF NOCOMP CODE (uncompressed)*
2514 *********************************************/
2515
2522 NoCompLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2523 {
2524 }
2525
2526 size_t Read(uint8_t *buf, size_t size) override
2527 {
2528 return this->chain->Read(buf, size);
2529 }
2530};
2531
2538 NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(std::move(chain))
2539 {
2540 }
2541
2542 void Write(uint8_t *buf, size_t size) override
2543 {
2544 this->chain->Write(buf, size);
2545 }
2546};
2547
2548/********************************************
2549 ********** START OF ZLIB CODE **************
2550 ********************************************/
2551
2552#if defined(WITH_ZLIB)
2553#include <zlib.h>
2554
2557 z_stream z;
2559
2564 ZlibLoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain))
2565 {
2566 memset(&this->z, 0, sizeof(this->z));
2567 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2568 }
2569
2572 {
2573 inflateEnd(&this->z);
2574 }
2575
2576 size_t Read(uint8_t *buf, size_t size) override
2577 {
2578 this->z.next_out = buf;
2579 this->z.avail_out = (uint)size;
2580
2581 do {
2582 /* read more bytes from the file? */
2583 if (this->z.avail_in == 0) {
2584 this->z.next_in = this->fread_buf;
2585 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2586 }
2587
2588 /* inflate the data */
2589 int r = inflate(&this->z, 0);
2590 if (r == Z_STREAM_END) break;
2591
2592 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
2593 } while (this->z.avail_out != 0);
2594
2595 return size - this->z.avail_out;
2596 }
2597};
2598
2601 z_stream z;
2603
2609 ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain))
2610 {
2611 memset(&this->z, 0, sizeof(this->z));
2612 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2613 }
2614
2617 {
2618 deflateEnd(&this->z);
2619 }
2620
2627 void WriteLoop(uint8_t *p, size_t len, int mode)
2628 {
2629 uint n;
2630 this->z.next_in = p;
2631 this->z.avail_in = (uInt)len;
2632 do {
2633 this->z.next_out = this->fwrite_buf;
2634 this->z.avail_out = sizeof(this->fwrite_buf);
2635
2643 int r = deflate(&this->z, mode);
2644
2645 /* bytes were emitted? */
2646 if ((n = sizeof(this->fwrite_buf) - this->z.avail_out) != 0) {
2647 this->chain->Write(this->fwrite_buf, n);
2648 }
2649 if (r == Z_STREAM_END) break;
2650
2651 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
2652 } while (this->z.avail_in || !this->z.avail_out);
2653 }
2654
2655 void Write(uint8_t *buf, size_t size) override
2656 {
2657 this->WriteLoop(buf, size, 0);
2658 }
2659
2660 void Finish() override
2661 {
2662 this->WriteLoop(nullptr, 0, Z_FINISH);
2663 this->chain->Finish();
2664 }
2665};
2666
2667#endif /* WITH_ZLIB */
2668
2669/********************************************
2670 ********** START OF LZMA CODE **************
2671 ********************************************/
2672
2673#if defined(WITH_LIBLZMA)
2674#include <lzma.h>
2675
2682static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
2683
2686 lzma_stream lzma;
2688
2693 LZMALoadFilter(std::shared_ptr<LoadFilter> chain) : LoadFilter(std::move(chain)), lzma(_lzma_init)
2694 {
2695 /* Allow saves up to 256 MB uncompressed */
2696 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
2697 }
2698
2701 {
2702 lzma_end(&this->lzma);
2703 }
2704
2705 size_t Read(uint8_t *buf, size_t size) override
2706 {
2707 this->lzma.next_out = buf;
2708 this->lzma.avail_out = size;
2709
2710 do {
2711 /* read more bytes from the file? */
2712 if (this->lzma.avail_in == 0) {
2713 this->lzma.next_in = this->fread_buf;
2714 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
2715 }
2716
2717 /* inflate the data */
2718 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
2719 if (r == LZMA_STREAM_END) break;
2720 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2721 } while (this->lzma.avail_out != 0);
2722
2723 return size - this->lzma.avail_out;
2724 }
2725};
2726
2729 lzma_stream lzma;
2731
2737 LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init)
2738 {
2739 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
2740 }
2741
2744 {
2745 lzma_end(&this->lzma);
2746 }
2747
2754 void WriteLoop(uint8_t *p, size_t len, lzma_action action)
2755 {
2756 size_t n;
2757 this->lzma.next_in = p;
2758 this->lzma.avail_in = len;
2759 do {
2760 this->lzma.next_out = this->fwrite_buf;
2761 this->lzma.avail_out = sizeof(this->fwrite_buf);
2762
2763 lzma_ret r = lzma_code(&this->lzma, action);
2764
2765 /* bytes were emitted? */
2766 if ((n = sizeof(this->fwrite_buf) - this->lzma.avail_out) != 0) {
2767 this->chain->Write(this->fwrite_buf, n);
2768 }
2769 if (r == LZMA_STREAM_END) break;
2770 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
2771 } while (this->lzma.avail_in || !this->lzma.avail_out);
2772 }
2773
2774 void Write(uint8_t *buf, size_t size) override
2775 {
2776 this->WriteLoop(buf, size, LZMA_RUN);
2777 }
2778
2779 void Finish() override
2780 {
2781 this->WriteLoop(nullptr, 0, LZMA_FINISH);
2782 this->chain->Finish();
2783 }
2784};
2785
2786#endif /* WITH_LIBLZMA */
2787
2788/*******************************************
2789 ************* END OF CODE *****************
2790 *******************************************/
2791
2794 const char *name;
2795 uint32_t tag;
2796
2797 std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain);
2798 std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression);
2799
2803};
2804
2805static const uint32_t SAVEGAME_TAG_LZO = TO_BE32('OTTD');
2806static const uint32_t SAVEGAME_TAG_NONE = TO_BE32('OTTN');
2807static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32('OTTZ');
2808static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32('OTTX');
2809
2812#if defined(WITH_LZO)
2813 /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
2814 {"lzo", SAVEGAME_TAG_LZO, CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
2815#else
2816 {"lzo", SAVEGAME_TAG_LZO, nullptr, nullptr, 0, 0, 0},
2817#endif
2818 /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
2819 {"none", SAVEGAME_TAG_NONE, CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
2820#if defined(WITH_ZLIB)
2821 /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
2822 * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
2823 * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
2824 {"zlib", SAVEGAME_TAG_ZLIB, CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
2825#else
2826 {"zlib", SAVEGAME_TAG_ZLIB, nullptr, nullptr, 0, 0, 0},
2827#endif
2828#if defined(WITH_LIBLZMA)
2829 /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
2830 * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower.
2831 * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
2832 * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
2833 * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
2834 {"lzma", SAVEGAME_TAG_LZMA, CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
2835#else
2836 {"lzma", SAVEGAME_TAG_LZMA, nullptr, nullptr, 0, 0, 0},
2837#endif
2838};
2839
2846static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(const std::string &full_name)
2847{
2848 /* Find default savegame format, the highest one with which files can be written. */
2849 auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
2850 if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
2851
2852 const SaveLoadFormat &def = *it;
2853
2854 if (!full_name.empty()) {
2855 /* Get the ":..." of the compression level out of the way */
2856 size_t separator = full_name.find(':');
2857 bool has_comp_level = separator != std::string::npos;
2858 const std::string name(full_name, 0, has_comp_level ? separator : full_name.size());
2859
2860 for (const auto &slf : _saveload_formats) {
2861 if (slf.init_write != nullptr && name.compare(slf.name) == 0) {
2862 if (has_comp_level) {
2863 const std::string complevel(full_name, separator + 1);
2864
2865 /* Get the level and determine whether all went fine. */
2866 size_t processed;
2867 long level = std::stol(complevel, &processed, 10);
2868 if (processed == 0 || level != Clamp(level, slf.min_compression, slf.max_compression)) {
2870 GetEncodedString(STR_CONFIG_ERROR),
2871 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel),
2872 WL_CRITICAL);
2873 } else {
2874 return {slf, ClampTo<uint8_t>(level)};
2875 }
2876 }
2877 return {slf, slf.default_compression};
2878 }
2879 }
2880
2882 GetEncodedString(STR_CONFIG_ERROR),
2883 GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name),
2884 WL_CRITICAL);
2885 }
2886 return {def, def.default_compression};
2887}
2888
2889/* actual loader/saver function */
2890void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
2891extern bool AfterLoadGame();
2892extern bool LoadOldSaveGame(const std::string &file);
2893
2899static void ResetSettings()
2900{
2901 for (auto &desc : GetSaveLoadSettingTable()) {
2902 const SettingDesc *sd = GetSettingDesc(desc);
2903 if (sd->flags.Test(SettingFlag::NotInSave)) continue;
2905
2907 }
2908}
2909
2914{
2915 ResetTempEngineData();
2916 ClearRailTypeLabelList();
2917 ClearRoadTypeLabelList();
2918 ResetOldWaypoints();
2919 ResetSettings();
2920}
2921
2925static inline void ClearSaveLoadState()
2926{
2927 _sl.dumper = nullptr;
2928 _sl.sf = nullptr;
2929 _sl.reader = nullptr;
2930 _sl.lf = nullptr;
2931}
2932
2934static void SaveFileStart()
2935{
2936 SetMouseCursorBusy(true);
2937
2939 _sl.saveinprogress = true;
2940}
2941
2943static void SaveFileDone()
2944{
2945 SetMouseCursorBusy(false);
2946
2948 _sl.saveinprogress = false;
2949
2950#ifdef __EMSCRIPTEN__
2951 EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
2952#endif
2953}
2954
2957{
2958 _sl.error_str = str;
2959}
2960
2963{
2964 return GetEncodedString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
2965}
2966
2972
2979
2985{
2986 try {
2987 auto [fmt, compression] = GetSavegameFormat(_savegame_format);
2988
2989 /* We have written our stuff to memory, now write it to file! */
2990 uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
2991 _sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
2992
2993 _sl.sf = fmt.init_write(_sl.sf, compression);
2994 _sl.dumper->Flush(_sl.sf);
2995
2997
2998 if (threaded) SetAsyncSaveFinish(SaveFileDone);
2999
3000 return SL_OK;
3001 } catch (...) {
3003
3005
3006 /* We don't want to shout when saving is just
3007 * cancelled due to a client disconnecting. */
3008 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
3009 /* Skip the "colour" character */
3010 Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
3011 asfp = SaveFileError;
3012 }
3013
3014 if (threaded) {
3015 SetAsyncSaveFinish(asfp);
3016 } else {
3017 asfp();
3018 }
3019 return SL_ERROR;
3020 }
3021}
3022
3023void WaitTillSaved()
3024{
3025 if (!_save_thread.joinable()) return;
3026
3027 _save_thread.join();
3028
3029 /* Make sure every other state is handled properly as well. */
3031}
3032
3041static SaveOrLoadResult DoSave(std::shared_ptr<SaveFilter> writer, bool threaded)
3042{
3043 assert(!_sl.saveinprogress);
3044
3045 _sl.dumper = std::make_unique<MemoryDumper>();
3046 _sl.sf = std::move(writer);
3047
3049
3050 SaveViewportBeforeSaveGame();
3051 SlSaveChunks();
3052
3053 SaveFileStart();
3054
3055 if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) {
3056 if (threaded) Debug(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
3057
3058 SaveOrLoadResult result = SaveFileToDisk(false);
3059 SaveFileDone();
3060
3061 return result;
3062 }
3063
3064 return SL_OK;
3065}
3066
3073SaveOrLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threaded)
3074{
3075 try {
3077 return DoSave(std::move(writer), threaded);
3078 } catch (...) {
3080 return SL_ERROR;
3081 }
3082}
3083
3092static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
3093{
3094 auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
3095 if (fmt != std::end(_saveload_formats)) {
3096 /* Check version number */
3097 _sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
3098 /* Minor is not used anymore from version 18.0, but it is still needed
3099 * in versions before that (4 cases) which can't be removed easy.
3100 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
3101 _sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
3102
3103 Debug(sl, 1, "Loading savegame version {}", _sl_version);
3104
3105 /* Is the version higher than the current? */
3106 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
3107 if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
3108 return fmt;
3109 }
3110
3111 Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
3112 _sl.lf->Reset();
3115
3116 /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
3117 fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
3118 if (fmt == std::end(_saveload_formats)) {
3119 /* Who removed the LZO savegame format definition? When built without LZO support,
3120 * the formats must still list it just without a method to read the file.
3121 * The caller of this function has to check for the existence of load function. */
3122 NOT_REACHED();
3123 }
3124 return fmt;
3125}
3126
3133static SaveOrLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_check)
3134{
3135 _sl.lf = std::move(reader);
3136
3137 if (load_check) {
3138 /* Clear previous check data */
3140 /* Mark SL_LOAD_CHECK as supported for this savegame. */
3142 }
3143
3144 uint32_t hdr[2];
3145 if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3146
3147 /* see if we have any loader for this type. */
3148 const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
3149
3150 /* loader for this savegame type is not implemented? */
3151 if (fmt->init_load == nullptr) {
3152 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, fmt::format("Loader for '{}' is not available.", fmt->name));
3153 }
3154
3155 _sl.lf = fmt->init_load(_sl.lf);
3156 _sl.reader = std::make_unique<ReadBuffer>(_sl.lf);
3157 _next_offs = 0;
3158
3159 if (!load_check) {
3161
3162 /* Old maps were hardcoded to 256x256 and thus did not contain
3163 * any mapsize information. Pre-initialize to 256x256 to not to
3164 * confuse old games */
3165 InitializeGame(256, 256, true, true);
3166
3167 _gamelog.Reset();
3168
3170 /*
3171 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
3172 * shared savegame version 4. Anything before that 'obviously'
3173 * does not have any NewGRFs. Between the introduction and
3174 * savegame version 41 (just before 0.5) the NewGRF settings
3175 * were not stored in the savegame and they were loaded by
3176 * using the settings from the main menu.
3177 * So, to recap:
3178 * - savegame version < 4: do not load any NewGRFs.
3179 * - savegame version >= 41: load NewGRFs from savegame, which is
3180 * already done at this stage by
3181 * overwriting the main menu settings.
3182 * - other savegame versions: use main menu settings.
3183 *
3184 * This means that users *can* crash savegame version 4..40
3185 * savegames if they set incompatible NewGRFs in the main menu,
3186 * but can't crash anymore for savegame version < 4 savegames.
3187 *
3188 * Note: this is done here because AfterLoadGame is also called
3189 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
3190 */
3192 }
3193 }
3194
3195 if (load_check) {
3196 /* Load chunks into _load_check_data.
3197 * No pools are loaded. References are not possible, and thus do not need resolving. */
3199 } else {
3200 /* Load chunks and resolve references */
3201 SlLoadChunks();
3202 SlFixPointers();
3203 }
3204
3206
3208
3209 if (load_check) {
3210 /* The only part from AfterLoadGame() we need */
3212 } else {
3214
3215 /* After loading fix up savegame for any internal changes that
3216 * might have occurred since then. If it fails, load back the old game. */
3217 if (!AfterLoadGame()) {
3219 return SL_REINIT;
3220 }
3221
3223 }
3224
3225 return SL_OK;
3226}
3227
3233SaveOrLoadResult LoadWithFilter(std::shared_ptr<LoadFilter> reader)
3234{
3235 try {
3237 return DoLoad(std::move(reader), false);
3238 } catch (...) {
3240 return SL_REINIT;
3241 }
3242}
3243
3253SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
3254{
3255 /* An instance of saving is already active, so don't go saving again */
3256 if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) {
3257 /* if not an autosave, but a user action, show error message */
3258 if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WL_ERROR);
3259 return SL_OK;
3260 }
3261 WaitTillSaved();
3262
3263 try {
3264 /* Load a TTDLX or TTDPatch game */
3265 if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) {
3267
3268 InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
3269
3270 /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
3271 * and if so a new NewGRF list will be made in LoadOldSaveGame.
3272 * Note: this is done here because AfterLoadGame is also called
3273 * for OTTD savegames which have their own NewGRF logic. */
3275 _gamelog.Reset();
3276 if (!LoadOldSaveGame(filename)) return SL_REINIT;
3280 if (!AfterLoadGame()) {
3282 return SL_REINIT;
3283 }
3285 return SL_OK;
3286 }
3287
3288 assert(dft == DFT_GAME_FILE);
3289 switch (fop) {
3290 case SLO_CHECK:
3292 break;
3293
3294 case SLO_LOAD:
3296 break;
3297
3298 case SLO_SAVE:
3300 break;
3301
3302 default: NOT_REACHED();
3303 }
3304
3305 auto fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
3306
3307 /* Make it a little easier to load savegames from the console */
3308 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
3309 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
3310 if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
3311
3312 if (!fh.has_value()) {
3313 SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
3314 }
3315
3316 if (fop == SLO_SAVE) { // SAVE game
3317 Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
3318 if (!_settings_client.gui.threaded_saves) threaded = false;
3319
3320 return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
3321 }
3322
3323 /* LOAD game */
3324 assert(fop == SLO_LOAD || fop == SLO_CHECK);
3325 Debug(desync, 1, "load: {}", filename);
3326 return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SLO_CHECK);
3327 } catch (...) {
3328 /* This code may be executed both for old and new save games. */
3330
3331 /* Skip the "colour" character */
3332 if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString());
3333
3334 /* A saver/loader exception!! reinitialize all variables to prevent crash! */
3335 return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;
3336 }
3337}
3338
3345{
3346 std::string filename;
3347
3349 filename = GenerateDefaultSaveName() + counter.Extension();
3350 } else {
3351 filename = counter.Filename();
3352 }
3353
3354 Debug(sl, 2, "Autosaving to '{}'", filename);
3355 if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) {
3356 ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WL_ERROR);
3357 }
3358}
3359
3360
3363{
3365}
3366
3371{
3372 /* Check if we have a name for this map, which is the name of the first
3373 * available company. When there's no company available we'll use
3374 * 'Spectator' as "company" name. */
3376 if (!Company::IsValidID(cid)) {
3377 for (const Company *c : Company::Iterate()) {
3378 cid = c->index;
3379 break;
3380 }
3381 }
3382
3383 std::array<StringParameter, 4> params{};
3384 auto it = params.begin();
3385 *it++ = cid;
3386
3387 /* We show the current game time differently depending on the timekeeping units used by this game. */
3389 /* Insert time played. */
3390 const auto play_time = TimerGameTick::counter / Ticks::TICKS_PER_SECOND;
3391 *it++ = STR_SAVEGAME_DURATION_REALTIME;
3392 *it++ = play_time / 60 / 60;
3393 *it++ = (play_time / 60) % 60;
3394 } else {
3395 /* Insert current date */
3397 case 0: *it++ = STR_JUST_DATE_LONG; break;
3398 case 1: *it++ = STR_JUST_DATE_TINY; break;
3399 case 2: *it++ = STR_JUST_DATE_ISO; break;
3400 default: NOT_REACHED();
3401 }
3402 *it++ = TimerGameEconomy::date;
3403 }
3404
3405 /* Get the correct string (special string for when there's not company) */
3406 std::string filename = GetStringWithArgs(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, params);
3407 SanitizeFilename(filename);
3408 return filename;
3409}
3410
3417{
3418 if (ft.abstract == FT_INVALID || ft.abstract == FT_NONE) {
3419 this->file_op = SLO_INVALID;
3420 this->ftype = FIOS_TYPE_INVALID;
3421 return;
3422 }
3423
3424 this->file_op = fop;
3425 this->ftype = ft;
3426}
3427
3433{
3434 this->SetMode(item.type);
3435 this->name = item.name;
3436 this->title = item.title;
3437}
3438
3440{
3441 assert(this->load_description.has_value());
3442 return *this->load_description;
3443}
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
void PutUtf8(char32_t c)
Append UTF.8 char.
void Put(std::string_view str)
Append string.
void PutIntegerBase(T value, int base)
Append integer 'value' in given number 'base'.
Container for an encoded string, created by GetEncodedString.
Enum-as-bit-set wrapper.
void StartAction(GamelogActionType at)
Stores information about new action, but doesn't allocate it Action is allocated only when there is a...
Definition gamelog.cpp:65
void Reset()
Resets and frees all memory allocated - used before loading or starting a new game.
Definition gamelog.cpp:94
void StopAction()
Stops logging of any changes.
Definition gamelog.cpp:74
Class for calculation jobs to be run on link graphs.
A connected component of a link graph.
Definition linkgraph.h:37
Handler for saving/loading an object to/from disk.
Definition saveload.h:525
SaveLoadTable GetLoadDescription() const
Get the description for how to load the chunk.
Handler that is assigned when there is a struct read in the savegame which is not known to the code.
virtual SaveLoadTable GetDescription() const override
Get the description of the fields in the savegame.
virtual SaveLoadCompatTable GetCompatDescription() const override
Get the pre-header description of the fields in the savegame.
void LoadCheck(void *object) const override
Similar to load, but used only to validate savegames.
void Load(void *object) const override
Load the object from disk.
void Save(void *) const override
Save the object to disk.
Template class to help with list-like types.
static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd=SL_VAR)
Internal templated helper to save/load a list-like type.
static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd=SL_VAR)
Internal templated helper to return the size in bytes of a list-like type.
Compose data into a growing std::string.
Parse data from a string / buffer.
T ReadIntegerBase(int base, T def=0)
Read and parse an integer in number 'base', and advance the reader.
@ READ_ONE_SEPARATOR
Read one separator, and include it in the result.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
std::optional< T > TryReadIntegerBase(int base)
Try to read and parse an integer in number 'base', and then advance the reader.
std::optional< char32_t > TryReadUtf8()
Try to read a UTF-8 character, and then advance reader.
bool ReadUtf8If(char32_t c)
Check whether the next UTF-8 char matches 'c', and skip it.
std::string_view ReadUntilUtf8(char32_t c, SeparatorUsage sep)
Read data until the first occurrence of UTF-8 char 'c', and advance reader.
static constexpr TimerGameTick::Ticks TICKS_PER_SECOND
Estimation of how many ticks fit in a single second.
static Date date
Current date in days (day counter).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static DateFract date_fract
Fractional part of the day.
static TickCounter counter
Monotonic counter, in ticks, since start of game.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
@ SCC_ENCODED
Encoded string marker and sub-string parameter.
@ SCC_ENCODED_NUMERIC
Encoded numeric parameter.
@ SCC_ENCODED_STRING
Encoded string parameter.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition fileio.cpp:1002
std::optional< FileHandle > FioFOpenFile(std::string_view filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:242
SaveLoadOperation
Operation performed on the file.
Definition fileio_type.h:51
@ SLO_CHECK
Load file for checking and/or preview.
Definition fileio_type.h:52
@ SLO_SAVE
File is being saved.
Definition fileio_type.h:54
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:53
@ SLO_INVALID
Unknown file operation.
Definition fileio_type.h:56
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:27
@ DFT_GAME_FILE
Save game or scenario file.
Definition fileio_type.h:30
@ DFT_OLD_GAME_FILE
Old save game or scenario file.
Definition fileio_type.h:29
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition fileio_type.h:87
@ SCENARIO_DIR
Base directory for all scenarios.
Definition fileio_type.h:91
@ BASE_DIR
Base directory for all subdirectories.
Definition fileio_type.h:88
@ SAVE_DIR
Base directory for all savegames.
Definition fileio_type.h:89
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition fileio_type.h:90
@ FT_NONE
nothing to do
Definition fileio_type.h:17
@ FT_INVALID
Invalid or unknown file type.
Definition fileio_type.h:23
LoadCheckData _load_check_data
Data loaded from save during SL_LOAD_CHECK.
Definition fios_gui.cpp:41
fluid_settings_t * settings
FluidSynth settings handle.
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
@ GLAT_LOAD
Game loaded.
Definition gamelog.h:18
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1641
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:51
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _networking
are we in networking mode?
Definition network.cpp:66
bool _network_server
network-server is active
Definition network.cpp:67
GRFConfigList _grfconfig
First item in list of current GRF set up.
GRFListCompatibility IsGoodGRFConfigList(GRFConfigList &grfconfig)
Check if all GRFs in the GRF config from a savegame can be loaded.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
static void SlRefVector(void *vector, VarType conv)
Save/Load a vector.
static const uint LZO_BUFFER_SIZE
Buffer size for the LZO compressor.
void SlError(StringID string, const std::string &extra_msg)
Error handler.
Definition saveload.cpp:327
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:558
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:382
void FixSCCEncodedNegative(std::string &str)
Scan the string for SCC_ENCODED_NUMERIC with negative values, and reencode them as uint64_t.
Definition saveload.cpp:988
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:68
static const SaveLoadFormat _saveload_formats[]
The different saveload formats known/understood by OpenTTD.
std::string _savegame_format
how to compress savegames
Definition saveload.cpp:71
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:69
static const std::vector< ChunkHandlerRef > & ChunkHandlers()
Definition saveload.cpp:208
uint8_t _sl_minor_version
the minor savegame version, DO NOT USE!
Definition saveload.cpp:70
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:500
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:72
static std::atomic< AsyncSaveFinishProc > _async_save_finish
Callback to call when the savegame loading is finished.
Definition saveload.cpp:364
static std::thread _save_thread
The thread we're using to compress and write a savegame.
Definition saveload.cpp:365
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:594
std::vector< SaveLoad > SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct)
Load a table header in a savegame compatible way.
static void ResetSettings()
Reset all settings to their default, so any settings missing in the savegame are their default,...
void SlWriteByte(uint8_t b)
Wrapper for writing a byte to the dumper.
Definition saveload.cpp:407
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:824
void(* AsyncSaveFinishProc)()
Callback for when the savegame loading is finished.
Definition saveload.cpp:363
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:665
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:371
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:788
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:648
NeedLength
Definition saveload.cpp:83
@ NL_WANTLENGTH
writing length and data
Definition saveload.cpp:85
@ NL_NONE
not working in NeedLength mode
Definition saveload.cpp:84
@ NL_CALCLENGTH
need to calculate the length
Definition saveload.cpp:86
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:302
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:357
void SlSkipArray()
Skip an array or sparse array.
Definition saveload.cpp:707
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:905
static uint SlReadSimpleGamma()
Read in the header descriptor of an object or an array.
Definition saveload.cpp:458
SaveLoadAction
What are we currently doing?
Definition saveload.cpp:75
@ SLA_LOAD
loading
Definition saveload.cpp:76
@ SLA_NULL
null all pointers (on loading error)
Definition saveload.cpp:79
@ SLA_SAVE
saving
Definition saveload.cpp:77
@ SLA_LOAD_CHECK
partial loading into _load_check_data
Definition saveload.cpp:80
@ SLA_PTRS
fixing pointers
Definition saveload.cpp:78
static void SlCopyBytes(void *ptr, size_t length)
Save/Load bytes.
Definition saveload.cpp:771
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:65
static void SlSaveLoadConv(void *ptr, VarType conv)
Handle all conversion and typechecking of variables here.
Definition saveload.cpp:850
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition saveload.cpp:66
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:719
uint8_t SlReadByte()
Wrapper for reading a byte from the buffer.
Definition saveload.cpp:398
static SaveLoadParams _sl
Parameters used for/at saveload.
Definition saveload.cpp:206
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:624
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:800
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:921
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:90
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.
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:525
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:427
@ SGT_OTTD
OTTD savegame.
Definition saveload.h:431
SaveOrLoadResult
Save or load result codes.
Definition saveload.h:409
@ SL_OK
completed successfully
Definition saveload.h:410
@ SL_REINIT
error that was caught in the middle of updating game state, need to clear it. (can only happen during...
Definition saveload.h:412
@ SLE_VAR_NULL
useful to write zeros in savegame.
Definition saveload.h:657
@ SLE_FILE_END
Used to mark end-of-header in tables.
Definition saveload.h:630
@ SLE_FILE_TYPE_MASK
Mask to get the file-type (and not any flags).
Definition saveload.h:644
@ SLE_FILE_HAS_LENGTH_FIELD
Bit stored in savegame to indicate field has a length field for each entry.
Definition saveload.h:645
@ SLF_ALLOW_NEWLINE
Allow new lines in the strings.
Definition saveload.h:693
@ SLF_ALLOW_CONTROL
Allow control codes in the strings.
Definition saveload.h:692
@ SLE_VAR_STR
string pointer
Definition saveload.h:658
@ SLE_VAR_NAME
old custom name to be converted to a char pointer
Definition saveload.h:660
@ SLE_VAR_STRQ
string pointer enclosed in quotes
Definition saveload.h:659
@ SLE_FILE_STRINGID
StringID offset into strings-array.
Definition saveload.h:639
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:1351
constexpr VarType GetVarFileType(VarType type)
Get the FileType of a setting.
Definition saveload.h:766
SLRefType
Type of reference (SLE_REF, SLE_CONDREF).
Definition saveload.h:604
@ REF_VEHICLE_OLD
Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
Definition saveload.h:609
@ REF_LINK_GRAPH_JOB
Load/save a reference to a link graph job.
Definition saveload.h:616
@ REF_TOWN
Load/save a reference to a town.
Definition saveload.h:608
@ REF_LINK_GRAPH
Load/save a reference to a link graph.
Definition saveload.h:615
@ REF_CARGO_PACKET
Load/save a reference to a cargo packet.
Definition saveload.h:612
@ REF_ENGINE_RENEWS
Load/save a reference to an engine renewal (autoreplace).
Definition saveload.h:611
@ REF_STATION
Load/save a reference to a station.
Definition saveload.h:607
@ REF_ORDER
Load/save a reference to an order.
Definition saveload.h:605
@ REF_ORDERLIST
Load/save a reference to an orderlist.
Definition saveload.h:613
@ REF_STORAGE
Load/save a reference to a persistent storage.
Definition saveload.h:614
@ REF_VEHICLE
Load/save a reference to a vehicle.
Definition saveload.h:606
@ REF_ROADSTOPS
Load/save a reference to a bus/truck stop.
Definition saveload.h:610
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1305
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:516
SaveLoadType
Type of data saved.
Definition saveload.h:699
@ SL_NULL
Save null-bytes and load to nowhere.
Definition saveload.h:713
@ SL_STRUCTLIST
Save/load a list of structs.
Definition saveload.h:710
@ SL_STDSTR
Save/load a std::string.
Definition saveload.h:704
@ SL_REF
Save/load a reference.
Definition saveload.h:701
@ SL_SAVEBYTE
Save (but not load) a byte.
Definition saveload.h:712
@ SL_DEQUE
Save/load a deque of SL_VAR elements.
Definition saveload.h:707
@ SL_STRUCT
Save/load a struct.
Definition saveload.h:702
@ SL_VECTOR
Save/load a vector of SL_VAR elements.
Definition saveload.h:708
@ SL_REFVECTOR
Save/load a vector of SL_REF elements.
Definition saveload.h:715
@ SL_REFLIST
Save/load a list of SL_REF elements.
Definition saveload.h:709
@ SL_ARR
Save/load a fixed-size array of SL_VAR elements.
Definition saveload.h:706
@ SL_VAR
Save/load a variable.
Definition saveload.h:700
std::span< const struct SaveLoadCompat > SaveLoadCompatTable
A table of SaveLoadCompat entries.
Definition saveload.h:522
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1267
constexpr VarType GetVarMemType(VarType type)
Get the NumberType of a setting.
Definition saveload.h:755
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_FIX_SCC_ENCODED_NEGATIVE
353 PR#14049 Fix encoding of negative parameters.
Definition saveload.h:403
@ 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:405
@ 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:519
@ CH_TYPE_MASK
All ChunkType values have to be within this mask.
Definition saveload.h:462
@ CH_READONLY
Chunk is never saved.
Definition saveload.h:463
Declaration of filters used for saving and loading savegames.
Declaration of functions used in more save/load files.
StringID RemapOldStringID(StringID s)
Remap a string ID from the old format to the new format.
std::string CopyFromOldName(StringID id)
Copy and convert old custom names to UTF-8.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:60
SettingTable GetSaveLoadSettingTable()
Create a single table with all settings that should be stored/loaded in the savegame.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:59
@ NotInSave
Do not save with savegame, basically client-based.
@ NoNetworkSync
Do not synchronize over network (but it is saved if SettingFlag::NotInSave is not set).
static constexpr const SettingDesc * GetSettingDesc(const SettingVariant &desc)
Helper to convert the type of the iterated settings description to a pointer to it.
@ SBI_SAVELOAD_FINISH
finished saving
@ SBI_SAVELOAD_START
started saving
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
void StrMakeValidInPlace(char *str, StringValidationSettings settings)
Scans the string for invalid characters and replaces them with a question mark '?' (if not ignored).
Definition string.cpp:155
@ ReplaceWithQuestionMark
Replace the unknown/bad bits with question marks.
@ AllowControlCode
Allow the special control codes.
@ AllowNewline
Allow newlines; replaces '\r ' with ' ' during processing.
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:327
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:91
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Container for cargo from the same location and time.
Definition cargopacket.h:41
Handlers and description of chunk.
Definition saveload.h:467
ChunkType type
Type of the chunk.
Definition saveload.h:469
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:468
virtual void Save() const
Save the chunk.
Definition saveload.h:479
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:416
void SetMode(const FiosType &ft, SaveLoadOperation fop=SLO_LOAD)
Set the mode and file type of the file to save or load.
std::string title
Internal name of the game.
Definition saveload.h:420
FiosType ftype
File type.
Definition saveload.h:418
SaveLoadOperation file_op
File operation to perform.
Definition saveload.h:417
std::string name
Name of the file.
Definition saveload.h:419
void Set(const FiosItem &item)
Set the title of the file.
Yes, simply writing to a file.
std::optional< FileHandle > file
The file to write to.
FileWriter(FileHandle &&file)
Create the file writer, so it writes to a specific file.
void Finish() override
Prepare everything to finish writing the savegame.
~FileWriter()
Make sure everything is cleaned up.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
Deals with finding savegames.
Definition fios.h:78
A savegame name automatically numbered.
Definition fios.h:129
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
Definition fios.cpp:751
std::string Extension()
Generate an extension for a savegame name.
Definition fios.cpp:761
Elements of a file system that are recognized.
Definition fileio_type.h:62
AbstractFileType abstract
Abstract file type.
Definition fileio_type.h:63
bool keep_all_autosave
name the autosave in a different way
uint8_t date_format_in_default_names
should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) ...
bool threaded_saves
should we do threaded saves?
std::optional< size_t > savegame_size
Size of the last saved savegame in bytes, or std::nullopt if not saved yet.
Definition openttd.h:58
Filter without any compression.
~LZMALoadFilter()
Clean everything up.
lzma_stream lzma
Stream state that we are reading from.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
LZMALoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
Filter using LZMA compression.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
void Finish() override
Prepare everything to finish writing the savegame.
void WriteLoop(uint8_t *p, size_t len, lzma_action action)
Helper loop for writing the data.
LZMASaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
lzma_stream lzma
Stream state that we are writing to.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
~LZMASaveFilter()
Clean up what we allocated.
Filter using LZO compression.
LZOLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
size_t Read(uint8_t *buf, size_t ssize) override
Read a given number of bytes from the savegame.
Filter using LZO compression.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
LZOSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t)
Initialise this filter.
bool checkable
True if the savegame could be checked by SL_LOAD_CHECK. (Old savegames are not checkable....
Definition fios.h:34
std::string error_msg
Data to pass to string parameters when displaying error.
Definition fios.h:36
StringID error
Error message from loading. INVALID_STRING_ID if no error.
Definition fios.h:35
void Clear()
Reset read data.
Definition fios_gui.cpp:49
GRFListCompatibility grf_compatibility
Summary state of NewGrfs, whether missing files or only compatible found.
Definition fios.h:48
GRFConfigList grfconfig
NewGrf configuration from save.
Definition fios.h:47
Interface for filtering a savegame till it is loaded.
std::shared_ptr< LoadFilter > chain
Chained to the (savegame) filters.
Container for dumping the savegame (quickly) to memory.
Definition saveload.cpp:134
uint8_t * buf
Buffer we're going to write to.
Definition saveload.cpp:136
void WriteByte(uint8_t b)
Write a single byte into the dumper.
Definition saveload.cpp:143
std::vector< std::unique_ptr< uint8_t[]> > blocks
Buffer with blocks of allocated memory.
Definition saveload.cpp:135
uint8_t * bufe
End of the buffer we write to.
Definition saveload.cpp:137
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:177
void Flush(std::shared_ptr< SaveFilter > writer)
Flush this dumper into a writer.
Definition saveload.cpp:158
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:93
uint8_t * bufp
Location we're at reading the buffer.
Definition saveload.cpp:95
ReadBuffer(std::shared_ptr< LoadFilter > reader)
Initialise our variables.
Definition saveload.cpp:104
size_t read
The amount of read bytes so far from the filter.
Definition saveload.cpp:98
size_t GetSize() const
Get the size of the memory dump made so far.
Definition saveload.cpp:126
std::shared_ptr< LoadFilter > reader
The filter used to actually read.
Definition saveload.cpp:97
uint8_t buf[MEMORY_CHUNK_SIZE]
Buffer we're going to read from.
Definition saveload.cpp:94
uint8_t * bufe
End of the buffer we can read from.
Definition saveload.cpp:96
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:184
std::unique_ptr< ReadBuffer > reader
Savegame reading buffer.
Definition saveload.cpp:197
std::shared_ptr< SaveFilter > sf
Filter to write the savegame to.
Definition saveload.cpp:195
std::unique_ptr< MemoryDumper > dumper
Memory dumper to write the savegame to.
Definition saveload.cpp:194
StringID error_str
the translatable error message to show
Definition saveload.cpp:200
SaveLoadAction action
are we doing a save or a load atm.
Definition saveload.cpp:185
std::string extra_msg
the error message
Definition saveload.cpp:201
NeedLength need_length
working in NeedLength (Autolength) mode?
Definition saveload.cpp:186
uint8_t block_mode
???
Definition saveload.cpp:187
bool saveinprogress
Whether there is currently a save in progress.
Definition saveload.cpp:203
std::shared_ptr< LoadFilter > lf
Filter to read the savegame from.
Definition saveload.cpp:198
bool expect_table_header
In the case of a table, if the header is saved/loaded.
Definition saveload.cpp:192
size_t obj_len
the length of the current object we are busy with
Definition saveload.cpp:190
bool error
did an error occur or not
Definition saveload.cpp:188
int last_array_index
in the case of an array, the current and last positions
Definition saveload.cpp:191
SaveLoad type struct.
Definition saveload.h:721
uint16_t length
(Conditional) length of the variable (eg. arrays) (max array size is 65536 elements).
Definition saveload.h:725
std::shared_ptr< SaveLoadHandler > handler
Custom handler for Save/Load procs.
Definition saveload.h:730
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:727
SaveLoadType cmd
The action to take with the saved/loaded type, All types need different action.
Definition saveload.h:723
std::string name
Name of this field (optional, used for tables).
Definition saveload.h:722
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:724
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:726
Properties of config file settings.
SettingFlags flags
Handles how a setting would show up in the GUI (text/currency, etc.).
virtual void ResetToDefault(void *object) const =0
Reset the setting to its default value.
static bool IsValidID(auto index)
Tests whether given index is a valid index for station of this type.
static Station * Get(auto index)
Gets station with given index.
Station data structure.
Town data structure.
Definition town.h:52
Vehicle data structure.
Filter using Zlib compression.
size_t Read(uint8_t *buf, size_t size) override
Read a given number of bytes from the savegame.
ZlibLoadFilter(std::shared_ptr< LoadFilter > chain)
Initialise this filter.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]
Buffer for reading from the file.
~ZlibLoadFilter()
Clean everything up.
z_stream z
Stream state we are reading from.
Filter using Zlib compression.
void WriteLoop(uint8_t *p, size_t len, int mode)
Helper loop for writing the data.
z_stream z
Stream state we are writing to.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]
Buffer for writing to the file.
void Finish() override
Prepare everything to finish writing the savegame.
~ZlibSaveFilter()
Clean up what we allocated.
ZlibSaveFilter(std::shared_ptr< SaveFilter > chain, uint8_t compression_level)
Initialise this filter.
void Write(uint8_t *buf, size_t size) override
Write a given number of bytes into the savegame.
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Definition thread.h:24
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3232
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:66