diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2024-01-21 15:49:02 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2024-01-21 15:49:02 +0100 |
commit | 282cbae99ad55ed066595ee05736a89f93cc7381 (patch) | |
tree | 997a94bb9e249fd766589f211b076cb6cf26ecb7 | |
parent | c0f27919a98b9083c967cfd7c464a53b7cdc7f14 (diff) |
w
-rw-r--r-- | serialize/savegame.cpp | 175 | ||||
-rw-r--r-- | src/tile-defs.hpp | 2 |
2 files changed, 122 insertions, 55 deletions
diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp index 917234e1..c9902522 100644 --- a/serialize/savegame.cpp +++ b/serialize/savegame.cpp @@ -1,4 +1,5 @@ #include "binary-writer.inl" +#include "binary-reader.inl" #include "compat/defs.hpp" #include "compat/strerror.hpp" #include "compat/int-hash.hpp" @@ -66,6 +67,7 @@ struct buffer std::unique_ptr<char[]> data; size_t size; + operator ArrayView<const char>() const { return {&data[0], size}; } bool empty() const { return size == 0; } buffer() : data{nullptr}, size{0} {} buffer(size_t len) : // todo use allocator @@ -97,14 +99,16 @@ struct visitor_ using tilemeta = uint8_t; using atlasid = uint32_t; using chunksiz = uint32_t; - using proto_t = uint32_t; + using proto_t = uint16_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>); static constexpr inline proto_t proto_version = 20; + static constexpr inline proto_t proto_version_min = 20; static constexpr inline auto file_magic = ".floormat.save"_s; - static constexpr inline auto chunk_magic = (uint16_t)0xdead; - static constexpr inline auto object_magic = (uint16_t)0xb00b; - static constexpr inline auto atlas_magic = (uint16_t)0xbeef; - static constexpr inline auto null_atlas = (atlasid)-1; + static constexpr inline auto chunk_magic = maybe_byteswap((uint16_t)0xadde); + static constexpr inline auto object_magic = maybe_byteswap((uint16_t)0x0bb0); template<typename T, typename F> CORRADE_ALWAYS_INLINE void do_visit_nonconst(const T& value, F&& fun) @@ -197,7 +201,7 @@ struct writer final : visitor_<writer> buffer header_buf{}, string_buf{}; - writer(const world& w) : w{w} { } // added to avoid spurious warning until GCC 14: warning: missing initializer for member ::<anonymous> + writer(const world& w) : w{w} {} // avoid spurious warning until GCC 14: warning: missing initializer for member ::<anonymous> struct size_counter { @@ -257,9 +261,32 @@ struct writer final : visitor_<writer> } template<typename F> - void intern_atlas_(void* atlas, atlas_type type, F&& f) + void visit(anim_atlas& a, F&& f) + { + atlasid id = intern_atlas(&a, atlas_type::object); + do_visit(id, f); + } + + template<typename F> void visit(const chunk_coords_& coord, F&& f) + { + f(coord.x); + f(coord.y); + f(coord.z); + } + + template<typename F> void visit(const local_coords& pt, F&& f) + { + f(pt.to_index()); + } + + template<typename F> void visit(StringView name, F&& f) + { + f(intern_string(name)); + } + + template<typename F> + void intern_atlas_(const void* atlas, atlas_type type, F&& f) { - do_visit(atlas_magic, f); do_visit(type, f); StringView name; @@ -277,7 +304,7 @@ ok: do_visit(intern_string(name), f); } - [[nodiscard]] atlasid intern_atlas(void* atlas, atlas_type type) + [[nodiscard]] atlasid intern_atlas(const void* atlas, atlas_type type) { atlas_array.reserve(vector_initial_size); fm_assert(atlas != nullptr); @@ -300,15 +327,15 @@ ok: fm_assert(s.bytes_written() == s.bytes_allocated()); atlas_array.emplace_back(std::move(buf), atlas, type); kv.value() = id; - fm_assert(id != null_atlas); + fm_assert(id < null<atlasid>); return atlasid{id}; } } - atlasid maybe_intern_atlas(void* atlas, atlas_type type) + atlasid maybe_intern_atlas(const void* atlas, atlas_type type) { if (!atlas) - return null_atlas; + return null<atlasid>; else return intern_atlas(atlas, type); } @@ -324,7 +351,7 @@ ok: auto id = (uint32_t)string_array.size(); string_array.emplace_back(str); kv.value() = id; - fm_assert(id != null_atlas); + fm_assert(id != null<atlasid>); return atlasid{id}; } } @@ -342,27 +369,73 @@ ok: } } - template<typename F> - void serialize_tile_(tile_ref t, F&& f) - { - auto g = maybe_intern_atlas(t.ground_atlas().get(), atlas_type::ground), - n = maybe_intern_atlas(t.wall_north_atlas().get(), atlas_type::wall), - w = maybe_intern_atlas(t.wall_west_atlas().get(), atlas_type::wall); - do_visit(g, f); - do_visit(n, f); - do_visit(w, f); - if (g != null_atlas) do_visit(t.ground().variant, f); - if (n != null_atlas) do_visit(t.wall_north().variant, f); - if (w != null_atlas) do_visit(t.wall_west().variant, f); + void serialize_tile_(auto&& g, uint32_t& i, auto&& f) + { + 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); + 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) + break; + else + num_idempotent++; + + if (num_idempotent) + { + INT num = highbit | INT(num_idempotent); + do_visit(num, f); + } + + if (a != null) + do_visit(a, f); + else + do_visit(null, f); + + i += num_idempotent; + fm_debug_assert(i <= TILE_COUNT); } void serialize_chunk_(chunk& c, buffer& buf) { - const auto fn = [this](chunk& c, auto&& f) { + const auto fn = [this](chunk& c, auto&& f) + { + static_assert(null<uint8_t> == 127 && highbit<uint8_t> == 128); + static_assert(null<uint32_t> == 0x7fffffff && highbit<uint32_t> == 0x80000000); do_visit(chunk_magic, f); do_visit(c.coord(), f); + // todo do atlases and variants separately + for (uint32_t i = 0; i < TILE_COUNT; i++) + serialize_tile_([&](uint32_t i) { + return maybe_intern_atlas(c[i].ground_atlas().get(), atlas_type::ground); + }, i, f); + for (uint32_t i = 0; i < TILE_COUNT; i++) + serialize_tile_([&](uint32_t i) { + return maybe_intern_atlas(c[i].wall_north_atlas().get(), atlas_type::wall); + }, i, f); for (uint32_t i = 0; i < TILE_COUNT; i++) - serialize_tile_(c[i], f); + serialize_tile_([&](uint32_t i) { + return maybe_intern_atlas(c[i].wall_west_atlas().get(), atlas_type::wall); + }, i, f); + for (uint32_t i = 0; i < TILE_COUNT; i++) + serialize_tile_([&](uint32_t i) { + auto v = c[i].ground().variant; return v == (variant_t)-1 ? null<variant_t> : v; + }, i, f); + for (uint32_t i = 0; i < TILE_COUNT; i++) + serialize_tile_([&c](uint32_t i) { + auto v = c[i].wall_north().variant; return v == (variant_t)-1 ? null<variant_t> : v; + }, i, f); + for (uint32_t i = 0; i < TILE_COUNT; i++) + serialize_tile_([&c](uint32_t i) { + auto v = c[i].wall_west().variant; return v == (variant_t)-1 ? null<variant_t> : v; + }, i, f); + serialize_objects_(c, f); }; @@ -411,10 +484,10 @@ ok: void serialize_world() { fm_assert(string_array.empty()); - fm_assert(atlas_array.empty()); - fm_assert(chunk_array.empty()); fm_assert(header_buf.empty()); fm_assert(string_buf.empty()); + fm_assert(atlas_array.empty()); + fm_assert(chunk_array.empty()); for (auto& [coord, c] : non_const(w.chunks())) chunk_array.push_back({.c = &c }); @@ -441,41 +514,35 @@ ok: serialize_strings_(); } +}; - template<typename F> - void visit(anim_atlas& a, F&& f) - { - atlasid id = intern_atlas(&a, atlas_type::object); - do_visit(id, f); - } +struct reader final : visitor_<reader> +{ + proto_t PROTO = (proto_t)-1; - template<typename F> void visit(const chunk_coords_& coord, F&& f) - { - f(coord.x); - f(coord.y); - f(coord.z); - } + class world& w; + reader(class world& w) : w{w} {} - template<typename F> void visit(const local_coords& pt, F&& f) + void deserialize_header_(binary_reader<const char*>& s) { - f(pt.to_index()); + fm_assert(PROTO == (proto_t)-1); + auto magic = s.read<file_magic.size()>(); + fm_soft_assert(StringView{magic.data(), magic.size()} == file_magic); + PROTO << s; + fm_soft_assert(PROTO >= proto_version_min); + fm_soft_assert(PROTO <= proto_version); } - template<typename F> void visit(StringView name, F&& f) + void deserialize_world(ArrayView<const char> buf) { - f(intern_string(name)); + binary_reader s{buf.data(), buf.data() + buf.size()}; + deserialize_header_(s); } }; -struct reader final : visitor_<reader> -{ - class world& w; - reader(class world& w) : w{w} {} -}; - void my_fwrite(FILE_raii& f, const buffer& buf, char(&errbuf)[128]) { - auto len = ::fwrite(&buf.data[0], buf.size, 1, f); + auto len = std::fwrite(&buf.data[0], buf.size, 1, f); int error = errno; if (len != 1) fm_abort("fwrite: %s", get_error_string(errbuf, error).data()); @@ -550,7 +617,7 @@ class world world::deserialize(StringView filename) noexcept(false) if (int ret = std::fseek(f, 0, SEEK_END); ret != 0) fm_throw("fseek(SEEK_END): {}"_cf, get_error_string(errbuf)); size_t len; - if (auto len_ = ::ftell(f); len_ >= 0) + if (auto len_ = std::ftell(f); len_ >= 0) len = (size_t)len_; else fm_throw("ftell: {}"_cf, get_error_string(errbuf)); @@ -565,7 +632,7 @@ class world world::deserialize(StringView filename) noexcept(false) } class world w; - struct reader reader(w); + struct reader r{w}; r.deserialize_world(buf); fm_assert("todo" && false); diff --git a/src/tile-defs.hpp b/src/tile-defs.hpp index 46e72984..0dc74e20 100644 --- a/src/tile-defs.hpp +++ b/src/tile-defs.hpp @@ -4,7 +4,7 @@ namespace floormat { -using variant_t = uint8_t; +using variant_t = uint8_t; // todo change later to 16 bits constexpr inline uint32_t TILE_MAX_DIM = 16; constexpr inline size_t TILE_COUNT = size_t{TILE_MAX_DIM}*size_t{TILE_MAX_DIM}; |