diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -53,11 +53,23 @@ const SaveLoad *GetLinkGraphJobDesc() static std::vector saveloads; static const char *prefix = "linkgraph."; + static const SaveLoad job_desc[] = { + SLE_VAR(LinkGraphJob, join_date, SLE_INT32), + SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), + SLE_END() + }; + + /* The member offset arithmetic below is only valid if the types in question + * are standard layout types. Otherwise, it would be undefined behaviour. */ + static_assert(std::is_standard_layout::value, "LinkGraphSettings needs to be a standard layout type"); + + /* We store the offset of each member of the #LinkGraphSettings in the + * extra data of the saveload struct. Use it together with the address + * of the settings struct inside the job to find the final memory address. */ + static SaveLoadAddrProc * const proc = [](void *b, size_t extra) -> void * { return const_cast(static_cast(reinterpret_cast(std::addressof(static_cast(b)->settings)) + extra)); }; + /* Build the SaveLoad array on first call and don't touch it later on */ if (saveloads.size() == 0) { - size_t offset_gamesettings = cpp_offsetof(GameSettings, linkgraph); - size_t offset_component = cpp_offsetof(LinkGraphJob, settings); - size_t prefixlen = strlen(prefix); int setting = 0; @@ -65,20 +77,12 @@ const SaveLoad *GetLinkGraphJobDesc() while (desc->save.cmd != SL_END) { if (desc->desc.name != nullptr && strncmp(desc->desc.name, prefix, prefixlen) == 0) { SaveLoad sl = desc->save; - char *&address = reinterpret_cast(sl.address); - address -= offset_gamesettings; - address += offset_component; + sl.address_proc = proc; saveloads.push_back(sl); } desc = GetSettingDescription(++setting); } - const SaveLoad job_desc[] = { - SLE_VAR(LinkGraphJob, join_date, SLE_INT32), - SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), - SLE_END() - }; - int i = 0; do { saveloads.push_back(job_desc[i++]);