From 3caa77fcb5057ca40752082da095d7f8ecdcd9ef Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sun, 14 Jul 2024 19:36:18 +0200 Subject: serialize/save: load newest save format without branching --- serialize/savegame.cpp | 128 +++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp index 693b098b..f7893094 100644 --- a/serialize/savegame.cpp +++ b/serialize/savegame.cpp @@ -123,36 +123,42 @@ struct critter_header_s using proto_t = uint16_t; -template -struct visitor_ +// ---------- proto versions ---------- +// 19: see old-savegame.cpp +// 20: complete rewrite +// 21: oops, forgot the object counter +// 22: add object::speed +// 23: switch object::delta to 32-bit +// 24: switch object::offset_frac from Vector2us to uint16_t +static constexpr proto_t proto_version = 24; + +static constexpr size_t string_max = 512; +static constexpr proto_t proto_version_min = 20; +static constexpr auto file_magic = ".floormat.save"_s; +static constexpr auto chunk_magic = maybe_byteswap((uint16_t)0xadde); +static constexpr auto object_magic = maybe_byteswap((uint16_t)0x0bb0); + +using tilemeta = uint8_t; +using atlasid = uint32_t; +using chunksiz = uint32_t; + +template static constexpr T highbit = (T{1} << sizeof(T)*8-1); +template static constexpr T null = T(~highbit); + +template struct visitor_base; +template<> struct visitor_base { [[maybe_unused]] static constexpr proto_t PROTO = proto_version; }; +template<> struct visitor_base { proto_t PROTO = (proto_t)-1; }; + +template +struct visitor_ : visitor_base { + using visitor_base::PROTO; + explicit visitor_() = default; fm_DECLARE_DELETED_COPY_ASSIGNMENT(visitor_); fm_DECLARE_DELETED_MOVE_ASSIGNMENT(visitor_); - // ---------- proto versions ---------- - // 19: see old-savegame.cpp - // 20: complete rewrite - // 21: oops, forgot the object counter - // 22: add object::speed - // 23: switch object::delta to 32-bit - // 24: switch object::offset_frac from Vector2us to uint16_t - static constexpr proto_t proto_version = 24; - - static constexpr size_t string_max = 512; - static constexpr proto_t proto_version_min = 20; - static constexpr auto file_magic = ".floormat.save"_s; - static constexpr auto chunk_magic = maybe_byteswap((uint16_t)0xadde); - static constexpr auto object_magic = maybe_byteswap((uint16_t)0x0bb0); - - using tilemeta = uint8_t; - using atlasid = uint32_t; - using chunksiz = uint32_t; - - template static constexpr T highbit = (T{1} << sizeof(T)*8-1); - template static constexpr T null = T(~highbit); - template using qual2 = std::conditional_t; template using qual = std::conditional_t; @@ -362,9 +368,9 @@ struct visitor_ constexpr size_t vector_initial_size = 128, hash_initial_size = vector_initial_size*2; -struct writer final : visitor_ +struct writer final : visitor_ { - using visitor_::visit; + using visitor_::visit; struct serialized_atlas { @@ -379,8 +385,6 @@ struct writer final : visitor_ chunk* c; }; - static constexpr proto_t PROTO = proto_version; - const world& w; std::vector string_array{}; @@ -552,23 +556,21 @@ ok: void(); { using INT = std::decay_t; static_assert(std::is_unsigned_v); - constexpr auto highbit = writer::highbit; - constexpr auto null = writer::null; const auto a = INT{ g(i) }; - fm_assert(a == null || a < null); + fm_assert(a == null || a < null); uint_fast16_t num_idempotent = 0; for (uint32_t j = i+1; j < TILE_COUNT; j++) if (a != g(j)) break; - else if ((size_t)num_idempotent + 1uz == (size_t)null) + else if ((size_t)num_idempotent + 1uz == (size_t)null) break; else num_idempotent++; if (num_idempotent) { - INT num = highbit | INT(num_idempotent); + INT num = highbit | INT(num_idempotent); visit(num, f); } @@ -694,7 +696,7 @@ ok: void(); } }; -template struct visitor_; +template struct visitor_; void my_fwrite(FILE_raii& f, const buffer& buf, char(&errbuf)[128]) { @@ -765,10 +767,14 @@ template<> struct atlas_from_type { using Type = wall_atlas; } template<> struct atlas_from_type { using Type = anim_atlas; }; template<> struct atlas_from_type { using Type = anim_atlas; }; -struct reader final : visitor_ +template +struct reader final : visitor_, false, IsNewest> { - using visitor_::visit; - using visitor_::visit_object_proto; + using visitor_, false, IsNewest>::visit; + using visitor_, false, IsNewest>::visit_object_proto; + using visitor_, false, IsNewest>::visit_object_header; + using visitor_, false, IsNewest>::visit_scenery_proto; + using visitor_, false, IsNewest>::PROTO; struct atlas_pair { @@ -778,7 +784,6 @@ struct reader final : visitor_ std::vector strings; std::vector atlases; - proto_t PROTO = (proto_t)-1; object_id object_counter = world::object_counter_init; uint32_t nstrings = 0, natlases = 0, nchunks = 0; @@ -916,12 +921,22 @@ ok: object_counter = Math::max(object_counter, id); } - bool deserialize_header_(binary_reader& s, ArrayView buf) + static proto_t deserialize_header_1(binary_reader& s) { - fm_assert(PROTO == (proto_t)-1); + proto_t proto; auto magic = s.read(); fm_soft_assert(StringView{magic.data(), magic.size()} == file_magic); - PROTO << s; + proto << s; + return proto; + } + + bool deserialize_header_(binary_reader& s, ArrayView buf, proto_t proto) + { + fm_assert(IsNewest || PROTO == (proto_t)-1); + + if constexpr(!IsNewest) + PROTO = proto; + if (PROTO < proto_version_min && PROTO > 0) { w.deserialize_old(w, buf.exceptPrefix(s.bytes_read()), PROTO, asset_policy); @@ -1001,21 +1016,18 @@ ok: template void deserialize_tile_part(auto&& g, uint32_t& i, byte_reader& r) { - constexpr auto highbit = reader::highbit; - constexpr auto null = reader::null; - INT num; uint32_t num_idempotent = 0; visit(num, r); - if (num & highbit) + if (num & highbit) { - num_idempotent = num & ~highbit; + num_idempotent = num & ~highbit; visit(num, r); } - if (num != null) + if (num != null) for (uint32_t j = 0; j <= num_idempotent; j++) g(i+j, num); @@ -1086,10 +1098,9 @@ ok: deserialize_objects_(c, r); } - void deserialize_world(ArrayView buf) + void deserialize_world(binary_reader& s, ArrayView buf, proto_t proto) { - binary_reader s{buf.data(), buf.data() + buf.size()}; - if (deserialize_header_(s, buf)) + if (deserialize_header_(s, buf, proto)) return; deserialize_strings_(s); deserialize_atlases(s); @@ -1101,7 +1112,8 @@ ok: } }; -template struct visitor_; +template struct visitor_, false, true>; +template struct visitor_, false, false>; } // namespace @@ -1133,8 +1145,18 @@ class world world::deserialize(StringView filename, loader_policy asset_policy) } class world w; - struct reader r{w, asset_policy}; - r.deserialize_world(buf); + auto s = binary_reader{&buf.data[0], &buf.data[buf.size]}; + auto proto = reader::deserialize_header_1(s); + if (proto == proto_version) + { + struct reader r{w, asset_policy}; + r.deserialize_world(s, buf, proto); + } + else + { + struct reader r{w, asset_policy}; + r.deserialize_world(s, buf, proto); + } //fm_assert("t o d o && false); return w; -- cgit v1.2.3