Update to 15.0-beta1

This commit is contained in:
dP
2024-12-25 20:34:06 +05:00
parent 46dc456049
commit a86fd7c621
963 changed files with 38070 additions and 33798 deletions

View File

@@ -61,7 +61,7 @@ FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop.
uint32_t _ttdp_version; ///< version of TTDP savegame (if applicable)
SaveLoadVersion _sl_version; ///< the major savegame version identifier
byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
uint8_t _sl_minor_version; ///< the minor savegame version, DO NOT USE!
std::string _savegame_format; ///< how to compress savegames
bool _do_autosave; ///< are we doing an autosave at the moment?
@@ -85,9 +85,9 @@ static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
/** A buffer for reading (and buffering) savegame data. */
struct ReadBuffer {
byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
byte *bufp; ///< Location we're at reading the buffer.
byte *bufe; ///< End of the buffer we can read from.
uint8_t buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
uint8_t *bufp; ///< Location we're at reading the buffer.
uint8_t *bufe; ///< End of the buffer we can read from.
std::shared_ptr<LoadFilter> reader; ///< The filter used to actually read.
size_t read; ///< The amount of read bytes so far from the filter.
@@ -99,7 +99,7 @@ struct ReadBuffer {
{
}
inline byte ReadByte()
inline uint8_t ReadByte()
{
if (this->bufp == this->bufe) {
size_t len = this->reader->Read(this->buf, lengthof(this->buf));
@@ -126,32 +126,19 @@ struct ReadBuffer {
/** Container for dumping the savegame (quickly) to memory. */
struct MemoryDumper {
std::vector<byte *> blocks; ///< Buffer with blocks of allocated memory.
byte *buf; ///< Buffer we're going to write to.
byte *bufe; ///< End of the buffer we write to.
/** Initialise our variables. */
MemoryDumper() : buf(nullptr), bufe(nullptr)
{
}
~MemoryDumper()
{
for (auto p : this->blocks) {
free(p);
}
}
std::vector<std::unique_ptr<uint8_t[]>> blocks{}; ///< Buffer with blocks of allocated memory.
uint8_t *buf = nullptr; ///< Buffer we're going to write to.
uint8_t *bufe = nullptr; ///< End of the buffer we write to.
/**
* Write a single byte into the dumper.
* @param b The byte to write.
*/
inline void WriteByte(byte b)
inline void WriteByte(uint8_t b)
{
/* Are we at the end of this chunk? */
if (this->buf == this->bufe) {
this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
this->blocks.push_back(this->buf);
this->buf = this->blocks.emplace_back(std::make_unique<uint8_t[]>(MEMORY_CHUNK_SIZE)).get();
this->bufe = this->buf + MEMORY_CHUNK_SIZE;
}
@@ -170,7 +157,7 @@ struct MemoryDumper {
while (t > 0) {
size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
writer->Write(this->blocks[i++], to_write);
writer->Write(this->blocks[i++].get(), to_write);
t -= to_write;
}
@@ -191,7 +178,7 @@ struct MemoryDumper {
struct SaveLoadParams {
SaveLoadAction action; ///< are we doing a save or a load atm.
NeedLength need_length; ///< working in NeedLength (Autolength) mode?
byte block_mode; ///< ???
uint8_t block_mode; ///< ???
bool error; ///< did an error occur or not
size_t obj_len; ///< the length of the current object we are busy with
@@ -402,7 +389,7 @@ void ProcessAsyncSaveFinish()
* Wrapper for reading a byte from the buffer.
* @return The read byte.
*/
byte SlReadByte()
uint8_t SlReadByte()
{
return _sl.reader->ReadByte();
}
@@ -411,7 +398,7 @@ byte SlReadByte()
* Wrapper for writing a byte to the dumper.
* @param b The byte to write.
*/
void SlWriteByte(byte b)
void SlWriteByte(uint8_t b)
{
_sl.dumper->WriteByte(b);
}
@@ -511,21 +498,21 @@ static void SlWriteSimpleGamma(size_t i)
if (i >= (1 << 21)) {
if (i >= (1 << 28)) {
assert(i <= UINT32_MAX); // We can only support 32 bits for now.
SlWriteByte((byte)(0xF0));
SlWriteByte((byte)(i >> 24));
SlWriteByte((uint8_t)(0xF0));
SlWriteByte((uint8_t)(i >> 24));
} else {
SlWriteByte((byte)(0xE0 | (i >> 24)));
SlWriteByte((uint8_t)(0xE0 | (i >> 24)));
}
SlWriteByte((byte)(i >> 16));
SlWriteByte((uint8_t)(i >> 16));
} else {
SlWriteByte((byte)(0xC0 | (i >> 16)));
SlWriteByte((uint8_t)(0xC0 | (i >> 16)));
}
SlWriteByte((byte)(i >> 8));
SlWriteByte((uint8_t)(i >> 8));
} else {
SlWriteByte((byte)(0x80 | (i >> 8)));
SlWriteByte((uint8_t)(0x80 | (i >> 8)));
}
}
SlWriteByte((byte)i);
SlWriteByte((uint8_t)i);
}
/** Return how many bytes used to encode a gamma value */
@@ -578,6 +565,7 @@ static uint8_t GetSavegameFileType(const SaveLoad &sld)
return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
case SL_REFLIST:
case SL_REFVECTOR:
return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD;
case SL_SAVEBYTE:
@@ -599,17 +587,25 @@ static uint8_t GetSavegameFileType(const SaveLoad &sld)
*/
static inline uint SlCalcConvMemLen(VarType conv)
{
static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
switch (GetVarMemType(conv)) {
case SLE_VAR_BL: return sizeof(bool);
case SLE_VAR_I8: return sizeof(int8_t);
case SLE_VAR_U8: return sizeof(uint8_t);
case SLE_VAR_I16: return sizeof(int16_t);
case SLE_VAR_U16: return sizeof(uint16_t);
case SLE_VAR_I32: return sizeof(int32_t);
case SLE_VAR_U32: return sizeof(uint32_t);
case SLE_VAR_I64: return sizeof(int64_t);
case SLE_VAR_U64: return sizeof(uint64_t);
case SLE_VAR_NULL: return 0;
case SLE_VAR_STR:
case SLE_VAR_STRQ:
return SlReadArrayLength();
case SLE_VAR_NAME:
default:
uint8_t type = GetVarMemType(conv) >> 4;
assert(type < lengthof(conv_mem_size));
return conv_mem_size[type];
NOT_REACHED();
}
}
@@ -619,19 +615,26 @@ static inline uint SlCalcConvMemLen(VarType conv)
* @param conv VarType type of variable that is used for calculating the size
* @return Return the size of this type in bytes
*/
static inline byte SlCalcConvFileLen(VarType conv)
static inline uint8_t SlCalcConvFileLen(VarType conv)
{
static const byte conv_file_size[] = {0, 1, 1, 2, 2, 4, 4, 8, 8, 2};
switch (GetVarFileType(conv)) {
case SLE_FILE_END: return 0;
case SLE_FILE_I8: return sizeof(int8_t);
case SLE_FILE_U8: return sizeof(uint8_t);
case SLE_FILE_I16: return sizeof(int16_t);
case SLE_FILE_U16: return sizeof(uint16_t);
case SLE_FILE_I32: return sizeof(int32_t);
case SLE_FILE_U32: return sizeof(uint32_t);
case SLE_FILE_I64: return sizeof(int64_t);
case SLE_FILE_U64: return sizeof(uint64_t);
case SLE_FILE_STRINGID: return sizeof(uint16_t);
case SLE_FILE_STRING:
return SlReadArrayLength();
case SLE_FILE_STRUCT:
default:
uint8_t type = GetVarFileType(conv);
if (type >= lengthof(conv_file_size)) fmt::println("{}", type);
assert(type < lengthof(conv_file_size));
return conv_file_size[type];
NOT_REACHED();
}
}
@@ -761,7 +764,7 @@ void SlSetLength(size_t length)
*/
static void SlCopyBytes(void *ptr, size_t length)
{
byte *p = (byte *)ptr;
uint8_t *p = (uint8_t *)ptr;
switch (_sl.action) {
case SLA_LOAD_CHECK:
@@ -793,7 +796,7 @@ int64_t ReadValue(const void *ptr, VarType conv)
switch (GetVarMemType(conv)) {
case SLE_VAR_BL: return (*(const bool *)ptr != 0);
case SLE_VAR_I8: return *(const int8_t *)ptr;
case SLE_VAR_U8: return *(const byte *)ptr;
case SLE_VAR_U8: return *(const uint8_t *)ptr;
case SLE_VAR_I16: return *(const int16_t *)ptr;
case SLE_VAR_U16: return *(const uint16_t*)ptr;
case SLE_VAR_I32: return *(const int32_t *)ptr;
@@ -817,7 +820,7 @@ void WriteValue(void *ptr, VarType conv, int64_t val)
switch (GetVarMemType(conv)) {
case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
case SLE_VAR_I8: *(int8_t *)ptr = val; break;
case SLE_VAR_U8: *(byte *)ptr = val; break;
case SLE_VAR_U8: *(uint8_t *)ptr = val; break;
case SLE_VAR_I16: *(int16_t *)ptr = val; break;
case SLE_VAR_U16: *(uint16_t*)ptr = val; break;
case SLE_VAR_I32: *(int32_t *)ptr = val; break;
@@ -865,7 +868,7 @@ static void SlSaveLoadConv(void *ptr, VarType conv)
/* Read a value from the file */
switch (GetVarFileType(conv)) {
case SLE_FILE_I8: x = (int8_t )SlReadByte(); break;
case SLE_FILE_U8: x = (byte )SlReadByte(); break;
case SLE_FILE_U8: x = (uint8_t )SlReadByte(); break;
case SLE_FILE_I16: x = (int16_t )SlReadUint16(); break;
case SLE_FILE_U16: x = (uint16_t)SlReadUint16(); break;
case SLE_FILE_I32: x = (int32_t )SlReadUint32(); break;
@@ -1006,8 +1009,8 @@ static void SlCopyInternal(void *object, size_t length, VarType conv)
if (conv == SLE_INT8 || conv == SLE_UINT8) {
SlCopyBytes(object, length);
} else {
byte *a = (byte*)object;
byte mem_size = SlCalcConvMemLen(conv);
uint8_t *a = (uint8_t*)object;
uint8_t mem_size = SlCalcConvMemLen(conv);
for (; length != 0; length --) {
SlSaveLoadConv(a, conv);
@@ -1052,7 +1055,7 @@ static inline size_t SlCalcArrayLen(size_t length, VarType conv)
* Save/Load the length of the array followed by the array of SL_VAR elements.
* @param array The array being manipulated
* @param length The length of the array in elements
* @param conv VarType type of the atomic array (int, byte, uint64_t, etc.)
* @param conv VarType type of the atomic array (int, uint8_t, uint64_t, etc.)
*/
static void SlArray(void *array, size_t length, VarType conv)
{
@@ -1258,6 +1261,7 @@ public:
switch (cmd) {
case SL_VAR: SlSaveLoadConv(item, conv); break;
case SL_REF: SlSaveLoadRef(item, conv); break;
case SL_STDSTR: SlStdString(item, conv); break;
default:
NOT_REACHED();
}
@@ -1271,7 +1275,7 @@ public:
*/
static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR)
{
assert(cmd == SL_VAR || cmd == SL_REF);
assert(cmd == SL_VAR || cmd == SL_REF || cmd == SL_STDSTR);
SlStorageT *list = static_cast<SlStorageT *>(storage);
@@ -1290,9 +1294,15 @@ public:
switch (cmd) {
case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break;
case SL_STDSTR: length = SlReadArrayLength(); break;
default: NOT_REACHED();
}
list->clear();
if constexpr (std::is_same_v<SlStorageT, std::vector<Tvar, Tallocator>>) {
list->reserve(length);
}
/* Load each value and push to the end of the storage. */
for (size_t i = 0; i < length; i++) {
Tvar &data = list->emplace_back();
@@ -1343,6 +1353,33 @@ static void SlRefList(void *list, VarType conv)
SlStorageHelper<std::list, void *>::SlSaveLoad(list, conv, SL_REF);
}
/**
* Return the size in bytes of a vector.
* @param vector The std::vector to find the size of.
* @param conv VarType type of variable that is used for calculating the size.
*/
static size_t SlCalcRefVectorLen(const void *vector, VarType conv)
{
return SlStorageHelper<std::vector, void *>::SlCalcLen(vector, conv, SL_REF);
}
/**
* Save/Load a vector.
* @param vector The vector being manipulated.
* @param conv VarType type of variable that is used for calculating the size.
*/
static void SlRefVector(void *vector, VarType conv)
{
/* Automatically calculate the length? */
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcRefVectorLen(vector, conv));
/* Determine length only? */
if (_sl.need_length == NL_CALCLENGTH) return;
}
SlStorageHelper<std::vector, void *>::SlSaveLoad(vector, conv, SL_REF);
}
/**
* Return the size in bytes of a std::deque.
* @param deque The std::deque to find the size of
@@ -1360,6 +1397,12 @@ static inline size_t SlCalcDequeLen(const void *deque, VarType conv)
case SLE_VAR_U32: return SlStorageHelper<std::deque, uint32_t>::SlCalcLen(deque, conv);
case SLE_VAR_I64: return SlStorageHelper<std::deque, int64_t>::SlCalcLen(deque, conv);
case SLE_VAR_U64: return SlStorageHelper<std::deque, uint64_t>::SlCalcLen(deque, conv);
case SLE_VAR_STR:
/* Strings are a length-prefixed field type in the savegame table format,
* these may not be directly stored in another length-prefixed container type. */
NOT_REACHED();
default: NOT_REACHED();
}
}
@@ -1381,6 +1424,16 @@ static void SlDeque(void *deque, VarType conv)
case SLE_VAR_U32: SlStorageHelper<std::deque, uint32_t>::SlSaveLoad(deque, conv); break;
case SLE_VAR_I64: SlStorageHelper<std::deque, int64_t>::SlSaveLoad(deque, conv); break;
case SLE_VAR_U64: SlStorageHelper<std::deque, uint64_t>::SlSaveLoad(deque, conv); break;
case SLE_VAR_STR:
/* Strings are a length-prefixed field type in the savegame table format,
* these may not be directly stored in another length-prefixed container type.
* This is permitted for load-related actions, because invalid fields of this type are present
* from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
assert(_sl.action != SLA_SAVE);
SlStorageHelper<std::deque, std::string>::SlSaveLoad(deque, conv, SL_STDSTR);
break;
default: NOT_REACHED();
}
}
@@ -1402,6 +1455,12 @@ static inline size_t SlCalcVectorLen(const void *vector, VarType conv)
case SLE_VAR_U32: return SlStorageHelper<std::vector, uint32_t>::SlCalcLen(vector, conv);
case SLE_VAR_I64: return SlStorageHelper<std::vector, int64_t>::SlCalcLen(vector, conv);
case SLE_VAR_U64: return SlStorageHelper<std::vector, uint64_t>::SlCalcLen(vector, conv);
case SLE_VAR_STR:
/* Strings are a length-prefixed field type in the savegame table format,
* these may not be directly stored in another length-prefixed container type. */
NOT_REACHED();
default: NOT_REACHED();
}
}
@@ -1423,6 +1482,16 @@ static void SlVector(void *vector, VarType conv)
case SLE_VAR_U32: SlStorageHelper<std::vector, uint32_t>::SlSaveLoad(vector, conv); break;
case SLE_VAR_I64: SlStorageHelper<std::vector, int64_t>::SlSaveLoad(vector, conv); break;
case SLE_VAR_U64: SlStorageHelper<std::vector, uint64_t>::SlSaveLoad(vector, conv); break;
case SLE_VAR_STR:
/* Strings are a length-prefixed field type in the savegame table format,
* these may not be directly stored in another length-prefixed container type.
* This is permitted for load-related actions, because invalid fields of this type are present
* from SLV_COMPANY_ALLOW_LIST up to SLV_COMPANY_ALLOW_LIST_V2. */
assert(_sl.action != SLA_SAVE);
SlStorageHelper<std::vector, std::string>::SlSaveLoad(vector, conv, SL_STDSTR);
break;
default: NOT_REACHED();
}
}
@@ -1489,6 +1558,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
case SL_REF: return SlCalcRefLen();
case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
case SL_REFVECTOR: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv);
case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv);
case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
@@ -1534,6 +1604,7 @@ static bool SlObjectMember(void *object, const SaveLoad &sld)
case SL_REF:
case SL_ARR:
case SL_REFLIST:
case SL_REFVECTOR:
case SL_DEQUE:
case SL_VECTOR:
case SL_STDSTR: {
@@ -1544,6 +1615,7 @@ static bool SlObjectMember(void *object, const SaveLoad &sld)
case SL_REF: SlSaveLoadRef(ptr, conv); break;
case SL_ARR: SlArray(ptr, sld.length, conv); break;
case SL_REFLIST: SlRefList(ptr, conv); break;
case SL_REFVECTOR: SlRefVector(ptr, conv); break;
case SL_DEQUE: SlDeque(ptr, conv); break;
case SL_VECTOR: SlVector(ptr, conv); break;
case SL_STDSTR: SlStdString(ptr, sld.conv); break;
@@ -1764,7 +1836,7 @@ std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt)
}
/* We don't know this field, so read to nothing. */
saveloads.push_back({key, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, 0, nullptr, 0, handler});
saveloads.push_back({key, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, handler});
continue;
}
@@ -1871,7 +1943,7 @@ std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLo
/* In old savegames there can be data we no longer care for. We
* skip this by simply reading the amount of bytes indicated and
* send those to /dev/null. */
saveloads.push_back({"", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr});
saveloads.push_back({"", SL_NULL, GetVarFileType(slc.null_type) | SLE_VAR_NULL, slc.null_length, slc.version_from, slc.version_to, nullptr, 0, nullptr});
} else {
auto sld_it = key_lookup.find(slc.name);
/* If this branch triggers, it means that an entry in the
@@ -1913,7 +1985,7 @@ void SlGlobList(const SaveLoadTable &slt)
* @param proc The callback procedure that is called
* @param arg The variable that will be used for the callback procedure
*/
void SlAutolength(AutolengthProc *proc, void *arg)
void SlAutolength(AutolengthProc *proc, int arg)
{
assert(_sl.action == SLA_SAVE);
@@ -1962,7 +2034,7 @@ void ChunkHandler::LoadCheck(size_t len) const
*/
static void SlLoadChunk(const ChunkHandler &ch)
{
byte m = SlReadByte();
uint8_t m = SlReadByte();
_sl.block_mode = m & CH_TYPE_MASK;
_sl.obj_len = 0;
@@ -2015,7 +2087,7 @@ static void SlLoadChunk(const ChunkHandler &ch)
*/
static void SlLoadCheckChunk(const ChunkHandler &ch)
{
byte m = SlReadByte();
uint8_t m = SlReadByte();
_sl.block_mode = m & CH_TYPE_MASK;
_sl.obj_len = 0;
@@ -2168,39 +2240,37 @@ static void SlFixPointers()
/** Yes, simply reading from a file. */
struct FileReader : LoadFilter {
FILE *file; ///< The file to read from.
std::optional<FileHandle> file; ///< The file to read from.
long begin; ///< The begin of the file.
/**
* Create the file reader, so it reads from a specific file.
* @param file The file to read from.
*/
FileReader(FILE *file) : LoadFilter(nullptr), file(file), begin(ftell(file))
FileReader(FileHandle &&file) : LoadFilter(nullptr), file(std::move(file)), begin(ftell(*this->file))
{
}
/** Make sure everything is cleaned up. */
~FileReader()
{
if (this->file != nullptr) {
_game_session_stats.savegame_size = ftell(this->file) - this->begin;
fclose(this->file);
if (this->file.has_value()) {
_game_session_stats.savegame_size = ftell(*this->file) - this->begin;
}
this->file = nullptr;
}
size_t Read(byte *buf, size_t size) override
size_t Read(uint8_t *buf, size_t size) override
{
/* We're in the process of shutting down, i.e. in "failure" mode. */
if (this->file == nullptr) return 0;
if (!this->file.has_value()) return 0;
return fread(buf, 1, size, this->file);
return fread(buf, 1, size, *this->file);
}
void Reset() override
{
clearerr(this->file);
if (fseek(this->file, this->begin, SEEK_SET)) {
clearerr(*this->file);
if (fseek(*this->file, this->begin, SEEK_SET)) {
Debug(sl, 1, "Could not reset the file reading");
}
}
@@ -2208,13 +2278,13 @@ struct FileReader : LoadFilter {
/** Yes, simply writing to a file. */
struct FileWriter : SaveFilter {
FILE *file; ///< The file to write to.
std::optional<FileHandle> file; ///< The file to write to.
/**
* Create the file writer, so it writes to a specific file.
* @param file The file to write to.
*/
FileWriter(FILE *file) : SaveFilter(nullptr), file(file)
FileWriter(FileHandle &&file) : SaveFilter(nullptr), file(std::move(file))
{
}
@@ -2224,21 +2294,20 @@ struct FileWriter : SaveFilter {
this->Finish();
}
void Write(byte *buf, size_t size) override
void Write(uint8_t *buf, size_t size) override
{
/* We're in the process of shutting down, i.e. in "failure" mode. */
if (this->file == nullptr) return;
if (!this->file.has_value()) return;
if (fwrite(buf, 1, size, this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
if (fwrite(buf, 1, size, *this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
}
void Finish() override
{
if (this->file != nullptr) {
_game_session_stats.savegame_size = ftell(this->file);
fclose(this->file);
if (this->file.has_value()) {
_game_session_stats.savegame_size = ftell(*this->file);
this->file.reset();
}
this->file = nullptr;
}
};
@@ -2263,18 +2332,18 @@ struct LZOLoadFilter : LoadFilter {
if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
}
size_t Read(byte *buf, size_t ssize) override
size_t Read(uint8_t *buf, size_t ssize) override
{
assert(ssize >= LZO_BUFFER_SIZE);
/* Buffer size is from the LZO docs plus the chunk header size. */
byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
uint32_t tmp[2];
uint32_t size;
lzo_uint len = ssize;
/* Read header*/
if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
if (this->chain->Read((uint8_t*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
/* Check if size is bad */
((uint32_t*)out)[0] = size = tmp[1];
@@ -2305,17 +2374,17 @@ struct LZOSaveFilter : SaveFilter {
* Initialise this filter.
* @param chain The next filter in this chain.
*/
LZOSaveFilter(std::shared_ptr<SaveFilter> chain, byte) : SaveFilter(chain)
LZOSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(chain)
{
if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
}
void Write(byte *buf, size_t size) override
void Write(uint8_t *buf, size_t size) override
{
const lzo_bytep in = buf;
/* Buffer size is from the LZO docs plus the chunk header size. */
byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
byte wrkmem[LZO1X_1_MEM_COMPRESS];
uint8_t out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32_t) * 2];
uint8_t wrkmem[LZO1X_1_MEM_COMPRESS];
lzo_uint outlen;
do {
@@ -2349,7 +2418,7 @@ struct NoCompLoadFilter : LoadFilter {
{
}
size_t Read(byte *buf, size_t size) override
size_t Read(uint8_t *buf, size_t size) override
{
return this->chain->Read(buf, size);
}
@@ -2361,11 +2430,11 @@ struct NoCompSaveFilter : SaveFilter {
* Initialise this filter.
* @param chain The next filter in this chain.
*/
NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, byte) : SaveFilter(chain)
NoCompSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t) : SaveFilter(chain)
{
}
void Write(byte *buf, size_t size) override
void Write(uint8_t *buf, size_t size) override
{
this->chain->Write(buf, size);
}
@@ -2381,7 +2450,7 @@ struct NoCompSaveFilter : SaveFilter {
/** Filter using Zlib compression. */
struct ZlibLoadFilter : LoadFilter {
z_stream z; ///< Stream state we are reading from.
byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file.
/**
* Initialise this filter.
@@ -2399,7 +2468,7 @@ struct ZlibLoadFilter : LoadFilter {
inflateEnd(&this->z);
}
size_t Read(byte *buf, size_t size) override
size_t Read(uint8_t *buf, size_t size) override
{
this->z.next_out = buf;
this->z.avail_out = (uint)size;
@@ -2425,14 +2494,14 @@ struct ZlibLoadFilter : LoadFilter {
/** Filter using Zlib compression. */
struct ZlibSaveFilter : SaveFilter {
z_stream z; ///< Stream state we are writing to.
byte fwrite_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for writing to the file.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for writing to the file.
/**
* Initialise this filter.
* @param chain The next filter in this chain.
* @param compression_level The requested level of compression.
*/
ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, byte compression_level) : SaveFilter(chain)
ZlibSaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(chain)
{
memset(&this->z, 0, sizeof(this->z));
if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
@@ -2450,7 +2519,7 @@ struct ZlibSaveFilter : SaveFilter {
* @param len Amount of bytes to write.
* @param mode Mode for deflate.
*/
void WriteLoop(byte *p, size_t len, int mode)
void WriteLoop(uint8_t *p, size_t len, int mode)
{
uint n;
this->z.next_in = p;
@@ -2478,7 +2547,7 @@ struct ZlibSaveFilter : SaveFilter {
} while (this->z.avail_in || !this->z.avail_out);
}
void Write(byte *buf, size_t size) override
void Write(uint8_t *buf, size_t size) override
{
this->WriteLoop(buf, size, 0);
}
@@ -2510,7 +2579,7 @@ static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
/** Filter without any compression. */
struct LZMALoadFilter : LoadFilter {
lzma_stream lzma; ///< Stream state that we are reading from.
byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file.
uint8_t fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file.
/**
* Initialise this filter.
@@ -2528,7 +2597,7 @@ struct LZMALoadFilter : LoadFilter {
lzma_end(&this->lzma);
}
size_t Read(byte *buf, size_t size) override
size_t Read(uint8_t *buf, size_t size) override
{
this->lzma.next_out = buf;
this->lzma.avail_out = size;
@@ -2553,14 +2622,14 @@ struct LZMALoadFilter : LoadFilter {
/** Filter using LZMA compression. */
struct LZMASaveFilter : SaveFilter {
lzma_stream lzma; ///< Stream state that we are writing to.
byte fwrite_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for writing to the file.
uint8_t fwrite_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for writing to the file.
/**
* Initialise this filter.
* @param chain The next filter in this chain.
* @param compression_level The requested level of compression.
*/
LZMASaveFilter(std::shared_ptr<SaveFilter> chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init)
LZMASaveFilter(std::shared_ptr<SaveFilter> chain, uint8_t compression_level) : SaveFilter(chain), lzma(_lzma_init)
{
if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
}
@@ -2577,7 +2646,7 @@ struct LZMASaveFilter : SaveFilter {
* @param len Amount of bytes to write.
* @param action Action for lzma_code.
*/
void WriteLoop(byte *p, size_t len, lzma_action action)
void WriteLoop(uint8_t *p, size_t len, lzma_action action)
{
size_t n;
this->lzma.next_in = p;
@@ -2597,7 +2666,7 @@ struct LZMASaveFilter : SaveFilter {
} while (this->lzma.avail_in || !this->lzma.avail_out);
}
void Write(byte *buf, size_t size) override
void Write(uint8_t *buf, size_t size) override
{
this->WriteLoop(buf, size, LZMA_RUN);
}
@@ -2621,30 +2690,35 @@ struct SaveLoadFormat {
uint32_t tag; ///< the 4-letter tag by which it is identified in the savegame
std::shared_ptr<LoadFilter> (*init_load)(std::shared_ptr<LoadFilter> chain); ///< Constructor for the load filter.
std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, byte compression); ///< Constructor for the save filter.
std::shared_ptr<SaveFilter> (*init_write)(std::shared_ptr<SaveFilter> chain, uint8_t compression); ///< Constructor for the save filter.
byte min_compression; ///< the minimum compression level of this format
byte default_compression; ///< the default compression level of this format
byte max_compression; ///< the maximum compression level of this format
uint8_t min_compression; ///< the minimum compression level of this format
uint8_t default_compression; ///< the default compression level of this format
uint8_t max_compression; ///< the maximum compression level of this format
};
static const uint32_t SAVEGAME_TAG_LZO = TO_BE32X('OTTD');
static const uint32_t SAVEGAME_TAG_NONE = TO_BE32X('OTTN');
static const uint32_t SAVEGAME_TAG_ZLIB = TO_BE32X('OTTZ');
static const uint32_t SAVEGAME_TAG_LZMA = TO_BE32X('OTTX');
/** The different saveload formats known/understood by OpenTTD. */
static const SaveLoadFormat _saveload_formats[] = {
#if defined(WITH_LZO)
/* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */
{"lzo", TO_BE32X('OTTD'), CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
{"lzo", SAVEGAME_TAG_LZO, CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
#else
{"lzo", TO_BE32X('OTTD'), nullptr, nullptr, 0, 0, 0},
{"lzo", SAVEGAME_TAG_LZO, nullptr, nullptr, 0, 0, 0},
#endif
/* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */
{"none", TO_BE32X('OTTN'), CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
{"none", SAVEGAME_TAG_NONE, CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
#if defined(WITH_ZLIB)
/* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is
* fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level
* 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */
{"zlib", TO_BE32X('OTTZ'), CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
{"zlib", SAVEGAME_TAG_ZLIB, CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
#else
{"zlib", TO_BE32X('OTTZ'), nullptr, nullptr, 0, 0, 0},
{"zlib", SAVEGAME_TAG_ZLIB, nullptr, nullptr, 0, 0, 0},
#endif
#if defined(WITH_LIBLZMA)
/* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves.
@@ -2652,9 +2726,9 @@ static const SaveLoadFormat _saveload_formats[] = {
* The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50%
* slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much.
* It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */
{"lzma", TO_BE32X('OTTX'), CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
{"lzma", SAVEGAME_TAG_LZMA, CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
#else
{"lzma", TO_BE32X('OTTX'), nullptr, nullptr, 0, 0, 0},
{"lzma", SAVEGAME_TAG_LZMA, nullptr, nullptr, 0, 0, 0},
#endif
};
@@ -2662,15 +2736,15 @@ static const SaveLoadFormat _saveload_formats[] = {
* Return the savegameformat of the game. Whether it was created with ZLIB compression
* uncompressed, or another type
* @param full_name Name of the savegame format. If empty it picks the first available one
* @param compression_level Output for telling what compression level we want.
* @return Pointer to SaveLoadFormat struct giving all characteristics of this type of savegame
* @return Pair containing reference to SaveLoadFormat struct giving all characteristics of this type of savegame, and a compression level to use.
*/
static const SaveLoadFormat *GetSavegameFormat(const std::string &full_name, byte *compression_level)
static std::pair<const SaveLoadFormat &, uint8_t> GetSavegameFormat(const std::string &full_name)
{
const SaveLoadFormat *def = lastof(_saveload_formats);
/* Find default savegame format, the highest one with which files can be written. */
auto it = std::find_if(std::rbegin(_saveload_formats), std::rend(_saveload_formats), [](const auto &slf) { return slf.init_write != nullptr; });
if (it == std::rend(_saveload_formats)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "no writeable savegame formats");
/* find default savegame format, the highest one with which files can be written */
while (!def->init_write) def--;
const SaveLoadFormat &def = *it;
if (!full_name.empty()) {
/* Get the ":..." of the compression level out of the way */
@@ -2678,32 +2752,30 @@ static const SaveLoadFormat *GetSavegameFormat(const std::string &full_name, byt
bool has_comp_level = separator != std::string::npos;
const std::string name(full_name, 0, has_comp_level ? separator : full_name.size());
for (const SaveLoadFormat *slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
if (slf->init_write != nullptr && name.compare(slf->name) == 0) {
*compression_level = slf->default_compression;
for (const auto &slf : _saveload_formats) {
if (slf.init_write != nullptr && name.compare(slf.name) == 0) {
if (has_comp_level) {
const std::string complevel(full_name, separator + 1);
/* Get the level and determine whether all went fine. */
size_t processed;
long level = std::stol(complevel, &processed, 10);
if (processed == 0 || level != Clamp(level, slf->min_compression, slf->max_compression)) {
if (processed == 0 || level != Clamp(level, slf.min_compression, slf.max_compression)) {
SetDParamStr(0, complevel);
ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, WL_CRITICAL);
} else {
*compression_level = level;
return {slf, ClampTo<uint8_t>(level)};
}
}
return slf;
return {slf, slf.default_compression};
}
}
SetDParamStr(0, name);
SetDParamStr(1, def->name);
SetDParamStr(1, def.name);
ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, WL_CRITICAL);
}
*compression_level = def->default_compression;
return def;
return {def, def.default_compression};
}
/* actual loader/saver function */
@@ -2760,22 +2832,23 @@ void SetSaveLoadError(StringID str)
_sl.error_str = str;
}
/** Get the string representation of the error message */
const char *GetSaveLoadErrorString()
/** Return the appropriate initial string for an error depending on whether we are saving or loading. */
StringID GetSaveLoadErrorType()
{
SetDParam(0, _sl.error_str);
SetDParamStr(1, _sl.extra_msg);
return _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED;
}
static std::string err_str;
err_str = GetString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
return err_str.c_str();
/** Return the description of the error. **/
StringID GetSaveLoadErrorMessage()
{
SetDParamStr(0, _sl.extra_msg);
return _sl.error_str;
}
/** Show a gui message when saving has failed */
static void SaveFileError()
{
SetDParamStr(0, GetSaveLoadErrorString());
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
ShowErrorMessage(GetSaveLoadErrorType(), GetSaveLoadErrorMessage(), WL_ERROR);
SaveFileDone();
}
@@ -2786,14 +2859,13 @@ static void SaveFileError()
static SaveOrLoadResult SaveFileToDisk(bool threaded)
{
try {
byte compression;
const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
auto [fmt, compression] = GetSavegameFormat(_savegame_format);
/* We have written our stuff to memory, now write it to file! */
uint32_t hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
_sl.sf->Write((byte*)hdr, sizeof(hdr));
uint32_t hdr[2] = { fmt.tag, TO_BE32(SAVEGAME_VERSION << 16) };
_sl.sf->Write((uint8_t*)hdr, sizeof(hdr));
_sl.sf = fmt->init_write(_sl.sf, compression);
_sl.sf = fmt.init_write(_sl.sf, compression);
_sl.dumper->Flush(_sl.sf);
ClearSaveLoadState();
@@ -2810,7 +2882,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
* cancelled due to a client disconnecting. */
if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
/* Skip the "colour" character */
Debug(sl, 0, "{}", GetSaveLoadErrorString() + 3);
Debug(sl, 0, "{}", GetString(GetSaveLoadErrorType()).substr(3) + GetString(GetSaveLoadErrorMessage()));
asfp = SaveFileError;
}
@@ -2884,6 +2956,49 @@ SaveOrLoadResult SaveWithFilter(std::shared_ptr<SaveFilter> writer, bool threade
}
}
/**
* Determines the SaveLoadFormat that is connected to the given tag.
* When the given tag is known, that format is chosen and a check on the validity of the version is performed.
* Otherwise a fallback to an ancient buggy format using LZO is chosen.
* @param tag The tag from the header describing the savegame compression/format.
* @param raw_version The raw version from the savegame header.
* @return The SaveLoadFormat to use for attempting to open the savegame.
*/
static const SaveLoadFormat *DetermineSaveLoadFormat(uint32_t tag, uint32_t raw_version)
{
auto fmt = std::ranges::find(_saveload_formats, tag, &SaveLoadFormat::tag);
if (fmt != std::end(_saveload_formats)) {
/* Check version number */
_sl_version = (SaveLoadVersion)(TO_BE32(raw_version) >> 16);
/* Minor is not used anymore from version 18.0, but it is still needed
* in versions before that (4 cases) which can't be removed easy.
* Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
_sl_minor_version = (TO_BE32(raw_version) >> 8) & 0xFF;
Debug(sl, 1, "Loading savegame version {}", _sl_version);
/* Is the version higher than the current? */
if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
return fmt;
}
Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
_sl.lf->Reset();
_sl_version = SL_MIN_VERSION;
_sl_minor_version = 0;
/* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
fmt = std::ranges::find(_saveload_formats, SAVEGAME_TAG_LZO, &SaveLoadFormat::tag);
if (fmt == std::end(_saveload_formats)) {
/* Who removed the LZO savegame format definition? When built without LZO support,
* the formats must still list it just without a method to read the file.
* The caller of this function has to check for the existence of load function. */
NOT_REACHED();
}
return fmt;
}
/**
* Actually perform the loading of a "non-old" savegame.
* @param reader The filter to read the savegame from.
@@ -2902,49 +3017,10 @@ static SaveOrLoadResult DoLoad(std::shared_ptr<LoadFilter> reader, bool load_che
}
uint32_t hdr[2];
if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
if (_sl.lf->Read((uint8_t*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
/* see if we have any loader for this type. */
const SaveLoadFormat *fmt = _saveload_formats;
for (;;) {
/* No loader found, treat as version 0 and use LZO format */
if (fmt == endof(_saveload_formats)) {
Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
_sl.lf->Reset();
_sl_version = SL_MIN_VERSION;
_sl_minor_version = 0;
/* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
fmt = _saveload_formats;
for (;;) {
if (fmt == endof(_saveload_formats)) {
/* Who removed LZO support? */
NOT_REACHED();
}
if (fmt->tag == TO_BE32X('OTTD')) break;
fmt++;
}
break;
}
if (fmt->tag == hdr[0]) {
/* check version number */
_sl_version = (SaveLoadVersion)(TO_BE32(hdr[1]) >> 16);
/* Minor is not used anymore from version 18.0, but it is still needed
* in versions before that (4 cases) which can't be removed easy.
* Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
_sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
Debug(sl, 1, "Loading savegame version {}", _sl_version);
/* Is the version higher than the current? */
if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
break;
}
fmt++;
}
const SaveLoadFormat *fmt = DetermineSaveLoadFormat(hdr[0], hdr[1]);
/* loader for this savegame type is not implemented? */
if (fmt->init_load == nullptr) {
@@ -3101,14 +3177,14 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
default: NOT_REACHED();
}
FILE *fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
auto fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
/* Make it a little easier to load savegames from the console */
if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
if (!fh.has_value() && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
if (fh == nullptr) {
if (!fh.has_value()) {
SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
}
@@ -3116,19 +3192,19 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, filename);
if (!_settings_client.gui.threaded_saves) threaded = false;
return DoSave(std::make_shared<FileWriter>(fh), threaded);
return DoSave(std::make_shared<FileWriter>(std::move(*fh)), threaded);
}
/* LOAD game */
assert(fop == SLO_LOAD || fop == SLO_CHECK);
Debug(desync, 1, "load: {}", filename);
return DoLoad(std::make_shared<FileReader>(fh), fop == SLO_CHECK);
return DoLoad(std::make_shared<FileReader>(std::move(*fh)), fop == SLO_CHECK);
} catch (...) {
/* This code may be executed both for old and new save games. */
ClearSaveLoadState();
/* Skip the "colour" character */
if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetSaveLoadErrorString() + 3);
if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetString(GetSaveLoadErrorType()).substr(3) + GetString(GetSaveLoadErrorMessage()));
/* A saver/loader exception!! reinitialize all variables to prevent crash! */
return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;