Changeset - r25696:265cb5def15f
[Not reviewed]
master
0 2 0
Patric Stout - 3 years ago 2021-06-15 09:59:38
truebrain@openttd.org
Codechange: use SL_NULL if you want to store null-bytes or load to nothing

Using SL_ARR for this gives us a bit of trouble later on, where we
add a length-field to SL_ARR. This of course is not the intention
of SLE_CONDNULL. So better seperate it.
2 files changed with 31 insertions and 30 deletions:
0 comments (0 inline, 0 general)
src/saveload/saveload.cpp
Show inline comments
 
@@ -1416,72 +1416,61 @@ size_t SlCalcObjLength(const void *objec
 

	
 
	/* Need to determine the length and write a length tag. */
 
	for (auto &sld : slt) {
 
		length += SlCalcObjMemberLength(object, sld);
 
	}
 
	return length;
 
}
 

	
 
size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
 
{
 
	assert(_sl.action == SLA_SAVE);
 

	
 
	if (!SlIsObjectValidInSavegame(sld)) return 0;
 

	
 
	switch (sld.cmd) {
 
		case SL_VAR:
 
		case SL_REF:
 
		case SL_ARR:
 
		case SL_STR:
 
		case SL_REFLIST:
 
		case SL_DEQUE:
 
		case SL_STDSTR:
 
			/* CONDITIONAL saveload types depend on the savegame version */
 
			if (!SlIsObjectValidInSavegame(sld)) break;
 

	
 
			switch (sld.cmd) {
 
				case SL_VAR: return SlCalcConvFileLen(sld.conv);
 
				case SL_REF: return SlCalcRefLen();
 
				case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
 
				case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld.length, sld.conv);
 
				case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
 
				case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
 
				case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
 
				default: NOT_REACHED();
 
			}
 
			break;
 
		case SL_VAR: return SlCalcConvFileLen(sld.conv);
 
		case SL_REF: return SlCalcRefLen();
 
		case SL_ARR: return SlCalcArrayLen(sld.length, sld.conv);
 
		case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld.length, sld.conv);
 
		case SL_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv);
 
		case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv);
 
		case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
 
		case SL_SAVEBYTE: return 1; // a byte is logically of size 1
 
		case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length;
 

	
 
		case SL_STRUCT:
 
		case SL_STRUCTLIST: {
 
			if (!SlIsObjectValidInSavegame(sld)) break;
 

	
 
			NeedLength old_need_length = _sl.need_length;
 
			size_t old_obj_len = _sl.obj_len;
 

	
 
			_sl.need_length = NL_CALCLENGTH;
 
			_sl.obj_len = 0;
 

	
 
			/* Pretend that we are saving to collect the object size. Other
 
			 * means are difficult, as we don't know the length of the list we
 
			 * are about to store. */
 
			sld.handler->Save(const_cast<void *>(object));
 
			size_t length = _sl.obj_len;
 

	
 
			_sl.obj_len = old_obj_len;
 
			_sl.need_length = old_need_length;
 

	
 
			if (sld.cmd == SL_STRUCT) {
 
				length += SlGetArrayLength(1);
 
			}
 

	
 
			return length;
 
		}
 

	
 
		default: NOT_REACHED();
 
	}
 
	return 0;
 
}
 

	
 
/**
 
 * Check whether the variable size of the variable in the saveload configuration
 
 * matches with the actual variable size.
 
 * @param sld The saveload configuration to test.
 
 */
 
[[maybe_unused]] static bool IsVariableSizeRight(const SaveLoad &sld)
 
{
 
@@ -1521,36 +1510,35 @@ size_t SlCalcObjMemberLength(const void 
 
			/* These should be all pointers to std::string. */
 
			return sld.size == sizeof(std::string);
 

	
 
		default:
 
			return true;
 
	}
 
}
 

	
 
static bool SlObjectMember(void *object, const SaveLoad &sld)
 
