From 280b4c235fe11dd629f882f0fb5054384fcee1d7 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Fri, 28 Oct 2022 22:10:27 +0200 Subject: more work --- serialize/binary-reader.hpp | 6 ++- serialize/binary-reader.inl | 15 ++++++-- serialize/binary-writer.hpp | 1 - serialize/world-impl.hpp | 5 ++- serialize/world-reader.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++--- serialize/world-writer.cpp | 21 +++++++---- serialize/world.cpp | 12 ------ 7 files changed, 116 insertions(+), 33 deletions(-) delete mode 100644 serialize/world.cpp (limited to 'serialize') diff --git a/serialize/binary-reader.hpp b/serialize/binary-reader.hpp index 3b6eae9a..7eed2145 100644 --- a/serialize/binary-reader.hpp +++ b/serialize/binary-reader.hpp @@ -1,11 +1,12 @@ #pragma once #include "binary-serializer.hpp" #include +#include namespace floormat::Serialize { -union value_u { - alignas(alignof(double)) char bytes[8]; +union alignas(alignof(double)) value_u { + char bytes[8]; unsigned char uc; std::uint8_t u8; std::uint16_t u16; @@ -44,6 +45,7 @@ struct binary_reader final { template T read() noexcept; template constexpr std::array read() noexcept; constexpr std::size_t bytes_read() const noexcept { return num_bytes_read; } + constexpr StringView read_asciiz_string() noexcept; private: std::size_t num_bytes_read = 0; diff --git a/serialize/binary-reader.inl b/serialize/binary-reader.inl index cf935a76..ad2d59bc 100644 --- a/serialize/binary-reader.inl +++ b/serialize/binary-reader.inl @@ -33,7 +33,7 @@ template T binary_reader::read() noexcept { value_u buf = read_u(); - return *reinterpret_cast(buf.bytes); + return *reinterpret_cast(buf.bytes); } template @@ -79,9 +79,18 @@ constexpr value_u binary_reader::read_u() noexcept template binary_reader& operator>>(binary_reader& reader, T& x) noexcept { - value_u u = reader.template read(); - x = *reinterpret_cast(&u.bytes[0]); + x = reader.template read(); return reader; } +template +constexpr StringView binary_reader::read_asciiz_string() noexcept +{ + const It pos = it; + while (it != end) + if (char c = *it++; c == '\0') + return StringView{pos, (std::size_t)std::distance(pos, end), StringViewFlag::NullTerminated}; + fm_abort("unexpected EOF while reading a string"); +} + } // namespace floormat::Serialize diff --git a/serialize/binary-writer.hpp b/serialize/binary-writer.hpp index ead0824d..02fe427d 100644 --- a/serialize/binary-writer.hpp +++ b/serialize/binary-writer.hpp @@ -11,7 +11,6 @@ struct binary_writer final { template constexpr void write(T x) noexcept; template void write(T x) noexcept; constexpr void write_asciiz_string(StringView str) noexcept; - constexpr std::size_t bytes_written() const noexcept { return _bytes_written; } private: diff --git a/serialize/world-impl.hpp b/serialize/world-impl.hpp index b2d74db3..26073edd 100644 --- a/serialize/world-impl.hpp +++ b/serialize/world-impl.hpp @@ -12,8 +12,9 @@ namespace floormat::Serialize { namespace { using tilemeta = std::uint8_t; -using imgvar = std::uint8_t; +using imgvar = decltype(tile_image::variant); using atlasid = std::uint16_t; +using chunksiz = std::uint16_t; using enum tile::pass_mode; template constexpr inline T int_max = std::numeric_limits::max(); @@ -45,7 +46,7 @@ namespace { struct FILE_raii final { FILE_raii(FILE* s) noexcept : s{s} {} - ~FILE_raii() noexcept { if (s) ::fclose(s); } + ~FILE_raii() noexcept { close(); } operator FILE*() noexcept { return s; } void close() noexcept { if (s) ::fclose(s); s = nullptr; } private: diff --git a/serialize/world-reader.cpp b/serialize/world-reader.cpp index 86b07834..299386de 100644 --- a/serialize/world-reader.cpp +++ b/serialize/world-reader.cpp @@ -2,6 +2,8 @@ #include "world-impl.hpp" #include "binary-reader.inl" #include "src/world.hpp" +#include "src/loader.hpp" +#include "src/tile-atlas.hpp" namespace floormat::Serialize { @@ -12,9 +14,11 @@ struct reader_state final { void deserialize_world(ArrayView buf); private: + using reader_t = binary_reader{}.cbegin())>; + std::shared_ptr lookup_atlas(atlasid id); - void read_atlases(); - void read_chunks(); + void read_atlases(reader_t& reader); + void read_chunks(reader_t& reader); std::unordered_map> atlases; struct world* world; @@ -22,11 +26,84 @@ private: reader_state::reader_state(struct world& world) noexcept : world{&world} {} +void reader_state::read_atlases(reader_t& s) +{ + const auto N = s.read(); + atlases.reserve(N * 2); + for (atlasid i = 0; i < N; i++) + { + Vector2ub size; + s >> size[0]; + s >> size[1]; + atlases[i] = loader.tile_atlas(s.read_asciiz_string(), size); + } +} + +std::shared_ptr reader_state::lookup_atlas(atlasid id) +{ + if (auto it = atlases.find(id); it != atlases.end()) + return it->second; + else + fm_abort("not such atlas: '%zu'", (std::size_t)id); +} + +void reader_state::read_chunks(reader_t& s) +{ + const auto N = s.read(); + + for (std::size_t i = 0; i < N; i++) + { + std::decay_t magic; + s >> magic; + if (magic != chunk_magic) + fm_abort("bad chunk magic"); + chunk_coords coord; + s >> coord.x; + s >> coord.y; + auto& chunk = (*world)[coord]; + for (std::size_t i = 0; i < TILE_COUNT; i++) + { + const tilemeta flags = s.read(); + const auto make_atlas = [&] -> tile_image { + auto atlas = lookup_atlas(s.read()); + auto id = s.read(); + return { atlas, id }; + }; + tile t; + if (flags & meta_ground) + t.ground_image = make_atlas(); + if (flags & meta_wall_n) + t.wall_north = make_atlas(); + if (flags & meta_wall_w) + t.wall_west = make_atlas(); + switch (auto x = flags & pass_mask) + { + case tile::pass_shoot_through: + case tile::pass_blocked: + case tile::pass_ok: + t.passability = (tile::pass_mode)x; + break; + default: + fm_abort("bad pass mode '%zu' for tile %zu", i, (std::size_t)x); + } + chunk[i] = std::move(t); + } + } +} + void reader_state::deserialize_world(ArrayView buf) { auto s = binary_reader{buf}; if (!!::memcmp(s.read().data(), file_magic, std::size(file_magic)-1)) fm_abort("bad magic"); + std::decay_t proto; + s >> proto; + if (proto != proto_version) + fm_abort("bad proto version '%zu' (should be '%zu')", + (std::size_t)proto, (std::size_t)proto_version); + read_atlases(s); + read_chunks(s); + s.assert_end(); } } // namespace @@ -47,17 +124,17 @@ world world::deserialize(StringView filename) if (!f) fm_abort("fopen(\"%s\", \"r\"): %s", filename.data(), strerror(errbuf)); if (int ret = ::fseek(f, 0, SEEK_END); ret != 0) - fm_abort("fseek(\"%s\", 0, SEEK_END): %s", filename.data(), strerror(errbuf)); + fm_abort("fseek(SEEK_END): %s", strerror(errbuf)); std::size_t len; if (auto len_ = ::ftell(f); len_ >= 0) len = (std::size_t)len_; else - fm_abort("ftell(\"%s\"): %s", filename.data(), strerror(errbuf)); + fm_abort("ftell: %s", strerror(errbuf)); if (int ret = ::fseek(f, 0, SEEK_SET); ret != 0) - fm_abort("fseek(\"%s\", 0, SEEK_SET): %s", filename.data(), strerror(errbuf)); + fm_abort("fseek(SEEK_SET): %s", strerror(errbuf)); auto buf_ = std::make_unique(len); if (auto ret = ::fread(&buf_[0], len, 1, f); ret != 1) - fm_abort("fread(\"%s\", %zu): %s", filename.data(), len, strerror(errbuf)); + fm_abort("fread %zu: %s", len, strerror(errbuf)); world w; Serialize::reader_state s{w}; diff --git a/serialize/world-writer.cpp b/serialize/world-writer.cpp index 80c8de4a..c0f59e9b 100644 --- a/serialize/world-writer.cpp +++ b/serialize/world-writer.cpp @@ -106,8 +106,6 @@ void writer_state::serialize_chunk(const chunk& c, chunk_coords coord) s << flags; - static_assert(std::is_same_v); - if (img_g != null_atlas) s << img_g << x.ground_image.variant; if (img_n != null_atlas) @@ -126,8 +124,8 @@ void writer_state::serialize_chunk(const chunk& c, chunk_coords coord) void writer_state::serialize_atlases() { - const std::size_t sz = tile_images.size(); - fm_assert(sz < int_max); + fm_assert(tile_images.size() < int_max); + const auto sz = (atlasid)tile_images.size(); const auto atlasbuf_size = sizeof(sz) + atlas_name_max*sz; atlas_buf.resize(atlasbuf_size); auto s = binary_writer{atlas_buf.begin()}; @@ -143,6 +141,8 @@ void writer_state::serialize_atlases() fm_debug_assert(s.bytes_written() + namesiz + 1 <= atlasbuf_size); fm_assert(namesiz <= atlas_name_max - 1); // null terminated fm_debug_assert(name.find('\0') == name.cend()); + const auto sz2 = atlas->num_tiles2(); + s << sz2[0]; s << sz2[1]; s.write_asciiz_string(name); } fm_assert(s.bytes_written() <= atlasbuf_size); @@ -165,9 +165,15 @@ ArrayView writer_state::serialize_world() } serialize_atlases(); + using proto_t = std::decay_t; + union { chunksiz x; char bytes[sizeof x]; } c = {.x = maybe_byteswap((chunksiz)world->size())}; + union { proto_t x; char bytes[sizeof x]; } p = {.x = maybe_byteswap(proto_version)}; + fm_assert(world->size() <= int_max); + std::size_t len = 0; len += std::size(file_magic)-1; - len += sizeof(proto_version); + len += sizeof(p.x); + len += sizeof(c.x); for (const auto& buf : chunk_bufs) len += buf.size(); len += atlas_buf.size(); @@ -176,14 +182,15 @@ ArrayView writer_state::serialize_world() const auto copy = [&](const auto& in) { #ifndef FM_NO_DEBUG auto len1 = std::distance(std::cbegin(in), std::cend(in)), - len2 = std::distance(it, file_buf.end()); + len2 = std::distance(it, file_buf.end()); fm_assert(len1 <= len2); #endif it = std::copy(std::cbegin(in), std::cend(in), it); }; copy(Containers::StringView{file_magic, std::size(file_magic)-1}); - copy(std::initializer_list{ char(proto_version & 0xff), char((proto_version >> 8) & 0xff) }); + copy(p.bytes); copy(atlas_buf); + copy(c.bytes); for (const auto& buf : chunk_bufs) copy(buf); return {file_buf.data(), file_buf.size()}; diff --git a/serialize/world.cpp b/serialize/world.cpp deleted file mode 100644 index 70d4d0c9..00000000 --- a/serialize/world.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#define FM_SERIALIZE_WORLD_IMPL -#include "world-impl.hpp" -#include "binary-reader.inl" -#include - -namespace floormat { - -namespace Path = Corrade::Utility::Path; - - - -} // namespace floormat -- cgit v1.2.3