Skip to content

Commit

Permalink
serialize/save: load newest save format without branching
Browse files Browse the repository at this point in the history
  • Loading branch information
sthalik committed Jul 14, 2024
1 parent 819017f commit 3caa77f
Showing 1 changed file with 75 additions and 53 deletions.
128 changes: 75 additions & 53 deletions serialize/savegame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,36 +123,42 @@ struct critter_header_s

using proto_t = uint16_t;

template<typename Derived, bool IsWriter>
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<std::unsigned_integral T> static constexpr T highbit = (T{1} << sizeof(T)*8-1);
template<std::unsigned_integral T> static constexpr T null = T(~highbit<T>);

template<bool IsNewest> struct visitor_base;
template<> struct visitor_base<true> { [[maybe_unused]] static constexpr proto_t PROTO = proto_version; };
template<> struct visitor_base<false> { proto_t PROTO = (proto_t)-1; };

template<typename Derived, bool IsWriter, bool IsNewest>
struct visitor_ : visitor_base<IsNewest>
{
using visitor_base<IsNewest>::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<std::unsigned_integral T> static constexpr T highbit = (T{1} << sizeof(T)*8-1);
template<std::unsigned_integral T> static constexpr T null = T(~highbit<T>);

template<typename T, typename U> using qual2 = std::conditional_t<IsWriter, const T, U>;
template<typename T> using qual = std::conditional_t<IsWriter, const T, T>;

Expand Down Expand Up @@ -362,9 +368,9 @@ struct visitor_

constexpr size_t vector_initial_size = 128, hash_initial_size = vector_initial_size*2;

struct writer final : visitor_<writer, true>
struct writer final : visitor_<writer, true, true>
{
using visitor_<writer, true>::visit;
using visitor_<writer, true, true>::visit;

struct serialized_atlas
{
Expand All @@ -379,8 +385,6 @@ struct writer final : visitor_<writer, true>
chunk* c;
};

static constexpr proto_t PROTO = proto_version;

const world& w;

std::vector<StringView> string_array{};
Expand Down Expand Up @@ -552,23 +556,21 @@ ok: void();
{
using INT = std::decay_t<decltype(g(i))>;
static_assert(std::is_unsigned_v<INT>);
constexpr auto highbit = writer::highbit<INT>;
constexpr auto null = writer::null<INT>;
const auto a = INT{ g(i) };
fm_assert(a == null || a < null);
fm_assert(a == null<INT> || a < null<INT>);
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<INT>)
break;
else
num_idempotent++;

if (num_idempotent)
{
INT num = highbit | INT(num_idempotent);
INT num = highbit<INT> | INT(num_idempotent);
visit(num, f);
}

Expand Down Expand Up @@ -694,7 +696,7 @@ ok: void();
}
};

template struct visitor_<writer, true>;
template struct visitor_<writer, true, true>;

void my_fwrite(FILE_raii& f, const buffer& buf, char(&errbuf)[128])
{
Expand Down Expand Up @@ -765,10 +767,14 @@ template<> struct atlas_from_type<atlas_type::wall> { using Type = wall_atlas; }
template<> struct atlas_from_type<atlas_type::anim> { using Type = anim_atlas; };
template<> struct atlas_from_type<atlas_type::vobj> { using Type = anim_atlas; };

struct reader final : visitor_<reader, false>
template<bool IsNewest>
struct reader final : visitor_<reader<IsNewest>, false, IsNewest>
{
using visitor_<reader, false>::visit;
using visitor_<reader, false>::visit_object_proto;
using visitor_<reader<IsNewest>, false, IsNewest>::visit;
using visitor_<reader<IsNewest>, false, IsNewest>::visit_object_proto;
using visitor_<reader<IsNewest>, false, IsNewest>::visit_object_header;
using visitor_<reader<IsNewest>, false, IsNewest>::visit_scenery_proto;
using visitor_<reader<IsNewest>, false, IsNewest>::PROTO;

struct atlas_pair
{
Expand All @@ -778,7 +784,6 @@ struct reader final : visitor_<reader, false>

std::vector<StringView> strings;
std::vector<atlas_pair> atlases;
proto_t PROTO = (proto_t)-1;
object_id object_counter = world::object_counter_init;
uint32_t nstrings = 0, natlases = 0, nchunks = 0;

Expand Down Expand Up @@ -916,12 +921,22 @@ struct reader final : visitor_<reader, false>
object_counter = Math::max(object_counter, id);
}

bool deserialize_header_(binary_reader<const char*>& s, ArrayView<const char> buf)
static proto_t deserialize_header_1(binary_reader<const char*>& s)
{
fm_assert(PROTO == (proto_t)-1);
proto_t proto;
auto magic = s.read<file_magic.size()>();
fm_soft_assert(StringView{magic.data(), magic.size()} == file_magic);
PROTO << s;
proto << s;
return proto;
}

bool deserialize_header_(binary_reader<const char*>& s, ArrayView<const char> 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);
Expand Down Expand Up @@ -1001,21 +1016,18 @@ struct reader final : visitor_<reader, false>

template<typename INT> void deserialize_tile_part(auto&& g, uint32_t& i, byte_reader& r)
{
constexpr auto highbit = reader::highbit<INT>;
constexpr auto null = reader::null<INT>;

INT num;
uint32_t num_idempotent = 0;

visit(num, r);

if (num & highbit)
if (num & highbit<INT>)
{
num_idempotent = num & ~highbit;
num_idempotent = num & ~highbit<INT>;
visit(num, r);
}

if (num != null)
if (num != null<INT>)
for (uint32_t j = 0; j <= num_idempotent; j++)
g(i+j, num);

Expand Down Expand Up @@ -1086,10 +1098,9 @@ struct reader final : visitor_<reader, false>
deserialize_objects_(c, r);
}

void deserialize_world(ArrayView<const char> buf)
void deserialize_world(binary_reader<const char*>& s, ArrayView<const char> 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);
Expand All @@ -1101,7 +1112,8 @@ struct reader final : visitor_<reader, false>
}
};

template struct visitor_<reader, false>;
template struct visitor_<reader<true>, false, true>;
template struct visitor_<reader<false>, false, false>;

} // namespace

Expand Down Expand Up @@ -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<const char*>{&buf.data[0], &buf.data[buf.size]};
auto proto = reader<false>::deserialize_header_1(s);
if (proto == proto_version)
{
struct reader<true> r{w, asset_policy};
r.deserialize_world(s, buf, proto);
}
else
{
struct reader<false> r{w, asset_policy};
r.deserialize_world(s, buf, proto);
}

//fm_assert("t o d o && false);
return w;
Expand Down

0 comments on commit 3caa77f

Please sign in to comment.