Update to 15.0-beta1
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user