{
 
	assert(IsVariableSizeRight(sld));
 

	
 
	if (!SlIsObjectValidInSavegame(sld)) return false;
 

	
 
	VarType conv = GB(sld.conv, 0, 8);
 
	switch (sld.cmd) {
 
		case SL_VAR:
 
		case SL_REF:
 
		case SL_ARR:
 
		case SL_STR:
 
		case SL_REFLIST:
 
		case SL_DEQUE:
 
		case SL_STDSTR: {
 
			/* CONDITIONAL saveload types depend on the savegame version */
 
			if (!SlIsObjectValidInSavegame(sld)) return false;
 

	
 
			void *ptr = GetVariableAddress(object, sld);
 

	
 
			switch (sld.cmd) {
 
				case SL_VAR: SlSaveLoadConv(ptr, conv); break;
 
				case SL_REF: SlSaveLoadRef(ptr, conv); break;
 
				case SL_ARR: SlArray(ptr, sld.length, conv); break;
 
				case SL_STR: SlString(ptr, sld.length, sld.conv); break;
 
				case SL_REFLIST: SlRefList(ptr, conv); break;
 
				case SL_DEQUE: SlDeque(ptr, conv); break;
 
				case SL_STDSTR: SlStdString(ptr, sld.conv); break;
 
				default: NOT_REACHED();
 
			}
 
@@ -1565,28 +1553,40 @@ static bool SlObjectMember(void *object,
 

	
 
			switch (_sl.action) {
 
				case SLA_SAVE: SlWriteByte(*(uint8 *)ptr); break;
 
				case SLA_LOAD_CHECK:
 
				case SLA_LOAD:
 
				case SLA_PTRS:
 
				case SLA_NULL: break;
 
				default: NOT_REACHED();
 
			}
 
			break;
 
		}
 

	
 
		case SL_NULL: {
 
			assert(GetVarMemType(sld.conv) == SLE_VAR_NULL);
 

	
 
			switch (_sl.action) {
 
				case SLA_LOAD_CHECK:
 
				case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break;
 
				case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break;
 
				case SLA_PTRS:
 
				case SLA_NULL: break;
 
				default: NOT_REACHED();
 
			}
 
			break;
 
		}
 

	
 
		case SL_STRUCT:
 
		case SL_STRUCTLIST:
 
			if (!SlIsObjectValidInSavegame(sld)) return false;
 

	
 
			switch (_sl.action) {
 
				case SLA_SAVE: {
 
					if (sld.cmd == SL_STRUCT) {
 
						/* Store in the savegame if this struct was written or not. */
 
						SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0);
 
					}
 
					sld.handler->Save(object);
 
					break;
 
				}
 

	
 
				case SLA_LOAD_CHECK: {
 
					if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) {
src/saveload/saveload.h
Show inline comments
 
@@ -564,24 +564,25 @@ typedef uint32 VarType;
 
/** Type of data saved. */
 
enum SaveLoadType : byte {
 
	SL_VAR         =  0, ///< Save/load a variable.
 
	SL_REF         =  1, ///< Save/load a reference.
 
	SL_ARR         =  2, ///< Save/load a fixed-size array of #SL_VAR elements.
 
	SL_STR         =  3, ///< Save/load a string.
 
	SL_REFLIST     =  4, ///< Save/load a list of #SL_REF elements.
 
	SL_DEQUE       =  5, ///< Save/load a deque of #SL_VAR elements.
 
	SL_STDSTR      =  6, ///< Save/load a \c std::string.
 
	SL_STRUCT      =  7, ///< Save/load a struct.
 
	SL_STRUCTLIST  =  8, ///< Save/load a list of structs.
 
	SL_SAVEBYTE    =  9, ///< Save (but not load) a byte.
 
	SL_NULL        = 10, ///< Save null-bytes and load to nowhere.
 
};
 

	
 
typedef void *SaveLoadAddrProc(void *base, size_t extra);
 

	
 
/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
 
struct SaveLoad {
 
	SaveLoadType cmd;    ///< the action to take with the saved/loaded type, All types need different action
 
	VarType conv;        ///< type of the variable to be saved, int
 
	uint16 length;       ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements)
 
	SaveLoadVersion version_from;   ///< save/load the variable starting from this savegame version
 
	SaveLoadVersion version_to;     ///< save/load the variable until this savegame version
 
	size_t size;                    ///< the sizeof size.
 
@@ -728,25 +729,25 @@ struct SaveLoad {
 
/**
 
 * Empty space in every savegame version.
 
 * @param length Length of the empty space.
 
 */
 
#define SLE_NULL(length) SLE_CONDNULL(length, SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Empty space in some savegame versions.
 
 * @param length Length of the empty space.
 
 * @param from   First savegame version that has the empty space.
 
 * @param to     Last savegame version that has the empty space.
 
 */
 
#define SLE_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr}
 
#define SLE_CONDNULL(length, from, to) {SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr}
 

	
 
/**
 
 * Only write byte during saving; never read it during loading.
 
 * When using SLE_SAVEBYTE you will have to read this byte before the table
 
 * this is in is read. This also means SLE_SAVEBYTE can only be used at the
 
 * top of a chunk.
 
 * This is intended to be used to indicate what type of entry this is in a
 
 * list of entries.
 
 * @param base     Name of the class or struct containing the variable.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 */
 
#define SLE_SAVEBYTE(base, variable) SLE_GENERAL(SL_SAVEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0)
 
@@ -886,25 +887,25 @@ struct SaveLoad {
 
/**
 
 * Storage of a list of structs in every savegame version.
 
 * @param handler SaveLoadHandler for the list of structs.
 
 */
 
#define SLEG_STRUCTLIST(handler) SLEG_CONDSTRUCTLIST(handler, SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Empty global space in some savegame versions.
 
 * @param length Length of the empty space.
 
 * @param from   First savegame version that has the empty space.
 
 * @param to     Last savegame version that has the empty space.
 
 */
 
#define SLEG_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr}
 
#define SLEG_CONDNULL(length, from, to) {SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr}
 

	
 
/**
 
 * Checks whether the savegame is below \a major.\a minor.
 
 * @param major Major number of the version to check against.
 
 * @param minor Minor number of the version to check against. If \a minor is 0 or not specified, only the major number is checked.
 
 * @return Savegame version is earlier than the specified version.
 
 */
 
static inline bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor = 0)
 
{
 
	extern SaveLoadVersion _sl_version;
 
	extern byte            _sl_minor_version;
 
	return _sl_version < major || (minor > 0 && _sl_version == major && _sl_minor_version < minor);
0 comments (0 inline, 0 general)